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)
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)
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)
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)
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)
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
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
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
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)
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)
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#1042Fixes#1153
Assisted-by: Claude Opus 4.8 (1M context)
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
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
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)
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)
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)
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)
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)
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)
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.
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)
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)
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)
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)
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.
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)
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)
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>
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>
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
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.
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
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
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
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)
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)
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)
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)
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.
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)
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)
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)
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)
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)
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).
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.
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.