From: Douglas Gregor Date: Fri, 24 Feb 2012 23:56:31 +0000 (+0000) Subject: Implement C++11 [over.match.copy]p1b2, which allows the use of X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ed878af7914df535b32d64f555fa118413186672;p=clang Implement C++11 [over.match.copy]p1b2, which allows the use of explicit conversion functions to initialize the argument to a copy/move constructor that itself is the subject of direct initialization. Since we don't have that much context in overload resolution, we end up threading more flags :(. Fixes / PR10456. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@151409 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index a36b5e6bd8..1c31c574b1 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -365,17 +365,18 @@ class InitializationKind { public: /// \brief The kind of initialization being performed. enum InitKind { - IK_Direct, ///< Direct initialization - IK_DirectList, ///< Direct list-initialization - IK_Copy, ///< Copy initialization - IK_Default, ///< Default initialization - IK_Value ///< Value initialization + IK_Direct, ///< Direct initialization + IK_DirectList, ///< Direct list-initialization + IK_Copy, ///< Copy initialization + IK_Default, ///< Default initialization + IK_Value ///< Value initialization }; private: /// \brief The context of the initialization. enum InitContext { IC_Normal, ///< Normal context + IC_ExplicitConvs, ///< Normal context, but allows explicit conversion funcs IC_Implicit, ///< Implicit context (value initialization) IC_StaticCast, ///< Static cast context IC_CStyleCast, ///< C-style cast context @@ -442,8 +443,11 @@ public: /// \brief Create a copy initialization. static InitializationKind CreateCopy(SourceLocation InitLoc, - SourceLocation EqualLoc) { - return InitializationKind(IK_Copy, IC_Normal, InitLoc, EqualLoc, EqualLoc); + SourceLocation EqualLoc, + bool AllowExplicitConvs = false) { + return InitializationKind(IK_Copy, + AllowExplicitConvs? IC_ExplicitConvs : IC_Normal, + InitLoc, EqualLoc, EqualLoc); } /// \brief Create a default initialization. @@ -511,6 +515,12 @@ public: /// constructors. bool AllowExplicit() const { return !isCopyInit(); } + /// \brief Retrieve whether this initialization allows the use of explicit + /// conversion functions. + bool allowExplicitConversionFunctions() const { + return !isCopyInit() || Context == IC_ExplicitConvs; + } + /// \brief Retrieve the source range containing the locations of the open /// and closing parentheses for value and direct initializations. SourceRange getParenRange() const { diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 8131c6372e..875b021f96 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1458,7 +1458,8 @@ public: ExprResult PerformCopyInitialization(const InitializedEntity &Entity, SourceLocation EqualLoc, ExprResult Init, - bool TopLevelOfInitList = false); + bool TopLevelOfInitList = false, + bool AllowExplicit = false); ExprResult PerformObjectArgumentInitialization(Expr *From, NestedNameSpecifier *Qualifier, NamedDecl *FoundDecl, @@ -1502,7 +1503,8 @@ public: Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions = false, - bool PartialOverloading = false); + bool PartialOverloading = false, + bool AllowExplicit = false); void AddFunctionCandidates(const UnresolvedSetImpl &Functions, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, @@ -3128,7 +3130,8 @@ public: bool CompleteConstructorCall(CXXConstructorDecl *Constructor, MultiExprArg ArgsPtr, SourceLocation Loc, - ASTOwningVector &ConvertedArgs); + ASTOwningVector &ConvertedArgs, + bool AllowExplicit = false); ParsedType getDestructorName(SourceLocation TildeLoc, IdentifierInfo &II, SourceLocation NameLoc, @@ -5911,7 +5914,8 @@ public: unsigned FirstProtoArg, Expr **Args, unsigned NumArgs, SmallVector &AllArgs, - VariadicCallType CallType = VariadicDoesNotApply); + VariadicCallType CallType = VariadicDoesNotApply, + bool AllowExplicit = false); // DefaultVariadicArgumentPromotion - Like DefaultArgumentPromotion, but // will warn if the resulting type is not a POD type. diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 6d2f3b252b..a2180be53c 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -9032,7 +9032,8 @@ bool Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor, MultiExprArg ArgsPtr, SourceLocation Loc, - ASTOwningVector &ConvertedArgs) { + ASTOwningVector &ConvertedArgs, + bool AllowExplicit) { // FIXME: This duplicates a lot of code from Sema::ConvertArgumentsForCall. unsigned NumArgs = ArgsPtr.size(); Expr **Args = (Expr **)ArgsPtr.get(); @@ -9053,7 +9054,7 @@ Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor, SmallVector AllArgs; bool Invalid = GatherArgumentsForCall(Loc, Constructor, Proto, 0, Args, NumArgs, AllArgs, - CallType); + CallType, AllowExplicit); ConvertedArgs.append(AllArgs.begin(), AllArgs.end()); DiagnoseSentinelCalls(Constructor, Loc, AllArgs.data(), AllArgs.size()); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index fcb2d410cb..2868b822e6 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -3248,7 +3248,8 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, unsigned FirstProtoArg, Expr **Args, unsigned NumArgs, SmallVector &AllArgs, - VariadicCallType CallType) { + VariadicCallType CallType, + bool AllowExplicit) { unsigned NumArgsInProto = Proto->getNumArgs(); unsigned NumArgsToCheck = NumArgs; bool Invalid = false; @@ -3288,7 +3289,9 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, Proto->isArgConsumed(i)); ExprResult ArgE = PerformCopyInitialization(Entity, SourceLocation(), - Owned(Arg)); + Owned(Arg), + /*TopLevelOfInitList=*/false, + AllowExplicit); if (ArgE.isInvalid()) return true; diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index fcc74e7d3f..df0a49b79e 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -2827,10 +2827,22 @@ ResolveConstructorOverload(Sema &S, SourceLocation DeclLoc, /*ExplicitArgs*/ 0, Args, NumArgs, CandidateSet, SuppressUserConversions); - else + else { + // C++ [over.match.copy]p1: + // - When initializing a temporary to be bound to the first parameter + // of a constructor that takes a reference to possibly cv-qualified + // T as its first argument, called with a single argument in the + // context of direct-initialization, explicit conversion functions + // are also considered. + bool AllowExplicitConv = AllowExplicit && !CopyInitializing && + NumArgs == 1 && + Constructor->isCopyOrMoveConstructor(); S.AddOverloadCandidate(Constructor, FoundDecl, Args, NumArgs, CandidateSet, - SuppressUserConversions); + SuppressUserConversions, + /*PartialOverloading=*/false, + /*AllowExplicit=*/AllowExplicitConv); + } } } @@ -3122,8 +3134,8 @@ static void TryListInitialization(Sema &S, static OverloadingResult TryRefInitWithConversionFunction(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, - Expr *Initializer, - bool AllowRValues, + Expr *Initializer, + bool AllowRValues, InitializationSequence &Sequence) { QualType DestType = Entity.getType(); QualType cv1T1 = DestType->getAs()->getPointeeType(); @@ -3151,7 +3163,8 @@ static OverloadingResult TryRefInitWithConversionFunction(Sema &S, // Determine whether we are allowed to call explicit constructors or // explicit conversion operators. bool AllowExplicit = Kind.AllowExplicit(); - + bool AllowExplicitConvs = Kind.allowExplicitConversionFunctions(); + const RecordType *T1RecordType = 0; if (AllowRValues && (T1RecordType = T1->getAs()) && !S.RequireCompleteType(Kind.getLocation(), T1, 0)) { @@ -3220,7 +3233,7 @@ static OverloadingResult TryRefInitWithConversionFunction(Sema &S, // FIXME: Do we need to make sure that we only consider conversion // candidates with reference-compatible results? That might be needed to // break recursion. - if ((AllowExplicit || !Conv->isExplicit()) && + if ((AllowExplicitConvs || !Conv->isExplicit()) && (AllowRValues || Conv->getConversionType()->isLValueReferenceType())){ if (ConvTemplate) S.AddTemplateConversionCandidate(ConvTemplate, I.getPair(), @@ -4636,10 +4649,21 @@ PerformConstructorInitialization(Sema &S, ExprResult CurInit = S.Owned((Expr *)0); + // C++ [over.match.copy]p1: + // - When initializing a temporary to be bound to the first parameter + // of a constructor that takes a reference to possibly cv-qualified + // T as its first argument, called with a single argument in the + // context of direct-initialization, explicit conversion functions + // are also considered. + bool AllowExplicitConv = Kind.AllowExplicit() && !Kind.isCopyInit() && + Args.size() == 1 && + Constructor->isCopyOrMoveConstructor(); + // Determine the arguments required to actually perform the constructor // call. if (S.CompleteConstructorCall(Constructor, move(Args), - Loc, ConstructorArgs)) + Loc, ConstructorArgs, + AllowExplicitConv)) return ExprError(); @@ -6097,7 +6121,8 @@ ExprResult Sema::PerformCopyInitialization(const InitializedEntity &Entity, SourceLocation EqualLoc, ExprResult Init, - bool TopLevelOfInitList) { + bool TopLevelOfInitList, + bool AllowExplicit) { if (Init.isInvalid()) return ExprError(); @@ -6108,7 +6133,8 @@ Sema::PerformCopyInitialization(const InitializedEntity &Entity, EqualLoc = InitE->getLocStart(); InitializationKind Kind = InitializationKind::CreateCopy(InitE->getLocStart(), - EqualLoc); + EqualLoc, + AllowExplicit); InitializationSequence Seq(*this, Entity, Kind, &InitE, 1); Init.release(); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 2b567b3984..c7f3394343 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -4213,7 +4213,8 @@ static ImplicitConversionSequence TryCopyInitialization(Sema &S, Expr *From, QualType ToType, bool SuppressUserConversions, bool InOverloadResolution, - bool AllowObjCWritebackConversion); + bool AllowObjCWritebackConversion, + bool AllowExplicit = false); /// TryListConversion - Try to copy-initialize a value of type ToType from the /// initializer list From. @@ -4413,7 +4414,8 @@ static ImplicitConversionSequence TryCopyInitialization(Sema &S, Expr *From, QualType ToType, bool SuppressUserConversions, bool InOverloadResolution, - bool AllowObjCWritebackConversion) { + bool AllowObjCWritebackConversion, + bool AllowExplicit) { if (InitListExpr *FromInitList = dyn_cast(From)) return TryListConversion(S, FromInitList, ToType, SuppressUserConversions, InOverloadResolution,AllowObjCWritebackConversion); @@ -4422,7 +4424,7 @@ TryCopyInitialization(Sema &S, Expr *From, QualType ToType, return TryReferenceInit(S, From, ToType, /*FIXME:*/From->getLocStart(), SuppressUserConversions, - /*AllowExplicit=*/false); + AllowExplicit); return TryImplicitConversion(S, From, ToType, SuppressUserConversions, @@ -5103,7 +5105,8 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions, - bool PartialOverloading) { + bool PartialOverloading, + bool AllowExplicit) { const FunctionProtoType* Proto = dyn_cast(Function->getType()->getAs()); assert(Proto && "Functions without a prototype cannot be overloaded"); @@ -5204,7 +5207,8 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, SuppressUserConversions, /*InOverloadResolution=*/true, /*AllowObjCWritebackConversion=*/ - getLangOptions().ObjCAutoRefCount); + getLangOptions().ObjCAutoRefCount, + AllowExplicit); if (Candidate.Conversions[ArgIdx].isBad()) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_bad_conversion; diff --git a/test/CXX/over/over.match/over.match.funcs/over.match.copy/p1.cpp b/test/CXX/over/over.match/over.match.funcs/over.match.copy/p1.cpp new file mode 100644 index 0000000000..8b8a1efd0e --- /dev/null +++ b/test/CXX/over/over.match/over.match.funcs/over.match.copy/p1.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s -verify + +namespace ExplicitConv { + struct X { }; // expected-note 2{{candidate constructor}} + + struct Y { + explicit operator X() const; + }; + + void test(const Y& y) { + X x(static_cast(y)); + X x2((X)y); + X x3 = y; // expected-error{{no viable conversion from 'const ExplicitConv::Y' to 'ExplicitConv::X'}} + } +} diff --git a/test/SemaCXX/explicit.cpp b/test/SemaCXX/explicit.cpp index 12f0ca2643..477463771e 100644 --- a/test/SemaCXX/explicit.cpp +++ b/test/SemaCXX/explicit.cpp @@ -43,16 +43,8 @@ namespace Conversion { class Y { }; // expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Conversion::Z' to 'const Conversion::Y &' for 1st argument}} \ expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y &&' for 1st argument}} \ expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Conversion::Z' to 'const Conversion::Y &' for 1st argument}} \ - expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y' for 1st argument}} \ - expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Conversion::Z' to 'const Conversion::Y' for 1st argument}} \ - expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y' for 1st argument}} \ - expected-note {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} \ - expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Conversion::Z' to 'const Conversion::Y' for 1st argument}} \ - expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y' for 1st argument}} \ - expected-note {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} \ - expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Conversion::Z' to 'const Conversion::Y' for 1st argument}} \ - expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y &&' for 1st argument}} \ - expected-note {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} + expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y &&' for 1st argument}} + struct Z { explicit operator Y() const; explicit operator int() const; @@ -61,10 +53,9 @@ namespace Conversion { Z z; // 13.3.1.4p1 & 8.5p16: Y y2 = z; // expected-error {{no viable conversion from 'Conversion::Z' to 'Conversion::Y'}} - // FIXME: These are well-formed per C++0x 13.3.1.4p1 (see DR899). - Y y3 = (Y)z; // expected-error {{no matching conversion for C-style cast from 'Conversion::Z' to 'Conversion::Y'}} - Y y4 = Y(z); // expected-error {{no matching conversion for functional-style cast from 'Conversion::Z' to 'Conversion::Y'}} - Y y5 = static_cast(z); // expected-error {{no matching conversion for static_cast from 'Conversion::Z' to 'Conversion::Y'}} + Y y3 = (Y)z; + Y y4 = Y(z); + Y y5 = static_cast(z); // 13.3.1.5p1 & 8.5p16: int i1 = (int)z; int i2 = int(z);