diff --git a/CHANGES.current b/CHANGES.current index 83518ed96..75e4c1fe2 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -6,6 +6,13 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.0.0 (in progress) =========================== +2017-10-06: wsfulton + [Python] Issue #1108. Fix platorm inconsistency in Python default argument handling. + 32 bit and 64 bit compiled versions of SWIG generated different Python files + when default arguments were outside the range of 32 bit signed integers. + The default arguments specified in Python are now only those that are in the + range of a 32 bit signed integer, otherwise the default is obtained from C/C++ code. + 2017-10-02: wsfulton [C#] Fix std::complex types passed by value. diff --git a/Examples/test-suite/default_args.i b/Examples/test-suite/default_args.i index 1f7183f47..1401cd576 100644 --- a/Examples/test-suite/default_args.i +++ b/Examples/test-suite/default_args.i @@ -28,6 +28,14 @@ int value_perm(int first, int mode = 0640 | 0004) { return mode; } int value_m01(int first, int val = -01) { return val; } bool booltest2(bool x = 0 | 1) { return x; } + int max_32bit_int1(int a = 0x7FFFFFFF) { return a; } + int max_32bit_int2(int a = 2147483647) { return a; } + int min_32bit_int1(int a = -0x80000000) { return a; } + int min_32bit_int2(int a = -2147483648) { return a; } + long long too_big_32bit_int1(long long a = 0x80000000) { return a; } + long long too_big_32bit_int2(long long a = 2147483648LL) { return a; } + long long too_small_32bit_int1(long long a = -0x80000001) { return a; } + long long too_small_32bit_int2(long long a = -2147483649LL) { return a; } }; void doublevalue1(int first, double num = 0.0e-1) {} diff --git a/Examples/test-suite/python/default_args_runme.py b/Examples/test-suite/python/default_args_runme.py index ddaf2cd4f..d745cae10 100644 --- a/Examples/test-suite/python/default_args_runme.py +++ b/Examples/test-suite/python/default_args_runme.py @@ -139,6 +139,24 @@ def run(module_name): print "booltest2 failed" tricky_failure = True + if tricky.max_32bit_int1() != 0x7FFFFFFF: + print "max_32bit_int1 failed" + tricky_failure = True + if tricky.min_32bit_int1() != -2147483648: + print "min_32bit_int1 failed" + tricky_failure = True + if tricky.max_32bit_int2() != 0x7FFFFFFF: + print "max_32bit_int2 failed" + tricky_failure = True + if tricky.min_32bit_int2() != -2147483648: + print "min_32bit_int2 failed" + tricky_failure = True + + tricky.too_big_32bit_int1() + tricky.too_small_32bit_int1() + tricky.too_big_32bit_int2() + tricky.too_small_32bit_int2() + if tricky_failure: raise RuntimeError diff --git a/Source/Modules/python.cxx b/Source/Modules/python.cxx index 259d7c0d8..58b61c97f 100644 --- a/Source/Modules/python.cxx +++ b/Source/Modules/python.cxx @@ -18,9 +18,15 @@ #include #include +#include +#include + #define PYSHADOW_MEMBER 0x2 #define WARN_PYTHON_MULTIPLE_INH 405 +#define PYTHON_INT_MAX (2147483647) +#define PYTHON_INT_MIN (-2147483647-1) + static String *const_code = 0; static String *module = 0; static String *package = 0; @@ -2048,6 +2054,7 @@ public: long value = strtol(s, &end, 0); if (errno == ERANGE || end == s) return NIL; + if (*end != '\0') { // If there is a suffix after the number, we can safely ignore "l" // and (provided the number is unsigned) "u", and also combinations of @@ -2070,6 +2077,14 @@ public: // So now we are certain that we are indeed dealing with an integer // that has a representation as long given by value. + // Restrict to guaranteed supported range in Python, see maxint docs: https://docs.python.org/2/library/sys.html#sys.maxint + // Don't do this pointless check when long is 32 bits or smaller as strtol will have already failed with ERANGE +#if LONG_MAX > PYTHON_INT_MAX || LONG_MIN < PYTHON_INT_MIN + if (value > PYTHON_INT_MAX || value < PYTHON_INT_MIN) { + return NIL; + } +#endif + if (Cmp(resolved_type, "bool") == 0) // Allow integers as the default value for a bool parameter. return NewString(value ? "True" : "False");