aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler')
-rw-r--r--lib/compiler/doc/src/notes.xml69
-rw-r--r--lib/compiler/src/beam_block.erl4
-rw-r--r--lib/compiler/src/beam_validator.erl25
-rw-r--r--lib/compiler/src/cerl_trees.erl4
-rwxr-xr-xlib/compiler/src/genop.tab10
-rw-r--r--lib/compiler/src/sys_core_fold.erl6
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl22
-rw-r--r--lib/compiler/test/compile_SUITE.erl44
-rw-r--r--lib/compiler/test/map_SUITE.erl58
-rw-r--r--lib/compiler/vsn.mk2
10 files changed, 213 insertions, 31 deletions
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index 6db8d19b5a..3c06e4f98e 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -32,6 +32,49 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 6.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix cerl_trees:label/2 bug with map K/V swap</p>
+ <p>
+ Own Id: OTP-13091</p>
+ </item>
+ <item>
+ <p>
+ Warnings produced when the '<c>bin_opt_info</c>' option
+ was given could sometimes lack filenames and line
+ numbers. (Thanks to José Valim for reporting this bug.)</p>
+ <p>
+ Own Id: OTP-13113</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Compiler 6.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix <c>get_map_elements</c> register corruption</p>
+ <p>
+ Instruction <c>get_map_elements</c> might destroy target
+ registers when the fail-label is taken. Only seen for
+ patterns with two, and only two, target registers.
+ Specifically if we copy one register and then jump.</p>
+ <p>
+ Own Id: OTP-12967</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 6.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -438,22 +481,28 @@
<p>
EEP43: New data type - Maps</p>
<p>
- With Maps you may for instance: <taglist> <item><c>M0 =
- #{ a =&gt; 1, b =&gt; 2}, % create
- associations</c></item> <item><c>M1 = M0#{ a := 10 }, %
- update values</c></item> <item><c>M2 = M1#{ "hi" =&gt;
- "hello"}, % add new associations</c></item> <item><c>#{
- "hi" := V1, a := V2, b := V3} = M2. % match keys with
- values</c></item> </taglist></p>
+ With Maps you may for instance:</p>
+ <taglist>
+ <tag/> <item><c>M0 = #{ a =&gt; 1, b =&gt; 2}, % create
+ associations</c></item>
+ <tag/><item><c>M1 = M0#{ a := 10 }, % update values</c></item>
+ <tag/><item><c>M2 = M1#{ "hi" =&gt;
+ "hello"}, % add new associations</c></item>
+ <tag/><item><c>#{ "hi" := V1, a := V2, b := V3} = M2.
+ % match keys with values</c></item>
+ </taglist>
<p>
For information on how to use Maps please see Map Expressions in the
<seealso marker="doc/reference_manual:expressions#map_expressions">
Reference Manual</seealso>.</p>
<p>
The current implementation is without the following
- features: <taglist> <item>No variable keys</item>
- <item>No single value access</item> <item>No map
- comprehensions</item> </taglist></p>
+ features:</p>
+ <taglist>
+ <tag/><item>No variable keys</item>
+ <tag/><item>No single value access</item>
+ <tag/><item>No map comprehensions</item>
+ </taglist>
<p>
Note that Maps is <em>experimental</em> during OTP 17.0.</p>
<p>
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 2def3de7f3..0321b1c07b 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -251,7 +251,9 @@ opt([{set,_,_,{line,_}}=Line1,
{set,[D2],[{integer,Idx2},Reg],{bif,element,{f,0}}}=I2|Is])
when Idx1 < Idx2, D1 =/= D2, D1 =/= Reg, D2 =/= Reg ->
opt([Line2,I2,Line1,I1|Is]);
-opt([{set,Ds0,Ss,Op}|Is0]) ->
+opt([{set,[_|_],_Ss,{get_map_elements,_F}}=I|Is]) ->
+ [I|opt(Is)];
+opt([{set,Ds0,Ss,Op}|Is0]) ->
{Ds,Is} = opt_moves(Ds0, Is0),
[{set,Ds,Ss,Op}|opt(Is)];
opt([{'%live',_,_}=I|Is]) ->
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 942d69a756..6004f1974e 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -758,10 +758,20 @@ valfun_4(_, _) ->
verify_get_map(Fail, Src, List, Vst0) ->
assert_type(map, Src, Vst0),
- Vst1 = branch_state(Fail, Vst0),
+ Vst1 = foldl(fun(D, Vsti) ->
+ case is_reg_defined(D,Vsti) of
+ true -> set_type_reg(term,D,Vsti);
+ false -> Vsti
+ end
+ end, Vst0, extract_map_vals(List)),
+ Vst2 = branch_state(Fail, Vst1),
Keys = extract_map_keys(List),
assert_unique_map_keys(Keys),
- verify_get_map_pair(List,Vst0,Vst1).
+ verify_get_map_pair(List,Vst0,Vst2).
+
+extract_map_vals([_Key,Val|T]) ->
+ [Val|extract_map_vals(T)];
+extract_map_vals([]) -> [].
extract_map_keys([Key,_Val|T]) ->
[Key|extract_map_keys(T)];
@@ -1093,6 +1103,17 @@ set_catch_end({y,Y}, #vst{current=#st{y=Ys0}=St}=Vst) ->
Ys = gb_trees:update(Y, initialized, Ys0),
Vst#vst{current=St#st{y=Ys}}.
+
+is_reg_defined({x,_}=Reg, Vst) -> is_type_defined_x(Reg, Vst);
+is_reg_defined({y,_}=Reg, Vst) -> is_type_defined_y(Reg, Vst);
+is_reg_defined(V, #vst{}) -> error({not_a_register, V}).
+
+is_type_defined_x({x,X}, #vst{current=#st{x=Xs}}) ->
+ gb_trees:is_defined(X,Xs).
+
+is_type_defined_y({y,Y}, #vst{current=#st{y=Ys}}) ->
+ gb_trees:is_defined(Y,Ys).
+
assert_term(Src, Vst) ->
get_term_type(Src, Vst),
ok.
diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl
index 2c9b72a30b..58bb18e34a 100644
--- a/lib/compiler/src/cerl_trees.erl
+++ b/lib/compiler/src/cerl_trees.erl
@@ -731,8 +731,8 @@ label(T, N, Env) ->
{ann_c_map(As, M, Ts), N3};
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),
+ {Key, N2} = label(map_pair_key(T), N1, Env),
+ {Val, N3} = label(map_pair_val(T), N2, Env),
{As, N4} = label_ann(T, N3),
{ann_c_map_pair(As,Op,Key,Val), N4};
'let' ->
diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab
index 8124729b35..3a877f2403 100755
--- a/lib/compiler/src/genop.tab
+++ b/lib/compiler/src/genop.tab
@@ -45,7 +45,7 @@ BEAM_FORMAT_NUMBER=0
## Save the next instruction as the return address in the CP register.
4: call/2
-## @spec call_last Arity Label Dellocate
+## @spec call_last Arity Label Deallocate
## @doc Deallocate and do a tail recursive call to the function at Label.
## Do not update the CP register.
## Before the call deallocate Deallocate words of stack.
@@ -137,7 +137,7 @@ BEAM_FORMAT_NUMBER=0
# Sending & receiving.
#
## @spec send
-## @doc Send argument in x(0) as a message to the destination process in x(0).
+## @doc Send argument in x(1) as a message to the destination process in x(0).
## The message in x(1) ends up as the result of the send in x(0).
20: send/0
@@ -164,12 +164,12 @@ BEAM_FORMAT_NUMBER=0
25: wait/1
## @spec wait_timeout Lable Time
-## @doc Sets up a timeout of Time milllisecons and saves the address of the
+## @doc Sets up a timeout of Time milliseconds and saves the address of the
## following instruction as the entry point if the timeout triggers.
26: wait_timeout/2
#
-# Arithmethic opcodes.
+# Arithmetic opcodes.
#
27: -m_plus/4
28: -m_minus/4
@@ -316,7 +316,7 @@ BEAM_FORMAT_NUMBER=0
66: get_tuple_element/3
## @spec set_tuple_element NewElement Tuple Position
-## @doc Update the element at postition Position of the tuple Tuple
+## @doc Update the element at position Position of the tuple Tuple
## with the new element NewElement.
67: set_tuple_element/3
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 27d023d067..65699ccda9 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -3091,12 +3091,12 @@ bsm_ensure_no_partition_2([#c_var{name=V}|Ps], N, G, Vstate, S) ->
bsm_ensure_no_partition_2([_|Ps], N, G, _, S) ->
bsm_ensure_no_partition_2(Ps, N-1, G, bin_argument_order, S).
-bsm_ensure_no_partition_after([#c_clause{pats=Ps}|Cs], Pos) ->
+bsm_ensure_no_partition_after([#c_clause{pats=Ps}=C|Cs], Pos) ->
case nth(Pos, Ps) of
#c_var{} ->
bsm_ensure_no_partition_after(Cs, Pos);
- P ->
- bsm_problem(P, bin_partition)
+ _ ->
+ bsm_problem(C, bin_partition)
end;
bsm_ensure_no_partition_after([], _) -> ok.
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 6e138b0a43..b4601b0798 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -36,7 +36,7 @@
match_string/1,zero_width/1,bad_size/1,haystack/1,
cover_beam_bool/1,matched_out_size/1,follow_fail_branch/1,
no_partition/1,calling_a_binary/1,binary_in_map/1,
- match_string_opt/1]).
+ match_string_opt/1,map_and_binary/1]).
-export([coverage_id/1,coverage_external_ignore/2]).
@@ -62,7 +62,7 @@ groups() ->
otp_7498,match_string,zero_width,bad_size,haystack,
cover_beam_bool,matched_out_size,follow_fail_branch,
no_partition,calling_a_binary,binary_in_map,
- match_string_opt]}].
+ match_string_opt,map_and_binary]}].
init_per_suite(Config) ->
@@ -1225,6 +1225,24 @@ match_string_opt(Config) when is_list(Config) ->
do_match_string_opt({<<1>>,{v,V}}=T) ->
{x,V,T}.
+%% If 'bin_opt_info' was given the warning would lack filename
+%% and line number.
+
+map_and_binary(_Config) ->
+ {<<"10">>,<<"37">>,<<"am">>} = do_map_and_binary(<<"10:37am">>),
+ Map1 = #{time => "noon"},
+ {ok,Map1} = do_map_and_binary(Map1),
+ Map2 = #{hour => 8, min => 42},
+ {8,42,Map2} = do_map_and_binary(Map2),
+ ok.
+
+do_map_and_binary(<<Hour:2/bytes, $:, Min:2/bytes, Rest/binary>>) ->
+ {Hour, Min, Rest};
+do_map_and_binary(#{time := _} = T) ->
+ {ok, T};
+do_map_and_binary(#{hour := Hour, min := Min} = T) ->
+ {Hour, Min, T}.
+
check(F, R) ->
R = F().
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index df401ccc2b..cbdd9ce8cd 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -31,7 +31,9 @@
other_output/1, encrypted_abstr/1,
bad_record_use1/1, bad_record_use2/1, strict_record/1,
missing_testheap/1, cover/1, env/1, core/1, asm/1,
- sys_pre_attributes/1, dialyzer/1]).
+ sys_pre_attributes/1, dialyzer/1,
+ warnings/1
+ ]).
-export([init/3]).
@@ -48,7 +50,7 @@ all() ->
other_output, encrypted_abstr,
{group, bad_record_use}, strict_record,
missing_testheap, cover, env, core, asm,
- sys_pre_attributes, dialyzer].
+ sys_pre_attributes, dialyzer, warnings].
groups() ->
[{bad_record_use, [],
@@ -895,6 +897,44 @@ dialyzer(Config) ->
[{a,b,c}] = M:M(),
ok.
+
+%% Test that warnings contain filenames and line numbers.
+warnings(_Config) ->
+ TestDir = filename:dirname(code:which(?MODULE)),
+ Files = filelib:wildcard(filename:join(TestDir, "*.erl")),
+ test_lib:p_run(fun do_warnings/1, Files).
+
+do_warnings(F) ->
+ {ok,_,_,Ws} = compile:file(F, [binary,bin_opt_info,return]),
+ do_warnings_1(Ws, F).
+
+do_warnings_1([{"no_file",Ws}|_], F) ->
+ io:format("~s:\nMissing file for warnings: ~p\n",
+ [F,Ws]),
+ error;
+do_warnings_1([{Name,Ws}|T], F) ->
+ case filename:extension(Name) of
+ ".erl" ->
+ do_warnings_2(Ws, T, F);
+ _ ->
+ io:format("~s:\nNo .erl extension\n", [F]),
+ error
+ end;
+do_warnings_1([], _) -> ok.
+
+do_warnings_2([{Int,_,_}=W|T], Next, F) ->
+ if
+ is_integer(Int) ->
+ do_warnings_2(T, Next, F);
+ true ->
+ io:format("~s:\nMissing line number: ~p\n",
+ [F,W]),
+ error
+ end;
+do_warnings_2([], Next, F) ->
+ do_warnings_1(Next, F).
+
+
%%%
%%% Utilities.
%%%
diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl
index abc12a359d..411b15eebe 100644
--- a/lib/compiler/test/map_SUITE.erl
+++ b/lib/compiler/test/map_SUITE.erl
@@ -63,7 +63,10 @@
%% errors in 17.0-rc1
t_update_values/1,
t_expand_map_update/1,
- t_export/1
+ t_export/1,
+
+ %% errors in 18
+ t_register_corruption/1
]).
suite() -> [].
@@ -108,11 +111,13 @@ all() ->
t_build_and_match_nil,
t_build_and_match_structure,
-
%% errors in 17.0-rc1
t_update_values,
t_expand_map_update,
- t_export
+ t_export,
+
+ %% errors in 18
+ t_register_corruption
].
groups() -> [].
@@ -1827,6 +1832,53 @@ map_guard_sequence_mixed(K1,K2,M) ->
#{ K1 := 1, c := 6, K2 := 8, h := 3} -> 6
end.
+%% register corruption discovered in 18 due to
+%% get_map_elements might destroys registers when fail-label is taken.
+%% Only seen when patterns have two targets,
+%% specifically: we copy one register, and then jump.
+%% {test,is_map,{f,5},[{x,1}]}.
+%%
+%% {get_map_elements,{f,7},{x,1},{list,[{atom,a},{x,1},{atom,b},{x,2}]}}.
+%% %% if 'a' exists but not 'b' {x,1} is overwritten, jump {f,7}
+%%
+%% {move,{integer,1},{x,0}}.
+%% {call_only,3,{f,10}}.
+%%
+%% {label,7}.
+%% {get_map_elements,{f,8},{x,1},{list,[{atom,b},{x,2}]}}.
+%% %% {x,1} (src) is now corrupt
+%%
+%% {move,{x,0},{x,1}}.
+%% {move,{integer,2},{x,0}}.
+%% {call_only,3,{f,10}}.
+%%
+%% Only happens in beam_block opt_move pass with two destinations.
+
+t_register_corruption(Config) when is_list(Config) ->
+ M = #{a=> <<"value">>, c=>3},
+ {3,wanted,<<"value">>} = register_corruption_bar(M,wanted),
+ {3,wanted,<<"value">>} = register_corruption_foo(wanted,M),
+ ok.
+
+register_corruption_foo(A,#{a := V1, b := V2}) ->
+ register_corruption_dummy_call(1,V1,V2);
+register_corruption_foo(A,#{b := V}) ->
+ register_corruption_dummy_call(2,A,V);
+register_corruption_foo(A,#{a := V}) ->
+ register_corruption_dummy_call(3,A,V).
+
+register_corruption_bar(M,A) ->
+ case M of
+ #{a := V1, b := V2} ->
+ register_corruption_dummy_call(1,V1,V2);
+ #{b := V} ->
+ register_corruption_dummy_call(2,A,V);
+ #{a := V} ->
+ register_corruption_dummy_call(3,A,V)
+ end.
+
+
+register_corruption_dummy_call(A,B,C) -> {A,B,C}.
t_frequency_table(Config) when is_list(Config) ->
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index 69f71ba5dd..c5089ff57e 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 6.0
+COMPILER_VSN = 6.0.2