From 4433aafbc2591b82e4ea2fc39c723b21d2497f4d Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Sun, 25 Jan 2009 19:43:20 +0000 Subject: [PATCH] Implement implicit conversions for pointers-to-member. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@62971 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticKinds.def | 6 ++ lib/Sema/Sema.h | 3 + lib/Sema/SemaExprCXX.cpp | 5 +- lib/Sema/SemaOverload.cpp | 107 ++++++++++++++++++++-- lib/Sema/SemaType.cpp | 11 ++- test/SemaCXX/member-pointer.cpp | 13 +++ test/SemaCXX/qualification-conversion.cpp | 12 +++ 7 files changed, 147 insertions(+), 10 deletions(-) diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 93b023e560..e9dbad94b6 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -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, diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index a993ebe2cb..51d2e38c93 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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, diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 1266d32d3f..04e37d1ebf 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -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: diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 9441ea5f16..b2ae90b4b8 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -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). diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 21db14a2ae..d62e767a1c 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -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; } diff --git a/test/SemaCXX/member-pointer.cpp b/test/SemaCXX/member-pointer.cpp index 2568cb59d8..4aa3098461 100644 --- a/test/SemaCXX/member-pointer.cpp +++ b/test/SemaCXX/member-pointer.cpp @@ -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; } diff --git a/test/SemaCXX/qualification-conversion.cpp b/test/SemaCXX/qualification-conversion.cpp index d467d3101c..1b818c3392 100644 --- a/test/SemaCXX/qualification-conversion.cpp +++ b/test/SemaCXX/qualification-conversion.cpp @@ -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::*' }} +}