[v2] c++: P2865R5, Remove Deprecated Array Comparisons from C++26 [PR117788]

Message ID Z09gLIkEN97M0drr@redhat.com
State New
Headers
Series [v2] c++: P2865R5, Remove Deprecated Array Comparisons from C++26 [PR117788] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed

Commit Message

Marek Polacek Dec. 3, 2024, 7:46 p.m. UTC
  On Thu, Nov 28, 2024 at 12:04:56PM -0500, Jason Merrill wrote:
> On 11/27/24 9:06 PM, Marek Polacek wrote:
> > Not a bugfix, but this should only affect C++26.
> > 
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> > 
> > -- >8--
> > This patch implements P2865R5 by promoting the warning to error in C++26
> > only.  -Wno-array-compare shouldn't disable the error, so adjust the call
> > sites as well.
> 
> I think it's fine for -Wno-array-compare to suppress the error (and
> -Wno-error=array-compare to reduce it to a warning), so how about
> DK_PERMERROR rather than DK_ERROR?

Sounds good.
 
> We also need SFINAE for this when !tf_warning_or_error.

I've added Warray-compare-1.C, which has:

  template<int I>
  void f (int(*)[arr1 == arr2 ? I : I]);

but when we call cp_build_binary_op from the parser, complain is
tf_warning_or_error, so we warn (as does clang++).  I suspect
that goes against [temp.deduct.general]/8.  Do you think I should
avoid calling do_warn_array_compare when processing_template_decl?
Or downgrade the permerror to a warning?

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
This patch implements P2865R5 by promoting the warning to permerror in
C++26 only.

In C++20 we should warn even without -Wall.  Jason fixed this in r15-5713
but let's add a test that doesn't use -Wall.

	PR c++/117788

gcc/c-family/ChangeLog:

	* c-warn.cc (do_warn_array_compare): Emit a permerror in C++26.

gcc/cp/ChangeLog:

	* typeck.cc (cp_build_binary_op) <case EQ_EXPR>: Don't check
	warn_array_compare.  Check tf_warning_or_error instead of just
	tf_warning.
	<case LE_EXPR>: Likewise.

gcc/testsuite/ChangeLog:

	* c-c++-common/Warray-compare-1.c: Expect an error in C++26.
	* c-c++-common/Warray-compare-3.c: Likewise.
	* c-c++-common/Warray-compare-4.c: New test.
	* c-c++-common/Warray-compare-5.c: New test.
	* g++.dg/warn/Warray-compare-1.C: New test.
---
 gcc/c-family/c-warn.cc                        | 25 +++++++---
 gcc/cp/typeck.cc                              | 10 ++--
 gcc/testsuite/c-c++-common/Warray-compare-1.c | 21 +++++---
 gcc/testsuite/c-c++-common/Warray-compare-3.c |  7 +--
 gcc/testsuite/c-c++-common/Warray-compare-4.c | 50 +++++++++++++++++++
 gcc/testsuite/c-c++-common/Warray-compare-5.c | 44 ++++++++++++++++
 gcc/testsuite/g++.dg/warn/Warray-compare-1.C  | 26 ++++++++++
 7 files changed, 161 insertions(+), 22 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/Warray-compare-4.c
 create mode 100644 gcc/testsuite/c-c++-common/Warray-compare-5.c
 create mode 100644 gcc/testsuite/g++.dg/warn/Warray-compare-1.C


base-commit: 0b01549916d7526d27d652cbea1f448399fcee33
  

Comments

Jason Merrill Dec. 3, 2024, 9:27 p.m. UTC | #1
On 12/3/24 2:46 PM, Marek Polacek wrote:
> On Thu, Nov 28, 2024 at 12:04:56PM -0500, Jason Merrill wrote:
>> On 11/27/24 9:06 PM, Marek Polacek wrote:
>>> Not a bugfix, but this should only affect C++26.
>>>
>>> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
>>>
>>> -- >8--
>>> This patch implements P2865R5 by promoting the warning to error in C++26
>>> only.  -Wno-array-compare shouldn't disable the error, so adjust the call
>>> sites as well.
>>
>> I think it's fine for -Wno-array-compare to suppress the error (and
>> -Wno-error=array-compare to reduce it to a warning), so how about
>> DK_PERMERROR rather than DK_ERROR?
> 
> Sounds good.
>   
>> We also need SFINAE for this when !tf_warning_or_error.
> 
> I've added Warray-compare-1.C, which has:
> 
>    template<int I>
>    void f (int(*)[arr1 == arr2 ? I : I]);
> 
> but when we call cp_build_binary_op from the parser, complain is
> tf_warning_or_error, so we warn (as does clang++).  I suspect
> that goes against [temp.deduct.general]/8.

No, that's fine; in C++26 that template is IFNDR because no well-formed 
instantiation exists, it's OK for us to give a diagnostic and then 
continue just like in a non-template.

I'm not sure there is a SFINAE situation where this would come up, but 
I'd still like to adjust this:

> @@ -6125,11 +6124,10 @@ cp_build_binary_op (const op_location_t &location,
>   			"comparison with string literal results "
>   			"in unspecified behavior");
>   	}
> -      else if (warn_array_compare
> -	       && TREE_CODE (TREE_TYPE (orig_op0)) == ARRAY_TYPE
> +      else if (TREE_CODE (TREE_TYPE (orig_op0)) == ARRAY_TYPE
>   	       && TREE_CODE (TREE_TYPE (orig_op1)) == ARRAY_TYPE
>   	       && code != SPACESHIP_EXPR
> -	       && (complain & tf_warning))
> +	       && (complain & tf_warning_or_error))
>   	do_warn_array_compare (location, code,
>   			       tree_strip_any_location_wrapper (orig_op0),
>   			       tree_strip_any_location_wrapper (orig_op1));

If we happen to get here when not complaining, we'll silently accept it. 
  Either we should handle that case by returning error_mark_node in 
C++26 and above, or we should assert that it can't happen.

Jason
  
Marek Polacek Dec. 4, 2024, 5:27 p.m. UTC | #2
On Tue, Dec 03, 2024 at 04:27:22PM -0500, Jason Merrill wrote:
> On 12/3/24 2:46 PM, Marek Polacek wrote:
> > On Thu, Nov 28, 2024 at 12:04:56PM -0500, Jason Merrill wrote:
> > > On 11/27/24 9:06 PM, Marek Polacek wrote:
> > > > Not a bugfix, but this should only affect C++26.
> > > > 
> > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> > > > 
> > > > -- >8--
> > > > This patch implements P2865R5 by promoting the warning to error in C++26
> > > > only.  -Wno-array-compare shouldn't disable the error, so adjust the call
> > > > sites as well.
> > > 
> > > I think it's fine for -Wno-array-compare to suppress the error (and
> > > -Wno-error=array-compare to reduce it to a warning), so how about
> > > DK_PERMERROR rather than DK_ERROR?
> > 
> > Sounds good.
> > > We also need SFINAE for this when !tf_warning_or_error.
> > 
> > I've added Warray-compare-1.C, which has:
> > 
> >    template<int I>
> >    void f (int(*)[arr1 == arr2 ? I : I]);
> > 
> > but when we call cp_build_binary_op from the parser, complain is
> > tf_warning_or_error, so we warn (as does clang++).  I suspect
> > that goes against [temp.deduct.general]/8.
> 
> No, that's fine; in C++26 that template is IFNDR because no well-formed
> instantiation exists, it's OK for us to give a diagnostic and then continue
> just like in a non-template.

Ah yes.

> I'm not sure there is a SFINAE situation where this would come up, but I'd
> still like to adjust this:
> 
> > @@ -6125,11 +6124,10 @@ cp_build_binary_op (const op_location_t &location,
> >   			"comparison with string literal results "
> >   			"in unspecified behavior");
> >   	}
> > -      else if (warn_array_compare
> > -	       && TREE_CODE (TREE_TYPE (orig_op0)) == ARRAY_TYPE
> > +      else if (TREE_CODE (TREE_TYPE (orig_op0)) == ARRAY_TYPE
> >   	       && TREE_CODE (TREE_TYPE (orig_op1)) == ARRAY_TYPE
> >   	       && code != SPACESHIP_EXPR
> > -	       && (complain & tf_warning))
> > +	       && (complain & tf_warning_or_error))
> >   	do_warn_array_compare (location, code,
> >   			       tree_strip_any_location_wrapper (orig_op0),
> >   			       tree_strip_any_location_wrapper (orig_op1));
> 
> If we happen to get here when not complaining, we'll silently accept it.
> Either we should handle that case by returning error_mark_node in C++26 and
> above, or we should assert that it can't happen.

We actually can get there.  But returning error_mark_node in C++26
causes problems: we hit:

        /* If we ran into a problem, make sure we complained.  */
        gcc_assert (seen_error ());

because a permerror doesn't count as an error.  Either we'd have to go
back to DK_ERROR, or leave the patch as-is.

Marek
  
Jason Merrill Dec. 5, 2024, 6:15 p.m. UTC | #3
On 12/4/24 12:27 PM, Marek Polacek wrote:
> On Tue, Dec 03, 2024 at 04:27:22PM -0500, Jason Merrill wrote:
>> On 12/3/24 2:46 PM, Marek Polacek wrote:
>>> On Thu, Nov 28, 2024 at 12:04:56PM -0500, Jason Merrill wrote:
>>>> On 11/27/24 9:06 PM, Marek Polacek wrote:
>>>>> Not a bugfix, but this should only affect C++26.
>>>>>
>>>>> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
>>>>>
>>>>> -- >8--
>>>>> This patch implements P2865R5 by promoting the warning to error in C++26
>>>>> only.  -Wno-array-compare shouldn't disable the error, so adjust the call
>>>>> sites as well.
>>>>
>>>> I think it's fine for -Wno-array-compare to suppress the error (and
>>>> -Wno-error=array-compare to reduce it to a warning), so how about
>>>> DK_PERMERROR rather than DK_ERROR?
>>>
>>> Sounds good.
>>>> We also need SFINAE for this when !tf_warning_or_error.
>>>
>>> I've added Warray-compare-1.C, which has:
>>>
>>>     template<int I>
>>>     void f (int(*)[arr1 == arr2 ? I : I]);
>>>
>>> but when we call cp_build_binary_op from the parser, complain is
>>> tf_warning_or_error, so we warn (as does clang++).  I suspect
>>> that goes against [temp.deduct.general]/8.
>>
>> No, that's fine; in C++26 that template is IFNDR because no well-formed
>> instantiation exists, it's OK for us to give a diagnostic and then continue
>> just like in a non-template.
> 
> Ah yes.
> 
>> I'm not sure there is a SFINAE situation where this would come up, but I'd
>> still like to adjust this:
>>
>>> @@ -6125,11 +6124,10 @@ cp_build_binary_op (const op_location_t &location,
>>>    			"comparison with string literal results "
>>>    			"in unspecified behavior");
>>>    	}
>>> -      else if (warn_array_compare
>>> -	       && TREE_CODE (TREE_TYPE (orig_op0)) == ARRAY_TYPE
>>> +      else if (TREE_CODE (TREE_TYPE (orig_op0)) == ARRAY_TYPE
>>>    	       && TREE_CODE (TREE_TYPE (orig_op1)) == ARRAY_TYPE
>>>    	       && code != SPACESHIP_EXPR
>>> -	       && (complain & tf_warning))
>>> +	       && (complain & tf_warning_or_error))
>>>    	do_warn_array_compare (location, code,
>>>    			       tree_strip_any_location_wrapper (orig_op0),
>>>    			       tree_strip_any_location_wrapper (orig_op1));
>>
>> If we happen to get here when not complaining, we'll silently accept it.
>> Either we should handle that case by returning error_mark_node in C++26 and
>> above, or we should assert that it can't happen.
> 
> We actually can get there.  But returning error_mark_node in C++26
> causes problems: we hit:
> 
>          /* If we ran into a problem, make sure we complained.  */
>          gcc_assert (seen_error ());
> 
> because a permerror doesn't count as an error.  Either we'd have to go
> back to DK_ERROR, or leave the patch as-is.

Hmm, I guess cp_seen_error should also consider werrorcount.

Jason
  
Marek Polacek Dec. 6, 2024, 5:29 p.m. UTC | #4
On Thu, Dec 05, 2024 at 01:15:49PM -0500, Jason Merrill wrote:
> On 12/4/24 12:27 PM, Marek Polacek wrote:
> > On Tue, Dec 03, 2024 at 04:27:22PM -0500, Jason Merrill wrote:
> > > On 12/3/24 2:46 PM, Marek Polacek wrote:
> > > > On Thu, Nov 28, 2024 at 12:04:56PM -0500, Jason Merrill wrote:
> > > > > On 11/27/24 9:06 PM, Marek Polacek wrote:
> > > > > > Not a bugfix, but this should only affect C++26.
> > > > > > 
> > > > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> > > > > > 
> > > > > > -- >8--
> > > > > > This patch implements P2865R5 by promoting the warning to error in C++26
> > > > > > only.  -Wno-array-compare shouldn't disable the error, so adjust the call
> > > > > > sites as well.
> > > > > 
> > > > > I think it's fine for -Wno-array-compare to suppress the error (and
> > > > > -Wno-error=array-compare to reduce it to a warning), so how about
> > > > > DK_PERMERROR rather than DK_ERROR?
> > > > 
> > > > Sounds good.
> > > > > We also need SFINAE for this when !tf_warning_or_error.
> > > > 
> > > > I've added Warray-compare-1.C, which has:
> > > > 
> > > >     template<int I>
> > > >     void f (int(*)[arr1 == arr2 ? I : I]);
> > > > 
> > > > but when we call cp_build_binary_op from the parser, complain is
> > > > tf_warning_or_error, so we warn (as does clang++).  I suspect
> > > > that goes against [temp.deduct.general]/8.
> > > 
> > > No, that's fine; in C++26 that template is IFNDR because no well-formed
> > > instantiation exists, it's OK for us to give a diagnostic and then continue
> > > just like in a non-template.
> > 
> > Ah yes.
> > 
> > > I'm not sure there is a SFINAE situation where this would come up, but I'd
> > > still like to adjust this:
> > > 
> > > > @@ -6125,11 +6124,10 @@ cp_build_binary_op (const op_location_t &location,
> > > >    			"comparison with string literal results "
> > > >    			"in unspecified behavior");
> > > >    	}
> > > > -      else if (warn_array_compare
> > > > -	       && TREE_CODE (TREE_TYPE (orig_op0)) == ARRAY_TYPE
> > > > +      else if (TREE_CODE (TREE_TYPE (orig_op0)) == ARRAY_TYPE
> > > >    	       && TREE_CODE (TREE_TYPE (orig_op1)) == ARRAY_TYPE
> > > >    	       && code != SPACESHIP_EXPR
> > > > -	       && (complain & tf_warning))
> > > > +	       && (complain & tf_warning_or_error))
> > > >    	do_warn_array_compare (location, code,
> > > >    			       tree_strip_any_location_wrapper (orig_op0),
> > > >    			       tree_strip_any_location_wrapper (orig_op1));
> > > 
> > > If we happen to get here when not complaining, we'll silently accept it.
> > > Either we should handle that case by returning error_mark_node in C++26 and
> > > above, or we should assert that it can't happen.
> > 
> > We actually can get there.  But returning error_mark_node in C++26
> > causes problems: we hit:
> > 
> >          /* If we ran into a problem, make sure we complained.  */
> >          gcc_assert (seen_error ());
> > 
> > because a permerror doesn't count as an error.  Either we'd have to go
> > back to DK_ERROR, or leave the patch as-is.
> 
> Hmm, I guess cp_seen_error should also consider werrorcount.

That still wouldn't work with -Wno-array-compare.  Nor would adding
permerrorcount.

I suppose I could still add permerrorcount and do permerrorcount++;,
and have cp_seen_error check permerrorcount.  Does that seem acceptable?

Marek
  
Jason Merrill Dec. 6, 2024, 5:48 p.m. UTC | #5
On 12/6/24 12:29 PM, Marek Polacek wrote:
> On Thu, Dec 05, 2024 at 01:15:49PM -0500, Jason Merrill wrote:
>> On 12/4/24 12:27 PM, Marek Polacek wrote:
>>> On Tue, Dec 03, 2024 at 04:27:22PM -0500, Jason Merrill wrote:
>>>> On 12/3/24 2:46 PM, Marek Polacek wrote:
>>>>> On Thu, Nov 28, 2024 at 12:04:56PM -0500, Jason Merrill wrote:
>>>>>> On 11/27/24 9:06 PM, Marek Polacek wrote:
>>>>>>> Not a bugfix, but this should only affect C++26.
>>>>>>>
>>>>>>> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
>>>>>>>
>>>>>>> -- >8--
>>>>>>> This patch implements P2865R5 by promoting the warning to error in C++26
>>>>>>> only.  -Wno-array-compare shouldn't disable the error, so adjust the call
>>>>>>> sites as well.
>>>>>>
>>>>>> I think it's fine for -Wno-array-compare to suppress the error (and
>>>>>> -Wno-error=array-compare to reduce it to a warning), so how about
>>>>>> DK_PERMERROR rather than DK_ERROR?
>>>>>
>>>>> Sounds good.
>>>>>> We also need SFINAE for this when !tf_warning_or_error.
>>>>>
>>>>> I've added Warray-compare-1.C, which has:
>>>>>
>>>>>      template<int I>
>>>>>      void f (int(*)[arr1 == arr2 ? I : I]);
>>>>>
>>>>> but when we call cp_build_binary_op from the parser, complain is
>>>>> tf_warning_or_error, so we warn (as does clang++).  I suspect
>>>>> that goes against [temp.deduct.general]/8.
>>>>
>>>> No, that's fine; in C++26 that template is IFNDR because no well-formed
>>>> instantiation exists, it's OK for us to give a diagnostic and then continue
>>>> just like in a non-template.
>>>
>>> Ah yes.
>>>
>>>> I'm not sure there is a SFINAE situation where this would come up, but I'd
>>>> still like to adjust this:
>>>>
>>>>> @@ -6125,11 +6124,10 @@ cp_build_binary_op (const op_location_t &location,
>>>>>     			"comparison with string literal results "
>>>>>     			"in unspecified behavior");
>>>>>     	}
>>>>> -      else if (warn_array_compare
>>>>> -	       && TREE_CODE (TREE_TYPE (orig_op0)) == ARRAY_TYPE
>>>>> +      else if (TREE_CODE (TREE_TYPE (orig_op0)) == ARRAY_TYPE
>>>>>     	       && TREE_CODE (TREE_TYPE (orig_op1)) == ARRAY_TYPE
>>>>>     	       && code != SPACESHIP_EXPR
>>>>> -	       && (complain & tf_warning))
>>>>> +	       && (complain & tf_warning_or_error))
>>>>>     	do_warn_array_compare (location, code,
>>>>>     			       tree_strip_any_location_wrapper (orig_op0),
>>>>>     			       tree_strip_any_location_wrapper (orig_op1));
>>>>
>>>> If we happen to get here when not complaining, we'll silently accept it.
>>>> Either we should handle that case by returning error_mark_node in C++26 and
>>>> above, or we should assert that it can't happen.
>>>
>>> We actually can get there.  But returning error_mark_node in C++26
>>> causes problems: we hit:
>>>
>>>           /* If we ran into a problem, make sure we complained.  */
>>>           gcc_assert (seen_error ());
>>>
>>> because a permerror doesn't count as an error.  Either we'd have to go
>>> back to DK_ERROR, or leave the patch as-is.
>>
>> Hmm, I guess cp_seen_error should also consider werrorcount.
> 
> That still wouldn't work with -Wno-array-compare.  Nor would adding
> permerrorcount.
> 
> I suppose I could still add permerrorcount and do permerrorcount++;,
> and have cp_seen_error check permerrorcount.  Does that seem acceptable?

If we didn't actually give an error, we shouldn't return 
error_mark_node.  That's what the assert is checking, and it's important 
to preserve that property (outside of SFINAE).  An error_mark_node 
without an error means silently generating garbage.

Jason
  

Patch

diff --git a/gcc/c-family/c-warn.cc b/gcc/c-family/c-warn.cc
index 7433bc00679..d77e60e0995 100644
--- a/gcc/c-family/c-warn.cc
+++ b/gcc/c-family/c-warn.cc
@@ -3818,8 +3818,9 @@  maybe_warn_sizeof_array_div (location_t loc, tree arr, tree arr_type,
 
 /* Warn about C++20 [depr.array.comp] array comparisons: "Equality
    and relational comparisons between two operands of array type are
-   deprecated."  We also warn in C and earlier C++ standards.  CODE is
-   the code for this comparison, OP0 and OP1 are the operands.  */
+   deprecated."  In C++26 this is a permerror.  We also warn in C and earlier
+   C++ standards.  CODE is the code for this comparison, OP0 and OP1 are
+   the operands.  */
 
 void
 do_warn_array_compare (location_t location, tree_code code, tree op0, tree op1)
@@ -3832,10 +3833,22 @@  do_warn_array_compare (location_t location, tree_code code, tree op0, tree op1)
     op1 = TREE_OPERAND (op1, 0);
 
   auto_diagnostic_group d;
-  if (warning_at (location, OPT_Warray_compare,
-		  (c_dialect_cxx () && cxx_dialect >= cxx20)
-		  ? G_("comparison between two arrays is deprecated in C++20")
-		  : G_("comparison between two arrays")))
+  diagnostic_t kind = DK_WARNING;
+  const char *msg;
+  if (c_dialect_cxx () && cxx_dialect >= cxx20)
+    {
+      /* P2865R5 made this comparison ill-formed in C++26.  */
+      if (cxx_dialect >= cxx26)
+	{
+	  msg = G_("comparison between two arrays is not allowed in C++26");
+	  kind = DK_PERMERROR;
+	}
+      else
+	msg = G_("comparison between two arrays is deprecated in C++20");
+    }
+  else
+    msg = G_("comparison between two arrays");
+  if (emit_diagnostic (kind, location, OPT_Warray_compare, msg))
     {
       /* C doesn't allow +arr.  */
       if (c_dialect_cxx ())
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index adc71132721..9cbee5e7c2a 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -5830,7 +5830,7 @@  cp_build_binary_op (const op_location_t &location,
 	warning_at (location, OPT_Wfloat_equal,
 		    "comparing floating-point with %<==%> "
 		    "or %<!=%> is unsafe");
-      if (complain & tf_warning)
+      if (complain & tf_warning_or_error)
 	{
 	  tree stripped_orig_op0 = tree_strip_any_location_wrapper (orig_op0);
 	  tree stripped_orig_op1 = tree_strip_any_location_wrapper (orig_op1);
@@ -5841,8 +5841,7 @@  cp_build_binary_op (const op_location_t &location,
 	    warning_at (location, OPT_Waddress,
 			"comparison with string literal results in "
 			"unspecified behavior");
-	  else if (warn_array_compare
-		   && TREE_CODE (TREE_TYPE (orig_op0)) == ARRAY_TYPE
+	  else if (TREE_CODE (TREE_TYPE (orig_op0)) == ARRAY_TYPE
 		   && TREE_CODE (TREE_TYPE (orig_op1)) == ARRAY_TYPE)
 	    do_warn_array_compare (location, code, stripped_orig_op0,
 				   stripped_orig_op1);
@@ -6125,11 +6124,10 @@  cp_build_binary_op (const op_location_t &location,
 			"comparison with string literal results "
 			"in unspecified behavior");
 	}
-      else if (warn_array_compare
-	       && TREE_CODE (TREE_TYPE (orig_op0)) == ARRAY_TYPE
+      else if (TREE_CODE (TREE_TYPE (orig_op0)) == ARRAY_TYPE
 	       && TREE_CODE (TREE_TYPE (orig_op1)) == ARRAY_TYPE
 	       && code != SPACESHIP_EXPR
-	       && (complain & tf_warning))
+	       && (complain & tf_warning_or_error))
 	do_warn_array_compare (location, code,
 			       tree_strip_any_location_wrapper (orig_op0),
 			       tree_strip_any_location_wrapper (orig_op1));
diff --git a/gcc/testsuite/c-c++-common/Warray-compare-1.c b/gcc/testsuite/c-c++-common/Warray-compare-1.c
index 922396c0a1a..90191ecd056 100644
--- a/gcc/testsuite/c-c++-common/Warray-compare-1.c
+++ b/gcc/testsuite/c-c++-common/Warray-compare-1.c
@@ -14,12 +14,18 @@  int arr4[2][2];
 bool
 g ()
 {
-  bool b = arr1 == arr2; /* { dg-warning "comparison between two arrays" } */
-  b &= arr1 != arr2; /* { dg-warning "comparison between two arrays" } */
-  b &= arr1 > arr2; /* { dg-warning "comparison between two arrays" } */
-  b &= arr1 >= arr2; /* { dg-warning "comparison between two arrays" } */
-  b &= arr1 < arr2; /* { dg-warning "comparison between two arrays" } */
-  b &= arr1 <= arr2; /* { dg-warning "comparison between two arrays" } */
+  bool b = arr1 == arr2; /* { dg-warning "comparison between two arrays" "" { target { c || c++23_down } } } */
+/* { dg-error "comparison between two arrays" "" { target c++26 } .-1 } */
+  b &= arr1 != arr2; /* { dg-warning "comparison between two arrays" "" { target { c || c++23_down } } } */
+/* { dg-error "comparison between two arrays" "" { target c++26 } .-1 } */
+  b &= arr1 > arr2; /* { dg-warning "comparison between two arrays" "" { target { c || c++23_down } } } */
+/* { dg-error "comparison between two arrays" "" { target c++26 } .-1 } */
+  b &= arr1 >= arr2; /* { dg-warning "comparison between two arrays" "" { target { c || c++23_down } } } */
+/* { dg-error "comparison between two arrays" "" { target c++26 } .-1 } */
+  b &= arr1 < arr2; /* { dg-warning "comparison between two arrays" "" { target { c || c++23_down } } } */
+/* { dg-error "comparison between two arrays" "" { target c++26 } .-1 } */
+  b &= arr1 <= arr2; /* { dg-warning "comparison between two arrays" "" { target { c || c++23_down } } } */
+/* { dg-error "comparison between two arrays" "" { target c++26 } .-1 } */
 #ifdef __cplusplus
   b &= +arr1 == +arr2;
   b &= +arr1 != +arr2;
@@ -35,7 +41,8 @@  g ()
   b &= &arr1[0] < &arr2[0];
   b &= &arr1[0] <= &arr2[0];
 
-  b &= arr3 == arr4; /* { dg-warning "comparison between two arrays" } */
+  b &= arr3 == arr4; /* { dg-warning "comparison between two arrays" "" { target { c || c++23_down } } } */
+/* { dg-error "comparison between two arrays" "" { target c++26 } .-1 } */
 
 #if defined(__cplusplus) && __cplusplus > 201703L
   auto cmp = arr1 <=> arr2; /* { dg-error "invalid operands" "" { target c++20 } } */
diff --git a/gcc/testsuite/c-c++-common/Warray-compare-3.c b/gcc/testsuite/c-c++-common/Warray-compare-3.c
index 4725aa2b38b..afcc934010e 100644
--- a/gcc/testsuite/c-c++-common/Warray-compare-3.c
+++ b/gcc/testsuite/c-c++-common/Warray-compare-3.c
@@ -7,7 +7,8 @@  int a[32][32], b[32][32];
 int
 foo (int x, int y)
 {
-  return (x ? a : b) == (y ? a : b); /* { dg-warning "comparison between two arrays" } */
-/* { dg-message "use '&\\\(\[^\n\r]*\\\)\\\[0\\\] == &\\\(\[^\n\r]*\\\)\\\[0\\\]' to compare the addresses" "" { target c } .-1 } */
-/* { dg-message "use unary '\\\+' which decays operands to pointers or '&\\\(\[^\n\r]*\\\)\\\[0\\\] == &\\\(\[^\n\r]*\\\)\\\[0\\\]' to compare the addresses" "" { target c++ } .-2 } */
+  return (x ? a : b) == (y ? a : b); /* { dg-warning "comparison between two arrays" "" { target { c || c++23_down } } } */
+/* { dg-error "comparison between two arrays" "" { target c++26 } .-1 } */
+/* { dg-message "use '&\\\(\[^\n\r]*\\\)\\\[0\\\] == &\\\(\[^\n\r]*\\\)\\\[0\\\]' to compare the addresses" "" { target c } .-2 } */
+/* { dg-message "use unary '\\\+' which decays operands to pointers or '&\\\(\[^\n\r]*\\\)\\\[0\\\] == &\\\(\[^\n\r]*\\\)\\\[0\\\]' to compare the addresses" "" { target c++ } .-3 } */
 }
diff --git a/gcc/testsuite/c-c++-common/Warray-compare-4.c b/gcc/testsuite/c-c++-common/Warray-compare-4.c
new file mode 100644
index 00000000000..8cfb4b2ffe6
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Warray-compare-4.c
@@ -0,0 +1,50 @@ 
+/* PR c++/97573 */
+/* { dg-do compile } */
+
+#ifndef __cplusplus
+# define bool _Bool
+#endif
+
+int arr1[5];
+int arr2[5];
+int arr3[2][2];
+int arr4[2][2];
+
+bool
+g ()
+{
+  bool b = arr1 == arr2; /* { dg-warning "comparison between two arrays" "" { target { c++20 && c++23_down } } } */
+/* { dg-error "comparison between two arrays" "" { target c++26 } .-1 } */
+  b &= arr1 != arr2; /* { dg-warning "comparison between two arrays" "" { target { c++20 && c++23_down } } } */
+/* { dg-error "comparison between two arrays" "" { target c++26 } .-1 } */
+  b &= arr1 > arr2; /* { dg-warning "comparison between two arrays" "" { target { c++20 && c++23_down } } } */
+/* { dg-error "comparison between two arrays" "" { target c++26 } .-1 } */
+  b &= arr1 >= arr2; /* { dg-warning "comparison between two arrays" "" { target { c++20 && c++23_down } } } */
+/* { dg-error "comparison between two arrays" "" { target c++26 } .-1 } */
+  b &= arr1 < arr2; /* { dg-warning "comparison between two arrays" "" { target { c++20 && c++23_down } } } */
+/* { dg-error "comparison between two arrays" "" { target c++26 } .-1 } */
+  b &= arr1 <= arr2; /* { dg-warning "comparison between two arrays" "" { target { c++20 && c++23_down } } } */
+/* { dg-error "comparison between two arrays" "" { target c++26 } .-1 } */
+#ifdef __cplusplus
+  b &= +arr1 == +arr2;
+  b &= +arr1 != +arr2;
+  b &= +arr1 > +arr2;
+  b &= +arr1 >= +arr2;
+  b &= +arr1 < +arr2;
+  b &= +arr1 <= +arr2;
+#endif
+  b &= &arr1[0] == &arr2[0];
+  b &= &arr1[0] != &arr2[0];
+  b &= &arr1[0] > &arr2[0];
+  b &= &arr1[0] >= &arr2[0];
+  b &= &arr1[0] < &arr2[0];
+  b &= &arr1[0] <= &arr2[0];
+
+  b &= arr3 == arr4; /* { dg-warning "comparison between two arrays" "" { target { c++20 && c++23_down } } } */
+/* { dg-error "comparison between two arrays" "" { target c++26 } .-1 } */
+
+#if defined(__cplusplus) && __cplusplus > 201703L
+  auto cmp = arr1 <=> arr2; /* { dg-error "invalid operands" "" { target c++20 } } */
+#endif
+  return b;
+}
diff --git a/gcc/testsuite/c-c++-common/Warray-compare-5.c b/gcc/testsuite/c-c++-common/Warray-compare-5.c
new file mode 100644
index 00000000000..eab00d63bd0
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Warray-compare-5.c
@@ -0,0 +1,44 @@ 
+/* PR c++/97573 */
+/* { dg-do compile } */
+/* { dg-options "-Wall -Wno-error=array-compare" } */
+
+#ifndef __cplusplus
+# define bool _Bool
+#endif
+
+int arr1[5];
+int arr2[5];
+int arr3[2][2];
+int arr4[2][2];
+
+bool
+g ()
+{
+  bool b = arr1 == arr2; /* { dg-warning "comparison between two arrays" } */
+  b &= arr1 != arr2; /* { dg-warning "comparison between two arrays" } */
+  b &= arr1 > arr2; /* { dg-warning "comparison between two arrays" } */
+  b &= arr1 >= arr2; /* { dg-warning "comparison between two arrays" } */
+  b &= arr1 < arr2; /* { dg-warning "comparison between two arrays" } */
+  b &= arr1 <= arr2; /* { dg-warning "comparison between two arrays" } */
+#ifdef __cplusplus
+  b &= +arr1 == +arr2;
+  b &= +arr1 != +arr2;
+  b &= +arr1 > +arr2;
+  b &= +arr1 >= +arr2;
+  b &= +arr1 < +arr2;
+  b &= +arr1 <= +arr2;
+#endif
+  b &= &arr1[0] == &arr2[0];
+  b &= &arr1[0] != &arr2[0];
+  b &= &arr1[0] > &arr2[0];
+  b &= &arr1[0] >= &arr2[0];
+  b &= &arr1[0] < &arr2[0];
+  b &= &arr1[0] <= &arr2[0];
+
+  b &= arr3 == arr4; /* { dg-warning "comparison between two arrays" } */
+
+#if defined(__cplusplus) && __cplusplus > 201703L
+  auto cmp = arr1 <=> arr2; /* { dg-error "invalid operands" "" { target c++20 } } */
+#endif
+  return b;
+}
diff --git a/gcc/testsuite/g++.dg/warn/Warray-compare-1.C b/gcc/testsuite/g++.dg/warn/Warray-compare-1.C
new file mode 100644
index 00000000000..32413292f44
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Warray-compare-1.C
@@ -0,0 +1,26 @@ 
+// PR c++/117788
+// { dg-do compile { target c++11 } }
+
+constexpr int arr1[5]{};
+constexpr int arr2[5]{};
+
+template<int I>
+void f1 (int(*)[arr1 == arr2 ? I : I]) = delete;  // { dg-warning "comparison between two arrays" "" { target { c++20 && c++23_down } } }
+// { dg-error "comparison between two arrays" "" { target c++26 } .-1 }
+
+template<int>
+void f1 (...) { }
+
+template<int I>
+void f2 (int(*)[arr1 > arr2 ? I : 1]) = delete; // { dg-warning "comparison between two arrays" "" { target { c++20 && c++23_down } } }
+// { dg-error "comparison between two arrays" "" { target c++26 } .-1 }
+
+template<int>
+void f2 (...) { }
+
+void
+g ()
+{
+  f1<0>(nullptr);
+  f2<0>(nullptr);
+}