Repository: numba/numba

Issue:
'key already in dictionary' while analyzing bytecode
<!--

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 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!

-->
I got an error of 
`AssertionError: Failed in nopython mode pipeline (step: analyzing bytecode)`
`key already in dictionary: '$phi22.0'`
when the below code was run
```
import numba as nb
from numba.experimental import jitclass
from collections import OrderedDict

spec = OrderedDict()
spec['member'] = nb.b1

@jitclass(spec)
class NbClass:
    def __init__(self):
        self.member = True

sig = nb.i8(NbClass.class_type.instance_type, NbClass.class_type.instance_type)

@nb.jit(sig, nopython=True)
def func1(ins1, ins2):
    for k in range(10):
        if ins1.member:
            pass
        if ins2.member:
            pass
    return 1

if __name__ == '__main__':
    nb_ins1 = NbClass()
    nb_ins2 = NbClass()
    ret_val = func1(nb_ins1, nb_ins2)
```
The whole error infomation is
```
Traceback (most recent call last):
  File "E:/Proj/Python/numbadebug/n49/mini_rep.py", line 16, in <module>
    @nb.jit(sig, nopython=True)
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\decorators.py", line 198, in wrapper
    disp.compile(sig)
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\compiler_lock.py", line 32, in _acquire_compile_lock
    return func(*args, **kwargs)
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\dispatcher.py", line 794, in compile
    cres = self._compiler.compile(args, return_type)
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\dispatcher.py", line 77, in compile
    status, retval = self._compile_cached(args, return_type)
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\dispatcher.py", line 91, in _compile_cached
    retval = self._compile_core(args, return_type)
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\dispatcher.py", line 109, in _compile_core
    pipeline_class=self.pipeline_class)
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\compiler.py", line 568, in compile_extra
    return pipeline.compile_extra(func)
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\compiler.py", line 339, in compile_extra
    return self._compile_bytecode()
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\compiler.py", line 401, in _compile_bytecode
    return self._compile_core()
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\compiler.py", line 381, in _compile_core
    raise e
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\compiler.py", line 372, in _compile_core
    pm.run(self.state)
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\compiler_machinery.py", line 341, in run
    raise patched_exception
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\compiler_machinery.py", line 332, in run
    self._runPass(idx, pass_inst, state)
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\compiler_lock.py", line 32, in _acquire_compile_lock
    return func(*args, **kwargs)
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\compiler_machinery.py", line 291, in _runPass
    mutated |= check(pss.run_pass, internal_state)
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\compiler_machinery.py", line 264, in check
    mangled = func(compiler_state)
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\untyped_passes.py", line 86, in run_pass
    func_ir = interp.interpret(bc)
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\interpreter.py", line 116, in interpret
    flow.run()
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\byteflow.py", line 137, in run
    out_states = state.get_outgoing_states()
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\byteflow.py", line 1298, in get_outgoing_states
    self._outgoing_phis[phi] = edge.stack[i]
  File "D:\ProgramData\Anaconda3\envs\numba49\lib\site-packages\numba\core\utils.py", line 309, in __setitem__
    raise AssertionError("key already in dictionary: %r" % (key,))
AssertionError: Failed in nopython mode pipeline (step: analyzing bytecode)
key already in dictionary: '$phi22.0'
```
I also tried previous versions, found that version 0.46.0 and previous versions (e.g. 0.44.1) are worked. However, starting from 0.47.0 it raises error.
The main part is not necessary to trigger the error, as the signature of the function is explicitly expressed. If the signature is removed, then the main part is needed to reproduce the error.
I tried my best but didn't find a more concise program to reproduce the error.
Is this an issue easy to deal with?




Discussion:
Thanks for the report, I can reproduce, this is a smaller reproducer:
```python
from numba import njit

@njit
def foo():
    for k in range(0):
        if 1 == 1:
            ...
        if 'a' == 'a':
            ...
foo()
```
cc @sklam 
Seems like having a `break` inside of an `except` block also produces this error: 

```
@numba.njit
def foo():
    for i in range(5):
        try:
            print('main block')
        except Exception:
            print('handler block')
            break
```

```
AssertionError: Failed in nopython mode pipeline (step: analyzing bytecode)
key already in dictionary: 64
```

Resolving patch:
diff --git a/numba/core/byteflow.py b/numba/core/byteflow.py
--- a/numba/core/byteflow.py
+++ b/numba/core/byteflow.py
@@ -605,8 +605,14 @@ def op_BUILD_SLICE(self, state, inst):
     def _op_POP_JUMP_IF(self, state, inst):
         pred = state.pop()
         state.append(inst, pred=pred)
-        state.fork(pc=inst.next)
-        state.fork(pc=inst.get_jump_target())
+
+        target_inst = inst.get_jump_target()
+        next_inst = inst.next
+        # if the next inst and the jump target are the same location, issue one
+        # fork else issue a fork for the next and the target.
+        state.fork(pc=next_inst)
+        if target_inst != next_inst:
+            state.fork(pc=target_inst)
 
     op_POP_JUMP_IF_TRUE = _op_POP_JUMP_IF
     op_POP_JUMP_IF_FALSE = _op_POP_JUMP_IF


Test patch:
diff --git a/numba/tests/test_byteflow.py b/numba/tests/test_byteflow.py
--- a/numba/tests/test_byteflow.py
+++ b/numba/tests/test_byteflow.py
@@ -1,6 +1,8 @@
 """
 Test byteflow.py specific issues
 """
+import unittest
+
 from numba.tests.support import TestCase
 from numba.core.compiler import run_frontend
 
@@ -65,3 +67,28 @@ def udt():
                         print
 
         run_frontend(udt)
+
+    def test_issue_5097(self):
+        # Inspired by https://github.com/numba/numba/issues/5097
+        def udt():
+            for i in range(0):
+                if i > 0:
+                    pass
+                a = None # noqa: F841
+
+        run_frontend(udt)
+
+    def test_issue_5680(self):
+        # From https://github.com/numba/numba/issues/5680#issuecomment-625351336
+        def udt():
+            for k in range(0):
+                if 1 == 1:
+                    ...
+                if 'a' == 'a':
+                    ...
+
+        run_frontend(udt)
+
+
+if __name__ == '__main__':
+    unittest.main()