Message ID | 20201028180153.569279-1-polacek@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: Deprecate arithmetic convs on different enums [PR97573] | expand |
On 10/28/20 2:01 PM, Marek Polacek wrote: > I noticed that C++20 P1120R0 deprecated certain arithmetic conversions > as outlined in [depr.arith.conv.enum], but we don't warn about them. In > particular, "If one operand is of enumeration type and the other operand > is of a different enumeration type or a floating-point type, this > behavior is deprecated." These will likely become ill-formed in C++23, > so we should warn by default in C++20. To this effect, this patch adds > two new warnings (like clang++): -Wdeprecated-enum-enum-conversion and > -Wdeprecated-enum-float-conversion. They are enabled by default in > C++20. In older dialects, to enable these warnings you can now use > -Wenum-conversion which I made available in C++ too. Note that unlike > C, in C++ it is not enabled by -Wextra, because that breaks bootstrap. > > We already warn about comparisons of two different enumeration types via > -Wenum-compare, the rest is handled in this patch: we're performing the > usual arithmetic conversions in these contexts: > - an arithmetic operation, > - a bitwise operation, > - a comparison, > - a conditional operator, > - a compound assign operator. > > Using the spaceship operator as enum <=> real_type is ill-formed but we > don't reject it yet. Hmm, oops. Will you fix that as well? It should be simple to fix in the SPACESHIP_EXPR block that starts just at the end of this patch. > We should also address [depr.array.comp] too, but > it's not handled in this patch. > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? OK, thanks. > gcc/c-family/ChangeLog: > > PR c++/97573 > * c-opts.c (c_common_post_options): In C++20, turn on > -Wdeprecated-enum-enum-conversion and > -Wdeprecated-enum-float-conversion. > * c.opt (Wdeprecated-enum-enum-conversion, > Wdeprecated-enum-float-conversion): New options. > (Wenum-conversion): Allow for C++ too. > > gcc/cp/ChangeLog: > > PR c++/97573 > * call.c (build_conditional_expr_1): Warn about the deprecated > enum/real type conversion in C++20. Also warn about a non-enumerated > and enumerated type in ?: when -Wenum-conversion is on. > * typeck.c (do_warn_enum_conversions): New function. > (cp_build_binary_op): Call it. > > gcc/ChangeLog: > > PR c++/97573 > * doc/invoke.texi: Document -Wdeprecated-enum-enum-conversion > and -Wdeprecated-enum-float-conversion. -Wenum-conversion is > no longer C/ObjC only. > > gcc/testsuite/ChangeLog: > > PR c++/97573 > * g++.dg/cpp0x/linkage2.C: Add dg-warning. > * g++.dg/parse/attr3.C: Likewise. > * g++.dg/cpp2a/enum-conv1.C: New test. > * g++.dg/cpp2a/enum-conv2.C: New test. > * g++.dg/cpp2a/enum-conv3.C: New test. > --- > gcc/c-family/c-opts.c | 10 ++ > gcc/c-family/c.opt | 11 ++- > gcc/cp/call.c | 35 +++++-- > gcc/cp/typeck.c | 112 +++++++++++++++++++++- > gcc/doc/invoke.texi | 44 ++++++++- > gcc/testsuite/g++.dg/cpp0x/linkage2.C | 2 +- > gcc/testsuite/g++.dg/cpp2a/enum-conv1.C | 120 ++++++++++++++++++++++++ > gcc/testsuite/g++.dg/cpp2a/enum-conv2.C | 115 +++++++++++++++++++++++ > gcc/testsuite/g++.dg/cpp2a/enum-conv3.C | 115 +++++++++++++++++++++++ > gcc/testsuite/g++.dg/parse/attr3.C | 2 +- > 10 files changed, 549 insertions(+), 17 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp2a/enum-conv1.C > create mode 100644 gcc/testsuite/g++.dg/cpp2a/enum-conv2.C > create mode 100644 gcc/testsuite/g++.dg/cpp2a/enum-conv3.C > > diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c > index 38d33849423..120f4489f6c 100644 > --- a/gcc/c-family/c-opts.c > +++ b/gcc/c-family/c-opts.c > @@ -925,6 +925,16 @@ c_common_post_options (const char **pfilename) > SET_OPTION_IF_UNSET (&global_options, &global_options_set, warn_volatile, > cxx_dialect >= cxx20 && warn_deprecated); > > + /* -Wdeprecated-enum-enum-conversion is enabled by default in C++20. */ > + SET_OPTION_IF_UNSET (&global_options, &global_options_set, > + warn_deprecated_enum_enum_conv, > + cxx_dialect >= cxx20 && warn_deprecated); > + > + /* -Wdeprecated-enum-float-conversion is enabled by default in C++20. */ > + SET_OPTION_IF_UNSET (&global_options, &global_options_set, > + warn_deprecated_enum_float_conv, > + cxx_dialect >= cxx20 && warn_deprecated); > + > /* Declone C++ 'structors if -Os. */ > if (flag_declone_ctor_dtor == -1) > flag_declone_ctor_dtor = optimize_size; > diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt > index 1009defbf16..10e53ea67c9 100644 > --- a/gcc/c-family/c.opt > +++ b/gcc/c-family/c.opt > @@ -518,6 +518,15 @@ C++ ObjC++ Var(warn_deprecated_copy, 2) Warning > Mark implicitly-declared copy operations as deprecated if the class has a > user-provided copy operation or destructor. > > +Wdeprecated-enum-enum-conversion > +C++ ObjC++ Var(warn_deprecated_enum_enum_conv) Warning > +Warn about deprecated arithmetic conversions on operands of enumeration types. > + > +Wdeprecated-enum-float-conversion > +C++ ObjC++ Var(warn_deprecated_enum_float_conv) Warning > +Warn about deprecated arithmetic conversions on operands where one is of enumeration > +type and the other is of a floating-point type. > + > Wdesignated-init > C ObjC Var(warn_designated_init) Init(1) Warning > Warn about positional initialization of structs requiring designated initializers. > @@ -559,7 +568,7 @@ C ObjC C++ ObjC++ Var(warn_enum_compare) Init(-1) Warning LangEnabledBy(C ObjC,W > Warn about comparison of different enum types. > > Wenum-conversion > -C ObjC Var(warn_enum_conversion) Init(0) Warning LangEnabledBy(C ObjC,Wextra) > +C ObjC C++ ObjC++ Var(warn_enum_conversion) Init(0) Warning LangEnabledBy(C ObjC,Wextra) > Warn about implicit conversion of enum types. > > Werror > diff --git a/gcc/cp/call.c b/gcc/cp/call.c > index bd662518958..9861be1f856 100644 > --- a/gcc/cp/call.c > +++ b/gcc/cp/call.c > @@ -5643,17 +5643,40 @@ build_conditional_expr_1 (const op_location_t &loc, > "in conditional expression: %qT vs %qT", > arg2_type, arg3_type); > } > - else if (extra_warnings > + else if ((complain & tf_warning) > + && warn_deprecated_enum_float_conv > + && ((TREE_CODE (arg2_type) == ENUMERAL_TYPE > + && TREE_CODE (arg3_type) == REAL_TYPE) > + || (TREE_CODE (arg2_type) == REAL_TYPE > + && TREE_CODE (arg3_type) == ENUMERAL_TYPE))) > + { > + if (TREE_CODE (arg2_type) == ENUMERAL_TYPE) > + warning_at (loc, OPT_Wdeprecated_enum_float_conversion, > + "conditional expression between enumeration type " > + "%qT and floating-point type %qT is deprecated", > + arg2_type, arg3_type); > + else > + warning_at (loc, OPT_Wdeprecated_enum_float_conversion, > + "conditional expression between floating-point " > + "type %qT and enumeration type %qT is deprecated", > + arg2_type, arg3_type); > + } > + else if ((extra_warnings || warn_enum_conversion) > && ((TREE_CODE (arg2_type) == ENUMERAL_TYPE > && !same_type_p (arg3_type, type_promotes_to (arg2_type))) > || (TREE_CODE (arg3_type) == ENUMERAL_TYPE > && !same_type_p (arg2_type, > type_promotes_to (arg3_type))))) > - { > - if (complain & tf_warning) > - warning_at (loc, OPT_Wextra, "enumerated and non-enumerated " > - "type in conditional expression"); > - } > + { > + if (complain & tf_warning) > + { > + enum opt_code opt = (warn_enum_conversion > + ? OPT_Wenum_conversion > + : OPT_Wextra); > + warning_at (loc, opt, "enumerated and " > + "non-enumerated type in conditional expression"); > + } > + } > > arg2 = perform_implicit_conversion (result_type, arg2, complain); > arg3 = perform_implicit_conversion (result_type, arg3, complain); > diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c > index 48d34f1132a..7305310ecbe 100644 > --- a/gcc/cp/typeck.c > +++ b/gcc/cp/typeck.c > @@ -4428,6 +4428,104 @@ warn_for_null_address (location_t location, tree op, tsubst_flags_t complain) > } > } > > +/* Warn about [expr.arith.conv]/2: If one operand is of enumeration type and > + the other operand is of a different enumeration type or a floating-point > + type, this behavior is deprecated ([depr.arith.conv.enum]). CODE is the > + code of the binary operation, TYPE0 and TYPE1 are the types of the operands, > + and LOC is the location for the whole binary expression. > + TODO: Consider combining this with -Wenum-compare in build_new_op_1. */ > + > +static void > +do_warn_enum_conversions (location_t loc, enum tree_code code, tree type0, > + tree type1) > +{ > + if (TREE_CODE (type0) == ENUMERAL_TYPE > + && TREE_CODE (type1) == ENUMERAL_TYPE > + && TYPE_MAIN_VARIANT (type0) != TYPE_MAIN_VARIANT (type1)) > + { > + /* In C++20, -Wdeprecated-enum-enum-conversion is on by default. > + Otherwise, warn if -Wenum-conversion is on. */ > + enum opt_code opt; > + if (warn_deprecated_enum_enum_conv) > + opt = OPT_Wdeprecated_enum_enum_conversion; > + else if (warn_enum_conversion) > + opt = OPT_Wenum_conversion; > + else > + return; > + > + switch (code) > + { > + case GT_EXPR: > + case LT_EXPR: > + case GE_EXPR: > + case LE_EXPR: > + case EQ_EXPR: > + case NE_EXPR: > + /* Comparisons are handled by -Wenum-compare. */ > + return; > + case SPACESHIP_EXPR: > + /* This is invalid, don't warn. */ > + return; > + case BIT_AND_EXPR: > + case BIT_IOR_EXPR: > + case BIT_XOR_EXPR: > + warning_at (loc, opt, "bitwise operation between different " > + "enumeration types %qT and %qT is deprecated", > + type0, type1); > + return; > + default: > + warning_at (loc, opt, "arithmetic between different enumeration " > + "types %qT and %qT is deprecated", type0, type1); > + return; > + } > + } > + else if ((TREE_CODE (type0) == ENUMERAL_TYPE > + && TREE_CODE (type1) == REAL_TYPE) > + || (TREE_CODE (type0) == REAL_TYPE > + && TREE_CODE (type1) == ENUMERAL_TYPE)) > + { > + const bool enum_first_p = TREE_CODE (type0) == ENUMERAL_TYPE; > + /* In C++20, -Wdeprecated-enum-float-conversion is on by default. > + Otherwise, warn if -Wenum-conversion is on. */ > + enum opt_code opt; > + if (warn_deprecated_enum_float_conv) > + opt = OPT_Wdeprecated_enum_float_conversion; > + else if (warn_enum_conversion) > + opt = OPT_Wenum_conversion; > + else > + return; > + > + switch (code) > + { > + case GT_EXPR: > + case LT_EXPR: > + case GE_EXPR: > + case LE_EXPR: > + case EQ_EXPR: > + case NE_EXPR: > + if (enum_first_p) > + warning_at (loc, opt, "comparison of enumeration type %qT with " > + "floating-point type %qT is deprecated", > + type0, type1); > + else > + warning_at (loc, opt, "comparison of floating-point type %qT " > + "with enumeration type %qT is deprecated", > + type0, type1); > + return; > + default: > + if (enum_first_p) > + warning_at (loc, opt, "arithmetic between enumeration type %qT " > + "and floating-point type %qT is deprecated", > + type0, type1); > + else > + warning_at (loc, opt, "arithmetic between floating-point type %qT " > + "and enumeration type %qT is deprecated", > + type0, type1); > + return; > + } > + } > +} > + > /* Build a binary-operation expression without default conversions. > CODE is the kind of expression to build. > LOCATION is the location_t of the operator in the source code. > @@ -5445,11 +5543,15 @@ cp_build_binary_op (const op_location_t &location, > { > result_type = cp_common_type (type0, type1); > if (complain & tf_warning) > - do_warn_double_promotion (result_type, type0, type1, > - "implicit conversion from %qH to %qI " > - "to match other operand of binary " > - "expression", > - location); > + { > + do_warn_double_promotion (result_type, type0, type1, > + "implicit conversion from %qH to %qI " > + "to match other operand of binary " > + "expression", > + location); > + do_warn_enum_conversions (location, code, TREE_TYPE (orig_op0), > + TREE_TYPE (orig_op1)); > + } > } > > if (code == SPACESHIP_EXPR) > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi > index f82eeea097a..72ae4a23203 100644 > --- a/gcc/doc/invoke.texi > +++ b/gcc/doc/invoke.texi > @@ -239,6 +239,7 @@ in the following sections. > -Wno-conversion-null -Wctad-maybe-unsupported @gol > -Wctor-dtor-privacy -Wno-delete-incomplete @gol > -Wdelete-non-virtual-dtor -Wdeprecated-copy -Wdeprecated-copy-dtor @gol > +-Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion @gol > -Weffc++ -Wextra-semi -Wno-inaccessible-base @gol > -Wno-inherited-variadic-ctor -Wno-init-list-lifetime @gol > -Wno-invalid-offsetof -Wno-literal-suffix -Wmismatched-tags @gol > @@ -3358,6 +3359,42 @@ warning is enabled by @option{-Wextra}. With > @option{-Wdeprecated-copy-dtor}, also deprecate if the class has a > user-provided destructor. > > +@item -Wno-deprecated-enum-enum-conversion @r{(C++ and Objective-C++ only)} > +@opindex Wdeprecated-enum-enum-conversion > +@opindex Wno-deprecated-enum-enum-conversion > +Disable the warning about the case when the usual arithmetic conversions > +are applied on operands where one is of enumeration type and the other is > +of a different enumeration type. This conversion was deprecated in C++20. > +For example: > + > +@smallexample > +enum E1 @{ e @}; > +enum E2 @{ f @}; > +int k = f - e; > +@end smallexample > + > +@option{-Wdeprecated-enum-enum-conversion} is enabled by default with > +@option{-std=c++20}. In pre-C++20 dialects, this warning can be enabled > +by @option{-Wenum-conversion}. > + > +@item -Wno-deprecated-enum-float-conversion @r{(C++ and Objective-C++ only)} > +@opindex Wdeprecated-enum-float-conversion > +@opindex Wno-deprecated-enum-float-conversion > +Disable the warning about the case when the usual arithmetic conversions > +are applied on operands where one is of enumeration type and the other is > +of a floating-point type. This conversion was deprecated in C++20. For > +example: > + > +@smallexample > +enum E1 @{ e @}; > +enum E2 @{ f @}; > +bool b = e <= 3.7; > +@end smallexample > + > +@option{-Wdeprecated-enum-float-conversion} is enabled by default with > +@option{-std=c++20}. In pre-C++20 dialects, this warning can be enabled > +by @option{-Wenum-conversion}. > + > @item -Wno-init-list-lifetime @r{(C++ and Objective-C++ only)} > @opindex Winit-list-lifetime > @opindex Wno-init-list-lifetime > @@ -5271,7 +5308,6 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}. > -Wcomment @gol > -Wduplicate-decl-specifier @r{(C and Objective-C only)} @gol > -Wenum-compare @r{(in C/ObjC; this is on by default in C++)} @gol > --Wenum-conversion @r{in C/ObjC;} @gol > -Wformat @gol > -Wformat-overflow @gol > -Wformat-truncation @gol > @@ -5340,6 +5376,7 @@ name is still supported, but the newer name is more descriptive.) > -Wcast-function-type @gol > -Wdeprecated-copy @r{(C++ only)} @gol > -Wempty-body @gol > +-Wenum-conversion @r{(C only)} @gol > -Wignored-qualifiers @gol > -Wimplicit-fallthrough=3 @gol > -Wmissing-field-initializers @gol > @@ -8006,11 +8043,12 @@ In C++ enumerated type mismatches in conditional expressions are also > diagnosed and the warning is enabled by default. In C this warning is > enabled by @option{-Wall}. > > -@item -Wenum-conversion @r{(C, Objective-C only)} > +@item -Wenum-conversion > @opindex Wenum-conversion > @opindex Wno-enum-conversion > Warn when a value of enumerated type is implicitly converted to a > -different enumerated type. This warning is enabled by @option{-Wextra}. > +different enumerated type. This warning is enabled by @option{-Wextra} > +in C@. > > @item -Wjump-misses-init @r{(C, Objective-C only)} > @opindex Wjump-misses-init > diff --git a/gcc/testsuite/g++.dg/cpp0x/linkage2.C b/gcc/testsuite/g++.dg/cpp0x/linkage2.C > index 52858687ed3..549bd825aab 100644 > --- a/gcc/testsuite/g++.dg/cpp0x/linkage2.C > +++ b/gcc/testsuite/g++.dg/cpp0x/linkage2.C > @@ -29,5 +29,5 @@ void f() { > ba.g(a); // OK > ba.h(a); // error, B<T>::h never defined > i(ba, a); // OK > - e1+e2+e3; > + e1+e2+e3; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } } > } > diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C > new file mode 100644 > index 00000000000..d4960f334dd > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C > @@ -0,0 +1,120 @@ > +// PR c++/97573 > +// { dg-do compile } > +// No special options. In C++20 (only), we should get the deprecated warnings > +// by default. -Wenum-compare is enabled by default so some of them will be > +// printed even pre-C++20. > + > +enum E1 { e } e1; > +enum E2 { f } e2; > +__extension__ static enum { } u1; > +__extension__ static enum { } u2; > +static double d; > + > +void > +conv () > +{ > + bool b1 = e == e1; > + bool b2 = e == f; // { dg-warning "comparison between .enum E1. and .enum E2." } > + bool b3 = e == 0.0; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." "" { target c++20 } } > + bool b4 = 0.0 == f; // { dg-warning "comparison of floating-point type .double. with enumeration type .E2." "" { target c++20 } } > + int n1 = true ? e : f; // { dg-warning "enumerated mismatch" } > + int n2 = true ? e : 0.0; // { dg-warning "conditional expression between" "" { target c++20 } } > +} > + > +int > +enum_enum (bool b) > +{ > + int r = 0; > + const E1 e1c = e; > + > + r += e - e; > + r += e - e1; > + r += e - f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } } > + r += f - e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." "" { target c++20 } } > + > + r += f + f; > + r += f + e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." "" { target c++20 } } > + r += e + f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } } > + > + r += e1 - e2; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } } > + r += e1 - e1c; > + r += e1c - e1; > + > + r += e * f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } } > + r += f * e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." "" { target c++20 } } > + r += e * e; > + > + r += e1 < e1c; > + r += e < e1; > + r += e1 < e2; // { dg-warning "comparison between .enum E1. and .enum E2." } > + r += e < f; // { dg-warning "comparison between .enum E1. and .enum E2." } > + r += f < e; // { dg-warning "comparison between .enum E2. and .enum E1." } > + > + r += e1 == e1c; > + r += e == e1; > + r += e == f; // { dg-warning "comparison between .enum E1. and .enum E2." } > + r += f == e; // { dg-warning "comparison between .enum E2. and .enum E1." } > + r += e1 == e2; // { dg-warning "comparison between .enum E1. and .enum E2." } > + r += e2 == e1; // { dg-warning "comparison between .enum E2. and .enum E1." } > + > + r += b ? e1 : e1c; > + r += b ? e1 : e; > + r += b ? f : e; // { dg-warning "enumerated mismatch in conditional expression: .E2. vs .E1." } > + r += b ? e1 : e2; // { dg-warning "enumerated mismatch in conditional expression: .E1. vs .E2." } > + > + r += e | f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." "" { target c++20 } } > + r += e ^ f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." "" { target c++20 } } > + r += e & f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." "" { target c++20 } } > + r += !e; > + r += e1 | e; > + > + r += e << f; > + r += e >> f; > + r += e || f; > + r += e && f; > + e1 = e1c; > + > + // Anonymous enum. > + r += u1 - u1; > + r += u1 + u2; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } } > + r += u1 * u2; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } } > + r += u1 == u2; // { dg-warning "comparison between" } > + r += u1 & u2; // { dg-warning "bitwise operation between different enumeration types" "" { target c++20 } } > + > + return r; > +} > + > +double > +enum_float (bool b) > +{ > + double r = 0.0; > + > + r += e1 - d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." "" { target c++20 } } > + r += d - e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } } > + r += e1 + d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." "" { target c++20 } } > + r += d + e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } } > + r += e1 * d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." "" { target c++20 } } > + r += d * e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } } > + r += u1 * d; // { dg-warning "arithmetic between enumeration type" "" { target c++20 } } > + r += d * u1; // { dg-warning "arithmetic between floating-point type" "" { target c++20 } } > + > + r += e1 < d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." "" { target c++20 } } > + r += d < e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." "" { target c++20 } } > + r += d == e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." "" { target c++20 } } > + r += e1 == d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." "" { target c++20 } } > + r += u1 == d; // { dg-warning "comparison of enumeration type" "" { target c++20 } } > + r += d == u1; // { dg-warning "comparison of floating-point type" "" { target c++20 } } > + > + r += b ? e1 : d; // { dg-warning "conditional expression between enumeration type .E1. and floating-point type .double." "" { target c++20 } } > + r += b ? d : e1; // { dg-warning "conditional expression between floating-point type .double. and enumeration type .E1." "" { target c++20 } } > + r += b ? d : u1; // { dg-warning "conditional expression between" "" { target c++20 } } > + r += b ? u1 : d; // { dg-warning "conditional expression between" "" { target c++20 } } > + > + // FIXME should be error > + // e1 <=> d; > + > + d += e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } } > + d = e1; > + > + return r; > +} > diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv2.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv2.C > new file mode 100644 > index 00000000000..f15827bda14 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv2.C > @@ -0,0 +1,115 @@ > +// PR c++/97573 > +// { dg-do compile { target c++20 } } > +// { dg-options "-Wno-deprecated -Wno-enum-compare" } > + > +enum E1 { e } e1; > +enum E2 { f } e2; > +__extension__ static enum { } u1; > +__extension__ static enum { } u2; > +static double d; > + > +void > +conv () > +{ > + bool b1 = e == e1; > + bool b2 = e == f; > + bool b3 = e == 0.0; > + bool b4 = 0.0 == f; > + int n1 = true ? e : f; > + int n2 = true ? e : 0.0; > +} > + > +int > +enum_enum (bool b) > +{ > + int r = 0; > + const E1 e1c = e; > + > + r += e - e; > + r += e - e1; > + r += e - f; > + r += f - e; > + > + r += f + f; > + r += f + e; > + r += e + f; > + > + r += e1 - e2; > + r += e1 - e1c; > + r += e1c - e1; > + > + r += e * f; > + r += f * e; > + r += e * e; > + > + r += e1 < e1c; > + r += e < e1; > + r += e1 < e2; > + r += e < f; > + r += f < e; > + > + r += e1 == e1c; > + r += e == e1; > + r += e == f; > + r += f == e; > + r += e1 == e2; > + r += e2 == e1; > + > + r += b ? e1 : e1c; > + r += b ? e1 : e; > + r += b ? f : e; > + r += b ? e1 : e2; > + > + r += e | f; > + r += e ^ f; > + r += e & f; > + r += !e; > + r += e1 | e; > + > + r += e << f; > + r += e >> f; > + r += e || f; > + r += e && f; > + e1 = e1c; > + > + // Anonymous enum. > + r += u1 - u1; > + r += u1 + u2; > + r += u1 * u2; > + r += u1 == u2; > + r += u1 & u2; > + > + return r; > +} > + > +double > +enum_float (bool b) > +{ > + double r = 0.0; > + > + r += e1 - d; > + r += d - e1; > + r += e1 + d; > + r += d + e1; > + r += e1 * d; > + r += d * e1; > + r += u1 * d; > + r += d * u1; > + > + r += e1 < d; > + r += d < e1; > + r += d == e1; > + r += e1 == d; > + r += u1 == d; > + r += d == u1; > + > + r += b ? e1 : d; > + r += b ? d : e1; > + r += b ? d : u1; > + r += b ? u1 : d; > + > + d += e1; > + d = e1; > + > + return r; > +} > diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv3.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv3.C > new file mode 100644 > index 00000000000..67bdf1600d7 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv3.C > @@ -0,0 +1,115 @@ > +// PR c++/97573 > +// { dg-do compile { target { c++17_down } } } > +// { dg-options "-Wenum-conversion" } > + > +enum E1 { e } e1; > +enum E2 { f } e2; > +__extension__ static enum { } u1; > +__extension__ static enum { } u2; > +static double d; > + > +void > +conv () > +{ > + bool b1 = e == e1; > + bool b2 = e == f; // { dg-warning "comparison between .enum E1. and .enum E2." } > + bool b3 = e == 0.0; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." } > + bool b4 = 0.0 == f; // { dg-warning "comparison of floating-point type .double. with enumeration type .E2." } > + int n1 = true ? e : f; // { dg-warning "enumerated mismatch" } > + int n2 = true ? e : 0.0; // { dg-warning "enumerated and non-enumerated type in conditional expression" } > +} > + > +int > +enum_enum (bool b) > +{ > + int r = 0; > + const E1 e1c = e; > + > + r += e - e; > + r += e - e1; > + r += e - f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." } > + r += f - e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." } > + > + r += f + f; > + r += f + e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." } > + r += e + f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." } > + > + r += e1 - e2; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." } > + r += e1 - e1c; > + r += e1c - e1; > + > + r += e * f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." } > + r += f * e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." } > + r += e * e; > + > + r += e1 < e1c; > + r += e < e1; > + r += e1 < e2; // { dg-warning "comparison between .enum E1. and .enum E2." } > + r += e < f; // { dg-warning "comparison between .enum E1. and .enum E2." } > + r += f < e; // { dg-warning "comparison between .enum E2. and .enum E1." } > + > + r += e1 == e1c; > + r += e == e1; > + r += e == f; // { dg-warning "comparison between .enum E1. and .enum E2." } > + r += f == e; // { dg-warning "comparison between .enum E2. and .enum E1." } > + r += e1 == e2; // { dg-warning "comparison between .enum E1. and .enum E2." } > + r += e2 == e1; // { dg-warning "comparison between .enum E2. and .enum E1." } > + > + r += b ? e1 : e1c; > + r += b ? e1 : e; > + r += b ? f : e; // { dg-warning "enumerated mismatch in conditional expression: .E2. vs .E1." } > + r += b ? e1 : e2; // { dg-warning "enumerated mismatch in conditional expression: .E1. vs .E2." } > + > + r += e | f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." } > + r += e ^ f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." } > + r += e & f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." } > + r += !e; > + r += e1 | e; > + > + r += e << f; > + r += e >> f; > + r += e || f; > + r += e && f; > + e1 = e1c; > + > + // Anonymous enum. > + r += u1 - u1; > + r += u1 + u2; // { dg-warning "arithmetic between different enumeration types" } > + r += u1 * u2; // { dg-warning "arithmetic between different enumeration types" } > + r += u1 == u2; // { dg-warning "comparison between" } > + r += u1 & u2; // { dg-warning "bitwise operation between different enumeration types" } > + > + return r; > +} > + > +double > +enum_float (bool b) > +{ > + double r = 0.0; > + > + r += e1 - d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." } > + r += d - e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." } > + r += e1 + d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." } > + r += d + e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." } > + r += e1 * d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." } > + r += d * e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." } > + r += u1 * d; // { dg-warning "arithmetic between enumeration type" } > + r += d * u1; // { dg-warning "arithmetic between floating-point type" } > + > + r += e1 < d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." } > + r += d < e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." } > + r += d == e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." } > + r += e1 == d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." } > + r += u1 == d; // { dg-warning "comparison of enumeration type" } > + r += d == u1; // { dg-warning "comparison of floating-point type" } > + > + r += b ? e1 : d; // { dg-warning "enumerated and non-enumerated type in conditional expression" } > + r += b ? d : e1; // { dg-warning "enumerated and non-enumerated type in conditional expression" } > + r += b ? d : u1; // { dg-warning "enumerated and non-enumerated type in conditional expression" } > + r += b ? u1 : d; // { dg-warning "enumerated and non-enumerated type in conditional expression" } > + > + d += e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." } > + d = e1; > + > + return r; > +} > diff --git a/gcc/testsuite/g++.dg/parse/attr3.C b/gcc/testsuite/g++.dg/parse/attr3.C > index 57fa60e130e..de095988015 100644 > --- a/gcc/testsuite/g++.dg/parse/attr3.C > +++ b/gcc/testsuite/g++.dg/parse/attr3.C > @@ -10,5 +10,5 @@ int main () { > S::F y; // { dg-warning "'F' is deprecated" } > y = S::f; > > - return x + y; > + return x + y; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } } > } > > base-commit: 75ce04fba49eb30b6a8fe23bc3605cf0ef9a8e28 >
On Wed, Oct 28, 2020 at 02:43:30PM -0400, Jason Merrill wrote: > On 10/28/20 2:01 PM, Marek Polacek wrote: > > I noticed that C++20 P1120R0 deprecated certain arithmetic conversions > > as outlined in [depr.arith.conv.enum], but we don't warn about them. In > > particular, "If one operand is of enumeration type and the other operand > > is of a different enumeration type or a floating-point type, this > > behavior is deprecated." These will likely become ill-formed in C++23, > > so we should warn by default in C++20. To this effect, this patch adds > > two new warnings (like clang++): -Wdeprecated-enum-enum-conversion and > > -Wdeprecated-enum-float-conversion. They are enabled by default in > > C++20. In older dialects, to enable these warnings you can now use > > -Wenum-conversion which I made available in C++ too. Note that unlike > > C, in C++ it is not enabled by -Wextra, because that breaks bootstrap. > > > > We already warn about comparisons of two different enumeration types via > > -Wenum-compare, the rest is handled in this patch: we're performing the > > usual arithmetic conversions in these contexts: > > - an arithmetic operation, > > - a bitwise operation, > > - a comparison, > > - a conditional operator, > > - a compound assign operator. > > > > Using the spaceship operator as enum <=> real_type is ill-formed but we > > don't reject it yet. > > Hmm, oops. Will you fix that as well? It should be simple to fix in the > SPACESHIP_EXPR block that starts just at the end of this patch. Sure. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? From 8ae2e45f2dd35510aed3be1ab249b8612e33f00d Mon Sep 17 00:00:00 2001 From: Marek Polacek <polacek@redhat.com> Date: Wed, 28 Oct 2020 19:02:29 -0400 Subject: [PATCH] c++: Reject float <=> enum. As [depr.arith.conv.enum] says, these are ill-formed. gcc/cp/ChangeLog: * typeck.c (do_warn_enum_conversions): Don't warn for SPACESHIP_EXPR. (cp_build_binary_op): Reject float <=> enum or enum <=> float. Use CP_INTEGRAL_TYPE_P instead of INTEGRAL_OR_ENUMERATION_TYPE_P. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/enum-conv1.C: Remove unused code. * g++.dg/cpp2a/spaceship-err5.C: New test. --- gcc/cp/typeck.c | 13 ++++++++++-- gcc/testsuite/g++.dg/cpp2a/enum-conv1.C | 3 --- gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C | 23 +++++++++++++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 7305310ecbe..d3b701610cf 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -4512,6 +4512,9 @@ do_warn_enum_conversions (location_t loc, enum tree_code code, tree type0, "with enumeration type %qT is deprecated", type0, type1); return; + case SPACESHIP_EXPR: + /* This is invalid, don't warn. */ + return; default: if (enum_first_p) warning_at (loc, opt, "arithmetic between enumeration type %qT " @@ -5584,6 +5587,12 @@ cp_build_binary_op (const op_location_t &location, arithmetic conversions are applied to the operands." So we don't do arithmetic conversions if the operands both have enumeral type. */ result_type = NULL_TREE; + else if ((orig_code0 == ENUMERAL_TYPE && orig_code1 == REAL_TYPE) + || (orig_code0 == REAL_TYPE && orig_code1 == ENUMERAL_TYPE)) + /* [depr.arith.conv.enum]: Three-way comparisons between such operands + [where one is of enumeration type and the other is of a different + enumeration type or a floating-point type] are ill-formed. */ + result_type = NULL_TREE; if (result_type) { @@ -5598,12 +5607,12 @@ cp_build_binary_op (const op_location_t &location, type to a floating point type, the program is ill-formed. */ bool ok = true; if (TREE_CODE (result_type) == REAL_TYPE - && INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (orig_op0))) + && CP_INTEGRAL_TYPE_P (orig_type0)) /* OK */; else if (!check_narrowing (result_type, orig_op0, complain)) ok = false; if (TREE_CODE (result_type) == REAL_TYPE - && INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (orig_op1))) + && CP_INTEGRAL_TYPE_P (orig_type1)) /* OK */; else if (!check_narrowing (result_type, orig_op1, complain)) ok = false; diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C index d4960f334dd..4571b5e8968 100644 --- a/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C +++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C @@ -110,9 +110,6 @@ enum_float (bool b) r += b ? d : u1; // { dg-warning "conditional expression between" "" { target c++20 } } r += b ? u1 : d; // { dg-warning "conditional expression between" "" { target c++20 } } - // FIXME should be error - // e1 <=> d; - d += e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } } d = e1; diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C new file mode 100644 index 00000000000..3dc2a0f2365 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C @@ -0,0 +1,23 @@ +// { dg-do compile { target c++20 } } +// Test [depr.arith.conv.enum] for <=>. + +#include <compare> + +enum E1 { e } e1; +enum E2 { f } e2; +static double d; + +void +g () +{ + void(e1 <=> e); + e1 <=> d; // { dg-error "invalid operands of types .E1. and .double." } + d <=> e1; // { dg-error "invalid operands of types .double. and .E1." } + e <=> d; // { dg-error "invalid operands of types .E1. and .double." } + d <=> e; // { dg-error "invalid operands of types .double. and .E1." } + + e <=> f; // { dg-error "invalid operands of types .E1. and .E2." } + f <=> e; // { dg-error "invalid operands of types .E2. and .E1." } + e1 <=> e2; // { dg-error "invalid operands of types .E1. and .E2." } + e2 <=> e1; // { dg-error "invalid operands of types .E2. and .E1." } +} base-commit: 4166ebedf8b8a302b86132fdf846fac204c83368
On 10/28/20 10:46 PM, Marek Polacek wrote: > On Wed, Oct 28, 2020 at 02:43:30PM -0400, Jason Merrill wrote: >> On 10/28/20 2:01 PM, Marek Polacek wrote: >>> I noticed that C++20 P1120R0 deprecated certain arithmetic conversions >>> as outlined in [depr.arith.conv.enum], but we don't warn about them. In >>> particular, "If one operand is of enumeration type and the other operand >>> is of a different enumeration type or a floating-point type, this >>> behavior is deprecated." These will likely become ill-formed in C++23, >>> so we should warn by default in C++20. To this effect, this patch adds >>> two new warnings (like clang++): -Wdeprecated-enum-enum-conversion and >>> -Wdeprecated-enum-float-conversion. They are enabled by default in >>> C++20. In older dialects, to enable these warnings you can now use >>> -Wenum-conversion which I made available in C++ too. Note that unlike >>> C, in C++ it is not enabled by -Wextra, because that breaks bootstrap. >>> >>> We already warn about comparisons of two different enumeration types via >>> -Wenum-compare, the rest is handled in this patch: we're performing the >>> usual arithmetic conversions in these contexts: >>> - an arithmetic operation, >>> - a bitwise operation, >>> - a comparison, >>> - a conditional operator, >>> - a compound assign operator. >>> >>> Using the spaceship operator as enum <=> real_type is ill-formed but we >>> don't reject it yet. >> >> Hmm, oops. Will you fix that as well? It should be simple to fix in the >> SPACESHIP_EXPR block that starts just at the end of this patch. > > Sure. > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? OK, thanks. > From 8ae2e45f2dd35510aed3be1ab249b8612e33f00d Mon Sep 17 00:00:00 2001 > From: Marek Polacek <polacek@redhat.com> > Date: Wed, 28 Oct 2020 19:02:29 -0400 > Subject: [PATCH] c++: Reject float <=> enum. > > As [depr.arith.conv.enum] says, these are ill-formed. > > gcc/cp/ChangeLog: > > * typeck.c (do_warn_enum_conversions): Don't warn for SPACESHIP_EXPR. > (cp_build_binary_op): Reject float <=> enum or enum <=> float. Use > CP_INTEGRAL_TYPE_P instead of INTEGRAL_OR_ENUMERATION_TYPE_P. > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp2a/enum-conv1.C: Remove unused code. > * g++.dg/cpp2a/spaceship-err5.C: New test. > --- > gcc/cp/typeck.c | 13 ++++++++++-- > gcc/testsuite/g++.dg/cpp2a/enum-conv1.C | 3 --- > gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C | 23 +++++++++++++++++++++ > 3 files changed, 34 insertions(+), 5 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C > > diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c > index 7305310ecbe..d3b701610cf 100644 > --- a/gcc/cp/typeck.c > +++ b/gcc/cp/typeck.c > @@ -4512,6 +4512,9 @@ do_warn_enum_conversions (location_t loc, enum tree_code code, tree type0, > "with enumeration type %qT is deprecated", > type0, type1); > return; > + case SPACESHIP_EXPR: > + /* This is invalid, don't warn. */ > + return; > default: > if (enum_first_p) > warning_at (loc, opt, "arithmetic between enumeration type %qT " > @@ -5584,6 +5587,12 @@ cp_build_binary_op (const op_location_t &location, > arithmetic conversions are applied to the operands." So we don't do > arithmetic conversions if the operands both have enumeral type. */ > result_type = NULL_TREE; > + else if ((orig_code0 == ENUMERAL_TYPE && orig_code1 == REAL_TYPE) > + || (orig_code0 == REAL_TYPE && orig_code1 == ENUMERAL_TYPE)) > + /* [depr.arith.conv.enum]: Three-way comparisons between such operands > + [where one is of enumeration type and the other is of a different > + enumeration type or a floating-point type] are ill-formed. */ > + result_type = NULL_TREE; > > if (result_type) > { > @@ -5598,12 +5607,12 @@ cp_build_binary_op (const op_location_t &location, > type to a floating point type, the program is ill-formed. */ > bool ok = true; > if (TREE_CODE (result_type) == REAL_TYPE > - && INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (orig_op0))) > + && CP_INTEGRAL_TYPE_P (orig_type0)) > /* OK */; > else if (!check_narrowing (result_type, orig_op0, complain)) > ok = false; > if (TREE_CODE (result_type) == REAL_TYPE > - && INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (orig_op1))) > + && CP_INTEGRAL_TYPE_P (orig_type1)) > /* OK */; > else if (!check_narrowing (result_type, orig_op1, complain)) > ok = false; > diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C > index d4960f334dd..4571b5e8968 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C > +++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C > @@ -110,9 +110,6 @@ enum_float (bool b) > r += b ? d : u1; // { dg-warning "conditional expression between" "" { target c++20 } } > r += b ? u1 : d; // { dg-warning "conditional expression between" "" { target c++20 } } > > - // FIXME should be error > - // e1 <=> d; > - > d += e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } } > d = e1; > > diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C > new file mode 100644 > index 00000000000..3dc2a0f2365 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C > @@ -0,0 +1,23 @@ > +// { dg-do compile { target c++20 } } > +// Test [depr.arith.conv.enum] for <=>. > + > +#include <compare> > + > +enum E1 { e } e1; > +enum E2 { f } e2; > +static double d; > + > +void > +g () > +{ > + void(e1 <=> e); > + e1 <=> d; // { dg-error "invalid operands of types .E1. and .double." } > + d <=> e1; // { dg-error "invalid operands of types .double. and .E1." } > + e <=> d; // { dg-error "invalid operands of types .E1. and .double." } > + d <=> e; // { dg-error "invalid operands of types .double. and .E1." } > + > + e <=> f; // { dg-error "invalid operands of types .E1. and .E2." } > + f <=> e; // { dg-error "invalid operands of types .E2. and .E1." } > + e1 <=> e2; // { dg-error "invalid operands of types .E1. and .E2." } > + e2 <=> e1; // { dg-error "invalid operands of types .E2. and .E1." } > +} > > base-commit: 4166ebedf8b8a302b86132fdf846fac204c83368 >
diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c index 38d33849423..120f4489f6c 100644 --- a/gcc/c-family/c-opts.c +++ b/gcc/c-family/c-opts.c @@ -925,6 +925,16 @@ c_common_post_options (const char **pfilename) SET_OPTION_IF_UNSET (&global_options, &global_options_set, warn_volatile, cxx_dialect >= cxx20 && warn_deprecated); + /* -Wdeprecated-enum-enum-conversion is enabled by default in C++20. */ + SET_OPTION_IF_UNSET (&global_options, &global_options_set, + warn_deprecated_enum_enum_conv, + cxx_dialect >= cxx20 && warn_deprecated); + + /* -Wdeprecated-enum-float-conversion is enabled by default in C++20. */ + SET_OPTION_IF_UNSET (&global_options, &global_options_set, + warn_deprecated_enum_float_conv, + cxx_dialect >= cxx20 && warn_deprecated); + /* Declone C++ 'structors if -Os. */ if (flag_declone_ctor_dtor == -1) flag_declone_ctor_dtor = optimize_size; diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 1009defbf16..10e53ea67c9 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -518,6 +518,15 @@ C++ ObjC++ Var(warn_deprecated_copy, 2) Warning Mark implicitly-declared copy operations as deprecated if the class has a user-provided copy operation or destructor. +Wdeprecated-enum-enum-conversion +C++ ObjC++ Var(warn_deprecated_enum_enum_conv) Warning +Warn about deprecated arithmetic conversions on operands of enumeration types. + +Wdeprecated-enum-float-conversion +C++ ObjC++ Var(warn_deprecated_enum_float_conv) Warning +Warn about deprecated arithmetic conversions on operands where one is of enumeration +type and the other is of a floating-point type. + Wdesignated-init C ObjC Var(warn_designated_init) Init(1) Warning Warn about positional initialization of structs requiring designated initializers. @@ -559,7 +568,7 @@ C ObjC C++ ObjC++ Var(warn_enum_compare) Init(-1) Warning LangEnabledBy(C ObjC,W Warn about comparison of different enum types. Wenum-conversion -C ObjC Var(warn_enum_conversion) Init(0) Warning LangEnabledBy(C ObjC,Wextra) +C ObjC C++ ObjC++ Var(warn_enum_conversion) Init(0) Warning LangEnabledBy(C ObjC,Wextra) Warn about implicit conversion of enum types. Werror diff --git a/gcc/cp/call.c b/gcc/cp/call.c index bd662518958..9861be1f856 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -5643,17 +5643,40 @@ build_conditional_expr_1 (const op_location_t &loc, "in conditional expression: %qT vs %qT", arg2_type, arg3_type); } - else if (extra_warnings + else if ((complain & tf_warning) + && warn_deprecated_enum_float_conv + && ((TREE_CODE (arg2_type) == ENUMERAL_TYPE + && TREE_CODE (arg3_type) == REAL_TYPE) + || (TREE_CODE (arg2_type) == REAL_TYPE + && TREE_CODE (arg3_type) == ENUMERAL_TYPE))) + { + if (TREE_CODE (arg2_type) == ENUMERAL_TYPE) + warning_at (loc, OPT_Wdeprecated_enum_float_conversion, + "conditional expression between enumeration type " + "%qT and floating-point type %qT is deprecated", + arg2_type, arg3_type); + else + warning_at (loc, OPT_Wdeprecated_enum_float_conversion, + "conditional expression between floating-point " + "type %qT and enumeration type %qT is deprecated", + arg2_type, arg3_type); + } + else if ((extra_warnings || warn_enum_conversion) && ((TREE_CODE (arg2_type) == ENUMERAL_TYPE && !same_type_p (arg3_type, type_promotes_to (arg2_type))) || (TREE_CODE (arg3_type) == ENUMERAL_TYPE && !same_type_p (arg2_type, type_promotes_to (arg3_type))))) - { - if (complain & tf_warning) - warning_at (loc, OPT_Wextra, "enumerated and non-enumerated " - "type in conditional expression"); - } + { + if (complain & tf_warning) + { + enum opt_code opt = (warn_enum_conversion + ? OPT_Wenum_conversion + : OPT_Wextra); + warning_at (loc, opt, "enumerated and " + "non-enumerated type in conditional expression"); + } + } arg2 = perform_implicit_conversion (result_type, arg2, complain); arg3 = perform_implicit_conversion (result_type, arg3, complain); diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 48d34f1132a..7305310ecbe 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -4428,6 +4428,104 @@ warn_for_null_address (location_t location, tree op, tsubst_flags_t complain) } } +/* Warn about [expr.arith.conv]/2: If one operand is of enumeration type and + the other operand is of a different enumeration type or a floating-point + type, this behavior is deprecated ([depr.arith.conv.enum]). CODE is the + code of the binary operation, TYPE0 and TYPE1 are the types of the operands, + and LOC is the location for the whole binary expression. + TODO: Consider combining this with -Wenum-compare in build_new_op_1. */ + +static void +do_warn_enum_conversions (location_t loc, enum tree_code code, tree type0, + tree type1) +{ + if (TREE_CODE (type0) == ENUMERAL_TYPE + && TREE_CODE (type1) == ENUMERAL_TYPE + && TYPE_MAIN_VARIANT (type0) != TYPE_MAIN_VARIANT (type1)) + { + /* In C++20, -Wdeprecated-enum-enum-conversion is on by default. + Otherwise, warn if -Wenum-conversion is on. */ + enum opt_code opt; + if (warn_deprecated_enum_enum_conv) + opt = OPT_Wdeprecated_enum_enum_conversion; + else if (warn_enum_conversion) + opt = OPT_Wenum_conversion; + else + return; + + switch (code) + { + case GT_EXPR: + case LT_EXPR: + case GE_EXPR: + case LE_EXPR: + case EQ_EXPR: + case NE_EXPR: + /* Comparisons are handled by -Wenum-compare. */ + return; + case SPACESHIP_EXPR: + /* This is invalid, don't warn. */ + return; + case BIT_AND_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + warning_at (loc, opt, "bitwise operation between different " + "enumeration types %qT and %qT is deprecated", + type0, type1); + return; + default: + warning_at (loc, opt, "arithmetic between different enumeration " + "types %qT and %qT is deprecated", type0, type1); + return; + } + } + else if ((TREE_CODE (type0) == ENUMERAL_TYPE + && TREE_CODE (type1) == REAL_TYPE) + || (TREE_CODE (type0) == REAL_TYPE + && TREE_CODE (type1) == ENUMERAL_TYPE)) + { + const bool enum_first_p = TREE_CODE (type0) == ENUMERAL_TYPE; + /* In C++20, -Wdeprecated-enum-float-conversion is on by default. + Otherwise, warn if -Wenum-conversion is on. */ + enum opt_code opt; + if (warn_deprecated_enum_float_conv) + opt = OPT_Wdeprecated_enum_float_conversion; + else if (warn_enum_conversion) + opt = OPT_Wenum_conversion; + else + return; + + switch (code) + { + case GT_EXPR: + case LT_EXPR: + case GE_EXPR: + case LE_EXPR: + case EQ_EXPR: + case NE_EXPR: + if (enum_first_p) + warning_at (loc, opt, "comparison of enumeration type %qT with " + "floating-point type %qT is deprecated", + type0, type1); + else + warning_at (loc, opt, "comparison of floating-point type %qT " + "with enumeration type %qT is deprecated", + type0, type1); + return; + default: + if (enum_first_p) + warning_at (loc, opt, "arithmetic between enumeration type %qT " + "and floating-point type %qT is deprecated", + type0, type1); + else + warning_at (loc, opt, "arithmetic between floating-point type %qT " + "and enumeration type %qT is deprecated", + type0, type1); + return; + } + } +} + /* Build a binary-operation expression without default conversions. CODE is the kind of expression to build. LOCATION is the location_t of the operator in the source code. @@ -5445,11 +5543,15 @@ cp_build_binary_op (const op_location_t &location, { result_type = cp_common_type (type0, type1); if (complain & tf_warning) - do_warn_double_promotion (result_type, type0, type1, - "implicit conversion from %qH to %qI " - "to match other operand of binary " - "expression", - location); + { + do_warn_double_promotion (result_type, type0, type1, + "implicit conversion from %qH to %qI " + "to match other operand of binary " + "expression", + location); + do_warn_enum_conversions (location, code, TREE_TYPE (orig_op0), + TREE_TYPE (orig_op1)); + } } if (code == SPACESHIP_EXPR) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index f82eeea097a..72ae4a23203 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -239,6 +239,7 @@ in the following sections. -Wno-conversion-null -Wctad-maybe-unsupported @gol -Wctor-dtor-privacy -Wno-delete-incomplete @gol -Wdelete-non-virtual-dtor -Wdeprecated-copy -Wdeprecated-copy-dtor @gol +-Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion @gol -Weffc++ -Wextra-semi -Wno-inaccessible-base @gol -Wno-inherited-variadic-ctor -Wno-init-list-lifetime @gol -Wno-invalid-offsetof -Wno-literal-suffix -Wmismatched-tags @gol @@ -3358,6 +3359,42 @@ warning is enabled by @option{-Wextra}. With @option{-Wdeprecated-copy-dtor}, also deprecate if the class has a user-provided destructor. +@item -Wno-deprecated-enum-enum-conversion @r{(C++ and Objective-C++ only)} +@opindex Wdeprecated-enum-enum-conversion +@opindex Wno-deprecated-enum-enum-conversion +Disable the warning about the case when the usual arithmetic conversions +are applied on operands where one is of enumeration type and the other is +of a different enumeration type. This conversion was deprecated in C++20. +For example: + +@smallexample +enum E1 @{ e @}; +enum E2 @{ f @}; +int k = f - e; +@end smallexample + +@option{-Wdeprecated-enum-enum-conversion} is enabled by default with +@option{-std=c++20}. In pre-C++20 dialects, this warning can be enabled +by @option{-Wenum-conversion}. + +@item -Wno-deprecated-enum-float-conversion @r{(C++ and Objective-C++ only)} +@opindex Wdeprecated-enum-float-conversion +@opindex Wno-deprecated-enum-float-conversion +Disable the warning about the case when the usual arithmetic conversions +are applied on operands where one is of enumeration type and the other is +of a floating-point type. This conversion was deprecated in C++20. For +example: + +@smallexample +enum E1 @{ e @}; +enum E2 @{ f @}; +bool b = e <= 3.7; +@end smallexample + +@option{-Wdeprecated-enum-float-conversion} is enabled by default with +@option{-std=c++20}. In pre-C++20 dialects, this warning can be enabled +by @option{-Wenum-conversion}. + @item -Wno-init-list-lifetime @r{(C++ and Objective-C++ only)} @opindex Winit-list-lifetime @opindex Wno-init-list-lifetime @@ -5271,7 +5308,6 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}. -Wcomment @gol -Wduplicate-decl-specifier @r{(C and Objective-C only)} @gol -Wenum-compare @r{(in C/ObjC; this is on by default in C++)} @gol --Wenum-conversion @r{in C/ObjC;} @gol -Wformat @gol -Wformat-overflow @gol -Wformat-truncation @gol @@ -5340,6 +5376,7 @@ name is still supported, but the newer name is more descriptive.) -Wcast-function-type @gol -Wdeprecated-copy @r{(C++ only)} @gol -Wempty-body @gol +-Wenum-conversion @r{(C only)} @gol -Wignored-qualifiers @gol -Wimplicit-fallthrough=3 @gol -Wmissing-field-initializers @gol @@ -8006,11 +8043,12 @@ In C++ enumerated type mismatches in conditional expressions are also diagnosed and the warning is enabled by default. In C this warning is enabled by @option{-Wall}. -@item -Wenum-conversion @r{(C, Objective-C only)} +@item -Wenum-conversion @opindex Wenum-conversion @opindex Wno-enum-conversion Warn when a value of enumerated type is implicitly converted to a -different enumerated type. This warning is enabled by @option{-Wextra}. +different enumerated type. This warning is enabled by @option{-Wextra} +in C@. @item -Wjump-misses-init @r{(C, Objective-C only)} @opindex Wjump-misses-init diff --git a/gcc/testsuite/g++.dg/cpp0x/linkage2.C b/gcc/testsuite/g++.dg/cpp0x/linkage2.C index 52858687ed3..549bd825aab 100644 --- a/gcc/testsuite/g++.dg/cpp0x/linkage2.C +++ b/gcc/testsuite/g++.dg/cpp0x/linkage2.C @@ -29,5 +29,5 @@ void f() { ba.g(a); // OK ba.h(a); // error, B<T>::h never defined i(ba, a); // OK - e1+e2+e3; + e1+e2+e3; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } } } diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C new file mode 100644 index 00000000000..d4960f334dd --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C @@ -0,0 +1,120 @@ +// PR c++/97573 +// { dg-do compile } +// No special options. In C++20 (only), we should get the deprecated warnings +// by default. -Wenum-compare is enabled by default so some of them will be +// printed even pre-C++20. + +enum E1 { e } e1; +enum E2 { f } e2; +__extension__ static enum { } u1; +__extension__ static enum { } u2; +static double d; + +void +conv () +{ + bool b1 = e == e1; + bool b2 = e == f; // { dg-warning "comparison between .enum E1. and .enum E2." } + bool b3 = e == 0.0; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." "" { target c++20 } } + bool b4 = 0.0 == f; // { dg-warning "comparison of floating-point type .double. with enumeration type .E2." "" { target c++20 } } + int n1 = true ? e : f; // { dg-warning "enumerated mismatch" } + int n2 = true ? e : 0.0; // { dg-warning "conditional expression between" "" { target c++20 } } +} + +int +enum_enum (bool b) +{ + int r = 0; + const E1 e1c = e; + + r += e - e; + r += e - e1; + r += e - f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } } + r += f - e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." "" { target c++20 } } + + r += f + f; + r += f + e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." "" { target c++20 } } + r += e + f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } } + + r += e1 - e2; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } } + r += e1 - e1c; + r += e1c - e1; + + r += e * f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } } + r += f * e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." "" { target c++20 } } + r += e * e; + + r += e1 < e1c; + r += e < e1; + r += e1 < e2; // { dg-warning "comparison between .enum E1. and .enum E2." } + r += e < f; // { dg-warning "comparison between .enum E1. and .enum E2." } + r += f < e; // { dg-warning "comparison between .enum E2. and .enum E1." } + + r += e1 == e1c; + r += e == e1; + r += e == f; // { dg-warning "comparison between .enum E1. and .enum E2." } + r += f == e; // { dg-warning "comparison between .enum E2. and .enum E1." } + r += e1 == e2; // { dg-warning "comparison between .enum E1. and .enum E2." } + r += e2 == e1; // { dg-warning "comparison between .enum E2. and .enum E1." } + + r += b ? e1 : e1c; + r += b ? e1 : e; + r += b ? f : e; // { dg-warning "enumerated mismatch in conditional expression: .E2. vs .E1." } + r += b ? e1 : e2; // { dg-warning "enumerated mismatch in conditional expression: .E1. vs .E2." } + + r += e | f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." "" { target c++20 } } + r += e ^ f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." "" { target c++20 } } + r += e & f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." "" { target c++20 } } + r += !e; + r += e1 | e; + + r += e << f; + r += e >> f; + r += e || f; + r += e && f; + e1 = e1c; + + // Anonymous enum. + r += u1 - u1; + r += u1 + u2; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } } + r += u1 * u2; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } } + r += u1 == u2; // { dg-warning "comparison between" } + r += u1 & u2; // { dg-warning "bitwise operation between different enumeration types" "" { target c++20 } } + + return r; +} + +double +enum_float (bool b) +{ + double r = 0.0; + + r += e1 - d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." "" { target c++20 } } + r += d - e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } } + r += e1 + d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." "" { target c++20 } } + r += d + e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } } + r += e1 * d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." "" { target c++20 } } + r += d * e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } } + r += u1 * d; // { dg-warning "arithmetic between enumeration type" "" { target c++20 } } + r += d * u1; // { dg-warning "arithmetic between floating-point type" "" { target c++20 } } + + r += e1 < d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." "" { target c++20 } } + r += d < e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." "" { target c++20 } } + r += d == e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." "" { target c++20 } } + r += e1 == d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." "" { target c++20 } } + r += u1 == d; // { dg-warning "comparison of enumeration type" "" { target c++20 } } + r += d == u1; // { dg-warning "comparison of floating-point type" "" { target c++20 } } + + r += b ? e1 : d; // { dg-warning "conditional expression between enumeration type .E1. and floating-point type .double." "" { target c++20 } } + r += b ? d : e1; // { dg-warning "conditional expression between floating-point type .double. and enumeration type .E1." "" { target c++20 } } + r += b ? d : u1; // { dg-warning "conditional expression between" "" { target c++20 } } + r += b ? u1 : d; // { dg-warning "conditional expression between" "" { target c++20 } } + + // FIXME should be error + // e1 <=> d; + + d += e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } } + d = e1; + + return r; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv2.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv2.C new file mode 100644 index 00000000000..f15827bda14 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv2.C @@ -0,0 +1,115 @@ +// PR c++/97573 +// { dg-do compile { target c++20 } } +// { dg-options "-Wno-deprecated -Wno-enum-compare" } + +enum E1 { e } e1; +enum E2 { f } e2; +__extension__ static enum { } u1; +__extension__ static enum { } u2; +static double d; + +void +conv () +{ + bool b1 = e == e1; + bool b2 = e == f; + bool b3 = e == 0.0; + bool b4 = 0.0 == f; + int n1 = true ? e : f; + int n2 = true ? e : 0.0; +} + +int +enum_enum (bool b) +{ + int r = 0; + const E1 e1c = e; + + r += e - e; + r += e - e1; + r += e - f; + r += f - e; + + r += f + f; + r += f + e; + r += e + f; + + r += e1 - e2; + r += e1 - e1c; + r += e1c - e1; + + r += e * f; + r += f * e; + r += e * e; + + r += e1 < e1c; + r += e < e1; + r += e1 < e2; + r += e < f; + r += f < e; + + r += e1 == e1c; + r += e == e1; + r += e == f; + r += f == e; + r += e1 == e2; + r += e2 == e1; + + r += b ? e1 : e1c; + r += b ? e1 : e; + r += b ? f : e; + r += b ? e1 : e2; + + r += e | f; + r += e ^ f; + r += e & f; + r += !e; + r += e1 | e; + + r += e << f; + r += e >> f; + r += e || f; + r += e && f; + e1 = e1c; + + // Anonymous enum. + r += u1 - u1; + r += u1 + u2; + r += u1 * u2; + r += u1 == u2; + r += u1 & u2; + + return r; +} + +double +enum_float (bool b) +{ + double r = 0.0; + + r += e1 - d; + r += d - e1; + r += e1 + d; + r += d + e1; + r += e1 * d; + r += d * e1; + r += u1 * d; + r += d * u1; + + r += e1 < d; + r += d < e1; + r += d == e1; + r += e1 == d; + r += u1 == d; + r += d == u1; + + r += b ? e1 : d; + r += b ? d : e1; + r += b ? d : u1; + r += b ? u1 : d; + + d += e1; + d = e1; + + return r; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv3.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv3.C new file mode 100644 index 00000000000..67bdf1600d7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv3.C @@ -0,0 +1,115 @@ +// PR c++/97573 +// { dg-do compile { target { c++17_down } } } +// { dg-options "-Wenum-conversion" } + +enum E1 { e } e1; +enum E2 { f } e2; +__extension__ static enum { } u1; +__extension__ static enum { } u2; +static double d; + +void +conv () +{ + bool b1 = e == e1; + bool b2 = e == f; // { dg-warning "comparison between .enum E1. and .enum E2." } + bool b3 = e == 0.0; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." } + bool b4 = 0.0 == f; // { dg-warning "comparison of floating-point type .double. with enumeration type .E2." } + int n1 = true ? e : f; // { dg-warning "enumerated mismatch" } + int n2 = true ? e : 0.0; // { dg-warning "enumerated and non-enumerated type in conditional expression" } +} + +int +enum_enum (bool b) +{ + int r = 0; + const E1 e1c = e; + + r += e - e; + r += e - e1; + r += e - f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." } + r += f - e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." } + + r += f + f; + r += f + e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." } + r += e + f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." } + + r += e1 - e2; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." } + r += e1 - e1c; + r += e1c - e1; + + r += e * f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." } + r += f * e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." } + r += e * e; + + r += e1 < e1c; + r += e < e1; + r += e1 < e2; // { dg-warning "comparison between .enum E1. and .enum E2." } + r += e < f; // { dg-warning "comparison between .enum E1. and .enum E2." } + r += f < e; // { dg-warning "comparison between .enum E2. and .enum E1." } + + r += e1 == e1c; + r += e == e1; + r += e == f; // { dg-warning "comparison between .enum E1. and .enum E2." } + r += f == e; // { dg-warning "comparison between .enum E2. and .enum E1." } + r += e1 == e2; // { dg-warning "comparison between .enum E1. and .enum E2." } + r += e2 == e1; // { dg-warning "comparison between .enum E2. and .enum E1." } + + r += b ? e1 : e1c; + r += b ? e1 : e; + r += b ? f : e; // { dg-warning "enumerated mismatch in conditional expression: .E2. vs .E1." } + r += b ? e1 : e2; // { dg-warning "enumerated mismatch in conditional expression: .E1. vs .E2." } + + r += e | f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." } + r += e ^ f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." } + r += e & f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." } + r += !e; + r += e1 | e; + + r += e << f; + r += e >> f; + r += e || f; + r += e && f; + e1 = e1c; + + // Anonymous enum. + r += u1 - u1; + r += u1 + u2; // { dg-warning "arithmetic between different enumeration types" } + r += u1 * u2; // { dg-warning "arithmetic between different enumeration types" } + r += u1 == u2; // { dg-warning "comparison between" } + r += u1 & u2; // { dg-warning "bitwise operation between different enumeration types" } + + return r; +} + +double +enum_float (bool b) +{ + double r = 0.0; + + r += e1 - d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." } + r += d - e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." } + r += e1 + d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." } + r += d + e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." } + r += e1 * d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." } + r += d * e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." } + r += u1 * d; // { dg-warning "arithmetic between enumeration type" } + r += d * u1; // { dg-warning "arithmetic between floating-point type" } + + r += e1 < d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." } + r += d < e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." } + r += d == e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." } + r += e1 == d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." } + r += u1 == d; // { dg-warning "comparison of enumeration type" } + r += d == u1; // { dg-warning "comparison of floating-point type" } + + r += b ? e1 : d; // { dg-warning "enumerated and non-enumerated type in conditional expression" } + r += b ? d : e1; // { dg-warning "enumerated and non-enumerated type in conditional expression" } + r += b ? d : u1; // { dg-warning "enumerated and non-enumerated type in conditional expression" } + r += b ? u1 : d; // { dg-warning "enumerated and non-enumerated type in conditional expression" } + + d += e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." } + d = e1; + + return r; +} diff --git a/gcc/testsuite/g++.dg/parse/attr3.C b/gcc/testsuite/g++.dg/parse/attr3.C index 57fa60e130e..de095988015 100644 --- a/gcc/testsuite/g++.dg/parse/attr3.C +++ b/gcc/testsuite/g++.dg/parse/attr3.C @@ -10,5 +10,5 @@ int main () { S::F y; // { dg-warning "'F' is deprecated" } y = S::f; - return x + y; + return x + y; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } } }