Repository: numba/numba

Issue:
Integer overflow when using range provided with unsigned integers
I'm feeding range in a jitted function with integers taken from an unsigned int64 numpy array. When the numbers are sufficiently large (exceeding the maximum value of a signed int32 integer), an integer overflow happens.

Here is a minimal working example:

``` python
import numpy
import numba

@numba.jit
def iterate_over_range():
    arr = numpy.array([2552764644, 2552764645], dtype='uint64')

    for val in range(arr[0], arr[1]):
        print(val)
```

When running `iterate_over_range()`, it prints -1742202652. I'm using numba 0.18.2 on a 64 bit Linux system.

Integer overflow when using range provided with unsigned integers
I'm feeding range in a jitted function with integers taken from an unsigned int64 numpy array. When the numbers are sufficiently large (exceeding the maximum value of a signed int32 integer), an integer overflow happens.

Here is a minimal working example:

``` python
import numpy
import numba

@numba.jit
def iterate_over_range():
    arr = numpy.array([2552764644, 2552764645], dtype='uint64')

    for val in range(arr[0], arr[1]):
        print(val)
```

When running `iterate_over_range()`, it prints -1742202652. I'm using numba 0.18.2 on a 64 bit Linux system.



Discussion:
I can confirm the problem.  Looks like bad coercion for uint64.  Works with int64.

I can confirm the problem.  Looks like bad coercion for uint64.  Works with int64.


Resolving patch:
diff --git a/numba/targets/rangeobj.py b/numba/targets/rangeobj.py
--- a/numba/targets/rangeobj.py
+++ b/numba/targets/rangeobj.py
@@ -142,3 +142,5 @@ def iternext(self, context, builder, result):
 
 make_range_impl(types.range_state32_type, types.range_iter32_type, types.int32)
 make_range_impl(types.range_state64_type, types.range_iter64_type, types.int64)
+make_range_impl(types.unsigned_range_state64_type, types.unsigned_range_iter64_type,
+                types.uint64)
diff --git a/numba/types.py b/numba/types.py
--- a/numba/types.py
+++ b/numba/types.py
@@ -1117,8 +1117,11 @@ def is_int_tuple(x):
 
 range_iter32_type = RangeIteratorType('range_iter32', int32)
 range_iter64_type = RangeIteratorType('range_iter64', int64)
+unsigned_range_iter64_type = RangeIteratorType('unsigned_range_iter64', uint64)
 range_state32_type = RangeType('range_state32', range_iter32_type)
 range_state64_type = RangeType('range_state64', range_iter64_type)
+unsigned_range_state64_type = RangeType('unsigned_range_state64',
+                                        unsigned_range_iter64_type)
 
 # slice2_type = Type('slice2_type')
 slice3_type = Slice3Type('slice3_type')
diff --git a/numba/typing/builtins.py b/numba/typing/builtins.py
--- a/numba/typing/builtins.py
+++ b/numba/typing/builtins.py
@@ -77,6 +77,10 @@ class Range(ConcreteTemplate):
         signature(types.range_state64_type, types.int64, types.int64),
         signature(types.range_state64_type, types.int64, types.int64,
                   types.int64),
+        signature(types.unsigned_range_state64_type, types.uint64),
+        signature(types.unsigned_range_state64_type, types.uint64, types.uint64),
+        signature(types.unsigned_range_state64_type, types.uint64, types.uint64,
+                  types.uint64),
     ]
 
 


Test patch:
diff --git a/numba/tests/test_range.py b/numba/tests/test_range.py
--- a/numba/tests/test_range.py
+++ b/numba/tests/test_range.py
@@ -1,5 +1,6 @@
 from __future__ import print_function
 import numba.unittest_support as unittest
+import numpy
 from numba.compiler import compile_isolated
 from numba import types
 
@@ -25,6 +26,30 @@ def loop3(a, b, c):
     return s
 
 
+def range1_writeout(n, out):
+    i = 0
+    for j in range(n):
+        out[i] = j
+        i += 1
+    return i
+
+
+def range2_writeout(a, b, out):
+    i = 0
+    for j in range(a, b):
+        out[i] = j
+        i += 1
+    return i
+
+
+def range3_writeout(a, b, c, out):
+    i = 0
+    for j in range(a, b, c):
+        out[i] = j
+        i += 1
+    return i
+
+
 class TestRange(unittest.TestCase):
     def test_loop1_int16(self):
         pyfunc = loop1
@@ -51,6 +76,78 @@ def test_loop3_int32(self):
         for args in arglist:
             self.assertEqual(cfunc(*args), pyfunc(*args))
 
+    def test_range1_writeout_uint64(self):
+        pyfunc = range1_writeout
+        cres = compile_isolated(pyfunc, [types.uint64, types.int64[:]])
+        cfunc = cres.entry_point
+        n = 50
+        expected = numpy.zeros(n, dtype=numpy.int64)
+        got = numpy.zeros(n, dtype=numpy.int64)
+        self.assertTrue(cfunc(n, got), pyfunc(n, expected))
+        numpy.testing.assert_equal(expected, got)
+
+    def test_range2_writeout_uint64(self):
+        pyfunc = range2_writeout
+        cres = compile_isolated(pyfunc, [types.uint64, types.uint64,
+                                         types.int64[:]])
+        cfunc = cres.entry_point
+        a, b = 2552764644, 2552764787
+        expected = numpy.zeros(b - a, dtype=numpy.int64)
+        got = numpy.zeros(b - a, dtype=numpy.int64)
+        self.assertTrue(cfunc(a, b, got), pyfunc(a, b, expected))
+        numpy.testing.assert_equal(expected, got)
+
+    def test_range3_writeout_uint64(self):
+        pyfunc = range3_writeout
+        cres = compile_isolated(pyfunc, [types.uint64, types.uint64,
+                                         types.uint64, types.int64[:]])
+        cfunc = cres.entry_point
+        a, b = 2552764644, 2552764787
+        c = 1
+        expected = numpy.zeros(b - a, dtype=numpy.int64)
+        got = numpy.zeros(b - a, dtype=numpy.int64)
+        self.assertTrue(cfunc(a, b, c, got), pyfunc(a, b, c, expected))
+        numpy.testing.assert_equal(expected, got)
+
+    def test_range1_writeout_uint32(self):
+        pyfunc = range1_writeout
+        cres = compile_isolated(pyfunc, [types.uint32, types.int64[:]])
+        cfunc = cres.entry_point
+        n = 50
+        expected = numpy.zeros(n, dtype=numpy.int64)
+        got = numpy.zeros(n, dtype=numpy.int64)
+        self.assertTrue(cfunc(n, got), pyfunc(n, expected))
+        numpy.testing.assert_equal(expected, got)
+
+    def test_range2_writeout_uint32(self):
+        pyfunc = range2_writeout
+        cres = compile_isolated(pyfunc, [types.uint32, types.uint32,
+                                         types.int64[:]])
+        cfunc = cres.entry_point
+        # Test with integer that used the MSB of uint32
+        a, b = 0xF0000001, 0xF0000010
+        self.assertEqual(a >> 31, 1)
+        self.assertEqual(b >> 31, 1)
+        expected = numpy.zeros(b - a, dtype=numpy.int64)
+        got = numpy.zeros(b - a, dtype=numpy.int64)
+        self.assertTrue(cfunc(a, b, got), pyfunc(a, b, expected))
+        numpy.testing.assert_equal(expected, got)
+
+    def test_range3_writeout_uint32(self):
+        pyfunc = range3_writeout
+        cres = compile_isolated(pyfunc, [types.uint32, types.uint32,
+                                         types.uint32, types.int64[:]])
+        cfunc = cres.entry_point
+        # Test with integer that used the MSB of uint32
+        a, b = 0xF0000001, 0xF0000010
+        self.assertEqual(a >> 31, 1)
+        self.assertEqual(b >> 31, 1)
+        c = 1
+        expected = numpy.zeros(b - a, dtype=numpy.int64)
+        got = numpy.zeros(b - a, dtype=numpy.int64)
+        self.assertTrue(cfunc(a, b, c, got), pyfunc(a, b, c, expected))
+        numpy.testing.assert_equal(expected, got)
+
 
 if __name__ == '__main__':
     unittest.main()