From 723fa9efadf7f841e7b835393ce801aa0a03e1cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 15 Apr 2013 18:28:15 +0200 Subject: Teach erl_scan to recognize '=>' --- lib/stdlib/src/erl_scan.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl index 4ba6dd01fa..52376a8f3a 100644 --- a/lib/stdlib/src/erl_scan.erl +++ b/lib/stdlib/src/erl_scan.erl @@ -569,7 +569,7 @@ scan1("++"++Cs, St, Line, Col, Toks) -> tok2(Cs, St, Line, Col, Toks, "++", '++', 2); scan1("+"=Cs, _St, Line, Col, Toks) -> {more,{Cs,Col,Toks,Line,[],fun scan/6}}; -%% =:= =/= =< == +%% =:= =/= =< == => scan1("=:="++Cs, St, Line, Col, Toks) -> tok2(Cs, St, Line, Col, Toks, "=:=", '=:=', 3); scan1("=:"=Cs, _St, Line, Col, Toks) -> @@ -580,6 +580,8 @@ scan1("=/"=Cs, _St, Line, Col, Toks) -> {more,{Cs,Col,Toks,Line,[],fun scan/6}}; scan1("=<"++Cs, St, Line, Col, Toks) -> tok2(Cs, St, Line, Col, Toks, "=<", '=<', 2); +scan1("=>"++Cs, St, Line, Col, Toks) -> + tok2(Cs, St, Line, Col, Toks, "=>", '=>', 2); scan1("=="++Cs, St, Line, Col, Toks) -> tok2(Cs, St, Line, Col, Toks, "==", '==', 2); scan1("="=Cs, _St, Line, Col, Toks) -> -- cgit v1.2.3 From 703a9aa47168f7f8c11bf835502281bd59ba77f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 15 Apr 2013 18:09:50 +0200 Subject: Extend erl_parse with the new map syntax Example how to match or construct: #{ K1 => V1, K2 => V2 } How to update: M#{ K => V } --- lib/stdlib/src/erl_parse.yrl | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 59a05a48ee..5a846266c5 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -34,6 +34,7 @@ binary_comprehension tuple %struct record_expr record_tuple record_field record_fields +map_expr map_tuple map_field map_fields map_key if_expr if_clause if_clauses case_expr cr_clause cr_clauses receive_expr fun_expr fun_clause fun_clauses atom_or_var integer_or_var try_expr try_catch try_clause try_clauses @@ -59,7 +60,7 @@ char integer float atom string var '*' '/' 'div' 'rem' 'band' 'and' '+' '-' 'bor' 'bxor' 'bsl' 'bsr' 'or' 'xor' '++' '--' -'==' '/=' '=<' '<' '>=' '>' '=:=' '=/=' '<=' +'==' '/=' '=<' '<' '>=' '>' '=:=' '=/=' '<=' '=>' '<<' '>>' '!' '=' '::' '..' '...' 'spec' 'callback' % helper @@ -247,6 +248,7 @@ expr_500 -> expr_600 : '$1'. expr_600 -> prefix_op expr_700 : ?mkop1('$1', '$2'). +expr_600 -> map_expr : '$1'. expr_600 -> expr_700 : '$1'. expr_700 -> function_call : '$1'. @@ -327,6 +329,26 @@ tuple -> '{' exprs '}' : {tuple,?line('$1'),'$2'}. %%struct -> atom tuple : %% {struct,?line('$1'),element(3, '$1'),element(3, '$2')}. +map_expr -> '#' map_tuple : + {map, ?line('$1'),'$2'}. +map_expr -> expr_max '#' map_tuple : + {map, ?line('$2'),'$1','$3'}. +map_expr -> map_expr '#' map_tuple : + {map, ?line('$2'),'$1','$3'}. +map_expr -> map_expr '.' atom : + {map_field, ?line('$2'),'$1','$3'}. + +map_tuple -> '{' '}' : []. +map_tuple -> '{' map_fields '}' : '$2'. + +map_fields -> map_field : ['$1']. +map_fields -> map_field ',' map_fields : ['$1' | '$3']. + +map_field -> map_key '=>' expr : + {map_field,?line('$1'),'$1','$3'}. + +map_key -> expr : '$1'. + %% N.B. This is called from expr_700. %% N.B. Field names are returned as the complete object, even if they are @@ -1060,3 +1082,5 @@ get_attribute(L, Name) -> get_attributes(L) -> erl_scan:attributes_info(L). + +%% vim: ft=erlang -- cgit v1.2.3 From 860171895f5858314abd85f8461568f34e76c427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 16 May 2012 10:47:58 +0200 Subject: Update erl_lint, erl_expand_records, sys_pre_expand for maps --- lib/compiler/src/sys_pre_expand.erl | 16 ++++++++++++++++ lib/stdlib/src/erl_expand_records.erl | 14 ++++++++++++++ lib/stdlib/src/erl_lint.erl | 17 +++++++++++++++++ 3 files changed, 47 insertions(+) (limited to 'lib') diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl index 48d9c16718..969e95e8dc 100644 --- a/lib/compiler/src/sys_pre_expand.erl +++ b/lib/compiler/src/sys_pre_expand.erl @@ -228,6 +228,12 @@ pattern({cons,Line,H,T}, St0) -> pattern({tuple,Line,Ps}, St0) -> {TPs,St1} = pattern_list(Ps, St0), {{tuple,Line,TPs},St1}; +pattern({map,Line,Ps}, St0) -> + {TPs,St1} = pattern_list(Ps, St0), + {{map,Line,TPs},St1}; +pattern({map_field,Line,Key,V0}, St0) -> + {V,St1} = pattern(V0, St0), + {{map_field,Line,Key,V},St1}; %%pattern({struct,Line,Tag,Ps}, St0) -> %% {TPs,TPsvs,St1} = pattern_list(Ps, St0), %% {{tuple,Line,[{atom,Line,Tag}|TPs]},TPsvs,St1}; @@ -321,6 +327,16 @@ expr({tuple,Line,Es0}, St0) -> %%expr({struct,Line,Tag,Es0}, Vs, St0) -> %% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0), %% {{tuple,Line,[{atom,Line,Tag}|Es1]},Esvs,Esus,St1}; +expr({map,Line,Es0}, St0) -> + {Es1,St1} = expr_list(Es0, St0), + {{map,Line,Es1},St1}; +expr({map,Line,Var,Es0}, St0) -> + {Es1,St1} = expr_list(Es0, St0), + {{map,Line,Var,Es1},St1}; +expr({map_field,Line,K0,V0}, St0) -> + {K,St1} = expr(K0, St0), + {V,St2} = expr(V0, St1), + {{map_field,Line,K,V},St2}; expr({bin,Line,Es0}, St0) -> {Es1,St1} = expr_bin(Es0, St0), {{bin,Line,Es1},St1}; diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl index 776b433613..7fc4cc7887 100644 --- a/lib/stdlib/src/erl_expand_records.erl +++ b/lib/stdlib/src/erl_expand_records.erl @@ -132,6 +132,12 @@ pattern({cons,Line,H,T}, St0) -> pattern({tuple,Line,Ps}, St0) -> {TPs,St1} = pattern_list(Ps, St0), {{tuple,Line,TPs},St1}; +pattern({map,Line,Ps}, St0) -> + {TPs,St1} = pattern_list(Ps, St0), + {{map,Line,TPs},St1}; +pattern({map_field,Line,Key,V0}, St0) -> + {V,St1} = pattern(V0, St0), + {{map_field,Line,Key,V},St1}; %%pattern({struct,Line,Tag,Ps}, St0) -> %% {TPs,TPsvs,St1} = pattern_list(Ps, St0), %% {{struct,Line,Tag,TPs},TPsvs,St1}; @@ -301,6 +307,14 @@ expr({bc,Line,E0,Qs0}, St0) -> expr({tuple,Line,Es0}, St0) -> {Es1,St1} = expr_list(Es0, St0), {{tuple,Line,Es1},St1}; +expr({map,Line,Es0}, St0) -> + {Es1,St1} = expr_list(Es0, St0), + {{map,Line,Es1},St1}; +expr({map,Line,Var,Es0}, St0) -> + {Es1,St1} = expr_list(Es0, St0), + {{map,Line,Var,Es1},St1}; +expr({map_field,_Line,_,_}=M, St0) -> + {M,St0}; %%expr({struct,Line,Tag,Es0}, Vs, St0) -> %% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0), %% {{struct,Line,Tag,Es1},Esvs,Esus,St1}; diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index f0d50df4c7..b74a3ed924 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -1366,6 +1366,11 @@ pattern({cons,_Line,H,T}, Vt, Old, Bvt, St0) -> {vtmerge_pat(Hvt, Tvt),vtmerge_pat(Bvt1,Bvt2),St2}; pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) -> pattern_list(Ps, Vt, Old, Bvt, St); +pattern({map,_Line,Ps}, Vt, Old, Bvt, St) -> + pattern_list(Ps, Vt, Old, Bvt, St); +pattern({map_field,_Line,_,P}, Vt, Old, Bvt0, St0) -> + {Pvt,Bvt,St1} = pattern(P, Vt, Old, Bvt0, St0), + {Pvt,Bvt,St1}; %%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) -> @@ -1753,6 +1758,12 @@ gexpr({cons,_Line,H,T}, Vt, St) -> gexpr_list([H,T], Vt, St); gexpr({tuple,_Line,Es}, Vt, St) -> gexpr_list(Es, Vt, St); +gexpr({map,_Line,Es}, Vt, St) -> + gexpr_list(Es, Vt, St); +gexpr({map,_Line,Src,Es}, Vt, St) -> + gexpr_list([Src|Es], Vt, St); +gexpr({map_field,_Line,K,V}, Vt, St) -> + gexpr_list([K,V], Vt, St); gexpr({record_index,Line,Name,Field}, _Vt, St) -> check_record(Line, Name, St, fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end ); @@ -1970,6 +1981,12 @@ expr({bc,_Line,E,Qs}, Vt, St) -> handle_comprehension(E, Qs, Vt, St); expr({tuple,_Line,Es}, Vt, St) -> expr_list(Es, Vt, St); +expr({map,_Line,Es}, Vt, St) -> + expr_list(Es, Vt, St); +expr({map,_Line,Src,Es}, Vt, St) -> + expr_list([Src|Es], Vt, St); +expr({map_field,_Line,K,V}, Vt, St) -> + expr_list([K,V], Vt, St); expr({record_index,Line,Name,Field}, _Vt, St) -> check_record(Line, Name, St, fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end); -- cgit v1.2.3 From d3ece60d52880fb273da3c4aa07655a0eeddeafb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 16 May 2012 10:49:36 +0200 Subject: Introduce a representation for maps in Core Erlang --- lib/compiler/src/core_parse.hrl | 6 ++++++ lib/compiler/src/core_parse.yrl | 30 +++++++++++++++++++++++++++++- lib/compiler/src/core_pp.erl | 16 ++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl index 0b8f4d8895..c134747991 100644 --- a/lib/compiler/src/core_parse.hrl +++ b/lib/compiler/src/core_parse.hrl @@ -96,3 +96,9 @@ -record(c_values, {anno=[], es}). % es :: [Tree] -record(c_var, {anno=[], name :: cerl:var_name()}). + +-record(c_map_pair, {anno=[], key, val}). + +-record(c_map, {anno=[], + var=#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 4e98a8c2da..b072950eeb 100644 --- a/lib/compiler/src/core_parse.yrl +++ b/lib/compiler/src/core_parse.yrl @@ -21,6 +21,8 @@ %% Have explicit productions for annotated phrases named anno_XXX. %% This just does an XXX and adds the annotation. +Expect 1. + Nonterminals module_definition module_export module_attribute module_defs @@ -44,6 +46,9 @@ receive_expr timeout try_expr sequence catch_expr variable clause clause_pattern +map_expr map_pairs map_pair +map_pattern map_pair_patterns map_pair_pattern + annotation anno_fun anno_expression anno_expressions anno_variable anno_variables anno_pattern anno_patterns anno_function_name @@ -53,7 +58,7 @@ Terminals %% Separators -'(' ')' '{' '}' '[' ']' '|' ',' '->' '=' '/' '<' '>' ':' '-|' '#' +'(' ')' '{' '}' '[' ']' '|' ',' '->' '=' '/' '<' '>' ':' '-|' '#' '~' %% Keywords (atoms are assumed to always be single-quoted). @@ -166,6 +171,7 @@ anno_patterns -> anno_pattern : ['$1']. other_pattern -> atomic_pattern : '$1'. other_pattern -> tuple_pattern : '$1'. +other_pattern -> map_pattern : '$1'. other_pattern -> cons_pattern : '$1'. other_pattern -> binary_pattern : '$1'. other_pattern -> anno_variable '=' anno_pattern : @@ -176,6 +182,16 @@ atomic_pattern -> atomic_literal : '$1'. tuple_pattern -> '{' '}' : c_tuple([]). tuple_pattern -> '{' anno_patterns '}' : c_tuple('$2'). +map_pattern -> '~' '{' '}' '~' : #c_map{es=[]}. +map_pattern -> '~' '{' map_pair_patterns '}' '~' : + #c_map{es=lists:sort('$3')}. + +map_pair_patterns -> map_pair_pattern : ['$1']. +map_pair_patterns -> map_pair_pattern ',' map_pair_patterns : ['$1' | '$3']. + +map_pair_pattern -> '~' '<' anno_pattern ',' anno_pattern '>' : + #c_map_pair{key='$3',val='$5'}. + cons_pattern -> '[' anno_pattern tail_pattern : #c_cons{hd='$2',tl='$3'}. @@ -240,6 +256,7 @@ single_expression -> primop_expr : '$1'. single_expression -> try_expr : '$1'. single_expression -> sequence : '$1'. single_expression -> catch_expr : '$1'. +single_expression -> map_expr : '$1'. literal -> atomic_literal : '$1'. literal -> tuple_literal : '$1'. @@ -267,6 +284,17 @@ tail_literal -> ',' literal tail_literal : #c_cons{hd='$2',tl='$3'}. 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='$',es=[]}. +map_expr -> variable '~' '{' map_pairs '}' '~' : #c_map{var='$1',es='$4'}. + +map_pairs -> map_pair : ['$1']. +map_pairs -> map_pair ',' map_pairs : ['$1' | '$3']. + +map_pair -> '~' '<' anno_expression ',' anno_expression'>' : + #c_map_pair{key='$3',val='$5'}. + cons -> '[' anno_expression tail : c_cons('$2', '$3'). tail -> ']' : #c_literal{val=[]}. diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl index 1f91a52be3..c8c59b9246 100644 --- a/lib/compiler/src/core_pp.erl +++ b/lib/compiler/src/core_pp.erl @@ -161,6 +161,22 @@ 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_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2), + "}~" + ]; +format_1(#c_map{es=Es}, Ctxt) -> + ["~{", + format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2), + "}~" + ]; +format_1(#c_map_pair{key=K,val=V}, Ctxt) -> + ["~<", + format_hseq([K,V], ",", add_indent(Ctxt, 1), fun format/2), + ">" + ]; format_1(#c_cons{hd=H,tl=T}, Ctxt) -> Txt = ["["|format(H, add_indent(Ctxt, 1))], [Txt|format_list_tail(T, add_indent(Ctxt, width(Txt, Ctxt)))]; -- cgit v1.2.3 From 64ee859fc4c17259ab95192abf7493fed8f2b0ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 16 May 2012 10:50:30 +0200 Subject: Implement support for maps in the compiler To make it possible to build the entire OTP system, also define dummys for the instructions in ops.tab. --- lib/compiler/src/beam_block.erl | 5 +++ lib/compiler/src/beam_clean.erl | 29 ++++++++++++++++ lib/compiler/src/beam_flatten.erl | 4 +++ lib/compiler/src/beam_jump.erl | 4 +++ lib/compiler/src/beam_split.erl | 6 ++++ lib/compiler/src/beam_validator.erl | 15 +++++++++ lib/compiler/src/core_lib.erl | 4 +++ lib/compiler/src/genop.tab | 7 ++++ lib/compiler/src/sys_core_dsetel.erl | 12 +++++++ lib/compiler/src/sys_core_fold.erl | 32 ++++++++++++++++++ lib/compiler/src/v3_codegen.erl | 64 +++++++++++++++++++++++++++++++++++- lib/compiler/src/v3_core.erl | 32 ++++++++++++++++++ lib/compiler/src/v3_kernel.erl | 60 ++++++++++++++++++++++++++++++--- lib/compiler/src/v3_kernel.hrl | 2 ++ lib/compiler/src/v3_kernel_pp.erl | 13 ++++++++ lib/compiler/src/v3_life.erl | 14 ++++++-- 16 files changed, 296 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl index 402fbe2e2e..f9e90b81d2 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -152,6 +152,10 @@ collect({get_tuple_element,S,I,D}) -> {set,[D],[S],{get_tuple_element,I}}; collect({set_tuple_element,S,D,I}) -> {set,[],[S,D],{set_tuple_element,I}}; collect({get_list,S,D1,D2}) -> {set,[D1,D2],[S],get_list}; collect(remove_message) -> {set,[],[],remove_message}; +collect({put_map,F,S,D,R,{list,Puts}}) -> + {set,[D],[S|Puts],{alloc,R,{put_map,F}}}; +collect({get_map_element,F,S,K,D}) -> + {set,[D],[S],{get_map_element,K,F}}; collect({'catch',R,L}) -> {set,[R],[],{'catch',L}}; collect(fclearerror) -> {set,[],[],fclearerror}; collect({fcheckerror,{f,0}}) -> {set,[],[],fcheckerror}; @@ -383,6 +387,7 @@ gen_init(Fs, Regs, Y, Acc) -> init_yreg([{set,_,_,{bif,_,_}}|_], Reg) -> Reg; init_yreg([{set,_,_,{alloc,_,{gc_bif,_,_}}}|_], Reg) -> Reg; +init_yreg([{set,_,_,{alloc,_,{put_map,_}}}|_], Reg) -> Reg; init_yreg([{set,Ds,_,_}|Is], Reg) -> init_yreg(Is, add_yregs(Ds, Reg)); init_yreg(_Is, Reg) -> Reg. diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl index 9d89e21a4e..6f802d0436 100644 --- a/lib/compiler/src/beam_clean.erl +++ b/lib/compiler/src/beam_clean.erl @@ -234,6 +234,35 @@ replace([{bs_init,{f,Lbl},Info,Live,Ss,Dst}|Is], Acc, D) when Lbl =/= 0 -> replace(Is, [{bs_init,{f,label(Lbl, D)},Info,Live,Ss,Dst}|Acc], D); replace([{bs_put,{f,Lbl},Info,Ss}|Is], Acc, D) when Lbl =/= 0 -> replace(Is, [{bs_put,{f,label(Lbl, D)},Info,Ss}|Acc], D); +replace([{bs_init2,{f,Lbl},Sz,Words,R,F,Dst}|Is], Acc, D) when Lbl =/= 0 -> + replace(Is, [{bs_init2,{f,label(Lbl, D)},Sz,Words,R,F,Dst}|Acc], D); +replace([{bs_init_bits,{f,Lbl},Sz,Words,R,F,Dst}|Is], Acc, D) when Lbl =/= 0 -> + replace(Is, [{bs_init_bits,{f,label(Lbl, D)},Sz,Words,R,F,Dst}|Acc], D); +replace([{bs_put_integer,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> + replace(Is, [{bs_put_integer,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D); +replace([{bs_put_utf8=I,{f,Lbl},Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> + replace(Is, [{I,{f,label(Lbl, D)},Fl,Val}|Acc], D); +replace([{bs_put_utf16=I,{f,Lbl},Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> + replace(Is, [{I,{f,label(Lbl, D)},Fl,Val}|Acc], D); +replace([{bs_put_utf32=I,{f,Lbl},Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> + replace(Is, [{I,{f,label(Lbl, D)},Fl,Val}|Acc], D); +replace([{bs_put_binary,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> + replace(Is, [{bs_put_binary,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D); +replace([{bs_put_float,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> + replace(Is, [{bs_put_float,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D); +replace([{bs_add,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 -> + replace(Is, [{bs_add,{f,label(Lbl, D)},Src,Dst}|Acc], D); +replace([{bs_append,{f,Lbl},_,_,_,_,_,_,_}=I0|Is], Acc, D) when Lbl =/= 0 -> + I = setelement(2, I0, {f,label(Lbl, D)}), + replace(Is, [I|Acc], D); +replace([{bs_utf8_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 -> + replace(Is, [{I,{f,label(Lbl, D)},Src,Dst}|Acc], D); +replace([{bs_utf16_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 -> + replace(Is, [{I,{f,label(Lbl, D)},Src,Dst}|Acc], D); +replace([{put_map=I,{f,Lbl},Src,Dst,Live,List}|Is], Acc, D) when Lbl =/= 0 -> + replace(Is, [{I,{f,label(Lbl, D)},Src,Dst,Live,List}|Acc], D); +replace([{get_map_element=I,{f,Lbl},Src,Key,Dst}|Is], Acc, D) when Lbl =/= 0 -> + replace(Is, [{I,{f,label(Lbl, D)},Src,Key,Dst}|Acc], D); replace([I|Is], Acc, D) -> replace(Is, [I|Acc], D); replace([], Acc, _) -> Acc. diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl index 5603a677e8..ad9591ff05 100644 --- a/lib/compiler/src/beam_flatten.erl +++ b/lib/compiler/src/beam_flatten.erl @@ -61,6 +61,10 @@ norm({set,[],[S],put}) -> {put,S}; norm({set,[D],[S],{get_tuple_element,I}}) -> {get_tuple_element,S,I,D}; norm({set,[],[S,D],{set_tuple_element,I}}) -> {set_tuple_element,S,D,I}; norm({set,[D1,D2],[S],get_list}) -> {get_list,S,D1,D2}; +norm({set,[D],[S|Puts],{alloc,R,{put_map,F}}}) -> + {put_map,F,S,D,R,{list,Puts}}; +norm({set,[D],[S],{get_map_element,K,F}}) -> + {get_map_element,F,S,K,D}; norm({set,[],[],remove_message}) -> remove_message; norm({set,[],[],fclearerror}) -> fclearerror; norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}}. diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl index d57fb80ac2..bbef75c219 100644 --- a/lib/compiler/src/beam_jump.erl +++ b/lib/compiler/src/beam_jump.erl @@ -527,6 +527,10 @@ ulbl({bs_init,Lbl,_,_,_,_}, Used) -> mark_used(Lbl, Used); ulbl({bs_put,Lbl,_,_}, Used) -> mark_used(Lbl, Used); +ulbl({put_map,Lbl,_Src,_Dst,_Live,_List}, Used) -> + mark_used(Lbl, Used); +ulbl({get_map_element,Lbl,_Src,_Key,_Dst}, Used) -> + mark_used(Lbl, Used); ulbl(_, Used) -> Used. mark_used({f,0}, Used) -> Used; diff --git a/lib/compiler/src/beam_split.erl b/lib/compiler/src/beam_split.erl index cacaaebffe..bbd4695289 100644 --- a/lib/compiler/src/beam_split.erl +++ b/lib/compiler/src/beam_split.erl @@ -49,6 +49,12 @@ split_block([{set,[R],As,{bif,N,{f,Lbl}=Fail}}|Is], Bl, Acc) when Lbl =/= 0 -> split_block([{set,[R],As,{alloc,Live,{gc_bif,N,{f,Lbl}=Fail}}}|Is], Bl, Acc) when Lbl =/= 0 -> split_block(Is, [], [{gc_bif,N,Fail,Live,As,R}|make_block(Bl, Acc)]); +split_block([{set,[D],[S|Puts],{alloc,R,{put_map,{f,Lbl}=Fail}}}|Is], Bl, Acc) + when Lbl =/= 0 -> + split_block(Is, [], [{put_map,Fail,S,D,R,{list,Puts}}|make_block(Bl, Acc)]); +split_block([{set,[D],[S],{get_map_element,K,{f,Lbl}=Fail}}|Is], Bl, Acc) + when Lbl =/= 0 -> + split_block(Is, [], [{get_map_element,Fail,S,K,D}|make_block(Bl, Acc)]); split_block([{set,[R],[],{'catch',L}}|Is], Bl, Acc) -> split_block(Is, [], [{'catch',R,L}|make_block(Bl, Acc)]); split_block([{set,[],[],{line,_}=Line}|Is], Bl, Acc) -> diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 48f5135aca..37b08f43d3 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -865,6 +865,21 @@ valfun_4({bs_final,{f,Fail},Dst}, Vst0) -> valfun_4({bs_final2,Src,Dst}, Vst0) -> assert_term(Src, Vst0), set_type_reg(binary, Dst, Vst0); +%% Map instructions. +valfun_4({put_map,{f,Fail},Src,Dst,Live,{list,List}}, Vst0) -> + verify_live(Live, Vst0), + verify_y_init(Vst0), + [assert_term(Term, Vst0) || Term <- List], + assert_term(Src, Vst0), + Vst1 = heap_alloc(0, Vst0), + Vst2 = branch_state(Fail, Vst1), + Vst = prune_x_regs(Live, Vst2), + set_type_reg(term, Dst, Vst); +valfun_4({get_map_element,{f,Fail},Src,Key,Dst}, Vst0) -> + assert_term(Src, Vst0), + assert_term(Key, Vst0), + Vst = branch_state(Fail, Vst0), + set_type_reg(term, Dst, Vst); valfun_4(_, _) -> error(unknown_instruction). diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl index 824be9ff7f..f506901099 100644 --- a/lib/compiler/src/core_lib.erl +++ b/lib/compiler/src/core_lib.erl @@ -105,6 +105,10 @@ 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{es=Es}) -> + vu_expr_list(V, Es); +vu_expr(V, #c_map_pair{key=Key,val=Val}) -> + vu_expr_list(V, [Key,Val]); vu_expr(V, #c_binary{segments=Ss}) -> vu_seg_list(V, Ss); vu_expr(V, #c_fun{vars=Vs,body=B}) -> diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab index ebc9b1c85b..861acd3932 100755 --- a/lib/compiler/src/genop.tab +++ b/lib/compiler/src/genop.tab @@ -528,3 +528,10 @@ BEAM_FORMAT_NUMBER=0 # R15A 153: line/1 + +# R16 + +154: put_map/5 +155: is_map/2 +156: has_map_field/3 +157: get_map_element/4 diff --git a/lib/compiler/src/sys_core_dsetel.erl b/lib/compiler/src/sys_core_dsetel.erl index f6696992b9..c7c8343c35 100644 --- a/lib/compiler/src/sys_core_dsetel.erl +++ b/lib/compiler/src/sys_core_dsetel.erl @@ -102,6 +102,13 @@ visit(Env, #c_literal{}=R) -> visit(Env0, #c_tuple{es=Es0}=R) -> {Es1,Env1} = visit_list(Env0, Es0), {R#c_tuple{es=Es1}, Env1}; +visit(Env0, #c_map{es=Es0}=R) -> + {Es1,Env1} = visit_list(Env0, Es0), + {R#c_map{es=Es1}, Env1}; +visit(Env0, #c_map_pair{key=K0,val=V0}=R) -> + {K,Env1} = visit(Env0, K0), + {V,Env2} = visit(Env1, V0), + {R#c_map_pair{key=K,val=V}, Env2}; visit(Env0, #c_cons{hd=H0,tl=T0}=R) -> {H1,Env1} = visit(Env0, H0), {T1,Env2} = visit(Env1, T0), @@ -212,6 +219,11 @@ visit_pat(Env0, #c_var{name=V}, Vs) -> {[V|Vs], dict:store(V, 0, Env0)}; visit_pat(Env0, #c_tuple{es=Es}, Vs) -> visit_pats(Es, Env0, Vs); +visit_pat(Env0, #c_map{es=Es}, Vs) -> + visit_pats(Es, Env0, Vs); +visit_pat(Env0, #c_map_pair{key=V,val=K}, Vs0) -> + {Vs1, Env1} = visit_pat(Env0, V, Vs0), + visit_pat(Env1, K, Vs1); visit_pat(Env0, #c_cons{hd=H,tl=T}, Vs0) -> {Vs1, Env1} = visit_pat(Env0, H, Vs0), visit_pat(Env1, T, Vs1); diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index a388960312..672a8b94c0 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -246,6 +246,16 @@ 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) -> + Es = pair_list(Es0, Ctxt, Sub), + case Ctxt of + effect -> + add_warning(Map, useless_building), + expr(make_effect_seq(Es, Sub), Ctxt, Sub); + value -> + V = expr(V0, Ctxt, Sub), + Map#c_map{var=V,es=Es} + end; expr(#c_binary{segments=Ss}=Bin0, Ctxt, Sub) -> %% Warn for useless building, but always build the binary %% anyway to preserve a possible exception. @@ -408,6 +418,16 @@ expr(#c_try{anno=A,arg=E0,vars=Vs0,body=B0,evars=Evs0,handler=H0}=Try, _, Sub0) expr_list(Es, Ctxt, Sub) -> [expr(E, Ctxt, Sub) || E <- Es]. +pair_list(Es, Ctxt, Sub) -> + [pair(E, Ctxt, Sub) || E <- Es]. + +pair(#c_map_pair{key=K,val=V}, effect, Sub) -> + make_effect_seq([K,V], Sub); +pair(#c_map_pair{key=K0,val=V0}=Pair, value=Ctxt, Sub) -> + K = expr(K0, Ctxt, Sub), + V = expr(V0, Ctxt, Sub), + Pair#c_map_pair{key=K,val=V}. + bitstr_list(Es, Sub) -> [bitstr(E, Sub) || E <- Es]. @@ -1500,6 +1520,9 @@ pattern(#c_cons{anno=Anno,hd=H0,tl=T0}, Isub, Osub0) -> pattern(#c_tuple{anno=Anno,es=Es0}, Isub, Osub0) -> {Es1,Osub1} = pattern_list(Es0, Isub, Osub0), {ann_c_tuple(Anno, Es1),Osub1}; +pattern(#c_map{anno=Anno,es=Es0}=Map, Isub, Osub0) -> + {Es1,Osub1} = map_pair_pattern_list(Es0, Isub, Osub0), + {Map#c_map{anno=Anno,es=Es1},Osub1}; pattern(#c_binary{segments=V0}=Pat, Isub, Osub0) -> {V1,Osub1} = bin_pattern_list(V0, Isub, Osub0), {Pat#c_binary{segments=V1},Osub1}; @@ -1509,6 +1532,15 @@ pattern(#c_alias{var=V0,pat=P0}=Pat, Isub, Osub0) -> Osub = update_types(V1, [P1], Osub2), {Pat#c_alias{var=V1,pat=P1},Osub}. +map_pair_pattern_list(Ps0, Isub, Osub0) -> + {Ps,{_,Osub}} = mapfoldl(fun map_pair_pattern/2, {Isub,Osub0}, Ps0), + {Ps,Osub}. + +map_pair_pattern(#c_map_pair{key=K0,val=V0}=Pair, {Isub,Osub0}) -> + {K,Osub1} = pattern(K0, Isub, Osub0), + {V,Osub} = pattern(V0, Isub, Osub1), + {Pair#c_map_pair{key=K,val=V},{Isub,Osub}}. + bin_pattern_list(Ps0, Isub, Osub0) -> {Ps,{_,Osub}} = mapfoldl(fun bin_pattern/2, {Isub,Osub0}, Ps0), {Ps,Osub}. diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index f534500671..eff43b584a 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -210,6 +210,8 @@ need_heap_0([], H, Acc) -> need_heap_1(#l{ke={set,_,{binary,_}},i=I}, H) -> {need_heap_need(I, H),0}; +need_heap_1(#l{ke={set,_,{map,_,_}},i=I}, H) -> + {need_heap_need(I, H),0}; need_heap_1(#l{ke={set,_,Val}}, H) -> %% Just pass through adding to needed heap. {[],H + case Val of @@ -623,6 +625,8 @@ select_cg(#l{ke={type_clause,bin_int,S}}, {var,V}, Tf, _Vf, Bef, St) -> select_bin_segs(S, V, Tf, Bef, St); select_cg(#l{ke={type_clause,bin_end,[S]}}, {var,V}, Tf, _Vf, Bef, St) -> select_bin_end(S, V, Tf, Bef, St); +select_cg(#l{ke={type_clause,map,S}}, {var,V}, Tf, Vf, Bef, St) -> + select_map(S, V, Tf, Vf, Bef, St); select_cg(#l{ke={type_clause,Type,Scs}}, {var,V}, Tf, Vf, Bef, St0) -> {Vis,{Aft,St1}} = mapfoldl(fun (S, {Int,Sta}) -> @@ -637,6 +641,10 @@ select_val_cg(tuple, R, [Arity,{f,Lbl}], Tf, Vf, [{label,Lbl}|Sis]) -> [{test,is_tuple,{f,Tf},[R]},{test,test_arity,{f,Vf},[R,Arity]}|Sis]; select_val_cg(tuple, R, Vls, Tf, Vf, Sis) -> [{test,is_tuple,{f,Tf},[R]},{select_tuple_arity,R,{f,Vf},{list,Vls}}|Sis]; +select_val_cg(map, R, [_Val,{f,Lbl}], Fail, Fail, [{label,Lbl}|Sis]) -> + [{test,is_map,{f,Fail},[R]}|Sis]; +select_val_cg(map, R, [_Val,{f,Lbl}|_], Tf, _Vf, [{label,Lbl}|Sis]) -> + [{test,is_map,{f,Tf},[R]}|Sis]; select_val_cg(Type, R, [Val, {f,Lbl}], Fail, Fail, [{label,Lbl}|Sis]) -> [{test,is_eq_exact,{f,Fail},[R,{Type,Val}]}|Sis]; select_val_cg(Type, R, [Val, {f,Lbl}], Tf, Vf, [{label,Lbl}|Sis]) -> @@ -915,6 +923,36 @@ select_extract_tuple(Src, Vs, I, Vdb, Bef, St) -> {Es,{Aft,_}} = flatmapfoldl(F, {Bef,0}, Vs), {Es,Aft,St}. +select_map(Scs, V, Tf, Vf, Bef, St0) -> + Reg = fetch_var(V, Bef), + {Is,Aft,St1} = + match_fmf(fun(#l{ke={val_clause,{map,Es},B},i=I,vdb=Vdb}, Fail, St1) -> + select_map_val(V, Es, B, Fail, I, Vdb, Bef, St1) + end, Vf, St0, Scs), + {[{test,is_map,{f,Tf},[Reg]}|Is],Aft,St1}. + +select_map_val(V, Es, B, Fail, I, Vdb, Bef, St0) -> + {Eis,Int,St1} = select_extract_map(V, Es, Fail, I, Vdb, Bef, St0), + {Bis,Aft,St2} = match_cg(B, Fail, Int, St1), + {Eis++Bis,Aft,St2}. + +select_extract_map(Src, Vs, Fail, I, Vdb, Bef, St) -> + F = fun ({map_pair,Key,{var,V}}, Int0) -> + Rsrc = fetch_var(Src, Int0), + case vdb_find(V, Vdb) of + {V,_,L} when L =< I -> + {[{test,has_map_field,{f,Fail},[Rsrc,Key]}],Int0}; + _Other -> + Reg1 = put_reg(V, Int0#sr.reg), + Int1 = Int0#sr{reg=Reg1}, + {[{get_map_element,{f,Fail}, + Rsrc,Key,fetch_reg(V, Reg1)}], + Int1} + end + end, + {Es,Aft} = flatmapfoldl(F, Bef, Vs), + {Es,Aft,St}. + select_extract_cons(Src, [{var,Hd}, {var,Tl}], I, Vdb, Bef, St) -> {Es,Aft} = case {vdb_find(Hd, Vdb), vdb_find(Tl, Vdb)} of {{_,_,Lhd}, {_,_,Ltl}} when Lhd =< I, Ltl =< I -> @@ -1408,7 +1446,7 @@ catch_cg(C, {var,R}, Le, Vdb, Bef, St0) -> %% annotation must reflect this and make sure that the return %% variable is allocated first. %% -%% put_list for constructing a cons is an atomic instruction +%% put_list and put_map are atomic instructions, both of %% which can safely resuse one of the source registers as target. set_cg([{var,R}], {cons,Es}, Le, Vdb, Bef, St) -> @@ -1448,6 +1486,30 @@ set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef, %% Now generate the complete code for constructing the binary. Code = cg_binary(PutCode, Target, Temp, Fail, MaxRegs, Le#l.a), {Sis++Code,Aft,St}; +set_cg([{var,R}], {map,SrcMap,Es}, Le, Vdb, Bef, + #cg{in_catch=InCatch,bfail=Bfail}=St) -> + Fail = {f,Bfail}, + {Sis,Int0} = + case InCatch of + true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb); + false -> {[],Bef} + end, + Line = line(Le#l.a), + SrcReg = case SrcMap of + {var,SrcVar} -> fetch_var(SrcVar, Int0); + _ -> SrcMap + end, + List = flatmap(fun({map_pair,K,{var,V}}) -> + [K,fetch_var(V, Int0)]; + ({map_pair,K,E}) -> + [K,E] + end, sort(Es)), + Live = max_reg(Bef#sr.reg), + Int1 = clear_dead(Int0, Le#l.i, Vdb), + Aft = Bef#sr{reg=put_reg(R, Int1#sr.reg)}, + Target = fetch_reg(R, Aft#sr.reg), + Code = [Line,{put_map,Fail,SrcReg,Target,Live,{list,List}}], + {Sis++Code,Aft,St}; set_cg([{var,R}], Con, Le, Vdb, Bef, St) -> %% Find a place for the return register first. Int = Bef#sr{reg=put_reg(R, Bef#sr.reg)}, diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index a5f31f3844..1ef710a260 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -487,6 +487,15 @@ expr({tuple,L,Es0}, St0) -> {Es1,Eps,St1} = safe_list(Es0, St0), A = lineno_anno(L, St1), {ann_c_tuple(A, Es1),Eps,St1}; +expr({map,L,Es0}, St0) -> + {Es1,Eps,St1} = map_pair_list(Es0, St0), + A = lineno_anno(L, St1), + {#c_map{anno=A,es=Es1},Eps,St1}; +expr({map,L,{var,Vl,Map},Es0}, St0) -> + {Es1,Eps,St1} = map_pair_list(Es0, St0), + A = lineno_anno(L, St1), + Av = lineno_anno(Vl, St1), + {#c_map{anno=A,var=#c_var{anno=Av,name=Map},es=Es1},Eps,St1}; expr({bin,L,Es0}, St0) -> try expr_bin(Es0, lineno_anno(L, St0), St0) of {_,_,_}=Res -> Res @@ -695,6 +704,14 @@ make_bool_switch_guard(L, E, V, T, F) -> {clause,NegL,[V],[],[V]} ]}. +map_pair_list(Es, St) -> + foldr(fun ({map_field,L,K0,V0}, {Ces,Esp,St0}) -> + {K,Ep0,St1} = safe(K0, St0), + {V,Ep1,St2} = safe(V0, St1), + A = lineno_anno(L, St2), + Pair = #c_map_pair{anno=A,key=K,val=V}, + {[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2} + end, {[],[],St}, Es). %% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}. @@ -1478,6 +1495,12 @@ pattern({cons,L,H,T}, St) -> ann_c_cons(lineno_anno(L, St), pattern(H, St), pattern(T, St)); pattern({tuple,L,Ps}, St) -> ann_c_tuple(lineno_anno(L, St), pattern_list(Ps, St)); +pattern({map,L,Ps}, St) -> + #c_map{anno=lineno_anno(L, St),es=sort(pattern_list(Ps, St))}; +pattern({map_field,L,K,V}, St) -> + #c_map_pair{anno=lineno_anno(L, St), + key=pattern(K, St), + val=pattern(V, St)}; pattern({bin,L,Ps}, St) -> %% We don't create a #ibinary record here, since there is %% no need to hold any used/new annotations in a pattern. @@ -1823,6 +1846,12 @@ upattern(#c_cons{hd=H0,tl=T0}=Cons, Ks, St0) -> upattern(#c_tuple{es=Es0}=Tuple, Ks, St0) -> {Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0), {Tuple#c_tuple{es=Es1},Esg,Esv,Eus,St1}; +upattern(#c_map{es=Es0}=Map, Ks, St0) -> + {Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0), + {Map#c_map{es=Es1},Esg,Esv,Eus,St1}; +upattern(#c_map_pair{val=V0}=MapPair, Ks, St0) -> + {V,Vg,Vv,Vu,St1} = upattern(V0, Ks, St0), + {MapPair#c_map_pair{val=V},Vg,Vv,Vu,St1}; upattern(#c_binary{segments=Es0}=Bin, Ks, St0) -> {Es1,Esg,Esv,Eus,St1} = upat_bin(Es0, Ks, St0), {Bin#c_binary{segments=Es1},Esg,Esv,Eus,St1}; @@ -2152,6 +2181,9 @@ is_simple(#c_literal{}) -> true; is_simple(#c_cons{hd=H,tl=T}) -> is_simple(H) andalso is_simple(T); is_simple(#c_tuple{es=Es}) -> is_simple_list(Es); +is_simple(#c_map{es=Es}) -> is_simple_list(Es); +is_simple(#c_map_pair{key=K,val=V}) -> + is_simple(K) andalso is_simple(V); is_simple(_) -> false. -spec is_simple_list([cerl:cerl()]) -> boolean(). diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index 65f1251099..84b3a4e37f 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -272,6 +272,10 @@ 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), + {Kes,Ep,St2} = map_pairs(Ces, Sub, St1), + {#k_map{anno=A,var=Var,es=Kes},Ep,St2}; expr(#c_binary{anno=A,segments=Cv}, Sub, St0) -> try atomic_bin(Cv, Sub, St0) of {Kv,Ep,St1} -> @@ -493,6 +497,13 @@ translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) -> translate_fc(Args) -> [#c_literal{val=function_clause},make_list(Args)]. +map_pairs(Es, Sub, St) -> + foldr(fun(#c_map_pair{key=K0,val=V0}, {Kes,Esp,St0}) -> + {K,[],St1} = expr(K0, Sub, St0), + {V,Ep,St2} = atomic(V0, Sub, St1), + {[#k_map_pair{key=K,val=V}|Kes],Ep ++ Esp,St2} + end, {[],[],St}, Es). + %% call_type(Module, Function, Arity) -> call | bif | apply | error. %% Classify the call. call_type(#c_literal{val=M}, #c_literal{val=F}, Ar) when is_atom(M), is_atom(F) -> @@ -648,6 +659,13 @@ pattern(#c_cons{anno=A,hd=Ch,tl=Ct}, Isub, Osub0, St0) -> pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) -> {Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0), {#k_tuple{anno=A,es=Kes},Osub1,St1}; +pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) -> + {Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0), + {#k_map{anno=A,es=Kes},Osub1,St1}; +pattern(#c_map_pair{anno=A,key=Kk0,val=Cv},Isub, Osub0, St0) -> + Kk = #k_atom{val=Kk0#c_literal.val}, + {Kv,Osub1,St1} = pattern(Cv, Isub, Osub0, St0), + {#k_map_pair{anno=A,key=Kk,val=Kv},Osub1,St1}; pattern(#c_binary{anno=A,segments=Cv}, Isub, Osub0, St0) -> {Kv,Osub1,St1} = pattern_bin(Cv, Isub, Osub0, St0), {#k_binary{anno=A,segs=Kv},Osub1,St1}; @@ -1015,7 +1033,8 @@ match_con_1([U|_Us] = L, Cs, Def, St0) -> %% Extract clauses for different constructors (types). %%ok = io:format("match_con ~p~n", [Cs]), Ttcs = select_types([k_binary], Cs) ++ select_bin_con(Cs) ++ - select_types([k_cons,k_tuple,k_atom,k_float,k_int,k_nil,k_literal], Cs), + select_types([k_cons,k_tuple,k_map,k_atom,k_float,k_int, + k_nil,k_literal], Cs), %%ok = io:format("ttcs = ~p~n", [Ttcs]), {Scs,St1} = mapfoldl(fun ({T,Tcs}, St) -> @@ -1255,6 +1274,8 @@ group_value(k_bin_seg, Cs) -> group_bin_seg(Cs); group_value(k_bin_int, Cs) -> [Cs]; +group_value(k_map, Cs) -> + group_map(Cs); group_value(_, Cs) -> %% group_value(Cs). Cd = foldl(fun (C, Gcs0) -> dict:append(clause_val(C), C, Gcs0) end, @@ -1267,6 +1288,12 @@ group_bin_seg([C1|Cs]) -> [[C1|More]|group_bin_seg(Rest)]; group_bin_seg([]) -> []. +group_map([C1|Cs]) -> + V1 = clause_val(C1), + {More,Rest} = splitwith(fun (C) -> clause_val(C) =:= V1 end, Cs), + [[C1|More]|group_map(Rest)]; +group_map([]) -> []. + %% Profiling shows that this quadratic implementation account for a big amount %% of the execution time if there are many values. % group_value([C|Cs]) -> @@ -1315,6 +1342,12 @@ get_match(#k_bin_int{}=BinInt, St0) -> get_match(#k_tuple{es=Es}, St0) -> {Mes,St1} = new_vars(length(Es), St0), {#k_tuple{es=Mes},Mes,St1}; +get_match(#k_map{es=Es0}, St0) -> + {Mes,St1} = new_vars(length(Es0), St0), + {Es,_} = mapfoldl(fun(#k_map_pair{}=Pair, [V|Vs]) -> + {Pair#k_map_pair{val=V},Vs} + end, Mes, Es0), + {#k_map{es=Es},Mes,St1}; get_match(M, St) -> {M,[],St}. @@ -1331,7 +1364,11 @@ new_clauses(Cs0, U, St) -> [S,N|As]; #k_bin_int{next=N} -> [N|As]; - _Other -> As + #k_map{es=Es} -> + Vals = [V || #k_map_pair{val=V} <- Es], + Vals ++ As; + _Other -> + As end, Vs = arg_alias(Arg), Osub1 = foldl(fun (#k_var{name=V}, Acc) -> @@ -1406,6 +1443,7 @@ arg_con(Arg) -> #k_nil{} -> k_nil; #k_cons{} -> k_cons; #k_tuple{} -> k_tuple; + #k_map{} -> k_map; #k_binary{} -> k_binary; #k_bin_end{} -> k_bin_end; #k_bin_seg{} -> k_bin_seg; @@ -1426,7 +1464,13 @@ arg_val(Arg, C) -> {#k_var{name=get_vsub(V, Isub)},U,T,Fs}; _ -> {set_kanno(S, []),U,T,Fs} - end + end; + #k_map{es=Es} -> + Keys = [begin + #k_map_pair{key=#k_atom{val=Key}} = Pair, + Key + end || Pair <- Es], + ordsets:from_list(Keys) end. %% ubody_used_vars(Expr, State) -> [UsedVar] @@ -1795,6 +1839,10 @@ lit_vars(#k_atom{}) -> []; lit_vars(#k_nil{}) -> []; lit_vars(#k_cons{hd=H,tl=T}) -> union(lit_vars(H), lit_vars(T)); +lit_vars(#k_map{var=Var,es=Es}) -> + lit_list_vars([Var|Es]); +lit_vars(#k_map_pair{key=K,val=V}) -> + union(lit_vars(K), lit_vars(V)); lit_vars(#k_binary{segs=V}) -> lit_vars(V); lit_vars(#k_bin_end{}) -> []; lit_vars(#k_bin_seg{size=Size,seg=S,next=N}) -> @@ -1830,7 +1878,11 @@ pat_vars(#k_bin_int{size=Size}) -> {U,[]}; pat_vars(#k_bin_end{}) -> {[],[]}; pat_vars(#k_tuple{es=Es}) -> - pat_list_vars(Es). + pat_list_vars(Es); +pat_vars(#k_map{es=Es}) -> + pat_list_vars(Es); +pat_vars(#k_map_pair{val=V}) -> + pat_vars(V). pat_list_vars(Ps) -> foldl(fun (P, {Used0,New0}) -> diff --git a/lib/compiler/src/v3_kernel.hrl b/lib/compiler/src/v3_kernel.hrl index fb8baf398b..6671109afb 100644 --- a/lib/compiler/src/v3_kernel.hrl +++ b/lib/compiler/src/v3_kernel.hrl @@ -38,6 +38,8 @@ -record(k_nil, {anno=[]}). -record(k_tuple, {anno=[],es}). +-record(k_map, {anno=[],var,es}). +-record(k_map_pair, {anno=[],key,val}). -record(k_cons, {anno=[],hd,tl}). -record(k_binary, {anno=[],segs}). -record(k_bin_seg, {anno=[],size,unit,type,flags,seg,next}). diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl index e363a5387a..1af90792d3 100644 --- a/lib/compiler/src/v3_kernel_pp.erl +++ b/lib/compiler/src/v3_kernel_pp.erl @@ -104,6 +104,19 @@ 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_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), + " | ",format_1(Var, Ctxt), + $},$~ + ]; +format_1(#k_map{es=Es}, Ctxt) -> + [$~,${, + format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), + $},$~ + ]; +format_1(#k_map_pair{key=K,val=V}, Ctxt) -> + ["~<",format(K, Ctxt),",",format(V, Ctxt),">"]; format_1(#k_binary{segs=S}, Ctxt) -> ["#<",format(S, ctxt_bump_indent(Ctxt, 2)),">#"]; format_1(#k_bin_seg{next=Next}=S, Ctxt) -> diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl index 2cc3493570..507f5d0df1 100644 --- a/lib/compiler/src/v3_life.erl +++ b/lib/compiler/src/v3_life.erl @@ -323,7 +323,9 @@ type(k_tuple) -> tuple; type(k_binary) -> binary; type(k_bin_seg) -> bin_seg; type(k_bin_int) -> bin_int; -type(k_bin_end) -> bin_end. +type(k_bin_end) -> bin_end; +type(k_map) -> map; +type(k_map_pair) -> map_pair. %% variable(Klit) -> Lit. %% var_list([Klit]) -> [Lit]. @@ -365,6 +367,10 @@ literal(#k_bin_end{}, Ctxt) -> {bin_end,Ctxt}; literal(#k_tuple{es=Es}, Ctxt) -> {tuple,literal_list(Es, Ctxt)}; +literal(#k_map{var=Var,es=Es}, Ctxt) -> + {map,literal(Var, Ctxt),literal_list(Es, Ctxt)}; +literal(#k_map_pair{key=K,val=V}, Ctxt) -> + {map_pair,literal(K, Ctxt),literal(V, Ctxt)}; literal(#k_literal{val=V}, _Ctxt) -> {literal,V}. @@ -393,7 +399,11 @@ literal2(#k_bin_int{size=S,unit=U,flags=Fs,val=Int,next=N}, Ctxt) -> literal2(#k_bin_end{}, Ctxt) -> {bin_end,Ctxt}; literal2(#k_tuple{es=Es}, Ctxt) -> - {tuple,literal_list2(Es, Ctxt)}. + {tuple,literal_list2(Es, Ctxt)}; +literal2(#k_map{es=Es}, Ctxt) -> + {map,literal_list2(Es, Ctxt)}; +literal2(#k_map_pair{key=K,val=V}, Ctxt) -> + {map_pair,literal2(K, Ctxt),literal2(V, Ctxt)}. literal_list2(Ks, Ctxt) -> [literal2(K, Ctxt) || K <- Ks]. -- cgit v1.2.3 From fa49c8ecac4996b47b17f9025240e202eb273389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 17 Sep 2013 16:48:13 +0200 Subject: compiler: Fix stack register reordering Can now handle {list [reg()]} elements in instructions. --- lib/compiler/src/v3_codegen.erl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index eff43b584a..bd5ced39e5 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -596,14 +596,13 @@ top_level_block(Keis, Bef, MaxRegs, _St) -> %% number to the outer catch, which is wrong. turn_yregs(0, Tp, _) -> Tp; -turn_yregs(El, Tp, MaxY) when element(1, element(El, Tp)) =:= yy -> - turn_yregs(El-1, setelement(El, Tp, {y,MaxY-element(2, element(El, Tp))}), MaxY); -turn_yregs(El, Tp, MaxY) when is_list(element(El, Tp)) -> - New = map(fun ({yy,YY}) -> {y,MaxY-YY}; - (Other) -> Other end, element(El, Tp)), - turn_yregs(El-1, setelement(El, Tp, New), MaxY); turn_yregs(El, Tp, MaxY) -> - turn_yregs(El-1, Tp, MaxY). + turn_yregs(El-1,setelement(El,Tp,turn_yreg(element(El,Tp),MaxY)),MaxY). + +turn_yreg({yy,YY},MaxY) -> {y,MaxY-YY}; +turn_yreg({list,Ls},MaxY) -> {list, turn_yreg(Ls,MaxY)}; +turn_yreg(Ts,MaxY) when is_list(Ts) -> [turn_yreg(T,MaxY)||T<-Ts]; +turn_yreg(Other,_MaxY) -> Other. %% select_cg(Sclause, V, TypeFail, ValueFail, StackReg, State) -> %% {Is,StackReg,State}. -- cgit v1.2.3 From 2a943f14a44903eb8e67549978de5dbf7275ad57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 20 Sep 2013 16:28:30 +0200 Subject: compiler: Fix no basic blocks for maps --- lib/compiler/src/v3_codegen.erl | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index bd5ced39e5..667f10a7f5 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -455,8 +455,11 @@ basic_block([Le|Les], Acc) -> end; no_block -> {reverse(Acc, [Le]),Les} end. + +%% sets that may garbage collect are not allowed in basic blocks. collect_block({set,_,{binary,_}}) -> no_block; +collect_block({set,_,{map,_,_}}) -> no_block; collect_block({set,_,_}) -> include; collect_block({call,{var,_}=Var,As,_Rs}) -> {block_end,As++[Var]}; collect_block({call,Func,As,_Rs}) -> {block_end,As++func_vars(Func)}; -- cgit v1.2.3 From 08a9a9a7113d310de2e6511d898a06cb8a559ce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 17 Sep 2013 16:50:05 +0200 Subject: compiler: Handle literals, not just atoms, as keys in maps --- lib/compiler/src/sys_pre_expand.erl | 7 ++++--- lib/compiler/src/v3_core.erl | 15 ++++++++++++++- lib/compiler/src/v3_kernel.erl | 19 ++++++++----------- 3 files changed, 26 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl index 969e95e8dc..04084d22ff 100644 --- a/lib/compiler/src/sys_pre_expand.erl +++ b/lib/compiler/src/sys_pre_expand.erl @@ -231,9 +231,10 @@ pattern({tuple,Line,Ps}, St0) -> pattern({map,Line,Ps}, St0) -> {TPs,St1} = pattern_list(Ps, St0), {{map,Line,TPs},St1}; -pattern({map_field,Line,Key,V0}, St0) -> - {V,St1} = pattern(V0, St0), - {{map_field,Line,Key,V},St1}; +pattern({map_field,Line,K0,V0}, St0) -> + {K,St1} = pattern(K0, St0), + {V,St2} = pattern(V0, St1), + {{map_field,Line,K,V},St2}; %%pattern({struct,Line,Tag,Ps}, St0) -> %% {TPs,TPsvs,St1} = pattern_list(Ps, St0), %% {{tuple,Line,[{atom,Line,Tag}|TPs]},TPsvs,St1}; diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 1ef710a260..32b65333a4 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -1498,8 +1498,21 @@ pattern({tuple,L,Ps}, St) -> pattern({map,L,Ps}, St) -> #c_map{anno=lineno_anno(L, St),es=sort(pattern_list(Ps, St))}; pattern({map_field,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 -> + throw(badmatch); + Bin -> + #c_literal{anno=lineno_anno(L,St),val=Bin} + end; + _ -> + pattern(K,St) + end, #c_map_pair{anno=lineno_anno(L, St), - key=pattern(K, St), + key=Key, val=pattern(V, St)}; pattern({bin,L,Ps}, St) -> %% We don't create a #ibinary record here, since there is diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index 84b3a4e37f..3d2dbf2088 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -662,10 +662,10 @@ pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) -> pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) -> {Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0), {#k_map{anno=A,es=Kes},Osub1,St1}; -pattern(#c_map_pair{anno=A,key=Kk0,val=Cv},Isub, Osub0, St0) -> - Kk = #k_atom{val=Kk0#c_literal.val}, - {Kv,Osub1,St1} = pattern(Cv, Isub, Osub0, St0), - {#k_map_pair{anno=A,key=Kk,val=Kv},Osub1,St1}; +pattern(#c_map_pair{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) -> + {Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0), + {Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1), + {#k_map_pair{anno=A,key=Kk,val=Kv},Osub2,St2}; pattern(#c_binary{anno=A,segments=Cv}, Isub, Osub0, St0) -> {Kv,Osub1,St1} = pattern_bin(Cv, Isub, Osub0, St0), {#k_binary{anno=A,segs=Kv},Osub1,St1}; @@ -1270,12 +1270,9 @@ group_value(k_cons, Cs) -> [Cs]; %These are single valued group_value(k_nil, Cs) -> [Cs]; group_value(k_binary, Cs) -> [Cs]; group_value(k_bin_end, Cs) -> [Cs]; -group_value(k_bin_seg, Cs) -> - group_bin_seg(Cs); -group_value(k_bin_int, Cs) -> - [Cs]; -group_value(k_map, Cs) -> - group_map(Cs); +group_value(k_bin_seg, Cs) -> group_bin_seg(Cs); +group_value(k_bin_int, Cs) -> [Cs]; +group_value(k_map, Cs) -> group_map(Cs); group_value(_, Cs) -> %% group_value(Cs). Cd = foldl(fun (C, Gcs0) -> dict:append(clause_val(C), C, Gcs0) end, @@ -1467,7 +1464,7 @@ arg_val(Arg, C) -> end; #k_map{es=Es} -> Keys = [begin - #k_map_pair{key=#k_atom{val=Key}} = Pair, + #k_map_pair{key=#k_literal{val=Key}} = Pair, Key end || Pair <- Es], ordsets:from_list(Keys) -- cgit v1.2.3 From be782d05bcd259b229efcac47c0566e4026cf1f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 24 Sep 2013 15:22:41 +0200 Subject: compiler: Check for duplicate key literals in Map If a literal key already is present in a Map update the latter should be used. Warn for previous duplicates in the Map. --- lib/compiler/src/sys_core_fold.erl | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 672a8b94c0..cdbe2d0753 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -418,8 +418,31 @@ expr(#c_try{anno=A,arg=E0,vars=Vs0,body=B0,evars=Evs0,handler=H0}=Try, _, Sub0) expr_list(Es, Ctxt, Sub) -> [expr(E, Ctxt, Sub) || E <- Es]. +%% traverse pairs in reverse +%% - remove later literals since they will be overwritten. + pair_list(Es, Ctxt, Sub) -> - [pair(E, Ctxt, Sub) || E <- Es]. + pair_list_reversed(lists:reverse(Es), Ctxt, Sub, [], gb_sets:empty()). + +pair_list_reversed([],_,_,Es,_) -> Es; +pair_list_reversed([E|Es],Ctxt,Sub,Out,Keys) -> + Pair = pair(E,Ctxt,Sub), + case map_has_key(Pair,Keys) of + {false,Keys1} -> + pair_list_reversed(Es,Ctxt,Sub,[Pair|Out],Keys1); + {true,K} -> + add_warning(E, {map_pair_key_overloaded,K}), + pair_list_reversed(Es,Ctxt,Sub,Out,Keys) + end. + +%% check if key already is present in map, i.e. #{ a=>1, a=>2 } +%% where 'a' is duplicate. Update maps set with the key if not present. + +map_has_key(#c_map_pair{key=#c_literal{val=K}},Ks) -> + case gb_sets:is_element(K,Ks) of + false -> {false, gb_sets:add(K,Ks)}; + true -> {true, K} + end. pair(#c_map_pair{key=K,val=V}, effect, Sub) -> make_effect_seq([K,V], Sub); @@ -697,7 +720,7 @@ useless_call(effect, #c_call{anno=Anno, useless_call(_, _) -> no. %% make_effect_seq([Expr], Sub) -> #c_seq{}|void() -%% Convert a list of epressions evaluated in effect context to a chain of +%% Convert a list of expressions evaluated in effect context to a chain of %% #c_seq{}. The body in the innermost #c_seq{} will be void(). %% Anything that will not have any effect will be thrown away. @@ -3019,6 +3042,9 @@ format_error(result_ignored) -> "(suppress the warning by assigning the expression to the _ variable)"; format_error(useless_building) -> "a term is constructed, but never used"; +format_error({map_pair_key_overloaded,K}) -> + M = io_lib:format("the key ~p is used multiple times in map value association",[K]), + flatten(M); format_error(bin_opt_alias) -> "INFO: the '=' operator will prevent delayed sub binary optimization"; format_error(bin_partition) -> -- cgit v1.2.3 From 1b701e059c36154d88caa24c4aaa68a2d19971cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 25 Sep 2013 18:17:58 +0200 Subject: compiler: Fix multiple same keys --- lib/compiler/src/v3_kernel.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index 3d2dbf2088..f4ba60e529 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -1467,7 +1467,9 @@ arg_val(Arg, C) -> #k_map_pair{key=#k_literal{val=Key}} = Pair, Key end || Pair <- Es], - ordsets:from_list(Keys) + %% multiple keys may have the same name + %% do not use ordsets + lists:sort(Keys) end. %% ubody_used_vars(Expr, State) -> [UsedVar] -- cgit v1.2.3 From e491dc2ff804107daecf36244587566210682850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 1 Oct 2013 17:52:33 +0200 Subject: compiler: Fix sorted keys in put_map instruction All pairs in a Map needs to be in strict ascending key order. --- lib/compiler/src/v3_codegen.erl | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index 667f10a7f5..e8eec08323 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -1501,11 +1501,18 @@ set_cg([{var,R}], {map,SrcMap,Es}, Le, Vdb, Bef, {var,SrcVar} -> fetch_var(SrcVar, Int0); _ -> SrcMap end, - List = flatmap(fun({map_pair,K,{var,V}}) -> - [K,fetch_var(V, Int0)]; - ({map_pair,K,E}) -> - [K,E] - end, sort(Es)), + + % MapPairs in put_map must be sorted in ascending Key Order + % Key literals must be unique when arriving here in v3_codegen + + SortedEs = lists:sort(fun + ({map_pair,{_T1,K1},_},{map_pair,{_T2,K2},_}) when K1 < K2 -> true; + ({map_pair,{_,_},_},{map_pair,{_,_},_}) -> false + end, Es), + List = flatmap(fun + ({map_pair,K,{var,V}}) -> [K,fetch_var(V, Int0)]; + ({map_pair,K,E}) -> [K,E] + end, SortedEs), Live = max_reg(Bef#sr.reg), Int1 = clear_dead(Int0, Le#l.i, Vdb), Aft = Bef#sr{reg=put_reg(R, Int1#sr.reg)}, -- cgit v1.2.3 From 63ef0bbfdfb70673fe7f3ce2fc6fa4f0f801747d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 19 Sep 2013 17:48:28 +0200 Subject: erts: Add the size-testing guard BIF map_size/1 --- lib/stdlib/src/erl_internal.erl | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index 28de7205ea..480675554a 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -70,6 +70,7 @@ guard_bif(bit_size, 1) -> true; guard_bif(byte_size, 1) -> true; guard_bif(element, 2) -> true; guard_bif(self, 0) -> true; +guard_bif(map_size, 1) -> true; guard_bif(node, 0) -> true; guard_bif(node, 1) -> true; guard_bif(tuple_size, 1) -> true; @@ -335,6 +336,7 @@ bif(list_to_pid, 1) -> true; bif(list_to_tuple, 1) -> true; bif(load_module, 2) -> true; bif(make_ref, 0) -> true; +bif(map_size,1) -> true; bif(max,2) -> true; bif(min,2) -> true; bif(module_loaded, 1) -> true; -- cgit v1.2.3 From 43ed0cc716039c3b2f65a5395f00424169639309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 21 May 2013 18:11:39 +0200 Subject: erts: Add the type-testing guard BIF is_map/1 To add a type-testing guard BIF, the following steps are needed: * The BIF itself is added to bif.tab (note that it should be declared using "ubif", not "bif"), and its implementation to erl_bif_op.c. * erl_internal must be modified in 3 places: The type test must be recognized as guard BIF, as a type test, and it must be auto-imported. * There must be an instruction that implements the same type test as the BIF (it will be used in guards). beam_utils:bif_to_test/3 must be updated to recognize the new guard BIF. --- lib/compiler/src/beam_utils.erl | 1 + lib/stdlib/src/erl_internal.erl | 3 +++ 2 files changed, 4 insertions(+) (limited to 'lib') diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index 36f3200d11..a3f16cfa8f 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -152,6 +152,7 @@ bif_to_test(is_function, [_]=Ops, Fail) -> {test,is_function,Fail,Ops}; bif_to_test(is_function, [_,_]=Ops, Fail) -> {test,is_function2,Fail,Ops}; bif_to_test(is_integer, [_]=Ops, Fail) -> {test,is_integer,Fail,Ops}; bif_to_test(is_list, [_]=Ops, Fail) -> {test,is_list,Fail,Ops}; +bif_to_test(is_map, [_]=Ops, Fail) -> {test,is_map,Fail,Ops}; bif_to_test(is_number, [_]=Ops, Fail) -> {test,is_number,Fail,Ops}; bif_to_test(is_pid, [_]=Ops, Fail) -> {test,is_pid,Fail,Ops}; bif_to_test(is_port, [_]=Ops, Fail) -> {test,is_port,Fail,Ops}; diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index 480675554a..edfb097de0 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -83,6 +83,7 @@ guard_bif(is_function, 1) -> true; guard_bif(is_function, 2) -> true; guard_bif(is_integer, 1) -> true; guard_bif(is_list, 1) -> true; +guard_bif(is_map, 1) -> true; guard_bif(is_number, 1) -> true; guard_bif(is_pid, 1) -> true; guard_bif(is_port, 1) -> true; @@ -114,6 +115,7 @@ new_type_test(is_function, 1) -> true; new_type_test(is_function, 2) -> true; new_type_test(is_integer, 1) -> true; new_type_test(is_list, 1) -> true; +new_type_test(is_map, 1) -> true; new_type_test(is_number, 1) -> true; new_type_test(is_pid, 1) -> true; new_type_test(is_port, 1) -> true; @@ -316,6 +318,7 @@ bif(is_function, 1) -> true; bif(is_function, 2) -> true; bif(is_integer, 1) -> true; bif(is_list, 1) -> true; +bif(is_map, 1) -> true; bif(is_number, 1) -> true; bif(is_pid, 1) -> true; bif(is_port, 1) -> true; -- cgit v1.2.3 From d6b62db709a9e8e80689416bd7a5e5444ac3d3bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 17 May 2013 11:49:48 +0200 Subject: stdlib: Update printing to handle Maps --- lib/stdlib/src/io_lib.erl | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl index 375d05f359..886939761c 100644 --- a/lib/stdlib/src/io_lib.erl +++ b/lib/stdlib/src/io_lib.erl @@ -257,7 +257,9 @@ write(T, D) when is_tuple(T) -> [write(element(1, T), D-1)| write_tail(tl(tuple_to_list(T)), D-1, $,)], $}] - end. + end; +%write(Term, D) when is_map(Term) -> write_map(Term, D); +write(Term, D) -> write_map(Term, D). %% write_tail(List, Depth, CharacterBeforeDots) %% Test the terminating case first as this looks better with depth. @@ -275,6 +277,18 @@ write_port(Port) -> write_ref(Ref) -> erlang:ref_to_list(Ref). +write_map(Map, D) when is_integer(D) -> + [$#,${,write_map_body(map:to_list(Map), D),$}]. + +write_map_body(_, 0) -> "..."; +write_map_body([],_) -> []; +write_map_body([{K,V}],D) -> write_map_assoc(K,V,D); +write_map_body([{K,V}|KVs], D) -> + [write_map_assoc(K,V,D),$, | write_map_body(KVs,D-1)]. + +write_map_assoc(K,V,D) -> + [write(K,D - 1),"=>",write(V,D-1)]. + write_binary(B, D) when is_integer(D) -> [$<,$<,write_binary_body(B, D),$>,$>]. -- cgit v1.2.3 From 542c79e9685adbdab398856af99aaf9137966ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 4 Jun 2012 00:36:11 +0200 Subject: erl_eval: Add functions to interpreter for maps --- lib/stdlib/src/erl_eval.erl | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'lib') diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl index 18d8148b15..335134d1f0 100644 --- a/lib/stdlib/src/erl_eval.erl +++ b/lib/stdlib/src/erl_eval.erl @@ -239,6 +239,21 @@ expr({record,_,Name,_}, _Bs, _Lf, _Ef, _RBs) -> erlang:raise(error, {undef_record,Name}, stacktrace()); expr({record,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs) -> erlang:raise(error, {undef_record,Name}, stacktrace()); + +%% map +expr({map_field,_,EK, EV}, Bs0, Lf, Ef, RBs) -> + {value,K,Bs1} = expr(EK, Bs0, Lf, Ef, none), + {value,V,Bs2} = expr(EV, Bs0, Lf, Ef, none), + ret_expr({K,V}, merge_bindings(Bs1,Bs2), RBs); +expr({map,_, Binding,Es}, Bs0, Lf, Ef, RBs) -> + {value, Map0, Bs1} = expr(Binding, Bs0, Lf, Ef, RBs), + {Vs,Bs} = expr_list(Es, Bs1, Lf, Ef), + ret_expr(lists:foldl(fun({K,V}, Mi) -> map:put(K,V,Mi) end, Map0, Vs), Bs, RBs); +expr({map,_,Es}, Bs0, Lf, Ef, RBs) -> + {Vs,Bs} = expr_list(Es, Bs0, Lf, Ef), + ret_expr(lists:foldl(fun({K,V}, Mi) -> map:put(K,V,Mi) end, map:new(), Vs), Bs, RBs); + + expr({block,_,Es}, Bs, Lf, Ef, RBs) -> exprs(Es, Bs, Lf, Ef, RBs); expr({'if',_,Cs}, Bs, Lf, Ef, RBs) -> @@ -994,6 +1009,7 @@ type_test(port) -> is_port; type_test(function) -> is_function; type_test(binary) -> is_binary; type_test(record) -> is_record; +type_test(map) -> is_map; type_test(Test) -> Test. @@ -1075,6 +1091,9 @@ match1({tuple,_,Elts}, Tuple, Bs, BBs) match_tuple(Elts, Tuple, 1, Bs, BBs); match1({tuple,_,_}, _, _Bs, _BBs) -> throw(nomatch); +match1({map,_,Fs}, Map, Bs, BBs) -> + match_map(Fs, Map, Bs, BBs); + match1({bin, _, Fs}, <<_/bitstring>>=B, Bs0, BBs) -> eval_bits:match_bits(Fs, B, Bs0, BBs, match_fun(BBs), @@ -1118,6 +1137,15 @@ match_tuple([E|Es], Tuple, I, Bs0, BBs) -> match_tuple([], _, _, Bs, _BBs) -> {match,Bs}. +match_map([{map_field, _, K, V}|Fs], Map, Bs0, BBs) -> + Vm = try map:get(element(3,K),Map) + catch error:_ -> throw(nomatch) + end, + {match, Bs} = match1(V, Vm, Bs0, BBs), + match_map(Fs, Map, Bs, BBs); +match_map([], _, Bs, _) -> + {match, Bs}. + %% match_list(PatternList, TermList, Bindings) -> %% {match,NewBindings} | nomatch %% Try to match a list of patterns against a list of terms with the -- cgit v1.2.3 From 226e1e5898058aa3459c4d562df13132c9bc0899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 1 Oct 2013 22:08:14 +0200 Subject: Teach erl_scan to recognize ':=' --- lib/stdlib/src/erl_scan.erl | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl index 52376a8f3a..ae59d5f44f 100644 --- a/lib/stdlib/src/erl_scan.erl +++ b/lib/stdlib/src/erl_scan.erl @@ -596,6 +596,9 @@ scan1("||"++Cs, St, Line, Col, Toks) -> tok2(Cs, St, Line, Col, Toks, "||", '||', 2); scan1("|"=Cs, _St, Line, Col, Toks) -> {more,{Cs,Col,Toks,Line,[],fun scan/6}}; +%% := +scan1(":="++Cs, St, Line, Col, Toks) -> + tok2(Cs, St, Line, Col, Toks, ":=", ':=', 2); %% :- scan1(":-"++Cs, St, Line, Col, Toks) -> tok2(Cs, St, Line, Col, Toks, ":-", ':-', 2); -- cgit v1.2.3 From b15b2098627f702958771c3497949c62623a415d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 1 Oct 2013 22:29:29 +0200 Subject: Extend erl_parse with two Op Map syntax Example how to construct: #{ K1 => V1, K2 => V2 } How to update: M#{ K1 => V1, K2 := V2 } How to match: #{ K1 := V1, K2 := V2 } = M --- lib/stdlib/src/erl_parse.yrl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 5a846266c5..35c33c7796 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -34,7 +34,7 @@ binary_comprehension tuple %struct record_expr record_tuple record_field record_fields -map_expr map_tuple map_field map_fields map_key +map_expr map_tuple map_field map_field_assoc map_field_exact map_fields map_key if_expr if_clause if_clauses case_expr cr_clause cr_clauses receive_expr fun_expr fun_clause fun_clauses atom_or_var integer_or_var try_expr try_catch try_clause try_clauses @@ -60,7 +60,7 @@ char integer float atom string var '*' '/' 'div' 'rem' 'band' 'and' '+' '-' 'bor' 'bxor' 'bsl' 'bsr' 'or' 'xor' '++' '--' -'==' '/=' '=<' '<' '>=' '>' '=:=' '=/=' '<=' '=>' +'==' '/=' '=<' '<' '>=' '>' '=:=' '=/=' '<=' '=>' ':=' '<<' '>>' '!' '=' '::' '..' '...' 'spec' 'callback' % helper @@ -344,8 +344,14 @@ map_tuple -> '{' map_fields '}' : '$2'. map_fields -> map_field : ['$1']. map_fields -> map_field ',' map_fields : ['$1' | '$3']. -map_field -> map_key '=>' expr : - {map_field,?line('$1'),'$1','$3'}. +map_field -> map_field_assoc : '$1'. +map_field -> map_field_exact : '$1'. + +map_field_assoc -> map_key '=>' expr : + {map_field_assoc,?line('$1'),'$1','$3'}. + +map_field_exact -> map_key ':=' expr : + {map_field_exact,?line('$1'),'$1','$3'}. map_key -> expr : '$1'. -- cgit v1.2.3 From a0133aa75da3756918a5b9fd427ae75a75ae1dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Sat, 26 Oct 2013 01:21:23 +0200 Subject: stdlib: Fix erl_parse:parse_term/1 Did not handle Maps. --- lib/stdlib/src/erl_parse.yrl | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib') diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 35c33c7796..e9a813e098 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -888,6 +888,12 @@ normalise({cons,_,Head,Tail}) -> [normalise(Head)|normalise(Tail)]; normalise({tuple,_,Args}) -> list_to_tuple(normalise_list(Args)); +normalise({map,_,Pairs}=M) -> + map:from_list(lists:map(fun + %% only allow '=>' + ({map_field_assoc,_,K,V}) -> {normalise(K),normalise(V)}; + (_) -> erlang:error({badarg,M}) + end, Pairs)); %% Special case for unary +/-. normalise({op,_,'+',{char,_,I}}) -> I; normalise({op,_,'+',{integer,_,I}}) -> I; -- cgit v1.2.3 From 758702e24f0780130c6b3058426062a3761b7a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 1 Oct 2013 22:42:18 +0200 Subject: Update erl_lint, erl_expand_records, sys_pre_expand for Maps Update erlang lint and syntax expand for #{ K := V } --- lib/compiler/src/sys_pre_expand.erl | 12 ++++++++---- lib/stdlib/src/erl_expand_records.erl | 14 ++++++++++---- lib/stdlib/src/erl_lint.erl | 12 +++++++++--- 3 files changed, 27 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl index 04084d22ff..9998043013 100644 --- a/lib/compiler/src/sys_pre_expand.erl +++ b/lib/compiler/src/sys_pre_expand.erl @@ -231,10 +231,10 @@ pattern({tuple,Line,Ps}, St0) -> pattern({map,Line,Ps}, St0) -> {TPs,St1} = pattern_list(Ps, St0), {{map,Line,TPs},St1}; -pattern({map_field,Line,K0,V0}, St0) -> +pattern({map_field_exact,Line,K0,V0}, St0) -> {K,St1} = pattern(K0, St0), {V,St2} = pattern(V0, St1), - {{map_field,Line,K,V},St2}; + {{map_field_exact,Line,K,V},St2}; %%pattern({struct,Line,Tag,Ps}, St0) -> %% {TPs,TPsvs,St1} = pattern_list(Ps, St0), %% {{tuple,Line,[{atom,Line,Tag}|TPs]},TPsvs,St1}; @@ -334,10 +334,14 @@ expr({map,Line,Es0}, St0) -> expr({map,Line,Var,Es0}, St0) -> {Es1,St1} = expr_list(Es0, St0), {{map,Line,Var,Es1},St1}; -expr({map_field,Line,K0,V0}, St0) -> +expr({map_field_assoc,Line,K0,V0}, St0) -> {K,St1} = expr(K0, St0), {V,St2} = expr(V0, St1), - {{map_field,Line,K,V},St2}; + {{map_field_assoc,Line,K,V},St2}; +expr({map_field_exact,Line,K0,V0}, St0) -> + {K,St1} = expr(K0, St0), + {V,St2} = expr(V0, St1), + {{map_field_exact,Line,K,V},St2}; expr({bin,Line,Es0}, St0) -> {Es1,St1} = expr_bin(Es0, St0), {{bin,Line,Es1},St1}; diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl index 7fc4cc7887..4741bef6b9 100644 --- a/lib/stdlib/src/erl_expand_records.erl +++ b/lib/stdlib/src/erl_expand_records.erl @@ -135,9 +135,9 @@ pattern({tuple,Line,Ps}, St0) -> pattern({map,Line,Ps}, St0) -> {TPs,St1} = pattern_list(Ps, St0), {{map,Line,TPs},St1}; -pattern({map_field,Line,Key,V0}, St0) -> +pattern({map_field_exact,Line,Key,V0}, St0) -> {V,St1} = pattern(V0, St0), - {{map_field,Line,Key,V},St1}; + {{map_field_exact,Line,Key,V},St1}; %%pattern({struct,Line,Tag,Ps}, St0) -> %% {TPs,TPsvs,St1} = pattern_list(Ps, St0), %% {{struct,Line,Tag,TPs},TPsvs,St1}; @@ -313,8 +313,14 @@ expr({map,Line,Es0}, St0) -> expr({map,Line,Var,Es0}, St0) -> {Es1,St1} = expr_list(Es0, St0), {{map,Line,Var,Es1},St1}; -expr({map_field,_Line,_,_}=M, St0) -> - {M,St0}; +expr({map_field_assoc,Line,K0,V0}, St0) -> + {K,St1} = expr(K0, St0), + {V,St2} = expr(V0, St1), + {{map_field_assoc,Line,K,V},St2}; +expr({map_field_exact,Line,K0,V0}, St0) -> + {K,St1} = expr(K0, St0), + {V,St2} = expr(V0, St1), + {{map_field_exact,Line,K,V},St2}; %%expr({struct,Line,Tag,Es0}, Vs, St0) -> %% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0), %% {{struct,Line,Tag,Es1},Esvs,Esus,St1}; diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index b74a3ed924..86ebe82acc 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -1368,7 +1368,9 @@ pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) -> pattern_list(Ps, Vt, Old, Bvt, St); pattern({map,_Line,Ps}, Vt, Old, Bvt, St) -> pattern_list(Ps, Vt, Old, Bvt, St); -pattern({map_field,_Line,_,P}, Vt, Old, Bvt0, St0) -> +pattern({map_field_assoc,_Line,_,_}=Pat, _, _, _, St) -> + {[],[],add_error(element(2, Pat), illegal_pattern, St)}; +pattern({map_field_exact,_Line,_,P}, Vt, Old, Bvt0, St0) -> {Pvt,Bvt,St1} = pattern(P, Vt, Old, Bvt0, St0), {Pvt,Bvt,St1}; %%pattern({struct,_Line,_Tag,Ps}, Vt, Old, Bvt, St) -> @@ -1762,7 +1764,9 @@ gexpr({map,_Line,Es}, Vt, St) -> gexpr_list(Es, Vt, St); gexpr({map,_Line,Src,Es}, Vt, St) -> gexpr_list([Src|Es], Vt, St); -gexpr({map_field,_Line,K,V}, Vt, St) -> +gexpr({map_field_assoc,_Line,K,V}, Vt, St) -> + gexpr_list([K,V], Vt, St); +gexpr({map_field_exact,_Line,K,V}, Vt, St) -> gexpr_list([K,V], Vt, St); gexpr({record_index,Line,Name,Field}, _Vt, St) -> check_record(Line, Name, St, @@ -1985,7 +1989,9 @@ expr({map,_Line,Es}, Vt, St) -> expr_list(Es, Vt, St); expr({map,_Line,Src,Es}, Vt, St) -> expr_list([Src|Es], Vt, St); -expr({map_field,_Line,K,V}, Vt, St) -> +expr({map_field_assoc,_Line,K,V}, Vt, St) -> + expr_list([K,V], Vt, St); +expr({map_field_exact,_Line,K,V}, Vt, St) -> expr_list([K,V], Vt, St); expr({record_index,Line,Name,Field}, _Vt, St) -> check_record(Line, Name, St, -- cgit v1.2.3 From 652ddd4c360cd2cc381ed033915d87d967310a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 1 Oct 2013 23:15:50 +0200 Subject: Extend representation for maps in Core Erlang --- lib/compiler/src/core_parse.hrl | 6 ++++-- lib/compiler/src/core_parse.yrl | 19 +++++++++++++------ lib/compiler/src/core_pp.erl | 7 ++++++- 3 files changed, 23 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl index c134747991..40c3ae4582 100644 --- a/lib/compiler/src/core_parse.hrl +++ b/lib/compiler/src/core_parse.hrl @@ -97,8 +97,10 @@ -record(c_var, {anno=[], name :: cerl:var_name()}). --record(c_map_pair, {anno=[], key, val}). +-record(c_map_pair_assoc, {anno=[], key, val}). + +-record(c_map_pair_exact, {anno=[], key, val}). -record(c_map, {anno=[], var=#c_literal{val=[]} :: #c_var{} | #c_literal{}, - es :: [#c_map_pair{}]}). + es :: [#c_map_pair_assoc{}|#c_map_pair_exact{}]}). diff --git a/lib/compiler/src/core_parse.yrl b/lib/compiler/src/core_parse.yrl index b072950eeb..82ba11b0fc 100644 --- a/lib/compiler/src/core_parse.yrl +++ b/lib/compiler/src/core_parse.yrl @@ -46,7 +46,7 @@ receive_expr timeout try_expr sequence catch_expr variable clause clause_pattern -map_expr map_pairs map_pair +map_expr map_pairs map_pair map_pair_assoc map_pair_exact map_pattern map_pair_patterns map_pair_pattern annotation anno_fun anno_expression anno_expressions @@ -58,7 +58,7 @@ Terminals %% Separators -'(' ')' '{' '}' '[' ']' '|' ',' '->' '=' '/' '<' '>' ':' '-|' '#' '~' +'(' ')' '{' '}' '[' ']' '|' ',' '->' '=' '/' '<' '>' ':' '-|' '#' '~' '::' %% Keywords (atoms are assumed to always be single-quoted). @@ -190,7 +190,7 @@ map_pair_patterns -> map_pair_pattern : ['$1']. map_pair_patterns -> map_pair_pattern ',' map_pair_patterns : ['$1' | '$3']. map_pair_pattern -> '~' '<' anno_pattern ',' anno_pattern '>' : - #c_map_pair{key='$3',val='$5'}. + #c_map_pair_exact{key='$3',val='$5'}. cons_pattern -> '[' anno_pattern tail_pattern : #c_cons{hd='$2',tl='$3'}. @@ -286,14 +286,19 @@ 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='$',es=[]}. +map_expr -> variable '~' '{' '}' '~' : #c_map{var='$1',es=[]}. map_expr -> variable '~' '{' map_pairs '}' '~' : #c_map{var='$1',es='$4'}. map_pairs -> map_pair : ['$1']. map_pairs -> map_pair ',' map_pairs : ['$1' | '$3']. -map_pair -> '~' '<' anno_expression ',' anno_expression'>' : - #c_map_pair{key='$3',val='$5'}. +map_pair -> map_pair_assoc : '$1'. +map_pair -> map_pair_exact : '$1'. + +map_pair_assoc -> '::' '<' anno_expression ',' anno_expression'>' : + #c_map_pair_assoc{key='$3',val='$5'}. +map_pair_exact -> '~' '<' anno_expression ',' anno_expression'>' : + #c_map_pair_exact{key='$3',val='$5'}. cons -> '[' anno_expression tail : c_cons('$2', '$3'). @@ -409,3 +414,5 @@ Erlang code. tok_val(T) -> element(3, T). tok_line(T) -> element(2, T). + +%% vim: syntax=erlang diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl index c8c59b9246..f775d87507 100644 --- a/lib/compiler/src/core_pp.erl +++ b/lib/compiler/src/core_pp.erl @@ -172,7 +172,12 @@ format_1(#c_map{es=Es}, Ctxt) -> format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2), "}~" ]; -format_1(#c_map_pair{key=K,val=V}, Ctxt) -> +format_1(#c_map_pair_assoc{key=K,val=V}, Ctxt) -> + ["::<", + format_hseq([K,V], ",", add_indent(Ctxt, 1), fun format/2), + ">" + ]; +format_1(#c_map_pair_exact{key=K,val=V}, Ctxt) -> ["~<", format_hseq([K,V], ",", add_indent(Ctxt, 1), fun format/2), ">" -- cgit v1.2.3 From 3c5067eab057391e101845b03c1ba7b76d2b3562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 1 Oct 2013 23:42:17 +0200 Subject: compiler: Implement support for exact Op in Maps The syntax is handled upto v3_kernel where it is reduced to previous behaviour for construction and updates. Meaning, the ':=' operator is handled exactly as '=>' operator. --- lib/compiler/src/core_lib.erl | 4 +++- lib/compiler/src/sys_core_dsetel.erl | 10 +++++++--- lib/compiler/src/sys_core_fold.erl | 22 ++++++++++++++++------ lib/compiler/src/v3_core.erl | 35 +++++++++++++++++++++++------------ lib/compiler/src/v3_kernel.erl | 18 ++++++++++++------ 5 files changed, 61 insertions(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl index f506901099..d6221a22bb 100644 --- a/lib/compiler/src/core_lib.erl +++ b/lib/compiler/src/core_lib.erl @@ -107,7 +107,9 @@ vu_expr(V, #c_tuple{es=Es}) -> vu_expr_list(V, Es); vu_expr(V, #c_map{es=Es}) -> vu_expr_list(V, Es); -vu_expr(V, #c_map_pair{key=Key,val=Val}) -> +vu_expr(V, #c_map_pair_assoc{key=Key,val=Val}) -> + vu_expr_list(V, [Key,Val]); +vu_expr(V, #c_map_pair_exact{key=Key,val=Val}) -> vu_expr_list(V, [Key,Val]); vu_expr(V, #c_binary{segments=Ss}) -> vu_seg_list(V, Ss); diff --git a/lib/compiler/src/sys_core_dsetel.erl b/lib/compiler/src/sys_core_dsetel.erl index c7c8343c35..f921429409 100644 --- a/lib/compiler/src/sys_core_dsetel.erl +++ b/lib/compiler/src/sys_core_dsetel.erl @@ -105,10 +105,14 @@ visit(Env0, #c_tuple{es=Es0}=R) -> visit(Env0, #c_map{es=Es0}=R) -> {Es1,Env1} = visit_list(Env0, Es0), {R#c_map{es=Es1}, Env1}; -visit(Env0, #c_map_pair{key=K0,val=V0}=R) -> +visit(Env0, #c_map_pair_assoc{key=K0,val=V0}=R) -> {K,Env1} = visit(Env0, K0), {V,Env2} = visit(Env1, V0), - {R#c_map_pair{key=K,val=V}, Env2}; + {R#c_map_pair_assoc{key=K,val=V}, Env2}; +visit(Env0, #c_map_pair_exact{key=K0,val=V0}=R) -> + {K,Env1} = visit(Env0, K0), + {V,Env2} = visit(Env1, V0), + {R#c_map_pair_exact{key=K,val=V}, Env2}; visit(Env0, #c_cons{hd=H0,tl=T0}=R) -> {H1,Env1} = visit(Env0, H0), {T1,Env2} = visit(Env1, T0), @@ -221,7 +225,7 @@ visit_pat(Env0, #c_tuple{es=Es}, Vs) -> visit_pats(Es, Env0, Vs); visit_pat(Env0, #c_map{es=Es}, Vs) -> visit_pats(Es, Env0, Vs); -visit_pat(Env0, #c_map_pair{key=V,val=K}, Vs0) -> +visit_pat(Env0, #c_map_pair_exact{key=V,val=K}, Vs0) -> {Vs1, Env1} = visit_pat(Env0, V, Vs0), visit_pat(Env1, K, Vs1); visit_pat(Env0, #c_cons{hd=H,tl=T}, Vs0) -> diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index cdbe2d0753..2cd6ea0e53 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -438,18 +438,28 @@ pair_list_reversed([E|Es],Ctxt,Sub,Out,Keys) -> %% check if key already is present in map, i.e. #{ a=>1, a=>2 } %% where 'a' is duplicate. Update maps set with the key if not present. -map_has_key(#c_map_pair{key=#c_literal{val=K}},Ks) -> +map_has_key(MapPair,Ks) -> + K = map_get_literal_key_from_pair(MapPair), case gb_sets:is_element(K,Ks) of false -> {false, gb_sets:add(K,Ks)}; true -> {true, K} end. -pair(#c_map_pair{key=K,val=V}, effect, Sub) -> +map_get_literal_key_from_pair(#c_map_pair_assoc{key=#c_literal{val=K}}) -> K; +map_get_literal_key_from_pair(#c_map_pair_exact{key=#c_literal{val=K}}) -> K. + +pair(#c_map_pair_assoc{key=K,val=V}, effect, Sub) -> + make_effect_seq([K,V], Sub); +pair(#c_map_pair_exact{key=K,val=V}, effect, Sub) -> make_effect_seq([K,V], Sub); -pair(#c_map_pair{key=K0,val=V0}=Pair, value=Ctxt, Sub) -> +pair(#c_map_pair_assoc{key=K0,val=V0}=Pair, value=Ctxt, Sub) -> + K = expr(K0, Ctxt, Sub), + V = expr(V0, Ctxt, Sub), + Pair#c_map_pair_assoc{key=K,val=V}; +pair(#c_map_pair_exact{key=K0,val=V0}=Pair, value=Ctxt, Sub) -> K = expr(K0, Ctxt, Sub), V = expr(V0, Ctxt, Sub), - Pair#c_map_pair{key=K,val=V}. + Pair#c_map_pair_exact{key=K,val=V}. bitstr_list(Es, Sub) -> [bitstr(E, Sub) || E <- Es]. @@ -1559,10 +1569,10 @@ map_pair_pattern_list(Ps0, Isub, Osub0) -> {Ps,{_,Osub}} = mapfoldl(fun map_pair_pattern/2, {Isub,Osub0}, Ps0), {Ps,Osub}. -map_pair_pattern(#c_map_pair{key=K0,val=V0}=Pair, {Isub,Osub0}) -> +map_pair_pattern(#c_map_pair_exact{key=K0,val=V0}=Pair, {Isub,Osub0}) -> {K,Osub1} = pattern(K0, Isub, Osub0), {V,Osub} = pattern(V0, Isub, Osub1), - {Pair#c_map_pair{key=K,val=V},{Isub,Osub}}. + {Pair#c_map_pair_exact{key=K,val=V},{Isub,Osub}}. bin_pattern_list(Ps0, Isub, Osub0) -> {Ps,{_,Osub}} = mapfoldl(fun bin_pattern/2, {Isub,Osub0}, Ps0), diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 32b65333a4..b33799275b 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -488,6 +488,8 @@ expr({tuple,L,Es0}, St0) -> A = lineno_anno(L, St1), {ann_c_tuple(A, Es1),Eps,St1}; expr({map,L,Es0}, St0) -> + % erl_lint should make sure only #{ K => V } are allowed + % in map construction. {Es1,Eps,St1} = map_pair_list(Es0, St0), A = lineno_anno(L, St1), {#c_map{anno=A,es=Es1},Eps,St1}; @@ -705,13 +707,20 @@ make_bool_switch_guard(L, E, V, T, F) -> ]}. map_pair_list(Es, St) -> - foldr(fun ({map_field,L,K0,V0}, {Ces,Esp,St0}) -> - {K,Ep0,St1} = safe(K0, St0), - {V,Ep1,St2} = safe(V0, St1), - A = lineno_anno(L, St2), - Pair = #c_map_pair{anno=A,key=K,val=V}, - {[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2} - end, {[],[],St}, Es). + foldr(fun + ({map_field_assoc,L,K0,V0}, {Ces,Esp,St0}) -> + {K,Ep0,St1} = safe(K0, St0), + {V,Ep1,St2} = safe(V0, St1), + A = lineno_anno(L, St2), + Pair = #c_map_pair_assoc{anno=A,key=K,val=V}, + {[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2}; + ({map_field_exact,L,K0,V0}, {Ces,Esp,St0}) -> + {K,Ep0,St1} = safe(K0, St0), + {V,Ep1,St2} = safe(V0, St1), + A = lineno_anno(L, St2), + Pair = #c_map_pair_exact{anno=A,key=K,val=V}, + {[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2} + end, {[],[],St}, Es). %% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}. @@ -1497,7 +1506,7 @@ pattern({tuple,L,Ps}, St) -> ann_c_tuple(lineno_anno(L, St), pattern_list(Ps, St)); pattern({map,L,Ps}, St) -> #c_map{anno=lineno_anno(L, St),es=sort(pattern_list(Ps, St))}; -pattern({map_field,L,K,V}, St) -> +pattern({map_field_exact,L,K,V}, St) -> %% FIXME: Better way to construct literals? or missing case %% {Key,_,_} = expr(K, St), Key = case K of @@ -1511,7 +1520,7 @@ pattern({map_field,L,K,V}, St) -> _ -> pattern(K,St) end, - #c_map_pair{anno=lineno_anno(L, St), + #c_map_pair_exact{anno=lineno_anno(L, St), key=Key, val=pattern(V, St)}; pattern({bin,L,Ps}, St) -> @@ -1862,9 +1871,9 @@ upattern(#c_tuple{es=Es0}=Tuple, Ks, St0) -> upattern(#c_map{es=Es0}=Map, Ks, St0) -> {Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0), {Map#c_map{es=Es1},Esg,Esv,Eus,St1}; -upattern(#c_map_pair{val=V0}=MapPair, Ks, St0) -> +upattern(#c_map_pair_exact{val=V0}=MapPair, Ks, St0) -> {V,Vg,Vv,Vu,St1} = upattern(V0, Ks, St0), - {MapPair#c_map_pair{val=V},Vg,Vv,Vu,St1}; + {MapPair#c_map_pair_exact{val=V},Vg,Vv,Vu,St1}; upattern(#c_binary{segments=Es0}=Bin, Ks, St0) -> {Es1,Esg,Esv,Eus,St1} = upat_bin(Es0, Ks, St0), {Bin#c_binary{segments=Es1},Esg,Esv,Eus,St1}; @@ -2195,7 +2204,9 @@ is_simple(#c_cons{hd=H,tl=T}) -> is_simple(H) andalso is_simple(T); is_simple(#c_tuple{es=Es}) -> is_simple_list(Es); is_simple(#c_map{es=Es}) -> is_simple_list(Es); -is_simple(#c_map_pair{key=K,val=V}) -> +is_simple(#c_map_pair_assoc{key=K,val=V}) -> + is_simple(K) andalso is_simple(V); +is_simple(#c_map_pair_exact{key=K,val=V}) -> is_simple(K) andalso is_simple(V); is_simple(_) -> false. diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index f4ba60e529..44f853ec2b 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -497,12 +497,18 @@ translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) -> translate_fc(Args) -> [#c_literal{val=function_clause},make_list(Args)]. +%% FIXME: Not completed map_pairs(Es, Sub, St) -> - foldr(fun(#c_map_pair{key=K0,val=V0}, {Kes,Esp,St0}) -> - {K,[],St1} = expr(K0, Sub, St0), - {V,Ep,St2} = atomic(V0, Sub, St1), - {[#k_map_pair{key=K,val=V}|Kes],Ep ++ Esp,St2} - end, {[],[],St}, Es). + foldr(fun + (#c_map_pair_assoc{key=K0,val=V0}, {Kes,Esp,St0}) -> + {K,[],St1} = expr(K0, Sub, St0), + {V,Ep,St2} = atomic(V0, Sub, St1), + {[#k_map_pair{key=K,val=V}|Kes],Ep ++ Esp,St2}; + (#c_map_pair_exact{key=K0,val=V0}, {Kes,Esp,St0}) -> + {K,[],St1} = expr(K0, Sub, St0), + {V,Ep,St2} = atomic(V0, Sub, St1), + {[#k_map_pair{key=K,val=V}|Kes],Ep ++ Esp,St2} + end, {[],[],St}, Es). %% call_type(Module, Function, Arity) -> call | bif | apply | error. %% Classify the call. @@ -662,7 +668,7 @@ pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) -> pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) -> {Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0), {#k_map{anno=A,es=Kes},Osub1,St1}; -pattern(#c_map_pair{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) -> +pattern(#c_map_pair_exact{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) -> {Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0), {Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1), {#k_map_pair{anno=A,key=Kk,val=Kv},Osub2,St2}; -- cgit v1.2.3 From c0bb7f866257168e0a05110767fc7096a93d351d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 3 Oct 2013 13:22:32 +0200 Subject: sys_core_fold: Remove optimization that has become invalid With the => and := operators for updating maps, this optimization is no longer valid. --- lib/compiler/src/sys_core_fold.erl | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 2cd6ea0e53..6792e2c894 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -418,35 +418,8 @@ expr(#c_try{anno=A,arg=E0,vars=Vs0,body=B0,evars=Evs0,handler=H0}=Try, _, Sub0) expr_list(Es, Ctxt, Sub) -> [expr(E, Ctxt, Sub) || E <- Es]. -%% traverse pairs in reverse -%% - remove later literals since they will be overwritten. - pair_list(Es, Ctxt, Sub) -> - pair_list_reversed(lists:reverse(Es), Ctxt, Sub, [], gb_sets:empty()). - -pair_list_reversed([],_,_,Es,_) -> Es; -pair_list_reversed([E|Es],Ctxt,Sub,Out,Keys) -> - Pair = pair(E,Ctxt,Sub), - case map_has_key(Pair,Keys) of - {false,Keys1} -> - pair_list_reversed(Es,Ctxt,Sub,[Pair|Out],Keys1); - {true,K} -> - add_warning(E, {map_pair_key_overloaded,K}), - pair_list_reversed(Es,Ctxt,Sub,Out,Keys) - end. - -%% check if key already is present in map, i.e. #{ a=>1, a=>2 } -%% where 'a' is duplicate. Update maps set with the key if not present. - -map_has_key(MapPair,Ks) -> - K = map_get_literal_key_from_pair(MapPair), - case gb_sets:is_element(K,Ks) of - false -> {false, gb_sets:add(K,Ks)}; - true -> {true, K} - end. - -map_get_literal_key_from_pair(#c_map_pair_assoc{key=#c_literal{val=K}}) -> K; -map_get_literal_key_from_pair(#c_map_pair_exact{key=#c_literal{val=K}}) -> K. + [pair(E, Ctxt, Sub) || E <- Es]. pair(#c_map_pair_assoc{key=K,val=V}, effect, Sub) -> make_effect_seq([K,V], Sub); -- cgit v1.2.3 From 760ed909f8e2a655100ea773829c7d0e7dd40088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 3 Oct 2013 12:58:54 +0200 Subject: Pass the map pair operators through to the v3_codegen pass --- lib/compiler/src/v3_codegen.erl | 17 +++++++++-------- lib/compiler/src/v3_kernel.erl | 25 ++++++++++++++++--------- lib/compiler/src/v3_kernel.hrl | 3 ++- lib/compiler/src/v3_kernel_pp.erl | 4 +++- lib/compiler/src/v3_life.erl | 10 +++++++--- 5 files changed, 37 insertions(+), 22 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index e8eec08323..c1d555efac 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -1502,16 +1502,17 @@ set_cg([{var,R}], {map,SrcMap,Es}, Le, Vdb, Bef, _ -> SrcMap end, - % MapPairs in put_map must be sorted in ascending Key Order - % Key literals must be unique when arriving here in v3_codegen + %% MapPairs in put_map must be sorted in ascending Key order. + %% Key literals must be unique when arriving here in v3_codegen. - SortedEs = lists:sort(fun - ({map_pair,{_T1,K1},_},{map_pair,{_T2,K2},_}) when K1 < K2 -> true; - ({map_pair,{_,_},_},{map_pair,{_,_},_}) -> false - end, Es), + SortedEs = lists:sort(fun({_,{_T1,K1},_},{_,{_T2,K2},_}) -> + K1 =< K2 + end, Es), List = flatmap(fun - ({map_pair,K,{var,V}}) -> [K,fetch_var(V, Int0)]; - ({map_pair,K,E}) -> [K,E] + ({map_pair_assoc,K,{var,V}}) -> [K,fetch_var(V, Int0)]; + ({map_pair_exact,K,{var,V}}) -> [K,fetch_var(V, Int0)]; + ({map_pair_assoc,K,E}) -> [K,E]; + ({map_pair_exact,K,E}) -> [K,E] end, SortedEs), Live = max_reg(Bef#sr.reg), Int1 = clear_dead(Int0, Le#l.i, Vdb), diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index 44f853ec2b..14e776fd3c 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -503,11 +503,11 @@ map_pairs(Es, Sub, St) -> (#c_map_pair_assoc{key=K0,val=V0}, {Kes,Esp,St0}) -> {K,[],St1} = expr(K0, Sub, St0), {V,Ep,St2} = atomic(V0, Sub, St1), - {[#k_map_pair{key=K,val=V}|Kes],Ep ++ Esp,St2}; + {[#k_map_pair_assoc{key=K,val=V}|Kes],Ep ++ Esp,St2}; (#c_map_pair_exact{key=K0,val=V0}, {Kes,Esp,St0}) -> {K,[],St1} = expr(K0, Sub, St0), {V,Ep,St2} = atomic(V0, Sub, St1), - {[#k_map_pair{key=K,val=V}|Kes],Ep ++ Esp,St2} + {[#k_map_pair_exact{key=K,val=V}|Kes],Ep ++ Esp,St2} end, {[],[],St}, Es). %% call_type(Module, Function, Arity) -> call | bif | apply | error. @@ -668,10 +668,14 @@ pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) -> pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) -> {Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0), {#k_map{anno=A,es=Kes},Osub1,St1}; +pattern(#c_map_pair_assoc{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) -> + {Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0), + {Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1), + {#k_map_pair_assoc{anno=A,key=Kk,val=Kv},Osub2,St2}; pattern(#c_map_pair_exact{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) -> {Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0), {Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1), - {#k_map_pair{anno=A,key=Kk,val=Kv},Osub2,St2}; + {#k_map_pair_assoc{anno=A,key=Kk,val=Kv},Osub2,St2}; pattern(#c_binary{anno=A,segments=Cv}, Isub, Osub0, St0) -> {Kv,Osub1,St1} = pattern_bin(Cv, Isub, Osub0, St0), {#k_binary{anno=A,segs=Kv},Osub1,St1}; @@ -1347,8 +1351,8 @@ get_match(#k_tuple{es=Es}, St0) -> {#k_tuple{es=Mes},Mes,St1}; get_match(#k_map{es=Es0}, St0) -> {Mes,St1} = new_vars(length(Es0), St0), - {Es,_} = mapfoldl(fun(#k_map_pair{}=Pair, [V|Vs]) -> - {Pair#k_map_pair{val=V},Vs} + {Es,_} = mapfoldl(fun(#k_map_pair_assoc{}=Pair, [V|Vs]) -> + {Pair#k_map_pair_assoc{val=V},Vs} end, Mes, Es0), {#k_map{es=Es},Mes,St1}; get_match(M, St) -> @@ -1368,7 +1372,8 @@ new_clauses(Cs0, U, St) -> #k_bin_int{next=N} -> [N|As]; #k_map{es=Es} -> - Vals = [V || #k_map_pair{val=V} <- Es], + Vals = [V || + #k_map_pair_assoc{val=V} <- Es], Vals ++ As; _Other -> As @@ -1470,7 +1475,7 @@ arg_val(Arg, C) -> end; #k_map{es=Es} -> Keys = [begin - #k_map_pair{key=#k_literal{val=Key}} = Pair, + #k_map_pair_assoc{key=#k_literal{val=Key}} = Pair, Key end || Pair <- Es], %% multiple keys may have the same name @@ -1846,7 +1851,9 @@ lit_vars(#k_cons{hd=H,tl=T}) -> union(lit_vars(H), lit_vars(T)); lit_vars(#k_map{var=Var,es=Es}) -> lit_list_vars([Var|Es]); -lit_vars(#k_map_pair{key=K,val=V}) -> +lit_vars(#k_map_pair_assoc{key=K,val=V}) -> + union(lit_vars(K), lit_vars(V)); +lit_vars(#k_map_pair_exact{key=K,val=V}) -> union(lit_vars(K), lit_vars(V)); lit_vars(#k_binary{segs=V}) -> lit_vars(V); lit_vars(#k_bin_end{}) -> []; @@ -1886,7 +1893,7 @@ pat_vars(#k_tuple{es=Es}) -> pat_list_vars(Es); pat_vars(#k_map{es=Es}) -> pat_list_vars(Es); -pat_vars(#k_map_pair{val=V}) -> +pat_vars(#k_map_pair_assoc{val=V}) -> pat_vars(V). pat_list_vars(Ps) -> diff --git a/lib/compiler/src/v3_kernel.hrl b/lib/compiler/src/v3_kernel.hrl index 6671109afb..9da394d50d 100644 --- a/lib/compiler/src/v3_kernel.hrl +++ b/lib/compiler/src/v3_kernel.hrl @@ -39,7 +39,8 @@ -record(k_tuple, {anno=[],es}). -record(k_map, {anno=[],var,es}). --record(k_map_pair, {anno=[],key,val}). +-record(k_map_pair_assoc, {anno=[],key,val}). +-record(k_map_pair_exact, {anno=[],key,val}). -record(k_cons, {anno=[],hd,tl}). -record(k_binary, {anno=[],segs}). -record(k_bin_seg, {anno=[],size,unit,type,flags,seg,next}). diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl index 1af90792d3..e8d19336f6 100644 --- a/lib/compiler/src/v3_kernel_pp.erl +++ b/lib/compiler/src/v3_kernel_pp.erl @@ -115,8 +115,10 @@ format_1(#k_map{es=Es}, Ctxt) -> format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), $},$~ ]; -format_1(#k_map_pair{key=K,val=V}, Ctxt) -> +format_1(#k_map_pair_assoc{key=K,val=V}, Ctxt) -> ["~<",format(K, Ctxt),",",format(V, Ctxt),">"]; +format_1(#k_map_pair_exact{key=K,val=V}, Ctxt) -> + ["::<",format(K, Ctxt),",",format(V, Ctxt),">"]; format_1(#k_binary{segs=S}, Ctxt) -> ["#<",format(S, ctxt_bump_indent(Ctxt, 2)),">#"]; format_1(#k_bin_seg{next=Next}=S, Ctxt) -> diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl index 507f5d0df1..a3bd781c98 100644 --- a/lib/compiler/src/v3_life.erl +++ b/lib/compiler/src/v3_life.erl @@ -369,8 +369,10 @@ literal(#k_tuple{es=Es}, Ctxt) -> {tuple,literal_list(Es, Ctxt)}; literal(#k_map{var=Var,es=Es}, Ctxt) -> {map,literal(Var, Ctxt),literal_list(Es, Ctxt)}; -literal(#k_map_pair{key=K,val=V}, Ctxt) -> - {map_pair,literal(K, Ctxt),literal(V, Ctxt)}; +literal(#k_map_pair_assoc{key=K,val=V}, Ctxt) -> + {map_pair_assoc,literal(K, Ctxt),literal(V, Ctxt)}; +literal(#k_map_pair_exact{key=K,val=V}, Ctxt) -> + {map_pair_exact,literal(K, Ctxt),literal(V, Ctxt)}; literal(#k_literal{val=V}, _Ctxt) -> {literal,V}. @@ -402,7 +404,9 @@ literal2(#k_tuple{es=Es}, Ctxt) -> {tuple,literal_list2(Es, Ctxt)}; literal2(#k_map{es=Es}, Ctxt) -> {map,literal_list2(Es, Ctxt)}; -literal2(#k_map_pair{key=K,val=V}, Ctxt) -> +literal2(#k_map_pair_assoc{key=K,val=V}, Ctxt) -> + {map_pair,literal2(K, Ctxt),literal2(V, Ctxt)}; +literal2(#k_map_pair_exact{key=K,val=V}, Ctxt) -> {map_pair,literal2(K, Ctxt),literal2(V, Ctxt)}. literal_list2(Ks, Ctxt) -> -- cgit v1.2.3 From c2702eb35db00ad67f922708eeea48616d908306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 3 Oct 2013 14:06:57 +0200 Subject: compiler: Implement different instructions for => and := --- lib/compiler/src/beam_a.erl | 4 ++ lib/compiler/src/beam_block.erl | 6 +- lib/compiler/src/beam_clean.erl | 5 +- lib/compiler/src/beam_flatten.erl | 4 +- lib/compiler/src/beam_jump.erl | 2 +- lib/compiler/src/beam_split.erl | 7 ++- lib/compiler/src/beam_validator.erl | 23 +++++--- lib/compiler/src/beam_z.erl | 4 ++ lib/compiler/src/genop.tab | 9 +-- lib/compiler/src/v3_codegen.erl | 111 ++++++++++++++++++++++++++++++------ 10 files changed, 135 insertions(+), 40 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/beam_a.erl b/lib/compiler/src/beam_a.erl index c590c5e35b..3dfa67a771 100644 --- a/lib/compiler/src/beam_a.erl +++ b/lib/compiler/src/beam_a.erl @@ -88,6 +88,10 @@ rename_instr({bs_private_append=I,F,Sz,U,Src,Flags,Dst}) -> {bs_init,F,{I,U,Flags},none,[Sz,Src],Dst}; rename_instr(bs_init_writable=I) -> {bs_init,{f,0},I,1,[{x,0}],{x,0}}; +rename_instr({put_map_assoc,Fail,S,D,R,L}) -> + {put_map,Fail,assoc,S,D,R,L}; +rename_instr({put_map_exact,Fail,S,D,R,L}) -> + {put_map,Fail,exact,S,D,R,L}; rename_instr({select_val=I,Reg,Fail,{list,List}}) -> {select,I,Reg,Fail,List}; rename_instr({select_tuple_arity=I,Reg,Fail,{list,List}}) -> diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl index f9e90b81d2..d5f2ffc444 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -152,8 +152,8 @@ collect({get_tuple_element,S,I,D}) -> {set,[D],[S],{get_tuple_element,I}}; collect({set_tuple_element,S,D,I}) -> {set,[],[S,D],{set_tuple_element,I}}; collect({get_list,S,D1,D2}) -> {set,[D1,D2],[S],get_list}; collect(remove_message) -> {set,[],[],remove_message}; -collect({put_map,F,S,D,R,{list,Puts}}) -> - {set,[D],[S|Puts],{alloc,R,{put_map,F}}}; +collect({put_map,F,Op,S,D,R,{list,Puts}}) -> + {set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}; collect({get_map_element,F,S,K,D}) -> {set,[D],[S],{get_map_element,K,F}}; collect({'catch',R,L}) -> {set,[R],[],{'catch',L}}; @@ -387,7 +387,7 @@ gen_init(Fs, Regs, Y, Acc) -> init_yreg([{set,_,_,{bif,_,_}}|_], Reg) -> Reg; init_yreg([{set,_,_,{alloc,_,{gc_bif,_,_}}}|_], Reg) -> Reg; -init_yreg([{set,_,_,{alloc,_,{put_map,_}}}|_], Reg) -> Reg; +init_yreg([{set,_,_,{alloc,_,{put_map,_,_}}}|_], Reg) -> Reg; init_yreg([{set,Ds,_,_}|Is], Reg) -> init_yreg(Is, add_yregs(Ds, Reg)); init_yreg(_Is, Reg) -> Reg. diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl index 6f802d0436..55f985ad0e 100644 --- a/lib/compiler/src/beam_clean.erl +++ b/lib/compiler/src/beam_clean.erl @@ -259,8 +259,9 @@ replace([{bs_utf8_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 -> replace(Is, [{I,{f,label(Lbl, D)},Src,Dst}|Acc], D); replace([{bs_utf16_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 -> replace(Is, [{I,{f,label(Lbl, D)},Src,Dst}|Acc], D); -replace([{put_map=I,{f,Lbl},Src,Dst,Live,List}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{I,{f,label(Lbl, D)},Src,Dst,Live,List}|Acc], D); +replace([{put_map=I,{f,Lbl},Op,Src,Dst,Live,List}|Is], Acc, D) + when Lbl =/= 0 -> + replace(Is, [{I,{f,label(Lbl, D)},Op,Src,Dst,Live,List}|Acc], D); replace([{get_map_element=I,{f,Lbl},Src,Key,Dst}|Is], Acc, D) when Lbl =/= 0 -> replace(Is, [{I,{f,label(Lbl, D)},Src,Key,Dst}|Acc], D); replace([I|Is], Acc, D) -> diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl index ad9591ff05..534bc6d954 100644 --- a/lib/compiler/src/beam_flatten.erl +++ b/lib/compiler/src/beam_flatten.erl @@ -61,8 +61,8 @@ norm({set,[],[S],put}) -> {put,S}; norm({set,[D],[S],{get_tuple_element,I}}) -> {get_tuple_element,S,I,D}; norm({set,[],[S,D],{set_tuple_element,I}}) -> {set_tuple_element,S,D,I}; norm({set,[D1,D2],[S],get_list}) -> {get_list,S,D1,D2}; -norm({set,[D],[S|Puts],{alloc,R,{put_map,F}}}) -> - {put_map,F,S,D,R,{list,Puts}}; +norm({set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}) -> + {put_map,F,Op,S,D,R,{list,Puts}}; norm({set,[D],[S],{get_map_element,K,F}}) -> {get_map_element,F,S,K,D}; norm({set,[],[],remove_message}) -> remove_message; diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl index bbef75c219..1f720b94c3 100644 --- a/lib/compiler/src/beam_jump.erl +++ b/lib/compiler/src/beam_jump.erl @@ -527,7 +527,7 @@ ulbl({bs_init,Lbl,_,_,_,_}, Used) -> mark_used(Lbl, Used); ulbl({bs_put,Lbl,_,_}, Used) -> mark_used(Lbl, Used); -ulbl({put_map,Lbl,_Src,_Dst,_Live,_List}, Used) -> +ulbl({put_map,Lbl,_Op,_Src,_Dst,_Live,_List}, Used) -> mark_used(Lbl, Used); ulbl({get_map_element,Lbl,_Src,_Key,_Dst}, Used) -> mark_used(Lbl, Used); diff --git a/lib/compiler/src/beam_split.erl b/lib/compiler/src/beam_split.erl index bbd4695289..638a4826ea 100644 --- a/lib/compiler/src/beam_split.erl +++ b/lib/compiler/src/beam_split.erl @@ -49,9 +49,10 @@ split_block([{set,[R],As,{bif,N,{f,Lbl}=Fail}}|Is], Bl, Acc) when Lbl =/= 0 -> split_block([{set,[R],As,{alloc,Live,{gc_bif,N,{f,Lbl}=Fail}}}|Is], Bl, Acc) when Lbl =/= 0 -> split_block(Is, [], [{gc_bif,N,Fail,Live,As,R}|make_block(Bl, Acc)]); -split_block([{set,[D],[S|Puts],{alloc,R,{put_map,{f,Lbl}=Fail}}}|Is], Bl, Acc) - when Lbl =/= 0 -> - split_block(Is, [], [{put_map,Fail,S,D,R,{list,Puts}}|make_block(Bl, Acc)]); +split_block([{set,[D],[S|Puts],{alloc,R,{put_map,Op,{f,Lbl}=Fail}}}|Is], + Bl, Acc) when Lbl =/= 0 -> + split_block(Is, [], [{put_map,Fail,Op,S,D,R,{list,Puts}}| + make_block(Bl, Acc)]); split_block([{set,[D],[S],{get_map_element,K,{f,Lbl}=Fail}}|Is], Bl, Acc) when Lbl =/= 0 -> split_block(Is, [], [{get_map_element,Fail,S,K,D}|make_block(Bl, Acc)]); diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 37b08f43d3..97f84da08f 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -866,15 +866,10 @@ valfun_4({bs_final2,Src,Dst}, Vst0) -> assert_term(Src, Vst0), set_type_reg(binary, Dst, Vst0); %% Map instructions. -valfun_4({put_map,{f,Fail},Src,Dst,Live,{list,List}}, Vst0) -> - verify_live(Live, Vst0), - verify_y_init(Vst0), - [assert_term(Term, Vst0) || Term <- List], - assert_term(Src, Vst0), - Vst1 = heap_alloc(0, Vst0), - Vst2 = branch_state(Fail, Vst1), - Vst = prune_x_regs(Live, Vst2), - set_type_reg(term, Dst, Vst); +valfun_4({put_map_assoc,{f,Fail},Src,Dst,Live,{list,List}}, Vst) -> + verify_put_map(Fail, Src, Dst, Live, List, Vst); +valfun_4({put_map_exact,{f,Fail},Src,Dst,Live,{list,List}}, Vst) -> + verify_put_map(Fail, Src, Dst, Live, List, Vst); valfun_4({get_map_element,{f,Fail},Src,Key,Dst}, Vst0) -> assert_term(Src, Vst0), assert_term(Key, Vst0), @@ -883,6 +878,16 @@ valfun_4({get_map_element,{f,Fail},Src,Key,Dst}, Vst0) -> valfun_4(_, _) -> error(unknown_instruction). +verify_put_map(Fail, Src, Dst, Live, List, Vst0) -> + verify_live(Live, Vst0), + verify_y_init(Vst0), + [assert_term(Term, Vst0) || Term <- List], + assert_term(Src, Vst0), + Vst1 = heap_alloc(0, Vst0), + Vst2 = branch_state(Fail, Vst1), + Vst = prune_x_regs(Live, Vst2), + set_type_reg(term, Dst, Vst). + %% %% Common code for validating bs_get* instructions. %% diff --git a/lib/compiler/src/beam_z.erl b/lib/compiler/src/beam_z.erl index 8c6b0c916d..9953a48710 100644 --- a/lib/compiler/src/beam_z.erl +++ b/lib/compiler/src/beam_z.erl @@ -74,6 +74,10 @@ undo_rename({bs_init,F,{I,Extra,U,Flags},Live,[Sz,Src],Dst}) -> {I,F,Sz,Extra,Live,U,Src,Flags,Dst}; undo_rename({bs_init,_,bs_init_writable=I,_,_,_}) -> I; +undo_rename({put_map,Fail,assoc,S,D,R,L}) -> + {put_map_assoc,Fail,S,D,R,L}; +undo_rename({put_map,Fail,exact,S,D,R,L}) -> + {put_map_exact,Fail,S,D,R,L}; undo_rename({select,I,Reg,Fail,List}) -> {I,Reg,Fail,{list,List}}; undo_rename(I) -> I. diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab index 861acd3932..79b467f949 100755 --- a/lib/compiler/src/genop.tab +++ b/lib/compiler/src/genop.tab @@ -531,7 +531,8 @@ BEAM_FORMAT_NUMBER=0 # R16 -154: put_map/5 -155: is_map/2 -156: has_map_field/3 -157: get_map_element/4 +154: put_map_assoc/5 +155: put_map_exact/5 +156: is_map/2 +157: has_map_field/3 +158: get_map_element/4 diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index c1d555efac..db8ea04778 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -1488,7 +1488,7 @@ set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef, %% Now generate the complete code for constructing the binary. Code = cg_binary(PutCode, Target, Temp, Fail, MaxRegs, Le#l.a), {Sis++Code,Aft,St}; -set_cg([{var,R}], {map,SrcMap,Es}, Le, Vdb, Bef, +set_cg([{var,R}], {map,SrcMap,Es0}, Le, Vdb, Bef, #cg{in_catch=InCatch,bfail=Bfail}=St) -> Fail = {f,Bfail}, {Sis,Int0} = @@ -1501,24 +1501,41 @@ set_cg([{var,R}], {map,SrcMap,Es}, Le, Vdb, Bef, {var,SrcVar} -> fetch_var(SrcVar, Int0); _ -> SrcMap end, - - %% MapPairs in put_map must be sorted in ascending Key order. - %% Key literals must be unique when arriving here in v3_codegen. - - SortedEs = lists:sort(fun({_,{_T1,K1},_},{_,{_T2,K2},_}) -> - K1 =< K2 - end, Es), - List = flatmap(fun - ({map_pair_assoc,K,{var,V}}) -> [K,fetch_var(V, Int0)]; - ({map_pair_exact,K,{var,V}}) -> [K,fetch_var(V, Int0)]; - ({map_pair_assoc,K,E}) -> [K,E]; - ({map_pair_exact,K,E}) -> [K,E] - end, SortedEs), - Live = max_reg(Bef#sr.reg), + {Assoc,Exact} = + try + cg_map_pairs(Es0) + catch + throw:badarg -> + {[],[{{float,0.0},{atom,badarg}}, + {{integer,0},{atom,badarg}}]} + end, + F = fun ({K,{var,V}}) -> [K,fetch_var(V, Int0)]; + ({K,E}) -> [K,E] + end, + AssocList = flatmap(F, Assoc), + ExactList = flatmap(F, Exact), + Live0 = max_reg(Bef#sr.reg), Int1 = clear_dead(Int0, Le#l.i, Vdb), Aft = Bef#sr{reg=put_reg(R, Int1#sr.reg)}, Target = fetch_reg(R, Aft#sr.reg), - Code = [Line,{put_map,Fail,SrcReg,Target,Live,{list,List}}], + Code = [Line] ++ + case {AssocList,ExactList} of + {[_|_],[]} -> + [{put_map_assoc,Fail,SrcReg,Target,Live0,{list,AssocList}}]; + {[_|_],[_|_]} -> + Live = case Target of + {x,TargetX} when TargetX =:= Live0 -> + Live0 + 1; + _ -> + Live0 + end, + [{put_map_assoc,Fail,SrcReg,Target,Live0,{list,AssocList}}, + {put_map_exact,Fail,Target,Target,Live,{list,ExactList}}]; + {[],[_|_]} -> + [{put_map_exact,Fail,SrcReg,Target,Live0,{list,ExactList}}]; + {[],[]} -> + [{put_map_assoc,Fail,SrcReg,Target,Live0,{list,[]}}] + end, {Sis++Code,Aft,St}; set_cg([{var,R}], Con, Le, Vdb, Bef, St) -> %% Find a place for the return register first. @@ -1532,6 +1549,68 @@ set_cg([{var,R}], Con, Le, Vdb, Bef, St) -> end, {Ais,clear_dead(Int, Le#l.i, Vdb),St}. +%% cg_map_pairs(MapPairs) -> {Assoc,Exact} +%% Assoc = Exact = [{K,V}] +%% +%% Remove multiple assignments to the same key, and return +%% one list key-value list with all keys that may or may not exist +%% (Assoc), and one with keys that must exist (Exact). +%% + +cg_map_pairs(Es0) -> + Es = cg_map_pairs_1(Es0, 0), + R0 = sofs:relation(Es), + R1 = sofs:relation_to_family(R0), + R2 = sofs:to_external(R1), + + %% R2 is now [{KeyValue,[{Order,Op,OriginalKey,Value}]}] + R3 = [begin + %% The value for the last pair determines the value. + {_,_,_,V} = lists:last(Vs), + {Op,{_,SortOrder}=K} = map_pair_op_and_key(Vs), + {Op,{SortOrder,K,V}} + end || {_,Vs} <- R2], + + %% R3 is now [{Op,{Key,Value}}] + R = lists:sort(R3), + + %% R4 is now sorted with all alloc first in the list, followed by + %% all exact. + {Assoc,Exact} = lists:partition(fun({Op,_}) -> Op =:= assoc end, R), + {[{K,V} || {_,{_,K,V}} <- Assoc], + [{K,V} || {_,{_,K,V}} <- Exact]}. + +cg_map_pairs_1([{map_pair_assoc,{_,Kv}=K,V}|T], Order) -> + [{Kv,{Order,assoc,K,V}}|cg_map_pairs_1(T, Order+1)]; +cg_map_pairs_1([{map_pair_exact,{_,Kv}=K,V}|T], Order) -> + [{Kv,{Order,exact,K,V}}|cg_map_pairs_1(T, Order+1)]; +cg_map_pairs_1([], _) -> []. + +%% map_pair_op_and_key({_,Op,K,_}) -> {Operator,Key} +%% Determine the operator and key to use. Throw a 'badarg' +%% exception if there are contradictory exact updates. + +map_pair_op_and_key(L) -> + case [K || {_,exact,K,_} <- L] of + [K] -> + %% There is a single ':=' operator. Use that key. + {exact,K}; + [K|T] -> + %% There is more than one ':=' operator. All of them + %% must have the same key. + case lists:all(fun(E) -> E =:= K end, T) of + true -> + {exact,K}; + false -> + %% Some keys are different, e.g. 1 and 1.0. + throw(badarg) + end; + [] -> + %% Only '=>' operators. Use the first key in the list. + [{_,assoc,K,_}|_] = L, + {assoc,K} + end. + %%% %%% Code generation for constructing binaries. %%% -- cgit v1.2.3 From d4379900790b37498fefa237cd03ab2d5781bb6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 6 Nov 2013 08:58:32 +0100 Subject: compiler: Teach Maps understanding to inliner --- lib/compiler/src/cerl.erl | 107 +++++++++++++++++++++++++++++++++++++--- lib/compiler/src/cerl_trees.erl | 49 +++++++++++++++++- lib/compiler/src/core_lint.erl | 23 ++++++++- 3 files changed, 170 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index 4b74d60e9f..30bb4c5d96 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -120,7 +120,16 @@ update_c_bitstr/5, update_c_bitstr/6, ann_c_bitstr/5, ann_c_bitstr/6, is_c_bitstr/1, bitstr_val/1, bitstr_size/1, bitstr_bitsize/1, bitstr_unit/1, bitstr_type/1, - bitstr_flags/1]). + bitstr_flags/1, + + %% keep map exports here for now + map_es/1, + update_c_map_skel/2, + update_c_map_pair_assoc_skel/2, update_c_map_pair_exact_skel/2, + ann_c_map_skel/2, + ann_c_map_pair_assoc_skel/2, ann_c_map_pair_exact_skel/2, + map_pair_es/1, map_pair_assoc_es/1, map_pair_exact_es/1 + ]). -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]). @@ -250,8 +259,8 @@ -type ctype() :: 'alias' | 'apply' | 'binary' | 'bitrst' | 'call' | 'case' | 'catch' | 'clause' | 'cons' | 'fun' | 'let' | 'letrec' - | 'literal' | 'module' | 'primop' | 'receive' | 'seq' | 'try' - | 'tuple' | 'values' | 'var'. + | 'literal' | 'map' | 'module' | 'primop' | 'receive' | 'seq' + | 'try' | 'tuple' | 'values' | 'var'. -spec type(cerl()) -> ctype(). @@ -268,6 +277,9 @@ type(#c_fun{}) -> 'fun'; type(#c_let{}) -> 'let'; type(#c_letrec{}) -> letrec; type(#c_literal{}) -> literal; +type(#c_map{}) -> map; +type(#c_map_pair_assoc{}) -> map_pair_assoc; +type(#c_map_pair_exact{}) -> map_pair_exact; type(#c_module{}) -> module; type(#c_primop{}) -> primop; type(#c_receive{}) -> 'receive'; @@ -1557,6 +1569,58 @@ ann_make_list(_, [], Node) -> Node. +%% --------------------------------------------------------------------- +%% maps + +map_es(#c_map{es = Es}) -> + Es. + +map_pair_assoc_es(#c_map_pair_assoc{key=K,val=V}) -> [K,V]. +map_pair_exact_es(#c_map_pair_exact{key=K,val=V}) -> [K,V]. +map_pair_es(#c_map_pair_assoc{key=K,val=V}) -> [K,V]; +map_pair_es(#c_map_pair_exact{key=K,val=V}) -> [K,V]. + +update_c_map_pair_assoc_skel(Old, [K,V]) -> + #c_map_pair_assoc{key=K, val=V, anno = get_ann(Old)}. + +update_c_map_pair_exact_skel(Old, [K,V]) -> + #c_map_pair_exact{key=K, val=V, anno = get_ann(Old)}. + +ann_c_map_pair_assoc_skel(As, [K,V]) -> + #c_map_pair_assoc{key = K, val=V, anno = As}. + +ann_c_map_pair_exact_skel(As, [K,V]) -> + #c_map_pair_exact{key = K, val=V, anno = As}. + +%c_map_skel(Es) -> +% #c_map{es = Es}. +% + +ann_c_map(As, Es) -> + #c_map{es = Es, anno = As }. +%% TODO: when we have map literals use a variant of +%% case is_lit_list(Es) of +%% false -> +%% #c_map{es = Es, anno = As}; +%% true -> +%% #c_literal{val = maps:from_list(lit_list_vals(Es)), anno = As} +%% end. + + +ann_c_map_pair_assoc(As, [K,V]) -> + #c_map_pair_assoc{key = K, val=V, anno = As}. + +ann_c_map_pair_exact(As, [K,V]) -> + #c_map_pair_exact{key = K, val=V, anno = As}. + + +ann_c_map_skel(As, Es) -> + #c_map{es = Es, anno = As}. + +update_c_map_skel(Old, Es) -> + #c_map{es = Es, anno = get_ann(Old)}. + + %% --------------------------------------------------------------------- %% @spec c_tuple(Elements::[cerl()]) -> cerl() @@ -2945,6 +3009,10 @@ pat_vars(Node, Vs) -> pat_vars(cons_hd(Node), pat_vars(cons_tl(Node), Vs)); tuple -> pat_list_vars(tuple_es(Node), Vs); + map -> + pat_list_vars(map_es(Node), Vs); + map_pair_exact -> + pat_list_vars(map_pair_exact_es(Node), Vs); binary -> pat_list_vars(binary_segments(Node), Vs); bitstr -> @@ -3756,6 +3824,12 @@ is_data(#c_cons{}) -> true; is_data(#c_tuple{}) -> true; +is_data(#c_map{}) -> + true; +is_data(#c_map_pair_assoc{}) -> + true; +is_data(#c_map_pair_exact{}) -> + true; is_data(_) -> false. @@ -3801,7 +3875,13 @@ data_type(#c_literal{val = V}) -> data_type(#c_cons{}) -> cons; data_type(#c_tuple{}) -> - tuple. + tuple; +data_type(#c_map{}) -> + map; +data_type(#c_map_pair_assoc{}) -> + map_pair_assoc; +data_type(#c_map_pair_exact{}) -> + map_pair_exact. %% @spec data_es(Node::cerl()) -> [cerl()] @@ -3833,7 +3913,13 @@ data_es(#c_literal{val = V}) -> data_es(#c_cons{hd = H, tl = T}) -> [H, T]; data_es(#c_tuple{es = Es}) -> - Es. + Es; +data_es(#c_map{es=Es}) -> + Es; +data_es(#c_map_pair_assoc{key=K,val=V}) -> + [K,V]; +data_es(#c_map_pair_exact{key=K,val=V}) -> + [K,V]. %% @spec data_arity(Node::cerl()) -> integer() @@ -3890,7 +3976,10 @@ make_data(CType, Es) -> ann_make_data(As, {atomic, V}, []) -> #c_literal{val = V, anno = As}; ann_make_data(As, cons, [H, T]) -> ann_c_cons(As, H, T); -ann_make_data(As, tuple, Es) -> ann_c_tuple(As, Es). +ann_make_data(As, tuple, Es) -> ann_c_tuple(As, Es); +ann_make_data(As, map, Es) -> ann_c_map(As, Es); +ann_make_data(As, map_pair_assoc, Es) -> ann_c_map_pair_assoc(As, Es); +ann_make_data(As, map_pair_exact, Es) -> ann_c_map_pair_exact(As, Es). %% @spec update_data(Old::cerl(), Type::dtype(), @@ -4022,6 +4111,12 @@ subtrees(T) -> [[cons_hd(T)], [cons_tl(T)]]; tuple -> [tuple_es(T)]; + map -> + [map_es(T)]; + map_pair_assoc -> + [map_pair_assoc_es(T)]; + map_pair_exact -> + [map_pair_exact_es(T)]; 'let' -> [let_vars(T), [let_arg(T)], [let_body(T)]]; seq -> diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl index 1e3755025f..dc1cc606b3 100644 --- a/lib/compiler/src/cerl_trees.erl +++ b/lib/compiler/src/cerl_trees.erl @@ -55,7 +55,15 @@ update_c_let/4, update_c_letrec/3, update_c_module/5, update_c_primop/3, update_c_receive/4, update_c_seq/3, update_c_try/6, update_c_tuple/2, update_c_tuple_skel/2, - update_c_values/2, values_es/1, var_name/1]). + update_c_values/2, values_es/1, var_name/1, + + map_es/1, + update_c_map_skel/2, + update_c_map_pair_assoc_skel/2, update_c_map_pair_exact_skel/2, + ann_c_map_skel/2, + ann_c_map_pair_assoc_skel/2, ann_c_map_pair_exact_skel/2, + map_pair_assoc_es/1, map_pair_exact_es/1 + ]). %% --------------------------------------------------------------------- @@ -129,6 +137,12 @@ map_1(F, T) -> map(F, cons_tl(T))); tuple -> update_c_tuple_skel(T, map_list(F, tuple_es(T))); + map -> + update_c_map_skel(T, map_list(F, map_es(T))); + map_pair_assoc -> + update_c_map_pair_assoc_skel(T, map_list(F, map_pair_assoc_es(T))); + map_pair_exact -> + update_c_map_pair_exact_skel(T, map_list(F, map_pair_exact_es(T))); 'let' -> update_c_let(T, map_list(F, let_vars(T)), map(F, let_arg(T)), @@ -235,6 +249,12 @@ fold_1(F, S, T) -> fold(F, fold(F, S, cons_hd(T)), cons_tl(T)); tuple -> fold_list(F, S, tuple_es(T)); + map -> + fold_list(F, S, map_es(T)); + map_pair_assoc -> + fold_list(F, S, map_pair_assoc_es(T)); + map_pair_exact -> + fold_list(F, S, map_pair_exact_es(T)); 'let' -> fold(F, fold(F, fold_list(F, S, let_vars(T)), let_arg(T)), @@ -349,6 +369,15 @@ mapfold(F, S0, T) -> tuple -> {Ts, S1} = mapfold_list(F, S0, tuple_es(T)), F(update_c_tuple_skel(T, Ts), S1); + map -> + {Ts, S1} = mapfold_list(F, S0, map_es(T)), + F(update_c_map_skel(T, Ts), S1); + map_pair_assoc -> + {Ts, S1} = mapfold_list(F, S0, map_pair_assoc_es(T)), + F(update_c_map_pair_assoc_skel(T,Ts), S1); + map_pair_exact -> + {Ts, S1} = mapfold_list(F, S0, map_pair_exact_es(T)), + F(update_c_map_pair_exact_skel(T,Ts), S1); 'let' -> {Vs, S1} = mapfold_list(F, S0, let_vars(T)), {A, S2} = mapfold(F, S1, let_arg(T)), @@ -488,6 +517,12 @@ variables(T, S) -> variables(cons_tl(T), S)); tuple -> vars_in_list(tuple_es(T), S); + map -> + vars_in_list(map_es(T), S); + map_pair_assoc -> + vars_in_list(map_pair_assoc_es(T), S); + map_pair_exact -> + vars_in_list(map_pair_exact_es(T), S); 'let' -> Vs = variables(let_body(T), S), Vs1 = var_list_names(let_vars(T)), @@ -688,6 +723,18 @@ label(T, N, Env) -> {Ts, N1} = label_list(tuple_es(T), N, Env), {As, N2} = label_ann(T, N1), {ann_c_tuple_skel(As, Ts), N2}; + map -> + {Ts, N1} = label_list(map_es(T), N, Env), + {As, N2} = label_ann(T, N1), + {ann_c_map_skel(As, Ts), N2}; + map_pair_assoc -> + {Ts, N1} = label_list(map_pair_assoc_es(T), N, Env), + {As, N2} = label_ann(T, N1), + {ann_c_map_pair_assoc_skel(As, Ts), N2}; + map_pair_exact -> + {Ts, N1} = label_list(map_pair_exact_es(T), N, Env), + {As, N2} = label_ann(T, N1), + {ann_c_map_pair_exact_skel(As, Ts), N2}; 'let' -> {A, N1} = label(let_arg(T), N, Env), {Vs, N2, Env1} = label_vars(let_vars(T), N1, Env), diff --git a/lib/compiler/src/core_lint.erl b/lib/compiler/src/core_lint.erl index 67d37ff1fc..185193e45d 100644 --- a/lib/compiler/src/core_lint.erl +++ b/lib/compiler/src/core_lint.erl @@ -254,6 +254,12 @@ gexpr(#c_cons{hd=H,tl=T}, Def, _Rt, St) -> gexpr_list([H,T], Def, St); gexpr(#c_tuple{es=Es}, Def, _Rt, St) -> gexpr_list(Es, Def, St); +gexpr(#c_map{es=Es}, Def, _Rt, St) -> + gexpr_list(Es, Def, St); +gexpr(#c_map_pair_assoc{key=K,val=V}, Def, _Rt, St) -> + gexpr_list([K,V], Def, St); +gexpr(#c_map_pair_exact{key=K,val=V}, Def, _Rt, St) -> + gexpr_list([K,V], Def, St); gexpr(#c_binary{segments=Ss}, Def, _Rt, St) -> gbitstr_list(Ss, Def, St); gexpr(#c_seq{arg=Arg,body=B}, Def, Rt, St0) -> @@ -278,6 +284,7 @@ gexpr(#c_case{arg=Arg,clauses=Cs}, Def, Rt, St0) -> St1 = gbody(Arg, Def, PatCount, St0), clauses(Cs, Def, PatCount, Rt, St1); gexpr(_Core, _, _, St) -> + %%io:fwrite("clint gexpr: ~p~n", [_Core]), add_error({illegal_guard,St#lint.func}, St). %% gexpr_list([Expr], Defined, State) -> State. @@ -303,6 +310,12 @@ expr(#c_cons{hd=H,tl=T}, Def, _Rt, St) -> expr_list([H,T], Def, St); expr(#c_tuple{es=Es}, Def, _Rt, St) -> expr_list(Es, Def, St); +expr(#c_map{es=Es}, Def, _Rt, St) -> + expr_list(Es, Def, St); +expr(#c_map_pair_assoc{key=K,val=V},Def,_Rt,St) -> + expr_list([K,V],Def,St); +expr(#c_map_pair_exact{key=K,val=V},Def,_Rt,St) -> + expr_list([K,V],Def,St); expr(#c_binary{segments=Ss}, Def, _Rt, St) -> bitstr_list(Ss, Def, St); expr(#c_fun{vars=Vs,body=B}, Def, Rt, St0) -> @@ -355,7 +368,7 @@ expr(#c_try{arg=A,vars=Vs,body=B,evars=Evs,handler=H}, Def, Rt, St0) -> {Ens,St5} = variable_list(Evs, St4), body(H, union(Ens, Def), Rt, St5); expr(_Other, _, _, St) -> - %%io:fwrite("clint: ~p~n", [_Other]), + %%io:fwrite("clint expr: ~p~n", [_Other]), add_error({illegal_expr,St#lint.func}, St). %% expr_list([Expr], Defined, State) -> State. @@ -454,13 +467,19 @@ pattern(#c_cons{hd=H,tl=T}, Def, Ps, St) -> pattern_list([H,T], Def, Ps, St); pattern(#c_tuple{es=Es}, Def, Ps, St) -> pattern_list(Es, Def, Ps, St); +pattern(#c_map{es=Es}, Def, Ps, St) -> + pattern_list(Es, Def, Ps, St); +pattern(#c_map_pair_exact{key=K,val=V},Def,Ps,St) -> + pattern_list([K,V],Def,Ps,St); pattern(#c_binary{segments=Ss}, Def, Ps, St0) -> St = pat_bin_tail_check(Ss, St0), pat_bin(Ss, Def, Ps, St); pattern(#c_alias{var=V,pat=P}, Def, Ps, St0) -> {Vvs,St1} = variable(V, Ps, St0), pattern(P, Def, union(Vvs, Ps), St1); -pattern(_, _, Ps, St) -> {Ps,add_error({not_pattern,St#lint.func}, St)}. +pattern(_Other, _, Ps, St) -> + %%io:fwrite("clint pattern: ~p~n", [_Other]), + {Ps,add_error({not_pattern,St#lint.func}, St)}. pat_var(N, _Def, Ps, St) -> case is_element(N, Ps) of -- cgit v1.2.3 From 1014059ea438798960cf858e6f37b16e321152ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 5 Nov 2013 15:42:27 +0100 Subject: compiler: Test Maps inlining --- lib/compiler/test/compile_SUITE.erl | 11 ++-- lib/compiler/test/inline_SUITE.erl | 3 +- .../test/inline_SUITE_data/maps_inline_test.erl | 60 ++++++++++++++++++++++ 3 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 lib/compiler/test/inline_SUITE_data/maps_inline_test.erl (limited to 'lib') diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index 4ec75d015e..de35ebc7bd 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -415,11 +415,11 @@ encrypted_abstr(Config) when is_list(Config) -> ?line {Simple,Target} = files(Config, "encrypted_abstr"), Res = case has_crypto() of - no -> + false -> %% No crypto. ?line encrypted_abstr_no_crypto(Simple, Target), {comment,"The crypto application is missing or broken"}; - yes -> + true -> %% Simulate not having crypto by removing %% the crypto application from the path. ?line OldPath = code:get_path(), @@ -511,6 +511,7 @@ write_crypt_file(Contents0) -> ok = file:write_file(".erlang.crypt", Contents). encrypted_abstr_no_crypto(Simple, Target) -> + io:format("simpe: ~p~n", [Simple]), ?line TargetDir = filename:dirname(Target), ?line Key = "ablurf123BX#$;3", ?line error = compile:file(Simple, @@ -525,11 +526,11 @@ verify_abstract(Target) -> has_crypto() -> try crypto:start(), - crypto:info(), + <<_,_,_,_,_>> = crypto:rand_bytes(5), crypto:stop(), - yes + true catch - error:_ -> no + error:_ -> false end. install_crypto_key(Key) -> diff --git a/lib/compiler/test/inline_SUITE.erl b/lib/compiler/test/inline_SUITE.erl index e5c2d4f73a..47851e680b 100644 --- a/lib/compiler/test/inline_SUITE.erl +++ b/lib/compiler/test/inline_SUITE.erl @@ -37,7 +37,7 @@ all() -> groups() -> [{p,test_lib:parallel(), [attribute,bsdecode,bsdes,barnes2,decode1,smith,fname, - itracer,pseudoknot,comma_splitter,lists,really_inlined,otp_7223, + itracer,pseudoknot,maps_inline_test,comma_splitter,lists,really_inlined,otp_7223, coverage]}]. init_per_suite(Config) -> @@ -85,6 +85,7 @@ attribute(Config) when is_list(Config) -> ?comp(pseudoknot). ?comp(comma_splitter). ?comp(fname). +?comp(maps_inline_test). try_inline(Mod, Config) -> Node = ?config(testing_node, Config), diff --git a/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl b/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl new file mode 100644 index 0000000000..87a8f86b47 --- /dev/null +++ b/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl @@ -0,0 +1,60 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(maps_inline_test). + +-export([?MODULE/0]). + +?MODULE() -> + 21 = mval(#{val => 1}) + + mval(#{val => 2}) + + mval(#{val => 3}) + + mval(#{val => 4}) + + mval(#{val => 5}) + + mval(#{val => 6}), + + 21 = gval(#{id => 1}) + + gval(#{id => 2}) + + gval(#{id => 3}) + + gval(#{id => 4}) + + gval(#{id => 5}) + + gval(#{id => 6}), + + 21 = sval(#{id => 1}) + + sval(#{id => 2}) + + sval(#{id => 3}) + + sval(#{id => 4}) + + sval(#{id => 5}) + + sval(#{id => 6}). + +mval(#{val := V}) -> V. + +sval(#{id := 1}) -> 6; +sval(#{id := 2}) -> 5; +sval(#{id := 3}) -> 4; +sval(#{id := 4}) -> 3; +sval(#{id := 5}) -> 2; +sval(#{id := 6}) -> 1. + +gval(M) when is_map(M) andalso M =:= #{ id => 1} -> 1; +gval(M) when is_map(M) andalso M =:= #{ id => 2} -> 4; +gval(M) when is_map(M) andalso M =:= #{ id => 3} -> 2; +gval(M) when is_map(M) andalso M =:= #{ id => 4} -> 5; +gval(M) when is_map(M) andalso M =:= #{ id => 5} -> 3; +gval(M) when is_map(M) andalso M =:= #{ id => 6} -> 6. -- cgit v1.2.3 From f3ea2236bc8d11dd09b7eaaec3f175361e4771d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Sat, 26 Oct 2013 16:04:29 +0200 Subject: compiler: Fix get_map_element bug with allocate The instruction get_map_element has a faillabel so you may not use the instruction within a allocate/deallocate block. --- lib/compiler/src/beam_block.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl index d5f2ffc444..3723cc19e1 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -240,6 +240,7 @@ move_allocates_2(Alloc, [], Acc) -> alloc_may_pass({set,_,_,{alloc,_,_}}) -> false; alloc_may_pass({set,_,_,{set_tuple_element,_}}) -> false; +alloc_may_pass({set,_,_,{get_map_element,_,_}}) -> false; alloc_may_pass({set,_,_,put_list}) -> false; alloc_may_pass({set,_,_,put}) -> false; alloc_may_pass({set,_,_,_}) -> true. -- cgit v1.2.3 From 03588c989909b9bca00a3d685f7486d567a55e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 30 Oct 2013 17:07:22 +0100 Subject: compiler: Fix v3_core for map update syntax Make map update expressions safe, i.e. (foo())#{ k1 := 1 } --- lib/compiler/src/v3_core.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index b33799275b..b5fb65ff08 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -493,11 +493,11 @@ expr({map,L,Es0}, St0) -> {Es1,Eps,St1} = map_pair_list(Es0, St0), A = lineno_anno(L, St1), {#c_map{anno=A,es=Es1},Eps,St1}; -expr({map,L,{var,Vl,Map},Es0}, St0) -> - {Es1,Eps,St1} = map_pair_list(Es0, St0), - A = lineno_anno(L, St1), - Av = lineno_anno(Vl, St1), - {#c_map{anno=A,var=#c_var{anno=Av,name=Map},es=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}; expr({bin,L,Es0}, St0) -> try expr_bin(Es0, lineno_anno(L, St0), St0) of {_,_,_}=Res -> Res @@ -1505,7 +1505,7 @@ pattern({cons,L,H,T}, St) -> pattern({tuple,L,Ps}, St) -> ann_c_tuple(lineno_anno(L, St), pattern_list(Ps, St)); pattern({map,L,Ps}, St) -> - #c_map{anno=lineno_anno(L, St),es=sort(pattern_list(Ps, St))}; + #c_map{anno=lineno_anno(L, St), es=sort(pattern_list(Ps, St))}; pattern({map_field_exact,L,K,V}, St) -> %% FIXME: Better way to construct literals? or missing case %% {Key,_,_} = expr(K, St), -- cgit v1.2.3 From 05b06b217755bd0903677d650b364084f3a063ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 4 Nov 2013 17:29:58 +0100 Subject: compiler: Teach Maps to beam_disasm --- lib/compiler/src/beam_disasm.erl | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl index 1a8bbcee22..e0d0d0fd1d 100644 --- a/lib/compiler/src/beam_disasm.erl +++ b/lib/compiler/src/beam_disasm.erl @@ -365,6 +365,10 @@ disasm_instr(B, Bs, Atoms, Literals) -> disasm_select_inst(select_val, Bs, Atoms, Literals); select_tuple_arity -> disasm_select_inst(select_tuple_arity, Bs, Atoms, Literals); + put_map_assoc -> + disasm_map_inst(put_map_assoc, Bs, Atoms, Literals); + put_map_exact -> + disasm_map_inst(put_map_exact, Bs, Atoms, Literals); _ -> try decode_n_args(Arity, Bs, Atoms, Literals) of {Args, RestBs} -> @@ -395,6 +399,17 @@ disasm_select_inst(Inst, Bs, Atoms, Literals) -> {List, RestBs} = decode_n_args(Len, Bs4, Atoms, Literals), {{Inst, [X,F,{Z,U,List}]}, RestBs}. +disasm_map_inst(Inst, Bs0, Atoms, Literals) -> + {F, Bs1} = decode_arg(Bs0, Atoms, Literals), + {S, Bs2} = decode_arg(Bs1, Atoms, Literals), + {X, Bs3} = decode_arg(Bs2, Atoms, Literals), + {N, Bs4} = decode_arg(Bs3, Atoms, Literals), + {Z, Bs5} = decode_arg(Bs4, Atoms, Literals), + {U, Bs6} = decode_arg(Bs5, Atoms, Literals), + {u, Len} = U, + {List, RestBs} = decode_n_args(Len, Bs6, Atoms, Literals), + {{Inst, [F,S,X,N,{Z,U,List}]}, RestBs}. + %%----------------------------------------------------------------------- %% decode_arg([Byte]) -> {Arg, [Byte]} %% @@ -421,7 +436,7 @@ decode_arg([B|Bs]) -> decode_arg([B|Bs0], Atoms, Literals) -> Tag = decode_tag(B band 2#111), - ?NO_DEBUG('Tag = ~p, B = ~p, Bs = ~p~n', [Tag, B, Bs]), + ?NO_DEBUG('Tag = ~p, B = ~p, Bs = ~p~n', [Tag, B, Bs0]), case Tag of z -> decode_z_tagged(Tag, B, Bs0, Literals); @@ -1118,6 +1133,27 @@ resolve_inst({recv_set,[Lbl]},_,_,_) -> resolve_inst({line,[Index]},_,_,_) -> {line,resolve_arg(Index)}; +%% +%% R17A. +%% +resolve_inst({put_map_assoc,Args},_,_,_) -> + [FLbl,Src,Dst,{u,N},{{z,1},{u,_Len},List0}] = Args, + List = resolve_args(List0), + {put_map_assoc,FLbl,Src,Dst,N,{list,List}}; + +resolve_inst({put_map_exact,Args},_,_,_) -> + [FLbl,Src,Dst,{u,N},{{z,1},{u,_Len},List0}] = Args, + List = resolve_args(List0), + {put_map_exact,FLbl,Src,Dst,N,{list,List}}; + +resolve_inst({is_map,Args0},_,_,_) -> + [FLbl|Args] = resolve_args(Args0), + {test, is_map, FLbl, Args}; + +resolve_inst({get_map_element,Args},_,_,_) -> + [FLbl,Src,Key,Dst] = resolve_args(Args), + {get_map_element,FLbl,Src,Key,Dst}; + %% %% Catches instructions that are not yet handled. %% -- cgit v1.2.3 From b10a3b3aa5efa08488623ecba1e24a0de48d4b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 12 Nov 2013 19:05:22 +0100 Subject: compiler: Format stacktrace on error --- lib/compiler/src/compile.erl | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 3db7ffc4d2..0bb4de6f17 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -230,12 +230,25 @@ format_error({undef_parse_transform,M}) -> format_error({core_transform,M,R}) -> io_lib:format("error in core transform '~s': ~tp", [M, R]); format_error({crash,Pass,Reason}) -> - io_lib:format("internal error in ~p;\ncrash reason: ~tp", [Pass,Reason]); + io_lib:format("internal error in ~p;\ncrash reason: ~ts", [Pass,format_error_reason(Reason)]); format_error({bad_return,Pass,Reason}) -> - io_lib:format("internal error in ~p;\nbad return value: ~tp", [Pass,Reason]); + io_lib:format("internal error in ~p;\nbad return value: ~ts", [Pass,format_error_reason(Reason)]); format_error({module_name,Mod,Filename}) -> - io_lib:format("Module name '~s' does not match file name '~ts'", - [Mod,Filename]). + io_lib:format("Module name '~s' does not match file name '~ts'", [Mod,Filename]). + +format_error_reason({Reason, Stack}) when is_list(Stack) -> + StackFun = fun + (escript, run, 2) -> true; + (escript, start, 1) -> true; + (init, start_it, 1) -> true; + (init, start_em, 1) -> true; + (_Mod, _Fun, _Arity) -> false + end, + FormatFun = fun (Term, _) -> io_lib:format("~tp", [Term]) end, + [io_lib:format("~tp", [Reason]),"\n\n", + lib:format_stacktrace(1, erlang:get_stacktrace(), StackFun, FormatFun)]; +format_error_reason(Reason) -> + io_lib:format("~tp", [Reason]). %% The compile state record. -record(compile, {filename="" :: file:filename(), -- cgit v1.2.3 From 64e5677c883990e1d5b5666d857413293910972a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 12 Nov 2013 12:19:30 +0100 Subject: compiler: Add Maps types to cerl --- lib/compiler/src/cerl.erl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index 30bb4c5d96..c6bf046c4d 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -154,6 +154,9 @@ -type c_let() :: #c_let{}. -type c_letrec() :: #c_letrec{}. -type c_literal() :: #c_literal{}. +-type c_map() :: #c_map{}. +-type c_map_pair_assoc() :: #c_map_pair_assoc{}. +-type c_map_pair_exact() :: #c_map_pair_exact{}. -type c_module() :: #c_module{}. -type c_primop() :: #c_primop{}. -type c_receive() :: #c_receive{}. @@ -164,9 +167,10 @@ -type c_var() :: #c_var{}. -type cerl() :: c_alias() | c_apply() | c_binary() | c_bitstr() - | c_call() | c_case() | c_catch() | c_clause() | c_cons() + | c_call() | c_case() | c_catch() | c_clause() | c_cons() | c_fun() | c_let() | c_letrec() | c_literal() - | c_module() | c_primop() | c_receive() | c_seq() + | c_map() | c_map_pair_assoc() | c_map_pair_exact() + | c_module() | c_primop() | c_receive() | c_seq() | c_try() | c_tuple() | c_values() | c_var(). %% ===================================================================== @@ -1572,6 +1576,8 @@ ann_make_list(_, [], Node) -> %% --------------------------------------------------------------------- %% maps +-spec map_es(c_map()) -> [cerl()]. + map_es(#c_map{es = Es}) -> Es. -- cgit v1.2.3 From b6f6b19cc114af6f88458624c1470f0f70e24d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 12 Nov 2013 19:18:22 +0100 Subject: compiler: Rename map pattern to proper name --- lib/compiler/src/v3_kernel.erl | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index 14e776fd3c..a5a94c1294 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -668,14 +668,10 @@ pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) -> pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) -> {Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0), {#k_map{anno=A,es=Kes},Osub1,St1}; -pattern(#c_map_pair_assoc{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) -> - {Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0), - {Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1), - {#k_map_pair_assoc{anno=A,key=Kk,val=Kv},Osub2,St2}; pattern(#c_map_pair_exact{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) -> {Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0), {Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1), - {#k_map_pair_assoc{anno=A,key=Kk,val=Kv},Osub2,St2}; + {#k_map_pair_exact{anno=A,key=Kk,val=Kv},Osub2,St2}; pattern(#c_binary{anno=A,segments=Cv}, Isub, Osub0, St0) -> {Kv,Osub1,St1} = pattern_bin(Cv, Isub, Osub0, St0), {#k_binary{anno=A,segs=Kv},Osub1,St1}; @@ -1351,9 +1347,10 @@ get_match(#k_tuple{es=Es}, St0) -> {#k_tuple{es=Mes},Mes,St1}; get_match(#k_map{es=Es0}, St0) -> {Mes,St1} = new_vars(length(Es0), St0), - {Es,_} = mapfoldl(fun(#k_map_pair_assoc{}=Pair, [V|Vs]) -> - {Pair#k_map_pair_assoc{val=V},Vs} - end, Mes, Es0), + {Es,_} = mapfoldl(fun + (#k_map_pair_exact{}=Pair, [V|Vs]) -> + {Pair#k_map_pair_exact{val=V},Vs} + end, Mes, Es0), {#k_map{es=Es},Mes,St1}; get_match(M, St) -> {M,[],St}. @@ -1373,7 +1370,7 @@ new_clauses(Cs0, U, St) -> [N|As]; #k_map{es=Es} -> Vals = [V || - #k_map_pair_assoc{val=V} <- Es], + #k_map_pair_exact{val=V} <- Es], Vals ++ As; _Other -> As @@ -1475,7 +1472,7 @@ arg_val(Arg, C) -> end; #k_map{es=Es} -> Keys = [begin - #k_map_pair_assoc{key=#k_literal{val=Key}} = Pair, + #k_map_pair_exact{key=#k_literal{val=Key}} = Pair, Key end || Pair <- Es], %% multiple keys may have the same name @@ -1893,7 +1890,7 @@ pat_vars(#k_tuple{es=Es}) -> pat_list_vars(Es); pat_vars(#k_map{es=Es}) -> pat_list_vars(Es); -pat_vars(#k_map_pair_assoc{val=V}) -> +pat_vars(#k_map_pair_exact{val=V}) -> pat_vars(V). pat_list_vars(Ps) -> -- cgit v1.2.3 From e9a3d0b6533b72cba3d4e7ea129f835e80bd2647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 2 Oct 2013 16:40:33 +0200 Subject: stdlib: Teach erl_eval Maps ':=' exact operator --- lib/stdlib/src/erl_eval.erl | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl index 335134d1f0..fcd1945c63 100644 --- a/lib/stdlib/src/erl_eval.erl +++ b/lib/stdlib/src/erl_eval.erl @@ -241,18 +241,26 @@ expr({record,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs) -> erlang:raise(error, {undef_record,Name}, stacktrace()); %% map -expr({map_field,_,EK, EV}, Bs0, Lf, Ef, RBs) -> +expr({map_field_assoc,_,EK, EV}, Bs0, Lf, Ef, RBs) -> {value,K,Bs1} = expr(EK, Bs0, Lf, Ef, none), {value,V,Bs2} = expr(EV, Bs0, Lf, Ef, none), - ret_expr({K,V}, merge_bindings(Bs1,Bs2), RBs); + ret_expr({map_assoc,K,V}, merge_bindings(Bs1,Bs2), RBs); +expr({map_field_exact,_,EK, EV}, Bs0, Lf, Ef, RBs) -> + {value,K,Bs1} = expr(EK, Bs0, Lf, Ef, none), + {value,V,Bs2} = expr(EV, Bs0, Lf, Ef, none), + ret_expr({map_exact,K,V}, merge_bindings(Bs1,Bs2), RBs); expr({map,_, Binding,Es}, Bs0, Lf, Ef, RBs) -> {value, Map0, Bs1} = expr(Binding, Bs0, Lf, Ef, RBs), {Vs,Bs} = expr_list(Es, Bs1, Lf, Ef), - ret_expr(lists:foldl(fun({K,V}, Mi) -> map:put(K,V,Mi) end, Map0, Vs), Bs, RBs); + ret_expr(lists:foldl(fun + ({map_assoc,K,V}, Mi) -> map:put(K,V,Mi); + ({map_exact,K,V}, Mi) -> map:update(K,V,Mi) + end, Map0, Vs), Bs, RBs); expr({map,_,Es}, Bs0, Lf, Ef, RBs) -> {Vs,Bs} = expr_list(Es, Bs0, Lf, Ef), - ret_expr(lists:foldl(fun({K,V}, Mi) -> map:put(K,V,Mi) end, map:new(), Vs), Bs, RBs); - + ret_expr(lists:foldl(fun + ({map_assoc,K,V}, Mi) -> map:put(K,V,Mi) + end, map:new(), Vs), Bs, RBs); expr({block,_,Es}, Bs, Lf, Ef, RBs) -> exprs(Es, Bs, Lf, Ef, RBs); @@ -1137,9 +1145,12 @@ match_tuple([E|Es], Tuple, I, Bs0, BBs) -> match_tuple([], _, _, Bs, _BBs) -> {match,Bs}. -match_map([{map_field, _, K, V}|Fs], Map, Bs0, BBs) -> - Vm = try map:get(element(3,K),Map) - catch error:_ -> throw(nomatch) +match_map([{map_field_exact, _, K, V}|Fs], Map, Bs0, BBs) -> + Vm = try + {value, Ke, _} = expr(K, new_bindings()), + map:get(Ke,Map) + catch error:_ -> + throw(nomatch) end, {match, Bs} = match1(V, Vm, Bs0, BBs), match_map(Fs, Map, Bs, BBs); -- cgit v1.2.3 From f00675d3c682f53824e23f1599cbb09ff2d57daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 9 Oct 2013 16:37:58 +0200 Subject: stdlib: Deny variables as keys and disallow ':=' in map construction In the current iteration of Maps we should deny *any* variables in Map keys. --- lib/stdlib/src/erl_lint.erl | 57 +++++++++++++++++++++++++++++++++++--------- lib/stdlib/src/erl_parse.yrl | 2 -- 2 files changed, 46 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 86ebe82acc..a41bcb91cc 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_variable,K}) -> + io_lib:format("illegal use of variable ~w in map",[K]); format_error(illegal_bin_pattern) -> "binary patterns cannot be matched in parallel using '='"; format_error(illegal_expr) -> "illegal expression"; @@ -232,6 +234,9 @@ format_error({illegal_guard_local_call, {F,A}}) -> io_lib:format("call to local/imported function ~w/~w is illegal in guard", [F,A]); format_error(illegal_guard_expr) -> "illegal guard expression"; +%% --- maps --- +format_error(illegal_map_construction) -> + "only association operators '=>' are allowed in map construction"; %% --- records --- format_error({undefined_record,T}) -> io_lib:format("record ~w undefined", [T]); @@ -1368,11 +1373,17 @@ pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) -> pattern_list(Ps, Vt, Old, Bvt, St); pattern({map,_Line,Ps}, Vt, Old, Bvt, St) -> pattern_list(Ps, Vt, Old, Bvt, St); -pattern({map_field_assoc,_Line,_,_}=Pat, _, _, _, St) -> - {[],[],add_error(element(2, Pat), illegal_pattern, St)}; -pattern({map_field_exact,_Line,_,P}, Vt, Old, Bvt0, St0) -> - {Pvt,Bvt,St1} = pattern(P, Vt, Old, Bvt0, St0), - {Pvt,Bvt,St1}; +pattern({map_field_assoc,Line,_,_}, _, _, _, St) -> + {[],[],add_error(Line, illegal_pattern, St)}; +pattern({map_field_exact,Line,KP,VP}, Vt, Old, Bvt0, St0) -> + %% if the key pattern has variables we should fail + case expr(KP,[],St0) of + {[],_} -> + pattern(VP, Vt, Old, Bvt0, St0); + {[Var|_],_} -> + %% found variables in key expression + {Vt,Old,add_error(Line,{illegal_map_key_variable,element(1,Var)},St0)} + end; %%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) -> @@ -1985,14 +1996,24 @@ expr({bc,_Line,E,Qs}, Vt, St) -> handle_comprehension(E, Qs, Vt, St); expr({tuple,_Line,Es}, Vt, St) -> expr_list(Es, Vt, St); -expr({map,_Line,Es}, Vt, St) -> - expr_list(Es, Vt, St); +expr({map,Line,Es}, Vt, St) -> + {Rvt,St1} = expr_list(Es,Vt,St), + case is_valid_map_construction(Es) of + true -> {Rvt,St1}; + false -> {[],add_error(Line,illegal_map_construction,St1)} + end; expr({map,_Line,Src,Es}, Vt, St) -> expr_list([Src|Es], Vt, St); -expr({map_field_assoc,_Line,K,V}, Vt, St) -> - expr_list([K,V], Vt, St); -expr({map_field_exact,_Line,K,V}, Vt, St) -> - expr_list([K,V], Vt, St); +expr({map_field_assoc,Line,K,V}, Vt, St) -> + case is_valid_map_key(K,St) of + true -> expr_list([K,V], Vt, St); + {false,Var} -> {[],add_error(Line,{illegal_map_key_variable,Var},St)} + end; +expr({map_field_exact,Line,K,V}, Vt, St) -> + case is_valid_map_key(K,St) of + true -> expr_list([K,V], Vt, St); + {false,Var} -> {[],add_error(Line,{illegal_map_key_variable,Var},St)} + end; expr({record_index,Line,Name,Field}, _Vt, St) -> check_record(Line, Name, St, fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end); @@ -2252,6 +2273,20 @@ is_valid_call(Call) -> _ -> true end. +%% check_map_construction +%% Only #{ K => V }, i.e. assoc is a valid construction +is_valid_map_construction([{map_field_assoc,_,_,_}|Es]) -> + is_valid_map_construction(Es); +is_valid_map_construction([]) -> true; +is_valid_map_construction(_) -> false. + +is_valid_map_key(K,St) -> + case expr(K,[],St) of + {[],_} -> true; + {[Var|_],_} -> + {false,element(1,Var)} + end. + %% record_def(Line, RecordName, [RecField], State) -> State. %% Add a record definition if it does not already exist. Normalise %% so that all fields have explicit initial value. diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index e9a813e098..c04a1aeb89 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -335,8 +335,6 @@ map_expr -> expr_max '#' map_tuple : {map, ?line('$2'),'$1','$3'}. map_expr -> map_expr '#' map_tuple : {map, ?line('$2'),'$1','$3'}. -map_expr -> map_expr '.' atom : - {map_field, ?line('$2'),'$1','$3'}. map_tuple -> '{' '}' : []. map_tuple -> '{' map_fields '}' : '$2'. -- cgit v1.2.3 From 34b043379ee6952136db98c782f69e670c93f9fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 17 Oct 2013 15:01:52 +0200 Subject: stdlib: Update erl_scan_SUITE for ':=' and '=>' tokens --- lib/stdlib/test/erl_scan_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl index e628f7248d..447e159cd4 100644 --- a/lib/stdlib/test/erl_scan_SUITE.erl +++ b/lib/stdlib/test/erl_scan_SUITE.erl @@ -224,8 +224,8 @@ atoms() -> punctuations() -> L = ["<<", "<-", "<=", "<", ">>", ">=", ">", "->", "--", - "-", "++", "+", "=:=", "=/=", "=<", "==", "=", "/=", - "/", "||", "|", ":-", "::", ":"], + "-", "++", "+", "=:=", "=/=", "=<", "=>", "==", "=", "/=", + "/", "||", "|", ":=", ":-", "::", ":"], %% One token at a time: [begin W = list_to_atom(S), -- cgit v1.2.3 From c3a45ed9ae4f84018a63df18666d0752beb8458a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 10 Oct 2013 20:13:49 +0200 Subject: stdlib: Add Map module The map type is set to term. --- lib/stdlib/src/Makefile | 1 + lib/stdlib/src/map.erl | 215 ++++++++++++++++++++++++++++++++++++++++++ lib/stdlib/src/stdlib.app.src | 1 + 3 files changed, 217 insertions(+) create mode 100644 lib/stdlib/src/map.erl (limited to 'lib') diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile index f3387d669b..376083c7d6 100644 --- a/lib/stdlib/src/Makefile +++ b/lib/stdlib/src/Makefile @@ -91,6 +91,7 @@ MODULES= \ lib \ lists \ log_mf_h \ + map \ math \ ms_transform \ otp_internal \ diff --git a/lib/stdlib/src/map.erl b/lib/stdlib/src/map.erl new file mode 100644 index 0000000000..6257a90180 --- /dev/null +++ b/lib/stdlib/src/map.erl @@ -0,0 +1,215 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(map). + +-export([ + foldl/3, + foldr/3, + map/2, + size/1, + without/2 + ]). + + +%%% BIFs +-export([ + get/2, + find/2, + from_list/1, + is_key/2, + keys/1, + merge/2, + new/0, + put/3, + remove/2, + to_list/1, + update/3, + values/1 + ]). + +-type map() :: term(). %% FIXME: remove when erl_bif_types knows map(). + +%% Shadowed by erl_bif_types: map:get/3 +-spec get(Key,Map) -> Value when + Key :: term(), + Map :: map(), + Value :: term(). + +get(_,_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: map:find/3 +-spec find(Key,Map) -> {ok, Value} | error when + Key :: term(), + Map :: map(), + Value :: term(). + +find(_,_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: map:from_list/1 +-spec from_list([{Key,Value}]) -> Map when + Key :: term(), + Value :: term(), + Map :: map(). + +from_list(_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: map:is_key/2 +-spec is_key(Key,Map) -> boolean() when + Key :: term(), + Map :: map(). + +is_key(_,_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: map:keys/1 +-spec keys(Map) -> Keys when + Map :: map(), + Keys :: [Key], + Key :: term(). + +keys(_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: map:merge/2 +-spec merge(Map1,Map2) -> Map3 when + Map1 :: map(), + Map2 :: map(), + Map3 :: map(). + +merge(_,_) -> erlang:nif_error(undef). + + + +%% Shadowed by erl_bif_types: map:new/0 +-spec new() -> Map when + Map :: map(). + +new() -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: map:put/3 +-spec put(Key,Value,Map1) -> Map2 when + Key :: term(), + Value :: term(), + Map1 :: map(), + Map2 :: map(). + +put(_,_,_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: map:put/3 +-spec remove(Key,Map1) -> Map2 when + Key :: term(), + Map1 :: map(), + Map2 :: map(). + +remove(_,_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: map:to_list/1 +-spec to_list(Map) -> [{Key,Value}] when + Map :: map(), + Key :: term(), + Value :: term(). + +to_list(_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: map:update/3 +-spec update(Key,Value,Map1) -> Map2 when + Key :: term(), + Value :: term(), + Map1 :: map(), + Map2 :: map(). + +update(_,_,_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: map:values/1 +-spec values(Map) -> Keys when + Map :: map(), + Keys :: [Key], + Key :: term(). + +values(_) -> erlang:nif_error(undef). + + +%%% End of BIFs + +-spec foldl(Fun,Init,Map) -> Acc when + Fun :: fun((K, V, AccIn) -> AccOut), + Init :: term(), + Acc :: term(), + AccIn :: term(), + AccOut :: term(), + Map :: map(), + K :: term(), + V :: term(). + +foldl(Fun, Init, Map) -> + lists:foldl(fun({K,V},A) -> Fun(K,V,A) end,Init,map:to_list(Map)). + +-spec foldr(Fun,Init,Map) -> Acc when + Fun :: fun((K,V,AccIn) -> AccOut), + Init :: term(), + Acc :: term(), + AccIn :: term(), + AccOut :: term(), + Map :: map(), + K :: term(), + V :: term(). + + +foldr(Fun, Init, Map) -> + lists:foldr(fun({K,V},A) -> Fun(K,V,A) end,Init,map:to_list(Map)). + + +-spec map(Fun,Map1) -> Map2 when + Fun :: fun((K, V1) -> V2), + Map1 :: map(), + Map2 :: map(), + K :: term(), + V1 :: term(), + V2 :: term(). + + +map(Fun, Map) -> + map:from_list(lists:map(fun + ({K,V}) -> + {K,Fun(K,V)} + end,map:to_list(Map))). + +-spec size(Map) -> non_neg_integer() when + Map :: map(). + +size(Map) -> + erlang:map_size(Map). + +-spec without(Ks,Map1) -> Map2 when + Ks :: [K], + Map1 :: map(), + Map2 :: map(), + K :: term(). + +without(Ks, M) -> + map:from_list([{K,V}||{K,V} <- map:to_list(M), not lists:member(K, Ks)]). diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src index a30685e830..9a77ae9d66 100644 --- a/lib/stdlib/src/stdlib.app.src +++ b/lib/stdlib/src/stdlib.app.src @@ -71,6 +71,7 @@ lib, lists, log_mf_h, + map, math, ms_transform, orddict, -- cgit v1.2.3 From 351987be44e11e6eb9e5841adb197ec8e49bc49d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 6 Nov 2013 09:01:30 +0100 Subject: dialyzer,hipe,stdlib: Add Maps understanding to Dialyzer --- lib/dialyzer/src/dialyzer_cl.erl | 2 +- lib/dialyzer/src/dialyzer_dataflow.erl | 56 ++++++++++++++++- lib/dialyzer/src/dialyzer_dep.erl | 12 ++++ lib/dialyzer/src/dialyzer_typesig.erl | 34 ++++++++++- lib/hipe/cerl/cerl_prettypr.erl | 29 ++++++++- lib/hipe/cerl/erl_bif_types.erl | 17 +++++- lib/hipe/cerl/erl_types.erl | 108 ++++++++++++++++++++++++--------- lib/stdlib/src/erl_lint.erl | 8 +++ lib/stdlib/src/erl_parse.yrl | 9 +++ 9 files changed, 239 insertions(+), 36 deletions(-) (limited to 'lib') diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl index a7be6e0d05..cda801bf6c 100644 --- a/lib/dialyzer/src/dialyzer_cl.erl +++ b/lib/dialyzer/src/dialyzer_cl.erl @@ -603,7 +603,7 @@ cl_loop(State, LogCache) -> Msg = failed_anal_msg(Reason, LogCache), cl_error(State, Msg); {'EXIT', BackendPid, Reason} when Reason =/= 'normal' -> - Msg = failed_anal_msg(io_lib:format("~P", [Reason, 12]), LogCache), + Msg = failed_anal_msg(io_lib:format("~p", [Reason]), LogCache), cl_error(State, Msg); _Other -> %% io:format("Received ~p\n", [_Other]), diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index 3591d5be8e..4d614320c2 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -67,7 +67,9 @@ t_to_string/2, t_to_tlist/1, t_tuple/0, t_tuple/1, t_tuple_args/1, t_tuple_args/2, t_tuple_subtypes/2, - t_unit/0, t_unopaque/2]). + t_unit/0, t_unopaque/2, + t_map/1 + ]). %%-define(DEBUG, true). %%-define(DEBUG_PP, true). @@ -305,6 +307,12 @@ traverse(Tree, Map, State) -> handle_try(Tree, Map, State); tuple -> handle_tuple(Tree, Map, State); + map -> + handle_map(Tree, Map, State); + map_pair_assoc -> + handle_map_pair_assoc(Tree, Map, State); + map_pair_exact -> + handle_map_pair_exact(Tree, Map, State); values -> Elements = cerl:values_es(Tree), {State1, Map1, EsType} = traverse_list(Elements, Map, State), @@ -657,7 +665,8 @@ is_opaque_type_test_problem(Fun, Args, ArgTypes, State) -> FN =:= is_float; FN =:= is_function; FN =:= is_integer; FN =:= is_list; FN =:= is_number; FN =:= is_pid; FN =:= is_port; - FN =:= is_reference; FN =:= is_tuple -> + FN =:= is_reference; FN =:= is_tuple; + FN =:= is_map -> type_test_opaque_arg(Args, ArgTypes, State#state.opaques); {erlang, FN, 2} when FN =:= is_function -> type_test_opaque_arg(Args, ArgTypes, State#state.opaques); @@ -1054,6 +1063,23 @@ handle_try(Tree, Map, State) -> %%---------------------------------------- +handle_map(Tree,Map,State) -> + Pairs = cerl:map_es(Tree), + {State1, Map1, TypePairs} = traverse_list(Pairs,Map,State), + {State1, Map1, t_map(TypePairs)}. + +handle_map_pair_assoc(Tree,Map,State) -> + Elements = cerl:map_pair_assoc_es(Tree), + {State1, Map1, [K,V]} = traverse_list(Elements,Map,State), + {State1, Map1, {K,V}}. + +handle_map_pair_exact(Tree,Map,State) -> + Elements = cerl:map_pair_exact_es(Tree), + {State1, Map1, [K,V]} = traverse_list(Elements,Map,State), + {State1, Map1, {K,V}}. + +%%---------------------------------------- + handle_tuple(Tree, Map, State) -> Elements = cerl:tuple_es(Tree), {State1, Map1, EsType} = traverse_list(Elements, Map, State), @@ -1431,6 +1457,30 @@ bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) -> bind_opaque_pats(Literal, Type, Pat, Map, State, Rev); false -> {Map, Literal} end; + map -> + {Map, t_map([])}; +% Pairs = [ cerl:map_pair_es(Pair) || Pair <- cerl:map_es(Pat) ], +% MapType = t_inf(t_map([]), Type), +% case t_is_none(MapType) of +% true -> +% bind_opaque_pats(MapType, Type, Pat, Map, State, Rev); +% false -> +% MapJ = join_maps_begin(Map), +% Results = case Rev of +% true -> +% [bind_pat_vars_reverse(Pair, [t_any(),t_any()], [], MapJ, State) || Pair <- Pairs]; +% false -> +% [bind_pat_vars(Pair, [t_any(),t_any()], [], MapJ, State) || Pair <- Pairs] +% end, +% case [M || {M, _} <- Results, M =/= error] of +% [] -> bind_error([Pat], MapType, t_none(), bind); +% Maps -> +% Map1 = join_maps_end(Maps, MapJ), +% _PairTypes = [{Ktype,Vtype} || {M, [Ktype,Vtype]} <- Results, M =/= error], +% % add t_sup +% {Map1, t_map([])} +% end +% end; tuple -> Es = cerl:tuple_es(Pat), {TypedRecord, Prototype} = @@ -1641,6 +1691,8 @@ bind_guard(Guard, Map, Env, Eval, State) -> Es0 = cerl:tuple_es(Guard), {Map1, Es} = bind_guard_list(Es0, Map, Env, dont_know, State), {Map1, t_tuple(Es)}; + map -> + {Map, t_map([])}; 'let' -> Arg = cerl:let_arg(Guard), [Var] = cerl:let_vars(Guard), diff --git a/lib/dialyzer/src/dialyzer_dep.erl b/lib/dialyzer/src/dialyzer_dep.erl index 05f81399fb..12833b49c7 100644 --- a/lib/dialyzer/src/dialyzer_dep.erl +++ b/lib/dialyzer/src/dialyzer_dep.erl @@ -182,6 +182,18 @@ traverse(Tree, Out, State, CurrentFun) -> Args = cerl:tuple_es(Tree), {List, State1} = traverse_list(Args, Out, State, CurrentFun), {merge_outs(List), State1}; + map -> + Args = cerl:map_es(Tree), + {List, State1} = traverse_list(Args, Out, State, CurrentFun), + {merge_outs(List), State1}; + map_pair_assoc -> + Args = cerl:map_pair_assoc_es(Tree), + {List, State1} = traverse_list(Args, Out, State, CurrentFun), + {merge_outs(List), State1}; + map_pair_exact -> + Args = cerl:map_pair_exact_es(Tree), + {List, State1} = traverse_list(Args, Out, State, CurrentFun), + {merge_outs(List), State1}; values -> traverse_list(cerl:values_es(Tree), Out, State, CurrentFun); var -> diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl index db7875704a..4e18e9c7d2 100644 --- a/lib/dialyzer/src/dialyzer_typesig.erl +++ b/lib/dialyzer/src/dialyzer_typesig.erl @@ -55,7 +55,9 @@ t_subst/2, t_timeout/0, t_tuple/0, t_tuple/1, t_var/1, t_var_name/1, - t_none/0, t_unit/0]). + t_none/0, t_unit/0, + t_map/1 + ]). -include("dialyzer.hrl"). @@ -470,6 +472,33 @@ traverse(Tree, DefinedVars, State) -> end; [] -> {State2, TupleType} end; + map -> + {State, t_map([])}; +% Pairs = cerl:map_es(Tree), +% {State1, EVars} = traverse_list(Pairs, DefinedVars, State), +% case cerl:is_literal(cerl:fold_literal(Tree)) of +% true -> +% %% We do not need to do anything more here. +% {State, t_map([])}; +% false -> +% Fun = fun(Var, AccState) -> +% case t_has_var(Var) of +% true -> +% {AccState1, NewVar} = state__mk_var(AccState), +% {NewVar, +% state__store_conj(Var, eq, NewVar, AccState1)}; +% false -> +% {Var, AccState} +% end +% end, +% {_NewEvars, State2} = lists:mapfoldl(Fun, State1, EVars), +% {State2, t_map([])} +% end; +% map_pair_assoc -> +% [K,V] = cerl:map_pair_assoc_es(Tree), +% {State1,_} = traverse(K,DefinedVars,State), +% {State2,_} = traverse(V,DefinedVars,State), +% {_,State2}; values -> %% We can get into trouble when unifying products that have the %% same element appearing several times. Handle these cases by @@ -1037,6 +1066,9 @@ get_safe_underapprox_1([Pat|Left], Acc, Map) -> {Ts, Map1} = get_safe_underapprox_1(Es, [], Map), Type = t_tuple(Ts), get_safe_underapprox_1(Left, [Type|Acc], Map1); + map -> + %% TODO: Can maybe do something here + throw(dont_know); values -> Es = cerl:values_es(Pat), {Ts, Map1} = get_safe_underapprox_1(Es, [], Map), diff --git a/lib/hipe/cerl/cerl_prettypr.erl b/lib/hipe/cerl/cerl_prettypr.erl index fba9a48cda..60926e16e1 100644 --- a/lib/hipe/cerl/cerl_prettypr.erl +++ b/lib/hipe/cerl/cerl_prettypr.erl @@ -62,7 +62,10 @@ receive_action/1, receive_clauses/1, receive_timeout/1, seq_arg/1, seq_body/1, string_lit/1, try_arg/1, try_body/1, try_vars/1, try_evars/1, try_handler/1, - tuple_es/1, type/1, values_es/1, var_name/1]). + tuple_es/1, type/1, values_es/1, var_name/1, + + map_es/1, map_pair_assoc_es/1, map_pair_exact_es/1 + ]). -define(PAPER, 76). -define(RIBBON, 45). @@ -424,6 +427,12 @@ lay_1(Node, Ctxt) -> lay_cons(Node, Ctxt); tuple -> lay_tuple(Node, Ctxt); + map -> + lay_map(Node, Ctxt); + map_pair_assoc -> + lay_map_pair_assoc(Node, Ctxt); + map_pair_exact -> + lay_map_pair_exact(Node, Ctxt); 'let' -> lay_let(Node, Ctxt); seq -> @@ -589,6 +598,24 @@ lay_tuple(Node, Ctxt) -> Ctxt, fun lay/2)), floating(text("}")))). +lay_map(Node, Ctxt) -> + beside(floating(text("~{")), + beside(par(seq(map_es(Node), floating(text(",")), + Ctxt, fun lay/2)), + floating(text("}~")))). + +lay_map_pair_assoc(Node, Ctxt) -> + [K,V] = map_pair_assoc_es(Node), + beside(floating(text("::<")), + beside(lay(K,Ctxt),beside(floating(text(",")), beside(lay(V,Ctxt), + floating(text(">")))))). + +lay_map_pair_exact(Node, Ctxt) -> + [K,V] = map_pair_exact_es(Node), + beside(floating(text("~<")), + beside(lay(K,Ctxt),beside(floating(text(",")), beside(lay(V,Ctxt), + floating(text(">")))))). + lay_let(Node, Ctxt) -> V = lay_value_list(let_vars(Node), Ctxt), D1 = par([follow(text("let"), diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 32a502e212..8b610ac893 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -114,7 +114,9 @@ t_tuple/1, t_tuple_args/2, t_tuple_size/2, - t_tuple_subtypes/2 + t_tuple_subtypes/2, + t_is_map/2, + t_map/0 ]). -ifdef(DO_ERL_BIF_TYPES_TEST). @@ -654,6 +656,11 @@ type(erlang, is_list, 1, Xs, Opaques) -> check_guard(X, Fun2, t_maybe_improper_list(), Opaques) end, strict(erlang, is_list, 1, Xs, Fun, Opaques); +type(erlang, is_map, 1, Xs, Opaques) -> + Fun = fun (X) -> + check_guard(X, fun (Y) -> t_is_map(Y, Opaques) end, + t_map(), Opaques) end, + strict(erlang, is_map, 1, Xs, Fun, Opaques); type(erlang, is_number, 1, Xs, Opaques) -> Fun = fun (X) -> check_guard(X, fun (Y) -> t_is_number(Y, Opaques) end, @@ -756,6 +763,9 @@ type(erlang, is_tuple, 1, Xs, Opaques) -> %% Guard bif, needs to be here. type(erlang, length, 1, Xs, Opaques) -> strict(erlang, length, 1, Xs, fun (_) -> t_non_neg_fixnum() end, Opaques); +%% Guard bif, needs to be here. +type(erlang, map_size, 1, Xs, Opaques) -> + strict(erlang, map_size, 1, Xs, fun (_) -> t_non_neg_integer() end, Opaques); type(erlang, make_tuple, 2, Xs, Opaques) -> strict(erlang, make_tuple, 2, Xs, fun ([Int, _]) -> @@ -2297,6 +2307,8 @@ arg_types(erlang, is_integer, 1) -> [t_any()]; arg_types(erlang, is_list, 1) -> [t_any()]; +arg_types(erlang, is_map, 1) -> + [t_any()]; arg_types(erlang, is_number, 1) -> [t_any()]; arg_types(erlang, is_pid, 1) -> @@ -2314,6 +2326,9 @@ arg_types(erlang, is_tuple, 1) -> %% Guard bif, needs to be here. arg_types(erlang, length, 1) -> [t_list()]; +%% Guard bif, needs to be here. +arg_types(erlang, map_size, 1) -> + [t_map()]; arg_types(erlang, make_tuple, 2) -> [t_non_neg_fixnum(), t_any()]; % the value 0 is OK as first argument arg_types(erlang, make_tuple, 3) -> diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index cfa72d85b7..af34e355bb 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -208,7 +208,11 @@ lift_list_to_pos_empty/1, is_opaque_type/2, is_erl_type/1, - atom_to_string/1 + atom_to_string/1, + + t_is_map/2, + t_map/1, + t_map/0 ]). %%-define(DO_ERL_TYPES_TEST, true). @@ -266,6 +270,7 @@ -define(function_tag, function). -define(identifier_tag, identifier). -define(list_tag, list). +-define(map_tag, map). -define(matchstate_tag, matchstate). -define(nil_tag, nil). -define(number_tag, number). @@ -278,7 +283,7 @@ -define(var_tag, var). -type tag() :: ?atom_tag | ?binary_tag | ?function_tag | ?identifier_tag - | ?list_tag | ?matchstate_tag | ?nil_tag | ?number_tag + | ?list_tag | ?map_tag | ?matchstate_tag | ?nil_tag | ?number_tag | ?opaque_tag | ?product_tag | ?remote_tag | ?tuple_tag | ?tuple_set_tag | ?union_tag | ?var_tag. @@ -338,6 +343,7 @@ -define(nonempty_list(Types, Term),?list(Types, Term, ?nonempty_qual)). -define(number(Set, Qualifier), #c{tag=?number_tag, elements=Set, qualifier=Qualifier}). +-define(map(Pairs), #c{tag=?map_tag, elements=Pairs}). -define(opaque(Optypes), #c{tag=?opaque_tag, elements=Optypes}). -define(product(Types), #c{tag=?product_tag, elements=Types}). -define(remote(RemTypes), #c{tag=?remote_tag, elements=RemTypes}). @@ -361,18 +367,19 @@ %% Unions %% --define(union(List), #c{tag=?union_tag, elements=[_,_,_,_,_,_,_,_,_,_]=List}). - --define(atom_union(T), ?union([T,?none,?none,?none,?none,?none,?none,?none,?none,?none])). --define(bitstr_union(T), ?union([?none,T,?none,?none,?none,?none,?none,?none,?none,?none])). --define(function_union(T), ?union([?none,?none,T,?none,?none,?none,?none,?none,?none,?none])). --define(identifier_union(T), ?union([?none,?none,?none,T,?none,?none,?none,?none,?none,?none])). --define(list_union(T), ?union([?none,?none,?none,?none,T,?none,?none,?none,?none,?none])). --define(number_union(T), ?union([?none,?none,?none,?none,?none,T,?none,?none,?none,?none])). --define(tuple_union(T), ?union([?none,?none,?none,?none,?none,?none,T,?none,?none,?none])). --define(matchstate_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,T,?none,?none])). --define(opaque_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,T,?none])). --define(remote_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,T])). +-define(union(List), #c{tag=?union_tag, elements=[_,_,_,_,_,_,_,_,_,_,_]=List}). + +-define(atom_union(T), ?union([T,?none,?none,?none,?none,?none,?none,?none,?none,?none,?none])). +-define(bitstr_union(T), ?union([?none,T,?none,?none,?none,?none,?none,?none,?none,?none,?none])). +-define(function_union(T), ?union([?none,?none,T,?none,?none,?none,?none,?none,?none,?none,?none])). +-define(identifier_union(T), ?union([?none,?none,?none,T,?none,?none,?none,?none,?none,?none,?none])). +-define(list_union(T), ?union([?none,?none,?none,?none,T,?none,?none,?none,?none,?none,?none])). +-define(number_union(T), ?union([?none,?none,?none,?none,?none,T,?none,?none,?none,?none,?none])). +-define(tuple_union(T), ?union([?none,?none,?none,?none,?none,?none,T,?none,?none,?none,?none])). +-define(matchstate_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,T,?none,?none,?none])). +-define(opaque_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,T,?none,?none])). +-define(remote_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,T,?none])). +-define(map_union(T), ?union([?none,?none,?none,?none,?none,?none,?none,?none,?none,?none,T])). -define(integer_union(T), ?number_union(T)). -define(float_union(T), ?number_union(T)). -define(nil_union(T), ?list_union(T)). @@ -482,6 +489,9 @@ t_contains_opaque(?int_range(_From, _To), _Opaques) -> false; t_contains_opaque(?int_set(_Set), _Opaques) -> false; t_contains_opaque(?list(Type, Tail, _), Opaques) -> t_contains_opaque(Type, Opaques) orelse t_contains_opaque(Tail, Opaques); +t_contains_opaque(?map(Pairs), Opaques) -> + list_contains_opaque([V||{_,V}<-Pairs], Opaques) orelse + list_contains_opaque([K||{K,_}<-Pairs], Opaques); t_contains_opaque(?matchstate(_P, _Slots), _Opaques) -> false; t_contains_opaque(?nil, _Opaques) -> false; t_contains_opaque(?number(_Set, _Tag), _Opaques) -> false; @@ -655,9 +665,9 @@ list_decorate(List, L, Opaques) -> union_decorate(U1, U2, Opaques) -> Union = union_decorate(U1, U2, Opaques, 0, []), - [A,B,F,I,L,N,T,M,_,_R] = U1, - [_,_,_,_,_,_,_,_,Opaque,_] = U2, - List = [A,B,F,I,L,N,T,M], + [A,B,F,I,L,N,T,M,_,_R,Map] = U1, + [_,_,_,_,_,_,_,_,Opaque,_,_] = U2, + List = [A,B,F,I,L,N,T,M,Map], DecList = [Dec || E <- List, not t_is_none(Dec = decorate(E, Opaque, Opaques))], @@ -1694,6 +1704,29 @@ lift_list_to_pos_empty(?nil) -> ?nil; lift_list_to_pos_empty(?list(Content, Termination, _)) -> ?list(Content, Termination, ?unknown_qual). +%%----------------------------------------------------------------------------- +%% Maps +%% + +-spec t_map() -> erl_type(). + +t_map() -> + ?map([]). + +-spec t_map([{erl_type(),erl_type()}]) -> erl_type(). + +t_map(_) -> + ?map([]). + +-spec t_is_map(erl_type(), opaques()) -> boolean(). + +t_is_map(Type, Opaques) -> + do_opaque(Type, Opaques, fun is_map1/1). + +is_map1(?map(_)) -> true; +is_map1(_) -> false. + + %%----------------------------------------------------------------------------- %% Tuples %% @@ -2517,6 +2550,7 @@ force_union(T = ?nil) -> ?list_union(T); force_union(T = ?number(_,_)) -> ?number_union(T); force_union(T = ?opaque(_)) -> ?opaque_union(T); force_union(T = ?remote(_)) -> ?remote_union(T); +force_union(T = ?map(_)) -> ?map_union(T); force_union(T = ?tuple(_, _, _)) -> ?tuple_union(T); force_union(T = ?tuple_set(_)) -> ?tuple_union(T); force_union(T = ?matchstate(_, _)) -> ?matchstate_union(T); @@ -2553,6 +2587,7 @@ t_elements(?number(_, _) = T) -> end; t_elements(?opaque(_) = T) -> do_elements(T); +t_elements(?map(_) = T) -> [T]; t_elements(?tuple(_, _, _) = T) -> [T]; t_elements(?tuple_set(_) = TS) -> case t_tuple_subtypes(TS) of @@ -2914,9 +2949,9 @@ inf_tuples_in_sets2(_, [], Acc, _Opaques) -> lists:reverse(Acc). inf_union(U1, U2, Opaques) -> OpaqueFun = fun(Union1, Union2, InfFun) -> - [_,_,_,_,_,_,_,_,Opaque,_] = Union1, - [A,B,F,I,L,N,T,M,_,_R] = Union2, - List = [A,B,F,I,L,N,T,M], + [_,_,_,_,_,_,_,_,Opaque,_,_] = Union1, + [A,B,F,I,L,N,T,M,_,_R,Map] = Union2, + List = [A,B,F,I,L,N,T,M,Map], inf_union_collect(List, Opaque, InfFun, [], []) end, O1 = OpaqueFun(U1, U2, fun(E, Opaque) -> t_inf(Opaque, E, Opaques) end), @@ -3182,11 +3217,11 @@ unify_union1(?union(List), T1, T2) -> end. unify_union(List) -> - [A,B,F,I,L,N,T,M,O,R] = List, + [A,B,F,I,L,N,T,M,O,R,Map] = List, if O =:= ?none -> no; true -> S = t_opaque_structure(O), - {yes, t_sup([A,B,F,I,L,N,T,M,S,R])} + {yes, t_sup([A,B,F,I,L,N,T,M,S,R,Map])} end. -spec is_opaque_type(erl_type(), [erl_type()]) -> boolean(). @@ -3537,10 +3572,10 @@ t_subtract_lists([], [], Acc) -> -spec subtract_union([erl_type(),...], [erl_type(),...]) -> erl_type(). subtract_union(U1, U2) -> - [A1,B1,F1,I1,L1,N1,T1,M1,O1,R1] = U1, - [A2,B2,F2,I2,L2,N2,T2,M2,O2,R2] = U2, - List1 = [A1,B1,F1,I1,L1,N1,T1,M1,?none,R1], - List2 = [A2,B2,F2,I2,L2,N2,T2,M2,?none,R2], + [A1,B1,F1,I1,L1,N1,T1,M1,O1,R1,Map1] = U1, + [A2,B2,F2,I2,L2,N2,T2,M2,O2,R2,Map2] = U2, + List1 = [A1,B1,F1,I1,L1,N1,T1,M1,?none,R1,Map1], + List2 = [A2,B2,F2,I2,L2,N2,T2,M2,?none,R2,Map2], Sub1 = subtract_union(List1, List2, 0, []), O = if O1 =:= ?none -> O1; true -> t_subtract(O1, ?union(U2)) @@ -3656,7 +3691,7 @@ t_unopaque(?product(Types), Opaques) -> ?product([t_unopaque(T, Opaques) || T <- Types]); t_unopaque(?function(Domain, Range), Opaques) -> ?function(t_unopaque(Domain, Opaques), t_unopaque(Range, Opaques)); -t_unopaque(?union([A,B,F,I,L,N,T,M,O,R]), Opaques) -> +t_unopaque(?union([A,B,F,I,L,N,T,M,O,R,Map]), Opaques) -> UL = t_unopaque(L, Opaques), UT = t_unopaque(T, Opaques), UF = t_unopaque(F, Opaques), @@ -3664,7 +3699,7 @@ t_unopaque(?union([A,B,F,I,L,N,T,M,O,R]), Opaques) -> ?opaque(_) = O1 -> {O1, []}; Type -> {?none, [Type]} end, - t_sup([?union([A,B,UF,I,UL,N,UT,M,OF,R])|UO]); + t_sup([?union([A,B,UF,I,UL,N,UT,M,OF,R,Map])|UO]); t_unopaque(T, _) -> T. @@ -3941,6 +3976,8 @@ t_to_string(?remote(Set), RecDict) -> || #remote{mod = Mod, name = Name, args = Args} <- set_to_list(Set)], " | "); +t_to_string(?map(Pairs), RecDict) -> + "#{" ++ map_pairs_to_string(Pairs,RecDict) ++ "}"; t_to_string(?tuple(?any, ?any, ?any), _RecDict) -> "tuple()"; t_to_string(?tuple(Elements, _Arity, ?any), RecDict) -> "{" ++ comma_sequence(Elements, RecDict) ++ "}"; @@ -3960,6 +3997,13 @@ t_to_string(?var(Id), _RecDict) when is_atom(Id) -> t_to_string(?var(Id), _RecDict) when is_integer(Id) -> flat_format("var(~w)", [Id]). + +map_pairs_to_string([],_) -> []; +map_pairs_to_string(Pairs,RecDict) -> + StrPairs = [{t_to_string(K,RecDict),t_to_string(V,RecDict)}||{K,V}<-Pairs], + string:join([K ++ "=>" ++ V||{K,V}<-StrPairs], ", "). + + record_to_string(Tag, [_|Fields], FieldNames, RecDict) -> FieldStrings = record_fields_to_string(Fields, FieldNames, RecDict, []), "#" ++ atom_to_string(Tag) ++ "{" ++ string:join(FieldStrings, ",") ++ "}". @@ -4153,6 +4197,8 @@ t_from_form({type, _L, list, []}, _TypeNames, _RecDict, _VarDict) -> t_from_form({type, _L, list, [Type]}, TypeNames, RecDict, VarDict) -> {T, R} = t_from_form(Type, TypeNames, RecDict, VarDict), {t_list(T), R}; +t_from_form({type, _L, map, _}, _TypeNames, _RecDict, _VarDict) -> + {t_map([]), []}; t_from_form({type, _L, mfa, []}, _TypeNames, _RecDict, _VarDict) -> {t_mfa(), []}; t_from_form({type, _L, module, []}, _TypeNames, _RecDict, _VarDict) -> @@ -4437,6 +4483,8 @@ t_form_to_string({type, _L, iodata, []}) -> "iodata()"; t_form_to_string({type, _L, iolist, []}) -> "iolist()"; t_form_to_string({type, _L, list, [Type]}) -> "[" ++ t_form_to_string(Type) ++ "]"; +t_form_to_string({type, _L, map, _}) -> + "#{}"; t_form_to_string({type, _L, mfa, []}) -> "mfa()"; t_form_to_string({type, _L, module, []}) -> "module()"; t_form_to_string({type, _L, node, []}) -> "node()"; @@ -4566,13 +4614,13 @@ do_opaque(?opaque(_) = Type, Opaques, Pred) -> false -> Pred(Type) end; do_opaque(?union(List) = Type, Opaques, Pred) -> - [A,B,F,I,L,N,T,M,O,R] = List, + [A,B,F,I,L,N,T,M,O,R,Map] = List, if O =:= ?none -> Pred(Type); true -> case Opaques =:= 'universe' orelse is_opaque_type(O, Opaques) of true -> S = t_opaque_structure(O), - do_opaque(t_sup([A,B,F,I,L,N,T,M,S,R]), Opaques, Pred); + do_opaque(t_sup([A,B,F,I,L,N,T,M,S,R,Map]), Opaques, Pred); false -> Pred(Type) end end; diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index a41bcb91cc..f630db6032 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -2607,6 +2607,13 @@ check_type({type, L, range, [From, To]}, SeenVars, St) -> _ -> add_error(L, {type_syntax, range}, St) end, {SeenVars, St1}; +check_type({type, _L, map, any}, SeenVars, St) -> {SeenVars, St}; +check_type({type, _L, map, Pairs}, SeenVars, St) -> + lists:foldl(fun(Pair, {AccSeenVars, AccSt}) -> + check_type(Pair, AccSeenVars, AccSt) + end, {SeenVars, St}, Pairs); +check_type({type, _L, map_field_assoc, Dom, Range}, SeenVars, St) -> + check_type({type, -1, product, [Dom, Range]}, SeenVars, St); check_type({type, _L, tuple, any}, SeenVars, St) -> {SeenVars, St}; check_type({type, _L, any}, SeenVars, St) -> {SeenVars, St}; check_type({type, L, binary, [Base, Unit]}, SeenVars, St) -> @@ -2712,6 +2719,7 @@ 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; diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index c04a1aeb89..6d9aafa980 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -48,6 +48,7 @@ opt_bit_size_expr bit_size_expr opt_bit_type_list bit_type_list bit_type top_type top_type_100 top_types type typed_expr typed_attr_val type_sig type_sigs type_guard type_guards fun_type fun_type_100 binary_type type_spec spec_fun typed_exprs typed_record_fields field_types field_type +map_pair_types map_pair_type bin_base_type bin_unit_type type_200 type_300 type_400 type_500. Terminals @@ -155,6 +156,8 @@ type -> '[' ']' : {type, ?line('$1'), nil, []}. type -> '[' top_type ']' : {type, ?line('$1'), list, ['$2']}. type -> '[' top_type ',' '...' ']' : {type, ?line('$1'), nonempty_list, ['$2']}. +type -> '#' '{' '}' : {type, ?line('$1'), map, []}. +type -> '#' '{' map_pair_types '}' : {type, ?line('$1'), map, '$3'}. type -> '{' '}' : {type, ?line('$1'), tuple, []}. type -> '{' top_types '}' : {type, ?line('$1'), tuple, '$2'}. type -> '#' atom '{' '}' : {type, ?line('$1'), record, ['$2']}. @@ -176,6 +179,10 @@ fun_type -> '(' top_types ')' '->' top_type : {type, ?line('$1'), 'fun', [{type, ?line('$1'), product, '$2'},'$5']}. +map_pair_types -> map_pair_type : ['$1']. +map_pair_types -> map_pair_type ',' map_pair_types : ['$1'|'$3']. +map_pair_type -> top_type '=>' top_type : {type, ?line('$2'), map_field_assoc,'$1','$3'}. + field_types -> field_type : ['$1']. field_types -> field_type ',' field_types : ['$1'|'$3']. @@ -674,6 +681,8 @@ skip_paren(Type) -> build_gen_type({atom, La, tuple}) -> {type, La, tuple, any}; +build_gen_type({atom, La, map}) -> + {type, La, map, any}; build_gen_type({atom, La, Name}) -> {type, La, Name, []}. -- cgit v1.2.3 From b42453915e4c6aa1133e88c06a60dfbc5fe5e1f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 24 Oct 2013 21:17:11 +0200 Subject: edoc,syntax_tools: Teach Map understanding --- lib/edoc/src/edoc_doclet.erl | 2 +- lib/edoc/src/edoc_layout.erl | 10 +++++++++ lib/edoc/src/edoc_lib.erl | 2 +- lib/edoc/src/edoc_parser.yrl | 16 +++++++++++++-- lib/edoc/src/edoc_scanner.erl | 2 ++ lib/edoc/src/edoc_specs.erl | 13 ++++++++++++ lib/edoc/src/edoc_tags.erl | 2 ++ lib/edoc/src/edoc_types.erl | 4 ++++ lib/edoc/src/edoc_types.hrl | 4 ++++ lib/erl_docgen/src/docgen_otp_specs.erl | 5 +++++ lib/syntax_tools/src/erl_syntax.erl | 36 ++++++++++++++++++++++++++++++++- 11 files changed, 91 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/edoc/src/edoc_doclet.erl b/lib/edoc/src/edoc_doclet.erl index ce1e94a26a..5653b5894b 100644 --- a/lib/edoc/src/edoc_doclet.erl +++ b/lib/edoc/src/edoc_doclet.erl @@ -200,7 +200,7 @@ source({M, P, Name, Path}, Dir, Suffix, Env, Set, Private, Hidden, {Set, Error} end; R -> - report("skipping source file '~ts': ~W.", [File, R, 15]), + report("skipping source file '~ts': ~P.", [File, R, 15]), {Set, true} end. diff --git a/lib/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl index 7bd0615f5c..e164ff060f 100644 --- a/lib/edoc/src/edoc_layout.erl +++ b/lib/edoc/src/edoc_layout.erl @@ -829,6 +829,10 @@ t_type([#xmlElement{name = list, content = Es}]) -> t_list(Es); t_type([#xmlElement{name = nonempty_list, content = Es}]) -> t_nonempty_list(Es); +t_type([#xmlElement{name = map, content = Es}]) -> + t_map(Es); +t_type([#xmlElement{name = map_field, content=Es}]) -> + t_map_field(Es); t_type([#xmlElement{name = tuple, content = Es}]) -> t_tuple(Es); t_type([#xmlElement{name = 'fun', content = Es}]) -> @@ -877,6 +881,12 @@ t_fun(Es) -> ["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [") -> "] ++ t_utype(get_elem(type, Es))). +t_map(Es) -> + ["#{"] ++ seq(fun t_utype_elem/1, Es, ["}"]). + +t_map_field([K,V]) -> + [t_utype_elem(K) ++ " => " ++ t_utype_elem(V)]. + t_record(E, Es) -> Name = ["#"] ++ t_type(get_elem(atom, Es)), case get_elem(field, Es) of diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl index ca9df2b632..c46338a2e1 100644 --- a/lib/edoc/src/edoc_lib.erl +++ b/lib/edoc/src/edoc_lib.erl @@ -1032,7 +1032,7 @@ run_plugin(Name, Key, Default, Fun, Opts) when is_atom(Name) -> {ok, Value} -> Value; R -> - report("error in ~ts '~w': ~W.", [Name, Module, R, 20]), + report("error in ~ts '~w': ~P.", [Name, Module, R, 20]), exit(error) end. diff --git a/lib/edoc/src/edoc_parser.yrl b/lib/edoc/src/edoc_parser.yrl index 7762f2da7d..c6f8a04775 100644 --- a/lib/edoc/src/edoc_parser.yrl +++ b/lib/edoc/src/edoc_parser.yrl @@ -29,13 +29,14 @@ Nonterminals start spec func_type utype_list utype_tuple utypes utype ptypes ptype nutype function_name where_defs defs defs2 def typedef etype throws qname ref aref mref lref pref var_list vars fields field +utype_map utype_map_fields utype_map_field futype_list bin_base_type bin_unit_type. Terminals atom float integer var an_var string start_spec start_typedef start_throws start_ref -'(' ')' ',' '.' '->' '{' '}' '[' ']' '|' '+' ':' '::' '=' '/' '//' '*' +'(' ')' ',' '.' '=>' '->' '{' '}' '[' ']' '|' '+' ':' '::' '=' '/' '//' '*' '#' 'where' '<<' '>>' '..' '...'. Rootsymbol start. @@ -69,6 +70,14 @@ utype_list -> '(' utypes ')' : {lists:reverse('$2'), tok_line('$1')}. futype_list -> utype_list : '$1'. futype_list -> '(' '...' ')' : {[#t_var{name = '...'}], tok_line('$1')}. +utype_map -> '#' '{' utype_map_fields '}' : lists:reverse('$3'). + +utype_map_fields -> '$empty' : []. +utype_map_fields -> utype_map_field : ['$1']. +utype_map_fields -> utype_map_fields ',' utype_map_field : ['$3' | '$1']. + +utype_map_field -> utype '=>' utype : #t_map_field{ k_type = '$1', v_type = '$3'}. + utype_tuple -> '{' utypes '}' : lists:reverse('$2'). %% Produced in reverse order. @@ -91,9 +100,10 @@ ptype -> var : #t_var{name = tok_val('$1')}. ptype -> atom : #t_atom{val = tok_val('$1')}. ptype -> integer: #t_integer{val = tok_val('$1')}. ptype -> integer '..' integer: #t_integer_range{from = tok_val('$1'), - to = tok_val('$3')}. + to = tok_val('$3')}. ptype -> float: #t_float{val = tok_val('$1')}. ptype -> utype_tuple : #t_tuple{types = '$1'}. +ptype -> utype_map : #t_map{types = '$1'}. ptype -> '[' ']' : #t_nil{}. ptype -> '[' utype ']' : #t_list{type = '$2'}. ptype -> '[' utype ',' '...' ']' : #t_nonempty_list{type = '$2'}. @@ -462,3 +472,5 @@ throw_error(parse_param, L) -> throw({error, L, "missing parameter name"}); throw_error({Where, E}, L) when is_list(Where) -> throw({error,L,{"unknown error parsing ~ts: ~P.",[Where,E,15]}}). + +%% vim: ft=erlang diff --git a/lib/edoc/src/edoc_scanner.erl b/lib/edoc/src/edoc_scanner.erl index 754fcef643..6ff97a134c 100644 --- a/lib/edoc/src/edoc_scanner.erl +++ b/lib/edoc/src/edoc_scanner.erl @@ -137,6 +137,8 @@ scan1([$"|Cs0], Toks, Pos) -> % String scan_error({illegal, string}, Pos) end; %% Punctuation characters and operators, first recognise multiples. +scan1([$=,$>|Cs], Toks, Pos) -> + scan1(Cs, [{'=>',Pos}|Toks], Pos); scan1([$<,$<|Cs], Toks, Pos) -> scan1(Cs, [{'<<',Pos}|Toks], Pos); scan1([$>,$>|Cs], Toks, Pos) -> diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl index 5acf8ac0d5..466c9df951 100644 --- a/lib/edoc/src/edoc_specs.erl +++ b/lib/edoc/src/edoc_specs.erl @@ -358,6 +358,14 @@ d2e({type,_,tuple,any}) -> d2e({type,_,binary,[Base,Unit]}) -> #t_binary{base_size = element(3, Base), unit_size = element(3, Unit)}; +d2e({type,_,map,any}) -> + #t_map{ types = []}; +d2e({type,_,map,Es}) -> + #t_map{ types = d2e(Es) }; +d2e({type,_,map_field_assoc,K,V}) -> + #t_map_field{ k_type = d2e(K), v_type=d2e(V) }; +d2e({type,_,map_field_exact,K,V}) -> + #t_map_field{ k_type = d2e(K), v_type=d2e(V) }; d2e({type,_,tuple,Ts0}) -> Ts = d2e(Ts0), typevar_anno(#t_tuple{types = Ts}, Ts); @@ -476,6 +484,11 @@ xrecs(#t_fun{args = Args0, range = Range0}=T, P) -> Args = xrecs(Args0, P), Range = xrecs(Range0, P), T#t_fun{args = Args, range = Range}; +xrecs(#t_map{ types = Ts0 }=T,P) -> + Ts = xrecs(Ts0, P), + T#t_map{ types = Ts }; +xrecs(#t_map_field{ k_type=Kt, v_type=Vt}=T, P) -> + T#t_map_field{ k_type=xrecs(Kt,P), v_type=xrecs(Vt,P)}; xrecs(#t_tuple{types = Types0}=T, P) -> Types = xrecs(Types0, P), T#t_tuple{types = Types}; diff --git a/lib/edoc/src/edoc_tags.erl b/lib/edoc/src/edoc_tags.erl index eb41f1922a..74702102f0 100644 --- a/lib/edoc/src/edoc_tags.erl +++ b/lib/edoc/src/edoc_tags.erl @@ -460,6 +460,8 @@ check_type(#t_var{}, P, Ls, Ts) -> check_types3(Ts, P, Ls); check_type(#t_fun{args = Args, range = Range}, P, Ls, Ts) -> check_type(Range, P, Ls, Args++Ts); +check_type(#t_map{}, P, Ls, Ts) -> + check_types3(Ts, P, Ls); check_type(#t_tuple{types = Types}, P, Ls, Ts) -> check_types3(Types ++Ts, P, Ls); check_type(#t_list{type = Type}, P, Ls, Ts) -> diff --git a/lib/edoc/src/edoc_types.erl b/lib/edoc/src/edoc_types.erl index 60c6cecb97..af8f1230fb 100644 --- a/lib/edoc/src/edoc_types.erl +++ b/lib/edoc/src/edoc_types.erl @@ -141,6 +141,10 @@ to_xml(#t_type{name = N, args = As}, Env) -> to_xml(#t_fun{args = As, range = T}, Env) -> {'fun', [{argtypes, map(fun wrap_utype/2, As, Env)}, wrap_utype(T, Env)]}; +to_xml(#t_map{ types = Ts}, Env) -> + {map, map(fun wrap_utype/2, Ts, Env)}; +to_xml(#t_map_field{ k_type=K, v_type=V}, Env) -> + {map_field, [wrap_utype(K,Env), wrap_utype(V, Env)]}; to_xml(#t_tuple{types = Ts}, Env) -> {tuple, map(fun wrap_utype/2, Ts, Env)}; to_xml(#t_list{type = T}, Env) -> diff --git a/lib/edoc/src/edoc_types.hrl b/lib/edoc/src/edoc_types.hrl index 05c61d70ff..7fec10d936 100644 --- a/lib/edoc/src/edoc_types.hrl +++ b/lib/edoc/src/edoc_types.hrl @@ -155,3 +155,7 @@ %% @type t_paren() = #t_paren{a = list(), type = type()} -record(t_paren, {a=[], type}). % parentheses + +-record(t_map, {a=[], types=[]}). +-record(t_map_field, {a=[], k_type, v_type}). + diff --git a/lib/erl_docgen/src/docgen_otp_specs.erl b/lib/erl_docgen/src/docgen_otp_specs.erl index 3929e66515..3240edd68e 100644 --- a/lib/erl_docgen/src/docgen_otp_specs.erl +++ b/lib/erl_docgen/src/docgen_otp_specs.erl @@ -388,6 +388,8 @@ t_type([#xmlElement{name = nonempty_list, content = Es}]) -> t_nonempty_list(Es); t_type([#xmlElement{name = tuple, content = Es}]) -> t_tuple(Es); +t_type([#xmlElement{name = map}]) -> + t_map(); t_type([#xmlElement{name = 'fun', content = Es}]) -> ["fun("] ++ t_fun(Es) ++ [")"]; t_type([E = #xmlElement{name = record, content = Es}]) -> @@ -430,6 +432,9 @@ t_nonempty_list(Es) -> t_tuple(Es) -> ["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]). +t_map() -> + ["#{}"]. + t_fun(Es) -> ["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [") -> "] ++ t_utype(get_elem(type, Es))). diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl index 4f7f9e83ac..78a37445ed 100644 --- a/lib/syntax_tools/src/erl_syntax.erl +++ b/lib/syntax_tools/src/erl_syntax.erl @@ -582,6 +582,9 @@ type(Node) -> {match, _, _, _} -> match_expr; {op, _, _, _, _} -> infix_expr; {op, _, _, _} -> prefix_expr; + {map,_,_} -> map; + {map_field_assoc,_,_,_} -> map_field_assoc; + {map_field_exact,_,_,_} -> map_field_exact; {record, _, _, _, _} -> record_expr; {record, _, _, _} -> record_expr; {record_field, _, _, _, _} -> record_access; @@ -1909,6 +1912,28 @@ atom_literal(Node) -> io_lib:write_atom(atom_value(Node)). +%% ===================================================================== + +map_elements(Node) -> + case unwrap(Node) of + {map, _, List} -> + List; + Node1 -> + data(Node1) + end. + +map_field_elements({_,_,K,V}) -> + [K,V]. + +map(List) -> + tree(map, List). + +map_field_assoc(List) -> + tree(map_field_assoc, List). + +map_field_exact(List) -> + tree(map_field_exact, List). + %% ===================================================================== %% @doc Creates an abstract tuple. If `Elements' is %% `[X1, ..., Xn]', the result represents @@ -6396,6 +6421,12 @@ subtrees(T) -> try_expr_clauses(T), try_expr_handlers(T), try_expr_after(T)]; + map -> + [map_elements(T)]; + map_field_assoc -> + [map_field_elements(T)]; + map_field_exact -> + [map_field_elements(T)]; tuple -> [tuple_elements(T)] end @@ -6491,7 +6522,10 @@ make_tree(record_index_expr, [[T], [F]]) -> make_tree(rule, [[N], C]) -> rule(N, C); make_tree(size_qualifier, [[N], [A]]) -> size_qualifier(N, A); make_tree(try_expr, [B, C, H, A]) -> try_expr(B, C, H, A); -make_tree(tuple, [E]) -> tuple(E). +make_tree(tuple, [E]) -> tuple(E); +make_tree(map, [E]) -> map(E); +make_tree(map_field_assoc, [E]) -> map_field_assoc(E); +make_tree(map_field_exact, [E]) -> map_field_exact(E). %% ===================================================================== -- cgit v1.2.3 From 40706594b498291d57528ccb3e966febccb959b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 7 Nov 2013 15:27:56 +0100 Subject: edoc: Add test for Maps in EDoc --- lib/edoc/test/edoc_SUITE.erl | 39 +++++++++++++++------------- lib/edoc/test/edoc_SUITE_data/map_module.erl | 27 +++++++++++++++++++ 2 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 lib/edoc/test/edoc_SUITE_data/map_module.erl (limited to 'lib') diff --git a/lib/edoc/test/edoc_SUITE.erl b/lib/edoc/test/edoc_SUITE.erl index 5b95c35756..b649971e99 100644 --- a/lib/edoc/test/edoc_SUITE.erl +++ b/lib/edoc/test/edoc_SUITE.erl @@ -22,12 +22,12 @@ init_per_group/2,end_per_group/2]). %% Test cases --export([build_std/1]). +-export([build_std/1,build_map_module/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [build_std]. + [build_std,build_map_module]. groups() -> []. @@ -45,25 +45,28 @@ end_per_group(_GroupName, Config) -> Config. -build_std(suite) -> - []; -build_std(doc) -> - ["Build some documentation using standard EDoc layout"]; +build_std(suite) -> []; +build_std(doc) -> ["Build some documentation using standard EDoc layout"]; build_std(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Overview1 = filename:join(DataDir, "overview.edoc"), + Overview2 = filename:join(DataDir, "overview.syntax_tools"), + PrivDir = ?config(priv_dir, Config), - ?line DataDir = ?config(data_dir, Config), - ?line Overview1 = filename:join(DataDir, "overview.edoc"), - ?line Overview2 = filename:join(DataDir, "overview.syntax_tools"), - ?line PrivDir = ?config(priv_dir, Config), + ok = edoc:application(edoc, [{overview, Overview1}, + {def, {vsn,"TEST"}}, + {dir, PrivDir}]), - ?line ok = edoc:application(edoc, [{overview, Overview1}, - {def, {vsn,"TEST"}}, - {dir, PrivDir}]), + ok = edoc:application(syntax_tools, [{overview, Overview2}, + {def, {vsn,"TEST"}}, + {dir, PrivDir}]), - ?line ok = edoc:application(syntax_tools, [{overview, Overview2}, - {def, {vsn,"TEST"}}, - {dir, PrivDir}]), - - ?line ok = edoc:application(xmerl, [{dir, PrivDir}]), + ok = edoc:application(xmerl, [{dir, PrivDir}]), + ok. +build_map_module(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + Filename = filename:join(DataDir, "map_module.erl"), + ok = edoc:file(Filename, [{dir, PrivDir}]), ok. diff --git a/lib/edoc/test/edoc_SUITE_data/map_module.erl b/lib/edoc/test/edoc_SUITE_data/map_module.erl new file mode 100644 index 0000000000..94ee7e6f26 --- /dev/null +++ b/lib/edoc/test/edoc_SUITE_data/map_module.erl @@ -0,0 +1,27 @@ +-module(map_module). + +-export([foo1/1,foo2/3]). + +%% @type wazzup() = integer() +%% @type some_type() = map() +%% @type some_other_type() = {a, #{ list() => term()}} + +-type some_type() :: map(). +-type some_other_type() :: {'a', #{ list() => term()} }. +-type wazzup() :: integer(). + +-spec foo1(Map :: #{ 'a' => integer(), 'b' => term()}) -> term(). + +%% @doc Gets value from map. + +foo1(#{ a:= 1, b := V}) -> V. + +%% @spec foo2(some_type(), Type2 :: some_other_type(), map()) -> Value +%% @doc Gets value from map. + +-spec foo2( + Type1 :: some_type(), + Type2 :: some_other_type(), + Map :: #{ get => 'value', 'value' => binary()}) -> binary(). + +foo2(Type1, {a,#{ "a" := _}}, #{get := value, value := B}) when is_map(Type1) -> B. -- cgit v1.2.3 From bd524e53cc258e11785bfc8bd42348b471479878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 12 Nov 2013 14:34:51 +0100 Subject: typer: Rename map() type to map_dict() The name conflicted with builtin type map(). --- lib/typer/src/typer.erl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index 0ace1d5fb8..20ae4d6066 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -63,10 +63,10 @@ %% Files in 'fms' are compilable with option 'to_pp'; we keep them %% as {FileName, ModuleName} in case the ModuleName is different fms = [] :: [{file:filename(), module()}], - ex_func = map__new() :: map(), - record = map__new() :: map(), - func = map__new() :: map(), - inc_func = map__new() :: map(), + ex_func = map__new() :: map_dict(), + record = map__new() :: map_dict(), + func = map__new() :: map_dict(), + inc_func = map__new() :: map_dict(), trust_plt = dialyzer_plt:new() :: plt()}). -type analysis() :: #analysis{}. @@ -220,11 +220,11 @@ get_external(Exts, Plt) -> -type fa() :: {atom(), arity()}. -type func_info() :: {line(), atom(), arity()}. --record(info, {records = map__new() :: map(), +-record(info, {records = map__new() :: map_dict(), functions = [] :: [func_info()], - types = map__new() :: map(), + types = map__new() :: map_dict(), edoc = false :: boolean()}). --record(inc, {map = map__new() :: map(), filter = [] :: files()}). +-record(inc, {map = map__new() :: map_dict(), filter = [] :: files()}). -type inc() :: #inc{}. -spec show_or_annotate(analysis()) -> 'ok'. @@ -1094,29 +1094,29 @@ rcv_ext_types(Self, ExtTypes) -> %% specialized for the uses in this module %%-------------------------------------------------------------------- --type map() :: dict(). +-type map_dict() :: dict(). --spec map__new() -> map(). +-spec map__new() -> map_dict(). map__new() -> dict:new(). --spec map__insert({term(), term()}, map()) -> map(). +-spec map__insert({term(), term()}, map_dict()) -> map_dict(). map__insert(Object, Map) -> {Key, Value} = Object, dict:store(Key, Value, Map). --spec map__lookup(term(), map()) -> term(). +-spec map__lookup(term(), map_dict()) -> term(). map__lookup(Key, Map) -> try dict:fetch(Key, Map) catch error:_ -> none end. --spec map__from_list([{fa(), term()}]) -> map(). +-spec map__from_list([{fa(), term()}]) -> map_dict(). map__from_list(List) -> dict:from_list(List). --spec map__remove(term(), map()) -> map(). +-spec map__remove(term(), map_dict()) -> map_dict(). map__remove(Key, Dict) -> dict:erase(Key, Dict). --spec map__fold(fun((term(), term(), term()) -> map()), map(), map()) -> map(). +-spec map__fold(fun((term(), term(), term()) -> map_dict()), map_dict(), map_dict()) -> map_dict(). map__fold(Fun, Acc0, Dict) -> dict:fold(Fun, Acc0, Dict). -- cgit v1.2.3 From 6868557c0244140ea7709822e8f7a0ba26029b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 25 Oct 2013 03:57:38 +0200 Subject: stdlib: Fixup id_transform_SUITE --- lib/stdlib/examples/erl_id_trans.erl | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'lib') diff --git a/lib/stdlib/examples/erl_id_trans.erl b/lib/stdlib/examples/erl_id_trans.erl index 2c842fafc7..5fcb74310e 100644 --- a/lib/stdlib/examples/erl_id_trans.erl +++ b/lib/stdlib/examples/erl_id_trans.erl @@ -144,6 +144,13 @@ pattern({cons,Line,H0,T0}) -> pattern({tuple,Line,Ps0}) -> Ps1 = pattern_list(Ps0), {tuple,Line,Ps1}; +pattern({map,Line,Ps0}) -> + Ps1 = pattern_list(Ps0), + {map,Line,Ps1}; +pattern({map_field_exact,Line,K,V}) -> + Ke = pattern(K), + Ve = pattern(V), + {map_field_exact,Line,Ke,Ve}; %%pattern({struct,Line,Tag,Ps0}) -> %% Ps1 = pattern_list(Ps0), %% {struct,Line,Tag,Ps1}; @@ -251,6 +258,20 @@ gexpr({float,Line,F}) -> {float,Line,F}; gexpr({atom,Line,A}) -> {atom,Line,A}; gexpr({string,Line,S}) -> {string,Line,S}; gexpr({nil,Line}) -> {nil,Line}; +gexpr({map,Line,Map0,Es0}) -> + [Map1|Es1] = gexpr_list([Map0|Es0]), + {map,Line,Map1,Es1}; +gexpr({map,Line,Es0}) -> + Es1 = gexpr_list(Es0), + {map,Line,Es1}; +gexpr({map_field_assoc,Line,K,V}) -> + Ke = gexpr(K), + Ve = gexpr(V), + {map_field_assoc,Line,Ke,Ve}; +gexpr({map_field_exact,Line,K,V}) -> + Ke = gexpr(K), + Ve = gexpr(V), + {map_field_exact,Line,Ke,Ve}; gexpr({cons,Line,H0,T0}) -> H1 = gexpr(H0), T1 = gexpr(T0), %They see the same variables @@ -356,6 +377,20 @@ expr({bc,Line,E0,Qs0}) -> expr({tuple,Line,Es0}) -> Es1 = expr_list(Es0), {tuple,Line,Es1}; +expr({map,Line,Map0,Es0}) -> + [Map1|Es1] = exprs([Map0|Es0]), + {map,Line,Map1,Es1}; +expr({map,Line,Es0}) -> + Es1 = exprs(Es0), + {map,Line,Es1}; +expr({map_field_assoc,Line,K,V}) -> + Ke = expr(K), + Ve = expr(V), + {map_field_assoc,Line,Ke,Ve}; +expr({map_field_exact,Line,K,V}) -> + Ke = expr(K), + Ve = expr(V), + {map_field_exact,Line,Ke,Ve}; %%expr({struct,Line,Tag,Es0}) -> %% Es1 = pattern_list(Es0), %% {struct,Line,Tag,Es1}; -- cgit v1.2.3 From 457b29c9ddd2b338e80916a5e7bad8dfe4b36ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 25 Oct 2013 16:28:50 +0200 Subject: erts,stdlib: Teach matchspec compiler map guards --- lib/stdlib/src/ms_transform.erl | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl index 25b04fe45e..27dfcf52e1 100644 --- a/lib/stdlib/src/ms_transform.erl +++ b/lib/stdlib/src/ms_transform.erl @@ -910,6 +910,7 @@ bool_test(is_pid,1) -> true; bool_test(is_port,1) -> true; bool_test(is_reference,1) -> true; bool_test(is_tuple,1) -> true; +bool_test(is_map,1) -> true; bool_test(is_binary,1) -> true; bool_test(is_function,1) -> true; bool_test(is_record,2) -> true; @@ -924,6 +925,7 @@ real_guard_function(node,0) -> true; real_guard_function(node,1) -> true; real_guard_function(round,1) -> true; real_guard_function(size,1) -> true; +real_guard_function(map_size,1) -> true; real_guard_function(tl,1) -> true; real_guard_function(trunc,1) -> true; real_guard_function(self,0) -> true; -- cgit v1.2.3 From 6fdad74f41803089a0f9026c98f319daecda9a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 24 Oct 2013 19:06:34 +0200 Subject: erts,stdlib: Change map module name to maps Name conforms to EEP. --- lib/stdlib/src/Makefile | 2 +- lib/stdlib/src/erl_eval.erl | 10 +- lib/stdlib/src/erl_parse.yrl | 2 +- lib/stdlib/src/io_lib.erl | 2 +- lib/stdlib/src/map.erl | 215 ------------------------------------------ lib/stdlib/src/maps.erl | 214 +++++++++++++++++++++++++++++++++++++++++ lib/stdlib/src/stdlib.app.src | 2 +- 7 files changed, 223 insertions(+), 224 deletions(-) delete mode 100644 lib/stdlib/src/map.erl create mode 100644 lib/stdlib/src/maps.erl (limited to 'lib') diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile index 376083c7d6..9ab2cd4134 100644 --- a/lib/stdlib/src/Makefile +++ b/lib/stdlib/src/Makefile @@ -91,7 +91,7 @@ MODULES= \ lib \ lists \ log_mf_h \ - map \ + maps \ math \ ms_transform \ otp_internal \ diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl index fcd1945c63..5f96795d92 100644 --- a/lib/stdlib/src/erl_eval.erl +++ b/lib/stdlib/src/erl_eval.erl @@ -253,14 +253,14 @@ expr({map,_, Binding,Es}, Bs0, Lf, Ef, RBs) -> {value, Map0, Bs1} = expr(Binding, Bs0, Lf, Ef, RBs), {Vs,Bs} = expr_list(Es, Bs1, Lf, Ef), ret_expr(lists:foldl(fun - ({map_assoc,K,V}, Mi) -> map:put(K,V,Mi); - ({map_exact,K,V}, Mi) -> map:update(K,V,Mi) + ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi); + ({map_exact,K,V}, Mi) -> maps:update(K,V,Mi) end, Map0, Vs), Bs, RBs); expr({map,_,Es}, Bs0, Lf, Ef, RBs) -> {Vs,Bs} = expr_list(Es, Bs0, Lf, Ef), ret_expr(lists:foldl(fun - ({map_assoc,K,V}, Mi) -> map:put(K,V,Mi) - end, map:new(), Vs), Bs, RBs); + ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi) + end, maps:new(), Vs), Bs, RBs); expr({block,_,Es}, Bs, Lf, Ef, RBs) -> exprs(Es, Bs, Lf, Ef, RBs); @@ -1148,7 +1148,7 @@ match_tuple([], _, _, Bs, _BBs) -> match_map([{map_field_exact, _, K, V}|Fs], Map, Bs0, BBs) -> Vm = try {value, Ke, _} = expr(K, new_bindings()), - map:get(Ke,Map) + maps:get(Ke,Map) catch error:_ -> throw(nomatch) end, diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 6d9aafa980..6316db7054 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -896,7 +896,7 @@ normalise({cons,_,Head,Tail}) -> normalise({tuple,_,Args}) -> list_to_tuple(normalise_list(Args)); normalise({map,_,Pairs}=M) -> - map:from_list(lists:map(fun + maps:from_list(lists:map(fun %% only allow '=>' ({map_field_assoc,_,K,V}) -> {normalise(K),normalise(V)}; (_) -> erlang:error({badarg,M}) diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl index 886939761c..adc9a0cf5f 100644 --- a/lib/stdlib/src/io_lib.erl +++ b/lib/stdlib/src/io_lib.erl @@ -278,7 +278,7 @@ write_ref(Ref) -> erlang:ref_to_list(Ref). write_map(Map, D) when is_integer(D) -> - [$#,${,write_map_body(map:to_list(Map), D),$}]. + [$#,${,write_map_body(maps:to_list(Map), D),$}]. write_map_body(_, 0) -> "..."; write_map_body([],_) -> []; diff --git a/lib/stdlib/src/map.erl b/lib/stdlib/src/map.erl deleted file mode 100644 index 6257a90180..0000000000 --- a/lib/stdlib/src/map.erl +++ /dev/null @@ -1,215 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2013. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% - --module(map). - --export([ - foldl/3, - foldr/3, - map/2, - size/1, - without/2 - ]). - - -%%% BIFs --export([ - get/2, - find/2, - from_list/1, - is_key/2, - keys/1, - merge/2, - new/0, - put/3, - remove/2, - to_list/1, - update/3, - values/1 - ]). - --type map() :: term(). %% FIXME: remove when erl_bif_types knows map(). - -%% Shadowed by erl_bif_types: map:get/3 --spec get(Key,Map) -> Value when - Key :: term(), - Map :: map(), - Value :: term(). - -get(_,_) -> erlang:nif_error(undef). - - -%% Shadowed by erl_bif_types: map:find/3 --spec find(Key,Map) -> {ok, Value} | error when - Key :: term(), - Map :: map(), - Value :: term(). - -find(_,_) -> erlang:nif_error(undef). - - -%% Shadowed by erl_bif_types: map:from_list/1 --spec from_list([{Key,Value}]) -> Map when - Key :: term(), - Value :: term(), - Map :: map(). - -from_list(_) -> erlang:nif_error(undef). - - -%% Shadowed by erl_bif_types: map:is_key/2 --spec is_key(Key,Map) -> boolean() when - Key :: term(), - Map :: map(). - -is_key(_,_) -> erlang:nif_error(undef). - - -%% Shadowed by erl_bif_types: map:keys/1 --spec keys(Map) -> Keys when - Map :: map(), - Keys :: [Key], - Key :: term(). - -keys(_) -> erlang:nif_error(undef). - - -%% Shadowed by erl_bif_types: map:merge/2 --spec merge(Map1,Map2) -> Map3 when - Map1 :: map(), - Map2 :: map(), - Map3 :: map(). - -merge(_,_) -> erlang:nif_error(undef). - - - -%% Shadowed by erl_bif_types: map:new/0 --spec new() -> Map when - Map :: map(). - -new() -> erlang:nif_error(undef). - - -%% Shadowed by erl_bif_types: map:put/3 --spec put(Key,Value,Map1) -> Map2 when - Key :: term(), - Value :: term(), - Map1 :: map(), - Map2 :: map(). - -put(_,_,_) -> erlang:nif_error(undef). - - -%% Shadowed by erl_bif_types: map:put/3 --spec remove(Key,Map1) -> Map2 when - Key :: term(), - Map1 :: map(), - Map2 :: map(). - -remove(_,_) -> erlang:nif_error(undef). - - -%% Shadowed by erl_bif_types: map:to_list/1 --spec to_list(Map) -> [{Key,Value}] when - Map :: map(), - Key :: term(), - Value :: term(). - -to_list(_) -> erlang:nif_error(undef). - - -%% Shadowed by erl_bif_types: map:update/3 --spec update(Key,Value,Map1) -> Map2 when - Key :: term(), - Value :: term(), - Map1 :: map(), - Map2 :: map(). - -update(_,_,_) -> erlang:nif_error(undef). - - -%% Shadowed by erl_bif_types: map:values/1 --spec values(Map) -> Keys when - Map :: map(), - Keys :: [Key], - Key :: term(). - -values(_) -> erlang:nif_error(undef). - - -%%% End of BIFs - --spec foldl(Fun,Init,Map) -> Acc when - Fun :: fun((K, V, AccIn) -> AccOut), - Init :: term(), - Acc :: term(), - AccIn :: term(), - AccOut :: term(), - Map :: map(), - K :: term(), - V :: term(). - -foldl(Fun, Init, Map) -> - lists:foldl(fun({K,V},A) -> Fun(K,V,A) end,Init,map:to_list(Map)). - --spec foldr(Fun,Init,Map) -> Acc when - Fun :: fun((K,V,AccIn) -> AccOut), - Init :: term(), - Acc :: term(), - AccIn :: term(), - AccOut :: term(), - Map :: map(), - K :: term(), - V :: term(). - - -foldr(Fun, Init, Map) -> - lists:foldr(fun({K,V},A) -> Fun(K,V,A) end,Init,map:to_list(Map)). - - --spec map(Fun,Map1) -> Map2 when - Fun :: fun((K, V1) -> V2), - Map1 :: map(), - Map2 :: map(), - K :: term(), - V1 :: term(), - V2 :: term(). - - -map(Fun, Map) -> - map:from_list(lists:map(fun - ({K,V}) -> - {K,Fun(K,V)} - end,map:to_list(Map))). - --spec size(Map) -> non_neg_integer() when - Map :: map(). - -size(Map) -> - erlang:map_size(Map). - --spec without(Ks,Map1) -> Map2 when - Ks :: [K], - Map1 :: map(), - Map2 :: map(), - K :: term(). - -without(Ks, M) -> - map:from_list([{K,V}||{K,V} <- map:to_list(M), not lists:member(K, Ks)]). diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl new file mode 100644 index 0000000000..218dc28bec --- /dev/null +++ b/lib/stdlib/src/maps.erl @@ -0,0 +1,214 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(maps). + +-export([ + foldl/3, + foldr/3, + map/2, + size/1, + without/2 + ]). + + +%%% BIFs +-export([ + get/2, + find/2, + from_list/1, + is_key/2, + keys/1, + merge/2, + new/0, + put/3, + remove/2, + to_list/1, + update/3, + values/1 + ]). + +-type map() :: term(). %% FIXME: remove when erl_bif_types knows map(). + +%% Shadowed by erl_bif_types: maps:get/3 +-spec get(Key,Map) -> Value when + Key :: term(), + Map :: map(), + Value :: term(). + +get(_,_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: maps:find/3 +-spec find(Key,Map) -> {ok, Value} | error when + Key :: term(), + Map :: map(), + Value :: term(). + +find(_,_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: maps:from_list/1 +-spec from_list([{Key,Value}]) -> Map when + Key :: term(), + Value :: term(), + Map :: map(). + +from_list(_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: maps:is_key/2 +-spec is_key(Key,Map) -> boolean() when + Key :: term(), + Map :: map(). + +is_key(_,_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: maps:keys/1 +-spec keys(Map) -> Keys when + Map :: map(), + Keys :: [Key], + Key :: term(). + +keys(_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: maps:merge/2 +-spec merge(Map1,Map2) -> Map3 when + Map1 :: map(), + Map2 :: map(), + Map3 :: map(). + +merge(_,_) -> erlang:nif_error(undef). + + + +%% Shadowed by erl_bif_types: maps:new/0 +-spec new() -> Map when + Map :: map(). + +new() -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: maps:put/3 +-spec put(Key,Value,Map1) -> Map2 when + Key :: term(), + Value :: term(), + Map1 :: map(), + Map2 :: map(). + +put(_,_,_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: maps:put/3 +-spec remove(Key,Map1) -> Map2 when + Key :: term(), + Map1 :: map(), + Map2 :: map(). + +remove(_,_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: maps:to_list/1 +-spec to_list(Map) -> [{Key,Value}] when + Map :: map(), + Key :: term(), + Value :: term(). + +to_list(_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: maps:update/3 +-spec update(Key,Value,Map1) -> Map2 when + Key :: term(), + Value :: term(), + Map1 :: map(), + Map2 :: map(). + +update(_,_,_) -> erlang:nif_error(undef). + + +%% Shadowed by erl_bif_types: maps:values/1 +-spec values(Map) -> Keys when + Map :: map(), + Keys :: [Key], + Key :: term(). + +values(_) -> erlang:nif_error(undef). + + +%%% End of BIFs + +-spec foldl(Fun,Init,Map) -> Acc when + Fun :: fun((K, V, AccIn) -> AccOut), + Init :: term(), + Acc :: term(), + AccIn :: term(), + AccOut :: term(), + Map :: map(), + K :: term(), + V :: term(). + +foldl(Fun, Init, Map) -> + lists:foldl(fun({K,V},A) -> Fun(K,V,A) end,Init,maps:to_list(Map)). + +-spec foldr(Fun,Init,Map) -> Acc when + Fun :: fun((K,V,AccIn) -> AccOut), + Init :: term(), + Acc :: term(), + AccIn :: term(), + AccOut :: term(), + Map :: map(), + K :: term(), + V :: term(). + +foldr(Fun, Init, Map) -> + lists:foldr(fun({K,V},A) -> Fun(K,V,A) end,Init,maps:to_list(Map)). + + +-spec map(Fun,Map1) -> Map2 when + Fun :: fun((K, V1) -> V2), + Map1 :: map(), + Map2 :: map(), + K :: term(), + V1 :: term(), + V2 :: term(). + +map(Fun, Map) -> + maps:from_list(lists:map(fun + ({K,V}) -> + {K,Fun(K,V)} + end,maps:to_list(Map))). + + +-spec size(Map) -> non_neg_integer() when + Map :: map(). + +size(Map) -> + erlang:map_size(Map). + +-spec without(Ks,Map1) -> Map2 when + Ks :: [K], + Map1 :: map(), + Map2 :: map(), + K :: term(). + +without(Ks, M) -> + maps:from_list([{K,V}||{K,V} <- maps:to_list(M), not lists:member(K, Ks)]). diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src index 9a77ae9d66..a64b8e13c0 100644 --- a/lib/stdlib/src/stdlib.app.src +++ b/lib/stdlib/src/stdlib.app.src @@ -71,7 +71,7 @@ lib, lists, log_mf_h, - map, + maps, math, ms_transform, orddict, -- cgit v1.2.3 From 94963032a2bb7bd5bfe5ea05d99ca507e67e7ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 6 Nov 2013 11:56:58 +0100 Subject: stdlib: Remove bogus map() type --- lib/stdlib/src/maps.erl | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl index 218dc28bec..59ac80b46c 100644 --- a/lib/stdlib/src/maps.erl +++ b/lib/stdlib/src/maps.erl @@ -44,8 +44,6 @@ values/1 ]). --type map() :: term(). %% FIXME: remove when erl_bif_types knows map(). - %% Shadowed by erl_bif_types: maps:get/3 -spec get(Key,Map) -> Value when Key :: term(), -- cgit v1.2.3 From 5dda9e038891500a0849a3bf4d1079a355cdd30f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 24 Oct 2013 18:00:57 +0200 Subject: stdlib: Pretty print maps Pretty printing behaviour mimic records. This commit requires Map enabled bootstrap compiler. --- lib/stdlib/src/io_lib_pretty.erl | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'lib') diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl index 7637ad7a3d..f02a7921f8 100644 --- a/lib/stdlib/src/io_lib_pretty.erl +++ b/lib/stdlib/src/io_lib_pretty.erl @@ -101,6 +101,7 @@ print(Term, Col, Ll, D, M, RecDefFun, Enc, Str) when Col =< 0 -> print(Term, 1, Ll, D, M, RecDefFun, Enc, Str); print(Term, Col, Ll, D, M0, RecDefFun, Enc, Str) when is_tuple(Term); is_list(Term); + is_map(Term); is_bitstring(Term) -> If = {_S, Len} = print_length(Term, D, RecDefFun, Enc, Str), M = max_cs(M0, Len), @@ -137,6 +138,10 @@ pp({{tuple,true,L}, _Len}, Col, Ll, M, TInd, Ind, LD, W) -> [${, pp_tag_tuple(L, Col, Ll, M, TInd, Ind, LD, W + 1), $}]; pp({{tuple,false,L}, _Len}, Col, Ll, M, TInd, Ind, LD, W) -> [${, pp_list(L, Col + 1, Ll, M, TInd, indent(1, Ind), LD, $,, W + 1), $}]; +pp({{map,Pairs},_Len}, Col, Ll, M, TInd, Ind, LD, W) -> + [$#,${, pp_list(Pairs, Col + 2, Ll, M, TInd, indent(2, Ind), LD, $,, W + 1), $}]; +pp({{map_pair,K,V},_Len}, Col, Ll, M, TInd, Ind, LD, W) -> + [pp(K, Col, Ll, M, TInd, Ind, LD, W), " => ", pp(V, Col, Ll, M, TInd, Ind, LD, W)]; pp({{record,[{Name,NLen} | L]}, _Len}, Col, Ll, M, TInd, Ind, LD, W) -> [Name, ${, pp_record(L, NLen, Col, Ll, M, TInd, Ind, LD, W + NLen+1), $}]; pp({{bin,S}, _Len}, Col, Ll, M, _TInd, Ind, LD, W) -> @@ -283,6 +288,10 @@ write({{tuple, _IsTagged, L}, _}) -> [${, write_list(L, $,), $}]; write({{list, L}, _}) -> [$[, write_list(L, $|), $]]; +write({{map, Pairs}, _}) -> + [$#,${, write_list(Pairs, $,), $}]; +write({{map_pair, K, V}, _}) -> + [write(K)," => ",write(V)]; write({{record, [{Name,_} | L]}, _}) -> [Name, ${, write_fields(L), $}]; write({{bin, S}, _}) -> @@ -331,6 +340,8 @@ print_length([], _D, _RF, _Enc, _Str) -> {"[]", 2}; print_length({}, _D, _RF, _Enc, _Str) -> {"{}", 2}; +print_length(#{}=M, _D, _RF, _Enc, _Str) when map_size(M) =:= 0 -> + {"#{}", 3}; print_length(List, D, RF, Enc, Str) when is_list(List) -> case Str andalso printable_list(List, D, Enc) of true -> @@ -356,6 +367,8 @@ print_length(R, D, RF, Enc, Str) when is_atom(element(1, R)), end; print_length(Tuple, D, RF, Enc, Str) when is_tuple(Tuple) -> print_length_tuple(Tuple, D, RF, Enc, Str); +print_length(Map, D, RF, Enc, Str) when is_map(Map) -> + print_length_map(Map, D, RF, Enc, Str); print_length(<<>>, _D, _RF, _Enc, _Str) -> {"<<>>", 4}; print_length(<<_/bitstring>>, 1, _RF, _Enc, _Str) -> @@ -389,6 +402,25 @@ print_length(Term, _D, _RF, _Enc, _Str) -> S = io_lib:write(Term), {S, lists:flatlength(S)}. +print_length_map(_Map, 1, _RF, _Enc, _Str) -> + {"#{...}", 6}; +print_length_map(Map, D, RF, Enc, Str) when is_map(Map) -> + Pairs = print_length_map_pairs(maps:to_list(Map), D, RF, Enc, Str), + {{map, Pairs}, list_length(Pairs, 3)}. + +print_length_map_pairs([], _D, _RF, _Enc, _Str) -> + []; +print_length_map_pairs(_Pairs, 1, _RF, _Enc, _Str) -> + {dots, 3}; +print_length_map_pairs([{K,V}|Pairs], D, RF, Enc, Str) -> + [print_length_map_pair(K,V,D-1,RF,Enc,Str) | + print_length_map_pairs(Pairs,D-1,RF,Enc,Str)]. + +print_length_map_pair(K, V, D, RF, Enc, Str) -> + {KS, KL} = print_length(K, D, RF, Enc, Str), + {VS, VL} = print_length(V, D, RF, Enc, Str), + {{map_pair, {KS,KL}, {VS,VL}}, KL + VL}. + print_length_tuple(_Tuple, 1, _RF, _Enc, _Str) -> {"{...}", 5}; print_length_tuple(Tuple, D, RF, Enc, Str) -> -- cgit v1.2.3 From 1a234c9eba8ac2c78f97e5f3e33521b8cc5d3748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 24 Oct 2013 18:13:31 +0200 Subject: stdlib: Strengthen Map module with guards This commit requires Map enabled bootstrap compiler. --- lib/stdlib/src/maps.erl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl index 59ac80b46c..0e6a1ddb1a 100644 --- a/lib/stdlib/src/maps.erl +++ b/lib/stdlib/src/maps.erl @@ -164,7 +164,7 @@ values(_) -> erlang:nif_error(undef). K :: term(), V :: term(). -foldl(Fun, Init, Map) -> +foldl(Fun, Init, Map) when is_function(Fun,3), is_map(Map) -> lists:foldl(fun({K,V},A) -> Fun(K,V,A) end,Init,maps:to_list(Map)). -spec foldr(Fun,Init,Map) -> Acc when @@ -177,7 +177,7 @@ foldl(Fun, Init, Map) -> K :: term(), V :: term(). -foldr(Fun, Init, Map) -> +foldr(Fun, Init, Map) when is_function(Fun,3), is_map(Map) -> lists:foldr(fun({K,V},A) -> Fun(K,V,A) end,Init,maps:to_list(Map)). @@ -189,7 +189,7 @@ foldr(Fun, Init, Map) -> V1 :: term(), V2 :: term(). -map(Fun, Map) -> +map(Fun, Map) when is_function(Fun, 2), is_map(Map) -> maps:from_list(lists:map(fun ({K,V}) -> {K,Fun(K,V)} @@ -199,14 +199,15 @@ map(Fun, Map) -> -spec size(Map) -> non_neg_integer() when Map :: map(). -size(Map) -> +size(Map) when is_map(Map) -> erlang:map_size(Map). + -spec without(Ks,Map1) -> Map2 when Ks :: [K], Map1 :: map(), Map2 :: map(), K :: term(). -without(Ks, M) -> +without(Ks, M) when is_list(Ks), is_map(M) -> maps:from_list([{K,V}||{K,V} <- maps:to_list(M), not lists:member(K, Ks)]). -- cgit v1.2.3 From 701ed39cc53e40783c4b48c83f9f686eb2668d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 12 Dec 2013 13:24:05 +0100 Subject: compiler: Fix term order compiler for maps --- lib/compiler/src/v3_codegen.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index db8ea04778..c8735a76e8 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -1572,7 +1572,7 @@ cg_map_pairs(Es0) -> end || {_,Vs} <- R2], %% R3 is now [{Op,{Key,Value}}] - R = lists:sort(R3), + R = termsort(R3), %% R4 is now sorted with all alloc first in the list, followed by %% all exact. @@ -1611,6 +1611,9 @@ map_pair_op_and_key(L) -> {assoc,K} end. +termsort(Ls) -> + lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, Ls). + %%% %%% Code generation for constructing binaries. %%% -- cgit v1.2.3 From 1f6c5e88f4f387cc27c9affbdf1ceaae7cfdcfd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 13 Jan 2014 17:33:44 +0100 Subject: compiler: Squash #k_map_pair_*{} to #k_map_pair{} Simplify compiler internals for kernel passes. --- lib/compiler/src/v3_kernel.erl | 20 +++++++++----------- lib/compiler/src/v3_kernel.hrl | 3 +-- lib/compiler/src/v3_kernel_pp.erl | 4 ++-- lib/compiler/src/v3_life.erl | 8 +++----- 4 files changed, 15 insertions(+), 20 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index a5a94c1294..1d7d168214 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -503,11 +503,11 @@ map_pairs(Es, Sub, St) -> (#c_map_pair_assoc{key=K0,val=V0}, {Kes,Esp,St0}) -> {K,[],St1} = expr(K0, Sub, St0), {V,Ep,St2} = atomic(V0, Sub, St1), - {[#k_map_pair_assoc{key=K,val=V}|Kes],Ep ++ Esp,St2}; + {[#k_map_pair{op=assoc,key=K,val=V}|Kes],Ep ++ Esp,St2}; (#c_map_pair_exact{key=K0,val=V0}, {Kes,Esp,St0}) -> {K,[],St1} = expr(K0, Sub, St0), {V,Ep,St2} = atomic(V0, Sub, St1), - {[#k_map_pair_exact{key=K,val=V}|Kes],Ep ++ Esp,St2} + {[#k_map_pair{op=exact,key=K,val=V}|Kes],Ep ++ Esp,St2} end, {[],[],St}, Es). %% call_type(Module, Function, Arity) -> call | bif | apply | error. @@ -671,7 +671,7 @@ pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) -> pattern(#c_map_pair_exact{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) -> {Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0), {Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1), - {#k_map_pair_exact{anno=A,key=Kk,val=Kv},Osub2,St2}; + {#k_map_pair{anno=A,op=exact,key=Kk,val=Kv},Osub2,St2}; pattern(#c_binary{anno=A,segments=Cv}, Isub, Osub0, St0) -> {Kv,Osub1,St1} = pattern_bin(Cv, Isub, Osub0, St0), {#k_binary{anno=A,segs=Kv},Osub1,St1}; @@ -1348,8 +1348,8 @@ get_match(#k_tuple{es=Es}, St0) -> get_match(#k_map{es=Es0}, St0) -> {Mes,St1} = new_vars(length(Es0), St0), {Es,_} = mapfoldl(fun - (#k_map_pair_exact{}=Pair, [V|Vs]) -> - {Pair#k_map_pair_exact{val=V},Vs} + (#k_map_pair{}=Pair, [V|Vs]) -> + {Pair#k_map_pair{val=V},Vs} end, Mes, Es0), {#k_map{es=Es},Mes,St1}; get_match(M, St) -> @@ -1370,7 +1370,7 @@ new_clauses(Cs0, U, St) -> [N|As]; #k_map{es=Es} -> Vals = [V || - #k_map_pair_exact{val=V} <- Es], + #k_map_pair{op=exact,val=V} <- Es], Vals ++ As; _Other -> As @@ -1472,7 +1472,7 @@ arg_val(Arg, C) -> end; #k_map{es=Es} -> Keys = [begin - #k_map_pair_exact{key=#k_literal{val=Key}} = Pair, + #k_map_pair{op=exact,key=#k_literal{val=Key}} = Pair, Key end || Pair <- Es], %% multiple keys may have the same name @@ -1848,9 +1848,7 @@ lit_vars(#k_cons{hd=H,tl=T}) -> union(lit_vars(H), lit_vars(T)); lit_vars(#k_map{var=Var,es=Es}) -> lit_list_vars([Var|Es]); -lit_vars(#k_map_pair_assoc{key=K,val=V}) -> - union(lit_vars(K), lit_vars(V)); -lit_vars(#k_map_pair_exact{key=K,val=V}) -> +lit_vars(#k_map_pair{key=K,val=V}) -> union(lit_vars(K), lit_vars(V)); lit_vars(#k_binary{segs=V}) -> lit_vars(V); lit_vars(#k_bin_end{}) -> []; @@ -1890,7 +1888,7 @@ pat_vars(#k_tuple{es=Es}) -> pat_list_vars(Es); pat_vars(#k_map{es=Es}) -> pat_list_vars(Es); -pat_vars(#k_map_pair_exact{val=V}) -> +pat_vars(#k_map_pair{op=exact,val=V}) -> pat_vars(V). pat_list_vars(Ps) -> diff --git a/lib/compiler/src/v3_kernel.hrl b/lib/compiler/src/v3_kernel.hrl index 9da394d50d..c7886a070d 100644 --- a/lib/compiler/src/v3_kernel.hrl +++ b/lib/compiler/src/v3_kernel.hrl @@ -39,8 +39,7 @@ -record(k_tuple, {anno=[],es}). -record(k_map, {anno=[],var,es}). --record(k_map_pair_assoc, {anno=[],key,val}). --record(k_map_pair_exact, {anno=[],key,val}). +-record(k_map_pair, {anno=[],op,key,val}). -record(k_cons, {anno=[],hd,tl}). -record(k_binary, {anno=[],segs}). -record(k_bin_seg, {anno=[],size,unit,type,flags,seg,next}). diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl index e8d19336f6..edbd3f74f8 100644 --- a/lib/compiler/src/v3_kernel_pp.erl +++ b/lib/compiler/src/v3_kernel_pp.erl @@ -115,9 +115,9 @@ format_1(#k_map{es=Es}, Ctxt) -> format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), $},$~ ]; -format_1(#k_map_pair_assoc{key=K,val=V}, Ctxt) -> +format_1(#k_map_pair{op=assoc,key=K,val=V}, Ctxt) -> ["~<",format(K, Ctxt),",",format(V, Ctxt),">"]; -format_1(#k_map_pair_exact{key=K,val=V}, Ctxt) -> +format_1(#k_map_pair{op=exact,key=K,val=V}, Ctxt) -> ["::<",format(K, Ctxt),",",format(V, Ctxt),">"]; format_1(#k_binary{segs=S}, Ctxt) -> ["#<",format(S, ctxt_bump_indent(Ctxt, 2)),">#"]; diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl index a3bd781c98..ae928e955c 100644 --- a/lib/compiler/src/v3_life.erl +++ b/lib/compiler/src/v3_life.erl @@ -369,9 +369,9 @@ literal(#k_tuple{es=Es}, Ctxt) -> {tuple,literal_list(Es, Ctxt)}; literal(#k_map{var=Var,es=Es}, Ctxt) -> {map,literal(Var, Ctxt),literal_list(Es, Ctxt)}; -literal(#k_map_pair_assoc{key=K,val=V}, Ctxt) -> +literal(#k_map_pair{op=assoc,key=K,val=V}, Ctxt) -> {map_pair_assoc,literal(K, Ctxt),literal(V, Ctxt)}; -literal(#k_map_pair_exact{key=K,val=V}, Ctxt) -> +literal(#k_map_pair{op=exact,key=K,val=V}, Ctxt) -> {map_pair_exact,literal(K, Ctxt),literal(V, Ctxt)}; literal(#k_literal{val=V}, _Ctxt) -> {literal,V}. @@ -404,9 +404,7 @@ literal2(#k_tuple{es=Es}, Ctxt) -> {tuple,literal_list2(Es, Ctxt)}; literal2(#k_map{es=Es}, Ctxt) -> {map,literal_list2(Es, Ctxt)}; -literal2(#k_map_pair_assoc{key=K,val=V}, Ctxt) -> - {map_pair,literal2(K, Ctxt),literal2(V, Ctxt)}; -literal2(#k_map_pair_exact{key=K,val=V}, Ctxt) -> +literal2(#k_map_pair{key=K,val=V}, Ctxt) -> {map_pair,literal2(K, Ctxt),literal2(V, Ctxt)}. literal_list2(Ks, Ctxt) -> -- cgit v1.2.3 From 5b4b6c9b6bd1cf258841004f2e11bb2a82ebfe24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 13 Jan 2014 18:05:00 +0100 Subject: compiler: Squash #c_map_pair_*{} to #c_map_pair{} Simplify compiler internals and parsing of core format. --- lib/compiler/src/cerl.erl | 106 +++++++++-------------------------- lib/compiler/src/cerl_inline.erl | 46 ++++++++++++++- lib/compiler/src/cerl_trees.erl | 65 ++++++++++----------- lib/compiler/src/core_lib.erl | 4 +- lib/compiler/src/core_lint.erl | 10 +--- lib/compiler/src/core_parse.hrl | 8 +-- lib/compiler/src/core_parse.yrl | 6 +- lib/compiler/src/core_pp.erl | 4 +- lib/compiler/src/sys_core_dsetel.erl | 10 +--- lib/compiler/src/sys_core_fold.erl | 16 ++---- lib/compiler/src/v3_core.erl | 15 +++-- lib/compiler/src/v3_kernel.erl | 11 ++-- 12 files changed, 134 insertions(+), 167 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index c6bf046c4d..60a8559950 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -124,11 +124,11 @@ %% keep map exports here for now map_es/1, - update_c_map_skel/2, - update_c_map_pair_assoc_skel/2, update_c_map_pair_exact_skel/2, - ann_c_map_skel/2, - ann_c_map_pair_assoc_skel/2, ann_c_map_pair_exact_skel/2, - map_pair_es/1, map_pair_assoc_es/1, map_pair_exact_es/1 + update_c_map/2, + ann_c_map/2, + map_pair_op/1,map_pair_key/1,map_pair_val/1, + update_c_map_pair/4, + ann_c_map_pair/4 ]). -export_type([c_binary/0, c_call/0, c_clause/0, c_cons/0, c_fun/0, c_literal/0, @@ -155,8 +155,7 @@ -type c_letrec() :: #c_letrec{}. -type c_literal() :: #c_literal{}. -type c_map() :: #c_map{}. --type c_map_pair_assoc() :: #c_map_pair_assoc{}. --type c_map_pair_exact() :: #c_map_pair_exact{}. +-type c_map_pair() :: #c_map_pair{}. -type c_module() :: #c_module{}. -type c_primop() :: #c_primop{}. -type c_receive() :: #c_receive{}. @@ -169,7 +168,7 @@ -type cerl() :: c_alias() | c_apply() | c_binary() | c_bitstr() | c_call() | c_case() | c_catch() | c_clause() | c_cons() | c_fun() | c_let() | c_letrec() | c_literal() - | c_map() | c_map_pair_assoc() | c_map_pair_exact() + | c_map() | c_map_pair() | c_module() | c_primop() | c_receive() | c_seq() | c_try() | c_tuple() | c_values() | c_var(). @@ -282,8 +281,7 @@ type(#c_let{}) -> 'let'; type(#c_letrec{}) -> letrec; type(#c_literal{}) -> literal; type(#c_map{}) -> map; -type(#c_map_pair_assoc{}) -> map_pair_assoc; -type(#c_map_pair_exact{}) -> map_pair_exact; +type(#c_map_pair{}) -> map_pair; type(#c_module{}) -> module; type(#c_primop{}) -> primop; type(#c_receive{}) -> 'receive'; @@ -1581,50 +1579,24 @@ ann_make_list(_, [], Node) -> map_es(#c_map{es = Es}) -> Es. -map_pair_assoc_es(#c_map_pair_assoc{key=K,val=V}) -> [K,V]. -map_pair_exact_es(#c_map_pair_exact{key=K,val=V}) -> [K,V]. -map_pair_es(#c_map_pair_assoc{key=K,val=V}) -> [K,V]; -map_pair_es(#c_map_pair_exact{key=K,val=V}) -> [K,V]. - -update_c_map_pair_assoc_skel(Old, [K,V]) -> - #c_map_pair_assoc{key=K, val=V, anno = get_ann(Old)}. - -update_c_map_pair_exact_skel(Old, [K,V]) -> - #c_map_pair_exact{key=K, val=V, anno = get_ann(Old)}. - -ann_c_map_pair_assoc_skel(As, [K,V]) -> - #c_map_pair_assoc{key = K, val=V, anno = As}. - -ann_c_map_pair_exact_skel(As, [K,V]) -> - #c_map_pair_exact{key = K, val=V, anno = As}. - -%c_map_skel(Es) -> -% #c_map{es = Es}. -% - ann_c_map(As, Es) -> #c_map{es = Es, anno = As }. -%% TODO: when we have map literals use a variant of -%% case is_lit_list(Es) of -%% false -> -%% #c_map{es = Es, anno = As}; -%% true -> -%% #c_literal{val = maps:from_list(lit_list_vals(Es)), anno = As} -%% end. - -ann_c_map_pair_assoc(As, [K,V]) -> - #c_map_pair_assoc{key = K, val=V, anno = As}. +update_c_map(Old, Es) -> + #c_map{es = Es, anno = get_ann(Old)}. -ann_c_map_pair_exact(As, [K,V]) -> - #c_map_pair_exact{key = K, val=V, anno = As}. +map_pair_key(#c_map_pair{key=K}) -> K. +map_pair_val(#c_map_pair{val=V}) -> V. +map_pair_op(#c_map_pair{op=Op}) -> Op. +-spec ann_c_map_pair([term()], cerl(), cerl(), cerl()) -> + c_map_pair(). -ann_c_map_skel(As, Es) -> - #c_map{es = Es, anno = As}. +ann_c_map_pair(As,Op,K,V) -> + #c_map_pair{op=Op, key = K, val=V, anno = As}. -update_c_map_skel(Old, Es) -> - #c_map{es = Es, anno = get_ann(Old)}. +update_c_map_pair(Old,Op,K,V) -> + #c_map_pair{op=Op, key=K, val=V, anno = get_ann(Old)}. %% --------------------------------------------------------------------- @@ -3017,8 +2989,8 @@ pat_vars(Node, Vs) -> pat_list_vars(tuple_es(Node), Vs); map -> pat_list_vars(map_es(Node), Vs); - map_pair_exact -> - pat_list_vars(map_pair_exact_es(Node), Vs); + map_pair -> + pat_list_vars([map_pair_op(Node),map_pair_key(Node),map_pair_val(Node)],Vs); binary -> pat_list_vars(binary_segments(Node), Vs); bitstr -> @@ -3830,12 +3802,6 @@ is_data(#c_cons{}) -> true; is_data(#c_tuple{}) -> true; -is_data(#c_map{}) -> - true; -is_data(#c_map_pair_assoc{}) -> - true; -is_data(#c_map_pair_exact{}) -> - true; is_data(_) -> false. @@ -3881,14 +3847,7 @@ data_type(#c_literal{val = V}) -> data_type(#c_cons{}) -> cons; data_type(#c_tuple{}) -> - tuple; -data_type(#c_map{}) -> - map; -data_type(#c_map_pair_assoc{}) -> - map_pair_assoc; -data_type(#c_map_pair_exact{}) -> - map_pair_exact. - + tuple. %% @spec data_es(Node::cerl()) -> [cerl()] %% @@ -3919,14 +3878,7 @@ data_es(#c_literal{val = V}) -> data_es(#c_cons{hd = H, tl = T}) -> [H, T]; data_es(#c_tuple{es = Es}) -> - Es; -data_es(#c_map{es=Es}) -> - Es; -data_es(#c_map_pair_assoc{key=K,val=V}) -> - [K,V]; -data_es(#c_map_pair_exact{key=K,val=V}) -> - [K,V]. - + Es. %% @spec data_arity(Node::cerl()) -> integer() %% @@ -3982,11 +3934,7 @@ make_data(CType, Es) -> ann_make_data(As, {atomic, V}, []) -> #c_literal{val = V, anno = As}; ann_make_data(As, cons, [H, T]) -> ann_c_cons(As, H, T); -ann_make_data(As, tuple, Es) -> ann_c_tuple(As, Es); -ann_make_data(As, map, Es) -> ann_c_map(As, Es); -ann_make_data(As, map_pair_assoc, Es) -> ann_c_map_pair_assoc(As, Es); -ann_make_data(As, map_pair_exact, Es) -> ann_c_map_pair_exact(As, Es). - +ann_make_data(As, tuple, Es) -> ann_c_tuple(As, Es). %% @spec update_data(Old::cerl(), Type::dtype(), %% Elements::[cerl()]) -> cerl() @@ -4119,10 +4067,8 @@ subtrees(T) -> [tuple_es(T)]; map -> [map_es(T)]; - map_pair_assoc -> - [map_pair_assoc_es(T)]; - map_pair_exact -> - [map_pair_exact_es(T)]; + map_pair -> + [[map_pair_op(T)],[map_pair_key(T)],[map_pair_val(T)]]; 'let' -> [let_vars(T), [let_arg(T)], [let_body(T)]]; seq -> diff --git a/lib/compiler/src/cerl_inline.erl b/lib/compiler/src/cerl_inline.erl index c6de63c69f..3837b57750 100644 --- a/lib/compiler/src/cerl_inline.erl +++ b/lib/compiler/src/cerl_inline.erl @@ -63,7 +63,11 @@ receive_clauses/1, receive_timeout/1, seq_arg/1, 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]). + type/1, values_es/1, var_name/1, + map_es/1, update_c_map/2, + update_c_map_pair/4, + map_pair_op/1, map_pair_key/1, map_pair_val/1 + ]). -import(lists, [foldl/3, foldr/3, mapfoldl/3, reverse/1]). @@ -128,6 +132,8 @@ weight(call) -> 3; % Assume remote-calls as efficient as `apply'. weight(primop) -> 2; % Assume more efficient than `apply'. weight(binary) -> 4; % Initialisation base cost. weight(bitstr) -> 3; % Coding/decoding a value; like a primop. +weight(map) -> 4; % Initialisation base cost. +weight(map_pair) -> 3; % Coding/decoding a value; like a primop. weight(module) -> 1. % Like a letrec with a constant body %% These "reference" structures are used for variables and function @@ -333,6 +339,8 @@ i(E, Ctxt, Ren, Env, S0) -> i_catch(E, Ctxt, Ren, Env, S); binary -> i_binary(E, Ren, Env, S); + map -> + i_map(E, Ctxt, Ren, Env, S); module -> i_module(E, Ctxt, Ren, Env, S) end @@ -1324,6 +1332,25 @@ i_bitstr(E, Ren, Env, S) -> S3 = count_size(weight(bitstr), S2), {update_c_bitstr(E, Val, Size, Unit, Type, Flags), S3}. +i_map(E, Ctx, Ren, Env, S) -> + %% Visit the segments for value. + {Es, S1} = mapfoldl(fun (E, S) -> + i_map_pair(E, Ctx, Ren, Env, S) + end, + S, map_es(E)), + S2 = count_size(weight(map), S1), + {update_c_map(E, Es), S2}. + +i_map_pair(E, Ctx, Ren, Env, S) -> + %% It is not necessary to visit the Op and Key fields, + %% since these are always literals. + {Val, S1} = i(map_pair_val(E), Ctx, Ren, Env, S), + Op = map_pair_op(E), + Key = map_pair_key(E), + S2 = count_size(weight(map_pair), S1), + {update_c_map_pair(E, Op, Key, Val), S2}. + + %% This is a simplified version of `i_pattern', for lists of parameter %% variables only. It does not modify the state. @@ -1383,6 +1410,14 @@ i_pattern(E, Ren, Env, Ren0, Env0, S) -> S, binary_segments(E)), S2 = count_size(weight(binary), S1), {update_c_binary(E, Es), S2}; + map -> + {Es, S1} = mapfoldl(fun (E, S) -> + i_map_pair_pattern(E, Ren, Env, + Ren0, Env0, S) + end, + S, map_es(E)), + S2 = count_size(weight(map), S1), + {update_c_map(E, Es), S2}; _ -> case is_literal(E) of true -> @@ -1416,6 +1451,15 @@ i_bitstr_pattern(E, Ren, Env, Ren0, Env0, S) -> S3 = count_size(weight(bitstr), S2), {update_c_bitstr(E, Val, Size, Unit, Type, Flags), S3}. +i_map_pair_pattern(E, Ren, Env, Ren0, Env0, S) -> + %% It is not necessary to visit the Op it is always a literal. + %% Same goes for Key + {Val, S1} = i_pattern(map_pair_val(E), Ren, Env, Ren0, Env0, S), + Op = map_pair_op(E), %% should be 'exact' literal + Key = map_pair_key(E), + S2 = count_size(weight(map_pair), S1), + {update_c_map_pair(E, Op, Key, Val), S2}. + %% --------------------------------------------------------------------- %% Other central inlining functions diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl index dc1cc606b3..2542841eef 100644 --- a/lib/compiler/src/cerl_trees.erl +++ b/lib/compiler/src/cerl_trees.erl @@ -58,11 +58,11 @@ update_c_values/2, values_es/1, var_name/1, map_es/1, - update_c_map_skel/2, - update_c_map_pair_assoc_skel/2, update_c_map_pair_exact_skel/2, - ann_c_map_skel/2, - ann_c_map_pair_assoc_skel/2, ann_c_map_pair_exact_skel/2, - map_pair_assoc_es/1, map_pair_exact_es/1 + ann_c_map/2, + update_c_map/2, + map_pair_key/1,map_pair_val/1,map_pair_op/1, + ann_c_map_pair/4, + update_c_map_pair/4 ]). @@ -138,11 +138,11 @@ map_1(F, T) -> tuple -> update_c_tuple_skel(T, map_list(F, tuple_es(T))); map -> - update_c_map_skel(T, map_list(F, map_es(T))); - map_pair_assoc -> - update_c_map_pair_assoc_skel(T, map_list(F, map_pair_assoc_es(T))); - map_pair_exact -> - update_c_map_pair_exact_skel(T, map_list(F, map_pair_exact_es(T))); + update_c_map(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)), + map(F, map_pair_val(T))); 'let' -> update_c_let(T, map_list(F, let_vars(T)), map(F, let_arg(T)), @@ -251,10 +251,12 @@ fold_1(F, S, T) -> fold_list(F, S, tuple_es(T)); map -> fold_list(F, S, map_es(T)); - map_pair_assoc -> - fold_list(F, S, map_pair_assoc_es(T)); - map_pair_exact -> - fold_list(F, S, map_pair_exact_es(T)); + map_pair -> + fold(F, + fold(F, + fold(F, S, map_pair_op(T)), + map_pair_key(T)), + map_pair_val(T)); 'let' -> fold(F, fold(F, fold_list(F, S, let_vars(T)), let_arg(T)), @@ -371,13 +373,12 @@ mapfold(F, S0, T) -> F(update_c_tuple_skel(T, Ts), S1); map -> {Ts, S1} = mapfold_list(F, S0, map_es(T)), - F(update_c_map_skel(T, Ts), S1); - map_pair_assoc -> - {Ts, S1} = mapfold_list(F, S0, map_pair_assoc_es(T)), - F(update_c_map_pair_assoc_skel(T,Ts), S1); - map_pair_exact -> - {Ts, S1} = mapfold_list(F, S0, map_pair_exact_es(T)), - F(update_c_map_pair_exact_skel(T,Ts), S1); + F(update_c_map(T, Ts), S1); + map_pair -> + {Op, S1} = mapfold(F, S0, map_pair_op(T)), + {Key, S2} = mapfold(F, S1, map_pair_key(T)), + {Val, S3} = mapfold(F, S2, map_pair_val(T)), + F(update_c_map_pair(T,Op,Key,Val), S3); 'let' -> {Vs, S1} = mapfold_list(F, S0, let_vars(T)), {A, S2} = mapfold(F, S1, let_arg(T)), @@ -519,10 +520,8 @@ variables(T, S) -> vars_in_list(tuple_es(T), S); map -> vars_in_list(map_es(T), S); - map_pair_assoc -> - vars_in_list(map_pair_assoc_es(T), S); - map_pair_exact -> - vars_in_list(map_pair_exact_es(T), S); + map_pair -> + vars_in_list([map_pair_op(T),map_pair_key(T), map_pair_val(T)], S); 'let' -> Vs = variables(let_body(T), S), Vs1 = var_list_names(let_vars(T)), @@ -726,15 +725,13 @@ label(T, N, Env) -> map -> {Ts, N1} = label_list(map_es(T), N, Env), {As, N2} = label_ann(T, N1), - {ann_c_map_skel(As, Ts), N2}; - map_pair_assoc -> - {Ts, N1} = label_list(map_pair_assoc_es(T), N, Env), - {As, N2} = label_ann(T, N1), - {ann_c_map_pair_assoc_skel(As, Ts), N2}; - map_pair_exact -> - {Ts, N1} = label_list(map_pair_exact_es(T), N, Env), - {As, N2} = label_ann(T, N1), - {ann_c_map_pair_exact_skel(As, Ts), N2}; + {ann_c_map(As, Ts), N2}; + map_pair -> + {Op, N1} = label(map_pair_op(T), N, Env), + {Val, N2} = label(map_pair_key(T), N1, Env), + {Key, N3} = label(map_pair_val(T), N2, Env), + {As, N4} = label_ann(T, N3), + {ann_c_map_pair(As,Op,Key,Val), N4}; 'let' -> {A, N1} = label(let_arg(T), N, Env), {Vs, N2, Env1} = label_vars(let_vars(T), N1, Env), diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl index d6221a22bb..f506901099 100644 --- a/lib/compiler/src/core_lib.erl +++ b/lib/compiler/src/core_lib.erl @@ -107,9 +107,7 @@ vu_expr(V, #c_tuple{es=Es}) -> vu_expr_list(V, Es); vu_expr(V, #c_map{es=Es}) -> vu_expr_list(V, Es); -vu_expr(V, #c_map_pair_assoc{key=Key,val=Val}) -> - vu_expr_list(V, [Key,Val]); -vu_expr(V, #c_map_pair_exact{key=Key,val=Val}) -> +vu_expr(V, #c_map_pair{key=Key,val=Val}) -> vu_expr_list(V, [Key,Val]); vu_expr(V, #c_binary{segments=Ss}) -> vu_seg_list(V, Ss); diff --git a/lib/compiler/src/core_lint.erl b/lib/compiler/src/core_lint.erl index 185193e45d..36165245a6 100644 --- a/lib/compiler/src/core_lint.erl +++ b/lib/compiler/src/core_lint.erl @@ -256,9 +256,7 @@ gexpr(#c_tuple{es=Es}, Def, _Rt, St) -> gexpr_list(Es, Def, St); gexpr(#c_map{es=Es}, Def, _Rt, St) -> gexpr_list(Es, Def, St); -gexpr(#c_map_pair_assoc{key=K,val=V}, Def, _Rt, St) -> - gexpr_list([K,V], Def, St); -gexpr(#c_map_pair_exact{key=K,val=V}, Def, _Rt, St) -> +gexpr(#c_map_pair{key=K,val=V}, Def, _Rt, St) -> gexpr_list([K,V], Def, St); gexpr(#c_binary{segments=Ss}, Def, _Rt, St) -> gbitstr_list(Ss, Def, St); @@ -312,9 +310,7 @@ expr(#c_tuple{es=Es}, Def, _Rt, St) -> expr_list(Es, Def, St); expr(#c_map{es=Es}, Def, _Rt, St) -> expr_list(Es, Def, St); -expr(#c_map_pair_assoc{key=K,val=V},Def,_Rt,St) -> - expr_list([K,V],Def,St); -expr(#c_map_pair_exact{key=K,val=V},Def,_Rt,St) -> +expr(#c_map_pair{key=K,val=V},Def,_Rt,St) -> expr_list([K,V],Def,St); expr(#c_binary{segments=Ss}, Def, _Rt, St) -> bitstr_list(Ss, Def, St); @@ -469,7 +465,7 @@ pattern(#c_tuple{es=Es}, Def, Ps, St) -> pattern_list(Es, Def, Ps, St); pattern(#c_map{es=Es}, Def, Ps, St) -> pattern_list(Es, Def, Ps, St); -pattern(#c_map_pair_exact{key=K,val=V},Def,Ps,St) -> +pattern(#c_map_pair{op=#c_literal{val=exact},key=K,val=V},Def,Ps,St) -> pattern_list([K,V],Def,Ps,St); pattern(#c_binary{segments=Ss}, Def, Ps, St0) -> St = pat_bin_tail_check(Ss, St0), diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl index 40c3ae4582..067a1ae895 100644 --- a/lib/compiler/src/core_parse.hrl +++ b/lib/compiler/src/core_parse.hrl @@ -97,10 +97,10 @@ -record(c_var, {anno=[], name :: cerl:var_name()}). --record(c_map_pair_assoc, {anno=[], key, val}). - --record(c_map_pair_exact, {anno=[], key, val}). +-record(c_map_pair, {anno=[], + op :: 'assoc' | 'exact', + key, val}). -record(c_map, {anno=[], var=#c_literal{val=[]} :: #c_var{} | #c_literal{}, - es :: [#c_map_pair_assoc{}|#c_map_pair_exact{}]}). + es :: [#c_map_pair{}]}). diff --git a/lib/compiler/src/core_parse.yrl b/lib/compiler/src/core_parse.yrl index 82ba11b0fc..67acb0856c 100644 --- a/lib/compiler/src/core_parse.yrl +++ b/lib/compiler/src/core_parse.yrl @@ -190,7 +190,7 @@ map_pair_patterns -> map_pair_pattern : ['$1']. map_pair_patterns -> map_pair_pattern ',' map_pair_patterns : ['$1' | '$3']. map_pair_pattern -> '~' '<' anno_pattern ',' anno_pattern '>' : - #c_map_pair_exact{key='$3',val='$5'}. + #c_map_pair{op=exact,key='$3',val='$5'}. cons_pattern -> '[' anno_pattern tail_pattern : #c_cons{hd='$2',tl='$3'}. @@ -296,9 +296,9 @@ map_pair -> map_pair_assoc : '$1'. map_pair -> map_pair_exact : '$1'. map_pair_assoc -> '::' '<' anno_expression ',' anno_expression'>' : - #c_map_pair_assoc{key='$3',val='$5'}. + #c_map_pair{op=#c_literal{val=assoc},key='$3',val='$5'}. map_pair_exact -> '~' '<' anno_expression ',' anno_expression'>' : - #c_map_pair_exact{key='$3',val='$5'}. + #c_map_pair{op=#c_literal{val=exact},key='$3',val='$5'}. cons -> '[' anno_expression tail : c_cons('$2', '$3'). diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl index f775d87507..faa26ec6df 100644 --- a/lib/compiler/src/core_pp.erl +++ b/lib/compiler/src/core_pp.erl @@ -172,12 +172,12 @@ format_1(#c_map{es=Es}, Ctxt) -> format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2), "}~" ]; -format_1(#c_map_pair_assoc{key=K,val=V}, Ctxt) -> +format_1(#c_map_pair{op=#c_literal{val=assoc},key=K,val=V}, Ctxt) -> ["::<", format_hseq([K,V], ",", add_indent(Ctxt, 1), fun format/2), ">" ]; -format_1(#c_map_pair_exact{key=K,val=V}, Ctxt) -> +format_1(#c_map_pair{op=#c_literal{val=exact},key=K,val=V}, Ctxt) -> ["~<", format_hseq([K,V], ",", add_indent(Ctxt, 1), fun format/2), ">" diff --git a/lib/compiler/src/sys_core_dsetel.erl b/lib/compiler/src/sys_core_dsetel.erl index f921429409..60d83763f8 100644 --- a/lib/compiler/src/sys_core_dsetel.erl +++ b/lib/compiler/src/sys_core_dsetel.erl @@ -105,14 +105,10 @@ visit(Env0, #c_tuple{es=Es0}=R) -> visit(Env0, #c_map{es=Es0}=R) -> {Es1,Env1} = visit_list(Env0, Es0), {R#c_map{es=Es1}, Env1}; -visit(Env0, #c_map_pair_assoc{key=K0,val=V0}=R) -> +visit(Env0, #c_map_pair{key=K0,val=V0}=R) -> {K,Env1} = visit(Env0, K0), {V,Env2} = visit(Env1, V0), - {R#c_map_pair_assoc{key=K,val=V}, Env2}; -visit(Env0, #c_map_pair_exact{key=K0,val=V0}=R) -> - {K,Env1} = visit(Env0, K0), - {V,Env2} = visit(Env1, V0), - {R#c_map_pair_exact{key=K,val=V}, Env2}; + {R#c_map_pair{key=K,val=V}, Env2}; visit(Env0, #c_cons{hd=H0,tl=T0}=R) -> {H1,Env1} = visit(Env0, H0), {T1,Env2} = visit(Env1, T0), @@ -225,7 +221,7 @@ visit_pat(Env0, #c_tuple{es=Es}, Vs) -> visit_pats(Es, Env0, Vs); visit_pat(Env0, #c_map{es=Es}, Vs) -> visit_pats(Es, Env0, Vs); -visit_pat(Env0, #c_map_pair_exact{key=V,val=K}, Vs0) -> +visit_pat(Env0, #c_map_pair{op=#c_literal{val=exact},key=V,val=K}, Vs0) -> {Vs1, Env1} = visit_pat(Env0, V, Vs0), visit_pat(Env1, K, Vs1); visit_pat(Env0, #c_cons{hd=H,tl=T}, Vs0) -> diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 6792e2c894..1cdbac5693 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -421,18 +421,12 @@ expr_list(Es, Ctxt, Sub) -> pair_list(Es, Ctxt, Sub) -> [pair(E, Ctxt, Sub) || E <- Es]. -pair(#c_map_pair_assoc{key=K,val=V}, effect, Sub) -> +pair(#c_map_pair{key=K,val=V}, effect, Sub) -> make_effect_seq([K,V], Sub); -pair(#c_map_pair_exact{key=K,val=V}, effect, Sub) -> - make_effect_seq([K,V], Sub); -pair(#c_map_pair_assoc{key=K0,val=V0}=Pair, value=Ctxt, Sub) -> - K = expr(K0, Ctxt, Sub), - V = expr(V0, Ctxt, Sub), - Pair#c_map_pair_assoc{key=K,val=V}; -pair(#c_map_pair_exact{key=K0,val=V0}=Pair, value=Ctxt, Sub) -> +pair(#c_map_pair{key=K0,val=V0}=Pair, value=Ctxt, Sub) -> K = expr(K0, Ctxt, Sub), V = expr(V0, Ctxt, Sub), - Pair#c_map_pair_exact{key=K,val=V}. + Pair#c_map_pair{key=K,val=V}. bitstr_list(Es, Sub) -> [bitstr(E, Sub) || E <- Es]. @@ -1542,10 +1536,10 @@ map_pair_pattern_list(Ps0, Isub, Osub0) -> {Ps,{_,Osub}} = mapfoldl(fun map_pair_pattern/2, {Isub,Osub0}, Ps0), {Ps,Osub}. -map_pair_pattern(#c_map_pair_exact{key=K0,val=V0}=Pair, {Isub,Osub0}) -> +map_pair_pattern(#c_map_pair{op=#c_literal{val=exact},key=K0,val=V0}=Pair, {Isub,Osub0}) -> {K,Osub1} = pattern(K0, Isub, Osub0), {V,Osub} = pattern(V0, Isub, Osub1), - {Pair#c_map_pair_exact{key=K,val=V},{Isub,Osub}}. + {Pair#c_map_pair{key=K,val=V},{Isub,Osub}}. bin_pattern_list(Ps0, Isub, Osub0) -> {Ps,{_,Osub}} = mapfoldl(fun bin_pattern/2, {Isub,Osub0}, Ps0), diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index b5fb65ff08..e30bfa729c 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -712,13 +712,13 @@ map_pair_list(Es, St) -> {K,Ep0,St1} = safe(K0, St0), {V,Ep1,St2} = safe(V0, St1), A = lineno_anno(L, St2), - Pair = #c_map_pair_assoc{anno=A,key=K,val=V}, + Pair = #c_map_pair{op=#c_literal{val=assoc},anno=A,key=K,val=V}, {[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2}; ({map_field_exact,L,K0,V0}, {Ces,Esp,St0}) -> {K,Ep0,St1} = safe(K0, St0), {V,Ep1,St2} = safe(V0, St1), A = lineno_anno(L, St2), - Pair = #c_map_pair_exact{anno=A,key=K,val=V}, + Pair = #c_map_pair{op=#c_literal{val=exact},anno=A,key=K,val=V}, {[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2} end, {[],[],St}, Es). @@ -1520,7 +1520,8 @@ pattern({map_field_exact,L,K,V}, St) -> _ -> pattern(K,St) end, - #c_map_pair_exact{anno=lineno_anno(L, St), + #c_map_pair{anno=lineno_anno(L, St), + op=#c_literal{val=exact}, key=Key, val=pattern(V, St)}; pattern({bin,L,Ps}, St) -> @@ -1871,9 +1872,9 @@ upattern(#c_tuple{es=Es0}=Tuple, Ks, St0) -> upattern(#c_map{es=Es0}=Map, Ks, St0) -> {Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0), {Map#c_map{es=Es1},Esg,Esv,Eus,St1}; -upattern(#c_map_pair_exact{val=V0}=MapPair, Ks, St0) -> +upattern(#c_map_pair{op=#c_literal{val=exact},val=V0}=MapPair, Ks, St0) -> {V,Vg,Vv,Vu,St1} = upattern(V0, Ks, St0), - {MapPair#c_map_pair_exact{val=V},Vg,Vv,Vu,St1}; + {MapPair#c_map_pair{val=V},Vg,Vv,Vu,St1}; upattern(#c_binary{segments=Es0}=Bin, Ks, St0) -> {Es1,Esg,Esv,Eus,St1} = upat_bin(Es0, Ks, St0), {Bin#c_binary{segments=Es1},Esg,Esv,Eus,St1}; @@ -2204,9 +2205,7 @@ is_simple(#c_cons{hd=H,tl=T}) -> is_simple(H) andalso is_simple(T); is_simple(#c_tuple{es=Es}) -> is_simple_list(Es); is_simple(#c_map{es=Es}) -> is_simple_list(Es); -is_simple(#c_map_pair_assoc{key=K,val=V}) -> - is_simple(K) andalso is_simple(V); -is_simple(#c_map_pair_exact{key=K,val=V}) -> +is_simple(#c_map_pair{key=K,val=V}) -> is_simple(K) andalso is_simple(V); is_simple(_) -> false. diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index 1d7d168214..9a2b1605ad 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -500,14 +500,11 @@ translate_fc(Args) -> %% FIXME: Not completed map_pairs(Es, Sub, St) -> foldr(fun - (#c_map_pair_assoc{key=K0,val=V0}, {Kes,Esp,St0}) -> + (#c_map_pair{op=#c_literal{val=Op},key=K0,val=V0}, {Kes,Esp,St0}) when + Op =:= assoc; Op =:= exact -> %% assert Op {K,[],St1} = expr(K0, Sub, St0), {V,Ep,St2} = atomic(V0, Sub, St1), - {[#k_map_pair{op=assoc,key=K,val=V}|Kes],Ep ++ Esp,St2}; - (#c_map_pair_exact{key=K0,val=V0}, {Kes,Esp,St0}) -> - {K,[],St1} = expr(K0, Sub, St0), - {V,Ep,St2} = atomic(V0, Sub, St1), - {[#k_map_pair{op=exact,key=K,val=V}|Kes],Ep ++ Esp,St2} + {[#k_map_pair{op=Op,key=K,val=V}|Kes],Ep ++ Esp,St2} end, {[],[],St}, Es). %% call_type(Module, Function, Arity) -> call | bif | apply | error. @@ -668,7 +665,7 @@ pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) -> pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) -> {Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0), {#k_map{anno=A,es=Kes},Osub1,St1}; -pattern(#c_map_pair_exact{anno=A,key=Ck,val=Cv},Isub, Osub0, St0) -> +pattern(#c_map_pair{op=#c_literal{val=exact},anno=A,key=Ck,val=Cv},Isub, Osub0, St0) -> {Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0), {Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1), {#k_map_pair{anno=A,op=exact,key=Kk,val=Kv},Osub2,St2}; -- cgit v1.2.3 From 1ebfc08a004c530faa592a09cf101036329b18fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 17 Jan 2014 18:39:47 +0100 Subject: compiler: Update inliner tests --- lib/compiler/test/inline_SUITE_data/maps_inline_test.erl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl b/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl index 87a8f86b47..d9762e2647 100644 --- a/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl +++ b/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl @@ -41,7 +41,17 @@ sval(#{id => 3}) + sval(#{id => 4}) + sval(#{id => 5}) + - sval(#{id => 6}). + sval(#{id => 6}), + + M = #{v => 1, m => #{v => 21, m => #{v => 7, m => 13}}}, + + 42 = decompose(M). + +% switch key orders +decompose(#{ m := M, v := V}) when is_map(M) -> + V + decompose(M); +decompose(#{ v := V, m := M}) -> V + M. + mval(#{val := V}) -> V. -- cgit v1.2.3 From f95c907a0fda5d185c895bfb43cd2a76e46b7e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 15 Jan 2014 17:59:26 +0100 Subject: hipe: Update cerl pretty printer --- lib/hipe/cerl/cerl_prettypr.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/hipe/cerl/cerl_prettypr.erl b/lib/hipe/cerl/cerl_prettypr.erl index 60926e16e1..ee5b41207b 100644 --- a/lib/hipe/cerl/cerl_prettypr.erl +++ b/lib/hipe/cerl/cerl_prettypr.erl @@ -64,7 +64,7 @@ try_body/1, try_vars/1, try_evars/1, try_handler/1, tuple_es/1, type/1, values_es/1, var_name/1, - map_es/1, map_pair_assoc_es/1, map_pair_exact_es/1 + map_es/1, map_pair_es/1 ]). -define(PAPER, 76). @@ -605,13 +605,13 @@ lay_map(Node, Ctxt) -> floating(text("}~")))). lay_map_pair_assoc(Node, Ctxt) -> - [K,V] = map_pair_assoc_es(Node), + [K,V] = map_pair_es(Node), beside(floating(text("::<")), beside(lay(K,Ctxt),beside(floating(text(",")), beside(lay(V,Ctxt), floating(text(">")))))). lay_map_pair_exact(Node, Ctxt) -> - [K,V] = map_pair_exact_es(Node), + [K,V] = map_pair_es(Node), beside(floating(text("~<")), beside(lay(K,Ctxt),beside(floating(text(",")), beside(lay(V,Ctxt), floating(text(">")))))). -- cgit v1.2.3 From 23e728cd0f68de11feca27358bedb0a6b71e4715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 16 Jan 2014 16:19:45 +0100 Subject: dialyzer: Reflect map_pair core changes in dialyzer --- lib/dialyzer/src/dialyzer_dataflow.erl | 18 ++++++------------ lib/dialyzer/src/dialyzer_dep.erl | 11 ++++------- 2 files changed, 10 insertions(+), 19 deletions(-) (limited to 'lib') diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index 4d614320c2..239df1b8bd 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -309,10 +309,8 @@ traverse(Tree, Map, State) -> handle_tuple(Tree, Map, State); map -> handle_map(Tree, Map, State); - map_pair_assoc -> - handle_map_pair_assoc(Tree, Map, State); - map_pair_exact -> - handle_map_pair_exact(Tree, Map, State); + map_pair -> + handle_map_pair(Tree, Map, State); values -> Elements = cerl:values_es(Tree), {State1, Map1, EsType} = traverse_list(Elements, Map, State), @@ -1068,14 +1066,10 @@ handle_map(Tree,Map,State) -> {State1, Map1, TypePairs} = traverse_list(Pairs,Map,State), {State1, Map1, t_map(TypePairs)}. -handle_map_pair_assoc(Tree,Map,State) -> - Elements = cerl:map_pair_assoc_es(Tree), - {State1, Map1, [K,V]} = traverse_list(Elements,Map,State), - {State1, Map1, {K,V}}. - -handle_map_pair_exact(Tree,Map,State) -> - Elements = cerl:map_pair_exact_es(Tree), - {State1, Map1, [K,V]} = traverse_list(Elements,Map,State), +handle_map_pair(Tree,Map,State) -> + Key = cerl:map_pair_key(Tree), + Val = cerl:map_pair_val(Tree), + {State1, Map1, [K,V]} = traverse_list([Key,Val],Map,State), {State1, Map1, {K,V}}. %%---------------------------------------- diff --git a/lib/dialyzer/src/dialyzer_dep.erl b/lib/dialyzer/src/dialyzer_dep.erl index 12833b49c7..a81ea1a98b 100644 --- a/lib/dialyzer/src/dialyzer_dep.erl +++ b/lib/dialyzer/src/dialyzer_dep.erl @@ -186,13 +186,10 @@ traverse(Tree, Out, State, CurrentFun) -> Args = cerl:map_es(Tree), {List, State1} = traverse_list(Args, Out, State, CurrentFun), {merge_outs(List), State1}; - map_pair_assoc -> - Args = cerl:map_pair_assoc_es(Tree), - {List, State1} = traverse_list(Args, Out, State, CurrentFun), - {merge_outs(List), State1}; - map_pair_exact -> - Args = cerl:map_pair_exact_es(Tree), - {List, State1} = traverse_list(Args, Out, State, CurrentFun), + map_pair -> + Key = cerl:map_pair_key(Tree), + Val = cerl:map_pair_val(Tree), + {List, State1} = traverse_list([Key,Val], Out, State, CurrentFun), {merge_outs(List), State1}; values -> traverse_list(cerl:values_es(Tree), Out, State, CurrentFun); -- cgit v1.2.3 From b7b79aaf0453995ef2145647c5c50acc5fae811b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 17 Jan 2014 15:33:29 +0100 Subject: dialyzer: Remove dead code --- lib/dialyzer/src/dialyzer_dataflow.erl | 22 ---------------------- lib/dialyzer/src/dialyzer_typesig.erl | 25 ------------------------- 2 files changed, 47 deletions(-) (limited to 'lib') diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index 239df1b8bd..33fa107019 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -1453,28 +1453,6 @@ bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) -> end; map -> {Map, t_map([])}; -% Pairs = [ cerl:map_pair_es(Pair) || Pair <- cerl:map_es(Pat) ], -% MapType = t_inf(t_map([]), Type), -% case t_is_none(MapType) of -% true -> -% bind_opaque_pats(MapType, Type, Pat, Map, State, Rev); -% false -> -% MapJ = join_maps_begin(Map), -% Results = case Rev of -% true -> -% [bind_pat_vars_reverse(Pair, [t_any(),t_any()], [], MapJ, State) || Pair <- Pairs]; -% false -> -% [bind_pat_vars(Pair, [t_any(),t_any()], [], MapJ, State) || Pair <- Pairs] -% end, -% case [M || {M, _} <- Results, M =/= error] of -% [] -> bind_error([Pat], MapType, t_none(), bind); -% Maps -> -% Map1 = join_maps_end(Maps, MapJ), -% _PairTypes = [{Ktype,Vtype} || {M, [Ktype,Vtype]} <- Results, M =/= error], -% % add t_sup -% {Map1, t_map([])} -% end -% end; tuple -> Es = cerl:tuple_es(Pat), {TypedRecord, Prototype} = diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl index 4e18e9c7d2..b4b3d5a092 100644 --- a/lib/dialyzer/src/dialyzer_typesig.erl +++ b/lib/dialyzer/src/dialyzer_typesig.erl @@ -474,31 +474,6 @@ traverse(Tree, DefinedVars, State) -> end; map -> {State, t_map([])}; -% Pairs = cerl:map_es(Tree), -% {State1, EVars} = traverse_list(Pairs, DefinedVars, State), -% case cerl:is_literal(cerl:fold_literal(Tree)) of -% true -> -% %% We do not need to do anything more here. -% {State, t_map([])}; -% false -> -% Fun = fun(Var, AccState) -> -% case t_has_var(Var) of -% true -> -% {AccState1, NewVar} = state__mk_var(AccState), -% {NewVar, -% state__store_conj(Var, eq, NewVar, AccState1)}; -% false -> -% {Var, AccState} -% end -% end, -% {_NewEvars, State2} = lists:mapfoldl(Fun, State1, EVars), -% {State2, t_map([])} -% end; -% map_pair_assoc -> -% [K,V] = cerl:map_pair_assoc_es(Tree), -% {State1,_} = traverse(K,DefinedVars,State), -% {State2,_} = traverse(V,DefinedVars,State), -% {_,State2}; values -> %% We can get into trouble when unifying products that have the %% same element appearing several times. Handle these cases by -- cgit v1.2.3 From fa92c876756032bc7f824ed81bb9ba4667f14805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 17 Jan 2014 17:29:16 +0100 Subject: dialyzer: Add maps tests --- lib/dialyzer/test/small_SUITE_data/src/maps1.erl | 41 ++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 lib/dialyzer/test/small_SUITE_data/src/maps1.erl (limited to 'lib') diff --git a/lib/dialyzer/test/small_SUITE_data/src/maps1.erl b/lib/dialyzer/test/small_SUITE_data/src/maps1.erl new file mode 100644 index 0000000000..06ced5b69e --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/maps1.erl @@ -0,0 +1,41 @@ +%% +%% File: maps1.erl +%% Author: Björn-Egil Dahlberg +%% Created: 2014-01-17 +%% + +-module(maps1). + +-compile([export_all]). + + +-export([recv/3, decode/1]). + +%-record(can_pkt, {id, data :: binary(), timestamp}). + +-type can_pkt() :: #{ id => term(), data => binary(), timestamp => term() }. +-type channel() :: atom() | pid() | {atom(),_}. + +-spec recv(<<_:64,_:_*8>>, fun((can_pkt()) -> R), channel()) -> R. +recv(Packet, Fun, Chan) -> + #{id := Can_id, data := Can_data} = P = decode(Packet), + Fun(P). + +-spec decode(<<_:64,_:_*8>>) -> #{id => <<_:11>>,timestamp => char()}. +decode(<<_:12, Len:4, Timestamp:16, 0:3, Id:11/bitstring, 0:18, + Data:Len/binary, _/binary>>) -> + #{id => Id, data => Data, timestamp => Timestamp}. + + + +t1() -> + #{bar=>fun t2/0}. + +t2() -> ok. + +-type map_state() :: #{ id => integer(), val => term() }. + +-spec update(map_state(), term()) -> map_state(). + +update(#{ id := Id, val := Val } = M, X) when is_integer(Id) -> + M#{ val := [Val,X] }. -- cgit v1.2.3 From f6370a6d73406e11d4a7664e566502c005062b0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 Jan 2014 11:52:00 +0100 Subject: hipe: Fixup update cerl pretty printer --- lib/hipe/cerl/cerl_prettypr.erl | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/hipe/cerl/cerl_prettypr.erl b/lib/hipe/cerl/cerl_prettypr.erl index ee5b41207b..22f5b8945a 100644 --- a/lib/hipe/cerl/cerl_prettypr.erl +++ b/lib/hipe/cerl/cerl_prettypr.erl @@ -63,8 +63,7 @@ seq_arg/1, seq_body/1, string_lit/1, try_arg/1, try_body/1, try_vars/1, try_evars/1, try_handler/1, tuple_es/1, type/1, values_es/1, var_name/1, - - map_es/1, map_pair_es/1 + map_es/1, map_pair_key/1, map_pair_val/1, map_pair_op/1 ]). -define(PAPER, 76). @@ -429,10 +428,8 @@ lay_1(Node, Ctxt) -> lay_tuple(Node, Ctxt); map -> lay_map(Node, Ctxt); - map_pair_assoc -> - lay_map_pair_assoc(Node, Ctxt); - map_pair_exact -> - lay_map_pair_exact(Node, Ctxt); + map_pair -> + lay_map_pair(Node, Ctxt); 'let' -> lay_let(Node, Ctxt); seq -> @@ -604,15 +601,14 @@ lay_map(Node, Ctxt) -> Ctxt, fun lay/2)), floating(text("}~")))). -lay_map_pair_assoc(Node, Ctxt) -> - [K,V] = map_pair_es(Node), - beside(floating(text("::<")), - beside(lay(K,Ctxt),beside(floating(text(",")), beside(lay(V,Ctxt), - floating(text(">")))))). - -lay_map_pair_exact(Node, Ctxt) -> - [K,V] = map_pair_es(Node), - beside(floating(text("~<")), +lay_map_pair(Node, Ctxt) -> + K = map_pair_key(Node), + V = map_pair_val(Node), + OpTxt = case concrete(map_pair_op(Node)) of + assoc -> "::<"; + exact -> "~<" + end, + beside(floating(text(OpTxt)), beside(lay(K,Ctxt),beside(floating(text(",")), beside(lay(V,Ctxt), floating(text(">")))))). -- cgit v1.2.3 From 335e6bf3e4987e5f549136c1f692c628b1dfc360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 Jan 2014 16:00:34 +0100 Subject: stdlib: Make maps:fold/3 order-independent This means replacing maps:foldl/3 and maps:foldr/3 with maps:fold/3. --- lib/stdlib/src/maps.erl | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl index 0e6a1ddb1a..41de174e7d 100644 --- a/lib/stdlib/src/maps.erl +++ b/lib/stdlib/src/maps.erl @@ -20,8 +20,7 @@ -module(maps). -export([ - foldl/3, - foldr/3, + fold/3, map/2, size/1, without/2 @@ -154,7 +153,7 @@ values(_) -> erlang:nif_error(undef). %%% End of BIFs --spec foldl(Fun,Init,Map) -> Acc when +-spec fold(Fun,Init,Map) -> Acc when Fun :: fun((K, V, AccIn) -> AccOut), Init :: term(), Acc :: term(), @@ -164,23 +163,9 @@ values(_) -> erlang:nif_error(undef). K :: term(), V :: term(). -foldl(Fun, Init, Map) when is_function(Fun,3), is_map(Map) -> +fold(Fun, Init, Map) when is_function(Fun,3), is_map(Map) -> lists:foldl(fun({K,V},A) -> Fun(K,V,A) end,Init,maps:to_list(Map)). --spec foldr(Fun,Init,Map) -> Acc when - Fun :: fun((K,V,AccIn) -> AccOut), - Init :: term(), - Acc :: term(), - AccIn :: term(), - AccOut :: term(), - Map :: map(), - K :: term(), - V :: term(). - -foldr(Fun, Init, Map) when is_function(Fun,3), is_map(Map) -> - lists:foldr(fun({K,V},A) -> Fun(K,V,A) end,Init,maps:to_list(Map)). - - -spec map(Fun,Map1) -> Map2 when Fun :: fun((K, V1) -> V2), Map1 :: map(), -- cgit v1.2.3 From 8bd7113e5b9c53eb29b7211d43f6d8bc2f58eb81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 28 Jan 2014 10:39:14 +0100 Subject: compiler: Fixup #map_pair{} spec --- lib/compiler/src/core_parse.hrl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl index 067a1ae895..d54715ef59 100644 --- a/lib/compiler/src/core_parse.hrl +++ b/lib/compiler/src/core_parse.hrl @@ -98,8 +98,9 @@ -record(c_var, {anno=[], name :: cerl:var_name()}). -record(c_map_pair, {anno=[], - op :: 'assoc' | 'exact', - key, val}). + op :: #c_literal{val::'assoc'} | #c_literal{val::'exact'}, + key, + val}). -record(c_map, {anno=[], var=#c_literal{val=[]} :: #c_var{} | #c_literal{}, -- cgit v1.2.3 From 54e921a5944394665acab44b210fdb677b4d15e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 28 Jan 2014 11:17:33 +0100 Subject: compiler: Fix core parse for Maps --- lib/compiler/src/core_parse.yrl | 2 +- lib/compiler/src/core_scan.erl | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/compiler/src/core_parse.yrl b/lib/compiler/src/core_parse.yrl index 67acb0856c..b8db0f683a 100644 --- a/lib/compiler/src/core_parse.yrl +++ b/lib/compiler/src/core_parse.yrl @@ -190,7 +190,7 @@ map_pair_patterns -> map_pair_pattern : ['$1']. map_pair_patterns -> map_pair_pattern ',' map_pair_patterns : ['$1' | '$3']. map_pair_pattern -> '~' '<' anno_pattern ',' anno_pattern '>' : - #c_map_pair{op=exact,key='$3',val='$5'}. + #c_map_pair{op=#c_literal{val=exact},key='$3',val='$5'}. cons_pattern -> '[' anno_pattern tail_pattern : #c_cons{hd='$2',tl='$3'}. diff --git a/lib/compiler/src/core_scan.erl b/lib/compiler/src/core_scan.erl index a4fe920258..b7799b373a 100644 --- a/lib/compiler/src/core_scan.erl +++ b/lib/compiler/src/core_scan.erl @@ -271,6 +271,8 @@ scan1("->" ++ Cs, Toks, Pos) -> scan1(Cs, [{'->',Pos}|Toks], Pos); scan1("-|" ++ Cs, Toks, Pos) -> scan1(Cs, [{'-|',Pos}|Toks], Pos); +scan1("::" ++ Cs, Toks, Pos) -> + scan1(Cs, [{'::',Pos}|Toks], Pos); scan1([C|Cs], Toks, Pos) -> %Punctuation character P = list_to_atom([C]), scan1(Cs, [{P,Pos}|Toks], Pos); -- cgit v1.2.3 From cf5bc2e917dbcb2c2841bf07b995efe105bea4be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 28 Jan 2014 14:46:16 +0100 Subject: compiler: Add core compile test for maps --- lib/compiler/test/core_SUITE.erl | 8 +- .../test/core_SUITE_data/map_core_test.core | 95 ++++++++++++++++++++++ 2 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 lib/compiler/test/core_SUITE_data/map_core_test.core (limited to 'lib') diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl index a40dc32d59..1a521c3591 100644 --- a/lib/compiler/test/core_SUITE.erl +++ b/lib/compiler/test/core_SUITE.erl @@ -23,7 +23,8 @@ init_per_testcase/2,end_per_testcase/2, dehydrated_itracer/1,nested_tries/1, seq_in_guard/1,make_effect_seq/1,eval_is_boolean/1, - unsafe_case/1,nomatch_shadow/1,reversed_annos/1]). + unsafe_case/1,nomatch_shadow/1,reversed_annos/1, + map_core_test/1]). -include_lib("test_server/include/test_server.hrl"). @@ -48,7 +49,9 @@ all() -> groups() -> [{p,test_lib:parallel(), [dehydrated_itracer,nested_tries,seq_in_guard,make_effect_seq, - eval_is_boolean,unsafe_case,nomatch_shadow,reversed_annos]}]. + eval_is_boolean,unsafe_case,nomatch_shadow,reversed_annos, + map_core_test + ]}]. init_per_suite(Config) -> @@ -72,6 +75,7 @@ end_per_group(_GroupName, Config) -> ?comp(unsafe_case). ?comp(nomatch_shadow). ?comp(reversed_annos). +?comp(map_core_test). try_it(Mod, Conf) -> Src = filename:join(?config(data_dir, Conf), atom_to_list(Mod)), diff --git a/lib/compiler/test/core_SUITE_data/map_core_test.core b/lib/compiler/test/core_SUITE_data/map_core_test.core new file mode 100644 index 0000000000..7ece8a8bbd --- /dev/null +++ b/lib/compiler/test/core_SUITE_data/map_core_test.core @@ -0,0 +1,95 @@ +module 'map_core_test' ['map_core_test'/0, + 'module_info'/0, + 'module_info'/1] + attributes [] +'map_core_test'/0 = + %% Line 14 + fun () -> + let <_cor0> = + %% Line 15 + ~{::<'check','ok'>,::<1337,#{#<104>(8,1,'integer',['unsigned'|['big']]), + #<101>(8,1,'integer',['unsigned'|['big']]), + #<108>(8,1,'integer',['unsigned'|['big']]), + #<108>(8,1,'integer',['unsigned'|['big']]), + #<111>(8,1,'integer',['unsigned'|['big']])}#>,::<'val',0>}~ + in let = + %% Line 15 + apply 'id'/1 + (_cor0) + in let <_cor2> = + %% Line 16 + apply 'id'/1 + ([1|[2|[3|[4|[5|[6]]]]]]) + in %% Line 16 + case apply 'call'/2 + (M, _cor2) of + <~{~<1337,#{#<104>(8,1,'integer',['unsigned'|['big']]), + #<101>(8,1,'integer',['unsigned'|['big']]), + #<108>(8,1,'integer',['unsigned'|['big']]), + #<108>(8,1,'integer',['unsigned'|['big']]), + #<111>(8,1,'integer',['unsigned'|['big']]), + #<32>(8,1,'integer',['unsigned'|['big']]), + #<49>(8,1,'integer',['unsigned'|['big']]), + #<32>(8,1,'integer',['unsigned'|['big']]), + #<50>(8,1,'integer',['unsigned'|['big']]), + #<32>(8,1,'integer',['unsigned'|['big']]), + #<51>(8,1,'integer',['unsigned'|['big']]), + #<32>(8,1,'integer',['unsigned'|['big']]), + #<52>(8,1,'integer',['unsigned'|['big']]), + #<32>(8,1,'integer',['unsigned'|['big']]), + #<53>(8,1,'integer',['unsigned'|['big']]), + #<32>(8,1,'integer',['unsigned'|['big']]), + #<54>(8,1,'integer',['unsigned'|['big']])}#>,~<'check','ok'>,~<'val',21>}~> when 'true' -> + %% Line 17 + 'ok' + ( <_cor3> when 'true' -> + primop 'match_fail' + ({'badmatch',_cor3}) + -| ['compiler_generated'] ) + end +'call'/2 = + %% Line 20 + fun (_cor1,_cor0) -> + case <_cor1,_cor0> of + ,~<'check',_cor8>,~<'val',Val>}~,[V|Vs]> when 'true' -> + let <_cor3> = + %% Line 21 + call 'erlang':'+' + (V, 48) + in let <_cor4> = + %% Line 21 + #{#('all',8,'binary',['unsigned'|['big']]), + #<32>(8,1,'integer',['unsigned'|['big']]), + #<_cor3>(8,1,'integer',['unsigned'|['big']])}# + in let <_cor2> = + %% Line 21 + call 'erlang':'+' + (Val, V) + in let <_cor5> = + %% Line 21 + M~{~<1337,_cor4>,~<'val',_cor2>}~ + in %% Line 21 + apply 'call'/2 + (_cor5, Vs) + %% Line 22 + when 'true' -> + M + ( <_cor7,_cor6> when 'true' -> + ( primop 'match_fail' + ({'function_clause',_cor7,_cor6}) + -| [{'function_name',{'call',2}}] ) + -| ['compiler_generated'] ) + end +'id'/1 = + %% Line 24 + fun (_cor0) -> + _cor0 +'module_info'/0 = + fun () -> + call 'erlang':'get_module_info' + ('map_core_test') +'module_info'/1 = + fun (_cor0) -> + call 'erlang':'get_module_info' + ('map_core_test', _cor0) +end \ No newline at end of file -- cgit v1.2.3