Hi,

this is a rejects valid issue with constexpr conversion operators and enums, eg:

struct X0
{
  constexpr operator int() const { return 1; }
};

enum E0 { e0 = X0() };

Given build_expr_type_conversion, we can handle the bug rather easily by using it when the enum doesn't have an underlying type, and directly perform_implicit_conversion_flags otherwise (as also suggested by Marc in the audit trail time ago). Some details: 1- Evidently, we can't just do everything with build_expr_type_conversion, similarly, eg, to finish_switch_cond, because we would not do the right thing for tests like F5 below, it would result in an ambiguous conversion. 2- I moved up, inside the value == NULL_TREE conditional, the existing code dealing with fixed underlying type enums, because now it's actually used only when we automatically compute the current value starting from the previous one (thus the tweaks to the existing testcases) 3- I'm taking the occasion to replace a pair of errors in build_expr_type_conversion with error + inform.

Thanks,
Paolo.

////////////////////////
/cp
2014-08-07  Paolo Carlini  <paolo.carl...@oracle.com>

        PR c++/51312
        * decl.c (build_enumerator): Handle class types with conversion
        operators via build_expr_type_conversion.

        * cvt.c (build_expr_type_conversion): Replace pair of errors
        with error + inform.

/testsuite
2014-08-07  Paolo Carlini  <paolo.carl...@oracle.com>

        PR c++/51312
        * g++.dg/cpp0x/enum29.C: New.
        * g++.dg/cpp0x/enum27.C: Adjust.
        * g++.dg/cpp0x/enum_base.C: Likewise.
Index: cp/cvt.c
===================================================================
--- cp/cvt.c    (revision 213692)
+++ cp/cvt.c    (working copy)
@@ -1658,8 +1658,9 @@ build_expr_type_conversion (int desires, tree expr
                    {
                      error ("ambiguous default type conversion from %qT",
                             basetype);
-                     error ("  candidate conversions include %qD and %qD",
-                            winner, cand);
+                     inform (input_location,
+                             "  candidate conversions include %qD and %qD",
+                             winner, cand);
                    }
                  return error_mark_node;
                }
Index: cp/decl.c
===================================================================
--- cp/decl.c   (revision 213692)
+++ cp/decl.c   (working copy)
@@ -12966,14 +12966,32 @@ build_enumerator (tree name, tree value, tree enum
       /* Validate and default VALUE.  */
       if (value != NULL_TREE)
        {
-         value = cxx_constant_value (value);
+         if (ENUM_UNDERLYING_TYPE (enumtype))
+           value = perform_implicit_conversion_flags
+             (ENUM_UNDERLYING_TYPE (enumtype), value, tf_warning_or_error,
+              LOOKUP_IMPLICIT | LOOKUP_NO_NARROWING);
+         else
+           {
+             tree tmp_value = build_expr_type_conversion (WANT_INT | WANT_ENUM,
+                                                          value, true);
+             if (tmp_value)
+               value = tmp_value;
+           }
 
-         if (TREE_CODE (value) != INTEGER_CST
-             || ! INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (value)))
+         if (value == error_mark_node)
+           value = NULL_TREE;
+
+         if (value != NULL_TREE)
            {
-             error ("enumerator value for %qD is not an integer constant",
-                    name);
-             value = NULL_TREE;
+             value = cxx_constant_value (value);
+
+             if (TREE_CODE (value) != INTEGER_CST
+                 || ! INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (value)))
+               {
+                 error ("enumerator value for %qD is not an integer constant",
+                        name);
+                 value = NULL_TREE;
+               }
            }
        }
 
@@ -13036,25 +13054,25 @@ incremented enumerator value is too large for %<lo
            }
          else
            value = integer_zero_node;
-       }
 
-      /* Remove no-op casts from the value.  */
-      STRIP_TYPE_NOPS (value);
+         /* Remove no-op casts from the value.  */
+         STRIP_TYPE_NOPS (value);
 
-      /* If the underlying type of the enum is fixed, check whether
-         the enumerator values fits in the underlying type.  If it
-         does not fit, the program is ill-formed [C++0x dcl.enum].  */
-      if (ENUM_UNDERLYING_TYPE (enumtype)
-          && value
-          && TREE_CODE (value) == INTEGER_CST)
-        {
-         if (!int_fits_type_p (value, ENUM_UNDERLYING_TYPE (enumtype)))
-           error ("enumerator value %E is outside the range of underlying "
-                  "type %<%T%>", value, ENUM_UNDERLYING_TYPE (enumtype));
+         /* If the underlying type of the enum is fixed, check whether
+            the enumerator values fits in the underlying type.  If it
+            does not fit, the program is ill-formed [C++0x dcl.enum].  */
+         if (ENUM_UNDERLYING_TYPE (enumtype)
+             && value
+             && TREE_CODE (value) == INTEGER_CST)
+           {
+             if (!int_fits_type_p (value, ENUM_UNDERLYING_TYPE (enumtype)))
+               error ("enumerator value %E is outside the range of underlying "
+                      "type %<%T%>", value, ENUM_UNDERLYING_TYPE (enumtype));
 
-          /* Convert the value to the appropriate type.  */
-          value = convert (ENUM_UNDERLYING_TYPE (enumtype), value);
-        }
+             /* Convert the value to the appropriate type.  */
+             value = convert (ENUM_UNDERLYING_TYPE (enumtype), value);
+           }
+       }
     }
 
   /* C++ associates enums with global, function, or class declarations.  */
Index: testsuite/g++.dg/cpp0x/enum27.C
===================================================================
--- testsuite/g++.dg/cpp0x/enum27.C     (revision 213692)
+++ testsuite/g++.dg/cpp0x/enum27.C     (working copy)
@@ -1,4 +1,4 @@
 // PR c++/53745
 // { dg-do compile { target c++11 } }
 
-enum E : unsigned { e = -1 };  // { dg-error "outside the range" }
+enum E : unsigned { e = -1 };  // { dg-error "narrowing" }
Index: testsuite/g++.dg/cpp0x/enum29.C
===================================================================
--- testsuite/g++.dg/cpp0x/enum29.C     (revision 0)
+++ testsuite/g++.dg/cpp0x/enum29.C     (working copy)
@@ -0,0 +1,58 @@
+// PR c++/51312
+// { dg-do compile { target c++11 } }
+
+struct X0
+{
+  constexpr operator int() const { return 1; }
+};
+
+struct X1
+{
+  enum RE1 { re1 = 1 };
+  constexpr operator RE1() const { return re1; }
+};
+
+struct X2
+{
+  constexpr operator int() const { return __INT_MAX__; }
+};
+
+struct X3
+{
+  enum RE3 { re3 = __INT_MAX__ };
+  constexpr operator RE3() const { return re3; }
+};
+
+struct X4
+{
+  constexpr operator double() const { return 1.0; }
+};
+
+struct X5
+{
+  constexpr operator int() const { return __INT_MAX__; }
+  constexpr operator unsigned() const { return __INT_MAX__ * 2U + 1; }
+};
+
+enum E0 { e0 = X0() };
+enum E1 { e1 = X1() };
+enum E2 { e2 = X2() };
+enum E3 { e3 = X3() };
+enum E4 { e4 = X4() };  // { dg-error "integer constant" }
+enum E5 { e5 = X5() };  // { dg-error "ambiguous" }
+
+enum F0 : int { f0 = X0() };
+enum F1 : int { f1 = X1() };
+enum F2 : int { f2 = X2() };
+enum F3 : int { f3 = X3() };
+enum F4 : int { f4 = X4() };  // { dg-error "narrowing" }
+enum F5 : int { f5 = X5() };
+
+enum G0 : signed char { g0 = X0() };
+enum G1 : signed char { g1 = X1() };
+enum G2 : signed char { g2 = X2() };  // { dg-error "narrowing" }
+// { dg-warning "overflow" "" { target *-*-* } 53 }
+enum G3 : signed char { g3 = X3() };  // { dg-error "narrowing" }
+// { dg-warning "overflow" "" { target *-*-* } 55 }
+enum G4 : signed char { g4 = X4() };  // { dg-error "narrowing" }
+enum G5 : signed char { g5 = X5() };  // { dg-error "ambiguous" }
Index: testsuite/g++.dg/cpp0x/enum_base.C
===================================================================
--- testsuite/g++.dg/cpp0x/enum_base.C  (revision 213692)
+++ testsuite/g++.dg/cpp0x/enum_base.C  (working copy)
@@ -6,11 +6,13 @@ enum E1 : char { };
 enum E2 : signed const short { };
 enum E3 : uvlonglong { };
 enum E4 : char { 
-  val = 500 // { dg-error "outside the range" }
+  val = 500 // { dg-error "narrowing" }
+// { dg-warning "overflow" "" { target *-*-* } 9 }
 };
 
 enum class E5 {
-  val = (unsigned long long)-1 // { dg-error "outside the range" }
+  val = (unsigned long long)-1 // { dg-error "narrowing" }
+// { dg-warning "overflow" "" { target *-*-* } 14 }
 };
 
 typedef float Float;

Reply via email to