Commit Graph

3 Commits

Author SHA1 Message Date
William S Fulton 797c3c494a C++20: accept template-id concept-id as a type-constraint
Allow a concept-id with explicit template arguments to stand in for
'typename'/'class' in a template parameter list:

  template<std::convertible_to<int> T>  int to_int(T x);
  template<Pair<int> T>                 int first_int(T x);

Previously the type constrained template parameter classifier in
'classify_template_param_type()' short circuited on any template-id and
fell through to TPC_KEEP, leaving the parm misclassified as a non-type
template parameter with the template-id as its type.  The wrapper
compiled by accident (the C++ compiler resolved the constraint
regardless) but the templateparm node was wrong.

The classifier now extracts the bare template prefix via
'SwigType_templateprefix()' and looks that up in the symbol table.  When
the prefix resolves to a node with 'templatetype == "concept"' the parm
is remapped to 'typename T' with a 'concept-id' constraint atom carrying
the full template-id; when the prefix resolves to a class template the
parm is left as a non-type parameter of class type (C++20 NTTP of class-
type); when the prefix is unresolved the parm is remapped lazily and
warning 332 is deferred to '%template' instantiation as before.
Warning is reworded with more information and can be suppressed using
%warning..

Tests in cpp20_concepts_constrained_param cover STL, user defined,
variadic, default arg, class template and ::-qualified forms.

Assisted-by: Claude Opus 4.7
2026-05-12 23:04:20 +01:00
William S Fulton 7300e3710d C++20 concepts: handle unresolved type-constraints leniently
A type-constraint whose concept-id SWIG has not parsed is now remapped
to 'typename T' (matching the resolved case) and the parm is flagged
with 'constraint:unresolved'.  New warning 332 fires only when the
template is actually instantiated via %template, so unused declarations
are silent:

  example.i:3: Warning 332: Nothing known about type-constraint 'Numeric'. Treated as 'typename'.

The wrapper for an instantiation emits the templated call literally
('cube< int >(arg1)') and relies on the C++ compiler to resolve the
constraint at wrapper compile time, in line with SWIG's "best effort
wrap on partial type information" policy.

Assisted-by: Claude Code (Opus 4.7)
2026-05-12 23:04:20 +01:00
William S Fulton eb3fe4f6a1 C++20: parse type constrained template parameters
A C++20 type-constraint (e.g. 'Numeric T') used in place of 'typename' /
'class' in a template parameter list is now recognised as the standard
shorthand for 'typename T' plus a 'requires Concept<T>' clause.  The
parm fallback in 'templateparameter' classifies the parsed parm via
classify_template_param_type():

  - resolves to a concept in scope -> rewrite to 'typename T' (or
    'v.typename Ts...' for a variadic pack) and attach a 'concept-id'
    constraint atom on the parm's "constraint" attribute, matching the
    parm representation promote_abbreviated_template() already builds
    for 'Concept auto x';
  - resolves to a typedef, class or enum, or names a primitive -> leave
    the parm as a non-type template parameter;
  - is an unqualified identifier not declared anywhere SWIG has seen ->
    issue an error so the user knows to make the concept visible.

The classifier is built on existing SwigType helpers - SwigType_type
covers primitives (including multi word forms like 'unsigned int' /
'long long' and resolvable typedefs such as 'size_t'); SwigType_isenum,
SwigType_issimple, SwigType_istemplate and SwigType_isvariadic /
SwigType_del_variadic handle decoration, enum and template-id forms.

  template<Numeric T>             T cube(T x);
  template<nest::Integral T>      T half(T x);                   // ::-qualified
  template<Numeric T, typename U> T scale(T x, U factor);        // mixed
  template<Numeric T = int>       T identity(T x);               // default arg
  template<Numeric... Ts>         int count_numeric(Ts...);      // variadic
  template<typename X, Numeric... Ts>     int tag_count(X, Ts...);
  template<SmallNumeric X, Numeric... Ts> int small_tag_count(X, Ts...);
  template<Numeric T> class Box { T v; };                        // class template

A type-constraint that itself takes template arguments before the
parameter name (e.g. 'template<std::convertible_to<int> T>') is not yet
parsed and must still be moved to a 'requires'-clause.

Includes new errors/cpp_concept_not_visible test exercising the
"undeclared concept" error path, plus a non-template-member-with-
requires-clause case in cpp20_concepts_extra exercising
ConstrainedHolder<T>::cube() const requires Numeric<T> (no parser
change needed).

Assisted-by: Claude Code (Opus 4.7)
2026-05-12 23:04:20 +01:00