c++: Reject float <=> enum.
authorMarek Polacek <polacek@redhat.com>
Wed, 28 Oct 2020 23:02:29 +0000 (19:02 -0400)
committerMarek Polacek <polacek@redhat.com>
Thu, 29 Oct 2020 18:06:13 +0000 (14:06 -0400)
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
gcc/testsuite/g++.dg/cpp2a/enum-conv1.C
gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C [new file with mode: 0644]

index 7305310..d3b7016 100644 (file)
@@ -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;
index d4960f3..4571b5e 100644 (file)
@@ -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 (file)
index 0000000..3dc2a0f
--- /dev/null
@@ -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." }
+}