P0138R2: Allow direct-list-initialization of an enumeration from an integral
authorRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 28 Mar 2016 06:08:37 +0000 (06:08 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 28 Mar 2016 06:08:37 +0000 (06:08 +0000)
value that can convert to the enum's underlying type.

llvm-svn: 264564

clang/lib/Sema/SemaInit.cpp
clang/lib/Sema/SemaOverload.cpp
clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3.cpp [moved from clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3-0x.cpp with 53% similarity]
clang/www/cxx_status.html

index d308253..bd8522d 100644 (file)
@@ -3862,8 +3862,48 @@ static void TryListInitialization(Sema &S,
   }
 
   if (S.getLangOpts().CPlusPlus && !DestType->isAggregateType() &&
-      InitList->getNumInits() == 1 &&
-      InitList->getInit(0)->getType()->isRecordType()) {
+      InitList->getNumInits() == 1) {
+    Expr *E = InitList->getInit(0);
+
+    //   - Otherwise, if T is an enumeration with a fixed underlying type,
+    //     the initializer-list has a single element v, and the initialization
+    //     is direct-list-initialization, the object is initialized with the
+    //     value T(v); if a narrowing conversion is required to convert v to
+    //     the underlying type of T, the program is ill-formed.
+    auto *ET = DestType->getAs<EnumType>();
+    if (S.getLangOpts().CPlusPlus1z &&
+        Kind.getKind() == InitializationKind::IK_DirectList &&
+        ET && ET->getDecl()->isFixed() &&
+        !S.Context.hasSameUnqualifiedType(E->getType(), DestType) &&
+        (E->getType()->isIntegralOrEnumerationType() ||
+         E->getType()->isFloatingType())) {
+      // There are two ways that T(v) can work when T is an enumeration type.
+      // If there is either an implicit conversion sequence from v to T or
+      // a conversion function that can convert from v to T, then we use that.
+      // Otherwise, if v is of integral, enumeration, or floating-point type,
+      // it is converted to the enumeration type via its underlying type.
+      // There is no overlap possible between these two cases (except when the
+      // source value is already of the destination type), and the first
+      // case is handled by the general case for single-element lists below.
+      ImplicitConversionSequence ICS;
+      ICS.setStandard();
+      ICS.Standard.setAsIdentityConversion();
+      // If E is of a floating-point type, then the conversion is ill-formed
+      // due to narrowing, but go through the motions in order to produce the
+      // right diagnostic.
+      ICS.Standard.Second = E->getType()->isFloatingType()
+                                ? ICK_Floating_Integral
+                                : ICK_Integral_Conversion;
+      ICS.Standard.setFromType(E->getType());
+      ICS.Standard.setToType(0, E->getType());
+      ICS.Standard.setToType(1, DestType);
+      ICS.Standard.setToType(2, DestType);
+      Sequence.AddConversionSequenceStep(ICS, ICS.Standard.getToType(2),
+                                         /*TopLevelOfInitList*/true);
+      Sequence.RewrapReferenceInitList(Entity.getType(), InitList);
+      return;
+    }
+
     //   - Otherwise, if the initializer list has a single element of type E
     //     [...references are handled above...], the object or reference is
     //     initialized from that element (by copy-initialization for
@@ -3877,19 +3917,21 @@ static void TryListInitialization(Sema &S,
     // copy-initialization. This only matters if we might use an 'explicit'
     // conversion operator, so we only need to handle the cases where the source
     // is of record type.
-    InitializationKind SubKind =
-        Kind.getKind() == InitializationKind::IK_DirectList
-            ? InitializationKind::CreateDirect(Kind.getLocation(),
-                                               InitList->getLBraceLoc(),
-                                               InitList->getRBraceLoc())
-            : Kind;
-    Expr *SubInit[1] = { InitList->getInit(0) };
-    Sequence.InitializeFrom(S, Entity, SubKind, SubInit,
-                            /*TopLevelOfInitList*/true,
-                            TreatUnavailableAsInvalid);
-    if (Sequence)
-      Sequence.RewrapReferenceInitList(Entity.getType(), InitList);
-    return;
+    if (InitList->getInit(0)->getType()->isRecordType()) {
+      InitializationKind SubKind =
+          Kind.getKind() == InitializationKind::IK_DirectList
+              ? InitializationKind::CreateDirect(Kind.getLocation(),
+                                                 InitList->getLBraceLoc(),
+                                                 InitList->getRBraceLoc())
+              : Kind;
+      Expr *SubInit[1] = { InitList->getInit(0) };
+      Sequence.InitializeFrom(S, Entity, SubKind, SubInit,
+                              /*TopLevelOfInitList*/true,
+                              TreatUnavailableAsInvalid);
+      if (Sequence)
+        Sequence.RewrapReferenceInitList(Entity.getType(), InitList);
+      return;
+    }
   }
 
   InitListChecker CheckInitList(S, Entity, InitList,
index e9154bc..a0e189d 100644 (file)
@@ -293,6 +293,13 @@ StandardConversionSequence::getNarrowingKind(ASTContext &Ctx,
   //   A narrowing conversion is an implicit conversion ...
   QualType FromType = getToType(0);
   QualType ToType = getToType(1);
+
+  // A conversion to an enumeration type is narrowing if the conversion to
+  // the underlying type is narrowing. This only arises for expressions of
+  // the form 'Enum{init}'.
+  if (auto *ET = ToType->getAs<EnumType>())
+    ToType = ET->getDecl()->getIntegerType();
+
   switch (Second) {
   // 'bool' is an integral type; dispatch to the right place to handle it.
   case ICK_Boolean_Conversion:
@@ -1,4 +1,6 @@
 // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++1z -fsyntax-only -verify %s
 
 namespace std {
   typedef decltype(sizeof(int)) size_t;
@@ -125,3 +127,128 @@ namespace rdar13395022 {
     // expected-note@-2 {{in initialization of temporary of type 'rdar13395022::MoveOnly [1]' created to list-initialize this reference}}
   }
 }
+
+namespace cxx1z_direct_enum_init {
+  enum A {};
+  enum B : char {};
+  enum class C {};
+  enum class D : char {};
+  enum class E : char { k = 5 };
+
+  template<typename T> void good() {
+    (void)T{0};
+    T t1{0};
+    T t2 = T{0};
+
+    struct S { T t; };
+    S s{T{0}};
+
+    struct U { T t{0}; } u; // expected-note 0+{{instantiation of}}
+
+    struct V { T t; V() : t{0} {} }; // expected-note 0+{{instantiation of}}
+
+    void f(T);
+    f(T{0});
+  }
+#if __cplusplus <= 201402L
+  // expected-error@-15 5{{cannot initialize}}
+  // expected-error@-15 5{{cannot initialize}}
+  // expected-error@-15 5{{cannot initialize}}
+  //
+  //
+  // expected-error@-15 5{{cannot initialize}}
+  //
+  // expected-error@-15 5{{cannot initialize}}
+  //
+  // expected-error@-15 5{{cannot initialize}}
+  //
+  //
+  // expected-error@-15 5{{cannot initialize}}
+#else
+  // expected-error@-29 {{cannot initialize}}
+  // expected-error@-29 {{cannot initialize}}
+  // expected-error@-29 {{cannot initialize}}
+  //
+  //
+  // expected-error@-29 {{cannot initialize}}
+  //
+  // expected-error@-29 {{cannot initialize}}
+  //
+  // expected-error@-29 {{cannot initialize}}
+  //
+  //
+  // expected-error@-29 {{cannot initialize}}
+#endif
+
+  template<typename T> void bad() {
+    T t = {0};
+
+    struct S { T t; };
+    S s1{0};
+    S s2{{0}};
+
+    struct U { T t = {0}; } u; // expected-note 0+{{instantiation of}}
+
+    struct V { T t; V() : t({0}) {} }; // expected-note 0+{{instantiation of}}
+
+    void f(T); // expected-note 0+{{passing argument}}
+    f({0});
+  }
+  // expected-error@-13 5{{cannot initialize}}
+  //
+  //
+  // expected-error@-13 5{{cannot initialize}}
+  // expected-error@-13 5{{cannot initialize}}
+  //
+  // expected-error@-13 5{{cannot initialize}}
+  //
+  // expected-error@-13 5{{cannot initialize}}
+  //
+  //
+  // expected-error@-13 5{{cannot initialize}}
+
+  template<typename T> void ugly() {
+    extern char c;
+    T t1{char('0' + c)};
+    T t2{'0' + c};
+    T t3{1234};
+  }
+#if __cplusplus <= 201402L
+  // expected-error@-5 4{{cannot initialize}}
+  // expected-error@-5 4{{cannot initialize}}
+  // expected-error@-5 4{{cannot initialize}}
+#else
+  // expected-error@-8 3{{non-constant-expression cannot be narrowed}}
+  // expected-error@-8 3{{constant expression evaluates to 1234 which cannot be narrowed}} expected-warning@-8 {{changes value}}
+#endif
+
+  void test() {
+    good<A>(); // expected-note 4{{instantiation of}}
+    good<B>();
+    good<C>();
+    good<D>();
+    good<E>();
+#if __cplusplus <= 201402L
+    // expected-note@-5 4{{instantiation of}}
+    // expected-note@-5 4{{instantiation of}}
+    // expected-note@-5 4{{instantiation of}}
+    // expected-note@-5 4{{instantiation of}}
+#endif
+
+    bad<A>(); // expected-note 4{{instantiation of}}
+    bad<B>(); // expected-note 4{{instantiation of}}
+    bad<C>(); // expected-note 4{{instantiation of}}
+    bad<D>(); // expected-note 4{{instantiation of}}
+    bad<E>(); // expected-note 4{{instantiation of}}
+
+    ugly<B>(); // expected-note {{instantiation of}}
+    ugly<C>(); // ok
+    ugly<D>(); // expected-note {{instantiation of}}
+    ugly<E>(); // expected-note {{instantiation of}}
+#if __cplusplus <= 201402L
+    // expected-note@-4 {{instantiation of}}
+#else
+    (void)B{0.0}; // expected-error {{type 'double' cannot be narrowed}}
+#endif
+  }
+}
index f079b7e..7d1e746 100644 (file)
@@ -664,7 +664,7 @@ as the draft C++1z standard evolves.</p>
     <tr>
       <td>Direct-list-initialization of <tt>enum</tt>s</td>
       <td><a href="http://wg21.link/p0138r2">P0138R2</a></td>
-      <td class="none" align="center">No</td>
+      <td class="svn" align="center">SVN</td>
     </tr>
     <tr>
       <td>Hexadecimal floating-point literals</td>