File 6001-Allow-max-2-and-min-2-to-be-used-in-guards.patch of Package erlang
From 5da4b90d861e75bf913b420bd810f5ce3c1c0cbc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Fri, 16 Dec 2022 04:07:03 +0100
Subject: [PATCH] Allow max/2 and min/2 to be used in guards
Closes #6544
---
erts/doc/src/erlang.xml | 4 +++
erts/doc/src/match_spec.xml | 12 ++++++--
erts/emulator/beam/bif.tab | 6 ++++
erts/emulator/beam/erl_bif_guard.c | 10 +++++++
erts/emulator/beam/erl_db_util.c | 12 ++++++++
erts/emulator/test/bif_SUITE.erl | 32 ++++++++++-----------
erts/preloaded/src/erlang.erl | 4 +++
lib/compiler/src/beam_ssa_codegen.erl | 2 ++
lib/compiler/src/compile.erl | 2 +-
lib/compiler/src/v3_kernel.erl | 30 +++++++++++++------
lib/stdlib/src/erl_internal.erl | 2 ++
lib/stdlib/src/ms_transform.erl | 2 ++
lib/stdlib/test/ms_transform_SUITE.erl | 2 ++
system/doc/reference_manual/expressions.xml | 9 ++++++
14 files changed, 100 insertions(+), 29 deletions(-)
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 6ef15d73ab..27c72ce8b2 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -4229,6 +4229,8 @@ is_process_alive(P2Pid),
<pre>
> <input>max("abc", "b").</input>
"b"</pre>
+ <p>Allowed in guard tests.</p>
+ <change><p>Allowed in guards tests from Erlang/OTP 26.</p></change>
</desc>
</func>
@@ -4481,6 +4483,8 @@ RealSystem = system + MissedSystem</code>
<pre>
> <input>min("abc", "b").</input>
"abc"</pre>
+ <p>Allowed in guard tests.</p>
+ <change><p>Allowed in guards tests from Erlang/OTP 26.</p></change>
</desc>
</func>
diff --git a/erts/doc/src/match_spec.xml b/erts/doc/src/match_spec.xml
index 988f895dde..62ddefdc47 100644
--- a/erts/doc/src/match_spec.xml
+++ b/erts/doc/src/match_spec.xml
@@ -111,7 +111,9 @@
<item>GuardFunction ::= BoolFunction | <c><![CDATA[abs]]></c> |
<c><![CDATA[element]]></c> | <c><![CDATA[hd]]></c> |
<c><![CDATA[length]]></c> | <c><![CDATA[map_get]]></c> |
- <c><![CDATA[map_size]]></c> | <c><![CDATA[node]]></c> |
+ <c><![CDATA[map_size]]></c> |
+ <c><![CDATA[max]]></c> | <c><![CDATA[min]]></c> |
+ <c><![CDATA[node]]></c> |
<c><![CDATA[round]]></c> | <c><![CDATA[size]]></c> |
<c><![CDATA[bit_size]]></c> | <c><![CDATA[byte_size]]></c> |
<c><![CDATA[tl]]></c> | <c><![CDATA[trunc]]></c> |
@@ -195,7 +197,9 @@
<item>GuardFunction ::= BoolFunction | <c><![CDATA[abs]]></c> |
<c><![CDATA[element]]></c> | <c><![CDATA[hd]]></c> |
<c><![CDATA[length]]></c> | <c><![CDATA[map_get]]></c> |
- <c><![CDATA[map_size]]></c> | <c><![CDATA[node]]></c> |
+ <c><![CDATA[map_size]]></c> |
+ <c><![CDATA[max]]></c> | <c><![CDATA[min]]></c> |
+ <c><![CDATA[node]]></c> |
<c><![CDATA[round]]></c> | <c><![CDATA[size]]></c> |
<c><![CDATA[bit_size]]></c> | <c><![CDATA[byte_size]]></c> |
<c><![CDATA[tl]]></c> | <c><![CDATA[trunc]]></c> |
@@ -275,7 +279,9 @@
<c><![CDATA['xor']]></c> returns false.</p>
</item>
<tag><c>abs</c>, <c>element</c>, <c>hd</c>, <c>length</c>,
- <c>map_get</c>, <c>map_size</c>, <c>node</c>, <c>round</c>,
+ <c>map_get</c>, <c>map_size</c>,
+ <c>max</c>, <c>min</c>,
+ <c>node</c>, <c>round</c>,
<c>size</c>, <c>bit_size</c>, <c>byte_size</c>, <c>tl</c>,
<c>trunc</c>, <c>binary_part</c>, <c>'+'</c>,
<c>'-'</c>, <c>'*'</c>, <c>'div'</c>, <c>'rem'</c>, <c>'band'</c>,
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 455992e0e9..e1ca5a961b 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -778,3 +778,9 @@ bif erlang:unalias/1
# New in 24 (in a patch)
bif erts_internal:binary_to_integer/2
bif erts_internal:list_to_integer/2
+
+#
+# New in 26
+#
+ubif erlang:min/2
+ubif erlang:max/2
diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c
index 2242570cad..62a48a45ff 100644
--- a/erts/emulator/beam/erl_bif_guard.c
+++ b/erts/emulator/beam/erl_bif_guard.c
@@ -462,3 +462,13 @@ BIF_RETTYPE binary_part_2(BIF_ALIST_2)
badarg:
BIF_ERROR(BIF_P,BADARG);
}
+
+BIF_RETTYPE min_2(BIF_ALIST_2)
+{
+ return CMP_GT(BIF_ARG_1, BIF_ARG_2) ? BIF_ARG_2 : BIF_ARG_1;
+}
+
+BIF_RETTYPE max_2(BIF_ALIST_2)
+{
+ return CMP_LT(BIF_ARG_1, BIF_ARG_2) ? BIF_ARG_2 : BIF_ARG_1;
+}
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 8243ac1a73..e05c552c0e 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -629,6 +629,18 @@ static DMCGuardBif guard_tab[] =
1,
DBIF_ALL
},
+ {
+ am_max,
+ &max_2,
+ 2,
+ DBIF_ALL
+ },
+ {
+ am_min,
+ &min_2,
+ 2,
+ DBIF_ALL
+ },
{
am_node,
&node_1,
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl
index 69e911e796..32bf961fab 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -356,6 +356,11 @@ auto_imports([], Errors) ->
extract_functions(M, Abstr) ->
[{{M,F,A},Body} || {function,_,F,A,Body} <- Abstr].
+check_stub({_,F,2}, _B) when F =:= min; F =:= max ->
+ %% In Erlang/OTP 26, min/2 and max/2 are guard BIFs. For backward
+ %% compatibility with code compiled with an earlier version, the
+ %% Erlang implementation of them is kept.
+ ok;
check_stub({_,F,A}, B) ->
try
[{clause,_,Args,[],Body}] = B,
@@ -724,19 +729,7 @@ fail_atom_to_binary(Term) ->
end.
-min_max(Config) when is_list(Config) ->
- a = erlang:min(id(a), a),
- a = erlang:min(id(a), b),
- a = erlang:min(id(b), a),
- b = erlang:min(id(b), b),
- a = erlang:max(id(a), a),
- b = erlang:max(id(a), b),
- b = erlang:max(id(b), a),
- b = erlang:max(id(b), b),
-
- 42.0 = erlang:min(42.0, 42),
- 42.0 = erlang:max(42.0, 42),
- %% And now (R14) they are also autoimported!
+min_max(Config) when is_list(Config) ->
a = min(id(a), a),
a = min(id(a), b),
a = min(id(b), a),
@@ -746,10 +739,17 @@ min_max(Config) when is_list(Config) ->
b = max(id(b), a),
b = max(id(b), b),
- 42.0 = min(42.0, 42),
- 42.0 = max(42.0, 42),
- ok.
+ %% Return the first argument when arguments are equal.
+ 42.0 = min(id(42.0), 42),
+ 42.0 = max(id(42.0), 42),
+ Min = id(min),
+ Max = id(max),
+
+ "abc" = erlang:Min("abc", "def"),
+ <<"def">> = erlang:Max(<<"abc">>, <<"def">>),
+
+ ok.
erlang_halt(Config) when is_list(Config) ->
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 2703e865cd..b650eb70aa 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -4043,6 +4043,8 @@ rvrs([X|Xs],Ys) -> rvrs(Xs, [X|Ys]).
Term1 :: term(),
Term2 :: term(),
Minimum :: term().
+%% In Erlang/OTP 26, min/2 is a guard BIF. This implementation is kept
+%% for backward compatibility with code compiled with an earlier version.
min(A, B) when A > B -> B;
min(A, _) -> A.
@@ -4051,6 +4053,8 @@ min(A, _) -> A.
Term1 :: term(),
Term2 :: term(),
Maximum :: term().
+%% In Erlang/OTP 26, max/2 is a guard BIF. This implementation is kept
+%% for backward compatibility with code compiled with an earlier version.
max(A, B) when A < B -> B;
max(A, _) -> A.
diff --git a/lib/compiler/src/beam_ssa_codegen.erl b/lib/compiler/src/beam_ssa_codegen.erl
index 869490e710..93b707c476 100644
--- a/lib/compiler/src/beam_ssa_codegen.erl
+++ b/lib/compiler/src/beam_ssa_codegen.erl
@@ -2397,6 +2397,8 @@ local_func_label(Key, #cg{functable=Map}=St0) ->
is_gc_bif(hd, [_]) -> false;
is_gc_bif(tl, [_]) -> false;
is_gc_bif(self, []) -> false;
+is_gc_bif(max, [_,_]) -> false;
+is_gc_bif(min, [_,_]) -> false;
is_gc_bif(node, []) -> false;
is_gc_bif(node, [_]) -> false;
is_gc_bif(element, [_,_]) -> false;
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 70b2b56d68..4074f12cd4 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -119,6 +119,7 @@ copy_anno(Kdst, Ksrc) ->
free=#{}, %Free variables
ws=[] :: [warning()], %Warnings.
no_shared_fun_wrappers=false :: boolean(),
+ no_min_max_bifs=false :: boolean(),
labels=sets:new([{version, 2}])
}).
@@ -130,7 +131,9 @@ module(#c_module{anno=A,name=M,exports=Es,attrs=As,defs=Fs}, Options) ->
Kes = map(fun (#c_var{name={_,_}=Fname}) -> Fname end, Es),
NoSharedFunWrappers = proplists:get_bool(no_shared_fun_wrappers,
Options),
- St0 = #kern{no_shared_fun_wrappers=NoSharedFunWrappers},
+ NoMinMaxBifs = proplists:get_bool(no_min_max_bifs, Options),
+ St0 = #kern{no_shared_fun_wrappers=NoSharedFunWrappers,
+ no_min_max_bifs=NoMinMaxBifs},
{Kfs,St} = mapfoldl(fun function/2, St0, Fs),
{ok,#k_mdef{anno=A,name=M#c_literal.val,exports=Kes,attributes=Kas,
body=Kfs ++ St#kern.funs},sort(St#kern.ws)}.
@@ -323,7 +326,7 @@ expr(#c_call{anno=A,module=M0,name=F0,args=Cargs}, Sub, St0) ->
Ar = length(Cargs),
{[M,F|Kargs],Ap,St1} = atomic_list([M0,F0|Cargs], Sub, St0),
Remote = #k_remote{mod=M,name=F,arity=Ar},
- case call_type(M0, F0, Cargs) of
+ case call_type(M0, F0, Cargs, St1) of
bif ->
{#k_bif{anno=A,op=Remote,args=Kargs},Ap,St1};
call ->
@@ -543,15 +546,24 @@ map_key_clean(#k_literal{val=V}) -> {lit,V}.
%% call_type(Module, Function, Arity) -> call | bif | error.
%% Classify the call.
-call_type(#c_literal{val=M}, #c_literal{val=F}, As) when is_atom(M), is_atom(F) ->
+call_type(#c_literal{val=M}, #c_literal{val=F}, As, St) when is_atom(M), is_atom(F) ->
case is_remote_bif(M, F, As) of
- false -> call;
- true -> bif
+ false ->
+ call;
+ true ->
+ %% The guard BIFs min/2 and max/2 were introduced in
+ %% Erlang/OTP 26. If we are compiling for an earlier
+ %% version, we must translate them as call instructions.
+ case {M,F,St#kern.no_min_max_bifs} of
+ {erlang,min,true} -> call;
+ {erlang,max,true} -> call;
+ {_,_,_} -> bif
+ end
end;
-call_type(#c_var{}, #c_literal{val=A}, _) when is_atom(A) -> call;
-call_type(#c_literal{val=A}, #c_var{}, _) when is_atom(A) -> call;
-call_type(#c_var{}, #c_var{}, _) -> call;
-call_type(_, _, _) -> error.
+call_type(#c_var{}, #c_literal{val=A}, _, _) when is_atom(A) -> call;
+call_type(#c_literal{val=A}, #c_var{}, _, _) when is_atom(A) -> call;
+call_type(#c_var{}, #c_var{}, _, _) -> call;
+call_type(_, _, _, _) -> error.
%% match_vars(Kexpr, State) -> {[Kvar],[PreKexpr],State}.
%% Force return from body into a list of variables.
diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl
index 92651084c7..8453c1e7b7 100644
--- a/lib/stdlib/src/erl_internal.erl
+++ b/lib/stdlib/src/erl_internal.erl
@@ -78,6 +78,8 @@ guard_bif(is_map_key, 2) -> true;
guard_bif(length, 1) -> true;
guard_bif(map_size, 1) -> true;
guard_bif(map_get, 2) -> true;
+guard_bif(max, 2) -> true;
+guard_bif(min, 2) -> true;
guard_bif(node, 0) -> true;
guard_bif(node, 1) -> true;
guard_bif(round, 1) -> true;
diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl
index dde8e572a3..ba3a5c42bf 100644
--- a/lib/stdlib/src/ms_transform.erl
+++ b/lib/stdlib/src/ms_transform.erl
@@ -977,6 +977,8 @@ real_guard_function(abs,1) -> true;
real_guard_function(element,2) -> true;
real_guard_function(hd,1) -> true;
real_guard_function(length,1) -> true;
+real_guard_function(max,2) -> true;
+real_guard_function(min,2) -> true;
real_guard_function(node,0) -> true;
real_guard_function(node,1) -> true;
real_guard_function(round,1) -> true;
diff --git a/lib/stdlib/test/ms_transform_SUITE.erl b/lib/stdlib/test/ms_transform_SUITE.erl
index c34c7e9e69..2a7b003809 100644
--- a/lib/stdlib/test/ms_transform_SUITE.erl
+++ b/lib/stdlib/test/ms_transform_SUITE.erl
@@ -582,6 +582,8 @@ autoimported(Config) when is_list(Config) ->
{element,2},
{hd,1},
{length,1},
+ {max,2},
+ {min,2},
{node,0},
{node,1},
{round,1},
diff --git a/system/doc/reference_manual/expressions.xml b/system/doc/reference_manual/expressions.xml
index 341fa32432..3ad0616dd2 100644
--- a/system/doc/reference_manual/expressions.xml
+++ b/system/doc/reference_manual/expressions.xml
@@ -2022,6 +2022,12 @@ end</pre>
<row>
<cell align="left" valign="middle"><c>map_size(Map)</c></cell>
</row>
+ <row>
+ <cell align="left" valign="middle"><c>max(A, B)</c></cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle"><c>min(A, B)</c></cell>
+ </row>
<row>
<cell align="left" valign="middle"><c>node()</c></cell>
</row>
@@ -2049,6 +2055,9 @@ end</pre>
<tcaption>Other BIFs Allowed in Guard Expressions</tcaption>
</table>
+ <change><p>The <c>min/2</c> and <c>max/2</c> BIFs are allowed to be
+ used in guards from Erlang/OTP 26.</p></change>
+
<p>If an arithmetic expression, a Boolean expression, a
short-circuit expression, or a call to a guard BIF fails (because
of invalid arguments), the entire guard fails. If the guard was
--
2.35.3