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.