Repository: numba/numba

Issue:
empty_inferred typing error
<!--

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!

-->

Type inference doesn't work for this code. Any idea why?

```python
import numba

@numba.njit
def f():
    S = numba.unsafe.ndarray.empty_inferred((10,))
    a = 1.1
    for i in range(len(S)):
        S[i] = a+2
    return S

f()
```
```
numba.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Cannot infer the type of variable 'S', have imprecise type: array(undefined, 1d, C).

File "test_rm_emptyinferred.py", line 9:
def f():
    S = numba.unsafe.ndarray.empty_inferred((10,))
    ^
```
empty_inferred typing error
<!--

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!

-->

Type inference doesn't work for this code. Any idea why?

```python
import numba

@numba.njit
def f():
    S = numba.unsafe.ndarray.empty_inferred((10,))
    a = 1.1
    for i in range(len(S)):
        S[i] = a+2
    return S

f()
```
```
numba.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Cannot infer the type of variable 'S', have imprecise type: array(undefined, 1d, C).

File "test_rm_emptyinferred.py", line 9:
def f():
    S = numba.unsafe.ndarray.empty_inferred((10,))
    ^
```


Discussion:
I found that the issue is due to this part:
```python
    for i in range(len(S)):
        S[i] = a+2
```

Where `S[i] = a+2` would "refine" the type for `S` but the typing cannot occur because it cannot deduce the type of `i`.  The chain goes like:

```
typeof i -> typeof range(len(S)) -> typeof len(S)
```
`len(S)` cannot be deduced.  However, if the loop is written as:

```python
    for i in range(10):
        S[i] = a+2
```
the function will and should work.
`range(S.size)` also works
I found that the issue is due to this part:
```python
    for i in range(len(S)):
        S[i] = a+2
```

Where `S[i] = a+2` would "refine" the type for `S` but the typing cannot occur because it cannot deduce the type of `i`.  The chain goes like:

```
typeof i -> typeof range(len(S)) -> typeof len(S)
```
`len(S)` cannot be deduced.  However, if the loop is written as:

```python
    for i in range(10):
        S[i] = a+2
```
the function will and should work.
`range(S.size)` also works

Resolving patch:
diff --git a/numba/typeinfer.py b/numba/typeinfer.py
--- a/numba/typeinfer.py
+++ b/numba/typeinfer.py
@@ -433,14 +433,9 @@ def resolve(self, typeinfer, typevars, fnty):
 
         # Check argument to be precise
         for a in itertools.chain(pos_args, kw_args.values()):
-            if not a.is_precise():
-                # Getitem on non-precise array is allowed to
-                # support array-comprehension
-                if fnty == operator.getitem and isinstance(pos_args[0], types.Array):
-                    pass
-                # Otherwise, don't compute type yet
-                else:
-                    return
+            # Forbids imprecise type except array of undefined dtype
+            if not a.is_precise() and not isinstance(a, types.Array):
+                return
 
         # Resolve call type
         sig = typeinfer.resolve_call(fnty, pos_args, kw_args)


Test patch:
diff --git a/numba/tests/test_unsafe_intrinsics.py b/numba/tests/test_unsafe_intrinsics.py
--- a/numba/tests/test_unsafe_intrinsics.py
+++ b/numba/tests/test_unsafe_intrinsics.py
@@ -7,7 +7,7 @@
 from .support import TestCase
 from numba import njit
 from numba.unsafe.tuple import tuple_setitem
-from numba.unsafe.ndarray import to_fixed_tuple
+from numba.unsafe.ndarray import to_fixed_tuple, empty_inferred
 from numba.errors import TypingError
 
 
@@ -83,3 +83,29 @@ def tuple_with_length(array, length):
             tuple_with_length(np.random.random(3), 1)
         expectmsg = "*length* argument must be a constant"
         self.assertIn(expectmsg, str(raises.exception))
+
+    def test_issue_3586_variant1(self):
+        @njit
+        def func():
+            S = empty_inferred((10,))
+            a = 1.1
+            for i in range(len(S)):
+                S[i] = a + 2
+            return S
+
+        got = func()
+        expect = np.asarray([3.1] * 10)
+        np.testing.assert_array_equal(got, expect)
+
+    def test_issue_3586_variant2(self):
+        @njit
+        def func():
+            S = empty_inferred((10,))
+            a = 1.1
+            for i in range(S.size):
+                S[i] = a + 2
+            return S
+
+        got = func()
+        expect = np.asarray([3.1] * 10)
+        np.testing.assert_array_equal(got, expect)