Commit Graph

805 Commits

Author SHA1 Message Date
William S Fulton d3a20504be Fix multicharacter constants to have type int, not char
A character constant containing more than one character, e.g. 'ab', has
type int per both the C standard (6.4.4.4p10) and the C++ standard
([lex.ccon], which describes it as a "multicharacter literal"). SWIG
previously classified every character constant as char regardless of
length, which caused the generated wrapper accessor for a constant such
as #define X 'ab' to be declared char instead of int, silently
truncating the value.

The literal text is left untouched (SWIG does not evaluate constant
expressions), only the internal type tag changes, so the real compiler
that builds the generated wrapper still computes the (implementation
defined) value, exactly as it would for hand written C/C++ code.

Add a new C-only multichar_constant.i test case with per-language runme
files, and keep the new csharp/java char_constant runme files (which
had no coverage before) for the existing, unrelated character constant
checks.

Compiling a real multicharacter constant always triggers GCC's
-Wmultichar warning. #pragma GCC diagnostic ignored "-Wmultichar" does
not suppress it: this is a known, longstanding GCC C++ front-end bug
(gcc.gnu.org PR57241/PR53431, fixed in GCC 13) - verified locally that
it fails identically on GCC 11 and 12 regardless of file- or
function-level pragma scope, while working fine in C or with GCC 13+
or Clang (which also defines __GNUC__ but doesn't have this bug).

The new testcase avoids the warning entirely, with no build system
changes needed:

- The %inline global variable imulti_ab uses the real literal (guarded
  by the pragma) wherever that's known to work, and otherwise falls
  back to reconstructing the identical value GCC's own packing of
  'ab' produces (most significant byte first) without writing a
  multicharacter literal at all - the same symbol name and type either
  way, so the generated accessor links correctly regardless of which
  branch was compiled.
- The #define-driven MULTICHAR_AB constant (whose registration code is
  auto-generated per target language, so a similar fallback isn't
  portable to hand-write once for every language) is skipped entirely
  for octave and javascript's node/napi/v8 engines, the only
  configurations that always compile the generated wrapper as C++
  regardless of SWIG's own -c/-c++ mode (their runtime APIs require
  it) and so would otherwise still hit the GCC bug above.

Verified against both GCC 13 (default) and GCC 11 (matching the actual
CI toolchain that originally failed) across all configured languages.

Assisted-by: Claude Code (Sonnet 5)
2026-07-29 19:46:45 +01:00
William S Fulton 32a3e5b464 R: Honour %rename of enum items in the generated R code
R::enumvalueDeclaration built the enum item label passed to
defineEnumeration from the C++ name rather than sym:name, so %rename of an
enum item was not honoured in the generated R code. Use sym:name for the
label.

Add test coverage for %rename of an enum class and an enum item to the
cpp11_strongly_typed_enumerations runme of every target language that has
one, locking in the behaviour for the languages that were already correct.
The enum_thorough R runme now checks the renamed enum item too.

Assisted-by: Claude Code (Opus 4.8)
2026-07-15 00:24:05 +01:00
William S Fulton f156c99907 Fix protected/private nested class referenced in generated upcast helper
Reported on the swig-devel mailing list during early testing of swig-4.5,
where CrossWire SWORD's Perl bindings (GBFHTMLHREF::MyUserData deriving
from BasicFilterUserData) failed to build: a protected or private nested
class deriving from a class used elsewhere in the wrapped API could produce
a runtime upcast helper function referencing the nested class by its
inaccessible qualified name, a C++ compile error. No target language ever
wraps a non-public nested class, so typepass.cxx now simply skips
registering it for the cast table. Fixes it for Lua too, which hits the
same bug as Perl/Python/Ruby/Tcl despite having full nested class support,
since Java/C# are structurally immune (they don't use SWIG's generic
runtime type table at all). Added regression tests to nested_scope.i and
director_protected.i (covering directors/dirprot), with a runtime check in
every director_protected runme confirming polymorphic dispatch through the
wrapped base still works correctly.

Assisted-by: Claude Code (Sonnet 5)
2026-07-11 21:02:49 +01:00
William S Fulton 3dda830ac9 Add char *& string typemaps to all languages and char_strings runme parity
A 'char *&' (a reference to a char pointer) was only marshalled as a string by
C#, D, Go, Java and PHP; every other language treated it as an opaque char **
pointer.  Add char *& string typemaps to the languages that were missing them,
so a char *& function argument, return value or variable is marshalled as a
string in every target language.  SWIG's const reference stripping means this
also covers char *const&.

- Lib/typemaps/strings.swg: add Char *& to the %typemaps_string in, freearg,
  out, typecheck and director typemaps.  This gives char *& to the languages
  that use the Unified Typemap Library (Python, Ruby, Perl, Tcl, Octave, Scilab,
  R and JavaScript).  The shared in typemap now casts &buf to $1_ltype so that a
  const char * reference hidden behind a typedef also compiles.
- Lib/lua/luatypemaps.swg, Lib/guile/typemaps.i, Lib/ocaml/ocaml.swg: these
  define their own char * string typemaps; give each char *& and const char *&
  in/out/freearg typemaps too.
- Lib/r/rtype.swg: the C wrapper returned the char *& string correctly but the R
  proxy wrapped it as an undefined _p_p_char S4 class; add char *& to the
  scoerceout char list so it is returned as a plain character value.

char_strings.i is now exercised by a runme in every target language, all testing
the same set of functions (get/set/pingpong/global variables and all four char *&
functions), giving complete char *& typemap coverage and testing.

Where a language genuinely cannot support part of the char array portion of the
test (a char[] global has no varin typemap in most scripting languages, a char[16]
parameter is bounds checked, and Guile/OCaml reject char[] parameters), that one
assertion is adapted or skipped with an inline comment; the char *& coverage is
complete everywhere.

Assisted-by: Claude Code (Opus 4.8)
2026-07-09 21:55:58 +01:00
William S Fulton ae17418e48 Go: fix invalid conversion for const char * hidden behind a typedef
The Go 'in' typemap for char * wrote the null terminator through $1, which
fails to compile as C++ when the matched type is const char *, as happens
with a typedef such as 'typedef const char *MyString'. Allocate and write
the terminator through a char * temp, assign to $1 via $1_ltype, and cast
in the freearg so a const char * buffer can still be freed. Freeing $1
(rather than the temp) keeps the freearg working when char * is remapped
with %apply SWIGTYPE[], which overrides the in typemap but not the freearg.

Add coverage to the common char_strings.i test, which is compiled for
every language, rather than a Go only test: a typedef'd const char * setter
plus a runtime assertion in each language that has a char_strings runme
(c, csharp, d, java, javascript, lua, perl5, php).

See #3290.

Assisted-by: Claude Code (Opus 4.8)
2026-07-09 21:55:58 +01:00
Olly Betts 9140467c2d Improve handling of NULL vs nullptr vs 0 vs 0L
For some target languages (Octave, Python and Ruby), SWIG has previously
treated nullptr or NULL as an integer 0 if used in a situation where
the type wasn't known to be a pointer.

For nullptr this is never helpful, because it has type nullptr_t which
does not implicitly convert to 0, so we no longer do this.

For NULL it's rather dubious - C and C++ allow NULL to be defined as
integer 0, so `int i = NULL` may work and is occassionally seen in real
code, but it is semantically wrong.  Also GCC and clang define NULL to a
magic value and by default will warn about such misuse, so it's likely
to be less common than before they did this.  So now SWIG only converts
NULL to 0 if used in a context where we know the underlying type is an
arithmetic type.

Using an integer zero (or equivalent value such as 0L) for a NULL
pointer is valid, and SWIG will still treat it as a NULL pointer if used
in a context where know the type is a pointer.  This is now done based
on the value of the integer constant so also applies to 0L (previously
it was only done if the value was written in the code as literally `0`).

Fixes: #3472
2026-06-29 14:36:06 +12:00
William S Fulton c0754fbde2 Add test coverage and docs for alias templates as a base class and using-declaration qualifier
The test suite previously only exercised C++11 alias templates as function return
types (cpp11_alias_templates).  These add coverage for an alias template instantiation
used in inheritance positions:

  - cpp11_alias_template_inheritance: an identity alias template as a base with a member
    using-declaration, an identity alias whose inherited enum is imported through the alias
    qualifier (with the alias also naming a function parameter type), a non-identity alias
    as the base of a class template where the using-declaration imports an overload that
    merges with a local overload, and an inheriting constructor whose scope qualifier is an
    alias template
  - cpp17_using_pack_alias_template: a C++17 using-declaration pack over an alias template
    base pack, both for inherited member functions ('using Identity<Ts>::g...;') and for
    inherited constructors ('using Identity<Ts>::Identity...;') - the alias template
    analogue of cpp17_inheriting_constructors_pack, which inherits through direct bases

Each follows the documented pattern: the underlying template is instantiated with a
named %template and the alias instantiation is registered with an empty %template(),
after which the alias resolves in these positions just like the underlying type.

A new using_member_typedef_overload test covers the related typedef-qualifier cases: an
overload imported through a typedef-to-template-instantiation qualifier that merges with a
local overload, and an inherited enum imported through a typedef qualifier.

Investigated as issue #3478: an alias template used as a base or using-declaration
qualifier appears to be unresolved (Warning 401 / Warning 315), but this is the
documented requirement that the alias instantiation be registered with %template();
it is not a SWIG defect.

In cpp17_using_pack_alias_template the Over method-pack overloads are ignored for D only
('#if defined(SWIGD) %ignore Over<A, B>::g;'): D wraps multiple inheritance as single
inheritance plus mix-in methods and marks the g(int) overload inherited from the second
base 'override', which ldmd2 rejects.  The class is still wrapped for D and every other
language exercises the overloads.  For Visual C++ the alias-template-as-base-pack classes
are guarded with '#ifndef _MSC_VER', falling back to the equivalent direct 'Ts...' base
(MSVC rejects an alias template base in a pack expansion with C3770); SWIG still parses and
wraps the alias form so coverage is unchanged, mirroring cpp17_using_typename_pack.

The C++11 chapter's 'Type alias and alias templates' section is split into separate
'Type aliases' and 'Alias templates' sections, and the alias template section is
rewritten to explain the two-%template requirement and the base class / using-declaration
usage that was previously only shown for return types.

The swig-doc and swig-test skills gain a note that code examples and .i test cases must
not place a class or struct definition on a single line.  The swig-test skill also gains
guidance on handling a failure in a single target language (fix or work around it in that
language rather than excluding the test, except for experimental backends) and on
reporting which languages and tests were run.

Assisted-by: Claude Opus 4.8
2026-06-20 07:20:37 +01:00
William S Fulton c0d4f290d6 Fix C++17 inheriting constructor pack dropping the last base constructor
The inheriting constructor pack 'using T::T ...;' over a variadic base pack
wrapped the constructors of every base except the last, and emitted a spurious
Warning 526 naming the using declaration after the derived class instead of the
base.

During template instantiation the pack using declaration is expanded into one
using declaration per base: the first element is patched in place and the rest
are appended as siblings.  The appended siblings were only revisited by the
class child walk when the using declaration was not the last child, because that
walk captures the next sibling before recursing so that an empty pack can detach
itself.  As the pack using declaration is normally the last member, the appended
siblings kept the unexpanded template name and were never registered as
inheriting constructors of the instantiated class.

Expand and splice each appended sibling in place immediately after the first
element, so every base constructor is wrapped regardless of the using
declaration's position and the empty pack detach path is left untouched.

Closes #3481

Assisted-by: Claude Opus 4.8
2026-06-18 22:01:51 +01:00
William S Fulton 6723e09480 Fix syntax error for inherited conversion operator in a using declaration (#3480)
A using declaration naming an inherited conversion function, such as
'using Base::operator int;', was rejected with a syntax error. Other
using-declaration declarator-ids parsed fine, but a conversion-operator-id was
handled only by the standalone conversion operator rule, which requires a full
function definition; no using-declaration production accepted it.

Add a dedicated using-declaration production for a scope qualified conversion
operator. This is kept separate from the shared idcolon rule: idcolon would
greedily consume the trailing scope qualifier and never reach the
CONVERSIONOPERATOR token, and extending idcolon itself would change how out of
class conversion operator definitions are parsed. The conversion operator is now
brought into the derived class like any other inherited member.

New test typedef_inherit_using imports a privately inherited conversion operator
with a using declaration, exercised by mirrored Python and Java runme scripts.

Fixes #3480

Assisted-by: Claude Code (Opus 4.8)
2026-06-18 21:42:06 +01:00
William S Fulton 85ed426a49 Fix syntax error for typename member type pack in a using declaration (#3479)
A using declaration that combines the 'typename' disambiguator with a C++17
pack expansion to import a member type from each base in a base pack, such as
'using typename Bases::value_type ...;', was rejected with a syntax error.
The typename form and the pack form each parsed on their own, but there was no
grammar production for the combination.

Add the missing USING TYPENAME idcolon ELLIPSIS SEMI production to cpp_using_decl,
mirroring the existing pack expansion rule and setting the pack flag.

New test cpp17_using_typename_pack exercises the construct with mirrored Python
and Java runme scripts.

Fixes #3479

Assisted-by: Claude Code (Opus 4.8)
2026-06-18 21:42:06 +01:00
William S Fulton c5bfe9766f Fix template parameter expansion for members inherited via a typedef qualifier
A using declaration that brings an inherited member into a derived class
through a typedef or C++11 alias of a template instantiation base lost the
template arguments of the qualifier, so template parameters in the member's
type were left unexpanded and the generated wrapper failed to compile.

For example, given:

  template <typename LinksT> class NodeI {
  public:
    using links_type = LinksT;
    Owners<links_type> owners;
  };
  template <typename LinksT> class Cluster : public NodeI<LinksT> {
  public:
    using NodeIT = NodeI<LinksT>;
    using NodeIT::owners;
  };

the wrapped owners member was emitted as Owners<links_type> rather than
Owners<NodeI<int>::links_type> for Cluster<int>.

Swig_symbol_type_qualify resolved the typedef qualifier but rebuilt the
scope from the found member's symbol table, which is the bare template name
and drops the template arguments. The qualifier is now rewritten to the
template-id, with the template arguments typedef reduced, so the existing
template handling preserves them. Inheriting constructor using declarations
and global scope or operator qualifiers are left untouched.

This is the same root cause as a member function whose parameter or return
type is written in terms of the base template parameter, for example a method
taking Owners<links_type>& inherited through the typedef qualifier (#1153).
Both forms are now expanded correctly.

Fixes #1042
Fixes #1153

Assisted-by: Claude Opus 4.8 (1M context)
2026-06-17 22:28:32 +01:00
William S Fulton 65fbe448ac Move the #2933 duplicate using declaration regression test to its own test case
The regression test was added to li_std_vector.i, but it has nothing to do
with std::vector - move to its own duplicate using declarations test.
2026-06-17 01:53:49 +01:00
William S Fulton 638742fbcc Detect inheriting constructors by base class match in the parser
The parser flagged an inheriting constructor (using Base::Base) only when the
terminal name of the nested-name-specifier equalled the unqualified-id, which
missed the form 'using Alias::Base' where Alias is a typedef for the direct base
Base.  The parser now also flags a candidate when the unqualified-id matches a
base class of the enclosing class, resolved through typedefs.  The type pass
verifies the candidate and clears it when the nested-name-specifier does not
resolve to an immediate base whose own name the unqualified-id is, so an ordinary
member using declaration is imported instead.

Extend cpp11_template_using_base and cpp11_inheriting_constructors_typedef with
the typedef-qualifier forms, including the base's own member typedef as the
qualifier, the base named by its own name through a typedef, and a member typedef
and protected method import.

Assisted-by: Claude Opus 4.8
2026-06-17 01:47:06 +01:00
William S Fulton b78dba177a Inherit constructors named through a typedef base
C++11 inheriting constructors (using Base::Base;) now work when the immediate
base class is named through a typedef, a chain of typedefs, a typedef whose
template argument is itself a typedef, a scope-qualified or namespaced name, or
a type-template parameter used directly as the base class (the mixin idiom).

The inheriting constructor's using-declaration qualifier is normalized in the
typepass stage; the inheriting base is then found by identity in the resolved base
class list and the base class' constructors are used to implement the inherited
constructors.

An inheriting-constructor using declaration whose qualifier is not an immediate
base class is reported with Warning 329 (uses base '...' which is not an
immediate base of '...') rather than the generic Warning 315.

Closes #2951)

Assisted-by: Claude Opus 4.8
2026-06-17 01:47:06 +01:00
William S Fulton 055fe0e793 Inject a base class name into the derived class so it can be named there
In C++ a base class' name is a member of the base (its injected class name) and so
is visible from within a derived class, named either unqualified ('Base') or scope
qualified through the derived class ('Derived::Base').  SWIG did not model this, so
a base in a namespace named without its namespace qualifier was not resolved: a
using declaration through such a typedef gave a spurious Warning 315 and dropped the
member, and the base named as a type was either unresolved or treated as a distinct
type from the base named directly.

  namespace Space { struct Base { ... }; }
  struct Derived : Space::Base {
    typedef Base base_type;        // Base, not Space::Base
    using base_type::method;       // no longer Warning 315
    Base m(Base b);                // 'Base' resolves to Space::Base
  };
  Derived::Base f(Derived::Base);  // 'Derived::Base' resolves to Space::Base

This is handled in three places, mirroring the same C++ rule:

 - Swig_symbol_inherit() adds the base class' name to the derived class' C symbol
   table, so the symbol table resolves the base named from within the derived class.
   The node added is the base's own entry in its enclosing scope, where every class
   is registered; it does not clash, as the derived class' constructors carry the
   derived name.

 - The type system (typepass) aliases the base class' name to the base's own scope
   within the derived class, so a base named unqualified as a type resolves to the
   base's own type.

 - SwigType_typedef_qualified() resolves a scope qualified name that itself names a
   scope (such as Derived::Base) to that scope's canonical name, so the base named
   through the derived class resolves to the same type as the base named directly.

Closes #2659

Assisted-by: Claude Opus 4.8 (1M context)
2026-06-17 01:47:06 +01:00
William S Fulton 978f675ad7 Fix spurious Warning 315 for a member using-declaration through a typedef base with a constructor
When a base class declares a constructor of its own, the constructor shares the
base class name.  Resolving a typedef that names the base to its scope looked the
base class name up through the derived class' inherited scope and found the base
constructor ahead of the class itself, so no typedef scope alias was created and a
later 'using base_type::member;' was reported as Warning 315 and the member
silently dropped.

Skip constructor nodes (including the using-declaration nodes that inherit base
constructors, which also carry the class name) when resolving a typedef to its
scope, so the class node is found instead.

Resolving the typedef now goes through Swig_symbol_clookup_check, whose
using-declaration chase loop was missing the self-reference guard that
Swig_symbol_clookup already has.  Add it to avoid infinite recursion (a stack
overflow and crash) on a self-referential using declaration; the existing test
Examples/test-suite/using2.i, with a top-level 'using ::baz;', exercises this.

Assisted-by: Claude Opus 4.8 (1M context)
2026-06-17 01:47:06 +01:00
William S Fulton ef8c5af40a Add a 3-level deep nested test 2026-06-08 18:34:56 +01:00
William S Fulton 6ec4b440d6 Keep unsupported nested classes as ignored classes, not forward declarations
For a target language that does not implement nested class support, a C++
class nested inside another class was discarded after parsing and replaced
by a synthesized forward class declaration in the enclosing scope. That
forward declaration carries no symbol table or members, so names that
resolve through the nested class could not be resolved. In particular a
using declaration whose scope qualifier reaches the nested class through a
typedef, such as 'using Outer::Nested::Me::method;' where 'Me' is a typedef
naming the nested class, gave a spurious 'Nothing known about ...' warning
(Warning 315) and the member was silently dropped.

The nested class is now kept as the real parsed class node and marked with
feature:ignore, instead of being replaced by a forward declaration. It is
still not wrapped (and still reported with Warning 325), but its scope,
members and typedefs remain available for name resolution. This also
resolves an out of line nested class definition written inside the
enclosing class, such as 'struct Outer::Nested { ... };', which previously
reported 'Outer is not defined as a valid scope'.

Nested template classes are kept the same way. A %template instantiation of
such a class still cannot be wrapped as a proxy class and continues to be
reported with Warning 331.

Add the using_nested_member_typedef test and the cpp_nested_out_of_line_scope
error test. The nested_scope test was previously run only for Java and C#
to avoid the out of line scope error above; it now runs for all languages.

Assisted-by: Claude Code (Opus 4.8)
2026-06-08 07:57:22 +01:00
William S Fulton 15cab25669 Qualify typedef-to-template scope qualifier before lookup
When a using declaration's scope qualifier is a typedef to a template
instantiation, symbol_scope_lookup reduced the typedef target but did not
scope qualify it before looking up the instantiated template scope. The
reduce step qualifies template arguments but not the template name itself,
so a template name introduced by a using declaration (rather than being
directly visible or brought in by a using directive) was left unqualified
and the instantiated scope was not found, giving a spurious 'Nothing known
about ...' warning (Warning 315), eg:

  namespace Other { template <typename T> struct OtherBase { typedef int Integer; typedef OtherBase<T> Me; }; }
  using Other::OtherBase;
  struct Derived : OtherBase<int> {
    typedef OtherBase<int> Base;
    using Base::Me::Integer;
  };

symbol_scope_lookup now qualifies the reduced type with
Swig_symbol_type_qualify before the lookup, following the reduce/qualify
idiom used elsewhere (eg Swig_symbol_template_reduce). This resolves the
template name through the using declaration.

Expand the using_member_typedef_template testcase for testing.

Assisted-by: Claude Code (Opus 4.8)
2026-06-04 22:45:38 +01:00
William S Fulton bdbf3586a8 Resolve using declarations qualified by a typedef to a template instantiation
Extends the #2694 fix. When a using declaration's scope qualifier is a
typedef to a template instantiation whose template arguments are
themselves typedefs, eg:

  typedef int IntAlias;
  typedef UsingBase<IntAlias> UsingBaseClass;
  using UsingBaseClass::Me::Integer;

the template arguments were not reduced, so 'UsingBase<IntAlias>' did not
match the instantiated 'UsingBase<int>' scope and SWIG issued a 'Nothing
known about ...' warning (Warning 315).

symbol_scope_lookup now reduces the template arguments of a template
instantiation typedef target via Swig_symbol_typedef_reduce before
looking up its scope, so the instantiated scope is found. The reduction
follows a typedef chain in the template argument too.

Add the using_member_typedef_template test, a template-based variant of
using_member_typedef, and the using_method_typedefs test (a protected
base method brought into the derived public interface via a using
declaration through various typedef forms, covering issue #2951 for
members - the symbol lookup fix above resolves it without a separate
allocate change).

Assisted-by: Claude Code (Opus 4.8)
2026-06-04 22:45:38 +01:00
William S Fulton 71856ff1a5 Fix resolution of names with a typedef scope qualifier (#2694)
A using declaration that introduces a base class member via a typedef
used as a scope qualifier, such as 'using BaseAlias::Me::Integer;' where
BaseAlias and Me are typedefs naming a class, was not resolved and SWIG
issued a spurious 'Nothing known about ...' warning (Warning 315).

When the parser qualifies the using declaration target name, the
qualified name lookup only matched scope qualifiers against registered
scope names and did not resolve typedefs that appear as a scope
qualifier. The stored uname was therefore left unqualified and later
symbol and typedef resolution failed. Swig_symbol_clookup and
Swig_symbol_clookup_check now fall back to resolving a typedef scope
qualifier through to its real scope before looking up the member. Only a
qualifier reached through a typedef is resolved here; one that resolves
directly to a class or namespace scope (for example a namespace made
visible by a using namespace directive) is left to the normal lookup. A
template instantiation used as a scope qualifier (eg 'Base<T>::member')
is left to the template machinery, as resolving it here can pick up an
unsubstituted template parameter.

Extend the using_member_typedef test with single, double and namespace
qualified typedef scope qualifiers and add Java and Python runtime tests
that check every member and global function round trips an int.

Assisted-by: Claude Code (Opus 4.8)
2026-06-04 22:45:38 +01:00
William S Fulton 56fa3fb5d2 Re-enable template_parameters_global_scope test
Uncomment the rejig1-6 calls in the Java runme now that the
template_parameters_global_scope Rejig test code was enabled for Lua.

The Rejig case uses a default template argument qualified with the unary
scope operator (template<typename T=::Spade>). It has been correctly
supported since commit 26e14c4f1 (Fix scope lookup for template parameters
containing unary scope operators), which introduced this test file and
resolved the nested ::Integer typedef through scope-qualified template
arguments.
2026-05-31 15:10:03 +01:00
William S Fulton 86b46b3649 C++17: parse user-defined deduction guides
A user-defined deduction guide steers class template argument deduction.
It is written at the same scope as the class template, either as a plain
declaration or, when generic, under a template parameter list:

    Box(int) -> Box<int>;
    template <typename T> Box(T) -> Box<T>;

A deduction guide is not a function: it has no body and emits no symbol,
and only steers argument deduction at compile time.  There is nothing for
SWIG to wrap, so a new deduction_guide grammar rule parses the guide and
discards it.  The optional explicit specifier and a C++20 trailing
requires-clause are accepted.  Previously any deduction guide resulted in
a syntax error.

Assisted-by: Claude Code (Opus 4.8)
2026-05-30 10:56:19 +01:00
William S Fulton 74f7b4bb48 C++17: skip class template argument deduction variables
A variable whose declared type is a bare class template name uses class
template argument deduction (CTAD), added in C++17 - the arguments are
deduced from the initializer using guides synthesised from the class's
constructors.  C++20 P1816 also allows CTAD for aggregates with no
deduction guide, e.g. 'Overloaded ov{...};'.  SWIG performs no template
argument deduction, so it cannot determine the instantiated type; it
previously treated the bare template name as a concrete type and
generated a wrapper naming the template without arguments, which does
not compile.

cDeclaration now detects this, issues Warning 347 and skips the
declaration.  Declarations alongside it are unaffected.

Assisted-by: Claude Code (Opus 4.8)
2026-05-30 00:51:25 +01:00
William S Fulton 7e282b8b16 C++17: parse and support pack expansion in a using-declaration
Accept 'using Ts::name...;' inside a class template (P0195), so the
canonical 'Overloaded' helper used with std::visit on a std::variant
becomes wrappable:

  template <typename... Ts>
  struct Overloaded : Ts... {
    using Ts::operator()...;
  };

Parser: a new grammar rule in parser.y emits a 'using' node with the
'pack' flag set, mirroring the sibling using-decl rules.  The pack name
(e.g. 'Ts') is unresolvable at parse time, so add_symbols has nothing
concrete to register; the placeholder node carries the pack flag
through to template instantiation.

Template instantiation: cparse_template_expand detects the 'pack' flag
and expands the node into one concrete using-declaration per base type,
each then processed by the ordinary 'using' path.  An empty pack
introduces no names ([temp.variadic]), so the placeholder node is
detached with removeNode(); a DohIncref/Delete pair guards the node's
refcount across the unlink, and the class child loop saves nextSibling
before recursing so iteration survives the detach.

The testcase wraps %rename(call) *::operator() and instantiates the
helper with two functor bases, a nested-qualifier base
('using Base<Ts>::operator()...;'), and empty packs - the empty cases
exercise node removal, including one with member methods either side of
the using-decl to verify the sibling chain stays intact across removal.

Assisted-by: Claude Code (Opus 4.8)
2026-05-30 00:11:23 +01:00
William S Fulton 54cabe945e C++11: cpp11_using_functor - Overloaded functor with %rename and %template
Test case for the C++11 form of the Overloaded functor helper: two
concrete type parameters with explicit using-declarations to merge their
operator() overloads.

%rename(call) maps operator() to a valid identifier so SWIG can wrap it.
%template instantiates Overloaded<IntCase,DoubleCase>: SWIG substitutes
I->IntCase, D->DoubleCase, the using-declarations resolve to known methods,
and the proxy class gets an overloaded call(int)/call(double) pair.

Assisted-by: Claude Code (claude-sonnet-4-6)
2026-05-28 20:16:15 +01:00
William S Fulton 5d01d06e1d Test the inheriting-constructor form of the mixin pattern (#2951)
Extend cpp11_template_using_base to also cover

    template <typename I>
    struct Derived : I {
      using I::I;        // inheriting constructors through the parameter base
      using I::call;
    };

and exercise it from the python and java runmes by constructing
DerivedInt(10) and checking the seeded call() result.

Reference #2951 in CHANGES.current.
2026-05-28 00:19:45 +01:00
William S Fulton e88a88815e Substitute template parameters in using-declaration through a template parameter base
A using-declaration whose qualifier is a type-template parameter used
directly as the base class - the mixin idiom -

    template <typename I>
    struct Derived : I {
      using I::call;
    };

now has its template parameter substituted during instantiation, so a
wrapper for the inherited member is emitted on the instantiated derived
class. Previously the parameter was only substituted when the qualifier
was itself a template-id (e.g. BaseTemplate<T>::method), so the bare
parameter form emitted "Warning 315: Nothing known about 'I::call'" and
no wrapper was generated.

The change in templ.c removes the strchr(uname, '<') guard so the using
node's uname is always added to the template parameter substitution
patchlist.

Assisted-by: Claude Code (Opus 4.7)
2026-05-28 00:10:47 +01:00
William S Fulton 5f62959529 C++20 constrained alias templates: parse and support
The requires-clause form on alias templates previously emitted
'Syntax error in input(1).' at the using line:

  template<typename T> requires Numeric<T> using NumBox = Box<T>;

The type-constraint shorthand was already accepted via the existing
type-constrained template parameter support:

  template<Numeric T> using NumBox = Box<T>;

Both forms now parse and wrap identically to an unconstrained alias.
The C++ compiler enforces the constraint when compiling the emitted
wrapper.  Wrapping continues to use the documented two-step pattern:
%template(Name) on the underlying template, followed by an empty
%template() for each alias.

New test cases cpp11_alias_templates (focused unconstrained coverage,
including alias-of-alias and a non-type parameter on the underlying
template) and cpp20_alias_template (all three forms plus an unseen
concept best-effort case), with Python and Java runme files.

Assisted-by: Claude Code (Opus 4.7)
2026-05-27 20:35:03 +01:00
Erez Geva cd569d122a cdata.i changes and new test li_cdata_bytes
Fix guile cdata.

Add SWIG_BINSTR flag
- Add SWIG_FromBinCharPtrAndSize with the new flag
  To add languages that use Lib/cdata.i.
- python use binary string by using the new SWIG_BINSTR
  in SWIG_AsCharPtrAndSize and SWIG_FromBinCharPtrAndSize.

Languages that were changed to uses list
- Tcl use list of integers.
- scilab use list of uint8.
  And support passing list of numbers to C.

Add support to JavaScript
- use Uint8Array instead of strings.
- Add li_cdata_carrays. tests.
- Add li_cdata_bytes tests.
- Use Debian node-addon-api package location.
- napi folder location to Examples/test-suite/javascript/Makefile.in.
- Update documentation.

Update cdata.i documentation

This new cdata test focus on:
- Ensure we can receive proper data from C and pass proper data back to C.
- Use all possoble byte values , i.e. the full range of 0 to 255
  and ensure values 128 to 255 do not pass Unicode transformation (UTF-8/16).
- Ensure zero is a valid value and not a string null termination
  nor a modified UTF-8 which transform U+0000 to 0xC0 0x80.
- Check mutability of the cdata object.

Signed-off-by: Erez Geva <ErezGeva2@gmail.com>
2026-05-25 13:16:09 +01:00
William S Fulton 5fcb0ace4f Doxygen misc constructs: cover @{ group marker and /// @file variant
Adds two test fixtures to lock down the corner cases of the #3403 fix:

- SlashFileHeaderTestClass exercises the same @file bleed scenario but
  with single-line /// comments, which take a different scanner branch
  to //!.  Without the fix the file header would bleed into the class
  description in this style too.

- GroupedMembers exercises @name/@{ member grouping.  The discard rule
  must NOT fire here: @{ does not terminate the structural block (no
  blank line), so each member's own doc comment must still attach.  A
  regression here would either drop @{ or drop the per-member doc.

Java and Python runmes are kept in step.

Assisted-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 23:27:03 +01:00
Joachim Wuttke (o) d50e235551 Fix -doxygen: @file block bleeds into first class docstring
When a header uses consecutive `//!` (or `///`) single-line comments for a
file-level block starting with `@file`, SWIG's comment accumulation loop was
concatenating the file-header content into the following class or function
docstring.

Two bugs conspired:
- Only the `@file` line itself was recognised as structural and skipped;
  subsequent lines (`@brief`, `@authors`, ...) are not in `structuralTags[]`
  and so were accumulated into `yylval.str`.
- Blank lines between comment groups do not break the accumulation loop
  (all `SWIG_TOKEN_ENDLINE` tokens are consumed silently), so the next
  declaration's own doc comment was appended to the same string.

Fix: introduce an `in_structural_block` flag. When the first comment in a
group contains a structural command (`@file`, `@page`, ...), set the flag but
continue accumulating content normally. Count newlines in the inner do-while;
when `in_structural_block` is set and two or more consecutive newlines are seen
(a blank line), discard all accumulated content and break, so the following
declaration's doc comment is processed fresh.

Not breaking the loop on a natural exit (no blank line) is deliberate: it
correctly handles the `@name`/`@{` member-grouping pattern, where `@{`
immediately follows `@name` without a blank line and must still be attached to
the next member.

The block-comment style (`/*! @file ... */`) was already handled correctly
because the entire block is one scanner token and `isStructuralDoxygen()`
would see `@file` in it.

Fixes #3403
2026-05-15 23:24:44 +01:00
William S Fulton ef00c8af06 C++20: avoid 'Numeric auto' with trailing return type in abbreviated_template test
clang and MSVC reject a return type-constraint combined with a trailing return type.
The constrained-return form is now covered separately via the new half_numeric.
2026-05-14 01:27:45 +01:00
William S Fulton ec6354dc34 C++20: mix abbreviated function templates with a variadic explicit pack
template<typename... Ts>
  std::string f_mix(auto x, Ts... ys);
  %template(f_mix_isd) f_mix<int, std::string, double>;

Per [dcl.fct]/19 the invented type template-parameter for each 'auto'
is appended after the explicit template-parameter list, so when the
explicit list ends in a variadic pack the invented sits past the
pack.  Previously the %template matcher rejected this with "No
matching function template 'f_mix' found" because the matcher and
the expand machinery assumed the variadic was the last templateparm.
The %template argument list is now bound positionally: leading non-
variadic parameters one to one, the variadic pack absorbs the middle
args, then one trailing arg per 'auto' in declaration order.  For the
example above Ts={int, std::string} (pack absorbed) and the trailing
double binds to the invented parm for x, giving the effective wrapper
signature 'f_mix(double x, int y1, std::string y2)'.

Fix details:

- ParmList_find_variadic_parm() finds the variadic anywhere in a parm
  list, returning its zero based position.  ParmList_variadic_parm()
  (last only) is kept for the callers that rightly assume variadic-
  last (partial spec parmlists, etc.).

- Swig_cparse_template_locate() (function template branch),
  Swig_cparse_template_expand() (variadic substitution range), and
  merge_parameters() now find the variadic anywhere and pair the
  user's %template args around it: leading non-variadics one to one,
  the pack absorbs the middle entries, then trailing non-variadics
  (the invented parms) one to one.

- expand_variadic_parms() splices the expanded slice into a function-
  parm list at the variadic's actual position, preserving trailing
  parms - so 'auto z' may follow the pack in the function signature.

- Each invented parm is marked with abbreviated_auto:1 in
  promote_abbreviated_template().  Swig_cparse_template_expand() now
  drops trailing invented parms from the emitted C++ template arg
  list uniformly across all abbreviated function templates - the C++
  compiler deduces the invented type from the wrapper's already-
  concrete call argument.  Existing abbreviated template wrappers
  change cosmetically from 'a_mix<std::string,int>(...)' to
  'a_mix<std::string>(...)'; functionally identical.

New tests in Examples/test suite/cpp20_abbreviated_template_mixed.i
add cases f..j: auto before pack, leading explicit + auto + pack,
constrained auto + pack, two autos surrounding a pack (auto after
the pack exercises the function parm list variadic not last path),
and a decorated 'const auto&' with a pack.

Doc updates in CPlusPlus20.html section 10.2.4.

Assisted-by: Claude Opus 4.7
2026-05-13 19:29:27 +01:00
William S Fulton b8c3a58ca8 C++20: mix abbreviated function templates with explicit template parameter lists; decorated auto
Two related fixes for C++20 abbreviated function templates.

1. Mixing 'auto' parameters with an explicit template parameter list
   (e.g. 'template<typename T> T mix(T x, auto y);') used to segfault
   '%template' instantiation with an infinite recursion in
   cparse_template_expand: the inner cdecl was promoted to a template by
   promote_abbreviated_template() and the outer cpp_template_decl rule
   then repromoted the same node, leaving 'templatetype' equal to
   'template' and the invented parm list orphaned.  The fix detects the
   already promoted state and merges the explicit parameters with the
   invented ones via ParmList_join().  Mixing variadic explicit parms
   with an 'auto' parm is documented as not currently %template-
   instantiable.

2. Decorated 'auto' parms ('auto&', 'auto*', 'auto&&', 'const auto',
   'const auto&', 'Numeric auto&', 'const Numeric auto&') now wrap with
   the decoration preserved on the wrapped parameter.  New bison rules
   in parm_no_dox accept CV-qualifiers before AUTO.

The SwigType encoding for an abbreviated template parm follows the
standard reversed left to right convention: unconstrained 'auto' keeps
the bare 'auto' base, and a constrained 'Concept auto' encodes as an
'auto.' element prefix paired with a 'c(<id>)' base carrying the
concept-id, so 'Numeric auto&' is 'r.auto.c(Numeric)' and 'const Numeric
auto&' is 'r.q(const).auto.c(Numeric)'.  promote_abbreviated_template()
reads the concept-id via SwigType_concept_name() and strips the auto
placeholder via SwigType_replace_auto_base().

New tests:

  - cpp20_abbreviated_template_mixed exercises plain and constrained
    mixings (cases a-e); uses 'std::string' paired with a numeric type
    so the binding is observable in the target language.
  - cpp20_abbreviated_template_decorated exercises every decorated
    'auto' form (cases h-o).

Doc updates in CPlusPlus20.html section 10.2.4 and the SwigType encoding
tables in Doc/Manual/Extending.html and the top of file comments in
Source/Swig/typeobj.c and Source/Swig/stype.c.  AGENTS.md tightens the
Changelog guidance to call out that CHANGES.current is user facing only.

Assisted-by: Claude Opus 4.7
2026-05-13 19:23:12 +01:00
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 81406ae5f6 C++20: parse abbreviated function templates with constrained auto return type
Three new grammar rules in 'cpp_declaration' parallel the existing
'storage_class AUTO declarator cpp_const ...' alternatives, accepting
a leading 'idcolon' before AUTO so a constrained auto return type
('Numeric auto fn(...)') parses.  Each new rule mirrors the action of
its plain AUTO counterpart and additionally attaches the captured
concept-id as a 'concept-id' atom on the cdecl's 'constraint' attribute:

  Numeric auto half(int x) -> int { return x / 2; }                    // wrappable
  Numeric auto cube_constrained(Sized auto x) -> int { return x*x*x; } // wrappable
  Numeric auto times2(int x) { return x * 2; }                         // ignored with warning
  Numeric auto times3(int x);                                          // ignored with warning

When a trailing return type is present SWIG wraps the function using
that type and the constraint is metadata only; without one the
function inherits plain 'auto fn(...)' behaviour and is ignored with
a warning since SWIG cannot deduce the return type.

As a side fix, the existing 'storage_class AUTO declarator cpp_const
ARROW ...' rule now also calls promote_abbreviated_template, so a
constrained auto parameter combined with an explicit trailing return
('auto fn(Concept auto x) -> int') introduces an invented type
template parameter for the auto parameter and instantiates correctly
via %template (previously: 'X is not defined as a template' error).

cpp20_abbreviated_template.i (and matching Python/Java runmes) gain
'half', 'cube_constrained', 'twice_n_arrow', 'times2' and 'times3'
cases covering all five forms from the constrained auto return type
matrix; the two ignored with warning declarations are silenced via per name
%warnfilter(SWIGWARN_CPP14_AUTO).

Assisted-by: Claude Code (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
William S Fulton 0039cb9206 C++20: parse abbreviated function templates with constrained auto
A type constrained 'auto' parameter (e.g. 'Numeric auto') is now
accepted in parameter type position - via a new
'idcolon AUTO parameter_declarator' alternative in parm_no_dox.  The
invented type template parameter generated for each such auto parm
carries the type-constraint as a 'concept-id' constraint atom on its
'constraint' attribute, matching the requires-clause infrastructure
already used by explicit templates.

  int twice_numeric(Numeric auto x) { return x + x; }
  %template(twice_numeric_int) twice_numeric<int>;

Each constrained auto parm gets its own invented type template parameter
and constraint, so multi parm forms work too:

  double scale_mixed(Numeric auto x, Numeric auto factor);
  int add_same_concept(Sized auto a, Sized auto b);

cpp20_concepts_lambda.i picks up the constrained auto lambda case
('[](Numeric auto x){}') that previously failed to parse.

Assisted-by: Claude Code (Opus 4.7)
2026-05-12 23:04:20 +01:00
William S Fulton 0d88926e79 C++14/20: parse auto parameters in functions and lambdas
Adds an AUTO parameter_declarator alternative to parm_no_dox.  Placing
the new alternative in parm_no_dox rather than type_right scopes the
'auto' keyword to parameter context only, so it does not clash with
the dedicated 'storage_class AUTO declarator ...' rules at top level
and the existing %expect 7 conflict count is unchanged.

Functions whose parms include 'auto' are converted to a function
template - one invented type template parameter (named
"__dummy_auto_<N>__") per auto parm - via promote_abbreviated_template()
called from c_decl, so abbreviated function templates wrap through the
existing %template instantiation machinery.

Lambdas (which already route through parms) get the same fix
transparently and parse without choking, matching the C++14 generic
lambda form.

The function must declare an explicit (non-auto) return type for SWIG
to wrap it. The C++14 auto return restriction, (cpp14_auto_return_type
testcase) still applies.

  // C++14 generic lambda
  auto twice = [](auto x) { return x + x; };

  // C++20 abbreviated function template
  int twice(auto x) { return x + x; }
  %template(twice_int) twice<int>;

Assisted-by: Claude Code (Opus 4.7)
2026-05-12 23:04:20 +01:00
William S Fulton 3fc35e05f2 C++20 lambdas: accept prefix and trailing form with return type requires-clauses
cpp_lambda_decl now slots requires_clause_opt after lambda_template (in
all three productions) and after the trailing return type in form 2.
This lifts two parse limitations on templated lambdas:

  []<typename T> requires Numeric<T> (T x) { ... }       // prefix form
  []<typename T>(T x) -> T requires Numeric<T> { ... }   // with return type

The captured constraint subtree is discarded - lambdas are not wrapped,
matching how form 2 already discards cpp_const's constraint_node.
2026-05-12 23:04:20 +01:00
William S Fulton ad7b85eda2 C++20 concepts: extend tests and document broader support
Add testcases for concept constrained overload by arity, member operator
overloads, structural partial specialization with a requires-clause, and
the concept only function template redefinition that trips warning 302
(errors/cpp_concept_redefinition).  Document existing support for
constrained class templates and member function templates of plain
classes, and the limitations around constraint subsumption: same-
signature templates differing only by requires-clause are dropped, and
concept only "partial specializations" are not selected.

Assisted-by: Claude Code (Opus 4.7)
2026-05-12 23:04:20 +01:00
William S Fulton f202b8a2d1 C++20 concepts: hold the constraint subtree on a "constraint" attribute
The constraint subtree built by the parser was previously appended as
the host node's first child, which placed it among class members and
made it appear to take part in scoping.  Move it onto a "constraint"
attribute on the host (cdecl, class, template, concept, constructor)
so it is metadata about the declaration, mirroring how parameter lists
live on "parms".

Source/Swig/tree.c: Swig_print_node renders the "constraint" attribute
through Constraint_str, displaying the structured tree as a quoted
C++20 source string the same way ParmList_str_defaultargs renders
"parms".  -debug-top / -debug-module dumps now show entries like:

  | constraint   - 'Numeric< int > && Sized< int >'

Source/CParse/parser.y, templ.c, cscanner.c: the four attachment
points (c_decl, cpp_template_decl prefix and trailing combine,
cpp_concept_decl, and the constructor path) Setattr on "constraint"
instead of appendChild.  The %template substitution walker reads
"constraint" via Getattr on cdecl, class, and constructor branches
rather than walking children for it.

copy_node() in parser.y is taught to deep copy the "constraint"
attribute via a recursive copy_node call.  Without this the shallow
Copy() at the catchall branch only duplicates the root hash, leaving
inner atom / requires-expression / requirement nodes aliased between
the primary and every instantiation.  cparse_template_expand patches
the subtree in place, so the first instantiation's T => int
substitution leaks into every later instantiation - e.g. sum_all_ddd
would print 'AllNumeric< int,int,int >' after sum_all_iii had run.

Source/Modules/lang.cxx: drop the no-op dispatcher entries for
"constraint" / "requires-expression" / "requirement" - they are no
longer reached via the children chain.

The constraint atom for a concept-id used to hold the SwigType-
encoded identifier on a "name" attribute.  It is a SwigType, not an
identifier, so rename it to "type" in parser.y, cscanner.c, the
constraint renderer, and the substitution walker.  templ.c now feeds
the concept-id type to typelist (the SwigType aware substitution
list) rather than cpatchlist (text only).  The renderer decodes the
SwigType back to source form via SwigType_str, so dumps show
'Numeric< int >' rather than the encoded 'Numeric<(int)>'.

The dead render_template_args helper and the unused "templateargs"
ParmList attribute mentioned in early documentation are removed; the
concept-id type already carries the full identifier including the
template-argument list.

CheckedBox(T v) requires Numeric<T> : value(v) {} previously lost its
constraint because ctor_end did not propagate cpp_const.constraint_node
and cpp_constructor_decl did not Setattr it.  Add a constraint_node
field to struct Decl, propagate it through both cpp_const-bearing
ctor_end alternatives, and attach it on the constructor node.  The
templ.c constructor branch recurses into the new "constraint"
attribute so the trailing requires-clause is substituted at %template
time.

Examples/test suite/cpp20_concepts_classes.i and the python and Java
runme files: add a CheckedBox<double> instantiation to exercise the
constructor's constraint substitution with a different Numeric type.

Assisted-by: Claude Code (Opus 4.7)
2026-05-12 23:04:20 +01:00
William S Fulton 6d03096667 C++20 concepts: combine prefix and trailing requires-clauses structurally
cpp_template_decl's mid rule action no longer string concatenates the
prefix and trailing requires-clauses with ' && '.  Instead, it finds
any existing trailing constraint subtree on the inner cdecl/class
node, removes it from the child chain, and conjoins it with the
prefix subtree via Constraint_combine("and", prefix, trailing).  The
result is a single op="and" constraint subtree (or the bare prefix
when no trailing is present), and the rendered "requires" string is
regenerated from that subtree via Constraint_str.

For 'both_clauses' from cpp20_concepts_extra.i

  template<typename T>
  requires Numeric<T>
  T both_clauses(T x) requires Sized<T> { return x + x; }

the cdecl now carries a single constraint subtree:

  +++ constraint op="and"
        +++ constraint op="atom" kind="concept-id" name="Numeric<(T)>"
        +++ constraint op="atom" kind="concept-id" name="Sized<(T)>"

with the flat 'requires' string "Numeric<(T)> && Sized<(T)>" derived
from it.  After %template(both_clauses_int) both_clauses<int>; the
two atoms substitute to "Numeric<(int)>" and "Sized<(int)>" via the
structural walker added in commit b38b45cb6.

Per [temp.constr.decl]/3, the C++20 standard conjoins the prefix and
trailing requires-clauses via '&&'; the structural form preserves
that semantic exactly while producing a more useful tree for any
future consumer that wants to walk the conjunction operands.

A new both_clauses test case in cpp20_concepts_extra exercises the
combine path; the runtime tests confirm the wrapped function returns
the expected result.

Assisted-by: Claude Code (Opus 4.7)
2026-05-10 17:37:53 +01:00
William S Fulton e39776b377 cpp20_concepts: add extra constraint forms and out of line definition tests
A new cpp20_concepts_extra.i test case covers C++20 constraint primary
forms not previously exercised by the suite:

  - identity_non_numeric<T>   '(!Numeric<T>)' - negation only legal
                              when wrapped in parens
  - mix_add<T,U>              multi parameter requires-expression
                              binding two unrelated template parameters
  - AllNumeric<Ts...>         variadic concept defined by a fold-
                              expression over '&&'
  - sum_all<T,Rest...>        variadic function template constrained
                              by the variadic concept
  - trait_primary<T>          'std::is_integral_v<T>' as a constraint
                              atom (non-concept-id boolean primary)
  - deeper<T>                 deeper nesting of '&&' / '||' across
                              multiple parens levels

cpp20_concepts_classes.i gains an OutOfLineBox<T> case: the member
function template scaled<U> is declared in class with
'requires Numeric<U>' and defined out of line with the same prefix
requires-clause on its own template head, exercising the
requires_clause_opt path on a doubly templated declaration.

Matching Python and Java runme files validate the wrapped behaviour;
both new test cases are registered in Examples/test suite/common.mk
under CPP20_TEST_CASES.

No SWIG source changes are needed - the existing scanner level
constraint text capture handles every form added here.  The tests
serve as a baseline before the planned structural rework of the
constraint representation.

Assisted-by: Claude Code (Opus 4.7)
2026-05-10 17:37:52 +01:00
William S Fulton cbe8b154f7 cpp20_concepts: add class methods and class template test cases
Two new test cases extend existing concept coverage from free function
templates to member function templates and class templates, with
matching Python and Java runme files.

cpp20_concepts_class_methods.i - member function templates of a
non-templated Calculator class:
  - cube<T>      trailing requires-clause
  - quad<T>      prefix requires-clause
  - sum<T>       static method, trailing requires with an inline
                 requires-expression as the constraint
  - addn<T>      prefix requires using a named concept whose body is
                 itself a requires-expression
  - scale<T,U>   two template parameters with a compound '&&' constraint

cpp20_concepts_classes.i - concepts on class templates:
  - NumericBox<T>   class template with a prefix requires-clause on
                    the template head
  - Holder<T>       unconstrained class whose ordinary method
                    (doubled()) carries its own trailing requires-clause
  - SmallBox<T>     class template with a compound '&&' prefix
                    requires-clause
  - CheckedBox<T>   unconstrained class template with a constrained
                    constructor

Both new test cases are registered in Examples/test suite/common.mk
under CPP20_TEST_CASES.

Assisted-by: Claude Code (Opus 4.7)
2026-05-10 17:37:52 +01:00
William S Fulton 21adc1d7b6 cpp20_concepts: cover the remaining requires-expression body forms
Three additional concept / requires-expression tested.
All existing parser support (the requires-expression body
is skipped via skip_balanced):

- Inline 'requires requires' with a compound requirement (add_inline_same).
- A concept body listing multiple simple-requirements (Machine -> cycle).
- A concept body mixing the other three requirement kinds: a
  type-requirement, a noexcept compound-requirement and a nested
  requires-clause (BasicContainer -> check_container).
2026-05-10 17:37:52 +01:00
William S Fulton 1674f5ee64 C++20 concepts: support requires-expression compound-requirements
A requires-expression body may mix simple-requirements ('expr;')
with compound-requirements that carry a trailing return type
constraint ('{ expr } -> type-constraint;').
Add docs and an example testing this.

This works on existing parser support: the concept declaration is
silently consumed by skip_decl, whose skip_balanced('{', '}') already
handles the nested braces of the compound requirement, and the
trailing 'requires AddableSame<T>' goes through the existing
skip_constraint path

Also add test for 'requires requires' form with compound-requirement.
2026-05-10 17:37:51 +01:00
William S Fulton a8e62443c3 C++20 concepts: add Summable concept defined by a requires-expression
Show the named concept counterpart to the inline 'requires requires'
form already covered by the cpp20_concepts test case: a concept whose
body is a requires-expression, used as the constraint of a function
template.  Distinct name (Summable) so it does not clash with the
Addable variable template in cpp20_variable_templates.

  template<typename T>
  concept Summable = requires (T t) { t + t; };

  template<typename T>
  T sum_pair(T a, T b) requires Summable<T> {
    return a + b;
  }

This works on existing parser support: the concept declaration is
silently consumed by skip_decl, and the trailing 'requires Summable<T>'
goes through the existing skip_constraint path.
2026-05-10 17:37:51 +01:00