Commit f05f6164 authored by Robert Bradshaw's avatar Robert Bradshaw

Return a signed value for abs(int).

This means abs(MIN_INT) has the "overflow" behavior of returning
MIN_INT as in C, but avoids surprising unsigned arithmetic for all
other values.

To preserve Python compatibility, abs(x) dissallows integer inference
for x just like the other potentially overflowing arithmetic operators.

This fixes Github Issue #1837
parent 8b93e9ec
......@@ -95,16 +95,25 @@ builtin_function_table = [
is_strict_signature = True),
BuiltinFunction('abs', "f", "f", "fabsf",
is_strict_signature = True),
BuiltinFunction('abs', "i", "i", "abs",
is_strict_signature = True),
BuiltinFunction('abs', "l", "l", "labs",
is_strict_signature = True),
BuiltinFunction('abs', None, None, "__Pyx_abs_longlong",
utility_code = UtilityCode.load("abs_longlong", "Builtins.c"),
func_type = PyrexTypes.CFuncType(
PyrexTypes.c_longlong_type, [
PyrexTypes.CFuncTypeArg("arg", PyrexTypes.c_longlong_type, None)
],
is_strict_signature = True, nogil=True)),
] + list(
# uses getattr to get PyrexTypes.c_uint_type etc to allow easy iteration over a list
BuiltinFunction('abs', None, None, "__Pyx_abs_{0}".format(t),
utility_code = UtilityCode.load("abs_{0}".format(t), "Builtins.c"),
BuiltinFunction('abs', None, None, "/*abs_{0}*/".format(t.specialization_name()),
func_type = PyrexTypes.CFuncType(
getattr(PyrexTypes,"c_u{0}_type".format(t)), [
PyrexTypes.CFuncTypeArg("arg", getattr(PyrexTypes,"c_{0}_type".format(t)), None)
],
t,
[PyrexTypes.CFuncTypeArg("arg", t, None)],
is_strict_signature = True, nogil=True))
for t in ("int", "long", "longlong")
for t in (PyrexTypes.c_uint_type, PyrexTypes.c_ulong_type, PyrexTypes.c_ulonglong_type)
) + list(
BuiltinFunction('abs', None, None, "__Pyx_c_abs{0}".format(t.funcsuffix),
func_type = PyrexTypes.CFuncType(
......
......@@ -306,6 +306,14 @@ class MarkOverflowingArithmetic(CythonTransform):
else:
return self.visit_dangerous_node(node)
def visit_SimpleCallNode(self, node):
if (isinstance(node.function, ExprNodes.NameNode)
and node.function.name == 'abs'):
# Overflows for minimum value of fixed size ints.
return self.visit_dangerous_node(node)
else:
return self.visit_neutral_node(node)
visit_UnopNode = visit_neutral_node
visit_UnaryMinusNode = visit_dangerous_node
......
......@@ -245,9 +245,7 @@ static CYTHON_INLINE unsigned long __Pyx_abs_long(long x) {
//////////////////// abs_longlong.proto ////////////////////
static CYTHON_INLINE unsigned PY_LONG_LONG __Pyx_abs_longlong(PY_LONG_LONG x) {
if (unlikely(x == -PY_LLONG_MAX-1))
return ((unsigned PY_LONG_LONG)PY_LLONG_MAX) + 1U;
static CYTHON_INLINE PY_LONG_LONG __Pyx_abs_longlong(PY_LONG_LONG x) {
#if defined (__cplusplus) && __cplusplus >= 201103L
return (unsigned PY_LONG_LONG) std::abs(x);
#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
......
......@@ -486,6 +486,11 @@ def safe_only():
for j in range(10):
res = -j
assert typeof(j) == "Python object", typeof(j)
h = 1
res = abs(h)
assert typeof(h) == "Python object", typeof(h)
cdef int c_int = 1
assert typeof(abs(c_int)) == "int", typeof(abs(c_int))
@infer_types(None)
def safe_c_functions():
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment