From 331bb6cab54e6697e12cc9c5a4ca0ae618c37dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 23 May 2012 16:08:59 +0200 Subject: BEAM loader: Support preservation of extra operand in transforms It was not possible to preserve extra arguments in transformations. The following (hypothetical) example will now work: some_op Lit=c SizeArg Rest=* => move Lit x | some_op x SizeArg Rest --- erts/emulator/beam/beam_load.c | 18 ++++++++++++++++-- erts/emulator/utils/beam_makeops | 23 ++++++++++++++++------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 938fd8f2c9..58207ec75b 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4376,6 +4376,7 @@ transform_engine(LoaderState* st) Uint* restart; /* Where to restart if current match fails. */ GenOpArg def_vars[TE_MAX_VARS]; /* Default buffer for variables. */ GenOpArg* var = def_vars; + int num_vars = 0; int i; /* General index. */ Uint mask; GenOp* instr; @@ -4578,9 +4579,9 @@ transform_engine(LoaderState* st) { int n = *pc++; int formal_arity = gen_opc[instr->op].arity; - int num_vars = n + (instr->arity - formal_arity); int j = formal_arity; + num_vars = n + (instr->arity - formal_arity); var = erts_alloc(ERTS_ALC_T_LOADER_TMP, num_vars * sizeof(GenOpArg)); for (i = 0; i < n; i++) { @@ -4592,7 +4593,6 @@ transform_engine(LoaderState* st) } break; #endif - case TOP_next_arg: ap++; break; @@ -4680,6 +4680,20 @@ transform_engine(LoaderState* st) instr->a[ap].val = var[i].val; ap++; break; +#if defined(TOP_store_rest_args) + case TOP_store_rest_args: + { + int n = *pc++; + int num_extra = num_vars - n; + + ASSERT(n <= num_vars); + GENOP_ARITY(instr, instr->arity+num_extra); + memcpy(instr->a, instr->def_args, ap*sizeof(GenOpArg)); + memcpy(instr->a+ap, var+n, num_extra*sizeof(GenOpArg)); + ap += num_extra; + } + break; +#endif case TOP_try_me_else: restart = pc + 1; restart += *pc++; diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index 16a949c2a6..0b7c16f606 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -1202,6 +1202,7 @@ sub parse_transformation { my($from, $to) = split(/\s*=>\s*/); my(@op); + my $rest_var; # The source instructions. @@ -1212,7 +1213,7 @@ sub parse_transformation { $_ = (&compile_transform_function($name, split(/\s*,\s*/, $arglist))); } else { (@op) = split; - $_ = &compile_transform(1, @op); + ($rest_var,$_) = compile_transform(1, $rest_var, @op); } } @@ -1230,7 +1231,7 @@ sub parse_transformation { @to = split(/\s*\|\s*/, $to); foreach (@to) { (@op) = split; - $_ = &compile_transform(0, @op); + (undef,$_) = compile_transform(0, $rest_var, @op); } } push(@transformations, [$., $orig, [@from], [reverse @to]]); @@ -1243,12 +1244,18 @@ sub compile_transform_function { } sub compile_transform { - my($src, $name, @ops) = @_; + my($src, $rest_var, $name, @ops) = @_; my $arity = 0; - + foreach (@ops) { my(@list) = &tr_parse_op($src, $_); - $arity++ unless $list[1] eq '*'; + if ($list[1] eq '*') { + $rest_var = $list[0]; + } elsif (defined $rest_var and $list[0] eq $rest_var) { + $list[1] = '*'; + } else { + $arity++; + } $_ = [ @list ]; } @@ -1260,7 +1267,7 @@ sub compile_transform { $is_transformed{$name,$arity} = 1; } - [$name,$arity,@ops]; + ($rest_var,[$name,$arity,@ops]); } sub tr_parse_op { @@ -1681,7 +1688,9 @@ sub tr_gen_to { foreach $op (@ops) { my($var, $type, $type_val) = @$op; - if ($var ne '') { + if ($type eq '*') { + push(@code, make_op($var, 'store_rest_args', $var{$var})); + } elsif ($var ne '') { &error($where, "variable '$var' unbound") unless defined $var{$var}; push(@code, &make_op($var, 'store_var_next_arg', $var{$var})); -- cgit v1.2.3 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(-) 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(-) 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(+) 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(-) 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. --- erts/emulator/beam/ops.tab | 9 +++++ 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 ++++++-- 17 files changed, 305 insertions(+), 7 deletions(-) diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index c29f3f9b1b..b52ecfaefc 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1465,6 +1465,15 @@ fclearerror apply I apply_last I P +# +# Map instructions in R16. +# + +put_map Fail Src Dst Live Size Rest=* => jump Fail +is_map Fail Src => jump Fail +has_map_field Fail Src Key => jump Fail +get_map_element Fail Src Key Dst => jump Fail + # # Optimize addition and subtraction of small literals using # the i_increment/4 instruction (in bodies, not in guards). 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(-) 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(+) 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(-) 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(-) 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(-) 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(-) 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 345bf9d4634f81ad0343e8c60442bdd293e41835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 21 May 2013 17:00:11 +0200 Subject: erts: Maps beam-instruction definitions --- erts/emulator/beam/ops.tab | 67 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index b52ecfaefc..b89bdb2e3a 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1466,14 +1466,69 @@ apply I apply_last I P # -# Map instructions in R16. -# - -put_map Fail Src Dst Live Size Rest=* => jump Fail -is_map Fail Src => jump Fail -has_map_field Fail Src Key => jump Fail +# Map instructions in R17. +# + +# put_map Fail Src Dst Live Size Rest=* => jump Fail +# is_map Fail Src => jump Fail +# has_map_field Fail Src Key => jump Fail +# get_map_element Fail Src Key Dst => jump Fail + +put_map F n Dst Live Size Rest=* => new_map F Dst Live Size Rest +put_map F Src Dst Live Size Rest=* => update_map F Src Dst Live Size Rest + +new_map j d I I +update_map j d d I I + +is_map Fail cq => jump Fail + +%macro: is_map IsMap -fail_action +is_map f r +is_map f x +is_map f y + +has_map_field Fail Src=rxy Key=arxy => i_has_map_field Fail Src Key +has_map_field Fail Src Key => move Key x | i_has_map_field Fail Src x + +%macro: i_has_map_field HasMapField -fail_action +i_has_map_field f r a +i_has_map_field f x a +i_has_map_field f y a +i_has_map_field f r r +i_has_map_field f x r +i_has_map_field f y r +i_has_map_field f r x +i_has_map_field f x x +i_has_map_field f y x +i_has_map_field f r y +i_has_map_field f x y +i_has_map_field f y y + +get_map_element Fail Src=rxy Key=ax Dst => i_get_map_element Fail Src Key Dst +get_map_element Fail Src=rxy Key=rycq Dst => \ + move Key x | i_get_map_element Fail Src x Dst get_map_element Fail Src Key Dst => jump Fail +%macro: i_get_map_element GetMapElement -fail_action +i_get_map_element f r a r +i_get_map_element f x a r +i_get_map_element f y a r +i_get_map_element f r a x +i_get_map_element f x a x +i_get_map_element f y a x +i_get_map_element f r a y +i_get_map_element f x a y +i_get_map_element f y a y +i_get_map_element f r x r +i_get_map_element f x x r +i_get_map_element f y x r +i_get_map_element f r x x +i_get_map_element f x x x +i_get_map_element f y x x +i_get_map_element f r x y +i_get_map_element f x x y +i_get_map_element f y x y + # # Optimize addition and subtraction of small literals using # the i_increment/4 instruction (in bodies, not in guards). -- cgit v1.2.3 From 03d5bb0d1bd5d7c4e09ee793a680f2d538fe0122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 21 May 2013 18:09:55 +0200 Subject: erts: Initial Map instructions, type and structure --- erts/emulator/Makefile.in | 2 +- erts/emulator/beam/beam_emu.c | 341 ++++++++++++++++++++++++++++++++++- erts/emulator/beam/bif.tab | 6 + erts/emulator/beam/copy.c | 32 ++++ erts/emulator/beam/erl_bif_op.c | 12 +- erts/emulator/beam/erl_db_util.c | 1 + erts/emulator/beam/erl_debug.c | 1 + erts/emulator/beam/erl_gc.c | 1 + erts/emulator/beam/erl_gc.h | 37 ++-- erts/emulator/beam/erl_map.c | 225 +++++++++++++++++++++++ erts/emulator/beam/erl_map.h | 64 +++++++ erts/emulator/beam/erl_printf_term.c | 52 +++++- erts/emulator/beam/erl_term.c | 6 +- erts/emulator/beam/erl_term.h | 44 +++-- erts/emulator/beam/utils.c | 41 ++++- erts/emulator/hipe/hipe_bif2.c | 1 + erts/emulator/hipe/hipe_debug.c | 1 + 17 files changed, 811 insertions(+), 56 deletions(-) create mode 100644 erts/emulator/beam/erl_map.c create mode 100644 erts/emulator/beam/erl_map.h diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index b270099566..54365f279c 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -780,7 +780,7 @@ RUN_OBJS = \ $(OBJDIR)/erl_zlib.o $(OBJDIR)/erl_nif.o \ $(OBJDIR)/erl_bif_binary.o $(OBJDIR)/erl_ao_firstfit_alloc.o \ $(OBJDIR)/erl_thr_queue.o $(OBJDIR)/erl_sched_spec_pre_alloc.o \ - $(OBJDIR)/erl_ptab.o + $(OBJDIR)/erl_ptab.o $(OBJDIR)/erl_map.o ifeq ($(TARGET),win32) DRV_OBJS = \ diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 7fecdd5c5f..5c955b9566 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -31,6 +31,7 @@ #include "big.h" #include "beam_load.h" #include "erl_binary.h" +#include "erl_map.h" #include "erl_bits.h" #include "dist.h" #include "beam_bp.h" @@ -701,6 +702,19 @@ extern int count_instructions; Fail; \ } +#define IsMap(Src, Fail) if (is_not_map(Src)) { Fail; } + +#define HasMapField(Src, Key, Fail) if (has_not_map_field(Src, Key)) { Fail; } + +#define GetMapElement(Src, Key, Dst, Fail) \ + do { \ + Eterm _res = get_map_element(Src, Key); \ + if (is_non_value(_res)) { \ + Fail; \ + } \ + Dst = _res; \ + } while (0) + #define IsFunction(X, Action) \ do { \ if ( !(is_any_fun(X)) ) { \ @@ -944,7 +958,11 @@ static BeamInstr* apply_fun(Process* p, Eterm fun, Eterm args, Eterm* reg) NOINLINE; static Eterm new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free) NOINLINE; - +static Eterm new_map(Process* p, Eterm* reg, BeamInstr* I) NOINLINE; +static Eterm update_map(Process* p, Eterm* reg, + Eterm map, BeamInstr* I) NOINLINE; +static int has_not_map_field(Eterm map, Eterm key); +static Eterm get_map_element(Eterm map, Eterm key); /* * Functions not directly called by process_main(). OK to inline. @@ -2323,6 +2341,37 @@ void process_main(void) Goto(*I); } + OpCase(new_map_jdII): { + Eterm res; + + x(0) = r(0); + SWAPOUT; + res = new_map(c_p, reg, I); + SWAPIN; + r(0) = x(0); + StoreResult(res, Arg(1)); + Next(4+Arg(3)); + } + + OpCase(update_map_jddII): { + Eterm res; + Eterm map; + + GetArg1(1, map); + x(0) = r(0); + SWAPOUT; + res = update_map(c_p, reg, map, I); + SWAPIN; + if (res) { + r(0) = x(0); + StoreResult(res, Arg(2)); + Next(5+Arg(4)); + } else { + goto lb_Cl_error; + } + } + + /* * All guards with zero arguments have special instructions: * self/0 @@ -6227,6 +6276,296 @@ new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free) return make_fun(funp); } +static int has_not_map_field(Eterm map, Eterm key) +{ + map_t* mp; + Eterm* keys; + Uint i; + Uint n; + + mp = (map_t *)map_val(map); + keys = map_get_keys(mp); + n = map_get_size(mp); + if (is_immed(key)) { + for (i = 0; i < n; i++) { + if (keys[i] == key) { + return 0; + } + } + } else { + for (i = 0; i < n; i++) { + if (eq(keys[i], key)) { + return 0; + } + } + } + return 1; +} + +static Eterm get_map_element(Eterm map, Eterm key) +{ + map_t *mp; + Eterm* ks, *vs; + Uint i; + Uint n; + + mp = (map_t *)map_val(map); + ks = map_get_keys(mp); + vs = map_get_values(mp); + n = map_get_size(mp); + if (is_immed(key)) { + for (i = 0; i < n; i++) { + if (ks[i] == key) { + return vs[i]; + } + } + } else { + for (i = 0; i < n; i++) { + if (eq(ks[i], key)) { + return vs[i]; + } + } + } + return THE_NON_VALUE; +} + +#define GET_TERM(term, dest) \ +do { \ + Eterm src = term; \ + switch (src & _TAG_IMMED1_MASK) { \ + case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ + dest = x(0); \ + break; \ + case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ + dest = x(src >> _TAG_IMMED1_SIZE); \ + break; \ + case (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ + dest = y(src >> _TAG_IMMED1_SIZE); \ + break; \ + default: \ + dest = src; \ + break; \ + } \ +} while(0) + + +static Eterm +new_map(Process* p, Eterm* reg, BeamInstr* I) +{ + Uint n = Arg(3); + Uint i; + Uint need = n + 1 /* hdr */ + 1 /*size*/ + 1 /* ptr */ + 1 /* arity */; + Eterm keys; + Eterm *mhp,*thp; + Eterm *E; + Eterm *tp; + map_t *mp; + + if (HeapWordsLeft(p) < need) { + erts_garbage_collect(p, need, reg, Arg(2)); + } + + thp = p->htop; + mhp = thp + 1 + n/2; + E = p->stop; + tp = &Arg(4); + keys = make_tuple(thp); + *thp++ = make_arityval(n/2); + + mp = (map_t *)mhp; mhp += 3; + mp->thing_word = MAP_HEADER; + mp->size = n/2; + mp->keys = keys; + + for (i = 0; i < n/2; i++) { + GET_TERM(*tp++, *thp++); + GET_TERM(*tp++, *mhp++); + } + p->htop = mhp; + return make_map(mp); +} + + +/* This entire instruction will be split into two. + * 1) update_map_exact (literals) <- this can be much more optimized + * 2) update_map_assoc (literals) + * Also update_map is pretty bad code as it stands now. + */ + +static Eterm +update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) +{ + Uint n; + Uint i; + Uint num_old; + Uint num_updates; + Uint need; + map_t *old_mp, *mp; + Eterm res; + Eterm* hp; + Eterm* E; + Eterm* old_keys; + Eterm* old_vals; + Eterm* new_p; + Eterm new_key; + Eterm* kp; + + if (is_not_map(map)) { + return 0; + } + + old_mp = (map_t *) map_val(map); + num_old = map_get_size(old_mp); + + /* + * If the old map is empty, create a new map. + */ + + if (num_old == 0) { + return new_map(p, reg, I+1); + } + + /* + * Allocate heap space for the worst case (i.e. all keys are new). + */ + + num_updates = Arg(4) / 2; + need = 2*(num_old+num_updates) + 4; + if (HeapWordsLeft(p) < need) { + Uint live = Arg(3); + reg[live] = map; + erts_garbage_collect(p, need, reg, live+1); + map = reg[live]; + old_mp = (map_t *)map_val(map); + } + + /* + * Update map, optimistically assuming that there are no + * new keys, allowing us to keep the old key tuple. + */ + + hp = p->htop; + E = p->stop; + + old_vals = map_get_values(old_mp); + old_keys = map_get_keys(old_mp); + + res = make_map(hp); + mp = (map_t *)hp; hp += 3; + mp->thing_word = MAP_HEADER; + mp->size = num_old; + mp->keys = old_mp->keys; + + ASSERT(num_updates > 0); + + /* Get array of key/value pairs to be updated */ + new_p = &Arg(5); + GET_TERM(*new_p, new_key); + + n = num_updates; + + for (i = 0; i < num_old; i++) { + if (new_key == THE_NON_VALUE || !eq(*old_keys, new_key)) { + /* not same keys */ + *hp++ = *old_vals; + } else { + GET_TERM(new_p[1], *hp); + hp++; + n--; + if (n == 0) { + new_key = THE_NON_VALUE; + } else { + new_p += 2; + GET_TERM(*new_p, new_key); + } + } + old_vals++, old_keys++; + } + + /* + * If we have exhausted the update list we are done. + */ + + if (n == 0) { + p->htop = hp; + return res; + } + + /* + * There were some new keys. We'll have to start over and rebuild + * the key tuple too. + */ + + kp = p->htop; + *kp++ = make_arityval(0); + + res = make_map(hp); + mp = (map_t *)hp; hp += 3; + mp->keys = make_tuple(kp-1); + + old_vals = map_get_values(old_mp); + old_keys = map_get_keys(old_mp); + + new_p = &Arg(5); + GET_TERM(*new_p, new_key); + n = num_updates; + + for (;;) { + Eterm key; + Sint c; + + ASSERT(kp < (Eterm *)mp); + key = *old_keys; + if ((c = cmp(key, new_key)) < 0) { + *kp++ = key; + *hp++ = *old_vals; + old_keys++, old_vals++, num_old--; + } else { /* Replace or insert new */ + *kp++ = new_key; + GET_TERM(new_p[1], *hp++); + if (c == 0) { /* If replacement */ + old_keys++, old_vals++, num_old--; + } + n--; + if (n == 0) { + break; + } else { + new_p += 2; + GET_TERM(*new_p, new_key); + } + } + if (num_old == 0) { + break; + } + } + + while (n-- > 0) { + GET_TERM(new_p[0], *kp++); + GET_TERM(new_p[1], *hp++); + new_p += 2; + } + + /* + * All updates done. Now copy the remaining part of the frame's + * keys and values. + */ + + while (num_old-- > 0) { + ASSERT(kp < (Eterm *)mp); + *kp++ = *old_keys++; + *hp++ = *old_vals++; + } + if ((n = (Eterm *)mp - kp) > 0) { + *kp = make_pos_bignum_header(n-1); + } + n = kp - p->htop - 1; /* Actual number of keys/values */ + *p->htop = make_arityval(n); + mp->thing_word = MAP_HEADER; + mp->size = n; + p->htop = hp; + return res; +} +#undef GET_TERM int catchlevel(Process *p) { diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 3ec534f0bc..42515d0494 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -578,6 +578,12 @@ bif os:unsetenv/1 bif re:inspect/2 +ubif erlang:is_map/1 +bif map:to_list/1 +bif map:new/0 +bif map:get/2 +bif map:put/3 + # # Obsolete # diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index 23c0fca6aa..3a987e213b 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -27,6 +27,7 @@ #include "erl_process.h" #include "erl_gc.h" #include "big.h" +#include "erl_map.h" #include "erl_binary.h" #include "erl_bits.h" #include "dtrace-wrapper.h" @@ -150,6 +151,24 @@ Uint size_object(Eterm obj) goto pop_next; } break; + case MAP_SUBTAG: + { + Uint n; + map_t *mp; + mp = (map_t*)map_val_rel(obj,base); + ptr = (Eterm *)mp; + n = map_get_size(mp) + 1; + sum += n + 2; + ptr += 2; /* hdr + size words */ + while (n--) { + obj = *ptr++; + if (!IS_CONST(obj)) { + ESTACK_PUSH(s, obj); + } + } + goto pop_next; + } + break; case BIN_MATCHSTATE_SUBTAG: erl_exit(ERTS_ABORT_EXIT, "size_object: matchstate term not allowed"); @@ -318,6 +337,15 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) } } break; + case MAP_SUBTAG: + { + i = map_get_size(objp) + 3; + *argp = make_map_rel(htop, dst_base); + while (i--) { + *htop++ = *objp++; + } + } + break; case REFC_BINARY_SUBTAG: { ProcBin* pb; @@ -537,6 +565,10 @@ Eterm copy_shallow(Eterm* ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) } goto off_heap_common; + case MAP_SUBTAG: + *hp++ = *tp++; + sz--; + break; case EXTERNAL_PID_SUBTAG: case EXTERNAL_PORT_SUBTAG: case EXTERNAL_REF_SUBTAG: diff --git a/erts/emulator/beam/erl_bif_op.c b/erts/emulator/beam/erl_bif_op.c index adac0052d6..37dd6457db 100644 --- a/erts/emulator/beam/erl_bif_op.c +++ b/erts/emulator/beam/erl_bif_op.c @@ -36,6 +36,7 @@ #include "dist.h" #include "erl_version.h" #include "erl_binary.h" +#include "erl_map.h" BIF_RETTYPE and_2(BIF_ALIST_2) { @@ -321,7 +322,10 @@ BIF_RETTYPE is_record_3(BIF_ALIST_3) BIF_RET(am_false); } - - - - +BIF_RETTYPE is_map_1(BIF_ALIST_1) +{ + if (is_map(BIF_ARG_1)) { + BIF_RET(am_true); + } + BIF_RET(am_false); +} diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index a358ecf326..d903053aa4 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -35,6 +35,7 @@ #include "bif.h" #include "big.h" #include "erl_binary.h" +#include "erl_map.h" #include "erl_thr_progress.h" #include "erl_db_util.h" diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c index 873a9860da..50bdc79506 100644 --- a/erts/emulator/beam/erl_debug.c +++ b/erts/emulator/beam/erl_debug.c @@ -29,6 +29,7 @@ #include "bif.h" #include "beam_catches.h" #include "erl_debug.h" +#include "erl_map.h" #define WITHIN(ptr, x, y) ((x) <= (ptr) && (ptr) < (y)) diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index ab8448e8a1..2022f70cbb 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -28,6 +28,7 @@ #include "beam_catches.h" #include "erl_binary.h" #include "erl_bits.h" +#include "erl_map.h" #include "error.h" #include "big.h" #include "erl_gc.h" diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h index 1801df359a..5203dda263 100644 --- a/erts/emulator/beam/erl_gc.h +++ b/erts/emulator/beam/erl_gc.h @@ -20,6 +20,8 @@ #ifndef __ERL_GC_H__ #define __ERL_GC_H__ +#include "erl_map.h" + /* GC declarations shared by beam/erl_gc.c and hipe/hipe_gc.c */ #if defined(DEBUG) && !ERTS_GLB_INLINE_INCL_FUNC_DEF @@ -42,23 +44,24 @@ do { \ HTOP += 2; /* update tospace htop */ \ } while(0) -#define MOVE_BOXED(PTR,HDR,HTOP,ORIG) \ -do { \ - Eterm gval; \ - Sint nelts; \ - \ - ASSERT(is_header(HDR)); \ - gval = make_boxed(HTOP); \ - *ORIG = gval; \ - *HTOP++ = HDR; \ - *PTR++ = gval; \ - nelts = header_arity(HDR); \ - switch ((HDR) & _HEADER_SUBTAG_MASK) { \ - case SUB_BINARY_SUBTAG: nelts++; break; \ - case FUN_SUBTAG: nelts+=((ErlFunThing*)(PTR-1))->num_free+1; break; \ - } \ - while (nelts--) \ - *HTOP++ = *PTR++; \ +#define MOVE_BOXED(PTR,HDR,HTOP,ORIG) \ +do { \ + Eterm gval; \ + Sint nelts; \ + \ + ASSERT(is_header(HDR)); \ + nelts = header_arity(HDR); \ + switch ((HDR) & _HEADER_SUBTAG_MASK) { \ + case SUB_BINARY_SUBTAG: nelts++; break; \ + case MAP_SUBTAG: nelts+=map_get_size(PTR) + 1; break; \ + case FUN_SUBTAG: nelts+=((ErlFunThing*)(PTR))->num_free+1; break; \ + } \ + gval = make_boxed(HTOP); \ + *ORIG = gval; \ + *HTOP++ = HDR; \ + *PTR++ = gval; \ + while (nelts--) *HTOP++ = *PTR++; \ + \ } while(0) #define in_area(ptr,start,nbytes) \ diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c new file mode 100644 index 0000000000..1fd7943c30 --- /dev/null +++ b/erts/emulator/beam/erl_map.c @@ -0,0 +1,225 @@ +/* + * %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% + * + * Author: Björn-Egil Dahlberg + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#include "erl_vm.h" +#include "global.h" +#include "erl_process.h" +#include "error.h" +#include "bif.h" + + +#include "erl_map.h" + + +BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + Uint n; + Eterm* hp; + Eterm *ks,*vs, res, tup; + map_t *mp = (map_t*)map_val(BIF_ARG_1); + + ks = map_get_keys(mp); + vs = map_get_values(mp); + n = map_get_size(mp); + hp = HAlloc(BIF_P, (2 + 3) * n); + res = NIL; + + while(n--) { + tup = TUPLE2(hp, ks[n], vs[n]); hp += 3; + res = CONS(hp, tup, res); hp += 2; + } + + BIF_RET(res); + } + + BIF_ERROR(BIF_P, BADARG); +} + +BIF_RETTYPE map_new_0(BIF_ALIST_0) { + Eterm* hp; + Eterm tup; + map_t *mp; + + hp = HAlloc(BIF_P, (3 + 1)); + tup = make_tuple(hp); + *hp++ = make_arityval(0); + + mp = (map_t*)hp; + mp->thing_word = MAP_HEADER; + mp->size = 0; + mp->keys = tup; + + BIF_RET(make_map(mp)); +} + +BIF_RETTYPE map_get_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm *hp, *ks,*vs, key, error; + map_t *mp; + Uint n,i; + char *s_error; + + mp = (map_t*)map_val(BIF_ARG_2); + key = BIF_ARG_1; + n = map_get_size(mp); + + if (n == 0) + goto error; + + ks = map_get_keys(mp); + vs = map_get_values(mp); + + if (is_immed(key)) { + for( i = 0; i < n; i++) { + if (ks[i] == key) { + BIF_RET(vs[i]); + } + } + } + + for( i = 0; i < n; i++) { + if (eq(ks[i], key)) { + BIF_RET(vs[i]); + } + } +error: + + s_error = "bad_key"; + error = am_atom_put(s_error, sys_strlen(s_error)); + + hp = HAlloc(BIF_P, 3); + BIF_P->fvalue = TUPLE2(hp, error, key); + BIF_ERROR(BIF_P, EXC_ERROR_2); + } + BIF_ERROR(BIF_P, BADARG); +} + +BIF_RETTYPE map_put_3(BIF_ALIST_3) { + if (is_map(BIF_ARG_3)) { + Sint n,i; + Sint c = 0; + Eterm* hp, *shp; + Eterm *ks,*vs, res, key, tup; + map_t *mp = (map_t*)map_val(BIF_ARG_3); + + key = BIF_ARG_1; + n = map_get_size(mp); + + if (n == 0) { + hp = HAlloc(BIF_P, 4 + 2); + tup = make_tuple(hp); + *hp++ = make_arityval(1); + *hp++ = key; + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = 1; + *hp++ = tup; + *hp++ = BIF_ARG_2; + + BIF_RET(res); + } + + ks = map_get_keys(mp); + vs = map_get_values(mp); + /* only allocate for values, + * assume key-tuple will be intact + */ + + hp = HAlloc(BIF_P, 3 + n); + shp = hp; /* save hp, used if optimistic update fails */ + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = n; + *hp++ = mp->keys; + + if (is_immed(key)) { + for( i = 0; i < n; i ++) { + if (ks[i] == key) { + *hp++ = BIF_ARG_2; + vs++; + c = 1; + } else { + *hp++ = *vs++; + } + } + } else { + for( i = 0; i < n; i ++) { + if (eq(ks[i], key)) { + *hp++ = BIF_ARG_2; + vs++; + c = 1; + } else { + *hp++ = *vs++; + } + } + } + + if (c) + BIF_RET(res); + + /* need to make a new tuple, + * use old hp since it needs to be recreated anyway. + */ + tup = make_tuple(shp); + *shp++ = make_arityval(n+1); + + hp = HAlloc(BIF_P, 3 + n + 1); + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = n + 1; + *hp++ = tup; + + ks = map_get_keys(mp); + vs = map_get_values(mp); + + ASSERT(n >= 0); + + /* copy map in order */ + while (n && ((c = CMP(*ks, key)) < 0)) { + *shp++ = *ks++; + *hp++ = *vs++; + n--; + } + + *shp++ = key; + *hp++ = BIF_ARG_2; + + ASSERT(n >= 0); + + while(n--) { + *shp++ = *ks++; + *hp++ = *vs++; + } + /* we have one word remaining + * this will work out fine once we get the size word + * in the header. + */ + *shp = make_pos_bignum_header(0); + BIF_RET(res); + } + + BIF_ERROR(BIF_P, BADARG); +} diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h new file mode 100644 index 0000000000..ca8ce0f974 --- /dev/null +++ b/erts/emulator/beam/erl_map.h @@ -0,0 +1,64 @@ +/* + * %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% + */ + + +#ifndef __ERL_MAP_H__ +#define __ERL_MAP_H__ + +#include "sys.h" +/* MAP */ + +typedef struct map_s { + Eterm thing_word; + Uint size; + Eterm keys; /* tuple */ +} map_t; +/* map node + * + * ----------- + * Eterm THING + * Eterm Keys -> {K1, K2, K3, ..., Kn} where n = arity + * ---- + * Eterm V1 + * ... + * Eterm Vn, where n = arity + * ----------- + */ + + + +/* erl_term.h stuff */ +#define make_map(x) make_boxed((Eterm*)(x)) +#define make_map_rel(x, BASE) make_boxed_rel((Eterm*)(x),(BASE)) +#define is_map(x) (is_boxed((x)) && is_map_header(*boxed_val((x)))) +#define is_map_rel(RTERM,BASE) is_map(rterm2wterm(RTERM,BASE)) +#define is_not_map(x) (!is_map((x))) +#define is_map_header(x) (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_MAP) +#define header_is_map(x) ((((x) & (_HEADER_SUBTAG_MASK)) == MAP_SUBTAG)) +#define map_val(x) (_unchecked_boxed_val((x))) +#define map_val_rel(RTERM, BASE) map_val(rterm2wterm(RTERM, BASE)) + +#define map_get_values(x) (((Eterm *)(x)) + 3) +#define map_get_keys(x) (((Eterm *)tuple_val(((map_t *)(x))->keys)) + 1) +#define map_get_size(x) (((map_t*)(x))->size) + +#define MAP_HEADER _make_header(1,_TAG_HEADER_MAP) + +#endif + diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index 436147749e..d18760dc43 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -24,6 +24,7 @@ #include "erl_printf_term.h" #include "sys.h" #include "big.h" +#include "erl_map.h" #define PRINT_CHAR(CNT, FN, ARG, C) \ do { \ @@ -216,14 +217,15 @@ static int print_atom_name(fmtfn_t fn, void* arg, Eterm atom, long *dcount) } -#define PRT_BAR ((Eterm) 0) -#define PRT_COMMA ((Eterm) 1) -#define PRT_CLOSE_LIST ((Eterm) 2) -#define PRT_CLOSE_TUPLE ((Eterm) 3) -#define PRT_TERM ((Eterm) 4) -#define PRT_ONE_CONS ((Eterm) 5) -#define PRT_PATCH_FUN_SIZE ((Eterm) 6) -#define PRT_LAST_ARRAY_ELEMENT ((Eterm) 7) /* Note! Must be last... */ +#define PRT_BAR ((Eterm) 0) +#define PRT_COMMA ((Eterm) 1) +#define PRT_CLOSE_LIST ((Eterm) 2) +#define PRT_CLOSE_TUPLE ((Eterm) 3) +#define PRT_ASSOC ((Eterm) 4) +#define PRT_TERM ((Eterm) 5) +#define PRT_ONE_CONS ((Eterm) 6) +#define PRT_PATCH_FUN_SIZE ((Eterm) 7) +#define PRT_LAST_ARRAY_ELEMENT ((Eterm) 8) /* Note! Must be last... */ static int print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, @@ -260,6 +262,9 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, case PRT_CLOSE_TUPLE: PRINT_CHAR(res, fn, arg, '}'); goto L_outer_loop; + case PRT_ASSOC: + PRINT_STRING(res, fn, arg, "=>"); + goto L_outer_loop; default: popped.word = WSTACK_POP(s); @@ -483,6 +488,37 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, PRINT_CHAR(res, fn, arg, '>'); } break; + case MAP_DEF: + { + Uint n; + Eterm *ks, *vs; + map_t *mp = (map_t *)map_val(wobj); + n = map_get_size(mp); + ks = map_get_keys(mp); + vs = map_get_values(mp); + + PRINT_CHAR(res, fn, arg, '#'); + PRINT_CHAR(res, fn, arg, '{'); + WSTACK_PUSH(s, PRT_CLOSE_TUPLE); + if (n > 0) { + n--; + WSTACK_PUSH(s, vs[n]); + WSTACK_PUSH(s, PRT_TERM); + WSTACK_PUSH(s, PRT_ASSOC); + WSTACK_PUSH(s, ks[n]); + WSTACK_PUSH(s, PRT_TERM); + + while (n--) { + WSTACK_PUSH(s, PRT_COMMA); + WSTACK_PUSH(s, vs[n]); + WSTACK_PUSH(s, PRT_TERM); + WSTACK_PUSH(s, PRT_ASSOC); + WSTACK_PUSH(s, ks[n]); + WSTACK_PUSH(s, PRT_TERM); + } + } + } + break; default: PRINT_STRING(res, fn, arg, " #include @@ -85,7 +86,10 @@ unsigned tag_val_def(Wterm x) case (_TAG_HEADER_EXTERNAL_PID >> _TAG_PRIMARY_SIZE): return EXTERNAL_PID_DEF; case (_TAG_HEADER_EXTERNAL_PORT >> _TAG_PRIMARY_SIZE): return EXTERNAL_PORT_DEF; case (_TAG_HEADER_EXTERNAL_REF >> _TAG_PRIMARY_SIZE): return EXTERNAL_REF_DEF; - default: return BINARY_DEF; + case (_TAG_HEADER_REFC_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; + case (_TAG_HEADER_HEAP_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; + case (_TAG_HEADER_SUB_BIN >> _TAG_PRIMARY_SIZE): return BINARY_DEF; + case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE): return MAP_DEF; } break; } diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 50d3e63c58..f10a3a9d38 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -135,11 +135,12 @@ struct erl_node_; /* Declared in erl_node_tables.h */ #define REF_SUBTAG (0x4 << _TAG_PRIMARY_SIZE) /* REF */ #define FUN_SUBTAG (0x5 << _TAG_PRIMARY_SIZE) /* FUN */ #define FLOAT_SUBTAG (0x6 << _TAG_PRIMARY_SIZE) /* FLOAT */ -#define EXPORT_SUBTAG (0x7 << _TAG_PRIMARY_SIZE) /* FLOAT */ +#define EXPORT_SUBTAG (0x7 << _TAG_PRIMARY_SIZE) /* FLOAT */ #define _BINARY_XXX_MASK (0x3 << _TAG_PRIMARY_SIZE) #define REFC_BINARY_SUBTAG (0x8 << _TAG_PRIMARY_SIZE) /* BINARY */ #define HEAP_BINARY_SUBTAG (0x9 << _TAG_PRIMARY_SIZE) /* BINARY */ #define SUB_BINARY_SUBTAG (0xA << _TAG_PRIMARY_SIZE) /* BINARY */ +#define MAP_SUBTAG (0xB << _TAG_PRIMARY_SIZE) /* MAP */ #define EXTERNAL_PID_SUBTAG (0xC << _TAG_PRIMARY_SIZE) /* EXTERNAL_PID */ #define EXTERNAL_PORT_SUBTAG (0xD << _TAG_PRIMARY_SIZE) /* EXTERNAL_PORT */ #define EXTERNAL_REF_SUBTAG (0xE << _TAG_PRIMARY_SIZE) /* EXTERNAL_REF */ @@ -155,6 +156,7 @@ struct erl_node_; /* Declared in erl_node_tables.h */ #define _TAG_HEADER_REFC_BIN (TAG_PRIMARY_HEADER|REFC_BINARY_SUBTAG) #define _TAG_HEADER_HEAP_BIN (TAG_PRIMARY_HEADER|HEAP_BINARY_SUBTAG) #define _TAG_HEADER_SUB_BIN (TAG_PRIMARY_HEADER|SUB_BINARY_SUBTAG) +#define _TAG_HEADER_MAP (TAG_PRIMARY_HEADER|MAP_SUBTAG) #define _TAG_HEADER_EXTERNAL_PID (TAG_PRIMARY_HEADER|EXTERNAL_PID_SUBTAG) #define _TAG_HEADER_EXTERNAL_PORT (TAG_PRIMARY_HEADER|EXTERNAL_PORT_SUBTAG) #define _TAG_HEADER_EXTERNAL_REF (TAG_PRIMARY_HEADER|EXTERNAL_REF_SUBTAG) @@ -354,7 +356,10 @@ _ET_DECLARE_CHECKED(Uint,thing_subtag,Eterm) #define is_value(x) ((x) != THE_NON_VALUE) /* binary object access methods */ -#define is_binary_header(x) (((x) & (_TAG_HEADER_MASK-_BINARY_XXX_MASK)) == _TAG_HEADER_REFC_BIN) +#define is_binary_header(x) \ + ((((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_REFC_BIN) || \ + (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_HEAP_BIN) || \ + (((x) & (_TAG_HEADER_MASK)) == _TAG_HEADER_SUB_BIN)) #define make_binary(x) make_boxed((Eterm*)(x)) #define is_binary(x) (is_boxed((x)) && is_binary_header(*boxed_val((x)))) #define is_not_binary(x) (!is_binary((x))) @@ -1064,8 +1069,8 @@ _ET_DECLARE_CHECKED(Uint,y_reg_index,Uint) /* * Backwards compatibility definitions: - * - #define virtal *_DEF constants with values that fit term order: - * number < atom < ref < fun < port < pid < tuple < nil < cons < binary + * - #define virtual *_DEF constants with values that fit term order: + * number < atom < ref < fun < port < pid < tuple < map < nil < cons < binary * - tag_val_def() function generates virtual _DEF tag * - not_eq_tags() and NUMBER_CODE() defined in terms * of the tag_val_def() function @@ -1074,19 +1079,20 @@ _ET_DECLARE_CHECKED(Uint,y_reg_index,Uint) #define BINARY_DEF 0x0 #define LIST_DEF 0x1 #define NIL_DEF 0x2 -#define TUPLE_DEF 0x3 -#define PID_DEF 0x4 -#define EXTERNAL_PID_DEF 0x5 -#define PORT_DEF 0x6 -#define EXTERNAL_PORT_DEF 0x7 -#define EXPORT_DEF 0x8 -#define FUN_DEF 0x9 -#define REF_DEF 0xa -#define EXTERNAL_REF_DEF 0xb -#define ATOM_DEF 0xc -#define FLOAT_DEF 0xd -#define BIG_DEF 0xe -#define SMALL_DEF 0xf +#define MAP_DEF 0x3 +#define TUPLE_DEF 0x4 +#define PID_DEF 0x5 +#define EXTERNAL_PID_DEF 0x6 +#define PORT_DEF 0x7 +#define EXTERNAL_PORT_DEF 0x8 +#define EXPORT_DEF 0x9 +#define FUN_DEF 0xa +#define REF_DEF 0xb +#define EXTERNAL_REF_DEF 0xc +#define ATOM_DEF 0xd +#define FLOAT_DEF 0xe +#define BIG_DEF 0xf +#define SMALL_DEF 0x10 #if ET_DEBUG extern unsigned tag_val_def_debug(Wterm, const char*, unsigned); @@ -1096,8 +1102,8 @@ extern unsigned tag_val_def(Wterm); #endif #define not_eq_tags(X,Y) (tag_val_def((X)) ^ tag_val_def((Y))) -#define NUMBER_CODE(x,y) ((tag_val_def(x) << 4) | tag_val_def(y)) -#define _NUMBER_CODE(TX,TY) ((TX << 4) | TY) +#define NUMBER_CODE(x,y) ((tag_val_def(x) << 5) | tag_val_def(y)) +#define _NUMBER_CODE(TX,TY) ((TX << 5) | TY) #define SMALL_SMALL _NUMBER_CODE(SMALL_DEF,SMALL_DEF) #define SMALL_BIG _NUMBER_CODE(SMALL_DEF,BIG_DEF) #define SMALL_FLOAT _NUMBER_CODE(SMALL_DEF,FLOAT_DEF) diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index e0776cf67d..6cdfd1c989 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -31,6 +31,7 @@ #include "bif.h" #include "erl_binary.h" #include "erl_bits.h" +#include "erl_map.h" #include "packet_parser.h" #include "erl_gc.h" #define ERTS_WANT_DB_INTERNAL__ @@ -785,10 +786,10 @@ Uint32 make_hash(Eterm term_arg) unsigned op; /* Must not collide with the real tag_val_def's: */ -#define MAKE_HASH_TUPLE_OP 0x10 -#define MAKE_HASH_FUN_OP 0x11 -#define MAKE_HASH_CDR_PRE_OP 0x12 -#define MAKE_HASH_CDR_POST_OP 0x13 +#define MAKE_HASH_TUPLE_OP 0x11 +#define MAKE_HASH_FUN_OP 0x12 +#define MAKE_HASH_CDR_PRE_OP 0x13 +#define MAKE_HASH_CDR_POST_OP 0x14 /* ** Convenience macro for calculating a bytewise hash on an unsigned 32 bit @@ -2007,6 +2008,18 @@ tailrecur_ne: ++bb; goto term_array; } + case MAP_SUBTAG: + { + aa = map_val_rel(a, a_base); + if (!is_boxed(b) || *boxed_val_rel(b,b_base) != *aa) + goto not_equal; + bb = map_val_rel(b,b_base); + if ((sz = map_get_size((map_t*)aa)) == 0) goto pop_next; + aa += 2; + bb += 2; + sz += 1; /* increment for tuple-keys */ + goto term_array; + } case REFC_BINARY_SUBTAG: case HEAP_BINARY_SUBTAG: case SUB_BINARY_SUBTAG: @@ -2281,7 +2294,7 @@ static int cmpbytes(byte *s1, int l1, byte *s2, int l2) * * According to the Erlang Standard, types are orderered as follows: * numbers < (characters) < atoms < refs < funs < ports < pids < - * tuples < [] < conses < binaries. + * tuples < maps < [] < conses < binaries. * * Note that characters are currently not implemented. * @@ -2464,7 +2477,25 @@ tailrecur_ne: ++aa; ++bb; goto term_array; + case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE) : + if (!is_map_rel(b,b_base)) { + a_tag = MAP_DEF; + goto mixed_types; + } + aa = (Eterm *)map_val_rel(a,a_base); + bb = (Eterm *)map_val_rel(b,b_base); + i = map_get_size((map_t*)aa); + if (i != map_get_size((map_t*)bb)) { + RETURN_NEQ((int)(i - map_get_size((map_t*)bb))); + } + if (i == 0) { + goto pop_next; + } + aa += 2; + bb += 2; + i += 1; /* increment for tuple-keys */ + goto term_array; case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): if (!is_float_rel(b,b_base)) { a_tag = FLOAT_DEF; diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c index e09988e2c5..c3687681cf 100644 --- a/erts/emulator/hipe/hipe_bif2.c +++ b/erts/emulator/hipe/hipe_bif2.c @@ -31,6 +31,7 @@ #include "erl_process.h" #include "bif.h" #include "big.h" +#include "erl_map.h" #include "hipe_debug.h" #include "hipe_mode_switch.h" #include "hipe_arch.h" diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c index bf25ba82af..32694a8f97 100644 --- a/erts/emulator/hipe/hipe_debug.c +++ b/erts/emulator/hipe/hipe_debug.c @@ -36,6 +36,7 @@ #include "beam_load.h" #include "hipe_mode_switch.h" #include "hipe_debug.h" +#include "erl_map.h" static const char dashes[2*sizeof(long)+5] = { [0 ... 2*sizeof(long)+3] = '-' -- cgit v1.2.3 From 92303a2e1abdf74aa3bc3af095131a59a601c45e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 1 Oct 2013 19:27:54 +0200 Subject: erts: Add phash2 Map functionality --- erts/emulator/beam/utils.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 6cdfd1c989..bc7e91295d 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1088,6 +1088,7 @@ make_hash2(Eterm term) #define HCONST_13 0x08d12e65UL #define HCONST_14 0xa708a81eUL #define HCONST_15 0x454021d7UL +#define HCONST_16 0xe3779b90UL #define UINT32_HASH_2(Expr1, Expr2, AConst) \ do { \ @@ -1190,6 +1191,28 @@ make_hash2(Eterm term) term = elem[1]; } break; + case MAP_SUBTAG: + { + map_t *mp = (map_t *)map_val(term); + int i; + int size = map_get_size(mp); + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + UINT32_HASH(size, HCONST_16); + if (size == 0) /* Empty Map */ + goto hash2_common; + + for (i = size - 1; i >= 0; i--) { + tmp = vs[i]; + ESTACK_PUSH(s, tmp); + } + for (i = size - 1; i >= 1; i--) { + tmp = ks[i]; + ESTACK_PUSH(s, tmp); + } + term = ks[0]; + } + break; case EXPORT_SUBTAG: { Export* ep = *((Export **) (export_val(term) + 1)); -- 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 --- erts/emulator/beam/beam_emu.c | 2 ++ erts/emulator/beam/beam_load.c | 2 ++ erts/emulator/beam/bif.tab | 1 + erts/emulator/beam/erl_bif_guard.c | 23 +++++++++++++++++++++++ erts/emulator/beam/erl_map.c | 23 +++++++++++++++++++++-- erts/emulator/beam/global.h | 1 + lib/stdlib/src/erl_internal.erl | 2 ++ 7 files changed, 52 insertions(+), 2 deletions(-) diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 5c955b9566..1cad31be2c 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -5080,6 +5080,8 @@ translate_gc_bif(void* gcf) return bit_size_1; } else if (gcf == erts_gc_byte_size_1) { return byte_size_1; + } else if (gcf == erts_gc_map_size_1) { + return map_size_1; } else if (gcf == erts_gc_abs_1) { return abs_1; } else if (gcf == erts_gc_float_1) { diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 58207ec75b..b589d1c930 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -3783,6 +3783,8 @@ gen_guard_bif1(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif, op->a[1].val = (BeamInstr) (void *) erts_gc_bit_size_1; } else if (bf == byte_size_1) { op->a[1].val = (BeamInstr) (void *) erts_gc_byte_size_1; + } else if (bf == map_size_1) { + op->a[1].val = (BeamInstr) (void *) erts_gc_map_size_1; } else if (bf == abs_1) { op->a[1].val = (BeamInstr) (void *) erts_gc_abs_1; } else if (bf == float_1) { diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 42515d0494..a8623fcf61 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -579,6 +579,7 @@ bif os:unsetenv/1 bif re:inspect/2 ubif erlang:is_map/1 +ubif erlang:map_size/1 bif map:to_list/1 bif map:new/0 bif map:get/2 diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c index a715756c15..bbd8aa31d9 100644 --- a/erts/emulator/beam/erl_bif_guard.c +++ b/erts/emulator/beam/erl_bif_guard.c @@ -33,6 +33,7 @@ #include "bif.h" #include "big.h" #include "erl_binary.h" +#include "erl_map.h" static Eterm gc_double_to_integer(Process* p, double x, Eterm* reg, Uint live); @@ -455,6 +456,28 @@ Eterm erts_gc_byte_size_1(Process* p, Eterm* reg, Uint live) } } +Eterm erts_gc_map_size_1(Process* p, Eterm* reg, Uint live) +{ + Eterm arg = reg[live]; + if (is_map(arg)) { + map_t *mp = (map_t*)map_val(arg); + Uint size = map_get_size(mp); + if (IS_USMALL(0, size)) { + return make_small(size); + } else { + Eterm* hp; + if (ERTS_NEED_GC(p, BIG_UINT_HEAP_SIZE)) { + erts_garbage_collect(p, BIG_UINT_HEAP_SIZE, reg, live); + } + hp = p->htop; + p->htop += BIG_UINT_HEAP_SIZE; + return uint_to_big(size, hp); + } + } else { + BIF_ERROR(p, BADARG); + } +} + Eterm erts_gc_abs_1(Process* p, Eterm* reg, Uint live) { Eterm arg; diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 1fd7943c30..6a59fa29b8 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -30,10 +30,8 @@ #include "error.h" #include "bif.h" - #include "erl_map.h" - BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Uint n; @@ -58,6 +56,27 @@ BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { BIF_ERROR(BIF_P, BADARG); } + +/* erlang:map_size/1 + * the corresponding instruction is implemented in: + * beam/erl_bif_guard.c + */ + +BIF_RETTYPE map_size_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + Eterm *hp; + Uint hsz = 0; + map_t *mp = (map_t*)map_val(BIF_ARG_1); + Uint n = map_get_size(mp); + + erts_bld_uint(NULL, &hsz, n); + hp = HAlloc(BIF_P, hsz); + BIF_RET(erts_bld_uint(&hp, NULL, n)); + } + + BIF_ERROR(BIF_P, BADARG); +} + BIF_RETTYPE map_new_0(BIF_ALIST_0) { Eterm* hp; Eterm tup; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 83a8911a36..8fcb95d0e2 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -998,6 +998,7 @@ Eterm erts_gc_length_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_size_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_bit_size_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_byte_size_1(Process* p, Eterm* reg, Uint live); +Eterm erts_gc_map_size_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_abs_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_float_1(Process* p, Eterm* reg, Uint live); Eterm erts_gc_round_1(Process* p, Eterm* reg, Uint live); 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. --- erts/emulator/beam/bif.tab | 1 + lib/compiler/src/beam_utils.erl | 1 + lib/stdlib/src/erl_internal.erl | 3 +++ 3 files changed, 5 insertions(+) diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index a8623fcf61..8f85f931c9 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -572,6 +572,7 @@ bif erlang:binary_to_float/1 bif io:printable_range/0 bif os:unsetenv/1 + # # New in R17A # 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 bc0960c095e9482ba0f452c9df3623f5c9c54e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 30 Sep 2013 20:13:07 +0200 Subject: erts: Introduce more Maps BIFs * map:remove/2 * map:keys/1 * map:values/1 * map:is_key/2 * map:update/3 - Equivalent to ':=' operator in #{ K := V } maps. * map:from_list/1 - map:from_list/1 takes any unsorted key/value list, [{K,V}], and produces a map. Duplicate keys are removed. The latest key is kept. * map:find/2 - Searches for a pair that *equals* input key. * map:merge/2 - Merge two maps to one map. --- erts/emulator/beam/bif.tab | 11 +- erts/emulator/beam/erl_map.c | 565 ++++++++++++++++++++++++++++++++++++++++--- erts/emulator/beam/erl_map.h | 6 +- 3 files changed, 547 insertions(+), 35 deletions(-) diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 8f85f931c9..306861a4c5 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -572,7 +572,6 @@ bif erlang:binary_to_float/1 bif io:printable_range/0 bif os:unsetenv/1 - # # New in R17A # @@ -582,9 +581,17 @@ bif re:inspect/2 ubif erlang:is_map/1 ubif erlang:map_size/1 bif map:to_list/1 -bif map:new/0 +bif map:find/2 bif map:get/2 +bif map:from_list/1 +bif map:is_key/2 +bif map:keys/1 +bif map:merge/2 +bif map:new/0 bif map:put/3 +bif map:remove/2 +bif map:update/3 +bif map:values/1 # # Obsolete diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 6a59fa29b8..6fd60f0689 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -32,6 +32,57 @@ #include "erl_map.h" +/* BIFs + * + * DONE: + * - erlang:is_map/1 + * - erlang:map_size/1 + * + * - map:find/2 + * - map:from_list/1 + * - map:get/2 + * - map:is_key/2 + * - map:keys/1 + * - map:merge/2 + * - map:new/0 + * - map:put/3 + * - map:remove/2 + * - map:to_list/1 + * - map:update/3 + * - map:values/1 + * + * TODO: + * - map:foldl/3 + * - map:foldr/3 + * - map:map/3 + * - map:size/1 + * - map:without/2 + * + */ + +/* erlang:map_size/1 + * the corresponding instruction is implemented in: + * beam/erl_bif_guard.c + */ + +BIF_RETTYPE map_size_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + Eterm *hp; + Uint hsz = 0; + map_t *mp = (map_t*)map_val(BIF_ARG_1); + Uint n = map_get_size(mp); + + erts_bld_uint(NULL, &hsz, n); + hp = HAlloc(BIF_P, hsz); + BIF_RET(erts_bld_uint(&hp, NULL, n)); + } + + BIF_ERROR(BIF_P, BADARG); +} + +/* map:to_list/1 + */ + BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Uint n; @@ -56,43 +107,40 @@ BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { BIF_ERROR(BIF_P, BADARG); } - -/* erlang:map_size/1 - * the corresponding instruction is implemented in: - * beam/erl_bif_guard.c +/* map:find/2 + * return value if key *equals* a key in the map */ -BIF_RETTYPE map_size_1(BIF_ALIST_1) { - if (is_map(BIF_ARG_1)) { - Eterm *hp; - Uint hsz = 0; - map_t *mp = (map_t*)map_val(BIF_ARG_1); - Uint n = map_get_size(mp); +BIF_RETTYPE map_find_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm *hp, *ks,*vs, key, res; + map_t *mp; + Uint n,i; - erts_bld_uint(NULL, &hsz, n); - hp = HAlloc(BIF_P, hsz); - BIF_RET(erts_bld_uint(&hp, NULL, n)); - } + mp = (map_t*)map_val(BIF_ARG_2); + key = BIF_ARG_1; + n = map_get_size(mp); + ks = map_get_keys(mp); + vs = map_get_values(mp); + for( i = 0; i < n; i++) { + if (CMP(ks[i], key)==0) { + hp = HAlloc(BIF_P, 3); + res = make_tuple(hp); + *hp++ = make_arityval(2); + *hp++ = am_ok; + *hp++ = vs[i]; + BIF_RET(res); + } + } + BIF_RET(am_error); + } BIF_ERROR(BIF_P, BADARG); } - -BIF_RETTYPE map_new_0(BIF_ALIST_0) { - Eterm* hp; - Eterm tup; - map_t *mp; - - hp = HAlloc(BIF_P, (3 + 1)); - tup = make_tuple(hp); - *hp++ = make_arityval(0); - - mp = (map_t*)hp; - mp->thing_word = MAP_HEADER; - mp->size = 0; - mp->keys = tup; - - BIF_RET(make_map(mp)); -} +/* map:get/2 + * return value if key *matches* a key in the map + * exception bad_key if none matches + */ BIF_RETTYPE map_get_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { @@ -136,6 +184,297 @@ error: BIF_ERROR(BIF_P, BADARG); } +/* map:from_list/1 + * List may be unsorted [{K,V}] + */ + +BIF_RETTYPE map_from_list_1(BIF_ALIST_1) { + Eterm *kv, item = BIF_ARG_1; + Eterm *hp, *thp,*vs, *ks, keys, res; + map_t *mp; + Uint size = 0, unused_size = 0; + Sint c = 0; + Sint idx = 0; + + if (is_list(item) || is_nil(item)) { + + /* Calculate size and check validity */ + + while(is_list(item)) { + res = CAR(list_val(item)); + if (is_not_tuple(res)) + goto error; + + kv = tuple_val(res); + if (*kv != make_arityval(2)) + goto error; + + size++; + item = CDR(list_val(item)); + } + + if (is_not_nil(item)) + goto error; + + hp = HAlloc(BIF_P, 3 + 1 + (2 * size)); + thp = hp; + keys = make_tuple(hp); + *hp++ = make_arityval(size); + ks = hp; + hp += size; + mp = (map_t*)hp; + res = make_map(mp); + hp += MAP_HEADER_SIZE; + vs = hp; + + mp->thing_word = MAP_HEADER; + mp->size = size; /* set later, might shrink*/ + mp->keys = keys; + + if (size == 0) + BIF_RET(res); + + item = BIF_ARG_1; + + /* first entry */ + kv = tuple_val(CAR(list_val(item))); + ks[0] = kv[1]; + vs[0] = kv[2]; + size = 1; + item = CDR(list_val(item)); + + /* insert sort key/value pairs */ + while(is_list(item)) { + + kv = tuple_val(CAR(list_val(item))); + + /* compare ks backwards + * idx represent word index to be written (hole position). + * We cannot copy the elements when searching since we might + * have an equal key. So we search for just the index first =( + * + * It is perhaps faster to move the values in the first pass. + * Check for uniqueness during insert phase and then have a + * second phace compacting the map if duplicates are found + * during insert. .. or do someother sort .. shell-sort perhaps. + */ + + idx = size; + + while(idx > 0 && (c = CMP(kv[1],ks[idx-1])) < 0) { idx--; } + + if (c == 0) { + /* last compare was equal, + * i.e. we have to release memory + * and overwrite that key/value + */ + ks[idx-1] = kv[1]; + vs[idx-1] = kv[2]; + unused_size++; + } else { + Uint i = size; + while(i > idx) { + ks[i] = ks[i-1]; + vs[i] = vs[i-1]; + i--; + } + ks[idx] = kv[1]; + vs[idx] = kv[2]; + size++; + } + item = CDR(list_val(item)); + } + + if (unused_size) { + /* the key tuple is embedded in the heap + * write a bignum to clear it. + */ + /* release values as normal since they are on the top of the heap */ + + ks[size] = make_pos_bignum_header(unused_size - 1); + HRelease(BIF_P, vs + size + unused_size, vs + size); + } + + *thp = make_arityval(size); + mp->size = size; + BIF_RET(res); + } + +error: + + BIF_ERROR(BIF_P, BADARG); +} + +/* map:is_key/2 + */ + +BIF_RETTYPE map_is_key_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm *ks, key; + map_t *mp; + Uint n,i; + + mp = (map_t*)map_val(BIF_ARG_2); + key = BIF_ARG_1; + n = map_get_size(mp); + ks = map_get_keys(mp); + + if (n == 0) + BIF_RET(am_false); + + if (is_immed(key)) { + for( i = 0; i < n; i++) { + if (ks[i] == key) { + BIF_RET(am_true); + } + } + } + + for( i = 0; i < n; i++) { + if (eq(ks[i], key)) { + BIF_RET(am_true); + } + } + BIF_RET(am_false); + } + BIF_ERROR(BIF_P, BADARG); +} + +/* map:keys/1 + */ + +BIF_RETTYPE map_keys_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + Eterm *hp, *ks, res = NIL; + map_t *mp; + Uint n; + + mp = (map_t*)map_val(BIF_ARG_1); + n = map_get_size(mp); + + if (n == 0) + BIF_RET(res); + + hp = HAlloc(BIF_P, (2 * n)); + ks = map_get_keys(mp); + + while(n--) { + res = CONS(hp, ks[n], res); hp += 2; + } + + BIF_RET(res); + } + BIF_ERROR(BIF_P, BADARG); +} +/* map:merge/2 + */ + +BIF_RETTYPE map_merge_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_1) && is_map(BIF_ARG_2)) { + Eterm *hp,*thp; + Eterm tup; + Eterm *ks,*vs,*ks1,*vs1,*ks2,*vs2; + map_t *mp1,*mp2,*mp_new; + Uint n1,n2,i1,i2,need,unused_size=0; + int c = 0; + + mp1 = (map_t*)map_val(BIF_ARG_1); + mp2 = (map_t*)map_val(BIF_ARG_2); + n1 = map_get_size(mp1); + n2 = map_get_size(mp2); + + need = MAP_HEADER_SIZE + 1 + 2*(n1 + n2); + + hp = HAlloc(BIF_P, need); + thp = hp; + tup = make_tuple(thp); + ks = hp + 1; hp += 1 + n1 + n2; + mp_new = (map_t*)hp; hp += MAP_HEADER_SIZE; + vs = hp; hp += n1 + n2; + + mp_new->thing_word = MAP_HEADER; + mp_new->size = 0; + mp_new->keys = tup; + + i1 = 0; i2 = 0; + ks1 = map_get_keys(mp1); + vs1 = map_get_values(mp1); + ks2 = map_get_keys(mp2); + vs2 = map_get_values(mp2); + + while(i1 < n1 && i2 < n2) { + c = CMP(ks1[i1],ks2[i2]); + if ( c == 0) { + /* use righthand side arguments map value, + * but advance both maps */ + *ks++ = ks2[i2]; + *vs++ = vs2[i2]; + i1++, i2++, unused_size++; + } else if ( c < 0) { + *ks++ = ks1[i1]; + *vs++ = vs1[i1]; + i1++; + } else { + *ks++ = ks2[i2]; + *vs++ = vs2[i2]; + i2++; + } + } + + /* copy remaining */ + while (i1 < n1) { + *ks++ = ks1[i1]; + *vs++ = vs1[i1]; + i1++; + } + + while (i2 < n2) { + *ks++ = ks2[i2]; + *vs++ = vs2[i2]; + i2++; + } + + if (unused_size) { + /* the key tuple is embedded in the heap, write a bignum to clear it. + * + * release values as normal since they are on the top of the heap + * size = n1 + n1 - unused_size + */ + + *ks = make_pos_bignum_header(unused_size - 1); + HRelease(BIF_P, vs + unused_size, vs); + } + + mp_new->size = n1 + n2 - unused_size; + *thp = make_arityval(n1 + n2 - unused_size); + + BIF_RET(make_map(mp_new)); + } + BIF_ERROR(BIF_P, BADARG); +} +/* map:new/2 + */ + +BIF_RETTYPE map_new_0(BIF_ALIST_0) { + Eterm* hp; + Eterm tup; + map_t *mp; + + hp = HAlloc(BIF_P, (3 + 1)); + tup = make_tuple(hp); + *hp++ = make_arityval(0); + + mp = (map_t*)hp; + mp->thing_word = MAP_HEADER; + mp->size = 0; + mp->keys = tup; + + BIF_RET(make_map(mp)); +} + +/* map:put/3 + */ + BIF_RETTYPE map_put_3(BIF_ALIST_3) { if (is_map(BIF_ARG_3)) { Sint n,i; @@ -242,3 +581,167 @@ BIF_RETTYPE map_put_3(BIF_ALIST_3) { BIF_ERROR(BIF_P, BADARG); } + +/* map:remove/3 + */ + +BIF_RETTYPE map_remove_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Sint n; + Sint found = 0; + Uint need; + Eterm *thp, *mhp; + Eterm *ks, *vs, res, key,tup; + map_t *mp = (map_t*)map_val(BIF_ARG_2); + + key = BIF_ARG_1; + n = map_get_size(mp); + + if (n == 0) + BIF_RET(BIF_ARG_2); + + ks = map_get_keys(mp); + vs = map_get_values(mp); + + /* Assume key exists. + * Release allocated if it didn't. + * Allocate key tuple first. + */ + + need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */ + thp = HAlloc(BIF_P, need); + mhp = thp + n; /* offset with tuple heap size */ + + tup = make_tuple(thp); + *thp++ = make_arityval(n - 1); + + res = make_map(mhp); + *mhp++ = MAP_HEADER; + *mhp++ = n - 1; + *mhp++ = tup; + + if (is_immed(key)) { + while(n--) { + if (*ks == key) { + ks++; + vs++; + found = 1; + } else { + *mhp++ = *vs++; + *thp++ = *ks++; + } + } + } else { + while(n--) { + if (eq(*ks, key)) { + ks++; + vs++; + found = 1; + } else { + *mhp++ = *vs++; + *thp++ = *ks++; + } + } + } + + if (found) + BIF_RET(res); + + /* Not found, remove allocated memory + * and return previous map. + */ + HRelease(BIF_P, thp + need, thp); + BIF_RET(BIF_ARG_2); + } + BIF_ERROR(BIF_P, BADARG); +} + +/* map:update/3 + */ + +BIF_RETTYPE map_update_3(BIF_ALIST_3) { + if (is_map(BIF_ARG_3)) { + Sint n,i; + Sint found = 0; + Eterm* hp,*shp; + Eterm *ks,*vs, res, key; + map_t *mp = (map_t*)map_val(BIF_ARG_3); + + key = BIF_ARG_1; + n = map_get_size(mp); + + if (n == 0) { + BIF_ERROR(BIF_P, BADARG); + } + + ks = map_get_keys(mp); + vs = map_get_values(mp); + + /* only allocate for values, + * assume key-tuple will be intact + */ + + hp = HAlloc(BIF_P, 3 + n); + shp = hp; + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = n; + *hp++ = mp->keys; + + if (is_immed(key)) { + for( i = 0; i < n; i ++) { + if (ks[i] == key) { + *hp++ = BIF_ARG_2; + vs++; + found = 1; + } else { + *hp++ = *vs++; + } + } + } else { + for( i = 0; i < n; i ++) { + if (eq(ks[i], key)) { + *hp++ = BIF_ARG_2; + vs++; + found = 1; + } else { + *hp++ = *vs++; + } + } + } + + if (found) + BIF_RET(res); + + HRelease(BIF_P, shp + 3 + n, shp); + } + BIF_ERROR(BIF_P, BADARG); +} + + +/* map:values/1 + */ + +BIF_RETTYPE map_values_1(BIF_ALIST_1) { + if (is_map(BIF_ARG_1)) { + Eterm *hp, *vs, res = NIL; + map_t *mp; + Uint n; + + mp = (map_t*)map_val(BIF_ARG_1); + n = map_get_size(mp); + + if (n == 0) + BIF_RET(res); + + hp = HAlloc(BIF_P, (2 * n)); + vs = map_get_values(mp); + + while(n--) { + res = CONS(hp, vs[n], res); hp += 2; + } + + BIF_RET(res); + } + BIF_ERROR(BIF_P, BADARG); +} diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index ca8ce0f974..4f0d26e100 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -33,11 +33,12 @@ typedef struct map_s { * * ----------- * Eterm THING - * Eterm Keys -> {K1, K2, K3, ..., Kn} where n = arity + * Uint size + * Eterm Keys -> {K1, K2, K3, ..., Kn} where n = size * ---- * Eterm V1 * ... - * Eterm Vn, where n = arity + * Eterm Vn, where n = size * ----------- */ @@ -59,6 +60,7 @@ typedef struct map_s { #define map_get_size(x) (((map_t*)(x))->size) #define MAP_HEADER _make_header(1,_TAG_HEADER_MAP) +#define MAP_HEADER_SIZE (sizeof(map_t) / sizeof(Eterm)) #endif -- 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(-) 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(+) 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 60e7d2695dcf8ac2484db87c649ca69f1d0b763c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 May 2013 17:35:16 +0200 Subject: erts: Add testsuite for Maps --- erts/emulator/test/Makefile | 1 + erts/emulator/test/map_SUITE.erl | 532 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 533 insertions(+) create mode 100644 erts/emulator/test/map_SUITE.erl diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index f02ca3cb98..0b0568c31a 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -68,6 +68,7 @@ MODULES= \ hash_SUITE \ hibernate_SUITE \ list_bif_SUITE \ + map_SUITE \ match_spec_SUITE \ module_info_SUITE \ monitor_SUITE \ diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl new file mode 100644 index 0000000000..bbc5aa07b4 --- /dev/null +++ b/erts/emulator/test/map_SUITE.erl @@ -0,0 +1,532 @@ +%% %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_SUITE). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2 + ]). + +-export([ + t_build_and_match_literals/1, + t_update_literals/1,t_match_and_update_literals/1, + t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, + t_guard_receive/1, t_guard_fun/1, + t_list_comprehension/1, + t_map_sort_literals/1, + t_size/1, t_map_size/1, + + %% BIFs + t_bif_map_get/1, + t_bif_map_find/1, + t_bif_map_is_key/1, + t_bif_map_keys/1, + t_bif_map_new/1, + t_bif_map_put/1, + t_bif_map_remove/1, + t_bif_map_values/1 + ]). + +-include_lib("test_server/include/test_server.hrl"). + + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> [ + t_build_and_match_literals, + t_update_literals, t_match_and_update_literals, + t_guard_bifs, t_guard_sequence, t_guard_update, + t_guard_receive,t_guard_fun, t_list_comprehension, + t_map_sort_literals, + + %% Specific Map BIFs + t_bif_map_get,t_bif_map_find,t_bif_map_is_key, + t_bif_map_keys,t_bif_map_new,t_bif_map_put, + t_bif_map_remove,t_bif_map_values + ]. + +groups() -> []. + +init_per_suite(Config) -> Config. +end_per_suite(_Config) -> ok. + +init_per_group(_GroupName, Config) -> Config. +end_per_group(_GroupName, Config) -> Config. + +%% tests + +t_build_and_match_literals(Config) when is_list(Config) -> + #{} = id(#{}), + #{1=>a} = id(#{1=>a}), + #{1=>a,2=>b} = id(#{1=>a,2=>b}), + #{1=>a,2=>b,3=>"c"} = id(#{1=>a,2=>b,3=>"c"}), + #{1=>a,2=>b,3=>"c","4"=>"d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}), + #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>} = + id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>}), + #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"} = + id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"}), + #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g} = + id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}), + + #{<<"hi all">> => 1} = id(#{<<"hi",32,"all">> => 1}), + + #{a=>X,a=>X=3,b=>4} = id(#{a=>3,b=>4}), % weird but ok =) + + #{ a=>#{ b=>#{c => third, b=>second}}, b=>first} = + id(#{ b=>first, a=>#{ b=>#{c => third, b=> second}}}), + + M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}, + M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first} = + id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}), + + %% error case + %V = 32, + %{'EXIT',{{badmatch,_},_}} = (catch (#{<<"hi all">> => 1} = id(#{<<"hi",V,"all">> => 1}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3,x=>2} = id(#{x=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x=>2} = id(#{x=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id({a,b,c}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id(#{y=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id(#{x=>"three"}))), + ok. + +%% Tests size(Map). + +t_size(Config) when is_list(Config) -> +% 0 = size(#{}), +% 1 = size(#{a=>1}), +% 1 = size(#{a=>#{a=>1}}), +% 2 = size(#{a=>1, b=>2}), +% 3 = size(#{a=>1, b=>2, b=>"3"}), + ok. + +t_map_size(Config) when is_list(Config) -> + 0 = map_size(id(#{})), + 1 = map_size(id(#{a=>1})), + 1 = map_size(id(#{a=>"wat"})), + 2 = map_size(id(#{a=>1, b=>2})), + 3 = map_size(id(#{a=>1, b=>2, b=>"3","33"=><<"n">>})), + + true = map_is_size(#{a=>1}, 1), + true = map_is_size(#{a=>1, a=>2}, 2), + M = #{ "a" => 1, "b" => 2}, + true = map_is_size(M#{ "a" => 2}, 2), + false = map_is_size(M#{ "a" => 2}, 3), + + %% Error cases. + {'EXIT',{badarg,_}} = (catch map_size([])), + {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)), + {'EXIT',{badarg,_}} = (catch map_size(1)), + ok. + +map_is_size(M,N) when map_size(M) =:= N -> true; +map_is_size(_,_) -> false. + +% test map updates without matching +t_update_literals(Config) when is_list(Config) -> + Map = #{x=>1,y=>2,z=>3,q=>4}, + #{x=>"d",q=>"4"} = loop_update_literals_x_q(Map, [ + {"a","1"},{"b","2"},{"c","3"},{"d","4"} + ]), + ok. + +loop_update_literals_x_q(Map, []) -> Map; +loop_update_literals_x_q(Map, [{X,Q}|Vs]) -> + loop_update_literals_x_q(Map#{q=>Q,x=>X},Vs). + +% test map updates with matching +t_match_and_update_literals(Config) when is_list(Config) -> + Map = #{x=>0,y=>"untouched",z=>"also untouched",q=>1}, + #{x=>16,q=>21,y=>"untouched",z=>"also untouched"} = loop_match_and_update_literals_x_q(Map, [ + {1,2},{3,4},{5,6},{7,8} + ]), + M0 = id(#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}), + M1 = id(#{}), + M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + M0 = M2, + + #{ 4 => another_number, int => 3 } = M2#{ 4 => another_number }, + ok. + +loop_match_and_update_literals_x_q(Map, []) -> Map; +loop_match_and_update_literals_x_q(#{q=>Q0,x=>X0} = Map, [{X,Q}|Vs]) -> + loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). + + +t_guard_bifs(Config) when is_list(Config) -> + true = map_guard_head(#{a=>1}), + false = map_guard_head([]), + true = map_guard_body(#{a=>1}), + false = map_guard_body({}), + true = map_guard_pattern(#{a=>1, <<"hi">> => "hi" }), + false = map_guard_pattern("list"), + ok. + +map_guard_head(M) when is_map(M) -> true; +map_guard_head(_) -> false. + +map_guard_body(M) -> is_map(M). + +map_guard_pattern(#{}) -> true; +map_guard_pattern(_) -> false. + +t_guard_sequence(Config) when is_list(Config) -> + {1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}), + {2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}), + {3, "c"} = map_guard_sequence_1(#{seq=>3,val=>id("c")}), + {4, "d"} = map_guard_sequence_1(#{seq=>4,val=>id("d")}), + {5, "e"} = map_guard_sequence_1(#{seq=>5,val=>id("e")}), + + {1,M1} = map_guard_sequence_2(M1 = id(#{a=>3})), + {2,M2} = map_guard_sequence_2(M2 = id(#{a=>4, b=>4})), + {3,gg,M3} = map_guard_sequence_2(M3 = id(#{a=>gg, b=>4})), + {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(#{a=>sc, b=>3, c=>sc2})), + {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(#{a=>kk, b=>other, c=>sc2})), + + %% error case + {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>id("e")})), + {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})), + ok. + +map_guard_sequence_1(#{seq=>1=Seq, val=>Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq=>2=Seq, val=>Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq=>3=Seq, val=>Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq=>4=Seq, val=>Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq=>5=Seq, val=>Val}) -> {Seq,Val}. + +map_guard_sequence_2(#{ a=>3 }=M) -> {1, M}; +map_guard_sequence_2(#{ a=>4 }=M) -> {2, M}; +map_guard_sequence_2(#{ a=>X, a=>X, b=>4 }=M) -> {3,X,M}; +map_guard_sequence_2(#{ a=>X, a=>Y, b=>3 }=M) when X =:= Y -> {4,X,Y,M}; +map_guard_sequence_2(#{ a=>X, a=>Y }=M) when X =:= Y -> {5,X,Y,M}. + + +t_guard_update(Config) when is_list(Config) -> + error = map_guard_update(#{},#{}), + first = map_guard_update(#{}, #{x=>first}), + second = map_guard_update(#{y=>old}, #{x=>second,y=>old}), + ok. + +map_guard_update(M1, M2) when M1#{x=>first} =:= M2 -> first; +map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second; +map_guard_update(_, _) -> error. + +t_guard_receive(Config) when is_list(Config) -> + M0 = #{ id => 0 }, + Pid = spawn_link(fun() -> guard_receive_loop() end), + Big = 36893488147419103229, + B1 = <<"some text">>, + B2 = <<"was appended">>, + B3 = <>, + + #{id=>1, res=>Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}), + #{id=>2, res=>26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}), + #{id=>3, res=>832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}), + #{id=>4, res=>4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}), + #{id=>5, res=>Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}), + #{id=>6, res=>B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}), + #{id=>7, res=>4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}), + + + %% update old maps and check id update + #{id=>2, res=>B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}), + #{id=>5, res=>99} = call(Pid, M4#{op=>add,in=>{33, 66}}), + + %% cleanup + done = call(Pid, done), + ok. + +call(Pid, M) -> + Pid ! {self(), M}, receive {Pid, Res} -> Res end. + +guard_receive_loop() -> + receive + {Pid, #{ id=>Id, op=>"append", in=>{X,Y}}=M} when is_binary(X), is_binary(Y) -> + Pid ! {self(), M#{ id=>Id+1, res=><>}}, + guard_receive_loop(); + {Pid, #{ id=>Id, op=>add, in=>{X,Y}}} -> + Pid ! {self(), #{ id=>Id+1, res=>X+Y}}, + guard_receive_loop(); + {Pid, #{ id=>Id, op=>sub, in=>{X,Y}}=M} -> + Pid ! {self(), M#{ id=>Id+1, res=>X-Y}}, + guard_receive_loop(); + {Pid, #{ id=>Id, op=>idiv, in=>{X,Y}}=M} -> + Pid ! {self(), M#{ id=>Id+1, res=>X div Y}}, + guard_receive_loop(); + {Pid, #{ id=>Id, op=>imul, in=>{X,Y}}=M} -> + Pid ! {self(), M#{ id=>Id+1, res=>X * Y}}, + guard_receive_loop(); + {Pid, done} -> + Pid ! {self(), done}; + {Pid, Other} -> + Pid ! {error, Other}, + guard_receive_loop() + end. + + +t_list_comprehension(Config) when is_list(Config) -> + [#{k=>1},#{k=>2},#{k=>3}] = [#{k=>I} || I <- [1,2,3]], + ok. + +t_guard_fun(Config) when is_list(Config) -> + F1 = fun + (#{s=>v,v=>V}) -> {v,V}; + (#{s=>t,v=>{V,V}}) -> {t,V}; + (#{s=>l,v=>[V,V]}) -> {l,V} + end, + + F2 = fun + (#{s=>T,v=>{V,V}}) -> {T,V}; + (#{s=>T,v=>[V,V]}) -> {T,V}; + (#{s=>T,v=>V}) -> {T,V} + end, + V = <<"hi">>, + + {v,V} = F1(#{s=>v,v=>V}), + {t,V} = F1(#{s=>t,v=>{V,V}}), + {l,V} = F1(#{s=>l,v=>[V,V]}), + + {v,V} = F2(#{s=>v,v=>V}), + {t,V} = F2(#{s=>t,v=>{V,V}}), + {l,V} = F2(#{s=>l,v=>[V,V]}), + + %% error case + {'EXIT', {function_clause,[{?MODULE,_,[#{s=>none,v=>none}],_}|_]}} = (catch F1(#{s=>none,v=>none})), + ok. + + +t_map_sort_literals(Config) when is_list(Config) -> + % test relation + + %% size order + true = #{ a => 1, b => 2} < id(#{ a => 1, b => 1, c => 1}), + true = #{ b => 1, a => 1} < id(#{ c => 1, a => 1, b => 1}), + false = #{ c => 1, b => 1, a => 1} < id(#{ c => 1, a => 1}), + + %% key order + true = #{ a => 1 } < id(#{ b => 1}), + false = #{ b => 1 } < id(#{ a => 1}), + true = #{ a => 1, b => 1, c => 1 } < id(#{ b => 1, c => 1, d => 1}), + true = #{ b => 1, c => 1, d => 1 } > id(#{ a => 1, b => 1, c => 1}), + true = #{ c => 1, b => 1, a => 1 } < id(#{ b => 1, c => 1, d => 1}), + true = #{ "a" => 1 } < id(#{ <<"a">> => 1}), + false = #{ <<"a">> => 1 } < id(#{ "a" => 1}), + false = #{ 1 => 1 } < id(#{ 1.0 => 1}), + false = #{ 1.0 => 1 } < id(#{ 1 => 1}), + + %% value order + true = #{ a => 1 } < id(#{ a => 2}), + false = #{ a => 2 } < id(#{ a => 1}), + false = #{ a => 2, b => 1 } < id(#{ a => 1, b => 3}), + true = #{ a => 1, b => 1 } < id(#{ a => 1, b => 3}), + + true = #{ "a" => "hi", b => 134 } == id(#{ b => 134,"a" => "hi"}), + + %% lists:sort + + SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}], + [#{1=>ok},#{a=>ok},#{"a"=>ok},#{<<"a">>=>ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]), + [#{1=>3},#{a=>2},#{"a"=>1},#{<<"a">>=>4}] = lists:sort(SortVs), + [#{1=>3},#{a=>2},#{"a"=>1},#{<<"a">>=>4}] = lists:sort(lists:reverse(SortVs)), + + ok. + +%% BIFs +t_bif_map_get(Config) when is_list(Config) -> + + 1 = map:get(a, #{ a=> 1}), + 2 = map:get(b, #{ a=> 1, b => 2}), + "hi" = map:get("hello", #{ a=>1, "hello" => "hi"}), + "tuple hi" = map:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}), + + M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), + "v4" = map:get(<<"k2">>, M#{ <<"k2">> => "v4" }), + + %% error case + {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,[])), + {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,<<>>)), + {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get({1,1}, #{{1,1.0} => "tuple"})), + {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{})), + {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{ b=>1, c=>2})), + ok. + +t_bif_map_find(Config) when is_list(Config) -> + + {ok, 1} = map:find(a, #{ a=> 1}), + {ok, 2} = map:find(b, #{ a=> 1, b => 2}), + {ok, "int"} = map:find(1, #{ 1 => "int"}), + {ok, "int"} = map:find(1.0, #{ 1 => "int"}), + {ok, "float"} = map:find(1, #{ 1.0 => "float"}), + {ok, "float"} = map:find(1.0, #{ 1.0=> "float"}), + + {ok, "hi"} = map:find("hello", #{ a=>1, "hello" => "hi"}), + {ok, "tuple hi"} = map:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key + + M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), + {ok, "v4"} = map:find(<<"k2">>, M#{ <<"k2">> => "v4" }), + + %% error case + error = map:find(a,#{}), + error = map:find(a,#{b=>1, c=>2}), + + {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,[])), + {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,<<>>)), + ok. + + +t_bif_map_is_key(Config) when is_list(Config) -> + M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, + + true = map:is_key("hi", M1), + true = map:is_key(int, M1), + true = map:is_key(<<"key">>, M1), + true = map:is_key(4, M1), + + false = map:is_key(5, M1), + false = map:is_key(<<"key2">>, M1), + false = map:is_key("h", M1), + false = map:is_key("hello", M1), + false = map:is_key(atom, M1), + + false = map:is_key("hi", map:remove("hi", M1)), + true = map:is_key("hi", M1), + true = map:is_key(1, map:put(1, "number", M1)), + false = map:is_key(1.0, map:put(1, "number", M1)), + ok. + +t_bif_map_keys(Config) when is_list(Config) -> + [] = map:keys(#{}), + + [1,2,3,4,5] = map:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), + [1,2,3,4,5] = map:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), + + % values in key order: [4,int,"hi",<<"key">>] + M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, + [4,int,"hi",<<"key">>] = map:keys(M1), + + %% error case + {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(1 bsl 65 + 3)), + {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(154)), + {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(atom)), + {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys([])), + {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(<<>>)), + ok. + +t_bif_map_new(Config) when is_list(Config) -> + #{} = map:new(), + 0 = erlang:map_size(map:new()), + ok. + +t_bif_map_put(Config) when is_list(Config) -> + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + + M1 = #{ "hi" => "hello"} = map:put("hi", "hello", #{}), + + ["hi"] = map:keys(M1), + ["hello"] = map:values(M1), + + M2 = #{ int => 3 } = map:put(int, 3, M1), + + [int,"hi"] = map:keys(M2), + [3,"hello"] = map:values(M2), + + M3 = #{ <<"key">> => <<"value">> } = map:put(<<"key">>, <<"value">>, M2), + + [int,"hi",<<"key">>] = map:keys(M3), + [3,"hello",<<"value">>] = map:values(M3), + + M4 = #{ 18446744073709551629 => wat } = map:put(18446744073709551629, wat, M3), + + [18446744073709551629,int,"hi",<<"key">>] = map:keys(M4), + [wat,3,"hello",<<"value">>] = map:values(M4), + + M0 = #{ 4 => number } = M5 = map:put(4, number, M4), + + [4,18446744073709551629,int,"hi",<<"key">>] = map:keys(M5), + [number,wat,3,"hello",<<"value">>] = map:values(M5), + + %% error case + {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,1 bsl 65 + 3)), + {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,154)), + {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,atom)), + {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,[])), + {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,<<>>)), + ok. + +t_bif_map_remove(Config) when is_list(Config) -> + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + + M1 = map:remove("hi", M0), + [4,18446744073709551629,int,<<"key">>] = map:keys(M1), + [number,wat,3,<<"value">>] = map:values(M1), + + M2 = map:remove(int, M1), + [4,18446744073709551629,<<"key">>] = map:keys(M2), + [number,wat,<<"value">>] = map:values(M2), + + M3 = map:remove(<<"key">>, M2), + [4,18446744073709551629] = map:keys(M3), + [number,wat] = map:values(M3), + + M4 = map:remove(18446744073709551629, M3), + [4] = map:keys(M4), + [number] = map:values(M4), + + M5 = map:remove(4, M4), + [] = map:keys(M5), + [] = map:values(M5), + + M0 = map:remove(5,M0), + M0 = map:remove("hi there",M0), + + #{ "hi" => "hello", int => 3, 4 => number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)), + + %% error case + {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,1 bsl 65 + 3)), + {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,154)), + {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,atom)), + {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,[])), + {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,<<>>)), + ok. + + +t_bif_map_values(Config) when is_list(Config) -> + + [] = map:values(#{}), + + [a,b,c,d,e] = map:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), + [a,b,c,d,e] = map:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), + + % values in key order: [4,int,"hi",<<"key">>] + M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, + M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> }, + [number,3,"hello2",<<"value2">>] = map:values(M2), + [number,3,"hello",<<"value">>] = map:values(M1), + + %% error case + {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(1 bsl 65 + 3)), + {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(atom)), + {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values([])), + {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(<<>>)), + + ok. + + +%% Use this function to avoid compile-time evaluation of an expression. +id(I) -> I. -- 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(+) 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(-) 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(+) 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(-) 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(-) 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(-) 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 aee6128a1bfb2356440b27a9c7db44650336e4c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 2 Oct 2013 00:09:37 +0200 Subject: erts: Conform map_SUITE to extended syntax --- erts/emulator/test/map_SUITE.erl | 122 +++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index bbc5aa07b4..6d82a2cb59 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -70,36 +70,36 @@ end_per_group(_GroupName, Config) -> Config. t_build_and_match_literals(Config) when is_list(Config) -> #{} = id(#{}), - #{1=>a} = id(#{1=>a}), - #{1=>a,2=>b} = id(#{1=>a,2=>b}), - #{1=>a,2=>b,3=>"c"} = id(#{1=>a,2=>b,3=>"c"}), - #{1=>a,2=>b,3=>"c","4"=>"d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}), - #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>} = + #{1:=a} = id(#{1=>a}), + #{1:=a,2:=b} = id(#{1=>a,2=>b}), + #{1:=a,2:=b,3:="c"} = id(#{1=>a,2=>b,3=>"c"}), + #{1:=a,2:=b,3:="c","4":="d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}), + #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>} = id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>}), - #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"} = + #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f"} = id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"}), - #{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g} = + #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f",8:=g} = id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}), - #{<<"hi all">> => 1} = id(#{<<"hi",32,"all">> => 1}), + #{<<"hi all">> := 1} = id(#{<<"hi",32,"all">> => 1}), - #{a=>X,a=>X=3,b=>4} = id(#{a=>3,b=>4}), % weird but ok =) + #{a:=X,a:=X=3,b:=4} = id(#{a=>3,b=>4}), % weird but ok =) - #{ a=>#{ b=>#{c => third, b=>second}}, b=>first} = + #{ a:=#{ b:=#{c := third, b:=second}}, b:=first} = id(#{ b=>first, a=>#{ b=>#{c => third, b=> second}}}), M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}, - M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first} = + M = #{ map_1:=#{ map_2:=#{value_3 := third}, value_2:= second}, value_1:=first} = id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}), %% error case %V = 32, %{'EXIT',{{badmatch,_},_}} = (catch (#{<<"hi all">> => 1} = id(#{<<"hi",V,"all">> => 1}))), - {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3,x=>2} = id(#{x=>3}))), - {'EXIT',{{badmatch,_},_}} = (catch (#{x=>2} = id(#{x=>3}))), - {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id({a,b,c}))), - {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id(#{y=>3}))), - {'EXIT',{{badmatch,_},_}} = (catch (#{x=>3} = id(#{x=>"three"}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = id(#{x=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))), ok. %% Tests size(Map). @@ -137,7 +137,7 @@ map_is_size(_,_) -> false. % test map updates without matching t_update_literals(Config) when is_list(Config) -> Map = #{x=>1,y=>2,z=>3,q=>4}, - #{x=>"d",q=>"4"} = loop_update_literals_x_q(Map, [ + #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [ {"a","1"},{"b","2"},{"c","3"},{"d","4"} ]), ok. @@ -149,7 +149,7 @@ loop_update_literals_x_q(Map, [{X,Q}|Vs]) -> % test map updates with matching t_match_and_update_literals(Config) when is_list(Config) -> Map = #{x=>0,y=>"untouched",z=>"also untouched",q=>1}, - #{x=>16,q=>21,y=>"untouched",z=>"also untouched"} = loop_match_and_update_literals_x_q(Map, [ + #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [ {1,2},{3,4},{5,6},{7,8} ]), M0 = id(#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, @@ -159,11 +159,11 @@ t_match_and_update_literals(Config) when is_list(Config) -> 4 => number, 18446744073709551629 => wat}, M0 = M2, - #{ 4 => another_number, int => 3 } = M2#{ 4 => another_number }, + #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number }, ok. loop_match_and_update_literals_x_q(Map, []) -> Map; -loop_match_and_update_literals_x_q(#{q=>Q0,x=>X0} = Map, [{X,Q}|Vs]) -> +loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) -> loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). @@ -202,17 +202,17 @@ t_guard_sequence(Config) when is_list(Config) -> {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})), ok. -map_guard_sequence_1(#{seq=>1=Seq, val=>Val}) -> {Seq,Val}; -map_guard_sequence_1(#{seq=>2=Seq, val=>Val}) -> {Seq,Val}; -map_guard_sequence_1(#{seq=>3=Seq, val=>Val}) -> {Seq,Val}; -map_guard_sequence_1(#{seq=>4=Seq, val=>Val}) -> {Seq,Val}; -map_guard_sequence_1(#{seq=>5=Seq, val=>Val}) -> {Seq,Val}. +map_guard_sequence_1(#{seq:=1=Seq, val:=Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq:=2=Seq, val:=Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq:=3=Seq, val:=Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq:=4=Seq, val:=Val}) -> {Seq,Val}; +map_guard_sequence_1(#{seq:=5=Seq, val:=Val}) -> {Seq,Val}. -map_guard_sequence_2(#{ a=>3 }=M) -> {1, M}; -map_guard_sequence_2(#{ a=>4 }=M) -> {2, M}; -map_guard_sequence_2(#{ a=>X, a=>X, b=>4 }=M) -> {3,X,M}; -map_guard_sequence_2(#{ a=>X, a=>Y, b=>3 }=M) when X =:= Y -> {4,X,Y,M}; -map_guard_sequence_2(#{ a=>X, a=>Y }=M) when X =:= Y -> {5,X,Y,M}. +map_guard_sequence_2(#{ a:=3 }=M) -> {1, M}; +map_guard_sequence_2(#{ a:=4 }=M) -> {2, M}; +map_guard_sequence_2(#{ a:=X, a:=X, b:=4 }=M) -> {3,X,M}; +map_guard_sequence_2(#{ a:=X, a:=Y, b:=3 }=M) when X =:= Y -> {4,X,Y,M}; +map_guard_sequence_2(#{ a:=X, a:=Y }=M) when X =:= Y -> {5,X,Y,M}. t_guard_update(Config) when is_list(Config) -> @@ -233,18 +233,18 @@ t_guard_receive(Config) when is_list(Config) -> B2 = <<"was appended">>, B3 = <>, - #{id=>1, res=>Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}), - #{id=>2, res=>26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}), - #{id=>3, res=>832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}), - #{id=>4, res=>4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}), - #{id=>5, res=>Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}), - #{id=>6, res=>B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}), - #{id=>7, res=>4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}), + #{id:=1, res:=Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}), + #{id:=2, res:=26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}), + #{id:=3, res:=832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}), + #{id:=4, res:=4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}), + #{id:=5, res:=Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}), + #{id:=6, res:=B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}), + #{id:=7, res:=4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}), %% update old maps and check id update - #{id=>2, res=>B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}), - #{id=>5, res=>99} = call(Pid, M4#{op=>add,in=>{33, 66}}), + #{id:=2, res:=B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}), + #{id:=5, res:=99} = call(Pid, M4#{op=>add,in=>{33, 66}}), %% cleanup done = call(Pid, done), @@ -255,19 +255,19 @@ call(Pid, M) -> guard_receive_loop() -> receive - {Pid, #{ id=>Id, op=>"append", in=>{X,Y}}=M} when is_binary(X), is_binary(Y) -> + {Pid, #{ id:=Id, op:="append", in:={X,Y}}=M} when is_binary(X), is_binary(Y) -> Pid ! {self(), M#{ id=>Id+1, res=><>}}, guard_receive_loop(); - {Pid, #{ id=>Id, op=>add, in=>{X,Y}}} -> + {Pid, #{ id:=Id, op:=add, in:={X,Y}}} -> Pid ! {self(), #{ id=>Id+1, res=>X+Y}}, guard_receive_loop(); - {Pid, #{ id=>Id, op=>sub, in=>{X,Y}}=M} -> + {Pid, #{ id:=Id, op:=sub, in:={X,Y}}=M} -> Pid ! {self(), M#{ id=>Id+1, res=>X-Y}}, guard_receive_loop(); - {Pid, #{ id=>Id, op=>idiv, in=>{X,Y}}=M} -> + {Pid, #{ id:=Id, op:=idiv, in:={X,Y}}=M} -> Pid ! {self(), M#{ id=>Id+1, res=>X div Y}}, guard_receive_loop(); - {Pid, #{ id=>Id, op=>imul, in=>{X,Y}}=M} -> + {Pid, #{ id:=Id, op:=imul, in:={X,Y}}=M} -> Pid ! {self(), M#{ id=>Id+1, res=>X * Y}}, guard_receive_loop(); {Pid, done} -> @@ -279,20 +279,20 @@ guard_receive_loop() -> t_list_comprehension(Config) when is_list(Config) -> - [#{k=>1},#{k=>2},#{k=>3}] = [#{k=>I} || I <- [1,2,3]], + [#{k:=1},#{k:=2},#{k:=3}] = [#{k:=I} || I <- [1,2,3]], ok. t_guard_fun(Config) when is_list(Config) -> F1 = fun - (#{s=>v,v=>V}) -> {v,V}; - (#{s=>t,v=>{V,V}}) -> {t,V}; - (#{s=>l,v=>[V,V]}) -> {l,V} + (#{s:=v,v:=V}) -> {v,V}; + (#{s:=t,v:={V,V}}) -> {t,V}; + (#{s:=l,v:=[V,V]}) -> {l,V} end, F2 = fun - (#{s=>T,v=>{V,V}}) -> {T,V}; - (#{s=>T,v=>[V,V]}) -> {T,V}; - (#{s=>T,v=>V}) -> {T,V} + (#{s:=T,v:={V,V}}) -> {T,V}; + (#{s:=T,v:=[V,V]}) -> {T,V}; + (#{s:=T,v:=V}) -> {T,V} end, V = <<"hi">>, @@ -305,7 +305,7 @@ t_guard_fun(Config) when is_list(Config) -> {l,V} = F2(#{s=>l,v=>[V,V]}), %% error case - {'EXIT', {function_clause,[{?MODULE,_,[#{s=>none,v=>none}],_}|_]}} = (catch F1(#{s=>none,v=>none})), + {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} = (catch F1(#{s=>none,v=>none})), ok. @@ -339,9 +339,9 @@ t_map_sort_literals(Config) when is_list(Config) -> %% lists:sort SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}], - [#{1=>ok},#{a=>ok},#{"a"=>ok},#{<<"a">>=>ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]), - [#{1=>3},#{a=>2},#{"a"=>1},#{<<"a">>=>4}] = lists:sort(SortVs), - [#{1=>3},#{a=>2},#{"a"=>1},#{<<"a">>=>4}] = lists:sort(lists:reverse(SortVs)), + [#{1:=ok},#{a:=ok},#{"a":=ok},#{<<"a">>:=ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]), + [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(SortVs), + [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(lists:reverse(SortVs)), ok. @@ -435,27 +435,27 @@ t_bif_map_put(Config) when is_list(Config) -> M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, - M1 = #{ "hi" => "hello"} = map:put("hi", "hello", #{}), + M1 = #{ "hi" := "hello"} = map:put("hi", "hello", #{}), ["hi"] = map:keys(M1), ["hello"] = map:values(M1), - M2 = #{ int => 3 } = map:put(int, 3, M1), + M2 = #{ int := 3 } = map:put(int, 3, M1), [int,"hi"] = map:keys(M2), [3,"hello"] = map:values(M2), - M3 = #{ <<"key">> => <<"value">> } = map:put(<<"key">>, <<"value">>, M2), + M3 = #{ <<"key">> := <<"value">> } = map:put(<<"key">>, <<"value">>, M2), [int,"hi",<<"key">>] = map:keys(M3), [3,"hello",<<"value">>] = map:values(M3), - M4 = #{ 18446744073709551629 => wat } = map:put(18446744073709551629, wat, M3), + M4 = #{ 18446744073709551629 := wat } = map:put(18446744073709551629, wat, M3), [18446744073709551629,int,"hi",<<"key">>] = map:keys(M4), [wat,3,"hello",<<"value">>] = map:values(M4), - M0 = #{ 4 => number } = M5 = map:put(4, number, M4), + M0 = #{ 4 := number } = M5 = map:put(4, number, M4), [4,18446744073709551629,int,"hi",<<"key">>] = map:keys(M5), [number,wat,3,"hello",<<"value">>] = map:values(M5), @@ -495,7 +495,7 @@ t_bif_map_remove(Config) when is_list(Config) -> M0 = map:remove(5,M0), M0 = map:remove("hi there",M0), - #{ "hi" => "hello", int => 3, 4 => number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)), + #{ "hi" := "hello", int := 3, 4 := number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)), %% error case {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,1 bsl 65 + 3)), -- 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(-) 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(-) 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 := --- erts/emulator/beam/beam_emu.c | 301 +++++++++++++++++++++++++----------- erts/emulator/beam/ops.tab | 11 +- erts/emulator/test/map_SUITE.erl | 38 +++++ 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 +++++++++++-- 13 files changed, 392 insertions(+), 133 deletions(-) diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 1cad31be2c..b666b7c3f7 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -959,8 +959,10 @@ static BeamInstr* apply_fun(Process* p, Eterm fun, static Eterm new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free) NOINLINE; static Eterm new_map(Process* p, Eterm* reg, BeamInstr* I) NOINLINE; -static Eterm update_map(Process* p, Eterm* reg, - Eterm map, BeamInstr* I) NOINLINE; +static Eterm update_map_assoc(Process* p, Eterm* reg, + Eterm map, BeamInstr* I) NOINLINE; +static Eterm update_map_exact(Process* p, Eterm* reg, + Eterm map, BeamInstr* I) NOINLINE; static int has_not_map_field(Eterm map, Eterm key); static Eterm get_map_element(Eterm map, Eterm key); @@ -2353,21 +2355,39 @@ void process_main(void) Next(4+Arg(3)); } - OpCase(update_map_jddII): { + OpCase(update_map_assoc_jddII): { Eterm res; Eterm map; GetArg1(1, map); x(0) = r(0); SWAPOUT; - res = update_map(c_p, reg, map, I); + res = update_map_assoc(c_p, reg, map, I); SWAPIN; - if (res) { + if (is_value(res)) { r(0) = x(0); StoreResult(res, Arg(2)); Next(5+Arg(4)); } else { - goto lb_Cl_error; + goto badarg; + } + } + + OpCase(update_map_exact_jddII): { + Eterm res; + Eterm map; + + GetArg1(1, map); + x(0) = r(0); + SWAPOUT; + res = update_map_exact(c_p, reg, map, I); + SWAPIN; + if (is_value(res)) { + r(0) = x(0); + StoreResult(res, Arg(2)); + Next(5+Arg(4)); + } else { + goto badarg; } } @@ -6387,18 +6407,10 @@ new_map(Process* p, Eterm* reg, BeamInstr* I) return make_map(mp); } - -/* This entire instruction will be split into two. - * 1) update_map_exact (literals) <- this can be much more optimized - * 2) update_map_assoc (literals) - * Also update_map is pretty bad code as it stands now. - */ - static Eterm -update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) +update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) { Uint n; - Uint i; Uint num_old; Uint num_updates; Uint need; @@ -6413,7 +6425,7 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) Eterm* kp; if (is_not_map(map)) { - return 0; + return THE_NON_VALUE; } old_mp = (map_t *) map_val(map); @@ -6428,11 +6440,12 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) } /* - * Allocate heap space for the worst case (i.e. all keys are new). + * Allocate heap space for the worst case (i.e. all keys in the + * update list are new). */ num_updates = Arg(4) / 2; - need = 2*(num_old+num_updates) + 4; + need = 2*(num_old+num_updates) + 1 + sizeof(map_t) / sizeof(Eterm); if (HeapWordsLeft(p) < need) { Uint live = Arg(3); reg[live] = map; @@ -6442,67 +6455,37 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) } /* - * Update map, optimistically assuming that there are no - * new keys, allowing us to keep the old key tuple. + * Build the skeleton for the map, ready to be filled in. + * + * +-----------------------------------+ + * | (Space for aritvyal for keys) | <-----------+ + * +-----------------------------------+ | + * | (Space for key 1) | | <-- kp + * +-----------------------------------+ | + * . | + * . | + * . | + * +-----------------------------------+ | + * | (Space for last key) | | + * +-----------------------------------+ | + * | MAP_HEADER | | + * +-----------------------------------+ | + * | (Space for number of keys/values) | | + * +-----------------------------------+ | + * | Boxed tuple pointer >----------------+ + * +-----------------------------------+ + * | (Space for value 1) | <-- hp + * +-----------------------------------+ */ - hp = p->htop; - E = p->stop; - - old_vals = map_get_values(old_mp); - old_keys = map_get_keys(old_mp); + E = p->stop; + kp = p->htop + 1; /* Point to first key */ + hp = kp + num_old + num_updates; res = make_map(hp); - mp = (map_t *)hp; hp += 3; + mp = (map_t *)hp; + hp += sizeof(map_t) / sizeof(Eterm); mp->thing_word = MAP_HEADER; - mp->size = num_old; - mp->keys = old_mp->keys; - - ASSERT(num_updates > 0); - - /* Get array of key/value pairs to be updated */ - new_p = &Arg(5); - GET_TERM(*new_p, new_key); - - n = num_updates; - - for (i = 0; i < num_old; i++) { - if (new_key == THE_NON_VALUE || !eq(*old_keys, new_key)) { - /* not same keys */ - *hp++ = *old_vals; - } else { - GET_TERM(new_p[1], *hp); - hp++; - n--; - if (n == 0) { - new_key = THE_NON_VALUE; - } else { - new_p += 2; - GET_TERM(*new_p, new_key); - } - } - old_vals++, old_keys++; - } - - /* - * If we have exhausted the update list we are done. - */ - - if (n == 0) { - p->htop = hp; - return res; - } - - /* - * There were some new keys. We'll have to start over and rebuild - * the key tuple too. - */ - - kp = p->htop; - *kp++ = make_arityval(0); - - res = make_map(hp); - mp = (map_t *)hp; hp += 3; mp->keys = make_tuple(kp-1); old_vals = map_get_values(old_mp); @@ -6512,20 +6495,28 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) GET_TERM(*new_p, new_key); n = num_updates; + /* + * Fill in keys and values, until we run out of either updates + * or old values and keys. + */ + for (;;) { Eterm key; Sint c; ASSERT(kp < (Eterm *)mp); key = *old_keys; - if ((c = cmp(key, new_key)) < 0) { + if ((c = CMP(key, new_key)) < 0) { + /* Copy old key and value */ *kp++ = key; *hp++ = *old_vals; old_keys++, old_vals++, num_old--; } else { /* Replace or insert new */ - *kp++ = new_key; GET_TERM(new_p[1], *hp++); - if (c == 0) { /* If replacement */ + if (c > 0) { /* If new new key */ + *kp++ = new_key; + } else { /* If replacement */ + *kp++ = key; old_keys++, old_vals++, num_old--; } n--; @@ -6541,32 +6532,162 @@ update_map(Process* p, Eterm* reg, Eterm map, BeamInstr* I) } } - while (n-- > 0) { - GET_TERM(new_p[0], *kp++); - GET_TERM(new_p[1], *hp++); - new_p += 2; - } - /* - * All updates done. Now copy the remaining part of the frame's - * keys and values. + * At this point, we have run out of either old keys and values, + * or the update list. In other words, at least of one n and + * num_old must be zero. */ - while (num_old-- > 0) { - ASSERT(kp < (Eterm *)mp); - *kp++ = *old_keys++; - *hp++ = *old_vals++; + if (n > 0) { + /* + * All old keys and values have been copied, but there + * are still new keys and values in the update list that + * must be copied. + */ + ASSERT(num_old == 0); + while (n-- > 0) { + GET_TERM(new_p[0], *kp++); + GET_TERM(new_p[1], *hp++); + new_p += 2; + } + } else { + /* + * All updates are now done. We may still have old + * keys and values that we must copy. + */ + ASSERT(n == 0); + while (num_old-- > 0) { + ASSERT(kp < (Eterm *)mp); + *kp++ = *old_keys++; + *hp++ = *old_vals++; + } } + + /* + * Calculate how many values that are unused at the end of the + * key tuple and fill it out with a bignum header. + */ if ((n = (Eterm *)mp - kp) > 0) { *kp = make_pos_bignum_header(n-1); } + + /* + * Fill in the size of the map in both the key tuple and in the map. + */ + n = kp - p->htop - 1; /* Actual number of keys/values */ *p->htop = make_arityval(n); - mp->thing_word = MAP_HEADER; mp->size = n; p->htop = hp; return res; } + +/* + * Update values for keys that already exist in the map. + */ + +static Eterm +update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) +{ + Uint n; + Uint i; + Uint num_old; + Uint need; + map_t *old_mp, *mp; + Eterm res; + Eterm* hp; + Eterm* E; + Eterm* old_keys; + Eterm* old_vals; + Eterm* new_p; + Eterm new_key; + + if (is_not_map(map)) { + return THE_NON_VALUE; + } + + old_mp = (map_t *) map_val(map); + num_old = map_get_size(old_mp); + + /* + * If the old map is empty, create a new map. + */ + + if (num_old == 0) { + return new_map(p, reg, I+1); + } + + /* + * Allocate the exact heap space needed. + */ + + need = num_old + sizeof(map_t) / sizeof(Eterm); + if (HeapWordsLeft(p) < need) { + Uint live = Arg(3); + reg[live] = map; + erts_garbage_collect(p, need, reg, live+1); + map = reg[live]; + old_mp = (map_t *)map_val(map); + } + + /* + * Update map, keeping the old key tuple. + */ + + hp = p->htop; + E = p->stop; + + old_vals = map_get_values(old_mp); + old_keys = map_get_keys(old_mp); + + res = make_map(hp); + mp = (map_t *)hp; + hp += sizeof(map_t) / sizeof(Eterm); + mp->thing_word = MAP_HEADER; + mp->size = num_old; + mp->keys = old_mp->keys; + + /* Get array of key/value pairs to be updated */ + new_p = &Arg(5); + GET_TERM(*new_p, new_key); + + /* Update all values */ + n = Arg(4) / 2; /* Number of values to be updated */ + ASSERT(n > 0); + for (i = 0; i < num_old; i++) { + if (!EQ(*old_keys, new_key)) { + /* Not same keys */ + *hp++ = *old_vals; + } else { + GET_TERM(new_p[1], *hp); + hp++; + n--; + if (n == 0) { + /* + * All updates done. Copy remaining values + * and return the result. + */ + for (i++, old_vals++; i < num_old; i++) { + *hp++ = *old_vals++; + } + ASSERT(hp == p->htop + need); + p->htop = hp; + return res; + } else { + new_p += 2; + GET_TERM(*new_p, new_key); + } + } + old_vals++, old_keys++; + } + + /* + * Updates left. That means that at least one the keys in the + * update list did not previously exist. + */ + ASSERT(hp == p->htop + need); + return THE_NON_VALUE; +} #undef GET_TERM int catchlevel(Process *p) diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index b89bdb2e3a..f35997efee 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1474,11 +1474,16 @@ apply_last I P # has_map_field Fail Src Key => jump Fail # get_map_element Fail Src Key Dst => jump Fail -put_map F n Dst Live Size Rest=* => new_map F Dst Live Size Rest -put_map F Src Dst Live Size Rest=* => update_map F Src Dst Live Size Rest +put_map_assoc F n Dst Live Size Rest=* => new_map F Dst Live Size Rest +put_map_exact F n Dst Live Size Rest=* => new_map F Dst Live Size Rest +put_map_assoc F Src Dst Live Size Rest=* => \ + update_map_assoc F Src Dst Live Size Rest +put_map_exact F Src Dst Live Size Rest=* => \ + update_map_exact F Src Dst Live Size Rest new_map j d I I -update_map j d d I I +update_map_assoc j d d I I +update_map_exact j d d I I is_map Fail cq => jump Fail diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 6d82a2cb59..81d39fc97a 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -23,6 +23,7 @@ -export([ t_build_and_match_literals/1, t_update_literals/1,t_match_and_update_literals/1, + update_assoc/1,update_exact/1, t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, t_guard_receive/1, t_guard_fun/1, t_list_comprehension/1, @@ -48,6 +49,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [ t_build_and_match_literals, t_update_literals, t_match_and_update_literals, + update_assoc,update_exact, t_guard_bifs, t_guard_sequence, t_guard_update, t_guard_receive,t_guard_fun, t_list_comprehension, t_map_sort_literals, @@ -166,6 +168,42 @@ loop_match_and_update_literals_x_q(Map, []) -> Map; loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) -> loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). +update_assoc(Config) when is_list(Config) -> + M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), + + M1 = M0#{1=>42,2=>100,4=>[a,b,c]}, + #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1, + M1 = M0#{1.0=>wrong,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]}, + + M2 = M0#{3.0=>new}, + #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2, + M2 = M0#{3.0:=wrong,3.0=>new}, + + %% Errors cases. + BadMap = id(badmap), + {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}), + + ok. + +update_exact(Config) when is_list(Config) -> + M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), + + M1 = M0#{1:=42,2:=100,4:=[a,b,c]}, + #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1, + M1 = M0#{1:=wrong,1=>42,2=>wrong,2:=100,4:=[a,b,c]}, + + M2 = M0#{3.0:=new}, + #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2, + M2 = M0#{3.0=>wrong,3.0:=new}, + M2 = M0#{3=>wrong,3.0:=new}, + + %% Errors cases. + {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + + ok. t_guard_bifs(Config) when is_list(Config) -> true = map_guard_head(#{a=>1}), 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 813f61de8e7872481a0369de3297596c1b11881d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 9 Oct 2013 15:46:03 +0200 Subject: erts: Fixup map instructions for halfword --- erts/emulator/beam/beam_emu.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index b666b7c3f7..fe2e196785 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6353,7 +6353,7 @@ static Eterm get_map_element(Eterm map, Eterm key) #define GET_TERM(term, dest) \ do { \ - Eterm src = term; \ + Eterm src = (Eterm)(term); \ switch (src & _TAG_IMMED1_MASK) { \ case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: \ dest = x(0); \ @@ -6380,7 +6380,7 @@ new_map(Process* p, Eterm* reg, BeamInstr* I) Eterm keys; Eterm *mhp,*thp; Eterm *E; - Eterm *tp; + BeamInstr *ptr; map_t *mp; if (HeapWordsLeft(p) < need) { @@ -6390,18 +6390,18 @@ new_map(Process* p, Eterm* reg, BeamInstr* I) thp = p->htop; mhp = thp + 1 + n/2; E = p->stop; - tp = &Arg(4); + ptr = &Arg(4); keys = make_tuple(thp); *thp++ = make_arityval(n/2); - mp = (map_t *)mhp; mhp += 3; + mp = (map_t *)mhp; mhp += MAP_HEADER_SIZE; mp->thing_word = MAP_HEADER; mp->size = n/2; mp->keys = keys; for (i = 0; i < n/2; i++) { - GET_TERM(*tp++, *thp++); - GET_TERM(*tp++, *mhp++); + GET_TERM(*ptr++, *thp++); + GET_TERM(*ptr++, *mhp++); } p->htop = mhp; return make_map(mp); @@ -6420,7 +6420,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) Eterm* E; Eterm* old_keys; Eterm* old_vals; - Eterm* new_p; + BeamInstr* new_p; Eterm new_key; Eterm* kp; @@ -6445,7 +6445,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) */ num_updates = Arg(4) / 2; - need = 2*(num_old+num_updates) + 1 + sizeof(map_t) / sizeof(Eterm); + need = 2*(num_old+num_updates) + 1 + MAP_HEADER_SIZE; if (HeapWordsLeft(p) < need) { Uint live = Arg(3); reg[live] = map; @@ -6484,7 +6484,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) res = make_map(hp); mp = (map_t *)hp; - hp += sizeof(map_t) / sizeof(Eterm); + hp += MAP_HEADER_SIZE; mp->thing_word = MAP_HEADER; mp->keys = make_tuple(kp-1); @@ -6599,7 +6599,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) Eterm* E; Eterm* old_keys; Eterm* old_vals; - Eterm* new_p; + BeamInstr* new_p; Eterm new_key; if (is_not_map(map)) { @@ -6621,7 +6621,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) * Allocate the exact heap space needed. */ - need = num_old + sizeof(map_t) / sizeof(Eterm); + need = num_old + MAP_HEADER_SIZE; if (HeapWordsLeft(p) < need) { Uint live = Arg(3); reg[live] = map; @@ -6642,7 +6642,7 @@ update_map_exact(Process* p, Eterm* reg, Eterm map, BeamInstr* I) res = make_map(hp); mp = (map_t *)hp; - hp += sizeof(map_t) / sizeof(Eterm); + hp += MAP_HEADER_SIZE; mp->thing_word = MAP_HEADER; mp->size = num_old; mp->keys = old_mp->keys; -- 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(-) 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 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(+) 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(-) 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(-) 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(-) 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(-) 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(-) 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 eb218eb375d353bcd78ae895805d1d45f927b3a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 2 Oct 2013 16:19:22 +0200 Subject: erts: Teach term_to_binary/1 and binary_to_term/1 Map encoding --- erts/emulator/beam/external.c | 197 ++++++++++++++++++++++++++++++++++++++---- erts/emulator/beam/external.h | 1 + 2 files changed, 183 insertions(+), 15 deletions(-) diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 5e7a5cab6e..a31b2731be 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -42,6 +42,7 @@ #include "erl_binary.h" #include "erl_bits.h" #include "erl_zlib.h" +#include "erl_map.h" #ifdef HIPE #include "hipe_mode_switch.h" @@ -2555,6 +2556,38 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, } break; + case MAP_DEF: + { + map_t *mp = (map_t*)map_val(obj); + Uint size = map_get_size(mp); + Eterm *mptr; + + *ep++ = MAP_EXT; + put_int32(size, ep); ep += 4; + + /* Push values first */ + if (size > 0) { + mptr = map_get_values(mp); + for (i = size-1; i >= 1; i--) { + WSTACK_PUSH(s, ENC_TERM); + WSTACK_PUSH(s, (UWord) mptr[i]); + } + + WSTACK_PUSH(s, ENC_TERM); + WSTACK_PUSH(s, (UWord) mptr[0]); + + mptr = map_get_keys(mp); + for (i = size-1; i >= 1; i--) { + WSTACK_PUSH(s, ENC_TERM); + WSTACK_PUSH(s, (UWord) mptr[i]); + } + + obj = mptr[0]; + goto L_jump_start; + } + } + break; + case FLOAT_DEF: GET_DOUBLE(obj, f); if (dflags & DFLAG_NEW_FLOATS) { @@ -2845,6 +2878,7 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, int n; ErtsAtomEncoding char_enc; register Eterm* hp; /* Please don't take the address of hp */ + Eterm *maps_head = NULL; /* for validation of maps */ Eterm* next; SWord reds; @@ -3469,6 +3503,65 @@ dec_term_atom_common: break; } break; + case MAP_EXT: + { + map_t *mp; + Uint32 size,n; + Eterm *kptr,*vptr; + Eterm keys; + + size = get_int32(ep); ep += 4; + + keys = make_tuple(hp); + *hp++ = make_arityval(size); + kptr = hp; + hp += size; + + mp = (map_t*)hp; + hp += MAP_HEADER_SIZE; + vptr = hp; + hp += size; + + /* kptr, first word for keys + * vptr, first word for values + */ + + /* + * Use thing_word to link through decoded maps. + * The list of maps is for later validation. + */ + + mp->thing_word = (Eterm) COMPRESS_POINTER(maps_head); + maps_head = (Eterm *) mp; + + mp->size = size; + mp->keys = keys; + *objp = make_map(mp); + + /* We assume the map is wellformed, meaning: + * - ascending key order + * - unique keys + */ + + objp = vptr + size - 1; + n = size; + + while (n-- > 0) { + *objp = (Eterm) COMPRESS_POINTER(next); + next = objp; + objp--; + } + + objp = kptr + size - 1; + n = size; + + while (n-- > 0) { + *objp = (Eterm) COMPRESS_POINTER(next); + next = objp; + objp--; + } + } + break; case NEW_FUN_EXT: { ErlFunThing* funp = (ErlFunThing *) hp; @@ -3678,21 +3771,7 @@ dec_term_atom_common: } default: - error: - /* UNDO: - * Must unlink all off-heap objects that may have been - * linked into the process. - */ - if (hp < *hpp) { /* Sometimes we used hp and sometimes *hpp */ - hp = *hpp; /* the largest must be the freshest */ - } - undo_offheap_in_area(off_heap, hp_saved, hp); - *hpp = hp_saved; - if (ctx) { - ctx->state = B2TDecodeFail; - ctx->reds = reds; - } - return NULL; + goto error; } if (--reds <= 0) { @@ -3710,12 +3789,53 @@ dec_term_atom_common: } } } + + /* Iterate through all the maps and check for validity + * - done here for when we know it is complete. + */ + + while (maps_head) { + Eterm *keys; + Sint arity; + + next = (Eterm *)(EXPAND_POINTER(*maps_head)); + keys = tuple_val(*(maps_head + 2)); + arity = arityval(*keys++); + + while(arity-- > 1) { + if (CMP(keys[arity-1],keys[arity]) >= 0) { + goto error; + } + } + + *maps_head = MAP_HEADER; + maps_head = next; + } + if (ctx) { ctx->state = B2TDone; ctx->reds = reds; } + *hpp = hp; return ep; + +error: + /* UNDO: + * Must unlink all off-heap objects that may have been + * linked into the process. + */ + if (hp < *hpp) { /* Sometimes we used hp and sometimes *hpp */ + hp = *hpp; /* the largest must be the freshest */ + } + undo_offheap_in_area(off_heap, hp_saved, hp); + *hpp = hp_saved; + if (ctx) { + ctx->state = B2TDecodeFail; + ctx->reds = reds; + } + + return NULL; } /* returns the number of bytes needed to encode an object @@ -3885,6 +4005,46 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, goto outer_loop; } break; + case MAP_DEF: + { + map_t *mp = (map_t*)map_val(obj); + Uint size = map_get_size(mp); + Uint i; + Eterm *ptr; + + result += 1 + 4; /* tag + 4 bytes size */ + + /* push values first */ + ptr = map_get_values(mp); + i = size; + while(i--) { + if (is_list(*ptr)) { + if ((m = is_string(*ptr)) && (m < MAX_STRING_LEN)) { + result += m + 2 + 1; + } else { + result += 5; + } + } + ESTACK_PUSH(s,*ptr); + ++ptr; + } + + ptr = map_get_keys(mp); + i = size; + while(i--) { + if (is_list(*ptr)) { + if ((m = is_string(*ptr)) && (m < MAX_STRING_LEN)) { + result += m + 2 + 1; + } else { + result += 5; + } + } + ESTACK_PUSH(s,*ptr); + ++ptr; + } + goto outer_loop; + } + break; case FLOAT_DEF: if (dflags & DFLAG_NEW_FLOATS) { result += 9; @@ -4175,6 +4335,13 @@ init_done: ADDTERMS(n); heap_size += n + 1; break; + case MAP_EXT: + CHKSIZE(4); + n = get_int32(ep); + ep += 4; + ADDTERMS(2*n); + heap_size += 3 + n + 1 + n; + break; case STRING_EXT: CHKSIZE(2); n = get_int16(ep); diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index 83001b2c7e..bf00958eb1 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -50,6 +50,7 @@ #define LARGE_BIG_EXT 'o' #define NEW_FUN_EXT 'p' #define EXPORT_EXT 'q' +#define MAP_EXT 't' #define FUN_EXT 'u' #define ATOM_UTF8_EXT 'v' #define SMALL_ATOM_UTF8_EXT 'w' -- cgit v1.2.3 From 97d087263e1836f3efb4267e3990219fe3e3c045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 3 Oct 2013 00:35:45 +0200 Subject: erts: Add Map tests for hashing and external format - Update map_SUITE with hash and encode tests - Update map_SUITE with external format decode tests - Update map_SUITE with map:to_list/1 and map:from_list/1 tests --- erts/emulator/test/map_SUITE.erl | 143 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 3 deletions(-) diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 81d39fc97a..27f35ccd29 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -30,7 +30,7 @@ t_map_sort_literals/1, t_size/1, t_map_size/1, - %% BIFs + %% Specific Map BIFs t_bif_map_get/1, t_bif_map_find/1, t_bif_map_is_key/1, @@ -38,7 +38,13 @@ t_bif_map_new/1, t_bif_map_put/1, t_bif_map_remove/1, - t_bif_map_values/1 + t_bif_map_values/1, + t_bif_map_to_list/1, + t_bif_map_from_list/1, + + %% erlang + t_erlang_hash/1, + t_map_encode_decode/1 ]). -include_lib("test_server/include/test_server.hrl"). @@ -57,7 +63,11 @@ all() -> [ %% Specific Map BIFs t_bif_map_get,t_bif_map_find,t_bif_map_is_key, t_bif_map_keys,t_bif_map_new,t_bif_map_put, - t_bif_map_remove,t_bif_map_values + t_bif_map_remove,t_bif_map_values, + t_bif_map_to_list, t_bif_map_from_list, + + %% erlang + t_erlang_hash, t_map_encode_decode ]. groups() -> []. @@ -562,7 +572,134 @@ t_bif_map_values(Config) when is_list(Config) -> {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(atom)), {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values([])), {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(<<>>)), + ok. + +t_erlang_hash(Config) when is_list(Config) -> + + 39679005 = erlang:phash2(#{}), + 106033788 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), + 119861073 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), + 87559846 = erlang:phash2(#{ 1 => a }), + 76038729 = erlang:phash2(#{ a => 1 }), + + M0 = #{ a => 1, "key" => <<"value">> }, + M1 = map:remove("key",M0), + M2 = M1#{ "key" => <<"value">> }, + + 67968757 = erlang:phash2(M0), + 76038729 = erlang:phash2(M1), + 67968757 = erlang:phash2(M2), + ok. + +t_map_encode_decode(Config) when is_list(Config) -> + <<131,116,0,0,0,0>> = erlang:term_to_binary(#{}), + Pairs = [ + {a,b},{"key","values"},{<<"key">>,<<"value">>}, + {1,b},{[atom,1],{<<"wat">>,1,2,3}}, + {aa,"values"}, + {1 bsl 64 + (1 bsl 50 - 1), sc1}, + {99, sc2}, + {1 bsl 65 + (1 bsl 51 - 1), sc3}, + {88, sc4}, + {1 bsl 66 + (1 bsl 52 - 1), sc5}, + {77, sc6}, + {1 bsl 67 + (1 bsl 53 - 1), sc3}, + {75, sc6}, {-10,sc8}, + {<<>>, sc9}, {3.14158, sc10}, + {[3.14158], sc11}, {more_atoms, sc12}, + {{more_tuples}, sc13}, {self(), sc14}, + {{},{}},{[],[]} + ], + ok = map_encode_decode_and_match(Pairs,[],#{}), + + %% error cases + %% template: <<131,116,0,0,0,2,100,0,1,97,100,0,1,98,97,1,97,1>> + %% which is: #{ a=>1, b=>1 } + + %% order violation + %% literally #{ b=>1, a=>1 } in the internal order (bad) + {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch + erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,1,97,1>>)), + + %% uniqueness violation + %% literally #{ a=>1, a=>1 } + {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch + erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,97,100,0,1,97,97,1,97,1>>)), + + %% bad size (too large) + {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch + erlang:binary_to_term(<<131,116,0,0,0,12,100,0,1,97,100,0,1,98,97,1,97,1>>)), + + %% bad size (too small) .. should fail just truncate it .. weird. + %% possibly change external format so truncated will be #{a:=1} + #{ a:=b } = + erlang:binary_to_term(<<131,116,0,0,0,1,100,0,1,97,100,0,1,98,97,1,97,1>>), + + ok. + +map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) -> + M1 = map:put(K,V,M0), + B0 = erlang:term_to_binary(M1), + Ls = lists:sort([{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]), + %% sort Ks and Vs according to term spec, then match it + ok = match_encoded_map(B0, length(Ls), [Kbin||{_,Kbin,_}<-Ls] ++ [Vbin||{_,_,Vbin}<-Ls]), + %% decode and match it + M1 = erlang:binary_to_term(B0), + map_encode_decode_and_match(Pairs,Ls,M1); +map_encode_decode_and_match([],_,_) -> ok. + +match_encoded_map(<<131,116,Size:32,Encoded/binary>>,Size,Items) -> + match_encoded_map(Encoded,Items); +match_encoded_map(_,_,_) -> no_match_size. + +match_encoded_map(<<>>,[]) -> ok; +match_encoded_map(Bin,[<<131,Item/binary>>|Items]) -> + Size = erlang:byte_size(Item), + <> = Bin, + EncodedTerm = Item, %% Asssert + match_encoded_map(Bin1,Items). + + +t_bif_map_to_list(Config) when is_list(Config) -> + [] = map:to_list(#{}), + [{a,1},{b,2}] = map:to_list(#{a=>1,b=>2}), + [{a,1},{b,2},{c,3}] = map:to_list(#{c=>3,a=>1,b=>2}), + [{a,1},{b,2},{g,3}] = map:to_list(#{g=>3,a=>1,b=>2}), + [{a,1},{b,2},{g,3},{"c",4}] = map:to_list(#{g=>3,a=>1,b=>2,"c"=>4}), + [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = map:to_list(#{ + <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5}), + + [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = map:to_list(#{ + <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5, + <<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10}), + + %% error cases + {'EXIT', {badarg,_}} = (catch map:to_list(id(a))), + {'EXIT', {badarg,_}} = (catch map:to_list(id(42))), + ok. + + +t_bif_map_from_list(Config) when is_list(Config) -> + #{} = map:from_list([]), + A = map:from_list([]), + 0 = erlang:map_size(A), + + #{a:=1,b:=2} = map:from_list([{a,1},{b,2}]), + #{c:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{c,3}]), + #{g:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{g,3}]), + + #{a:=2} = map:from_list([{a,1},{a,3},{a,2}]), + + #{ <<"hi">>:=v1,3:=v3,"hi":=v6,hi:=v4,{hi,3}:=v5} = + map:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]), + + #{<<"hi">>:=v6,3:=v8,"hi":=v11,hi:=v9,{hi,3}:=v10} = + map:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2}, + {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]), + %% error cases + {'EXIT', {badarg,_}} = (catch map:from_list(id(a))), + {'EXIT', {badarg,_}} = (catch map:from_list(id(42))), ok. -- cgit v1.2.3 From ac2954982d6089b0970798581b2230bdd1a065d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 15 Oct 2013 17:21:30 +0200 Subject: erts: Add Maps to erlang:phash/2 and erlang:hash/2 The hashing a map in these functions uses the same strategy as the other terms. The exception being a prime number with size so we do not get erlang:phash(#{}) -> 1 which would be the same as erlang:phash({}) and erlang:phash(<<>>). Same argument for erlang:hash/1. --- erts/emulator/beam/utils.c | 53 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index bc7e91295d..3774f379cc 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -735,6 +735,8 @@ erts_bld_atom_2uint_3tup_list(Uint **hpp, Uint *szp, Sint length, #define FUNNY_NUMBER10 268440479 #define FUNNY_NUMBER11 268440577 #define FUNNY_NUMBER12 268440581 +#define FUNNY_NUMBER13 268440593 +#define FUNNY_NUMBER14 268440611 static Uint32 hash_binary_bytes(Eterm bin, Uint sz, Uint32 hash) @@ -787,7 +789,7 @@ Uint32 make_hash(Eterm term_arg) /* Must not collide with the real tag_val_def's: */ #define MAKE_HASH_TUPLE_OP 0x11 -#define MAKE_HASH_FUN_OP 0x12 +#define MAKE_HASH_TERM_ARRAY_OP 0x12 #define MAKE_HASH_CDR_PRE_OP 0x13 #define MAKE_HASH_CDR_POST_OP 0x14 @@ -878,7 +880,7 @@ tail_recur: hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq; if (num_free > 0) { if (num_free > 1) { - WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_FUN_OP); + WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_TERM_ARRAY_OP); } term = funp->env[0]; goto tail_recur; @@ -968,6 +970,24 @@ tail_recur: hash *= is_neg ? FUNNY_NUMBER4 : FUNNY_NUMBER3; break; } + case MAP_DEF: + { + map_t *mp = (map_t *)map_val(term); + int size = map_get_size(mp); + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + + /* Use a prime with size to remedy some of + * the {} and <<>> hash problems */ + hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + size; + if (size == 0) + break; + + /* push values first */ + WSTACK_PUSH3(stack, (UWord)vs, (UWord) size, MAKE_HASH_TERM_ARRAY_OP); + WSTACK_PUSH3(stack, (UWord)ks, (UWord) size, MAKE_HASH_TERM_ARRAY_OP); + break; + } case TUPLE_DEF: { Eterm* ptr = tuple_val(term); @@ -977,7 +997,7 @@ tail_recur: op = MAKE_HASH_TUPLE_OP; }/*fall through*/ case MAKE_HASH_TUPLE_OP: - case MAKE_HASH_FUN_OP: + case MAKE_HASH_TERM_ARRAY_OP: { Uint i = (Uint) WSTACK_POP(stack); Eterm* ptr = (Eterm*) WSTACK_POP(stack); @@ -1206,6 +1226,9 @@ make_hash2(Eterm term) tmp = vs[i]; ESTACK_PUSH(s, tmp); } + /* We do not want to expose the tuple representation. + * Do not push the keys as a tuple. + */ for (i = size - 1; i >= 1; i--) { tmp = ks[i]; ESTACK_PUSH(s, tmp); @@ -1514,7 +1537,7 @@ tail_recur: hash = hash*FUNNY_NUMBER2 + funp->fe->old_uniq; if (num_free > 0) { if (num_free > 1) { - WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_FUN_OP); + WSTACK_PUSH3(stack, (UWord) &funp->env[1], (num_free-1), MAKE_HASH_TERM_ARRAY_OP); } term = funp->env[0]; goto tail_recur; @@ -1627,6 +1650,24 @@ tail_recur: } break; + case MAP_DEF: + { + map_t *mp = (map_t *)map_val(term); + int size = map_get_size(mp); + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + + /* Use a prime with size to remedy some of + * the {} and <<>> hash problems */ + hash = hash*FUNNY_NUMBER13 + FUNNY_NUMBER14 + size; + if (size == 0) + break; + + /* push values first */ + WSTACK_PUSH3(stack, (UWord)vs, (UWord) size, MAKE_HASH_TERM_ARRAY_OP); + WSTACK_PUSH3(stack, (UWord)ks, (UWord) size, MAKE_HASH_TERM_ARRAY_OP); + break; + } case TUPLE_DEF: { Eterm* ptr = tuple_val(term); @@ -1636,7 +1677,7 @@ tail_recur: op = MAKE_HASH_TUPLE_OP; }/*fall through*/ case MAKE_HASH_TUPLE_OP: - case MAKE_HASH_FUN_OP: + case MAKE_HASH_TERM_ARRAY_OP: { Uint i = (Uint) WSTACK_POP(stack); Eterm* ptr = (Eterm*) WSTACK_POP(stack); @@ -1664,7 +1705,7 @@ tail_recur: return hash; #undef MAKE_HASH_TUPLE_OP -#undef MAKE_HASH_FUN_OP +#undef MAKE_HASH_TERM_ARRAY_OP #undef MAKE_HASH_CDR_PRE_OP #undef MAKE_HASH_CDR_POST_OP } -- cgit v1.2.3 From 912f0f2aae82ee4316a2ee1863775bcf1d8a3ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 15 Oct 2013 18:06:34 +0200 Subject: erts: Add Map tests for erlang:phash/2 --- erts/emulator/test/map_SUITE.erl | 60 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 27f35ccd29..70b5c33fb2 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -23,7 +23,7 @@ -export([ t_build_and_match_literals/1, t_update_literals/1,t_match_and_update_literals/1, - update_assoc/1,update_exact/1, + t_update_assoc/1,t_update_exact/1, t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, t_guard_receive/1, t_guard_fun/1, t_list_comprehension/1, @@ -55,7 +55,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [ t_build_and_match_literals, t_update_literals, t_match_and_update_literals, - update_assoc,update_exact, + t_update_assoc,t_update_exact, t_guard_bifs, t_guard_sequence, t_guard_update, t_guard_receive,t_guard_fun, t_list_comprehension, t_map_sort_literals, @@ -178,7 +178,7 @@ loop_match_and_update_literals_x_q(Map, []) -> Map; loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) -> loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). -update_assoc(Config) when is_list(Config) -> +t_update_assoc(Config) when is_list(Config) -> M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), M1 = M0#{1=>42,2=>100,4=>[a,b,c]}, @@ -195,7 +195,7 @@ update_assoc(Config) when is_list(Config) -> ok. -update_exact(Config) when is_list(Config) -> +t_update_exact(Config) when is_list(Config) -> M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), M1 = M0#{1:=42,2:=100,4:=[a,b,c]}, @@ -576,12 +576,23 @@ t_bif_map_values(Config) when is_list(Config) -> t_erlang_hash(Config) when is_list(Config) -> + ok = t_bif_erlang_phash2(), + ok = t_bif_erlang_phash(), + ok = t_bif_erlang_hash(), + + ok. + +t_bif_erlang_phash2() -> + 39679005 = erlang:phash2(#{}), 106033788 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), 119861073 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), 87559846 = erlang:phash2(#{ 1 => a }), 76038729 = erlang:phash2(#{ a => 1 }), + 79950245 = erlang:phash2(#{{} => <<>>}), + 11244490 = erlang:phash2(#{<<>> => {}}), + M0 = #{ a => 1, "key" => <<"value">> }, M1 = map:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, @@ -591,6 +602,47 @@ t_erlang_hash(Config) when is_list(Config) -> 67968757 = erlang:phash2(M2), ok. +t_bif_erlang_phash() -> + Sz = 1 bsl 32, + 268440612 = erlang:phash(#{},Sz), + 1196461908 = erlang:phash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz), + 3944426064 = erlang:phash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz), + 1394238263 = erlang:phash(#{ 1 => a },Sz), + 4066388227 = erlang:phash(#{ a => 1 },Sz), + + 1578050717 = erlang:phash(#{{} => <<>>},Sz), + 1578050717 = erlang:phash(#{<<>> => {}},Sz), % yep, broken + + M0 = #{ a => 1, "key" => <<"value">> }, + M1 = map:remove("key",M0), + M2 = M1#{ "key" => <<"value">> }, + + 3590546636 = erlang:phash(M0,Sz), + 4066388227 = erlang:phash(M1,Sz), + 3590546636 = erlang:phash(M2,Sz), + ok. + +t_bif_erlang_hash() -> + Sz = 1 bsl 27 - 1, + 5158 = erlang:hash(#{},Sz), + 71555838 = erlang:hash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz), + 5497225 = erlang:hash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz), + 126071654 = erlang:hash(#{ 1 => a },Sz), + 126426236 = erlang:hash(#{ a => 1 },Sz), + + 101655720 = erlang:hash(#{{} => <<>>},Sz), + 101655720 = erlang:hash(#{<<>> => {}},Sz), % yep, broken + + M0 = #{ a => 1, "key" => <<"value">> }, + M1 = map:remove("key",M0), + M2 = M1#{ "key" => <<"value">> }, + + 38260486 = erlang:hash(M0,Sz), + 126426236 = erlang:hash(M1,Sz), + 38260486 = erlang:hash(M2,Sz), + ok. + + t_map_encode_decode(Config) when is_list(Config) -> <<131,116,0,0,0,0>> = erlang:term_to_binary(#{}), Pairs = [ -- cgit v1.2.3 From ab54731c41f28a21bce526249e582b56106d4965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Dec 2013 17:07:54 +0100 Subject: erts: erlang:phash2 should hash Maps independent of order --- erts/emulator/beam/utils.c | 63 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 3774f379cc..8958d334ae 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -1091,9 +1091,11 @@ Uint32 make_hash2(Eterm term) { Uint32 hash; + Uint32 hash_xor_keys = 0; + Uint32 hash_xor_values = 0; DeclareTmpHeapNoproc(tmp_big,2); -/* (HCONST * {2, ..., 14}) mod 2^32 */ +/* (HCONST * {2, ..., 16}) mod 2^32 */ #define HCONST_2 0x3c6ef372UL #define HCONST_3 0xdaa66d2bUL #define HCONST_4 0x78dde6e4UL @@ -1110,6 +1112,10 @@ make_hash2(Eterm term) #define HCONST_15 0x454021d7UL #define HCONST_16 0xe3779b90UL +#define HASH_MAP_TAIL (_make_header(1,_TAG_HEADER_REF)) +#define HASH_MAP_KEY (_make_header(2,_TAG_HEADER_REF)) +#define HASH_MAP_VAL (_make_header(3,_TAG_HEADER_REF)) + #define UINT32_HASH_2(Expr1, Expr2, AConst) \ do { \ Uint32 a,b; \ @@ -1204,36 +1210,45 @@ make_hash2(Eterm term) UINT32_HASH(arity, HCONST_9); if (arity == 0) /* Empty tuple */ goto hash2_common; - for (i = arity; i >= 2; i--) { + for (i = arity; i >= 1; i--) { tmp = elem[i]; ESTACK_PUSH(s, tmp); } - term = elem[1]; + goto hash2_common; } break; case MAP_SUBTAG: { map_t *mp = (map_t *)map_val(term); int i; - int size = map_get_size(mp); + int size = map_get_size(mp); Eterm *ks = map_get_keys(mp); Eterm *vs = map_get_values(mp); UINT32_HASH(size, HCONST_16); - if (size == 0) /* Empty Map */ + if (size == 0) { goto hash2_common; - + } + ESTACK_PUSH(s, hash_xor_values); + ESTACK_PUSH(s, hash_xor_keys); + ESTACK_PUSH(s, hash); + ESTACK_PUSH(s, HASH_MAP_TAIL); + hash = 0; + hash_xor_keys = 0; + hash_xor_values = 0; for (i = size - 1; i >= 0; i--) { tmp = vs[i]; + ESTACK_PUSH(s, HASH_MAP_VAL); ESTACK_PUSH(s, tmp); } /* We do not want to expose the tuple representation. * Do not push the keys as a tuple. */ - for (i = size - 1; i >= 1; i--) { + for (i = size - 1; i >= 0; i--) { tmp = ks[i]; + ESTACK_PUSH(s, HASH_MAP_KEY); ESTACK_PUSH(s, tmp); } - term = ks[0]; + goto hash2_common; } break; case EXPORT_SUBTAG: @@ -1427,15 +1442,47 @@ make_hash2(Eterm term) default: erl_exit(1, "Invalid tag in make_hash2(0x%X)\n", term); hash2_common: + + /* Uint32 hash always has the hash value of the previous term, + * compounded or otherwise. + */ + if (ESTACK_ISEMPTY(s)) { DESTROY_ESTACK(s); UnUseTmpHeapNoproc(2); return hash; } + term = ESTACK_POP(s); + + switch (term) { + case HASH_MAP_TAIL: { + hash = (Uint32) ESTACK_POP(s); + UINT32_HASH(hash_xor_keys, HCONST_16); + UINT32_HASH(hash_xor_values, HCONST_16); + hash_xor_keys = (Uint32) ESTACK_POP(s); + hash_xor_values = (Uint32) ESTACK_POP(s); + goto hash2_common; + } + case HASH_MAP_KEY: + hash_xor_keys ^= hash; + hash = 0; + goto hash2_common; + case HASH_MAP_VAL: + hash_xor_values ^= hash; + hash = 0; + goto hash2_common; + default: + break; + } } } } + +#undef HASH_MAP_TAIL +#undef HASH_MAP_KEY +#undef HASH_MAP_VAL + #undef UINT32_HASH_2 #undef UINT32_HASH #undef SINT32_HASH -- cgit v1.2.3 From 9344e8ccf846af62576657244e3d526f88fdeb0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 16 Dec 2013 18:01:58 +0100 Subject: erts: Update Maps erlang:phash2/1 tests --- erts/emulator/test/map_SUITE.erl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 70b5c33fb2..1f4476defa 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -584,22 +584,22 @@ t_erlang_hash(Config) when is_list(Config) -> t_bif_erlang_phash2() -> - 39679005 = erlang:phash2(#{}), - 106033788 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), - 119861073 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), - 87559846 = erlang:phash2(#{ 1 => a }), - 76038729 = erlang:phash2(#{ a => 1 }), + 39679005 = erlang:phash2(#{}), + 78942764 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), + 37338230 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), + 14363616 = erlang:phash2(#{ 1 => a }), + 51612236 = erlang:phash2(#{ a => 1 }), - 79950245 = erlang:phash2(#{{} => <<>>}), - 11244490 = erlang:phash2(#{<<>> => {}}), + 37468437 = erlang:phash2(#{{} => <<>>}), + 44049159 = erlang:phash2(#{<<>> => {}}), M0 = #{ a => 1, "key" => <<"value">> }, M1 = map:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, - 67968757 = erlang:phash2(M0), - 76038729 = erlang:phash2(M1), - 67968757 = erlang:phash2(M2), + 118679416 = erlang:phash2(M0), + 51612236 = erlang:phash2(M1), + 118679416 = erlang:phash2(M2), ok. t_bif_erlang_phash() -> -- 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(-) 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. --- erts/emulator/test/map_SUITE.erl | 2 +- lib/stdlib/src/erl_lint.erl | 57 ++++++++++++++++++++++++++++++++-------- lib/stdlib/src/erl_parse.yrl | 2 -- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 1f4476defa..327e9b8060 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -327,7 +327,7 @@ guard_receive_loop() -> t_list_comprehension(Config) when is_list(Config) -> - [#{k:=1},#{k:=2},#{k:=3}] = [#{k:=I} || I <- [1,2,3]], + [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]], ok. t_guard_fun(Config) when is_list(Config) -> 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(-) 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 bbdec77e0749a81f5e1e94a00631249cbb420fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 15 Oct 2013 19:40:25 +0200 Subject: erts: Remove erlang:size/1 test from map_SUITE --- erts/emulator/test/map_SUITE.erl | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 327e9b8060..3b52295039 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -28,7 +28,8 @@ t_guard_receive/1, t_guard_fun/1, t_list_comprehension/1, t_map_sort_literals/1, - t_size/1, t_map_size/1, + %t_size/1, + t_map_size/1, %% Specific Map BIFs t_bif_map_get/1, @@ -67,7 +68,9 @@ all() -> [ t_bif_map_to_list, t_bif_map_from_list, %% erlang - t_erlang_hash, t_map_encode_decode + t_erlang_hash, t_map_encode_decode, + %t_size, + t_map_size ]. groups() -> []. @@ -115,14 +118,15 @@ t_build_and_match_literals(Config) when is_list(Config) -> ok. %% Tests size(Map). +%% not implemented, perhaps it shouldn't be either -t_size(Config) when is_list(Config) -> +%t_size(Config) when is_list(Config) -> % 0 = size(#{}), % 1 = size(#{a=>1}), % 1 = size(#{a=>#{a=>1}}), % 2 = size(#{a=>1, b=>2}), % 3 = size(#{a=>1, b=>2, b=>"3"}), - ok. +% ok. t_map_size(Config) when is_list(Config) -> 0 = map_size(id(#{})), @@ -132,10 +136,12 @@ t_map_size(Config) when is_list(Config) -> 3 = map_size(id(#{a=>1, b=>2, b=>"3","33"=><<"n">>})), true = map_is_size(#{a=>1}, 1), - true = map_is_size(#{a=>1, a=>2}, 2), + true = map_is_size(#{a=>1, a=>2}, 1), M = #{ "a" => 1, "b" => 2}, + true = map_is_size(M, 2), + false = map_is_size(M, 3), true = map_is_size(M#{ "a" => 2}, 2), - false = map_is_size(M#{ "a" => 2}, 3), + false = map_is_size(M#{ "c" => 2}, 2), %% Error cases. {'EXIT',{badarg,_}} = (catch map_size([])), -- 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 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 72146c6675aaff02b2452c2fd2026c111e641f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 11 Oct 2013 18:11:12 +0200 Subject: erts: Specs for Map BIFs --- erts/preloaded/src/erlang.erl | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index ee5bd3e515..ff0f9d0ead 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -60,6 +60,7 @@ -export_type([timestamp/0]). -type ext_binary() :: binary(). +-type map() :: term(). %% FIXME: remove when handled internally. -type timestamp() :: {MegaSecs :: non_neg_integer(), Secs :: non_neg_integer(), MicroSecs :: non_neg_integer()}. @@ -104,10 +105,9 @@ -export([list_to_bitstring/1, list_to_existing_atom/1, list_to_float/1]). -export([list_to_integer/1, list_to_integer/2]). -export([list_to_pid/1, list_to_tuple/1, loaded/0]). --export([localtime/0, make_ref/0, match_spec_test/3, md5/1, md5_final/1]). +-export([localtime/0, make_ref/0, map_size/1, match_spec_test/3, md5/1, md5_final/1]). -export([md5_init/0, md5_update/2, module_loaded/1, monitor/2]). --export([monitor_node/2, monitor_node/3, nif_error/1, nif_error/2 -]). +-export([monitor_node/2, monitor_node/3, nif_error/1, nif_error/2]). -export([node/0, node/1, now/0, phash/2, phash2/1, phash2/2]). -export([pid_to_list/1, port_close/1, port_command/2, port_command/3]). -export([port_connect/2, port_control/3, port_get_data/1]). @@ -128,7 +128,7 @@ -export([abs/1, append/2, element/2, get_module_info/2, hd/1, is_atom/1, is_binary/1, is_bitstring/1, is_boolean/1, is_float/1, is_function/1, is_function/2, is_integer/1, - is_list/1, is_number/1, is_pid/1, is_port/1, is_record/2, + is_list/1, is_map/1, is_number/1, is_pid/1, is_port/1, is_record/2, is_record/3, is_reference/1, is_tuple/1, load_module/2, load_nif/2, localtime_to_universaltime/2, make_fun/3, make_tuple/2, make_tuple/3, nodes/1, open_port/2, @@ -1149,6 +1149,12 @@ localtime() -> make_ref() -> erlang:nif_error(undefined). +%% Shadowed by erl_bif_types: erlang:map_size/1 +-spec map_size(Map) -> non_neg_integer() when + Map :: map(). +map_size(_Map) -> + erlang:nif_error(undefined). + %% match_spec_test/3 -spec erlang:match_spec_test(P1, P2, P3) -> TestResult when P1 :: [term()] | tuple(), @@ -1739,6 +1745,12 @@ is_number(_Term) -> is_pid(_Term) -> erlang:nif_error(undefined). +%% Shadowed by erl_bif_types: erlang:is_map/1 +-spec is_map(Map) -> boolean() when + Map :: map(). +is_map(_Map) -> + erlang:nif_error(undefined). + %% Shadowed by erl_bif_types: erlang:is_port/1 -spec is_port(Term) -> boolean() when Term :: term(). -- 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(-) 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(-) 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 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(-) 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(+) 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 --- erts/emulator/beam/erl_db_util.c | 12 ++++++++++++ lib/stdlib/src/ms_transform.erl | 2 ++ 2 files changed, 14 insertions(+) diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index d903053aa4..3986ccd4d3 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -565,6 +565,12 @@ static DMCGuardBif guard_tab[] = 1, DBIF_ALL }, + { + am_is_map, + &is_map_1, + 1, + DBIF_ALL + }, { am_is_binary, &is_binary_1, @@ -631,6 +637,12 @@ static DMCGuardBif guard_tab[] = 1, DBIF_ALL }, + { + am_map_size, + &map_size_1, + 1, + DBIF_ALL + }, { am_bit_size, &bit_size_1, 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 e1095860776d76294c1577079b2f24e0688c8e04 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 25 Oct 2013 14:38:50 +0200 Subject: Add tests for pditc, ets and tracing --- erts/emulator/test/map_SUITE.erl | 143 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 138 insertions(+), 5 deletions(-) diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 3b52295039..ea3483d09e 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -45,13 +45,18 @@ %% erlang t_erlang_hash/1, - t_map_encode_decode/1 - ]). + t_map_encode_decode/1, --include_lib("test_server/include/test_server.hrl"). + %% misc + t_pdict/1, + t_ets/1, + t_dets/1, + t_tracing/1 + ]). +-include_lib("stdlib/include/ms_transform.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> []. all() -> [ t_build_and_match_literals, @@ -70,7 +75,12 @@ all() -> [ %% erlang t_erlang_hash, t_map_encode_decode, %t_size, - t_map_size + t_map_size, + + %% Other functions + t_pdict, + t_ets, + t_tracing ]. groups() -> []. @@ -760,6 +770,129 @@ t_bif_map_from_list(Config) when is_list(Config) -> {'EXIT', {badarg,_}} = (catch map:from_list(id(42))), ok. +%% MISC +t_pdict(_Config) -> + + put(#{ a => b, b => a},#{ c => d}), + put(get(#{ a => b, b => a}),1), + 1 = get(#{ c => d}), + #{ c := d } = get(#{ a => b, b => a}). + +t_ets(_Config) -> + + Tid = ets:new(map_table,[]), + + [ets:insert(Tid,{maps:from_list([{I,-I}]),I}) || I <- lists:seq(1,100)], + + + [{#{ 2 := -2},2}] = ets:lookup(Tid,#{ 2 => -2 }), + + %% Test equal + [3,4] = lists:sort( + ets:select(Tid,[{{'$1','$2'}, + [{'or',{'==','$1',#{ 3 => -3 }}, + {'==','$1',#{ 4 => -4 }}}], + ['$2']}])), + %% Test match + [30,50] = lists:sort( + ets:select(Tid, + [{{#{ 30 => -30}, '$1'},[],['$1']}, + {{#{ 50 => -50}, '$1'},[],['$1']}] + )), + + ets:insert(Tid,{#{ a => b, b => c, c => a},transitivity}), + + %% Test equal with map of different size + [] = ets:select(Tid,[{{'$1','_'},[{'==','$1',#{ b => c }}],['$_']}]), + + %% Test match with map of different size + [{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]), + + %% Test match with don't care value + [{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]), + + %% Test is_map bif + ets:insert(Tid,{not_a_map,2}), + 100 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$_']}])), + + %% Test map_size bif + [3] = ets:select(Tid,[{{'$1','_'},[{'==',{map_size,'$1'},3}], + [{map_size,'$1'}]}]), + + true = ets:delete(Tid,#{50 => -50}), + [] = ets:lookup(Tid,#{50 => -50}), + + ets:delete(Tid), + ok. + +t_dets(_Config) -> + ok. + +t_tracing(_Config) -> + + dbg:stop_clear(), + {ok,Tracer} = dbg:tracer(process,{fun trace_collector/2, self()}), + dbg:p(self(),c), + + %% Test basic map call + {ok,_} = dbg:tpl(?MODULE,id,x), + id(#{ a => b }), + {trace,_,call,{?MODULE,id,[#{ a := b }]}} = getmsg(Tracer), + {trace,_,return_from,{?MODULE,id,1},#{ a := b }} = getmsg(Tracer), + dbg:ctpl(), + + %% Test equals in argument list + {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'==','$1',#{ b => c}}], + [{return_trace}]}]), + id(#{ a => b }), + id(#{ b => c }), + {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer), + {trace,_,return_from,{?MODULE,id,1},#{ b := c }} = getmsg(Tracer), + dbg:ctpl(), + + %% Test match in head + {ok,_} = dbg:tpl(?MODULE,id,[{[#{b => c}],[],[]}]), + id(#{ a => b }), + id(#{ b => c }), + {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer), + dbg:ctpl(), + + %% Test map guard bifs + {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'or',{is_map,{element,1,'$1'}}, + {'==',{map_size,'$1'},2}}],[]}]), + id(#{ a => b }), + id({1,2}), + id({#{ a => b},2}), + id(#{ a => b, b => c}), + {trace,_,call,{?MODULE,id,[{#{ a := b },2}]}} = getmsg(Tracer), + {trace,_,call,{?MODULE,id,[#{ a := b, b := c }]}} = getmsg(Tracer), + dbg:ctpl(), + + %% Test fun2ms, DOES NOT COMPILE!! + %MS = dbg:fun2ms(fun([A]) when A == #{ a => b} -> ok end), + %dbg:tpl(?MODULE,id,MS), + %id(#{ a => b }), + %id(#{ b => c }), + %{trace,_,call,{?MODULE,id,[#{ a := b }]}} = getmsg(Tracer), + %dbg:ctpl(), + + %% Check to extra messages + timeout = getmsg(Tracer), + + dbg:stop_clear(), + ok. + +getmsg(_Tracer) -> + receive + V -> V + after 50 -> + timeout + end. + +trace_collector(Msg,Parent) -> + io:format("~p~n",[Msg]), + Parent ! Msg, + Parent. %% Use this function to avoid compile-time evaluation of an expression. id(I) -> I. -- cgit v1.2.3 From f8345dc86a1787c3c8deb06befbb697f37db1c0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 25 Oct 2013 17:34:19 +0200 Subject: erts: Fix map_SUITE match spec tests --- erts/emulator/test/map_SUITE.erl | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index ea3483d09e..69b68c4b67 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -806,14 +806,17 @@ t_ets(_Config) -> [] = ets:select(Tid,[{{'$1','_'},[{'==','$1',#{ b => c }}],['$_']}]), %% Test match with map of different size - [{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]), + %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]), - %% Test match with don't care value - [{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]), + %%% Test match with don't care value + %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]), %% Test is_map bif + 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])), ets:insert(Tid,{not_a_map,2}), - 100 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$_']}])), + 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])), + ets:insert(Tid,{{nope,a,tuple},2}), + 101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])), %% Test map_size bif [3] = ets:select(Tid,[{{'$1','_'},[{'==',{map_size,'$1'},3}], @@ -857,18 +860,22 @@ t_tracing(_Config) -> {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer), dbg:ctpl(), - %% Test map guard bifs - {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'or',{is_map,{element,1,'$1'}}, - {'==',{map_size,'$1'},2}}],[]}]), + % Test map guard bifs + {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{is_map,{element,1,'$1'}}],[]}]), id(#{ a => b }), id({1,2}), id({#{ a => b},2}), - id(#{ a => b, b => c}), {trace,_,call,{?MODULE,id,[{#{ a := b },2}]}} = getmsg(Tracer), - {trace,_,call,{?MODULE,id,[#{ a := b, b := c }]}} = getmsg(Tracer), dbg:ctpl(), - %% Test fun2ms, DOES NOT COMPILE!! + {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'==',{map_size,{element,1,'$1'}},2}],[]}]), + id(#{ a => b }), + id({1,2}), + id({#{ a => b},2}), + id({#{ a => b, b => c},atom}), + {trace,_,call,{?MODULE,id,[{#{ a := b, b := c },atom}]}} = getmsg(Tracer), + dbg:ctpl(), + %MS = dbg:fun2ms(fun([A]) when A == #{ a => b} -> ok end), %dbg:tpl(?MODULE,id,MS), %id(#{ a => b }), @@ -883,11 +890,7 @@ t_tracing(_Config) -> ok. getmsg(_Tracer) -> - receive - V -> V - after 50 -> - timeout - end. + receive V -> V after 100 -> timeout end. trace_collector(Msg,Parent) -> io:format("~p~n",[Msg]), -- 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. --- erts/emulator/beam/bif.tab | 24 ++-- erts/emulator/beam/erl_map.c | 82 ++++++------- erts/emulator/test/map_SUITE.erl | 248 +++++++++++++++++++-------------------- 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 +- 10 files changed, 400 insertions(+), 401 deletions(-) delete mode 100644 lib/stdlib/src/map.erl create mode 100644 lib/stdlib/src/maps.erl diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 306861a4c5..b623e47b9a 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -580,18 +580,18 @@ bif re:inspect/2 ubif erlang:is_map/1 ubif erlang:map_size/1 -bif map:to_list/1 -bif map:find/2 -bif map:get/2 -bif map:from_list/1 -bif map:is_key/2 -bif map:keys/1 -bif map:merge/2 -bif map:new/0 -bif map:put/3 -bif map:remove/2 -bif map:update/3 -bif map:values/1 +bif maps:to_list/1 +bif maps:find/2 +bif maps:get/2 +bif maps:from_list/1 +bif maps:is_key/2 +bif maps:keys/1 +bif maps:merge/2 +bif maps:new/0 +bif maps:put/3 +bif maps:remove/2 +bif maps:update/3 +bif maps:values/1 # # Obsolete diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 6fd60f0689..27734276f9 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -38,25 +38,25 @@ * - erlang:is_map/1 * - erlang:map_size/1 * - * - map:find/2 - * - map:from_list/1 - * - map:get/2 - * - map:is_key/2 - * - map:keys/1 - * - map:merge/2 - * - map:new/0 - * - map:put/3 - * - map:remove/2 - * - map:to_list/1 - * - map:update/3 - * - map:values/1 + * - maps:find/2 + * - maps:from_list/1 + * - maps:get/2 + * - maps:is_key/2 + * - maps:keys/1 + * - maps:merge/2 + * - maps:new/0 + * - maps:put/3 + * - maps:remove/2 + * - maps:to_list/1 + * - maps:update/3 + * - maps:values/1 * * TODO: - * - map:foldl/3 - * - map:foldr/3 - * - map:map/3 - * - map:size/1 - * - map:without/2 + * - maps:foldl/3 + * - maps:foldr/3 + * - maps:map/3 + * - maps:size/1 + * - maps:without/2 * */ @@ -80,10 +80,10 @@ BIF_RETTYPE map_size_1(BIF_ALIST_1) { BIF_ERROR(BIF_P, BADARG); } -/* map:to_list/1 +/* maps:to_list/1 */ -BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { +BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Uint n; Eterm* hp; @@ -107,11 +107,11 @@ BIF_RETTYPE map_to_list_1(BIF_ALIST_1) { BIF_ERROR(BIF_P, BADARG); } -/* map:find/2 +/* maps:find/2 * return value if key *equals* a key in the map */ -BIF_RETTYPE map_find_2(BIF_ALIST_2) { +BIF_RETTYPE maps_find_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { Eterm *hp, *ks,*vs, key, res; map_t *mp; @@ -137,12 +137,12 @@ BIF_RETTYPE map_find_2(BIF_ALIST_2) { } BIF_ERROR(BIF_P, BADARG); } -/* map:get/2 +/* maps:get/2 * return value if key *matches* a key in the map * exception bad_key if none matches */ -BIF_RETTYPE map_get_2(BIF_ALIST_2) { +BIF_RETTYPE maps_get_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { Eterm *hp, *ks,*vs, key, error; map_t *mp; @@ -184,11 +184,11 @@ error: BIF_ERROR(BIF_P, BADARG); } -/* map:from_list/1 +/* maps:from_list/1 * List may be unsorted [{K,V}] */ -BIF_RETTYPE map_from_list_1(BIF_ALIST_1) { +BIF_RETTYPE maps_from_list_1(BIF_ALIST_1) { Eterm *kv, item = BIF_ARG_1; Eterm *hp, *thp,*vs, *ks, keys, res; map_t *mp; @@ -305,10 +305,10 @@ error: BIF_ERROR(BIF_P, BADARG); } -/* map:is_key/2 +/* maps:is_key/2 */ -BIF_RETTYPE map_is_key_2(BIF_ALIST_2) { +BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { Eterm *ks, key; map_t *mp; @@ -340,10 +340,10 @@ BIF_RETTYPE map_is_key_2(BIF_ALIST_2) { BIF_ERROR(BIF_P, BADARG); } -/* map:keys/1 +/* maps:keys/1 */ -BIF_RETTYPE map_keys_1(BIF_ALIST_1) { +BIF_RETTYPE maps_keys_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Eterm *hp, *ks, res = NIL; map_t *mp; @@ -366,10 +366,10 @@ BIF_RETTYPE map_keys_1(BIF_ALIST_1) { } BIF_ERROR(BIF_P, BADARG); } -/* map:merge/2 +/* maps:merge/2 */ -BIF_RETTYPE map_merge_2(BIF_ALIST_2) { +BIF_RETTYPE maps_merge_2(BIF_ALIST_2) { if (is_map(BIF_ARG_1) && is_map(BIF_ARG_2)) { Eterm *hp,*thp; Eterm tup; @@ -452,10 +452,10 @@ BIF_RETTYPE map_merge_2(BIF_ALIST_2) { } BIF_ERROR(BIF_P, BADARG); } -/* map:new/2 +/* maps:new/2 */ -BIF_RETTYPE map_new_0(BIF_ALIST_0) { +BIF_RETTYPE maps_new_0(BIF_ALIST_0) { Eterm* hp; Eterm tup; map_t *mp; @@ -472,10 +472,10 @@ BIF_RETTYPE map_new_0(BIF_ALIST_0) { BIF_RET(make_map(mp)); } -/* map:put/3 +/* maps:put/3 */ -BIF_RETTYPE map_put_3(BIF_ALIST_3) { +BIF_RETTYPE maps_put_3(BIF_ALIST_3) { if (is_map(BIF_ARG_3)) { Sint n,i; Sint c = 0; @@ -582,10 +582,10 @@ BIF_RETTYPE map_put_3(BIF_ALIST_3) { BIF_ERROR(BIF_P, BADARG); } -/* map:remove/3 +/* maps:remove/3 */ -BIF_RETTYPE map_remove_2(BIF_ALIST_2) { +BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { if (is_map(BIF_ARG_2)) { Sint n; Sint found = 0; @@ -656,10 +656,10 @@ BIF_RETTYPE map_remove_2(BIF_ALIST_2) { BIF_ERROR(BIF_P, BADARG); } -/* map:update/3 +/* maps:update/3 */ -BIF_RETTYPE map_update_3(BIF_ALIST_3) { +BIF_RETTYPE maps_update_3(BIF_ALIST_3) { if (is_map(BIF_ARG_3)) { Sint n,i; Sint found = 0; @@ -719,10 +719,10 @@ BIF_RETTYPE map_update_3(BIF_ALIST_3) { } -/* map:values/1 +/* maps:values/1 */ -BIF_RETTYPE map_values_1(BIF_ALIST_1) { +BIF_RETTYPE maps_values_1(BIF_ALIST_1) { if (is_map(BIF_ARG_1)) { Eterm *hp, *vs, res = NIL; map_t *mp; diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 69b68c4b67..59fdb82f50 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -412,182 +412,182 @@ t_map_sort_literals(Config) when is_list(Config) -> %% BIFs t_bif_map_get(Config) when is_list(Config) -> - 1 = map:get(a, #{ a=> 1}), - 2 = map:get(b, #{ a=> 1, b => 2}), - "hi" = map:get("hello", #{ a=>1, "hello" => "hi"}), - "tuple hi" = map:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}), + 1 = maps:get(a, #{ a=> 1}), + 2 = maps:get(b, #{ a=> 1, b => 2}), + "hi" = maps:get("hello", #{ a=>1, "hello" => "hi"}), + "tuple hi" = maps:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}), M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), - "v4" = map:get(<<"k2">>, M#{ <<"k2">> => "v4" }), + "v4" = maps:get(<<"k2">>, M#{ <<"k2">> => "v4" }), %% error case - {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,[])), - {'EXIT',{badarg,[{map,get,_,_}|_]}} = (catch map:get(a,<<>>)), - {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get({1,1}, #{{1,1.0} => "tuple"})), - {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{})), - {'EXIT',{bad_key,[{map,get,_,_}|_]}} = (catch map:get(a,#{ b=>1, c=>2})), + {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,[])), + {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,<<>>)), + {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get({1,1}, #{{1,1.0} => "tuple"})), + {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{})), + {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{ b=>1, c=>2})), ok. t_bif_map_find(Config) when is_list(Config) -> - {ok, 1} = map:find(a, #{ a=> 1}), - {ok, 2} = map:find(b, #{ a=> 1, b => 2}), - {ok, "int"} = map:find(1, #{ 1 => "int"}), - {ok, "int"} = map:find(1.0, #{ 1 => "int"}), - {ok, "float"} = map:find(1, #{ 1.0 => "float"}), - {ok, "float"} = map:find(1.0, #{ 1.0=> "float"}), + {ok, 1} = maps:find(a, #{ a=> 1}), + {ok, 2} = maps:find(b, #{ a=> 1, b => 2}), + {ok, "int"} = maps:find(1, #{ 1 => "int"}), + {ok, "int"} = maps:find(1.0, #{ 1 => "int"}), + {ok, "float"} = maps:find(1, #{ 1.0 => "float"}), + {ok, "float"} = maps:find(1.0, #{ 1.0=> "float"}), - {ok, "hi"} = map:find("hello", #{ a=>1, "hello" => "hi"}), - {ok, "tuple hi"} = map:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key + {ok, "hi"} = maps:find("hello", #{ a=>1, "hello" => "hi"}), + {ok, "tuple hi"} = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), - {ok, "v4"} = map:find(<<"k2">>, M#{ <<"k2">> => "v4" }), + {ok, "v4"} = maps:find(<<"k2">>, M#{ <<"k2">> => "v4" }), %% error case - error = map:find(a,#{}), - error = map:find(a,#{b=>1, c=>2}), + error = maps:find(a,#{}), + error = maps:find(a,#{b=>1, c=>2}), - {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,[])), - {'EXIT',{badarg,[{map,find,_,_}|_]}} = (catch map:find(a,<<>>)), + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,[])), + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,<<>>)), ok. t_bif_map_is_key(Config) when is_list(Config) -> M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, - true = map:is_key("hi", M1), - true = map:is_key(int, M1), - true = map:is_key(<<"key">>, M1), - true = map:is_key(4, M1), - - false = map:is_key(5, M1), - false = map:is_key(<<"key2">>, M1), - false = map:is_key("h", M1), - false = map:is_key("hello", M1), - false = map:is_key(atom, M1), - - false = map:is_key("hi", map:remove("hi", M1)), - true = map:is_key("hi", M1), - true = map:is_key(1, map:put(1, "number", M1)), - false = map:is_key(1.0, map:put(1, "number", M1)), + true = maps:is_key("hi", M1), + true = maps:is_key(int, M1), + true = maps:is_key(<<"key">>, M1), + true = maps:is_key(4, M1), + + false = maps:is_key(5, M1), + false = maps:is_key(<<"key2">>, M1), + false = maps:is_key("h", M1), + false = maps:is_key("hello", M1), + false = maps:is_key(atom, M1), + + false = maps:is_key("hi", maps:remove("hi", M1)), + true = maps:is_key("hi", M1), + true = maps:is_key(1, maps:put(1, "number", M1)), + false = maps:is_key(1.0, maps:put(1, "number", M1)), ok. t_bif_map_keys(Config) when is_list(Config) -> - [] = map:keys(#{}), + [] = maps:keys(#{}), - [1,2,3,4,5] = map:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), - [1,2,3,4,5] = map:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), + [1,2,3,4,5] = maps:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), + [1,2,3,4,5] = maps:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), % values in key order: [4,int,"hi",<<"key">>] M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, - [4,int,"hi",<<"key">>] = map:keys(M1), + [4,int,"hi",<<"key">>] = maps:keys(M1), %% error case - {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(1 bsl 65 + 3)), - {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(154)), - {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(atom)), - {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys([])), - {'EXIT',{badarg,[{map,keys,_,_}|_]}} = (catch map:keys(<<>>)), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(154)), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(atom)), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys([])), + {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(<<>>)), ok. t_bif_map_new(Config) when is_list(Config) -> - #{} = map:new(), - 0 = erlang:map_size(map:new()), + #{} = maps:new(), + 0 = erlang:map_size(maps:new()), ok. t_bif_map_put(Config) when is_list(Config) -> M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, - M1 = #{ "hi" := "hello"} = map:put("hi", "hello", #{}), + M1 = #{ "hi" := "hello"} = maps:put("hi", "hello", #{}), - ["hi"] = map:keys(M1), - ["hello"] = map:values(M1), + ["hi"] = maps:keys(M1), + ["hello"] = maps:values(M1), - M2 = #{ int := 3 } = map:put(int, 3, M1), + M2 = #{ int := 3 } = maps:put(int, 3, M1), - [int,"hi"] = map:keys(M2), - [3,"hello"] = map:values(M2), + [int,"hi"] = maps:keys(M2), + [3,"hello"] = maps:values(M2), - M3 = #{ <<"key">> := <<"value">> } = map:put(<<"key">>, <<"value">>, M2), + M3 = #{ <<"key">> := <<"value">> } = maps:put(<<"key">>, <<"value">>, M2), - [int,"hi",<<"key">>] = map:keys(M3), - [3,"hello",<<"value">>] = map:values(M3), + [int,"hi",<<"key">>] = maps:keys(M3), + [3,"hello",<<"value">>] = maps:values(M3), - M4 = #{ 18446744073709551629 := wat } = map:put(18446744073709551629, wat, M3), + M4 = #{ 18446744073709551629 := wat } = maps:put(18446744073709551629, wat, M3), - [18446744073709551629,int,"hi",<<"key">>] = map:keys(M4), - [wat,3,"hello",<<"value">>] = map:values(M4), + [18446744073709551629,int,"hi",<<"key">>] = maps:keys(M4), + [wat,3,"hello",<<"value">>] = maps:values(M4), - M0 = #{ 4 := number } = M5 = map:put(4, number, M4), + M0 = #{ 4 := number } = M5 = maps:put(4, number, M4), - [4,18446744073709551629,int,"hi",<<"key">>] = map:keys(M5), - [number,wat,3,"hello",<<"value">>] = map:values(M5), + [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M5), + [number,wat,3,"hello",<<"value">>] = maps:values(M5), %% error case - {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,1 bsl 65 + 3)), - {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,154)), - {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,atom)), - {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,[])), - {'EXIT',{badarg,[{map,put,_,_}|_]}} = (catch map:put(1,a,<<>>)), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,154)), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,atom)), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,[])), + {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,<<>>)), ok. t_bif_map_remove(Config) when is_list(Config) -> M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, - M1 = map:remove("hi", M0), - [4,18446744073709551629,int,<<"key">>] = map:keys(M1), - [number,wat,3,<<"value">>] = map:values(M1), + M1 = maps:remove("hi", M0), + [4,18446744073709551629,int,<<"key">>] = maps:keys(M1), + [number,wat,3,<<"value">>] = maps:values(M1), - M2 = map:remove(int, M1), - [4,18446744073709551629,<<"key">>] = map:keys(M2), - [number,wat,<<"value">>] = map:values(M2), + M2 = maps:remove(int, M1), + [4,18446744073709551629,<<"key">>] = maps:keys(M2), + [number,wat,<<"value">>] = maps:values(M2), - M3 = map:remove(<<"key">>, M2), - [4,18446744073709551629] = map:keys(M3), - [number,wat] = map:values(M3), + M3 = maps:remove(<<"key">>, M2), + [4,18446744073709551629] = maps:keys(M3), + [number,wat] = maps:values(M3), - M4 = map:remove(18446744073709551629, M3), - [4] = map:keys(M4), - [number] = map:values(M4), + M4 = maps:remove(18446744073709551629, M3), + [4] = maps:keys(M4), + [number] = maps:values(M4), - M5 = map:remove(4, M4), - [] = map:keys(M5), - [] = map:values(M5), + M5 = maps:remove(4, M4), + [] = maps:keys(M5), + [] = maps:values(M5), - M0 = map:remove(5,M0), - M0 = map:remove("hi there",M0), + M0 = maps:remove(5,M0), + M0 = maps:remove("hi there",M0), - #{ "hi" := "hello", int := 3, 4 := number} = map:remove(18446744073709551629,map:remove(<<"key">>,M0)), + #{ "hi" := "hello", int := 3, 4 := number} = maps:remove(18446744073709551629,maps:remove(<<"key">>,M0)), %% error case - {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,1 bsl 65 + 3)), - {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,154)), - {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,atom)), - {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(1,[])), - {'EXIT',{badarg,[{map,remove,_,_}|_]}} = (catch map:remove(a,<<>>)), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,154)), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,atom)), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,[])), + {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,<<>>)), ok. t_bif_map_values(Config) when is_list(Config) -> - [] = map:values(#{}), + [] = maps:values(#{}), - [a,b,c,d,e] = map:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), - [a,b,c,d,e] = map:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), + [a,b,c,d,e] = maps:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e}), + [a,b,c,d,e] = maps:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c}), % values in key order: [4,int,"hi",<<"key">>] M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number}, M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> }, - [number,3,"hello2",<<"value2">>] = map:values(M2), - [number,3,"hello",<<"value">>] = map:values(M1), + [number,3,"hello2",<<"value2">>] = maps:values(M2), + [number,3,"hello",<<"value">>] = maps:values(M1), %% error case - {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(1 bsl 65 + 3)), - {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(atom)), - {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values([])), - {'EXIT',{badarg,[{map,values,_,_}|_]}} = (catch map:values(<<>>)), + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(1 bsl 65 + 3)), + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(atom)), + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values([])), + {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(<<>>)), ok. t_erlang_hash(Config) when is_list(Config) -> @@ -610,7 +610,7 @@ t_bif_erlang_phash2() -> 44049159 = erlang:phash2(#{<<>> => {}}), M0 = #{ a => 1, "key" => <<"value">> }, - M1 = map:remove("key",M0), + M1 = maps:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, 118679416 = erlang:phash2(M0), @@ -630,7 +630,7 @@ t_bif_erlang_phash() -> 1578050717 = erlang:phash(#{<<>> => {}},Sz), % yep, broken M0 = #{ a => 1, "key" => <<"value">> }, - M1 = map:remove("key",M0), + M1 = maps:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, 3590546636 = erlang:phash(M0,Sz), @@ -650,7 +650,7 @@ t_bif_erlang_hash() -> 101655720 = erlang:hash(#{<<>> => {}},Sz), % yep, broken M0 = #{ a => 1, "key" => <<"value">> }, - M1 = map:remove("key",M0), + M1 = maps:remove("key",M0), M2 = M1#{ "key" => <<"value">> }, 38260486 = erlang:hash(M0,Sz), @@ -706,7 +706,7 @@ t_map_encode_decode(Config) when is_list(Config) -> ok. map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) -> - M1 = map:put(K,V,M0), + M1 = maps:put(K,V,M0), B0 = erlang:term_to_binary(M1), Ls = lists:sort([{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]), %% sort Ks and Vs according to term spec, then match it @@ -729,45 +729,45 @@ match_encoded_map(Bin,[<<131,Item/binary>>|Items]) -> t_bif_map_to_list(Config) when is_list(Config) -> - [] = map:to_list(#{}), - [{a,1},{b,2}] = map:to_list(#{a=>1,b=>2}), - [{a,1},{b,2},{c,3}] = map:to_list(#{c=>3,a=>1,b=>2}), - [{a,1},{b,2},{g,3}] = map:to_list(#{g=>3,a=>1,b=>2}), - [{a,1},{b,2},{g,3},{"c",4}] = map:to_list(#{g=>3,a=>1,b=>2,"c"=>4}), - [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = map:to_list(#{ + [] = maps:to_list(#{}), + [{a,1},{b,2}] = maps:to_list(#{a=>1,b=>2}), + [{a,1},{b,2},{c,3}] = maps:to_list(#{c=>3,a=>1,b=>2}), + [{a,1},{b,2},{g,3}] = maps:to_list(#{g=>3,a=>1,b=>2}), + [{a,1},{b,2},{g,3},{"c",4}] = maps:to_list(#{g=>3,a=>1,b=>2,"c"=>4}), + [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] = maps:to_list(#{ <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5}), - [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = map:to_list(#{ + [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] = maps:to_list(#{ <<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5, <<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10}), %% error cases - {'EXIT', {badarg,_}} = (catch map:to_list(id(a))), - {'EXIT', {badarg,_}} = (catch map:to_list(id(42))), + {'EXIT', {badarg,_}} = (catch maps:to_list(id(a))), + {'EXIT', {badarg,_}} = (catch maps:to_list(id(42))), ok. t_bif_map_from_list(Config) when is_list(Config) -> - #{} = map:from_list([]), - A = map:from_list([]), + #{} = maps:from_list([]), + A = maps:from_list([]), 0 = erlang:map_size(A), - #{a:=1,b:=2} = map:from_list([{a,1},{b,2}]), - #{c:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{c,3}]), - #{g:=3,a:=1,b:=2} = map:from_list([{a,1},{b,2},{g,3}]), + #{a:=1,b:=2} = maps:from_list([{a,1},{b,2}]), + #{c:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{c,3}]), + #{g:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{g,3}]), - #{a:=2} = map:from_list([{a,1},{a,3},{a,2}]), + #{a:=2} = maps:from_list([{a,1},{a,3},{a,2}]), #{ <<"hi">>:=v1,3:=v3,"hi":=v6,hi:=v4,{hi,3}:=v5} = - map:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]), + maps:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]), #{<<"hi">>:=v6,3:=v8,"hi":=v11,hi:=v9,{hi,3}:=v10} = - map:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2}, + maps:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2}, {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]), %% error cases - {'EXIT', {badarg,_}} = (catch map:from_list(id(a))), - {'EXIT', {badarg,_}} = (catch map:from_list(id(42))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id(a))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))), ok. %% MISC 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 7e42aefdecae6cc91bdaaef21924b4e7cb75861c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 30 Oct 2013 16:09:29 +0100 Subject: erts: Add tests for non BIFs in Maps module * maps:without/2 * maps:foldl/3 * maps:foldr/3 * maps:map/2 * maps:size/1 --- erts/emulator/test/map_SUITE.erl | 55 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 59fdb82f50..b6cfd668f1 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -47,6 +47,12 @@ t_erlang_hash/1, t_map_encode_decode/1, + %% maps module not bifs + t_maps_fold/1, + t_maps_map/1, + t_maps_size/1, + t_maps_without/1, + %% misc t_pdict/1, t_ets/1, @@ -74,9 +80,13 @@ all() -> [ %% erlang t_erlang_hash, t_map_encode_decode, - %t_size, t_map_size, + %% maps module + t_maps_fold, t_maps_map, + t_maps_size, t_maps_without, + + %% Other functions t_pdict, t_ets, @@ -770,6 +780,49 @@ t_bif_map_from_list(Config) when is_list(Config) -> {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))), ok. +%% Maps module, not BIFs +t_maps_fold(_Config) -> + + Vs = lists:seq(1,100), + Rs = lists:reverse(Vs), + M = maps:from_list([{{k,I},{v,I}}||I<-Vs]), + + %% foldl + 5050 = maps:foldl(fun({k,_},{v,V},A) -> V + A end, 0, M), + Rs = maps:foldl(fun({k,_},{v,V},A) -> [V|A] end, [], M), + + %% foldr + 5050 = maps:foldr(fun({k,_},{v,V},A) -> V + A end, 0, M), + Vs = maps:foldr(fun({k,_},{v,V},A) -> [V|A] end, [], M), + + ok. + +t_maps_map(_Config) -> + Vs = lists:seq(1,100), + M1 = maps:from_list([{I,I}||I<-Vs]), + M2 = maps:from_list([{I,{token,I}}||I<-Vs]), + + M2 = maps:map(fun(_K,V) -> {token,V} end, M1), + ok. + +t_maps_size(_Config) -> + Vs = lists:seq(1,100), + lists:foldl(fun(I,M) -> + M1 = maps:put(I,I,M), + I = maps:size(M1), + M1 + end, #{}, Vs), + ok. + + +t_maps_without(_Config) -> + Ki = [11,22,33,44,55,66,77,88,99], + M0 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100)]), + M1 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100) -- Ki]), + M1 = maps:without([{k,I}||I <- Ki],M0), + ok. + + %% MISC t_pdict(_Config) -> -- cgit v1.2.3 From 105dcb3eb7c6d5fd07cd76cd495230b0a1d1e7c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 30 Oct 2013 17:05:43 +0100 Subject: erts: Add tests for Map update on expressions (foo())#{ k1 := V1, k2 => V2 } --- erts/emulator/test/map_SUITE.erl | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index b6cfd668f1..a06cb43ac7 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -23,6 +23,7 @@ -export([ t_build_and_match_literals/1, t_update_literals/1,t_match_and_update_literals/1, + t_update_map_expressions/1, t_update_assoc/1,t_update_exact/1, t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, t_guard_receive/1, t_guard_fun/1, @@ -67,6 +68,7 @@ suite() -> []. all() -> [ t_build_and_match_literals, t_update_literals, t_match_and_update_literals, + t_update_map_expressions, t_update_assoc,t_update_exact, t_guard_bifs, t_guard_sequence, t_guard_update, t_guard_receive,t_guard_fun, t_list_comprehension, @@ -137,6 +139,7 @@ t_build_and_match_literals(Config) when is_list(Config) -> {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))), ok. + %% Tests size(Map). %% not implemented, perhaps it shouldn't be either @@ -163,7 +166,7 @@ t_map_size(Config) when is_list(Config) -> true = map_is_size(M#{ "a" => 2}, 2), false = map_is_size(M#{ "c" => 2}, 2), - %% Error cases. + %% Error cases. {'EXIT',{badarg,_}} = (catch map_size([])), {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)), {'EXIT',{badarg,_}} = (catch map_size(1)), @@ -204,6 +207,22 @@ loop_match_and_update_literals_x_q(Map, []) -> Map; loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) -> loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). + +t_update_map_expressions(Config) when is_list(Config) -> + M = maps:new(), + #{ a := 1 } = M#{a => 1}, + + #{ b := 2 } = (maps:new())#{ b => 2 }, + + #{ a :=42, b:=42, c:=42 } = (maps:from_list([{a,1},{b,2},{c,3}]))#{ a := 42, b := 42, c := 42 }, + #{ "a" :=1, "b":=42, "c":=42 } = (maps:from_list([{"a",1},{"b",2}]))#{ "b" := 42, "c" => 42 }, + + %% Error cases, FIXME: should be 'badmap'? + {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }), + {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }), + ok. + + t_update_assoc(Config) when is_list(Config) -> M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), -- 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(-) 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 039d3166bbe8d81b2f3bf72e4ade708b9ae5f2f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 17 Dec 2013 16:57:40 +0100 Subject: Update primary bootstrap --- bootstrap/bin/start.boot | Bin 5250 -> 5264 bytes bootstrap/bin/start_clean.boot | Bin 5250 -> 5264 bytes bootstrap/lib/compiler/ebin/beam_a.beam | Bin 2288 -> 2396 bytes bootstrap/lib/compiler/ebin/beam_block.beam | Bin 15048 -> 15260 bytes bootstrap/lib/compiler/ebin/beam_clean.beam | Bin 9476 -> 11256 bytes bootstrap/lib/compiler/ebin/beam_disasm.beam | Bin 25124 -> 26140 bytes bootstrap/lib/compiler/ebin/beam_flatten.beam | Bin 2924 -> 3092 bytes bootstrap/lib/compiler/ebin/beam_jump.beam | Bin 8568 -> 8700 bytes bootstrap/lib/compiler/ebin/beam_opcodes.beam | Bin 6812 -> 7064 bytes bootstrap/lib/compiler/ebin/beam_split.beam | Bin 2048 -> 2380 bytes bootstrap/lib/compiler/ebin/beam_utils.beam | Bin 13348 -> 13392 bytes bootstrap/lib/compiler/ebin/beam_validator.beam | Bin 34488 -> 34984 bytes bootstrap/lib/compiler/ebin/beam_z.beam | Bin 2080 -> 2232 bytes bootstrap/lib/compiler/ebin/cerl.beam | Bin 30084 -> 31576 bytes bootstrap/lib/compiler/ebin/cerl_trees.beam | Bin 18952 -> 20272 bytes bootstrap/lib/compiler/ebin/compile.beam | Bin 38164 -> 38720 bytes bootstrap/lib/compiler/ebin/compiler.appup | 2 +- bootstrap/lib/compiler/ebin/core_lib.beam | Bin 5460 -> 5812 bytes bootstrap/lib/compiler/ebin/core_lint.beam | Bin 11644 -> 12372 bytes bootstrap/lib/compiler/ebin/core_parse.beam | Bin 37664 -> 42808 bytes bootstrap/lib/compiler/ebin/core_pp.beam | Bin 12064 -> 12748 bytes bootstrap/lib/compiler/ebin/sys_core_dsetel.beam | Bin 6896 -> 7400 bytes bootstrap/lib/compiler/ebin/sys_core_fold.beam | Bin 47644 -> 48840 bytes bootstrap/lib/compiler/ebin/sys_pre_expand.beam | Bin 13620 -> 14216 bytes bootstrap/lib/compiler/ebin/v3_codegen.beam | Bin 51524 -> 55688 bytes bootstrap/lib/compiler/ebin/v3_core.beam | Bin 51880 -> 53520 bytes bootstrap/lib/compiler/ebin/v3_kernel.beam | Bin 42880 -> 44892 bytes bootstrap/lib/compiler/ebin/v3_kernel_pp.beam | Bin 11780 -> 12320 bytes bootstrap/lib/compiler/ebin/v3_life.beam | Bin 19992 -> 20856 bytes bootstrap/lib/kernel/ebin/kernel.appup | 6 +++--- bootstrap/lib/stdlib/ebin/erl_eval.beam | Bin 29232 -> 30492 bytes bootstrap/lib/stdlib/ebin/erl_expand_records.beam | Bin 21872 -> 22428 bytes bootstrap/lib/stdlib/ebin/erl_internal.beam | Bin 5200 -> 5236 bytes bootstrap/lib/stdlib/ebin/erl_lint.beam | Bin 84628 -> 85972 bytes bootstrap/lib/stdlib/ebin/erl_parse.beam | Bin 69620 -> 74708 bytes bootstrap/lib/stdlib/ebin/erl_scan.beam | Bin 30064 -> 30124 bytes bootstrap/lib/stdlib/ebin/io_lib.beam | Bin 9472 -> 9856 bytes bootstrap/lib/stdlib/ebin/maps.beam | Bin 0 -> 1816 bytes bootstrap/lib/stdlib/ebin/ms_transform.beam | Bin 20360 -> 20384 bytes bootstrap/lib/stdlib/ebin/stdlib.app | 1 + bootstrap/lib/stdlib/ebin/stdlib.appup | 6 +++--- 41 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 bootstrap/lib/stdlib/ebin/maps.beam diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot index 6f7fb5d7ca..f5800105d3 100644 Binary files a/bootstrap/bin/start.boot and b/bootstrap/bin/start.boot differ diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot index 6f7fb5d7ca..f5800105d3 100644 Binary files a/bootstrap/bin/start_clean.boot and b/bootstrap/bin/start_clean.boot differ diff --git a/bootstrap/lib/compiler/ebin/beam_a.beam b/bootstrap/lib/compiler/ebin/beam_a.beam index a4965abd48..459db28f43 100644 Binary files a/bootstrap/lib/compiler/ebin/beam_a.beam and b/bootstrap/lib/compiler/ebin/beam_a.beam differ diff --git a/bootstrap/lib/compiler/ebin/beam_block.beam b/bootstrap/lib/compiler/ebin/beam_block.beam index 6513a0f33b..c161220c93 100644 Binary files a/bootstrap/lib/compiler/ebin/beam_block.beam and b/bootstrap/lib/compiler/ebin/beam_block.beam differ diff --git a/bootstrap/lib/compiler/ebin/beam_clean.beam b/bootstrap/lib/compiler/ebin/beam_clean.beam index abe390267e..f6e3059e86 100644 Binary files a/bootstrap/lib/compiler/ebin/beam_clean.beam and b/bootstrap/lib/compiler/ebin/beam_clean.beam differ diff --git a/bootstrap/lib/compiler/ebin/beam_disasm.beam b/bootstrap/lib/compiler/ebin/beam_disasm.beam index eba17a7bbd..c19e467785 100644 Binary files a/bootstrap/lib/compiler/ebin/beam_disasm.beam and b/bootstrap/lib/compiler/ebin/beam_disasm.beam differ diff --git a/bootstrap/lib/compiler/ebin/beam_flatten.beam b/bootstrap/lib/compiler/ebin/beam_flatten.beam index f0a3dfac91..bf2d341c05 100644 Binary files a/bootstrap/lib/compiler/ebin/beam_flatten.beam and b/bootstrap/lib/compiler/ebin/beam_flatten.beam differ diff --git a/bootstrap/lib/compiler/ebin/beam_jump.beam b/bootstrap/lib/compiler/ebin/beam_jump.beam index 5c0f4a9996..75c02f082f 100644 Binary files a/bootstrap/lib/compiler/ebin/beam_jump.beam and b/bootstrap/lib/compiler/ebin/beam_jump.beam differ diff --git a/bootstrap/lib/compiler/ebin/beam_opcodes.beam b/bootstrap/lib/compiler/ebin/beam_opcodes.beam index 8425c2b2b5..5047d223c2 100644 Binary files a/bootstrap/lib/compiler/ebin/beam_opcodes.beam and b/bootstrap/lib/compiler/ebin/beam_opcodes.beam differ diff --git a/bootstrap/lib/compiler/ebin/beam_split.beam b/bootstrap/lib/compiler/ebin/beam_split.beam index 64a946db25..874245d501 100644 Binary files a/bootstrap/lib/compiler/ebin/beam_split.beam and b/bootstrap/lib/compiler/ebin/beam_split.beam differ diff --git a/bootstrap/lib/compiler/ebin/beam_utils.beam b/bootstrap/lib/compiler/ebin/beam_utils.beam index ff455ce50f..7da67b85ee 100644 Binary files a/bootstrap/lib/compiler/ebin/beam_utils.beam and b/bootstrap/lib/compiler/ebin/beam_utils.beam differ diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam index fed0bb3c48..9f154f58b8 100644 Binary files a/bootstrap/lib/compiler/ebin/beam_validator.beam and b/bootstrap/lib/compiler/ebin/beam_validator.beam differ diff --git a/bootstrap/lib/compiler/ebin/beam_z.beam b/bootstrap/lib/compiler/ebin/beam_z.beam index 4d28344035..2c4ca55563 100644 Binary files a/bootstrap/lib/compiler/ebin/beam_z.beam and b/bootstrap/lib/compiler/ebin/beam_z.beam differ diff --git a/bootstrap/lib/compiler/ebin/cerl.beam b/bootstrap/lib/compiler/ebin/cerl.beam index 2cba88e8c3..16c2e4ecb5 100644 Binary files a/bootstrap/lib/compiler/ebin/cerl.beam and b/bootstrap/lib/compiler/ebin/cerl.beam differ diff --git a/bootstrap/lib/compiler/ebin/cerl_trees.beam b/bootstrap/lib/compiler/ebin/cerl_trees.beam index ce81e67924..b25cfd07cf 100644 Binary files a/bootstrap/lib/compiler/ebin/cerl_trees.beam and b/bootstrap/lib/compiler/ebin/cerl_trees.beam differ diff --git a/bootstrap/lib/compiler/ebin/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam index 34b3cc86a9..5dcacbacc8 100644 Binary files a/bootstrap/lib/compiler/ebin/compile.beam and b/bootstrap/lib/compiler/ebin/compile.beam differ diff --git a/bootstrap/lib/compiler/ebin/compiler.appup b/bootstrap/lib/compiler/ebin/compiler.appup index 577ebb149d..2c0d46d5f5 100644 --- a/bootstrap/lib/compiler/ebin/compiler.appup +++ b/bootstrap/lib/compiler/ebin/compiler.appup @@ -1 +1 @@ -{"4.9.1",[],[]}. +{"4.9.4",[],[]}. diff --git a/bootstrap/lib/compiler/ebin/core_lib.beam b/bootstrap/lib/compiler/ebin/core_lib.beam index 5e40c086c2..84e8a8034d 100644 Binary files a/bootstrap/lib/compiler/ebin/core_lib.beam and b/bootstrap/lib/compiler/ebin/core_lib.beam differ diff --git a/bootstrap/lib/compiler/ebin/core_lint.beam b/bootstrap/lib/compiler/ebin/core_lint.beam index 831e2acc43..aeefed6fcb 100644 Binary files a/bootstrap/lib/compiler/ebin/core_lint.beam and b/bootstrap/lib/compiler/ebin/core_lint.beam differ diff --git a/bootstrap/lib/compiler/ebin/core_parse.beam b/bootstrap/lib/compiler/ebin/core_parse.beam index 3f9c212fe7..4fd3f2fbdb 100644 Binary files a/bootstrap/lib/compiler/ebin/core_parse.beam and b/bootstrap/lib/compiler/ebin/core_parse.beam differ diff --git a/bootstrap/lib/compiler/ebin/core_pp.beam b/bootstrap/lib/compiler/ebin/core_pp.beam index f7e0dc40ae..bc9decc9f7 100644 Binary files a/bootstrap/lib/compiler/ebin/core_pp.beam and b/bootstrap/lib/compiler/ebin/core_pp.beam differ diff --git a/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam b/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam index 120c4f4a76..82868f66bc 100644 Binary files a/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam and b/bootstrap/lib/compiler/ebin/sys_core_dsetel.beam differ diff --git a/bootstrap/lib/compiler/ebin/sys_core_fold.beam b/bootstrap/lib/compiler/ebin/sys_core_fold.beam index 5c87bdd3dd..5516ecaaf9 100644 Binary files a/bootstrap/lib/compiler/ebin/sys_core_fold.beam and b/bootstrap/lib/compiler/ebin/sys_core_fold.beam differ diff --git a/bootstrap/lib/compiler/ebin/sys_pre_expand.beam b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam index d726c3ca07..6a6216e80f 100644 Binary files a/bootstrap/lib/compiler/ebin/sys_pre_expand.beam and b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam differ diff --git a/bootstrap/lib/compiler/ebin/v3_codegen.beam b/bootstrap/lib/compiler/ebin/v3_codegen.beam index c683fc6c46..e6bbc49fba 100644 Binary files a/bootstrap/lib/compiler/ebin/v3_codegen.beam and b/bootstrap/lib/compiler/ebin/v3_codegen.beam differ diff --git a/bootstrap/lib/compiler/ebin/v3_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam index 8004afb882..d0e6b3d97e 100644 Binary files a/bootstrap/lib/compiler/ebin/v3_core.beam and b/bootstrap/lib/compiler/ebin/v3_core.beam differ diff --git a/bootstrap/lib/compiler/ebin/v3_kernel.beam b/bootstrap/lib/compiler/ebin/v3_kernel.beam index 0566a7ff51..8f7ba8803a 100644 Binary files a/bootstrap/lib/compiler/ebin/v3_kernel.beam and b/bootstrap/lib/compiler/ebin/v3_kernel.beam differ diff --git a/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam b/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam index 1b2d02c1d5..d123c58593 100644 Binary files a/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam and b/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam differ diff --git a/bootstrap/lib/compiler/ebin/v3_life.beam b/bootstrap/lib/compiler/ebin/v3_life.beam index bb0ceefdd1..89d1d70814 100644 Binary files a/bootstrap/lib/compiler/ebin/v3_life.beam and b/bootstrap/lib/compiler/ebin/v3_life.beam differ diff --git a/bootstrap/lib/kernel/ebin/kernel.appup b/bootstrap/lib/kernel/ebin/kernel.appup index 382d9deea1..b58d066898 100644 --- a/bootstrap/lib/kernel/ebin/kernel.appup +++ b/bootstrap/lib/kernel/ebin/kernel.appup @@ -15,13 +15,13 @@ %% under the License. %% %% %CopyrightEnd% -{"2.17", +{"3.0", %% Up from - max two major revisions back - [{<<"2\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 + [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 {<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16 {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R15 %% Down to - max two major revisions back - [{<<"2\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 + [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 {<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16 {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R15 }. diff --git a/bootstrap/lib/stdlib/ebin/erl_eval.beam b/bootstrap/lib/stdlib/ebin/erl_eval.beam index d58dd86070..19fadc9585 100644 Binary files a/bootstrap/lib/stdlib/ebin/erl_eval.beam and b/bootstrap/lib/stdlib/ebin/erl_eval.beam differ diff --git a/bootstrap/lib/stdlib/ebin/erl_expand_records.beam b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam index 0563699d5e..c8f45fd838 100644 Binary files a/bootstrap/lib/stdlib/ebin/erl_expand_records.beam and b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam differ diff --git a/bootstrap/lib/stdlib/ebin/erl_internal.beam b/bootstrap/lib/stdlib/ebin/erl_internal.beam index 01c6598cdc..b851ca484e 100644 Binary files a/bootstrap/lib/stdlib/ebin/erl_internal.beam and b/bootstrap/lib/stdlib/ebin/erl_internal.beam differ diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam index 10d32c9865..c413a7fda1 100644 Binary files a/bootstrap/lib/stdlib/ebin/erl_lint.beam and b/bootstrap/lib/stdlib/ebin/erl_lint.beam differ diff --git a/bootstrap/lib/stdlib/ebin/erl_parse.beam b/bootstrap/lib/stdlib/ebin/erl_parse.beam index 2815cad4a3..c1c114dd3c 100644 Binary files a/bootstrap/lib/stdlib/ebin/erl_parse.beam and b/bootstrap/lib/stdlib/ebin/erl_parse.beam differ diff --git a/bootstrap/lib/stdlib/ebin/erl_scan.beam b/bootstrap/lib/stdlib/ebin/erl_scan.beam index 851270bd37..fe45755343 100644 Binary files a/bootstrap/lib/stdlib/ebin/erl_scan.beam and b/bootstrap/lib/stdlib/ebin/erl_scan.beam differ diff --git a/bootstrap/lib/stdlib/ebin/io_lib.beam b/bootstrap/lib/stdlib/ebin/io_lib.beam index d34f69097a..ad77021190 100644 Binary files a/bootstrap/lib/stdlib/ebin/io_lib.beam and b/bootstrap/lib/stdlib/ebin/io_lib.beam differ diff --git a/bootstrap/lib/stdlib/ebin/maps.beam b/bootstrap/lib/stdlib/ebin/maps.beam new file mode 100644 index 0000000000..9a902a1ae4 Binary files /dev/null and b/bootstrap/lib/stdlib/ebin/maps.beam differ diff --git a/bootstrap/lib/stdlib/ebin/ms_transform.beam b/bootstrap/lib/stdlib/ebin/ms_transform.beam index 6e7a19c78a..9a55c521f8 100644 Binary files a/bootstrap/lib/stdlib/ebin/ms_transform.beam and b/bootstrap/lib/stdlib/ebin/ms_transform.beam differ diff --git a/bootstrap/lib/stdlib/ebin/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app index d1da05048b..31c7222100 100644 --- a/bootstrap/lib/stdlib/ebin/stdlib.app +++ b/bootstrap/lib/stdlib/ebin/stdlib.app @@ -71,6 +71,7 @@ lib, lists, log_mf_h, + maps, math, ms_transform, orddict, diff --git a/bootstrap/lib/stdlib/ebin/stdlib.appup b/bootstrap/lib/stdlib/ebin/stdlib.appup index b8460d543c..0871dc5a98 100644 --- a/bootstrap/lib/stdlib/ebin/stdlib.appup +++ b/bootstrap/lib/stdlib/ebin/stdlib.appup @@ -15,13 +15,13 @@ %% under the License. %% %% %CopyrightEnd% -{"1.20", +{"2.0", %% Up from - max two major revisions back - [{<<"1\\.20(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 + [{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16 {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R15 %% Down to - max two major revisions back - [{<<"1\\.20(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 + [{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16 {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R15 }. -- cgit v1.2.3 From b313f455be0def48b85c905efd98308aefb5bbc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 24 Oct 2013 14:40:12 +0200 Subject: preloaded: Remove bogus map type --- erts/preloaded/src/erlang.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index ff0f9d0ead..fbc37bd955 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -60,7 +60,6 @@ -export_type([timestamp/0]). -type ext_binary() :: binary(). --type map() :: term(). %% FIXME: remove when handled internally. -type timestamp() :: {MegaSecs :: non_neg_integer(), Secs :: non_neg_integer(), MicroSecs :: non_neg_integer()}. -- cgit v1.2.3 From 38047efbbb91b02bd8894654458350b5a9813798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 6 Nov 2013 16:44:55 +0100 Subject: Update preloaded erlang.beam --- erts/preloaded/ebin/erlang.beam | Bin 98008 -> 98256 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index f38377647c..eb696bb32f 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ -- 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(+) 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(-) 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 e0eb6d5bafcebc1c24b0a538e50a1d55a3724f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 8 Nov 2013 15:34:05 +0100 Subject: erts: Add NIFs for Maps - int enif_is_map(ErlNifEnv* env, ERL_NIF_TERM map) - int enif_get_map_size(ErlNifEnv *env, ERL_NIF_TERM, int*) - ERL_NIF_TERM enif_make_new_map(ErlNifEnv *env) - int enif_make_map_put(ErlNifEnv *env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out) - int enif_get_map_value(ErlNifEnv *env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value) - int enif_find_map_value(ErlNifEnv *env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value) - int enif_make_map_update(ErlNifEnv *env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out) - int enif_make_map_remove(ErlNifEnv *env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out) - int enif_map_iterator_create(ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter) - void enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter) - int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) - int enif_map_iterator_get_pair(ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value) --- erts/emulator/beam/erl_map.c | 443 ++++++++++++++++++--------------- erts/emulator/beam/erl_map.h | 6 + erts/emulator/beam/erl_nif.c | 209 ++++++++++++++++ erts/emulator/beam/erl_nif.h | 26 +- erts/emulator/beam/erl_nif_api_funcs.h | 33 +++ 5 files changed, 511 insertions(+), 206 deletions(-) diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 27734276f9..98aeee634b 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -111,28 +111,39 @@ BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) { * return value if key *equals* a key in the map */ -BIF_RETTYPE maps_find_2(BIF_ALIST_2) { - if (is_map(BIF_ARG_2)) { - Eterm *hp, *ks,*vs, key, res; - map_t *mp; - Uint n,i; +int erts_maps_find(Eterm key, Eterm map, Eterm *value) { - mp = (map_t*)map_val(BIF_ARG_2); - key = BIF_ARG_1; - n = map_get_size(mp); - ks = map_get_keys(mp); - vs = map_get_values(mp); + Eterm *ks,*vs; + map_t *mp; + Uint n,i; - for( i = 0; i < n; i++) { - if (CMP(ks[i], key)==0) { - hp = HAlloc(BIF_P, 3); - res = make_tuple(hp); - *hp++ = make_arityval(2); - *hp++ = am_ok; - *hp++ = vs[i]; - BIF_RET(res); - } + mp = (map_t*)map_val(map); + n = map_get_size(mp); + ks = map_get_keys(mp); + vs = map_get_values(mp); + + for( i = 0; i < n; i++) { + if (CMP(ks[i], key)==0) { + *value = vs[i]; + return 1; + } + } + return 0; +} + +BIF_RETTYPE maps_find_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm *hp, value,res; + + if (erts_maps_find(BIF_ARG_1, BIF_ARG_2, &value)) { + hp = HAlloc(BIF_P, 3); + res = make_tuple(hp); + *hp++ = make_arityval(2); + *hp++ = am_ok; + *hp++ = value; + BIF_RET(res); } + BIF_RET(am_error); } BIF_ERROR(BIF_P, BADARG); @@ -142,43 +153,54 @@ BIF_RETTYPE maps_find_2(BIF_ALIST_2) { * exception bad_key if none matches */ -BIF_RETTYPE maps_get_2(BIF_ALIST_2) { - if (is_map(BIF_ARG_2)) { - Eterm *hp, *ks,*vs, key, error; - map_t *mp; - Uint n,i; - char *s_error; - mp = (map_t*)map_val(BIF_ARG_2); - key = BIF_ARG_1; - n = map_get_size(mp); - - if (n == 0) - goto error; +int erts_maps_get(Eterm key, Eterm map, Eterm *value) { + Eterm *ks,*vs; + map_t *mp; + Uint n,i; - ks = map_get_keys(mp); - vs = map_get_values(mp); + mp = (map_t*)map_val(map); + n = map_get_size(mp); - if (is_immed(key)) { - for( i = 0; i < n; i++) { - if (ks[i] == key) { - BIF_RET(vs[i]); - } - } - } + if (n == 0) + return 0; + + ks = map_get_keys(mp); + vs = map_get_values(mp); + if (is_immed(key)) { for( i = 0; i < n; i++) { - if (eq(ks[i], key)) { - BIF_RET(vs[i]); + if (ks[i] == key) { + *value = vs[i]; + return 1; } } -error: + } + + for( i = 0; i < n; i++) { + if (eq(ks[i], key)) { + *value = vs[i]; + return 1; + } + } + return 0; +} + +BIF_RETTYPE maps_get_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm *hp; + Eterm value, error; + char *s_error; + + if (erts_maps_get(BIF_ARG_1, BIF_ARG_2, &value)) { + BIF_RET(value); + } s_error = "bad_key"; error = am_atom_put(s_error, sys_strlen(s_error)); hp = HAlloc(BIF_P, 3); - BIF_P->fvalue = TUPLE2(hp, error, key); + BIF_P->fvalue = TUPLE2(hp, error, BIF_ARG_1); BIF_ERROR(BIF_P, EXC_ERROR_2); } BIF_ERROR(BIF_P, BADARG); @@ -460,7 +482,7 @@ BIF_RETTYPE maps_new_0(BIF_ALIST_0) { Eterm tup; map_t *mp; - hp = HAlloc(BIF_P, (3 + 1)); + hp = HAlloc(BIF_P, (MAP_HEADER_SIZE + 1)); tup = make_tuple(hp); *hp++ = make_arityval(0); @@ -475,183 +497,197 @@ BIF_RETTYPE maps_new_0(BIF_ALIST_0) { /* maps:put/3 */ -BIF_RETTYPE maps_put_3(BIF_ALIST_3) { - if (is_map(BIF_ARG_3)) { - Sint n,i; - Sint c = 0; - Eterm* hp, *shp; - Eterm *ks,*vs, res, key, tup; - map_t *mp = (map_t*)map_val(BIF_ARG_3); - - key = BIF_ARG_1; - n = map_get_size(mp); - - if (n == 0) { - hp = HAlloc(BIF_P, 4 + 2); - tup = make_tuple(hp); - *hp++ = make_arityval(1); - *hp++ = key; - res = make_map(hp); - *hp++ = MAP_HEADER; - *hp++ = 1; - *hp++ = tup; - *hp++ = BIF_ARG_2; +Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { + Sint n,i; + Sint c = 0; + Eterm* hp, *shp; + Eterm *ks,*vs, res, tup; + map_t *mp = (map_t*)map_val(map); - BIF_RET(res); - } - - ks = map_get_keys(mp); - vs = map_get_values(mp); - /* only allocate for values, - * assume key-tuple will be intact - */ + n = map_get_size(mp); - hp = HAlloc(BIF_P, 3 + n); - shp = hp; /* save hp, used if optimistic update fails */ - res = make_map(hp); + if (n == 0) { + hp = HAlloc(p, MAP_HEADER_SIZE + 1 + 2); + tup = make_tuple(hp); + *hp++ = make_arityval(1); + *hp++ = key; + res = make_map(hp); *hp++ = MAP_HEADER; - *hp++ = n; - *hp++ = mp->keys; + *hp++ = 1; + *hp++ = tup; + *hp++ = value; - if (is_immed(key)) { - for( i = 0; i < n; i ++) { - if (ks[i] == key) { - *hp++ = BIF_ARG_2; - vs++; - c = 1; - } else { - *hp++ = *vs++; - } + return res; + } + + ks = map_get_keys(mp); + vs = map_get_values(mp); + + /* only allocate for values, + * assume key-tuple will be intact + */ + + hp = HAlloc(p, MAP_HEADER_SIZE + n); + shp = hp; /* save hp, used if optimistic update fails */ + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = n; + *hp++ = mp->keys; + + if (is_immed(key)) { + for( i = 0; i < n; i ++) { + if (ks[i] == key) { + *hp++ = value; + vs++; + c = 1; + } else { + *hp++ = *vs++; } - } else { - for( i = 0; i < n; i ++) { - if (eq(ks[i], key)) { - *hp++ = BIF_ARG_2; - vs++; - c = 1; - } else { - *hp++ = *vs++; - } + } + } else { + for( i = 0; i < n; i ++) { + if (eq(ks[i], key)) { + *hp++ = value; + vs++; + c = 1; + } else { + *hp++ = *vs++; } } + } - if (c) - BIF_RET(res); + if (c) + return res; - /* need to make a new tuple, - * use old hp since it needs to be recreated anyway. - */ - tup = make_tuple(shp); - *shp++ = make_arityval(n+1); + /* need to make a new tuple, + * use old hp since it needs to be recreated anyway. + */ + tup = make_tuple(shp); + *shp++ = make_arityval(n+1); - hp = HAlloc(BIF_P, 3 + n + 1); - res = make_map(hp); - *hp++ = MAP_HEADER; - *hp++ = n + 1; - *hp++ = tup; + hp = HAlloc(p, 3 + n + 1); + res = make_map(hp); + *hp++ = MAP_HEADER; + *hp++ = n + 1; + *hp++ = tup; - ks = map_get_keys(mp); - vs = map_get_values(mp); + ks = map_get_keys(mp); + vs = map_get_values(mp); - ASSERT(n >= 0); + ASSERT(n >= 0); - /* copy map in order */ - while (n && ((c = CMP(*ks, key)) < 0)) { - *shp++ = *ks++; - *hp++ = *vs++; - n--; - } + /* copy map in order */ + while (n && ((c = CMP(*ks, key)) < 0)) { + *shp++ = *ks++; + *hp++ = *vs++; + n--; + } - *shp++ = key; - *hp++ = BIF_ARG_2; + *shp++ = key; + *hp++ = value; - ASSERT(n >= 0); + ASSERT(n >= 0); - while(n--) { - *shp++ = *ks++; - *hp++ = *vs++; - } - /* we have one word remaining - * this will work out fine once we get the size word - * in the header. - */ - *shp = make_pos_bignum_header(0); - BIF_RET(res); + while(n--) { + *shp++ = *ks++; + *hp++ = *vs++; } + /* we have one word remaining + * this will work out fine once we get the size word + * in the header. + */ + *shp = make_pos_bignum_header(0); + return res; +} +BIF_RETTYPE maps_put_3(BIF_ALIST_3) { + if (is_map(BIF_ARG_3)) { + BIF_RET(erts_maps_put(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3)); + } BIF_ERROR(BIF_P, BADARG); } /* maps:remove/3 */ -BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { - if (is_map(BIF_ARG_2)) { - Sint n; - Sint found = 0; - Uint need; - Eterm *thp, *mhp; - Eterm *ks, *vs, res, key,tup; - map_t *mp = (map_t*)map_val(BIF_ARG_2); +int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { + Sint n; + Sint found = 0; + Uint need; + Eterm *thp, *mhp; + Eterm *ks, *vs, tup; + map_t *mp = (map_t*)map_val(map); - key = BIF_ARG_1; - n = map_get_size(mp); + n = map_get_size(mp); - if (n == 0) - BIF_RET(BIF_ARG_2); + if (n == 0) { + *res = map; + return 1; + } - ks = map_get_keys(mp); - vs = map_get_values(mp); + ks = map_get_keys(mp); + vs = map_get_values(mp); - /* Assume key exists. - * Release allocated if it didn't. - * Allocate key tuple first. - */ + /* Assume key exists. + * Release allocated if it didn't. + * Allocate key tuple first. + */ - need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */ - thp = HAlloc(BIF_P, need); - mhp = thp + n; /* offset with tuple heap size */ + need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */ + thp = HAlloc(p, need); + mhp = thp + n; /* offset with tuple heap size */ - tup = make_tuple(thp); - *thp++ = make_arityval(n - 1); + tup = make_tuple(thp); + *thp++ = make_arityval(n - 1); - res = make_map(mhp); - *mhp++ = MAP_HEADER; - *mhp++ = n - 1; - *mhp++ = tup; + *res = make_map(mhp); + *mhp++ = MAP_HEADER; + *mhp++ = n - 1; + *mhp++ = tup; - if (is_immed(key)) { - while(n--) { - if (*ks == key) { - ks++; - vs++; - found = 1; - } else { - *mhp++ = *vs++; - *thp++ = *ks++; - } + if (is_immed(key)) { + while(n--) { + if (*ks == key) { + ks++; + vs++; + found = 1; + } else { + *mhp++ = *vs++; + *thp++ = *ks++; } - } else { - while(n--) { - if (eq(*ks, key)) { - ks++; - vs++; - found = 1; - } else { - *mhp++ = *vs++; - *thp++ = *ks++; - } + } + } else { + while(n--) { + if (eq(*ks, key)) { + ks++; + vs++; + found = 1; + } else { + *mhp++ = *vs++; + *thp++ = *ks++; } } + } - if (found) - BIF_RET(res); + if (found) { + return 1; + } - /* Not found, remove allocated memory - * and return previous map. - */ - HRelease(BIF_P, thp + need, thp); - BIF_RET(BIF_ARG_2); + /* Not found, remove allocated memory + * and return previous map. + */ + HRelease(p, thp + need, thp); + + *res = map; + return 1; +} + +BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { + if (is_map(BIF_ARG_2)) { + Eterm res; + if (erts_maps_remove(BIF_P, BIF_ARG_1, BIF_ARG_2, &res)) { + BIF_RET(res); + } } BIF_ERROR(BIF_P, BADARG); } @@ -659,19 +695,15 @@ BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { /* maps:update/3 */ -BIF_RETTYPE maps_update_3(BIF_ALIST_3) { - if (is_map(BIF_ARG_3)) { +int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) { Sint n,i; Sint found = 0; Eterm* hp,*shp; - Eterm *ks,*vs, res, key; - map_t *mp = (map_t*)map_val(BIF_ARG_3); + Eterm *ks,*vs; + map_t *mp = (map_t*)map_val(map); - key = BIF_ARG_1; - n = map_get_size(mp); - - if (n == 0) { - BIF_ERROR(BIF_P, BADARG); + if ((n = map_get_size(mp)) == 0) { + return 0; } ks = map_get_keys(mp); @@ -681,9 +713,9 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) { * assume key-tuple will be intact */ - hp = HAlloc(BIF_P, 3 + n); + hp = HAlloc(p, MAP_HEADER_SIZE + n); shp = hp; - res = make_map(hp); + *res = make_map(hp); *hp++ = MAP_HEADER; *hp++ = n; *hp++ = mp->keys; @@ -691,7 +723,7 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) { if (is_immed(key)) { for( i = 0; i < n; i ++) { if (ks[i] == key) { - *hp++ = BIF_ARG_2; + *hp++ = value; vs++; found = 1; } else { @@ -701,7 +733,7 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) { } else { for( i = 0; i < n; i ++) { if (eq(ks[i], key)) { - *hp++ = BIF_ARG_2; + *hp++ = value; vs++; found = 1; } else { @@ -710,10 +742,19 @@ BIF_RETTYPE maps_update_3(BIF_ALIST_3) { } } - if (found) - BIF_RET(res); + if (found) { + return 1; + } + HRelease(p, shp + MAP_HEADER_SIZE + n, shp); + return 0; +} - HRelease(BIF_P, shp + 3 + n, shp); +BIF_RETTYPE maps_update_3(BIF_ALIST_3) { + if (is_map(BIF_ARG_3)) { + Eterm res; + if (erts_maps_update(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &res)) { + BIF_RET(res); + } } BIF_ERROR(BIF_P, BADARG); } diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index 4f0d26e100..616ecd24ce 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -62,5 +62,11 @@ typedef struct map_s { #define MAP_HEADER _make_header(1,_TAG_HEADER_MAP) #define MAP_HEADER_SIZE (sizeof(map_t) / sizeof(Eterm)) +Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map); +int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res); +int erts_maps_find(Eterm key, Eterm map, Eterm *value); +int erts_maps_get(Eterm key, Eterm map, Eterm *value); +int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res); + #endif diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index e1e213c4eb..c683847aaa 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -31,6 +31,7 @@ #include "bif.h" #include "error.h" #include "big.h" +#include "erl_map.h" #include "beam_bp.h" #include "erl_thr_progress.h" #include "dtrace-wrapper.h" @@ -1602,6 +1603,214 @@ enif_have_dirty_schedulers() #endif /* ERL_NIF_DIRTY_SCHEDULER_SUPPORT */ +/* Maps */ + +int enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term) +{ + return is_map(term); +} + +int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, int *size) +{ + if (is_map(term)) { + map_t *mp; + mp = (map_t*)map_val(term); + *size = map_get_size(mp); + return 1; + } + return 0; +} + +ERL_NIF_TERM enif_make_new_map(ErlNifEnv* env) +{ + Eterm* hp = alloc_heap(env,MAP_HEADER_SIZE+1); + Eterm tup; + map_t *mp; + + tup = make_tuple(hp); + *hp++ = make_arityval(0); + mp = (map_t*)hp; + mp->thing_word = MAP_HEADER; + mp->size = 0; + mp->keys = tup; + + return make_map(mp); +} + +int enif_make_map_put(ErlNifEnv* env, + Eterm map_in, + Eterm key, + Eterm value, + Eterm *map_out) +{ + if (is_not_map(map_in)) { + return 0; + } + flush_env(env); + *map_out = erts_maps_put(env->proc, key, value, map_in); + cache_env(env); + return 1; +} + +int enif_get_map_value(ErlNifEnv* env, + Eterm map, + Eterm key, + Eterm *value) +{ + if (is_not_map(map)) { + return 0; + } + return erts_maps_get(key, map, value); +} + +int enif_find_map_value(ErlNifEnv* env, + Eterm map, + Eterm key, + Eterm *value) +{ + if (is_not_map(map)) { + return 0; + } + return erts_maps_get(key, map, value); +} + +int enif_make_map_update(ErlNifEnv* env, + Eterm map_in, + Eterm key, + Eterm value, + Eterm *map_out) +{ + int res; + if (is_not_map(map_in)) { + return 0; + } + + flush_env(env); + res = erts_maps_update(env->proc, key, value, map_in, map_out); + cache_env(env); + return res; +} + +int enif_make_map_remove(ErlNifEnv* env, + Eterm map_in, + Eterm key, + Eterm *map_out) +{ + int res; + if (is_not_map(map_in)) { + return 0; + } + flush_env(env); + res = erts_maps_remove(env->proc, key, map_in, map_out); + cache_env(env); + return res; +} + +int enif_map_iterator_create(ErlNifEnv *env, + Eterm map, + ErlNifMapIterator *iter, + ErlNifMapIteratorEntry entry) +{ + if (is_map(map)) { + map_t *mp = (map_t*)map_val(map); + size_t offset; + + switch (entry) { + case ERL_NIF_MAP_ITERATOR_HEAD: offset = 0; break; + case ERL_NIF_MAP_ITERATOR_TAIL: offset = map_get_size(mp) - 1; break; + default: goto error; + } + + /* empty maps are ok but will leave the iterator + * in bad shape. + */ + + iter->map = map; + iter->ks = ((Eterm *)map_get_keys(mp)) + offset; + iter->vs = ((Eterm *)map_get_values(mp)) + offset; + iter->t_limit = map_get_size(mp) + 1; + iter->h_limit = 0; + iter->idx = offset + 1; + + return 1; + } + +error: + iter->map = THE_NON_VALUE; + return 0; +} + +void enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter) +{ + /* not used */ +} + +int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) +{ + ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); + if (is_map(iter->map) && ( + (iter->t_limit - iter->h_limit) == 1 || + iter->idx == iter->t_limit)) { + return 1; + } + return 0; +} + +int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) +{ + ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); + if (is_map(iter->map) && ( + (iter->t_limit - iter->h_limit) == 1 || + iter->idx == iter->h_limit)) { + return 1; + } + return 0; +} + + +int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) +{ + if (is_map(iter->map) && iter->idx < iter->t_limit) { + iter->idx++; + if (iter->idx != iter->t_limit) { + iter->ks++; + iter->vs++; + } + return 1; + } + return 0; +} + +int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) +{ + if (is_map(iter->map) && iter->idx > iter->h_limit ) { + iter->idx--; + if (iter->idx != iter->h_limit ) { + iter->ks--; + iter->vs--; + } + return 1; + } + return 0; +} + +int enif_map_iterator_get_pair(ErlNifEnv *env, + ErlNifMapIterator *iter, + Eterm *key, + Eterm *value) +{ + if (is_map(iter->map) && iter->idx > iter->h_limit && iter->idx < iter->t_limit) { + ASSERT(iter->ks >= map_get_keys(map_val(iter->map)) && + iter->ks < (map_get_keys(map_val(iter->map)) + map_get_size(map_val(iter->map)))); + ASSERT(iter->vs >= map_get_values(map_val(iter->map)) && + iter->vs < (map_get_values(map_val(iter->map)) + map_get_size(map_val(iter->map)))); + *key = *(iter->ks); + *value = *(iter->vs); + return 1; + } + return 0; +} + /*************************************************************************** ** load_nif/2 ** ***************************************************************************/ diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index fb3c359ec9..3c1e13f8a4 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -38,14 +38,11 @@ ** 2.2: R14B03 enif_is_exception ** 2.3: R15 enif_make_reverse_list, enif_is_number ** 2.4: R16 enif_consume_timeslice -** 2.5: R17 dirty schedulers +** 2.5: R17 Maps API additions +** R17 dirty schedulers */ #define ERL_NIF_MAJOR_VERSION 2 -#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT #define ERL_NIF_MINOR_VERSION 5 -#else -#define ERL_NIF_MINOR_VERSION 4 -#endif #include @@ -168,6 +165,7 @@ typedef int ErlNifTSDKey; typedef ErlDrvThreadOpts ErlNifThreadOpts; +<<<<<<< HEAD #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT typedef enum { @@ -175,6 +173,24 @@ typedef enum ERL_NIF_DIRTY_JOB_IO_BOUND = 2 }ErlNifDirtyTaskFlags; #endif +======= +typedef struct +{ + /* use a lot of memory, structure may change */ + ERL_NIF_TERM map; + ErlNifUInt64 h_limit; + ErlNifUInt64 t_limit; + ErlNifUInt64 idx; + ERL_NIF_TERM *ks; + ERL_NIF_TERM *vs; +} ErlNifMapIterator; + +typedef enum { + ERL_NIF_MAP_ITERATOR_HEAD = 1, + ERL_NIF_MAP_ITERATOR_TAIL = 2 +} ErlNifMapIteratorEntry; + +>>>>>>> erts: Add NIFs for Maps #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) # define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index f5b27dfdfa..2cabfd4ce1 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -149,6 +149,23 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void)); #endif +ERL_NIF_API_FUNC_DECL(int, enif_is_map, (ErlNifEnv* env, ERL_NIF_TERM term)); +ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term, int *size)); +ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env)); +ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); +ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); +ERL_NIF_API_FUNC_DECL(int, enif_find_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); +ERL_NIF_API_FUNC_DECL(int, enif_make_map_update, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); +ERL_NIF_API_FUNC_DECL(int, enif_make_map_remove, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_create, (ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)); +ERL_NIF_API_FUNC_DECL(void, enif_map_iterator_destroy, (ErlNifEnv *env, ErlNifMapIterator *iter)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_is_head, (ErlNifEnv *env, ErlNifMapIterator *iter)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_is_tail, (ErlNifEnv *env, ErlNifMapIterator *iter)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_next, (ErlNifEnv *env, ErlNifMapIterator *iter)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_prev, (ErlNifEnv *env, ErlNifMapIterator *iter)); +ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)); + + /* ** Add new entries here to keep compatibility on Windows!!! */ @@ -281,6 +298,22 @@ ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void)); # define enif_have_dirty_schedulers ERL_NIF_API_FUNC_MACRO(enif_have_dirty_schedulers) #endif +# define enif_is_map ERL_NIF_API_FUNC_MACRO(enif_is_map) +# define enif_get_map_size ERL_NIF_API_FUNC_MACRO(enif_get_map_size) +# define enif_make_new_map ERL_NIF_FUNC_MACRO(enif_make_new_map) +# define enif_make_map_put ERL_NIF_FUNC_MACRO(enif_map_map_put) +# define enif_get_map_value ERL_NIF_FUNC_MACRO(enif_get_map_value) +# define enif_find_map_value ERL_NIF_FUNC_MACRO(enif_find_map_value) +# define enif_make_map_update ERL_NIF_FUNC_MACRO(enif_make_map_update) +# define enif_make_map_remove ERL_NIF_FUNC_MACRO(enif_make_map_remove) +# define enif_map_iterator_create ERL_NIF_FUNC_MACRO(enif_map_iterator_create) +# define enif_map_iterator_destroy ERL_NIF_FUNC_MACRO(enif_map_iterator_destroy) +# define enif_map_iterator_is_head ERL_NIF_FUNC_MACRO(enif_map_iterator_is_head) +# define enif_map_iterator_is_tail ERL_NIF_FUNC_MACRO(enif_map_iterator_is_tail) +# define enif_map_iterator_next ERL_NIF_FUNC_MACRO(enif_map_iterator_next) +# define enif_map_iterator_prev ERL_NIF_FUNC_MACRO(enif_map_iterator_prev) +# define enif_map_iterator_get_pair NIF_FUNC_MACRO(enif_map_iterator_get_pair) + /* ** Add new entries here */ -- cgit v1.2.3 From c71408f7184090fee077e35d0220fc021d2817fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 19 Nov 2013 11:38:36 +0100 Subject: erts: Test Maps with NIFs --- erts/emulator/test/nif_SUITE.erl | 25 +++++++++++- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 59 +++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index affb66289b..5b7c310aa4 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -29,7 +29,8 @@ init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2, basic/1, reload/1, upgrade/1, heap_frag/1, - types/1, many_args/1, binaries/1, get_string/1, get_atom/1, + types/1, many_args/1, binaries/1, get_string/1, get_atom/1, + maps/1, api_macros/1, from_array/1, iolist_as_binary/1, resource/1, resource_binary/1, resource_takeover/1, @@ -58,7 +59,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [basic, reload, upgrade, heap_frag, types, many_args, - binaries, get_string, get_atom, api_macros, from_array, + binaries, get_string, get_atom, maps, api_macros, from_array, iolist_as_binary, resource, resource_binary, resource_takeover, threading, send, send2, send3, send_threaded, neg, is_checks, get_length, make_atom, @@ -435,6 +436,21 @@ get_atom(Config) when is_list(Config) -> ?line {0, <<>>} = atom_to_bin('',0), ok. +maps(doc) -> ["Test NIF maps handling."]; +maps(suite) -> []; +maps(Config) when is_list(Config) -> + TmpMem = tmpmem(), + Pairs = [{adam, "bert"}] ++ + [{I,I}||I <- lists:seq(1,10)] ++ + [{a,value},{"a","value"},{<<"a">>,<<"value">>}], + ok = ensure_lib_loaded(Config, 1), + M = maps_from_list(Pairs), + R = {RIs,Is} = sorted_list_from_maps(M), + io:format("Pairs: ~p~nMap: ~p~nReturned: ~p~n", [lists:sort(Pairs),M,R]), + Is = lists:sort(Pairs), + Is = lists:reverse(RIs), + ok. + api_macros(doc) -> ["Test macros enif_make_list and enif_make_tuple"]; api_macros(suite) -> []; api_macros(Config) when is_list(Config) -> @@ -1488,5 +1504,10 @@ otp_9668_nif(_) -> ?nif_stub. consume_timeslice_nif(_,_) -> ?nif_stub. call_dirty_nif(_,_,_) -> ?nif_stub. +%% maps +maps_from_list(_) -> ?nif_stub. +sorted_list_from_maps(_) -> ?nif_stub. + + nif_stub_error(Line) -> exit({nif_not_loaded,module,?MODULE,line,Line}). diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 6f902e186d..a0316a7861 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1536,6 +1536,63 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM } #endif +/* maps */ +static ERL_NIF_TERM maps_from_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM cell = argv[0]; + ERL_NIF_TERM map = enif_make_new_map(env); + ERL_NIF_TERM tuple; + const ERL_NIF_TERM *pair; + int arity = -1; + + if (argc != 1 && !enif_is_list(env, cell)) return enif_make_badarg(env); + + /* assume sorted keys */ + + while (!enif_is_empty_list(env,cell)) { + if (!enif_get_list_cell(env, cell, &tuple, &cell)) return enif_make_badarg(env); + if (enif_get_tuple(env,tuple,&arity,&pair)) { + enif_make_map_put(env, map, pair[0], pair[1], &map); + } + } + + return map; +} + +static ERL_NIF_TERM sorted_list_from_maps(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + + ERL_NIF_TERM map = argv[0]; + ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */ + ERL_NIF_TERM list_b = enif_make_list(env, 0); /* NIL */ + ERL_NIF_TERM key, value; + ErlNifMapIterator iter; + + if (argc != 1 && !enif_is_map(env, map)) + return enif_make_badarg(env); + + if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_HEAD)) + return enif_make_badarg(env); + + while(enif_map_iterator_get_pair(env,&iter,&key,&value)) { + list_f = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_f); + enif_map_iterator_next(env,&iter); + } + + enif_map_iterator_destroy(env, &iter); + + if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_TAIL)) + return enif_make_badarg(env); + + while(enif_map_iterator_get_pair(env,&iter,&key,&value)) { + list_b = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_b); + enif_map_iterator_prev(env,&iter); + } + + enif_map_iterator_destroy(env, &iter); + + return enif_make_tuple2(env, list_f, list_b); +} + static ErlNifFunc nif_funcs[] = { {"lib_version", 0, lib_version}, @@ -1589,6 +1646,8 @@ static ErlNifFunc nif_funcs[] = #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT {"call_dirty_nif", 3, call_dirty_nif}, #endif + {"maps_from_list", 1, maps_from_list}, + {"sorted_list_from_maps", 1, sorted_list_from_maps} }; ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload) -- cgit v1.2.3 From faf92c5174c7b86d2b5829c928b187753b3918e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 29 Nov 2013 12:12:53 +0100 Subject: erts: NIFs Map API fixup --- erts/emulator/beam/erl_nif_api_funcs.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 2cabfd4ce1..4a5aacad48 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -300,19 +300,19 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa # define enif_is_map ERL_NIF_API_FUNC_MACRO(enif_is_map) # define enif_get_map_size ERL_NIF_API_FUNC_MACRO(enif_get_map_size) -# define enif_make_new_map ERL_NIF_FUNC_MACRO(enif_make_new_map) -# define enif_make_map_put ERL_NIF_FUNC_MACRO(enif_map_map_put) -# define enif_get_map_value ERL_NIF_FUNC_MACRO(enif_get_map_value) -# define enif_find_map_value ERL_NIF_FUNC_MACRO(enif_find_map_value) -# define enif_make_map_update ERL_NIF_FUNC_MACRO(enif_make_map_update) -# define enif_make_map_remove ERL_NIF_FUNC_MACRO(enif_make_map_remove) -# define enif_map_iterator_create ERL_NIF_FUNC_MACRO(enif_map_iterator_create) -# define enif_map_iterator_destroy ERL_NIF_FUNC_MACRO(enif_map_iterator_destroy) -# define enif_map_iterator_is_head ERL_NIF_FUNC_MACRO(enif_map_iterator_is_head) -# define enif_map_iterator_is_tail ERL_NIF_FUNC_MACRO(enif_map_iterator_is_tail) -# define enif_map_iterator_next ERL_NIF_FUNC_MACRO(enif_map_iterator_next) -# define enif_map_iterator_prev ERL_NIF_FUNC_MACRO(enif_map_iterator_prev) -# define enif_map_iterator_get_pair NIF_FUNC_MACRO(enif_map_iterator_get_pair) +# define enif_make_new_map ERL_NIF_API_FUNC_MACRO(enif_make_new_map) +# define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_map_map_put) +# define enif_get_map_value ERL_NIF_API_FUNC_MACRO(enif_get_map_value) +# define enif_find_map_value ERL_NIF_API_FUNC_MACRO(enif_find_map_value) +# define enif_make_map_update ERL_NIF_API_FUNC_MACRO(enif_make_map_update) +# define enif_make_map_remove ERL_NIF_API_FUNC_MACRO(enif_make_map_remove) +# define enif_map_iterator_create ERL_NIF_API_FUNC_MACRO(enif_map_iterator_create) +# define enif_map_iterator_destroy ERL_NIF_API_FUNC_MACRO(enif_map_iterator_destroy) +# define enif_map_iterator_is_head ERL_NIF_API_FUNC_MACRO(enif_map_iterator_is_head) +# define enif_map_iterator_is_tail ERL_NIF_API_FUNC_MACRO(enif_map_iterator_is_tail) +# define enif_map_iterator_next ERL_NIF_API_FUNC_MACRO(enif_map_iterator_next) +# define enif_map_iterator_prev ERL_NIF_API_FUNC_MACRO(enif_map_iterator_prev) +# define enif_map_iterator_get_pair ERL_NIF_API_FUNC_MACRO(enif_map_iterator_get_pair) /* ** Add new entries here -- cgit v1.2.3 From d5c238473b9cec819d93faaef4ccc00ddb60465f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Sat, 26 Oct 2013 21:01:41 +0200 Subject: erts: Add cmp_term to compare Uses total order of types meaning int < float --- erts/emulator/beam/erl_utils.h | 46 +++++++++++++++++++++++++++--------------- erts/emulator/beam/utils.c | 13 ++++++++++-- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 292d135946..c6247c7246 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -202,23 +202,37 @@ int eq(Eterm, Eterm); #define EQ(x,y) (((x) == (y)) || (is_not_both_immed((x),(y)) && eq((x),(y)))) #if HALFWORD_HEAP -Sint cmp_rel(Eterm, Eterm*, Eterm, Eterm*); -#define CMP(A,B) cmp_rel(A,NULL,B,NULL) +Sint cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int); +#define cmp_rel(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,0) +#define cmp_rel_exact(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,1) +#define CMP(A,B) cmp_rel(A,NULL,B,NULL,0) +#define CMP_TERM(A,B) cmp_rel(A,NULL,B,NULL,1) #else -Sint cmp(Eterm, Eterm); -#define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B) -#define CMP(A,B) cmp(A,B) +Sint cmp(Eterm, Eterm, int); +#define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B,0) +#define cmp_rel_term(A,A_BASE,B,B_BASE) cmp(A,B,1) +#define CMP(A,B) cmp(A,B,0) +#define CMP_TERM(A,B) cmp(A,B,1) #endif -#define cmp_lt(a,b) (CMP((a),(b)) < 0) -#define cmp_le(a,b) (CMP((a),(b)) <= 0) -#define cmp_eq(a,b) (CMP((a),(b)) == 0) -#define cmp_ne(a,b) (CMP((a),(b)) != 0) -#define cmp_ge(a,b) (CMP((a),(b)) >= 0) -#define cmp_gt(a,b) (CMP((a),(b)) > 0) - -#define CMP_LT(a,b) ((a) != (b) && cmp_lt((a),(b))) -#define CMP_GE(a,b) ((a) == (b) || cmp_ge((a),(b))) -#define CMP_EQ(a,b) ((a) == (b) || cmp_eq((a),(b))) -#define CMP_NE(a,b) ((a) != (b) && cmp_ne((a),(b))) + +#define cmp_lt(a,b) (CMP((a),(b)) < 0) +#define cmp_le(a,b) (CMP((a),(b)) <= 0) +#define cmp_eq(a,b) (CMP((a),(b)) == 0) +#define cmp_ne(a,b) (CMP((a),(b)) != 0) +#define cmp_ge(a,b) (CMP((a),(b)) >= 0) +#define cmp_gt(a,b) (CMP((a),(b)) > 0) + +#define cmp_lt_term(a,b) (CMP_TERM((a),(b)) < 0) +#define cmp_le_term(a,b) (CMP_TERM((a),(b)) <= 0) +#define cmp_ge_term(a,b) (CMP_TERM((a),(b)) >= 0) +#define cmp_gt_term(a,b) (CMP_TERM((a),(b)) > 0) + +#define CMP_LT(a,b) ((a) != (b) && cmp_lt((a),(b))) +#define CMP_GE(a,b) ((a) == (b) || cmp_ge((a),(b))) +#define CMP_EQ(a,b) ((a) == (b) || cmp_eq((a),(b))) +#define CMP_NE(a,b) ((a) != (b) && cmp_ne((a),(b))) + +#define CMP_LT_TERM(a,b) ((a) != (b) && cmp_lt_term((a),(b))) +#define CMP_GE_TERM(a,b) ((a) == (b) || cmp_ge_term((a),(b))) #endif diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 8958d334ae..bc4a05d385 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2425,10 +2425,14 @@ static int cmp_atoms(Eterm a, Eterm b) bb->name+3, bb->len-3); } +/* cmp(Eterm a, Eterm b, int exact) + * exact = 1 -> term-based compare + * exact = 0 -> arith-based compare + */ #if HALFWORD_HEAP -Sint cmp_rel(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base) +Sint cmp_rel_opt(Eterm a, Eterm* a_base, Eterm b, Eterm* b_base, int exact) #else -Sint cmp(Eterm a, Eterm b) +Sint cmp(Eterm a, Eterm b, int exact) #endif { DECLARE_WSTACK(stack); @@ -2852,6 +2856,7 @@ tailrecur_ne: j = big_sign(aw) ? -1 : 1; break; case SMALL_FLOAT: + if (exact) goto exact_fall_through; GET_DOUBLE(bw, f2); if (f2.fd < MAX_LOSSLESS_FLOAT && f2.fd > MIN_LOSSLESS_FLOAT) { /* Float is within the no loss limit */ @@ -2877,12 +2882,14 @@ tailrecur_ne: #endif /* ERTS_SIZEOF_ETERM == 8 */ break; case FLOAT_BIG: + if (exact) goto exact_fall_through; { Wterm tmp = aw; aw = bw; bw = tmp; }/* fall through */ case BIG_FLOAT: + if (exact) goto exact_fall_through; GET_DOUBLE(bw, f2); if ((f2.fd < (double) (MAX_SMALL + 1)) && (f2.fd > (double) (MIN_SMALL - 1))) { @@ -2912,6 +2919,7 @@ tailrecur_ne: } break; case FLOAT_SMALL: + if (exact) goto exact_fall_through; GET_DOUBLE(aw, f1); if (f1.fd < MAX_LOSSLESS_FLOAT && f1.fd > MIN_LOSSLESS_FLOAT) { /* Float is within the no loss limit */ @@ -2936,6 +2944,7 @@ tailrecur_ne: } #endif /* ERTS_SIZEOF_ETERM == 8 */ break; +exact_fall_through: default: j = b_tag - a_tag; } -- cgit v1.2.3 From 76b8ea8ab1eb4ce099f88ccb8d1721c438d0ada4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 12 Dec 2013 10:58:59 +0100 Subject: erts: Add BIF erts_internal:cmp_term/2 Compares terms where integer() < float(). --- erts/emulator/beam/bif.c | 11 +++++++++++ erts/emulator/beam/bif.tab | 2 ++ erts/preloaded/src/erts_internal.erl | 10 ++++++++++ 3 files changed, 23 insertions(+) diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 61c1abedb5..9c4801041f 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -4615,6 +4615,17 @@ BIF_RETTYPE bump_reductions_1(BIF_ALIST_1) BIF_RET2(am_true, reds); } +BIF_RETTYPE erts_internal_cmp_term_2(BIF_ALIST_2) { + int res = CMP_TERM(BIF_ARG_1,BIF_ARG_2); + + /* ensure -1, 0, 1 result */ + if (res < 0) { + BIF_RET(make_small(-1)); + } else if (res > 0) { + BIF_RET(make_small(1)); + } + BIF_RET(make_small(0)); +} /* * Processes doing yield on return in a bif ends up in bif_return_trap(). */ diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index b623e47b9a..2d888862bf 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -593,6 +593,8 @@ bif maps:remove/2 bif maps:update/3 bif maps:values/1 +bif erts_internal:cmp_term/2 + # # Obsolete # diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index d6a185482e..88eb317f1d 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -170,3 +170,13 @@ binary_to_term(_Binary) -> Opts :: [safe]. binary_to_term(_Binary, _Opts) -> erlang:nif_error(undefined). + +%% term compare where integer() < float() = true + +-spec cmp_term(A,B) -> Result when + A :: term(), + B :: term(), + Result :: -1 | 0 | 1. + +cmp_term(_A,_B) -> + erlang:nif_error(undefined). -- cgit v1.2.3 From 862728a458729f4a71630f4a8fa93f1f26744c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 12 Dec 2013 11:15:20 +0100 Subject: erts: Update maps BIFs to use term order Maps internally uses term order to store keys in an ordered fashion. --- erts/emulator/beam/erl_map.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 98aeee634b..455ba25e11 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -108,7 +108,7 @@ BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) { } /* maps:find/2 - * return value if key *equals* a key in the map + * return value if key *matches* a key in the map */ int erts_maps_find(Eterm key, Eterm map, Eterm *value) { @@ -123,7 +123,7 @@ int erts_maps_find(Eterm key, Eterm map, Eterm *value) { vs = map_get_values(mp); for( i = 0; i < n; i++) { - if (CMP(ks[i], key)==0) { + if (EQ(ks[i], key)) { *value = vs[i]; return 1; } @@ -178,7 +178,7 @@ int erts_maps_get(Eterm key, Eterm map, Eterm *value) { } for( i = 0; i < n; i++) { - if (eq(ks[i], key)) { + if (EQ(ks[i], key)) { *value = vs[i]; return 1; } @@ -283,7 +283,7 @@ BIF_RETTYPE maps_from_list_1(BIF_ALIST_1) { idx = size; - while(idx > 0 && (c = CMP(kv[1],ks[idx-1])) < 0) { idx--; } + while(idx > 0 && (c = CMP_TERM(kv[1],ks[idx-1])) < 0) { idx--; } if (c == 0) { /* last compare was equal, @@ -353,7 +353,7 @@ BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) { } for( i = 0; i < n; i++) { - if (eq(ks[i], key)) { + if (EQ(ks[i], key)) { BIF_RET(am_true); } } @@ -425,7 +425,7 @@ BIF_RETTYPE maps_merge_2(BIF_ALIST_2) { vs2 = map_get_values(mp2); while(i1 < n1 && i2 < n2) { - c = CMP(ks1[i1],ks2[i2]); + c = CMP_TERM(ks1[i1],ks2[i2]); if ( c == 0) { /* use righthand side arguments map value, * but advance both maps */ @@ -546,7 +546,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { } } else { for( i = 0; i < n; i ++) { - if (eq(ks[i], key)) { + if (EQ(ks[i], key)) { *hp++ = value; vs++; c = 1; @@ -577,7 +577,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { ASSERT(n >= 0); /* copy map in order */ - while (n && ((c = CMP(*ks, key)) < 0)) { + while (n && ((c = CMP_TERM(*ks, key)) < 0)) { *shp++ = *ks++; *hp++ = *vs++; n--; @@ -658,7 +658,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { } } else { while(n--) { - if (eq(*ks, key)) { + if (EQ(*ks, key)) { ks++; vs++; found = 1; @@ -732,7 +732,7 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) } } else { for( i = 0; i < n; i ++) { - if (eq(ks[i], key)) { + if (EQ(ks[i], key)) { *hp++ = value; vs++; found = 1; -- cgit v1.2.3 From f3821597383fa20d1093dab70fa75b4a1018a6b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 12 Dec 2013 11:20:04 +0100 Subject: erts: Update maps instructions to use term order Maps internally uses term order to store keys in an ordered fashion. --- erts/emulator/beam/beam_emu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index fe2e196785..89d9442526 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6316,7 +6316,7 @@ static int has_not_map_field(Eterm map, Eterm key) } } else { for (i = 0; i < n; i++) { - if (eq(keys[i], key)) { + if (EQ(keys[i], key)) { return 0; } } @@ -6343,7 +6343,7 @@ static Eterm get_map_element(Eterm map, Eterm key) } } else { for (i = 0; i < n; i++) { - if (eq(ks[i], key)) { + if (EQ(ks[i], key)) { return vs[i]; } } @@ -6506,7 +6506,7 @@ update_map_assoc(Process* p, Eterm* reg, Eterm map, BeamInstr* I) ASSERT(kp < (Eterm *)mp); key = *old_keys; - if ((c = CMP(key, new_key)) < 0) { + if ((c = CMP_TERM(key, new_key)) < 0) { /* Copy old key and value */ *kp++ = key; *hp++ = *old_vals; -- cgit v1.2.3 From c334d4841d9d600237184233518ee13f7421f91c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 10 Jan 2014 18:04:00 +0100 Subject: erts: Update maps serializing to use term order * erlang:term_to_binary/1 * erlang:binary_to_term/1 --- erts/emulator/beam/external.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index a31b2731be..e8b77b9f37 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3803,7 +3803,7 @@ dec_term_atom_common: arity = arityval(*keys++); while(arity-- > 1) { - if (CMP(keys[arity-1],keys[arity]) >= 0) { + if (CMP_TERM(keys[arity-1],keys[arity]) >= 0) { goto error; } } -- cgit v1.2.3 From 0422247a9d8259ef1a28704500f07caf827f42f6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 16 Jan 2014 22:00:04 +0100 Subject: erts: Fix bug in erts_maps_remove HRelease was called with wrong arguments and left garbage on heap when key was not found. --- erts/emulator/beam/erl_map.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 455ba25e11..e68482be58 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2013. All Rights Reserved. + * Copyright Ericsson AB 2014. 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 @@ -614,6 +614,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { Sint n; Sint found = 0; Uint need; + Eterm *hp_start; Eterm *thp, *mhp; Eterm *ks, *vs, tup; map_t *mp = (map_t*)map_val(map); @@ -634,7 +635,8 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { */ need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */ - thp = HAlloc(p, need); + hp_start = HAlloc(p, need); + thp = hp_start; mhp = thp + n; /* offset with tuple heap size */ tup = make_tuple(thp); @@ -676,7 +678,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { /* Not found, remove allocated memory * and return previous map. */ - HRelease(p, thp + need, thp); + HRelease(p, hp_start + need, hp_start); *res = map; return 1; -- cgit v1.2.3 From f1003b26524c2321b1eef36fb2d3997aaf0b9e10 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 16 Jan 2014 23:03:33 +0100 Subject: erts: Optimize erts_map_update/remove to not continue comparing keys once it has been found. --- erts/emulator/beam/erl_map.c | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index e68482be58..57f156509c 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -612,7 +612,6 @@ BIF_RETTYPE maps_put_3(BIF_ALIST_3) { int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { Sint n; - Sint found = 0; Uint need; Eterm *hp_start; Eterm *thp, *mhp; @@ -650,9 +649,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { if (is_immed(key)) { while(n--) { if (*ks == key) { - ks++; - vs++; - found = 1; + goto found_key; } else { *mhp++ = *vs++; *thp++ = *ks++; @@ -661,9 +658,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { } else { while(n--) { if (EQ(*ks, key)) { - ks++; - vs++; - found = 1; + goto found_key; } else { *mhp++ = *vs++; *thp++ = *ks++; @@ -671,10 +666,6 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { } } - if (found) { - return 1; - } - /* Not found, remove allocated memory * and return previous map. */ @@ -682,6 +673,14 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { *res = map; return 1; + +found_key: + /* Copy rest of keys and values */ + if (n) { + sys_memcpy(mhp, vs+1, n*sizeof(Eterm)); + sys_memcpy(thp, ks+1, n*sizeof(Eterm)); + } + return 1; } BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { @@ -699,7 +698,6 @@ BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) { Sint n,i; - Sint found = 0; Eterm* hp,*shp; Eterm *ks,*vs; map_t *mp = (map_t*)map_val(map); @@ -717,7 +715,6 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) hp = HAlloc(p, MAP_HEADER_SIZE + n); shp = hp; - *res = make_map(hp); *hp++ = MAP_HEADER; *hp++ = n; *hp++ = mp->keys; @@ -725,9 +722,7 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) if (is_immed(key)) { for( i = 0; i < n; i ++) { if (ks[i] == key) { - *hp++ = value; - vs++; - found = 1; + goto found_key; } else { *hp++ = *vs++; } @@ -735,20 +730,23 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) } else { for( i = 0; i < n; i ++) { if (EQ(ks[i], key)) { - *hp++ = value; - vs++; - found = 1; + goto found_key; } else { *hp++ = *vs++; } } } - if (found) { - return 1; - } HRelease(p, shp + MAP_HEADER_SIZE + n, shp); return 0; + +found_key: + *hp++ = value; + vs++; + if (++i < n) + sys_memcpy(hp, vs, (n - i)*sizeof(Eterm)); + *res = make_map(shp); + return 1; } BIF_RETTYPE maps_update_3(BIF_ALIST_3) { -- 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(-) 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 4db2458bba34884448d3b3752dce74c17d92c698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 12 Dec 2013 13:24:20 +0100 Subject: Update map_SUITE to respect term order --- erts/emulator/test/map_SUITE.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index a06cb43ac7..081e1b852e 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -228,7 +228,7 @@ t_update_assoc(Config) when is_list(Config) -> M1 = M0#{1=>42,2=>100,4=>[a,b,c]}, #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1, - M1 = M0#{1.0=>wrong,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]}, + #{1:=42,2:=b,4:=d,5:=e,2.0:=100,3.0:=c,4.0:=[a,b,c]} = M0#{1.0=>float,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]}, M2 = M0#{3.0=>new}, #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2, @@ -462,12 +462,10 @@ t_bif_map_find(Config) when is_list(Config) -> {ok, 1} = maps:find(a, #{ a=> 1}), {ok, 2} = maps:find(b, #{ a=> 1, b => 2}), {ok, "int"} = maps:find(1, #{ 1 => "int"}), - {ok, "int"} = maps:find(1.0, #{ 1 => "int"}), - {ok, "float"} = maps:find(1, #{ 1.0 => "float"}), {ok, "float"} = maps:find(1.0, #{ 1.0=> "float"}), {ok, "hi"} = maps:find("hello", #{ a=>1, "hello" => "hi"}), - {ok, "tuple hi"} = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key + {ok, "tuple hi"} = maps:find({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}), M = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }), {ok, "v4"} = maps:find(<<"k2">>, M#{ <<"k2">> => "v4" }), @@ -475,6 +473,10 @@ t_bif_map_find(Config) when is_list(Config) -> %% error case error = maps:find(a,#{}), error = maps:find(a,#{b=>1, c=>2}), + error = maps:find(1.0, #{ 1 => "int"}), + error = maps:find(1, #{ 1.0 => "float"}), + error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,[])), {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,<<>>)), @@ -737,7 +739,7 @@ t_map_encode_decode(Config) when is_list(Config) -> map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) -> M1 = maps:put(K,V,M0), B0 = erlang:term_to_binary(M1), - Ls = lists:sort([{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]), + Ls = lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, [{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]), %% sort Ks and Vs according to term spec, then match it ok = match_encoded_map(B0, length(Ls), [Kbin||{_,Kbin,_}<-Ls] ++ [Vbin||{_,_,Vbin}<-Ls]), %% decode and match it -- cgit v1.2.3 From 39c35199f5118a59f337b695a934c6bfcbf0813b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 13 Jan 2014 16:23:56 +0100 Subject: erts: Let erlang:binary_to_term/1 handle unsorted Maps Maps may be encoded with keys in arbitrary order. This is fine, as long as keys are unique. --- erts/emulator/beam/external.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index e8b77b9f37..57251286c8 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3790,24 +3790,41 @@ dec_term_atom_common: } } - /* Iterate through all the maps and check for validity + /* Iterate through all the maps and check for validity and sort keys * - done here for when we know it is complete. */ while (maps_head) { - Eterm *keys; - Sint arity; + map_t *mp = (map_t*)maps_head; + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + Uint sz = map_get_size(mp); + Uint ix,jx; + Eterm tmp; + int c; next = (Eterm *)(EXPAND_POINTER(*maps_head)); - keys = tuple_val(*(maps_head + 2)); - arity = arityval(*keys++); - while(arity-- > 1) { - if (CMP_TERM(keys[arity-1],keys[arity]) >= 0) { - goto error; + /* sort */ + + for ( ix = 1; ix < sz; ix++) { + jx = ix; + while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) { + /* identical key -> error */ + if (c == 0) goto error; + + tmp = ks[jx]; + ks[jx] = ks[jx - 1]; + ks[jx - 1] = tmp; + + tmp = vs[jx]; + vs[jx] = vs[jx - 1]; + vs[jx - 1] = tmp; + + jx--; } - } + } *maps_head = MAP_HEADER; maps_head = next; } -- cgit v1.2.3 From 228ee23186639c8b66be8ed26def7c8e9cb09059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 13 Jan 2014 16:26:23 +0100 Subject: Update map_SUITE to test "unsorted" encoded maps --- erts/emulator/test/map_SUITE.erl | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 081e1b852e..75381de556 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -711,19 +711,32 @@ t_map_encode_decode(Config) when is_list(Config) -> ], ok = map_encode_decode_and_match(Pairs,[],#{}), + %% check sorting + + %% literally #{ b=>2, a=>1 } in the internal order + #{ a:=1, b:=2 } = + erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,2,97,1>>), + + + %% literally #{ "hi" => "value", a=>33, b=>55 } in the internal order + #{ a:=33, b:=55, "hi" := "value"} = erlang:binary_to_term(<<131,116,0,0,0,3, + 107,0,2,104,105, % "hi" :: list() + 100,0,1,97, % a :: atom() + 100,0,1,98, % b :: atom() + 107,0,5,118,97,108,117,101, % "value" :: list() + 97,33, % 33 :: integer() + 97,55 % 55 :: integer() + >>), + + %% error cases %% template: <<131,116,0,0,0,2,100,0,1,97,100,0,1,98,97,1,97,1>> %% which is: #{ a=>1, b=>1 } - %% order violation - %% literally #{ b=>1, a=>1 } in the internal order (bad) - {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch - erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,1,97,1>>)), - %% uniqueness violation - %% literally #{ a=>1, a=>1 } + %% literally #{ a=>1, "hi"=>"value", a=>2 } {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch - erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,97,100,0,1,97,97,1,97,1>>)), + erlang:binary_to_term(<<131,116,0,0,0,3,100,0,1,97,107,0,2,104,105,100,0,1,97,97,1,107,0,5,118,97,108,117,101,97,2>>)), %% bad size (too large) {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch -- cgit v1.2.3 From c8ecb6962a923ceb0b7e599d22adef773d042e4a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 13 Jan 2014 22:15:13 +0100 Subject: erts: Remove enif_find_map_value as it does the same thing as enif_get_map_value. Replace with placeholder to be ABI backward compatible on Windows as long as enif_find_map_value is not called. --- erts/emulator/beam/erl_nif.c | 13 +------------ erts/emulator/beam/erl_nif_api_funcs.h | 5 ++--- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index c683847aaa..1a539c730f 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. 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 @@ -1663,17 +1663,6 @@ int enif_get_map_value(ErlNifEnv* env, return erts_maps_get(key, map, value); } -int enif_find_map_value(ErlNifEnv* env, - Eterm map, - Eterm key, - Eterm *value) -{ - if (is_not_map(map)) { - return 0; - } - return erts_maps_get(key, map, value); -} - int enif_make_map_update(ErlNifEnv* env, Eterm map_in, Eterm key, diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 4a5aacad48..71abb5dc8b 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. 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 @@ -154,7 +154,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); -ERL_NIF_API_FUNC_DECL(int, enif_find_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); +ERL_NIF_API_FUNC_DECL(void, __enif_PLACEHOLDER__, (void)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_update, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_remove, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_create, (ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)); @@ -303,7 +303,6 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa # define enif_make_new_map ERL_NIF_API_FUNC_MACRO(enif_make_new_map) # define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_map_map_put) # define enif_get_map_value ERL_NIF_API_FUNC_MACRO(enif_get_map_value) -# define enif_find_map_value ERL_NIF_API_FUNC_MACRO(enif_find_map_value) # define enif_make_map_update ERL_NIF_API_FUNC_MACRO(enif_make_map_update) # define enif_make_map_remove ERL_NIF_API_FUNC_MACRO(enif_make_map_remove) # define enif_map_iterator_create ERL_NIF_API_FUNC_MACRO(enif_map_iterator_create) -- cgit v1.2.3 From b8a7203f04a1ed6d2756e6b782cd4c95a8c7c491 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 18:22:34 +0100 Subject: erts: Increase version for NIF API and reject experimental v2.5 --- erts/emulator/beam/erl_nif.c | 3 ++- erts/emulator/beam/erl_nif.h | 9 ++++----- erts/emulator/beam/erl_nif_api_funcs.h | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 1a539c730f..1eca7822eb 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1996,7 +1996,8 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) ret = load_nif_error(BIF_P, bad_lib, "Library init-call unsuccessful"); } else if (entry->major != ERL_NIF_MAJOR_VERSION - || entry->minor > ERL_NIF_MINOR_VERSION) { + || entry->minor > ERL_NIF_MINOR_VERSION + || (entry->major==2 && entry->minor == 5)) { /* experimental maps */ ret = load_nif_error(BIF_P, bad_lib, "Library version (%d.%d) not compatible (with %d.%d).", entry->major, entry->minor, ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION); diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 3c1e13f8a4..ae7f63f923 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -38,11 +38,13 @@ ** 2.2: R14B03 enif_is_exception ** 2.3: R15 enif_make_reverse_list, enif_is_number ** 2.4: R16 enif_consume_timeslice +** 2.5: First experimental maps API additions (libs of this version is not compatible with any other VM) ** 2.5: R17 Maps API additions +** 2.6: R17 with maps ** R17 dirty schedulers */ #define ERL_NIF_MAJOR_VERSION 2 -#define ERL_NIF_MINOR_VERSION 5 +#define ERL_NIF_MINOR_VERSION 6 #include @@ -165,7 +167,6 @@ typedef int ErlNifTSDKey; typedef ErlDrvThreadOpts ErlNifThreadOpts; -<<<<<<< HEAD #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT typedef enum { @@ -173,7 +174,7 @@ typedef enum ERL_NIF_DIRTY_JOB_IO_BOUND = 2 }ErlNifDirtyTaskFlags; #endif -======= + typedef struct { /* use a lot of memory, structure may change */ @@ -190,8 +191,6 @@ typedef enum { ERL_NIF_MAP_ITERATOR_TAIL = 2 } ErlNifMapIteratorEntry; ->>>>>>> erts: Add NIFs for Maps - #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) # define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS typedef struct { diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 71abb5dc8b..9c386a4c41 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -154,7 +154,6 @@ ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); -ERL_NIF_API_FUNC_DECL(void, __enif_PLACEHOLDER__, (void)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_update, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_remove, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_create, (ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)); -- cgit v1.2.3 From f321ea89fecdb343e58e2485c057326d08fed69c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 13 Jan 2014 22:16:40 +0100 Subject: erts: Do not allow map iterator created without map --- erts/emulator/beam/erl_nif.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 1eca7822eb..ca82590a78 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1725,21 +1725,27 @@ int enif_map_iterator_create(ErlNifEnv *env, } error: +#ifdef DEBUG iter->map = THE_NON_VALUE; +#endif return 0; } void enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter) { /* not used */ +#ifdef DEBUG + iter->map = THE_NON_VALUE; +#endif + } int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) { + ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if (is_map(iter->map) && ( - (iter->t_limit - iter->h_limit) == 1 || - iter->idx == iter->t_limit)) { + if ((iter->t_limit - iter->h_limit) == 1 + || iter->idx == iter->t_limit) { return 1; } return 0; @@ -1747,10 +1753,10 @@ int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) { + ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if (is_map(iter->map) && ( - (iter->t_limit - iter->h_limit) == 1 || - iter->idx == iter->h_limit)) { + if ((iter->t_limit - iter->h_limit) == 1 + || iter->idx == iter->h_limit) { return 1; } return 0; @@ -1759,7 +1765,8 @@ int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) { - if (is_map(iter->map) && iter->idx < iter->t_limit) { + ASSERT(iter && is_map(iter->map)); + if (iter->idx < iter->t_limit) { iter->idx++; if (iter->idx != iter->t_limit) { iter->ks++; @@ -1772,7 +1779,8 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) { - if (is_map(iter->map) && iter->idx > iter->h_limit ) { + ASSERT(iter && is_map(iter->map)); + if (iter->idx > iter->h_limit ) { iter->idx--; if (iter->idx != iter->h_limit ) { iter->ks--; @@ -1788,7 +1796,8 @@ int enif_map_iterator_get_pair(ErlNifEnv *env, Eterm *key, Eterm *value) { - if (is_map(iter->map) && iter->idx > iter->h_limit && iter->idx < iter->t_limit) { + ASSERT(iter && is_map(iter->map)); + if (iter->idx > iter->h_limit && iter->idx < iter->t_limit) { ASSERT(iter->ks >= map_get_keys(map_val(iter->map)) && iter->ks < (map_get_keys(map_val(iter->map)) + map_get_size(map_val(iter->map)))); ASSERT(iter->vs >= map_get_values(map_val(iter->map)) && -- cgit v1.2.3 From 609d835ce04a1f6320377780a11fb32382e1e419 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 11:25:34 +0100 Subject: erts: Let enif_map_iterator_next/prev return 0 to signal end of map. --- erts/emulator/beam/erl_nif.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index ca82590a78..def9a7efe1 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1771,8 +1771,8 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) if (iter->idx != iter->t_limit) { iter->ks++; iter->vs++; + return 1; } - return 1; } return 0; } @@ -1785,8 +1785,8 @@ int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) if (iter->idx != iter->h_limit ) { iter->ks--; iter->vs--; + return 1; } - return 1; } return 0; } -- cgit v1.2.3 From d39affeac223a7e2891b509395e7b19c931b5018 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 20:32:46 +0100 Subject: erts: Remove use of h_limit which is always zero. --- erts/emulator/beam/erl_nif.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index def9a7efe1..94c2512650 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1718,7 +1718,6 @@ int enif_map_iterator_create(ErlNifEnv *env, iter->ks = ((Eterm *)map_get_keys(mp)) + offset; iter->vs = ((Eterm *)map_get_values(mp)) + offset; iter->t_limit = map_get_size(mp) + 1; - iter->h_limit = 0; iter->idx = offset + 1; return 1; @@ -1744,8 +1743,7 @@ int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) { ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if ((iter->t_limit - iter->h_limit) == 1 - || iter->idx == iter->t_limit) { + if (iter->t_limit == 1 || iter->idx == iter->t_limit) { return 1; } return 0; @@ -1755,8 +1753,7 @@ int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) { ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if ((iter->t_limit - iter->h_limit) == 1 - || iter->idx == iter->h_limit) { + if (iter->t_limit == 1 || iter->idx == 0) { return 1; } return 0; @@ -1780,9 +1777,9 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) { ASSERT(iter && is_map(iter->map)); - if (iter->idx > iter->h_limit ) { + if (iter->idx > 0) { iter->idx--; - if (iter->idx != iter->h_limit ) { + if (iter->idx != 0) { iter->ks--; iter->vs--; return 1; @@ -1797,7 +1794,7 @@ int enif_map_iterator_get_pair(ErlNifEnv *env, Eterm *value) { ASSERT(iter && is_map(iter->map)); - if (iter->idx > iter->h_limit && iter->idx < iter->t_limit) { + if (iter->idx > 0 && iter->idx < iter->t_limit) { ASSERT(iter->ks >= map_get_keys(map_val(iter->map)) && iter->ks < (map_get_keys(map_val(iter->map)) + map_get_size(map_val(iter->map)))); ASSERT(iter->vs >= map_get_values(map_val(iter->map)) && -- cgit v1.2.3 From ada36d9024b4ceda974f32a78b5fe39b64f59319 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 20:55:10 +0100 Subject: erts: Simplify some map iterator code --- erts/emulator/beam/erl_nif.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 94c2512650..c6f7c8adb5 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1743,20 +1743,14 @@ int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter) { ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if (iter->t_limit == 1 || iter->idx == iter->t_limit) { - return 1; - } - return 0; + return (iter->t_limit == 1 || iter->idx == iter->t_limit); } int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter) { ASSERT(iter && is_map(iter->map)); ASSERT(iter->idx >= 0 && (iter->idx <= map_get_size(map_val(iter->map)) + 1)); - if (iter->t_limit == 1 || iter->idx == 0) { - return 1; - } - return 0; + return (iter->t_limit == 1 || iter->idx == 0); } -- cgit v1.2.3 From 00f9be42e43913bce9b110382e55bfbdaa9406d0 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 18:13:45 +0100 Subject: erts: Fix map iterator bug when reverting from end of map position and simplify code by ignoring h_limit which is always zero. --- erts/emulator/beam/erl_nif.c | 18 +++------ erts/emulator/test/nif_SUITE.erl | 5 ++- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 57 ++++++++++++++++++++------- 3 files changed, 53 insertions(+), 27 deletions(-) diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index c6f7c8adb5..6fceb8a0ba 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1759,13 +1759,10 @@ int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter) ASSERT(iter && is_map(iter->map)); if (iter->idx < iter->t_limit) { iter->idx++; - if (iter->idx != iter->t_limit) { - iter->ks++; - iter->vs++; - return 1; - } + iter->ks++; + iter->vs++; } - return 0; + return (iter->idx != iter->t_limit); } int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) @@ -1773,13 +1770,10 @@ int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter) ASSERT(iter && is_map(iter->map)); if (iter->idx > 0) { iter->idx--; - if (iter->idx != 0) { - iter->ks--; - iter->vs--; - return 1; - } + iter->ks--; + iter->vs--; } - return 0; + return (iter->idx > 0); } int enif_map_iterator_get_pair(ErlNifEnv *env, diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 5b7c310aa4..a34f70c618 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -449,6 +449,9 @@ maps(Config) when is_list(Config) -> io:format("Pairs: ~p~nMap: ~p~nReturned: ~p~n", [lists:sort(Pairs),M,R]), Is = lists:sort(Pairs), Is = lists:reverse(RIs), + + #{} = maps_from_list([]), + {[],[]} = sorted_list_from_maps(#{}), ok. api_macros(doc) -> ["Test macros enif_make_list and enif_make_tuple"]; diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index a0316a7861..8549d277de 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. 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 @@ -1564,31 +1564,60 @@ static ERL_NIF_TERM sorted_list_from_maps(ErlNifEnv* env, int argc, const ERL_NI ERL_NIF_TERM map = argv[0]; ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */ ERL_NIF_TERM list_b = enif_make_list(env, 0); /* NIL */ - ERL_NIF_TERM key, value; - ErlNifMapIterator iter; + ERL_NIF_TERM key, value, k2, v2; + ErlNifMapIterator iter_f; + ErlNifMapIterator iter_b; + int cnt, next_ret, prev_ret; if (argc != 1 && !enif_is_map(env, map)) - return enif_make_badarg(env); + return enif_make_int(env, __LINE__); - if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_HEAD)) - return enif_make_badarg(env); + if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_HEAD)) + return enif_make_int(env, __LINE__); - while(enif_map_iterator_get_pair(env,&iter,&key,&value)) { + cnt = 0; + while(enif_map_iterator_get_pair(env,&iter_f,&key,&value)) { + if (cnt && !next_ret) + return enif_make_int(env, __LINE__); list_f = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_f); - enif_map_iterator_next(env,&iter); + next_ret = enif_map_iterator_next(env,&iter_f); + cnt++; } + if (cnt && next_ret) + return enif_make_int(env, __LINE__); - enif_map_iterator_destroy(env, &iter); + if(!enif_map_iterator_create(env, map, &iter_b, ERL_NIF_MAP_ITERATOR_TAIL)) + return enif_make_int(env, __LINE__); - if(!enif_map_iterator_create(env, map, &iter, ERL_NIF_MAP_ITERATOR_TAIL)) - return enif_make_badarg(env); + cnt = 0; + while(enif_map_iterator_get_pair(env,&iter_b,&key,&value)) { + if (cnt && !prev_ret) + return enif_make_int(env, __LINE__); + + /* Test that iter_f can step "backwards" */ + if (!enif_map_iterator_prev(env,&iter_f) + || !enif_map_iterator_get_pair(env,&iter_f,&k2,&v2) + || k2 != key || v2 != value) { + return enif_make_int(env, __LINE__); + } - while(enif_map_iterator_get_pair(env,&iter,&key,&value)) { list_b = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_b); - enif_map_iterator_prev(env,&iter); + prev_ret = enif_map_iterator_prev(env,&iter_b); + } + + if (cnt) { + if (prev_ret || enif_map_iterator_prev(env,&iter_f)) + return enif_make_int(env, __LINE__); + + /* Test that iter_b can step "backwards" one step */ + if (!enif_map_iterator_next(env, &iter_b) + || !enif_map_iterator_get_pair(env,&iter_b,&k2,&v2) + || k2 != key || v2 != value) + return enif_make_int(env, __LINE__); } - enif_map_iterator_destroy(env, &iter); + enif_map_iterator_destroy(env, &iter_f); + enif_map_iterator_destroy(env, &iter_b); return enif_make_tuple2(env, list_f, list_b); } -- cgit v1.2.3 From a18853e6814ab42188e56343e6363ff94c794bb8 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Jan 2014 18:21:26 +0100 Subject: erts: Optimize struct ErlNifMapIterator No need to use 64bit integers on 32bit machines. --- erts/emulator/beam/erl_nif.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index ae7f63f923..7613446f64 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2014. 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 @@ -103,6 +103,8 @@ typedef unsigned long long ERL_NIF_TERM; # endif #endif +typedef ERL_NIF_TERM ERL_NIF_UINT; + struct enif_environment_t; typedef struct enif_environment_t ErlNifEnv; @@ -175,15 +177,14 @@ typedef enum }ErlNifDirtyTaskFlags; #endif -typedef struct +typedef struct /* All fields all internal and may change */ { - /* use a lot of memory, structure may change */ ERL_NIF_TERM map; - ErlNifUInt64 h_limit; - ErlNifUInt64 t_limit; - ErlNifUInt64 idx; + ERL_NIF_UINT t_limit; + ERL_NIF_UINT idx; ERL_NIF_TERM *ks; ERL_NIF_TERM *vs; + void* __spare__[2]; /* for future additions to be ABI compatible (same struct size) */ } ErlNifMapIterator; typedef enum { -- cgit v1.2.3 From e21989fdc834c9c9e75454bd06cb6ef29218bf9f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 16 Jan 2014 23:07:01 +0100 Subject: erts: Add more tests for the NIF map API --- erts/emulator/test/nif_SUITE.erl | 49 +++++++++++++++++++++--- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 55 +++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 10 deletions(-) diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index a34f70c618..bcc1f9e5af 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -444,14 +444,44 @@ maps(Config) when is_list(Config) -> [{I,I}||I <- lists:seq(1,10)] ++ [{a,value},{"a","value"},{<<"a">>,<<"value">>}], ok = ensure_lib_loaded(Config, 1), - M = maps_from_list(Pairs), - R = {RIs,Is} = sorted_list_from_maps(M), + M = maps_from_list_nif(Pairs), + R = {RIs,Is} = sorted_list_from_maps_nif(M), io:format("Pairs: ~p~nMap: ~p~nReturned: ~p~n", [lists:sort(Pairs),M,R]), Is = lists:sort(Pairs), Is = lists:reverse(RIs), - #{} = maps_from_list([]), - {[],[]} = sorted_list_from_maps(#{}), + #{} = maps_from_list_nif([]), + {[],[]} = sorted_list_from_maps_nif(#{}), + + 1 = is_map_nif(M), + 0 = is_map_nif("no map"), + + Msz = map_size(M), + {1,Msz} = get_map_size_nif(M), + {1,0} = get_map_size_nif(#{}), + {0,-123} = get_map_size_nif({#{}}), + + #{} = M0 = make_new_map_nif(), + + {1, #{key := value}=M1} = make_map_put_nif(M0, key, value), + {1, #{key := value, "key2" := "value2"}=M2} = make_map_put_nif(M1, "key2", "value2"), + {1, #{key := "value", "key2" := "value2"}=M3} = make_map_put_nif(M2, key, "value"), + {0, undefined} = make_map_put_nif(666, key, value), + + {1, "value2"} = get_map_value_nif(M3,"key2"), + {0, undefined} = get_map_value_nif(M3,"key3"), + {0, undefined} = get_map_value_nif(false,key), + + {0, undefined} = make_map_update_nif(M0, key, value), + {0, undefined} = make_map_update_nif(M1, "key2", "value2"), + {1, #{key := "value", "key2" := "value2"}} = make_map_update_nif(M2, key, "value"), + {0, undefined} = make_map_update_nif(666, key, value), + + {1, #{}} = make_map_remove_nif(M1, key), + {1, M1} = make_map_remove_nif(M2, "key2"), + {1, M2} = make_map_remove_nif(M2, "key3"), + {0, undefined} = make_map_remove_nif(self(), key), + ok. api_macros(doc) -> ["Test macros enif_make_list and enif_make_tuple"]; @@ -1508,8 +1538,15 @@ consume_timeslice_nif(_,_) -> ?nif_stub. call_dirty_nif(_,_,_) -> ?nif_stub. %% maps -maps_from_list(_) -> ?nif_stub. -sorted_list_from_maps(_) -> ?nif_stub. +is_map_nif(_) -> ?nif_stub. +get_map_size_nif(_) -> ?nif_stub. +make_new_map_nif() -> ?nif_stub. +make_map_put_nif(_,_,_) -> ?nif_stub. +get_map_value_nif(_,_) -> ?nif_stub. +make_map_update_nif(_,_,_) -> ?nif_stub. +make_map_remove_nif(_,_) -> ?nif_stub. +maps_from_list_nif(_) -> ?nif_stub. +sorted_list_from_maps_nif(_) -> ?nif_stub. nif_stub_error(Line) -> diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 8549d277de..9ee89a49a4 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1536,8 +1536,48 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM } #endif +static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + return enif_make_int(env, enif_is_map(env,argv[0])); +} +static ERL_NIF_TERM get_map_size_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int size = -123; + int ret = enif_get_map_size(env, argv[0], &size); + return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, size)); +} +static ERL_NIF_TERM make_new_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + return enif_make_new_map(env); +} +static ERL_NIF_TERM make_map_put_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM map_out = enif_make_atom(env, "undefined"); + int ret = enif_make_map_put(env, argv[0], argv[1], argv[2], &map_out); + return enif_make_tuple2(env, enif_make_int(env,ret), map_out); +} +static ERL_NIF_TERM get_map_value_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM value = enif_make_atom(env, "undefined"); + int ret = enif_get_map_value(env, argv[0], argv[1], &value); + return enif_make_tuple2(env, enif_make_int(env,ret), value); + +} +static ERL_NIF_TERM make_map_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM map_out = enif_make_atom(env, "undefined"); + int ret = enif_make_map_update(env, argv[0], argv[1], argv[2], &map_out); + return enif_make_tuple2(env, enif_make_int(env,ret), map_out); +} +static ERL_NIF_TERM make_map_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM map_out = enif_make_atom(env, "undefined"); + int ret = enif_make_map_remove(env, argv[0], argv[1], &map_out); + return enif_make_tuple2(env, enif_make_int(env,ret), map_out); +} + /* maps */ -static ERL_NIF_TERM maps_from_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +static ERL_NIF_TERM maps_from_list_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM cell = argv[0]; ERL_NIF_TERM map = enif_make_new_map(env); @@ -1559,7 +1599,7 @@ static ERL_NIF_TERM maps_from_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM return map; } -static ERL_NIF_TERM sorted_list_from_maps(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM map = argv[0]; ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */ @@ -1675,8 +1715,15 @@ static ErlNifFunc nif_funcs[] = #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT {"call_dirty_nif", 3, call_dirty_nif}, #endif - {"maps_from_list", 1, maps_from_list}, - {"sorted_list_from_maps", 1, sorted_list_from_maps} + {"is_map_nif", 1, is_map_nif}, + {"get_map_size_nif", 1, get_map_size_nif}, + {"make_new_map_nif", 0, make_new_map_nif}, + {"make_map_put_nif", 3, make_map_put_nif}, + {"get_map_value_nif", 2, get_map_value_nif}, + {"make_map_update_nif", 3, make_map_update_nif}, + {"make_map_remove_nif", 2, make_map_remove_nif}, + {"maps_from_list_nif", 1, maps_from_list_nif}, + {"sorted_list_from_maps_nif", 1, sorted_list_from_maps_nif} }; ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload) -- cgit v1.2.3 From 9eddc8eb4c7f4ef45b51650750334a787c5f9ef7 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 16 Jan 2014 23:45:42 +0100 Subject: erts: Fix compile error for halfword emulator --- erts/emulator/beam/erl_utils.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index c6247c7246..5b81d814c6 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012-2013. All Rights Reserved. + * Copyright Ericsson AB 2012-2014. 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 @@ -204,9 +204,9 @@ int eq(Eterm, Eterm); #if HALFWORD_HEAP Sint cmp_rel_opt(Eterm, Eterm*, Eterm, Eterm*, int); #define cmp_rel(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,0) -#define cmp_rel_exact(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,1) -#define CMP(A,B) cmp_rel(A,NULL,B,NULL,0) -#define CMP_TERM(A,B) cmp_rel(A,NULL,B,NULL,1) +#define cmp_rel_term(A,A_BASE,B,B_BASE) cmp_rel_opt(A,A_BASE,B,B_BASE,1) +#define CMP(A,B) cmp_rel_opt(A,NULL,B,NULL,0) +#define CMP_TERM(A,B) cmp_rel_opt(A,NULL,B,NULL,1) #else Sint cmp(Eterm, Eterm, int); #define cmp_rel(A,A_BASE,B,B_BASE) cmp(A,B,0) -- cgit v1.2.3 From f2b9a5623ec5c9a314560add82ef20a03ba91235 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 17 Jan 2014 12:32:42 +0100 Subject: erts: Change 'size' argument of enif_get_map_size from int* to size_t* --- erts/emulator/beam/erl_nif.c | 2 +- erts/emulator/beam/erl_nif_api_funcs.h | 2 +- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 6fceb8a0ba..c35f1fc2c6 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1610,7 +1610,7 @@ int enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term) return is_map(term); } -int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, int *size) +int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size) { if (is_map(term)) { map_t *mp; diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 9c386a4c41..4121378e1a 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -150,7 +150,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void)); #endif ERL_NIF_API_FUNC_DECL(int, enif_is_map, (ErlNifEnv* env, ERL_NIF_TERM term)); -ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term, int *size)); +ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term, size_t *size)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env)); ERL_NIF_API_FUNC_DECL(int, enif_make_map_put, (ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)); ERL_NIF_API_FUNC_DECL(int, enif_get_map_value, (ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)); diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 9ee89a49a4..b550d1f16d 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1542,9 +1542,9 @@ static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv } static ERL_NIF_TERM get_map_size_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - int size = -123; + size_t size = (size_t)-123; int ret = enif_get_map_size(env, argv[0], &size); - return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, size)); + return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, (int)size)); } static ERL_NIF_TERM make_new_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -- cgit v1.2.3 From 3645dc6b15d4fdd79768b92bb598c264005a8689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 14 Jan 2014 12:30:51 +0100 Subject: preloaded: Fixup export cmp_term in erts_internal --- erts/preloaded/src/erts_internal.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 88eb317f1d..edcd50c77e 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -30,6 +30,7 @@ -export([await_port_send_result/3]). -export([binary_to_term/1, binary_to_term/2]). +-export([cmp_term/2]). -export([port_command/3, port_connect/2, port_close/1, port_control/3, port_call/3, port_info/1, port_info/2]). -- 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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 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 bc34b5dafaebf07d7185900310246f5752c0a6c4 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 21 Jan 2014 12:21:11 +0100 Subject: erts: Add map construction to driver API erl_drv_output_term() and erl_drv_send_term() can send messages containing maps with the use of the new ERL_DRV_MAP. The driver API minor version is updated as new functionality is added. --- erts/doc/src/erl_driver.xml | 29 ++++++++- erts/emulator/beam/erl_driver.h | 4 +- erts/emulator/beam/erl_map.c | 31 ++++++++++ erts/emulator/beam/erl_map.h | 4 +- erts/emulator/beam/external.c | 31 +--------- erts/emulator/beam/io.c | 44 ++++++++++++- erts/emulator/test/send_term_SUITE.erl | 19 +++++- .../test/send_term_SUITE_data/send_term_drv.c | 72 ++++++++++++++++++++-- 8 files changed, 191 insertions(+), 43 deletions(-) diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index c2f7fa4588..710c9b19cf 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -4,7 +4,7 @@
- 20012013 + 20012014 Ericsson AB. All Rights Reserved. @@ -1742,15 +1742,19 @@ typedef struct ErlIOVec { term consists of one to four elements in the array. The term first has a term type, and then arguments. The port parameter specifies the sending port.

-

Tuple and lists (with the exception of strings, see below), +

Tuples, maps and lists (with the exception of strings, see below), are built in reverse polish notation, so that to build a tuple, the elements are given first, and then the tuple - term, with a count. Likewise for lists.

+ term, with a count. Likewise for lists and maps.

A tuple must be specified with the number of elements. (The elements precede the ERL_DRV_TUPLE term.)

A list must be specified with the number of elements, including the tail, which is the last term preceding ERL_DRV_LIST.

+

A map must be specified with the number of key-value pairs N. + The key-value pairs must precede the ERL_DRV_MAP in this order: + key1,value1,key2,value2,...,keyN,valueN. + Duplicate keys are not allowed.

The special term ERL_DRV_STRING_CONS is used to "splice" in a string in a list, a string given this way is not a list per se, but the elements are elements of the @@ -1774,6 +1778,7 @@ ERL_DRV_PID ErlDrvTermData pid (from driver_connected(ErlDrvPort port) ERL_DRV_STRING_CONS char *str, int len ERL_DRV_FLOAT double *dbl ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len +ERL_DRV_MAP int sz

The unsigned integer data type ErlDrvUInt and the signed integer data type ErlDrvSInt are 64 bits wide @@ -1856,6 +1861,24 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len }; erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]> + +

To build the map #{key1 => 100, key2 => {200, 300}}, the + following call could be made.

+ + +

If you want to pass a binary and don't already have the content of the binary in an ErlDrvBinary, you can benefit from using ERL_DRV_BUF2BINARY instead of creating an ErlDrvBinary diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 2bd3181bdc..ab9ee63104 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2013. All Rights Reserved. + * Copyright Ericsson AB 1999-2014. 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 @@ -605,6 +605,8 @@ EXTERN int null_func(void); #define ERL_DRV_INT64 ((ErlDrvTermData) 15) /* ErlDrvSInt64 * */ #define ERL_DRV_UINT64 ((ErlDrvTermData) 16) /* ErlDrvUInt64 * */ +#define ERL_DRV_MAP ((ErlDrvTermData) 17) /* ErlDrvUInt */ + #ifndef ERL_DRIVER_TYPES_ONLY /* make terms for driver_output_term and driver_send_term */ diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 57f156509c..2fff7f9390 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -786,3 +786,34 @@ BIF_RETTYPE maps_values_1(BIF_ALIST_1) { } BIF_ERROR(BIF_P, BADARG); } + +int erts_validate_and_sort_map(map_t* mp) +{ + Eterm *ks = map_get_keys(mp); + Eterm *vs = map_get_values(mp); + Uint sz = map_get_size(mp); + Uint ix,jx; + Eterm tmp; + int c; + + /* sort */ + + for (ix = 1; ix < sz; ix++) { + jx = ix; + while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) { + /* identical key -> error */ + if (c == 0) return 0; + + tmp = ks[jx]; + ks[jx] = ks[jx - 1]; + ks[jx - 1] = tmp; + + tmp = vs[jx]; + vs[jx] = vs[jx - 1]; + vs[jx - 1] = tmp; + + jx--; + } + } + return 1; +} diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index 616ecd24ce..cfacb2ec28 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2013. All Rights Reserved. + * Copyright Ericsson AB 2014. 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 @@ -67,6 +67,6 @@ int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res int erts_maps_find(Eterm key, Eterm map, Eterm *value); int erts_maps_get(Eterm key, Eterm map, Eterm *value); int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res); - +int erts_validate_and_sort_map(map_t* map); #endif diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 57251286c8..a4cc3435c3 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3795,37 +3795,10 @@ dec_term_atom_common: */ while (maps_head) { - map_t *mp = (map_t*)maps_head; - Eterm *ks = map_get_keys(mp); - Eterm *vs = map_get_values(mp); - Uint sz = map_get_size(mp); - Uint ix,jx; - Eterm tmp; - int c; - next = (Eterm *)(EXPAND_POINTER(*maps_head)); - - /* sort */ - - for ( ix = 1; ix < sz; ix++) { - jx = ix; - while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) { - /* identical key -> error */ - if (c == 0) goto error; - - tmp = ks[jx]; - ks[jx] = ks[jx - 1]; - ks[jx - 1] = tmp; - - tmp = vs[jx]; - vs[jx] = vs[jx - 1]; - vs[jx - 1] = tmp; - - jx--; - } - - } *maps_head = MAP_HEADER; + if (!erts_validate_and_sort_map((map_t*)maps_head)) + goto error; maps_head = next; } diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 49af86b36a..3b16cdeb4a 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2013. All Rights Reserved. + * Copyright Ericsson AB 1996-2014. 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 @@ -46,6 +46,7 @@ #define ERTS_WANT_EXTERNAL_TAGS #include "external.h" #include "dtrace-wrapper.h" +#include "erl_map.h" extern ErlDrvEntry fd_driver_entry; extern ErlDrvEntry vanilla_driver_entry; @@ -5293,6 +5294,17 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) depth++; break; } + case ERL_DRV_MAP: { /* int */ + ERTS_DDT_CHK_ENOUGH_ARGS(1); + if ((int) ptr[0] < 0) ERTS_DDT_FAIL; + need += MAP_HEADER_SIZE + 1 + 2*ptr[0]; + depth -= 2*ptr[0]; + if (depth < 0) ERTS_DDT_FAIL; + ptr++; + depth++; + break; + } + default: ERTS_DDT_FAIL; } @@ -5529,6 +5541,36 @@ driver_deliver_term(Eterm to, ErlDrvTermData* data, int len) ptr += 2; break; + case ERL_DRV_MAP: { /* int */ + int size = (int)ptr[0]; + Eterm* tp = hp; + Eterm* vp; + map_t *mp; + + *tp = make_arityval(size); + + hp += 1 + size; + mp = (map_t*)hp; + mp->thing_word = MAP_HEADER; + mp->size = size; + mp->keys = make_tuple(tp); + mess = make_map(mp); + + hp += MAP_HEADER_SIZE + size; /* advance "heap" pointer */ + + tp += size; /* point at last key */ + vp = hp - 1; /* point at last value */ + + while(size--) { + *vp-- = ESTACK_POP(stack); + *tp-- = ESTACK_POP(stack); + } + if (!erts_validate_and_sort_map(mp)) + ERTS_DDT_FAIL; + ptr++; + break; + } + } ESTACK_PUSH(stack, mess); } diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl index b631f55a03..8e1f8df43a 100644 --- a/erts/emulator/test/send_term_SUITE.erl +++ b/erts/emulator/test/send_term_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2013. All Rights Reserved. +%% Copyright Ericsson AB 2005-2014. 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 @@ -62,7 +62,19 @@ basic(Config) when is_list(Config) -> ?line [] = term(P, 0), ?line Self = self(), - ?line {blurf,42,[],[-42,{}|"abc"++P],"kalle",3.1416,Self} = term(P, 1), + {blurf,42,[],[-42,{}|"abc"++P],"kalle",3.1416,Self,#{}} = term(P, 1), + + Map41 = maps:from_list([{blurf, 42}, + {[], [-42,{}|"abc"++P]}, + {"kalle", 3.1416}, + {Self, #{}}]), + Map41 = term(P, 41), + + Map42 = maps:from_list([{42, []}, + {[-42,{}|"abc"++P], "kalle"}, + {3.1416, Self}, + {#{}, blurf}]), + Map42 = term(P, 42), ?line Deep = lists:seq(0, 199), ?line Deep = term(P, 2), ?line {B1,B2} = term(P, 3), @@ -125,7 +137,8 @@ basic(Config) when is_list(Config) -> {-1, 36}, % ERL_DRV_INT64 {-4711, 37}, % ERL_DRV_INT64 {-20233590931456, 38}, % ERL_DRV_INT64 - {-9223372036854775808, 39}], % ERL_DRV_INT64 + {-9223372036854775808, 39}, + {#{}, 40}], % ERL_DRV_MAP ?line {Terms, Ops} = lists:unzip(Singles), ?line Terms = term(P,Ops), diff --git a/erts/emulator/test/send_term_SUITE_data/send_term_drv.c b/erts/emulator/test/send_term_SUITE_data/send_term_drv.c index f8613487b0..381a4f20d5 100644 --- a/erts/emulator/test/send_term_SUITE_data/send_term_drv.c +++ b/erts/emulator/test/send_term_SUITE_data/send_term_drv.c @@ -104,7 +104,7 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count) double f = 3.1416; msg[0] = ERL_DRV_ATOM; - msg[1] = driver_mk_atom("blurf"), + msg[1] = driver_mk_atom("blurf"); msg[2] = ERL_DRV_INT; msg[3] = (ErlDrvTermData) 42; msg[4] = ERL_DRV_NIL; @@ -126,9 +126,11 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count) msg[20] = (ErlDrvTermData) &f; msg[21] = ERL_DRV_PID; msg[22] = driver_connected(erlang_port); - msg[23] = ERL_DRV_TUPLE; - msg[24] = (ErlDrvTermData) 7; - msg += 25; + msg[23] = ERL_DRV_MAP; + msg[24] = (ErlDrvTermData) 0; + msg[25] = ERL_DRV_TUPLE; + msg[26] = (ErlDrvTermData) 8; + msg += 27; } break; @@ -481,6 +483,52 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count) break; } + case 40: { + msg[0] = ERL_DRV_MAP; + msg[1] = (ErlDrvTermData) 0; + msg += 2; + break; + } + + case 41: /* Most term types inside a map */ + case 42: { + double f = 3.1416; + + if (buf[i] == 41) { + *msg++ = ERL_DRV_ATOM; + *msg++ = driver_mk_atom("blurf"); + } + *msg++ = ERL_DRV_INT; + *msg++ = (ErlDrvTermData)42; + *msg++ = ERL_DRV_NIL; + *msg++ = ERL_DRV_INT; + *msg++ = (ErlDrvTermData)-42; + *msg++ = ERL_DRV_TUPLE; + *msg++ = (ErlDrvTermData)0; + *msg++ = ERL_DRV_PORT; + *msg++ = driver_mk_port(erlang_port); + *msg++ = ERL_DRV_STRING_CONS; + *msg++ = (ErlDrvTermData)"abc"; + *msg++ = (ErlDrvTermData)3; + *msg++ = ERL_DRV_LIST; + *msg++ = (ErlDrvTermData)3; + *msg++ = ERL_DRV_STRING; + *msg++ = (ErlDrvTermData)"kalle"; + *msg++ = (ErlDrvTermData)5; + *msg++ = ERL_DRV_FLOAT; + *msg++ = (ErlDrvTermData)&f; + *msg++ = ERL_DRV_PID; + *msg++ = driver_connected(erlang_port); + *msg++ = ERL_DRV_MAP; + *msg++ = (ErlDrvTermData)0; + if (buf[i] == 42) { + *msg++ = ERL_DRV_ATOM; + *msg++ = driver_mk_atom("blurf"); + } + *msg++ = ERL_DRV_MAP; + *msg++ = (ErlDrvTermData)4; + break; + } case 127: /* Error cases */ { @@ -662,6 +710,22 @@ static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count) FAIL_TERM(msg, 2); } + msg[0] = ERL_DRV_MAP; + msg[1] = (ErlDrvTermData) 0; + FAIL_TERM(msg, 1); + + /* map with duplicate key */ + msg[0] = ERL_DRV_ATOM; + msg[1] = driver_mk_atom("key"); + msg[2] = ERL_DRV_NIL; + msg[3] = ERL_DRV_ATOM; + msg[4] = driver_mk_atom("key"); + msg[5] = ERL_DRV_INT; + msg[6] = (ErlDrvTermData) -4711; + msg[7] = ERL_DRV_MAP; + msg[8] = 2; + FAIL_TERM(msg, 9); + /* Signal end of test case */ msg[0] = ERL_DRV_NIL; erl_drv_output_term(driver_mk_port(erlang_port), msg, 1); -- 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(-) 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 dfce3c3ea8ed4064856c29a678c83fc277a05bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 Jan 2014 14:18:14 +0100 Subject: erts: Update preloaded erts_internal.beam --- erts/preloaded/ebin/erts_internal.beam | Bin 4096 -> 4276 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 12b36913a9..4a84b3945a 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ -- cgit v1.2.3 From 543074e7aab966a597e405bc52c94e57358663a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 Jan 2014 14:47:42 +0100 Subject: erts: Fixup enif_make_map_put on windows --- erts/emulator/beam/erl_nif_api_funcs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 4121378e1a..d7c554e60b 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -300,7 +300,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa # define enif_is_map ERL_NIF_API_FUNC_MACRO(enif_is_map) # define enif_get_map_size ERL_NIF_API_FUNC_MACRO(enif_get_map_size) # define enif_make_new_map ERL_NIF_API_FUNC_MACRO(enif_make_new_map) -# define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_map_map_put) +# define enif_make_map_put ERL_NIF_API_FUNC_MACRO(enif_make_map_put) # define enif_get_map_value ERL_NIF_API_FUNC_MACRO(enif_get_map_value) # define enif_make_map_update ERL_NIF_API_FUNC_MACRO(enif_make_map_update) # define enif_make_map_remove ERL_NIF_API_FUNC_MACRO(enif_make_map_remove) -- 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(-) 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 a7ff048b6b5d8649d1709a90f7514fccbb5e9235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 Jan 2014 16:04:49 +0100 Subject: erts: Update maps_fold test to respect maps:fold/3 --- erts/emulator/test/map_SUITE.erl | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 75381de556..faaafb33bc 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -816,18 +816,11 @@ t_bif_map_from_list(Config) when is_list(Config) -> %% Maps module, not BIFs t_maps_fold(_Config) -> - Vs = lists:seq(1,100), - Rs = lists:reverse(Vs), M = maps:from_list([{{k,I},{v,I}}||I<-Vs]), - %% foldl - 5050 = maps:foldl(fun({k,_},{v,V},A) -> V + A end, 0, M), - Rs = maps:foldl(fun({k,_},{v,V},A) -> [V|A] end, [], M), - - %% foldr - 5050 = maps:foldr(fun({k,_},{v,V},A) -> V + A end, 0, M), - Vs = maps:foldr(fun({k,_},{v,V},A) -> [V|A] end, [], M), + %% fold + 5050 = maps:fold(fun({k,_},{v,V},A) -> V + A end, 0, M), ok. -- cgit v1.2.3 From 7849ea4d398717bd48bad76c551dcc5322fe39cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 27 Jan 2014 17:39:37 +0100 Subject: erts: Strengthen map_SUITE tests * Add tests for maps:merge/2 * Add tests for maps:update/3 * Test more corner cases --- erts/emulator/test/map_SUITE.erl | 83 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index faaafb33bc..31c1486f1c 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -37,9 +37,11 @@ t_bif_map_find/1, t_bif_map_is_key/1, t_bif_map_keys/1, + t_bif_map_merge/1, t_bif_map_new/1, t_bif_map_put/1, t_bif_map_remove/1, + t_bif_map_update/1, t_bif_map_values/1, t_bif_map_to_list/1, t_bif_map_from_list/1, @@ -76,8 +78,10 @@ all() -> [ %% Specific Map BIFs t_bif_map_get,t_bif_map_find,t_bif_map_is_key, - t_bif_map_keys,t_bif_map_new,t_bif_map_put, - t_bif_map_remove,t_bif_map_values, + t_bif_map_keys, t_bif_map_merge, t_bif_map_new, + t_bif_map_put, + t_bif_map_remove, t_bif_map_update, + t_bif_map_values, t_bif_map_to_list, t_bif_map_from_list, %% erlang @@ -478,8 +482,8 @@ t_bif_map_find(Config) when is_list(Config) -> error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key - {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,[])), - {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,<<>>)), + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id([]))), + {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id(<<>>))), ok. @@ -496,11 +500,16 @@ t_bif_map_is_key(Config) when is_list(Config) -> false = maps:is_key("h", M1), false = maps:is_key("hello", M1), false = maps:is_key(atom, M1), + false = maps:is_key(any, id(#{})), false = maps:is_key("hi", maps:remove("hi", M1)), true = maps:is_key("hi", M1), true = maps:is_key(1, maps:put(1, "number", M1)), false = maps:is_key(1.0, maps:put(1, "number", M1)), + + %% error case + {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id([]))), + {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id(<<>>))), ok. t_bif_map_keys(Config) when is_list(Config) -> @@ -526,6 +535,34 @@ t_bif_map_new(Config) when is_list(Config) -> 0 = erlang:map_size(maps:new()), ok. +t_bif_map_merge(Config) when is_list(Config) -> + 0 = erlang:map_size(maps:merge(#{},#{})), + + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + + #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, + 4 := number, 18446744073709551629 := wat} = maps:merge(#{}, M0), + + #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, + 4 := number, 18446744073709551629 := wat} = maps:merge(M0, #{}), + + M1 = #{ "hi" => "hello again", float => 3.3, {1,2} => "tuple", 4 => integer }, + + #{4 := number, 18446744073709551629 := wat, float := 3.3, int := 3, + {1,2} := "tuple", "hi" := "hello", <<"key">> := <<"value">>} = maps:merge(M1,M0), + + #{4 := integer, 18446744073709551629 := wat, float := 3.3, int := 3, + {1,2} := "tuple", "hi" := "hello again", <<"key">> := <<"value">>} = maps:merge(M0,M1), + + %% error case + {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge((1 bsl 65 + 3), <<>>)), + {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(<<>>, id(#{ a => 1}))), + {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(id(#{ a => 2}), <<>> )), + + ok. + + t_bif_map_put(Config) when is_list(Config) -> M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, @@ -555,6 +592,11 @@ t_bif_map_put(Config) when is_list(Config) -> [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M5), [number,wat,3,"hello",<<"value">>] = maps:values(M5), + M6 = #{ <<"key">> := <<"other value">> } = maps:put(<<"key">>, <<"other value">>, M5), + + [4,18446744073709551629,int,"hi",<<"key">>] = maps:keys(M6), + [number,wat,3,"hello",<<"other value">>] = maps:values(M6), + %% error case {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,1 bsl 65 + 3)), {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,154)), @@ -564,6 +606,8 @@ t_bif_map_put(Config) when is_list(Config) -> ok. t_bif_map_remove(Config) when is_list(Config) -> + 0 = erlang:map_size(maps:remove(some_key, #{})), + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number, 18446744073709551629 => wat}, @@ -600,6 +644,33 @@ t_bif_map_remove(Config) when is_list(Config) -> {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,<<>>)), ok. +t_bif_map_update(Config) when is_list(Config) -> + M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + + #{ "hi" := "hello again", int := 3, <<"key">> := <<"value">>, + 4 := number, 18446744073709551629 := wat} = maps:update("hi", "hello again", M0), + + #{ "hi" := "hello", int := 1337, <<"key">> := <<"value">>, + 4 := number, 18446744073709551629 := wat} = maps:update(int, 1337, M0), + + #{ "hi" := "hello", int := 3, <<"key">> := <<"new value">>, + 4 := number, 18446744073709551629 := wat} = maps:update(<<"key">>, <<"new value">>, M0), + + #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, + 4 := integer, 18446744073709551629 := wat} = maps:update(4, integer, M0), + + #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>, + 4 := number, 18446744073709551629 := wazzup} = maps:update(18446744073709551629, wazzup, M0), + + %% error case + {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,{})), + {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,<<"value">>)), + {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(5,none,M0)), + + ok. + + t_bif_map_values(Config) when is_list(Config) -> @@ -810,6 +881,10 @@ t_bif_map_from_list(Config) when is_list(Config) -> {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]), %% error cases + {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},b]))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},{b,b,3}]))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},<<>>]))), + {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b}|{b,a}]))), {'EXIT', {badarg,_}} = (catch maps:from_list(id(a))), {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))), ok. -- 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(-) 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(-) 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 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