I was looking at how to represent try-after
in Core Erlang, and noticed something odd. Given
z(E, H, A, X) -> try E() catch _C:_R -> H() after A(X) end.
erlc +to_core
(OTP-25.0.3) generates
'z'/4 =
%% Line 4
( fun (_0,_1,_2,_3) ->
try
try
apply _0
()
of <_4> ->
_4
catch <_7,_6,_5> ->
apply _1
()
of <_9> ->
do apply _2
(_3)
_9
catch <_12,_11,_10> -> % (1)
do apply _2
(_3)
primop 'raise'
(_10, _11) % (2)
-| [{'function',{'z',4}}] )
which looks fine (a nested try
, the outer one deals with the after
logic).
However, if the inner try
raises an exception, we end up at (1) and bind _12
, _11
and _10
to the Class, Reason and (raw) Stacktrace of the exception, perform the after A(X)
call, and then re-raise the exception at (2). But at (2) we only pass the (raw) Stacktrace and Reason, not the Class. Yet when running this the correct exception class (exit, error, or throw) is propagated from A(X)
to the caller of z/4
.
Is there some side-channel which propagates the exception class even though the intermediate code (Core Erlang) doesn’t do so?