From b390e49da4f140fab12732a1c99f4074f5f351a2 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sat, 21 Sep 2013 21:55:46 +0000 Subject: [PATCH] PR17295: Do not allow explicit conversion functions to be used in cases where an additional conversion (other than a qualification conversion) would be required after the explicit conversion. Conversely, do allow explicit conversion functions to be used when initializing a temporary for a reference binding in direct-list-initialization. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@191150 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Initialization.h | 4 ++ lib/Sema/SemaInit.cpp | 35 ++++++++++ lib/Sema/SemaOverload.cpp | 11 ++++ lib/Sema/SemaTemplateInstantiateDecl.cpp | 4 +- test/SemaCXX/explicit.cpp | 81 ++++++++++++++++++++++-- 5 files changed, 127 insertions(+), 8 deletions(-) diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index 86b9029862..7681fcec0e 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -853,6 +853,10 @@ public: const InitializationKind &Kind, MultiExprArg Args, bool InInitList = false); + void InitializeFrom(Sema &S, const InitializedEntity &Entity, + const InitializationKind &Kind, MultiExprArg Args, + bool InInitList); + ~InitializationSequence(); /// \brief Perform the actual initialization of the given entity based on diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 1c33c329c7..afd9393123 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -3346,6 +3346,33 @@ static void TryListInitialization(Sema &S, return; } } + if (S.getLangOpts().CPlusPlus && !DestType->isAggregateType() && + InitList->getNumInits() == 1 && + InitList->getInit(0)->getType()->isRecordType()) { + // - 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; if a narrowing conversion is required + // to convert the element to T, the program is ill-formed. + // + // Per core-24034, this is direct-initialization if we were performing + // direct-list-initialization and copy-initialization otherwise. + // We can't use InitListChecker for this, because it always performs + // 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); + if (Sequence) + Sequence.RewrapReferenceInitList(Entity.getType(), InitList); + return; + } InitListChecker CheckInitList(S, Entity, InitList, DestType, /*VerifyOnly=*/true); @@ -4366,6 +4393,14 @@ InitializationSequence::InitializationSequence(Sema &S, MultiExprArg Args, bool TopLevelOfInitList) : FailedCandidateSet(Kind.getLocation()) { + InitializeFrom(S, Entity, Kind, Args, TopLevelOfInitList); +} + +void InitializationSequence::InitializeFrom(Sema &S, + const InitializedEntity &Entity, + const InitializationKind &Kind, + MultiExprArg Args, + bool TopLevelOfInitList) { ASTContext &Context = S.Context; // Eliminate non-overload placeholder types in the arguments. We diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 800f983249..df18752bfa 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -5829,6 +5829,17 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion, ConvType = Conversion->getConversionType().getNonReferenceType(); } + // Per C++ [over.match.conv]p1, [over.match.ref]p1, an explicit conversion + // operator is only a candidate if its return type is the target type or + // can be converted to the target type with a qualification conversion. + bool ObjCLifetimeConversion; + QualType ToNonRefType = ToType.getNonReferenceType(); + if (Conversion->isExplicit() && + !Context.hasSameUnqualifiedType(ConvType, ToNonRefType) && + !IsQualificationConversion(ConvType, ToNonRefType, /*CStyle*/false, + ObjCLifetimeConversion)) + return; + // Overload resolution is always an unevaluated context. EnterExpressionEvaluationContext Unevaluated(*this, Sema::Unevaluated); diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index e03e8c55c9..d712cec220 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3372,7 +3372,9 @@ void Sema::BuildVariableInstantiation( OldVar->isPreviousDeclInSameBlockScope()); NewVar->setAccess(OldVar->getAccess()); - if (!OldVar->isStaticDataMember()) { + // For local variables, inherit the 'used' and 'referenced' flags from the + // primary template. + if (OldVar->getLexicalDeclContext()->isFunctionOrMethod()) { NewVar->setIsUsed(OldVar->isUsed(false)); NewVar->setReferenced(OldVar->isReferenced()); } diff --git a/test/SemaCXX/explicit.cpp b/test/SemaCXX/explicit.cpp index 905cbdcd0c..e47f132fc6 100644 --- a/test/SemaCXX/explicit.cpp +++ b/test/SemaCXX/explicit.cpp @@ -4,11 +4,11 @@ struct A { A(int); }; -struct B { +struct B { // expected-note+ {{candidate}} explicit B(int); }; -B::B(int) { } +B::B(int) { } // expected-note+ {{here}} struct C { void f(const A&); @@ -18,6 +18,22 @@ struct C { void f(C c) { c.f(10); } + +A a0 = 0; +A a1(0); +A &&a2 = 0; +A &&a3(0); +A a4{0}; +A &&a5 = {0}; +A &&a6{0}; + +B b0 = 0; // expected-error {{no viable conversion}} +B b1(0); +B &&b2 = 0; // expected-error {{could not bind}} +B &&b3(0); // expected-error {{could not bind}} +B b4{0}; +B &&b5 = {0}; // expected-error {{chosen constructor is explicit}} +B &&b6{0}; } namespace Conversion { @@ -40,12 +56,11 @@ namespace Conversion { void testExplicit() { // Taken from 12.3.2p2 - class Y { }; // expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Z' to 'const Y &' for 1st argument}} \ - expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Z' to 'Y &&' for 1st argument}} \ - expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Z' to 'const Y &' for 1st argument}} \ - expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Z' to 'Y &&' for 1st argument}} + class X { X(); }; // expected-note+ {{candidate constructor}} + class Y { }; // expected-note+ {{candidate constructor (the implicit}} struct Z { + explicit operator X() const; explicit operator Y() const; explicit operator int() const; }; @@ -53,6 +68,7 @@ namespace Conversion { Z z; // 13.3.1.4p1 & 8.5p16: Y y2 = z; // expected-error {{no viable conversion from 'Z' to 'Y'}} + Y y2b(z); Y y3 = (Y)z; Y y4 = Y(z); Y y5 = static_cast(z); @@ -63,7 +79,24 @@ namespace Conversion { int i4(z); // 13.3.1.6p1 & 8.5.3p5: const Y& y6 = z; // expected-error {{no viable conversion from 'Z' to 'const Y'}} - const int& y7(z); + const int& y7 = z; // expected-error {{no viable conversion from 'Z' to 'const int'}} + const Y& y8(z); + const int& y9(z); + + // Y is an aggregate, so aggregate-initialization is performed and the + // conversion function is not considered. + const Y y10{z}; // expected-error {{excess elements}} + const Y& y11{z}; // expected-error {{no viable conversion from 'Z' to 'const Y'}} + const int& y12{z}; + + // X is not an aggregate, so constructors are considered. + // However, by 13.3.3.1/4, only standard conversion sequences and + // ellipsis conversion sequences are considered here, so this is not + // allowed. + // FIXME: It's not really clear that this is a sensible restriction for this + // case. g++ allows this, EDG does not. + const X x1{z}; // expected-error {{no matching constructor}} + const X& x2{z}; // expected-error {{no matching constructor}} } void testBool() { @@ -119,6 +152,40 @@ namespace Conversion { // 6.5.3: for (;b;) {} for (;n;) {} + + // 13.3.1.5p1: + bool direct1(b); + bool direct2(n); + int direct3(b); + int direct4(n); // expected-error {{no viable conversion}} + const bool &direct5(b); + const bool &direct6(n); + const int &direct7(b); + const int &direct8(n); // expected-error {{no viable conversion}} + bool directList1{b}; + bool directList2{n}; + int directList3{b}; + int directList4{n}; // expected-error {{no viable conversion}} + const bool &directList5{b}; + const bool &directList6{n}; + const int &directList7{b}; + const int &directList8{n}; // expected-error {{no viable conversion}} + bool copy1 = b; + bool copy2 = n; // expected-error {{no viable conversion}} + int copy3 = b; + int copy4 = n; // expected-error {{no viable conversion}} + const bool ©5 = b; + const bool ©6 = n; // expected-error {{no viable conversion}} + const int ©7 = b; + const int ©8 = n; // expected-error {{no viable conversion}} + bool copyList1 = {b}; + bool copyList2 = {n}; // expected-error {{no viable conversion}} + int copyList3 = {b}; + int copyList4 = {n}; // expected-error {{no viable conversion}} + const bool ©List5 = {b}; + const bool ©List6 = {n}; // expected-error {{no viable conversion}} + const int ©List7 = {b}; + const int ©List8 = {n}; // expected-error {{no viable conversion}} } void testNew() -- 2.40.0