From 4a959d8788179d654df6b213b08d2b064989211d Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 6 Aug 2009 16:20:37 +0000 Subject: [PATCH] When we encounter a dependent type that was parsed before we know that we were going to enter into the scope of a class template or class template partial specialization, rebuild that type so that it can refer to members of the current instantiation, as in code like template struct X { typedef T* pointer; pointer data(); }; template typename X::pointer X::data() { ... } Without rebuilding the return type of this out-of-line definition, the canonical return type of the out-of-line definition (a TypenameType) will not match the canonical return type of the declaration (the canonical type of T*). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@78316 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/Sema.h | 3 + lib/Sema/SemaDecl.cpp | 24 ++++ lib/Sema/SemaTemplate.cpp | 119 ++++++++++++++++++ lib/Sema/TreeTransform.h | 2 +- .../temp.class/temp.mem.func/p1-retmem.cpp | 1 - 5 files changed, 147 insertions(+), 2 deletions(-) diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 5093d71ac0..e9190fbbe9 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2321,6 +2321,9 @@ public: const IdentifierInfo &II, SourceRange Range); + QualType RebuildTypeInCurrentInstantiation(QualType T, SourceLocation Loc, + DeclarationName Name); + /// \brief Describes the result of template argument deduction. /// /// The TemplateDeductionResult enumeration describes the result of diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 90ba65a7ea..d6b32a72a3 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1628,6 +1628,30 @@ Sema::HandleDeclarator(Scope *S, Declarator &D, (S->getFlags() & Scope::TemplateParamScope) != 0) S = S->getParent(); + // If this is an out-of-line definition of a member of a class template + // or class template partial specialization, we may need to rebuild the + // type specifier in the declarator. See RebuildTypeInCurrentInstantiation() + // for more information. + // FIXME: cope with decltype(expr) and typeof(expr) once the rebuilder can + // handle expressions properly. + DeclSpec &DS = const_cast(D.getDeclSpec()); + if (D.getCXXScopeSpec().isSet() && !D.getCXXScopeSpec().isInvalid() && + isDependentScopeSpecifier(D.getCXXScopeSpec()) && + (DS.getTypeSpecType() == DeclSpec::TST_typename || + DS.getTypeSpecType() == DeclSpec::TST_typeofType || + DS.getTypeSpecType() == DeclSpec::TST_typeofExpr || + DS.getTypeSpecType() == DeclSpec::TST_decltype)) { + if (DeclContext *DC = computeDeclContext(D.getCXXScopeSpec(), true)) { + QualType T = QualType::getFromOpaquePtr(DS.getTypeRep()); + EnterDeclaratorContext(S, DC); + T = RebuildTypeInCurrentInstantiation(T, D.getIdentifierLoc(), Name); + ExitDeclaratorContext(S); + if (T.isNull()) + return DeclPtrTy(); + DS.UpdateTypeRep(T.getAsOpaquePtr()); + } + } + DeclContext *DC; NamedDecl *PrevDecl; NamedDecl *New; diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 8dfb7d3881..4dad3730bb 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -10,12 +10,14 @@ //===----------------------------------------------------------------------===/ #include "Sema.h" +#include "TreeTransform.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/Parse/DeclSpec.h" #include "clang/Basic/LangOptions.h" +#include "llvm/Support/Compiler.h" using namespace clang; @@ -3057,3 +3059,120 @@ Sema::CheckTypenameType(NestedNameSpecifier *NNS, const IdentifierInfo &II, << Name; return QualType(); } + +namespace { + // See Sema::RebuildTypeInCurrentInstantiation + class VISIBILITY_HIDDEN CurrentInstantiationRebuilder + : public TreeTransform + { + SourceLocation Loc; + DeclarationName Entity; + + public: + CurrentInstantiationRebuilder(Sema &SemaRef, + SourceLocation Loc, + DeclarationName Entity) + : TreeTransform(SemaRef), + Loc(Loc), Entity(Entity) { } + + /// \brief Determine whether the given type \p T has already been + /// transformed. + /// + /// For the purposes of type reconstruction, a type has already been + /// transformed if it is NULL or if it is not dependent. + bool AlreadyTransformed(QualType T) { + return T.isNull() || !T->isDependentType(); + } + + /// \brief Returns the location of the entity whose type is being + /// rebuilt. + SourceLocation getBaseLocation() { return Loc; } + + /// \brief Returns the name of the entity whose type is being rebuilt. + DeclarationName getBaseEntity() { return Entity; } + + /// \brief Transforms an expression by returning the expression itself + /// (an identity function). + /// + /// FIXME: This is completely unsafe; we will need to actually clone the + /// expressions. + Sema::OwningExprResult TransformExpr(Expr *E) { + return getSema().Owned(E); + } + + /// \brief Transforms a typename type by determining whether the type now + /// refers to a member of the current instantiation, and then + /// type-checking and building a QualifiedNameType (when possible). + QualType TransformTypenameType(const TypenameType *T); + }; +} + +QualType +CurrentInstantiationRebuilder::TransformTypenameType(const TypenameType *T) { + NestedNameSpecifier *NNS + = TransformNestedNameSpecifier(T->getQualifier(), + /*FIXME:*/SourceRange(getBaseLocation())); + if (!NNS) + return QualType(); + + // If the nested-name-specifier did not change, and we cannot compute the + // context corresponding to the nested-name-specifier, then this + // typename type will not change; exit early. + CXXScopeSpec SS; + SS.setRange(SourceRange(getBaseLocation())); + SS.setScopeRep(NNS); + if (NNS == T->getQualifier() && getSema().computeDeclContext(SS) == 0) + return QualType(T, 0); + + // Rebuild the typename type, which will probably turn into a + // QualifiedNameType. + if (const TemplateSpecializationType *TemplateId = T->getTemplateId()) { + QualType NewTemplateId + = TransformType(QualType(TemplateId, 0)); + if (NewTemplateId.isNull()) + return QualType(); + + if (NNS == T->getQualifier() && + NewTemplateId == QualType(TemplateId, 0)) + return QualType(T, 0); + + return getDerived().RebuildTypenameType(NNS, NewTemplateId); + } + + return getDerived().RebuildTypenameType(NNS, T->getIdentifier()); +} + +/// \brief Rebuilds a type within the context of the current instantiation. +/// +/// The type \p T is part of the type of an out-of-line member definition of +/// a class template (or class template partial specialization) that was parsed +/// and constructed before we entered the scope of the class template (or +/// partial specialization thereof). This routine will rebuild that type now +/// that we have entered the declarator's scope, which may produce different +/// canonical types, e.g., +/// +/// \code +/// template +/// struct X { +/// typedef T* pointer; +/// pointer data(); +/// }; +/// +/// template +/// typename X::pointer X::data() { ... } +/// \endcode +/// +/// Here, the type "typename X::pointer" will be created as a TypenameType, +/// since we do not know that we can look into X when we parsed the type. +/// This function will rebuild the type, performing the lookup of "pointer" +/// in X and returning a QualifiedNameType whose canonical type is the same +/// as the canonical type of T*, allowing the return types of the out-of-line +/// definition and the declaration to match. +QualType Sema::RebuildTypeInCurrentInstantiation(QualType T, SourceLocation Loc, + DeclarationName Name) { + if (T.isNull() || !T->isDependentType()) + return T; + + CurrentInstantiationRebuilder Rebuilder(*this, Loc, Name); + return Rebuilder.TransformType(T); +} \ No newline at end of file diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 24deb4bbd5..1f630b828f 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -1177,7 +1177,7 @@ template QualType TreeTransform::TransformTypenameType(const TypenameType *T) { NestedNameSpecifier *NNS = getDerived().TransformNestedNameSpecifier(T->getQualifier(), - SourceRange(getDerived().getBaseLocation())); + SourceRange(/*FIXME:*/getDerived().getBaseLocation())); if (!NNS) return QualType(); diff --git a/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp b/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp index 1f2d7d5b9c..fd3fb0bc7a 100644 --- a/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp +++ b/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp @@ -1,5 +1,4 @@ // RUN: clang-cc -fsyntax-only -verify %s -// XFAIL template struct X1 { }; -- 2.40.0