From 64b2fde4e58e0f743cb910afff656e06fdd2312d Mon Sep 17 00:00:00 2001 From: Isabell Huang Date: Mon, 9 Jun 2025 10:51:57 +0200 Subject: [PATCH] erts: Add guard BIF `erlang:is_integer/3` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add guard BIF `erlang:is_integer/3`, following the design of the original EEP-16, only changing the name from `is_between` to `is_integer`. This BIF takes in 3 parameters, `Term`, `LowerBound`, and `UpperBound`. It returns `true` if `Term`, `LowerBound`, and `UpperBound` are all integers, and `LowerBound =< Term =< UpperBound`; otherwise, it returns false. Failure: `badarg` if `LowerBound` or `UpperBound` does not evaluate to an integer. Example: ```` 1> is_integer(2, 1, 10). true 2> is_integer(11, 1, 10). false 3> is_integer(1, 1.0, 10.0). ** exception error: bad argument in function is_integer/3 called as is_integer(1,1.0,10.0) ```` Co-authored-by: Björn Gustavsson Co-authored-by: John Högberg --- erts/emulator/beam/bif.tab | 5 + erts/emulator/beam/emu/ops.tab | 2 + erts/emulator/beam/erl_bif_guard.c | 14 ++ erts/emulator/beam/erl_db_util.c | 6 + erts/emulator/beam/jit/arm/ops.tab | 1 + erts/emulator/beam/jit/x86/ops.tab | 1 + erts/emulator/test/bif_SUITE.erl | 71 +++++++++- erts/emulator/test/exception_SUITE.erl | 1 + erts/preloaded/ebin/erlang.beam | Bin 40316 -> 40356 bytes erts/preloaded/ebin/init.beam | Bin 27912 -> 27924 bytes erts/preloaded/src/erlang.erl | 27 +++- lib/compiler/src/beam_asm.erl | 3 +- lib/compiler/src/beam_call_types.erl | 42 ++++-- lib/compiler/src/beam_disasm.erl | 4 + lib/compiler/src/beam_ssa_codegen.erl | 1 + lib/compiler/src/beam_ssa_opt.erl | 51 ++++++- lib/compiler/src/beam_ssa_pre_codegen.erl | 1 + lib/compiler/src/beam_ssa_type.erl | 29 ++++ lib/compiler/src/beam_validator.erl | 33 +++++ lib/compiler/src/erl_bifs.erl | 1 + lib/compiler/src/genop.tab | 8 ++ lib/compiler/test/guard_SUITE.erl | 155 +++++++++++++++++++++- lib/dialyzer/src/erl_bif_types.erl | 2 + lib/kernel/src/erl_erts_errors.erl | 2 + lib/stdlib/src/erl_internal.erl | 2 + 25 files changed, 438 insertions(+), 24 deletions(-) diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 95fcb0589f11..e03f312443c1 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -834,3 +834,8 @@ bif erl_debugger:peek_xreg/3 # bif erts_debug:unaligned_bitstring/2 bif re:import/1 + +# +# New in 29. +# +ubif erlang:is_integer/3 diff --git a/erts/emulator/beam/emu/ops.tab b/erts/emulator/beam/emu/ops.tab index 68eebde0f24c..265ca44b55d6 100644 --- a/erts/emulator/beam/emu/ops.tab +++ b/erts/emulator/beam/emu/ops.tab @@ -1001,6 +1001,8 @@ bif1 Fail=f Bif S1 Dst => i_bif1 S1 Fail Bif Dst bif2 p Bif S1 S2 Dst => i_bif2_body S2 S1 Bif Dst bif2 Fail=f Bif S1 S2 Dst => i_bif2 S2 S1 Fail Bif Dst +bif3 p Bif S1 S2 S3 Dst => i_bif3_body S3 S2 S1 Bif Dst + i_get_hash c W d i_get s d diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c index 258e48a51e8f..5fc801179f54 100644 --- a/erts/emulator/beam/erl_bif_guard.c +++ b/erts/emulator/beam/erl_bif_guard.c @@ -334,6 +334,20 @@ BIF_RETTYPE size_1(BIF_ALIST_1) BIF_ERROR(BIF_P, BADARG); } +BIF_RETTYPE is_integer_3(BIF_ALIST_3) +{ + if(is_not_integer(BIF_ARG_2) || + is_not_integer(BIF_ARG_3)) { + BIF_ERROR(BIF_P, BADARG); + } + if(is_not_integer(BIF_ARG_1)) { + BIF_RET(am_false); + } + + BIF_RET((CMP_LE(BIF_ARG_2, BIF_ARG_1) && CMP_LE(BIF_ARG_1, BIF_ARG_3)) ? + am_true : am_false); +} + /**********************************************************************/ /* returns the bitsize of a bitstring */ diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index de67d804c0ab..1ffe59aa5e55 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -588,6 +588,12 @@ static DMCGuardBif guard_tab[] = 1, DBIF_ALL }, + { + am_is_integer, + &is_integer_3, + 3, + DBIF_ALL + }, { am_is_list, &is_list_1, diff --git a/erts/emulator/beam/jit/arm/ops.tab b/erts/emulator/beam/jit/arm/ops.tab index 6c0d1a172578..180c6925a5aa 100644 --- a/erts/emulator/beam/jit/arm/ops.tab +++ b/erts/emulator/beam/jit/arm/ops.tab @@ -814,6 +814,7 @@ bif2 _Fail Bif S1 S2 Dst | never_fails(Bif) => nofail_bif2 S1 S2 Bif Dst bif1 Fail Bif S1 Dst => i_bif1 S1 Fail Bif Dst bif2 Fail Bif S1 S2 Dst => i_bif2 S1 S2 Fail Bif Dst +bif3 Fail Bif S1 S2 S3 Dst => i_bif3 S1 S2 S3 Fail Bif Dst nofail_bif2 S1=d S2 Bif Dst | is_eq_exact_bif(Bif) => bif_is_eq_exact S1 S2 Dst nofail_bif2 S1=d S2 Bif Dst | is_ne_exact_bif(Bif) => bif_is_ne_exact S1 S2 Dst diff --git a/erts/emulator/beam/jit/x86/ops.tab b/erts/emulator/beam/jit/x86/ops.tab index 2b0dfb2e4a9f..5bbbaf747eac 100644 --- a/erts/emulator/beam/jit/x86/ops.tab +++ b/erts/emulator/beam/jit/x86/ops.tab @@ -745,6 +745,7 @@ bif2 _Fail Bif S1 S2 Dst | never_fails(Bif) => nofail_bif2 S1 S2 Bif Dst bif1 Fail Bif S1 Dst => i_bif1 S1 Fail Bif Dst bif2 Fail Bif S1 S2 Dst => i_bif2 S1 S2 Fail Bif Dst +bif3 Fail Bif S1 S2 S3 Dst => i_bif3 S1 S2 S3 Fail Bif Dst nofail_bif2 S1=d S2 Bif Dst | is_eq_exact_bif(Bif) => bif_is_eq_exact S1 S2 Dst nofail_bif2 S1=d S2 Bif Dst | is_ne_exact_bif(Bif) => bif_is_ne_exact S1 S2 Dst diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 5b286b7f6424..3d8782d9dac2 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -24,6 +24,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). +-include_lib("stdlib/include/assert.hrl"). -export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]). @@ -46,7 +47,7 @@ test_length/1, fixed_apply_badarg/1, external_fun_apply3/1, - node_1/1,doctests/1]). + node_1/1,doctests/1,is_integer_3_test/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -64,7 +65,7 @@ all() -> is_process_alive, is_process_alive_signal_from, process_info_blast, os_env_case_sensitivity, verify_middle_queue_save, test_length,fixed_apply_badarg, - external_fun_apply3, node_1, doctests]. + external_fun_apply3, node_1, doctests, is_integer_3_test]. init_per_testcase(guard_bifs_in_erl_bif_types, Config) when is_list(Config) -> skip_missing_erl_bif_types(Config); @@ -1780,6 +1781,72 @@ node_error(E0) -> doctests(_Config) -> shell_docs:test(erlang, []). +is_integer_3_test(_Config) -> + _ = [is_between_ten(X) || X <- lists:seq(-2, 12)], + + false = is_between_ten(0), + true = is_between_ten(1), + true = is_between_ten(10), + false = is_between_ten(11), + + false = is_between_ten(a), + false = is_between_ten(5.0), + false = is_between_ten(-7.0), + false = is_between_ten([1]), + + _ = [begin + is_between_negative(X), + false = is_between_negative(-X) + end || X <- lists:seq(-100, -70)], + + _ = [is_between_mixed(X) || X <- lists:seq(-10, 10)], + + _ = [begin + is_between_bignum(X), + false = is_between_bignum(-X), + false = is_between_bignum(X - (1 bsl 64)) + end || X <- lists:seq((1 bsl 64) - 3, (1 bsl 64) + 10)], + + is_between_badarg(2, 1.5, 10.0), + is_between_badarg(2, 10.0, 1.5), + is_between_badarg(2, 1.5, 10), + is_between_badarg(2, 1, 10.0), + is_between_badarg(2, lower, upper), + + ok. + +-define(IS_BETWEEN_TEST(Name, LB, UB), +Name(X0) -> + F = id(is_integer), + Lower0 = LB, + Upper0 = UB, + Lower = id(Lower0), + Upper = id(Upper0), + + X1 = id(X0), + Result = is_integer(X1, Lower0, Upper0), + Result = is_integer(X1, Lower, Upper), + Result = apply(erlang, F, id([X1, Lower, Upper])), + Result = erlang:F(X1, Lower, Upper), + + false = is_integer(id(X1), Upper, Lower), + + X = id(X1), + Result = is_integer(X) andalso Lower =< X andalso X =< Upper, + Result). + +?IS_BETWEEN_TEST(is_between_ten, 1, 10). +?IS_BETWEEN_TEST(is_between_negative, -89, -77). +?IS_BETWEEN_TEST(is_between_mixed, -7, 7). +?IS_BETWEEN_TEST(is_between_bignum, 1 bsl 64, (1 bsl 64) + 7). + +is_between_badarg(X, A, B) -> + F = id(is_integer), + + ?assertError(badarg, is_integer(id(X), id(A), id(B))), + ?assertError(badarg, erlang:F(X, A, B)), + ?assertError(badarg, apply(erlang, F, id([X, A, B]))). + %% helpers wait_until(Fun) -> diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl index 6f88b0105b57..db45d942cc7a 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -940,6 +940,7 @@ error_info(_Config) -> {is_builtin, [1, 2, a]}, {is_function, [abc, bad_arity]}, {is_function, [abc, -1]}, + {is_integer, [5, a, b]}, {is_map_key, [key, not_map]}, {is_process_alive, [abc]}, {is_record, [not_tuple,42]}, diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 8bab7acd94daf388755162e3dcee6dda010d0a27..f229bb4b043fd290cbdfe7335a3ed8306037417e 100644 GIT binary patch delta 14696 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 delta 14557 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 diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam index a64afe58c65423743dce5e8689950d54182ef369..db659c843ca6a88c66f25d4863e3eb59097e2ba8 100644 GIT binary patch delta 961 zcmYk5S!@kq7=~x&v{h3}X`+@?4kfYV5F{cra-~VK(MqF6N;Cg;;_j6r&6gl%oP0QH2_8 z#dhq*9z;=#I@IF;4&o?|<0Kkz8fS10jcCFJT*75s!woz_GhX2}-r+rjj}d&r7yQI8 zI!s_PyD@`7_Fzx;W?%MWhyyr~DQwGjOl3!=F`a`slp`5tHghss8YKCgNc4((|X`gCUr^7m;V>+ReY7o?};o;UUpgZ>+s6frEX(rz56Is5-lBZ*e;27$;x%ZLjp;` z&|14sX|ynFeBZL{Oe?d0XhWv^B=vkaJFPsBl31P>2)n%l7v1Kgd1;f<(u1CewPbzk zX_6N&M_FnMJu$7m6KhO8443ka$Jy5UBe_WcZR b#kBhj+k4sflYJjGO#SoC-XJz-$=Sr;X~A2? delta 816 zcmX|hTKxLie$pD6g5O6m5wqgx4C^+ zu18)lm(BI-zQ&uzc!BNv0?iAr%sl6$o;|MlDZEo0F%%i3sN z56N~^q#_n^@L&ScF%JunflMsK60AZt3Q&j~agxxsa=w!#w756Sr|Y zOSy+t+|RSDW-aSj&x1U~8lK{5Hn5RRY+*B7*~ZJPxWSve&AWWW$9%~re8#6FU+_8K zvXk%miJ$qC-TcG93R19K3ezBkYp9|Wqfr{81dZ1eCCZ~z%~6^bD@)6@Oe?io`6|$Q z6{$qqRH|Lttt#!;0afdeYIIn&Njjnid399B)vOabqq91%i@KyXT~>$g>493*s?FM> zttv)4wqPrE_(t^eM%eMb_F=OV>@sh6@}AJ0t}E_R*9&*Kw`@kMUG9zc%=eaf=1we2 ziMQe>xp&9AUZq4#jjPFBEXmAWm zIxWkZY05U0`(G{F erlang:nif_error(undefined). +%% Shadowed by erl_bif_types: erlang:is_integer/3 +-doc """ +Returns `true` if `Term`, `LB`, and `UB` all evaluate to integers, and `Term` +is between `LB` and `UB` inclusive; otherwise, returns `false`. + +## Examples + +```erlang +1> is_integer(15, 0, 1024). +true +2> is_integer(-1, 0, 1). +false +``` + +Failure: `badarg` if `LB` or `UB` does not evaluate to an integer. +""". +-doc #{ category => terms }. +-spec is_integer(Term, LB, UB) -> boolean() when + Term :: integer(), + LB :: integer(), + UB :: integer(). +is_integer(_Term, _LB, _UB) -> + erlang:nif_error(undefined). + + -doc """ Loads `Module` described by the object code contained within `Binary`. diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl index 3c2b364c5b79..d830870603c8 100644 --- a/lib/compiler/src/beam_asm.erl +++ b/lib/compiler/src/beam_asm.erl @@ -508,7 +508,8 @@ bif_type(fsub, 2) -> {op,fsub}; bif_type(fmul, 2) -> {op,fmul}; bif_type(fdiv, 2) -> {op,fdiv}; bif_type(_, 1) -> bif1; -bif_type(_, 2) -> bif2. +bif_type(_, 2) -> bif2; +bif_type(_, 3) -> bif3. make_op({'%',_}, Dict) -> {[],Dict}; diff --git a/lib/compiler/src/beam_call_types.erl b/lib/compiler/src/beam_call_types.erl index 332be5ca063a..0e375f438567 100644 --- a/lib/compiler/src/beam_call_types.erl +++ b/lib/compiler/src/beam_call_types.erl @@ -100,14 +100,15 @@ will_succeed(erlang, 'bsl'=Op, [LHS, RHS]=Args) -> will_succeed(erlang, '++', [LHS, _RHS]) -> succeeds_if_type(LHS, proper_list()); will_succeed(erlang, '--', [_, _] = Args) -> - succeeds_if_types(Args, proper_list()); + succeeds_if_types(Args, [proper_list(), proper_list()]); will_succeed(erlang, BoolOp, [_, _] = Args) when BoolOp =:= 'and'; BoolOp =:= 'or' -> - succeeds_if_types(Args, beam_types:make_boolean()); + Bool = beam_types:make_boolean(), + succeeds_if_types(Args, [Bool, Bool]); will_succeed(erlang, Op, [_, _] = Args) when Op =:= 'band'; Op =:= 'bor'; Op =:= 'bxor' -> - succeeds_if_types(Args, #t_integer{}); + succeeds_if_types(Args, [#t_integer{}, #t_integer{}]); will_succeed(erlang, bit_size, [Arg]) -> succeeds_if_type(Arg, #t_bs_matchable{}); will_succeed(erlang, byte_size, [Arg]) -> @@ -144,13 +145,18 @@ will_succeed(erlang, map_size, [Arg]) -> will_succeed(erlang, node, [Arg]) -> succeeds_if_type(Arg, identifier); will_succeed(erlang, 'and', [_, _]=Args) -> - succeeds_if_types(Args, beam_types:make_boolean()); + Bool = beam_types:make_boolean(), + succeeds_if_types(Args, [Bool, Bool]); will_succeed(erlang, 'not', [Arg]) -> succeeds_if_type(Arg, beam_types:make_boolean()); will_succeed(erlang, 'or', [_, _]=Args) -> - succeeds_if_types(Args, beam_types:make_boolean()); + Bool = beam_types:make_boolean(), + succeeds_if_types(Args, [Bool, Bool]); will_succeed(erlang, 'xor', [_, _]=Args) -> - succeeds_if_types(Args, beam_types:make_boolean()); + Bool = beam_types:make_boolean(), + succeeds_if_types(Args, [Bool, Bool]); +will_succeed(erlang, 'is_integer', [_, _, _]=Args) -> + succeeds_if_types(Args, [any, #t_integer{}, #t_integer{}]); will_succeed(erlang, setelement, [Pos, Tuple0, _Value]=Args) -> PosRange = #t_integer{elements={1,?MAX_TUPLE_SIZE}}, case {meet(Pos, PosRange), meet(Tuple0, #t_tuple{size=1})} of @@ -231,14 +237,20 @@ fails_on_conflict_1([ArgType | Args], [Required | Types]) -> fails_on_conflict_1([], []) -> 'maybe'. -succeeds_if_types([LHS, RHS], Required) -> - case {succeeds_if_type(LHS, Required), - succeeds_if_type(RHS, Required)} of - {yes, yes} -> yes; - {no, _} -> no; - {_, no} -> no; - {_, _} -> 'maybe' - end. +succeeds_if_types(Ts, Rs) -> + succeeds_if_types_1(Ts, Rs, yes). + +succeeds_if_types_1([T | Ts], [R | Rs], Acc) -> + case succeeds_if_type(T, R) of + yes when Acc =:= yes -> + succeeds_if_types_1(Ts, Rs, Acc); + no -> + no; + _ -> + succeeds_if_types_1(Ts, Rs, 'maybe') + end; +succeeds_if_types_1([], [], Acc) -> + Acc. succeeds_if_type(ArgType, Required) -> case meet(ArgType, Required) of @@ -396,6 +408,8 @@ types(erlang, is_function, [Type]) -> sub_unsafe_type_test(Type, #t_fun{}); types(erlang, is_integer, [Type]) -> sub_unsafe_type_test(Type, #t_integer{}); +types(erlang, is_integer, [_Term, _LB, _UB]) -> + sub_unsafe(beam_types:make_boolean(), [any, #t_integer{}, #t_integer{}]); types(erlang, is_list, [Type]) -> sub_unsafe_type_test(Type, #t_list{}); types(erlang, is_map, [Type]) -> diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl index e507e95db21d..da37f0487e32 100644 --- a/lib/compiler/src/beam_disasm.erl +++ b/lib/compiler/src/beam_disasm.erl @@ -820,6 +820,10 @@ resolve_inst({bif2,Args},Imports,_,_) -> [F,Bif,A1,A2,Reg] = resolve_args(Args), {extfunc,_Mod,BifName,_Arity} = lookup(Bif+1,Imports), {bif,BifName,F,[A1,A2],Reg}; +resolve_inst({bif3,Args},Imports,_,_) -> + [F,Bif,A1,A2,A3,Reg] = resolve_args(Args), + {extfunc,_Mod,BifName,_Arity} = lookup(Bif+1,Imports), + {bif,BifName,F,[A1,A2,A3],Reg}; resolve_inst({allocate,[{u,X0},{u,X1}]},_,_,_) -> {allocate,X0,X1}; resolve_inst({allocate_heap,[{u,X0},{u,X1},{u,X2}]},_,_,_) -> diff --git a/lib/compiler/src/beam_ssa_codegen.erl b/lib/compiler/src/beam_ssa_codegen.erl index 5d7beee0e3a5..55b12e618700 100644 --- a/lib/compiler/src/beam_ssa_codegen.erl +++ b/lib/compiler/src/beam_ssa_codegen.erl @@ -2792,6 +2792,7 @@ is_gc_bif(node, [_]) -> false; is_gc_bif(element, [_,_]) -> false; is_gc_bif(get, [_]) -> false; is_gc_bif(is_map_key, [_,_]) -> false; +is_gc_bif(is_integer, [_,_,_]) -> false; is_gc_bif(map_get, [_,_]) -> false; is_gc_bif(tuple_size, [_]) -> false; is_gc_bif(Bif, Args) -> diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl index bb55a5deb89e..dbf4ec96b20d 100644 --- a/lib/compiler/src/beam_ssa_opt.erl +++ b/lib/compiler/src/beam_ssa_opt.erl @@ -269,6 +269,7 @@ module_passes(Opts) -> %% are repeated as required. repeated_passes(Opts) -> Ps = [?PASS(ssa_opt_live), + ?PASS(ssa_opt_is_between), ?PASS(ssa_opt_ne), ?PASS(ssa_opt_bs_create_bin), ?PASS(ssa_opt_dead), @@ -549,7 +550,8 @@ merge_tuple_update_1([], Tuple) -> %%% ssa_opt_split_blocks({#opt_st{ssa=Blocks0,cnt=Count0}=St, FuncDb}) -> - P = fun(#b_set{op={bif,element}}) -> true; + P = fun(#b_set{op={bif,is_integer},args=[_,_,_]}) -> true; + (#b_set{op={bif,element}}) -> true; (#b_set{op=call}) -> true; (#b_set{op=bs_init_writable}) -> true; (#b_set{op=make_fun}) -> true; @@ -559,6 +561,53 @@ ssa_opt_split_blocks({#opt_st{ssa=Blocks0,cnt=Count0}=St, FuncDb}) -> {Blocks,Count} = beam_ssa:split_blocks_before(RPO, P, Blocks0, Count0), {St#opt_st{ssa=Blocks,cnt=Count}, FuncDb}. +%%% +%%% BIF is_integer/3 tests whether a number is between a given range. +%%% When the range is constant, rewrite it into 3 BIFs: is_integer/1 and two +%%% =<'s to enable later optimization. +%%% +ssa_opt_is_between({#opt_st{ssa=Blocks0,cnt=Count0}=St, FuncDb}) -> + {Blocks1, Count1} = ssa_opt_is_between_1(Blocks0, Count0), + {St#opt_st{ssa=Blocks1,cnt=Count1}, FuncDb}. + +ssa_opt_is_between_1([{L,#b_blk{}=B}=Blk0|Ls0], Count0) -> + case B of + #b_blk{is=[#b_set{op={bif,is_integer},dst=Bool1, + args=[_,#b_literal{val=Min}, + #b_literal{val=Max}]}], + last=#b_br{bool=Bool1}}=Blk when is_integer(Min), + is_integer(Max), + Min =< Max -> + {Blk1, Count1} = is_between_rewrite(Count0, L, Blk), + {Ls1, Count2} = ssa_opt_is_between_1(Ls0, Count1), + {Blk1++Ls1, Count2}; + #b_blk{} -> + {Ls1, Count1} = ssa_opt_is_between_1(Ls0, Count0), + {[Blk0|Ls1], Count1} + end; +ssa_opt_is_between_1([], Count0) -> + {[], Count0}. + +is_between_rewrite(Count0, L, Blk0) -> + LowerL = Count0, + UpperL = Count0 + 1, + LowerBool = #b_var{name=Count0}, + UpperBool = #b_var{name=Count0 + 1}, + Count = Count0 + 2, + #b_blk{is=[#b_set{dst=Bool1,args=[Term,LB,UB]}|_], + last=#b_br{fail=Fail}=Br0} = Blk0, + Blk1 = Blk0#b_blk{is=[#b_set{op={bif,is_integer},dst=Bool1, + args=[Term]}], + last=#b_br{bool=Bool1,succ=LowerL,fail=Fail}}, + BlkLower = #b_blk{is=[#b_set{op={bif,'=<'},dst=LowerBool, + args=[LB,Term]}], + last=#b_br{bool=LowerBool,succ=UpperL,fail=Fail}}, + BlkUpper = #b_blk{is=[#b_set{op={bif,'=<'},dst=UpperBool, + args=[Term,UB]}], + last=Br0#b_br{bool=UpperBool}}, + Blocks = [{L, Blk1}, {LowerL, BlkLower}, {UpperL, BlkUpper}], + {Blocks, Count}. + %%% %%% Coalesce phi nodes. %%% diff --git a/lib/compiler/src/beam_ssa_pre_codegen.erl b/lib/compiler/src/beam_ssa_pre_codegen.erl index 0ed15783be9f..23f5df622298 100644 --- a/lib/compiler/src/beam_ssa_pre_codegen.erl +++ b/lib/compiler/src/beam_ssa_pre_codegen.erl @@ -2822,6 +2822,7 @@ use_zreg(wait_timeout) -> yes; %% avoid using a z register if their result is used directly in a branch. use_zreg(call) -> no; use_zreg({bif,element}) -> no; +use_zreg({bif,is_integer}) -> no; use_zreg({bif,is_map_key}) -> no; use_zreg({bif,is_record}) -> no; use_zreg({bif,map_get}) -> no; diff --git a/lib/compiler/src/beam_ssa_type.erl b/lib/compiler/src/beam_ssa_type.erl index 03f97565b628..4d40651df8c7 100644 --- a/lib/compiler/src/beam_ssa_type.erl +++ b/lib/compiler/src/beam_ssa_type.erl @@ -2721,6 +2721,15 @@ make_number({'-inf','+inf'}) -> make_number({_,_}=R) -> #t_number{elements=R}. +make_integer({'-inf','+inf'}) -> + #t_integer{}; +make_integer({'-inf',_}=R) -> + #t_integer{elements=R}; +make_integer({Min,Max}=R) when is_integer(Min), Min =< Max -> + #t_integer{elements=R}; +make_integer(_) -> + #t_integer{}. + inv_relop({bif,Op}) -> inv_relop_1(Op); inv_relop(_) -> none. @@ -2736,6 +2745,14 @@ infer_get_range(#t_integer{elements=R}) -> R; infer_get_range(#t_number{elements=R}) -> R; infer_get_range(_) -> unknown. +infer_integer_get_range(Arg, Ts) -> + case concrete_type(Arg, Ts) of + #t_integer{elements={_,_}=R} -> + R; + _ -> + {'-inf','+inf'} + end. + infer_br_value(_V, _Bool, none) -> none; infer_br_value(V, Bool, NewTs) -> @@ -2814,6 +2831,18 @@ infer_type({bif,is_function}, [#b_var{}=Arg, Arity], _Ts, _Ds) -> infer_type({bif,is_integer}, [#b_var{}=Arg], _Ts, _Ds) -> T = {Arg, #t_integer{}}, {[T], [T]}; +infer_type({bif,is_integer}, [#b_var{}=Arg, + #b_literal{val=Min}, + #b_literal{val=Max}], _Ts, _Ds) when Min =< Max -> + T = {Arg, beam_types:make_integer(Min, Max)}, + {[T], [T]}; +infer_type({bif,is_integer}, [#b_var{}=Arg,Min0,Max0], Ts, _Ds) -> + {Min,_} = infer_integer_get_range(Min0, Ts), + {_,Max} = infer_integer_get_range(Max0, Ts), + T = {Arg, make_integer({Min,Max})}, + %% Conservatively never attempt to subtract the type; subtraction + %% will most likely be incorrect or useless. + {[T], []}; infer_type({bif,is_list}, [#b_var{}=Arg], _Ts, _Ds) -> T = {Arg, #t_list{}}, {[T], [T]}; diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index c44091a408d5..0c5aec9b07fe 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -2288,6 +2288,22 @@ infer_types_1(#value{op={bif,is_function},args=[Src]}, Val, Op, Vst) -> infer_type_test_bif(#t_fun{}, Src, Val, Op, Vst); infer_types_1(#value{op={bif,is_integer},args=[Src]}, Val, Op, Vst) -> infer_type_test_bif(#t_integer{}, Src, Val, Op, Vst); +infer_types_1(#value{op={bif,is_integer},args=[Src, + {integer, Min}, + {integer, Max}]}, Val, Op, Vst) -> + infer_type_test_bif(beam_types:make_integer(Min, Max), Src, Val, Op, Vst); +infer_types_1(#value{op={bif,is_integer},args=[Src,Min0,Max0]}, Val, Op, Vst) -> + %% If there is at least one unknown bound, we cannot subtract + %% when 'false'. + {Min,_} = infer_integer_get_range(Min0, Vst), + {_,Max} = infer_integer_get_range(Max0, Vst), + Type = make_integer({Min, Max}), + case Val of + {atom, Bool} when Op =:= eq_exact, Bool; Op =:= ne_exact, not Bool -> + update_type(fun meet/2, Type, Src, Vst); + _ -> + Vst + end; infer_types_1(#value{op={bif,is_list},args=[Src]}, Val, Op, Vst) -> infer_type_test_bif(#t_list{}, Src, Val, Op, Vst); infer_types_1(#value{op={bif,is_map},args=[Src]}, Val, Op, Vst) -> @@ -2354,6 +2370,23 @@ invert_relop('=<') -> '>'; invert_relop('>=') -> '<'; invert_relop('>') -> '=<'. +infer_integer_get_range(Arg, Vst) -> + case get_term_type(Arg, Vst) of + #t_integer{elements={_,_}=R} -> + R; + _ -> + {'-inf','+inf'} + end. + +make_integer({'-inf','+inf'}) -> + #t_integer{}; +make_integer({'-inf',_}=R) -> + #t_integer{elements=R}; +make_integer({Min,Max}=R) when is_integer(Min), Min =< Max -> + #t_integer{elements=R}; +make_integer(_) -> + #t_integer{}. + %%% %%% Keeping track of types. %%% diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl index ee81d218a42f..e3d137909d12 100644 --- a/lib/compiler/src/erl_bifs.erl +++ b/lib/compiler/src/erl_bifs.erl @@ -114,6 +114,7 @@ is_pure(erlang, is_float, 1) -> true; is_pure(erlang, is_function, 1) -> true; is_pure(erlang, is_function, 2) -> true; is_pure(erlang, is_integer, 1) -> true; +is_pure(erlang, is_integer, 3) -> true; is_pure(erlang, is_list, 1) -> true; is_pure(erlang, is_map, 1) -> true; is_pure(erlang, is_map_key, 2) -> true; diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab index 588ec5210ee1..d54edd0fef26 100755 --- a/lib/compiler/src/genop.tab +++ b/lib/compiler/src/genop.tab @@ -710,3 +710,11 @@ BEAM_FORMAT_NUMBER=0 ## * {atom,entry} - Function entry. ## * {atom,line} - Any other line in the function. 184: debug_line/4 + +# OTP 29 + +## @spec bif3 Lbl Bif Arg1 Arg2 Arg3 Reg +## @doc Call the bif Bif with the arguments Arg1, Arg2, and Arg3, +## and store the result in Reg. +## On failure jump to Lbl. +185: bif3/6 diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl index 1d6ee3232b24..3656672e36ec 100644 --- a/lib/compiler/test/guard_SUITE.erl +++ b/lib/compiler/test/guard_SUITE.erl @@ -26,6 +26,7 @@ -compile([nowarn_obsolete_guard]). -include_lib("syntax_tools/include/merl.hrl"). +-include_lib("stdlib/include/assert.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, @@ -44,7 +45,7 @@ bad_constants/1,bad_guards/1, guard_in_catch/1,beam_bool_SUITE/1, repeated_type_tests/1,use_after_branch/1, - body_in_guard/1]). + body_in_guard/1,is_integer_3_guard/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -63,12 +64,13 @@ groups() -> basic_andalso_orelse,traverse_dcd, check_qlc_hrl,andalso_semi,t_tuple_size,binary_part, bad_constants,bad_guards,guard_in_catch,beam_bool_SUITE, - repeated_type_tests,use_after_branch,body_in_guard]}, + repeated_type_tests,use_after_branch,body_in_guard, + is_integer_3_guard]}, {slow,[],[literal_type_tests,generated_combinations]}]. init_per_suite(Config) -> test_lib:recompile(?MODULE), - Config. + id(Config). end_per_suite(_Config) -> ok. @@ -233,6 +235,26 @@ basic_not(Config) when is_list(Config) -> check(fun() -> if not (False =:= true) -> ok; true -> error end end, ok), check(fun() -> if not (Glurf =:= true) -> ok; true -> error end end, ok), + check(fun() -> if + not is_integer(7, Glurf, Glurf) -> ok; + true -> error + end + end, error), + + check(fun() -> if + not is_integer(10, C, D) -> ok; + true -> error + end + end, ok), + + check(fun() -> + X = id(20), + if + not is_integer(X, 1, 10) -> ok; + true -> error + end + end, ok), + ok. complex_not(Config) when is_list(Config) -> @@ -783,6 +805,24 @@ more_or_guards(Config) when is_list(Config) -> element(19, ATuple) -> ok; true -> error end end, error), + + check(fun() -> + Lower = id(a), + Upper = id(b), + if + true or is_integer(1, Lower, Upper) -> ok; + true -> error + end + end, error), + + check(fun() -> + Lower = id(1), + Upper = id(10), + if + false or is_integer(1, Lower, Upper) -> ok; + true -> error + end + end, ok), ok. complex_or_guards(Config) when is_list(Config) -> @@ -1561,7 +1601,8 @@ is_digit(N) -> Bool = is_digit_8(N), Bool = is_digit_9(42, N), Bool = is_digit_10(N, 0), - Bool = is_digit_11(N, 0). + Bool = is_digit_11(N, 0), + Bool = is_digit_12(N). is_digit_1(X) when 16#0660 =< X, X =< 16#0669 -> true; is_digit_1(X) when 16#0030 =< X, X =< 16#0039 -> true; @@ -1622,6 +1663,12 @@ is_digit_11(X, _) when 16#0030 =< X, X =< 16#0039 -> true; is_digit_11(X, _) when 16#06F0 =< X, X =< 16#06F9 -> true; is_digit_11(_, _) -> false. +is_digit_12(X) when is_integer(X, 16#0030, 16#0039); + is_integer(X, 16#06F0, 16#06F9); + is_integer(X, 16#0660, 16#0669) -> true; +is_digit_12(16#0670) -> false; +is_digit_12(_) -> false. + rel_op_combinations_2(0, _) -> ok; rel_op_combinations_2(N, Range) -> @@ -1642,7 +1689,8 @@ broken_range(N) -> Bool = broken_range_10(N), Bool = broken_range_11(N), Bool = broken_range_12(N), - Bool = broken_range_13(N). + Bool = broken_range_13(N), + Bool = broken_range_14(N). broken_range_1(X) when X >= 10, X =< 20, X =/= 13 -> true; broken_range_1(X) when X >= 3, X =< 5 -> true; @@ -1707,6 +1755,10 @@ broken_range_13(X) when X >= 10, X =< 20, 13 =/= X -> true; broken_range_13(X) when X >= 3, X =< 5 -> true; broken_range_13(_) -> false. +broken_range_14(X) when is_integer(X, 10, 20), 13 =/= X -> true; +broken_range_14(X) when is_integer(X, 3, 5) -> true; +broken_range_14(_) -> false. + rel_op_combinations_3(0, _) -> ok; rel_op_combinations_3(N, Red) -> @@ -3363,6 +3415,99 @@ body_in_guard(_Config) -> demonitor(Mon) end. +is_integer_3_guard(_Config) -> + Lower = id(1), + Upper = id(10), + _ = [begin + Expected = Lower =< X andalso X =< Upper, + Expected = is_integer_3_guard_1(X, Lower, Upper), + false = is_integer_3_guard_1(float(X), Lower, Upper) + end || X <- lists:seq(-7, 17)], + + ?assertError(badarg, is_integer_3_guard_1(2, 1.5, 10)), + ?assertError(badarg, is_integer_3_guard_1(2, true, 10)), + ?assertError(badarg, is_integer_3_guard_1(2, 10, b)), + + false = is_integer_3_guard_2(id(0)), + true = is_integer_3_guard_2(id(1)), + true = is_integer_3_guard_2(id(32)), + true = is_integer_3_guard_2(id(1024)), + false = is_integer_3_guard_2(id(1025)), + + true = is_integer_3_guard_3(id(0)), + false = is_integer_3_guard_3(id(1)), + false = is_integer_3_guard_3(id(32)), + false = is_integer_3_guard_3(id(1024)), + true = is_integer_3_guard_3(id(1025)), + + 1 = is_integer_3_guard_4(id(1), id(0), id(5)), + false = is_integer_3_guard_4(id(1), id(-1), id(0)), + + 2 = is_integer_3_guard_5(id(2), id(0), id(9)), + false = is_integer_3_guard_5(id(1024), id(0), id(9)), + + false = is_integer_3_guard_6(id(0), id(1), id(-1)), + true = is_integer_3_guard_6(id(0), id(0), id(1)), + + false = is_integer_3_guard_7(id(0), id(1), id(9)), + true = is_integer_3_guard_7(id(1), id(1), id(9)), + + true = is_integer_3_guard_8(id(17), id(12), id(20)), + false = is_integer_3_guard_8(id(0), id(12), id(20)), + true = is_integer_3_guard_8(id(5), id(1), id(20)), + + ok. + +is_integer_3_guard_1(X, LB, UB) when is_integer(X, LB, UB) -> + true = is_integer(X, LB, UB); +is_integer_3_guard_1(X, LB, UB) -> + is_integer(X, LB, UB). + +is_integer_3_guard_2(X) when is_integer(X, 1, 1024) -> + true = is_integer(X, 1, 1024); +is_integer_3_guard_2(X) -> + is_integer(X, 1, 1024). + +is_integer_3_guard_3(X) when not is_integer(X, 1, 1024) -> + true = not is_integer(X, 1, 1024); +is_integer_3_guard_3(X) -> + not is_integer(X, 1, 1024). + +is_integer_3_guard_4(X, LB, UB) when 0 =< LB, UB < 10, + is_integer(X, LB, UB) -> + is_integer_3_guard_4_id(X); +is_integer_3_guard_4(X, LB, UB) -> + is_integer(X, LB, UB). + +is_integer_3_guard_4_id(I) -> I. + +is_integer_3_guard_5(X, LB, UB) when 0 =< LB, is_integer(UB), + UB < 10, is_integer(X, LB, UB) -> + is_integer_3_guard_5_id(X); +is_integer_3_guard_5(X, LB, UB) -> + is_integer(X, LB, UB). + +is_integer_3_guard_5_id(I) -> I. + +%% Test incorrect order of bounds. +is_integer_3_guard_6(X, LB, UB) when 10 =< LB, UB < 0, is_integer(X, LB, UB) -> + is_integer(X, LB, UB); +is_integer_3_guard_6(X, LB, UB) -> + is_integer(X, LB, UB). + +is_integer_3_guard_7(X, LB, UB) when is_number(UB), UB < 10, is_integer(X, LB, UB) -> + is_integer(X, LB, UB); +is_integer_3_guard_7(X, LB, UB) -> + is_integer(X, LB, UB). + +is_integer_3_guard_8(X, LB, UB) when is_number(LB), LB > 10, is_integer(X, LB, UB) -> + is_integer_3_guard_8_id(X), + is_integer(X, LB, UB); +is_integer_3_guard_8(X, LB, UB) -> + is_integer(X, LB, UB). + +is_integer_3_guard_8_id(I) -> I. + %% Call this function to turn off constant propagation. id(I) -> I. diff --git a/lib/dialyzer/src/erl_bif_types.erl b/lib/dialyzer/src/erl_bif_types.erl index 311b4cfc9e05..b54116ed4d77 100644 --- a/lib/dialyzer/src/erl_bif_types.erl +++ b/lib/dialyzer/src/erl_bif_types.erl @@ -2243,6 +2243,8 @@ arg_types(erlang, is_function, 2) -> [t_any(), t_arity()]; arg_types(erlang, is_integer, 1) -> [t_any()]; +arg_types(erlang, is_integer, 3) -> + [t_any(),t_integer(), t_integer()]; arg_types(erlang, is_list, 1) -> [t_any()]; arg_types(erlang, is_map, 1) -> diff --git a/lib/kernel/src/erl_erts_errors.erl b/lib/kernel/src/erl_erts_errors.erl index c4b852987472..c4e8283b7cda 100644 --- a/lib/kernel/src/erl_erts_errors.erl +++ b/lib/kernel/src/erl_erts_errors.erl @@ -477,6 +477,8 @@ format_erlang_error(is_function, [_,Arity], _) -> is_integer(Arity) -> range; true -> not_integer end]; +format_erlang_error(is_integer, [_,_,_], _) -> + [not_integer]; format_erlang_error(is_map_key, [_,_], _) -> [[],not_map]; format_erlang_error(is_process_alive, [Arg], _) -> diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index 34d3b5c5c06a..6ac0ccbee3e2 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -86,6 +86,7 @@ guard_bif(element, 2) -> true; guard_bif(float, 1) -> true; guard_bif(floor, 1) -> true; guard_bif(hd, 1) -> true; +guard_bif(is_integer, 3) -> true; guard_bif(is_map_key, 2) -> true; guard_bif(length, 1) -> true; guard_bif(map_size, 1) -> true; @@ -351,6 +352,7 @@ bif(is_float, 1) -> true; bif(is_function, 1) -> true; bif(is_function, 2) -> true; bif(is_integer, 1) -> true; +bif(is_integer, 3) -> true; bif(is_list, 1) -> true; bif(is_map, 1) -> true; bif(is_map_key, 2) -> true;