From 455ade18462e5076065b1970e21c622239797392 Mon Sep 17 00:00:00 2001 From: Marek Polacek Date: Tue, 27 Oct 2020 11:16:50 -0400 Subject: [PATCH] c++: Deprecate arithmetic convs on different enums [PR97573] 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. We should also address [depr.array.comp] too, but it's not handled in this patch. 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::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 } } } -- 2.30.2