From bc0805a6ca84e3c38a08c9f47441e138945244d4 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 23 Oct 2008 00:40:37 +0000 Subject: [PATCH] Add support for conversions from a pointer-to-derived to a pointer-to-base. Also, add overload ranking for pointer conversions (for both pointer-to-void and derived-to-base pointer conversions). Note that we do not yet diagnose derived-to-base pointer conversion errors that stem from ambiguous or inacessible base classes. These aren't handled during overload resolution; rather, when the conversion is actually used we go ahead and diagnose the error. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58017 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/Sema.h | 26 ++++-- lib/Sema/SemaInherit.cpp | 54 ++++++++++++ lib/Sema/SemaOverload.cpp | 145 +++++++++++++++++++++++++++++++-- lib/Sema/SemaOverload.h | 8 +- test/SemaCXX/overload-call.cpp | 29 +++++++ 5 files changed, 243 insertions(+), 19 deletions(-) create mode 100644 lib/Sema/SemaInherit.cpp diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 48b32e8ce1..5a5c99c700 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -385,6 +385,10 @@ private: CompareQualificationConversions(const StandardConversionSequence& SCS1, const StandardConversionSequence& SCS2); + ImplicitConversionSequence::CompareKind + CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1, + const StandardConversionSequence& SCS2); + /// OverloadingResult - Capture the result of performing overload /// resolution. enum OverloadingResult { @@ -752,14 +756,6 @@ public: //===--------------------------------------------------------------------===// // C++ Classes // - /// ActOnBaseSpecifier - Parsed a base specifier - virtual BaseResult ActOnBaseSpecifier(DeclTy *classdecl, - SourceRange SpecifierRange, - bool Virtual, AccessSpecifier Access, - TypeTy *basetype, SourceLocation BaseLoc); - - virtual void ActOnBaseSpecifiers(DeclTy *ClassDecl, BaseTy **Bases, - unsigned NumBases); virtual void ActOnStartCXXClassDef(Scope *S, DeclTy *TagDecl, SourceLocation LBrace); @@ -775,6 +771,20 @@ public: virtual void ActOnFinishCXXClassDef(DeclTy *TagDecl); + //===--------------------------------------------------------------------===// + // C++ Derived Classes + // + + /// ActOnBaseSpecifier - Parsed a base specifier + virtual BaseResult ActOnBaseSpecifier(DeclTy *classdecl, + SourceRange SpecifierRange, + bool Virtual, AccessSpecifier Access, + TypeTy *basetype, SourceLocation BaseLoc); + + virtual void ActOnBaseSpecifiers(DeclTy *ClassDecl, BaseTy **Bases, + unsigned NumBases); + + bool IsDerivedFrom(QualType Derived, QualType Base); // Objective-C declarations. virtual DeclTy *ActOnStartClassInterface(SourceLocation AtInterfaceLoc, diff --git a/lib/Sema/SemaInherit.cpp b/lib/Sema/SemaInherit.cpp new file mode 100644 index 0000000000..022c8043d0 --- /dev/null +++ b/lib/Sema/SemaInherit.cpp @@ -0,0 +1,54 @@ +//===---- SemaInherit.cpp - C++ Inheritance ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides Sema routines for C++ inheritance semantics, +// including searching the inheritance hierarchy and (eventually) +// access checking. +// +//===----------------------------------------------------------------------===// + +#include "Sema.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" + +namespace clang { + +/// IsDerivedFrom - Determine whether the class type Derived is +/// derived from the class type Base, ignoring qualifiers on Base and +/// Derived. This routine does not assess whether an actual conversion +/// from a Derived* to a Base* is legal, because it does not account +/// for ambiguous conversions or conversions to private/protected +/// bases. +bool Sema::IsDerivedFrom(QualType Derived, QualType Base) +{ + Derived = Context.getCanonicalType(Derived).getUnqualifiedType(); + Base = Context.getCanonicalType(Base).getUnqualifiedType(); + + assert(Derived->isRecordType() && "IsDerivedFrom requires a class type"); + assert(Base->isRecordType() && "IsDerivedFrom requires a class type"); + + if (Derived == Base) + return false; + + if (const RecordType *DerivedType = Derived->getAsRecordType()) { + const CXXRecordDecl *Decl + = static_cast(DerivedType->getDecl()); + for (unsigned idx = 0; idx < Decl->getNumBases(); ++idx) { + const CXXBaseSpecifier *BaseSpec = Decl->getBase(idx); + if (Context.getCanonicalType(BaseSpec->getType()) == Base + || IsDerivedFrom(BaseSpec->getType(), Base)) + return true; + } + } + + return false; +} + +} // end namespace clang + diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 19bc13a146..c967b04335 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -121,6 +121,30 @@ bool StandardConversionSequence::isPointerConversionToBool() const return false; } +/// isPointerConversionToVoidPointer - Determines whether this +/// conversion is a conversion of a pointer to a void pointer. This is +/// used as part of the ranking of standard conversion sequences (C++ +/// 13.3.3.2p4). +bool +StandardConversionSequence:: +isPointerConversionToVoidPointer(ASTContext& Context) const +{ + QualType FromType = QualType::getFromOpaquePtr(FromTypePtr); + QualType ToType = QualType::getFromOpaquePtr(ToTypePtr); + + // Note that FromType has not necessarily been transformed by the + // array-to-pointer implicit conversion, so check for its presence + // and redo the conversion to get a pointer. + if (First == ICK_Array_To_Pointer) + FromType = Context.getArrayDecayedType(FromType); + + if (Second == ICK_Pointer_Conversion) + if (const PointerType* ToPtrType = ToType->getAsPointerType()) + return ToPtrType->getPointeeType()->isVoidType(); + + return false; +} + /// DebugPrint - Print this standard conversion sequence to standard /// error. Useful for debugging overloading issues. void StandardConversionSequence::DebugPrint() const { @@ -635,9 +659,45 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType, return true; } - // FIXME: An rvalue of type "pointer to cv D," where D is a class - // type, can be converted to an rvalue of type "pointer to cv B," - // where B is a base class (clause 10) of D (C++ 4.10p3). + // C++ [conv.ptr]p3: + // + // An rvalue of type "pointer to cv D," where D is a class type, + // can be converted to an rvalue of type "pointer to cv B," where + // B is a base class (clause 10) of D. If B is an inaccessible + // (clause 11) or ambiguous (10.2) base class of D, a program that + // necessitates this conversion is ill-formed. The result of the + // conversion is a pointer to the base class sub-object of the + // derived class object. The null pointer value is converted to + // the null pointer value of the destination type. + // + // Note that we do not check for ambiguity or inaccessibility here. + if (const PointerType *FromPtrType = FromType->getAsPointerType()) + if (const PointerType *ToPtrType = ToType->getAsPointerType()) { + if (FromPtrType->getPointeeType()->isRecordType() && + ToPtrType->getPointeeType()->isRecordType() && + IsDerivedFrom(FromPtrType->getPointeeType(), + ToPtrType->getPointeeType())) { + // The conversion is okay. Now, we need to produce the type + // that results from this conversion, which will have the same + // qualifiers as the incoming type. + QualType CanonFromPointee + = Context.getCanonicalType(FromPtrType->getPointeeType()); + QualType ToPointee = ToPtrType->getPointeeType(); + QualType CanonToPointee = Context.getCanonicalType(ToPointee); + unsigned Quals = CanonFromPointee.getCVRQualifiers(); + + if (CanonToPointee.getCVRQualifiers() == Quals) { + // ToType is exactly the type we want. Use it. + ConvertedType = ToType; + } else { + // Build a new type with the right qualifiers. + ConvertedType + = Context.getPointerType(CanonToPointee.getQualifiedType(Quals)); + } + return true; + } + } + return false; } @@ -790,14 +850,30 @@ Sema::CompareStandardConversionSequences(const StandardConversionSequence& SCS1, ? ImplicitConversionSequence::Better : ImplicitConversionSequence::Worse; - // FIXME: The other bullets in (C++ 13.3.3.2p4) require support - // for derived classes. + // C++ [over.ics.rank]p4b2: + // + // If class B is derived directly or indirectly from class A, + // conversion of B* to A* is better than conversion of B* to void*, + // and (FIXME) conversion of A* to void* is better than conversion of B* + // to void*. + bool SCS1ConvertsToVoid + = SCS1.isPointerConversionToVoidPointer(Context); + bool SCS2ConvertsToVoid + = SCS2.isPointerConversionToVoidPointer(Context); + if (SCS1ConvertsToVoid != SCS2ConvertsToVoid) + return SCS2ConvertsToVoid ? ImplicitConversionSequence::Better + : ImplicitConversionSequence::Worse; + + if (!SCS1ConvertsToVoid && !SCS2ConvertsToVoid) + if (ImplicitConversionSequence::CompareKind DerivedCK + = CompareDerivedToBaseConversions(SCS1, SCS2)) + return DerivedCK; // Compare based on qualification conversions (C++ 13.3.3.2p3, // bullet 3). - if (ImplicitConversionSequence::CompareKind CK + if (ImplicitConversionSequence::CompareKind QualCK = CompareQualificationConversions(SCS1, SCS2)) - return CK; + return QualCK; // FIXME: Handle comparison of reference bindings. @@ -894,6 +970,61 @@ Sema::CompareQualificationConversions(const StandardConversionSequence& SCS1, return Result; } +/// CompareDerivedToBaseConversions - Compares two standard conversion +/// sequences to determine whether they can be ranked based on their +/// various kinds of derived-to-base conversions (C++ [over.ics.rank]p4b3). +ImplicitConversionSequence::CompareKind +Sema::CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1, + const StandardConversionSequence& SCS2) { + QualType FromType1 = QualType::getFromOpaquePtr(SCS1.FromTypePtr); + QualType ToType1 = QualType::getFromOpaquePtr(SCS1.ToTypePtr); + QualType FromType2 = QualType::getFromOpaquePtr(SCS2.FromTypePtr); + QualType ToType2 = QualType::getFromOpaquePtr(SCS2.ToTypePtr); + + // Adjust the types we're converting from via the array-to-pointer + // conversion, if we need to. + if (SCS1.First == ICK_Array_To_Pointer) + FromType1 = Context.getArrayDecayedType(FromType1); + if (SCS2.First == ICK_Array_To_Pointer) + FromType2 = Context.getArrayDecayedType(FromType2); + + // Canonicalize all of the types. + FromType1 = Context.getCanonicalType(FromType1); + ToType1 = Context.getCanonicalType(ToType1); + FromType2 = Context.getCanonicalType(FromType2); + ToType2 = Context.getCanonicalType(ToType2); + + // C++ [over.ics.rank]p4b4: + // + // If class B is derived directly or indirectly from class A and + // class C is derived directly or indirectly from B, + // + // FIXME: Verify that in this section we're talking about the + // unqualified forms of C, B, and A. + if (SCS1.Second == ICK_Pointer_Conversion && + SCS2.Second == ICK_Pointer_Conversion) { + // -- conversion of C* to B* is better than conversion of C* to A*, + QualType FromPointee1 + = FromType1->getAsPointerType()->getPointeeType().getUnqualifiedType(); + QualType ToPointee1 + = ToType1->getAsPointerType()->getPointeeType().getUnqualifiedType(); + QualType FromPointee2 + = FromType2->getAsPointerType()->getPointeeType().getUnqualifiedType(); + QualType ToPointee2 + = ToType2->getAsPointerType()->getPointeeType().getUnqualifiedType(); + if (FromPointee1 == FromPointee2 && ToPointee1 != ToPointee2) { + if (IsDerivedFrom(ToPointee1, ToPointee2)) + return ImplicitConversionSequence::Better; + else if (IsDerivedFrom(ToPointee2, ToPointee1)) + return ImplicitConversionSequence::Worse; + } + } + + // FIXME: many more sub-bullets of C++ [over.ics.rank]p4b4 to + // implement. + return ImplicitConversionSequence::Indistinguishable; +} + /// AddOverloadCandidate - Adds the given function to the set of /// candidate functions, using the given function call arguments. void diff --git a/lib/Sema/SemaOverload.h b/lib/Sema/SemaOverload.h index f9e03aebff..27190f6e99 100644 --- a/lib/Sema/SemaOverload.h +++ b/lib/Sema/SemaOverload.h @@ -97,17 +97,17 @@ namespace clang { bool Deprecated : 1; /// FromType - The type that this conversion is converting - /// from. This is an opaque pointer for that can be translated - /// into a QualType. + /// from. This is an opaque pointer that can be translated into a + /// QualType. void *FromTypePtr; /// ToType - The type that this conversion is converting to. This - /// is an opaque pointer for that can be translated into a - /// QualType. + /// is an opaque pointer that can be translated into a QualType. void *ToTypePtr; ImplicitConversionRank getRank() const; bool isPointerConversionToBool() const; + bool isPointerConversionToVoidPointer(ASTContext& Context) const; void DebugPrint() const; }; diff --git a/test/SemaCXX/overload-call.cpp b/test/SemaCXX/overload-call.cpp index 3036e67d75..1bd6496612 100644 --- a/test/SemaCXX/overload-call.cpp +++ b/test/SemaCXX/overload-call.cpp @@ -185,3 +185,32 @@ void test_quals_ranking(int * p, int volatile *pq, int * * pp, int * * * ppp) { quals_rank3(pq); } +// Test overloading based on derived-to-base conversions +class A { }; +class B : public A { }; +class C : public B { }; +class D : public C { }; + +int* derived1(A*); +char* derived1(const A*); +float* derived1(void*); + +int* derived2(A*); +float* derived2(B*); + +int* derived3(A*); +float* derived3(const B*); +char* derived3(C*); + +void test_derived(B* b, B const* bc, C* c, const C* cc, void* v, D* d) { + int* d1 = derived1(b); + char* d2 = derived1(bc); + int* d3 = derived1(c); + char* d4 = derived1(cc); + float* d5 = derived1(v); + + float* d6 = derived2(b); + float* d7 = derived2(c); + + char* d8 = derived3(d); +} -- 2.40.0