diff options
Diffstat (limited to 'lib/compiler/src')
32 files changed, 1127 insertions, 331 deletions
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 402fbe2e2e..3723cc19e1 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,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}}; collect(fclearerror) -> {set,[],[],fclearerror}; collect({fcheckerror,{f,0}}) -> {set,[],[],fcheckerror}; @@ -236,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. @@ -383,6 +388,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_bsm.erl b/lib/compiler/src/beam_bsm.erl index fdfcb08125..d54c2a9fde 100644 --- a/lib/compiler/src/beam_bsm.erl +++ b/lib/compiler/src/beam_bsm.erl @@ -209,6 +209,7 @@ btb_reaches_match_2([{call,Arity,{f,Lbl}}|Is], Regs, D) -> btb_reaches_match_2([{apply,Arity}|Is], Regs, D) -> btb_call(Arity+2, apply, Regs, Is, D); btb_reaches_match_2([{call_fun,Live}=I|Is], Regs, D) -> + btb_ensure_not_used([{x,Live}], I, Regs), btb_call(Live, I, Regs, Is, D); btb_reaches_match_2([{make_fun2,_,_,_,Live}|Is], Regs, D) -> btb_call(Live, make_fun2, Regs, Is, D); diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl index 9d89e21a4e..55f985ad0e 100644 --- a/lib/compiler/src/beam_clean.erl +++ b/lib/compiler/src/beam_clean.erl @@ -234,6 +234,36 @@ 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},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) -> replace(Is, [I|Acc], D); replace([], Acc, _) -> Acc. diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl index 1a8bbcee22..57fdf95677 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); @@ -1119,6 +1134,31 @@ resolve_inst({line,[Index]},_,_,_) -> {line,resolve_arg(Index)}; %% +%% 17.0 +%% +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({has_map_field,Args0},_,_,_) -> + [FLbl|Args] = resolve_args(Args0), + {test,has_map_field,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. %% resolve_inst(X,_,_,_) -> ?exit({resolve_inst,X}). diff --git a/lib/compiler/src/beam_except.erl b/lib/compiler/src/beam_except.erl index e5ec1bd904..d261809765 100644 --- a/lib/compiler/src/beam_except.erl +++ b/lib/compiler/src/beam_except.erl @@ -131,9 +131,13 @@ translate_exception(_, _, _, _) -> no. fix_block(Is, 0) -> reverse(Is); -fix_block(Is0, Words) -> - [{set,[],[],{alloc,Live,{F1,F2,Needed,F3}}}|Is] = reverse(Is0), - [{set,[],[],{alloc,Live,{F1,F2,Needed-Words,F3}}}|Is]. +fix_block(Is, Words) -> + fix_block_1(reverse(Is), Words). + +fix_block_1([{set,[],[],{alloc,Live,{F1,F2,Needed,F3}}}|Is], Words) -> + [{set,[],[],{alloc,Live,{F1,F2,Needed-Words,F3}}}|Is]; +fix_block_1([I|Is], Words) -> + [I|fix_block_1(Is, Words)]. dig_out_block_fc([{set,[],[],{alloc,Live,_}}|Bl]) -> case dig_out_fc(Bl, Live-1, nil) of diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl index 5603a677e8..534bc6d954 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,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; 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..1f720b94c3 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,_Op,_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..638a4826ea 100644 --- a/lib/compiler/src/beam_split.erl +++ b/lib/compiler/src/beam_split.erl @@ -49,6 +49,13 @@ 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,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)]); 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_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/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 48f5135aca..2486d486ed 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -574,6 +574,7 @@ valfun_4({apply,Live}, Vst) -> valfun_4({apply_last,Live,_}, Vst) -> tail_call(apply, Live+2, Vst); valfun_4({call_fun,Live}, Vst) -> + validate_src([{x,Live}], Vst), call('fun', Live+1, Vst); valfun_4({call,Live,Func}, Vst) -> call(Func, Live, Vst); @@ -865,9 +866,29 @@ 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_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), + Vst = branch_state(Fail, Vst0), + set_type_reg(term, Dst, Vst); valfun_4(_, _) -> error(unknown_instruction). +verify_put_map(Fail, Src, Dst, Live, List, Vst0) -> + verify_live(Live, Vst0), + verify_y_init(Vst0), + foreach(fun (Term) -> assert_term(Term, Vst0) end, 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. %% @@ -888,7 +909,7 @@ validate_bs_skip_utf(Fail, Ctx, Live, Vst0) -> branch_state(Fail, Vst). %% -%% Special state handling for setelement/3 and the set_tuple_element/3 instruction. +%% Special state handling for setelement/3 and set_tuple_element/3 instructions. %% A possibility for garbage collection must not occur between setelement/3 and %% set_tuple_element/3. %% 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/cerl.erl b/lib/compiler/src/cerl.erl index 4b74d60e9f..60a8559950 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/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, c_module/0, c_tuple/0, c_values/0, c_var/0, cerl/0, var_name/0]). @@ -145,6 +154,8 @@ -type c_let() :: #c_let{}. -type c_letrec() :: #c_letrec{}. -type c_literal() :: #c_literal{}. +-type c_map() :: #c_map{}. +-type c_map_pair() :: #c_map_pair{}. -type c_module() :: #c_module{}. -type c_primop() :: #c_primop{}. -type c_receive() :: #c_receive{}. @@ -155,9 +166,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() + | c_module() | c_primop() | c_receive() | c_seq() | c_try() | c_tuple() | c_values() | c_var(). %% ===================================================================== @@ -250,8 +262,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 +280,8 @@ type(#c_fun{}) -> 'fun'; type(#c_let{}) -> 'let'; type(#c_letrec{}) -> letrec; type(#c_literal{}) -> literal; +type(#c_map{}) -> map; +type(#c_map_pair{}) -> map_pair; type(#c_module{}) -> module; type(#c_primop{}) -> primop; type(#c_receive{}) -> 'receive'; @@ -1558,6 +1572,34 @@ ann_make_list(_, [], Node) -> %% --------------------------------------------------------------------- +%% maps + +-spec map_es(c_map()) -> [cerl()]. + +map_es(#c_map{es = Es}) -> + Es. + +ann_c_map(As, Es) -> + #c_map{es = Es, anno = As }. + +update_c_map(Old, Es) -> + #c_map{es = Es, anno = get_ann(Old)}. + +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_pair(As,Op,K,V) -> + #c_map_pair{op=Op, key = K, val=V, anno = As}. + +update_c_map_pair(Old,Op,K,V) -> + #c_map_pair{op=Op, key=K, val=V, anno = get_ann(Old)}. + + +%% --------------------------------------------------------------------- %% @spec c_tuple(Elements::[cerl()]) -> cerl() %% @@ -2945,6 +2987,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 -> + 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 -> @@ -3803,7 +3849,6 @@ data_type(#c_cons{}) -> data_type(#c_tuple{}) -> tuple. - %% @spec data_es(Node::cerl()) -> [cerl()] %% %% @doc Returns the list of subtrees of a data constructor node. If @@ -3835,7 +3880,6 @@ data_es(#c_cons{hd = H, tl = T}) -> data_es(#c_tuple{es = Es}) -> Es. - %% @spec data_arity(Node::cerl()) -> integer() %% %% @doc Returns the number of subtrees of a data constructor @@ -3892,7 +3936,6 @@ 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). - %% @spec update_data(Old::cerl(), Type::dtype(), %% Elements::[cerl()]) -> cerl() %% @see make_data/2 @@ -4022,6 +4065,10 @@ subtrees(T) -> [[cons_hd(T)], [cons_tl(T)]]; tuple -> [tuple_es(T)]; + map -> + [map_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 1e3755025f..2542841eef 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, + 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 + ]). %% --------------------------------------------------------------------- @@ -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(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)), @@ -235,6 +249,14 @@ 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 -> + 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)), @@ -349,6 +371,14 @@ 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(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)), @@ -488,6 +518,10 @@ 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 -> + 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)), @@ -688,6 +722,16 @@ 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(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/compile.erl b/lib/compiler/src/compile.erl index 3db7ffc4d2..e79fe41f9b 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(), @@ -610,9 +623,11 @@ core_passes() -> [{core_old_inliner,fun test_old_inliner/1,fun core_old_inliner/1}, {iff,doldinline,{listing,"oldinline"}}, ?pass(core_fold_module), + {iff,dcorefold,{listing,"corefold"}}, {core_inline_module,fun test_core_inliner/1,fun core_inline_module/1}, {iff,dinline,{listing,"inline"}}, - {core_fold_after_inlining,fun test_core_inliner/1,fun core_fold_module_after_inlining/1}, + {core_fold_after_inlining,fun test_any_inliner/1, + fun core_fold_module_after_inlining/1}, ?pass(core_transforms)]}, {iff,dcopt,{listing,"copt"}}, {iff,'to_core',{done,"core"}}]} @@ -1158,6 +1173,9 @@ test_core_inliner(#compile{options=Opts}) -> end, Opts) end. +test_any_inliner(St) -> + test_old_inliner(St) orelse test_core_inliner(St). + core_old_inliner(#compile{code=Code0,options=Opts}=St) -> {ok,Code} = sys_core_inline:module(Code0, Opts), {ok,St#compile{code=Code}}. diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl index 824be9ff7f..ed181e3baa 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{var=M,es=Es}) -> + vu_expr(V, M) orelse vu_expr_list(V, Es); +vu_expr(V, #c_map_pair{key=Key,val=Val}) -> + vu_expr_list(V, [Key,Val]); 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/core_lint.erl b/lib/compiler/src/core_lint.erl index 67d37ff1fc..36165245a6 100644 --- a/lib/compiler/src/core_lint.erl +++ b/lib/compiler/src/core_lint.erl @@ -254,6 +254,10 @@ 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{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 +282,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 +308,10 @@ 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{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 +364,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 +463,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{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), 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 diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl index 0b8f4d8895..d54715ef59 100644 --- a/lib/compiler/src/core_parse.hrl +++ b/lib/compiler/src/core_parse.hrl @@ -96,3 +96,12 @@ -record(c_values, {anno=[], es}). % es :: [Tree] -record(c_var, {anno=[], name :: cerl:var_name()}). + +-record(c_map_pair, {anno=[], + op :: #c_literal{val::'assoc'} | #c_literal{val::'exact'}, + 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..b8db0f683a 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_pair_assoc map_pair_exact +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{op=#c_literal{val=exact},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,22 @@ 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='$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 -> map_pair_assoc : '$1'. +map_pair -> map_pair_exact : '$1'. + +map_pair_assoc -> '::' '<' anno_expression ',' anno_expression'>' : + #c_map_pair{op=#c_literal{val=assoc},key='$3',val='$5'}. +map_pair_exact -> '~' '<' anno_expression ',' anno_expression'>' : + #c_map_pair{op=#c_literal{val=exact},key='$3',val='$5'}. + cons -> '[' anno_expression tail : c_cons('$2', '$3'). tail -> ']' : #c_literal{val=[]}. @@ -381,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 1f91a52be3..faa26ec6df 100644 --- a/lib/compiler/src/core_pp.erl +++ b/lib/compiler/src/core_pp.erl @@ -161,6 +161,27 @@ 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{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{op=#c_literal{val=exact},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)))]; 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); diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab index ebc9b1c85b..79b467f949 100755 --- a/lib/compiler/src/genop.tab +++ b/lib/compiler/src/genop.tab @@ -528,3 +528,11 @@ BEAM_FORMAT_NUMBER=0 # R15A 153: line/1 + +# R16 + +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/sys_core_dsetel.erl b/lib/compiler/src/sys_core_dsetel.erl index f6696992b9..60d83763f8 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{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) -> {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..e2b9213891 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. @@ -295,6 +305,10 @@ expr(#c_let{}=Let, Ctxt, Sub) -> %% Now recursively re-process the new expression. expr(Expr, Ctxt, sub_new_preserve_types(Sub)) end; +expr(#c_letrec{body=#c_var{}}=Letrec, effect, _Sub) -> + %% This is named fun in an 'effect' context. Warn and ignore. + add_warning(Letrec, useless_building), + void(); expr(#c_letrec{defs=Fs0,body=B0}=Letrec, Ctxt, Sub) -> Fs1 = map(fun ({Name,Fb}) -> {Name,expr(Fb, {letrec,Ctxt}, Sub)} @@ -339,7 +353,12 @@ expr(#c_case{}=Case0, Ctxt, Sub) -> Case = Case1#c_case{arg=Arg2,clauses=Cs2}, warn_no_clause_match(Case1, Case), Expr = eval_case(Case, Sub), - bsm_an(Expr); + case move_case_into_arg(Case, Sub) of + impossible -> + bsm_an(Expr); + Other -> + expr(Other, Ctxt, sub_new_preserve_types(Sub)) + end; Other -> expr(Other, Ctxt, Sub) end; @@ -408,6 +427,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]. @@ -578,6 +607,14 @@ eval_binary_1([#c_bitstr{val=#c_literal{val=Val},size=#c_literal{val=Sz}, error:_ -> throw(impossible) end; +eval_binary_1([#c_bitstr{val=#c_literal{},size=#c_literal{}, + unit=#c_literal{},type=#c_literal{}, + flags=#c_cons{}=Flags}=Bitstr|Ss], Acc0) -> + case cerl:fold_literal(Flags) of + #c_literal{} = Flags1 -> + eval_binary_1([Bitstr#c_bitstr{flags=Flags1}|Ss], Acc0); + _ -> throw(impossible) + end; eval_binary_1([], Acc) -> Acc; eval_binary_1(_, _) -> throw(impossible). @@ -677,7 +714,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. @@ -1500,6 +1537,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 +1549,23 @@ 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{op=#c_literal{val=exact},key=K0,val=V0}=Pair,{Isub,Osub0}) -> + {K,Osub1} = case cerl:type(K0) of + binary -> + K1 = eval_binary(K0), + case cerl:type(K1) of + literal -> {K1,Osub0}; + _ -> pattern(K0,Isub,Osub0) + end; + _ -> pattern(K0,Isub,Osub0) + end, + {V,Osub} = pattern(V0,Isub,Osub1), + {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}. @@ -1888,14 +1945,45 @@ opt_bool_case_guard(Arg, [#c_clause{pats=[#c_literal{val=false}]}=Fc,Tc]) -> %% last clause is guaranteed to match so if there is only one clause %% with a pattern containing only variables then rewrite to a let. -eval_case(#c_case{arg=E,clauses=[#c_clause{pats=Ps0,body=B}]}, Sub) -> +eval_case(#c_case{arg=E,clauses=[#c_clause{pats=Ps0, + guard=#c_literal{val=true}, + body=B}]}=Case, Sub) -> Es = case cerl:is_c_values(E) of true -> cerl:values_es(E); false -> [E] end, - {true,Bs} = cerl_clauses:match_list(Ps0, Es), - {Ps,As} = unzip(Bs), - expr(#c_let{vars=Ps,arg=core_lib:make_values(As),body=B}, sub_new(Sub)); + %% Consider: + %% + %% case SomeSideEffect() of + %% X=Y -> ... + %% end + %% + %% We must not rewrite it to: + %% + %% let <X,Y> = <SomeSideEffect(),SomeSideEffect()> in ... + %% + %% because SomeSideEffect() would be evaluated twice. + %% + %% Instead we must evaluate the case expression in an outer let + %% like this: + %% + %% let NewVar = SomeSideEffect() in + %% let <X,Y> = <NewVar,NewVar> in ... + %% + Vs = make_vars([], length(Es)), + case cerl_clauses:match_list(Ps0, Vs) of + {false,_} -> + %% This can only happen if the Core Erlang code is + %% handwritten or generated by another code generator + %% than v3_core. Assuming that the Core Erlang program + %% is correct, the clause will always match at run-time. + Case; + {true,Bs} -> + {Ps,As} = unzip(Bs), + InnerLet = cerl:c_let(Ps, core_lib:make_values(As), B), + Let = cerl:c_let(Vs, E, InnerLet), + expr(Let, sub_new(Sub)) + end; eval_case(Case, _) -> Case. %% case_opt(CaseArg, [Clause]) -> {CaseArg,[Clause]}. @@ -2523,6 +2611,77 @@ opt_simple_let_2(Let, Vs0, Arg0, Body, value, Sub) -> value, Sub) end. +move_case_into_arg(#c_case{arg=#c_let{vars=OuterVars0,arg=OuterArg, + body=InnerArg0}=Outer, + clauses=InnerClauses}=Inner, Sub) -> + %% + %% case let <OuterVars> = <OuterArg> in <InnerArg> of + %% <InnerClauses> + %% end + %% + %% ==> + %% + %% let <OuterVars> = <OuterArg> + %% in case <InnerArg> of <InnerClauses> end + %% + ScopeSub0 = sub_subst_scope(Sub#sub{t=[]}), + {OuterVars,ScopeSub} = pattern_list(OuterVars0, ScopeSub0), + InnerArg = body(InnerArg0, ScopeSub), + Outer#c_let{vars=OuterVars,arg=OuterArg, + body=Inner#c_case{arg=InnerArg,clauses=InnerClauses}}; +move_case_into_arg(#c_case{arg=#c_case{arg=OuterArg, + clauses=[OuterCa0,OuterCb]}=Outer, + clauses=InnerClauses}=Inner0, Sub) -> + case is_failing_clause(OuterCb) of + true -> + #c_clause{pats=OuterPats0,guard=OuterGuard0, + body=InnerArg0} = OuterCa0, + %% + %% case case <OuterArg> of + %% <OuterPats> when <OuterGuard> -> <InnerArg> + %% <OuterCb> + %% ... + %% end of + %% <InnerClauses> + %% end + %% + %% ==> + %% + %% case <OuterArg> of + %% <OuterPats> when <OuterGuard> -> + %% case <InnerArg> of <InnerClauses> end + %% <OuterCb> + %% end + %% + ScopeSub0 = sub_subst_scope(Sub#sub{t=[]}), + {OuterPats,ScopeSub} = pattern_list(OuterPats0, ScopeSub0), + OuterGuard = guard(OuterGuard0, ScopeSub), + InnerArg = body(InnerArg0, ScopeSub), + Inner = Inner0#c_case{arg=InnerArg,clauses=InnerClauses}, + OuterCa = OuterCa0#c_clause{pats=OuterPats,guard=OuterGuard, + body=Inner}, + Outer#c_case{arg=OuterArg, + clauses=[OuterCa,OuterCb]}; + false -> + impossible + end; +move_case_into_arg(#c_case{arg=#c_seq{arg=OuterArg,body=InnerArg}=Outer, + clauses=InnerClauses}=Inner, _Sub) -> + %% + %% case do <OuterArg> <InnerArg> of + %% <InnerClauses> + %% end + %% + %% ==> + %% + %% do <OuterArg> + %% case <InnerArg> of <InerClauses> end + %% + Outer#c_seq{arg=OuterArg, + body=Inner#c_case{arg=InnerArg,clauses=InnerClauses}}; +move_case_into_arg(_, _) -> + impossible. + %% In guards only, rewrite a case in a let argument like %% %% let <Var> = case <> of @@ -2987,6 +3146,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) -> diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl index 48d9c16718..9998043013 100644 --- a/lib/compiler/src/sys_pre_expand.erl +++ b/lib/compiler/src/sys_pre_expand.erl @@ -228,6 +228,13 @@ 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_exact,Line,K0,V0}, St0) -> + {K,St1} = pattern(K0, St0), + {V,St2} = pattern(V0, St1), + {{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}; @@ -321,6 +328,20 @@ 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_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({bin,Line,Es0}, St0) -> {Es1,St1} = expr_bin(Es0, St0), {{bin,Line,Es1},St1}; diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index f534500671..4d155c0fd0 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 @@ -453,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)}; @@ -594,14 +599,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}. @@ -623,6 +627,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 +643,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 +925,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 +1448,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 +1488,35 @@ 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,Op,Map,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, + SrcReg = cg_reg_arg(Map,Int0), + Line = line(Le#l.a), + + %% The instruction needs to store keys in term sorted order + %% All keys has to be unique here + Pairs = map_pair_strip_and_termsort(Es), + + %% fetch registers for values to be put into the map + List = flatmap(fun({K,V}) -> [K,cg_reg_arg(V,Int0)] end, Pairs), + + Live = max_reg(Bef#sr.reg), + Int1 = Int0#sr{reg=put_reg(R, Int0#sr.reg)}, + Aft = clear_dead(Int1, Le#l.i, Vdb), + Target = fetch_reg(R, Int1#sr.reg), + + I = case Op of + assoc -> put_map_assoc; + exact -> put_map_exact + end, + {Sis++[Line]++[{I,Fail,SrcReg,Target,Live,{list,List}}],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)}, @@ -1460,6 +1529,13 @@ set_cg([{var,R}], Con, Le, Vdb, Bef, St) -> end, {Ais,clear_dead(Int, Le#l.i, Vdb),St}. +map_pair_strip_and_termsort(Es) -> + %% format in + %% [{map_pair,K,V}] + %% where K is for example {integer, 1} and we want to sort on 1. + Ls = [{K,V}||{_,K,V}<-Es], + lists:sort(fun({{_,A},_},{{_,B},_}) -> erts_internal:cmp_term(A,B) < 0 end, Ls). + %%% %%% Code generation for constructing binaries. %%% @@ -1931,7 +2007,7 @@ load_vars(Vs, Regs) -> foldl(fun ({var,V}, Rs) -> put_reg(V, Rs) end, Regs, Vs). %% put_reg(Val, Regs) -> Regs. -%% find_reg(Val, Regs) -> ok{r{R}} | error. +%% find_reg(Val, Regs) -> {ok,r{R}} | error. %% fetch_reg(Val, Regs) -> r{R}. %% Functions to interface the registers. diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index a5f31f3844..3435a46ca9 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -74,7 +74,7 @@ -export([module/2,format_error/1]). -import(lists, [reverse/1,reverse/2,map/2,member/2,foldl/3,foldr/3,mapfoldl/3, - splitwith/2,keyfind/3,sort/1,foreach/2]). + splitwith/2,keyfind/3,sort/1,foreach/2,droplast/1,last/1]). -import(ordsets, [add_element/2,del_element/2,is_element/2, union/1,union/2,intersection/2,subtract/2]). -import(cerl, [ann_c_cons/3,ann_c_cons_skel/3,ann_c_tuple/2,c_tuple/1]). @@ -101,6 +101,8 @@ -record(ireceive2, {anno=#a{},clauses,timeout,action}). -record(iset, {anno=#a{},var,arg}). -record(itry, {anno=#a{},args,vars,body,evars,handler}). +-record(ifilter, {anno=#a{},arg}). +-record(igen, {anno=#a{},acc_pat,acc_guard,skip_pat,tail,tail_pat,arg}). -type iapply() :: #iapply{}. -type ibinary() :: #ibinary{}. @@ -117,10 +119,13 @@ -type ireceive2() :: #ireceive2{}. -type iset() :: #iset{}. -type itry() :: #itry{}. +-type ifilter() :: #ifilter{}. +-type igen() :: #igen{}. -type i() :: iapply() | ibinary() | icall() | icase() | icatch() | iclause() | ifun() | iletrec() | imatch() | iprimop() - | iprotect() | ireceive1() | ireceive2() | iset() | itry(). + | iprotect() | ireceive1() | ireceive2() | iset() | itry() + | ifilter() | igen(). -type warning() :: {file:filename(), [{integer(), module(), term()}]}. @@ -226,13 +231,13 @@ guard(Gs0, St0) -> Gt1 = guard_tests(Gt0), L = element(2, Gt1), {op,L,'or',Gt1,Rhs} - end, guard_tests(last(Gs0)), first(Gs0)), + end, guard_tests(last(Gs0)), droplast(Gs0)), {Gs,St} = gexpr_top(Gs1, St0#core{in_guard=true}), {Gs,St#core{in_guard=false}}. guard_tests(Gs) -> L = element(2, hd(Gs)), - {protect,L,foldr(fun (G, Rhs) -> {op,L,'and',G,Rhs} end, last(Gs), first(Gs))}. + {protect,L,foldr(fun (G, Rhs) -> {op,L,'and',G,Rhs} end, last(Gs), droplast(Gs))}. %% gexpr_top(Expr, State) -> {Cexpr,State}. %% Generate an internal core expression of a guard test. Explicitly @@ -479,14 +484,26 @@ expr({cons,L,H0,T0}, St0) -> {T1,Tps,St2} = safe(T0, St1), A = lineno_anno(L, St2), {ann_c_cons(A, H1, T1),Hps ++ Tps,St2}; -expr({lc,L,E,Qs}, St) -> - lc_tq(L, E, Qs, #c_literal{anno=lineno_anno(L, St),val=[]}, St); +expr({lc,L,E,Qs0}, St0) -> + {Qs1,St1} = preprocess_quals(L, Qs0, St0), + lc_tq(L, E, Qs1, #c_literal{anno=lineno_anno(L, St1),val=[]}, St1); expr({bc,L,E,Qs}, St) -> bc_tq(L, E, Qs, {nil,L}, St); 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) -> + % 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}; +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 @@ -502,7 +519,7 @@ expr({bin,L,Es0}, St0) -> end; expr({block,_,Es0}, St0) -> %% Inline the block directly. - {Es1,St1} = exprs(first(Es0), St0), + {Es1,St1} = exprs(droplast(Es0), St0), {E1,Eps,St2} = expr(last(Es0), St1), {E1,Es1 ++ Eps,St2}; expr({'if',L,Cs0}, St0) -> @@ -636,7 +653,7 @@ expr({match,L,P0,E0}, St0) -> Other when not is_atom(Other) -> {#imatch{anno=#a{anno=Lanno},pat=P2,arg=E2,fc=Fc},Eps,St4} end; -expr({op,_,'++',{lc,Llc,E,Qs},More}, St0) -> +expr({op,_,'++',{lc,Llc,E,Qs0},More}, St0) -> %% Optimise '++' here because of the list comprehension algorithm. %% %% To avoid achieving quadratic complexity if there is a chain of @@ -644,7 +661,8 @@ expr({op,_,'++',{lc,Llc,E,Qs},More}, St0) -> %% evaluation of More now. Evaluating More here could also reduce the %% number variables in the environment for letrec. {Mc,Mps,St1} = safe(More, St0), - {Y,Yps,St} = lc_tq(Llc, E, Qs, Mc, St1), + {Qs,St2} = preprocess_quals(Llc, Qs0, St1), + {Y,Yps,St} = lc_tq(Llc, E, Qs, Mc, St2), {Y,Mps++Yps,St}; expr({op,L,'andalso',E1,E2}, St0) -> {#c_var{name=V0},St} = new_var(L, St0), @@ -695,6 +713,21 @@ make_bool_switch_guard(L, E, V, T, F) -> {clause,NegL,[V],[],[V]} ]}. +map_pair_list(Es, St) -> + 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{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{op=#c_literal{val=exact},anno=A,key=K,val=V}, + {[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2} + end, {[],[],St}, Es). %% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}. @@ -863,133 +896,45 @@ fun_tq({_,_,Name}=Id, Cs0, L, St0, NameInfo) -> %% lc_tq(Line, Exp, [Qualifier], Mc, State) -> {LetRec,[PreExp],State}. %% This TQ from Simon PJ pp 127-138. -%% This gets a bit messy as we must transform all directly here. We -%% recognise guard tests and try to fold them together and join to a -%% preceding generators, this should give us better and more compact -%% code. -lc_tq(Line, E, [{generate,Lg,P,G}|Qs0], Mc, St0) -> - {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0), +lc_tq(Line, E, [#igen{anno=GAnno,acc_pat=AccPat,acc_guard=AccGuard, + skip_pat=SkipPat,tail=Tail,tail_pat=TailPat, + arg={Pre,Arg}}|Qs], Mc, St0) -> {Name,St1} = new_fun_name("lc", St0), - {Head,St2} = new_var(St1), - {Tname,St3} = new_var_name(St2), - LA = lineno_anno(Line, St3), - LAnno = #a{anno=LA}, - Tail = #c_var{anno=LA,name=Tname}, - {Arg,St4} = new_var(St3), - {Nc,[],St5} = expr({call,Lg,{atom,Lg,Name},[{var,Lg,Tname}]}, St4), - {Guardc,St6} = lc_guard_tests(Gs, St5), %These are always flat! - {Lc,Lps,St7} = lc_tq(Line, E, Qs1, Nc, St6), - {Pc,St8} = list_gen_pattern(P, Line, St7), - {Gc,Gps,St9} = safe(G, St8), %Will be a function argument! - Fc = function_clause([Arg], LA, {Name,1}), - - %% Avoid constructing a default clause if the list comprehension - %% only has a variable as generator and there are no guard - %% tests. In other words, if the comprehension is equivalent to - %% lists:map/2. - Cs0 = case {Guardc, Pc} of - {[], #c_var{}} -> - [#iclause{anno=LAnno, - pats=[#c_literal{anno=LA,val=[]}],guard=[], - body=[Mc]}]; - _ -> - [#iclause{anno=#a{anno=[compiler_generated|LA]}, - pats=[ann_c_cons(LA, Head, Tail)], - guard=[], - body=[Nc]}, - #iclause{anno=LAnno, - pats=[#c_literal{anno=LA,val=[]}],guard=[], - body=[Mc]}] - end, - Cs = case Pc of - nomatch -> Cs0; - _ -> - [#iclause{anno=LAnno, - pats=[ann_c_cons(LA, Pc, Tail)], - guard=Guardc, - body=Lps ++ [Lc]}|Cs0] - end, - Fun = #ifun{anno=LAnno,id=[],vars=[Arg],clauses=Cs,fc=Fc}, - {#iletrec{anno=LAnno,defs=[{{Name,1},Fun}], - body=Gps ++ [#iapply{anno=LAnno, - op=#c_var{anno=LA,name={Name,1}}, - args=[Gc]}]}, - [],St9}; -lc_tq(Line, E, [{b_generate,Lg,P,G}|Qs0], Mc, St0) -> - {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0), - {Name,St1} = new_fun_name("blc", St0), LA = lineno_anno(Line, St1), LAnno = #a{anno=LA}, - HeadBinPattern = pattern(P, St1), - #c_binary{segments=Ps0} = HeadBinPattern, - {Ps,Tail,St2} = append_tail_segment(Ps0, St1), - {EPs,St3} = emasculate_segments(Ps, St2), - Pattern = HeadBinPattern#c_binary{segments=Ps}, - EPattern = HeadBinPattern#c_binary{segments=EPs}, - {Arg,St4} = new_var(St3), - {Guardc,St5} = lc_guard_tests(Gs, St4), %These are always flat! - Tname = Tail#c_var.name, - {Nc,[],St6} = expr({call,Lg,{atom,Lg,Name},[{var,Lg,Tname}]}, St5), - {Bc,Bps,St7} = lc_tq(Line, E, Qs1, Nc, St6), - {Gc,Gps,St10} = safe(G, St7), %Will be a function argument! - Fc = function_clause([Arg], LA, {Name,1}), - {TailSegList,_,St} = append_tail_segment([], St10), - Cs = [#iclause{anno=#a{anno=[compiler_generated|LA]}, - pats=[Pattern], - guard=Guardc, - body=Bps ++ [Bc]}, - #iclause{anno=#a{anno=[compiler_generated|LA]}, - pats=[EPattern], - guard=[], - body=[#iapply{anno=LAnno, - op=#c_var{anno=LA,name={Name,1}}, - args=[Tail]}]}, - #iclause{anno=LAnno, - pats=[#c_binary{anno=LA,segments=TailSegList}],guard=[], - body=[Mc]}], - Fun = #ifun{anno=LAnno,id=[],vars=[Arg],clauses=Cs,fc=Fc}, - {#iletrec{anno=LAnno,defs=[{{Name,1},Fun}], - body=Gps ++ [#iapply{anno=LAnno, - op=#c_var{anno=LA,name={Name,1}}, - args=[Gc]}]}, - [],St}; -lc_tq(Line, E, [Fil0|Qs0], Mc, St0) -> - %% Special case sequences guard tests. - LA = lineno_anno(element(2, Fil0), St0), - LAnno = #a{anno=LA}, - case is_guard_test(Fil0) of - true -> - {Gs0,Qs1} = splitwith(fun is_guard_test/1, Qs0), - {Lc,Lps,St1} = lc_tq(Line, E, Qs1, Mc, St0), - {Gs,St2} = lc_guard_tests([Fil0|Gs0], St1), %These are always flat! - {#icase{anno=LAnno, - args=[], - clauses=[#iclause{anno=LAnno,pats=[], - guard=Gs,body=Lps ++ [Lc]}], - fc=#iclause{anno=LAnno#a{anno=[compiler_generated|LA]}, - pats=[],guard=[],body=[Mc]}}, - [],St2}; - false -> - {Lc,Lps,St1} = lc_tq(Line, E, Qs0, Mc, St0), - {Fpat,St2} = new_var(St1), - Fc = fail_clause([Fpat], LA, - c_tuple([#c_literal{val=case_clause},Fpat])), - %% Do a novars little optimisation here. - {Filc,Fps,St3} = novars(Fil0, St2), - {#icase{anno=LAnno, - args=[Filc], - clauses=[#iclause{anno=LAnno, - pats=[#c_literal{anno=LA,val=true}], - guard=[], - body=Lps ++ [Lc]}, - #iclause{anno=LAnno#a{anno=[compiler_generated|LA]}, - pats=[#c_literal{anno=LA,val=false}], - guard=[], - body=[Mc]}], - fc=Fc}, - Fps,St3} - end; + F = #c_var{anno=LA,name={Name,1}}, + Nc = #iapply{anno=GAnno,op=F,args=[Tail]}, + {Var,St2} = new_var(St1), + Fc = function_clause([Var], LA, {Name,1}), + TailClause = #iclause{anno=LAnno,pats=[TailPat],guard=[],body=[Mc]}, + Cs0 = case {AccPat,AccGuard} of + {SkipPat,[]} -> + %% Skip and accumulator patterns are the same and there is + %% no guard, no need to generate a skip clause. + [TailClause]; + _ -> + [#iclause{anno=#a{anno=[compiler_generated|LA]}, + pats=[SkipPat],guard=[],body=[Nc]}, + TailClause] + end, + {Cs,St4} = case AccPat of + nomatch -> + %% The accumulator pattern never matches, no need + %% for an accumulator clause. + {Cs0,St2}; + _ -> + {Lc,Lps,St3} = lc_tq(Line, E, Qs, Nc, St2), + {[#iclause{anno=LAnno,pats=[AccPat],guard=AccGuard, + body=Lps ++ [Lc]}|Cs0], + St3} + end, + Fun = #ifun{anno=LAnno,id=[],vars=[Var],clauses=Cs,fc=Fc}, + {#iletrec{anno=LAnno#a{anno=[list_comprehension|LA]},defs=[{{Name,1},Fun}], + body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg]}]}, + [],St4}; +lc_tq(Line, E, [#ifilter{}=Filter|Qs], Mc, St) -> + filter_tq(Line, E, Filter, Mc, St, Qs, fun lc_tq/5); lc_tq(Line, E0, [], Mc0, St0) -> {H1,Hps,St1} = safe(E0, St0), {T1,Tps,St} = force_safe(Mc0, St1), @@ -999,143 +944,60 @@ lc_tq(Line, E0, [], Mc0, St0) -> %% bc_tq(Line, Exp, [Qualifier], More, State) -> {LetRec,[PreExp],State}. %% This TQ from Gustafsson ERLANG'05. -%% This gets a bit messy as we must transform all directly here. We -%% recognise guard tests and try to fold them together and join to a -%% preceding generators, this should give us better and more compact -%% code. %% More could be transformed before calling bc_tq. -bc_tq(Line, Exp, Qualifiers, _, St0) -> +bc_tq(Line, Exp, Qs0, _, St0) -> {BinVar,St1} = new_var(St0), - {Sz,SzPre,St2} = bc_initial_size(Exp, Qualifiers, St1), - {E,BcPre,St} = bc_tq1(Line, Exp, Qualifiers, BinVar, St2), + {Sz,SzPre,St2} = bc_initial_size(Exp, Qs0, St1), + {Qs,St3} = preprocess_quals(Line, Qs0, St2), + {E,BcPre,St} = bc_tq1(Line, Exp, Qs, BinVar, St3), Pre = SzPre ++ [#iset{var=BinVar, arg=#iprimop{name=#c_literal{val=bs_init_writable}, args=[Sz]}}] ++ BcPre, {E,Pre,St}. -bc_tq1(Line, E, [{generate,Lg,P,G}|Qs0], AccExpr, St0) -> - {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0), +bc_tq1(Line, E, [#igen{anno=GAnno,acc_pat=AccPat,acc_guard=AccGuard, + skip_pat=SkipPat,tail=Tail,tail_pat=TailPat, + arg={Pre,Arg}}|Qs], Mc, St0) -> {Name,St1} = new_fun_name("lbc", St0), LA = lineno_anno(Line, St1), - {[Head,Tail,AccVar],St2} = new_vars(LA, 3, St1), LAnno = #a{anno=LA}, - {Arg,St3} = new_var(St2), - NewMore = {call,Lg,{atom,Lg,Name},[{var,Lg,Tail#c_var.name}, - {var,Lg,AccVar#c_var.name}]}, - {Guardc,St4} = lc_guard_tests(Gs, St3), %These are always flat! - {Lc,Lps,St5} = bc_tq1(Line, E, Qs1, AccVar, St4), - {Nc,Nps,St6} = expr(NewMore, St5), - {Pc,St7} = list_gen_pattern(P, Line, St6), - {Gc,Gps,St8} = safe(G, St7), %Will be a function argument! - Fc = function_clause([Arg,AccVar], LA, {Name,2}), - Cs0 = case {Guardc, Pc} of - {[], #c_var{}} -> - [#iclause{anno=LAnno, - pats=[#c_literal{anno=LA,val=[]},AccVar],guard=[], - body=[AccVar]}]; - _ -> - [#iclause{anno=#a{anno=[compiler_generated|LA]}, - pats=[ann_c_cons(LA, Head, Tail),AccVar], - guard=[], - body=Nps ++ [Nc]}, - #iclause{anno=LAnno, - pats=[#c_literal{anno=LA,val=[]},AccVar],guard=[], - body=[AccVar]}] - end, - Cs = case Pc of - nomatch -> Cs0; - _ -> - Body = Lps ++ Nps ++ [#iset{var=AccVar,arg=Lc},Nc], - [#iclause{anno=LAnno, - pats=[ann_c_cons(LA,Pc,Tail),AccVar], - guard=Guardc, - body=Body}|Cs0] - end, - Fun = #ifun{anno=LAnno,id=[],vars=[Arg,AccVar],clauses=Cs,fc=Fc}, - {#iletrec{anno=LAnno,defs=[{{Name,2},Fun}], - body=Gps ++ [#iapply{anno=LAnno, - op=#c_var{anno=LA,name={Name,2}}, - args=[Gc,AccExpr]}]}, - [],St8}; -bc_tq1(Line, E, [{b_generate,Lg,P,G}|Qs0], AccExpr, St0) -> - {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0), - {Name,St1} = new_fun_name("lbc", St0), - LA = lineno_anno(Line, St1), - {AccVar,St2} = new_var(LA, St1), - LAnno = #a{anno=LA}, - HeadBinPattern = pattern(P, St2), - #c_binary{segments=Ps0} = HeadBinPattern, - {Ps,Tail,St3} = append_tail_segment(Ps0, St2), - {EPs,St4} = emasculate_segments(Ps, St3), - Pattern = HeadBinPattern#c_binary{segments=Ps}, - EPattern = HeadBinPattern#c_binary{segments=EPs}, - {Arg,St5} = new_var(St4), - NewMore = {call,Lg,{atom,Lg,Name},[{var,Lg,Tail#c_var.name}, - {var,Lg,AccVar#c_var.name}]}, - {Guardc,St6} = lc_guard_tests(Gs, St5), %These are always flat! - {Bc,Bps,St7} = bc_tq1(Line, E, Qs1, AccVar, St6), - {Nc,Nps,St8} = expr(NewMore, St7), - {Gc,Gps,St9} = safe(G, St8), %Will be a function argument! - Fc = function_clause([Arg,AccVar], LA, {Name,2}), - Body = Bps ++ Nps ++ [#iset{var=AccVar,arg=Bc},Nc], - {TailSegList,_,St} = append_tail_segment([], St9), - Cs = [#iclause{anno=LAnno, - pats=[Pattern,AccVar], - guard=Guardc, - body=Body}, - #iclause{anno=#a{anno=[compiler_generated|LA]}, - pats=[EPattern,AccVar], - guard=[], - body=Nps ++ [Nc]}, - #iclause{anno=LAnno, - pats=[#c_binary{anno=LA,segments=TailSegList},AccVar], - guard=[], - body=[AccVar]}], - Fun = #ifun{anno=LAnno,id=[],vars=[Arg,AccVar],clauses=Cs,fc=Fc}, - {#iletrec{anno=LAnno,defs=[{{Name,2},Fun}], - body=Gps ++ [#iapply{anno=LAnno, - op=#c_var{anno=LA,name={Name,2}}, - args=[Gc,AccExpr]}]}, - [],St}; -bc_tq1(Line, E, [Fil0|Qs0], AccVar, St0) -> - %% Special case sequences guard tests. - LA = lineno_anno(element(2, Fil0), St0), - LAnno = #a{anno=LA}, - case is_guard_test(Fil0) of - true -> - {Gs0,Qs1} = splitwith(fun is_guard_test/1, Qs0), - {Bc,Bps,St1} = bc_tq1(Line, E, Qs1, AccVar, St0), - {Gs,St} = lc_guard_tests([Fil0|Gs0], St1), %These are always flat! - {#icase{anno=LAnno, - args=[], - clauses=[#iclause{anno=LAnno, - pats=[], - guard=Gs,body=Bps ++ [Bc]}], - fc=#iclause{anno=LAnno#a{anno=[compiler_generated|LA]}, - pats=[],guard=[],body=[AccVar]}}, - [],St}; - false -> - {Bc,Bps,St1} = bc_tq1(Line, E, Qs0, AccVar, St0), - {Fpat,St2} = new_var(St1), - Fc = fail_clause([Fpat], LA, - c_tuple([#c_literal{val=case_clause},Fpat])), - %% Do a novars little optimisation here. - {Filc,Fps,St} = novars(Fil0, St2), - {#icase{anno=LAnno, - args=[Filc], - clauses=[#iclause{anno=LAnno, - pats=[#c_literal{anno=LA,val=true}], - guard=[], - body=Bps ++ [Bc]}, - #iclause{anno=LAnno#a{anno=[compiler_generated|LA]}, - pats=[#c_literal{anno=LA,val=false}], - guard=[], - body=[AccVar]}], - fc=Fc}, - Fps,St} - end; + {Vars=[_,AccVar],St2} = new_vars(LA, 2, St1), + F = #c_var{anno=LA,name={Name,2}}, + Nc = #iapply{anno=GAnno,op=F,args=[Tail,AccVar]}, + Fc = function_clause(Vars, LA, {Name,2}), + TailClause = #iclause{anno=LAnno,pats=[TailPat,AccVar],guard=[], + body=[AccVar]}, + Cs0 = case {AccPat,AccGuard} of + {SkipPat,[]} -> + %% Skip and accumulator patterns are the same and there is + %% no guard, no need to generate a skip clause. + [TailClause]; + _ -> + [#iclause{anno=#a{anno=[compiler_generated|LA]}, + pats=[SkipPat,AccVar],guard=[],body=[Nc]}, + TailClause] + end, + {Cs,St4} = case AccPat of + nomatch -> + %% The accumulator pattern never matches, no need + %% for an accumulator clause. + {Cs0,St2}; + _ -> + {Bc,Bps,St3} = bc_tq1(Line, E, Qs, AccVar, St2), + Body = Bps ++ [#iset{var=AccVar,arg=Bc},Nc], + {[#iclause{anno=LAnno, + pats=[AccPat,AccVar],guard=AccGuard, + body=Body}|Cs0], + St3} + end, + Fun = #ifun{anno=LAnno,id=[],vars=Vars,clauses=Cs,fc=Fc}, + {#iletrec{anno=LAnno#a{anno=[list_comprehension|LA]},defs=[{{Name,2},Fun}], + body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg,Mc]}]}, + [],St4}; +bc_tq1(Line, E, [#ifilter{}=Filter|Qs], Mc, St) -> + filter_tq(Line, E, Filter, Mc, St, Qs, fun bc_tq1/5); bc_tq1(_, {bin,Bl,Elements}, [], AccVar, St0) -> {E,Pre,St} = expr({bin,Bl,[{bin_element,Bl, {var,Bl,AccVar#c_var.name}, @@ -1143,16 +1005,154 @@ bc_tq1(_, {bin,Bl,Elements}, [], AccVar, St0) -> [binary,{unit,1}]}|Elements]}, St0), #a{anno=A} = Anno0 = get_anno(E), Anno = Anno0#a{anno=[compiler_generated,single_use|A]}, - %%Anno = Anno0#a{anno=[compiler_generated|A]}, {set_anno(E, Anno),Pre,St}. +%% filter_tq(Line, Expr, Filter, Mc, State, [Qualifier], TqFun) -> +%% {Case,[PreExpr],State}. +%% Transform an intermediate comprehension filter to its intermediate case +%% representation. + +filter_tq(Line, E, #ifilter{anno=#a{anno=LA}=LAnno,arg={Pre,Arg}}, + Mc, St0, Qs, TqFun) -> + %% The filter is an expression, it is compiled to a case of degree 1 with + %% 3 clauses, one accumulating, one skipping and the final one throwing + %% {case_clause,Value} where Value is the result of the filter and is not a + %% boolean. + {Lc,Lps,St1} = TqFun(Line, E, Qs, Mc, St0), + {FailPat,St2} = new_var(St1), + Fc = fail_clause([FailPat], LA, + c_tuple([#c_literal{val=case_clause},FailPat])), + {#icase{anno=LAnno#a{anno=[list_comprehension|LA]},args=[Arg], + clauses=[#iclause{anno=LAnno, + pats=[#c_literal{val=true}],guard=[], + body=Lps ++ [Lc]}, + #iclause{anno=LAnno#a{anno=[compiler_generated|LA]}, + pats=[#c_literal{val=false}],guard=[], + body=[Mc]}], + fc=Fc}, + Pre,St2}; +filter_tq(Line, E, #ifilter{anno=#a{anno=LA}=LAnno,arg=Guard}, + Mc, St0, Qs, TqFun) when is_list(Guard) -> + %% Otherwise it is a guard, compiled to a case of degree 0 with 2 clauses, + %% the first matches if the guard succeeds and the comprehension continues + %% or the second one is selected and the current element is skipped. + {Lc,Lps,St1} = TqFun(Line, E, Qs, Mc, St0), + {#icase{anno=LAnno#a{anno=[list_comprehension|LA]},args=[], + clauses=[#iclause{anno=LAnno,pats=[],guard=Guard,body=Lps ++ [Lc]}], + fc=#iclause{anno=LAnno#a{anno=[compiler_generated|LA]}, + pats=[],guard=[],body=[Mc]}}, + [],St1}. + +%% preprocess_quals(Line, [Qualifier], State) -> {[Qualifier'],State}. +%% Preprocess a list of Erlang qualifiers into its intermediate representation, +%% represented as a list of #igen{} and #ifilter{} records. We recognise guard +%% tests and try to fold them together and join to a preceding generators, this +%% should give us better and more compact code. + +preprocess_quals(Line, Qs, St) -> + preprocess_quals(Line, Qs, St, []). + +preprocess_quals(Line, [Q|Qs0], St0, Acc) -> + case is_generator(Q) of + true -> + {Gs,Qs} = splitwith(fun is_guard_test/1, Qs0), + {Gen,St} = generator(Line, Q, Gs, St0), + preprocess_quals(Line, Qs, St, [Gen|Acc]); + false -> + LAnno = #a{anno=lineno_anno(get_anno(Q), St0)}, + case is_guard_test(Q) of + true -> + %% When a filter is a guard test, its argument in the + %% #ifilter{} record is a list as returned by + %% lc_guard_tests/2. + {Gs,Qs} = splitwith(fun is_guard_test/1, Qs0), + {Cg,St} = lc_guard_tests([Q|Gs], St0), + Filter = #ifilter{anno=LAnno,arg=Cg}, + preprocess_quals(Line, Qs, St, [Filter|Acc]); + false -> + %% Otherwise, it is a pair {Pre,Arg} as in a generator + %% input. + {Ce,Pre,St} = novars(Q, St0), + Filter = #ifilter{anno=LAnno,arg={Pre,Ce}}, + preprocess_quals(Line, Qs0, St, [Filter|Acc]) + end + end; +preprocess_quals(_, [], St, Acc) -> + {reverse(Acc),St}. + +is_generator({generate,_,_,_}) -> true; +is_generator({b_generate,_,_,_}) -> true; +is_generator(_) -> false. + +%% +%% Generators are abstracted as sextuplets: +%% - acc_pat is the accumulator pattern, e.g. [Pat|Tail] for Pat <- Expr. +%% - acc_guard is the list of guards immediately following the current +%% generator in the qualifier list input. +%% - skip_pat is the skip pattern, e.g. <<X,_:X,Tail/bitstring>> for +%% <<X,1:X>> <= Expr. +%% - tail is the variable used in AccPat and SkipPat bound to the rest of the +%% generator input. +%% - tail_pat is the tail pattern, respectively [] and <<_/bitstring>> for list +%% and bit string generators. +%% - arg is a pair {Pre,Arg} where Pre is the list of expressions to be +%% inserted before the comprehension function and Arg is the expression +%% that it should be passed. +%% + +%% generator(Line, Generator, Guard, State) -> {Generator',State}. +%% Transform a given generator into its #igen{} representation. + +generator(Line, {generate,Lg,P0,E}, Gs, St0) -> + LA = lineno_anno(Line, St0), + GA = lineno_anno(Lg, St0), + {Head,St1} = list_gen_pattern(P0, Line, St0), + {[Tail,Skip],St2} = new_vars(2, St1), + {Cg,St3} = lc_guard_tests(Gs, St2), + {AccPat,SkipPat} = case Head of + #c_var{} -> + %% If the generator pattern is a variable, the + %% pattern from the accumulator clause can be + %% reused in the skip one. lc_tq and bc_tq1 takes + %% care of dismissing the latter in that case. + Cons = ann_c_cons(LA, Head, Tail), + {Cons,Cons}; + nomatch -> + %% If it never matches, there is no need for + %% an accumulator clause. + {nomatch,ann_c_cons(LA, Skip, Tail)}; + _ -> + {ann_c_cons(LA, Head, Tail), + ann_c_cons(LA, Skip, Tail)} + end, + {Ce,Pre,St4} = safe(E, St3), + Gen = #igen{anno=#a{anno=GA},acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat, + tail=Tail,tail_pat=#c_literal{anno=LA,val=[]},arg={Pre,Ce}}, + {Gen,St4}; +generator(Line, {b_generate,Lg,P,E}, Gs, St0) -> + LA = lineno_anno(Line, St0), + GA = lineno_anno(Lg, St0), + Cp = #c_binary{segments=Segs} = pattern(P, St0), + %% The function append_tail_segment/2 keeps variable patterns as-is, making + %% it possible to have the same skip clause removal as with list generators. + {AccSegs,Tail,TailSeg,St1} = append_tail_segment(Segs, St0), + AccPat = Cp#c_binary{segments=AccSegs}, + {Cg,St2} = lc_guard_tests(Gs, St1), + {SkipSegs,St3} = emasculate_segments(AccSegs, St2), + SkipPat = Cp#c_binary{segments=SkipSegs}, + {Ce,Pre,St4} = safe(E, St3), + Gen = #igen{anno=#a{anno=GA},acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat, + tail=Tail,tail_pat=#c_binary{anno=LA,segments=[TailSeg]}, + arg={Pre,Ce}}, + {Gen,St4}. + append_tail_segment(Segs, St0) -> {Var,St} = new_var(St0), Tail = #c_bitstr{val=Var,size=#c_literal{val=all}, unit=#c_literal{val=1}, type=#c_literal{val=binary}, flags=#c_literal{val=[unsigned,big]}}, - {Segs++[Tail],Var,St}. + {Segs++[Tail],Var,Tail,St}. emasculate_segments(Segs, St) -> emasculate_segments(Segs, St, []). @@ -1163,7 +1163,7 @@ emasculate_segments([B|Rest], St0, Acc) -> {Var,St1} = new_var(St0), emasculate_segments(Rest, St1, [B#c_bitstr{val=Var}|Acc]); emasculate_segments([], St, Acc) -> - {lists:reverse(Acc),St}. + {reverse(Acc),St}. lc_guard_tests([], St) -> {[],St}; lc_guard_tests(Gs0, St0) -> @@ -1478,6 +1478,26 @@ 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_exact,L,K,V}, St) -> + %% FIXME: Better way to construct literals? or missing case + %% {Key,_,_} = expr(K, St), + Key = case K of + {bin,L,Es0} -> + case constant_bin(Es0) of + error -> + 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), + op=#c_literal{val=exact}, + key=Key, + 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. @@ -1542,15 +1562,6 @@ pat_alias_list(_, _) -> throw(nomatch). pattern_list(Ps, St) -> [pattern(P, St) || P <- Ps]. -%% first([A]) -> [A]. -%% last([A]) -> A. - -first([_]) -> []; -first([H|T]) -> [H|first(T)]. - -last([L]) -> L; -last([_|T]) -> last(T). - %% make_vars([Name]) -> [{Var,Name}]. make_vars(Vs) -> [ #c_var{name=V} || V <- Vs ]. @@ -1633,13 +1644,13 @@ uclause(#iclause{anno=Anno,pats=Ps0,guard=G0,body=B0}, Pks, Ks0, St0) -> uguard([], [], _, St) -> {[],St}; uguard(Pg, [], Ks, St) -> %% No guard, so fold together equality tests. - uguard(first(Pg), [last(Pg)], Ks, St); + uguard(droplast(Pg), [last(Pg)], Ks, St); uguard(Pg, Gs0, Ks, St0) -> %% Gs0 must contain at least one element here. {Gs3,St5} = foldr(fun (T, {Gs1,St1}) -> {L,St2} = new_var(St1), {R,St3} = new_var(St2), - {[#iset{var=L,arg=T}] ++ first(Gs1) ++ + {[#iset{var=L,arg=T}] ++ droplast(Gs1) ++ [#iset{var=R,arg=last(Gs1)}, #icall{anno=#a{}, %Must have an #a{} module=#c_literal{val=erlang}, @@ -1823,6 +1834,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{op=#c_literal{val=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}; 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}; @@ -2036,7 +2053,8 @@ cexpr(#ifun{anno=#a{us=Us0}=A0,name={named,Name},fc=#iclause{pats=Ps}}=Fun0, RecVar = #c_var{name={Name,length(Ps)}}, Let = #c_let{vars=[#c_var{name=Name}],arg=RecVar,body=Body}, CFun1 = CFun0#c_fun{body=Let}, - Letrec = #c_letrec{defs=[{RecVar,CFun1}], + Letrec = #c_letrec{anno=A0#a.anno, + defs=[{RecVar,CFun1}], body=RecVar}, {Letrec,[],Us1,St1} end; @@ -2152,6 +2170,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..bc5ca0314a 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -81,7 +81,7 @@ -export([module/2,format_error/1]). -import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,splitwith/2,member/2, - keymember/3,keyfind/3,partition/2]). + keymember/3,keyfind/3,partition/2,droplast/1,last/1]). -import(ordsets, [add_element/2,del_element/2,union/2,union/1,subtract/2]). -import(cerl, [c_tuple/1]). @@ -272,6 +272,9 @@ expr(#c_cons{anno=A,hd=Ch,tl=Ct}, Sub, St0) -> expr(#c_tuple{anno=A,es=Ces}, Sub, St0) -> {Kes,Ep,St1} = atomic_list(Ces, Sub, St0), {#k_tuple{anno=A,es=Kes},Ep,St1}; +expr(#c_map{anno=A,var=Var0,es=Ces}, Sub, St0) -> + {Var,[],St1} = expr(Var0, Sub, St0), + map_split_pairs(A, Var, Ces, Sub, St1); expr(#c_binary{anno=A,segments=Cv}, Sub, St0) -> try atomic_bin(Cv, Sub, St0) of {Kv,Ep,St1} -> @@ -347,7 +350,7 @@ expr(#c_case{arg=Ca,clauses=Ccs}, Sub, St0) -> {Kvs,Pv,St2} = match_vars(Ka, St1), %Must have variables here! {Km,St3} = kmatch(Kvs, Ccs, Sub, St2), Match = flatten_seq(build_match(Kvs, Km)), - {last(Match),Pa ++ Pv ++ first(Match),St3}; + {last(Match),Pa ++ Pv ++ droplast(Match),St3}; expr(#c_receive{anno=A,clauses=Ccs0,timeout=Ce,action=Ca}, Sub, St0) -> {Ke,Pe,St1} = atomic(Ce, Sub, St0), %Force this to be atomic! {Rvar,St2} = new_var(St1), @@ -493,6 +496,72 @@ translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) -> translate_fc(Args) -> [#c_literal{val=function_clause},make_list(Args)]. + %{Kes,Ep,St2} = map_pairs(Ces, Sub, St1), +map_split_pairs(A, Var, Ces, Sub, St0) -> + %% two steps + %% 1. force variables + %% 2. remove multiples + Pairs0 = [{Op,K,V} || #c_map_pair{op=#c_literal{val=Op},key=K,val=V} <- Ces], + {Pairs,Esp,St1} = foldr(fun + ({Op,K0,V0}, {Ops,Espi,Sti0}) when Op =:= assoc; Op =:= exact -> + {K,[],Sti1} = expr(K0, Sub, Sti0), + {V,Ep,Sti2} = atomic(V0, Sub, Sti1), + {[{Op,K,V}|Ops],Ep ++ Espi,Sti2} + end, {[],[],St0}, Pairs0), + + case map_group_pairs(Pairs) of + {Assoc,[]} -> + Kes = [#k_map_pair{key=K,val=V}||{_,{assoc,K,V}} <- Assoc], + {#k_map{anno=A,op=assoc,var=Var,es=Kes},Esp,St1}; + {[],Exact} -> + Kes = [#k_map_pair{key=K,val=V}||{_,{exact,K,V}} <- Exact], + {#k_map{anno=A,op=exact,var=Var,es=Kes},Esp,St1}; + {Assoc,Exact} -> + Kes1 = [#k_map_pair{key=K,val=V}||{_,{assoc,K,V}} <- Assoc], + {Mvar,Em,St2} = force_atomic(#k_map{anno=A,op=assoc,var=Var,es=Kes1},St1), + Kes2 = [#k_map_pair{key=K,val=V}||{_,{exact,K,V}} <- Exact], + {#k_map{anno=A,op=exact,var=Mvar,es=Kes2},Em ++ Esp,St2} + + end. + +%% Group map by Assoc operations and Exact operations + +map_group_pairs(Es) -> + Groups = dict:to_list(map_group_pairs(Es,dict:new())), + partition(fun({_,{Op,_,_}}) -> Op =:= assoc end, Groups). + +map_group_pairs([{assoc,K,V}|Es0],Used0) -> + Used1 = case map_key_is_used(K,Used0) of + {ok, {assoc,_,_}} -> map_key_set_used(K,{assoc,K,V},Used0); + {ok, {exact,_,_}} -> map_key_set_used(K,{exact,K,V},Used0); + _ -> map_key_set_used(K,{assoc,K,V},Used0) + end, + map_group_pairs(Es0,Used1); +map_group_pairs([{exact,K,V}|Es0],Used0) -> + Used1 = case map_key_is_used(K,Used0) of + {ok, {assoc,_,_}} -> map_key_set_used(K,{assoc,K,V},Used0); + {ok, {exact,_,_}} -> map_key_set_used(K,{exact,K,V},Used0); + _ -> map_key_set_used(K,{exact,K,V},Used0) + end, + map_group_pairs(Es0,Used1); +map_group_pairs([],Used) -> + Used. + +map_key_set_used(K,How,Used) -> + dict:store(map_key_clean(K),How,Used). + +map_key_is_used(K,Used) -> + dict:find(map_key_clean(K),Used). + +%% Be explicit instead of using set_kanno(K,[]) +map_key_clean(#k_literal{val=V}) -> {k_literal,V}; +map_key_clean(#k_int{val=V}) -> {k_int,V}; +map_key_clean(#k_float{val=V}) -> {k_float,V}; +map_key_clean(#k_atom{val=V}) -> {k_atom,V}; +map_key_clean(#k_nil{}) -> k_nil; +map_key_clean(#k_var{name=V}) -> {k_var,V}. + + %% 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 +717,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,op=exact,es=Kes},Osub1,St1}; +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,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}; @@ -826,15 +902,6 @@ foldr2(Fun, Acc0, [E1|L1], [E2|L2]) -> foldr2(Fun, Acc1, L1, L2); foldr2(_, Acc, [], []) -> Acc. -%% first([A]) -> [A]. -%% last([A]) -> A. - -last([L]) -> L; -last([_|T]) -> last(T). - -first([_]) -> []; -first([H|T]) -> [H|first(T)]. - %% This code implements the algorithm for an optimizing compiler for %% pattern matching given "The Implementation of Functional %% Programming Languages" by Simon Peyton Jones. The code is much @@ -1015,7 +1082,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) -> @@ -1251,10 +1319,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_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 +1334,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 +1388,13 @@ 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 +1411,11 @@ new_clauses(Cs0, U, St) -> [S,N|As]; #k_bin_int{next=N} -> [N|As]; - _Other -> As + #k_map{op=exact,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 +1490,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 +1511,15 @@ arg_val(Arg, C) -> {#k_var{name=get_vsub(V, Isub)},U,T,Fs}; _ -> {set_kanno(S, []),U,T,Fs} - end + end; + #k_map{op=exact,es=Es} -> + Keys = [begin + #k_map_pair{key=#k_literal{val=Key}} = Pair, + Key + end || Pair <- Es], + %% multiple keys may have the same name + %% do not use ordsets + lists:sort(Keys) end. %% ubody_used_vars(Expr, State) -> [UsedVar] @@ -1795,6 +1888,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 +1927,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..ab66445f73 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,op,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..639c6737e2 100644 --- a/lib/compiler/src/v3_kernel_pp.erl +++ b/lib/compiler/src/v3_kernel_pp.erl @@ -104,6 +104,24 @@ 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{op=assoc,es=Es}, Ctxt) -> + ["~{", + format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), + "}~" + ]; +format_1(#k_map{op=exact,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..c4f54a7970 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{op=Op,var=Var,es=Es}, Ctxt) -> + {map,Op,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{op=Op,es=Es}, Ctxt) -> + {map,Op,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]. |