From: Douglas Gregor Date: Wed, 9 Jun 2010 03:53:18 +0000 (+0000) Subject: Tweak our handling of the notion of a standard conversion sequence X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5a57efd7bf88a4a13018e0471ded8063a4abe8af;p=clang Tweak our handling of the notion of a standard conversion sequence being a subsequence of another standard conversion sequence. Instead of requiring exact type equality for the second conversion step, require type *similarity*, which is type equality with cv-qualifiers removed at all levels. This appears to match the behavior of EDG and VC++ (albeit not GCC), and feels more intuitive. Big thanks to John for the line of reasoning that supports this change: since cv-qualifiers are orthogonal to the second conversion step, we should ignore them in the type comparison. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@105678 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index b2da3d0b73..9746b0dfd3 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -1015,6 +1015,8 @@ public: return UnqualT1 == UnqualT2; } + bool UnwrapSimilarPointerTypes(QualType &T1, QualType &T2); + /// \brief Retrieves the "canonical" declaration of /// \brief Retrieves the "canonical" nested name specifier for a diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 75af89ee96..2e72449575 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2339,6 +2339,48 @@ QualType ASTContext::getUnqualifiedArrayType(QualType T, SourceRange()); } +/// UnwrapSimilarPointerTypes - If T1 and T2 are pointer 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 ignored. 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 ASTContext::UnwrapSimilarPointerTypes(QualType &T1, QualType &T2) { + const PointerType *T1PtrType = T1->getAs(), + *T2PtrType = T2->getAs(); + if (T1PtrType && T2PtrType) { + T1 = T1PtrType->getPointeeType(); + T2 = T2PtrType->getPointeeType(); + return true; + } + + const MemberPointerType *T1MPType = T1->getAs(), + *T2MPType = T2->getAs(); + if (T1MPType && T2MPType && + hasSameUnqualifiedType(QualType(T1MPType->getClass(), 0), + QualType(T2MPType->getClass(), 0))) { + T1 = T1MPType->getPointeeType(); + T2 = T2MPType->getPointeeType(); + return true; + } + + if (getLangOptions().ObjC1) { + const ObjCObjectPointerType *T1OPType = T1->getAs(), + *T2OPType = T2->getAs(); + if (T1OPType && T2OPType) { + T1 = T1OPType->getPointeeType(); + T2 = T2OPType->getPointeeType(); + return true; + } + } + + // FIXME: Block pointers, too? + + return false; +} + DeclarationName ASTContext::getNameForTemplate(TemplateName Name) { if (TemplateDecl *TD = Name.getAsTemplateDecl()) return TD->getDeclName(); diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 25f4e9d6c1..48b8d05d91 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -769,8 +769,6 @@ public: const FunctionProtoType *Target, SourceLocation TargetLoc, const FunctionProtoType *Source, SourceLocation SourceLoc); - bool UnwrapSimilarPointerTypes(QualType& T1, QualType& T2); - virtual TypeResult ActOnTypeName(Scope *S, Declarator &D); bool RequireCompleteType(SourceLocation Loc, QualType T, diff --git a/lib/Sema/SemaCXXCast.cpp b/lib/Sema/SemaCXXCast.cpp index 5485bb3e17..16f0af14d2 100644 --- a/lib/Sema/SemaCXXCast.cpp +++ b/lib/Sema/SemaCXXCast.cpp @@ -1019,7 +1019,7 @@ static TryCastResult TryConstCast(Sema &Self, Expr *SrcExpr, QualType DestType, // in multi-level pointers may change, but the level count must be the same, // as must be the final pointee type. while (SrcType != DestType && - Self.UnwrapSimilarPointerTypes(SrcType, DestType)) { + Self.Context.UnwrapSimilarPointerTypes(SrcType, DestType)) { Qualifiers Quals; SrcType = Self.Context.getUnqualifiedArrayType(SrcType, Quals); DestType = Self.Context.getUnqualifiedArrayType(DestType, Quals); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index d746ec3f6d..721c68f6c1 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -1785,7 +1785,7 @@ Sema::IsQualificationConversion(QualType FromType, QualType ToType) { // in multi-level pointers, subject to the following rules: [...] bool PreviousToQualsIncludeConst = true; bool UnwrappedAnyPointer = false; - while (UnwrapSimilarPointerTypes(FromType, ToType)) { + while (Context.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 @@ -2073,6 +2073,16 @@ Sema::CompareImplicitConversionSequences(const ImplicitConversionSequence& ICS1, return ImplicitConversionSequence::Indistinguishable; } +static bool hasSimilarType(ASTContext &Context, QualType T1, QualType T2) { + while (Context.UnwrapSimilarPointerTypes(T1, T2)) { + Qualifiers Quals; + T1 = Context.getUnqualifiedArrayType(T1, Quals); + T2 = Context.getUnqualifiedArrayType(T2, Quals); + } + + return Context.hasSameUnqualifiedType(T1, T2); +} + // Per 13.3.3.2p3, compare the given standard conversion sequences to // determine if one is a proper subset of the other. static ImplicitConversionSequence::CompareKind @@ -2098,7 +2108,7 @@ compareStandardConversionSubsets(ASTContext &Context, Result = ImplicitConversionSequence::Worse; else return ImplicitConversionSequence::Indistinguishable; - } else if (!Context.hasSameType(SCS1.getToType(1), SCS2.getToType(1))) + } else if (!hasSimilarType(Context, SCS1.getToType(1), SCS2.getToType(1))) return ImplicitConversionSequence::Indistinguishable; if (SCS1.Third == SCS2.Third) { @@ -2305,7 +2315,7 @@ Sema::CompareQualificationConversions(const StandardConversionSequence& SCS1, ImplicitConversionSequence::CompareKind Result = ImplicitConversionSequence::Indistinguishable; - while (UnwrapSimilarPointerTypes(T1, T2)) { + while (Context.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 diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 8242f61897..8327fd2686 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -1611,45 +1611,6 @@ void LocInfoType::getAsStringInternal(std::string &Str, " GetTypeFromParser"); } -/// UnwrapSimilarPointerTypes - If T1 and T2 are pointer 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 ignored. 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->getAs(), - *T2PtrType = T2->getAs(); - if (T1PtrType && T2PtrType) { - T1 = T1PtrType->getPointeeType(); - T2 = T2PtrType->getPointeeType(); - return true; - } - - const MemberPointerType *T1MPType = T1->getAs(), - *T2MPType = T2->getAs(); - if (T1MPType && T2MPType && - Context.getCanonicalType(T1MPType->getClass()) == - Context.getCanonicalType(T2MPType->getClass())) { - T1 = T1MPType->getPointeeType(); - T2 = T2MPType->getPointeeType(); - return true; - } - - if (getLangOptions().ObjC1) { - const ObjCObjectPointerType *T1OPType = T1->getAs(), - *T2OPType = T2->getAs(); - if (T1OPType && T2OPType) { - T1 = T1OPType->getPointeeType(); - T2 = T2OPType->getPointeeType(); - return true; - } - } - 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 29133c7c7f..9df5bc9f0c 100644 --- a/test/SemaCXX/overload-call.cpp +++ b/test/SemaCXX/overload-call.cpp @@ -460,3 +460,20 @@ namespace PR7224 { float &fr = foo(d2); } } + +namespace NontrivialSubsequence { + struct X0; + + class A { + operator X0 *(); + public: + operator const X0 *(); + }; + + A a; + void foo( void const * ); + + void g() { + foo(a); + } +}