[cfe-commits] r58513 - in /cfe/trunk: include/clang/AST/DeclCXX.h lib/AST/DeclCXX.cpp lib/Sema/Sema.h lib/Sema/SemaDeclCXX.cpp lib/Sema/SemaExprCXX.cpp lib/Sema/SemaOverload.cpp lib/Sema/SemaOverload.h test/SemaCXX/converting-constructor.cpp
Douglas Gregor
doug.gregor at gmail.com
Fri Oct 31 09:23:19 PDT 2008
Author: dgregor
Date: Fri Oct 31 11:23:19 2008
New Revision: 58513
URL: http://llvm.org/viewvc/llvm-project?rev=58513&view=rev
Log:
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)
Added:
cfe/trunk/test/SemaCXX/converting-constructor.cpp
Modified:
cfe/trunk/include/clang/AST/DeclCXX.h
cfe/trunk/lib/AST/DeclCXX.cpp
cfe/trunk/lib/Sema/Sema.h
cfe/trunk/lib/Sema/SemaDeclCXX.cpp
cfe/trunk/lib/Sema/SemaExprCXX.cpp
cfe/trunk/lib/Sema/SemaOverload.cpp
cfe/trunk/lib/Sema/SemaOverload.h
Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=58513&r1=58512&r2=58513&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Fri Oct 31 11:23:19 2008
@@ -414,13 +414,19 @@
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;
Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=58513&r1=58512&r2=58513&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Fri Oct 31 11:23:19 2008
@@ -92,6 +92,21 @@
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,
Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=58513&r1=58512&r2=58513&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Fri Oct 31 11:23:19 2008
@@ -371,12 +371,16 @@
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,
Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=58513&r1=58512&r2=58513&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Fri Oct 31 11:23:19 2008
@@ -575,6 +575,24 @@
/// @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;
}
Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=58513&r1=58512&r2=58513&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Fri Oct 31 11:23:19 2008
@@ -889,11 +889,11 @@
// 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;
Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=58513&r1=58512&r2=58513&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Fri Oct 31 11:23:19 2008
@@ -90,6 +90,17 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 @@
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 @@
!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 @@
(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 @@
// 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 @@
// 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 @@
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 @@
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;
Modified: cfe/trunk/lib/Sema/SemaOverload.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.h?rev=58513&r1=58512&r2=58513&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.h (original)
+++ cfe/trunk/lib/Sema/SemaOverload.h Fri Oct 31 11:23:19 2008
@@ -114,6 +114,7 @@
/// 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;
Added: cfe/trunk/test/SemaCXX/converting-constructor.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/converting-constructor.cpp?rev=58513&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/converting-constructor.cpp (added)
+++ cfe/trunk/test/SemaCXX/converting-constructor.cpp Fri Oct 31 11:23:19 2008
@@ -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}}
+}
+
More information about the cfe-commits
mailing list