]> granicus.if.org Git - clang/commitdiff
When we encounter a dependent type that was parsed before we know that
authorDouglas Gregor <dgregor@apple.com>
Thu, 6 Aug 2009 16:20:37 +0000 (16:20 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 6 Aug 2009 16:20:37 +0000 (16:20 +0000)
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

lib/Sema/Sema.h
lib/Sema/SemaDecl.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/TreeTransform.h
test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp

index 5093d71ac01b8b21e8c145c4fea2a3fbd72dd46b..e9190fbbe914862c84f205c2f1d9f4dd57f07b31 100644 (file)
@@ -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
index 90ba65a7ea81c124afb45addb2120baaf1ce410c..d6b32a72a305c3117ec36676ce31815d84f5c4ba 100644 (file)
@@ -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<DeclSpec&>(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;
index 8dfb7d3881479c2397f4daf6c49308dfc65414e2..4dad3730bbf1bad8e92ea8e6784ae6bb589b3ad3 100644 (file)
 //===----------------------------------------------------------------------===/
 
 #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
index 24deb4bbd5d1a1e6aa3ff1e57adcab430efc5388..1f630b828fbf258b6e06f2247c96c386b9f3bf51 100644 (file)
@@ -1177,7 +1177,7 @@ template<typename Derived>
 QualType TreeTransform<Derived>::TransformTypenameType(const TypenameType *T) {
   NestedNameSpecifier *NNS
     = getDerived().TransformNestedNameSpecifier(T->getQualifier(),
-                                SourceRange(getDerived().getBaseLocation()));
+                        SourceRange(/*FIXME:*/getDerived().getBaseLocation()));
   if (!NNS)
     return QualType();
   
index 1f2d7d5b9c9c1ebb4b8ff4ace3d4e9f93ad2acce..fd3fb0bc7a7d8e44ded120733f9fd1c5f57f7a83 100644 (file)
@@ -1,5 +1,4 @@
 // RUN: clang-cc -fsyntax-only -verify %s
-// XFAIL
 
 template<typename T> struct X1 { };