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
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)
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)