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
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)
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)
The deferred warning at template_directive (parser.y) surfaced a parser
limitation the user could not act on. SWIG's template substitution
machinery in templ.c is name based: a templateparm's name is replaced
throughout the body by the valparm's value/type regardless of whether
the templateparm was classified as 'typename T' or as a non-type
parameter. The 'constraint:unresolved' remap of 'Concept T' to
'typename T' therefore has no observable effect on the generated
wrapper, and the diagnostic was just noise.
The emission block is commented out (preserved for reference) and the
'constraint:unresolved' flag is still set in the parm classifier for
any downstream feature that may want to walk these parms. Warnings.html
entry 332 is now marked 'Reserved.' with the old text in an HTML
comment; CPlusPlus20.html drops the "resulting warning is..." example;
%warnfilter directives in cpp20_concepts_constrained_param.i are no
longer needed; and errors/cpp_concept_not_visible (whose sole purpose
was to assert the warning fired) is removed.
A side effect: 'template<class T, size_t N> class array' in
Lib/java/std_array.i no longer triggers warning 332 when 'size_t' is
unresolved.
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
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)
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)
Replace the scanner level constraint capture from earlier commits with
proper bison productions that build a structured constraint subtree.
The flat 'requires' string attribute is still set (via Constraint_str
on the new subtree) so downstream consumers see no change while the
migration is in flight.
Grammar changes (Source/CParse/parser.y):
constraint
: constraint_or
constraint_or
: constraint_and
| constraint_or LOR constraint_and { Constraint_combine "or" }
constraint_and
: constraint_primary
| constraint_and LAND constraint_primary { Constraint_combine "and" }
constraint_primary
: idcolon { atom kind="concept-id" }
| LPAREN { skip_balanced('(',')') } { atom kind="expression" }
| requires_expression { atom kind="requires-expression" }
requires_expression
: REQUIRES requirement_parameter_list_opt requirement_body
requirement_parameter_list_opt
: LPAREN parms RPAREN
| %empty
requirement_body
: LBRACE { skip_balanced('{','}'); parse_requirement_seq(...) }
The four attachment points are rewired:
- cpp_concept_decl parses CONCEPT idcolon EQUAL constraint SEMI
- cpp_const takes 'REQUIRES constraint' as the trailing form
- requires_clause_opt takes 'REQUIRES constraint' as the prefix form
- valexpr takes 'requires_expression' as a value position primary
The dtype struct's String *requires_clause becomes Node *constraint_node.
Two design choices follow from the bison conflict audit:
- constraint_primary uses idcolon directly: idcolon already covers
'Numeric<T,U>' via 'identifier less_valparms_greater', so no new
LESSTHAN production is needed (and the LESSTHAN ambiguity that one
would create is avoided).
- LPAREN content in constraint_primary is opaque captured: SWIG users
write parenthesised non constraint expressions in concept bodies
(e.g. 'Numeric<T> && (sizeof(T) <= 4)') that are not constraint-
expressions per [temp.constr] but are accepted by GCC/Clang as
extensions. Capturing the (...) span as a kind="expression" atom
matches the preexisting scanner behaviour and avoids the LALR(1)
conflict between 'LPAREN constraint RPAREN' and an expression
fallback. The requirement_body of a requires-expression is opaque-
captured for the same reason - the audit predicted 1-3 S/R conflicts
if valexpr were reused inside requirement context.
Source/CParse/cscanner.c gains parse_requirement_seq() and the
parse_one_requirement helper, which split the captured requirement-
body text at top level ';' (tracking '(' '[' '{' depth) and build a
chain of structured 'requirement' nodes with kind="simple" / "type" /
"compound" / "nested". Compound requirements parse the noexcept
keyword and the optional '-> Concept' return-type-requirement
structurally; the inner expression bodies remain text stored.
Source/Modules/lang.cxx Dispatcher::emit_one is taught to no-op on
'constraint', 'requires-expression', and 'requirement' node types so
the metadata children attached to cdecl, template, and concept nodes
do not generate "Unrecognized parse tree node type" errors.
bison -Wall -Werror reports no new shift/reduce or reduce/reduce
conflicts.
Tests:
- cpp20_concepts.i / _class_methods.i / _classes.i / _extra.i and
cpp20_variable_templates.i pass for Python and Java.
- The errors/cpp_template_concept test is updated to use a concept
body 'std::integral<T>' that the new grammar accepts (the previous
'true' literal needed an opaque expression fallback that the spec
does not require).
- cpp20_concepts_extra.i's 'Sized' concept gains parens around its
sizeof expression for the same reason.
The scanner level scanner_capture_decl, scanner_capture_constraint,
and scanner_capture_prefix_requires_clause helpers are now dead code;
they are removed in a later commit along with the flat 'requires'
attribute.
Assisted-by: Claude Code (Opus 4.7)
Previously a 'concept' declaration was silently consumed by skip_decl
and a trailing/prefix requires-clause by skip_constraint /
skip_prefix_requires_clause; nothing reached the parse tree. Make
concepts and constraint text first class so future passes can act on
them, and turn '%template' on a concept into a clear error instead of
silently producing a malformed wrapper.
Parse tree shape
----------------
A concept declaration is now a 'concept' node wrapped by the existing
'template' node (so the same machinery as a class/cdecl template head
applies, including symbol table registration and add_symbols). After
cpp_template_decl runs the node contains:
nodeType - 'template'
templatetype - 'concept'
name - the concept identifier
templateparms - the template parameter list
type - 'bool'
requires - the constraint expression as a flat string
For function templates, a trailing requires-clause attached to the
declarator and a prefix requires-clause attached to the template head
both populate a 'requires' attribute on the wrapping template node.
When both are present they are joined with ' && ' to mirror the C++20
spec's normalization of associated constraints
([temp.constr.decl]).
Scanner: capture instead of discard
-----------------------------------
Source/CParse/cscanner.c factors the existing skip_decl,
skip_constraint and skip_prefix_requires_clause / skip_constraint_primary
into '_inner' helpers that thread an optional String *capture buffer
through the existing token loops. The skip_constraint and
skip_prefix_requires_clause wrappers are dropped - their only callers
went through the capture path. Three new exports take their place:
scanner_capture_decl
scanner_capture_constraint
scanner_capture_prefix_requires_clause
A small capture_append_token helper records each token's raw text into
the capture buffer. Subspans returned by Scanner_skip_balanced
preserve the source's original whitespace inside the delimiters; for
the rest, a separating space is inserted only where it is needed for
readability:
- between two word character runs, so 'std' '::' 'integral' '<' 'T'
'>' joins as 'std::integral<T>' rather than 'std :: integral < T >',
while 'T' 't' (two ids) keeps the 'T t' spacing;
- around the logical operators '&&' and '||', so a compound
constraint reads 'Numeric<T> && SmallNumeric<T>' rather than
'Numeric<T>&&SmallNumeric<T>'.
Grammar
-------
Source/CParse/parser.y:
- Adds a String *requires_clause field to the dtype struct (Define).
default_dtype zero initialises it.
- cpp_concept_decl now consumes 'CONCEPT idcolon EQUAL', captures the
constraint via scanner_capture_decl, and produces a 'concept' node
with name/requires/type set. add_symbols is left to the wrapping
cpp_template_decl rule, matching what happens for class and cdecl
template heads.
- requires_clause_opt is typed <str> and returns the captured
constraint text via scanner_capture_prefix_requires_clause.
cpp_template_decl consumes $requires_clause_opt
after cpp_template_possible reduces and Setattr's 'requires' on
the inner template node, conjoining with any preexisting trailing
'requires' attribute via ' && '.
- cpp_const's REQUIRES branches use scanner_capture_constraint and
propagate $$.requires_clause to c_decl, which Setattr's 'requires'
on the resulting cdecl.
Template parameter substitution of 'requires'
---------------------------------------------
Source/CParse/templ.c appends Getattr(n, 'requires') to cpatchlist in the
cdecl and generic branches of cparse_template_expand, so the same template-
parameter substitution already used for 'code' rewrites the constraint at
%template instantiation time. After
template<typename T> requires Numeric<T> && SmallNumeric<T>
T half(T x) { return x / 2; }
%template(half_int) half<int>;
the instantiated cdecl contains:
"requires" - "Numeric<int> && SmallNumeric<int>"'.
Error on '%template' applied to a concept
-----------------------------------------
Source/CParse/templ.c's Swig_cparse_template_locate now checks the
templatetype before falling into the class/classforward and function-
template branches; if it is 'concept' it emits
Error: %template not allowed on concept 'Numeric' - concepts cannot
be instantiated like class or function templates.
and returns 0 so the surrounding %template directive becomes a no-op
rather than producing a malformed wrapper. A new errors test case
covers it: Examples/test suite/errors/cpp_template_concept.{i,stderr}.
Typepass
--------
Source/Modules/typepass.cxx's templateDeclaration supports the new 'concept'
value to mark it as a known templatetype, it is just another templatetype
carried by a 'template' node.
Out of scope
------------
The constraint expression is still a flat string. A structured
representation - a 'requires' node carrying a parms ParmList plus
per requirement child nodes for the simple-, type-, compound- and
nested-requirement forms in [expr.prim.req], and a 'constraint' tree
for requires-clauses themselves - is left for a future commit; it
would also let templ.c reuse SwigType machinery instead of patching a
String.
Assisted-by: Claude Code (Opus 4.7)
This rule for function bodies currently also handles C++ {...}
initialisers, so this change broke cases such as:
int x, y {2};
Just revert for 4.3.0 - not accepting valid C++ we previously accepted
is much worse that accepting invalid C++.
This reverts commit 18abdbca87.
Check _Complex is also not treated as a keyword in C++ mode.
Check that `bool bool;` fails since if it doesn't then our test of
`bool _Bool;` succeeding doesn't prove anything).
The new version generates identical partialcheck output for the
testsuite, but avoids right recursion.
This refactoring means SWIG no longer accepts the invalid definition
of a function as the final part of a declaration, e.g.
int x, f() { return 42; }
was previously accepted but now gives:
c_bad_function_definition.i:4: Error: Syntax error - possibly a missing semicolon (';').
GCC gives:
c_bad_function_definition.i:4:12: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘{’ token
clang gives:
c_bad_function_definition.i:4:11: error: expected ';' after top level declarator
See #2884
Report "Unexpected token" rather than "Illegal token" since this
error fires for certain valid C/C++ tokens when used in an invalid way
(as well as `@` and `$`, but it's reasonable to report these as
"unexpected" too).
Also we now exit after this error rather than trying to continue
parsing, which used to just lead to a potentially confusing second
error.
For example instead of:
c_unexpected_token.i:1: Error: Illegal token '/='.
c_unexpected_token.i:1: Error: Syntax error - possibly a missing semicolon (';').
we now report:
c_unexpected_token.i:1: Error: Unexpected token '/='.
This was being handled in the wrong place in the grammar leading to
SWIG accepting nonsensical uses. The new handling isn't perfect
(not least because C++20 added support for a bitfield with an
initialiser) but it's much less wrong than it was.
This is certainly a corner case, but GCC and clang both accept zero
bytes at least in comments, and SWIG's current handling is to ignore
the zero byte and all following characters up to and including the next
newline, so for example if a // comment contains a zero byte SWIG would
quietly ignore the next line.
Closes#3010
SWIG now gives an error for digits 8 and 9 in octal constants -
previously these were quietly accepted resulting in a bogus value.
C++11 binary constants are now treated similarly - only digits 0
and 1 were allowed before, but trying to use other digits now gives
a clearer error.
Appearing recently since 8c8e27d246.
Fixes warnings in test cases:
complextest, li_std_pair_extra, std_containers, template_nested_typemaps
Also attempts to instantiate a template for an unsupported nested template class
now have a separate new warning number 331 (SWIGWARN_PARSE_TEMPLATE_NESTED).
Closes#2965
* github-win-ci:
No need to test so many versions of Python
More cosmetic tweaks
Cosmetic corrections - MinGW-w64
Revert PYTHON_LIB change
fromdos consistency for removing CR
Show notest in name
Restore appveyor testing
Follow @wsfulton feedback
Remove win32 userenv library.
Replace 'cmd' with 'powershell'. As powershell stop on error. With 'Install MSYS2', as 'pacman' works with cmd, add checks after each command for errors. Revert Windows 'SWIG_LIB'. SWIG Main, check SWIG_LIB environment for null and empty string. Build with MING w64 compiler. Configure improve Windows python 3, try python-config first.
Add "Machine Info" to CMAKE-WIN workflows. Replace here-documents and tabs with multiple echo lines. Add comments.
As we add a new GitHub Windows test in ".github/workflows/win_ci.yml"
Add windows actions using GitHub. Update Windows document. Small update in configuration. Remove SWIG_LIB_SET, windows should use the same value. Add better striping for multiple test in common make file. Add library path to dynamic python linking, MSVC need to find the windows library linking file (*.lib). For other GCC, it does not change.
Closes issue #2813
Also fix mistakenly named todos which should be fromdos.
tr is used as it is always available and unfortunately
common.mk is not currently setup to use configure.ac output.
As powershell stop on error.
With 'Install MSYS2', as 'pacman' works with cmd,
add checks after each command for errors.
Revert Windows 'SWIG_LIB'.
SWIG Main, check SWIG_LIB environment for
null and empty string.
Build with MING w64 compiler.
Configure improve Windows python 3, try python-config first.
Signed-off-by: Erez Geva <ErezGeva2@gmail.com>
on forward template class declarations. A full template class definition is
required in order to wrap a template class as a proxy class.
Also tidyup handling of classforward code (no observable changes in
test-suite).
Update Windows document.
Small update in configuration.
Remove SWIG_LIB_SET, windows should use the same value.
Add better striping for multiple test in common make file.
Add library path to dynamic python linking,
MSVC need to find the windows library linking file (*.lib).
For other GCC, it does not change.
Signed-off-by: Erez Geva <ErezGeva2@gmail.com>
For example:
%nspace Space::OuterClass80;
namespace Space {
struct OuterClass80 {
struct InnerClass80 {
struct BottomClass80 {};
};
enum InnerEnum80 { ie80a, ie80b };
};
}
Previously the following were additionally required for some languages:
%nspace Space::OuterClass80::InnerClass80;
%nspace Space::OuterClass80::InnerClass80::Bottom80;
Now the appropriate nspace setting is taken from the outer class.
A new warning has also been introduced to check and correct conflicting
nspace usage, for example if the following is additionally added:
%nspacemove(AnotherSpace) Space::OuterClass80::InnerClass80;
The following warning appears as an inner class can't be moved outside
of the outer class:
Warning 406: Ignoring nspace setting (AnotherSpace) for 'Space::OuterClass80::InnerClass80',
Warning 406: as it conflicts with the nspace setting (Space) for outer class 'Space::OuterClass80'.
This really helps with %nspacemove as now one can simply move an outer
class to another namespace, like this:
%nspacemove(AnotherSpace) Space::OuterClass80;
and all the nested classes will automatically also be moved
into the appropriate namespace.
When template parameter typedefs are involved, a duplicate %template
instantiation was not properly ignoring the duplicate instantiation,
resulting in compile time errors.
Closes#2814
A different approach is taken for supporting casting smart pointers up the
inheritance hierarchy. We no longer try to replace the underlying pointer type,
provided in the 'feature:smartptr', with the base class type. Such as morphing
'std::shared_ptr<(Derived)>' into 'std::shared_ptr<(Base)>'. Instead, we simply
use 'feature:smartptr' from the base class. This is more reliable than trying to
pattern match the pointer type in the feature. The base class must of course
also have the 'feature:smartptr' set, and this is still checked for as before.
The feature is now parsed in one place and stored in the parse tree in the
new 'smart' attribute for handling by the target languages.
Fix also improves the handling of the type parsed in 'feature:smartptr' in that
the type is now normalized and resolved in the scope of the class it is attached
to.
Closes#2768
SWIG now warns:
Warning 202: Could not evaluate expression 'MY_VERSION_AT_LEAST(1,2,3)'
Warning 202: Use of undefined function-like macro
instead of:
Warning 202: Could not evaluate expression 'MY_VERSION_AT_LEAST(1,2,3)'
Warning 202: Syntax error: expected operator