Repository: numba/numba

Issue:
presence of while loop breaks literal_unroll compilation
I reproduce this issue by running on numba 0.56.0 on google colab.

I cannot understand why the presence of a while loop later in the block creates a `getitem` problem for literal_unroll on heterogenous tuples.

```python
from numba import njit,literal_unroll
@njit
def f1( t ):
    for t_i in literal_unroll(t):
        print('.')

    return 0

@njit
def f2( t ):
    for t_i in literal_unroll(t):
        print('.')

    x=0
    while x>0:
        x= x-1
    
    return 0

print('f1')
f1( (0,0.0) ) # works
print('f2')
f2( (0,0.0) ) # fails
```

The unexpected compile fail for `f2` is the following:

```
TypingError: Failed in nopython mode pipeline (step: nopython frontend)
No implementation of function Function(<built-in function getitem>) found for signature:
 
 >>> getitem(Tuple(int64, float64), int64)
 
There are 22 candidate implementations:
      - Of which 22 did not match due to:
      Overload of function 'getitem': File: <numerous>: Line N/A.
        With argument(s): '(Tuple(int64, float64), int64)':
       No match.

During: typing of intrinsic-call at C:\Users\pconlon\AppData\Local\Temp/ipykernel_18736/1219060107.py (11)

File "..\..\..\..\..\AppData\Local\Temp\ipykernel_18736\1219060107.py", line 11:
<source missing, REPL/exec in use?>
```
<!--

Before submitting a bug report please ensure that you can check off these boxes:

-->

- [X] I have tried using the latest released version of Numba (most recent is
 visible in the change log (https://github.com/numba/numba/blob/main/CHANGE_LOG).
- [X] I have included a self contained code sample to reproduce the problem.
  i.e. it's possible to run as 'python bug.py'.

<!--

Please include details of the bug here, including, if applicable, what you
expected to happen!

-->

What I expect to happen:
- the f2 should compiles just like f1 and does not have a compilation failure



Discussion:
Thanks for the report - I can reproduce the compilation failure.
A smaller reproducer:

```python
from numba import njit, literal_unroll

@njit('(Tuple((int64, float64)),)')
def f2(t):
    for t_i in literal_unroll(t):
        pass

    while True:
        x = 0
```

Resolving patch:
diff --git a/numba/core/untyped_passes.py b/numba/core/untyped_passes.py
--- a/numba/core/untyped_passes.py
+++ b/numba/core/untyped_passes.py
@@ -1043,8 +1043,8 @@ def get_call_args(init_arg, want):
             # scan loop header
             iternexts = [_ for _ in
                          func_ir.blocks[loop.header].find_exprs('iternext')]
-            if len(iternexts) != 1:
-                return False
+            if len(iternexts) != 1: # needs to be an single iternext driven loop
+                continue
             for iternext in iternexts:
                 # Walk the canonicalised loop structure and check it
                 # Check loop form range(literal_unroll(container)))


Test patch:
diff --git a/numba/tests/test_mixed_tuple_unroller.py b/numba/tests/test_mixed_tuple_unroller.py
--- a/numba/tests/test_mixed_tuple_unroller.py
+++ b/numba/tests/test_mixed_tuple_unroller.py
@@ -1818,6 +1818,18 @@ def foo():
 
         self.assertEqual(foo(), foo.py_func())
 
+    def test_unroll_with_non_conformant_loops_present(self):
+        # See issue #8311
+
+        @njit('(Tuple((int64, float64)),)')
+        def foo(tup):
+            for t in literal_unroll(tup):
+                pass
+
+            x = 1
+            while x == 1:
+                x = 0
+
 
 def capture(real_pass):
     """ Returns a compiler pass that captures the mutation state reported