aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjörn Gustavsson <[email protected]>2015-01-16 15:06:53 +0100
committerBjörn Gustavsson <[email protected]>2015-01-26 13:08:48 +0100
commit1ef53ead19d5bd07caad4daa71b8fecf2fbcd2a6 (patch)
tree00d9dd67d47c8cd7fdf7cee71d8fb0141dad03be
parenta1243173ed98d1006914337b94f28c9594bd8b02 (diff)
downloadotp-1ef53ead19d5bd07caad4daa71b8fecf2fbcd2a6.tar.gz
otp-1ef53ead19d5bd07caad4daa71b8fecf2fbcd2a6.tar.bz2
otp-1ef53ead19d5bd07caad4daa71b8fecf2fbcd2a6.zip
sys_core_fold: Strengthen optimization of letrecs in effect context
We used to evaluate the body of a 'letrec' in value context, even if the 'letrec' was being evaluated in effect context. In most cases, the context does not matter because the body is usually just an 'apply' which will never be optimized away. However, in the case of incorrect code described in the previous commit, it does matter. We can find such bad code by evaluating the body in effect context. For example, if we have the following incorrect code: letrec f/1 = fun(A) -> ... <use of Var> ... in let Var = <<2:301>> in apply(Arg) If the letrec is evaluated in effect context, the code will be reduced to: letrec f/1 = fun(A) -> ... <use of Var> ... in seq Var = <<2:301>> do apply(Arg) Now Var will be unbound and a later compiler pass will crash to ensure that the bad Core Erlang code is noticed. Also add a test case to ensure that the compiler crashes if the bug fixed in the previous commit re-surfaces.
-rw-r--r--lib/compiler/src/sys_core_fold.erl2
-rw-r--r--lib/compiler/test/lc_SUITE.erl21
2 files changed, 20 insertions, 3 deletions
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 09716d0866..e079fc072a 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -313,7 +313,7 @@ expr(#c_letrec{defs=Fs0,body=B0}=Letrec, Ctxt, Sub) ->
Fs1 = map(fun ({Name,Fb}) ->
{Name,expr(Fb, {letrec,Ctxt}, Sub)}
end, Fs0),
- B1 = body(B0, value, Sub),
+ B1 = body(B0, Ctxt, Sub),
Letrec#c_letrec{defs=Fs1,body=B1};
expr(#c_case{}=Case0, Ctxt, Sub) ->
%% Ideally, the compiler should only emit warnings when there is
diff --git a/lib/compiler/test/lc_SUITE.erl b/lib/compiler/test/lc_SUITE.erl
index 6b3472b37b..6c5b34498b 100644
--- a/lib/compiler/test/lc_SUITE.erl
+++ b/lib/compiler/test/lc_SUITE.erl
@@ -22,7 +22,8 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
basic/1,deeply_nested/1,no_generator/1,
- empty_generator/1,no_export/1,shadow/1]).
+ empty_generator/1,no_export/1,shadow/1,
+ effect/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -39,7 +40,8 @@ groups() ->
no_generator,
empty_generator,
no_export,
- shadow
+ shadow,
+ effect
]}].
init_per_suite(Config) ->
@@ -199,6 +201,21 @@ shadow(Config) when is_list(Config) ->
Shadowed < 10]),
ok.
+effect(Config) when is_list(Config) ->
+ [{42,{a,b,c}}] =
+ do_effect(fun(F, L) ->
+ [F({V1,V2}) ||
+ #{<<1:500>>:=V1,<<2:301>>:=V2} <- L],
+ ok
+ end, id([#{},x,#{<<1:500>>=>42,<<2:301>>=>{a,b,c}}])),
+ ok.
+
+do_effect(Lc, L) ->
+ put(?MODULE, []),
+ F = fun(V) -> put(?MODULE, [V|get(?MODULE)]) end,
+ ok = Lc(F, L),
+ lists:reverse(erase(?MODULE)).
+
id(I) -> I.
fc(Args, {'EXIT',{function_clause,[{?MODULE,_,Args,_}|_]}}) -> ok;