aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler/src/sys_core_fold.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler/src/sys_core_fold.erl')
-rw-r--r--lib/compiler/src/sys_core_fold.erl42
1 files changed, 40 insertions, 2 deletions
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 3a65b40fa5..d848cd8f19 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -99,7 +99,7 @@
t=#{} :: map(), %Types
in_guard=false}). %In guard or not.
--type type_info() :: cerl:cerl() | 'bool' | 'integer'.
+-type type_info() :: cerl:cerl() | 'bool' | 'integer' | {'fun', pos_integer()}.
-type yes_no_maybe() :: 'yes' | 'no' | 'maybe'.
-type sub() :: #sub{}.
@@ -478,9 +478,20 @@ expr(#c_try{anno=A,arg=E0,vars=Vs0,body=B0,evars=Evs0,handler=H0}=Try, _, Sub0)
false ->
{Evs1,Sub2} = var_list(Evs0, Sub0),
H1 = body(H0, value, Sub2),
- Try#c_try{arg=E1,vars=Vs1,body=B1,evars=Evs1,handler=H1}
+ H2 = opt_try_handler(H1, lists:last(Evs1)),
+ Try#c_try{arg=E1,vars=Vs1,body=B1,evars=Evs1,handler=H2}
end.
+%% Attempts to convert old erlang:get_stacktrace/0 calls into the new
+%% three-argument catch, with possibility of further optimisations.
+opt_try_handler(#c_call{anno=A,module=#c_literal{val=erlang},name=#c_literal{val=get_stacktrace},args=[]}, Var) ->
+ #c_primop{anno=A,name=#c_literal{val=build_stacktrace},args=[Var]};
+opt_try_handler(#c_case{clauses=Cs0} = Case, Var) ->
+ Cs = [C#c_clause{body=opt_try_handler(B, Var)} || #c_clause{body=B} = C <- Cs0],
+ Case#c_case{clauses=Cs};
+opt_try_handler(#c_let{arg=Arg} = Let, Var) ->
+ Let#c_let{arg=opt_try_handler(Arg, Var)};
+opt_try_handler(X, _) -> X.
%% If a fun or its application is used as an argument, then it's unsafe to
%% handle it in effect context as the side-effects may rely on its return
@@ -561,6 +572,7 @@ ifes_list(FVar, [E|Es], Safe) ->
ifes_list(_FVar, [], _Safe) ->
true.
+
expr_list(Es, Ctxt, Sub) ->
[expr(E, Ctxt, Sub) || E <- Es].
@@ -961,6 +973,10 @@ fold_non_lit_args(Call, erlang, setelement, [Arg1,Arg2,Arg3], _) ->
eval_setelement(Call, Arg1, Arg2, Arg3);
fold_non_lit_args(Call, erlang, is_record, [Arg1,Arg2,Arg3], Sub) ->
eval_is_record(Call, Arg1, Arg2, Arg3, Sub);
+fold_non_lit_args(Call, erlang, is_function, [Arg1], Sub) ->
+ eval_is_function_1(Call, Arg1, Sub);
+fold_non_lit_args(Call, erlang, is_function, [Arg1,Arg2], Sub) ->
+ eval_is_function_2(Call, Arg1, Arg2, Sub);
fold_non_lit_args(Call, erlang, N, Args, Sub) ->
NumArgs = length(Args),
case erl_internal:comp_op(N, NumArgs) of
@@ -976,6 +992,22 @@ fold_non_lit_args(Call, erlang, N, Args, Sub) ->
end;
fold_non_lit_args(Call, _, _, _, _) -> Call.
+eval_is_function_1(Call, Arg1, Sub) ->
+ case get_type(Arg1, Sub) of
+ none -> Call;
+ {'fun',_} -> #c_literal{anno=cerl:get_ann(Call),val=true};
+ _ -> #c_literal{anno=cerl:get_ann(Call),val=false}
+ end.
+
+eval_is_function_2(Call, Arg1, #c_literal{val=Arity}, Sub)
+ when is_integer(Arity), Arity > 0 ->
+ case get_type(Arg1, Sub) of
+ none -> Call;
+ {'fun',Arity} -> #c_literal{anno=cerl:get_ann(Call),val=true};
+ _ -> #c_literal{anno=cerl:get_ann(Call),val=false}
+ end;
+eval_is_function_2(Call, _Arg1, _Arg2, _Sub) -> Call.
+
%% Evaluate a relational operation using type information.
eval_rel_op(Call, Op, [#c_var{name=V},#c_var{name=V}], _) ->
Bool = erlang:Op(same, same),
@@ -3183,6 +3215,10 @@ update_types_2(V, [#c_tuple{}=P], Types) ->
Types#{V=>P};
update_types_2(V, [#c_literal{val=Bool}], Types) when is_boolean(Bool) ->
Types#{V=>bool};
+update_types_2(V, [#c_fun{vars=Vars}], Types) ->
+ Types#{V=>{'fun',length(Vars)}};
+update_types_2(V, [#c_var{name={_,Arity}}], Types) ->
+ Types#{V=>{'fun',Arity}};
update_types_2(V, [Type], Types) when is_atom(Type) ->
Types#{V=>Type};
update_types_2(_, _, Types) -> Types.
@@ -3201,6 +3237,8 @@ kill_types2(V, [{_,#c_tuple{}=Tuple}=Entry|Tdb]) ->
false -> [Entry|kill_types2(V, Tdb)];
true -> kill_types2(V, Tdb)
end;
+kill_types2(V, [{_, {'fun',_}}=Entry|Tdb]) ->
+ [Entry|kill_types2(V, Tdb)];
kill_types2(V, [{_,Atom}=Entry|Tdb]) when is_atom(Atom) ->
[Entry|kill_types2(V, Tdb)];
kill_types2(_, []) -> [].