aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler')
-rw-r--r--lib/compiler/src/beam_bsm.erl1
-rw-r--r--lib/compiler/src/beam_validator.erl1
-rw-r--r--lib/compiler/src/sys_core_fold.erl25
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl15
-rw-r--r--lib/compiler/test/core_fold_SUITE.erl36
5 files changed, 70 insertions, 8 deletions
diff --git a/lib/compiler/src/beam_bsm.erl b/lib/compiler/src/beam_bsm.erl
index fdfcb08125..d54c2a9fde 100644
--- a/lib/compiler/src/beam_bsm.erl
+++ b/lib/compiler/src/beam_bsm.erl
@@ -209,6 +209,7 @@ btb_reaches_match_2([{call,Arity,{f,Lbl}}|Is], Regs, D) ->
btb_reaches_match_2([{apply,Arity}|Is], Regs, D) ->
btb_call(Arity+2, apply, Regs, Is, D);
btb_reaches_match_2([{call_fun,Live}=I|Is], Regs, D) ->
+ btb_ensure_not_used([{x,Live}], I, Regs),
btb_call(Live, I, Regs, Is, D);
btb_reaches_match_2([{make_fun2,_,_,_,Live}|Is], Regs, D) ->
btb_call(Live, make_fun2, Regs, Is, D);
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 97f84da08f..682f7adbc2 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -574,6 +574,7 @@ valfun_4({apply,Live}, Vst) ->
valfun_4({apply_last,Live,_}, Vst) ->
tail_call(apply, Live+2, Vst);
valfun_4({call_fun,Live}, Vst) ->
+ validate_src([{x,Live}], Vst),
call('fun', Live+1, Vst);
valfun_4({call,Live,Func}, Vst) ->
call(Func, Live, Vst);
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 9ce6bb48bf..e302e2324d 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -1945,9 +1945,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]}.
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 9f15845d33..149b9bbb8f 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -34,7 +34,7 @@
otp_7188/1,otp_7233/1,otp_7240/1,otp_7498/1,
match_string/1,zero_width/1,bad_size/1,haystack/1,
cover_beam_bool/1,matched_out_size/1,follow_fail_branch/1,
- no_partition/1]).
+ no_partition/1,calling_a_binary/1]).
-export([coverage_id/1,coverage_external_ignore/2]).
@@ -59,7 +59,7 @@ groups() ->
matching_and_andalso,otp_7188,otp_7233,otp_7240,
otp_7498,match_string,zero_width,bad_size,haystack,
cover_beam_bool,matched_out_size,follow_fail_branch,
- no_partition]}].
+ no_partition,calling_a_binary]}].
init_per_suite(Config) ->
@@ -1178,6 +1178,17 @@ no_partition_2([], a5) ->
no_partition_2(42.0, a6) ->
six.
+calling_a_binary(Config) when is_list(Config) ->
+ [] = call_binary(<<>>, []),
+ {'EXIT',{badarg,_}} = (catch call_binary(<<1>>, [])),
+ {'EXIT',{badarg,_}} = (catch call_binary(<<1,2,3>>, [])),
+ ok.
+
+call_binary(<<>>, Acc) ->
+ Acc;
+call_binary(<<H,T/bits>>, Acc) ->
+ T(<<Acc/binary,H>>).
+
check(F, R) ->
R = F().
diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl
index 69f61a046f..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) ->
@@ -299,8 +301,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 +338,31 @@ do_something(I) ->
put(unused_multiple_values,
[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.