Repository: numba/numba

Issue:
Catch unsupported jump-like bytecodes
<!--

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).

From here https://stackoverflow.com/questions/55428418/

```python
from numba import jit
@jit(nopython=True)
def foo():
    try:
        pass
    except:
        pass

foo()
```
does this:
```
  File "<path>/numba/numba/controlflow.py", line 518, in run
    assert not inst.is_jump, inst
AssertionError: Failed in nopython mode pipeline (step: analyzing bytecode)
SETUP_EXCEPT(arg=4, lineno=4)
```


Discussion:

Resolving patch:
diff --git a/numba/controlflow.py b/numba/controlflow.py
--- a/numba/controlflow.py
+++ b/numba/controlflow.py
@@ -5,7 +5,8 @@
 import sys
 
 from numba import utils
-
+from numba.ir import Loc
+from numba.errors import UnsupportedError
 
 # List of bytecodes creating a new block in the control flow graph
 # (in addition to explicit jump labels).
@@ -33,8 +34,8 @@ def __iter__(self):
         return iter(self.body)
 
 
-class Loop(
-    collections.namedtuple("Loop", ("entries", "exits", "header", "body"))):
+class Loop(collections.namedtuple("Loop",
+                                  ("entries", "exits", "header", "body"))):
     """
     A control flow loop, as detected by a CFGraph object.
     """
@@ -515,7 +516,11 @@ def run(self):
             if fn is not None:
                 fn(inst)
             else:
-                assert not inst.is_jump, inst
+                # this catches e.g. try... except
+                if inst.is_jump:
+                    l = Loc(self.bytecode.func_id.filename, inst.lineno)
+                    msg = "Use of unsupported opcode (%s) found" % inst.opname
+                    raise UnsupportedError(msg, loc=l)
 
         # Close all blocks
         for cur, nxt in zip(self.blockseq, self.blockseq[1:]):


Test patch:
diff --git a/numba/tests/test_flow_control.py b/numba/tests/test_flow_control.py
--- a/numba/tests/test_flow_control.py
+++ b/numba/tests/test_flow_control.py
@@ -3,9 +3,9 @@
 import itertools
 
 import numba.unittest_support as unittest
-from numba.controlflow import CFGraph, Loop
+from numba.controlflow import CFGraph
 from numba.compiler import compile_isolated, Flags
-from numba import types
+from numba import types, errors
 from .support import TestCase, tag
 
 enable_pyobj_flags = Flags()
@@ -24,12 +24,14 @@ def for_loop_usecase1(x, y):
         result += i
     return result
 
+
 def for_loop_usecase2(x, y):
     result = 0
     for i, j in enumerate(range(x, y, -1)):
         result += i * j
     return result
 
+
 def for_loop_usecase3(x, y):
     result = 0
     lst = [x, y]
@@ -37,6 +39,7 @@ def for_loop_usecase3(x, y):
         result += i
     return result
 
+
 def for_loop_usecase4(x, y):
     result = 0
     for i in range(10):
@@ -44,6 +47,7 @@ def for_loop_usecase4(x, y):
             result += 1
     return result
 
+
 def for_loop_usecase5(x, y):
     result = 0
     for i in range(x):
@@ -52,6 +56,7 @@ def for_loop_usecase5(x, y):
             break
     return result
 
+
 def for_loop_usecase6(x, y):
     result = 0
     for i in range(x):
@@ -60,6 +65,7 @@ def for_loop_usecase6(x, y):
         result += 1
     return result
 
+
 def for_loop_usecase7(x, y):
     for i in range(x):
         x = 0
@@ -69,12 +75,14 @@ def for_loop_usecase7(x, y):
             pass
     return 0
 
+
 def for_loop_usecase8(x, y):
     result = 0
     for i in range(x, y, y - x + 1):
         result += 1
     return result
 
+
 def for_loop_usecase9(x, y):
     z = 0
     for i in range(x):
@@ -88,6 +96,7 @@ def for_loop_usecase9(x, y):
 
     return z
 
+
 def for_loop_usecase10(x, y):
     for i in range(x):
         if i == y:
@@ -106,12 +115,14 @@ def while_loop_usecase1(x, y):
         i += 1
     return result
 
+
 def while_loop_usecase2(x, y):
     result = 0
     while result != x:
         result += 1
     return result
 
+
 def while_loop_usecase3(x, y):
     result = 0
     i = 0
@@ -123,6 +134,7 @@ def while_loop_usecase3(x, y):
             j += 1
     return result
 
+
 def while_loop_usecase4(x, y):
     result = 0
     while True:
@@ -131,6 +143,7 @@ def while_loop_usecase4(x, y):
             break
     return result
 
+
 def while_loop_usecase5(x, y):
     result = 0
     while result < x:
@@ -140,6 +153,7 @@ def while_loop_usecase5(x, y):
         result += 1
     return result
 
+
 def ifelse_usecase1(x, y):
     if x > 0:
         pass
@@ -149,6 +163,7 @@ def ifelse_usecase1(x, y):
         pass
     return True
 
+
 def ifelse_usecase2(x, y):
     if x > y:
         return 1
@@ -157,6 +172,7 @@ def ifelse_usecase2(x, y):
     else:
         return 3
 
+
 def ifelse_usecase3(x, y):
     if x > 0:
         if y > 0:
@@ -170,6 +186,7 @@ def ifelse_usecase3(x, y):
     else:
         return 0
 
+
 def ifelse_usecase4(x, y):
     if x == y:
         return 1
@@ -194,6 +211,14 @@ def double_infinite_loop(x, y):
 
     return i, L
 
+
+def try_except_usecase():
+    try:
+        pass
+    except Exception:
+        pass
+
+
 class TestFlowControl(TestCase):
 
     def run_test(self, pyfunc, x_operands, y_operands,
@@ -370,6 +395,14 @@ def test_double_infinite_loop(self, flags=enable_pyobj_flags):
     def test_double_infinite_loop_npm(self):
         self.test_double_infinite_loop(flags=no_pyobj_flags)
 
+    def test_try_except_raises(self):
+        pyfunc = try_except_usecase
+        for f in [no_pyobj_flags, enable_pyobj_flags]:
+            with self.assertRaises(errors.UnsupportedError) as e:
+                compile_isolated(pyfunc, (), flags=f)
+            msg = "Use of unsupported opcode (SETUP_EXCEPT) found"
+            self.assertIn(msg, str(e.exception))
+
 
 class TestCFGraph(TestCase):
     """
@@ -454,25 +487,24 @@ def multiple_loops(self):
                 if z:
                     return ...
         """
-        g = self.from_adj_list(
-            {0: [7],
-             7: [10, 60],
-             10: [13],
-             13: [20],
-             20: [56, 23],
-             23: [32, 44],
-             32: [20],
-             44: [20],
-             56: [57],
-             57: [7],
-             60: [61],
-             61: [68],
-             68: [87, 71],
-             71: [80, 68],
-             80: [],
-             87: [88],
-             88: []}
-            )
+        g = self.from_adj_list({0: [7],
+                                7: [10, 60],
+                                10: [13],
+                                13: [20],
+                                20: [56, 23],
+                                23: [32, 44],
+                                32: [20],
+                                44: [20],
+                                56: [57],
+                                57: [7],
+                                60: [61],
+                                61: [68],
+                                68: [87, 71],
+                                71: [80, 68],
+                                80: [],
+                                87: [88],
+                                88: []}
+                               )
         g.set_entry_point(0)
         g.process()
         return g
@@ -670,46 +702,46 @@ def test_dominators_loops(self):
         g = self.multiple_exits()
         doms = g.dominators()
         self.check_dominators(doms,
-            {0: [0],
-             7: [0, 7],
-             10: [0, 7, 10],
-             19: [0, 7, 10, 19],
-             23: [0, 7, 10, 23],
-             29: [0, 7, 10, 23, 29],
-             36: [0, 7, 36],
-             37: [0, 7, 37],
-             })
+                              {0: [0],
+                               7: [0, 7],
+                               10: [0, 7, 10],
+                               19: [0, 7, 10, 19],
+                               23: [0, 7, 10, 23],
+                               29: [0, 7, 10, 23, 29],
+                               36: [0, 7, 36],
+                               37: [0, 7, 37],
+                               })
         g = self.multiple_loops()
         doms = g.dominators()
         self.check_dominators(doms,
-            {0: [0],
-             7: [0, 7],
-             10: [0, 10, 7],
-             13: [0, 10, 13, 7],
-             20: [0, 10, 20, 13, 7],
-             23: [0, 20, 23, 7, 10, 13],
-             32: [32, 0, 20, 23, 7, 10, 13],
-             44: [0, 20, 23, 7, 10, 44, 13],
-             56: [0, 20, 7, 56, 10, 13],
-             57: [0, 20, 7, 56, 57, 10, 13],
-             60: [0, 60, 7],
-             61: [0, 60, 61, 7],
-             68: [0, 68, 60, 61, 7],
-             71: [0, 68, 71, 7, 60, 61],
-             80: [80, 0, 68, 71, 7, 60, 61],
-             87: [0, 68, 87, 7, 60, 61],
-             88: [0, 68, 87, 88, 7, 60, 61]
-             })
+                              {0: [0],
+                               7: [0, 7],
+                               10: [0, 10, 7],
+                               13: [0, 10, 13, 7],
+                               20: [0, 10, 20, 13, 7],
+                               23: [0, 20, 23, 7, 10, 13],
+                               32: [32, 0, 20, 23, 7, 10, 13],
+                               44: [0, 20, 23, 7, 10, 44, 13],
+                               56: [0, 20, 7, 56, 10, 13],
+                               57: [0, 20, 7, 56, 57, 10, 13],
+                               60: [0, 60, 7],
+                               61: [0, 60, 61, 7],
+                               68: [0, 68, 60, 61, 7],
+                               71: [0, 68, 71, 7, 60, 61],
+                               80: [80, 0, 68, 71, 7, 60, 61],
+                               87: [0, 68, 87, 7, 60, 61],
+                               88: [0, 68, 87, 88, 7, 60, 61]
+                               })
         g = self.infinite_loop1()
         doms = g.dominators()
         self.check_dominators(doms,
-            {0: [0],
-             6: [0, 6],
-             10: [0, 10],
-             13: [0, 10, 13],
-             19: [0, 10, 19, 13],
-             26: [0, 10, 13, 26],
-             })
+                              {0: [0],
+                               6: [0, 6],
+                               10: [0, 10],
+                               13: [0, 10, 13],
+                               19: [0, 10, 19, 13],
+                               26: [0, 10, 13, 26],
+                               })
 
     def test_post_dominators_loopless(self):
         def eq_(d, l):
@@ -733,36 +765,36 @@ def test_post_dominators_loops(self):
         g = self.multiple_exits()
         doms = g.post_dominators()
         self.check_dominators(doms,
-            {0: [0, 7],
-             7: [7],
-             10: [10],
-             19: [19],
-             23: [23],
-             29: [29, 37],
-             36: [36, 37],
-             37: [37],
-            })
+                              {0: [0, 7],
+                               7: [7],
+                               10: [10],
+                               19: [19],
+                               23: [23],
+                               29: [29, 37],
+                               36: [36, 37],
+                               37: [37],
+                               })
         g = self.multiple_loops()
         doms = g.post_dominators()
         self.check_dominators(doms,
-            {0: [0, 60, 68, 61, 7],
-             7: [60, 68, 61, 7],
-             10: [68, 7, 10, 13, 20, 56, 57, 60, 61],
-             13: [68, 7, 13, 20, 56, 57, 60, 61],
-             20: [20, 68, 7, 56, 57, 60, 61],
-             23: [68, 7, 20, 23, 56, 57, 60, 61],
-             32: [32, 68, 7, 20, 56, 57, 60, 61],
-             44: [68, 7, 44, 20, 56, 57, 60, 61],
-             56: [68, 7, 56, 57, 60, 61],
-             57: [57, 60, 68, 61, 7],
-             60: [60, 68, 61],
-             61: [68, 61],
-             68: [68],
-             71: [71],
-             80: [80],
-             87: [88, 87],
-             88: [88]
-             })
+                              {0: [0, 60, 68, 61, 7],
+                               7: [60, 68, 61, 7],
+                               10: [68, 7, 10, 13, 20, 56, 57, 60, 61],
+                               13: [68, 7, 13, 20, 56, 57, 60, 61],
+                               20: [20, 68, 7, 56, 57, 60, 61],
+                               23: [68, 7, 20, 23, 56, 57, 60, 61],
+                               32: [32, 68, 7, 20, 56, 57, 60, 61],
+                               44: [68, 7, 44, 20, 56, 57, 60, 61],
+                               56: [68, 7, 56, 57, 60, 61],
+                               57: [57, 60, 68, 61, 7],
+                               60: [60, 68, 61],
+                               61: [68, 61],
+                               68: [68],
+                               71: [71],
+                               80: [80],
+                               87: [88, 87],
+                               88: [88]
+                               })
 
     def test_post_dominators_infinite_loops(self):
         # Post-dominators with infinite loops need special care
@@ -770,21 +802,21 @@ def test_post_dominators_infinite_loops(self):
         g = self.infinite_loop1()
         doms = g.post_dominators()
         self.check_dominators(doms,
-            {0: [0],
-             6: [6],
-             10: [10, 13],
-             13: [13],
-             19: [19],
-             26: [26],
-             })
+                              {0: [0],
+                               6: [6],
+                               10: [10, 13],
+                               13: [13],
+                               19: [19],
+                               26: [26],
+                               })
         g = self.infinite_loop2()
         doms = g.post_dominators()
         self.check_dominators(doms,
-            {0: [0, 3],
-             3: [3],
-             9: [9],
-             16: [16],
-             })
+                              {0: [0, 3],
+                               3: [3],
+                               9: [9],
+                               16: [16],
+                               })
 
     def test_backbone_loopless(self):
         for g in [self.loopless1(), self.loopless1_dead_nodes()]: