Implement basic support for converting constructors in user-defined

conversions.

Notes:
  - Overload resolution for converting constructors need to prohibit
    user-defined conversions (hence, the test isn't -verify safe yet).
  - We still use hacks for conversions from a class type to itself. 
    This will be the case until we start implicitly declaring the appropriate
    special member functions. (That's next on my list)

llvm-svn: 58513
This commit is contained in:
Douglas Gregor 2008-10-31 16:23:19 +00:00
parent 71d7e9b667
commit 26bee0b326
8 changed files with 230 additions and 35 deletions

View File

@ -414,13 +414,19 @@ public:
return ImplicitlyDefined;
}
/// setImplicitlyDefined
/// setImplicitlyDefined - Set whether this constructor was
/// implicitly defined or not.
void setImplicitlyDefined(bool ID) {
assert(getBody() != 0 &&
"Can only set the implicit-definition flag once the constructor has been defined");
ImplicitlyDefined = ID;
}
/// isConvertingConstructor - Whether this constructor is a
/// converting constructor (C++ [class.conv.ctor]), which can be
/// used for (of course) user-defined conversions.
bool isConvertingConstructor() const;
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) {
return D->getKind() == CXXConstructor;

View File

@ -92,6 +92,21 @@ CXXConstructorDecl::Create(ASTContext &C, CXXRecordDecl *RD,
isImplicitlyDeclared);
}
bool CXXConstructorDecl::isConvertingConstructor() const {
// C++ [class.conv.ctor]p1:
// A constructor declared without the function-specifier explicit
// that can be called with a single parameter specifies a
// conversion from the type of its first parameter to the type of
// its class. Such a constructor is called a converting
// constructor.
if (isExplicit())
return false;
return (getNumParams() == 0 &&
getType()->getAsFunctionTypeProto()->isVariadic()) ||
(getNumParams() == 1) ||
(getNumParams() > 1 && getParamDecl(1)->getDefaultArg() != 0);
}
CXXClassVarDecl *CXXClassVarDecl::Create(ASTContext &C, CXXRecordDecl *RD,
SourceLocation L, IdentifierInfo *Id,

View File

@ -371,12 +371,16 @@ private:
bool IsOverload(FunctionDecl *New, Decl* OldD,
OverloadedFunctionDecl::function_iterator &MatchedDecl);
ImplicitConversionSequence TryImplicitConversion(Expr* From, QualType ToType);
bool IsStandardConversion(Expr *From, QualType ToType,
StandardConversionSequence& SCS);
bool IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType);
bool IsFloatingPointPromotion(QualType FromType, QualType ToType);
bool IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
QualType& ConvertedType);
bool CheckPointerConversion(Expr *From, QualType ToType);
bool IsQualificationConversion(QualType FromType, QualType ToType);
bool IsUserDefinedConversion(Expr *From, QualType ToType,
UserDefinedConversionSequence& User);
ImplicitConversionSequence::CompareKind
CompareImplicitConversionSequences(const ImplicitConversionSequence& ICS1,

View File

@ -575,6 +575,24 @@ void Sema::ActOnFinishCXXClassDef(DeclTy *D) {
/// @endcode
Sema::DeclTy *Sema::ActOnConstructorDeclarator(CXXConstructorDecl *ConDecl) {
assert(ConDecl && "Expected to receive a constructor declaration");
// Check default arguments on the constructor
CheckCXXDefaultArguments(ConDecl);
// FIXME: Make sure this constructor is an overload of the existing
// constructors and update the class to reflect the addition of this
// constructor (e.g., it now has a user-defined constructor, might
// have a user-declared copy constructor, etc.).
// Add this constructor to the set of constructors of the current
// class.
if (CXXRecordDecl *ClassDecl = dyn_cast_or_null<CXXRecordDecl>(CurContext)) {
ClassDecl->addConstructor(ConDecl);
} else {
assert(false && "Cannot add a constructor if we're not in the class!");
}
return (DeclTy *)ConDecl;
}

View File

@ -889,11 +889,11 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType)
// the constructor or conversion operator, and then cope with the
// standard conversions.
ImpCastExprToType(From, ToType);
break;
return false;
case ImplicitConversionSequence::EllipsisConversion:
assert(false && "Cannot perform an ellipsis conversion");
break;
return false;
case ImplicitConversionSequence::BadConversion:
return true;

View File

@ -90,6 +90,17 @@ const char* GetImplicitConversionName(ImplicitConversionKind Kind) {
return Name[Kind];
}
/// StandardConversionSequence - Set the standard conversion
/// sequence to the identity conversion.
void StandardConversionSequence::setAsIdentityConversion() {
First = ICK_Identity;
Second = ICK_Identity;
Third = ICK_Identity;
Deprecated = false;
ReferenceBinding = false;
DirectBinding = false;
}
/// getRank - Retrieve the rank of this standard conversion sequence
/// (C++ 13.3.3.1.1p3). The rank is the largest rank of each of the
/// implicit conversions.
@ -337,13 +348,51 @@ ImplicitConversionSequence
Sema::TryImplicitConversion(Expr* From, QualType ToType)
{
ImplicitConversionSequence ICS;
if (IsStandardConversion(From, ToType, ICS.Standard))
ICS.ConversionKind = ImplicitConversionSequence::StandardConversion;
else if (IsUserDefinedConversion(From, ToType, ICS.UserDefined))
ICS.ConversionKind = ImplicitConversionSequence::UserDefinedConversion;
else {
// FIXME: This is a hack to allow a class type S to implicitly
// convert to another class type S, at least until we have proper
// support for implicitly-declared copy constructors.
QualType FromType = Context.getCanonicalType(From->getType());
ToType = Context.getCanonicalType(ToType);
if (FromType.getUnqualifiedType() == ToType.getUnqualifiedType()) {
ICS.Standard.setAsIdentityConversion();
ICS.Standard.FromTypePtr = From->getType().getAsOpaquePtr();
ICS.Standard.ToTypePtr = ToType.getAsOpaquePtr();
ICS.ConversionKind = ImplicitConversionSequence::StandardConversion;
return ICS;
}
ICS.ConversionKind = ImplicitConversionSequence::BadConversion;
}
return ICS;
}
/// IsStandardConversion - Determines whether there is a standard
/// conversion sequence (C++ [conv], C++ [over.ics.scs]) from the
/// expression From to the type ToType. Standard conversion sequences
/// only consider non-class types; for conversions that involve class
/// types, use TryImplicitConversion. If a conversion exists, SCS will
/// contain the standard conversion sequence required to perform this
/// conversion and this routine will return true. Otherwise, this
/// routine will return false and the value of SCS is unspecified.
bool
Sema::IsStandardConversion(Expr* From, QualType ToType,
StandardConversionSequence &SCS)
{
QualType FromType = From->getType();
// Standard conversions (C++ 4)
ICS.ConversionKind = ImplicitConversionSequence::StandardConversion;
ICS.Standard.Deprecated = false;
ICS.Standard.FromTypePtr = FromType.getAsOpaquePtr();
// There are no standard conversions for class types, so abort early.
if (FromType->isRecordType() || ToType->isRecordType())
return false;
// Standard conversions (C++ [conv])
SCS.Deprecated = false;
SCS.FromTypePtr = FromType.getAsOpaquePtr();
// The first conversion can be an lvalue-to-rvalue conversion,
// array-to-pointer conversion, or function-to-pointer conversion
@ -355,17 +404,16 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType)
Expr::isLvalueResult argIsLvalue = From->isLvalue(Context);
if (argIsLvalue == Expr::LV_Valid &&
!FromType->isFunctionType() && !FromType->isArrayType()) {
ICS.Standard.First = ICK_Lvalue_To_Rvalue;
SCS.First = ICK_Lvalue_To_Rvalue;
// If T is a non-class type, the type of the rvalue is the
// cv-unqualified version of T. Otherwise, the type of the rvalue
// is T (C++ 4.1p1).
if (!FromType->isRecordType())
FromType = FromType.getUnqualifiedType();
FromType = FromType.getUnqualifiedType();
}
// Array-to-pointer conversion (C++ 4.2)
else if (FromType->isArrayType()) {
ICS.Standard.First = ICK_Array_To_Pointer;
SCS.First = ICK_Array_To_Pointer;
// An lvalue or rvalue of type "array of N T" or "array of unknown
// bound of T" can be converted to an rvalue of type "pointer to
@ -374,21 +422,21 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType)
if (IsStringLiteralToNonConstPointerConversion(From, ToType)) {
// This conversion is deprecated. (C++ D.4).
ICS.Standard.Deprecated = true;
SCS.Deprecated = true;
// For the purpose of ranking in overload resolution
// (13.3.3.1.1), this conversion is considered an
// array-to-pointer conversion followed by a qualification
// conversion (4.4). (C++ 4.2p2)
ICS.Standard.Second = ICK_Identity;
ICS.Standard.Third = ICK_Qualification;
ICS.Standard.ToTypePtr = ToType.getAsOpaquePtr();
return ICS;
SCS.Second = ICK_Identity;
SCS.Third = ICK_Qualification;
SCS.ToTypePtr = ToType.getAsOpaquePtr();
return true;
}
}
// Function-to-pointer conversion (C++ 4.3).
else if (FromType->isFunctionType() && argIsLvalue == Expr::LV_Valid) {
ICS.Standard.First = ICK_Function_To_Pointer;
SCS.First = ICK_Function_To_Pointer;
// An lvalue of function type T can be converted to an rvalue of
// type "pointer to T." The result is a pointer to the
@ -399,7 +447,7 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType)
}
// We don't require any conversions for the first step.
else {
ICS.Standard.First = ICK_Identity;
SCS.First = ICK_Identity;
}
// The second conversion can be an integral promotion, floating
@ -410,28 +458,28 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType)
Context.getCanonicalType(ToType).getUnqualifiedType()) {
// The unqualified versions of the types are the same: there's no
// conversion to do.
ICS.Standard.Second = ICK_Identity;
SCS.Second = ICK_Identity;
}
// Integral promotion (C++ 4.5).
else if (IsIntegralPromotion(From, FromType, ToType)) {
ICS.Standard.Second = ICK_Integral_Promotion;
SCS.Second = ICK_Integral_Promotion;
FromType = ToType.getUnqualifiedType();
}
// Floating point promotion (C++ 4.6).
else if (IsFloatingPointPromotion(FromType, ToType)) {
ICS.Standard.Second = ICK_Floating_Promotion;
SCS.Second = ICK_Floating_Promotion;
FromType = ToType.getUnqualifiedType();
}
// Integral conversions (C++ 4.7).
// FIXME: isIntegralType shouldn't be true for enums in C++.
else if ((FromType->isIntegralType() || FromType->isEnumeralType()) &&
(ToType->isIntegralType() && !ToType->isEnumeralType())) {
ICS.Standard.Second = ICK_Integral_Conversion;
SCS.Second = ICK_Integral_Conversion;
FromType = ToType.getUnqualifiedType();
}
// Floating point conversions (C++ 4.8).
else if (FromType->isFloatingType() && ToType->isFloatingType()) {
ICS.Standard.Second = ICK_Floating_Conversion;
SCS.Second = ICK_Floating_Conversion;
FromType = ToType.getUnqualifiedType();
}
// Floating-integral conversions (C++ 4.9).
@ -441,12 +489,12 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType)
!ToType->isEnumeralType()) ||
((FromType->isIntegralType() || FromType->isEnumeralType()) &&
ToType->isFloatingType())) {
ICS.Standard.Second = ICK_Floating_Integral;
SCS.Second = ICK_Floating_Integral;
FromType = ToType.getUnqualifiedType();
}
// Pointer conversions (C++ 4.10).
else if (IsPointerConversion(From, FromType, ToType, FromType)) {
ICS.Standard.Second = ICK_Pointer_Conversion;
SCS.Second = ICK_Pointer_Conversion;
}
// FIXME: Pointer to member conversions (4.11).
// Boolean conversions (C++ 4.12).
@ -455,24 +503,29 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType)
(FromType->isArithmeticType() ||
FromType->isEnumeralType() ||
FromType->isPointerType())) {
ICS.Standard.Second = ICK_Boolean_Conversion;
SCS.Second = ICK_Boolean_Conversion;
FromType = Context.BoolTy;
} else {
// No second conversion required.
ICS.Standard.Second = ICK_Identity;
SCS.Second = ICK_Identity;
}
QualType CanonFrom;
QualType CanonTo;
// The third conversion can be a qualification conversion (C++ 4p1).
if (IsQualificationConversion(FromType, ToType)) {
ICS.Standard.Third = ICK_Qualification;
SCS.Third = ICK_Qualification;
FromType = ToType;
CanonFrom = Context.getCanonicalType(FromType);
CanonTo = Context.getCanonicalType(ToType);
} else {
// No conversion required
ICS.Standard.Third = ICK_Identity;
SCS.Third = ICK_Identity;
// C++ [over.best.ics]p6:
// [...] Any difference in top-level cv-qualification is
// subsumed by the initialization itself and does not constitute
// a conversion. [...]
// C++ [dcl.init]p14 last bullet:
// [ Note: an expression of type "cv1 T" can initialize an object
@ -482,8 +535,7 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType)
// FIXME: Where is the normative text?
CanonFrom = Context.getCanonicalType(FromType);
CanonTo = Context.getCanonicalType(ToType);
if (!FromType->isRecordType() &&
CanonFrom.getUnqualifiedType() == CanonTo.getUnqualifiedType() &&
if (CanonFrom.getUnqualifiedType() == CanonTo.getUnqualifiedType() &&
CanonFrom.getCVRQualifiers() != CanonTo.getCVRQualifiers()) {
FromType = ToType;
CanonFrom = CanonTo;
@ -493,10 +545,10 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType)
// If we have not converted the argument type to the parameter type,
// this is a bad conversion sequence.
if (CanonFrom != CanonTo)
ICS.ConversionKind = ImplicitConversionSequence::BadConversion;
return false;
ICS.Standard.ToTypePtr = FromType.getAsOpaquePtr();
return ICS;
SCS.ToTypePtr = FromType.getAsOpaquePtr();
return true;
}
/// IsIntegralPromotion - Determines whether the conversion from the
@ -791,6 +843,82 @@ Sema::IsQualificationConversion(QualType FromType, QualType ToType)
FromType.getUnqualifiedType() == ToType.getUnqualifiedType();
}
/// IsUserDefinedConversion - Determines whether there is a
/// user-defined conversion sequence (C++ [over.ics.user]) that
/// converts expression From to the type ToType. If such a conversion
/// exists, User will contain the user-defined conversion sequence
/// that performs such a conversion and this routine will return
/// true. Otherwise, this routine returns false and User is
/// unspecified.
bool Sema::IsUserDefinedConversion(Expr *From, QualType ToType,
UserDefinedConversionSequence& User)
{
OverloadCandidateSet CandidateSet;
if (const CXXRecordType *ToRecordType
= dyn_cast_or_null<CXXRecordType>(ToType->getAsRecordType())) {
// C++ [over.match.ctor]p1:
// When objects of class type are direct-initialized (8.5), or
// copy-initialized from an expression of the same or a
// derived class type (8.5), overload resolution selects the
// constructor. [...] For copy-initialization, the candidate
// functions are all the converting constructors (12.3.1) of
// that class. The argument list is the expression-list within
// the parentheses of the initializer.
CXXRecordDecl *ToRecordDecl = ToRecordType->getDecl();
const OverloadedFunctionDecl *Constructors = ToRecordDecl->getConstructors();
for (OverloadedFunctionDecl::function_const_iterator func
= Constructors->function_begin();
func != Constructors->function_end(); ++func) {
CXXConstructorDecl *Constructor = cast<CXXConstructorDecl>(*func);
if (Constructor->isConvertingConstructor())
// FIXME: Suppress user-defined conversions in here!
AddOverloadCandidate(Constructor, &From, 1, CandidateSet);
}
}
// FIXME: Implement support for user-defined conversion operators.
OverloadCandidateSet::iterator Best;
switch (BestViableFunction(CandidateSet, Best)) {
case OR_Success:
// Record the standard conversion we used and the conversion function.
// FIXME: Handle user-defined conversion operators.
if (CXXConstructorDecl *Constructor
= dyn_cast<CXXConstructorDecl>(Best->Function)) {
// C++ [over.ics.user]p1:
// If the user-defined conversion is specified by a
// constructor (12.3.1), the initial standard conversion
// sequence converts the source type to the type required by
// the argument of the constructor.
//
// FIXME: What about ellipsis conversions?
QualType ThisType = Constructor->getThisType(Context);
User.Before = Best->Conversions[0].Standard;
User.ConversionFunction = Constructor;
User.After.setAsIdentityConversion();
User.After.FromTypePtr
= ThisType->getAsPointerType()->getPointeeType().getAsOpaquePtr();
User.After.ToTypePtr = ToType.getAsOpaquePtr();
return true;
} else {
assert(false &&
"Cannot perform user-defined conversion via a conversion operator");
return false;
}
case OR_No_Viable_Function:
// No conversion here! We're done.
return false;
case OR_Ambiguous:
// FIXME: See C++ [over.best.ics]p10 for the handling of
// ambiguous conversion sequences.
return false;
}
return false;
}
/// CompareImplicitConversionSequences - Compare two implicit
/// conversion sequences to determine whether one is better than the
/// other or if they are indistinguishable (C++ 13.3.3.2).
@ -1155,7 +1283,7 @@ Sema::CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1,
ImplicitConversionSequence
Sema::TryCopyInitialization(Expr *From, QualType ToType) {
if (!getLangOptions().CPlusPlus) {
// In C, argument passing is the same as performing an assignment.
// In C, copy initialization is the same as performing an assignment.
AssignConvertType ConvTy =
CheckSingleAssignmentConstraints(ToType, From);
ImplicitConversionSequence ICS;

View File

@ -114,6 +114,7 @@ namespace clang {
/// is an opaque pointer that can be translated into a QualType.
void *ToTypePtr;
void setAsIdentityConversion();
ImplicitConversionRank getRank() const;
bool isPointerConversionToBool() const;
bool isPointerConversionToVoidPointer(ASTContext& Context) const;

View File

@ -0,0 +1,23 @@
// RUN: clang -fsyntax-only %s
class Z { };
class Y {
public:
Y(const Z&);
};
class X {
public:
X(int);
X(const Y&);
};
void f(X);
void g(short s, Y y, Z z) {
f(s);
f(1.0f);
f(y);
f(z); // expected-error{{incompatible}}
}