From 225c41eb3e960fd2e1d1b547f0f19a278d608bc5 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 3 Nov 2008 19:09:14 +0000 Subject: [PATCH] Standard conversion sequences now have a CopyConstructor field, to cope with the case where a user-defined conversion is actually a copy construction, and therefore can be compared against other standard conversion sequences. While I called this a hack before, now I'm convinced that it's the right way to go. Compare overloads based on derived-to-base conversions that invoke copy constructors. Suppress user-defined conversions when attempting to call a user-defined conversion. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58629 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/Sema.h | 17 +++-- lib/Sema/SemaDeclCXX.cpp | 8 ++- lib/Sema/SemaExprCXX.cpp | 14 ++-- lib/Sema/SemaOverload.cpp | 91 +++++++++++++++++-------- lib/Sema/SemaOverload.h | 8 +++ test/SemaCXX/converting-constructor.cpp | 4 +- test/SemaCXX/overload-call-copycon.cpp | 10 +++ 7 files changed, 106 insertions(+), 46 deletions(-) diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index fa87479df7..1b08a45fe8 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -370,7 +370,9 @@ private: /// C++ Overloading. bool IsOverload(FunctionDecl *New, Decl* OldD, OverloadedFunctionDecl::function_iterator &MatchedDecl); - ImplicitConversionSequence TryImplicitConversion(Expr* From, QualType ToType); + ImplicitConversionSequence + TryImplicitConversion(Expr* From, QualType ToType, + bool SuppressUserConversions = false); bool IsStandardConversion(Expr *From, QualType ToType, StandardConversionSequence& SCS); bool IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType); @@ -398,7 +400,9 @@ private: CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1, const StandardConversionSequence& SCS2); - ImplicitConversionSequence TryCopyInitialization(Expr* From, QualType ToType); + ImplicitConversionSequence + TryCopyInitialization(Expr* From, QualType ToType, + bool SuppressUserConversions = false); bool PerformCopyInitialization(Expr *&From, QualType ToType, const char *Flavor); @@ -412,10 +416,12 @@ private: void AddOverloadCandidate(FunctionDecl *Function, Expr **Args, unsigned NumArgs, - OverloadCandidateSet& CandidateSet); + OverloadCandidateSet& CandidateSet, + bool SuppressUserConversions = false); void AddOverloadCandidates(OverloadedFunctionDecl *Ovl, Expr **Args, unsigned NumArgs, - OverloadCandidateSet& CandidateSet); + OverloadCandidateSet& CandidateSet, + bool SuppressUserConversions = false); bool isBetterOverloadCandidate(const OverloadCandidate& Cand1, const OverloadCandidate& Cand2); OverloadingResult BestViableFunction(OverloadCandidateSet& CandidateSet, @@ -1165,7 +1171,8 @@ private: bool& DerivedToBase); bool CheckReferenceInit(Expr *&simpleInit_or_initList, QualType &declType, - ImplicitConversionSequence *ICS = 0); + ImplicitConversionSequence *ICS = 0, + bool SuppressUserConversions = false); /// CheckCastTypes - Check type constraints for casting between types. bool CheckCastTypes(SourceRange TyRange, QualType CastTy, Expr *&CastExpr); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 703522321a..1100e700c9 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -931,9 +931,13 @@ Sema::CompareReferenceRelationship(QualType T1, QualType T2, /// errors are found. Either way, a return value of true indicates /// that there was a failure, a return value of false indicates that /// the reference initialization succeeded. +/// +/// When @p SuppressUserConversions, user-defined conversions are +/// suppressed. bool Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, - ImplicitConversionSequence *ICS) { + ImplicitConversionSequence *ICS, + bool SuppressUserConversions) { assert(DeclType->isReferenceType() && "Reference init needs a reference"); QualType T1 = DeclType->getAsReferenceType()->getPointeeType(); @@ -1114,7 +1118,7 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, /// the argument expression. Any difference in top-level /// cv-qualification is subsumed by the initialization itself /// and does not constitute a conversion. - *ICS = TryImplicitConversion(Init, T1); + *ICS = TryImplicitConversion(Init, T1, SuppressUserConversions); return ICS->ConversionKind == ImplicitConversionSequence::BadConversion; } else { return PerformImplicitConversion(Init, T1); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 99b0829593..fde28529b7 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -929,6 +929,13 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, // anything here. QualType FromType = From->getType(); + if (SCS.CopyConstructor) { + // FIXME: Create a temporary object by calling the copy + // constructor. + ImpCastExprToType(From, ToType); + return false; + } + // Perform the first implicit conversion. switch (SCS.First) { case ICK_Identity: @@ -982,13 +989,6 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, ImpCastExprToType(From, FromType); break; - case ICK_Derived_To_Base: - // FIXME: This should never happen. It's a consequence of - // pretending that a user-defined conversion via copy constructor - // is actually a standard conversion. - ImpCastExprToType(From, ToType); - break; - default: assert(false && "Improper second standard conversion"); break; diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index b20c026f71..1c2f20b26b 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -99,6 +99,7 @@ void StandardConversionSequence::setAsIdentityConversion() { Deprecated = false; ReferenceBinding = false; DirectBinding = false; + CopyConstructor = 0; } /// getRank - Retrieve the rank of this standard conversion sequence @@ -174,6 +175,14 @@ void StandardConversionSequence::DebugPrint() const { fprintf(stderr, " -> "); } fprintf(stderr, "%s", GetImplicitConversionName(Second)); + + if (CopyConstructor) { + fprintf(stderr, " (by copy constructor)"); + } else if (DirectBinding) { + fprintf(stderr, " (direct reference binding)"); + } else if (ReferenceBinding) { + fprintf(stderr, " (reference binding)"); + } PrintedSomething = true; } @@ -344,13 +353,18 @@ Sema::IsOverload(FunctionDecl *New, Decl* OldD, /// it will not produce any diagnostics if no conversion is available, /// but will instead return an implicit conversion sequence of kind /// "BadConversion". +/// +/// If @p SuppressUserConversions, then user-defined conversions are +/// not permitted. ImplicitConversionSequence -Sema::TryImplicitConversion(Expr* From, QualType ToType) +Sema::TryImplicitConversion(Expr* From, QualType ToType, + bool SuppressUserConversions) { ImplicitConversionSequence ICS; if (IsStandardConversion(From, ToType, ICS.Standard)) ICS.ConversionKind = ImplicitConversionSequence::StandardConversion; - else if (IsUserDefinedConversion(From, ToType, ICS.UserDefined)) { + else if (!SuppressUserConversions && + IsUserDefinedConversion(From, ToType, ICS.UserDefined)) { ICS.ConversionKind = ImplicitConversionSequence::UserDefinedConversion; // C++ [over.ics.user]p4: // A conversion of an expression of class type to the same class @@ -362,15 +376,13 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType) if (CXXConstructorDecl *Constructor = dyn_cast(ICS.UserDefined.ConversionFunction)) { if (Constructor->isCopyConstructor(Context)) { - // FIXME: This is a temporary hack to give copy-constructor - // calls the appropriate rank (Exact Match or Conversion) by - // making them into standard conversions. To really fix this, we - // need to tweak the rank-checking logic to deal with ranking - // different kinds of user conversions. + // Turn this into a "standard" conversion sequence, so that it + // gets ranked with standard conversion sequences. ICS.ConversionKind = ImplicitConversionSequence::StandardConversion; ICS.Standard.setAsIdentityConversion(); ICS.Standard.FromTypePtr = From->getType().getAsOpaquePtr(); ICS.Standard.ToTypePtr = ToType.getAsOpaquePtr(); + ICS.Standard.CopyConstructor = Constructor; if (IsDerivedFrom(From->getType().getUnqualifiedType(), ToType.getUnqualifiedType())) ICS.Standard.Second = ICK_Derived_To_Base; @@ -403,6 +415,7 @@ Sema::IsStandardConversion(Expr* From, QualType ToType, // Standard conversions (C++ [conv]) SCS.Deprecated = false; SCS.FromTypePtr = FromType.getAsOpaquePtr(); + SCS.CopyConstructor = 0; // The first conversion can be an lvalue-to-rvalue conversion, // array-to-pointer conversion, or function-to-pointer conversion @@ -536,13 +549,6 @@ Sema::IsStandardConversion(Expr* From, QualType ToType, // [...] 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 - // of type “cv2 T” independently of the cv-qualifiers cv1 and - // cv2. -- end note] - // - // FIXME: Where is the normative text? CanonFrom = Context.getCanonicalType(FromType); CanonTo = Context.getCanonicalType(ToType); if (CanonFrom.getUnqualifiedType() == CanonTo.getUnqualifiedType() && @@ -881,8 +887,8 @@ bool Sema::IsUserDefinedConversion(Expr *From, QualType ToType, func != Constructors->function_end(); ++func) { CXXConstructorDecl *Constructor = cast(*func); if (Constructor->isConvertingConstructor()) - // FIXME: Suppress user-defined conversions in here! - AddOverloadCandidate(Constructor, &From, 1, CandidateSet); + AddOverloadCandidate(Constructor, &From, 1, CandidateSet, + /*SuppressUserConversions=*/true); } } @@ -1259,9 +1265,9 @@ Sema::CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1, return ImplicitConversionSequence::Worse; } - // -- binding of an expression of type B to a reference of type - // A& is better than binding an expression of type C to a - // reference of type A&, + // -- binding of an expression of type B to a reference of type + // A& is better than binding an expression of type C to a + // reference of type A&, if (FromType1.getUnqualifiedType() != FromType2.getUnqualifiedType() && ToType1.getUnqualifiedType() == ToType2.getUnqualifiedType()) { if (IsDerivedFrom(FromType2, FromType1)) @@ -1278,9 +1284,26 @@ Sema::CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1, // FIXME: conversion of B::* to C::* is better than conversion of // A::* to C::*, and - // FIXME: conversion of C to B is better than conversion of C to A, + if (SCS1.CopyConstructor && SCS2.CopyConstructor && + SCS1.Second == ICK_Derived_To_Base) { + // -- conversion of C to B is better than conversion of C to A, + if (FromType1.getUnqualifiedType() == FromType2.getUnqualifiedType() && + ToType1.getUnqualifiedType() != ToType2.getUnqualifiedType()) { + if (IsDerivedFrom(ToType1, ToType2)) + return ImplicitConversionSequence::Better; + else if (IsDerivedFrom(ToType2, ToType1)) + return ImplicitConversionSequence::Worse; + } - // FIXME: conversion of B to A is better than conversion of C to A. + // -- conversion of B to A is better than conversion of C to A. + if (FromType1.getUnqualifiedType() != FromType2.getUnqualifiedType() && + ToType1.getUnqualifiedType() == ToType2.getUnqualifiedType()) { + if (IsDerivedFrom(FromType2, FromType1)) + return ImplicitConversionSequence::Better; + else if (IsDerivedFrom(FromType1, FromType2)) + return ImplicitConversionSequence::Worse; + } + } return ImplicitConversionSequence::Indistinguishable; } @@ -1289,9 +1312,11 @@ Sema::CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1, /// ToType from the expression From. Return the implicit conversion /// sequence required to pass this argument, which may be a bad /// conversion sequence (meaning that the argument cannot be passed to -/// a parameter of this type). This is user for argument passing, +/// a parameter of this type). If @p SuppressUserConversions, then we +/// do not permit any user-defined conversion sequences. ImplicitConversionSequence -Sema::TryCopyInitialization(Expr *From, QualType ToType) { +Sema::TryCopyInitialization(Expr *From, QualType ToType, + bool SuppressUserConversions) { if (!getLangOptions().CPlusPlus) { // In C, copy initialization is the same as performing an assignment. AssignConvertType ConvTy = @@ -1305,10 +1330,10 @@ Sema::TryCopyInitialization(Expr *From, QualType ToType) { return ICS; } else if (ToType->isReferenceType()) { ImplicitConversionSequence ICS; - CheckReferenceInit(From, ToType, &ICS); + CheckReferenceInit(From, ToType, &ICS, SuppressUserConversions); return ICS; } else { - return TryImplicitConversion(From, ToType); + return TryImplicitConversion(From, ToType, SuppressUserConversions); } } @@ -1340,11 +1365,14 @@ bool Sema::PerformCopyInitialization(Expr *&From, QualType ToType, } /// AddOverloadCandidate - Adds the given function to the set of -/// candidate functions, using the given function call arguments. +/// candidate functions, using the given function call arguments. If +/// @p SuppressUserConversions, then don't allow user-defined +/// conversions via constructors or conversion operators. void Sema::AddOverloadCandidate(FunctionDecl *Function, Expr **Args, unsigned NumArgs, - OverloadCandidateSet& CandidateSet) + OverloadCandidateSet& CandidateSet, + bool SuppressUserConversions) { const FunctionTypeProto* Proto = dyn_cast(Function->getType()->getAsFunctionType()); @@ -1389,7 +1417,8 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, // parameter of F. QualType ParamType = Proto->getArgType(ArgIdx); Candidate.Conversions[ArgIdx] - = TryCopyInitialization(Args[ArgIdx], ParamType); + = TryCopyInitialization(Args[ArgIdx], ParamType, + SuppressUserConversions); if (Candidate.Conversions[ArgIdx].ConversionKind == ImplicitConversionSequence::BadConversion) Candidate.Viable = false; @@ -1408,11 +1437,13 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, void Sema::AddOverloadCandidates(OverloadedFunctionDecl *Ovl, Expr **Args, unsigned NumArgs, - OverloadCandidateSet& CandidateSet) + OverloadCandidateSet& CandidateSet, + bool SuppressUserConversions) { for (OverloadedFunctionDecl::function_iterator Func = Ovl->function_begin(); Func != Ovl->function_end(); ++Func) - AddOverloadCandidate(*Func, Args, NumArgs, CandidateSet); + AddOverloadCandidate(*Func, Args, NumArgs, CandidateSet, + SuppressUserConversions); } /// isBetterOverloadCandidate - Determines whether the first overload diff --git a/lib/Sema/SemaOverload.h b/lib/Sema/SemaOverload.h index 2fd6b1fb3e..af3d1b407e 100644 --- a/lib/Sema/SemaOverload.h +++ b/lib/Sema/SemaOverload.h @@ -18,6 +18,7 @@ #include "llvm/ADT/SmallVector.h" namespace clang { + class CXXConstructorDecl; class FunctionDecl; /// ImplicitConversionKind - The kind of implicit conversion used to @@ -114,6 +115,13 @@ namespace clang { /// is an opaque pointer that can be translated into a QualType. void *ToTypePtr; + /// CopyConstructor - The copy constructor that is used to perform + /// this conversion, when the conversion is actually just the + /// initialization of an object via copy constructor. Such + /// conversions are either identity conversions or derived-to-base + /// conversions. + CXXConstructorDecl *CopyConstructor; + void setAsIdentityConversion(); ImplicitConversionRank getRank() const; bool isPointerConversionToBool() const; diff --git a/test/SemaCXX/converting-constructor.cpp b/test/SemaCXX/converting-constructor.cpp index 4bcd5aa01e..b99a134328 100644 --- a/test/SemaCXX/converting-constructor.cpp +++ b/test/SemaCXX/converting-constructor.cpp @@ -1,4 +1,4 @@ -// RUN: clang -fsyntax-only %s +// RUN: clang -fsyntax-only -verify %s class Z { }; class Y { @@ -18,6 +18,6 @@ void g(short s, Y y, Z z) { f(s); f(1.0f); f(y); - f(z); // expected-error{{incompatible}} + f(z); // expected-error{{incompatible type passing 'class Z', expected 'class X'}} } diff --git a/test/SemaCXX/overload-call-copycon.cpp b/test/SemaCXX/overload-call-copycon.cpp index 8270928577..281f4ce343 100644 --- a/test/SemaCXX/overload-call-copycon.cpp +++ b/test/SemaCXX/overload-call-copycon.cpp @@ -36,3 +36,13 @@ void test_copycon3(B b, const B bc) { int& i1 = copycon3(b); float& f1 = copycon3(bc); } + + +class C : public B { }; + +float& copycon4(A a); +int& copycon4(B b); + +void test_copycon4(C c) { + int& i = copycon4(c); +};