diff options
author | Douglas Gregor <dgregor@apple.com> | 2009-08-06 16:20:37 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2009-08-06 16:20:37 +0000 |
commit | 4a959d8788179d654df6b213b08d2b064989211d (patch) | |
tree | 7236eed56d6d12ec441e2b814c60232aa78ae77f /lib/Sema/SemaTemplate.cpp | |
parent | 8a12b564923a72224730a467007e61b5701e4aa7 (diff) |
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<typename T>
struct X {
typedef T* pointer;
pointer data();
};
template<typename T>
typename X<T>::pointer X<T>::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
Diffstat (limited to 'lib/Sema/SemaTemplate.cpp')
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 119 |
1 files changed, 119 insertions, 0 deletions
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<CurrentInstantiationRebuilder> + { + SourceLocation Loc; + DeclarationName Entity; + + public: + CurrentInstantiationRebuilder(Sema &SemaRef, + SourceLocation Loc, + DeclarationName Entity) + : TreeTransform<CurrentInstantiationRebuilder>(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<typename T> +/// struct X { +/// typedef T* pointer; +/// pointer data(); +/// }; +/// +/// template<typename T> +/// typename X<T>::pointer X<T>::data() { ... } +/// \endcode +/// +/// Here, the type "typename X<T>::pointer" will be created as a TypenameType, +/// since we do not know that we can look into X<T> when we parsed the type. +/// This function will rebuild the type, performing the lookup of "pointer" +/// in X<T> 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 |