c++: Deprecate arithmetic convs on different enums [PR97573]
authorMarek Polacek <polacek@redhat.com>
Tue, 27 Oct 2020 15:16:50 +0000 (11:16 -0400)
committerMarek Polacek <polacek@redhat.com>
Wed, 28 Oct 2020 21:37:18 +0000 (17:37 -0400)
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
gcc/c-family/c.opt
gcc/cp/call.c
gcc/cp/typeck.c
gcc/doc/invoke.texi
gcc/testsuite/g++.dg/cpp0x/linkage2.C
gcc/testsuite/g++.dg/cpp2a/enum-conv1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/enum-conv2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/enum-conv3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/parse/attr3.C

index 38d3384942363f210104ed2d26166f4a25f2e37d..120f4489f6cf14ec2b06749c4965296d13a8928f 100644 (file)
@@ -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;
index 1009defbf168c96049d15291b076751589ddf81a..10e53ea67c90191d72928eaf787ff92371b5e8f2 100644 (file)
@@ -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
index bd662518958321ff5c0cbb99cf671418fc0befc5..9861be1f8561dfa1983c90c76a3c9217ac8ef62d 100644 (file)
@@ -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);
index 48d34f1132a0e320adb306472b9f3a4254141815..7305310ecbe1fc1a59e22bf896027edc1faffef2 100644 (file)
@@ -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)
index f82eeea097af61b6bc8fe96c0352b458fd49e1b5..72ae4a232037b15d038aeb14900993c7357924ed 100644 (file)
@@ -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
index 52858687ed33c6106f7562affb6852da2c5572ae..549bd825aabd7e22b76d487de9e757caae1fea65 100644 (file)
@@ -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 (file)
index 0000000..d4960f3
--- /dev/null
@@ -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 (file)
index 0000000..f15827b
--- /dev/null
@@ -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 (file)
index 0000000..67bdf16
--- /dev/null
@@ -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;
+}
index 57fa60e130e9090d6f2325100b11c610be2494ce..de0959880158818306feb9b0214512b41930c6e5 100644 (file)
@@ -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 } }
 }