From d39cac954fc76a838763ac1ac03cb71bf8e5d68d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Thu, 11 Aug 2016 15:35:29 +0200
Subject: [ERL-209] Fix ambiguous_catch_try_state inconsistency error

It is not safe to share code between 'catch' blocks.
---
 lib/compiler/src/beam_jump.erl        | 22 ++++++++++++++--------
 lib/compiler/test/beam_jump_SUITE.erl | 19 +++++++++++++++++--
 2 files changed, 31 insertions(+), 10 deletions(-)

(limited to 'lib')

diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index 09cd3aa2d4..48b5a32814 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -167,12 +167,18 @@ share_1([{label,L}=Lbl|Is], Dict0, Seq, Acc) ->
     end;
 share_1([{func_info,_,_,_}=I|Is], _, [], Acc) ->
     reverse(Is, [I|Acc]);
+share_1([{'catch',_,_}=I|Is], Dict0, Seq, Acc) ->
+    Dict = clean_non_sharable(Dict0),
+    share_1(Is, Dict, [I|Seq], Acc);
 share_1([{'try',_,_}=I|Is], Dict0, Seq, Acc) ->
     Dict = clean_non_sharable(Dict0),
     share_1(Is, Dict, [I|Seq], Acc);
 share_1([{try_case,_}=I|Is], Dict0, Seq, Acc) ->
     Dict = clean_non_sharable(Dict0),
     share_1(Is, Dict, [I|Seq], Acc);
+share_1([{catch_end,_}=I|Is], Dict0, Seq, Acc) ->
+    Dict = clean_non_sharable(Dict0),
+    share_1(Is, Dict, [I|Seq], Acc);
 share_1([I|Is], Dict, Seq, Acc) ->
     case is_unreachable_after(I) of
 	false ->
@@ -182,18 +188,18 @@ share_1([I|Is], Dict, Seq, Acc) ->
     end.
 
 clean_non_sharable(Dict) ->
-    %% We are passing in or out of a 'try' block. Remove
-    %% sequences that should not shared over the boundaries
-    %% of a 'try' block. Since the end of the sequence must match,
-    %% the only possible match between a sequence outside and
-    %% a sequence inside the 'try' block is a sequence that ends
-    %% with an instruction that causes an exception. Any sequence
-    %% that causes an exception must contain a line/1 instruction.
+    %% We are passing in or out of a 'catch' or 'try' block. Remove
+    %% sequences that should not be shared over the boundaries of the
+    %% block. Since the end of the sequence must match, the only
+    %% possible match between a sequence outside and a sequence inside
+    %% the 'catch'/'try' block is a sequence that ends with an
+    %% instruction that causes an exception. Any sequence that causes
+    %% an exception must contain a line/1 instruction.
     maps:filter(fun(K, _V) -> sharable_with_try(K) end, Dict).
 
 sharable_with_try([{line,_}|_]) ->
     %% This sequence may cause an exception and may potentially
-    %% match a sequence on the other side of the 'try' block
+    %% match a sequence on the other side of the 'catch'/'try' block
     %% boundary.
     false;
 sharable_with_try([_|Is]) ->
diff --git a/lib/compiler/test/beam_jump_SUITE.erl b/lib/compiler/test/beam_jump_SUITE.erl
index 0b13adaff2..088f63606c 100644
--- a/lib/compiler/test/beam_jump_SUITE.erl
+++ b/lib/compiler/test/beam_jump_SUITE.erl
@@ -21,7 +21,7 @@
 
 -export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
 	 init_per_group/2,end_per_group/2,
-	 undefined_label/1]).
+	 undefined_label/1,ambiguous_catch_try_state/1]).
 
 suite() ->
     [{ct_hooks,[ts_install_cth]}].
@@ -32,7 +32,8 @@ all() ->
 
 groups() ->
     [{p,[parallel],
-      [undefined_label
+      [undefined_label,
+       ambiguous_catch_try_state
       ]}].
 
 init_per_suite(Config) ->
@@ -57,3 +58,17 @@ flights(0, [], []) when [], 0; 0.0, [], false ->
     clark;
 flights(_, Reproduction, introduction) when false, Reproduction ->
     responsible.
+
+%% [ERL-209] beam_jump would share 'catch' blocks, causing an
+%% ambiguous_catch_try_state error in beam_validator.
+
+ambiguous_catch_try_state(_Config) ->
+    {{'EXIT',{{case_clause,song},_}},{'EXIT',{{case_clause,song},_}}} =
+	checks(42),
+    ok.
+
+river() -> song.
+
+checks(Wanted) ->
+    %% Must be one line to cause the unsafe optimization.
+    {catch case river() of sheet -> begin +Wanted, if "da" -> Wanted end end end, catch case river() of sheet -> begin + Wanted, if "da" -> Wanted end end end}.
-- 
cgit v1.2.3