Skip to content

Commit

Permalink
C++ reference errors when passing in a 'NULL' change of behaviour.
Browse files Browse the repository at this point in the history
Most languages now use "NullReferenceError" in the error message
where they previously used "ValueError". Also exception changes:

Guile:    "swig-null-reference-error" instead of "swig-value-error"
MzScheme: "swig-null-reference-error" instead of "swig-value-error"
PHP:      zend_ce_type_error instead of zend_ce_value_error
Python:   Consistently raises TypeError instead of a mix of ValueError
          and TypeError.
Ruby:     Consistently raises NullReferenceError instead of a mix of
          ArgumentError and NullReferenceErrorError.

The consistent raising of a TypeError instead of ValueError for Python
ensures that incorrectly passing 'None' into a C++ reference argument
will correctly convert the error into a NotImplemented error for
the rich comparisons implementations per PEP 207. Fixes swig#2987

Note that the li_constraints checking implementation for the NONNULL
typemap for pointers also makes the same error change from
SWIG_ValueError to SWIG_NullReferenceError.

The D typemaps use SWIG_DNullReferenceException instead of
SWIG_DIllegalArgumentException, although this ultimately has no change
as the same D Exception is still thrown.
  • Loading branch information
wsfulton committed Sep 14, 2024
1 parent a268c71 commit 17ead74
Show file tree
Hide file tree
Showing 34 changed files with 136 additions and 77 deletions.
20 changes: 20 additions & 0 deletions CHANGES.current
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.3.0 (in progress)
===========================

2024-09-14: wsfulton
C++ reference errors when passing in a 'NULL' change of behaviour.
Most languages now use "NullReferenceError" in the error message
where they previously used "ValueError". Also exception changes:

Guile: "swig-null-reference-error" instead of "swig-value-error"
MzScheme: "swig-null-reference-error" instead of "swig-value-error"
PHP: zend_ce_type_error instead of zend_ce_value_error
Python: Consistently raises TypeError instead of a mix of ValueError
and TypeError.
Ruby: Consistently raises NullReferenceError instead of a mix of
ArgumentError and NullReferenceErrorError.

The consistent raising of a TypeError instead of ValueError for Python
ensures that incorrectly passing 'None' into a C++ reference argument
will correctly convert the error into a NotImplemented error for
the rich comparisons implementations per PEP 207. Fixes #2987

*** POTENTIAL INCOMPATIBILITY ***

2024-09-12: olly
Remove remains of %nestedworkaround and the nestedworkaround
feature it uses, which were deprecated over 10 years ago in SWIG
Expand Down
21 changes: 11 additions & 10 deletions Doc/Manual/Guile.html
Original file line number Diff line number Diff line change
Expand Up @@ -456,16 +456,17 @@ <H2><a name="Guile_nn17">26.9 Exception Handling</a></H2>

<div class="code">
<pre>
MAP(SWIG_MemoryError, "swig-memory-error");
MAP(SWIG_IOError, "swig-io-error");
MAP(SWIG_RuntimeError, "swig-runtime-error");
MAP(SWIG_IndexError, "swig-index-error");
MAP(SWIG_TypeError, "swig-type-error");
MAP(SWIG_DivisionByZero, "swig-division-by-zero");
MAP(SWIG_OverflowError, "swig-overflow-error");
MAP(SWIG_SyntaxError, "swig-syntax-error");
MAP(SWIG_ValueError, "swig-value-error");
MAP(SWIG_SystemError, "swig-system-error");
MAP(SWIG_MemoryError, "swig-memory-error");
MAP(SWIG_IOError, "swig-io-error");
MAP(SWIG_RuntimeError, "swig-runtime-error");
MAP(SWIG_IndexError, "swig-index-error");
MAP(SWIG_TypeError, "swig-type-error");
MAP(SWIG_DivisionByZero, "swig-division-by-zero");
MAP(SWIG_OverflowError, "swig-overflow-error");
MAP(SWIG_SyntaxError, "swig-syntax-error");
MAP(SWIG_ValueError, "swig-value-error");
MAP(SWIG_SystemError, "swig-system-error");
MAP(SWIG_NullReferenceError, "swig-null-reference-error");
</pre>
</div>

Expand Down
1 change: 1 addition & 0 deletions Doc/Manual/Library.html
Original file line number Diff line number Diff line change
Expand Up @@ -2399,6 +2399,7 @@ <H3><a name="Library_nn17">12.5.1 exception.i</a></H3>
SWIG_SyntaxError
SWIG_ValueError
SWIG_SystemError
SWIG_NullReferenceError
</pre>
</div>

Expand Down
2 changes: 1 addition & 1 deletion Examples/test-suite/lua/li_constraints_runme.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ function check_func(except, f, val, name)
actual, err = pcall(f, val);
assert(actual == except, 'function perform exception wrongly');
if name == nil then
r = 'SWIG_ValueError:Received a NULL pointer.';
r = 'SWIG_NullReferenceError:Received a NULL pointer.';
else
r = 'SWIG_ValueError:Expected a ' .. name .. ' value.';
end
Expand Down
2 changes: 1 addition & 1 deletion Examples/test-suite/octave/li_constraints_runme.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function check_double(except, f, val, name)
# Empty matrix translate to null pointer
li_constraints.test_nonnull([]);
catch
have_exception = strcmp("Received a NULL pointer. (SWIG_ValueError)", lasterr) == 1;
have_exception = strcmp("Received a NULL pointer. (SWIG_NullReferenceError)", lasterr) == 1;
end
if (!have_exception)
error("test_nonnull should perform exception with 'null' value");
Expand Down
2 changes: 1 addition & 1 deletion Examples/test-suite/perl5/cpp17_string_view_runme.pl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

# Verify type-checking for %typemap(in) const std::string_view & {}
eval { cpp17_string_view::test_const_reference(undef) };
like($@, qr/\bValueError\b/, "Test 4");
like($@, qr/\bNullReferenceError\b/, "Test 4");

#
# Input and output typemaps for pointers and non-const references to
Expand Down
2 changes: 1 addition & 1 deletion Examples/test-suite/perl5/li_constraints_runme.pl
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ sub check_double {
my $ret = eval { li_constraints::test_nonnull(undef); 1; };
my $err = $@;
is($ret, undef, "li_constraints::test_nonnull throw exception with null");
ok($err =~ "^ValueError Received a NULL pointer.", "li_constraints::test_nonnull throw proper exception");
ok($err =~ "^NullReferenceError Received a NULL pointer.", "li_constraints::test_nonnull throw proper exception");
my $ptr = li_constraints::get_nonnull();
# There should be no exception, we can use Perl lambda function
$ret = (sub { li_constraints::test_nonnull($ptr); 1; })->();
Expand Down
2 changes: 1 addition & 1 deletion Examples/test-suite/perl5/li_std_string_runme.pl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

# Verify type-checking for %typemap(in) const std::string & {}
eval { li_std_string::test_const_reference(undef) };
like($@, qr/\bValueError\b/, "Test 4");
like($@, qr/\bNullReferenceError\b/, "Test 4");

#
# Input and output typemaps for pointers and non-const references to
Expand Down
2 changes: 1 addition & 1 deletion Examples/test-suite/php/li_constraints_runme.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function check_double(bool $except, $fn, string $f, $val) {
$have_exception = false;
try {
test_nonnull(null);
} catch(ValueError $e) {
} catch(TypeError $e) {
$msg = $e->getMessage();
$have_exception = strcmp($msg, "Received a NULL pointer.") === 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
exception_thrown = False
try:
MovableCopyable.movein(None)
except ValueError as e:
except TypeError as e:
if "invalid null reference" not in str(e):
raise RuntimeError("incorrect exception message:" + str(e))
exception_thrown = True
Expand Down
10 changes: 5 additions & 5 deletions Examples/test-suite/python/li_boost_shared_ptr_runme.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def runtest(self):
try:
li_boost_shared_ptr.valuetest(k)
raise RuntimeError("Failed to catch null pointer")
except ValueError:
except TypeError:
pass

if (li_boost_shared_ptr.pointertest(k) != None):
Expand All @@ -165,7 +165,7 @@ def runtest(self):
try:
li_boost_shared_ptr.reftest(k)
raise RuntimeError("Failed to catch null pointer")
except ValueError:
except TypeError:
pass

# test null pointers emitted from C++
Expand Down Expand Up @@ -476,7 +476,7 @@ def runtest(self):
try:
m.MemberValue = None
raise RuntimeError("Failed to catch null pointer")
except ValueError:
except TypeError:
pass

# ////////////////////////////////// Global variables /////////////////
Expand Down Expand Up @@ -514,7 +514,7 @@ def runtest(self):
try:
li_boost_shared_ptr.cvar.GlobalValue = None
raise RuntimeError("Failed to catch null pointer")
except ValueError:
except TypeError:
pass

# plain pointer
Expand Down Expand Up @@ -548,7 +548,7 @@ def runtest(self):
try:
li_boost_shared_ptr.cvar.GlobalReference = None
raise RuntimeError("Failed to catch null pointer")
except ValueError:
except TypeError:
pass

# ////////////////////////////////// Templates ////////////////////////
Expand Down
2 changes: 1 addition & 1 deletion Examples/test-suite/python/li_constraints_runme.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def check_double(et, fn, f, val):
have_exception = False
try:
li_constraints.test_nonnull(None)
except ValueError as e:
except TypeError as e:
have_exception = "Received a NULL pointer." == "%s"%(e)
if not have_exception:
raise Exception("test_nonnull should perform exception with 'null' value")
Expand Down
4 changes: 2 additions & 2 deletions Examples/test-suite/python/li_std_wstring_runme.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ def check_equal(a, b):
try:
li_std_wstring.test_reference(None)
raise RuntimeError("NULL check failed")
except ValueError as e:
except TypeError as e:
if "invalid null reference" not in str(e):
raise RuntimeError("Missing text {}".format(e))
try:
li_std_wstring.test_const_reference(None)
raise RuntimeError("NULL check failed")
except ValueError as e:
except TypeError as e:
if "invalid null reference" not in str(e):
raise RuntimeError("Missing text {}".format(e))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
exception_thrown = false
begin
Cpp11_rvalue_reference_move::MovableCopyable.movein(nil)
rescue ArgumentError => e
rescue NullReferenceError => e
if (!e.to_s.include? "invalid null reference")
raise RuntimeError, "incorrect exception message: #{e.to_s}"
end
Expand Down
10 changes: 5 additions & 5 deletions Examples/test-suite/ruby/li_boost_shared_ptr_runme.rb
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def runtest
begin
Li_boost_shared_ptr::valuetest(k)
raise RuntimeError, "Failed to catch null pointer"
rescue ArgumentError
rescue NullReferenceError
end

if (Li_boost_shared_ptr::pointertest(k) != nil)
Expand All @@ -194,7 +194,7 @@ def runtest
begin
Li_boost_shared_ptr::reftest(k)
raise RuntimeError, "Failed to catch null pointer"
rescue ArgumentError
rescue NullReferenceError
end

# $owner
Expand Down Expand Up @@ -508,7 +508,7 @@ def runtest
begin
m.MemberValue = nil
raise RuntimeError, "Failed to catch null pointer"
rescue ArgumentError
rescue NullReferenceError
end

# ////////////////////////////////// Global variables /////////////////
Expand Down Expand Up @@ -547,7 +547,7 @@ def runtest
begin
Li_boost_shared_ptr.GlobalValue = nil
raise RuntimeError, "Failed to catch null pointer"
rescue ArgumentError
rescue NullReferenceError
end

# plain pointer
Expand Down Expand Up @@ -582,7 +582,7 @@ def runtest
begin
Li_boost_shared_ptr.GlobalReference = nil
raise RuntimeError, "Failed to catch null pointer"
rescue ArgumentError
rescue NullReferenceError
end

# ////////////////////////////////// Templates ////////////////////////
Expand Down
2 changes: 1 addition & 1 deletion Examples/test-suite/ruby/li_constraints_runme.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def check_double(except, fn, f, val)
begin
test_nonnull(nil)
rescue => e
have_exception = e.class == ArgumentError && e.message == "Received a NULL pointer."
have_exception = e.class == NullReferenceError && e.message == "Received a NULL pointer."
end
if not have_exception
raise RuntimeError, "test_nonnull should perform exception with 'null' value"
Expand Down
4 changes: 2 additions & 2 deletions Examples/test-suite/ruby/li_std_wstring_runme.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@
begin
Li_std_wstring.test_reference(nil)
raise RuntimeError, "NULL check failed"
rescue ArgumentError => e
rescue NullReferenceError => e
swig_assert_simple(e.message.include? "invalid null reference")
end
begin
Li_std_wstring.test_const_reference(nil)
raise RuntimeError, "NULL check failed"
rescue ArgumentError => e
rescue NullReferenceError => e
swig_assert_simple(e.message.include? "invalid null reference")
end

Expand Down
2 changes: 1 addition & 1 deletion Examples/test-suite/tcl/li_constraints_runme.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ check_double 0 nonzero "nonzero" -10

set actual [ catch { test_nonnull NULL } err_msg ]
if { ($actual != 1) ||
([ string equal $err_msg "ValueError Received a NULL pointer." ] != 1) } {
([ string equal $err_msg "NullReferenceError Received a NULL pointer." ] != 1) } {
error "Test 'test_nonnull' with null value fail"
}
set nonnull [ get_nonnull ]
Expand Down
2 changes: 1 addition & 1 deletion Lib/constraints.i
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
Pointer NONNULL
{
if (!$1) {
SWIG_exception(SWIG_ValueError,"Received a NULL pointer.");
SWIG_exception(SWIG_NullReferenceError,"Received a NULL pointer.");
}
}

Expand Down
6 changes: 3 additions & 3 deletions Lib/d/boost_shared_ptr.i
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
%typemap(in, canthrow=1) CONST TYPE ($&1_type argp = 0) %{
argp = ((SWIG_SHARED_PTR_QNAMESPACE::shared_ptr< CONST TYPE > *)$input) ? ((SWIG_SHARED_PTR_QNAMESPACE::shared_ptr< CONST TYPE > *)$input)->get() : 0;
if (!argp) {
SWIG_DSetPendingException(SWIG_DIllegalArgumentException, "Attempt to dereference null $1_type");
SWIG_DSetPendingException(SWIG_DNullReferenceException, "Attempt to dereference null $1_type");
return $null;
}
$1 = *argp; %}
Expand All @@ -30,7 +30,7 @@

%typemap(directorout) CONST TYPE
%{ if (!$input) {
SWIG_DSetPendingException(SWIG_DIllegalArgumentException, "Attempt to dereference null $1_type");
SWIG_DSetPendingException(SWIG_DNullReferenceException, "Attempt to dereference null $1_type");
return $null;
}
SWIG_SHARED_PTR_QNAMESPACE::shared_ptr< CONST TYPE > *smartarg = (SWIG_SHARED_PTR_QNAMESPACE::shared_ptr< CONST TYPE > *)$input;
Expand All @@ -56,7 +56,7 @@
%typemap(in, canthrow=1) CONST TYPE & %{
$1 = ($1_ltype)(((SWIG_SHARED_PTR_QNAMESPACE::shared_ptr< CONST TYPE > *)$input) ? ((SWIG_SHARED_PTR_QNAMESPACE::shared_ptr< CONST TYPE > *)$input)->get() : 0);
if (!$1) {
SWIG_DSetPendingException(SWIG_DIllegalArgumentException, "$1_type reference is null");
SWIG_DSetPendingException(SWIG_DNullReferenceException, "$1_type reference is null");
return $null;
} %}
%typemap(out, fragment="SWIG_null_deleter") CONST TYPE &
Expand Down
11 changes: 8 additions & 3 deletions Lib/d/dhead.swg
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ typedef enum {
SWIG_DIllegalArgumentException,
SWIG_DIllegalElementException,
SWIG_DIOException,
SWIG_DNoSuchElementException
SWIG_DNoSuchElementException,
SWIG_DNullReferenceException
} SWIG_DExceptionCodes;

typedef void (* SWIG_DExceptionCallback_t)(const char *);
Expand All @@ -39,7 +40,8 @@ static SWIG_DException_t SWIG_d_exceptions[] = {
{ SWIG_DIllegalArgumentException, NULL },
{ SWIG_DIllegalElementException, NULL },
{ SWIG_DIOException, NULL },
{ SWIG_DNoSuchElementException, NULL }
{ SWIG_DNoSuchElementException, NULL },
{ SWIG_DNullReferenceException, NULL }
};

static void SWIGUNUSED SWIG_DSetPendingException(SWIG_DExceptionCodes code, const char *msg) {
Expand All @@ -58,12 +60,14 @@ SWIGEXPORT void SWIGRegisterExceptionCallbacks_$module(
SWIG_DExceptionCallback_t illegalArgumentCallback,
SWIG_DExceptionCallback_t illegalElementCallback,
SWIG_DExceptionCallback_t ioCallback,
SWIG_DExceptionCallback_t noSuchElementCallback) {
SWIG_DExceptionCallback_t noSuchElementCallback,
SWIG_DExceptionCallback_t nullReferenceCallback) {
SWIG_d_exceptions[SWIG_DException].callback = exceptionCallback;
SWIG_d_exceptions[SWIG_DIllegalArgumentException].callback = illegalArgumentCallback;
SWIG_d_exceptions[SWIG_DIllegalElementException].callback = illegalElementCallback;
SWIG_d_exceptions[SWIG_DIOException].callback = ioCallback;
SWIG_d_exceptions[SWIG_DNoSuchElementException].callback = noSuchElementCallback;
SWIG_d_exceptions[SWIG_DNullReferenceException].callback = nullReferenceCallback;
}
%}

Expand All @@ -80,6 +84,7 @@ private class SwigExceptionHelper {
&setException,
&setException,
&setException,
&setException,
&setException
);
}
Expand Down
Loading

0 comments on commit 17ead74

Please sign in to comment.