diff options
author | Björn Gustavsson <[email protected]> | 2014-02-04 09:19:39 +0100 |
---|---|---|
committer | Björn Gustavsson <[email protected]> | 2014-02-04 10:19:55 +0100 |
commit | 5d1471fae699fed9318ea6cad939156c2775d8be (patch) | |
tree | 32723185e8a5a69210fe41a1e3dc7ec721a7c9de /lib/compiler/src | |
parent | f7663318a618d7f18f0c1aa07a023f3f7353c0af (diff) | |
download | otp-5d1471fae699fed9318ea6cad939156c2775d8be.tar.gz otp-5d1471fae699fed9318ea6cad939156c2775d8be.tar.bz2 otp-5d1471fae699fed9318ea6cad939156c2775d8be.zip |
sys_core_fold: Prevent case expressions from being evaluated twice
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
Diffstat (limited to 'lib/compiler/src')
-rw-r--r-- | lib/compiler/src/sys_core_fold.erl | 25 |
1 files changed, 23 insertions, 2 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 <X,Y> = <SomeSideEffect(),SomeSideEffect()> 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 <X,Y> = <NewVar,NewVar> 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]}. |