From ed638864d3e19559ca0d88523356a1f5bd141b99 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 28 Mar 2016 06:08:37 +0000 Subject: [PATCH] P0138R2: Allow direct-list-initialization of an enumeration from an integral value that can convert to the enum's underlying type. llvm-svn: 264564 --- clang/lib/Sema/SemaInit.cpp | 72 +++++++++--- clang/lib/Sema/SemaOverload.cpp | 7 ++ .../dcl.init/dcl.init.list/{p3-0x.cpp => p3.cpp} | 127 +++++++++++++++++++++ clang/www/cxx_status.html | 2 +- 4 files changed, 192 insertions(+), 16 deletions(-) rename clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/{p3-0x.cpp => p3.cpp} (53%) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index d308253..bd8522d 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -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(); + 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, diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index e9154bc..a0e189d 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -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()) + ToType = ET->getDecl()->getIntegerType(); + switch (Second) { // 'bool' is an integral type; dispatch to the right place to handle it. case ICK_Boolean_Conversion: diff --git a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3-0x.cpp b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3.cpp similarity index 53% rename from clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3-0x.cpp rename to clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3.cpp index d7ffd07..a02b7de 100644 --- a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3-0x.cpp +++ b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3.cpp @@ -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 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 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 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(); // expected-note 4{{instantiation of}} + good(); + good(); + good(); + good(); +#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(); // expected-note 4{{instantiation of}} + bad(); // expected-note 4{{instantiation of}} + bad(); // expected-note 4{{instantiation of}} + bad(); // expected-note 4{{instantiation of}} + bad(); // expected-note 4{{instantiation of}} + + ugly(); // expected-note {{instantiation of}} + ugly(); // ok + ugly(); // expected-note {{instantiation of}} + ugly(); // 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 + } +} diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index f079b7e..7d1e746 100644 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -664,7 +664,7 @@ as the draft C++1z standard evolves.

Direct-list-initialization of enums
P0138R2 - No + SVN Hexadecimal floating-point literals -- 2.7.4