From f7663318a618d7f18f0c1aa07a023f3f7353c0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 4 Feb 2014 09:21:17 +0100 Subject: sys_core_fold_SUITE: For cleanliness, move id/1 to the end --- lib/compiler/test/core_fold_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl index 69f61a046f..afb8fa5599 100644 --- a/lib/compiler/test/core_fold_SUITE.erl +++ b/lib/compiler/test/core_fold_SUITE.erl @@ -299,8 +299,6 @@ cover_is_safe_bool_expr(X) -> bsm_an_inlined(<<_:8>>, _) -> ok; bsm_an_inlined(_, _) -> error. -id(I) -> I. - unused_multiple_values_error(Config) when is_list(Config) -> PrivDir = ?config(priv_dir, Config), Dir = filename:dirname(code:which(?MODULE)), @@ -338,3 +336,5 @@ do_something(I) -> put(unused_multiple_values, [I|get(unused_multiple_values)]), I. + +id(I) -> I. -- cgit v1.2.3 From 5d1471fae699fed9318ea6cad939156c2775d8be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 4 Feb 2014 09:19:39 +0100 Subject: sys_core_fold: Prevent case expressions from being evaluated twice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In e12b7d5331c58b41db06cadfa4af75b78b62a2b1, a bug was introduced that would cause case expressions to be evaluated more than once if there were aliases in the pattern. Example: X = Y = io:put_chars("some chars"), {X,Y} That would be rewritten to code similar to (but in Core Erlang): X = io:put_chars("some chars"), X = io:put_chars("some chars"), {X,Y} Make sure that we only evalute the expression once by doing a transformation similar to (but in Core Erlang): NewVar = io:put_chars("some chars"), X = NewVar, Y = NewVar, {X,Y} Reported-by: José Valim Reported-by: Anthony Ramine --- lib/compiler/src/sys_core_fold.erl | 25 +++++++++++++++++++++++-- lib/compiler/test/core_fold_SUITE.erl | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 1cdbac5693..b7bbf40b4a 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -1925,9 +1925,30 @@ eval_case(#c_case{arg=E,clauses=[#c_clause{pats=Ps0,body=B}]}, Sub) -> true -> cerl:values_es(E); false -> [E] end, - {true,Bs} = cerl_clauses:match_list(Ps0, Es), + %% Consider: + %% + %% case SomeSideEffect() of + %% X=Y -> ... + %% end + %% + %% We must not rewrite it to: + %% + %% let = in ... + %% + %% because SomeSideEffect() would be called evaluated twice. + %% + %% Instead we must evaluate the case expression in an outer let + %% like this: + %% + %% let NewVar = SomeSideEffect() in + %% let = in ... + %% + Vs = make_vars([], length(Es)), + {true,Bs} = cerl_clauses:match_list(Ps0, Vs), {Ps,As} = unzip(Bs), - expr(#c_let{vars=Ps,arg=core_lib:make_values(As),body=B}, sub_new(Sub)); + InnerLet = cerl:c_let(Ps, core_lib:make_values(As), B), + Let = cerl:c_let(Vs, E, InnerLet), + expr(Let, sub_new(Sub)); eval_case(Case, _) -> Case. %% case_opt(CaseArg, [Clause]) -> {CaseArg,[Clause]}. diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl index afb8fa5599..8151dc1b16 100644 --- a/lib/compiler/test/core_fold_SUITE.erl +++ b/lib/compiler/test/core_fold_SUITE.erl @@ -22,7 +22,8 @@ init_per_group/2,end_per_group/2, t_element/1,setelement/1,t_length/1,append/1,t_apply/1,bifs/1, eq/1,nested_call_in_case/1,guard_try_catch/1,coverage/1, - unused_multiple_values_error/1,unused_multiple_values/1]). + unused_multiple_values_error/1,unused_multiple_values/1, + multiple_aliases/1]). -export([foo/0,foo/1,foo/2,foo/3]). @@ -38,7 +39,8 @@ groups() -> [{p,test_lib:parallel(), [t_element,setelement,t_length,append,t_apply,bifs, eq,nested_call_in_case,guard_try_catch,coverage, - unused_multiple_values_error,unused_multiple_values]}]. + unused_multiple_values_error,unused_multiple_values, + multiple_aliases]}]. init_per_suite(Config) -> @@ -337,4 +339,30 @@ do_something(I) -> [I|get(unused_multiple_values)]), I. + +%% Make sure that multiple aliases does not cause +%% the case expression to be evaluated twice. +multiple_aliases(Config) when is_list(Config) -> + do_ma(fun() -> + X = Y = run_once(), + {X,Y} + end, {ok,ok}), + do_ma(fun() -> + case {true,run_once()} of + {true=A=B,ok=X=Y} -> + {A,B,X,Y} + end + end, {true,true,ok,ok}), + ok. + +do_ma(Fun, Expected) when is_function(Fun, 0) -> + Expected = Fun(), + ran_once = erase(run_once), + ok. + +run_once() -> + undefined = put(run_once, ran_once), + ok. + + id(I) -> I. -- cgit v1.2.3