diff options
Diffstat (limited to 'lib/compiler')
-rw-r--r-- | lib/compiler/src/beam_disasm.erl | 9 | ||||
-rw-r--r-- | lib/compiler/src/cerl.erl | 4 | ||||
-rw-r--r-- | lib/compiler/src/sys_core_fold.erl | 43 | ||||
-rw-r--r-- | lib/compiler/src/v3_codegen.erl | 4 | ||||
-rw-r--r-- | lib/compiler/src/v3_core.erl | 34 | ||||
-rw-r--r-- | lib/compiler/src/v3_kernel.erl | 3 | ||||
-rw-r--r-- | lib/compiler/src/v3_life.erl | 3 | ||||
-rw-r--r-- | lib/compiler/test/core_fold_SUITE.erl | 14 | ||||
-rw-r--r-- | lib/compiler/test/map_SUITE.erl | 36 | ||||
-rw-r--r-- | lib/compiler/test/warnings_SUITE.erl | 43 |
10 files changed, 148 insertions, 45 deletions
diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl index 4bdfe4e0c2..c45596f236 100644 --- a/lib/compiler/src/beam_disasm.erl +++ b/lib/compiler/src/beam_disasm.erl @@ -1030,6 +1030,7 @@ resolve_inst({gc_bif2,Args},Imports,_,_) -> [F,Live,Bif,A1,A2,Reg] = resolve_args(Args), {extfunc,_Mod,BifName,_Arity} = lookup(Bif+1,Imports), {gc_bif,BifName,F,Live,[A1,A2],Reg}; + %% %% New instruction in R14, gc_bif with 3 arguments %% @@ -1146,21 +1147,17 @@ 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},_,_,_) -> +resolve_inst({is_map=I,Args0},_,_,_) -> [FLbl|Args] = resolve_args(Args0), - {test, is_map, FLbl, Args}; - + {test,I,FLbl,Args}; resolve_inst({has_map_fields,Args0},_,_,_) -> [FLbl,Src,{{z,1},{u,_Len},List0}] = Args0, List = resolve_args(List0), {test,has_map_fields,FLbl,Src,{list,List}}; - resolve_inst({get_map_elements,Args0},_,_,_) -> [FLbl,Src,{{z,1},{u,_Len},List0}] = Args0, List = resolve_args(List0), diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index e400e4f185..ed11c8de4d 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -136,10 +136,6 @@ c_literal/0, c_map_pair/0, c_module/0, c_tuple/0, c_values/0, c_var/0, cerl/0, var_name/0]). -%% HiPE does not understand Maps -%% (guard functions is_map/1 and map_size/1 in ann_c_map/3) --compile(no_native). - -include("core_parse.hrl"). -type c_alias() :: #c_alias{}. diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 52d6dfe184..b7422318b2 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -2031,9 +2031,9 @@ case_opt(Arg, Cs0, Sub) -> case_opt_args([A0|As0], Cs0, Sub, LitExpr, Acc) -> case case_opt_arg(A0, Sub, Cs0, LitExpr) of - error -> + {error,Cs1} -> %% Nothing to be done. Move on to the next argument. - Cs = [{Ps,C,[P|PsAcc],Bs} || {[P|Ps],C,PsAcc,Bs} <- Cs0], + Cs = [{Ps,C,[P|PsAcc],Bs} || {[P|Ps],C,PsAcc,Bs} <- Cs1], case_opt_args(As0, Cs, Sub, LitExpr, [A0|Acc]); {ok,As1,Cs} -> %% The argument was either expanded (from tuple/list) or @@ -2052,7 +2052,7 @@ case_opt_arg(E0, Sub, Cs, LitExpr) -> E = maybe_replace_var(E0, Sub), case cerl:is_data(E) of false -> - error; + {error,Cs}; true -> case cerl:data_type(E) of {atomic,_} -> @@ -2102,35 +2102,44 @@ maybe_replace_var_1(E, #sub{t=Tdb}) -> %% pattern matching is tricky, so we will give up in that case. case_opt_lit(Lit, Cs0, LitExpr) -> - try case_opt_lit_1(Cs0, Lit, LitExpr) of + Cs1 = case_opt_lit_1(Lit, Cs0, LitExpr), + try case_opt_lit_2(Lit, Cs1) of Cs -> {ok,[],Cs} catch throw:impossible -> - error + {error,Cs1} end. -case_opt_lit_1([{[P|Ps],C,PsAcc,Bs0}|Cs], E, LitExpr) -> +case_opt_lit_1(E, [{[P|_],C,_,_}=Current|Cs], LitExpr) -> + case cerl_clauses:match(P, E) of + none -> + %% The pattern will not match the literal. Remove the clause. + %% Unless the entire case expression is a literal, also + %% emit a warning. + case LitExpr of + false -> add_warning(C, nomatch_clause_type); + true -> ok + end, + case_opt_lit_1(E, Cs, LitExpr); + _ -> + [Current|case_opt_lit_1(E, Cs, LitExpr)] + end; +case_opt_lit_1(_, [], _) -> []. + +case_opt_lit_2(E, [{[P|Ps],C,PsAcc,Bs0}|Cs]) -> + %% Non-matching clauses have already been removed in case_opt_lit_1/3. case cerl_clauses:match(P, E) of - none -> - %% The pattern will not match the literal. Remove the clause. - %% Unless the entire case expression is a literal, also - %% emit a warning. - case LitExpr of - false -> add_warning(C, nomatch_clause_type); - true -> ok - end, - case_opt_lit_1(Cs, E, LitExpr); {true,Bs} -> %% The pattern matches the literal. Remove the pattern %% and update the bindings. - [{Ps,C,PsAcc,Bs++Bs0}|case_opt_lit_1(Cs, E, LitExpr)]; + [{Ps,C,PsAcc,Bs++Bs0}|case_opt_lit_2(E, Cs)]; {false,_} -> %% Binary literal and pattern. We are not sure whether %% the pattern will match. throw(impossible) end; -case_opt_lit_1([], _, _) -> []. +case_opt_lit_2(_, []) -> []. %% case_opt_data(Expr, Clauses0, LitExpr) -> {ok,Exprs,Clauses} diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index f1331d1fe7..47a357c23d 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -960,7 +960,6 @@ select_extract_map(Src, Vs, Fail, I, Vdb, Bef, St) -> end, {{[],[]},Bef}, Vs), Code = case {HasKs,GetVs} of - {[],[]} -> {[],Aft,St}; {HasKs,[]} -> [{test,has_map_fields,{f,Fail},Rsrc,{list,HasKs}}]; {[],GetVs} -> @@ -1553,8 +1552,7 @@ map_pair_strip_and_termsort(Es) -> Ls = [{K,V}||{_,K,V}<-Es], lists:sort(fun ({{_,A},_}, {{_,B},_}) -> erts_internal:cmp_term(A,B) =< 0; ({nil,_}, {{_,B},_}) -> [] =< B; - ({{_,A},_}, {nil,_}) -> A =< []; - ({nil,_}, {nil,_}) -> true + ({{_,A},_}, {nil,_}) -> A =< [] end, Ls). %%% diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 04210ae243..a548ba2f7c 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -514,15 +514,26 @@ expr({tuple,L,Es0}, St0) -> 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), - {ann_c_map(A,Es1),Eps,St1}; + try map_pair_list(Es0, St0) of + {Es1,Eps,St1} -> + A = lineno_anno(L, St1), + {ann_c_map(A,Es1),Eps,St1} + catch + throw:{bad_map,Warning} -> + St = add_warning(L, Warning, St0), + LineAnno = lineno_anno(L, St), + As = [#c_literal{anno=LineAnno,val=badarg}], + {#icall{anno=#a{anno=LineAnno}, %Must have an #a{} + module=#c_literal{anno=LineAnno,val=erlang}, + name=#c_literal{anno=LineAnno,val=error}, + args=As},[],St} + end; expr({map,L,M0,Es0}, St0) -> try expr_map(M0,Es0,lineno_anno(L, St0),St0) of {_,_,_}=Res -> Res catch - throw:bad_map -> - St = add_warning(L, bad_map, St0), + throw:{bad_map,Warning} -> + St = add_warning(L, Warning, St0), LineAnno = lineno_anno(L, St), As = [#c_literal{anno=LineAnno,val=badarg}], {#icall{anno=#a{anno=LineAnno}, %Must have an #a{} @@ -762,7 +773,7 @@ expr_map(M0,Es0,A,St0) -> {Es1,Eps,St2} = map_pair_list(Es0, St1), {ann_c_map(A,M1,Es1),Mps++Eps,St2} end; - false -> throw(bad_map) + false -> throw({bad_map,bad_map}) end. is_valid_map_src(#c_literal{val = M}) when is_map(M) -> true; @@ -774,18 +785,23 @@ map_pair_list(Es, St) -> foldr(fun ({map_field_assoc,L,K0,V0}, {Ces,Esp,St0}) -> {K,Ep0,St1} = safe(K0, St0), + ok = ensure_valid_map_key(K), {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), + ok = ensure_valid_map_key(K), {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). +ensure_valid_map_key(#c_literal{}) -> ok; +ensure_valid_map_key(_) -> throw({bad_map,bad_map_key}). + %% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}. try_exception(Ecs0, St0) -> @@ -1595,7 +1611,9 @@ pattern_map_pair({map_field_exact,L,K,V}, St) -> {bin,L,Es0} -> case constant_bin(Es0) of error -> - throw(badmatch); + %% this will throw a cryptic error message + %% but it is better than nothing + throw(nomatch); Bin -> #c_literal{anno=lineno_anno(L,St),val=Bin} end; @@ -2299,6 +2317,8 @@ format_error(nomatch) -> "pattern cannot possibly match"; format_error(bad_binary) -> "binary construction will fail because of a type mismatch"; +format_error(bad_map_key) -> + "map construction will fail because of none literal key (large binaries are not literals)"; format_error(bad_map) -> "map construction will fail because of a type mismatch". diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index d3b785aa14..40d2f72b4c 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -581,8 +581,7 @@ 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}. +map_key_clean(#k_nil{}) -> k_nil. %% call_type(Module, Function, Arity) -> call | bif | apply | error. diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl index c4f54a7970..cd4b5fd674 100644 --- a/lib/compiler/src/v3_life.erl +++ b/lib/compiler/src/v3_life.erl @@ -324,8 +324,7 @@ type(k_binary) -> binary; type(k_bin_seg) -> bin_seg; type(k_bin_int) -> bin_int; type(k_bin_end) -> bin_end; -type(k_map) -> map; -type(k_map_pair) -> map_pair. +type(k_map) -> map. %% variable(Klit) -> Lit. %% var_list([Klit]) -> [Lit]. diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl index 9c986576d5..6a7036d728 100644 --- a/lib/compiler/test/core_fold_SUITE.erl +++ b/lib/compiler/test/core_fold_SUITE.erl @@ -23,7 +23,7 @@ t_element/1,setelement/1,t_length/1,append/1,t_apply/1,bifs/1, eq/1,nested_call_in_case/1,guard_try_catch/1,coverage/1, unused_multiple_values_error/1,unused_multiple_values/1, - multiple_aliases/1,redundant_boolean_clauses/1]). + multiple_aliases/1,redundant_boolean_clauses/1,mixed_matching_clauses/1]). -export([foo/0,foo/1,foo/2,foo/3]). @@ -40,7 +40,7 @@ groups() -> [t_element,setelement,t_length,append,t_apply,bifs, eq,nested_call_in_case,guard_try_catch,coverage, unused_multiple_values_error,unused_multiple_values, - multiple_aliases,redundant_boolean_clauses]}]. + multiple_aliases,redundant_boolean_clauses,mixed_matching_clauses]}]. init_per_suite(Config) -> @@ -373,5 +373,15 @@ redundant_boolean_clauses(Config) when is_list(Config) -> true -> yes end. +mixed_matching_clauses(Config) when is_list(Config) -> + 0 = case #{} of + #{} -> 0; + a -> 1 + end, + 0 = case <<>> of + <<>> -> 0; + a -> 1 + end, + ok. id(I) -> I. diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl index 90eae6fb4f..cc018e4305 100644 --- a/lib/compiler/test/map_SUITE.erl +++ b/lib/compiler/test/map_SUITE.erl @@ -40,6 +40,8 @@ t_build_and_match_over_alloc/1, t_build_and_match_empty_val/1, t_build_and_match_val/1, + t_build_and_match_nil/1, + t_build_and_match_structure/1, %% errors in 17.0-rc1 t_update_values/1, @@ -68,6 +70,8 @@ all() -> [ t_build_and_match_over_alloc, t_build_and_match_empty_val, t_build_and_match_val, + t_build_and_match_nil, + t_build_and_match_structure, %% errors in 17.0-rc1 t_update_values, @@ -118,6 +122,7 @@ t_build_and_match_literals(Config) when is_list(Config) -> {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))), {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))), {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))), + {'EXIT',{badarg,_}} = (catch id(#{<<0:258>> =>"three"})), ok. t_build_and_match_aliasing(Config) when is_list(Config) -> @@ -234,7 +239,8 @@ t_update_assoc(Config) when is_list(Config) -> %% Errors cases. BadMap = id(badmap), {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}), - + {'EXIT',{badarg,_}} = (catch <<>>#{nonexisting=>val}), + {'EXIT',{badarg,_}} = (catch M0#{<<0:257>> => val}), %% limitation ok. t_update_exact(Config) when is_list(Config) -> @@ -262,6 +268,8 @@ t_update_exact(Config) when is_list(Config) -> {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + {'EXIT',{badarg,_}} = (catch <<>>#{nonexisting:=val}), + {'EXIT',{badarg,_}} = (catch M0#{<<0:257>> := val}), %% limitation ok. t_update_values(Config) when is_list(Config) -> @@ -561,6 +569,32 @@ t_build_and_match_val(Config) when is_list(Config) -> test_server:fail({no_match, Other}) end. +t_build_and_match_nil(Config) when is_list(Config) -> + %% literals removed the coverage + V1 = id(cookie), + V2 = id(cake), + V3 = id(crisps), + + #{ [] := V1, "treat" := V2, {your,treat} := V3 } = id(#{ + {your,treat} => V3, + "treat" => V2, + [] => V1 }), + #{ [] := V3, [] := V3 } = id(#{ [] => V1, [] => V3 }), + ok. + +t_build_and_match_structure(Config) when is_list(Config) -> + V2 = id("it"), + S = id([42,{"hi", "=)", #{ "a" => 42, any => any, val => "get_" ++ V2}}]), + + %% match deep map values + V2 = case S of + [42,{"hi",_, #{ "a" := 42, val := "get_" ++ V1, any := _ }}] -> V1 + end, + %% match deep map + ok = case S of + [42,{"hi",_, #{ }}] -> ok + end, + ok. %% Use this function to avoid compile-time evaluation of an expression. id(I) -> I. diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl index c3b02819f9..ad4ad91f74 100644 --- a/lib/compiler/test/warnings_SUITE.erl +++ b/lib/compiler/test/warnings_SUITE.erl @@ -573,7 +573,48 @@ maps(Config) when is_list(Config) -> ">>, [], {warnings,[{3,sys_core_fold,no_clause_match}, - {9,sys_core_fold,nomatch_clause_type}]}}], + {9,sys_core_fold,nomatch_clause_type}]}}, + {bad_map_src1, + <<" + t() -> + M = {a,[]}, + {'EXIT',{badarg,_}} = (catch(M#{ a => 1})), + ok. + ">>, + [], + {warnings,[{4,v3_kernel,bad_map}]}}, + {bad_map_src2, + <<" + t() -> + M = id({a,[]}), + {'EXIT',{badarg,_}} = (catch(M#{ a => 1})), + ok. + id(I) -> I. + ">>, + [inline], + {warnings,[{4,v3_kernel,bad_map}]}}, + {bad_map_src3, + <<" + t() -> + {'EXIT',{badarg,_}} = (catch <<>>#{ a := 1}), + ok. + ">>, + [], + {warnings,[{3,v3_core,bad_map}]}}, + {bad_map_literal_key, + <<" + t() -> + V = id(1), + M = id(#{ <<$h,$i>> => V }), + V = case M of + #{ <<0:257>> := Val } -> Val; + #{ <<$h,$i>> := Val } -> Val + end, + ok. + id(I) -> I. + ">>, + [], + {warnings,[{6,v3_core,nomatch}]}}], run(Config, Ts), ok. |