[cfe-commits] r71471 - in /cfe/trunk: include/clang/AST/DeclCXX.h lib/AST/Decl.cpp lib/Sema/Sema.h lib/Sema/SemaCXXScopeSpec.cpp lib/Sema/SemaDecl.cpp lib/Sema/SemaDeclCXX.cpp lib/Sema/SemaLookup.cpp lib/Sema/SemaTemplate.cpp test/SemaTemplate/current-instantiation.cpp test/SemaTemplate/dependent-type-identity.cpp

Douglas Gregor dgregor at apple.com
Mon May 11 12:58:34 PDT 2009


Author: dgregor
Date: Mon May 11 14:58:34 2009
New Revision: 71471

URL: http://llvm.org/viewvc/llvm-project?rev=71471&view=rev
Log:
Implement the notions of the "current instantiation" and "unknown
specialization" within a C++ template, and permit name lookup into the
current instantiation. For example, given:

  template<typename T, typename U>
  struct X {
    typedef T type;

    X* x1;  // current instantiation
    X<T, U> *x2; // current instantiation
    X<U, T> *x3; // not current instantiation
    ::X<type, U> *x4; // current instantiation
    X<typename X<type, U>::type, U>: *x5; // current instantiation
  };


Added:
    cfe/trunk/test/SemaTemplate/current-instantiation.cpp
Modified:
    cfe/trunk/include/clang/AST/DeclCXX.h
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaLookup.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/test/SemaTemplate/dependent-type-identity.cpp

Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=71471&r1=71470&r2=71471&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Mon May 11 14:58:34 2009
@@ -408,7 +408,7 @@
   /// the CXXRecordDecl X<T>::A. When a complete definition of
   /// X<int>::A is required, it will be instantiated from the
   /// declaration returned by getInstantiatedFromMemberClass().
-  CXXRecordDecl *getInstantiatedFromMemberClass() {
+  CXXRecordDecl *getInstantiatedFromMemberClass() const {
     return TemplateOrInstantiation.dyn_cast<CXXRecordDecl*>();
   }
 
@@ -429,7 +429,7 @@
   /// CXXRecordDecl that from a ClassTemplateDecl, while
   /// getDescribedClassTemplate() retrieves the ClassTemplateDecl from
   /// a CXXRecordDecl.
-  ClassTemplateDecl *getDescribedClassTemplate() {
+  ClassTemplateDecl *getDescribedClassTemplate() const {
     return TemplateOrInstantiation.dyn_cast<ClassTemplateDecl*>();
   }
 

Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=71471&r1=71470&r2=71471&view=diff

==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Mon May 11 14:58:34 2009
@@ -507,9 +507,10 @@
 //===----------------------------------------------------------------------===//
 
 bool TagDecl::isDependentType() const {
-  if (isa<TemplateDecl>(this))
-    return true;
-
+  if (const CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(this))
+    if (Record->getDescribedClassTemplate())
+      return true;
+    
   if (const TagDecl *TD = dyn_cast_or_null<TagDecl>(getDeclContext()))
     return TD->isDependentType();
 

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=71471&r1=71470&r2=71471&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Mon May 11 14:58:34 2009
@@ -1602,6 +1602,8 @@
   
   DeclContext *computeDeclContext(const CXXScopeSpec &SS);
   bool isDependentScopeSpecifier(const CXXScopeSpec &SS);
+  CXXRecordDecl *getCurrentInstantiationOf(NestedNameSpecifier *NNS);
+  bool isUnknownSpecialization(const CXXScopeSpec &SS);
 
   /// ActOnCXXGlobalScopeSpecifier - Return the object that represents the
   /// global scope ('::').

Modified: cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp?rev=71471&r1=71470&r2=71471&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp Mon May 11 14:58:34 2009
@@ -13,6 +13,7 @@
 
 #include "Sema.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/AST/NestedNameSpecifier.h"
 #include "clang/Parse/DeclSpec.h"
 #include "llvm/ADT/STLExtras.h"
@@ -26,8 +27,14 @@
 
   NestedNameSpecifier *NNS 
     = static_cast<NestedNameSpecifier *>(SS.getScopeRep());
-  if (NNS->isDependent())
-    return 0;
+  if (NNS->isDependent()) {
+    // If this nested-name-specifier refers to the current
+    // instantiation, return its DeclContext.
+    if (CXXRecordDecl *Record = getCurrentInstantiationOf(NNS))
+      return Record;
+    else
+      return 0;
+  }
 
   switch (NNS->getKind()) {
   case NestedNameSpecifier::Identifier:
@@ -61,6 +68,90 @@
   return NNS->isDependent();
 }
 
+// \brief Determine whether this C++ scope specifier refers to an
+// unknown specialization, i.e., a dependent type that is not the
+// current instantiation.
+bool Sema::isUnknownSpecialization(const CXXScopeSpec &SS) {
+  if (!isDependentScopeSpecifier(SS))
+    return false;
+
+  NestedNameSpecifier *NNS 
+    = static_cast<NestedNameSpecifier *>(SS.getScopeRep());
+  return getCurrentInstantiationOf(NNS) == 0;
+}
+
+/// \brief If the given nested name specifier refers to the current
+/// instantiation, return the declaration that corresponds to that
+/// current instantiation (C++0x [temp.dep.type]p1).
+///
+/// \param NNS a dependent nested name specifier.
+CXXRecordDecl *Sema::getCurrentInstantiationOf(NestedNameSpecifier *NNS) {
+  assert(getLangOptions().CPlusPlus && "Only callable in C++");
+  assert(NNS->isDependent() && "Only dependent nested-name-specifier allowed");
+
+  QualType T = QualType(NNS->getAsType(), 0);
+  // If the nested name specifier does not refer to a type, then it
+  // does not refer to the current instantiation.
+  if (T.isNull())
+    return 0;
+
+  T = Context.getCanonicalType(T);
+
+  for (DeclContext *Ctx = CurContext; Ctx; Ctx = Ctx->getParent()) {
+    // If we've hit a namespace or the global scope, then the
+    // nested-name-specifier can't refer to the current instantiation.
+    if (Ctx->isFileContext())
+      return 0;
+
+    // Skip non-class contexts.
+    CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Ctx);
+    if (!Record)
+      continue;
+
+    // If this record type is not dependent, 
+    if (!Record->isDependentType())
+      return 0;
+
+    // C++ [temp.dep.type]p1:
+    //
+    //   In the definition of a class template, a nested class of a
+    //   class template, a member of a class template, or a member of a
+    //   nested class of a class template, a name refers to the current
+    //   instantiation if it is
+    //     -- the injected-class-name (9) of the class template or
+    //        nested class,
+    //     -- in the definition of a primary class template, the name
+    //        of the class template followed by the template argument
+    //        list of the primary template (as described below)
+    //        enclosed in <>,
+    //     -- in the definition of a nested class of a class template,
+    //        the name of the nested class referenced as a member of
+    //        the current instantiation, or 
+    //     -- in the definition of a partial specialization, the name
+    //        of the class template followed by the template argument
+    //        list of the partial specialization enclosed in <>. If
+    //        the nth template parameter is a parameter pack, the nth
+    //        template argument is a pack expansion (14.6.3) whose
+    //        pattern is the name of the parameter pack. (FIXME)
+    //
+    // All of these options come down to having the
+    // nested-name-specifier type that is equivalent to the
+    // injected-class-name of one of the types that is currently in
+    // our context.
+    if (Context.getTypeDeclType(Record) == T)
+      return Record;
+    
+    if (ClassTemplateDecl *Template = Record->getDescribedClassTemplate()) {
+      QualType InjectedClassName 
+        = Template->getInjectedClassNameType(Context);
+      if (T == Context.getCanonicalType(InjectedClassName))
+        return Template->getTemplatedDecl();
+    }
+  }
+
+  return 0;
+}
+
 /// \brief Require that the context specified by SS be complete.
 ///
 /// If SS refers to a type, this routine checks whether the type is
@@ -113,9 +204,10 @@
   NestedNameSpecifier *Prefix 
     = static_cast<NestedNameSpecifier *>(SS.getScopeRep());
 
-  // If the prefix is already dependent, there is no name lookup to
-  // perform. Just build the resulting nested-name-specifier.
-  if (Prefix && Prefix->isDependent())
+  // If the prefix already refers to an unknown specialization, there
+  // is no name lookup to perform. Just build the resulting
+  // nested-name-specifier.
+  if (Prefix && isUnknownSpecialization(SS))
     return NestedNameSpecifier::Create(Context, Prefix, &II);
 
   NamedDecl *SD = LookupParsedName(S, &SS, &II, LookupNestedNameSpecifierName);

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=71471&r1=71470&r2=71471&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Mon May 11 14:58:34 2009
@@ -64,16 +64,15 @@
   //   qualified-id denotes a type, forming an
   //   elaborated-type-specifier (7.1.5.3).
   //
-  // We therefore do not perform any name lookup up SS is a dependent
-  // scope name. FIXME: we will need to perform a special kind of
-  // lookup if the scope specifier names a member of the current
-  // instantiation.
-  if (SS && isDependentScopeSpecifier(*SS))
+  // We therefore do not perform any name lookup if the result would
+  // refer to a member of an unknown specialization.
+  if (SS && isUnknownSpecialization(*SS))
     return 0;
 
+  LookupResult Result 
+    = LookupParsedName(S, SS, &II, LookupOrdinaryName, false, false);
+
   NamedDecl *IIDecl = 0;
-  LookupResult Result = LookupParsedName(S, SS, &II, LookupOrdinaryName, 
-                                         false, false);
   switch (Result.getKind()) {
   case LookupResult::NotFound:
   case LookupResult::FoundOverloaded:
@@ -3402,7 +3401,7 @@
     }
   } else if (TK == TK_Reference && SS.isEmpty() && Name &&
              (Kind != TagDecl::TK_enum || !getLangOptions().CPlusPlus)) {
-    // C.scope.pdecl]p5:
+    // C++ [basic.scope.pdecl]p5:
     //   -- for an elaborated-type-specifier of the form 
     //
     //          class-key identifier

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=71471&r1=71470&r2=71471&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Mon May 11 14:58:34 2009
@@ -941,7 +941,7 @@
                                              DeclPtrTy TagDecl,
                                              SourceLocation LBrac,
                                              SourceLocation RBrac) {
-  TemplateDecl *Template = AdjustDeclIfTemplate(TagDecl);
+  AdjustDeclIfTemplate(TagDecl);
   ActOnFields(S, RLoc, TagDecl,
               (DeclPtrTy*)FieldCollector->getCurFields(),
               FieldCollector->getCurNumFields(), LBrac, RBrac, 0);
@@ -982,7 +982,7 @@
     }
   }
       
-  if (!Template)
+  if (!RD->isDependentType())
     AddImplicitlyDeclaredMembersToClass(RD);
 }
 

Modified: cfe/trunk/lib/Sema/SemaLookup.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLookup.cpp?rev=71471&r1=71470&r2=71471&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaLookup.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLookup.cpp Mon May 11 14:58:34 2009
@@ -17,6 +17,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Expr.h"
 #include "clang/Parse/DeclSpec.h"
 #include "clang/Basic/LangOptions.h"
@@ -1117,14 +1118,37 @@
                        DeclarationName Name, LookupNameKind NameKind,
                        bool RedeclarationOnly, bool AllowBuiltinCreation,
                        SourceLocation Loc) {
-  if (SS) {
-    if (SS->isInvalid() || RequireCompleteDeclContext(*SS))
+  if (SS && (SS->isSet() || SS->isInvalid())) {
+    // If the scope specifier is invalid, don't even look for
+    // anything.
+    if (SS->isInvalid())
       return LookupResult::CreateLookupResult(Context, 0);
 
-    if (SS->isSet()) {
-      return LookupQualifiedName(computeDeclContext(*SS),
-                                 Name, NameKind, RedeclarationOnly);
+    assert(!isUnknownSpecialization(*SS) && "Can't lookup dependent types");
+
+    if (isDependentScopeSpecifier(*SS)) {
+      // Determine whether we are looking into the current
+      // instantiation. 
+      NestedNameSpecifier *NNS 
+        = static_cast<NestedNameSpecifier *>(SS->getScopeRep());
+      CXXRecordDecl *Current = getCurrentInstantiationOf(NNS);
+      assert(Current && "Bad dependent scope specifier");
+      
+      // We nested name specifier refers to the current instantiation,
+      // so now we will look for a member of the current instantiation
+      // (C++0x [temp.dep.type]).
+      unsigned IDNS = getIdentifierNamespacesFromLookupNameKind(NameKind, true);
+      DeclContext::lookup_iterator I, E;
+      for (llvm::tie(I, E) = Current->lookup(Context, Name); I != E; ++I)
+        if (isAcceptableLookupResult(*I, NameKind, IDNS))
+          return LookupResult::CreateLookupResult(Context, I, E);
     }
+
+    if (RequireCompleteDeclContext(*SS))
+      return LookupResult::CreateLookupResult(Context, 0);
+
+    return LookupQualifiedName(computeDeclContext(*SS),
+                               Name, NameKind, RedeclarationOnly);
   }
 
   return LookupName(S, Name, NameKind, RedeclarationOnly, 
@@ -1601,4 +1625,3 @@
     }
   }
 }
-

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=71471&r1=71470&r2=71471&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Mon May 11 14:58:34 2009
@@ -2170,16 +2170,29 @@
 QualType
 Sema::CheckTypenameType(NestedNameSpecifier *NNS, const IdentifierInfo &II,
                         SourceRange Range) {
-  if (NNS->isDependent()) // FIXME: member of the current instantiation!
-    return Context.getTypenameType(NNS, &II);
+  CXXRecordDecl *CurrentInstantiation = 0;
+  if (NNS->isDependent()) {
+    CurrentInstantiation = getCurrentInstantiationOf(NNS);
+
+    // If the nested-name-specifier does not refer to the current
+    // instantiation, then build a typename type.
+    if (!CurrentInstantiation)
+      return Context.getTypenameType(NNS, &II);
+  }
 
-  CXXScopeSpec SS;
-  SS.setScopeRep(NNS);
-  SS.setRange(Range);
-  if (RequireCompleteDeclContext(SS))
-    return QualType();
+  DeclContext *Ctx = 0;
 
-  DeclContext *Ctx = computeDeclContext(SS);
+  if (CurrentInstantiation)
+    Ctx = CurrentInstantiation;
+  else {
+    CXXScopeSpec SS;
+    SS.setScopeRep(NNS);
+    SS.setRange(Range);
+    if (RequireCompleteDeclContext(SS))
+      return QualType();
+
+    Ctx = computeDeclContext(SS);
+  }
   assert(Ctx && "No declaration context?");
 
   DeclarationName Name(&II);

Added: cfe/trunk/test/SemaTemplate/current-instantiation.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/current-instantiation.cpp?rev=71471&view=auto

==============================================================================
--- cfe/trunk/test/SemaTemplate/current-instantiation.cpp (added)
+++ cfe/trunk/test/SemaTemplate/current-instantiation.cpp Mon May 11 14:58:34 2009
@@ -0,0 +1,71 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+// This test concerns the identity of dependent types within the
+// canonical type system, specifically focusing on the difference
+// between members of the current instantiation and membmers of an
+// unknown specialization. This considers C++ [temp.type], which
+// specifies type equivalence within a template, and C++0x
+// [temp.dep.type], which defines what it means to be a member of the
+// current instantiation.
+
+template<typename T, typename U>
+struct X0 {
+  typedef T T_type;
+  typedef U U_type;
+
+  void f0(T&); // expected-note{{previous}}
+  void f0(typename X0::U_type&);
+  void f0(typename X0::T_type&); // expected-error{{redecl}}
+
+  void f1(T&); // expected-note{{previous}}
+  void f1(typename X0::U_type&);
+  void f1(typename X0<T, U>::T_type&); // expected-error{{redecl}}
+
+  void f2(T&); // expected-note{{previous}}
+  void f2(typename X0::U_type&);
+  void f2(typename X0<T_type, U_type>::T_type&); // expected-error{{redecl}}
+
+  void f3(T&); // expected-note{{previous}}
+  void f3(typename X0::U_type&);
+  void f3(typename ::X0<T_type, U_type>::T_type&); // expected-error{{redecl}}
+
+  struct X1 {
+    typedef T my_T_type;
+
+    void g0(T&); // expected-note{{previous}}
+    void g0(typename X0::U_type&);
+    void g0(typename X0::T_type&); // expected-error{{redecl}}
+
+    void g1(T&); // expected-note{{previous}}
+    void g1(typename X0::U_type&);
+    void g1(typename X0<T, U>::T_type&); // expected-error{{redecl}}
+    
+    void g2(T&); // expected-note{{previous}}
+    void g2(typename X0::U_type&);
+    void g2(typename X0<T_type, U_type>::T_type&); // expected-error{{redecl}}
+    
+    void g3(T&); // expected-note{{previous}}
+    void g3(typename X0::U_type&);
+    void g3(typename ::X0<T_type, U_type>::T_type&); // expected-error{{redecl}}
+
+    void g4(T&); // expected-note{{previous}}
+    void g4(typename X0::U_type&);
+    void g4(typename X1::my_T_type&); // expected-error{{redecl}}
+
+    void g5(T&); // expected-note{{previous}}
+    void g5(typename X0::U_type&);
+    void g5(typename X0::X1::my_T_type&); // expected-error{{redecl}}
+
+    void g6(T&); // expected-note{{previous}}
+    void g6(typename X0::U_type&);
+    void g6(typename X0<T, U>::X1::my_T_type&); // expected-error{{redecl}}
+
+    void g7(T&); // expected-note{{previous}}
+    void g7(typename X0::U_type&);
+    void g7(typename ::X0<typename X1::my_T_type, U_type>::X1::my_T_type&); // expected-error{{redecl}}
+
+    void g8(T&); // expected-note{{previous}}
+    void g8(typename X0<U, T_type>::T_type&);
+    void g8(typename ::X0<typename X0<T_type, U>::X1::my_T_type, U_type>::X1::my_T_type&); // expected-error{{redecl}}
+  };
+};

Modified: cfe/trunk/test/SemaTemplate/dependent-type-identity.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/dependent-type-identity.cpp?rev=71471&r1=71470&r2=71471&view=diff

==============================================================================
--- cfe/trunk/test/SemaTemplate/dependent-type-identity.cpp (original)
+++ cfe/trunk/test/SemaTemplate/dependent-type-identity.cpp Mon May 11 14:58:34 2009
@@ -5,7 +5,6 @@
 // specifies type equivalence within a template.
 //
 // FIXME: template template parameters
-// FIXME: members of the current instantiation
 
 namespace N {
   template<typename T>





More information about the cfe-commits mailing list