diff --git a/lib/compiler/src/beam_ssa_codegen.erl b/lib/compiler/src/beam_ssa_codegen.erl
index 711e77684684..2ed93d5e182c 100644
--- a/lib/compiler/src/beam_ssa_codegen.erl
+++ b/lib/compiler/src/beam_ssa_codegen.erl
@@ -2267,6 +2267,10 @@ bs_translate([]) -> [].
bs_translate_collect([I|Is]=Is0, Ctx, Fail, Acc) ->
case bs_translate_instr(I) of
+ {Ctx,_,{ensure_at_least,_,_}} ->
+ %% There should only be a single `ensure_at_least`
+ %% instruction in each `bs_match` instruction.
+ {bs_translate_fixup(Acc),Fail,Is0};
{Ctx,Fail,Instr} ->
bs_translate_collect(Is, Ctx, Fail, [Instr|Acc]);
{Ctx,{f,0},Instr} ->
diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl
index 9a80933aca99..fce063643671 100644
--- a/lib/compiler/src/beam_ssa_opt.erl
+++ b/lib/compiler/src/beam_ssa_opt.erl
@@ -307,8 +307,8 @@ late_epilogue_passes(Opts) ->
?PASS(ssa_opt_sink),
?PASS(ssa_opt_blockify),
?PASS(ssa_opt_redundant_br),
- ?PASS(ssa_opt_bs_ensure),
?PASS(ssa_opt_merge_blocks),
+ ?PASS(ssa_opt_bs_ensure),
?PASS(ssa_opt_try),
?PASS(ssa_opt_get_tuple_element),
?PASS(ssa_opt_tail_literals),
@@ -3536,7 +3536,7 @@ redundant_br_safe_bool(Is, Bool) ->
end.
%%%
-%%% Add the bs_ensure instruction before a sequence of `bs_match`
+%%% Add the `bs_ensure` instruction before a sequence of `bs_match`
%%% (SSA) instructions, each having a literal size and the
%%% same failure label.
%%%
@@ -3544,6 +3544,15 @@ redundant_br_safe_bool(Is, Bool) ->
%%% instruction that can match multiple segments having the same
%%% failure label.
%%%
+%%% It is beneficial but not essential to run this pass after
+%%% the `merge_blocks/1` pass. For the following example, two separate
+%%% `bs_match/1` instructions will emitted if blocks have not been
+%%% merged before this pass:
+%%%
+%%% A = 0,
+%%% B = <<1, 2, 3>>,
+%%% <> = <<0, 1, 2, 3>>
+%%%
ssa_opt_bs_ensure({#opt_st{ssa=Blocks0,cnt=Count0,anno=Anno0}=St, FuncDb})
when is_map(Blocks0) ->
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 9211af62aade..2b06ec0a8de0 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -2749,6 +2749,8 @@ bs_match(_Config) ->
{'EXIT',{{badmatch,<<>>},_}} = catch do_bs_match_gh_7467(<<>>),
+ {0,<<1,2,3>>} = do_bs_match_gh_8280(),
+
ok.
do_bs_match_1(_, X) ->
@@ -2823,6 +2825,12 @@ do_bs_match_gh_6755(B) ->
do_bs_match_gh_7467(A) ->
do_bs_match_gh_7467(<<_:1/bits>> = A).
+do_bs_match_gh_8280() ->
+ A = 0,
+ B = <<1, 2, 3>>,
+ <> = id(<<0, 1, 2, 3>>),
+ {A, B}.
+
%% GH-6348/OTP-18297: Allow aliases for binaries.
-record(ba_foo, {a,b,c}).
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index adb4b153159f..3925b881b0b1 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -1549,6 +1549,9 @@ guard_expr({'orelse',_,E1,E2}, Bs) ->
{value,_Val}=Res -> Res
end
end;
+guard_expr({'case',_,E0,Cs}, Bs) ->
+ {value,E} = guard_expr(E0, Bs),
+ guard_case_clauses(E, Cs, Bs);
guard_expr({dbg,_,self,[]}, _) ->
{value,get(self)};
guard_expr({safe_bif,_,erlang,'not',As0}, Bs) ->
@@ -1591,6 +1594,20 @@ guard_expr({bin,_,Flds}, Bs) ->
end),
{value,V}.
+%% guard_case_clauses(Value, Clauses, Bindings, Error, Ieval)
+%% Error = try_clause | case_clause
+guard_case_clauses(Val, [{clause,_,[P],G,B}|Cs], Bs0) ->
+ case match(P, Val, Bs0) of
+ {match,Bs} ->
+ case guard(G, Bs) of
+ true ->
+ guard_expr(hd(B), Bs);
+ false ->
+ guard_case_clauses(Val, Cs, Bs0)
+ end;
+ nomatch ->
+ guard_case_clauses(Val, Cs, Bs0)
+ end.
%% eval_map_fields([Field], Bindings, IEvalState) ->
%% {[{map_assoc | map_exact,Key,Value}],Bindings}
diff --git a/lib/debugger/test/record_SUITE.erl b/lib/debugger/test/record_SUITE.erl
index 2f75dcda4245..e3d66804bbc8 100644
--- a/lib/debugger/test/record_SUITE.erl
+++ b/lib/debugger/test/record_SUITE.erl
@@ -28,7 +28,8 @@
-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
init_per_suite/1,end_per_suite/1,
- errors/1,record_test/1,eval_once/1]).
+ errors/1,record_test/1,eval_once/1,
+ nested_in_guard/1]).
-export([debug/0]).
@@ -50,7 +51,7 @@ end_per_group(_GroupName, Config) ->
cases() ->
- [errors, record_test, eval_once].
+ [errors, record_test, eval_once, nested_in_guard].
init_per_testcase(_Case, Config) ->
test_lib:interpret(?MODULE),
@@ -295,6 +296,30 @@ once(Test, Record) ->
end,
Result.
+nested_in_guard(_Config) ->
+ B = #bar{d = []},
+ F = #foo{a = B},
+
+ ok = do_nested_in_guard(#foo{a=#bar{d=[]}}),
+ not_ok = do_nested_in_guard(#foo{a=#bar{}}),
+ not_ok = do_nested_in_guard(#foo{a={no_bar,a,b,c,d}}),
+ not_ok = do_nested_in_guard(#foo{a={bar,a}}),
+ not_ok = do_nested_in_guard(42),
+ not_ok = do_nested_in_guard(#bar{}),
+ not_ok = do_nested_in_guard([]),
+
+ ok.
+
+-define(is_foo(X), (((X#foo.a)#bar.d == []))).
+
+do_nested_in_guard(F) ->
+ if
+ ?is_foo(F) ->
+ ok;
+ true ->
+ not_ok
+ end.
+
id(I) ->
I.