Repository: numba/numba

Issue:
Test failure on 32-bit Linux
```
======================================================================
FAIL: test_prod_magnitude (__main__.TestArrayMethods)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/antoine/numba/numba/tests/support.py", line 140, in assertPreciseEqual
    self._assertPreciseEqual(first, second, prec, ulps, msg)
  File "/home/antoine/numba/numba/tests/support.py", line 228, in _assertPreciseEqual
    _assertNumberEqual(first, second, delta)
  File "/home/antoine/numba/numba/tests/support.py", line 158, in _assertNumberEqual
    self.assertEqual(first, second, msg=msg)
AssertionError: -1373712384 != -4696715432980197376

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/antoine/numba/numba/tests/test_array_methods.py", line 306, in test_prod_magnitude
    self.check_aggregation_magnitude(array_prod)
  File "/home/antoine/numba/numba/tests/test_array_methods.py", line 295, in check_aggregation_magnitude
    self.assertPreciseEqual(npr, nbr)
  File "/home/antoine/numba/numba/tests/support.py", line 145, in assertPreciseEqual
    self.fail("when comparing %s and %s: %s" % (first, second, failure_msg))
AssertionError: when comparing -1373712384 and -4696715432980197376: -1373712384 != -4696715432980197376
```



Discussion:
Two things:
- Numpy's `array.prod` (and other aggregation functions) returns int32 on 32-bit machines, not int64
- LLVM may be generating wrong code here (16- to 64-bit sign extension followed by signed 64-bit multiplication)?

Error also occurs on win-32 and win-64:
https://alpha.binstar.org/antoine/numba/builds/47/2
https://alpha.binstar.org/antoine/numba/builds/47/3


Resolving patch:
diff --git a/numba/typing/builtins.py b/numba/typing/builtins.py
--- a/numba/typing/builtins.py
+++ b/numba/typing/builtins.py
@@ -487,10 +487,11 @@ def generic_homog(self, args, kws):
 
 def generic_expand(self, args, kws):
     if isinstance(self.this.dtype, types.Integer):
+        # Expand to a machine int, not larger (like Numpy)
         if self.this.dtype.signed:
-            return signature(types.int64, recvr=self.this)
+            return signature(max(types.intp, self.this.dtype), recvr=self.this)
         else:
-            return signature(types.uint64, recvr=self.this)
+            return signature(max(types.uintp, self.this.dtype), recvr=self.this)
     return signature(self.this.dtype, recvr=self.this)
 
 def generic_hetero_real(self, args, kws):
@@ -518,7 +519,7 @@ def array_attribute_attachment(self, ary):
 for fname in ["min", "max"]:
     install_array_method(fname, generic_homog)
 
-# Functions that return a type as large as possible, to avoid overflows
+# Functions that return a machine-width type, to avoid overflows
 for fname in ["sum", "prod"]:
     install_array_method(fname, generic_expand)
 
diff --git a/numba/typing/npydecl.py b/numba/typing/npydecl.py
--- a/numba/typing/npydecl.py
+++ b/numba/typing/npydecl.py
@@ -197,10 +197,11 @@ def generic(self, args, kws):
         assert not kws
         [arr] = args
         if isinstance(arr.dtype, types.Integer):
+            # Expand to a machine int, not larger (like Numpy)
             if arr.dtype.signed:
-                return signature(types.int64, arr)
+                return signature(max(arr.dtype, types.intp), arr)
             else:
-                return signature(types.uint64, arr)
+                return signature(max(arr.dtype, types.uintp), arr)
         else:
             return signature(arr.dtype, arr)
 


Test patch:
diff --git a/numba/tests/test_array_methods.py b/numba/tests/test_array_methods.py
--- a/numba/tests/test_array_methods.py
+++ b/numba/tests/test_array_methods.py
@@ -284,17 +284,22 @@ def test_array_prod_global(self):
 
         np.testing.assert_allclose(np.prod(arr), cfunc(arr))
 
-    def check_aggregation_magnitude(self, pyfunc):
+    def check_aggregation_magnitude(self, pyfunc, is_prod=False):
         """
         Check that integer overflows are avoided (issue #931).
         """
-        # Overflows are always avoided here (ints are cast either to int64
+        # Overflows are avoided here (ints are cast either to intp
         # or float64).
-        arr = np.arange(10, dtype='int16') + 60000
+        n_items = 2 if is_prod else 10  # avoid overflow on prod()
+        arr = (np.arange(n_items) + 40000).astype('int16')
         npr, nbr = run_comparative(pyfunc, arr)
         self.assertPreciseEqual(npr, nbr)
         # Overflows are avoided for functions returning floats here.
-        arr = np.arange(10, dtype='int64') + 2**63
+        # Other functions may wrap around.
+        arr = (np.arange(10) + 2**60).astype('int64')
+        npr, nbr = run_comparative(pyfunc, arr)
+        self.assertPreciseEqual(npr, nbr)
+        arr = arr.astype('uint64')
         npr, nbr = run_comparative(pyfunc, arr)
         self.assertPreciseEqual(npr, nbr)
 
@@ -303,8 +308,8 @@ def test_sum_magnitude(self):
         self.check_aggregation_magnitude(array_sum_global)
 
     def test_prod_magnitude(self):
-        self.check_aggregation_magnitude(array_prod)
-        self.check_aggregation_magnitude(array_prod_global)
+        self.check_aggregation_magnitude(array_prod, is_prod=True)
+        self.check_aggregation_magnitude(array_prod_global, is_prod=True)
 
     def test_mean_magnitude(self):
         self.check_aggregation_magnitude(array_mean)