diff options
Diffstat (limited to 'lib/compiler')
-rw-r--r-- | lib/compiler/src/beam_asm.erl | 4 | ||||
-rw-r--r-- | lib/compiler/src/beam_dead.erl | 20 | ||||
-rw-r--r-- | lib/compiler/src/beam_peep.erl | 6 | ||||
-rw-r--r-- | lib/compiler/src/beam_type.erl | 3 | ||||
-rw-r--r-- | lib/compiler/src/cerl_trees.erl | 7 | ||||
-rw-r--r-- | lib/compiler/src/compile.erl | 2 | ||||
-rw-r--r-- | lib/compiler/src/erl_bifs.erl | 1 | ||||
-rw-r--r-- | lib/compiler/src/sys_core_fold.erl | 37 | ||||
-rw-r--r-- | lib/compiler/src/sys_core_inline.erl | 4 | ||||
-rw-r--r-- | lib/compiler/src/v3_codegen.erl | 6 | ||||
-rw-r--r-- | lib/compiler/test/bs_match_SUITE.erl | 10 | ||||
-rw-r--r-- | lib/compiler/test/core_SUITE.erl | 5 | ||||
-rw-r--r-- | lib/compiler/test/core_SUITE_data/name_capture.core | 110 | ||||
-rw-r--r-- | lib/compiler/test/map_SUITE.erl | 18 |
14 files changed, 196 insertions, 37 deletions
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl index 5ef340c831..df0321e85a 100644 --- a/lib/compiler/src/beam_asm.erl +++ b/lib/compiler/src/beam_asm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -475,7 +475,7 @@ encode_alloc_list_1([{floats,Floats}|T], Dict, Acc0) -> encode_alloc_list_1([], Dict, Acc) -> {iolist_to_binary(Acc),Dict}. --spec encode(non_neg_integer(), integer()) -> iodata(). +-spec encode(non_neg_integer(), integer()) -> iolist() | integer(). encode(Tag, N) when N < 0 -> encode1(Tag, negative_to_bytes(N)); diff --git a/lib/compiler/src/beam_dead.erl b/lib/compiler/src/beam_dead.erl index dbbaae05eb..762c7bdf9e 100644 --- a/lib/compiler/src/beam_dead.erl +++ b/lib/compiler/src/beam_dead.erl @@ -392,6 +392,26 @@ backward([{bif,'or',{f,To0},[Dst,{atom,false}],Dst}=I|Is], D, _ -> backward(Is, D, [I|Acc]) end; +backward([{bif,map_get,{f,FF},[Key,Map],_}=I0, + {test,has_map_fields,{f,FT}=F,[Map|Keys0]}=I1|Is], D, Acc) when FF =/= 0 -> + case shortcut_label(FF, D) of + FT -> + case lists:delete(Key, Keys0) of + [] -> + backward([I0|Is], D, Acc); + Keys -> + Test = {test,has_map_fields,F,[Map|Keys]}, + backward([Test|Is], D, [I0|Acc]) + end; + _ -> + backward([I1|Is], D, [I0|Acc]) + end; +backward([{bif,map_get,{f,FF},[_,Map],_}=I0, + {test,is_map,{f,FT},[Map]}=I1|Is], D, Acc) when FF =/= 0 -> + case shortcut_label(FF, D) of + FT -> backward([I0|Is], D, Acc); + _ -> backward([I1|Is], D, [I0|Acc]) + end; backward([I|Is], D, Acc) -> backward(Is, D, [I|Acc]); backward([], _D, Acc) -> Acc. diff --git a/lib/compiler/src/beam_peep.erl b/lib/compiler/src/beam_peep.erl index eb3192fe8f..920fb00397 100644 --- a/lib/compiler/src/beam_peep.erl +++ b/lib/compiler/src/beam_peep.erl @@ -77,6 +77,12 @@ peep([{bif,tuple_size,_,[_]=Ops,Dst}=I|Is], SeenTests0, Acc) -> %% Kill all remembered tests that depend on the destination register. SeenTests = kill_seen(Dst, SeenTests1), peep(Is, SeenTests, [I|Acc]); +peep([{bif,map_get,_,[Key,Map],Dst}=I|Is], SeenTests0, Acc) -> + %% Pretend that we have seen {test,has_map_fields,_,[Map,Key]} + SeenTests1 = gb_sets:add({has_map_fields,[Map,Key]}, SeenTests0), + %% Kill all remembered tests that depend on the destination register. + SeenTests = kill_seen(Dst, SeenTests1), + peep(Is, SeenTests, [I|Acc]); peep([{bif,_,_,_,Dst}=I|Is], SeenTests0, Acc) -> %% Kill all remembered tests that depend on the destination register. SeenTests = kill_seen(Dst, SeenTests0), diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl index 28f36db399..12da8c9446 100644 --- a/lib/compiler/src/beam_type.erl +++ b/lib/compiler/src/beam_type.erl @@ -462,6 +462,9 @@ update({set,[D],[Index,Reg],{bif,element,_}}, Ts0) -> end, Ts = tdb_meet(Reg, {tuple,min_size,MinSize,[]}, Ts0), tdb_store(D, any, Ts); +update({set,[D],[_Key,Map],{bif,map_get,_}}, Ts0) -> + Ts = tdb_meet(Map, map, Ts0), + tdb_store(D, any, Ts); update({set,[D],Args,{bif,N,_}}, Ts) -> Ar = length(Args), BoolOp = erl_internal:new_type_test(N, Ar) orelse diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl index c7a129b42c..533c984221 100644 --- a/lib/compiler/src/cerl_trees.erl +++ b/lib/compiler/src/cerl_trees.erl @@ -351,10 +351,9 @@ mapfold(F, S0, T) -> mapfold(fun(T0, A) -> {T0, A} end, F, S0, T). -%% @spec mapfold(Pre, Post, Initial::term(), Tree::cerl()) -> -%% {cerl(), term()} -%% -%% Pre = Post = (cerl(), term()) -> {cerl(), term()} +%% @spec mapfold(Pre, Post, Initial::term(), Tree::cerl()) -> {cerl(), term()} +%% Pre = (cerl(), term()) -> {cerl(), term()} +%% Post = (cerl(), term()) -> {cerl(), term()} %% %% @doc Does a combined map/fold operation on the nodes of the %% tree. It begins by calling <code>Pre</code> on the tree, using the diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index c6a0056a70..a37b2064b2 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -295,7 +295,7 @@ format_error_reason({Reason, Stack}) when is_list(Stack) -> end, FormatFun = fun (Term, _) -> io_lib:format("~tp", [Term]) end, [io_lib:format("~tp", [Reason]),"\n\n", - lib:format_stacktrace(1, Stack, StackFun, FormatFun)]; + erl_error:format_stacktrace(1, Stack, StackFun, FormatFun)]; format_error_reason(Reason) -> io_lib:format("~tp", [Reason]). diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl index 70b36f029e..a7452aebc8 100644 --- a/lib/compiler/src/erl_bifs.erl +++ b/lib/compiler/src/erl_bifs.erl @@ -94,6 +94,7 @@ is_pure(erlang, is_function, 1) -> true; is_pure(erlang, is_integer, 1) -> true; is_pure(erlang, is_list, 1) -> true; is_pure(erlang, is_map, 1) -> true; +is_pure(erlang, is_map_key, 2) -> true; is_pure(erlang, is_number, 1) -> true; is_pure(erlang, is_pid, 1) -> true; is_pure(erlang, is_port, 1) -> true; diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index a13bdedaf9..47042c2393 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -1275,13 +1275,18 @@ let_subst_list([], [], _) -> {[],[],[]}. %%pattern(Pat, Sub) -> pattern(Pat, Sub, Sub). pattern(#c_var{}=Pat, Isub, Osub) -> - case sub_is_val(Pat, Isub) of + case sub_is_in_scope(Pat, Isub) of true -> + %% This variable either has a substitution or is used in + %% the variable list of an enclosing `let`. In either + %% case, it must be renamed to an unused name to avoid + %% name capture problems. V1 = make_var_name(), Pat1 = #c_var{name=V1}, {Pat1,sub_set_var(Pat, Pat1, sub_add_scope([V1], Osub))}; false -> - {Pat,sub_del_var(Pat, Osub)} + %% This variable has never been used. Add it to the scope. + {Pat,sub_add_scope([Pat#c_var.name], Osub)} end; pattern(#c_literal{}=Pat, _, Osub) -> {Pat,Osub}; pattern(#c_cons{anno=Anno,hd=H0,tl=T0}, Isub, Osub0) -> @@ -1460,8 +1465,8 @@ is_subst(_) -> false. %% sub_set_name(Name, Value, #sub{}) -> #sub{}. %% sub_del_var(Var, #sub{}) -> #sub{}. %% sub_subst_var(Var, Value, #sub{}) -> [{Name,Value}]. -%% sub_is_val(Var, #sub{}) -> boolean(). -%% sub_add_scope(#sub{}) -> #sub{} +%% sub_is_in_scope(Var, #sub{}) -> boolean(). +%% sub_add_scope([Var], #sub{}) -> #sub{} %% sub_subst_scope(#sub{}) -> #sub{} %% %% We use the variable name as key so as not have problems with @@ -1496,18 +1501,6 @@ sub_set_name(V, Val, #sub{v=S,s=Scope,t=Tdb0}=Sub) -> Tdb = copy_type(V, Val, Tdb1), Sub#sub{v=orddict:store(V, Val, S),s=cerl_sets:add_element(V, Scope),t=Tdb}. -sub_del_var(#c_var{name=V}, #sub{v=S,s=Scope,t=Tdb}=Sub) -> - %% Profiling shows that for programs with many record operations, - %% sub_del_var/2 is a bottleneck. Since the scope contains all - %% variables that are live, we know that V cannot be present in S - %% if it is not in the scope. - case cerl_sets:is_element(V, Scope) of - false -> - Sub#sub{s=cerl_sets:add_element(V, Scope)}; - true -> - Sub#sub{v=orddict:erase(V, S),t=kill_types(V, Tdb)} - end. - sub_subst_var(#c_var{name=V}, Val, #sub{v=S0}) -> %% Fold chained substitutions. [{V,Val}] ++ [ {K,Val} || {K,#c_var{name=V1}} <- S0, V1 =:= V]. @@ -1533,16 +1526,8 @@ sub_subst_scope_1([H|T], Key, Acc) -> sub_subst_scope_1(T, Key-1, [{Key,#c_var{name=H}}|Acc]); sub_subst_scope_1([], _, Acc) -> Acc. -sub_is_val(#c_var{name=V}, #sub{v=S,s=Scope}) -> - %% When the bottleneck in sub_del_var/2 was eliminated, this - %% became the new bottleneck. Since the scope contains all - %% live variables, a variable V can only be the target for - %% a substitution if it is in the scope. - cerl_sets:is_element(V, Scope) andalso v_is_value(V, S). - -v_is_value(Var, [{_,#c_var{name=Var}}|_]) -> true; -v_is_value(Var, [_|T]) -> v_is_value(Var, T); -v_is_value(_, []) -> false. +sub_is_in_scope(#c_var{name=V}, #sub{s=Scope}) -> + cerl_sets:is_element(V, Scope). %% warn_no_clause_match(CaseOrig, CaseOpt) -> ok %% Generate a warning if none of the user-specified clauses diff --git a/lib/compiler/src/sys_core_inline.erl b/lib/compiler/src/sys_core_inline.erl index 8c1f69d5de..eee3594922 100644 --- a/lib/compiler/src/sys_core_inline.erl +++ b/lib/compiler/src/sys_core_inline.erl @@ -143,7 +143,7 @@ inline_inline(#ifun{body=B,weight=Iw}=If, Is) -> case find_inl(F, A, Is) of #ifun{vars=Vs,body=B2,weight=W} when W < Iw -> #c_let{vars=Vs, - arg=core_lib:make_values(As), + arg=kill_id_anns(core_lib:make_values(As)), body=kill_id_anns(B2)}; _Other -> Call end; @@ -160,7 +160,7 @@ inline_func(#fstat{def={Name,F0}}=Fstat, Is) -> case find_inl(F, A, Is) of #ifun{vars=Vs,body=B} -> {#c_let{vars=Vs, - arg=core_lib:make_values(As), + arg=kill_id_anns(core_lib:make_values(As)), body=kill_id_anns(B)}, true}; %Have modified _Other -> {Call,Mod} diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index 8e73b613a0..9652a8476d 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -589,6 +589,7 @@ is_gc_bif(element, 2) -> false; is_gc_bif(get, 1) -> false; is_gc_bif(tuple_size, 1) -> false; is_gc_bif(map_get, 2) -> false; +is_gc_bif(is_map_key, 2) -> false; is_gc_bif(Bif, Arity) -> not (erl_internal:bool_op(Bif, Arity) orelse erl_internal:new_type_test(Bif, Arity) orelse @@ -1620,6 +1621,11 @@ test_cg(is_boolean, [#k_atom{val=Val}], Fail, I, Vdb, Bef, St) -> false -> [{jump,{f,Fail}}] end, {Is,Aft,St}; +test_cg(is_map_key, As, Fail, I, Vdb, Bef, St) -> + [Key,Map] = cg_reg_args(As, Bef), + Aft = clear_dead(Bef, I, Vdb), + F = {f,Fail}, + {[{test,is_map,F,[Map]},{test,has_map_fields,F,Map,{list,[Key]}}],Aft,St}; test_cg(Test, As, Fail, I, Vdb, Bef, St) -> Args = cg_reg_args(As, Bef), Aft = clear_dead(Bef, I, Vdb), diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl index 235956a714..3b6ffa8d68 100644 --- a/lib/compiler/test/bs_match_SUITE.erl +++ b/lib/compiler/test/bs_match_SUITE.erl @@ -330,6 +330,11 @@ save_restore(Config) when is_list(Config) -> {"-",<<"x">>} = nnn(C), {"-",<<"x">>} = ooo(C), + a = multiple_matches(<<777:16>>, <<777:16>>), + b = multiple_matches(<<777:16>>, <<999:16>>), + c = multiple_matches(<<777:16>>, <<57:8>>), + d = multiple_matches(<<17:8>>, <<1111:16>>), + Bin = <<-1:64>>, case bad_float_unpack_match(Bin) of -1 -> ok; @@ -357,6 +362,11 @@ nnn(<<Char, Tail/binary>>) -> {[Char],Tail}. %% Buggy Tail! ooo(<<" - ", Tail/binary>>) -> Tail; ooo(<<Char, Tail/binary>>) -> {[Char],Tail}. +multiple_matches(<<Y:16>>, <<Y:16>>) -> a; +multiple_matches(<<_:16>>, <<_:16>>) -> b; +multiple_matches(<<_:16>>, <<_:8>>) -> c; +multiple_matches(<<_:8>>, <<_:16>>) -> d. + bad_float_unpack_match(<<F:64/float>>) -> F; bad_float_unpack_match(<<I:64/integer-signed>>) -> I. diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl index 0e07e8dd2e..d07cd3b8b7 100644 --- a/lib/compiler/test/core_SUITE.erl +++ b/lib/compiler/test/core_SUITE.erl @@ -29,7 +29,7 @@ bs_shadowed_size_var/1, cover_v3_kernel_1/1,cover_v3_kernel_2/1,cover_v3_kernel_3/1, cover_v3_kernel_4/1,cover_v3_kernel_5/1, - non_variable_apply/1]). + non_variable_apply/1,name_capture/1]). -include_lib("common_test/include/ct.hrl"). @@ -58,7 +58,7 @@ groups() -> bs_shadowed_size_var, cover_v3_kernel_1,cover_v3_kernel_2,cover_v3_kernel_3, cover_v3_kernel_4,cover_v3_kernel_5, - non_variable_apply + non_variable_apply,name_capture ]}]. @@ -93,6 +93,7 @@ end_per_group(_GroupName, Config) -> ?comp(cover_v3_kernel_4). ?comp(cover_v3_kernel_5). ?comp(non_variable_apply). +?comp(name_capture). try_it(Mod, Conf) -> Src = filename:join(proplists:get_value(data_dir, Conf), diff --git a/lib/compiler/test/core_SUITE_data/name_capture.core b/lib/compiler/test/core_SUITE_data/name_capture.core new file mode 100644 index 0000000000..0969f95b72 --- /dev/null +++ b/lib/compiler/test/core_SUITE_data/name_capture.core @@ -0,0 +1,110 @@ +module 'name_capture' ['module_info'/0, + 'module_info'/1, + 'name_capture'/0] + attributes ['compile' = + [{'inline',[{'badarg_exit',2}]}]] +'name_capture'/0 = + fun () -> + case <> of + <> when 'true' -> + let <_0> = + catch + apply 'first'/1 + ('badarg') + in case _0 of + <{'EXIT',{'badarg',_7}}> when 'true' -> + let <Seq> = + call 'lists':'seq' + (7, 17) + in case apply 'first'/1 + ({'ok',Seq}) of + <_8> + when call 'erlang':'=:=' + (_8, + Seq) -> + let <SomeOtherTerm> = + {'some','other','term'} + in let <_5> = + catch + apply 'first'/1 + (SomeOtherTerm) + in case _5 of + <{'EXIT',_9}> + when call 'erlang':'=:=' + (_9, + SomeOtherTerm) -> + 'ok' + <_6> when 'true' -> + primop 'match_fail' + ({'badmatch',_6}) + end + <_3> when 'true' -> + primop 'match_fail' + ({'badmatch',_3}) + end + <_1> when 'true' -> + primop 'match_fail' + ({'badmatch',_1}) + end + <> when 'true' -> + primop 'match_fail' + ({'function_clause'}) + end +'first'/1 = + fun (_0) -> + case _0 of + <Tab> when 'true' -> + let <_1> = + apply 'treq'/2 + (Tab, 'first') + %% The _1 variable in the `let` must be renamed + %% to avoid a name capture problem. + in let <_0,_1> = + <_1,[Tab|[]]> + in case <_0,_1> of + <'badarg',A> when 'true' -> + call 'erlang':'error' + ('badarg', A) + <{'ok',Reply},_X_A> when 'true' -> + Reply + <Reply,_X_A> when 'true' -> + call 'erlang':'exit' + (Reply) + <_3,_2> when 'true' -> + primop 'match_fail' + ({'function_clause',_3,_2}) + end + <_2> when 'true' -> + primop 'match_fail' + ({'function_clause',_2}) + end +'treq'/2 = + fun (_0,_1) -> + case <_0,_1> of + <Action,_4> when 'true' -> + Action + <_3,_2> when 'true' -> + primop 'match_fail' + ({'function_clause',_3,_2}) + end +'module_info'/0 = + fun () -> + case <> of + <> when 'true' -> + call 'erlang':'get_module_info' + ('name_capture') + <> when 'true' -> + primop 'match_fail' + ({'function_clause'}) + end +'module_info'/1 = + fun (_0) -> + case _0 of + <X> when 'true' -> + call 'erlang':'get_module_info' + ('name_capture', X) + <_1> when 'true' -> + primop 'match_fail' + ({'function_clause',_1}) + end +end diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl index e98c295da6..6badc7a8b8 100644 --- a/lib/compiler/test/map_SUITE.erl +++ b/lib/compiler/test/map_SUITE.erl @@ -1203,12 +1203,18 @@ t_guard_bifs(Config) when is_list(Config) -> true = map_guard_empty_2(), true = map_guard_head(#{a=>1}), false = map_guard_head([]), + true = map_get_head(#{a=>1}), + false = map_get_head([]), + true = map_is_key_head(#{a=>1}), + false = map_is_key_head(#{}), true = map_guard_body(#{a=>1}), false = map_guard_body({}), true = map_guard_pattern(#{a=>1, <<"hi">> => "hi" }), false = map_guard_pattern("list"), true = map_guard_tautology(), true = map_guard_ill_map_size(), + true = map_field_check_sequence(#{a=>1}), + false = map_field_check_sequence(#{}), ok. map_guard_empty() when is_map(#{}); false -> true. @@ -1218,6 +1224,12 @@ map_guard_empty_2() when true; #{} andalso false -> true. map_guard_head(M) when is_map(M) -> true; map_guard_head(_) -> false. +map_get_head(M) when map_get(a, M) =:= 1 -> true; +map_get_head(_) -> false. + +map_is_key_head(M) when is_map_key(a, M) -> true; +map_is_key_head(M) -> false. + map_guard_body(M) -> is_map(M). map_guard_pattern(#{}) -> true; @@ -1227,6 +1239,12 @@ map_guard_tautology() when #{} =:= #{}; true -> true. map_guard_ill_map_size() when true; map_size(0) -> true. +map_field_check_sequence(M) + when is_map(M) andalso is_map_key(a, M) andalso (map_get(a, M) == 1) -> + true; +map_field_check_sequence(_) -> + 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")}), |