-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Exception Handling Transform
Many mypyc ops can raise an exception, and by default these need to be propagated to the caller (if there aren't any enclosing try statements). Mypyc has a pass that inserts code needed to propagate exceptions, and to add traceback entries so that CPython can show stack tracebacks as expected. The implementation is in mypyc.transform.exceptions
.
When generating IR in mypyc.irbuild
, checking exceptions generated by ops is implicit -- it's described by the error_kind
attributes of ops, but otherwise can be mostly ignored. This makes it easy and less error-prone to generate IR.
For example, consider this function which has two calls which could raise exceptions:
def f(x: int) -> int:
return g(h(x))
The initial IR mypyc generates in mypyc.irbuild
only generates a single basic block with no error handling:
def f(x):
x, r0, r1 :: int
L0:
r0 = h(x)
r1 = g(r0)
return r1
However, the two Call
ops in the generated IR have their error_kind
attribute set to ERR_MAGIC
, i.e. errors are signaled via a special error value (which is in this case 1 for int
values).
The exception handling transform generates this IR for the above example (this also includes reference counting ops, which we won't discuss here):
def f(x):
x, r0, r1, r2 :: int
L0:
r0 = h(x)
if is_error(r0) goto L3 (error at f:5) else goto L1
L1:
r1 = g(r0)
dec_ref r0 :: int
if is_error(r1) goto L3 (error at f:5) else goto L2
L2:
return r1
L3:
r2 = <error> :: int
return r2
Note that additional error checking ops were added based on the error_kind
attributes. The original basic block was split into three basic blocks. There is also a new basic block L3
that propagates the error value to the caller.
Let's examine one of the error checking ops in more detail:
if is_error(r0) goto L3 (error at f:5) else goto L1
This checks if r0
has the error value, and if so, it branches to L3
and adds a traceback entry pointing to line 5 in function f
. If there is no error, we continue to L1
.