diff options
author | Björn Gustavsson <[email protected]> | 2015-03-31 08:47:57 +0200 |
---|---|---|
committer | Björn Gustavsson <[email protected]> | 2015-04-15 15:12:33 +0200 |
commit | 5bb8262bd4ae8dbcc7438e80de5cedac7ccee1af (patch) | |
tree | 5e238aa9ab7e7dad021a56cb7ac9d0c07a54b46e /lib | |
parent | 44c5a955c8409cc98a5fbdec7898de46b9e5dfbe (diff) | |
download | otp-5bb8262bd4ae8dbcc7438e80de5cedac7ccee1af.tar.gz otp-5bb8262bd4ae8dbcc7438e80de5cedac7ccee1af.tar.bz2 otp-5bb8262bd4ae8dbcc7438e80de5cedac7ccee1af.zip |
Raise more descriptive error messages for failed map operations
According to EEP-43 for maps, a 'badmap' exception should be
generated when an attempt is made to update non-map term such as:
<<>>#{a=>42}
That was not implemented in the OTP 17.
José Valim suggested that we should take the opportunity to
improve the errors coming from map operations:
http://erlang.org/pipermail/erlang-questions/2015-February/083588.html
This commit implement better errors from map operations similar
to his suggestion.
When a map update operation (Map#{...}) or a BIF that expects a map
is given a non-map term, the exception will be:
{badmap,Term}
This kind of exception is similar to the {badfun,Term} exception
from operations that expect a fun.
When a map operation requires a key that is not present in a map,
the following exception will be raised:
{badkey,Key}
José Valim suggested that the exception should be
{badkey,Key,Map}. We decided not to do that because the map
could potentially be huge and cause problems if the error
propagated through links to other processes.
For BIFs, it could be argued that the exceptions could be simply
'badmap' and 'badkey', because the bad map and bad key can be found in
the argument list for the BIF in the stack backtrace. However, for the
map update operation (Map#{...}), the bad map or bad key will not be
included in the stack backtrace, so that information must be included
in the exception reason itself. For consistency, the BIFs should raise
the same exceptions as update operation.
If more than one key is missing, it is undefined which of
keys that will be reported in the {badkey,Key} exception.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/compiler/src/sys_core_fold.erl | 10 | ||||
-rw-r--r-- | lib/compiler/src/v3_core.erl | 38 | ||||
-rw-r--r-- | lib/compiler/test/map_SUITE.erl | 83 | ||||
-rw-r--r-- | lib/compiler/test/warnings_SUITE.erl | 4 | ||||
-rw-r--r-- | lib/debugger/src/dbg_ieval.erl | 16 | ||||
-rw-r--r-- | lib/debugger/test/int_eval_SUITE.erl | 2 | ||||
-rw-r--r-- | lib/debugger/test/map_SUITE.erl | 161 | ||||
-rw-r--r-- | lib/hipe/icode/hipe_beam_to_icode.erl | 15 | ||||
-rw-r--r-- | lib/hipe/test/maps_SUITE_data/maps_map_size.erl | 6 | ||||
-rw-r--r-- | lib/hipe/test/maps_SUITE_data/maps_put_map_assoc.erl | 2 | ||||
-rw-r--r-- | lib/hipe/test/maps_SUITE_data/maps_put_map_exact.erl | 4 | ||||
-rw-r--r-- | lib/hipe/test/maps_SUITE_data/maps_update_assoc.erl | 2 | ||||
-rw-r--r-- | lib/hipe/test/maps_SUITE_data/maps_update_exact.erl | 10 | ||||
-rw-r--r-- | lib/hipe/test/maps_SUITE_data/maps_update_map_expressions.erl | 6 | ||||
-rw-r--r-- | lib/stdlib/src/erl_eval.erl | 20 | ||||
-rw-r--r-- | lib/stdlib/test/erl_eval_SUITE.erl | 5 |
16 files changed, 215 insertions, 169 deletions
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 0d020578f5..beab2ce897 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -70,7 +70,8 @@ -export([module/2,format_error/1]). -import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,all/2,any/2, - reverse/1,reverse/2,member/2,nth/2,flatten/1,unzip/1]). + reverse/1,reverse/2,member/2,nth/2,flatten/1, + unzip/1,keyfind/3]). -import(cerl, [ann_c_cons/3,ann_c_map/3,ann_c_tuple/2]). @@ -1624,12 +1625,11 @@ eval_case(Case, _) -> Case. eval_case_warn(#c_primop{anno=Anno, name=#c_literal{val=match_fail}, - args=[#c_literal{val=Reason}]}=Core) - when is_atom(Reason) -> - case member(eval_failure, Anno) of + args=[_]}=Core) -> + case keyfind(eval_failure, 1, Anno) of false -> ok; - true -> + {eval_failure,Reason} -> %% Example: M = not_map, M#{k:=v} add_warning(Core, {eval_failure,Reason}) end; diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index c954d21e59..ed7b55df07 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -538,19 +538,8 @@ expr({tuple,L,Es0}, St0) -> {annotate_tuple(A, Es1, St1),Eps,St1}; expr({map,L,Es0}, St0) -> map_build_pairs(#c_literal{val=#{}}, Es0, full_anno(L, St0), St0); -expr({map,L,M0,Es0}, St0) -> - try expr_map(M0,Es0,lineno_anno(L, St0),St0) of - {_,_,_}=Res -> Res - 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,M,Es}, St) -> + expr_map(M, Es, L, St); expr({bin,L,Es0}, St0) -> try expr_bin(Es0, full_anno(L, St0), St0) of {_,_,_}=Res -> Res @@ -758,11 +747,14 @@ make_bool_switch_guard(L, E, V, T, F) -> {clause,NegL,[V],[],[V]} ]}. -expr_map(M0, Es0, A, St0) -> +expr_map(M0, Es0, L, St0) -> {M1,Eps0,St1} = safe(M0, St0), + Badmap = badmap_term(M1, St1), + A = lineno_anno(L, St1), + Fc = fail_clause([], [{eval_failure,badmap}|A], Badmap), case is_valid_map_src(M1) of true -> - {M2,Eps1,St2} = map_build_pairs(M1, Es0, A, St1), + {M2,Eps1,St2} = map_build_pairs(M1, Es0, full_anno(L, St1), St1), M3 = case Es0 of [] -> M1; [_|_] -> M2 @@ -775,13 +767,23 @@ expr_map(M0, Es0, A, St0) -> name=#c_literal{anno=A,val=is_map}, args=[M1]}], body=[M3]}], - Fc = fail_clause([], [eval_failure|A], #c_literal{val=badarg}), Eps = Eps0 ++ Eps1, {#icase{anno=#a{anno=A},args=[],clauses=Cs,fc=Fc},Eps,St2}; false -> - throw({bad_map,bad_map}) + %% Not a map source. The update will always fail. + St2 = add_warning(L, badmap, St1), + #iclause{body=[Fail]} = Fc, + {Fail,Eps0,St2} end. +badmap_term(_Map, #core{in_guard=true}) -> + %% The code generator cannot handle complex error reasons + %% in guards. But the exact error reason does not matter anyway + %% since it is not user-visible. + #c_literal{val=badmap}; +badmap_term(Map, #core{in_guard=false}) -> + #c_tuple{es=[#c_literal{val=badmap},Map]}. + map_build_pairs(Map, Es0, Ann, St0) -> {Es,Pre,St1} = map_build_pairs_1(Es0, St0), {ann_c_map(Ann, Map, Es),Pre,St1}. @@ -2395,7 +2397,7 @@ format_error(nomatch) -> "pattern cannot possibly match"; format_error(bad_binary) -> "binary construction will fail because of a type mismatch"; -format_error(bad_map) -> +format_error(badmap) -> "map construction will fail because of a type mismatch". add_warning(Line, Term, #core{ws=Ws,file=[{file,File}]}=St) when Line >= 0 -> diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl index 7db467a0d2..8768e47b65 100644 --- a/lib/compiler/test/map_SUITE.erl +++ b/lib/compiler/test/map_SUITE.erl @@ -669,9 +669,9 @@ t_map_size(Config) when is_list(Config) -> false = map_is_size(M#{ "c" => 2}, 2), %% Error cases. - {'EXIT',{badarg,_}} = (catch map_size([])), - {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)), - {'EXIT',{badarg,_}} = (catch map_size(1)), + {'EXIT',{{badmap,[]},_}} = (catch map_size([])), + {'EXIT',{{badmap,<<1,2,3>>},_}} = (catch map_size(<<1,2,3>>)), + {'EXIT',{{badmap,1},_}} = (catch map_size(1)), ok. map_is_size(M,N) when map_size(M) =:= N -> true; @@ -874,9 +874,9 @@ t_update_map_expressions(Config) when is_list(Config) -> #{ "a" := b } = F(), - %% Error cases, FIXME: should be 'badmap'? - {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }), - {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }), + %% Error cases. + {'EXIT',{{badmap,<<>>},_}} = (catch (id(<<>>))#{ a := 42, b => 2 }), + {'EXIT',{{badmap,[]},_}} = (catch (id([]))#{ a := 42, b => 2 }), ok. @@ -897,8 +897,14 @@ 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',{{badmap,BadMap},_}} = (catch BadMap#{nonexisting=>val}), + {'EXIT',{{badmap,<<>>},_}} = (catch <<>>#{nonexisting=>val}), + + %% Evaluation order. + {'EXIT',{blurf,_}} = + (catch BadMap#{whatever=>id(error(blurf))}), + {'EXIT',{blurf,_}} = + (catch BadMap#{id(error(blurf))=>whatever}), ok. t_update_assoc_large(Config) when is_list(Config) -> @@ -965,8 +971,8 @@ t_update_assoc_large(Config) when is_list(Config) -> M2 = M0#{13.0:=wrong,13.0=>new}, %% Errors cases. - BadMap = id(badmap), - {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>M0}), + BadMap = id({no,map}), + {'EXIT',{{badmap,BadMap},_}} = (catch BadMap#{nonexisting=>M0}), ok. @@ -991,19 +997,29 @@ t_update_exact(Config) when is_list(Config) -> 1.0 => new_val4 }, %% Errors cases. - {'EXIT',{badarg,_}} = (catch ((id(nil))#{ a := b })), - {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), - {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), - {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), - {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), - {'EXIT',{badarg,_}} = (catch <<>>#{nonexisting:=val}), - {'EXIT',{badarg,_}} = (catch M0#{<<0:257>> := val}), %% limitation + {'EXIT',{{badmap,nil},_}} = (catch ((id(nil))#{ a := b })), + {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{{badkey,42},_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{{badkey,42.0},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + {'EXIT',{{badmap,<<>>},_}} = (catch <<>>#{nonexisting:=val}), + {'EXIT',{{badkey,<<0:257>>},_}} = + (catch M0#{<<0:257>> := val}), %limitation %% A workaround for a bug allowed an empty map to be updated. - {'EXIT',{badarg,_}} = (catch (id(#{}))#{a:=1}), - {'EXIT',{badarg,_}} = (catch #{}#{a:=1}), + {'EXIT',{{badkey,a},_}} = (catch (id(#{}))#{a:=1}), + {'EXIT',{{badkey,a},_}} = (catch #{}#{a:=1}), Empty = #{}, - {'EXIT',{badarg,_}} = (catch Empty#{a:=1}), + {'EXIT',{{badkey,a},_}} = (catch Empty#{a:=1}), + + %% Evaluation order. + BadMap = id([no,map]), + {'EXIT',{blurf,_}} = + (catch BadMap#{whatever:=id(error(blurf))}), + {'EXIT',{blurf,_}} = + (catch BadMap#{id(error(blurf)):=whatever}), + {'EXIT',{{badmap,BadMap},_}} = + (catch BadMap#{id(nonexisting):=whatever}), ok. t_update_exact_large(Config) when is_list(Config) -> @@ -1081,10 +1097,10 @@ t_update_exact_large(Config) when is_list(Config) -> M2 = M0#{13.0=>wrong,13.0:=new}, %% Errors cases. - {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), - {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), - {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), - {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{{badkey,42},_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{{badkey,42.0},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), ok. @@ -1666,8 +1682,8 @@ t_update_assoc_variables(Config) when is_list(Config) -> %% Errors cases. BadMap = id(badmap), - {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}), - {'EXIT',{badarg,_}} = (catch <<>>#{nonexisting=>val}), + {'EXIT',{{badmap,BadMap},_}} = (catch BadMap#{nonexisting=>val}), + {'EXIT',{{badmap,<<>>},_}} = (catch <<>>#{nonexisting=>val}), ok. t_update_exact_variables(Config) when is_list(Config) -> @@ -1697,13 +1713,14 @@ t_update_exact_variables(Config) when is_list(Config) -> #{ "wat" := 3, 2 := a } = id(#{ "wat" => 1, K2 => 2 }#{ K2 := a, "wat" := 3 }), %% Errors cases. - {'EXIT',{badarg,_}} = (catch ((id(nil))#{ a := b })), - {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), - {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), - {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), - {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), - {'EXIT',{badarg,_}} = (catch <<>>#{nonexisting:=val}), - {'EXIT',{badarg,_}} = (catch M0#{<<0:257>> := val}), %% limitation + {'EXIT',{{badmap,nil},_}} = (catch ((id(nil))#{ a := b })), + {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{{badkey,42},_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{{badkey,42.0},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + {'EXIT',{{badmap,<<>>},_}} = (catch <<>>#{nonexisting:=val}), + {'EXIT',{{badkey,<<0:257>>},_}} = + (catch M0#{<<0:257>> := val}), %limitation ok. t_nested_pattern_expressions(Config) when is_list(Config) -> diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl index d0b7c71be8..e996a55db6 100644 --- a/lib/compiler/test/warnings_SUITE.erl +++ b/lib/compiler/test/warnings_SUITE.erl @@ -583,7 +583,7 @@ maps(Config) when is_list(Config) -> ok. ">>, [], - {warnings,[{4,sys_core_fold,{eval_failure,badarg}}]}}, + {warnings,[{4,sys_core_fold,{eval_failure,badmap}}]}}, {bad_map_src2, <<" t() -> @@ -601,7 +601,7 @@ maps(Config) when is_list(Config) -> ok. ">>, [], - {warnings,[{3,v3_core,bad_map}]}}, + {warnings,[{3,v3_core,badmap}]}}, {ok_map_literal_key, <<" t() -> diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index 96f9f91808..cfc2a19ccd 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -658,16 +658,12 @@ expr({map,Line,Fs0}, Bs0, Ieval) -> expr({map,Line,E0,Fs0}, Bs0, Ieval0) -> Ieval = Ieval0#ieval{line=Line,top=false}, {value,E,Bs1} = expr(E0, Bs0, Ieval), - case E of - #{} -> - {Fs,Bs2} = eval_map_fields(Fs0, Bs0, Ieval), - Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi); - ({map_exact,K,V}, Mi) -> maps:update(K,V,Mi) - end, E, Fs), - {value,Value,merge_bindings(Bs2, Bs1, Ieval)}; - _ -> - exception(error, {badarg,E}, Bs1, Ieval) - end; + {Fs,Bs2} = eval_map_fields(Fs0, Bs0, Ieval), + _ = maps:put(k, v, E), %Validate map. + Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi); + ({map_exact,K,V}, Mi) -> maps:update(K,V,Mi) + end, E, Fs), + {value,Value,merge_bindings(Bs2, Bs1, Ieval)}; %% A block of statements expr({block,Line,Es},Bs,Ieval) -> seq(Es, Bs, Ieval#ieval{line=Line}); diff --git a/lib/debugger/test/int_eval_SUITE.erl b/lib/debugger/test/int_eval_SUITE.erl index 9d7ef238e3..cda5c97ec4 100644 --- a/lib/debugger/test/int_eval_SUITE.erl +++ b/lib/debugger/test/int_eval_SUITE.erl @@ -293,7 +293,7 @@ stacktrace(Config) when is_list(Config) -> maps(Config) when is_list(Config) -> Fun = fun () -> ?IM:empty_map_update([camembert]) end, - {'EXIT',{{badarg,[camembert]},_}} = spawn_eval(Fun), + {'EXIT',{{badmap,[camembert]},_}} = spawn_eval(Fun), [#{hello := 0, price := 0}] = spawn_eval(fun () -> ?IM:update_in_fun() end), ok. diff --git a/lib/debugger/test/map_SUITE.erl b/lib/debugger/test/map_SUITE.erl index ac2e16f00a..457863d982 100644 --- a/lib/debugger/test/map_SUITE.erl +++ b/lib/debugger/test/map_SUITE.erl @@ -671,9 +671,10 @@ t_map_size(Config) when is_list(Config) -> false = map_is_size(M#{ "c" => 2}, 2), %% Error cases. - {'EXIT',{badarg,_}} = (catch map_size([])), - {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)), - {'EXIT',{badarg,_}} = (catch map_size(1)), + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},_}} = + (catch map_size(T)) + end), ok. map_is_size(M,N) when map_size(M) =:= N -> true; @@ -849,9 +850,9 @@ t_update_map_expressions(Config) when is_list(Config) -> #{ a :=42, b:=42, c:=42 } = (maps:from_list([{a,1},{b,2},{c,3}]))#{ a := 42, b := 42, c := 42 }, #{ "a" :=1, "b":=42, "c":=42 } = (maps:from_list([{"a",1},{"b",2}]))#{ "b" := 42, "c" => 42 }, - %% Error cases, FIXME: should be 'badmap'? - {'EXIT',{{badarg,<<>>},_}} = (catch (id(<<>>))#{ a := 42, b => 2 }), - {'EXIT',{{badarg,[]},_}} = (catch (id([]))#{ a := 42, b => 2 }), + %% Error cases. + {'EXIT',{{badmap,<<>>},_}} = (catch (id(<<>>))#{ a := 42, b => 2 }), + {'EXIT',{{badmap,[]},_}} = (catch (id([]))#{ a := 42, b => 2 }), ok. @@ -867,8 +868,8 @@ t_update_assoc(Config) when is_list(Config) -> M2 = M0#{3.0:=wrong,3.0=>new}, %% Errors cases. - BadMap = id(badmap), - {'EXIT',{{badarg,BadMap},_}} = (catch BadMap#{nonexisting=>val}), + BadMap = id(not_a_good_map), + {'EXIT',{{badmap,BadMap},_}} = (catch BadMap#{nonexisting=>val}), ok. @@ -936,8 +937,8 @@ t_update_assoc_large(Config) when is_list(Config) -> M2 = M0#{13.0:=wrong,13.0=>new}, %% Errors cases. - BadMap = id(badmap), - {'EXIT',{{badarg,BadMap},_}} = (catch BadMap#{nonexisting=>M0}), + BadMap = id(a_bad_map), + {'EXIT',{{badmap,BadMap},_}} = (catch BadMap#{nonexisting=>M0}), ok. @@ -954,10 +955,25 @@ t_update_exact(Config) when is_list(Config) -> %% M2 = M0#{3=>wrong,3.0:=new}, %% FIXME %% Errors cases. - {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), - {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), - {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), - {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + do_badmap(fun(T) -> + {'EXIT',{{badmap,T},_}} = + (catch T#{nonexisting=>val}) + end), + Empty = id(#{}), + {'EXIT',{{badkey,nonexisting},_}} = (catch Empty#{nonexisting:=val}), + {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{{badkey,_},_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{{badkey,_},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + + %% Evaluation order. + BadMap = id([no,map]), + {'EXIT',{blurf,_}} = + (catch BadMap#{whatever:=id(error(blurf))}), + {'EXIT',{blurf,_}} = + (catch BadMap#{id(error(blurf)):=whatever}), + {'EXIT',{{badmap,BadMap},_}} = + (catch BadMap#{nonexisting:=whatever}), ok. t_update_exact_large(Config) when is_list(Config) -> @@ -1035,10 +1051,10 @@ t_update_exact_large(Config) when is_list(Config) -> M2 = M0#{13.0=>wrong,13.0:=new}, %% Errors cases. - {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), - {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), - {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), - {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{{badkey,_},_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{{badkey,_},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), ok. @@ -1443,11 +1459,11 @@ t_bif_map_get(Config) when is_list(Config) -> "v4" = maps:get(<<"k2">>, M#{ <<"k2">> => "v4" }), %% error case - {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,[])), - {'EXIT',{badarg, [{maps,get,_,_}|_]}} = (catch maps:get(a,<<>>)), - {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get({1,1}, #{{1,1.0} => "tuple"})), - {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{})), - {'EXIT',{bad_key,[{maps,get,_,_}|_]}} = (catch maps:get(a,#{ b=>1, c=>2})), + {'EXIT',{{badmap,[]},[{maps,get,_,_}|_]}} = (catch maps:get(a, [])), + {'EXIT',{{badmap,<<>>},[{maps,get,_,_}|_]}} = (catch maps:get(a, <<>>)), + {'EXIT',{{badkey,{1,1}},[{maps,get,_,_}|_]}} = (catch maps:get({1,1}, #{{1,1.0} => "tuple"})), + {'EXIT',{{badkey,a},[{maps,get,_,_}|_]}} = (catch maps:get(a, #{})), + {'EXIT',{{badkey,a},[{maps,get,_,_}|_]}} = (catch maps:get(a, #{ b=>1, c=>2})), ok. t_bif_map_find(Config) when is_list(Config) -> @@ -1471,8 +1487,10 @@ t_bif_map_find(Config) when is_list(Config) -> error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key - {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id([]))), - {'EXIT',{badarg,[{maps,find,_,_}|_]}} = (catch maps:find(a,id(<<>>))), + {'EXIT',{{badmap,[]},[{maps,find,_,_}|_]}} = + (catch maps:find(a, id([]))), + {'EXIT',{{badmap,<<>>},[{maps,find,_,_}|_]}} = + (catch maps:find(a, id(<<>>))), ok. @@ -1497,8 +1515,8 @@ t_bif_map_is_key(Config) when is_list(Config) -> false = maps:is_key(1.0, maps:put(1, "number", M1)), %% error case - {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id([]))), - {'EXIT',{badarg,[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a,id(<<>>))), + {'EXIT',{{badmap,[]},[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a, id([]))), + {'EXIT',{{badmap,<<>>},[{maps,is_key,_,_}|_]}} = (catch maps:is_key(a, id(<<>>))), ok. t_bif_map_keys(Config) when is_list(Config) -> @@ -1511,11 +1529,12 @@ t_bif_map_keys(Config) when is_list(Config) -> [4,int,"hi",<<"key">>] = lists:sort(maps:keys(M1)), %% error case - {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(1 bsl 65 + 3)), - {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(154)), - {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(atom)), - {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys([])), - {'EXIT',{badarg,[{maps,keys,_,_}|_]}} = (catch maps:keys(<<>>)), + BigNum = 1 bsl 65 + 3, + {'EXIT',{{badmap,BigNum},[{maps,keys,_,_}|_]}} = (catch maps:keys(BigNum)), + {'EXIT',{{badmap,154},[{maps,keys,_,_}|_]}} = (catch maps:keys(154)), + {'EXIT',{{badmap,atom},[{maps,keys,_,_}|_]}} = (catch maps:keys(atom)), + {'EXIT',{{badmap,[]},[{maps,keys,_,_}|_]}} = (catch maps:keys([])), + {'EXIT',{{badmap,<<>>},[{maps,keys,_,_}|_]}} = (catch maps:keys(<<>>)), ok. t_bif_map_new(Config) when is_list(Config) -> @@ -1544,9 +1563,10 @@ t_bif_map_merge(Config) when is_list(Config) -> {1,2} := "tuple", "hi" := "hello again", <<"key">> := <<"value">>} = maps:merge(M0,M1), %% error case - {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge((1 bsl 65 + 3), <<>>)), - {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(<<>>, id(#{ a => 1}))), - {'EXIT',{badarg,[{maps,merge,_,_}|_]}} = (catch maps:merge(id(#{ a => 2}), <<>> )), + BigNum = 1 bsl 65 + 3, + {'EXIT',{{badmap,BigNum},[{maps,merge,_,_}|_]}} = (catch maps:merge(BigNum, <<>>)), + {'EXIT',{{badmap,<<>>},[{maps,merge,_,_}|_]}} = (catch maps:merge(<<>>, id(#{ a => 1}))), + {'EXIT',{{badmap,<<>>},[{maps,merge,_,_}|_]}} = (catch maps:merge(id(#{ a => 2}), <<>> )), ok. @@ -1585,11 +1605,12 @@ t_bif_map_put(Config) when is_list(Config) -> true = is_members([number,wat,3,"hello",<<"other value">>],maps:values(M6)), %% error case - {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,1 bsl 65 + 3)), - {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,154)), - {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,atom)), - {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,[])), - {'EXIT',{badarg,[{maps,put,_,_}|_]}} = (catch maps:put(1,a,<<>>)), + BigNum = 1 bsl 65 + 3, + {'EXIT',{{badmap,BigNum},[{maps,put,_,_}|_]}} = (catch maps:put(1, a, BigNum)), + {'EXIT',{{badmap,154},[{maps,put,_,_}|_]}} = (catch maps:put(1, a, 154)), + {'EXIT',{{badmap,atom},[{maps,put,_,_}|_]}} = (catch maps:put(1, a, atom)), + {'EXIT',{{badmap,[]},[{maps,put,_,_}|_]}} = (catch maps:put(1, a, [])), + {'EXIT',{{badmap,<<>>},[{maps,put,_,_}|_]}} = (catch maps:put(1, a, <<>>)), ok. is_members(Ks,Ls) when length(Ks) =/= length(Ls) -> false; @@ -1621,9 +1642,10 @@ t_bif_map_update(Config) when is_list(Config) -> 4 := number, 18446744073709551629 := wazzup} = maps:update(18446744073709551629, wazzup, M0), %% error case - {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,{})), - {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(1,none,<<"value">>)), - {'EXIT',{badarg,[{maps,update,_,_}|_]}} = (catch maps:update(5,none,M0)), + {'EXIT',{{badmap,{}},[{maps,update,_,_}|_]}} = (catch maps:update(1, none, {})), + {'EXIT',{{badmap,<<"value">>},[{maps,update,_,_}|_]}} = + (catch maps:update(1, none, <<"value">>)), + {'EXIT',{{badkey,5},[{maps,update,_,_}|_]}} = (catch maps:update(5, none, M0)), ok. @@ -1659,12 +1681,13 @@ t_bif_map_remove(Config) when is_list(Config) -> #{ "hi" := "hello", int := 3, 4 := number} = maps:remove(18446744073709551629,maps:remove(<<"key">>,M0)), %% error case - {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,1 bsl 65 + 3)), - {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,154)), - {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,atom)), - {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(1,[])), - {'EXIT',{badarg,[{maps,remove,_,_}|_]}} = (catch maps:remove(a,<<>>)), - ok. + BigNum = 1 bsl 65 + 3, + {'EXIT',{{badmap,BigNum},[{maps,remove,_,_}|_]}} = (catch maps:remove(a, BigNum)), + {'EXIT',{{badmap,154},[{maps,remove,_,_}|_]}} = (catch maps:remove(1, 154)), + {'EXIT',{{badmap,atom},[{maps,remove,_,_}|_]}} = (catch maps:remove(a, atom)), + {'EXIT',{{badmap,[]},[{maps,remove,_,_}|_]}} = (catch maps:remove(1, [])), + {'EXIT',{{badmap,<<>>},[{maps,remove,_,_}|_]}} = (catch maps:remove(a, <<>>)), + ok. t_bif_map_values(Config) when is_list(Config) -> @@ -1680,10 +1703,11 @@ t_bif_map_values(Config) when is_list(Config) -> true = is_members([number,3,"hello",<<"value">>],maps:values(M1)), %% error case - {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(1 bsl 65 + 3)), - {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(atom)), - {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values([])), - {'EXIT',{badarg,[{maps,values,_,_}|_]}} = (catch maps:values(<<>>)), + BigNum = 1 bsl 65 + 3, + {'EXIT',{{badmap,BigNum},[{maps,values,_,_}|_]}} = (catch maps:values(BigNum)), + {'EXIT',{{badmap,atom},[{maps,values,_,_}|_]}} = (catch maps:values(atom)), + {'EXIT',{{badmap,[]},[{maps,values,_,_}|_]}} = (catch maps:values([])), + {'EXIT',{{badmap,<<>>},[{maps,values,_,_}|_]}} = (catch maps:values(<<>>)), ok. @@ -1854,8 +1878,8 @@ t_bif_map_to_list(Config) when is_list(Config) -> <<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10})), %% error cases - {'EXIT', {badarg,_}} = (catch maps:to_list(id(a))), - {'EXIT', {badarg,_}} = (catch maps:to_list(id(42))), + {'EXIT', {{badmap,a},_}} = (catch maps:to_list(id(a))), + {'EXIT', {{badmap,42},_}} = (catch maps:to_list(id(42))), ok. @@ -2071,9 +2095,9 @@ t_update_assoc_variables(Config) when is_list(Config) -> #{ <<0:258>> := val } = id(M0#{<<0:258>> => val}), %% binary limitation %% Errors cases. - BadMap = id(badmap), - {'EXIT',{{badarg,_},_}} = (catch BadMap#{nonexisting=>val}), - {'EXIT',{{badarg,_},_}} = (catch <<>>#{nonexisting=>val}), + BadMap = id(a_bad_map), + {'EXIT',{{badmap,BadMap},_}} = (catch BadMap#{nonexisting=>val}), + {'EXIT',{{badmap,<<>>},_}} = (catch <<>>#{nonexisting=>val}), ok. t_update_exact_variables(Config) when is_list(Config) -> @@ -2101,14 +2125,14 @@ t_update_exact_variables(Config) when is_list(Config) -> 1.0 => new_val4 }, %% Errors cases. - {'EXIT',{{badarg,_},_}} = (catch ((id(nil))#{ a := b })), - {'EXIT',{{badarg,_},_}} = (catch <<>>#{nonexisting:=val}), - - {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), - {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), - {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), - {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), - {'EXIT',{badarg,_}} = (catch M0#{<<0:257>> := val}), %% limitation + {'EXIT',{{badmap,_},_}} = (catch ((id(nil))#{ a := b })), + {'EXIT',{{badmap,_},_}} = (catch <<>>#{nonexisting:=val}), + + {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{{badkey,_},_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{{badkey,_},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + {'EXIT',{{badkey,_},_}} = (catch M0#{<<0:257>> := val}), %% limitation ok. t_nested_pattern_expressions(Config) when is_list(Config) -> @@ -2359,6 +2383,11 @@ t_build_and_match_structure(Config) when is_list(Config) -> ok. +do_badmap(Test) -> + Terms = [Test,fun erlang:abs/1,make_ref(),self(),0.0/id(-1), + <<1:1>>,<<>>,<<1,2,3>>, + [],{a,b,c},[a,b],atom,10.0,42,(1 bsl 65) + 3], + [Test(T) || T <- Terms]. %% Use this function to avoid compile-time evaluation of an expression. id(I) -> I. diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl index 4691662f9f..3e099fcc25 100644 --- a/lib/hipe/icode/hipe_beam_to_icode.erl +++ b/lib/hipe/icode/hipe_beam_to_icode.erl @@ -1584,11 +1584,7 @@ gen_put_map_instrs(exists, Op, TempMapVar, Dst, FailLbl, Pairs, Env) -> end, {[IsMapCode, TrueLabel, PutInstructions, ReturnLbl], Env1}; gen_put_map_instrs(new, Op, TempMapVar, Dst, new, Pairs, Env) -> - TrueLabel = mk_label(new), FailLbl = mk_label(new), - IsMapCode = hipe_icode:mk_type([TempMapVar], map, - hipe_icode:label_name(TrueLabel), - hipe_icode:label_name(FailLbl)), DstMapVar = mk_var(Dst), {ReturnLbl, PutInstructions, Env1} = case Op of @@ -1596,10 +1592,10 @@ gen_put_map_instrs(new, Op, TempMapVar, Dst, new, Pairs, Env) -> trans_put_map_assoc(TempMapVar, DstMapVar, Pairs, Env, []); exact -> trans_put_map_exact(TempMapVar, DstMapVar, - hipe_icode:label_name(FailLbl), Pairs, Env, []) + none, Pairs, Env, []) end, Fail = hipe_icode:mk_fail([hipe_icode:mk_const(badarg)], error), - {[IsMapCode, TrueLabel, PutInstructions, FailLbl, Fail, ReturnLbl], Env1}. + {[PutInstructions, FailLbl, Fail, ReturnLbl], Env1}. %%----------------------------------------------------------------------- %% This function generates the instructions needed to insert several @@ -1629,6 +1625,13 @@ trans_put_map_exact(MapVar, DestMapVar, _FLbl, [], Env, Acc) -> ReturnLbl = mk_label(new), GotoReturn = hipe_icode:mk_goto(hipe_icode:label_name(ReturnLbl)), {ReturnLbl, lists:reverse([GotoReturn, MoveToReturnVar | Acc]), Env}; +trans_put_map_exact(MapVar, DestMapVar, none, [Key, Value | Rest], Env, Acc) -> + {MoveKey, KeyVar, Env1} = mk_move_and_var(Key, Env), + {MoveVal, ValVar, Env2} = mk_move_and_var(Value, Env1), + BifCallPut = hipe_icode:mk_call([MapVar], maps, update, + [KeyVar, ValVar, MapVar], remote), + Acc1 = [BifCallPut, MoveVal, MoveKey | Acc], + trans_put_map_exact(MapVar, DestMapVar, none, Rest, Env2, Acc1); trans_put_map_exact(MapVar, DestMapVar, FLbl, [Key, Value | Rest], Env, Acc) -> SuccLbl = mk_label(new), {MoveKey, KeyVar, Env1} = mk_move_and_var(Key, Env), diff --git a/lib/hipe/test/maps_SUITE_data/maps_map_size.erl b/lib/hipe/test/maps_SUITE_data/maps_map_size.erl index 25c8e5d4c7..3cd2d90dfb 100644 --- a/lib/hipe/test/maps_SUITE_data/maps_map_size.erl +++ b/lib/hipe/test/maps_SUITE_data/maps_map_size.erl @@ -17,9 +17,9 @@ test() -> false = map_is_size(M#{ "c" => 2}, 2), %% Error cases. - {'EXIT',{badarg,_}} = (catch map_size([])), - {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)), - {'EXIT',{badarg,_}} = (catch map_size(1)), + {'EXIT',{{badmap,[]},_}} = (catch map_size([])), + {'EXIT',{{badmap,<<1,2,3>>},_}} = (catch map_size(<<1,2,3>>)), + {'EXIT',{{badmap,1},_}} = (catch map_size(1)), ok. map_is_size(M,N) when map_size(M) =:= N -> true; diff --git a/lib/hipe/test/maps_SUITE_data/maps_put_map_assoc.erl b/lib/hipe/test/maps_SUITE_data/maps_put_map_assoc.erl index 72ac9ce078..2fe4f204d1 100644 --- a/lib/hipe/test/maps_SUITE_data/maps_put_map_assoc.erl +++ b/lib/hipe/test/maps_SUITE_data/maps_put_map_assoc.erl @@ -8,7 +8,7 @@ test() -> true = assoc_guard(#{}), false = assoc_guard(not_a_map), #{a := true} = assoc_update(#{}), - {'EXIT', {badarg, [{?MODULE, assoc_update, 1, _}|_]}} + {'EXIT', {{badmap, not_a_map}, [{?MODULE, assoc_update, 1, _}|_]}} = (catch assoc_update(not_a_map)), ok = assoc_guard_clause(#{}), {'EXIT', {function_clause, [{?MODULE, assoc_guard_clause, _, _}|_]}} diff --git a/lib/hipe/test/maps_SUITE_data/maps_put_map_exact.erl b/lib/hipe/test/maps_SUITE_data/maps_put_map_exact.erl index 1cfcd80180..3c85289a36 100644 --- a/lib/hipe/test/maps_SUITE_data/maps_put_map_exact.erl +++ b/lib/hipe/test/maps_SUITE_data/maps_put_map_exact.erl @@ -9,9 +9,9 @@ test() -> false = exact_guard(not_a_map), true = exact_guard(#{a => false}), #{a := true} = exact_update(#{a => false}), - {'EXIT', {badarg, [{?MODULE, exact_update, 1, _}|_]}} + {'EXIT', {{badmap, not_a_map}, [{?MODULE, exact_update, 1, _}|_]}} = (catch exact_update(not_a_map)), - {'EXIT', {badarg, [{?MODULE, exact_update, 1, _}|_]}} + {'EXIT', {{badkey, a}, [{?MODULE, exact_update, 1, _}|_]}} = (catch exact_update(#{})), ok = exact_guard_clause(#{a => yes}), {'EXIT', {function_clause, [{?MODULE, exact_guard_clause, _, _}|_]}} diff --git a/lib/hipe/test/maps_SUITE_data/maps_update_assoc.erl b/lib/hipe/test/maps_SUITE_data/maps_update_assoc.erl index cc7c1353de..99228a1927 100644 --- a/lib/hipe/test/maps_SUITE_data/maps_update_assoc.erl +++ b/lib/hipe/test/maps_SUITE_data/maps_update_assoc.erl @@ -14,7 +14,7 @@ test() -> %% Errors cases. BadMap = id(badmap), - {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}), + {'EXIT',{{badmap,badmap},_}} = (catch BadMap#{nonexisting=>val}), ok. diff --git a/lib/hipe/test/maps_SUITE_data/maps_update_exact.erl b/lib/hipe/test/maps_SUITE_data/maps_update_exact.erl index 6e5acb3283..1c38820a7c 100644 --- a/lib/hipe/test/maps_SUITE_data/maps_update_exact.erl +++ b/lib/hipe/test/maps_SUITE_data/maps_update_exact.erl @@ -21,11 +21,11 @@ test() -> 1.0 => new_val4 }, %% Errors cases. - {'EXIT',{badarg,_}} = (catch ((id(nil))#{ a := b })), - {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), - {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), - {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), - {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + {'EXIT',{{badmap,nil},_}} = (catch ((id(nil))#{ a := b })), + {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{{badkey,_},_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{{badkey,_},_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{{badkey,_},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), ok. %% Use this function to avoid compile-time evaluation of an expression. diff --git a/lib/hipe/test/maps_SUITE_data/maps_update_map_expressions.erl b/lib/hipe/test/maps_SUITE_data/maps_update_map_expressions.erl index 181e3f18f7..213fc33d97 100644 --- a/lib/hipe/test/maps_SUITE_data/maps_update_map_expressions.erl +++ b/lib/hipe/test/maps_SUITE_data/maps_update_map_expressions.erl @@ -23,9 +23,9 @@ test() -> #{ "a" := b } = F(), - %% Error cases, FIXME: should be 'badmap'? - {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }), - {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }), + %% Error cases. + {'EXIT',{{badmap,<<>>},_}} = (catch (id(<<>>))#{ a := 42, b => 2 }), + {'EXIT',{{badmap,[]},_}} = (catch (id([]))#{ a := 42, b => 2 }), ok. %% Use this function to avoid compile-time evaluation of an expression. diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl index 371573dc23..e86e10b170 100644 --- a/lib/stdlib/src/erl_eval.erl +++ b/lib/stdlib/src/erl_eval.erl @@ -246,18 +246,14 @@ expr({record,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs) -> %% map expr({map,_,Binding,Es}, Bs0, Lf, Ef, RBs) -> {value, Map0, Bs1} = expr(Binding, Bs0, Lf, Ef, none), - case Map0 of - #{} -> - {Vs,Bs2} = eval_map_fields(Es, Bs0, Lf, Ef), - Map1 = lists:foldl(fun ({map_assoc,K,V}, Mi) -> - maps:put(K, V, Mi); - ({map_exact,K,V}, Mi) -> - maps:update(K, V, Mi) - end, Map0, Vs), - ret_expr(Map1, merge_bindings(Bs2, Bs1), RBs); - _ -> - erlang:raise(error, {badarg,Map0}, stacktrace()) - end; + {Vs,Bs2} = eval_map_fields(Es, Bs0, Lf, Ef), + _ = maps:put(k, v, Map0), %Validate map. + Map1 = lists:foldl(fun ({map_assoc,K,V}, Mi) -> + maps:put(K, V, Mi); + ({map_exact,K,V}, Mi) -> + maps:update(K, V, Mi) + end, Map0, Vs), + ret_expr(Map1, merge_bindings(Bs2, Bs1), RBs); expr({map,_,Es}, Bs0, Lf, Ef, RBs) -> {Vs,Bs} = eval_map_fields(Es, Bs0, Lf, Ef), ret_expr(lists:foldl(fun diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl index 3427f431c5..a750c5cace 100644 --- a/lib/stdlib/test/erl_eval_SUITE.erl +++ b/lib/stdlib/test/erl_eval_SUITE.erl @@ -1482,8 +1482,11 @@ eep43(Config) when is_list(Config) -> " #{ K1 := 1, K2 := 2, K3 := 3, {2,2} := 4} = Map " "end.", #{ 1 => 1, <<42:301>> => 2, {3,<<42:301>>} => 3, {2,2} => 4}), - error_check("[camembert]#{}.", {badarg,[camembert]}), + error_check("[camembert]#{}.", {badmap,[camembert]}), + error_check("[camembert]#{nonexisting:=v}.", {badmap,[camembert]}), error_check("#{} = 1.", {badmatch,1}), + error_check("[]#{a=>error(bad)}.", bad), + error_check("(#{})#{nonexisting:=value}.", {badkey,nonexisting}), ok. %% Check the string in different contexts: as is; in fun; from compiled code. |