From ea699f3510b464d6b65b30342f2ddd5912e127a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 26 Feb 2014 17:46:57 +0100 Subject: compiler: Create literal Maps in creation if possible --- lib/compiler/src/cerl.erl | 20 ++++++++++++++++++-- lib/compiler/src/v3_core.erl | 7 ++++--- 2 files changed, 22 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index 3c121f3b04..744ebc7aca 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -126,7 +126,7 @@ map_es/1, map_val/1, update_c_map/3, - ann_c_map/3, + ann_c_map/2, ann_c_map/3, map_pair_op/1,map_pair_key/1,map_pair_val/1, update_c_map_pair/4, ann_c_map_pair/4 @@ -1584,8 +1584,24 @@ map_es(#c_map{es = Es}) -> map_val(#c_map{var = M}) -> M. +ann_c_map(As,Es) -> + ann_c_map(As, #c_literal{val=[]}, Es). + +ann_c_map(As,#c_literal{val=[]}=M,Es) -> + Pairs = [[K,V]||#c_map_pair{key=K,val=V}<-Es], + IsLit = lists:foldl(fun(Pair,Res) -> + Res andalso is_lit_list(Pair) + end, true, Pairs), + Fun = fun(Pair) -> [K,V] = lit_list_vals(Pair), {K,V} end, + case IsLit of + false -> + #c_map{var=M, es=Es, anno=As }; + true -> + #c_literal{anno=As, val=maps:from_list(lists:map(Fun, Pairs))} + end; ann_c_map(As,M,Es) -> - #c_map{var=M,es = Es, anno = As }. + #c_map{var=M, es = Es, anno = As }. + update_c_map(Old,M,Es) -> #c_map{var=M, es = Es, anno = get_ann(Old)}. diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 3d17557e01..80e9aca3d0 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -77,7 +77,8 @@ splitwith/2,keyfind/3,sort/1,foreach/2,droplast/1,last/1]). -import(ordsets, [add_element/2,del_element/2,is_element/2, union/1,union/2,intersection/2,subtract/2]). --import(cerl, [ann_c_cons/3,ann_c_cons_skel/3,ann_c_tuple/2,c_tuple/1]). +-import(cerl, [ann_c_cons/3,ann_c_cons_skel/3,ann_c_tuple/2,c_tuple/1, + ann_c_map/2, ann_c_map/3]). -include("core_parse.hrl"). @@ -515,12 +516,12 @@ expr({map,L,Es0}, St0) -> % in map construction. {Es1,Eps,St1} = map_pair_list(Es0, St0), A = lineno_anno(L, St1), - {#c_map{anno=A,es=Es1},Eps,St1}; + {ann_c_map(A,Es1),Eps,St1}; expr({map,L,M0,Es0}, St0) -> {M1,Mps,St1} = safe(M0, St0), {Es1,Eps,St2} = map_pair_list(Es0, St1), A = lineno_anno(L, St2), - {#c_map{anno=A,var=M1,es=Es1},Mps++Eps,St2}; + {ann_c_map(A,M1,Es1),Mps++Eps,St2}; expr({bin,L,Es0}, St0) -> try expr_bin(Es0, lineno_anno(L, St0), St0) of {_,_,_}=Res -> Res -- cgit v1.2.3 From 70c968442a8e2afbae7fa485527920006bfe2031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 6 Mar 2014 17:59:09 +0100 Subject: compiler: Change Maps Core Format Ex. Instead of: M~{~}~ The format is now: ~{~|M}~ This also removes a shift/reduce warning. The changes in core_pp now requires compiler-5.0 to compile because of is_map/1 guard, i.e. a need for a compiler with Maps know-how. --- lib/compiler/src/core_parse.yrl | 8 ++++---- lib/compiler/src/core_pp.erl | 14 ++++++++++---- lib/compiler/src/v3_kernel_pp.erl | 18 ++++++++++++------ lib/compiler/test/core_SUITE_data/map_core_test.core | 4 ++-- 4 files changed, 28 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/core_parse.yrl b/lib/compiler/src/core_parse.yrl index b8db0f683a..8d07ab42b0 100644 --- a/lib/compiler/src/core_parse.yrl +++ b/lib/compiler/src/core_parse.yrl @@ -21,7 +21,7 @@ %% Have explicit productions for annotated phrases named anno_XXX. %% This just does an XXX and adds the annotation. -Expect 1. +Expect 0. Nonterminals @@ -285,9 +285,9 @@ tuple -> '{' '}' : c_tuple([]). tuple -> '{' anno_expressions '}' : c_tuple('$2'). map_expr -> '~' '{' '}' '~' : #c_map{es=[]}. -map_expr -> '~' '{' map_pairs '}' '~' : #c_map{es='$3'}. -map_expr -> variable '~' '{' '}' '~' : #c_map{var='$1',es=[]}. -map_expr -> variable '~' '{' map_pairs '}' '~' : #c_map{var='$1',es='$4'}. +map_expr -> '~' '{' map_pairs '}' '~' : #c_map{es='$3'}. +map_expr -> '~' '{' map_pairs '|' variable '}' '~' : #c_map{var='$5',es='$3'}. +map_expr -> '~' '{' map_pairs '|' map_expr '}' '~' : #c_map{var='$5',es='$3'}. map_pairs -> map_pair : ['$1']. map_pairs -> map_pair ',' map_pairs : ['$1' | '$3']. diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl index faa26ec6df..ada556a970 100644 --- a/lib/compiler/src/core_pp.erl +++ b/lib/compiler/src/core_pp.erl @@ -118,6 +118,12 @@ format_1(#c_literal{val=Tuple}, Ctxt) when is_tuple(Tuple) -> format_1(#c_literal{anno=A,val=Bitstring}, Ctxt) when is_bitstring(Bitstring) -> Segs = segs_from_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}, + key=#c_literal{val=V}, + val=#c_literal{val=K}} || {K,V} <- Pairs], + format_1(#c_map{anno=A,var=#c_literal{val=#{}},es=Cpairs},Ctxt); format_1(#c_var{name={I,A}}, _) -> [core_atom(I),$/,integer_to_list(A)]; format_1(#c_var{name=V}, _) -> @@ -161,15 +167,15 @@ format_1(#c_tuple{es=Es}, Ctxt) -> format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2), $} ]; -format_1(#c_map{var=#c_var{}=Var,es=Es}, Ctxt) -> - [format_1(Var, Ctxt), - "~{", +format_1(#c_map{var=#c_literal{val=M},es=Es}, Ctxt) when is_map(M),map_size(M)=:=0 -> + ["~{", format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2), "}~" ]; -format_1(#c_map{es=Es}, Ctxt) -> +format_1(#c_map{var=Var,es=Es}, Ctxt) -> ["~{", format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2), + "|",format(Var, add_indent(Ctxt, 1)), "}~" ]; format_1(#c_map_pair{op=#c_literal{val=assoc},key=K,val=V}, Ctxt) -> diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl index b4e486f97c..b33eba50eb 100644 --- a/lib/compiler/src/v3_kernel_pp.erl +++ b/lib/compiler/src/v3_kernel_pp.erl @@ -104,20 +104,26 @@ format_1(#k_tuple{es=Es}, Ctxt) -> format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), $} ]; -format_1(#k_map{var=#k_var{}=Var,es=Es}, Ctxt) -> - [$~,${, +format_1(#k_map{var=#k_literal{val=M},op=assoc,es=Es}, Ctxt) when is_map(M), map_size(M) =:= 0 -> + ["~{", format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), - " | ",format_1(Var, Ctxt), - $},$~ + "}~" ]; -format_1(#k_map{op=assoc,es=Es}, Ctxt) -> +format_1(#k_map{var=#k_literal{val=M},op=exact,es=Es}, Ctxt) when is_map(M), map_size(M) =:= 0 -> + ["::{", + format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), + "}::" + ]; +format_1(#k_map{var=Var,op=assoc,es=Es}, Ctxt) -> ["~{", format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), + " | ",format_1(Var, Ctxt), "}~" ]; -format_1(#k_map{es=Es}, Ctxt) -> +format_1(#k_map{var=Var,op=exact,es=Es}, Ctxt) -> ["::{", format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), + " | ",format_1(Var, Ctxt), "}::" ]; format_1(#k_map_pair{key=K,val=V}, Ctxt) -> diff --git a/lib/compiler/test/core_SUITE_data/map_core_test.core b/lib/compiler/test/core_SUITE_data/map_core_test.core index 7ece8a8bbd..2aa853d450 100644 --- a/lib/compiler/test/core_SUITE_data/map_core_test.core +++ b/lib/compiler/test/core_SUITE_data/map_core_test.core @@ -67,7 +67,7 @@ module 'map_core_test' ['map_core_test'/0, (Val, V) in let <_cor5> = %% Line 21 - M~{~<1337,_cor4>,~<'val',_cor2>}~ + ~{~<1337,_cor4>,~<'val',_cor2>|M}~ in %% Line 21 apply 'call'/2 (_cor5, Vs) @@ -92,4 +92,4 @@ module 'map_core_test' ['map_core_test'/0, fun (_cor0) -> call 'erlang':'get_module_info' ('map_core_test', _cor0) -end \ No newline at end of file +end -- cgit v1.2.3 From 2890335ec4ecee6888d184a6dc73c7fc227c0566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 6 Mar 2014 19:07:10 +0100 Subject: compiler: Guard BIF is_map/1 is pure --- lib/compiler/src/erl_bifs.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl index 3ad3c8c690..6c75538194 100644 --- a/lib/compiler/src/erl_bifs.erl +++ b/lib/compiler/src/erl_bifs.erl @@ -91,6 +91,7 @@ is_pure(erlang, is_float, 1) -> true; is_pure(erlang, is_function, 1) -> true; is_pure(erlang, is_integer, 1) -> true; is_pure(erlang, is_list, 1) -> true; +is_pure(erlang, is_map, 1) -> true; is_pure(erlang, is_number, 1) -> true; is_pure(erlang, is_pid, 1) -> true; is_pure(erlang, is_port, 1) -> true; -- cgit v1.2.3 From c43dd2abaae653d5411fc662cad177c5971dde3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 6 Mar 2014 20:32:01 +0100 Subject: compiler: Support literal maps in cerl_clauses:match/2 --- lib/compiler/src/cerl_clauses.erl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/cerl_clauses.erl b/lib/compiler/src/cerl_clauses.erl index 76d70dcabf..87bd47c08b 100644 --- a/lib/compiler/src/cerl_clauses.erl +++ b/lib/compiler/src/cerl_clauses.erl @@ -356,14 +356,19 @@ match(P, E, Bs) -> end; map -> %% The most we can do is to say "definitely no match" if a - %% binary pattern is matched against non-binary data. + %% map pattern is matched against non-map data. case E of any -> {false, Bs}; _ -> case type(E) of - literal -> - none; + literal -> + case is_map(concrete(E)) of + false -> + none; + true -> + {false, Bs} + end; cons -> none; tuple -> -- cgit v1.2.3 From 90a948d7cf7f188b5461c2a7bb25a656ec681966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 11 Mar 2014 14:50:46 +0100 Subject: compiler: Validate Map src Reject all expressions that are known to fail. Emit 'badarg' for those expressions. Ex. []#{ a => 1} Is not a valid map update expression. --- lib/compiler/src/cerl.erl | 4 ++-- lib/compiler/src/core_parse.hrl | 2 +- lib/compiler/src/sys_core_fold.erl | 6 +++--- lib/compiler/src/v3_core.erl | 34 +++++++++++++++++++++++++++++----- lib/compiler/src/v3_kernel.erl | 34 ++++++++++++++++++++++++++++++---- 5 files changed, 65 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index 744ebc7aca..9024999d7f 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -1585,9 +1585,9 @@ map_val(#c_map{var = M}) -> M. ann_c_map(As,Es) -> - ann_c_map(As, #c_literal{val=[]}, Es). + ann_c_map(As, #c_literal{val=#{}}, Es). -ann_c_map(As,#c_literal{val=[]}=M,Es) -> +ann_c_map(As,#c_literal{val=Mval}=M,Es) when is_map(Mval), map_size(Mval) =:= 0 -> Pairs = [[K,V]||#c_map_pair{key=K,val=V}<-Es], IsLit = lists:foldl(fun(Pair,Res) -> Res andalso is_lit_list(Pair) diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl index d54715ef59..094bbb8a71 100644 --- a/lib/compiler/src/core_parse.hrl +++ b/lib/compiler/src/core_parse.hrl @@ -103,5 +103,5 @@ val}). -record(c_map, {anno=[], - var=#c_literal{val=[]} :: #c_var{} | #c_literal{}, + var=#c_literal{val=#{}} :: #c_var{} | #c_literal{}, es :: [#c_map_pair{}]}). diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 058abd3357..90cc3b9051 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -72,7 +72,7 @@ -import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,all/2,any/2, reverse/1,reverse/2,member/2,nth/2,flatten/1,unzip/1]). --import(cerl, [ann_c_cons/3,ann_c_tuple/2]). +-import(cerl, [ann_c_cons/3,ann_c_tuple/2,ann_c_map/3]). -include("core_parse.hrl"). @@ -246,7 +246,7 @@ expr(#c_tuple{anno=Anno,es=Es0}=Tuple, Ctxt, Sub) -> value -> ann_c_tuple(Anno, Es) end; -expr(#c_map{var=V0,es=Es0}=Map, Ctxt, Sub) -> +expr(#c_map{anno=Anno,var=V0,es=Es0}=Map, Ctxt, Sub) -> Es = pair_list(Es0, Ctxt, Sub), case Ctxt of effect -> @@ -254,7 +254,7 @@ expr(#c_map{var=V0,es=Es0}=Map, Ctxt, Sub) -> expr(make_effect_seq(Es, Sub), Ctxt, Sub); value -> V = expr(V0, Ctxt, Sub), - Map#c_map{var=V,es=Es} + ann_c_map(Anno,V,Es) end; expr(#c_binary{segments=Ss}=Bin0, Ctxt, Sub) -> %% Warn for useless building, but always build the binary diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 80e9aca3d0..bf3368c31d 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -518,10 +518,18 @@ expr({map,L,Es0}, St0) -> A = lineno_anno(L, St1), {ann_c_map(A,Es1),Eps,St1}; expr({map,L,M0,Es0}, St0) -> - {M1,Mps,St1} = safe(M0, St0), - {Es1,Eps,St2} = map_pair_list(Es0, St1), - A = lineno_anno(L, St2), - {ann_c_map(A,M1,Es1),Mps++Eps,St2}; + try expr_map(M0,Es0,lineno_anno(L, St0),St0) of + {_,_,_}=Res -> Res + catch + throw:bad_map -> + St = add_warning(L, bad_map, St0), + LineAnno = lineno_anno(L, St), + As = [#c_literal{anno=LineAnno,val=badarg}], + {#icall{anno=#a{anno=LineAnno}, %Must have an #a{} + module=#c_literal{anno=LineAnno,val=erlang}, + name=#c_literal{anno=LineAnno,val=error}, + args=As},[],St} + end; expr({bin,L,Es0}, St0) -> try expr_bin(Es0, lineno_anno(L, St0), St0) of {_,_,_}=Res -> Res @@ -731,6 +739,20 @@ make_bool_switch_guard(L, E, V, T, F) -> {clause,NegL,[V],[],[V]} ]}. +expr_map(M0,Es0,A,St0) -> + {M1,Mps,St1} = safe(M0, St0), + case is_valid_map_src(M1) of + true -> + {Es1,Eps,St2} = map_pair_list(Es0, St1), + {ann_c_map(A,M1,Es1),Mps++Eps,St2}; + false -> throw(bad_map) + end. + +is_valid_map_src(#c_literal{val = M}) when is_map(M) -> true; +is_valid_map_src(#c_map{}) -> true; +is_valid_map_src(#c_var{}) -> true; +is_valid_map_src(_) -> false. + map_pair_list(Es, St) -> foldr(fun ({map_field_assoc,L,K0,V0}, {Ces,Esp,St0}) -> @@ -2257,7 +2279,9 @@ is_simple_list(Es) -> lists:all(fun is_simple/1, Es). format_error(nomatch) -> "pattern cannot possibly match"; format_error(bad_binary) -> - "binary construction will fail because of a type mismatch". + "binary construction will fail because of a type mismatch"; +format_error(bad_map) -> + "map construction will fail because of a type mismatch". add_warning(Line, Term, #core{ws=Ws,file=[{file,File}]}=St) when Line >= 0 -> St#core{ws=[{File,[{location(Line),?MODULE,Term}]}|Ws]}; diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index d00dd56f30..ecff01c4e5 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -272,9 +272,18 @@ expr(#c_cons{anno=A,hd=Ch,tl=Ct}, Sub, St0) -> expr(#c_tuple{anno=A,es=Ces}, Sub, St0) -> {Kes,Ep,St1} = atomic_list(Ces, Sub, St0), {#k_tuple{anno=A,es=Kes},Ep,St1}; -expr(#c_map{anno=A,var=Var0,es=Ces}, Sub, St0) -> - {Var,[],St1} = expr(Var0, Sub, St0), - map_split_pairs(A, Var, Ces, Sub, St1); +expr(#c_map{anno=A,var=Var,es=Ces}, Sub, St0) -> + try expr_map(A,Var,Ces,Sub,St0) of + {_,_,_}=Res -> Res + catch + throw:bad_map -> + St1 = add_warning(get_line(A), bad_map, A, St0), + Erl = #c_literal{val=erlang}, + Name = #c_literal{val=error}, + Args = [#c_literal{val=badarg}], + Error = #c_call{anno=A,module=Erl,name=Name,args=Args}, + expr(Error, Sub, St1) + end; expr(#c_binary{anno=A,segments=Cv}, Sub, St0) -> try atomic_bin(Cv, Sub, St0) of {Kv,Ep,St1} -> @@ -496,6 +505,21 @@ translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) -> translate_fc(Args) -> [#c_literal{val=function_clause},make_list(Args)]. +expr_map(A,Var0,Ces,Sub,St0) -> + %% An extra pass of validation of Map src because of inlining + {Var,Mps,St1} = expr(Var0, Sub, St0), + case is_valid_map_src(Var) of + true -> + {Km,Eps,St2} = map_split_pairs(A, Var, Ces, Sub, St1), + {Km,Eps++Mps,St2}; + false -> throw(bad_map) + end. + +is_valid_map_src(#k_map{}) -> true; +is_valid_map_src(#k_literal{val=M}) when is_map(M) -> true; +is_valid_map_src(#k_var{}) -> true; +is_valid_map_src(_) -> false. + map_split_pairs(A, Var, Ces, Sub, St0) -> %% two steps %% 1. force variables @@ -1986,7 +2010,9 @@ format_error(nomatch_shadow) -> format_error(bad_call) -> "invalid module and/or function name; this call will always fail"; format_error(bad_segment_size) -> - "binary construction will fail because of a type mismatch". + "binary construction will fail because of a type mismatch"; +format_error(bad_map) -> + "map construction will fail because of a type mismatch". add_warning(none, Term, Anno, #kern{ws=Ws}=St) -> File = get_file(Anno), -- cgit v1.2.3 From 5d75a3b453b64ae1b10c8980ec5c146d7b2d5d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 6 Mar 2014 18:00:52 +0100 Subject: compiler: Constant fold Maps that are safe For updates of Map literals which may cause an error will be determined in runtime, i.e. instructions are emitted for those updates. The changes in cerl now requires compiler-5.0 to compile because of is_map/1 guard. --- lib/compiler/src/cerl.erl | 32 ++++++++++++++++++++++++++++++-- lib/compiler/src/sys_core_fold.erl | 4 +++- 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index 9024999d7f..61b47ed468 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -1588,7 +1588,7 @@ ann_c_map(As,Es) -> ann_c_map(As, #c_literal{val=#{}}, Es). ann_c_map(As,#c_literal{val=Mval}=M,Es) when is_map(Mval), map_size(Mval) =:= 0 -> - Pairs = [[K,V]||#c_map_pair{key=K,val=V}<-Es], + Pairs = [[Ck,Cv]||#c_map_pair{key=Ck,val=Cv}<-Es], IsLit = lists:foldl(fun(Pair,Res) -> Res andalso is_lit_list(Pair) end, true, Pairs), @@ -1599,8 +1599,36 @@ ann_c_map(As,#c_literal{val=Mval}=M,Es) when is_map(Mval), map_size(Mval) =:= 0 true -> #c_literal{anno=As, val=maps:from_list(lists:map(Fun, Pairs))} end; +ann_c_map(As,#c_literal{val=M},Es) when is_map(M) -> + fold_map_pairs(As,Es,M); ann_c_map(As,M,Es) -> - #c_map{var=M, es = Es, anno = As }. + #c_map{var=M, es=Es, anno=As }. + +fold_map_pairs(As,[],M) -> #c_literal{anno=As,val=M}; +%% M#{ K => V} +fold_map_pairs(As,[#c_map_pair{op=#c_literal{val=assoc},key=Ck,val=Cv}=E|Es],M) -> + case is_lit_list([Ck,Cv]) of + true -> + [K,V] = lit_list_vals([Ck,Cv]), + fold_map_pairs(As,Es,maps:put(K,V,M)); + false -> + #c_map{var=#c_literal{val=M,anno=As}, es=[E|Es], anno=As } + end; +%% M#{ K := V} +fold_map_pairs(As,[#c_map_pair{op=#c_literal{val=exact},key=Ck,val=Cv}=E|Es],M) -> + case is_lit_list([Ck,Cv]) of + true -> + [K,V] = lit_list_vals([Ck,Cv]), + case maps:is_key(K,M) of + true -> fold_map_pairs(As,Es,maps:put(K,V,M)); + false -> + #c_map{var=#c_literal{val=M,anno=As}, es=[E|Es], anno=As } + end; + false -> + #c_map{var=#c_literal{val=M,anno=As}, es=[E|Es], anno=As } + end; +fold_map_pairs(As,Es,M) -> + #c_map{var=#c_literal{val=M,anno=As}, es=Es, anno=As }. update_c_map(Old,M,Es) -> diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 90cc3b9051..bf1be6d773 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -72,7 +72,7 @@ -import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,all/2,any/2, reverse/1,reverse/2,member/2,nth/2,flatten/1,unzip/1]). --import(cerl, [ann_c_cons/3,ann_c_tuple/2,ann_c_map/3]). +-import(cerl, [ann_c_cons/3,ann_c_map/3,ann_c_tuple/2]). -include("core_parse.hrl"). @@ -1378,6 +1378,7 @@ eval_is_record(Call, _, _, _, _) -> Call. is_not_integer(#c_literal{val=Val}) when not is_integer(Val) -> true; is_not_integer(#c_tuple{}) -> true; is_not_integer(#c_cons{}) -> true; +is_not_integer(#c_map{}) -> true; is_not_integer(_) -> false. %% is_not_tuple(Core) -> true | false. @@ -1385,6 +1386,7 @@ is_not_integer(_) -> false. is_not_tuple(#c_literal{val=Val}) when not is_tuple(Val) -> true; is_not_tuple(#c_cons{}) -> true; +is_not_tuple(#c_map{}) -> true; is_not_tuple(_) -> false. %% eval_setelement(Call, Pos, Tuple, NewVal) -> Core. -- cgit v1.2.3 From fb3ffad98fb1b35d096eaa3ce941b8390395ae8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 17 Mar 2014 17:47:00 +0100 Subject: compiler: Change #c_map{var} to #c_map{arg} Not only variables are allowed as arguments, the name should reflect that. Change cerl Map argument interface * cerl:map_arg/1 is more suitable then cerl:map_val/1 in this case. --- lib/compiler/src/cerl.erl | 31 ++++++++++++++++++++----------- lib/compiler/src/cerl_inline.erl | 8 ++++---- lib/compiler/src/cerl_trees.erl | 8 ++++---- lib/compiler/src/core_lib.erl | 2 +- lib/compiler/src/core_parse.hrl | 2 +- lib/compiler/src/core_parse.yrl | 4 ++-- lib/compiler/src/core_pp.erl | 6 +++--- lib/compiler/src/sys_core_fold.erl | 2 +- lib/compiler/src/v3_kernel.erl | 2 +- 9 files changed, 37 insertions(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index 61b47ed468..ecc4b2c9b1 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -124,7 +124,7 @@ %% keep map exports here for now map_es/1, - map_val/1, + map_arg/1, update_c_map/3, ann_c_map/2, ann_c_map/3, map_pair_op/1,map_pair_key/1,map_pair_val/1, @@ -135,6 +135,9 @@ -export_type([c_binary/0, c_call/0, c_clause/0, c_cons/0, c_fun/0, c_literal/0, c_module/0, c_tuple/0, c_values/0, c_var/0, cerl/0, var_name/0]). +%% HiPE does not understand Maps +%% (guard functions is_map/1 and map_size/1 in ann_c_map/3) +-compile(no_native). %% %% needed by the include file below -- do not move %% @@ -1575,18 +1578,23 @@ ann_make_list(_, [], Node) -> %% --------------------------------------------------------------------- %% maps --spec map_es(c_map()) -> [cerl()]. +-spec map_es(c_map()) -> [c_map_pair()]. map_es(#c_map{es = Es}) -> Es. --spec map_val(c_map()) -> cerl(). -map_val(#c_map{var = M}) -> +-spec map_arg(c_map()) -> c_map() | c_literal(). + +map_arg(#c_map{arg = M}) -> M. +-spec ann_c_map([term()], [cerl()]) -> c_map() | c_literal(). + ann_c_map(As,Es) -> ann_c_map(As, #c_literal{val=#{}}, Es). +-spec ann_c_map([term()], c_map() | c_literal(), [c_map_pair()]) -> c_map() | c_literal(). + ann_c_map(As,#c_literal{val=Mval}=M,Es) when is_map(Mval), map_size(Mval) =:= 0 -> Pairs = [[Ck,Cv]||#c_map_pair{key=Ck,val=Cv}<-Es], IsLit = lists:foldl(fun(Pair,Res) -> @@ -1595,14 +1603,14 @@ ann_c_map(As,#c_literal{val=Mval}=M,Es) when is_map(Mval), map_size(Mval) =:= 0 Fun = fun(Pair) -> [K,V] = lit_list_vals(Pair), {K,V} end, case IsLit of false -> - #c_map{var=M, es=Es, anno=As }; + #c_map{arg=M, es=Es, anno=As }; true -> #c_literal{anno=As, val=maps:from_list(lists:map(Fun, Pairs))} end; ann_c_map(As,#c_literal{val=M},Es) when is_map(M) -> fold_map_pairs(As,Es,M); ann_c_map(As,M,Es) -> - #c_map{var=M, es=Es, anno=As }. + #c_map{arg=M, es=Es, anno=As }. fold_map_pairs(As,[],M) -> #c_literal{anno=As,val=M}; %% M#{ K => V} @@ -1612,7 +1620,7 @@ fold_map_pairs(As,[#c_map_pair{op=#c_literal{val=assoc},key=Ck,val=Cv}=E|Es],M) [K,V] = lit_list_vals([Ck,Cv]), fold_map_pairs(As,Es,maps:put(K,V,M)); false -> - #c_map{var=#c_literal{val=M,anno=As}, es=[E|Es], anno=As } + #c_map{arg=#c_literal{val=M,anno=As}, es=[E|Es], anno=As } end; %% M#{ K := V} fold_map_pairs(As,[#c_map_pair{op=#c_literal{val=exact},key=Ck,val=Cv}=E|Es],M) -> @@ -1622,17 +1630,18 @@ fold_map_pairs(As,[#c_map_pair{op=#c_literal{val=exact},key=Ck,val=Cv}=E|Es],M) case maps:is_key(K,M) of true -> fold_map_pairs(As,Es,maps:put(K,V,M)); false -> - #c_map{var=#c_literal{val=M,anno=As}, es=[E|Es], anno=As } + #c_map{arg=#c_literal{val=M,anno=As}, es=[E|Es], anno=As } end; false -> - #c_map{var=#c_literal{val=M,anno=As}, es=[E|Es], anno=As } + #c_map{arg=#c_literal{val=M,anno=As}, es=[E|Es], anno=As } end; fold_map_pairs(As,Es,M) -> - #c_map{var=#c_literal{val=M,anno=As}, es=Es, anno=As }. + #c_map{arg=#c_literal{val=M,anno=As}, es=Es, anno=As }. +%-spec update_c_map(c_map() | c_literal(), [c_map_pair()]) -> c_map() | c_literal(). update_c_map(Old,M,Es) -> - #c_map{var=M, es = Es, anno = get_ann(Old)}. + #c_map{arg=M, es = Es, anno = get_ann(Old)}. map_pair_key(#c_map_pair{key=K}) -> K. map_pair_val(#c_map_pair{val=V}) -> V. diff --git a/lib/compiler/src/cerl_inline.erl b/lib/compiler/src/cerl_inline.erl index fa1d34cc9b..75740e8b9d 100644 --- a/lib/compiler/src/cerl_inline.erl +++ b/lib/compiler/src/cerl_inline.erl @@ -64,7 +64,7 @@ seq_body/1, set_ann/2, try_arg/1, try_body/1, try_vars/1, try_evars/1, try_handler/1, tuple_es/1, tuple_arity/1, type/1, values_es/1, var_name/1, - map_val/1, map_es/1, update_c_map/3, + map_arg/1, map_es/1, update_c_map/3, update_c_map_pair/4, map_pair_op/1, map_pair_key/1, map_pair_val/1 ]). @@ -1343,7 +1343,7 @@ i_bitstr(E, Ren, Env, S) -> i_map(E, Ctx, Ren, Env, S) -> %% Visit the segments for value. - {M1, S1} = i(map_val(E), value, Ren, Env, S), + {M1, S1} = i(map_arg(E), value, Ren, Env, S), {Es, S2} = mapfoldl(fun (E, S) -> i_map_pair(E, Ctx, Ren, Env, S) end, S1, map_es(E)), @@ -1420,8 +1420,8 @@ i_pattern(E, Ren, Env, Ren0, Env0, S) -> S2 = count_size(weight(binary), S1), {update_c_binary(E, Es), S2}; map -> - %% map patterns should not have vals - M = map_val(E), + %% map patterns should not have args + M = map_arg(E), {Es, S1} = mapfoldl(fun (E, S) -> i_map_pair_pattern(E, Ren, Env, Ren0, Env0, S) diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl index 2ebeab243f..e53bdd4efb 100644 --- a/lib/compiler/src/cerl_trees.erl +++ b/lib/compiler/src/cerl_trees.erl @@ -57,7 +57,7 @@ update_c_try/6, update_c_tuple/2, update_c_tuple_skel/2, update_c_values/2, values_es/1, var_name/1, - map_val/1, map_es/1, + map_arg/1, map_es/1, ann_c_map/3, update_c_map/3, map_pair_key/1,map_pair_val/1,map_pair_op/1, @@ -138,7 +138,7 @@ map_1(F, T) -> tuple -> update_c_tuple_skel(T, map_list(F, tuple_es(T))); map -> - update_c_map(T, map(F,map_val(T)), map_list(F, map_es(T))); + update_c_map(T, map(F, map_arg(T)), map_list(F, map_es(T))); map_pair -> update_c_map_pair(T, map(F, map_pair_op(T)), map(F, map_pair_key(T)), @@ -372,7 +372,7 @@ mapfold(F, S0, T) -> {Ts, S1} = mapfold_list(F, S0, tuple_es(T)), F(update_c_tuple_skel(T, Ts), S1); map -> - {M , S1} = mapfold(F, S0, map_val(T)), + {M , S1} = mapfold(F, S0, map_arg(T)), {Ts, S2} = mapfold_list(F, S1, map_es(T)), F(update_c_map(T, M, Ts), S2); map_pair -> @@ -724,7 +724,7 @@ label(T, N, Env) -> {As, N2} = label_ann(T, N1), {ann_c_tuple_skel(As, Ts), N2}; map -> - {M, N1} = label(map_val(T), N, Env), + {M, N1} = label(map_arg(T), N, Env), {Ts, N2} = label_list(map_es(T), N1, Env), {As, N3} = label_ann(T, N2), {ann_c_map(As, M, Ts), N3}; diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl index ed181e3baa..93ec3bbad5 100644 --- a/lib/compiler/src/core_lib.erl +++ b/lib/compiler/src/core_lib.erl @@ -105,7 +105,7 @@ vu_expr(V, #c_cons{hd=H,tl=T}) -> vu_expr(V, H) orelse vu_expr(V, T); vu_expr(V, #c_tuple{es=Es}) -> vu_expr_list(V, Es); -vu_expr(V, #c_map{var=M,es=Es}) -> +vu_expr(V, #c_map{arg=M,es=Es}) -> vu_expr(V, M) orelse vu_expr_list(V, Es); vu_expr(V, #c_map_pair{key=Key,val=Val}) -> vu_expr_list(V, [Key,Val]); diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl index 094bbb8a71..20f3a46991 100644 --- a/lib/compiler/src/core_parse.hrl +++ b/lib/compiler/src/core_parse.hrl @@ -103,5 +103,5 @@ val}). -record(c_map, {anno=[], - var=#c_literal{val=#{}} :: #c_var{} | #c_literal{}, + arg=#c_literal{val=#{}} :: #c_var{} | #c_literal{}, es :: [#c_map_pair{}]}). diff --git a/lib/compiler/src/core_parse.yrl b/lib/compiler/src/core_parse.yrl index 8d07ab42b0..a66ad4235f 100644 --- a/lib/compiler/src/core_parse.yrl +++ b/lib/compiler/src/core_parse.yrl @@ -286,8 +286,8 @@ tuple -> '{' anno_expressions '}' : c_tuple('$2'). map_expr -> '~' '{' '}' '~' : #c_map{es=[]}. map_expr -> '~' '{' map_pairs '}' '~' : #c_map{es='$3'}. -map_expr -> '~' '{' map_pairs '|' variable '}' '~' : #c_map{var='$5',es='$3'}. -map_expr -> '~' '{' map_pairs '|' map_expr '}' '~' : #c_map{var='$5',es='$3'}. +map_expr -> '~' '{' map_pairs '|' variable '}' '~' : #c_map{arg='$5',es='$3'}. +map_expr -> '~' '{' map_pairs '|' map_expr '}' '~' : #c_map{arg='$5',es='$3'}. map_pairs -> map_pair : ['$1']. map_pairs -> map_pair ',' map_pairs : ['$1' | '$3']. diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl index ada556a970..a76327457d 100644 --- a/lib/compiler/src/core_pp.erl +++ b/lib/compiler/src/core_pp.erl @@ -123,7 +123,7 @@ format_1(#c_literal{anno=A,val=M},Ctxt) when is_map(M) -> Cpairs = [#c_map_pair{op=#c_literal{val=assoc}, key=#c_literal{val=V}, val=#c_literal{val=K}} || {K,V} <- Pairs], - format_1(#c_map{anno=A,var=#c_literal{val=#{}},es=Cpairs},Ctxt); + format_1(#c_map{anno=A,arg=#c_literal{val=#{}},es=Cpairs},Ctxt); format_1(#c_var{name={I,A}}, _) -> [core_atom(I),$/,integer_to_list(A)]; format_1(#c_var{name=V}, _) -> @@ -167,12 +167,12 @@ format_1(#c_tuple{es=Es}, Ctxt) -> format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2), $} ]; -format_1(#c_map{var=#c_literal{val=M},es=Es}, Ctxt) when is_map(M),map_size(M)=:=0 -> +format_1(#c_map{arg=#c_literal{val=M},es=Es}, Ctxt) when is_map(M),map_size(M)=:=0 -> ["~{", format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2), "}~" ]; -format_1(#c_map{var=Var,es=Es}, Ctxt) -> +format_1(#c_map{arg=Var,es=Es}, Ctxt) -> ["~{", format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2), "|",format(Var, add_indent(Ctxt, 1)), diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index bf1be6d773..52d6dfe184 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -246,7 +246,7 @@ expr(#c_tuple{anno=Anno,es=Es0}=Tuple, Ctxt, Sub) -> value -> ann_c_tuple(Anno, Es) end; -expr(#c_map{anno=Anno,var=V0,es=Es0}=Map, Ctxt, Sub) -> +expr(#c_map{anno=Anno,arg=V0,es=Es0}=Map, Ctxt, Sub) -> Es = pair_list(Es0, Ctxt, Sub), case Ctxt of effect -> diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index ecff01c4e5..d3b785aa14 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -272,7 +272,7 @@ expr(#c_cons{anno=A,hd=Ch,tl=Ct}, Sub, St0) -> expr(#c_tuple{anno=A,es=Ces}, Sub, St0) -> {Kes,Ep,St1} = atomic_list(Ces, Sub, St0), {#k_tuple{anno=A,es=Kes},Ep,St1}; -expr(#c_map{anno=A,var=Var,es=Ces}, Sub, St0) -> +expr(#c_map{anno=A,arg=Var,es=Ces}, Sub, St0) -> try expr_map(A,Var,Ces,Sub,St0) of {_,_,_}=Res -> Res catch -- cgit v1.2.3 From c57ac1a6e02ae0fc9b85b204052596ab74cde45e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 12 Mar 2014 22:08:13 +0100 Subject: stdlib: Properly lint map key expressions Only values are valid key expressions. --- lib/stdlib/src/erl_lint.erl | 70 +++++++++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 18 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 269e4b34cf..dc20a18485 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -225,6 +225,8 @@ format_error({too_many_arguments,Arity}) -> "maximum allowed is ~w", [Arity,?MAX_ARGUMENTS]); %% --- patterns and guards --- format_error(illegal_pattern) -> "illegal pattern"; +format_error(illegal_map_key) -> + "illegal map key"; format_error({illegal_map_key_variable,K}) -> io_lib:format("illegal use of variable ~w in map",[K]); format_error(illegal_bin_pattern) -> @@ -1385,19 +1387,20 @@ pattern({cons,_Line,H,T}, Vt, Old, Bvt, St0) -> pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) -> pattern_list(Ps, Vt, Old, Bvt, St); pattern({map,_Line,Ps}, Vt, Old, Bvt, St) -> - foldl(fun ({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 expr(KP, [], St0) of - {[],_} -> - {Pvt,Bvt1,St1} = pattern(VP, Vt, Old, Bvt, St0), - {vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt0, Bvt1), - St1}; - {[Var|_],_} -> - Error = {illegal_map_key_variable,element(1, Var)}, - {Psvt,Bvt0,add_error(L, Error, St0)} - end - end, {[],[],St}, Ps); + foldl(fun + ({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 + true -> + {Pvt,Bvt1,St1} = pattern(VP, Vt, Old, Bvt, St0), + {vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt0, Bvt1), St1}; + false -> + {Psvt,Bvt0,add_error(L, illegal_map_key, St0)}; + {false,variable,Var} -> + {Psvt,Bvt0,add_error(L, {illegal_map_key_variable,Var}, St0)} + end + end, {[],[],St}, Ps); %%pattern({struct,_Line,_Tag,Ps}, Vt, Old, Bvt, St) -> %% pattern_list(Ps, Vt, Old, Bvt, St); pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) -> @@ -2237,9 +2240,10 @@ check_assoc_fields([], St) -> map_fields([{Tag,Line,K,V}|Fs], Vt, St, F) when Tag =:= map_field_assoc; Tag =:= map_field_exact -> St1 = case is_valid_map_key(K, St) of - true -> St; - {false,Var} -> add_error(Line, {illegal_map_key_variable,Var}, St) - end, + true -> St; + false -> add_error(Line, illegal_map_key, St); + {false,variable,Var} -> add_error(Line, {illegal_map_key_variable,Var}, St) + end, {Pvt,St2} = F([K,V], Vt, St1), {Vts,St3} = map_fields(Fs, Vt, St2, F), {vtupdate(Pvt, Vts),St3}; @@ -2298,11 +2302,41 @@ is_valid_call(Call) -> _ -> true end. +%% is_valid_map_key(K,St) -> true | false | {false, Var::atom()} +%% check for value expression without variables + is_valid_map_key(K,St) -> case expr(K,[],St) of - {[],_} -> true; + {[],_} -> + is_valid_map_key_value(K); {[Var|_],_} -> - {false,element(1,Var)} + {false,variable,element(1,Var)} + end. + +is_valid_map_key_value(K) -> + case K of + {char,_,_} -> true; + {integer,_,_} -> true; + {float,_,_} -> true; + {string,_,_} -> true; + {nil,_} -> true; + {atom,_,_} -> true; + {cons,_,H,T} -> + is_valid_map_key_value(H) andalso + is_valid_map_key_value(T); + {tuple,_,Es} -> + foldl(fun(E,B) -> + B andalso is_valid_map_key_value(E) + end,true,Es); + {bin,_,Es} -> + % only check for value expressions to be valid + % invalid binary expressions are later checked in + % core and kernel + foldl(fun + ({bin_element,_,E,_,_},B) -> + B andalso is_valid_map_key_value(E) + end,true,Es); + _ -> false end. %% record_def(Line, RecordName, [RecField], State) -> State. -- cgit v1.2.3 From 66039320f16622b62fe9dbd413034171130d4de4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 12 Mar 2014 22:25:30 +0100 Subject: stdlib: Move map type to proper definition --- lib/stdlib/src/erl_lint.erl | 3 ++- lib/stdlib/test/erl_lint_SUITE.erl | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index dc20a18485..3b3e4db518 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -2761,6 +2761,7 @@ check_record_types([], _Name, _DefFields, SeenVars, St, _SeenFields) -> {SeenVars, St}. is_var_arity_type(tuple) -> true; +is_var_arity_type(map) -> true; is_var_arity_type(product) -> true; is_var_arity_type(union) -> true; is_var_arity_type(record) -> true; @@ -2793,7 +2794,6 @@ is_default_type({iodata, 0}) -> true; is_default_type({iolist, 0}) -> true; is_default_type({list, 0}) -> true; is_default_type({list, 1}) -> true; -is_default_type({map, 0}) -> true; is_default_type({maybe_improper_list, 0}) -> true; is_default_type({maybe_improper_list, 2}) -> true; is_default_type({mfa, 0}) -> true; @@ -2824,6 +2824,7 @@ is_default_type({timeout, 0}) -> true; is_default_type({var, 1}) -> true; is_default_type(_) -> false. +%% OTP 17.0 is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false. is_obsolete_builtin_type(TypePair) -> diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 5d189006a1..919489e3d7 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -63,7 +63,7 @@ too_many_arguments/1, basic_errors/1,bin_syntax_errors/1, predef/1, - maps/1 + maps/1,maps_type/1 ]). % Default timetrap timeout (set in init_per_testcase). @@ -91,7 +91,8 @@ all() -> otp_11772, otp_11771, export_all, bif_clash, behaviour_basic, behaviour_multiple, otp_7550, otp_8051, format_warn, {group, on_load}, - too_many_arguments, basic_errors, bin_syntax_errors, predef, maps]. + too_many_arguments, basic_errors, bin_syntax_errors, predef, + maps,maps_type]. groups() -> [{unused_vars_warn, [], @@ -3394,6 +3395,36 @@ maps(Config) -> [] = run(Config, Ts), ok. +maps_type(Config) when is_list(Config) -> + Ts = [ + {maps_type1, + <<" + -type m() :: #{a => integer()}. + -spec t1(#{k=>term()}) -> {term(), map()}. + + t1(#{k:=V}=M) -> {V,M}. + + -spec t2(m()) -> integer(). + + t2(#{a:=V}) -> V. + ">>, + [], + []}, + {maps_type2, + <<" + %% Built-in var arity map type: + -type map() :: tuple(). + -type a() :: map(). + + -spec t(a()) -> a(). + t(M) -> M. + ">>, + [], + {errors,[{3,erl_lint,{redefine_type,{map,0}}}],[]}}], + [] = run(Config, Ts), + ok. + + run(Config, Tests) -> F = fun({N,P,Ws,E}, BadL) -> case catch run_test(Config, P, Ws) of -- cgit v1.2.3 From 4043888ac064a1596da89d8c482d799f2b5a997f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 13 Mar 2014 02:10:28 +0100 Subject: stdlib: Accept Maps as Map keys --- lib/stdlib/src/erl_lint.erl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'lib') diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 3b3e4db518..db98c7020c 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -2328,6 +2328,23 @@ is_valid_map_key_value(K) -> foldl(fun(E,B) -> B andalso is_valid_map_key_value(E) 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 + ({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) + 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) + end, true, Ps); {bin,_,Es} -> % only check for value expressions to be valid % invalid binary expressions are later checked in -- cgit v1.2.3 From a65e0c133314e57adc39a53b48f2cd6ba73b2fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 14 Mar 2014 16:22:21 +0100 Subject: stdlib: Accept records as Map keys --- lib/stdlib/src/erl_lint.erl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index db98c7020c..4c0261a1ad 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -2337,7 +2337,7 @@ is_valid_map_key_value(K) -> Tag =:= map_field_exact -> B andalso is_valid_map_key_value(Ke) andalso is_valid_map_key_value(Ve) - end, true, Ps); + end,true,Ps); {map,_,Ps} -> foldl(fun ({Tag,_,Ke,Ve},B) when Tag =:= map_field_assoc; @@ -2345,13 +2345,19 @@ is_valid_map_key_value(K) -> B andalso is_valid_map_key_value(Ke) andalso is_valid_map_key_value(Ve) 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) + end,true,Fs); {bin,_,Es} -> % only check for value expressions to be valid % invalid binary expressions are later checked in % 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) end,true,Es); _ -> false end. -- cgit v1.2.3 From fdda3a16a4f37e44c496b9fef55b763afa4d77ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 13 Mar 2014 03:01:02 +0100 Subject: stdlib: Test Map key linting This will change in future release. --- lib/stdlib/test/erl_lint_SUITE.erl | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 919489e3d7..673a3cf159 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -3389,9 +3389,28 @@ maps(Config) -> {error_in_illegal_map_construction, <<"t() -> #{ a := X }.">>, [], - {errors,[{1,erl_lint,illegal_map_construction}, + {errors,[{1,erl_lint,illegal_map_construction}, {1,erl_lint,{unbound_var,'X'}}], - []}}], + []}}, + {errors_in_map_keys, + <<"t(V) -> #{ a => 1, + #{a=>V} => 2, + #{ \"hi\" => wazzup, hi => ho } => yep, + [try a catch _:_ -> b end] => nope, + ok => 1.0, + [3+3] => nope, + 1.0 => yep, + {3.0+3} => nope, + {yep} => yep, + [case a of a -> a end] => nope + }. + ">>, + [], + {errors,[{2,erl_lint,{illegal_map_key_variable,'V'}}, + {4,erl_lint,illegal_map_key}, + {6,erl_lint,illegal_map_key}, + {8,erl_lint,illegal_map_key}, + {10,erl_lint,illegal_map_key}],[]}}], [] = run(Config, Ts), ok. -- cgit v1.2.3 From d334837dfe34cc4085d29ff50293b6c1f3b15441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 13 Mar 2014 17:44:57 +0100 Subject: hipe: Properly identify map() type form terms --- lib/hipe/cerl/erl_types.erl | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'lib') diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index 32390045e3..aa69b57fa2 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -218,6 +218,10 @@ %%-define(DO_ERL_TYPES_TEST, true). -compile({no_auto_import,[min/2,max/2]}). +%% HiPE does not understand Maps +%% (guard function is_map/1 in t_from_term/1) +-compile(no_native). + -ifdef(DO_ERL_TYPES_TEST). -export([test/0]). -else. @@ -2156,6 +2160,7 @@ t_from_term(T) when is_integer(T) -> t_integer(T); t_from_term(T) when is_pid(T) -> t_pid(); t_from_term(T) when is_port(T) -> t_port(); t_from_term(T) when is_reference(T) -> t_reference(); +t_from_term(T) when is_map(T) -> t_map(); t_from_term(T) when is_tuple(T) -> t_tuple([t_from_term(E) || E <- tuple_to_list(T)]). -- cgit v1.2.3 From d92b9dc2aa02871a43fb23ff61dc002b47a14270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 13 Mar 2014 17:45:34 +0100 Subject: dialyzer: Do not native compile modules with Maps code hipe:c/1 ignores '-compile(no_native).' --- lib/dialyzer/src/dialyzer_cl.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl index 3e68d64d53..793efe4b50 100644 --- a/lib/dialyzer/src/dialyzer_cl.erl +++ b/lib/dialyzer/src/dialyzer_cl.erl @@ -504,7 +504,9 @@ hipe_compile(Files, #options{erlang_mode = ErlangMode} = Options) -> _ -> Mods = [lists, dict, digraph, digraph_utils, ets, gb_sets, gb_trees, ordsets, sets, sofs, - cerl, cerl_trees, erl_types, erl_bif_types, + %cerl, % uses maps instructions + %erl_types, % uses maps instructions + cerl_trees, erl_bif_types, dialyzer_analysis_callgraph, dialyzer, dialyzer_behaviours, dialyzer_codeserver, dialyzer_contracts, dialyzer_coordinator, dialyzer_dataflow, dialyzer_dep, @@ -533,7 +535,7 @@ hc(Mod) -> case code:is_module_native(Mod) of true -> ok; false -> - %% io:format(" ~s", [Mod]), + %% io:format(" ~w", [Mod]), {ok, Mod} = hipe:c(Mod), ok end. -- cgit v1.2.3 From 5638ca15d05e9b05d64ade0e03492c13d020439b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 17 Mar 2014 16:53:37 +0100 Subject: compiler: Transform M#{} to is_map(M) Core should not understand M#{} Instead transform M#{} to case _cor0 of <_cor1> when call 'erlang':'is_map' (_cor0) -> _cor1 ( <_cor2> when 'true' -> primop 'match_fail' ('badarg') -| ['compiler_generated'] ) end --- lib/compiler/src/v3_core.erl | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index bf3368c31d..082809b8a0 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -743,8 +743,25 @@ expr_map(M0,Es0,A,St0) -> {M1,Mps,St1} = safe(M0, St0), case is_valid_map_src(M1) of true -> - {Es1,Eps,St2} = map_pair_list(Es0, St1), - {ann_c_map(A,M1,Es1),Mps++Eps,St2}; + case {M1,Es0} of + {#c_var{}, []} -> + %% transform M#{} to is_map(M) + {Vpat,St2} = new_var(St1), + {Fpat,St3} = new_var(St2), + Cs = [#iclause{ + anno=A, + pats=[Vpat], + guard=[#icall{anno=#a{anno=A}, + module=#c_literal{anno=A,val=erlang}, + name=#c_literal{anno=A,val=is_map}, + args=[Vpat]}], + body=[Vpat]}], + Fc = fail_clause([Fpat], A, #c_literal{val=badarg}), + {#icase{anno=#a{anno=A},args=[M1],clauses=Cs,fc=Fc},Mps,St3}; + {_,_} -> + {Es1,Eps,St2} = map_pair_list(Es0, St1), + {ann_c_map(A,M1,Es1),Mps++Eps,St2} + end; false -> throw(bad_map) end. -- cgit v1.2.3