On Fri, Dec 06, 2024 at 12:48:35PM -0500, Jason Merrill wrote: > 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.
Hopefully done correctly in the following patch. conditionally_borrowed.cc regressed with this patch as described below, so I adjusted the test. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? Thanks, -- >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. This caused a FAIL in conditionally_borrowed.cc because we end up comparing two array types in equality_comparable_with -> __weakly_eq_cmp_with. That could be fixed in libstc++, perhaps by adding std::decay in the appropriate place. 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. Maybe return an error_mark_node in C++26. <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. libstdc++-v3/ChangeLog: * testsuite/std/ranges/adaptors/conditionally_borrowed.cc: Add a FIXME, adjust. --- gcc/c-family/c-warn.cc | 25 +++++++-- gcc/cp/typeck.cc | 55 +++++++++++-------- 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 +++++++++ .../ranges/adaptors/conditionally_borrowed.cc | 5 +- 8 files changed, 192 insertions(+), 41 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 diff --git a/gcc/c-family/c-warn.cc b/gcc/c-family/c-warn.cc index 140d97a00d8..0b71842e48d 100644 --- a/gcc/c-family/c-warn.cc +++ b/gcc/c-family/c-warn.cc @@ -3820,8 +3820,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) @@ -3834,10 +3835,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..b35eb4d17cc 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -5830,23 +5830,27 @@ 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) - { - tree stripped_orig_op0 = tree_strip_any_location_wrapper (orig_op0); - tree stripped_orig_op1 = tree_strip_any_location_wrapper (orig_op1); - if ((TREE_CODE (stripped_orig_op0) == STRING_CST - && !integer_zerop (cp_fully_fold (op1))) - || (TREE_CODE (stripped_orig_op1) == STRING_CST - && !integer_zerop (cp_fully_fold (op0)))) - 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 - && TREE_CODE (TREE_TYPE (orig_op1)) == ARRAY_TYPE) - do_warn_array_compare (location, code, stripped_orig_op0, - stripped_orig_op1); - } + { + tree stripped_orig_op0 = tree_strip_any_location_wrapper (orig_op0); + tree stripped_orig_op1 = tree_strip_any_location_wrapper (orig_op1); + if ((complain & tf_warning_or_error) + && ((TREE_CODE (stripped_orig_op0) == STRING_CST + && !integer_zerop (cp_fully_fold (op1))) + || (TREE_CODE (stripped_orig_op1) == STRING_CST + && !integer_zerop (cp_fully_fold (op0))))) + warning_at (location, OPT_Waddress, + "comparison with string literal results in " + "unspecified behavior"); + else if (TREE_CODE (TREE_TYPE (orig_op0)) == ARRAY_TYPE + && TREE_CODE (TREE_TYPE (orig_op1)) == ARRAY_TYPE) + { + if (complain & tf_warning_or_error) + do_warn_array_compare (location, code, stripped_orig_op0, + stripped_orig_op1); + else if (cxx_dialect >= cxx26) + return error_mark_node; + } + } build_type = boolean_type_node; if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE @@ -6125,14 +6129,17 @@ 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)) - do_warn_array_compare (location, code, - tree_strip_any_location_wrapper (orig_op0), - tree_strip_any_location_wrapper (orig_op1)); + && code != SPACESHIP_EXPR) + { + if (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)); + else if (cxx_dialect >= cxx26) + return error_mark_node; + } if (gnu_vector_type_p (type0) && gnu_vector_type_p (type1)) { 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); +} diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/conditionally_borrowed.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/conditionally_borrowed.cc index 4a0d1ff001a..4bc0d2da9e0 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/conditionally_borrowed.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/conditionally_borrowed.cc @@ -61,7 +61,10 @@ void test02() { std::pair<int, std::string_view> a[2]{ {1,"two"}, {3,"four"}}; - auto pos = ranges::find(a | views::values, "four"); + // FIXME: We should be able to get rid of the decay via the + here. + // But we'd end up comparing two array types in equality_comparable_with + // -> __weakly_eq_cmp_with which is ill-formed in C++26 due to P2865. + auto pos = ranges::find(a | views::values, +"four"); VERIFY( *pos == "four" ); static_assert( ranges::borrowed_range<decltype(a | views::keys)> ); base-commit: d4525729b747827afa62320696709ca499904860 -- 2.47.1