]> granicus.if.org Git - clang/commitdiff
Implement implicit conversions for pointers-to-member.
authorSebastian Redl <sebastian.redl@getdesigned.at>
Sun, 25 Jan 2009 19:43:20 +0000 (19:43 +0000)
committerSebastian Redl <sebastian.redl@getdesigned.at>
Sun, 25 Jan 2009 19:43:20 +0000 (19:43 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@62971 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticKinds.def
lib/Sema/Sema.h
lib/Sema/SemaExprCXX.cpp
lib/Sema/SemaOverload.cpp
lib/Sema/SemaType.cpp
test/SemaCXX/member-pointer.cpp
test/SemaCXX/qualification-conversion.cpp

index 93b023e560999a6e649af406dbdf55fc5d714c09..e9dbad94b676e5681a91f9e2530d709a3e4d5d64 100644 (file)
@@ -1590,6 +1590,12 @@ DIAG(err_duplicate_base_class, ERROR,
 // FIXME: better way to display derivation?  Pass entire thing into diagclient?
 DIAG(err_ambiguous_derived_to_base_conv, ERROR,
      "ambiguous conversion from derived class %0 to base class %1:%2")
+DIAG(err_ambiguous_base_to_derived_memptr_conv, ERROR,
+     "ambiguous conversion from pointer to member of base class %0 "
+     "to pointer to member of derived class %1:%2")
+DIAG(err_memptr_conv_via_virtual, ERROR,
+     "conversion from pointer to member of class %0 to pointer to member "
+     "of class %1 via virtual base %2 is not allowed")
 
 // C++ member name lookup
 DIAG(err_ambiguous_member_multiple_subobjects, ERROR,
index a993ebe2cbd984dd3632bca9b8cb1cb53cd8c875..51d2e38c938bf3761e2fb561719dd7645176f5bc 100644 (file)
@@ -428,6 +428,9 @@ public:
   bool isObjCPointerConversion(QualType FromType, QualType ToType,
                                QualType& ConvertedType, bool &IncompatibleObjC);
   bool CheckPointerConversion(Expr *From, QualType ToType);
+  bool IsMemberPointerConversion(Expr *From, QualType FromType, QualType ToType,
+                                 QualType &ConvertedType);
+  bool CheckMemberPointerConversion(Expr *From, QualType ToType);
   bool IsQualificationConversion(QualType FromType, QualType ToType);
   bool IsUserDefinedConversion(Expr *From, QualType ToType, 
                                UserDefinedConversionSequence& User,
index 1266d32d3f8a61f7d4ce2cf50125f4d8e3130147..04e37d1ebf43d11aad11277f4f490284698bf032 100644 (file)
@@ -832,8 +832,9 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType,
     break;
 
   case ICK_Pointer_Member:
-    // FIXME: Implement pointer-to-member conversions.
-    assert(false && "Pointer-to-member conversions are unsupported");
+    if (CheckMemberPointerConversion(From, ToType))
+      return true;
+    ImpCastExprToType(From, ToType);
     break;
 
   case ICK_Boolean_Conversion:
index 9441ea5f166b8374a09d6887de19878fc0b0fab4..b2ae90b4b875001f70842b43f4680cdb47cfaa5c 100644 (file)
@@ -544,14 +544,17 @@ Sema::IsStandardConversion(Expr* From, QualType ToType,
     SCS.Second = ICK_Pointer_Conversion;
     SCS.IncompatibleObjC = IncompatibleObjC;
   }
-  // FIXME: Pointer to member conversions (4.11).
+  // Pointer to member conversions (4.11).
+  else if (IsMemberPointerConversion(From, FromType, ToType, FromType)) {
+    SCS.Second = ICK_Pointer_Member;
+  }
   // Boolean conversions (C++ 4.12).
-  // FIXME: pointer-to-member type
   else if (ToType->isBooleanType() &&
            (FromType->isArithmeticType() ||
             FromType->isEnumeralType() ||
             FromType->isPointerType() ||
-            FromType->isBlockPointerType())) {
+            FromType->isBlockPointerType() ||
+            FromType->isMemberPointerType())) {
     SCS.Second = ICK_Boolean_Conversion;
     FromType = Context.BoolTy;
   } else {
@@ -999,7 +1002,7 @@ bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType,
     }
   }
 
-  return false;  
+  return false;
 }
 
 /// CheckPointerConversion - Check the pointer conversion from the
@@ -1013,8 +1016,6 @@ bool Sema::CheckPointerConversion(Expr *From, QualType ToType) {
 
   if (const PointerType *FromPtrType = FromType->getAsPointerType())
     if (const PointerType *ToPtrType = ToType->getAsPointerType()) {
-      BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/false,
-                      /*DetectVirtual=*/false);
       QualType FromPointeeType = FromPtrType->getPointeeType(),
                ToPointeeType   = ToPtrType->getPointeeType();
 
@@ -1040,6 +1041,100 @@ bool Sema::CheckPointerConversion(Expr *From, QualType ToType) {
   return false;
 }
 
+/// IsMemberPointerConversion - Determines whether the conversion of the
+/// expression From, which has the (possibly adjusted) type FromType, can be
+/// converted to the type ToType via a member pointer conversion (C++ 4.11).
+/// If so, returns true and places the converted type (that might differ from
+/// ToType in its cv-qualifiers at some level) into ConvertedType.
+bool Sema::IsMemberPointerConversion(Expr *From, QualType FromType,
+                                     QualType ToType, QualType &ConvertedType)
+{
+  const MemberPointerType *ToTypePtr = ToType->getAsMemberPointerType();
+  if (!ToTypePtr)
+    return false;
+
+  // A null pointer constant can be converted to a member pointer (C++ 4.11p1)
+  if (From->isNullPointerConstant(Context)) {
+    ConvertedType = ToType;
+    return true;
+  }
+
+  // Otherwise, both types have to be member pointers.
+  const MemberPointerType *FromTypePtr = FromType->getAsMemberPointerType();
+  if (!FromTypePtr)
+    return false;
+
+  // A pointer to member of B can be converted to a pointer to member of D,
+  // where D is derived from B (C++ 4.11p2).
+  QualType FromClass(FromTypePtr->getClass(), 0);
+  QualType ToClass(ToTypePtr->getClass(), 0);
+  // FIXME: What happens when these are dependent? Is this function even called?
+
+  if (IsDerivedFrom(ToClass, FromClass)) {
+    ConvertedType = Context.getMemberPointerType(FromTypePtr->getPointeeType(),
+                                                 ToClass.getTypePtr());
+    return true;
+  }
+
+  return false;
+}
+
+/// CheckMemberPointerConversion - Check the member pointer conversion from the
+/// expression From to the type ToType. This routine checks for ambiguous or
+/// virtual (FIXME: or inaccessible) base-to-derived member pointer conversions
+/// for which IsMemberPointerConversion has already returned true. It returns
+/// true and produces a diagnostic if there was an error, or returns false
+/// otherwise.
+bool Sema::CheckMemberPointerConversion(Expr *From, QualType ToType) {
+  QualType FromType = From->getType();
+
+  if (const MemberPointerType *FromPtrType =
+        FromType->getAsMemberPointerType()) {
+    if (const MemberPointerType *ToPtrType =
+          ToType->getAsMemberPointerType()) {
+      QualType FromClass = QualType(FromPtrType->getClass(), 0);
+      QualType ToClass   = QualType(ToPtrType->getClass(), 0);
+
+      // FIXME: What about dependent types?
+      assert(FromClass->isRecordType() && "Pointer into non-class.");
+      assert(ToClass->isRecordType() && "Pointer into non-class.");
+
+      BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/false,
+                      /*DetectVirtual=*/true);
+      bool DerivationOkay = IsDerivedFrom(ToClass, FromClass, Paths);
+      assert(DerivationOkay &&
+             "Should not have been called if derivation isn't OK.");
+      if (!DerivationOkay)
+        return true;
+
+      if (Paths.isAmbiguous(Context.getCanonicalType(FromClass).
+                                      getUnqualifiedType())) {
+        // Derivation is ambiguous. Redo the check to find the exact paths.
+        Paths.clear();
+        Paths.setRecordingPaths(true);
+        bool StillOkay = IsDerivedFrom(ToClass, FromClass, Paths);
+        assert(StillOkay && "Derivation changed due to quantum fluctuation.");
+        if (!StillOkay)
+          return true;
+
+        std::string PathDisplayStr = getAmbiguousPathsDisplayString(Paths);
+        Diag(From->getExprLoc(),
+             diag::err_ambiguous_base_to_derived_memptr_conv)
+          << FromClass << ToClass << PathDisplayStr << From->getSourceRange();
+        return true;
+      }
+
+      if (const CXXRecordType *VBase = Paths.getDetectedVirtual()) {
+        Diag(From->getExprLoc(), diag::err_memptr_conv_via_virtual)
+          << FromClass << ToClass << QualType(VBase, 0)
+          << From->getSourceRange();
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 /// IsQualificationConversion - Determines whether the conversion from
 /// an rvalue of type FromType to ToType is a qualification conversion
 /// (C++ 4.4).
index 21db14a2ae9b7e97ef9cd74222926d5a69fc4558..d62e767a1c1198f291c2a0975d8ccfcd6a4ebd91 100644 (file)
@@ -561,7 +561,8 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip) {
         DeclType.Mem.TypeQuals &= ~QualType::Restrict;
       }
 
-      T = Context.getMemberPointerType(T, ClsType.getTypePtr());
+      T = Context.getMemberPointerType(T, ClsType.getTypePtr()).
+                    getQualifiedType(DeclType.Mem.TypeQuals);
 
       break;
     }
@@ -660,7 +661,13 @@ bool Sema::UnwrapSimilarPointerTypes(QualType& T1, QualType& T2)
     return true;
   }
 
-  // FIXME: pointer-to-member types
+  const MemberPointerType *T1MPType = T1->getAsMemberPointerType(),
+                          *T2MPType = T2->getAsMemberPointerType();
+  if (T1MPType && T2MPType) {
+    T1 = T1MPType->getPointeeType();
+    T2 = T2MPType->getPointeeType();
+    return true;
+  }
   return false;
 }
 
index 2568cb59d8b79495b32b59aee0ecf5b4c62665f3..4aa309846101e6dc274f0bcab19298a82a6784df 100644 (file)
@@ -3,6 +3,7 @@
 struct A {};
 enum B { Dummy };
 namespace C {}
+struct D : A {};
 
 int A::*pdi1;
 int (::A::*pdi2);
@@ -16,4 +17,16 @@ int& A::*pdr; // expected-error {{'pdr' declared as a pointer to a reference}}
 void f() {
   // This requires tentative parsing.
   int (A::*pf)(int, int);
+
+  // Implicit conversion to bool.
+  bool b = pdi1;
+  b = pfi;
+
+  // Conversion from null pointer constant.
+  pf = 0;
+  pf = __null;
+
+  // Conversion to member of derived.
+  int D::*pdid = pdi1;
+  pdid = pdi2;
 }
index d467d3101c4410e0b80dfe894772e35723aa4332..1b818c33927c929bc99a2e5bdacfdce114ea63c2 100644 (file)
@@ -9,3 +9,15 @@ void test_quals(int * p, int * * pp, int * * * ppp) {
   quals2(pp);
   quals3(ppp); // expected-error {{ incompatible type passing 'int ***', expected 'int const **const *' }}
 }
+
+struct A {};
+void mquals1(int const A::*p);
+void mquals2(int const A::* const A::*pp);
+void mquals3(int const A::* A::* const A::*ppp);
+
+void test_mquals(int A::*p, int A::* A::*pp, int A::* A::* A::*ppp) {
+  int const A::* const A::* pp2 = pp;
+  mquals1(p);
+  mquals2(pp);
+  mquals3(ppp); // expected-error {{ incompatible type passing 'int struct A::*struct A::*struct A::*', expected 'int const struct A::*struct A::*const struct A::*' }}
+}