File 3331-Pass-Anno-to-ExternalFunctionHandler-in-erl_eval.patch of Package erlang
From 1499ec5ad07906ab892424dfd93180834e2b4bd1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Valim?= <jose.valim@dashbit.co>
Date: Fri, 14 Jan 2022 18:08:42 +0100
Subject: [PATCH] Pass Anno to ExternalFunctionHandler in erl_eval
This allows code using `erl_eval` to provide better
stacktraces if desired. For this purpose, we also
make sure all errors raised from `erl_eval` pass
through the ExternalFunctionHandler.
---
lib/debugger/src/dbg_ieval.erl | 9 +-
.../src/big_external_type.erl | 2 +-
.../opaque_SUITE_data/src/big_local_type.erl | 2 +-
.../src/big_external_type.erl | 2 +-
.../small_SUITE_data/src/big_local_type.erl | 2 +-
lib/stdlib/doc/src/erl_eval.xml | 11 +-
lib/stdlib/doc/src/shell.xml | 8 +
lib/stdlib/src/erl_eval.erl | 484 +++++++++---------
lib/stdlib/src/erl_parse.yrl | 2 +-
lib/stdlib/src/eval_bits.erl | 132 ++---
lib/stdlib/src/ms_transform.erl | 2 +-
lib/stdlib/test/erl_eval_SUITE.erl | 197 ++++++-
lib/stdlib/test/shell_SUITE.erl | 2 +
lib/syntax_tools/src/erl_syntax.erl | 2 +-
14 files changed, 509 insertions(+), 348 deletions(-)
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index e03ca7fa11..18f226967d 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -1043,9 +1043,7 @@ expr({bin,Line,Fs}, Bs0, Ieval0) ->
Ieval = Ieval0#ieval{line=Line,top=false},
try
eval_bits:expr_grp(Fs, Bs0,
- fun (E, B) -> expr(E, B, Ieval) end,
- [],
- false)
+ fun (E, B) -> expr(E, B, Ieval) end)
catch
Class:Reason ->
exception(Class, Reason, Bs0, Ieval)
@@ -1504,7 +1502,7 @@ guard_expr({bin,_,Flds}, Bs) ->
fun(E,B) ->
{value,V} = guard_expr(E,B),
{value,V,B}
- end, [], false),
+ end),
{value,V}.
%% guard_case_clauses(Value, Clauses, Bindings, Error, Ieval)
%% Error = try_clause | case_clause
@@ -1583,8 +1581,7 @@ match1({map,_,Fields}, Map, Bs, BBs) when is_map(Map) ->
match1({bin,_,Fs}, B, Bs0, BBs) when is_bitstring(B) ->
try eval_bits:match_bits(Fs, B, Bs0, BBs,
match_fun(BBs),
- fun(E, Bs) -> expr(E, Bs, #ieval{}) end,
- false)
+ fun(E, Bs) -> expr(E, Bs, #ieval{}) end)
catch
_:_ -> throw(nomatch)
end;
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/big_external_type.erl b/lib/dialyzer/test/opaque_SUITE_data/src/big_external_type.erl
index d286a378ed..7ad3f9366b 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/big_external_type.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/big_external_type.erl
@@ -409,7 +409,7 @@ normalise({bin,_,Fs}) ->
eval_bits:expr_grp(Fs, [],
fun(E, _) ->
{value, normalise(E), []}
- end, [], true),
+ end),
B;
normalise({cons,_,Head,Tail}) ->
[normalise(Head)|normalise(Tail)];
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/big_local_type.erl b/lib/dialyzer/test/opaque_SUITE_data/src/big_local_type.erl
index 7daceb5260..958ebeff93 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/big_local_type.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/big_local_type.erl
@@ -406,7 +406,7 @@ normalise({bin,_,Fs}) ->
eval_bits:expr_grp(Fs, [],
fun(E, _) ->
{value, normalise(E), []}
- end, [], true),
+ end),
B;
normalise({cons,_,Head,Tail}) ->
[normalise(Head)|normalise(Tail)];
diff --git a/lib/dialyzer/test/small_SUITE_data/src/big_external_type.erl b/lib/dialyzer/test/small_SUITE_data/src/big_external_type.erl
index 9ad4810a5e..3fa55dcc9c 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/big_external_type.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/big_external_type.erl
@@ -412,7 +412,7 @@ normalise({bin,_,Fs}) ->
eval_bits:expr_grp(Fs, [],
fun(E, _) ->
{value, normalise(E), []}
- end, [], true),
+ end),
B;
normalise({cons,_,Head,Tail}) ->
[normalise(Head)|normalise(Tail)];
diff --git a/lib/dialyzer/test/small_SUITE_data/src/big_local_type.erl b/lib/dialyzer/test/small_SUITE_data/src/big_local_type.erl
index fe567ff10d..89e0942ab5 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/big_local_type.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/big_local_type.erl
@@ -409,7 +409,7 @@ normalise({bin,_,Fs}) ->
eval_bits:expr_grp(Fs, [],
fun(E, _) ->
{value, normalise(E), []}
- end, [], true),
+ end),
B;
normalise({cons,_,Head,Tail}) ->
[normalise(Head)|normalise(Tail)];
diff --git a/lib/stdlib/doc/src/erl_eval.xml b/lib/stdlib/doc/src/erl_eval.xml
index 41566d9aec..71347b4319 100644
--- a/lib/stdlib/doc/src/erl_eval.xml
+++ b/lib/stdlib/doc/src/erl_eval.xml
@@ -273,10 +273,13 @@ Func(Name, Arguments, Bindings)</code>
<taglist>
<tag><c>{value,Func}</c></tag>
<item>
- <p>This defines a non-local function handler that is called with:</p>
- <code type="none">
-Func(FuncSpec, Arguments)</code>
- <p><c>FuncSpec</c> is the name of the function on the form
+ <p>This defines a non-local function handler. The function
+ may be called with two arguments:</p>
+ <code type="none">Func(FuncSpec, Arguments)</code>
+ <p>or three arguments:</p>
+ <code type="none">Func(Anno, FuncSpec, Arguments)</code>
+ <p><c>Anno</c> is the <c>erl_anno:anno()</c> of the node,
+ <c>FuncSpec</c> is the name of the function on the form
<c>{Module,Function}</c> or a fun, and <c>Arguments</c> is a
list of the <em>evaluated</em> arguments. The function
handler returns the value of the function. To
diff --git a/lib/stdlib/doc/src/shell.xml b/lib/stdlib/doc/src/shell.xml
index dc68af4328..39912a4bcf 100644
--- a/lib/stdlib/doc/src/shell.xml
+++ b/lib/stdlib/doc/src/shell.xml
@@ -789,6 +789,14 @@ q - quit erlang
manual page. (Arguments in <c>ArgList</c> are evaluated before the
callback functions are called.)</p>
+ <p>From OTP 25.0, if there are errors evaluating Erlang constructs,
+ such as <c>badmatch</c> during pattern matching or <c>bad_generator</c>
+ in a comprehension, the evaluator will dispatch to
+ <c>erlang:raise(error, Reason, Stacktrace)</c>. This call will be
+ checked against the <c>non_local_allowed/3</c> callback function.
+ You can either forbid it, allow it, or redirect to another call of
+ your choice.</p>
+
<p>Argument <c>State</c> is a tuple
<c>{ShellState,ExprState}</c>. The return value <c>NewState</c>
has the same form. This can be used to carry a state between calls
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 7a809c4402..fc2e2a4194 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -61,8 +61,9 @@
-type(func_spec() :: {Module :: module(), Function :: atom()} | function()).
-type(nlfun_handler() :: fun((FuncSpec :: func_spec(),
- Arguments :: [term()]) ->
- term())).
+ Arguments :: [term()]) -> term())
+ | fun((Anno :: erl_anno:anno(), FuncSpec :: func_spec(),
+ Arguments :: [term()]) -> term())).
-type(non_local_function_handler() :: {value, nlfun_handler()}
| none).
@@ -197,9 +198,9 @@ fun_data(F) when is_function(F) ->
case erlang:fun_info(F, module) of
{module,?MODULE} ->
case erlang:fun_info(F, env) of
- {env,[{FBs,_FLf,_FEf,_FUVs,FCs}]} ->
+ {env,[{_FAnno,FBs,_FLf,_FEf,_FUVs,FCs}]} ->
{fun_data,FBs,FCs};
- {env,[{FBs,_FLf,_FEf,_FUVs,FCs,FName}]} ->
+ {env,[{_FAnno,FBs,_FLf,_FEf,_FUVs,FCs,FName}]} ->
{named_fun_data,FBs,FName,FCs}
end;
_ ->
@@ -232,12 +233,12 @@ expr(Expr, Bs, Lf, Ef, Rbs) ->
FunUsedVars :: erl_lint:fun_used_vars(),
Value :: value(),
NewBindings :: binding_struct()).
-expr({var,_,V}, Bs, _Lf, _Ef, RBs, _FUVs) ->
+expr({var,Anno,V}, Bs, _Lf, Ef, RBs, _FUVs) ->
case binding(V, Bs) of
{value,Val} ->
ret_expr(Val, Bs, RBs);
unbound -> % Cannot not happen if checked by erl_lint
- erlang:raise(error, {unbound,V}, ?STACKTRACE)
+ apply_error({unbound,V}, ?STACKTRACE, Anno, Bs, Ef, RBs)
end;
expr({char,_,C}, Bs, _Lf, _Ef, RBs, _FUVs) ->
ret_expr(C, Bs, RBs);
@@ -251,10 +252,10 @@ expr({string,_,S}, Bs, _Lf, _Ef, RBs, _FUVs) ->
ret_expr(S, Bs, RBs);
expr({nil, _}, Bs, _Lf, _Ef, RBs, _FUVs) ->
ret_expr([], Bs, RBs);
-expr({cons,_,H0,T0}, Bs0, Lf, Ef, RBs, FUVs) ->
+expr({cons,Anno,H0,T0}, Bs0, Lf, Ef, RBs, FUVs) ->
{value,H,Bs1} = expr(H0, Bs0, Lf, Ef, none, FUVs),
{value,T,Bs2} = expr(T0, Bs0, Lf, Ef, none, FUVs),
- ret_expr([H|T], merge_bindings(Bs1, Bs2), RBs);
+ ret_expr([H|T], merge_bindings(Bs1, Bs2, Anno, Ef), RBs);
expr({lc,_,E,Qs}, Bs, Lf, Ef, RBs, FUVs) ->
eval_lc(E, Qs, Bs, Lf, Ef, RBs, FUVs);
expr({bc,_,E,Qs}, Bs, Lf, Ef, RBs, FUVs) ->
@@ -262,17 +263,17 @@ expr({bc,_,E,Qs}, Bs, Lf, Ef, RBs, FUVs) ->
expr({tuple,_,Es}, Bs0, Lf, Ef, RBs, FUVs) ->
{Vs,Bs} = expr_list(Es, Bs0, Lf, Ef, FUVs),
ret_expr(list_to_tuple(Vs), Bs, RBs);
-expr({record_field,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs, _FUVs) ->
- erlang:raise(error, {undef_record,Name}, ?STACKTRACE);
-expr({record_index,_,Name,_}, _Bs, _Lf, _Ef, _RBs, _FUVs) ->
- erlang:raise(error, {undef_record,Name}, ?STACKTRACE);
-expr({record,_,Name,_}, _Bs, _Lf, _Ef, _RBs, _FUVs) ->
- erlang:raise(error, {undef_record,Name}, ?STACKTRACE);
-expr({record,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs, _FUVs) ->
- erlang:raise(error, {undef_record,Name}, ?STACKTRACE);
+expr({record_field,Anno,_,Name,_}, Bs, _Lf, Ef, RBs, _FUVs) ->
+ apply_error({undef_record,Name}, ?STACKTRACE, Anno, Bs, Ef, RBs);
+expr({record_index,Anno,Name,_}, Bs, _Lf, Ef, RBs, _FUVs) ->
+ apply_error({undef_record,Name}, ?STACKTRACE, Anno, Bs, Ef, RBs);
+expr({record,Anno,Name,_}, Bs, _Lf, Ef, RBs, _FUVs) ->
+ apply_error({undef_record,Name}, ?STACKTRACE, Anno, Bs, Ef, RBs);
+expr({record,Anno,_,Name,_}, Bs, _Lf, Ef, RBs, _FUVs) ->
+ apply_error({undef_record,Name}, ?STACKTRACE, Anno, Bs, Ef, RBs);
%% map
-expr({map,_,Binding,Es}, Bs0, Lf, Ef, RBs, FUVs) ->
+expr({map,Anno,Binding,Es}, Bs0, Lf, Ef, RBs, FUVs) ->
{value, Map0, Bs1} = expr(Binding, Bs0, Lf, Ef, none, FUVs),
{Vs,Bs2} = eval_map_fields(Es, Bs0, Lf, Ef, FUVs),
_ = maps:put(k, v, Map0), %Validate map.
@@ -281,7 +282,7 @@ expr({map,_,Binding,Es}, Bs0, Lf, Ef, RBs, FUVs) ->
({map_exact,K,V}, Mi) ->
maps:update(K, V, Mi)
end, Map0, Vs),
- ret_expr(Map1, merge_bindings(Bs2, Bs1), RBs);
+ ret_expr(Map1, merge_bindings(Bs2, Bs1, Anno, Ef), RBs);
expr({map,_,Es}, Bs0, Lf, Ef, RBs, FUVs) ->
{Vs,Bs} = eval_map_fields(Es, Bs0, Lf, Ef, FUVs),
ret_expr(lists:foldl(fun
@@ -290,13 +291,13 @@ expr({map,_,Es}, Bs0, Lf, Ef, RBs, FUVs) ->
expr({block,_,Es}, Bs, Lf, Ef, RBs, FUVs) ->
exprs(Es, Bs, Lf, Ef, RBs, FUVs);
-expr({'if',_,Cs}, Bs, Lf, Ef, RBs, FUVs) ->
- if_clauses(Cs, Bs, Lf, Ef, RBs, FUVs);
-expr({'case',_,E,Cs}, Bs0, Lf, Ef, RBs, FUVs) ->
+expr({'if',Anno,Cs}, Bs, Lf, Ef, RBs, FUVs) ->
+ if_clauses(Cs, Anno, Bs, Lf, Ef, RBs, FUVs);
+expr({'case',Anno,E,Cs}, Bs0, Lf, Ef, RBs, FUVs) ->
{value,Val,Bs} = expr(E, Bs0, Lf, Ef, none, FUVs),
- case_clauses(Val, Cs, Bs, Lf, Ef, RBs, FUVs);
-expr({'try',_,B,Cases,Catches,AB}, Bs, Lf, Ef, RBs, FUVs) ->
- try_clauses(B, Cases, Catches, AB, Bs, Lf, Ef, RBs, FUVs);
+ case_clauses(Val, Cs, Anno, Bs, Lf, Ef, RBs, FUVs);
+expr({'try',Anno,B,Cases,Catches,AB}, Bs, Lf, Ef, RBs, FUVs) ->
+ try_clauses(B, Cases, Catches, AB, Anno, Bs, Lf, Ef, RBs, FUVs);
expr({'receive',_,Cs}, Bs, Lf, Ef, RBs, FUVs) ->
receive_clauses(Cs, Bs, Lf, Ef, RBs, FUVs);
expr({'receive',_, Cs, E, TB}, Bs0, Lf, Ef, RBs, FUVs) ->
@@ -306,12 +307,12 @@ expr({'fun',_Anno,{function,Mod0,Name0,Arity0}}, Bs0, Lf, Ef, RBs, FUVs) ->
{[Mod,Name,Arity],Bs} = expr_list([Mod0,Name0,Arity0], Bs0, Lf, Ef, FUVs),
F = erlang:make_fun(Mod, Name, Arity),
ret_expr(F, Bs, RBs);
-expr({'fun',_Anno,{function,Name,Arity}}, _Bs0, _Lf, _Ef, _RBs, _FUVs) -> % R8
+expr({'fun',Anno,{function,Name,Arity}}, Bs0, _Lf, Ef, RBs, _FUVs) -> % R8
%% Don't know what to do...
- erlang:raise(error, undef, [{?MODULE,Name,Arity}|?STACKTRACE]);
+ apply_error(undef, [{?MODULE,Name,Arity}|?STACKTRACE], Anno, Bs0, Ef, RBs);
expr({'fun',Anno,{clauses,Cs}} = Ex, Bs, Lf, Ef, RBs, FUVs) ->
{En,NewFUVs} = fun_used_bindings(Ex, Cs, Bs, FUVs),
- Info = {En,Lf,Ef,NewFUVs,Cs},
+ Info = {Anno,En,Lf,Ef,NewFUVs,Cs},
%% This is a really ugly hack!
F =
@@ -350,13 +351,13 @@ expr({'fun',Anno,{clauses,Cs}} = Ex, Bs, Lf, Ef, RBs, FUVs) ->
eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T], Info) end;
_Other ->
L = erl_anno:location(Anno),
- erlang:raise(error, {'argument_limit',{'fun',L,to_terms(Cs)}},
- ?STACKTRACE)
+ Reason = {'argument_limit',{'fun',L,to_terms(Cs)}},
+ apply_error(Reason, ?STACKTRACE, Anno, Bs, Ef, RBs)
end,
ret_expr(F, Bs, RBs);
expr({named_fun,Anno,Name,Cs} = Ex, Bs, Lf, Ef, RBs, FUVs) ->
{En,NewFUVs} = fun_used_bindings(Ex, Cs, Bs, FUVs),
- Info = {En,Lf,Ef,NewFUVs,Cs,Name},
+ Info = {Anno,En,Lf,Ef,NewFUVs,Cs,Name},
%% This is a really ugly hack!
F =
@@ -400,9 +401,8 @@ expr({named_fun,Anno,Name,Cs} = Ex, Bs, Lf, Ef, RBs, FUVs) ->
RF, Info) end;
_Other ->
L = erl_anno:location(Anno),
- erlang:raise(error, {'argument_limit',
- {named_fun,L,Name,to_terms(Cs)}},
- ?STACKTRACE)
+ Reason = {'argument_limit',{named_fun,L,Name,to_terms(Cs)}},
+ apply_error(Reason, ?STACKTRACE, Anno, Bs, Ef, RBs)
end,
ret_expr(F, Bs, RBs);
expr({call,_,{remote,_,{atom,_,qlc},{atom,_,q}},[{lc,_,_E,_Qs}=LC | As0]},
@@ -422,33 +422,33 @@ expr({call,A1,{remote,A2,{record_field,_,{atom,_,''},{atom,_,qlc}=Mod},
[{lc,_,_E,_Qs} | As0]=As},
Bs, Lf, Ef, RBs, FUVs) when length(As0) =< 1 ->
expr({call,A1,{remote,A2,Mod,Func},As}, Bs, Lf, Ef, RBs, FUVs);
-expr({call,_,{remote,_,Mod,Func},As0}, Bs0, Lf, Ef, RBs, FUVs) ->
+expr({call,Anno,{remote,_,Mod,Func},As0}, Bs0, Lf, Ef, RBs, FUVs) ->
{value,M,Bs1} = expr(Mod, Bs0, Lf, Ef, none, FUVs),
{value,F,Bs2} = expr(Func, Bs0, Lf, Ef, none, FUVs),
- {As,Bs3} = expr_list(As0, merge_bindings(Bs1, Bs2), Lf, Ef, FUVs),
+ {As,Bs3} = expr_list(As0, merge_bindings(Bs1, Bs2, Anno, Ef), Lf, Ef, FUVs),
%% M could be a parameterized module (not an atom).
case is_atom(M) andalso erl_internal:bif(M, F, length(As)) of
true ->
- bif(F, As, Bs3, Ef, RBs);
+ bif(F, As, Anno, Bs3, Ef, RBs);
false ->
- do_apply(M, F, As, Bs3, Ef, RBs)
+ do_apply(M, F, As, Anno, Bs3, Ef, RBs)
end;
-expr({call,_,{atom,_,Func},As0}, Bs0, Lf, Ef, RBs, FUVs) ->
+expr({call,Anno,{atom,_,Func},As0}, Bs0, Lf, Ef, RBs, FUVs) ->
case erl_internal:bif(Func, length(As0)) of
true ->
{As,Bs} = expr_list(As0, Bs0, Lf, Ef),
- bif(Func, As, Bs, Ef, RBs);
+ bif(Func, As, Anno, Bs, Ef, RBs);
false ->
- local_func(Func, As0, Bs0, Lf, Ef, RBs, FUVs)
+ local_func(Func, As0, Anno, Bs0, Lf, Ef, RBs, FUVs)
end;
-expr({call,_,Func0,As0}, Bs0, Lf, Ef, RBs, FUVs) -> % function or {Mod,Fun}
+expr({call,Anno,Func0,As0}, Bs0, Lf, Ef, RBs, FUVs) -> % function or {Mod,Fun}
{value,Func,Bs1} = expr(Func0, Bs0, Lf, Ef, none, FUVs),
{As,Bs2} = expr_list(As0, Bs1, Lf, Ef, FUVs),
case Func of
{M,F} when is_atom(M), is_atom(F) ->
- erlang:raise(error, {badfun,Func}, ?STACKTRACE);
+ apply_error({badfun,Func}, ?STACKTRACE, Anno, Bs0, Ef, RBs);
_ ->
- do_apply(Func, As, Bs2, Ef, RBs)
+ do_apply(Func, As, Anno, Bs2, Ef, RBs)
end;
expr({'catch',_,Expr}, Bs0, Lf, Ef, RBs, FUVs) ->
try expr(Expr, Bs0, Lf, Ef, none, FUVs) of
@@ -462,46 +462,50 @@ expr({'catch',_,Expr}, Bs0, Lf, Ef, RBs, FUVs) ->
error:Reason:Stacktrace ->
ret_expr({'EXIT',{Reason,Stacktrace}}, Bs0, RBs)
end;
-expr({match,_,Lhs,Rhs0}, Bs0, Lf, Ef, RBs, FUVs) ->
+expr({match,Anno,Lhs,Rhs0}, Bs0, Lf, Ef, RBs, FUVs) ->
{value,Rhs,Bs1} = expr(Rhs0, Bs0, Lf, Ef, none, FUVs),
- case match(Lhs, Rhs, Bs1) of
+ case match(Lhs, Rhs, Anno, Bs1, Bs1, Ef) of
{match,Bs} ->
ret_expr(Rhs, Bs, RBs);
- nomatch -> erlang:raise(error, {badmatch,Rhs}, ?STACKTRACE)
+ nomatch -> apply_error({badmatch,Rhs}, ?STACKTRACE, Anno, Bs0, Ef, RBs)
end;
-expr({op,_,Op,A0}, Bs0, Lf, Ef, RBs, FUVs) ->
+expr({op,Anno,Op,A0}, Bs0, Lf, Ef, RBs, FUVs) ->
{value,A,Bs} = expr(A0, Bs0, Lf, Ef, none, FUVs),
- eval_op(Op, A, Bs, Ef, RBs);
-expr({op,_,'andalso',L0,R0}, Bs0, Lf, Ef, RBs, FUVs) ->
+ eval_op(Op, A, Anno, Bs, Ef, RBs);
+expr({op,Anno,'andalso',L0,R0}, Bs0, Lf, Ef, RBs, FUVs) ->
{value,L,Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
V = case L of
true ->
{value,R,_} = expr(R0, Bs1, Lf, Ef, none, FUVs),
R;
false -> false;
- _ -> erlang:raise(error, {badarg,L}, ?STACKTRACE)
+ _ -> apply_error({badarg,L}, ?STACKTRACE, Anno, Bs0, Ef, RBs)
end,
ret_expr(V, Bs1, RBs);
-expr({op,_,'orelse',L0,R0}, Bs0, Lf, Ef, RBs, FUVs) ->
+expr({op,Anno,'orelse',L0,R0}, Bs0, Lf, Ef, RBs, FUVs) ->
{value,L,Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
V = case L of
true -> true;
false ->
{value,R,_} = expr(R0, Bs1, Lf, Ef, none, FUVs),
R;
- _ -> erlang:raise(error, {badarg,L}, ?STACKTRACE)
+ _ -> apply_error({badarg,L}, ?STACKTRACE, Anno, Bs0, Ef, RBs)
end,
ret_expr(V, Bs1, RBs);
-expr({op,_,Op,L0,R0}, Bs0, Lf, Ef, RBs, FUVs) ->
+expr({op,Anno,Op,L0,R0}, Bs0, Lf, Ef, RBs, FUVs) ->
{value,L,Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
{value,R,Bs2} = expr(R0, Bs0, Lf, Ef, none, FUVs),
- eval_op(Op, L, R, merge_bindings(Bs1, Bs2), Ef, RBs);
+ eval_op(Op, L, R, Anno, merge_bindings(Bs1, Bs2, Anno, Ef), Ef, RBs);
expr({bin,_,Fs}, Bs0, Lf, Ef, RBs, FUVs) ->
EvalFun = fun(E, B) -> expr(E, B, Lf, Ef, none, FUVs) end,
- {value,V,Bs} = eval_bits:expr_grp(Fs, Bs0, EvalFun),
+ ErrorFun = fun(A, R, S) -> apply_error(R, S, A, Bs0, Ef, RBs) end,
+ {value,V,Bs} = eval_bits:expr_grp(Fs, Bs0, EvalFun, ErrorFun),
ret_expr(V, Bs, RBs);
-expr({remote,_,_,_}, _Bs, _Lf, _Ef, _RBs, _FUVs) ->
- erlang:raise(error, {badexpr,':'}, ?STACKTRACE).
+expr({remote,Anno,_,_}, Bs0, _Lf, Ef, RBs, _FUVs) ->
+ apply_error({badexpr,':'}, ?STACKTRACE, Anno, Bs0, Ef, RBs).
+
+apply_error(Reason, Stack, Anno, Bs0, Ef, RBs) ->
+ do_apply(erlang, raise, [error, Reason, Stack], Anno, Bs0, Ef, RBs).
find_maxline(LC) ->
put('$erl_eval_max_line', 0),
@@ -584,62 +588,62 @@ unhide_calls([E | Es], MaxLine, D) ->
unhide_calls(E, _MaxLine, _D) ->
E.
-%% local_func(Function, Arguments, Bindings, LocalFuncHandler,
+%% local_func(Function, Arguments, Anno, Bindings, LocalFuncHandler,
%% ExternalFuncHandler, RBs, FunUsedVars) ->
%% {value,Value,Bindings} | Value when
%% LocalFuncHandler = {value,F} | {value,F,Eas} |
%% {eval,F} | {eval,F,Eas} | none.
-local_func(Func, As0, Bs0, {value,F}, Ef, value, FUVs) ->
+local_func(Func, As0, _Anno, Bs0, {value,F}, Ef, value, FUVs) ->
{As1,_Bs1} = expr_list(As0, Bs0, {value,F}, Ef, FUVs),
%% Make tail recursive calls when possible.
F(Func, As1);
-local_func(Func, As0, Bs0, {value,F}, Ef, RBs, FUVs) ->
+local_func(Func, As0, _Anno, Bs0, {value,F}, Ef, RBs, FUVs) ->
{As1,Bs1} = expr_list(As0, Bs0, {value,F}, Ef, FUVs),
ret_expr(F(Func, As1), Bs1, RBs);
-local_func(Func, As0, Bs0, {value,F,Eas}, Ef, RBs, FUVs) ->
+local_func(Func, As0, Anno, Bs0, {value,F,Eas}, Ef, RBs, FUVs) ->
Fun = fun(Name, Args) -> apply(F, [Name,Args|Eas]) end,
- local_func(Func, As0, Bs0, {value, Fun}, Ef, RBs, FUVs);
-local_func(Func, As, Bs, {eval,F}, _Ef, RBs, _FUVs) ->
- local_func2(F(Func, As, Bs), RBs);
-local_func(Func, As, Bs, {eval,F,Eas}, _Ef, RBs, _FUVs) ->
- local_func2(apply(F, [Func,As,Bs|Eas]), RBs);
+ local_func(Func, As0, Anno, Bs0, {value, Fun}, Ef, RBs, FUVs);
+local_func(Func, As, Anno, Bs, {eval,F}, _Ef, RBs, _FUVs) ->
+ local_func2(F(Func, As, Bs), Anno, RBs);
+local_func(Func, As, Anno, Bs, {eval,F,Eas}, _Ef, RBs, _FUVs) ->
+ local_func2(apply(F, [Func,As,Bs|Eas]), Anno, RBs);
%% These two clauses are for backwards compatibility.
-local_func(Func, As0, Bs0, {M,F}, Ef, RBs, FUVs) ->
+local_func(Func, As0, _Anno, Bs0, {M,F}, Ef, RBs, FUVs) ->
{As1,Bs1} = expr_list(As0, Bs0, {M,F}, Ef, FUVs),
ret_expr(M:F(Func,As1), Bs1, RBs);
-local_func(Func, As, _Bs, {M,F,Eas}, _Ef, RBs, _FUVs) ->
- local_func2(apply(M, F, [Func,As|Eas]), RBs);
+local_func(Func, As, Anno, _Bs, {M,F,Eas}, _Ef, RBs, _FUVs) ->
+ local_func2(apply(M, F, [Func,As|Eas]), Anno, RBs);
%% Default unknown function handler to undefined function.
-local_func(Func, As0, _Bs0, none, _Ef, _RBs, _FUVs) ->
- erlang:raise(error, undef, [{?MODULE,Func,length(As0)}|?STACKTRACE]).
+local_func(Func, As0, Anno, Bs0, none, Ef, RBs, _FUVs) ->
+ apply_error(undef, [{?MODULE,Func,length(As0)}|?STACKTRACE], Anno, Bs0, Ef, RBs).
-local_func2({value,V,Bs}, RBs) ->
+local_func2({value,V,Bs}, _Anno, RBs) ->
ret_expr(V, Bs, RBs);
-local_func2({eval,F,As,Bs}, RBs) -> % This reply is not documented.
+local_func2({eval,F,As,Bs}, Anno, RBs) -> % This reply is not documented.
%% The shell found F. erl_eval tries to do a tail recursive call,
%% something the shell cannot do. Do not use Ef here.
- do_apply(F, As, Bs, none, RBs).
+ do_apply(F, As, Anno, Bs, none, RBs).
%% bif(Name, Arguments, RBs)
%% Evaluate the Erlang auto-imported function Name. erlang:apply/2,3
%% are "hidden" from the external function handler.
-bif(apply, [erlang,apply,As], Bs, Ef, RBs) ->
- bif(apply, As, Bs, Ef, RBs);
-bif(apply, [M,F,As], Bs, Ef, RBs) ->
- do_apply(M, F, As, Bs, Ef, RBs);
-bif(apply, [F,As], Bs, Ef, RBs) ->
- do_apply(F, As, Bs, Ef, RBs);
-bif(Name, As, Bs, Ef, RBs) ->
- do_apply(erlang, Name, As, Bs, Ef, RBs).
+bif(apply, [erlang,apply,As], Anno, Bs, Ef, RBs) ->
+ bif(apply, As, Anno, Bs, Ef, RBs);
+bif(apply, [M,F,As], Anno, Bs, Ef, RBs) ->
+ do_apply(M, F, As, Anno, Bs, Ef, RBs);
+bif(apply, [F,As], Anno, Bs, Ef, RBs) ->
+ do_apply(F, As, Anno, Bs, Ef, RBs);
+bif(Name, As, Anno, Bs, Ef, RBs) ->
+ do_apply(erlang, Name, As, Anno, Bs, Ef, RBs).
%% do_apply(Func, Arguments, Bindings, ExternalFuncHandler, RBs) ->
%% {value,Value,Bindings} | Value when
%% ExternalFuncHandler = {value,F} | none,
%% Func = fun()
-do_apply(Func, As, Bs0, Ef, RBs) ->
+do_apply(Func, As, Anno, Bs0, Ef, RBs) ->
Env = if
is_function(Func) ->
case {erlang:fun_info(Func, module),
@@ -653,7 +657,7 @@ do_apply(Func, As, Bs0, Ef, RBs) ->
no_env
end,
case {Env,Ef} of
- {{env,[{FBs,FLf,FEf,FFUVs,FCs}]},_} ->
+ {{env,[{FAnno,FBs,FLf,FEf,FFUVs,FCs}]},_} ->
%% If we are evaluting within another function body
%% (RBs =/= none), we return RBs when this function body
%% has been evalutated, otherwise we return Bs0, the
@@ -664,20 +668,20 @@ do_apply(Func, As, Bs0, Ef, RBs) ->
end,
case {erlang:fun_info(Func, arity), length(As)} of
{{arity, Arity}, Arity} ->
- eval_fun(FCs, As, FBs, FLf, FEf, NRBs, FFUVs);
+ eval_fun(FCs, As, FAnno, FBs, FLf, FEf, NRBs, FFUVs);
_ ->
- erlang:raise(error, {badarity,{Func,As}},?STACKTRACE)
+ apply_error({badarity,{Func,As}}, ?STACKTRACE, Anno, Bs0, Ef, RBs)
end;
- {{env,[{FBs,FLf,FEf,FFUVs,FCs,FName}]},_} ->
+ {{env,[{FAnno,FBs,FLf,FEf,FFUVs,FCs,FName}]},_} ->
NRBs = if
RBs =:= none -> Bs0;
true -> RBs
end,
case {erlang:fun_info(Func, arity), length(As)} of
{{arity, Arity}, Arity} ->
- eval_named_fun(FCs, As, FBs, FLf, FEf, FName, Func, NRBs, FFUVs);
+ eval_named_fun(FCs, As, FAnno, FBs, FLf, FEf, FName, Func, NRBs, FFUVs);
_ ->
- erlang:raise(error, {badarity,{Func,As}},?STACKTRACE)
+ apply_error({badarity,{Func,As}}, ?STACKTRACE, Anno, Bs0, Ef, RBs)
end;
{no_env,none} when RBs =:= value ->
%% Make tail recursive calls when possible.
@@ -685,12 +689,12 @@ do_apply(Func, As, Bs0, Ef, RBs) ->
{no_env,none} ->
ret_expr(apply(Func, As), Bs0, RBs);
{no_env,{value,F}} when RBs =:= value ->
- F(Func,As);
+ do_apply(F, Anno, Func, As);
{no_env,{value,F}} ->
- ret_expr(F(Func, As), Bs0, RBs)
+ ret_expr(do_apply(F, Anno, Func, As), Bs0, RBs)
end.
-do_apply(Mod, Func, As, Bs0, Ef, RBs) ->
+do_apply(Mod, Func, As, Anno, Bs0, Ef, RBs) ->
case Ef of
none when RBs =:= value ->
%% Make tail recursive calls when possible.
@@ -698,11 +702,16 @@ do_apply(Mod, Func, As, Bs0, Ef, RBs) ->
none ->
ret_expr(apply(Mod, Func, As), Bs0, RBs);
{value,F} when RBs =:= value ->
- F({Mod,Func}, As);
+ do_apply(F, Anno, {Mod,Func}, As);
{value,F} ->
- ret_expr(F({Mod,Func}, As), Bs0, RBs)
+ ret_expr(do_apply(F, Anno, {Mod,Func}, As), Bs0, RBs)
end.
+do_apply(F, Anno, FunOrModFun, Args) when is_function(F, 3) ->
+ F(Anno, FunOrModFun, Args);
+do_apply(F, _Anno, FunOrModFun, Args) when is_function(F, 2) ->
+ F(FunOrModFun, Args).
+
%% eval_lc(Expr, [Qualifier], Bindings, LocalFunctionHandler,
%% ExternalFuncHandler, RetBindings) ->
%% {value,Value,Bindings} | Value
@@ -710,14 +719,14 @@ do_apply(Mod, Func, As, Bs0, Ef, RBs) ->
eval_lc(E, Qs, Bs, Lf, Ef, RBs, FUVs) ->
ret_expr(lists:reverse(eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, [])), Bs, RBs).
-eval_lc1(E, [{generate,_,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
+eval_lc1(E, [{generate,Anno,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
{value,L1,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
CompFun = fun(Bs, Acc) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
- eval_generate(L1, P, Bs0, Lf, Ef, CompFun, Acc0);
-eval_lc1(E, [{b_generate,_,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
+ eval_generate(L1, P, Anno, Bs0, Lf, Ef, CompFun, Acc0);
+eval_lc1(E, [{b_generate,Anno,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
{value,Bin,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
CompFun = fun(Bs, Acc) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
- eval_b_generate(Bin, P, Bs0, Lf, Ef, CompFun, Acc0);
+ eval_b_generate(Bin, P, Anno, Bs0, Lf, Ef, CompFun, Acc0);
eval_lc1(E, [F|Qs], Bs0, Lf, Ef, FUVs, Acc) ->
CompFun = fun(Bs) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc);
@@ -732,14 +741,14 @@ eval_lc1(E, [], Bs, Lf, Ef, FUVs, Acc) ->
eval_bc(E, Qs, Bs, Lf, Ef, RBs, FUVs) ->
ret_expr(eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, <<>>), Bs, RBs).
-eval_bc1(E, [{b_generate,_,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
+eval_bc1(E, [{b_generate,Anno,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
{value,Bin,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
CompFun = fun(Bs, Acc) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
- eval_b_generate(Bin, P, Bs0, Lf, Ef, CompFun, Acc0);
-eval_bc1(E, [{generate,_,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
+ eval_b_generate(Bin, P, Anno, Bs0, Lf, Ef, CompFun, Acc0);
+eval_bc1(E, [{generate,Anno,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
{value,List,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
CompFun = fun(Bs, Acc) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
- eval_generate(List, P, Bs0, Lf, Ef, CompFun, Acc0);
+ eval_generate(List, P, Anno, Bs0, Lf, Ef, CompFun, Acc0);
eval_bc1(E, [F|Qs], Bs0, Lf, Ef, FUVs, Acc) ->
CompFun = fun(Bs) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc);
@@ -747,35 +756,36 @@ eval_bc1(E, [], Bs, Lf, Ef, FUVs, Acc) ->
{value,V,_} = expr(E, Bs, Lf, Ef, none, FUVs),
<<Acc/bitstring,V/bitstring>>.
-eval_generate([V|Rest], P, Bs0, Lf, Ef, CompFun, Acc) ->
- case match(P, V, new_bindings(Bs0), Bs0) of
+eval_generate([V|Rest], P, Anno, Bs0, Lf, Ef, CompFun, Acc) ->
+ case match(P, V, Anno, new_bindings(Bs0), Bs0, Ef) of
{match,Bsn} ->
Bs2 = add_bindings(Bsn, Bs0),
NewAcc = CompFun(Bs2, Acc),
- eval_generate(Rest, P, Bs0, Lf, Ef, CompFun, NewAcc);
+ eval_generate(Rest, P, Anno, Bs0, Lf, Ef, CompFun, NewAcc);
nomatch ->
- eval_generate(Rest, P, Bs0, Lf, Ef, CompFun, Acc)
+ eval_generate(Rest, P, Anno, Bs0, Lf, Ef, CompFun, Acc)
end;
-eval_generate([], _P, _Bs0, _Lf, _Ef, _CompFun, Acc) ->
+eval_generate([], _P, _Anno, _Bs0, _Lf, _Ef, _CompFun, Acc) ->
Acc;
-eval_generate(Term, _P, _Bs0, _Lf, _Ef, _CompFun, _Acc) ->
- erlang:raise(error, {bad_generator,Term}, ?STACKTRACE).
+eval_generate(Term, _P, Anno, Bs0, _Lf, Ef, _CompFun, _Acc) ->
+ apply_error({bad_generator,Term}, ?STACKTRACE, Anno, Bs0, Ef, none).
-eval_b_generate(<<_/bitstring>>=Bin, P, Bs0, Lf, Ef, CompFun, Acc) ->
- Mfun = match_fun(Bs0),
+eval_b_generate(<<_/bitstring>>=Bin, P, Anno, Bs0, Lf, Ef, CompFun, Acc) ->
+ Mfun = match_fun(Bs0, Ef),
Efun = fun(Exp, Bs) -> expr(Exp, Bs, Lf, Ef, none) end,
- case eval_bits:bin_gen(P, Bin, new_bindings(Bs0), Bs0, Mfun, Efun) of
+ ErrorFun = fun(A, R, S) -> apply_error(R, S, A, Bs0, Ef, none) end,
+ case eval_bits:bin_gen(P, Bin, new_bindings(Bs0), Bs0, Mfun, Efun, ErrorFun) of
{match, Rest, Bs1} ->
Bs2 = add_bindings(Bs1, Bs0),
NewAcc = CompFun(Bs2, Acc),
- eval_b_generate(Rest, P, Bs0, Lf, Ef, CompFun, NewAcc);
+ eval_b_generate(Rest, P, Anno, Bs0, Lf, Ef, CompFun, NewAcc);
{nomatch, Rest} ->
- eval_b_generate(Rest, P, Bs0, Lf, Ef, CompFun, Acc);
+ eval_b_generate(Rest, P, Anno, Bs0, Lf, Ef, CompFun, Acc);
done ->
Acc
end;
-eval_b_generate(Term, _P, _Bs0, _Lf, _Ef, _CompFun, _Acc) ->
- erlang:raise(error, {bad_generator,Term}, ?STACKTRACE).
+eval_b_generate(Term, _P, Anno, Bs0, _Lf, Ef, _CompFun, _Acc) ->
+ apply_error({bad_generator,Term}, ?STACKTRACE, Anno, Bs0, Ef, none).
eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc) ->
case erl_lint:is_guard_test(F) of
@@ -789,7 +799,7 @@ eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc) ->
{value,true,Bs1} -> CompFun(Bs1);
{value,false,_} -> Acc;
{value,V,_} ->
- erlang:raise(error, {bad_filter,V}, ?STACKTRACE)
+ apply_error({bad_filter,V}, ?STACKTRACE, element(2, F), Bs0, Ef, none)
end
end.
@@ -824,48 +834,48 @@ ret_expr(V, Bs, none) ->
ret_expr(V, _Bs, RBs) when is_list(RBs); is_map(RBs) ->
{value,V,RBs}.
-%% eval_fun(Arguments, {Bindings,LocalFunctionHandler,
+%% eval_fun(Arguments, {Anno,Bindings,LocalFunctionHandler,
%% ExternalFunctionHandler,FunUsedVars,Clauses}) -> Value
%% This function is called when the fun is called from compiled code
%% or from apply.
-eval_fun(As, {Bs0,Lf,Ef,FUVs,Cs}) ->
- eval_fun(Cs, As, Bs0, Lf, Ef, value, FUVs).
+eval_fun(As, {Anno,Bs0,Lf,Ef,FUVs,Cs}) ->
+ eval_fun(Cs, As, Anno, Bs0, Lf, Ef, value, FUVs).
-eval_fun([{clause,_,H,G,B}|Cs], As, Bs0, Lf, Ef, RBs, FUVs) ->
- case match_list(H, As, new_bindings(Bs0), Bs0) of
+eval_fun([{clause,_,H,G,B}|Cs], As, Anno, Bs0, Lf, Ef, RBs, FUVs) ->
+ case match_list(H, As, Anno, new_bindings(Bs0), Bs0, Ef) of
{match,Bsn} -> % The new bindings for the head
Bs1 = add_bindings(Bsn, Bs0), % which then shadow!
case guard(G, Bs1, Lf, Ef) of
true -> exprs(B, Bs1, Lf, Ef, RBs, FUVs);
- false -> eval_fun(Cs, As, Bs0, Lf, Ef, RBs, FUVs)
+ false -> eval_fun(Cs, As, Anno, Bs0, Lf, Ef, RBs, FUVs)
end;
nomatch ->
- eval_fun(Cs, As, Bs0, Lf, Ef, RBs, FUVs)
+ eval_fun(Cs, As, Anno, Bs0, Lf, Ef, RBs, FUVs)
end;
-eval_fun([], As, _Bs, _Lf, _Ef, _RBs, _FUVs) ->
- erlang:raise(error, function_clause,
- [{?MODULE,'-inside-an-interpreted-fun-',As}|?STACKTRACE]).
+eval_fun([], As, Anno, Bs, _Lf, Ef, RBs, _FUVs) ->
+ Stack = [{?MODULE,'-inside-an-interpreted-fun-',As}|?STACKTRACE],
+ apply_error(function_clause, Stack, Anno, Bs, Ef, RBs).
-eval_named_fun(As, Fun, {Bs0,Lf,Ef,FUVs,Cs,Name}) ->
- eval_named_fun(Cs, As, Bs0, Lf, Ef, Name, Fun, value, FUVs).
+eval_named_fun(As, Fun, {Anno,Bs0,Lf,Ef,FUVs,Cs,Name}) ->
+ eval_named_fun(Cs, As, Anno, Bs0, Lf, Ef, Name, Fun, value, FUVs).
-eval_named_fun([{clause,_,H,G,B}|Cs], As, Bs0, Lf, Ef, Name, Fun, RBs, FUVs) ->
+eval_named_fun([{clause,_,H,G,B}|Cs], As, Anno, Bs0, Lf, Ef, Name, Fun, RBs, FUVs) ->
Bs1 = add_binding(Name, Fun, Bs0),
- case match_list(H, As, new_bindings(Bs0), Bs1) of
+ case match_list(H, As, Anno, new_bindings(Bs0), Bs1, Ef) of
{match,Bsn} -> % The new bindings for the head
Bs2 = add_bindings(Bsn, Bs1), % which then shadow!
case guard(G, Bs2, Lf, Ef) of
true -> exprs(B, Bs2, Lf, Ef, RBs, FUVs);
- false -> eval_named_fun(Cs, As, Bs0, Lf, Ef, Name, Fun, RBs, FUVs)
+ false -> eval_named_fun(Cs, As, Anno, Bs0, Lf, Ef, Name, Fun, RBs, FUVs)
end;
nomatch ->
- eval_named_fun(Cs, As, Bs0, Lf, Ef, Name, Fun, RBs, FUVs)
+ eval_named_fun(Cs, As, Anno, Bs0, Lf, Ef, Name, Fun, RBs, FUVs)
end;
-eval_named_fun([], As, _Bs, _Lf, _Ef, _Name, _Fun, _RBs, _FUVs) ->
- erlang:raise(error, function_clause,
- [{?MODULE,'-inside-an-interpreted-fun-',As}|?STACKTRACE]).
+eval_named_fun([], As, Anno, Bs, _Lf, Ef, _Name, _Fun, RBs, _FUVs) ->
+ Stack = [{?MODULE,'-inside-an-interpreted-fun-',As}|?STACKTRACE],
+ apply_error(function_clause, Stack, Anno, Bs, Ef, RBs).
%% expr_list(ExpressionList, Bindings)
@@ -908,31 +918,31 @@ expr_list(Es, Bs, Lf, Ef, FUVs) ->
expr_list([E|Es], Vs, BsOrig, Bs0, Lf, Ef, FUVs) ->
{value,V,Bs1} = expr(E, BsOrig, Lf, Ef, none, FUVs),
- expr_list(Es, [V|Vs], BsOrig, merge_bindings(Bs1, Bs0), Lf, Ef, FUVs);
+ expr_list(Es, [V|Vs], BsOrig, merge_bindings(Bs1, Bs0, element(2, E), Ef), Lf, Ef, FUVs);
expr_list([], Vs, _, Bs, _Lf, _Ef, _FUVs) ->
{reverse(Vs),Bs}.
-eval_op(Op, Arg1, Arg2, Bs, Ef, RBs) ->
- do_apply(erlang, Op, [Arg1,Arg2], Bs, Ef, RBs).
+eval_op(Op, Arg1, Arg2, Anno, Bs, Ef, RBs) ->
+ do_apply(erlang, Op, [Arg1,Arg2], Anno, Bs, Ef, RBs).
-eval_op(Op, Arg, Bs, Ef, RBs) ->
- do_apply(erlang, Op, [Arg], Bs, Ef, RBs).
+eval_op(Op, Arg, Anno, Bs, Ef, RBs) ->
+ do_apply(erlang, Op, [Arg], Anno, Bs, Ef, RBs).
-%% if_clauses(Clauses, Bindings, LocalFuncHandler, ExtFuncHandler, RBs)
+%% if_clauses(Clauses, Anno, Bindings, LocalFuncHandler, ExtFuncHandler, RBs)
-if_clauses([{clause,_,[],G,B}|Cs], Bs, Lf, Ef, RBs, FUVs) ->
+if_clauses([{clause,_,[],G,B}|Cs], Anno, Bs, Lf, Ef, RBs, FUVs) ->
case guard(G, Bs, Lf, Ef) of
true -> exprs(B, Bs, Lf, Ef, RBs, FUVs);
- false -> if_clauses(Cs, Bs, Lf, Ef, RBs, FUVs)
+ false -> if_clauses(Cs, Anno, Bs, Lf, Ef, RBs, FUVs)
end;
-if_clauses([], _Bs, _Lf, _Ef, _RBs, _FUVs) ->
- erlang:raise(error, if_clause, ?STACKTRACE).
+if_clauses([], Anno, Bs, _Lf, Ef, RBs, _FUVs) ->
+ apply_error(if_clause, ?STACKTRACE, Anno, Bs, Ef, RBs).
-%% try_clauses(Body, CaseClauses, CatchClauses, AfterBody, Bindings,
+%% try_clauses(Body, CaseClauses, CatchClauses, AfterBody, Anno, Bindings,
%% LocalFuncHandler, ExtFuncHandler, RBs)
-try_clauses(B, Cases, Catches, AB, Bs, Lf, Ef, RBs, FUVs) ->
- check_stacktrace_vars(Catches, Bs),
+try_clauses(B, Cases, Catches, AB, Anno, Bs, Lf, Ef, RBs, FUVs) ->
+ check_stacktrace_vars(Catches, Anno, Bs, Ef, RBs),
try exprs(B, Bs, Lf, Ef, none, FUVs) of
{value,V,Bs1} when Cases =:= [] ->
ret_expr(V, Bs1, RBs);
@@ -941,7 +951,7 @@ try_clauses(B, Cases, Catches, AB, Bs, Lf, Ef, RBs, FUVs) ->
{B2,Bs2} ->
exprs(B2, Bs2, Lf, Ef, RBs, FUVs);
nomatch ->
- erlang:raise(error, {try_clause,V}, ?STACKTRACE)
+ apply_error({try_clause, V}, ?STACKTRACE, Anno, Bs, Ef, RBs)
end
catch
Class:Reason:Stacktrace when Catches =:= [] ->
@@ -962,36 +972,36 @@ try_clauses(B, Cases, Catches, AB, Bs, Lf, Ef, RBs, FUVs) ->
end
end.
-check_stacktrace_vars([{clause,_,[{tuple,_,[_,_,STV]}],_,_}|Cs], Bs) ->
+
+check_stacktrace_vars([{clause,_,[{tuple,_,[_,_,STV]}],_,_}|Cs], Anno, Bs, Ef, RBs) ->
case STV of
{var,_,V} ->
case binding(V, Bs) of
{value, _} ->
- erlang:raise(error, stacktrace_bound, ?STACKTRACE);
+ apply_error(stacktrace_bound, ?STACKTRACE, Anno, Bs, Ef, RBs);
unbound ->
- check_stacktrace_vars(Cs, Bs)
+ check_stacktrace_vars(Cs, Anno, Bs, Ef, RBs)
end;
_ ->
- erlang:raise(error,
- {illegal_stacktrace_variable,STV},
- ?STACKTRACE)
+ Reason = {illegal_stacktrace_variable,STV},
+ apply_error(Reason, ?STACKTRACE, Anno, Bs, Ef, RBs)
end;
-check_stacktrace_vars([], _Bs) ->
+check_stacktrace_vars([], _Anno, _Bs, _Ef, _RBs) ->
ok.
-%% case_clauses(Value, Clauses, Bindings, LocalFuncHandler, ExtFuncHandler,
-%% RBs)
+%% case_clauses(Value, Clauses, Anno, Bindings, LocalFuncHandler,
+%% ExtFuncHandler, RBs)
-case_clauses(Val, Cs, Bs, Lf, Ef, RBs, FUVs) ->
+case_clauses(Val, Cs, Anno, Bs, Lf, Ef, RBs, FUVs) ->
case match_clause(Cs, [Val], Bs, Lf, Ef) of
{B, Bs1} ->
exprs(B, Bs1, Lf, Ef, RBs, FUVs);
nomatch ->
- erlang:raise(error, {case_clause,Val}, ?STACKTRACE)
+ apply_error({case_clause,Val}, ?STACKTRACE, Anno, Bs, Ef, RBs)
end.
%%
-%% receive_clauses(Clauses, Bindings, LocalFuncHnd,ExtFuncHnd, RBs)
+%% receive_clauses(Clauses, Bindings, LocalFuncHnd, ExtFuncHnd, RBs)
%%
receive_clauses(Cs, Bs, Lf, Ef, RBs, FUVs) ->
receive_clauses(infinity, Cs, unused, Bs, Lf, Ef, RBs, FUVs).
@@ -1023,8 +1033,8 @@ receive_clauses(T, Cs, TB, Bs, Lf, Ef, RBs, FUVs) ->
match_clause(Cs, Vs, Bs, Lf) ->
match_clause(Cs, Vs, Bs, Lf, none).
-match_clause([{clause,_,H,G,B}|Cs], Vals, Bs, Lf, Ef) ->
- case match_list(H, Vals, Bs) of
+match_clause([{clause,Anno,H,G,B}|Cs], Vals, Bs, Lf, Ef) ->
+ case match_list(H, Vals, Anno, Bs, Bs, Ef) of
{match, Bs1} ->
case guard(G, Bs1, Lf, Ef) of
true -> {B, Bs1};
@@ -1062,7 +1072,7 @@ guard0([G|Gs], Bs0, Lf, Ef) ->
{value,false,_} -> false
end;
false ->
- erlang:raise(error, guard_expr, ?STACKTRACE)
+ apply_error(guard_expr, ?STACKTRACE, element(2, G), Bs0, Ef, none)
end;
guard0([], _Bs, _Lf, _Ef) -> true.
@@ -1101,23 +1111,19 @@ type_test(record) -> is_record;
type_test(map) -> is_map;
type_test(Test) -> Test.
-
-%% match(Pattern, Term, Bindings) ->
-%% {match,NewBindings} | nomatch
+%% match(Pattern, Term, Anno, NewBindings, Bindings, ExternalFunHnd) ->
+%% {match,NewBindings} | nomatch
%% or erlang:error({illegal_pattern, Pattern}).
-%% Try to match Pattern against Term with the current bindings.
-
-match(Pat, Term, Bs) ->
- match(Pat, Term, Bs, Bs).
-
+%%
+%% Try to match Pattern against Term with the current bindings.
%% Bs are the bindings that are augmented with new bindings. BBs are
%% the bindings used for "binsize" variables (in <<X:Y>>, Y is a
%% binsize variable).
-match(Pat, Term, Bs, BBs) ->
- case catch match1(Pat, Term, Bs, BBs) of
+match(Pat, Term, Anno, Bs, BBs, Ef) ->
+ case catch match1(Pat, Term, Bs, BBs, Ef) of
invalid ->
- erlang:raise(error, {illegal_pattern,to_term(Pat)}, ?STACKTRACE);
+ apply_error({illegal_pattern,to_term(Pat)}, ?STACKTRACE, Anno, Bs, Ef, none);
Other ->
Other
end.
@@ -1126,29 +1132,29 @@ string_to_conses([], _, Tail) -> Tail;
string_to_conses([E|Rest], Anno, Tail) ->
{cons, Anno, {integer, Anno, E}, string_to_conses(Rest, Anno, Tail)}.
-match1({atom,_,A0}, A, Bs, _BBs) ->
+match1({atom,_,A0}, A, Bs, _BBs, _Ef) ->
case A of
A0 -> {match,Bs};
_ -> throw(nomatch)
end;
-match1({integer,_,I0}, I, Bs, _BBs) ->
+match1({integer,_,I0}, I, Bs, _BBs, _Ef) ->
case I of
I0 -> {match,Bs};
_ -> throw(nomatch)
end;
-match1({float,_,F0}, F, Bs, _BBs) ->
+match1({float,_,F0}, F, Bs, _BBs, _Ef) ->
case F of
F0 -> {match,Bs};
_ -> throw(nomatch)
end;
-match1({char,_,C0}, C, Bs, _BBs) ->
+match1({char,_,C0}, C, Bs, _BBs, _Ef) ->
case C of
C0 -> {match,Bs};
_ -> throw(nomatch)
end;
-match1({var,_,'_'}, _, Bs, _BBs) -> %Anonymous variable matches
+match1({var,_,'_'}, _, Bs, _BBs, _Ef) -> %Anonymous variable matches
{match,Bs}; % everything, no new bindings
-match1({var,_,Name}, Term, Bs, _BBs) ->
+match1({var,_,Name}, Term, Bs, _BBs, _Ef) ->
case binding(Name, Bs) of
{value,Term} ->
{match,Bs};
@@ -1157,34 +1163,34 @@ match1({var,_,Name}, Term, Bs, _BBs) ->
unbound ->
{match,add_binding(Name, Term, Bs)}
end;
-match1({match,_,Pat1,Pat2}, Term, Bs0, BBs) ->
- {match, Bs1} = match1(Pat1, Term, Bs0, BBs),
- match1(Pat2, Term, Bs1, BBs);
-match1({string,_,S0}, S, Bs, _BBs) ->
+match1({match,_,Pat1,Pat2}, Term, Bs0, BBs, Ef) ->
+ {match, Bs1} = match1(Pat1, Term, Bs0, BBs, Ef),
+ match1(Pat2, Term, Bs1, BBs, Ef);
+match1({string,_,S0}, S, Bs, _BBs, _Ef) ->
case S of
S0 -> {match,Bs};
_ -> throw(nomatch)
end;
-match1({nil,_}, Nil, Bs, _BBs) ->
+match1({nil,_}, Nil, Bs, _BBs, _Ef) ->
case Nil of
[] -> {match,Bs};
_ -> throw(nomatch)
end;
-match1({cons,_,H,T}, [H1|T1], Bs0, BBs) ->
- {match,Bs} = match1(H, H1, Bs0, BBs),
- match1(T, T1, Bs, BBs);
-match1({cons,_,_,_}, _, _Bs, _BBs) ->
+match1({cons,_,H,T}, [H1|T1], Bs0, BBs, Ef) ->
+ {match,Bs} = match1(H, H1, Bs0, BBs, Ef),
+ match1(T, T1, Bs, BBs, Ef);
+match1({cons,_,_,_}, _, _Bs, _BBs, _Ef) ->
throw(nomatch);
-match1({tuple,_,Elts}, Tuple, Bs, BBs)
+match1({tuple,_,Elts}, Tuple, Bs, BBs, Ef)
when length(Elts) =:= tuple_size(Tuple) ->
- match_tuple(Elts, Tuple, 1, Bs, BBs);
-match1({tuple,_,_}, _, _Bs, _BBs) ->
+ match_tuple(Elts, Tuple, 1, Bs, BBs, Ef);
+match1({tuple,_,_}, _, _Bs, _BBs, _Ef) ->
throw(nomatch);
-match1({map,_,Fs}, #{}=Map, Bs, BBs) ->
- match_map(Fs, Map, Bs, BBs);
-match1({map,_,_}, _, _Bs, _BBs) ->
+match1({map,_,Fs}, #{}=Map, Bs, BBs, Ef) ->
+ match_map(Fs, Map, Bs, BBs, Ef);
+match1({map,_,_}, _, _Bs, _BBs, _Ef) ->
throw(nomatch);
-match1({bin, _, Fs}, <<_/bitstring>>=B, Bs0, BBs) ->
+match1({bin, _, Fs}, <<_/bitstring>>=B, Bs0, BBs, Ef) ->
EvalFun = fun(E, Bs) ->
case erl_lint:is_guard_expr(E) of
true -> ok;
@@ -1197,74 +1203,72 @@ match1({bin, _, Fs}, <<_/bitstring>>=B, Bs0, BBs) ->
throw(invalid)
end
end,
- eval_bits:match_bits(Fs, B, Bs0, BBs, match_fun(BBs), EvalFun);
-match1({bin,_,_}, _, _Bs, _BBs) ->
+ ErrorFun = fun(A, R, S) -> apply_error(R, S, A, Bs0, Ef, none) end,
+ eval_bits:match_bits(Fs, B, Bs0, BBs, match_fun(BBs, Ef), EvalFun, ErrorFun);
+match1({bin,_,_}, _, _Bs, _BBs, _Ef) ->
throw(nomatch);
-match1({op,_,'++',{nil,_},R}, Term, Bs, BBs) ->
- match1(R, Term, Bs, BBs);
-match1({op,_,'++',{cons,Ai,{integer,A2,I},T},R}, Term, Bs, BBs) ->
- match1({cons,Ai,{integer,A2,I},{op,Ai,'++',T,R}}, Term, Bs, BBs);
-match1({op,_,'++',{cons,Ai,{char,A2,C},T},R}, Term, Bs, BBs) ->
- match1({cons,Ai,{char,A2,C},{op,Ai,'++',T,R}}, Term, Bs, BBs);
-match1({op,_,'++',{string,Ai,L},R}, Term, Bs, BBs) ->
- match1(string_to_conses(L, Ai, R), Term, Bs, BBs);
-match1({op,Anno,Op,A}, Term, Bs, BBs) ->
+match1({op,_,'++',{nil,_},R}, Term, Bs, BBs, Ef) ->
+ match1(R, Term, Bs, BBs, Ef);
+match1({op,_,'++',{cons,Ai,{integer,A2,I},T},R}, Term, Bs, BBs, Ef) ->
+ match1({cons,Ai,{integer,A2,I},{op,Ai,'++',T,R}}, Term, Bs, BBs, Ef);
+match1({op,_,'++',{cons,Ai,{char,A2,C},T},R}, Term, Bs, BBs, Ef) ->
+ match1({cons,Ai,{char,A2,C},{op,Ai,'++',T,R}}, Term, Bs, BBs, Ef);
+match1({op,_,'++',{string,Ai,L},R}, Term, Bs, BBs, Ef) ->
+ match1(string_to_conses(L, Ai, R), Term, Bs, BBs, Ef);
+match1({op,Anno,Op,A}, Term, Bs, BBs, Ef) ->
case partial_eval({op,Anno,Op,A}) of
{op,Anno,Op,A} ->
throw(invalid);
X ->
- match1(X, Term, Bs, BBs)
+ match1(X, Term, Bs, BBs, Ef)
end;
-match1({op,Anno,Op,L,R}, Term, Bs, BBs) ->
+match1({op,Anno,Op,L,R}, Term, Bs, BBs, Ef) ->
case partial_eval({op,Anno,Op,L,R}) of
{op,Anno,Op,L,R} ->
throw(invalid);
X ->
- match1(X, Term, Bs, BBs)
+ match1(X, Term, Bs, BBs, Ef)
end;
-match1(_, _, _Bs, _BBs) ->
+match1(_, _, _Bs, _BBs, _Ef) ->
throw(invalid).
-match_fun(BBs) ->
- fun(match, {L,R,Bs}) -> match1(L, R, Bs, BBs);
+match_fun(BBs, Ef) ->
+ fun(match, {L,R,Bs}) -> match1(L, R, Bs, BBs, Ef);
(binding, {Name,Bs}) -> binding(Name, Bs);
(add_binding, {Name,Val,Bs}) -> add_binding(Name, Val, Bs)
end.
-match_tuple([E|Es], Tuple, I, Bs0, BBs) ->
- {match,Bs} = match1(E, element(I, Tuple), Bs0, BBs),
- match_tuple(Es, Tuple, I+1, Bs, BBs);
-match_tuple([], _, _, Bs, _BBs) ->
+match_tuple([E|Es], Tuple, I, Bs0, BBs, Ef) ->
+ {match,Bs} = match1(E, element(I, Tuple), Bs0, BBs, Ef),
+ match_tuple(Es, Tuple, I+1, Bs, BBs, Ef);
+match_tuple([], _, _, Bs, _BBs, _Ef) ->
{match,Bs}.
-match_map([{map_field_exact, _, K, V}|Fs], Map, Bs0, BBs) ->
+match_map([{map_field_exact, _, K, V}|Fs], Map, Bs0, BBs, Ef) ->
Vm = try
{value, Ke, _} = expr(K, BBs),
maps:get(Ke,Map)
catch error:_ ->
throw(nomatch)
end,
- {match, Bs} = match1(V, Vm, Bs0, BBs),
- match_map(Fs, Map, Bs, BBs);
-match_map([], _, Bs, _) ->
+ {match, Bs} = match1(V, Vm, Bs0, BBs, Ef),
+ match_map(Fs, Map, Bs, BBs, Ef);
+match_map([], _, Bs, _, _) ->
{match, Bs}.
-%% match_list(PatternList, TermList, Bindings) ->
+%% match_list(PatternList, TermList, Anno, NewBindings, Bindings, ExternalFunHnd) ->
%% {match,NewBindings} | nomatch
%% Try to match a list of patterns against a list of terms with the
%% current bindings.
-match_list(Ps, Ts, Bs) ->
- match_list(Ps, Ts, Bs, Bs).
-
-match_list([P|Ps], [T|Ts], Bs0, BBs) ->
- case match(P, T, Bs0, BBs) of
- {match,Bs1} -> match_list(Ps, Ts, Bs1, BBs);
+match_list([P|Ps], [T|Ts], Anno, Bs0, BBs, Ef) ->
+ case match(P, T, Anno, Bs0, BBs, Ef) of
+ {match,Bs1} -> match_list(Ps, Ts, Anno, Bs1, BBs, Ef);
nomatch -> nomatch
end;
-match_list([], [], Bs, _BBs) ->
+match_list([], [], _Anno, Bs, _BBs, _Ef) ->
{match,Bs};
-match_list(_, _, _Bs, _BBs) ->
+match_list(_, _, _Anno, _Bs, _BBs, _Ef) ->
nomatch.
%% new_bindings()
@@ -1313,17 +1317,17 @@ add_bindings(Bs1, Bs2) ->
foldl(fun ({Name,Val}, Bs) -> orddict:store(Name, Val, Bs) end,
Bs2, orddict:to_list(Bs1)).
-merge_bindings(Bs1, Bs2) when is_map(Bs1), is_map(Bs2) ->
+merge_bindings(Bs1, Bs2, Anno, Ef) when is_map(Bs1), is_map(Bs2) ->
maps:merge_with(fun
(_K, V, V) -> V;
- (_K, _, V) -> erlang:raise(error, {badmatch,V}, ?STACKTRACE)
+ (_K, _, V) -> apply_error({badmatch,V}, ?STACKTRACE, Anno, Bs1, Ef, none)
end, Bs2, Bs1);
-merge_bindings(Bs1, Bs2) ->
+merge_bindings(Bs1, Bs2, Anno, Ef) ->
foldl(fun ({Name,Val}, Bs) ->
case orddict:find(Name, Bs) of
{ok,Val} -> Bs; %Already with SAME value
{ok,V1} ->
- erlang:raise(error, {badmatch,V1}, ?STACKTRACE);
+ apply_error({badmatch,V1}, ?STACKTRACE, Anno, Bs1, Ef, none);
error -> orddict:store(Name, Val, Bs)
end end,
Bs2, orddict:to_list(Bs1)).
@@ -1413,10 +1417,10 @@ expr_fixup([E0|Es0]) ->
expr_fixup(T) ->
T.
-string_fixup(Ann, String, Token) ->
- Text = erl_anno:text(Ann),
+string_fixup(Anno, String, Token) ->
+ Text = erl_anno:text(Anno),
FixupTag = fixup_tag(Text, String),
- fixup_ast(FixupTag, Ann, String, Token).
+ fixup_ast(FixupTag, Anno, String, Token).
reset_token_anno(Tokens) ->
[setelement(2, T, (reset_anno())(element(2, T))) || T <- Tokens].
@@ -1503,7 +1507,7 @@ normalise({bin,_,Fs}) ->
eval_bits:expr_grp(Fs, [],
fun(E, _) ->
{value, normalise(E), []}
- end, [], true),
+ end),
B;
normalise({cons,_,Head,Tail}) ->
[normalise(Head)|normalise(Tail)];
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 33569d4a8d..f562c6d50a 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -1397,7 +1397,7 @@ normalise({bin,_,Fs}) ->
eval_bits:expr_grp(Fs, [],
fun(E, _) ->
{value, normalise(E), []}
- end, [], true),
+ end),
B;
normalise({cons,_,Head,Tail}) ->
[normalise(Head)|normalise(Tail)];
diff --git a/lib/stdlib/src/eval_bits.erl b/lib/stdlib/src/eval_bits.erl
index 966bc65902..09ec175dc4 100644
--- a/lib/stdlib/src/eval_bits.erl
+++ b/lib/stdlib/src/eval_bits.erl
@@ -22,8 +22,8 @@
%% Avoid warning for local function error/1 clashing with autoimported BIF.
-compile({no_auto_import,[error/1]}).
--export([expr_grp/3,expr_grp/5,match_bits/6,
- match_bits/7,bin_gen/6]).
+-export([expr_grp/3,expr_grp/4,match_bits/6,
+ match_bits/7,bin_gen/6,bin_gen/7]).
-define(STACKTRACE,
element(2, erlang:process_info(self(), current_stacktrace))).
@@ -40,11 +40,13 @@
%% lookup a variable in the bindings, or add a new binding
%%
%% @type field(). Represents a field in a "bin".
+%%
+%% @type errorfun(). A closure invoked in case of errors.
%%% Part 1: expression evaluation (binary construction)
%% @spec expr_grp(Fields::[field()], Bindings::bindings(),
-%% EvalFun::evalfun(), term(), term()) ->
+%% EvalFun::evalfun(), ErrorFun::errorfun()) ->
%% {value, binary(), bindings()}
%%
%% @doc Returns a tuple with {value,Bin,Bs} where Bin is the binary
@@ -52,29 +54,23 @@
%% contains the present bindings. This function can also throw an
%% exception if the construction fails.
-expr_grp(Fields, Bindings, EvalFun, [], _) ->
- expr_grp(Fields, Bindings, EvalFun, <<>>);
-expr_grp(Fields, Bindings, EvalFun, ListOfBits, _) ->
- Bin = convert_list(ListOfBits),
- expr_grp(Fields, Bindings, EvalFun, Bin).
-
-convert_list(List) ->
- << <<X:1>> || X <- List >>.
+expr_grp(Fields, Bindings, EvalFun, ErrorFun) ->
+ expr_grp(Fields, Bindings, EvalFun, ErrorFun, <<>>).
expr_grp(Fields, Bindings, EvalFun) ->
- expr_grp(Fields, Bindings, EvalFun, <<>>).
+ expr_grp(Fields, Bindings, EvalFun, fun default_error/3, <<>>).
-expr_grp(FS, Bs0, Ef, Acc) ->
+expr_grp(FS, Bs0, EvalFun, ErrorFun, Acc) ->
%% Separate the evaluation of values, sizes, and TLS:s from the
%% creation of the binary in order to mimic compiled code when it
%% comes to loops and failures.
- {ListOfEvalField,Bs1} = expr_grp1(FS, Bs0, Ef, []),
+ {ListOfEvalField,Bs1} = expr_grp1(FS, Bs0, EvalFun, ErrorFun, []),
{value,create_binary(ListOfEvalField, Acc),Bs1}.
-expr_grp1([Field | FS], Bs0, Ef, ListOfEvalField) ->
- {EvalField,Bs} = eval_field(Field, Bs0, Ef),
- expr_grp1(FS, Bs, Ef, [EvalField|ListOfEvalField]);
-expr_grp1([], Bs, _Ef, ListOfFieldData) ->
+expr_grp1([Field | FS], Bs0, EvalFun, ErrorFun, ListOfEvalField) ->
+ {EvalField,Bs} = eval_field(Field, Bs0, EvalFun, ErrorFun),
+ expr_grp1(FS, Bs, EvalFun, ErrorFun, [EvalField|ListOfEvalField]);
+expr_grp1([], Bs, _EvalFun, _ErrorFun, ListOfFieldData) ->
{lists:reverse(ListOfFieldData),Bs}.
create_binary([EvalField|ListOfEvalField], Acc) ->
@@ -83,44 +79,44 @@ create_binary([EvalField|ListOfEvalField], Acc) ->
create_binary([], Acc) ->
Acc.
-eval_field({bin_element, _, {string, _, S}, {integer,_,8}, [integer,{unit,1},unsigned,big]}, Bs0, _Fun) ->
+eval_field({bin_element, _, {string, _, S}, {integer,_,8}, [integer,{unit,1},unsigned,big]}, Bs0, _Fun, _ErrorFun) ->
Latin1 = [C band 16#FF || C <- S],
{fun() -> list_to_binary(Latin1) end,Bs0};
-eval_field({bin_element, _, {string, _, S}, default, default}, Bs0, _Fun) ->
+eval_field({bin_element, _, {string, _, S}, default, default}, Bs0, _Fun, _ErrorFun) ->
Latin1 = [C band 16#FF || C <- S],
{fun() ->list_to_binary(Latin1) end,Bs0};
-eval_field({bin_element, Anno, {string, _, S}, Size0, Options0}, Bs0, Fun) ->
+eval_field({bin_element, Anno, {string, _, S}, Size0, Options0}, Bs0, Fun, ErrorFun) ->
{Size1,[Type,{unit,Unit},Sign,Endian]} =
- make_bit_type(Anno, Size0, Options0),
+ make_bit_type(Anno, Size0, Options0, ErrorFun),
{value,Size,Bs1} = Fun(Size1, Bs0),
{fun() ->
- Res = << <<(eval_exp_field1(C, Size, Unit,
- Type, Endian, Sign))/bitstring>> ||
+ Res = << <<(eval_exp_field1(Anno, C, Size, Unit,
+ Type, Endian, Sign, ErrorFun))/bitstring>> ||
C <- S >>,
case S of
"" -> % find errors also when the string is empty
- _ = eval_exp_field1(0, Size, Unit, Type, Endian, Sign),
+ _ = eval_exp_field1(Anno, 0, Size, Unit, Type, Endian, Sign, ErrorFun),
ok;
_ ->
ok
end,
Res
end,Bs1};
-eval_field({bin_element,Anno,E,Size0,Options0}, Bs0, Fun) ->
+eval_field({bin_element,Anno,E,Size0,Options0}, Bs0, Fun, ErrorFun) ->
{value,V,Bs1} = Fun(E, Bs0),
{Size1,[Type,{unit,Unit},Sign,Endian]} =
- make_bit_type(Anno, Size0, Options0),
+ make_bit_type(Anno, Size0, Options0, ErrorFun),
{value,Size,Bs} = Fun(Size1, Bs1),
- {fun() -> eval_exp_field1(V, Size, Unit, Type, Endian, Sign) end,Bs}.
+ {fun() -> eval_exp_field1(Anno, V, Size, Unit, Type, Endian, Sign, ErrorFun) end,Bs}.
-eval_exp_field1(V, Size, Unit, Type, Endian, Sign) ->
+eval_exp_field1(Anno, V, Size, Unit, Type, Endian, Sign, ErrorFun) ->
try
eval_exp_field(V, Size, Unit, Type, Endian, Sign)
catch
error:system_limit ->
- erlang:raise(error, system_limit, ?STACKTRACE);
+ ErrorFun(Anno, system_limit, ?STACKTRACE);
error:_ ->
- erlang:raise(error, badarg, ?STACKTRACE)
+ ErrorFun(Anno, badarg, ?STACKTRACE)
end.
eval_exp_field(Val, Size, Unit, integer, little, signed) ->
@@ -169,7 +165,8 @@ eval_exp_field(Val, Size, Unit, binary, _, _) ->
%%% Part 2: matching in binary comprehensions
%% @spec bin_gen(BinPattern::{bin,integer(),[field()]}, Bin::binary(),
%% GlobalEnv::bindings(), LocalEnv::bindings(),
-%% MatchFun::matchfun(), EvalFun::evalfun()) ->
+%% MatchFun::matchfun(), EvalFun::evalfun(),
+%% ErrorFun::errorfun()) ->
%% {match, binary(), bindings()} | {nomatch, binary()} | done
%%
%% @doc Used to perform matching in a comprehension. If the match
@@ -178,25 +175,28 @@ eval_exp_field(Val, Size, Unit, binary, _, _) ->
%% If nothing remains of the binary the atom 'done' is returned.
bin_gen({bin,_,Fs}, Bin, Bs0, BBs0, Mfun, Efun) ->
- bin_gen(Fs, Bin, Bs0, BBs0, Mfun, Efun, true).
+ bin_gen(Fs, Bin, Bs0, BBs0, Mfun, Efun, fun default_error/3, true).
-bin_gen([F|Fs], Bin, Bs0, BBs0, Mfun, Efun, Flag)
+bin_gen({bin,_,Fs}, Bin, Bs0, BBs0, Mfun, Efun, ErrorFun) ->
+ bin_gen(Fs, Bin, Bs0, BBs0, Mfun, Efun, ErrorFun, true).
+
+bin_gen([F|Fs], Bin, Bs0, BBs0, Mfun, Efun, ErrorFun, Flag)
when is_function(Mfun, 2), is_function(Efun, 2) ->
- case bin_gen_field(F, Bin, Bs0, BBs0, Mfun, Efun) of
+ case bin_gen_field(F, Bin, Bs0, BBs0, Mfun, Efun, ErrorFun) of
{match,Bs,BBs,Rest} ->
- bin_gen(Fs, Rest, Bs, BBs, Mfun, Efun, Flag);
+ bin_gen(Fs, Rest, Bs, BBs, Mfun, Efun, ErrorFun, Flag);
{nomatch,Rest} ->
- bin_gen(Fs, Rest, Bs0, BBs0, Mfun, Efun, false);
+ bin_gen(Fs, Rest, Bs0, BBs0, Mfun, Efun, ErrorFun, false);
done ->
done
end;
-bin_gen([], Bin, Bs0, _BBs0, _Mfun, _Efun, true) ->
+bin_gen([], Bin, Bs0, _BBs0, _Mfun, _Efun, _ErrorFun, true) ->
{match, Bin, Bs0};
-bin_gen([], Bin, _Bs0, _BBs0, _Mfun, _Efun, false) ->
+bin_gen([], Bin, _Bs0, _BBs0, _Mfun, _Efun, _ErrorFun, false) ->
{nomatch, Bin}.
bin_gen_field({bin_element,_,{string,_,S},default,default},
- Bin, Bs, BBs, _Mfun, _Efun) ->
+ Bin, Bs, BBs, _Mfun, _Efun, _ErrorFun) ->
Bits = try list_to_binary(S)
catch _:_ -> <<>>
end,
@@ -210,9 +210,9 @@ bin_gen_field({bin_element,_,{string,_,S},default,default},
done
end;
bin_gen_field({bin_element,Anno,{string,SAnno,S},Size0,Options0},
- Bin0, Bs0, BBs0, Mfun, Efun) ->
+ Bin0, Bs0, BBs0, Mfun, Efun, ErrorFun) ->
{Size1, [Type,{unit,Unit},Sign,Endian]} =
- make_bit_type(Anno, Size0, Options0),
+ make_bit_type(Anno, Size0, Options0, ErrorFun),
case catch Efun(Size1, BBs0) of
{value, Size, _BBs} -> %
F = fun(C, Bin, Bs, BBs) ->
@@ -222,9 +222,9 @@ bin_gen_field({bin_element,Anno,{string,SAnno,S},Size0,Options0},
bin_gen_field_string(S, Bin0, Bs0, BBs0, F)
end;
bin_gen_field({bin_element,Anno,VE,Size0,Options0},
- Bin, Bs0, BBs0, Mfun, Efun) ->
+ Bin, Bs0, BBs0, Mfun, Efun, ErrorFun) ->
{Size1, [Type,{unit,Unit},Sign,Endian]} =
- make_bit_type(Anno, Size0, Options0),
+ make_bit_type(Anno, Size0, Options0, ErrorFun),
V = erl_eval:partial_eval(VE),
NewV = coerce_to_float(V, Type),
case catch Efun(Size1, BBs0) of
@@ -264,41 +264,42 @@ bin_gen_field1(Bin, Type, Size, Unit, Sign, Endian, NewV, Bs0, BBs0, Mfun) ->
%%% Part 3: binary pattern matching
%% @spec match_bits(Fields::[field()], Bin::binary(),
%% GlobalEnv::bindings(), LocalEnv::bindings(),
-%% MatchFun::matchfun(),EvalFun::evalfun(), term()) ->
-%% {match, bindings()}
+%% MatchFun::matchfun(),EvalFun::evalfun(),
+%% ErrorFun::errorfun()) ->
+%% {match, bindings()}
%% @doc Used to perform matching. If the match succeeds a new
%% environment is returned. If the match have some syntactic or
%% semantic problem which would have been caught at compile time this
%% function throws 'invalid', if the matching fails for other reasons
%% the function throws 'nomatch'
-match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun, _) ->
- match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun).
+match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun) ->
+ match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun, fun default_error/3).
-match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun)
- when is_function(Mfun, 2), is_function(Efun, 2) ->
- case catch match_bits_1(Fs, Bin, Bs0, BBs, Mfun, Efun) of
+match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun, ErrorFun)
+ when is_function(Mfun, 2), is_function(Efun, 2), is_function(ErrorFun, 3) ->
+ case catch match_bits_1(Fs, Bin, Bs0, BBs, Mfun, Efun, ErrorFun) of
{match,Bs} -> {match,Bs};
invalid -> throw(invalid);
_Error -> throw(nomatch)
end.
-match_bits_1([], <<>>, Bs, _BBs, _Mfun, _Efun) ->
+match_bits_1([], <<>>, Bs, _BBs, _Mfun, _Efun, _ErrorFun) ->
{match,Bs};
-match_bits_1([F|Fs], Bits0, Bs0, BBs0, Mfun, Efun) ->
- {Bs,BBs,Bits} = match_field_1(F, Bits0, Bs0, BBs0, Mfun, Efun),
- match_bits_1(Fs, Bits, Bs, BBs, Mfun, Efun).
+match_bits_1([F|Fs], Bits0, Bs0, BBs0, Mfun, Efun, ErrorFun) ->
+ {Bs,BBs,Bits} = match_field_1(F, Bits0, Bs0, BBs0, Mfun, Efun, ErrorFun),
+ match_bits_1(Fs, Bits, Bs, BBs, Mfun, Efun, ErrorFun).
match_field_1({bin_element,_,{string,_,S},default,default},
- Bin, Bs, BBs, _Mfun, _Efun) ->
+ Bin, Bs, BBs, _Mfun, _Efun, _ErrorFun) ->
Bits = list_to_binary(S), % fails if there are characters > 255
Size = byte_size(Bits),
<<Bits:Size/binary,Rest/binary-unit:1>> = Bin,
{Bs,BBs,Rest};
match_field_1({bin_element,Anno,{string,SAnno,S},Size0,Options0},
- Bin0, Bs0, BBs0, Mfun, Efun) ->
+ Bin0, Bs0, BBs0, Mfun, Efun, ErrorFun) ->
{Size1, [Type,{unit,Unit},Sign,Endian]} =
- make_bit_type(Anno, Size0, Options0),
+ make_bit_type(Anno, Size0, Options0, ErrorFun),
Size2 = erl_eval:partial_eval(Size1),
{value, Size, _BBs} = Efun(Size2, BBs0),
F = fun(C, Bin, Bs, BBs) ->
@@ -307,9 +308,9 @@ match_field_1({bin_element,Anno,{string,SAnno,S},Size0,Options0},
end,
match_field_string(S, Bin0, Bs0, BBs0, F);
match_field_1({bin_element,Anno,VE,Size0,Options0},
- Bin, Bs0, BBs0, Mfun, Efun) ->
+ Bin, Bs0, BBs0, Mfun, Efun, ErrorFun) ->
{Size1, [Type,{unit,Unit},Sign,Endian]} =
- make_bit_type(Anno, Size0, Options0),
+ make_bit_type(Anno, Size0, Options0, ErrorFun),
V = erl_eval:partial_eval(VE),
NewV = coerce_to_float(V, Type),
Size2 = erl_eval:partial_eval(Size1),
@@ -410,15 +411,18 @@ get_float(Bin, Size, big) ->
{Val,Rest}.
%% Identical to the one in sys_pre_expand.
-make_bit_type(Anno, default, Type0) ->
+make_bit_type(Anno, default, Type0, ErrorFun) ->
case erl_bits:set_bit_type(default, Type0) of
{ok,all,Bt} -> {{atom,Anno,all},erl_bits:as_list(Bt)};
{ok,undefined,Bt} -> {{atom,Anno,undefined},erl_bits:as_list(Bt)};
{ok,Size,Bt} -> {{integer,Anno,Size},erl_bits:as_list(Bt)};
- {error,Reason} -> erlang:raise(error, Reason, ?STACKTRACE)
+ {error,Reason} -> ErrorFun(Anno, Reason, ?STACKTRACE)
end;
-make_bit_type(_Anno, Size, Type0) -> %Size evaluates to an integer or 'all'
+make_bit_type(Anno, Size, Type0, ErrorFun) -> %Size evaluates to an integer or 'all'
case erl_bits:set_bit_type(Size, Type0) of
{ok,Size,Bt} -> {Size,erl_bits:as_list(Bt)};
- {error,Reason} -> erlang:raise(error, Reason, ?STACKTRACE)
+ {error,Reason} -> ErrorFun(Anno, Reason, ?STACKTRACE)
end.
+
+default_error(_Anno, Reason, Stacktrace) ->
+ erlang:raise(error, Reason, Stacktrace).
diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl
index b9fa534a69..51d4600d40 100644
--- a/lib/stdlib/src/ms_transform.erl
+++ b/lib/stdlib/src/ms_transform.erl
@@ -1126,7 +1126,7 @@ normalise({bin,_,Fs}) ->
eval_bits:expr_grp(Fs, [],
fun(E, _) ->
{value, normalise(E), []}
- end, [], true),
+ end),
B;
normalise({cons,_,Head,Tail}) ->
[normalise(Head)|normalise(Tail)];
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index f4d58e61c5..16a92f7ff5 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -43,6 +43,7 @@
otp_13228/1,
otp_14826/1,
funs/1,
+ custom_stacktrace/1,
try_catch/1,
eval_expr_5/1,
zero_width/1,
@@ -91,7 +92,7 @@ all() ->
simple_cases, unary_plus, apply_atom, otp_5269,
otp_6539, otp_6543, otp_6787, otp_6977, otp_7550,
otp_8133, otp_10622, otp_13228, otp_14826,
- funs, try_catch, eval_expr_5, zero_width,
+ funs, custom_stacktrace, try_catch, eval_expr_5, zero_width,
eep37, eep43, otp_15035, otp_16439, otp_14708, otp_16545, otp_16865,
binary_skip].
@@ -1000,7 +1001,7 @@ otp_14826(_Config) ->
backtrace_check("fun(P) when is_pid(P) -> true end(a).",
function_clause,
[{erl_eval,'-inside-an-interpreted-fun-',[a],[]},
- {erl_eval,eval_fun,7},
+ {erl_eval,eval_fun,8},
?MODULE]),
backtrace_check("B.",
{unbound_var, 'B'},
@@ -1012,11 +1013,11 @@ otp_14826(_Config) ->
backtrace_check("1/0.",
badarith,
[{erlang,'/',[1,0],[]},
- {erl_eval,do_apply,6}]),
+ {erl_eval,do_apply,7}]),
backtrace_catch("catch 1/0.",
badarith,
[{erlang,'/',[1,0],[]},
- {erl_eval,do_apply,6}]),
+ {erl_eval,do_apply,7}]),
check(fun() -> catch exit(foo) end,
"catch exit(foo).",
{'EXIT', foo}),
@@ -1026,33 +1027,33 @@ otp_14826(_Config) ->
backtrace_check("try 1/0 after foo end.",
badarith,
[{erlang,'/',[1,0],[]},
- {erl_eval,do_apply,6}]),
+ {erl_eval,do_apply,7}]),
backtrace_catch("catch (try 1/0 after foo end).",
badarith,
[{erlang,'/',[1,0],[]},
- {erl_eval,do_apply,6}]),
+ {erl_eval,do_apply,7}]),
backtrace_catch("try catch 1/0 after foo end.",
badarith,
[{erlang,'/',[1,0],[]},
- {erl_eval,do_apply,6}]),
+ {erl_eval,do_apply,7}]),
backtrace_check("try a of b -> bar after foo end.",
{try_clause,a},
- [{erl_eval,try_clauses,9}]),
+ [{erl_eval,try_clauses,10}]),
check(fun() -> X = try foo:bar() catch A:B:C -> {A,B} end, X end,
"try foo:bar() catch A:B:C -> {A,B} end.",
{error, undef}),
backtrace_check("C = 4, try foo:bar() catch A:B:C -> {A,B,C} end.",
stacktrace_bound,
- [{erl_eval,check_stacktrace_vars,2},
- {erl_eval,try_clauses,9}],
+ [{erl_eval,check_stacktrace_vars,5},
+ {erl_eval,try_clauses,10}],
none, none),
backtrace_catch("catch (try a of b -> bar after foo end).",
{try_clause,a},
- [{erl_eval,try_clauses,9}]),
+ [{erl_eval,try_clauses,10}]),
backtrace_check("try 1/0 catch exit:a -> foo end.",
badarith,
[{erlang,'/',[1,0],[]},
- {erl_eval,do_apply,6}]),
+ {erl_eval,do_apply,7}]),
Es = [{'try',1,[{call,1,{remote,1,{atom,1,foo},{atom,1,bar}},[]}],
[],
[{clause,1,[{tuple,1,[{var,1,'A'},{var,1,'B'},{atom,1,'C'}]}],
@@ -1062,8 +1063,8 @@ otp_14826(_Config) ->
ct:fail(stacktrace_variable)
catch
error:{illegal_stacktrace_variable,{atom,1,'C'}}:S ->
- [{erl_eval,check_stacktrace_vars,2,_},
- {erl_eval,try_clauses,9,_}|_] = S
+ [{erl_eval,check_stacktrace_vars,5,_},
+ {erl_eval,try_clauses,10,_}|_] = S
end,
backtrace_check("{1,1} = {A = 1, A = 2}.",
{badmatch, 1},
@@ -1073,40 +1074,40 @@ otp_14826(_Config) ->
[{erl_eval,guard0,4}], none, none),
backtrace_check("case a of foo() -> ok end.",
{illegal_pattern,{call,1,{atom,1,foo},[]}},
- [{erl_eval,match,4}], none, none),
+ [{erl_eval,match,6}], none, none),
backtrace_check("case a of b -> ok end.",
{case_clause,a},
- [{erl_eval,case_clauses,7}, ?MODULE]),
+ [{erl_eval,case_clauses,8}, ?MODULE]),
backtrace_check("if a =:= b -> ok end.",
if_clause,
- [{erl_eval,if_clauses,6}, ?MODULE]),
+ [{erl_eval,if_clauses,7}, ?MODULE]),
backtrace_check("fun A(b) -> ok end(a).",
function_clause,
[{erl_eval,'-inside-an-interpreted-fun-',[a],[]},
- {erl_eval,eval_named_fun,9},
+ {erl_eval,eval_named_fun,10},
?MODULE]),
backtrace_check("[A || A <- a].",
{bad_generator, a},
- [{erl_eval,eval_generate,7}, {erl_eval, eval_lc, 7}]),
+ [{erl_eval,eval_generate,8}, {erl_eval, eval_lc, 7}]),
backtrace_check("<< <<A>> || <<A>> <= a>>.",
{bad_generator, a},
- [{erl_eval,eval_b_generate,7}, {erl_eval, eval_bc, 7}]),
+ [{erl_eval,eval_b_generate,8}, {erl_eval, eval_bc, 7}]),
backtrace_check("[A || A <- [1], begin a end].",
{bad_filter, a},
- [{erl_eval,eval_filter,7}, {erl_eval, eval_generate, 7}]),
+ [{erl_eval,eval_filter,7}, {erl_eval, eval_generate, 8}]),
fun() ->
{'EXIT', {{badarity, {_Fun, []}}, BT}} =
(catch parse_and_run("fun(A) -> A end().")),
- check_backtrace([{erl_eval,do_apply,5}, ?MODULE], BT)
+ check_backtrace([{erl_eval,do_apply,6}, ?MODULE], BT)
end(),
fun() ->
{'EXIT', {{badarity, {_Fun, []}}, BT}} =
(catch parse_and_run("fun F(A) -> A end().")),
- check_backtrace([{erl_eval,do_apply,5}, ?MODULE], BT)
+ check_backtrace([{erl_eval,do_apply,6}, ?MODULE], BT)
end(),
backtrace_check("foo().",
undef,
- [{erl_eval,foo,0},{erl_eval,local_func,7}],
+ [{erl_eval,foo,0},{erl_eval,local_func,8}],
none, none),
backtrace_check("a orelse false.",
{badarg, a},
@@ -1141,16 +1142,16 @@ otp_14826(_Config) ->
%% eval_bits
backtrace_check("<<100:8/bitstring>>.",
badarg,
- [{eval_bits,eval_exp_field1,6},
+ [{eval_bits,eval_exp_field1,8},
eval_bits,eval_bits,erl_eval]),
backtrace_check("<<100:8/foo>>.",
{undefined_bittype,foo},
- [{eval_bits,make_bit_type,3},eval_bits,
+ [{eval_bits,make_bit_type,4},eval_bits,
eval_bits,eval_bits],
none, none),
backtrace_check("B = <<\"foo\">>, <<B/binary-unit:7>>.",
badarg,
- [{eval_bits,eval_exp_field1,6},
+ [{eval_bits,eval_exp_field1,8},
eval_bits,eval_bits,erl_eval],
none, none),
ok.
@@ -1172,13 +1173,143 @@ simple1() ->
WillNeverHappen -> WillNeverHappen
end.
+custom_stacktrace(Config) when is_list(Config) ->
+ EFH = {value, fun custom_stacktrace_eval_handler/3},
+
+ backtrace_check("1 + atom.", badarith,
+ [{erlang,'+',[1,atom]}, mystack(1)], none, EFH),
+ backtrace_check("\n1 + atom.", badarith,
+ [{erlang,'+',[1,atom]}, mystack(2)], none, EFH),
+
+ backtrace_check("lists:flatten(atom).", function_clause,
+ [{lists,flatten,[atom]}, mystack(1)], none, EFH),
+
+ backtrace_check("invalid andalso true.", {badarg, invalid},
+ [mystack(1)], none, EFH),
+ backtrace_check("invalid orelse true.", {badarg, invalid},
+ [mystack(1)], none, EFH),
+
+ backtrace_check("invalid = valid.", {badmatch, valid},
+ [erl_eval, mystack(1)], none, EFH),
+
+ backtrace_check("1:2.", {badexpr, ':'},
+ [erl_eval, mystack(1)], none, EFH),
+
+ backtrace_check("Unknown.", {unbound, 'Unknown'},
+ [erl_eval, mystack(1)], none, EFH),
+
+ backtrace_check("#unknown{}.", {undef_record,unknown},
+ [erl_eval, mystack(1)], none, EFH),
+ backtrace_check("#unknown{foo=bar}.", {undef_record,unknown},
+ [erl_eval, mystack(1)], none, EFH),
+ backtrace_check("#unknown.index.", {undef_record,unknown},
+ [erl_eval, mystack(1)], none, EFH),
+
+ backtrace_check("fun foo/2.", undef,
+ [{erl_eval, foo, 2}, erl_eval, mystack(1)], none, EFH),
+ backtrace_check("foo(1, 2).", undef,
+ [{erl_eval, foo, 2}, erl_eval, mystack(1)], none, EFH),
+
+ fun() ->
+ {'EXIT', {{badarity, {_Fun, []}}, BT}} =
+ (catch parse_and_run("fun(A) -> A end().", none, EFH)),
+ check_backtrace([erl_eval, mystack(1)], BT)
+ end(),
+
+ fun() ->
+ {'EXIT', {{badarity, {_Fun, []}}, BT}} =
+ (catch parse_and_run("fun F(A) -> A end().", none, EFH)),
+ check_backtrace([erl_eval, mystack(1)], BT)
+ end(),
+
+ backtrace_check("[X || X <- 1].", {bad_generator, 1},
+ [erl_eval, mystack(1)], none, EFH),
+ backtrace_check("[X || <<X>> <= 1].", {bad_generator, 1},
+ [erl_eval, mystack(1)], none, EFH),
+ backtrace_check("<<X || X <- 1>>.", {bad_generator, 1},
+ [erl_eval, mystack(1)], none, EFH),
+ backtrace_check("<<X || <<X>> <= 1>>.", {bad_generator, 1},
+ [erl_eval, mystack(1)], none, EFH),
+
+ backtrace_check("if false -> true end.", if_clause,
+ [erl_eval, mystack(1)], none, EFH),
+ backtrace_check("case 0 of 1 -> true end.", {case_clause, 0},
+ [erl_eval, mystack(1)], none, EFH),
+ backtrace_check("try 0 of 1 -> true after ok end.", {try_clause, 0},
+ [mystack(1)], none, EFH),
+
+ backtrace_check("fun(0) -> 1 end(1).", function_clause,
+ [{erl_eval,'-inside-an-interpreted-fun-', [1]}, erl_eval, mystack(1)],
+ none, EFH),
+ backtrace_check("fun F(0) -> 1 end(1).", function_clause,
+ [{erl_eval,'-inside-an-interpreted-fun-', [1]}, erl_eval, mystack(1)],
+ none, EFH),
+
+ fun() ->
+ {'EXIT', {{illegal_pattern,_}, BT}} =
+ (catch parse_and_run("make_ref() = 1.", none, EFH)),
+ check_backtrace([erl_eval, mystack(1)], BT)
+ end(),
+
+ %% eval_bits
+ backtrace_check("<<100:8/bitstring>>.",
+ badarg,
+ [{eval_bits,eval_exp_field1,8}, mystack(1)],
+ none, EFH),
+ backtrace_check("<<100:8/foo>>.",
+ {undefined_bittype,foo},
+ [{eval_bits,make_bit_type,4}, mystack(1)],
+ none, EFH),
+ backtrace_check("B = <<\"foo\">>, <<B/binary-unit:7>>.",
+ badarg,
+ [{eval_bits,eval_exp_field1,8}, mystack(1)],
+ none, EFH),
+
+ ok.
+
+mystack(Line) ->
+ {my_module, my_function, 0, [{file, "evaluator"}, {line, Line}]}.
+
+custom_stacktrace_eval_handler(Ann, FunOrModFun, Args) ->
+ try
+ case FunOrModFun of
+ {Mod, Fun} -> apply(Mod, Fun, Args);
+ Fun -> apply(Fun, Args)
+ end
+ catch
+ Kind:Reason:Stacktrace ->
+ %% Take everything up to the evaluation function
+ Pruned =
+ lists:takewhile(fun
+ ({erl_eval_SUITE,backtrace_check,5,_}) -> false;
+ (_) -> true
+ end, Stacktrace),
+ %% Now we prune any shared code path from erl_eval
+ {current_stacktrace, Current} =
+ erlang:process_info(self(), current_stacktrace),
+ Reversed = drop_common(lists:reverse(Current), lists:reverse(Pruned)),
+ Location = [{file, "evaluator"}, {line, erl_anno:line(Ann)}],
+ %% Add our file+line information at the bottom
+ Custom = lists:reverse([{my_module, my_function, 0, Location} | Reversed]),
+ erlang:raise(Kind, Reason, Custom)
+ end.
+
+drop_common([H | T1], [H | T2]) -> drop_common(T1, T2);
+drop_common([H | T1], T2) -> drop_common(T1, T2);
+drop_common([], [{?MODULE, custom_stacktrace_eval_handler, _, _} | T2]) -> T2;
+drop_common([], T2) -> T2.
+
%% Simple cases, just to cover some code.
funs(Config) when is_list(Config) ->
do_funs(none, none),
do_funs(lfh(), none),
+ do_funs(none, efh()),
do_funs(lfh(), efh()),
+ do_funs(none, ann_efh()),
+ do_funs(lfh(), ann_efh()),
error_check("nix:foo().", {access_not_allowed,nix}, lfh(), efh()),
+ error_check("nix:foo().", {access_not_allowed,nix}, lfh(), ann_efh()),
error_check("bar().", undef, none, none),
check(fun() -> F1 = fun(F,N) -> ?MODULE:count_down(F, N) end,
@@ -1217,6 +1348,15 @@ funs(Config) when is_list(Config) ->
error_check("apply(timer, sleep, [1]).", got_it, none, EFH),
error_check("begin F = fun(T) -> timer:sleep(T) end,F(1) end.",
got_it, none, EFH),
+
+ AnnEF = fun(1, {timer,sleep}, As) when length(As) == 1 -> exit({got_it,sleep});
+ (1, {M,F}, As) -> apply(M, F, As)
+ end,
+ AnnEFH = {value, AnnEF},
+ error_check("apply(timer, sleep, [1]).", got_it, none, AnnEFH),
+ error_check("begin F = fun(T) -> timer:sleep(T) end,F(1) end.",
+ got_it, none, AnnEFH),
+
error_check("fun c/1.", undef),
error_check("fun a:b/0().", undef),
@@ -1398,6 +1538,9 @@ local_func_value(F, As) when is_atom(F) ->
efh() ->
{value, fun(F, As) -> external_func(F, As) end}.
+ann_efh() ->
+ {value, fun(_Ann, F, As) -> external_func(F, As) end}.
+
external_func({M,_}, _As) when M == nix ->
exit({{access_not_allowed,M},[mfa]});
external_func(F, As) when is_function(F) ->
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index 56f1bb5b6f..89b745057f 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -256,6 +256,8 @@ restricted_local(Config) when is_list(Config) ->
local_allowed(_,_,State) ->
{false,State}.
+ non_local_allowed({erlang,raise},[error, _, _],State) ->
+ {true,State};
non_local_allowed({shell,stop_restricted},[],State) ->
{true,State};
non_local_allowed(_,_,State) ->
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 354b65c9a3..f62cfb4108 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -7310,7 +7310,7 @@ concrete(Node) ->
eval_bits:expr_grp(Fs, [],
fun(F, _) ->
{value, concrete(F), []}
- end, [], true),
+ end),
B;
arity_qualifier ->
A = erl_syntax:arity_qualifier_argument(Node),
--
2.34.1