]> granicus.if.org Git - clang/commitdiff
Add support for conversions from a pointer-to-derived to a
authorDouglas Gregor <dgregor@apple.com>
Thu, 23 Oct 2008 00:40:37 +0000 (00:40 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 23 Oct 2008 00:40:37 +0000 (00:40 +0000)
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
lib/Sema/SemaInherit.cpp [new file with mode: 0644]
lib/Sema/SemaOverload.cpp
lib/Sema/SemaOverload.h
test/SemaCXX/overload-call.cpp

index 48b32e8ce13bc47ebfc357ba1fda4a6a5d8d412a..5a5c99c70049bc27b240f1207449740fb35af8a0 100644 (file)
@@ -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 (file)
index 0000000..022c804
--- /dev/null
@@ -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<const CXXRecordDecl *>(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
+
index 19bc13a14666aa422127118fd2c9a8999de05936..c967b043352869a560c97d453cf09de4332fe33e 100644 (file)
@@ -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 
index f9e03aebffc83dbfe2ee7446cc214107bae5ab3b..27190f6e9966c9494275bdc78a6289e8d1a9bed1 100644 (file)
@@ -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;
   };
 
index 3036e67d75bfbb7883b975c173a53c2bc524d325..1bd64966128b50b617a769ad54e2fe29dbbebe52 100644 (file)
@@ -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);
+}