Repository: numba/numba

Issue:
Array analysis fails for slice objects
<!--

Thanks for opening an issue! To help the Numba team handle your information
efficiently, please first ensure that there is no other issue present that
already describes the issue you have
(search at https://github.com/numba/numba/issues?&q=is%3Aissue).

For more general "how do I do X?" type questions, please speak to us in real
time on https://gitter.im/numba/numba or post to the Numba mailing list
https://groups.google.com/a/continuum.io/forum/#!forum/numba-users.

-->

## Reporting a bug

<!--

Before submitting a bug report please ensure that you can check off these boxes:

-->

- [x] I am using the latest ~~released~~ master version of Numba (most recent is visible in
 the change log (https://github.com/numba/numba/blob/master/CHANGE_LOG).
- [x] I have included below a minimal working reproducer (if you are unsure how
 to write one see http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports).

<!--

Please include details of the bug here, including, if applicable, what you
expected to happen!

-->
Array analysis fails for standalone slice objects for some reason. Needs investigation.

```python
@numba.njit(parallel=True)
def f():
    a = slice(None, None)
```
~/pse-hpc/python/numba/numba/array_analysis.py in set_shape(self, obj, shape)
    810             assert(isinstance(obj, ir.Var))
    811             typ = self.typemap[obj.name]
--> 812             super(SymbolicEquivSet, self).set_shape(obj, shape)
    813
    814     def _get_shape(self, obj):
AttributeError: Failed in nopython mode pipeline (step: convert to parfors)
'super' object has no attribute 'set_shape'
```

```


Discussion:
Seems like there is indeed no `set_shape` in the class super.
https://github.com/numba/numba/blob/3618091896cb54c6b8d442c290cfe5f9480f6e89/numba/array_analysis.py#L809-L812

probably need something similar to the `_get_shape` just below it to handle the slice?
I ran into this issue again. Slice objects seem problematic in general in array analysis:
```python
import numba
import numpy as np

@numba.njit(parallel=True)
def f(A, a):
    b = a
    return A[b]

f(np.ones(3), slice(None))
```
```
  File "/home/ehsan/dev/numba/numba/compiler.py", line 245, in run
    stage()
  File "/home/ehsan/dev/numba/numba/compiler.py", line 565, in stage_parfor_pass
    parfor_pass.run()
  File "/home/ehsan/dev/numba/numba/parfor.py", line 1448, in run
    self.array_analysis.run(self.func_ir.blocks)
  File "/home/ehsan/dev/numba/numba/array_analysis.py", line 916, in run
    pre, post = self._analyze_inst(label, scope, equiv_set, inst)
  File "/home/ehsan/dev/numba/numba/array_analysis.py", line 985, in _analyze_inst
    equiv_set.set_shape(lhs, shape)
  File "/home/ehsan/dev/numba/numba/array_analysis.py", line 811, in set_shape
    super(SymbolicEquivSet, self).set_shape(obj, shape)
AttributeError: Failed in nopython mode pipeline (step: convert to parfors)
'super' object has no attribute 'set_shape'
```

Resolving patch:
diff --git a/numba/array_analysis.py b/numba/array_analysis.py
--- a/numba/array_analysis.py
+++ b/numba/array_analysis.py
@@ -800,15 +800,11 @@ def get_or_set(d, k):
         for names in offset_dict.values():
             self._insert(names)
 
-    def set_shape(self, obj, shape):
-        """Overload set_shape to remember shapes of SetItem IR nodes.
+    def set_shape_setitem(self, obj, shape):
+        """remember shapes of SetItem IR nodes.
         """
-        if isinstance(obj, ir.StaticSetItem) or isinstance(obj, ir.SetItem):
-            self.ext_shapes[obj] = shape
-        else:
-            assert(isinstance(obj, ir.Var))
-            typ = self.typemap[obj.name]
-            super(SymbolicEquivSet, self).set_shape(obj, shape)
+        assert isinstance(obj, ir.StaticSetItem) or isinstance(obj, ir.SetItem)
+        self.ext_shapes[obj] = shape
 
     def _get_shape(self, obj):
         """Overload _get_shape to retrieve the shape of SetItem IR nodes.
@@ -981,10 +977,7 @@ def _analyze_inst(self, label, scope, equiv_set, inst):
                                                          len(typ), shape)
 
             if shape != None:
-                if isinstance(typ, types.SliceType):
-                    equiv_set.set_shape(lhs, shape)
-                else:
-                    equiv_set.insert_equiv(lhs, shape)
+                equiv_set.insert_equiv(lhs, shape)
             equiv_set.define(lhs, self.func_ir, typ)
         elif isinstance(inst, ir.StaticSetItem) or isinstance(inst, ir.SetItem):
             index = inst.index if isinstance(inst, ir.SetItem) else inst.index_var
@@ -995,7 +988,7 @@ def _analyze_inst(self, label, scope, equiv_set, inst):
             (target_shape, pre) = result
             value_shape = equiv_set.get_shape(inst.value)
             if value_shape is (): # constant
-                equiv_set.set_shape(inst, target_shape)
+                equiv_set.set_shape_setitem(inst, target_shape)
                 return pre, []
             elif value_shape != None:
                 target_typ = self.typemap[inst.target.name]
@@ -1008,7 +1001,7 @@ def _analyze_inst(self, label, scope, equiv_set, inst):
                 n = len(shape)
                 # shape dimension must be within target dimension
                 assert(target_ndim >= n)
-                equiv_set.set_shape(inst, shape)
+                equiv_set.set_shape_setitem(inst, shape)
                 return pre + asserts, []
             else:
                 return pre, []


Test patch:
diff --git a/numba/tests/test_array_analysis.py b/numba/tests/test_array_analysis.py
--- a/numba/tests/test_array_analysis.py
+++ b/numba/tests/test_array_analysis.py
@@ -929,5 +929,24 @@ def test_impl(t_obj, X):
         self.assertEqual(
             njit(test_impl, parallel=True)(t_obj, X1), test_impl(t_obj, X2))
 
+    @skip_unsupported
+    def test_slice_shape_issue_3380(self):
+        # these tests shouldn't throw error in array analysis
+        def test_impl1():
+            a = slice(None, None)
+            return True
+
+        self.assertEqual(njit(test_impl1, parallel=True)(), test_impl1())
+
+        def test_impl2(A, a):
+            b = a
+            return A[b]
+
+        A = np.arange(10)
+        a = slice(None)
+        np.testing.assert_array_equal(
+            njit(test_impl2, parallel=True)(A, a), test_impl2(A, a))
+
+
 if __name__ == '__main__':
     unittest.main()