From cc7a3c5896bc266bb97287dd4e31b696d8cf604f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 2 Apr 2014 17:00:43 +0200 Subject: compiler,stdlib: Fix Map literals as keys for Maps in patterns --- lib/compiler/src/cerl.erl | 2 +- lib/compiler/src/core_lib.erl | 11 +++++++++-- lib/compiler/src/sys_pre_expand.erl | 2 +- lib/compiler/src/v3_core.erl | 29 ++++++++++------------------- lib/compiler/test/map_SUITE.erl | 5 +++++ lib/stdlib/src/erl_expand_records.erl | 6 +++--- 6 files changed, 29 insertions(+), 26 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index ed11c8de4d..54eac20ac4 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -133,7 +133,7 @@ ]). -export_type([c_binary/0, c_bitstr/0, c_call/0, c_clause/0, c_cons/0, c_fun/0, - c_literal/0, c_map_pair/0, c_module/0, c_tuple/0, + c_literal/0, c_map/0, c_map_pair/0, c_module/0, c_tuple/0, c_values/0, c_var/0, cerl/0, var_name/0]). -include("core_parse.hrl"). diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl index 93ec3bbad5..2792fd8fa5 100644 --- a/lib/compiler/src/core_lib.erl +++ b/lib/compiler/src/core_lib.erl @@ -59,7 +59,7 @@ is_lit_bin(Es) -> %% Return the value of LitExpr. -spec literal_value(cerl:c_literal() | cerl:c_binary() | - cerl:c_cons() | cerl:c_tuple()) -> term(). + cerl:c_map() | cerl:c_cons() | cerl:c_tuple()) -> term(). literal_value(#c_literal{val=V}) -> V; literal_value(#c_binary{segments=Es}) -> @@ -67,7 +67,14 @@ literal_value(#c_binary{segments=Es}) -> literal_value(#c_cons{hd=H,tl=T}) -> [literal_value(H)|literal_value(T)]; literal_value(#c_tuple{es=Es}) -> - list_to_tuple(literal_value_list(Es)). + list_to_tuple(literal_value_list(Es)); +literal_value(#c_map{arg=Cm,es=Cmps}) -> + M = literal_value(Cm), + lists:foldl(fun(#c_map_pair{ key=Ck, val=Cv },Mi) -> + K = literal_value(Ck), + V = literal_value(Cv), + maps:put(K,V,Mi) + end, M, Cmps). literal_value_list(Vals) -> [literal_value(V) || V <- Vals]. diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl index 91a46a20fe..761ae8409c 100644 --- a/lib/compiler/src/sys_pre_expand.erl +++ b/lib/compiler/src/sys_pre_expand.erl @@ -232,7 +232,7 @@ pattern({map,Line,Ps}, St0) -> {TPs,St1} = pattern_list(Ps, St0), {{map,Line,TPs},St1}; pattern({map_field_exact,Line,K0,V0}, St0) -> - {K,St1} = pattern(K0, St0), + {K,St1} = expr(K0, St0), {V,St2} = pattern(V0, St1), {{map_field_exact,Line,K,V},St2}; %%pattern({struct,Line,Tag,Ps}, St0) -> diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index a548ba2f7c..8c18f6a9f7 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -1605,26 +1605,17 @@ pattern_alias_map_pair_patterns([Cv1,Cv2|Cvs]) -> pattern_alias_map_pair_patterns([pat_alias(Cv1,Cv2)|Cvs]). pattern_map_pair({map_field_exact,L,K,V}, St) -> - %% FIXME: Better way to construct literals? or missing case - %% {Key,_,_} = expr(K, St), - Key = case K of - {bin,L,Es0} -> - case constant_bin(Es0) of - error -> - %% this will throw a cryptic error message - %% but it is better than nothing - throw(nomatch); - Bin -> - #c_literal{anno=lineno_anno(L,St),val=Bin} - end; + case expr(K,St) of + {#c_literal{}=Key,_,_} -> + #c_map_pair{anno=lineno_anno(L, St), + op=#c_literal{val=exact}, + key=Key, + val=pattern(V, St)}; _ -> - pattern(K,St) - end, - #c_map_pair{anno=lineno_anno(L, St), - op=#c_literal{val=exact}, - key=Key, - val=pattern(V, St)}. - + %% this will throw a cryptic error message + %% but it is better than nothing + throw(nomatch) + end. %% pat_bin([BinElement], State) -> [BinSeg]. diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl index cc018e4305..403b7e8405 100644 --- a/lib/compiler/test/map_SUITE.erl +++ b/lib/compiler/test/map_SUITE.erl @@ -113,6 +113,10 @@ t_build_and_match_literals(Config) when is_list(Config) -> M = #{ map_1:=#{ map_2:=#{value_3 := third}, value_2:= second}, value_1:=first} = id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}), + %% map key + #{ #{} := 42 } = id(#{ #{} => 42 }), + #{ #{ "a" => 3 } := 42 } = id(#{ #{ "a" => 3} => 42 }), + %% nil key #{[]:=ok,1:=2} = id(#{[]=>ok,1=>2}), @@ -123,6 +127,7 @@ t_build_and_match_literals(Config) when is_list(Config) -> {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))), {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))), {'EXIT',{badarg,_}} = (catch id(#{<<0:258>> =>"three"})), + {'EXIT',{{badmatch,_},_}} = (catch (#{#{"a"=>42} := 3}=id(#{#{"a"=>3}=>42}))), ok. t_build_and_match_aliasing(Config) when is_list(Config) -> diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl index f53c6e1278..57e768ba9d 100644 --- a/lib/stdlib/src/erl_expand_records.erl +++ b/lib/stdlib/src/erl_expand_records.erl @@ -135,10 +135,10 @@ pattern({tuple,Line,Ps}, St0) -> pattern({map,Line,Ps}, St0) -> {TPs,St1} = pattern_list(Ps, St0), {{map,Line,TPs},St1}; -pattern({map_field_exact,Line,Key0,V0}, St0) -> - {Key,St1} = pattern(Key0, St0), +pattern({map_field_exact,Line,K0,V0}, St0) -> + {K,St1} = expr(K0, St0), {V,St2} = pattern(V0, St1), - {{map_field_exact,Line,Key,V},St2}; + {{map_field_exact,Line,K,V},St2}; %%pattern({struct,Line,Tag,Ps}, St0) -> %% {TPs,TPsvs,St1} = pattern_list(Ps, St0), %% {{struct,Line,Tag,TPs},TPsvs,St1}; -- cgit v1.2.3 From c655521ab142547c371df3470a3fc62beef048ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 2 Apr 2014 18:35:53 +0200 Subject: compiler: Fix compiling map keys in patterns from core --- lib/compiler/src/core_pp.erl | 6 +++++- lib/compiler/src/sys_core_fold.erl | 12 ++---------- 2 files changed, 7 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl index a76327457d..83412ecdd7 100644 --- a/lib/compiler/src/core_pp.erl +++ b/lib/compiler/src/core_pp.erl @@ -120,7 +120,11 @@ format_1(#c_literal{anno=A,val=Bitstring}, Ctxt) when is_bitstring(Bitstring) -> format_1(#c_binary{anno=A,segments=Segs}, Ctxt); format_1(#c_literal{anno=A,val=M},Ctxt) when is_map(M) -> Pairs = maps:to_list(M), - Cpairs = [#c_map_pair{op=#c_literal{val=assoc}, + Op = case Ctxt of + #ctxt{ class = clause } -> exact; + _ -> assoc + end, + Cpairs = [#c_map_pair{op=#c_literal{val=Op}, key=#c_literal{val=V}, val=#c_literal{val=K}} || {K,V} <- Pairs], format_1(#c_map{anno=A,arg=#c_literal{val=#{}},es=Cpairs},Ctxt); diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index b7422318b2..ce40213bad 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -1556,16 +1556,8 @@ map_pair_pattern_list(Ps0, Isub, Osub0) -> {Ps,Osub}. map_pair_pattern(#c_map_pair{op=#c_literal{val=exact},key=K0,val=V0}=Pair,{Isub,Osub0}) -> - {K,Osub1} = case cerl:type(K0) of - binary -> - K1 = eval_binary(K0), - case cerl:type(K1) of - literal -> {K1,Osub0}; - _ -> pattern(K0,Isub,Osub0) - end; - _ -> pattern(K0,Isub,Osub0) - end, - {V,Osub} = pattern(V0,Isub,Osub1), + K = expr(K0, Isub), + {V,Osub} = pattern(V0,Isub,Osub0), {Pair#c_map_pair{key=K,val=V},{Isub,Osub}}. bin_pattern_list(Ps0, Isub, Osub0) -> -- cgit v1.2.3 From 7b11f5a2608ea2a539454fe8f14e4d24d3a0170f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 2 Apr 2014 18:57:49 +0200 Subject: stdlib: Deny map keys defined as #{ .. := .. } in patterns --- lib/stdlib/src/erl_lint.erl | 36 ++++++++++++++++++++---------------- lib/stdlib/test/erl_lint_SUITE.erl | 14 +++++++++++++- 2 files changed, 33 insertions(+), 17 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index c4c94fbee4..7c064ce902 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -1407,7 +1407,7 @@ pattern({map,_Line,Ps}, Vt, Old, Bvt, St) -> ({map_field_assoc,L,_,_}, {Psvt,Bvt0,St0}) -> {Psvt,Bvt0,add_error(L, illegal_pattern, St0)}; ({map_field_exact,L,KP,VP}, {Psvt,Bvt0,St0}) -> - case is_valid_map_key(KP, St0) of + case is_valid_map_key(KP, pattern, St0) of true -> {Pvt,Bvt1,St1} = pattern(VP, Vt, Old, Bvt, St0), {vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt0, Bvt1), St1}; @@ -2322,14 +2322,16 @@ is_valid_call(Call) -> %% check for value expression without variables is_valid_map_key(K,St) -> + is_valid_map_key(K,expr,St). +is_valid_map_key(K,Ctx,St) -> case expr(K,[],St) of {[],_} -> - is_valid_map_key_value(K); + is_valid_map_key_value(K,Ctx); {[Var|_],_} -> {false,variable,element(1,Var)} end. -is_valid_map_key_value(K) -> +is_valid_map_key_value(K,Ctx) -> case K of {char,_,_} -> true; {integer,_,_} -> true; @@ -2338,34 +2340,36 @@ is_valid_map_key_value(K) -> {nil,_} -> true; {atom,_,_} -> true; {cons,_,H,T} -> - is_valid_map_key_value(H) andalso - is_valid_map_key_value(T); + is_valid_map_key_value(H,Ctx) andalso + is_valid_map_key_value(T,Ctx); {tuple,_,Es} -> foldl(fun(E,B) -> - B andalso is_valid_map_key_value(E) + B andalso is_valid_map_key_value(E,Ctx) end,true,Es); {map,_,Arg,Ps} -> % only check for value expressions to be valid % invalid map expressions are later checked in % core and kernel - is_valid_map_key_value(Arg) andalso foldl(fun + is_valid_map_key_value(Arg,Ctx) andalso foldl(fun ({Tag,_,Ke,Ve},B) when Tag =:= map_field_assoc; - Tag =:= map_field_exact -> - B andalso is_valid_map_key_value(Ke) - andalso is_valid_map_key_value(Ve) + Tag =:= map_field_exact, Ctx =:= expr -> + B andalso is_valid_map_key_value(Ke,Ctx) + andalso is_valid_map_key_value(Ve,Ctx); + (_,_) -> false end,true,Ps); {map,_,Ps} -> foldl(fun ({Tag,_,Ke,Ve},B) when Tag =:= map_field_assoc; - Tag =:= map_field_exact -> - B andalso is_valid_map_key_value(Ke) - andalso is_valid_map_key_value(Ve) + Tag =:= map_field_exact, Ctx =:= expr -> + B andalso is_valid_map_key_value(Ke,Ctx) + andalso is_valid_map_key_value(Ve,Ctx); + (_,_) -> false end, true, Ps); {record,_,_,Fs} -> foldl(fun ({record_field,_,Ke,Ve},B) -> - B andalso is_valid_map_key_value(Ke) - andalso is_valid_map_key_value(Ve) + B andalso is_valid_map_key_value(Ke,Ctx) + andalso is_valid_map_key_value(Ve,Ctx) end,true,Fs); {bin,_,Es} -> % only check for value expressions to be valid @@ -2373,7 +2377,7 @@ is_valid_map_key_value(K) -> % core and kernel foldl(fun ({bin_element,_,E,_,_},B) -> - B andalso is_valid_map_key_value(E) + B andalso is_valid_map_key_value(E,Ctx) end,true,Es); _ -> false end. diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index bb14de333d..d9512c0ef4 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -3406,7 +3406,19 @@ maps(Config) -> {4,erl_lint,illegal_map_key}, {6,erl_lint,illegal_map_key}, {8,erl_lint,illegal_map_key}, - {10,erl_lint,illegal_map_key}],[]}}], + {10,erl_lint,illegal_map_key}],[]}}, + {errors_in_map_keys_pattern, + <<"t(#{ a := 2, + #{} := A, + #{ 3 => 33 } := hi, + #{ 3 := 33 } := hi, + #{ hi => 54, \"hello\" => 45 } := hi, + #{ V => 33 } := hi }) -> + A. + ">>, + [], + {errors,[{4,erl_lint,illegal_map_key}, + {6,erl_lint,{illegal_map_key_variable,'V'}}],[]}}], [] = run(Config, Ts), ok. -- cgit v1.2.3 From e26dbc750fdd207c852bf9006668c5771f465dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 3 Apr 2014 15:36:50 +0200 Subject: stdlib: Fix erl_id_trans example --- lib/stdlib/examples/erl_id_trans.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/stdlib/examples/erl_id_trans.erl b/lib/stdlib/examples/erl_id_trans.erl index 5fcb74310e..e71e26e51a 100644 --- a/lib/stdlib/examples/erl_id_trans.erl +++ b/lib/stdlib/examples/erl_id_trans.erl @@ -148,7 +148,7 @@ pattern({map,Line,Ps0}) -> Ps1 = pattern_list(Ps0), {map,Line,Ps1}; pattern({map_field_exact,Line,K,V}) -> - Ke = pattern(K), + Ke = expr(K), Ve = pattern(V), {map_field_exact,Line,Ke,Ve}; %%pattern({struct,Line,Tag,Ps0}) -> -- cgit v1.2.3