Repository: numba/numba

Issue:
Prange incorrectly compute `ifloordiv` reduction
MWR:
```python
from numba import njit, prange
import numpy as np

@njit(parallel=True)
def foo():
    acc = 10000
    for i in prange(4):
        acc //= 2
    return acc

print(foo())
print(foo.py_func())
```

_Originally posted by @stuartarchibald in https://github.com/numba/numba/pull/7837#discussion_r812944548_


Discussion:
CC @DrTodd13

Resolving patch:
diff --git a/numba/parfors/parfor.py b/numba/parfors/parfor.py
--- a/numba/parfors/parfor.py
+++ b/numba/parfors/parfor.py
@@ -3611,14 +3611,27 @@ def get_reduction_init(nodes):
     if acc_expr.fn == operator.iadd or acc_expr.fn == operator.isub:
         return 0, acc_expr.fn
     if (  acc_expr.fn == operator.imul
-       or acc_expr.fn == operator.itruediv
-       or acc_expr.fn == operator.ifloordiv ):
+       or acc_expr.fn == operator.itruediv ):
         return 1, acc_expr.fn
     return None, None
 
 def supported_reduction(x, func_ir):
     if x.op == 'inplace_binop' or x.op == 'binop':
-        return True
+        if x.fn == operator.ifloordiv or x.fn == operator.floordiv:
+            raise errors.NumbaValueError(("Parallel floordiv reductions are not supported. "
+                              "If all divisors are integers then a floordiv "
+                              "reduction can in some cases be parallelized as "
+                              "a multiply reduction followed by a floordiv of "
+                              "the resulting product."), x.loc)
+        supps = [operator.iadd,
+                 operator.isub,
+                 operator.imul,
+                 operator.itruediv,
+                 operator.add,
+                 operator.sub,
+                 operator.mul,
+                 operator.truediv]
+        return x.fn in supps
     if x.op == 'call':
         callname = guard(find_callname, func_ir, x)
         if callname == ('max', 'builtins') or callname == ('min', 'builtins'):


Test patch:
diff --git a/numba/tests/test_parfors.py b/numba/tests/test_parfors.py
--- a/numba/tests/test_parfors.py
+++ b/numba/tests/test_parfors.py
@@ -1395,6 +1395,35 @@ def test_impl():
             pcfunc = self.compile_parallel(test_impl, ())
         self.assertIn(msg, str(e.exception))
 
+    def test_unsupported_floordiv1(self):
+        def test_impl():
+            acc = 100
+            for i in prange(2):
+                acc //= 2
+            return acc
+
+        # checks that invalid use of ifloordiv reduction operator is detected
+        msg = ("Parallel floordiv reductions are not supported. "
+               "If all divisors are integers then a floordiv "
+               "reduction can in some cases be parallelized as "
+               "a multiply reduction followed by a floordiv of "
+               "the resulting product.")
+        with self.assertRaises(errors.NumbaValueError) as e:
+            pcfunc = self.compile_parallel(test_impl, ())
+        self.assertIn(msg, str(e.exception))
+
+    def test_unsupported_xor1(self):
+        def test_impl():
+            acc = 100
+            for i in prange(2):
+                acc ^= i + 2
+            return acc
+
+        msg = ("Use of reduction variable acc in an unsupported reduction function.")
+        with self.assertRaises(ValueError) as e:
+            pcfunc = self.compile_parallel(test_impl, ())
+        self.assertIn(msg, str(e.exception))
+
     def test_parfor_array_access1(self):
         # signed index of the prange generated by sum() should be replaced
         # resulting in array A to be eliminated (see issue #2846)