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

openSUSE Build Service is sponsored by