aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/compiler/src/v3_core.erl66
-rw-r--r--lib/compiler/test/match_SUITE.erl80
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) ->