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 <rdar://problem/10903741> / PR10456. 

llvm-svn: 151409
This commit is contained in:
Douglas Gregor 2012-02-24 23:56:31 +00:00
parent 41bd8ac206
commit 6073dcab38
8 changed files with 97 additions and 43 deletions

View File

@ -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 {

View File

@ -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<Expr*> &ConvertedArgs);
ASTOwningVector<Expr*> &ConvertedArgs,
bool AllowExplicit = false);
ParsedType getDestructorName(SourceLocation TildeLoc,
IdentifierInfo &II, SourceLocation NameLoc,
@ -5911,7 +5914,8 @@ public:
unsigned FirstProtoArg,
Expr **Args, unsigned NumArgs,
SmallVector<Expr *, 8> &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.

View File

@ -9032,7 +9032,8 @@ bool
Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor,
MultiExprArg ArgsPtr,
SourceLocation Loc,
ASTOwningVector<Expr*> &ConvertedArgs) {
ASTOwningVector<Expr*> &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<Expr *, 8> 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());

View File

@ -3248,7 +3248,8 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc,
unsigned FirstProtoArg,
Expr **Args, unsigned NumArgs,
SmallVector<Expr *, 8> &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;

View File

@ -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<ReferenceType>()->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<RecordType>()) &&
!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();

View File

@ -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<InitListExpr>(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<FunctionProtoType>(Function->getType()->getAs<FunctionType>());
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;

View File

@ -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<X>(y));
X x2((X)y);
X x3 = y; // expected-error{{no viable conversion from 'const ExplicitConv::Y' to 'ExplicitConv::X'}}
}
}

View File

@ -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<Y>(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<Y>(z);
// 13.3.1.5p1 & 8.5p16:
int i1 = (int)z;
int i2 = int(z);