]> granicus.if.org Git - clang/commitdiff
Reimplement much of the way that we track nested classes in the
authorDouglas Gregor <dgregor@apple.com>
Wed, 27 May 2009 23:11:45 +0000 (23:11 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 27 May 2009 23:11:45 +0000 (23:11 +0000)
parser. Rather than placing all of the delayed member function
declarations and inline definitions into a single bucket corresponding
to the top-level class, we instead mirror the nesting structure of the
nested classes and place the delayed member functions into their
appropriate place. Then, when we actually parse the delayed member
function declarations, set up the scope stack the same way as it was
when we originally saw the declaration, so that we can find, e.g.,
template parameters that are in scope.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@72502 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Parse/Action.h
include/clang/Parse/Parser.h
lib/Parse/ParseCXXInlineMethods.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Parse/Parser.cpp
lib/Sema/Sema.h
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaTemplateInstantiateStmt.cpp
test/SemaTemplate/instantiate-declref.cpp

index 95ecbecba012b433e2cd41f1c886af0ba8c7ff3b..8fa9f8cc49ba8bbc02df0a790036d2b14491a181 100644 (file)
@@ -930,6 +930,24 @@ public:
     return;
   }
 
+  /// \brief Called when we re-enter a template parameter scope.
+  ///
+  /// This action occurs when we are going to parse an member
+  /// function's default arguments or inline definition after the
+  /// outermost class definition has been completed, and when one or
+  /// more of the class definitions enclosing the member function is a
+  /// template. The "entity" in the given scope will be set as it was
+  /// when we entered the scope of the template initially, and should
+  /// be used to, e.g., reintroduce the names of template parameters
+  /// into the current scope so that they can be found by name lookup.
+  ///
+  /// \param S The (new) template parameter scope.
+  ///
+  /// \param Template the class template declaration whose template
+  /// parameters should be reintroduced into the current scope.
+  virtual void ActOnReenterTemplateScope(Scope *S, DeclPtrTy Template) {
+  }
+
   /// ActOnStartDelayedCXXMethodDeclaration - We have completed
   /// parsing a top-level (non-nested) C++ class, and we are now
   /// parsing those parts of the given Method declaration that could
index 910a921c448649dd29b7aade30f616c9e2d87d56..9750cab5e7801aa67612435e2c19ac21dc21ff2b 100644 (file)
@@ -512,11 +512,26 @@ private:
   /// nested classes are lexed and stored here.
   typedef std::list<LexedMethod> LexedMethodsForTopClass;
 
+  /// \brief Representation of a class that has been parsed, including
+  /// any member function declarations or definitions that need to be
+  /// parsed after the corresponding top-level class is complete.
+  struct ParsingClass {
+    ParsingClass(DeclPtrTy TagOrTemplate, bool TopLevelClass) 
+      : TopLevelClass(TopLevelClass), TemplateScope(false), 
+        TagOrTemplate(TagOrTemplate) { }
+
+    /// \brief Whether this is a "top-level" class, meaning that it is
+    /// not nested within another class.
+    bool TopLevelClass : 1;
+
+    /// \brief Whether this class had an associated template
+    /// scope. When true, TagOrTemplate is a template declaration;
+    /// othewise, it is a tag declaration.
+    bool TemplateScope : 1;
+
+    /// \brief The class or class template whose definition we are parsing.
+    DeclPtrTy TagOrTemplate;
 
-  /// TopClass - Contains information about parts of the top
-  /// (non-nested) C++ class that will need to be parsed after the
-  /// class is fully defined.
-  struct TopClass {
     /// MethodDecls - Method declarations that contain pieces whose
     /// parsing will be delayed until the class is fully defined.
     LateParsedMethodDecls MethodDecls;
@@ -524,27 +539,52 @@ private:
     /// MethodDefs - Methods whose definitions will be parsed once the
     /// class has been fully defined.
     LexedMethodsForTopClass MethodDefs;
+
+    /// \brief Nested classes inside this class.
+    llvm::SmallVector<ParsingClass*, 4> NestedClasses;
   };
 
-  /// TopClassStacks - This is initialized with one TopClass used
-  /// for lexing all top classes, until a local class in an inline method is
-  /// encountered, at which point a new TopClass is pushed here
-  /// and used until the parsing of that local class is finished.
-  std::stack<TopClass> TopClassStacks;
+  /// \brief The stack of classes that is currently being
+  /// parsed. Nested and local classes will be pushed onto this stack
+  /// when they are parsed, and removed afterward.
+  std::stack<ParsingClass *> ClassStack;
 
-  TopClass &getCurTopClassStack() {
-    assert(!TopClassStacks.empty() && "No lexed method stacks!");
-    return TopClassStacks.top();
+  ParsingClass &getCurrentClass() {
+    assert(!ClassStack.empty() && "No lexed method stacks!");
+    return *ClassStack.top();
   }
 
-  void PushTopClassStack() {
-    TopClassStacks.push(TopClass());
-  }
-  void PopTopClassStack() { TopClassStacks.pop(); }
+  /// \brief RAII object used to 
+  class ParsingClassDefinition {
+    Parser &P;
+    bool Popped;
+
+  public:
+    ParsingClassDefinition(Parser &P, DeclPtrTy TagOrTemplate, bool TopLevelClass) 
+      : P(P), Popped(false) { 
+      P.PushParsingClass(TagOrTemplate, TopLevelClass);
+    }
+
+    /// \brief Pop this class of the stack.
+    void Pop() { 
+      assert(!Popped && "Nested class has already been popped");
+      Popped = true;
+      P.PopParsingClass();
+    }
+
+    ~ParsingClassDefinition() { 
+      if (!Popped)
+        P.PopParsingClass(); 
+    }
+  };
+
+  void PushParsingClass(DeclPtrTy TagOrTemplate, bool TopLevelClass);
+  void DeallocateParsedClasses(ParsingClass *Class);
+  void PopParsingClass();
 
   DeclPtrTy ParseCXXInlineMethodDef(AccessSpecifier AS, Declarator &D);
-  void ParseLexedMethodDeclarations();
-  void ParseLexedMethodDefs();
+  void ParseLexedMethodDeclarations(ParsingClass &Class);
+  void ParseLexedMethodDefs(ParsingClass &Class);
   bool ConsumeAndStoreUntil(tok::TokenKind T1, tok::TokenKind T2, 
                             CachedTokens &Toks,
                             tok::TokenKind EarlyAbortIf = tok::unknown,
index 525b088714d19707f628b75d9f07c02e3416f59f..af6fab7cb188c7de4c9fa5998d0197ba0b0e1d10 100644 (file)
@@ -31,8 +31,8 @@ Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, Declarator &D) {
 
   // Consume the tokens and store them for later parsing.
 
-  getCurTopClassStack().MethodDefs.push_back(LexedMethod(FnD));
-  CachedTokens &Toks = getCurTopClassStack().MethodDefs.back().Toks;
+  getCurrentClass().MethodDefs.push_back(LexedMethod(FnD));
+  CachedTokens &Toks = getCurrentClass().MethodDefs.back().Toks;
 
   tok::TokenKind kind = Tok.getKind();
   // We may have a constructor initializer or function-try-block here.
@@ -46,7 +46,7 @@ Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, Declarator &D) {
         // don't try to parse this method later.
         Diag(Tok.getLocation(), diag::err_expected_lbrace);
         ConsumeAnyToken();
-        getCurTopClassStack().MethodDefs.pop_back();
+        getCurrentClass().MethodDefs.pop_back();
         return FnD;
       }
     }
@@ -74,11 +74,22 @@ Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, Declarator &D) {
 /// specification of a top (non-nested) C++ class. Now go over the
 /// stack of method declarations with some parts for which parsing was
 /// delayed (such as default arguments) and parse them.
-void Parser::ParseLexedMethodDeclarations() {
-  for (; !getCurTopClassStack().MethodDecls.empty();
-       getCurTopClassStack().MethodDecls.pop_front()) {
-    LateParsedMethodDeclaration &LM = getCurTopClassStack().MethodDecls.front();
+void Parser::ParseLexedMethodDeclarations(ParsingClass &Class) {
+  bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope;
+  ParseScope TemplateScope(this, Scope::TemplateParamScope, HasTemplateScope);
+  if (HasTemplateScope)
+    Actions.ActOnReenterTemplateScope(CurScope, Class.TagOrTemplate);
+
+  bool HasClassScope = !Class.TopLevelClass;
+  ParseScope ClassScope(this, Scope::ClassScope|Scope::DeclScope,
+                        HasClassScope);
+
+  for (; !Class.MethodDecls.empty(); Class.MethodDecls.pop_front()) {
+    LateParsedMethodDeclaration &LM = Class.MethodDecls.front();
     
+    // FIXME: For member function templates, we'll need to introduce a
+    // scope for the template parameters.
+
     // Start the delayed C++ method declaration
     Actions.ActOnStartDelayedCXXMethodDeclaration(CurScope, LM.Method);
 
@@ -117,15 +128,26 @@ void Parser::ParseLexedMethodDeclarations() {
     // Finish the delayed C++ method declaration.
     Actions.ActOnFinishDelayedCXXMethodDeclaration(CurScope, LM.Method);
   }
+
+  for (unsigned I = 0, N = Class.NestedClasses.size(); I != N; ++I)
+    ParseLexedMethodDeclarations(*Class.NestedClasses[I]);
 }
 
 /// ParseLexedMethodDefs - We finished parsing the member specification of a top
 /// (non-nested) C++ class. Now go over the stack of lexed methods that were
 /// collected during its parsing and parse them all.
-void Parser::ParseLexedMethodDefs() {
-  for (; !getCurTopClassStack().MethodDefs.empty(); 
-       getCurTopClassStack().MethodDefs.pop_front()) {
-    LexedMethod &LM = getCurTopClassStack().MethodDefs.front();
+void Parser::ParseLexedMethodDefs(ParsingClass &Class) {
+  bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope;
+  ParseScope TemplateScope(this, Scope::TemplateParamScope, HasTemplateScope);
+  if (HasTemplateScope)
+    Actions.ActOnReenterTemplateScope(CurScope, Class.TagOrTemplate);
+
+  bool HasClassScope = !Class.TopLevelClass;
+  ParseScope ClassScope(this, Scope::ClassScope|Scope::DeclScope,
+                        HasClassScope);
+
+  for (; !Class.MethodDefs.empty(); Class.MethodDefs.pop_front()) {
+    LexedMethod &LM = Class.MethodDefs.front();
 
     assert(!LM.Toks.empty() && "Empty body!");
     // Append the current token at the end of the new token stream so that it
@@ -152,6 +174,9 @@ void Parser::ParseLexedMethodDefs() {
     // FIXME: What if ParseConstructorInitializer doesn't leave us with a '{'??
     ParseFunctionStatementBody(LM.D);
   }
+
+  for (unsigned I = 0, N = Class.NestedClasses.size(); I != N; ++I)
+    ParseLexedMethodDefs(*Class.NestedClasses[I]);
 }
 
 /// ConsumeAndStoreUntil - Consume and store the token at the passed token
index ee142138768af4ec062265305955fd8aed12cb3e..a7d3b52bd2393309cebe4def4a42239f3b7ab805 100644 (file)
@@ -921,9 +921,9 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS) {
           if (!LateMethod) {
             // Push this method onto the stack of late-parsed method
             // declarations.
-            getCurTopClassStack().MethodDecls.push_back(
+            getCurrentClass().MethodDecls.push_back(
                                    LateParsedMethodDeclaration(ThisDecl));
-            LateMethod = &getCurTopClassStack().MethodDecls.back();
+            LateMethod = &getCurrentClass().MethodDecls.back();
 
             // Add all of the parameters prior to this one (they don't
             // have default arguments).
@@ -1000,16 +1000,16 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
   
   SourceLocation LBraceLoc = ConsumeBrace();
 
-  if (!CurScope->isClassScope() && // Not about to define a nested class.
-      CurScope->isInCXXInlineMethodScope()) {
-    // We will define a local class of an inline method.
-    // Push a new LexedMethodsForTopClass for its inline methods.
-    PushTopClassStack();
-  }
+  // Determine whether this is a top-level (non-nested) class.
+  bool TopLevelClass = ClassStack.empty() || 
+    CurScope->isInCXXInlineMethodScope();
 
   // Enter a scope for the class.
   ParseScope ClassScope(this, Scope::ClassScope|Scope::DeclScope);
 
+  // Note that we are parsing a new (potentially-nested) class definition.
+  ParsingClassDefinition ParsingDef(*this, TagDecl, TopLevelClass);
+
   if (TagDecl)
     Actions.ActOnTagStartDefinition(CurScope, TagDecl);
   else {
@@ -1067,26 +1067,16 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
   //
   // FIXME: Only function bodies and constructor ctor-initializers are
   // parsed correctly, fix the rest.
-  if (!CurScope->getParent()->isClassScope() && 
-      !(CurScope->getParent()->isTemplateParamScope() &&
-        CurScope->getParent()->getParent()->isClassScope())) {
+  if (TopLevelClass) {
     // We are not inside a nested class. This class and its nested classes
     // are complete and we can parse the delayed portions of method
     // declarations and the lexed inline method definitions.
-    ParseLexedMethodDeclarations();
-    ParseLexedMethodDefs();
-
-    // For a local class of inline method, pop the LexedMethodsForTopClass that
-    // was previously pushed.
-
-    assert((CurScope->isInCXXInlineMethodScope() ||
-           TopClassStacks.size() == 1) &&
-           "MethodLexers not getting popped properly!");
-    if (CurScope->isInCXXInlineMethodScope())
-      PopTopClassStack();
+    ParseLexedMethodDeclarations(getCurrentClass());
+    ParseLexedMethodDefs(getCurrentClass());
   }
 
   // Leave the class scope.
+  ParsingDef.Pop();
   ClassScope.Exit();
 
   Actions.ActOnTagFinishDefinition(CurScope, TagDecl);
@@ -1236,3 +1226,60 @@ bool Parser::ParseExceptionSpecification(SourceLocation &EndLoc,
   EndLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc);
   return false;
 }
+
+/// \brief We have just started parsing the definition of a new class,
+/// so push that class onto our stack of classes that is currently
+/// being parsed.
+void Parser::PushParsingClass(DeclPtrTy ClassDecl, bool TopLevelClass) {
+  assert((TopLevelClass || !ClassStack.empty()) && 
+         "Nested class without outer class");
+  ClassStack.push(new ParsingClass(ClassDecl, TopLevelClass));
+}
+
+/// \brief Deallocate the given parsed class and all of its nested
+/// classes.
+void Parser::DeallocateParsedClasses(Parser::ParsingClass *Class) {
+  for (unsigned I = 0, N = Class->NestedClasses.size(); I != N; ++I)
+    DeallocateParsedClasses(Class->NestedClasses[I]);
+  delete Class;
+}
+
+/// \brief Pop the top class of the stack of classes that are
+/// currently being parsed.
+///
+/// This routine should be called when we have finished parsing the
+/// definition of a class, but have not yet popped the Scope
+/// associated with the class's definition.
+///
+/// \returns true if the class we've popped is a top-level class,
+/// false otherwise.
+void Parser::PopParsingClass() {
+  assert(!ClassStack.empty() && "Mismatched push/pop for class parsing");
+  
+  ParsingClass *Victim = ClassStack.top();
+  ClassStack.pop();
+  if (Victim->TopLevelClass) {
+    // Deallocate all of the nested classes of this class,
+    // recursively: we don't need to keep any of this information.
+    DeallocateParsedClasses(Victim);
+    return;
+  } 
+  assert(!ClassStack.empty() && "Missing top-level class?");
+
+  if (Victim->MethodDecls.empty() && Victim->MethodDefs.empty() &&
+      Victim->NestedClasses.empty()) {
+    // The victim is a nested class, but we will not need to perform
+    // any processing after the definition of this class since it has
+    // no members whose handling was delayed. Therefore, we can just
+    // remove this nested class.
+    delete Victim;
+    return;
+  }
+
+  // This nested class has some members that will need to be processed
+  // after the top-level class is completely defined. Therefore, add
+  // it to the list of nested classes within its parent.
+  assert(CurScope->isClassScope() && "Nested class outside of class scope?");
+  ClassStack.top()->NestedClasses.push_back(Victim);
+  Victim->TemplateScope = CurScope->getParent()->isTemplateParamScope();
+}
index 12e584377d851e53cf74b18f1b3fab4814292f03..1c2e8a62e1f9d8a3251ac15b889a735770fa1c6c 100644 (file)
@@ -39,9 +39,6 @@ Parser::Parser(Preprocessor &pp, Action &actions)
           PragmaUnusedHandler(&PP.getIdentifierTable().get("unused"), actions,
                               *this));
   PP.AddPragmaHandler(0, UnusedHandler.get());
-
-  // Instantiate a LexedMethodsForTopClass for all the non-nested classes.
-  PushTopClassStack();
 }
 
 /// If a crash happens while the parser is active, print out a line indicating
index 3825b7cc06a2c3a44b2614f8414e4a0296fccdeb..0a285bd3095028e8b66b85306ecfdc43b0df6cdf 100644 (file)
@@ -1731,6 +1731,7 @@ public:
                                                  SourceLocation LBrac,
                                                  SourceLocation RBrac);
 
+  virtual void ActOnReenterTemplateScope(Scope *S, DeclPtrTy Template);
   virtual void ActOnStartDelayedCXXMethodDeclaration(Scope *S,
                                                      DeclPtrTy Method);
   virtual void ActOnDelayedCXXMethodParameter(Scope *S, DeclPtrTy Param);
index 6edb29044c3c91b9f6c7dabc69fcad764179e424..0bf97f560d548392a51e1b8be95c5f0235241470 100644 (file)
@@ -1205,6 +1205,23 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) {
   }
 }
 
+void Sema::ActOnReenterTemplateScope(Scope *S, DeclPtrTy TemplateD) {
+  TemplateDecl *Template = TemplateD.getAs<TemplateDecl>();
+  if (!Template)
+    return;
+
+  TemplateParameterList *Params = Template->getTemplateParameters();
+  for (TemplateParameterList::iterator Param = Params->begin(),
+                                    ParamEnd = Params->end();
+       Param != ParamEnd; ++Param) {
+    NamedDecl *Named = cast<NamedDecl>(*Param);
+    if (Named->getDeclName()) {
+      S->AddDecl(DeclPtrTy::make(Named));
+      IdResolver.AddDecl(Named);
+    }
+  }
+}
+
 /// ActOnStartDelayedCXXMethodDeclaration - We have completed
 /// parsing a top-level (non-nested) C++ class, and we are now
 /// parsing those parts of the given Method declaration that could
index 6d2e28aff80129ab4f774f52577684e227bfd4c3..a62607d116ab1eae68f810f5ad9747d1db191ee5 100644 (file)
@@ -69,8 +69,7 @@ Sema::OwningStmtResult TemplateStmtInstantiator::VisitDeclStmt(DeclStmt *S) {
       return SemaRef.StmtError();
 
     Decls.push_back(Instantiated);
-    SemaRef.CurrentInstantiationScope->InstantiatedLocal(cast<VarDecl>(*D),
-                                                  cast<VarDecl>(Instantiated));
+    SemaRef.CurrentInstantiationScope->InstantiatedLocal(*D, Instantiated);
   }
 
   return SemaRef.Owned(new (SemaRef.Context) DeclStmt(
index 0e1e562056e15a19087605f7d1cac481df4db092..1a5f21663994b7196e5ef18ba7e4de935271cfd8 100644 (file)
@@ -38,3 +38,24 @@ namespace N {
 typedef int INT;
 template struct N::Outer::Inner::InnerTemplate<INT>::VeryInner;
 template struct N::Outer::Inner::InnerTemplate<INT>::UeberInner; // expected-error{{'UeberInner' does not name a tag member}}
+
+namespace N2 {
+  struct Outer2 {
+    template<typename T>
+    struct Inner {
+      void foo() {
+        enum { K1Val = sizeof(T) } k1;
+        enum K2 { K2Val = sizeof(T)*2 };
+
+        K2 k2 = K2Val;
+
+        Inner i1;
+        i1.foo();
+        Inner<T> i2;
+        i2.foo();
+      }
+    };
+  };
+}
+
+// FIXME: template struct N2::Outer2::Inner<float>;