diff options
author | Björn Gustavsson <[email protected]> | 2016-05-18 19:14:09 +0200 |
---|---|---|
committer | Björn Gustavsson <[email protected]> | 2016-05-23 08:39:52 +0200 |
commit | 9ab0dcce5a2bf609917cb09c36cbf63dad6bb679 (patch) | |
tree | 0a92402f81e8bbf327987cf9462901adbab4e21a | |
parent | 121cc9ba61e44e9c47d831d837dfb0f0b2d81990 (diff) | |
download | otp-9ab0dcce5a2bf609917cb09c36cbf63dad6bb679.tar.gz otp-9ab0dcce5a2bf609917cb09c36cbf63dad6bb679.tar.bz2 otp-9ab0dcce5a2bf609917cb09c36cbf63dad6bb679.zip |
v3_core: Don't depend on sys_core_fold for cleaning up
a3ec2644f5 attempted to teach v3_core not to generate code with
unbound variables. The approach taken in that commit is to
discard all expressions following a badmatch. That does not
work if the badmatch is nested:
{[V] = [] = foo,V},
V
That would be rewritten to:
{error({badmatch,foo})},
V
where V is unbound.
If we were to follow the same approach, the tuple construction
code would have to look out for a badmatch. As would list construction,
begin...end, and so on.
Therefore, as it is impractical to discard all expressions that
follow a badmatch, the only other solution is to ensure that the
variables that the pattern binds will somehow be bound. That can
be arranged by rewriting the pattern to a pattern that binds the
same variables. Thus:
error({badmatch,foo}),
E = foo,
case E of
{[V],[]} ->
V;
Other ->
error({badmatch,Other}
end
-rw-r--r-- | lib/compiler/src/v3_core.erl | 66 | ||||
-rw-r--r-- | lib/compiler/test/match_SUITE.erl | 80 |
2 files changed, 120 insertions, 26 deletions
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 83b3650180..a3b0236134 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -510,16 +510,8 @@ unforce(_, Vs) -> Vs. exprs([E0|Es0], St0) -> {E1,Eps,St1} = expr(E0, St0), - case E1 of - #iprimop{name=#c_literal{val=match_fail}} -> - %% Must discard the rest of the body, because it - %% may refer to variables that have not been bound. - %% Example: {ok={error,E}} = foo(), E. - {Eps ++ [E1],St1}; - _ -> - {Es1,St2} = exprs(Es0, St1), - {Eps ++ [E1] ++ Es1,St2} - end; + {Es1,St2} = exprs(Es0, St1), + {Eps ++ [E1] ++ Es1,St2}; exprs([], St) -> {[],St}. %% expr(Expr, State) -> {Cexpr,[PreExp],State}. @@ -689,14 +681,36 @@ expr({match,L,P0,E0}, St0) -> Fc = fail_clause([Fpat], Lanno, c_tuple([#c_literal{val=badmatch},Fpat])), case P2 of nomatch -> + %% The pattern will not match. We must take care here to + %% bind all variables that the pattern would have bound + %% so that subsequent expressions do not refer to unbound + %% variables. + %% + %% As an example, this code: + %% + %% [X] = {Y} = E, + %% X + Y. + %% + %% will be rewritten to: + %% + %% error({badmatch,E}), + %% case E of + %% {[X],{Y}} -> + %% X + Y; + %% Other -> + %% error({badmatch,Other}) + %% end. + %% St6 = add_warning(L, nomatch, St5), - {Expr,Eps3,St} = safe(E1, St6), - Eps = Eps1 ++ Eps2 ++ Eps3, + {Expr,Eps3,St7} = safe(E1, St6), + SanPat0 = sanitize(P1), + {SanPat,Eps4,St} = pattern(SanPat0, St7), Badmatch = c_tuple([#c_literal{val=badmatch},Expr]), Fail = #iprimop{anno=#a{anno=Lanno}, name=#c_literal{val=match_fail}, args=[Badmatch]}, - {Fail,Eps,St}; + Eps = Eps3 ++ Eps4 ++ [Fail], + {#imatch{anno=#a{anno=Lanno},pat=SanPat,arg=Expr,fc=Fc},Eps,St}; Other when not is_atom(Other) -> {#imatch{anno=#a{anno=Lanno},pat=P2,arg=E2,fc=Fc},Eps1++Eps2,St5} end; @@ -738,6 +752,32 @@ expr({op,L,Op,L0,R0}, St0) -> module=#c_literal{anno=LineAnno,val=erlang}, name=#c_literal{anno=LineAnno,val=Op},args=As},Aps,St1}. + +%% sanitize(Pat) -> SanitizedPattern +%% Rewrite Pat so that it will be accepted by pattern/2 and will +%% bind the same variables as the original pattern. +%% +%% Here is an example of a pattern that would cause a pattern/2 +%% to generate a 'nomatch' exception: +%% +%% #{k:=X,k:=Y} = [Z] +%% +%% The sanitized pattern will look like: +%% +%% {{X,Y},[Z]} + +sanitize({match,L,P1,P2}) -> + {tuple,L,[sanitize(P1),sanitize(P2)]}; +sanitize({cons,L,H,T}) -> + {cons,L,sanitize(H),sanitize(T)}; +sanitize({tuple,L,Ps0}) -> + Ps = [sanitize(P) || P <- Ps0], + {tuple,L,Ps}; +sanitize({map,L,Ps0}) -> + Ps = [sanitize(V) || {map_field_exact,_,_,V} <- Ps0], + {tuple,L,Ps}; +sanitize(P) -> P. + make_bool_switch(L, E, V, T, F, #core{in_guard=true}) -> make_bool_switch_guard(L, E, V, T, F); make_bool_switch(L, E, V, T, F, #core{}) -> diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl index 92a9802cad..31402ac717 100644 --- a/lib/compiler/test/match_SUITE.erl +++ b/lib/compiler/test/match_SUITE.erl @@ -21,8 +21,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, - pmatch/1,mixed/1,aliases/1,match_in_call/1, - untuplify/1,shortcut_boolean/1,letify_guard/1, + pmatch/1,mixed/1,aliases/1,non_matching_aliases/1, + match_in_call/1,untuplify/1,shortcut_boolean/1,letify_guard/1, selectify/1,underscore/1,match_map/1,map_vars_used/1, coverage/1,grab_bag/1]). @@ -36,7 +36,8 @@ all() -> groups() -> [{p,[parallel], - [pmatch,mixed,aliases,match_in_call,untuplify, + [pmatch,mixed,aliases,non_matching_aliases, + match_in_call,untuplify, shortcut_boolean,letify_guard,selectify, underscore,match_map,map_vars_used,coverage, grab_bag]}]. @@ -143,16 +144,6 @@ aliases(Config) when is_list(Config) -> {a,b} = list_alias2([a,b]), {a,b} = list_alias3([a,b]), - %% Non-matching aliases. - none = mixed_aliases(<<42>>), - none = mixed_aliases([b]), - none = mixed_aliases([d]), - none = mixed_aliases({a,42}), - none = mixed_aliases(42), - - %% Non-matching aliases. - {'EXIT',{{badmatch,42},_}} = (catch nomatch_alias(42)), - ok. str_alias(V) -> @@ -256,6 +247,33 @@ list_alias2([X,Y]=[a,b]) -> list_alias3([X,b]=[a,Y]) -> {X,Y}. +non_matching_aliases(_Config) -> + none = mixed_aliases(<<42>>), + none = mixed_aliases([b]), + none = mixed_aliases([d]), + none = mixed_aliases({a,42}), + none = mixed_aliases(42), + + {'EXIT',{{badmatch,42},_}} = (catch nomatch_alias(42)), + {'EXIT',{{badmatch,job},_}} = (catch entirely()), + {'EXIT',{{badmatch,associates},_}} = (catch printer()), + {'EXIT',{{badmatch,borogoves},_}} = (catch tench()), + + put(perch, 0), + {'EXIT',{{badmatch,{spine,42}},_}} = (catch perch(42)), + 1 = erase(perch), + + put(salmon, 0), + {'EXIT',{{badmatch,mimsy},_}} = (catch salmon()), + 1 = erase(salmon), + + put(shark, 0), + {'EXIT',{{badmatch,_},_}} = (catch shark()), + 1 = erase(shark), + + {'EXIT',{{badmatch,_},_}} = (catch radio(research)), + ok. + mixed_aliases(<<X:8>> = x) -> {a,X}; mixed_aliases([b] = <<X:8>>) -> {b,X}; mixed_aliases(<<X:8>> = {a,X}) -> {c,X}; @@ -266,6 +284,42 @@ nomatch_alias(I) -> {ok={A,B}} = id(I), {A,B}. +entirely() -> + 0(((Voice = true) = cool) = job), + [receive _ -> Voice end || banking <- printer]. + +printer() -> + {[Indoor] = [] = associates}, + [ireland || Indoor <- Indoor]. + +tench() -> + E = begin + [A] = [] = borogoves, + A + 1 + end, + E + 7 * A. + +perch(X) -> + begin + put(perch, get(perch)+1), + [A] = [] = {spine,X} + end. + +salmon() -> + {put(salmon, get(salmon)+1),#{key:=([A]=[])}=mimsy,exit(fail)}, + A + 10. + +shark() -> + (hello = there) = (catch shark(put(shark, get(shark)+1), a = b)). + +shark(_, _) -> + ok. + +radio(research) -> + (connection = proof) = + (catch erlang:trace_pattern(catch mechanisms + assist, + summary = mechanisms)). + %% OTP-7018. match_in_call(Config) when is_list(Config) -> |