The varargs and libffi typemap examples in the manual now convert a Python str
with PyUnicode_AsUTF8 instead of requiring a bytes object, so bring the matching
example interface files into line.
The libffi runme no longer needs byte string literals for the printf format
string, the printf arguments or the execlp arguments.
Assisted-by: Claude Code (Opus 5)
The annotation tests skipped their checks with -builtin or -fastproxy as
neither adds __annotations__ to the generated runtime objects. That is no
longer a reason to skip when -pyi is also used, as the annotations are then
in the generated stub file, but the stub lookup only worked for -builtin.
With -fastproxy a method is the flat C wrapper function, so its __qualname__
is Class_method instead of Class.method. Pass a fastproxy flag from each
runme down to the stub lookup so that such a name is split on the stub class
names only when it really is a flat name. Reword the comment explaining when
annotations_supported is false, which was misleading with -pyi.
Assisted-by: Claude Code (Opus 5)
A generated stub class with no wrapped members had an empty suite, which
is invalid Python syntax. Stubs could also refer to base classes from
%imported modules without importing the corresponding Python proxy
module, leaving those names unresolved.
Track whether each stub class emits a member and use an ellipsis when
its body would otherwise be empty. Collect modules referenced through
%import and emit them before declarations, using the same package and
relative import rules as the generated Python proxy.
Keep annotation tests active with -pyi by parsing annotations from the
generated stub instead of expecting them on runtime objects. Include
each generated .pyi file in multi-module Pyrefly checks.
Add Python 3.14 Linux CI configurations for -pyi -typehints with and
without -builtin.
See #3473.
Assisted-by: Codex (GPT-5.6 Sol)
These are the .pyi stub file equivalents of %pythoncode and %pythonbegin,
for adding Python code to the stub file generated by the -pyi and -pyifile
options. They do nothing unless a stub file is being generated.
%pythonstubcode inserts the code at the point the directive appears,
indenting it into the class body when used inside a class. %pythonstubbegin
inserts the code just after the SWIG banner, before any real code.
The stub file is generated independently of the .py file, so code added with
%pythoncode or %pythonbegin does not appear in it. Classes configured with
%pythonabc use names from collections.abc as generated base classes and
pyabc.i imports this module into the .py file, but a separately generated
stub had no import, leaving its base class unresolved. Add the equivalent
%pythonstubcode block to pyabc.i.
Extend the python_pyi test with a collections.abc base class and blocks
exercising both new directives.
See #3473.
Assisted-by: Claude Code (Opus 5)
Remove generated .pyi files alongside Python proxy files during
test-suite and example cleanup. Use .py* patterns so stubs produced
with SWIG_FEATURES=-pyi and other Python sidecar files are covered by
the existing clean rules.
With -builtin, alternate constructor aliases such as pair = new_pair
were emitted as pure Python code. Pyrefly could not resolve the internal
new_ entry points imported from the native module, so these assignments
caused type checking to fail.
Register alternate constructor wrappers under their public names in the
low-level module. Do not expose internal new_ names or generate Python
assignments to provide the public names.
This covers explicitly renamed constructors, named constructor template
instantiations and implicit constructors for renamed C structs.
Add a Python 3.14 CI build with -builtin -typehints to exercise the
generated type hints with Pyrefly.
Assisted-by: Codex (GPT-5.6 Sol)
Add -typehints to enable PEP 484 annotations for a whole interface.
Run Pyrefly on generated wrappers in the Python examples and test suite
with SWIG_FEATURES=-typehints.
Keep C/C++ annotation tests in their original mode, and add a Python
3.14 Linux CI build that runs with -typehints.
See #735.
Assisted-by: Codex (GPT-5.6 Sol)
Build PEP 484 annotations from every Python output value, including a
native return that is not void. Use typing.List[typing.Union[...]] for
multiple results.
See #3469.
Assisted-by: Codex (GPT-5.6 Sol)
Factor the duplicated name qualification in Swig_symbol_type_qualify and
Swig_symbol_template_reduce out into symbol_qualified_name, which documents why
the scope must not be added when the name is qualified already. The two had
drifted apart, which is how #3526 came about - only Swig_symbol_type_qualify
checked for an already qualified name.
Move the regression test from cpp11_using_typedef_struct, which covers using
declarations rather than alias declarations, to cpp11_template_typedefs. Extend
it to cover a nested class and a nested namespace as the template argument, both
of which were qualified twice in the same way, plus a control case where the
alias is declared in a class and the scope does still have to be added.
Add the CHANGES.current entry for #3526.
Assisted-by: Claude Code (Opus 5)
Closes#3526
Regression introduced by commit c5bfe9766f, which
rewrites a typedef qualifier to the template-id it names so that the template
arguments are kept. Reducing those arguments looks up the argument types after
TypePass has patched their names with the enclosing scope, and the scope was
then added a second time, for example:
namespace ANamespace {
struct ControlPoint { int abc; };
}
namespace Another {
template <typename T> struct Vec { using size_type = int; Vec(T v) {} };
}
using InNamespaceInt = Another::Vec<ANamespace::ControlPoint>;
using WrapAgain = Another::Vec<InNamespaceInt::size_type>;
generated ANamespace::ANamespace::ControlPoint in the wrappers, which did not
compile. Reported for hugin at
https://sourceforge.net/p/swig/mailman/message/59368160/
The directorin typemap fixed a couple of commits ago is in the Unified
Typemap Library and so is shared by every language defining SWIG_Object,
but the test for it was written twice, once against PyObject * and once
against VALUE. Replace python_director_pyobject and ruby_director_value
with director_langobj, written against SWIG_Object in the same spirit as
the existing langobj test, and run it from the common test list so that
every language at least compiles it.
Runme files are added for the four languages that define both
SWIG_Object and SWIG_DIRECTOR_TYPEMAPS, that is the four that generate
director code and were affected: Python, Ruby, Perl and Octave. Tcl, R
and Scilab define SWIG_Object but have no directors, so they get compile
only coverage.
python_director_pyobject is kept for the Python specific parts that have
no equivalent in the other languages: the swig::SwigPtr_PyObject and
swig::SwigVar_PyObject smart pointers, by value and by const reference,
and a null PyObject * argument.
Assisted-by: Claude Code (Opus 5)
There is a directorin typemap for SWIG_Object but none for
SWIG_Object const &, so the const reference form fell through to the
generic SWIGTYPE *const& typemap and was wrapped as an opaque proxy
object instead of being passed through unchanged. Affects every
language defining both SWIG_Object and SWIG_DIRECTOR_TYPEMAPS, that is
Python, Ruby, Perl and Octave. For Ruby the generated code did not even
compile, as SWIG_as_voidptr cannot cast a VALUE to void *.
For Python this also covers swig::SwigPtr_PyObject const& and
swig::SwigVar_PyObject const&, which are %applied from PyObject *const&.
Additionally fix a reference count leak for a swig::SwigVar_PyObject
director method argument passed by value. Assigning it into the
SwigVar_PyObject wrapper variable selects the implicitly declared copy
assignment operator, which adds a reference of its own, so the typemap's
SWIG_Py_XINCREF was a second increment against a single decrement on
scope exit. Casting to PyObject * selects
SwigVar_PyObject::operator=(PyObject *), which does not adjust the
count, making the SWIG_Py_XINCREF the one and only increment for every
argument type the typemap handles.
The existing python_director_pyobject test did not catch the leak
because its C++ caller built the SwigVar_PyObject from a borrowed raw
pointer, so the temporary stole a reference and cancelled it out. The
callers are now reference count neutral, the const reference forms are
covered, and the test checks that the object arriving in Python is the
object passed from C++, not just that the count is stable.
Assisted-by: Claude Code (Opus 5)
Follow on fixes to the previous commit:
Use SWIG_Py_XINCREF instead of Py_INCREF. Py_INCREF crashes on a null
PyObject * argument, which a C++ caller may legitimately pass, turning
what was a recoverable director error into a segfault. SWIG_Py_XINCREF
also honours the stable ABI, where it expands to Py_IncRef, and matches
the macros used elsewhere in pyclasses.swg.
Declare the typemap before the %apply directives in pyclasses.swg. %apply
copies the typemaps that exist at the point it appears, so a directorin
typemap declared after them was never propagated to swig::SwigPtr_PyObject
or swig::SwigVar_PyObject, which suffered from the same underflow. Passing
a swig::SwigPtr_PyObject by value was worse, as the wrapper variable stole
the reference held by the argument.
Rename the test to python_director_pyobject following the naming used for
the other Python only tests, and extend it to cover swig::SwigPtr_PyObject,
swig::SwigVar_PyObject and a null argument. Remove the unused variable and
the no-op if from the runme file, check the reference count after the
garbage collection loop and drop the success message, as the tests are
silent when they pass.
Add the CHANGES.current entry.
Assisted-by: Claude Code (Opus 5)
Each director upcall for a method taking PyObject* created a
SwigVar_PyObject, assigned the C++ parameter (a borrowed reference)
to it, then let the SwigVar_PyObject destructor DECREF it on scope
exit -- with no matching INCREF. After enough calls the object's
reference count underflowed, the object was freed prematurely, and
the next call crashed with 'deletion of interned string failed'.
Fix: add a %typemap(directorin,noblock=1) for PyObject* that INCREFs
the parameter after assignment, balancing the destructor's DECREF.
Fixes#2015.
Assisted-by: opencode (deepseek-v4-flash)
Drop noblock=0 from the in typemap - remove_block() only tests for the
presence of the attribute, so noblock=0 strips the braces and the block is
merely put back by the JavaScript module. Omitting it says what was meant.
Correct the comments, which described a %{ ... %} delimited typemap this test
does not have, and note that only jsc and napi define the two names as macros.
Use angle brackets for the library %include and fix the assertIsEqual
parameter order, which was the reverse of every call site.
Closes#3523
Assisted-by: Claude Code (Opus 5)
The IntStructPtrMap and IntStructConstPtrMap sections passed newly
constructed Struct objects straight into the map and kept no Lua
reference to them. These maps store just the raw pointers, so once the
incremental collector completed a cycle the Struct objects were deleted
and the maps were left holding dangling pointers. The following value
checks then read freed memory, which sporadically failed in CI with:
li_std_map_runme.lua:68: ispmap:get(8)
Hold the Struct objects in a Lua table for the lifetime of the maps.
Confirmed as a use-after-free with valgrind and reproducible before the
fix by lowering the collector pause with collectgarbage("setpause", 105).
Assisted-by: Claude Code (Opus 5)
The v8 engine generates a C++ wrapper even for the C test cases, but the
test-suite build rule compiled it with $(CC) and $(CFLAGS), so the C standard
meant for C code was handed to the C++ compiler.
That was worked around by setting CSTD, which is the C standard, to c++14 for
the two v8 CI jobs. CSTD also gives the C standard used to build swig itself,
so every C file of swig and ccache-swig then warned:
cc1: warning: command-line option '-std=c++14' is valid for C++/ObjC++ but not for C
Use $(CXX), $(CXXFLAGS), $(CXXSHARED) and $(CPP_DLLIBS) in the rule instead,
matching the equivalent v8 rule for the examples in this file and the node/napi
rule just below it, and drop the CSTD override so it derives c11 as it does for
the other javascript engines.
Assisted-by: Claude Code (Opus 5)
importtest2 did strcpy(str, "white") on its argument. The Lua char *
typemap passes lua_tostring(), a pointer into Lua's interned string
storage, so this overwrote the interned literal "black" in place: its
bytes became "white" while its cached hash stayed that of "black".
lua_pushstring() then re-interned "white" via internshrstr(), which
scans one hash bucket comparing contents with memcmp only. When the
corrupted string sat in that bucket ahead of the real "white", it was
returned instead, and since Lua compares short strings by identity the
runme's inctest.importtest2("black") == "white" was false.
Whether it bit depended on the per process string hash seed and on
string table growth, which reverses bucket chain order, so the test
failed only occasionally under Lua 5.5 in CI. Sweeping the seed with a
forced table growth reproduced it in 36 of 20000 seeds; none fail now.
Return a static buffer instead. inctest passes for all 17 target
languages configured here.
Assisted-by: Claude Code (Opus 5)
NAPI_DIR was empty when node-addon-api is not installed, so
CXXFLAGS="... -I$(NAPI_DIR)" ended in a bare -I. That swallowed the -c
option generated by node-gyp, so g++ linked instead of compiled and
failed with an undefined reference to main. This broke the node engine,
which does not use node-addon-api at all:
cd Examples/javascript/class && make check ENGINE=node
Configure now substitutes the complete option as NAPI_INCLUDE, either
empty or a well formed -I, so the makefiles need no special casing.
Detection of distro supplied headers also moves out of the npm
conditional, enabling napi with the Debian/Ubuntu node-addon-api package
and no npm. The test-suite Makefile no longer hardcodes an include
directory derived from 'npm config get prefix', which is bogus without
npm and is already worked out by configure.
Assisted-by: Claude Code (Opus 5)
A bare #define at the start of a line in a brace delimited %fragment body is
consumed by the SWIG preprocessor and never reaches the generated wrapper. The
macro therefore only exists at SWIG level and code that SWIG does not macro
expand, such as a typemap delimited with %{ ... %}, is left referring to an
undefined name. Use %#define so the definition is emitted for the C compiler.
Fixes SWIG_FromCharPtrAndSize for Python, SWIG_FromBinaryCharPtrAndSize for
Octave, Perl, R and Ruby, SWIG_ToUint8Array and SWIG_FromUint8Array for
JavaScript, and the SWIG_AsVal_* and SWIG_From_* macros in the Scilab char,
short, signed char, unsigned char, unsigned long and long long files.
Lib/scilab/scipointer.swg is deliberately left alone. Its fragments are never
requested by any typemap, so they are only emitted as SWIG preprocessor
definitions; switching them to %#define drops SWIG_ConvertPtr and
SWIG_NewPointerObj from the generated code entirely. Fixing those needs the
definitions moving to a runtime insert, as the other target languages do.
Add a common test case charptr_fragment. It uses a %{ ... %} delimited typemap
so the macro name survives into the wrapper, which only compiles when the
library defines the macro for the C compiler. The target languages without
these macros wrap the same functions using their default typemaps. Verified
that the generated Python module fails to load before this change with an
undefined symbol for SWIG_FromCharPtrAndSize.
Follows on from #3522.
Assisted-by: Claude Code (Opus 5)
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)
A .pyi always takes precedence over its .py companion for type
checking, so once -pyi/-pyifile is active, annotations left in the .py
file are never consulted by any type checker - dead weight. Suppress
them there; the .pyi keeps the full annotations.
- returnTypeAnnotation()/variableAnnotation() now return empty once
pyi_stub is set. The previous always-full versions are renamed to
returnTypeAnnotationForStubFile()/variableAnnotationForStubFile()
and used only for the .pyi output.
- make_pyParmList() gains a for_stub flag to suppress per-parameter
annotations in a def's parameter list the same way.
- emitTypeWrapperClasses()/emitTypeWrapperClass() take a
guard_with_type_checking flag: the opaque SWIGTYPE_* wrapper classes
stay guarded by 'if typing.TYPE_CHECKING:' in the .py file (meaningful
there - they must not exist at runtime), but are emitted unconditionally
in the .pyi (a stub file has no runtime, so the guard is vacuous there).
Documentation: 33.12.1.2 (PEP 484 annotation types) now includes a
worked example of the SWIGTYPE_* opaque type wrapper class fallback
(a single extra function plus its generated .py output), and
33.12.1.3 (Generating .pyi stub files) reuses that same
OptionalInt/Unwrapped example instead of a separate Shape class,
showing the type wrapper class in the .pyi output too - which
visibly lacks the 'if typing.TYPE_CHECKING:' guard the .py version
has. All shown output verified against actual swig output (mypy
clean on the .pyi, ast-parsed the .py).
Assisted-by: Claude Code (Opus 4.8)
Fixes and finishes off the -pyi-stub feature (generates a .pyi PEP 484
stub file alongside the wrapped module):
- printClassHeader() called _swig_add_metaclass, a runtime helper that
no longer exists, which would raise a NameError for any class using
%feature("python:nondynamic"). Switched to the current metaclass=
keyword-argument approach used elsewhere in this file.
- classHandler() called addSymbol() unconditionally, so plain -noproxy
builds (unrelated to -pyi) could fail with spurious "multiply
defined" errors. Gated back to (shadow || pyi_stub).
- The opaque SWIGTYPE_* wrapper classes are now emitted into the .pyi
stub too, not just the .py file, so $pytypename annotations falling
back to an opaque type resolve to a name actually defined in the stub.
- Unannotated variables/constants were emitted into the .pyi as a bare
name with nothing else on the line, which is not a valid attribute
declaration. Falls back to ": typing.Any".
- Regular instance methods and static methods were both silently
missing from the .pyi under -builtin (the option's primary intended
use case), because two separate code paths never reached the shared
pyi_stub emission logic.
- Renamed the command line option from -pyi-stub to -pyi, and improved
the -help text with the PYI ("Python Interface") acronym.
- Added header comment blocks to the new helper methods, matching the
file's existing convention.
- Documented -pyi in the manual with a worked, verified example, and
marked the pytyping/-pyi work as experimental/still evolving in
CHANGES.current.
- Added a python test-suite case (python_pyi.i) built with -builtin
-pyi, covering a constructor, regular method, static method, member
variable and an opaque-type fallback in one go. Clean up generated
.pyi files in the Makefiles the same way .py files already are.
Assisted-by: Claude Code (Sonnet 5)
Finalises the PR keeping the $pytypename mechanism but leaving the default
pytyping SWIGTYPE typemaps as typing.Any. The special variables are exercised
only through explicit typemaps; the default annotation change and the
library-wide pytyping sweep are left for a follow-up.
python.cxx:
- getProxyClassLocalName() uses import_name_string() so a class from an
imported module gets its fully qualified, package-aware name, as in
classDeclaration().
- Guard substitutePytypingVars() against a null typemap and skip work when
there is no special variable to substitute.
- Error when $*pytypename is applied to a non-pointer type instead of
emitting the literal.
- Remove the _swig_python_version_info >= (3, 5) guards; SWIG supports Python
3.5 and later, so import typing unconditionally and use a bare
if typing.TYPE_CHECKING.
- Fix two leaks (SwigType_manglestr, SwigType_typedef_resolve_all).
Doc/Manual/Python.html: correct the feature value pytyping to typing and
document the new special variables with valid HTML.
Tests: python_annotations_typing opts in to proxy-name annotations via explicit
typemaps and covers $*pytypename, class-typed member variables, a class-typed
%constant, and the opaque fallback for a forward-declared class; add
python_annotations_import for the cross-module module-qualified name.
Assisted-by: Claude Code (Opus 4.8)
- HTML doc cleanup/edits.
- Drop a stray Printf argument in emitIncompleteClass; the format string
has no conversion for it.
- Take ownership of the getProxyClassLocalName() result directly instead
of copying it, fixing a small string leak.
- Use SwigType_lstr instead of SwigType_str for the opaque class docstring
so the shown type matches the mangled SWIGTYPE_ name - short &, short *
and short[] all resolve to "short *" for SWIGTYPE_p_short.
- Emit the generated SWIGTYPE_ type wrapper classes inside an
'if typing.TYPE_CHECKING' block instead of as real runtime classes. They
exist only to give the PEP 484 annotations a named type to refer to, so
declaring them for static type checkers only keeps them out of the runtime
module namespace. A preceding comment replaces the previous per-class
"only used for type annotations ..." docstring line.
- Quote the type in the type wrapper docstrings with single quotes rather
than reStructuredText double backticks as this is the SWIG convention
and the tools that would actually render RST docstrings — Sphinx autodoc,
help()/pydoc (to inspect runtime objects) wouldn't even see them as
they inspect runtime objects.
- Replace the "is this always correct?" FIXME in the enum branch of
substituteTypenameSpecialVariable with a comment describing when it is
reached and why int is correct. The default 'enum SWIGTYPE' typemap maps
straight to "int" without substituting, so enums only reach this branch
when a pytyping typemap uses $pytypename on an enum type; int is right
because Python wraps enums as ints. Add a test that exercises this via a
custom typemap (without it the type would wrongly resolve to an opaque
SWIGTYPE_ class).
- Assert in the python_annotations_typing_runme test that the SWIGTYPE_
type wrapper classes are not present at runtime, confirming they are
declared for type checkers only.
Assisted-by: Claude Code (Opus 4.8)
Hand written code targeting SWIG 4.4 and earlier invalidates a proxy object
with 'DATA_PTR(obj) = NULL', which used to clear the wrapped C/C++ pointer.
SWIG 4.5 stores that pointer in a swig_ruby_wrapped_object reached through
RTYPEDDATA_GET_DATA, so the assignment now detaches the wrapper itself unless
Ruby embedded it in the object slot, which it only does from Ruby 3.3. The next
conversion of the object then dereferenced a null wrapper and crashed.
Treat a detached object as one whose pointer has been cleared, restoring the
pre 4.5 behaviour, and guard the remaining wrapper dereferences in
SWIG_Ruby_AcquirePtr, SWIG_RubyUnlinkObjects and the mark and free callbacks.
Extend ruby_manual_proxy, which models the Subversion Ruby bindings, with the
legacy close idiom that this fixes.
Closes#3512
Assisted-by: Claude Code (Opus 5)
Follow up fixes to #3408.
A pytyping typemap must be defined alongside the in and out typemaps for the
type, otherwise a type wrapped as a pointer to an opaque type is annotated as
though it were a native Python type. Move the remaining pytyping typemaps that
were defined by default but whose in typemaps are not:
wchar_t and wchar_t * to pywstrings.swg, which is included by wchar.i,
cwstring.i and std_wstring.i
float _Complex, double _Complex and _Complex to ccomplex.i
Delete the long double pytyping typemap from pytyping.swg. Unlike the types
above there is no library file to move it to, as long double has no in and out
typemaps anywhere in the SWIG library. It is always wrapped as a pointer to an
opaque type and a Python float is never accepted for it, so annotating it as
float was always wrong. It now falls back to the SWIGTYPE default typing.Any.
Include pytyping.swg from pytypemaps.swg before the Unified Typemap Library
instead of from python.swg after it. A %apply in the library, such as
%apply size_t { std::size_t } in typemaps/misctypes.swg, only copies the
typemaps defined at that point, so std::size_t and std::ptrdiff_t now pick up
the pytyping typemaps instead of having to be listed again.
Also fix the const long double & annotation which was wrongly changed to float.
Add the CHANGES.current entry, correct the PEP 484 example in the Python
documentation for the char * annotation change, and align the indentation of
the char typemap with the other grouped typemaps.
Assisted-by: Claude Code (Opus 4.8)
The SwigPyObjectType metaclass and SwigPyStaticVar type created via
PyType_FromSpec in the heap types path of builtin.swg were allocated as
distinct heap type instances in each compiled module's copy of the
TypeOnce functions. When a class inherited from types defined in
different SWIG extension modules -- such as a director class from
module A and a non-director class from module B -- PyType_FromSpecWithBases
detected incompatible metaclasses and raised:
TypeError: metaclass conflict
Both TypeOnce functions now check the shared runtime data module for an
existing instance before creating a new one, so all modules share a
single metaclass. This regression was introduced when SWIG_HEAPTYPES
was enabled by default (SWIG 4.4) and only manifests on Python 3.12+.
Fixes#3315.
Assisted-by: opencode (deepseek-v4-flash)
R 4.3.0 and later print a "using C compiler: ..." / "using C++ compiler: ..."
banner via message() (stderr) on every R CMD SHLIB compile. The R example and
test-suite rules already redirect SHLIB stdout to /dev/null to hide the old
compiler echo, but the new banner goes to stderr, so it leaked into the logs -
hundreds of lines per test-suite run.
Filter just those banner lines out of stderr in the r and r_cpp rules while
keeping genuine compiler errors and warnings, and preserve the SHLIB exit
status so a failed compile still fails the rule.
Assisted-by: Claude Code (Opus 4.8)
R::enumDeclaration built the enum name passed to defineEnumeration from
the enum's C++ name, so %rename was ignored for the R enum name: a
renamed enum was registered under its original name while its enum item
names used the renamed name. Use sym:name for the final name component,
keeping any C++ scope.
The enum class name emitted for the $R_class typemap special variable
(used by enumFromInteger / enumToInteger in the generated accessors) was
similarly built from the C++ name. Substitute the renamed name there too
via replaceRClass, using Language::enumLookup to find the enum, so the
accessor coercions and defineEnumeration agree on the enum name.
Update the cpp11_strongly_typed_enumerations R runme to expect the
renamed enum name _Enum18 and remove the known-limitation TODO.
Assisted-by: Claude Code (Opus 4.8)
goEnumName built the Go enum type name from the enum's C++ name, so
%rename was ignored for the Go type. Use sym:name for the final name
component, keeping any C++ scope.
A side effect is that a typedef named enum, "typedef enum class Foo {...}
Bar;", now uses the typedef name Bar for the Go type as well as for its
enum item constants, matching the other target languages.
Assisted-by: Claude Code (Opus 4.8)
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)
Simplify the enum name prefixing in R::enumValue() - the Copy() calls were
not needed as Setattr() adds a reference and setting sym:name to the value it
already holds is a no-op, so both copies leaked.
Add a CHANGES.current entry and note in the test that %rename is not honoured
in the generated R code, which the test currently expects.
Assisted-by: Claude Opus 4.8
In R::enumValue() (Source/Modules/r.cxx:2749), for non-class scoped enums
(enum class at file scope), the enum value name was being prefixed twice:
1. R::enumValue() manually prefixes sym:name with the parent enum name via
Swig_name_member(0, parent_sym:name, symname) giving VarType_Discretized
2. Then Language::variableWrapper() prefixes again via EnumClassPrefix (set
by Language::enumDeclaration for scoped enums) giving
VarType_VarType_Discretized
This caused the C wrapper function to be named
R_swig_VarType_VarType_Discretized_get while the R .Call referenced
R_swig_VarType_Discretized_get, a mismatch that crashed at runtime.
In R::enumValue(), for scoped enums, skip the manual prefixing and let
Language::variableWrapper() handle it through the EnumClassPrefix mechanism
(which already exists for this purpose).
Closes#2984
the new Lua directors feature uses lua_setuservalue, added in Lua 5.2.
Removing the LUA_VERSION_NUM-guarded compatibility defines for lua_rawlen,
lua_pushglobaltable and lua_rawsetp/lua_rawgetp from Lib/lua/luarun.swg.
LuaJIT implements the Lua 5.1 API and, per its own documentation, is
"API+ABI-compatible with Lua 5.1, which prevents implementing features
that would otherwise break the Lua/C API and ABI" (https://luajit.org/extensions.html,
"Extensions from Lua 5.2" section) - so it never defines these Lua 5.2 additions.
Noticed when rebuilding obs-studio's LuaJIT-based Lua bindings against SWIG master.
Restore those compatibility defines, scoped to 5.1 and later only (the old
Lua 5.0-only shims are not restored).
Also revert an incidental ok!=LUA_OK comparison introduced by #3394
back to ok!=0, since LUA_OK isn't defined pre-5.2 either and luaL_dostring()
already returns 0 on success on every Lua version.
Fix the examples and test-suite where they themselves used Lua 5.2 only
features: LUA_OK in Examples/lua/embed/embed.c, _ENV in helpers.lua, and
string.format("%c", ...) in li_cdata_bytes, which yields an empty string for a
NUL byte in Lua 5.1 as strlen() is applied to the formatted result, so
string.char() is used instead.
Directors continue to require Lua 5.2 or later and are unaffected by this
change; the Lua 5.1 director test failures are addressed separately.
Assisted-by: Claude Code (Sonnet 5)
Converting signed to unsigned may change the value
Converting signed to unsigned may change the value.
The test need to pass values unchanged.
As long as the values are in the type range!
Converting signed to unsigned we may convert the value to a different one if the value is outside the range.
Though the test fails. For example on i386
uint16_td(0xffff) => 0x7fff + 1
Signed-off-by: Erez Geva <ErezGeva2@gmail.com>
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)