diff --git a/lib/compiler/src/beam_ssa_codegen.erl b/lib/compiler/src/beam_ssa_codegen.erl index 9f6169829b41..6f6de9996268 100644 --- a/lib/compiler/src/beam_ssa_codegen.erl +++ b/lib/compiler/src/beam_ssa_codegen.erl @@ -1270,9 +1270,22 @@ cg_block([#cg_set{op=is_tagged_tuple,anno=Anno,dst=Bool,args=Args0}], {Bool,Fail [Src,{integer,Arity},Tag] = typed_args(Args0, Anno, St), {[{test,is_tagged_tuple,ensure_label(Fail, St),[Src,Arity,Tag]}],St} end; -cg_block([#cg_set{op=is_nonempty_list,dst=Bool,args=Args0}], {Bool,Fail}, St) -> +cg_block([#cg_set{op=is_nonempty_list,dst=Bool0,args=Args0}=Set], {Bool0,Fail0}, St) -> + Fail = ensure_label(Fail0, St), Args = beam_args(Args0, St), - {[{test,is_nonempty_list,ensure_label(Fail, St),Args}],St}; + case beam_args([Bool0|Args0], St) of + [{z,0}|Args] -> + {[{test,is_nonempty_list,Fail,Args}],St}; + [Dst|Args] -> + %% This instruction was a call to is_list/1, which was + %% rewritten to an is_nonempty_list test by + %% beam_ssa_type. BEAM has no is_nonempty_list instruction + %% that will return a boolean, so we must revert it to an + %% is_list/1 call. + #cg_set{anno=#{was_bif_is_list := true}} = Set, %Assertion. + {[{bif,is_list,Fail0,Args,Dst}, + {test,is_eq_exact,Fail,[Dst,{atom,true}]}],St} + end; cg_block([#cg_set{op=has_map_field,dst=Dst0,args=Args0}], {Dst0,Fail0}, St) -> Fail = ensure_label(Fail0, St), case beam_args([Dst0|Args0], St) of diff --git a/lib/compiler/test/beam_type_SUITE.erl b/lib/compiler/test/beam_type_SUITE.erl index 94f1b8b0a6f6..f65dce28c31d 100644 --- a/lib/compiler/test/beam_type_SUITE.erl +++ b/lib/compiler/test/beam_type_SUITE.erl @@ -1094,6 +1094,11 @@ cs_2({bar,baz}) -> is_list_opt(_Config) -> true = is_list_opt_1(id(<<"application/a2l">>)), false = is_list_opt_1(id(<<"">>)), + + ok = is_list_opt_3(id([])), + true = is_list_opt_3(id([a])), + {'EXIT',{badarg,_}} = catch is_list_opt_3(id(no_list)), + ok. is_list_opt_1(Type) -> @@ -1105,6 +1110,16 @@ is_list_opt_1(Type) -> is_list_opt_2(<<"application/a2l">>) -> [<<"a2l">>]; is_list_opt_2(_Type) -> nil. +is_list_opt_3([]) -> + ok; +is_list_opt_3(A) -> + %% The call to is_list/1 would be optimized to an is_nonempty_list + %% instruction, which only exists as a guard test that cannot + %% produce boolean value. + _ = (Bool = is_list(A)) orelse binary_to_integer(<<"">>), + Bool. + + %% We used to determine the type of `get_tuple_element` at the time of %% extraction, which is simple but sometimes throws away type information when %% on tuple unions.