diff --git a/bootstrap/bin/no_dot_erlang.boot b/bootstrap/bin/no_dot_erlang.boot index e6781adea148..79f4e28b222b 100644 Binary files a/bootstrap/bin/no_dot_erlang.boot and b/bootstrap/bin/no_dot_erlang.boot differ diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot index 4be573edafc4..5247c3691919 100644 Binary files a/bootstrap/bin/start.boot and b/bootstrap/bin/start.boot differ diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot index 4be573edafc4..5247c3691919 100644 Binary files a/bootstrap/bin/start_clean.boot and b/bootstrap/bin/start_clean.boot differ diff --git a/bootstrap/lib/compiler/ebin/beam_asm.beam b/bootstrap/lib/compiler/ebin/beam_asm.beam index 64394af5d205..10afec5f41d6 100644 Binary files a/bootstrap/lib/compiler/ebin/beam_asm.beam and b/bootstrap/lib/compiler/ebin/beam_asm.beam differ diff --git a/bootstrap/lib/compiler/ebin/compiler.app b/bootstrap/lib/compiler/ebin/compiler.app index 7d2a015d9966..2ca5bd38eb98 100644 --- a/bootstrap/lib/compiler/ebin/compiler.app +++ b/bootstrap/lib/compiler/ebin/compiler.app @@ -21,7 +21,7 @@ {application, compiler, [{description, "ERTS CXC 138 10"}, - {vsn, "8.5.4"}, + {vsn, "9.0"}, {modules, [ beam_a, beam_asm, diff --git a/bootstrap/lib/compiler/ebin/compiler.appup b/bootstrap/lib/compiler/ebin/compiler.appup index f58dfd802dde..b87105337e95 100644 --- a/bootstrap/lib/compiler/ebin/compiler.appup +++ b/bootstrap/lib/compiler/ebin/compiler.appup @@ -18,7 +18,7 @@ %% limitations under the License. %% %% %CopyrightEnd% -{"8.2", +{"9.0", [{<<".*">>,[{restart_application, compiler}]}], [{<<".*">>,[{restart_application, compiler}]}] }. diff --git a/bootstrap/lib/kernel/ebin/group.beam b/bootstrap/lib/kernel/ebin/group.beam index d96478458d99..efba245821b2 100644 Binary files a/bootstrap/lib/kernel/ebin/group.beam and b/bootstrap/lib/kernel/ebin/group.beam differ diff --git a/bootstrap/lib/kernel/ebin/kernel.app b/bootstrap/lib/kernel/ebin/kernel.app index 5ed9de3c2d24..8c29fd09b0ff 100644 --- a/bootstrap/lib/kernel/ebin/kernel.app +++ b/bootstrap/lib/kernel/ebin/kernel.app @@ -2,9 +2,9 @@ %% %CopyrightBegin% %% %% SPDX-License-Identifier: Apache-2.0 -%% +%% %% Copyright Ericsson AB 1996-2025. All Rights Reserved. -%% +%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -16,7 +16,7 @@ %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. -%% +%% %% %CopyrightEnd% %% %% This is an -*- erlang -*- file. @@ -24,7 +24,7 @@ {application, kernel, [ {description, "ERTS CXC 138 10"}, - {vsn, "10.2.1"}, + {vsn, "10.3"}, {modules, [application, application_controller, application_master, @@ -35,6 +35,7 @@ dist_util, erl_boot_server, erl_compile_server, + erl_debugger, erl_distribution, erl_erts_errors, erl_reply, @@ -167,7 +168,7 @@ {shell_history_drop,[]} ]}, {mod, {kernel, []}}, - {runtime_dependencies, ["erts-15.1", "stdlib-6.0", + {runtime_dependencies, ["erts-15.2.5", "stdlib-6.0", "sasl-3.0", "crypto-5.0"]} ] }. diff --git a/bootstrap/lib/kernel/ebin/kernel.appup b/bootstrap/lib/kernel/ebin/kernel.appup index 78fce58272c8..f5529c4ad211 100644 --- a/bootstrap/lib/kernel/ebin/kernel.appup +++ b/bootstrap/lib/kernel/ebin/kernel.appup @@ -21,66 +21,68 @@ %% %% We allow upgrade from, and downgrade to all previous %% versions from the following OTP releases: -%% - OTP 23 -%% - OTP 24 -%% - OTP 25 +%% - OTP 26 +%% - OTP 27 +%% - OTP 28 %% %% We also allow upgrade from, and downgrade to all %% versions that have branched off from the above %% stated previous versions. %% -{"8.4.1", - [{<<"^7\\.0$">>,[restart_new_emulator]}, - {<<"^7\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^7\\.1$">>,[restart_new_emulator]}, - {<<"^7\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^7\\.2$">>,[restart_new_emulator]}, - {<<"^7\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^7\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^7\\.3$">>,[restart_new_emulator]}, - {<<"^7\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^7\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.0$">>,[restart_new_emulator]}, - {<<"^8\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^8\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.0\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.1$">>,[restart_new_emulator]}, - {<<"^8\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^8\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.1\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.1\\.3(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.2$">>,[restart_new_emulator]}, - {<<"^8\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^8\\.3$">>,[restart_new_emulator]}, - {<<"^8\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^8\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.3\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.4$">>,[restart_new_emulator]}, - {<<"^8\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}], - [{<<"^7\\.0$">>,[restart_new_emulator]}, - {<<"^7\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^7\\.1$">>,[restart_new_emulator]}, - {<<"^7\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^7\\.2$">>,[restart_new_emulator]}, - {<<"^7\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^7\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^7\\.3$">>,[restart_new_emulator]}, - {<<"^7\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^7\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.0$">>,[restart_new_emulator]}, - {<<"^8\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^8\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.0\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.1$">>,[restart_new_emulator]}, - {<<"^8\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^8\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.1\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.1\\.3(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.2$">>,[restart_new_emulator]}, - {<<"^8\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^8\\.3$">>,[restart_new_emulator]}, - {<<"^8\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^8\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.3\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^8\\.4$">>,[restart_new_emulator]}, - {<<"^8\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}. +{"10.3", + [{<<"^10\\.0$">>,[restart_new_emulator]}, + {<<"^10\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^10\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.1$">>,[restart_new_emulator]}, + {<<"^10\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^10\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.1\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.2$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.3(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.4(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.5(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.6(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.7(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^9\\.0$">>,[restart_new_emulator]}, + {<<"^9\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^9\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^9\\.0\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^9\\.1$">>,[restart_new_emulator]}, + {<<"^9\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^9\\.2$">>,[restart_new_emulator]}, + {<<"^9\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^9\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^9\\.2\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^9\\.2\\.3(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^9\\.2\\.4(?:\\.[0-9]+)*$">>,[restart_new_emulator]}], + [{<<"^10\\.0$">>,[restart_new_emulator]}, + {<<"^10\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^10\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.1$">>,[restart_new_emulator]}, + {<<"^10\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^10\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.1\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.2$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.3(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.4(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.5(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.6(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^10\\.2\\.7(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^9\\.0$">>,[restart_new_emulator]}, + {<<"^9\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^9\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^9\\.0\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^9\\.1$">>,[restart_new_emulator]}, + {<<"^9\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^9\\.2$">>,[restart_new_emulator]}, + {<<"^9\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^9\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^9\\.2\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^9\\.2\\.3(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^9\\.2\\.4(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}. diff --git a/bootstrap/lib/stdlib/ebin/erl_internal.beam b/bootstrap/lib/stdlib/ebin/erl_internal.beam index 36eca47498aa..8f8f9578211d 100644 Binary files a/bootstrap/lib/stdlib/ebin/erl_internal.beam and b/bootstrap/lib/stdlib/ebin/erl_internal.beam differ diff --git a/bootstrap/lib/stdlib/ebin/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app index 07410179116b..96ffaf347d99 100644 --- a/bootstrap/lib/stdlib/ebin/stdlib.app +++ b/bootstrap/lib/stdlib/ebin/stdlib.app @@ -22,7 +22,7 @@ %% {application, stdlib, [{description, "ERTS CXC 138 10"}, - {vsn, "6.2"}, + {vsn, "7.0"}, {modules, [argparse, array, base64, @@ -116,11 +116,12 @@ unicode_util, uri_string, win32reg, - zip]}, + zip, + zstd]}, {registered,[timer_server,rsh_starter,take_over_monitor,pool_master, dets]}, {applications, [kernel]}, {env, []}, - {runtime_dependencies, ["sasl-3.0","kernel-10.0","erts-15.0","crypto-4.5", + {runtime_dependencies, ["sasl-3.0","kernel-10.0","erts-16.0","crypto-4.5", "compiler-5.0", "syntax_tools-3.2.1"]} ]}. diff --git a/bootstrap/lib/stdlib/ebin/stdlib.appup b/bootstrap/lib/stdlib/ebin/stdlib.appup index 48350eded47d..dec875ac510e 100644 --- a/bootstrap/lib/stdlib/ebin/stdlib.appup +++ b/bootstrap/lib/stdlib/ebin/stdlib.appup @@ -21,50 +21,58 @@ %% %% We allow upgrade from, and downgrade to all previous %% versions from the following OTP releases: -%% - OTP 23 -%% - OTP 24 -%% - OTP 25 +%% - OTP 26 +%% - OTP 27 +%% - OTP 28 %% %% We also allow upgrade from, and downgrade to all %% versions that have branched off from the above %% stated previous versions. %% -{"4.0", - [{<<"^3\\.13$">>,[restart_new_emulator]}, - {<<"^3\\.13\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^3\\.13\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.13\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.14$">>,[restart_new_emulator]}, - {<<"^3\\.14\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^3\\.14\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.14\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.15$">>,[restart_new_emulator]}, - {<<"^3\\.15\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^3\\.15\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.15\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.16$">>,[restart_new_emulator]}, - {<<"^3\\.16\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^3\\.16\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.17$">>,[restart_new_emulator]}, - {<<"^3\\.17\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^3\\.17\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.17\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}], - [{<<"^3\\.13$">>,[restart_new_emulator]}, - {<<"^3\\.13\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^3\\.13\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.13\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.14$">>,[restart_new_emulator]}, - {<<"^3\\.14\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^3\\.14\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.14\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.15$">>,[restart_new_emulator]}, - {<<"^3\\.15\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^3\\.15\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.15\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.16$">>,[restart_new_emulator]}, - {<<"^3\\.16\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^3\\.16\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.17$">>,[restart_new_emulator]}, - {<<"^3\\.17\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^3\\.17\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.17\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}. +{"7.0", + [{<<"^5\\.0$">>,[restart_new_emulator]}, + {<<"^5\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^5\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.0\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.1$">>,[restart_new_emulator]}, + {<<"^5\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^5\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.2$">>,[restart_new_emulator]}, + {<<"^5\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^5\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.2\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.2\\.3(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.0$">>,[restart_new_emulator]}, + {<<"^6\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^6\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.1$">>,[restart_new_emulator]}, + {<<"^6\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^6\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.1\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.2$">>,[restart_new_emulator]}, + {<<"^6\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^6\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.2\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}], + [{<<"^5\\.0$">>,[restart_new_emulator]}, + {<<"^5\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^5\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.0\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.1$">>,[restart_new_emulator]}, + {<<"^5\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^5\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.2$">>,[restart_new_emulator]}, + {<<"^5\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^5\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.2\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.2\\.3(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.0$">>,[restart_new_emulator]}, + {<<"^6\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^6\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.1$">>,[restart_new_emulator]}, + {<<"^6\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^6\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.1\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.2$">>,[restart_new_emulator]}, + {<<"^6\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^6\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.2\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}. diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 20c469620f52..ea0a39ffd1fe 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -828,3 +828,8 @@ bif erl_debugger:stack_frames/2 bif erl_debugger:peek_stack_frame_slot/4 bif erl_debugger:xregs_count/1 bif erl_debugger:peek_xreg/3 + +# +# New in 29. +# +ubif erlang:is_between/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..41dec6ede96d 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_between_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..ca48466aaca1 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -774,6 +774,12 @@ static DMCGuardBif guard_tab[] = 3, DBIF_ALL }, + { + am_is_between, + &is_between_3, + 3, + DBIF_ALL + }, { am_tl, &tl_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..93065dd58ee6 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_between_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_between_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_between_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_between), + Lower0 = LB, + Upper0 = UB, + Lower = id(Lower0), + Upper = id(Upper0), + + X1 = id(X0), + Result = is_between(X1, Lower0, Upper0), + Result = is_between(X1, Lower, Upper), + Result = apply(erlang, F, id([X1, Lower, Upper])), + Result = erlang:F(X1, Lower, Upper), + + false = is_between(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_between), + + ?assertError(badarg, is_between(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..9a3854038045 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_between, [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 7507a883b625..78cd050ea767 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 74428e41098d..daf1b2ddbf67 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -486,8 +486,8 @@ A list of binaries. This datatype is useful to use together with is_atom/1, is_binary/1, is_bitstring/1, is_boolean/1, is_float/1, is_function/1, is_function/2, is_integer/1, is_list/1, is_map/1, is_number/1, is_pid/1, is_port/1, is_record/2, - is_record/3, is_reference/1, is_tuple/1, load_module/2, - load_nif/2, localtime_to_universaltime/2, make_fun/3, + is_record/3, is_reference/1, is_tuple/1, is_between/3, + load_module/2, load_nif/2, localtime_to_universaltime/2, make_fun/3, make_tuple/2, make_tuple/3, open_port/2, port_call/2, port_call/3, port_info/1, port_info/2, process_flag/2, process_info/2, send/2, send/3, seq_trace_info/1, @@ -7302,6 +7302,31 @@ false is_tuple(_Term) -> erlang:nif_error(undefined). +%% Shadowed by erl_bif_types: erlang:is_between/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_between(15, 0, 1024). +true +2> is_between(-1, 0, 1). +false +``` + +Failure: `badarg` if `LB` or `UB` does not evaluate to an integer. +""". +-doc #{ category => terms }. +-spec is_between(Term, LB, UB) -> boolean() when + Term :: integer(), + LB :: integer(), + UB :: integer(). +is_between(_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..21fe8c2a87a1 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_between', [_, _, _]=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 @@ -519,6 +531,9 @@ types(erlang, abs, [Type]) -> sub_unsafe(#t_number{}, [#t_number{}]) end; +types(erlang, is_between, [_Term, _LB, _UB]) -> + sub_unsafe(beam_types:make_boolean(), [any, #t_integer{}, #t_integer{}]); + %% The rest of the mixed-type arithmetic is handled in the catch-all %% clause for the 'erlang' module. diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl index ef6de4a8693e..d6dc7e37135d 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 1f274580f575..d61e05e1c550 100644 --- a/lib/compiler/src/beam_ssa_codegen.erl +++ b/lib/compiler/src/beam_ssa_codegen.erl @@ -2763,6 +2763,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_between, [_,_,_]) -> 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 4fc82ca964f5..fbb758f7fe59 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), @@ -550,7 +551,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_between}}) -> 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; @@ -560,6 +562,49 @@ 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}. + +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_between},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 b455f3f4a91d..4a231409180c 100644 --- a/lib/compiler/src/beam_ssa_pre_codegen.erl +++ b/lib/compiler/src/beam_ssa_pre_codegen.erl @@ -2824,6 +2824,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_between}) -> 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..f3a8d199edd4 100644 --- a/lib/compiler/src/beam_ssa_type.erl +++ b/lib/compiler/src/beam_ssa_type.erl @@ -2835,6 +2835,16 @@ infer_type({bif,is_reference}, [#b_var{}=Arg], _Ts, _Ds) -> infer_type({bif,is_tuple}, [#b_var{}=Arg], _Ts, _Ds) -> T = {Arg, #t_tuple{}}, {[T], [T]}; +infer_type({bif,is_between}, [#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_between}, [#b_var{}=Arg, _, _], _Ts, _Ds) -> + %% We cannot subtract the type when the bounds are unknown: `Arg` may still + %% be an integer if it is not in the tested range. + T = {Arg, #t_integer{}}, + {[T], []}; infer_type({bif,'and'}, [#b_var{}=LHS,#b_var{}=RHS], Ts, Ds) -> %% When this BIF yields true, we know that both `LHS` and `RHS` are 'true' %% and should infer accordingly, lest we break later optimizations that diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index e89fecb38275..d8d4ff3a64ab 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -2347,6 +2347,19 @@ infer_types_1(#value{op={bif,is_reference},args=[Src]}, Val, Op, Vst) -> infer_type_test_bif(reference, Src, Val, Op, Vst); infer_types_1(#value{op={bif,is_tuple},args=[Src]}, Val, Op, Vst) -> infer_type_test_bif(#t_tuple{}, Src, Val, Op, Vst); +infer_types_1(#value{op={bif,is_between},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_between},args=[Src, _, _]}, Val, Op, Vst) -> + %% Unknown bounds; we know it's an integer when 'true', but cannot draw + %% any conclusions when 'false'. + case Val of + {atom, Bool} when Op =:= eq_exact, Bool; Op =:= ne_exact, not Bool -> + update_type(fun meet/2, #t_integer{}, Src, Vst); + _ -> + Vst + end; infer_types_1(#value{op={bif,tuple_size}, args=[Tuple]}, {integer,Arity}, Op, Vst) -> Type = #t_tuple{exact=true,size=Arity}, diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl index ee81d218a42f..360418bfacea 100644 --- a/lib/compiler/src/erl_bifs.erl +++ b/lib/compiler/src/erl_bifs.erl @@ -106,6 +106,7 @@ is_pure(erlang, hd, 1) -> true; is_pure(erlang, integer_to_binary, 1) -> true; is_pure(erlang, integer_to_list, 1) -> true; is_pure(erlang, is_atom, 1) -> true; +is_pure(erlang, is_between, 3) -> true; is_pure(erlang, is_boolean, 1) -> true; is_pure(erlang, is_binary, 1) -> true; is_pure(erlang, is_bitstring, 1) -> true; diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab index 5e736988186e..d5ad7248ff0a 100755 --- a/lib/compiler/src/genop.tab +++ b/lib/compiler/src/genop.tab @@ -707,3 +707,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..3ee694100316 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_between_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_between_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_between(7, Glurf, Glurf) -> ok; + true -> error + end + end, error), + + check(fun() -> if + not is_between(10, C, D) -> ok; + true -> error + end + end, ok), + + check(fun() -> + X = id(20), + if + not is_between(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_between(1, Lower, Upper) -> ok; + true -> error + end + end, error), + + check(fun() -> + Lower = id(1), + Upper = id(10), + if + false or is_between(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_between(X, 16#0030, 16#0039); + is_between(X, 16#06F0, 16#06F9); + is_between(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_between(X, 10, 20), 13 =/= X -> true; +broken_range_14(X) when is_between(X, 3, 5) -> true; +broken_range_14(_) -> false. + rel_op_combinations_3(0, _) -> ok; rel_op_combinations_3(N, Red) -> @@ -3363,6 +3415,48 @@ body_in_guard(_Config) -> demonitor(Mon) end. +is_between_guard(_Config) -> + Lower = id(1), + Upper = id(10), + _ = [begin + Expected = Lower =< X andalso X =< Upper, + Expected = is_between_guard_1(X, Lower, Upper), + false = is_between_guard_1(float(X), Lower, Upper) + end || X <- lists:seq(-7, 17)], + + ?assertError(badarg, is_between_guard_1(2, 1.5, 10)), + ?assertError(badarg, is_between_guard_1(2, true, 10)), + ?assertError(badarg, is_between_guard_1(2, 10, b)), + + false = is_between_guard_2(id(0)), + true = is_between_guard_2(id(1)), + true = is_between_guard_2(id(32)), + true = is_between_guard_2(id(1024)), + false = is_between_guard_2(id(1025)), + + true = is_between_guard_3(id(0)), + false = is_between_guard_3(id(1)), + false = is_between_guard_3(id(32)), + false = is_between_guard_3(id(1024)), + true = is_between_guard_3(id(1025)), + + ok. + +is_between_guard_1(X, LB, UB) when is_between(X, LB, UB) -> + true = is_between(X, LB, UB); +is_between_guard_1(X, LB, UB) -> + is_between(X, LB, UB). + +is_between_guard_2(X) when is_between(X, 1, 1024) -> + true = is_between(X, 1, 1024); +is_between_guard_2(X) -> + is_between(X, 1, 1024). + +is_between_guard_3(X) when not is_between(X, 1, 1024) -> + true = not is_between(X, 1, 1024); +is_between_guard_3(X) -> + not is_between(X, 1, 1024). + %% 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..6abfc4d30526 100644 --- a/lib/dialyzer/src/erl_bif_types.erl +++ b/lib/dialyzer/src/erl_bif_types.erl @@ -2337,6 +2337,8 @@ arg_types(erlang, tuple_size, 1) -> [t_tuple()]; arg_types(erlang, tuple_to_list, 1) -> [t_tuple()]; +arg_types(erlang, is_between, 3) -> + [t_any(),t_integer(), t_integer()]; %%------- lists --------------------------------------------------------------- arg_types(lists, all, 2) -> diff --git a/lib/kernel/src/erl_erts_errors.erl b/lib/kernel/src/erl_erts_errors.erl index c4b852987472..c403c9a92771 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_between, [_,_,_], _) -> + [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..add5711bb27c 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_between, 3) -> true; guard_bif(is_map_key, 2) -> true; guard_bif(length, 1) -> true; guard_bif(map_size, 1) -> true; @@ -342,6 +343,7 @@ bif(integer_to_list, 2) -> true; bif(iolist_size, 1) -> true; bif(iolist_to_binary, 1) -> true; bif(is_alive, 0) -> true; +bif(is_between, 3) -> true; bif(is_process_alive, 1) -> true; bif(is_atom, 1) -> true; bif(is_boolean, 1) -> true;