From: Douglas Gregor Date: Wed, 22 Oct 2008 14:17:15 +0000 (+0000) Subject: Implement ranking of standard conversion sequences by their qualification X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=57373266011f73418381b736015d8d2bb0381176;p=clang Implement ranking of standard conversion sequences by their qualification conversions (e.g., comparing int* -> const int* against int* -> const volatile int*); see C++ 13.3.3.2p3 bullet 3. Add Sema::UnwrapSimilarPointerTypes to simplify the control flow of IsQualificationConversion and CompareQualificationConversion (and fix the handling of the int* -> volatile int* conversion in the former). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@57978 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 2508d1ac9f..b176bc9ceb 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -262,7 +262,8 @@ public: QualType ObjCGetTypeForMethodDefinition(DeclTy *D); - + bool UnwrapSimilarPointerTypes(QualType& T1, QualType& T2); + virtual TypeResult ActOnTypeName(Scope *S, Declarator &D); private: //===--------------------------------------------------------------------===// @@ -380,6 +381,10 @@ private: CompareStandardConversionSequences(const StandardConversionSequence& SCS1, const StandardConversionSequence& SCS2); + ImplicitConversionSequence::CompareKind + CompareQualificationConversions(const StandardConversionSequence& SCS1, + const StandardConversionSequence& SCS2); + /// OverloadingResult - Capture the result of performing overload /// resolution. enum OverloadingResult { diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 6d01709c5b..8e440258b7 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -659,46 +659,32 @@ Sema::IsQualificationConversion(QualType FromType, QualType ToType) // A conversion can add cv-qualifiers at levels other than the first // in multi-level pointers, subject to the following rules: [...] bool PreviousToQualsIncludeConst = true; - bool UnwrappedPointer; bool UnwrappedAnyPointer = false; - do { + while (UnwrapSimilarPointerTypes(FromType, ToType)) { // Within each iteration of the loop, we check the qualifiers to // determine if this still looks like a qualification // conversion. Then, if all is well, we unwrap one more level of // pointers (FIXME: or pointers-to-members) and do it all again // until there are no more pointers or pointers-to-members left to // unwrap. - UnwrappedPointer = false; - - // -- the pointer types are similar. - const PointerType *FromPtrType = FromType->getAsPointerType(), - *ToPtrType = ToType->getAsPointerType(); - if (FromPtrType && ToPtrType) { - // The pointer types appear similar. Look at their pointee types. - FromType = FromPtrType->getPointeeType(); - ToType = ToPtrType->getPointeeType(); - UnwrappedPointer = true; - UnwrappedAnyPointer = true; - } - - // FIXME: Cope with pointer-to-member types. + UnwrappedAnyPointer = true; // -- for every j > 0, if const is in cv 1,j then const is in cv // 2,j, and similarly for volatile. if (!ToType.isAtLeastAsQualifiedAs(FromType)) return false; - + // -- if the cv 1,j and cv 2,j are different, then const is in // every cv for 0 < k < j. if (FromType.getCVRQualifiers() != ToType.getCVRQualifiers() - && !PreviousToQualsIncludeConst) + && !PreviousToQualsIncludeConst) return false; - + // Keep track of whether all prior cv-qualifiers in the "to" type // include const. PreviousToQualsIncludeConst = PreviousToQualsIncludeConst && ToType.isConstQualified(); - } while (UnwrappedPointer); + } // We are left with FromType and ToType being the pointee types // after unwrapping the original FromType and ToType the same number @@ -791,28 +777,122 @@ Sema::CompareStandardConversionSequences(const StandardConversionSequence& SCS1, return ImplicitConversionSequence::Better; else if (Rank2 < Rank1) return ImplicitConversionSequence::Worse; - else { - // (C++ 13.3.3.2p4): Two conversion sequences with the same rank - // are indistinguishable unless one of the following rules - // applies: - - // A conversion that is not a conversion of a pointer, or - // pointer to member, to bool is better than another conversion - // that is such a conversion. - if (SCS1.isPointerConversionToBool() != SCS2.isPointerConversionToBool()) - return SCS2.isPointerConversionToBool() - ? ImplicitConversionSequence::Better - : ImplicitConversionSequence::Worse; - // FIXME: The other bullets in (C++ 13.3.3.2p4) require support - // for derived classes. - } + // (C++ 13.3.3.2p4): Two conversion sequences with the same rank + // are indistinguishable unless one of the following rules + // applies: + + // A conversion that is not a conversion of a pointer, or + // pointer to member, to bool is better than another conversion + // that is such a conversion. + if (SCS1.isPointerConversionToBool() != SCS2.isPointerConversionToBool()) + return SCS2.isPointerConversionToBool() + ? ImplicitConversionSequence::Better + : ImplicitConversionSequence::Worse; + + // FIXME: The other bullets in (C++ 13.3.3.2p4) require support + // for derived classes. + + // Compare based on qualification conversions (C++ 13.3.3.2p3, + // bullet 3). + if (ImplicitConversionSequence::CompareKind CK + = CompareQualificationConversions(SCS1, SCS2)) + return CK; - // FIXME: Handle comparison by qualifications. // FIXME: Handle comparison of reference bindings. + return ImplicitConversionSequence::Indistinguishable; } +/// CompareQualificationConversions - Compares two standard conversion +/// sequences to determine whether they can be ranked based on their +/// qualification conversions (C++ 13.3.3.2p3 bullet 3). +ImplicitConversionSequence::CompareKind +Sema::CompareQualificationConversions(const StandardConversionSequence& SCS1, + const StandardConversionSequence& SCS2) +{ + // -- S1 and S2 differ only in their qualification conversion and + // yield similar types T1 and T2 (C++ 4.4), respectively, and the + // cv-qualification signature of type T1 is a proper subset of + // the cv-qualification signature of type T2, and S1 is not the + // deprecated string literal array-to-pointer conversion (4.2). + if (SCS1.First != SCS2.First || SCS1.Second != SCS2.Second || + SCS1.Third != SCS2.Third || SCS1.Third != ICK_Qualification) + return ImplicitConversionSequence::Indistinguishable; + + // FIXME: the example in the standard doesn't use a qualification + // conversion (!) + QualType T1 = QualType::getFromOpaquePtr(SCS1.ToTypePtr); + QualType T2 = QualType::getFromOpaquePtr(SCS2.ToTypePtr); + T1 = Context.getCanonicalType(T1); + T2 = Context.getCanonicalType(T2); + + // If the types are the same, we won't learn anything by unwrapped + // them. + if (T1.getUnqualifiedType() == T2.getUnqualifiedType()) + return ImplicitConversionSequence::Indistinguishable; + + ImplicitConversionSequence::CompareKind Result + = ImplicitConversionSequence::Indistinguishable; + while (UnwrapSimilarPointerTypes(T1, T2)) { + // Within each iteration of the loop, we check the qualifiers to + // determine if this still looks like a qualification + // conversion. Then, if all is well, we unwrap one more level of + // pointers (FIXME: or pointers-to-members) and do it all again + // until there are no more pointers or pointers-to-members left + // to unwrap. This essentially mimics what + // IsQualificationConversion does, but here we're checking for a + // strict subset of qualifiers. + if (T1.getCVRQualifiers() == T2.getCVRQualifiers()) + // The qualifiers are the same, so this doesn't tell us anything + // about how the sequences rank. + ; + else if (T2.isMoreQualifiedThan(T1)) { + // T1 has fewer qualifiers, so it could be the better sequence. + if (Result == ImplicitConversionSequence::Worse) + // Neither has qualifiers that are a subset of the other's + // qualifiers. + return ImplicitConversionSequence::Indistinguishable; + + Result = ImplicitConversionSequence::Better; + } else if (T1.isMoreQualifiedThan(T2)) { + // T2 has fewer qualifiers, so it could be the better sequence. + if (Result == ImplicitConversionSequence::Better) + // Neither has qualifiers that are a subset of the other's + // qualifiers. + return ImplicitConversionSequence::Indistinguishable; + + Result = ImplicitConversionSequence::Worse; + } else { + // Qualifiers are disjoint. + return ImplicitConversionSequence::Indistinguishable; + } + + // If the types after this point are equivalent, we're done. + if (T1.getUnqualifiedType() == T2.getUnqualifiedType()) + break; + } + + // Check that the winning standard conversion sequence isn't using + // the deprecated string literal array to pointer conversion. + switch (Result) { + case ImplicitConversionSequence::Better: + if (SCS1.Deprecated) + Result = ImplicitConversionSequence::Indistinguishable; + break; + + case ImplicitConversionSequence::Indistinguishable: + break; + + case ImplicitConversionSequence::Worse: + if (SCS2.Deprecated) + Result = ImplicitConversionSequence::Indistinguishable; + break; + } + + return Result; +} + /// 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 8d7ccd2c95..f9e03aebff 100644 --- a/lib/Sema/SemaOverload.h +++ b/lib/Sema/SemaOverload.h @@ -170,9 +170,9 @@ namespace clang { // sequences. Use Sema::CompareImplicitConversionSequences to // actually perform the comparison. enum CompareKind { - Better, - Indistinguishable, - Worse + Better = -1, + Indistinguishable = 0, + Worse = 1 }; void DebugPrint() const; diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index f1edabfa7e..82e16cfc31 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -535,6 +535,29 @@ QualType Sema::ObjCGetTypeForMethodDefinition(DeclTy *D) { return T; } +/// UnwrapSimilarPointerTypes - If T1 and T2 are pointer types (FIXME: +/// or pointer-to-member types) that may be similar (C++ 4.4), +/// replaces T1 and T2 with the type that they point to and return +/// true. If T1 and T2 aren't pointer types or pointer-to-member +/// types, or if they are not similar at this level, returns false and +/// leaves T1 and T2 unchanged. Top-level qualifiers on T1 and T2 are +/// ignord. This function will typically be called in a loop that +/// successively "unwraps" pointer and pointer-to-member types to +/// compare them at each level. +bool Sema::UnwrapSimilarPointerTypes(QualType& T1, QualType& T2) +{ + const PointerType *T1PtrType = T1->getAsPointerType(), + *T2PtrType = T2->getAsPointerType(); + if (T1PtrType && T2PtrType) { + T1 = T1PtrType->getPointeeType(); + T2 = T2PtrType->getPointeeType(); + return true; + } + + // FIXME: pointer-to-member types + return false; +} + Sema::TypeResult Sema::ActOnTypeName(Scope *S, Declarator &D) { // C99 6.7.6: Type names have no identifier. This is already validated by // the parser. diff --git a/test/SemaCXX/overload-call.cpp b/test/SemaCXX/overload-call.cpp index 165ee60c84..3036e67d75 100644 --- a/test/SemaCXX/overload-call.cpp +++ b/test/SemaCXX/overload-call.cpp @@ -156,11 +156,32 @@ void test_quals(int * p, int * * pp, int * * * ppp) { // Test overloading based on qualification ranking (C++ 13.3.2)p3. int* quals_rank1(int const * p); float* quals_rank1(int const volatile *p); +char* quals_rank1(char*); +double* quals_rank1(const char*); int* quals_rank2(int const * const * pp); float* quals_rank2(int * const * pp); +void quals_rank3(int const * const * const volatile * p); // expected-note{{candidate function}} +void quals_rank3(int const * const volatile * const * p); // expected-note{{candidate function}} + +void quals_rank3(int const *); // expected-note{{candidate function}} +void quals_rank3(int volatile *); // expected-note{{candidate function}} + void test_quals_ranking(int * p, int volatile *pq, int * * pp, int * * * ppp) { - // int* q1 = quals_rank1(p); + int* q1 = quals_rank1(p); float* q2 = quals_rank1(pq); + double* q3 = quals_rank1("string literal"); + char a[17]; + const char* ap = a; + char* q4 = quals_rank1(a); + double* q5 = quals_rank1(ap); + + float* q6 = quals_rank2(pp); + + quals_rank3(ppp); // expected-error {{call to 'quals_rank3' is ambiguous; candidates are:}} + + quals_rank3(p); // expected-error {{call to 'quals_rank3' is ambiguous; candidates are:}} + quals_rank3(pq); } +