summaryrefslogtreecommitdiff
path: root/lib/compiler/test/match_SUITE.erl
diff options
context:
space:
mode:
authorBjörn Gustavsson <bjorn@erlang.org>2022-11-03 09:33:24 +0100
committerBjörn Gustavsson <bjorn@erlang.org>2022-12-13 12:35:45 +0100
commit6a9ebe62aadd241424d9f15998e7ac488b20ac6b (patch)
tree67b558ab522bbbc2108c7def562b72079ffcf632 /lib/compiler/test/match_SUITE.erl
parent58b62e50263ac9f9a524bc14bcbef2cd751d71bd (diff)
downloaderlang-6a9ebe62aadd241424d9f15998e7ac488b20ac6b.tar.gz
Lift restrictions for matching of binaries and maps
There has always been an implementation limitation for matching of binaries (for technical reasons). For example: foo(Bin) -> <<A:8>> = <<X:4,Y:4>> = Bin, {A,X,Y}. This would fail to compile with the following message: t.erl:5:5: binary patterns cannot be matched in parallel using '=' % 5| <<A:8>> = <<X:4,Y:4>> = Bin, % | ^ This commit lifts this restriction, making the example legal. A restriction for map matching is also lifted, but before we can describe that, we'll need a digression to talk about the `=` operator. The `=` operator can be used for two similar but slightly differently purposes. When used in a pattern in a clause, for example in a function head, both the left-hand and right-hand side operands must be patterns: Pattern1 = Pattern2 For example: bar(#{a := A} = #{b := B}) -> {A, B}. The following example will not compile because the right-hand side is not a pattern but an expression: wrong(#{a := A} = #{b => B}) -> {A, B}. t.erl:4:23: illegal pattern % 4| wrong(#{a := A} = #{b => B}) -> {A, B}. % | ^ Used in this context, the `=` operator does not imply that the two patterns are matched in any particular order. Attempting to use a variable matched out on the left-hand side on the right-hand side, or vice versa, will fail: also_wrong1(#{B := A} = #{b := B}) -> {A,B}. also_wrong2(#{a := A} = #{A := B}) -> {A,B}. t.erl:6:15: variable 'B' is unbound % 6| also_wrong1(#{B := A} = #{b := B}) -> {A,B}. % | ^ t.erl:7:27: variable 'A' is unbound % 7| also_wrong2(#{a := A} = #{A := B}) -> {A,B}. % | ^ The other way to use `=` is in a function body. Used in this way, the right-hand side must be an expression: Pattern = Expression For example: foobar(Value) -> #{a := A} = #{a => Value}, A. Used in this context, the right-hand side of `=` must **not** be a pattern: illegal_foobar(Value) -> #{a := A} = #{a := Value}, A. t.erl:18:21: only association operators '=>' are allowed in map construction % 18| #{a := A} = #{a := Value}, % | ^ When used in a body context, the value of the `=` operator is the value of its right-hand side operand. When multiple `=` operators are combined, they are evaluted from right to left. That means that any number of patterns can be matched at once: Pattern1 = Pattern2 = ... = PatternN = Expr which is equivalent to: Var = Expr PatternN = Var . . . Pattern2 = Var Pattern1 = Var Given that there is a well-defined evaluation order from right to left, one would expect that the following example would be legal: baz(M) -> #{K := V} = #{k := K} = M, V. It is not. In Erlang/OTP 25 or earlier, the compilation fails with the following message: t.erl:28:7: variable 'K' is unbound % 28| #{K := V} = #{k := K} = M, % | ^ That restriction is now lifted, making the example legal. Closes #6348 Closes #6444 Closes #6467
Diffstat (limited to 'lib/compiler/test/match_SUITE.erl')
-rw-r--r--lib/compiler/test/match_SUITE.erl139
1 files changed, 123 insertions, 16 deletions
diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl
index 5fc487e7a9..55ff6f7d63 100644
--- a/lib/compiler/test/match_SUITE.erl
+++ b/lib/compiler/test/match_SUITE.erl
@@ -26,7 +26,8 @@
selectify/1,deselectify/1,underscore/1,match_map/1,map_vars_used/1,
coverage/1,grab_bag/1,literal_binary/1,
unary_op/1,eq_types/1,match_after_return/1,match_right_tuple/1,
- tuple_size_in_try/1,match_boolean_list/1]).
+ tuple_size_in_try/1,match_boolean_list/1,
+ heisen_variables/1]).
-include_lib("common_test/include/ct.hrl").
@@ -43,7 +44,8 @@ groups() ->
underscore,match_map,map_vars_used,coverage,
grab_bag,literal_binary,unary_op,eq_types,
match_after_return,match_right_tuple,
- tuple_size_in_try,match_boolean_list]}].
+ tuple_size_in_try,match_boolean_list,
+ heisen_variables]}].
init_per_suite(Config) ->
@@ -151,14 +153,19 @@ aliases(Config) when is_list(Config) ->
6 = tup_lit_alias({1,2,3}),
6 = tup_lit_alias_rev({1,2,3}),
- {42,42,42,42} = multiple_aliases_1(42),
- {7,7,7} = multiple_aliases_2(7),
- {{a,b},{a,b},{a,b}} = multiple_aliases_3({a,b}),
+ {1,2,3,4} = list_in_tuple({container, [1,2,3], 4}),
+ {a,b,c,d} = list_in_tuple({container, [a,b,c], d}),
+
+ {13,y,13,17,x,{y,13},17} = tuple_in_tuple({x, {y,13}, 17}),
+ {a,y,a,b,x,{y,a},b} = tuple_in_tuple({x, {y,a}, b}),
+
+ {42,42,42,42} = multiple_aliases_1(id(42)),
+ {7,7,7} = multiple_aliases_2(id(7)),
+ {{a,b},{a,b},{a,b}} = multiple_aliases_3(id({a,b})),
+ {[x,y,z],[x,y,z],[x,y,z]} = multiple_aliases_4(id([x,y,z])),
%% Lists/literals.
- {a,b} = list_alias1([a,b]),
- {a,b} = list_alias2([a,b]),
- {a,b} = list_alias3([a,b]),
+ {a,b} = list_alias(id([a,b])),
%% Multiple matches.
{'EXIT',{{badmatch,home},_}} =
@@ -259,9 +266,20 @@ three_2(A=
C) ->
{A,B,C}.
-tuple_alias({A,B,C}={X,Y,Z}) ->
+tuple_alias(Expr) ->
+ Res = tuple_alias_a(Expr),
+ Res = tuple_alias_b(Expr).
+
+tuple_alias_a({A,B,C} = {X,Y,Z}) ->
+ {A,B,C,X,Y,Z};
+tuple_alias_a({A,B} = {C,D} = {E,F}) ->
+ {A,B,C,D,E,F}.
+
+tuple_alias_b({_,_,_}=Expr) ->
+ {A,B,C} = {X,Y,Z} = Expr,
{A,B,C,X,Y,Z};
-tuple_alias({A,B}={C,D}={E,F}) ->
+tuple_alias_b({_,_}=Expr) ->
+ {A,B} = {C,D} = {E,F} = Expr,
{A,B,C,D,E,F}.
tup_lit_alias({A,B,C}={1,2,3}) ->
@@ -270,22 +288,91 @@ tup_lit_alias({A,B,C}={1,2,3}) ->
tup_lit_alias_rev({1,2,3}={A,B,C}) ->
A+B+C.
-multiple_aliases_1((A=B)=(C=D)) ->
+list_in_tuple(E) ->
+ Res = list_in_tuple_a(E),
+ Res = list_in_tuple_b(E).
+
+list_in_tuple_a({container, [_,_,_] = [A,B,C], D}) ->
+ {A,B,C,D}.
+
+list_in_tuple_b(E) ->
+ {container, [_,_,_] = [A,B,C], D} = E,
+ {A,B,C,D}.
+
+tuple_in_tuple(Expr) ->
+ Res = tuple_in_tuple_a(Expr),
+ Res = tuple_in_tuple_b(Expr).
+
+tuple_in_tuple_a({x, {y,A} = {B,C}, D} = {E, F, G}) ->
+ {A,B,C,D,E,F,G}.
+
+tuple_in_tuple_b(Expr) ->
+ {x, {y,A} = {B,C}, D} = {E, F, G} = Expr,
+ {A,B,C,D,E,F,G}.
+
+multiple_aliases_1(Expr) ->
+ Res = multiple_aliases_1a(Expr),
+ Res = multiple_aliases_1b(Expr).
+
+multiple_aliases_1a((A=B) = (C=D)) ->
{A,B,C,D}.
-multiple_aliases_2((A=B)=(A=C)) ->
+multiple_aliases_1b(Expr) ->
+ (A=B) = (C=D) = Expr,
+ {A,B,C,D}.
+
+multiple_aliases_2((A=B) = (A=C)) ->
+ {A,B,C}.
+
+multiple_aliases_3(Expr) ->
+ Res = multiple_aliases_3a(Expr),
+ Res = multiple_aliases_3b(Expr).
+
+multiple_aliases_3a((A={_,_}=B)={_,_}=C) ->
{A,B,C}.
-multiple_aliases_3((A={_,_}=B)={_,_}=C) ->
+multiple_aliases_3b(Expr) ->
+ (A={_,_}=B) = {_,_} = C = Expr,
{A,B,C}.
-list_alias1([a,b]=[X,Y]) ->
+multiple_aliases_4(Expr) ->
+ Res = multiple_aliases_4a(Expr),
+ Res = multiple_aliases_4b(Expr).
+
+multiple_aliases_4a((A=[_,_,_]=B) = [_,_,_] = C) ->
+ {A,B,C}.
+
+multiple_aliases_4b(Expr) ->
+ (A=[_,_,_]=B) = [_,_,_] = C = Expr,
+ {A,B,C}.
+
+list_alias(Expr) ->
+ Res = list_alias1a(Expr),
+ Res = list_alias1b(Expr),
+ Res = list_alias2a(Expr),
+ Res = list_alias2b(Expr),
+ Res = list_alias3a(Expr),
+ Res = list_alias3b(Expr).
+
+list_alias1a([a,b]=[X,Y]) ->
+ {X,Y}.
+
+list_alias1b(Expr) ->
+ [a,b] = [X,Y] = Expr,
+ {X,Y}.
+
+list_alias2a([X,Y]=[a,b]) ->
{X,Y}.
-list_alias2([X,Y]=[a,b]) ->
+list_alias2b(Expr) ->
+ [X,Y] = [a,b] = Expr,
{X,Y}.
-list_alias3([X,b]=[a,Y]) ->
+list_alias3a([X,b]=[a,Y]) ->
+ {X,Y}.
+
+list_alias3b(Expr) ->
+ [X,b] = [a,Y]= Expr,
{X,Y}.
non_matching_aliases(_Config) ->
@@ -320,6 +407,9 @@ non_matching_aliases(_Config) ->
{'EXIT',{{case_clause,whatever},_}} = (catch pike1(whatever)),
{'EXIT',{{case_clause,whatever},_}} = (catch pike2(whatever)),
+ {'EXIT',{badarith,_}} = catch squid(a),
+ {'EXIT',{{badmatch,43},_}} = catch squid(42),
+
ok.
mixed_aliases(<<X:8>> = x) -> {a,X};
@@ -402,6 +492,11 @@ pike2(X) ->
end,
Var.
+squid(E) ->
+ ([X] = {Y}) = V = E + 1,
+ {V,X + Y}.
+
+
%% OTP-7018.
match_in_call(Config) when is_list(Config) ->
@@ -1018,4 +1113,16 @@ match_boolean_list(Config) when is_list(Config) ->
[false | _] -> ok
end.
+heisen_variables(_Config) ->
+ {'EXIT',{{badmatch,3},_}} = catch gh_6516_scope1(),
+ {'EXIT',{{badmatch,3},_}} = catch gh_6516_scope2(),
+
+ ok.
+
+gh_6516_scope1() ->
+ {X = 4, X = 3}.
+
+gh_6516_scope2() ->
+ {X = 4, _ = X = 3}.
+
id(I) -> I.