aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler/test
diff options
context:
space:
mode:
authorBjörn Gustavsson <[email protected]>2016-06-02 09:58:46 +0200
committerBjörn Gustavsson <[email protected]>2016-06-02 12:51:18 +0200
commit5facb518a9ee2d756564eccd92a2c11f334d6282 (patch)
tree459b4574356863255a4e27aa017c04796780fdd9 /lib/compiler/test
parentaab1db16d0a732823fa9e2964c6a52b109c61742 (diff)
downloadotp-5facb518a9ee2d756564eccd92a2c11f334d6282.tar.gz
otp-5facb518a9ee2d756564eccd92a2c11f334d6282.tar.bz2
otp-5facb518a9ee2d756564eccd92a2c11f334d6282.zip
Eliminate crash for map updates in guards
beam_validator would complain that x(1) is uninitialized in a test_heap instruction when attempting to compile the following code with sys_core_fold turned off: foo(M) when not (M#{true := 0}); [M] -> ok. Simplified, the generated BEAM assembly code looked like this: test is_map BadMap x(0) put_map_exact Fail x(0) => x(1) ... jump BooleanStuff BadMap: move ok => x(1) jump Fail BooleanStuff: ... move Boolean => x(2) jump Build Fail: move false => x(2) Build: test_heap 2 3 %% x(0), x(1), x(2) must be live. ... That is, if put_map_exact failed, control would transfer to the label Fail without initializing x(1). Fix that by making sure that x(1) is initilized even if put_map_exact fails: test is_map BadMap x(0) put_map_exact BadLbl x(0) => x(1) ... jump OkLbl BadLbl: move ok => x(1) jump Fail OkLbl: jump BooleanStuff BadMap: move ok => x(1) jump Fail BooleanStuff: ... move Boolean => x(2) jump Build Fail: move false => x(2) Build: test_heap 2 3 %% x(0), x(1), x(2) must be live. ... Note that this situation is rare, and that other optimization passes (beam_dead and beam_jump in particular) will clean up this mess.
Diffstat (limited to 'lib/compiler/test')
-rw-r--r--lib/compiler/test/map_SUITE.erl24
1 files changed, 24 insertions, 0 deletions
diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl
index c3c4862794..36e82c1459 100644
--- a/lib/compiler/test/map_SUITE.erl
+++ b/lib/compiler/test/map_SUITE.erl
@@ -1287,6 +1287,7 @@ t_guard_update(Config) when is_list(Config) ->
first = map_guard_update(#{}, #{x=>first}),
second = map_guard_update(#{y=>old}, #{x=>second,y=>old}),
third = map_guard_update(#{x=>old,y=>old}, #{x=>third,y=>old}),
+ bad_map_guard_update(),
ok.
t_guard_update_large(Config) when is_list(Config) ->
@@ -1353,6 +1354,29 @@ map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second;
map_guard_update(M1, M2) when M1#{x:=third} =:= M2 -> third;
map_guard_update(_, _) -> error.
+bad_map_guard_update() ->
+ do_bad_map_guard_update(fun burns/1),
+ do_bad_map_guard_update(fun turns/1),
+ ok.
+
+do_bad_map_guard_update(Fun) ->
+ do_bad_map_guard_update_1(Fun, #{}),
+ do_bad_map_guard_update_1(Fun, #{true=>1}),
+ ok.
+
+do_bad_map_guard_update_1(Fun, Value) ->
+ %% Note: The business with the seemingly redundant fun
+ %% disables inlining, which would otherwise change the
+ %% EXIT reason.
+ {'EXIT',{function_clause,_}} = (catch Fun(Value)),
+ ok.
+
+burns(Richmond) when not (Richmond#{true := 0}); [Richmond] ->
+ specification.
+
+turns(Richmond) when not (Richmond#{true => 0}); [Richmond] ->
+ specification.
+
t_guard_receive(Config) when is_list(Config) ->
M0 = #{ id => 0 },
Pid = spawn_link(fun() -> guard_receive_loop() end),