diff options
author | Douglas Gregor <dgregor@apple.com> | 2009-01-30 23:27:23 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2009-01-30 23:27:23 +0000 |
commit | 734d9869efb5d126df53ba70a6060789887e0d68 (patch) | |
tree | a9032656d874ab03253c8ea9738c28d09b6adda9 | |
parent | ad53eff73224b29955da59f65e6b9eba1a5f6b76 (diff) |
Improve our handling of the second step in a user-defined conversion
sequence. Previously, we weren't permitting the second step to call
copy constructors, which left user-defined conversion sequences
surprisingly broken.
Now, we perform overload resolution among all of the constructors, but
only accept the result if it makes the conversion a standard
conversion. Note that this behavior is different from both GCC and EDG
(which don't agree with each other, either); I've submitted a core
issue on the matter.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@63450 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/Sema/Sema.h | 3 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 15 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.cpp | 48 | ||||
-rw-r--r-- | test/SemaCXX/dcl_init_aggr.cpp | 2 | ||||
-rw-r--r-- | test/SemaCXX/user-defined-conversions.cpp | 32 |
5 files changed, 83 insertions, 17 deletions
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 45f08bc8df..6108f9447f 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -433,7 +433,8 @@ public: bool IsQualificationConversion(QualType FromType, QualType ToType); bool IsUserDefinedConversion(Expr *From, QualType ToType, UserDefinedConversionSequence& User, - bool AllowExplicit = false); + bool AllowConversionFunctions, + bool AllowExplicit); ImplicitConversionSequence::CompareKind CompareImplicitConversionSequences(const ImplicitConversionSequence& ICS1, diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 24f753164d..f3bb9a5dfb 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1867,7 +1867,6 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, // the temporary or to a sub-object within the // temporary. // - // // The constructor that would be used to make the copy // shall be callable whether or not the copy is actually // done. @@ -1915,6 +1914,20 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, return true; } + // If at least one of the types is a class type, the types are not + // related, and we aren't allowed any user conversions, the + // reference binding fails. This case is important for breaking + // recursion, since TryImplicitConversion below will attempt to + // create a temporary through the use of a copy constructor. + if (SuppressUserConversions && RefRelationship == Ref_Incompatible && + (T1->isRecordType() || T2->isRecordType())) { + if (!ICS) + Diag(Init->getSourceRange().getBegin(), + diag::err_typecheck_convert_incompatible) + << DeclType << Init->getType() << "initializing" << Init->getSourceRange(); + return true; + } + // Actually try to convert the initializer to T1. if (ICS) { /// C++ [over.ics.ref]p2: diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 17ad387ac3..1fa703c775 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -366,13 +366,13 @@ Sema::IsOverload(FunctionDecl *New, Decl* OldD, ImplicitConversionSequence Sema::TryImplicitConversion(Expr* From, QualType ToType, bool SuppressUserConversions, - bool AllowExplict) + bool AllowExplicit) { ImplicitConversionSequence ICS; if (IsStandardConversion(From, ToType, ICS.Standard)) ICS.ConversionKind = ImplicitConversionSequence::StandardConversion; - else if (!SuppressUserConversions && - IsUserDefinedConversion(From, ToType, ICS.UserDefined, AllowExplict)) { + else if (IsUserDefinedConversion(From, ToType, ICS.UserDefined, + !SuppressUserConversions, AllowExplicit)) { ICS.ConversionKind = ImplicitConversionSequence::UserDefinedConversion; // C++ [over.ics.user]p4: // A conversion of an expression of class type to the same class @@ -396,6 +396,17 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType, ICS.Standard.Second = ICK_Derived_To_Base; } } + + // C++ [over.best.ics]p4: + // However, when considering the argument of a user-defined + // conversion function that is a candidate by 13.3.1.3 when + // invoked for the copying of the temporary in the second step + // of a class copy-initialization, or by 13.3.1.4, 13.3.1.5, or + // 13.3.1.6 in all cases, only standard conversion sequences and + // ellipsis conversion sequences are allowed. + if (SuppressUserConversions && + ICS.ConversionKind == ImplicitConversionSequence::UserDefinedConversion) + ICS.ConversionKind = ImplicitConversionSequence::BadConversion; } else ICS.ConversionKind = ImplicitConversionSequence::BadConversion; @@ -1188,17 +1199,23 @@ 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. AllowExplicit is true if the conversion should -/// consider C++0x "explicit" conversion functions as well as -/// non-explicit conversion functions (C++0x [class.conv.fct]p2). +/// 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. +/// +/// \param AllowConversionFunctions true if the conversion should +/// consider conversion functions at all. If false, only constructors +/// will be considered. +/// +/// \param AllowExplicit true if the conversion should consider C++0x +/// "explicit" conversion functions as well as non-explicit conversion +/// functions (C++0x [class.conv.fct]p2). bool Sema::IsUserDefinedConversion(Expr *From, QualType ToType, UserDefinedConversionSequence& User, + bool AllowConversionFunctions, bool AllowExplicit) { OverloadCandidateSet CandidateSet; @@ -1226,8 +1243,11 @@ bool Sema::IsUserDefinedConversion(Expr *From, QualType ToType, } } - if (const CXXRecordType *FromRecordType - = dyn_cast_or_null<CXXRecordType>(From->getType()->getAsRecordType())) { + if (!AllowConversionFunctions) { + // Don't allow any conversion functions to enter the overload set. + } else if (const CXXRecordType *FromRecordType + = dyn_cast_or_null<CXXRecordType>( + From->getType()->getAsRecordType())) { // Add all of the conversion functions as candidates. // FIXME: Look for conversions in base classes! CXXRecordDecl *FromRecordDecl = FromRecordType->getDecl(); diff --git a/test/SemaCXX/dcl_init_aggr.cpp b/test/SemaCXX/dcl_init_aggr.cpp index 4103227106..c2c9d67a4d 100644 --- a/test/SemaCXX/dcl_init_aggr.cpp +++ b/test/SemaCXX/dcl_init_aggr.cpp @@ -96,7 +96,7 @@ C2 c2; D2 d2; B2 b2 = { 4, a2, a2 }; B2 b2_2 = { 4, d2, 0 }; -// FIXME: B2 b2_3 = { c2, a2, a2 }; +B2 b2_3 = { c2, a2, a2 }; // C++ [dcl.init.aggr]p15: union u { int a; char* b; }; diff --git a/test/SemaCXX/user-defined-conversions.cpp b/test/SemaCXX/user-defined-conversions.cpp index 8292c7a009..e4b12fc3f7 100644 --- a/test/SemaCXX/user-defined-conversions.cpp +++ b/test/SemaCXX/user-defined-conversions.cpp @@ -35,3 +35,35 @@ void h(volatile A&); void h_test(C c) { h(c); } + +// Test conversion followed by copy-construction +struct FunkyDerived; + +struct Base { + Base(const FunkyDerived&); +}; + +struct Derived : Base { }; + +struct FunkyDerived : Base { }; + +struct ConvertibleToBase { + operator Base(); +}; + +struct ConvertibleToDerived { + operator Derived(); +}; + +struct ConvertibleToFunkyDerived { + operator FunkyDerived(); +}; + +void test_conversion(ConvertibleToBase ctb, ConvertibleToDerived ctd, + ConvertibleToFunkyDerived ctfd) { + Base b1 = ctb; + Base b2(ctb); + Base b3 = ctd; + Base b4(ctd); + Base b5 = ctfd; // expected-error{{cannot initialize 'b5' with an lvalue of type 'struct ConvertibleToFunkyDerived'}} +} |