diff options
-rw-r--r-- | include/clang/Parse/Action.h | 18 | ||||
-rw-r--r-- | include/clang/Parse/Parser.h | 76 | ||||
-rw-r--r-- | lib/Parse/ParseCXXInlineMethods.cpp | 47 | ||||
-rw-r--r-- | lib/Parse/ParseDeclCXX.cpp | 91 | ||||
-rw-r--r-- | lib/Parse/Parser.cpp | 3 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 1 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 17 | ||||
-rw-r--r-- | lib/Sema/SemaTemplateInstantiateStmt.cpp | 3 | ||||
-rw-r--r-- | test/SemaTemplate/instantiate-declref.cpp | 21 |
9 files changed, 221 insertions, 56 deletions
diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 95ecbecba0..8fa9f8cc49 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -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 diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 910a921c44..9750cab5e7 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -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, diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp index 525b088714..af6fab7cb1 100644 --- a/lib/Parse/ParseCXXInlineMethods.cpp +++ b/lib/Parse/ParseCXXInlineMethods.cpp @@ -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 diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index ee14213876..a7d3b52bd2 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -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(); +} diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 12e584377d..1c2e8a62e1 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -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 diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 3825b7cc06..0a285bd309 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 6edb29044c..0bf97f560d 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -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 diff --git a/lib/Sema/SemaTemplateInstantiateStmt.cpp b/lib/Sema/SemaTemplateInstantiateStmt.cpp index 6d2e28aff8..a62607d116 100644 --- a/lib/Sema/SemaTemplateInstantiateStmt.cpp +++ b/lib/Sema/SemaTemplateInstantiateStmt.cpp @@ -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( diff --git a/test/SemaTemplate/instantiate-declref.cpp b/test/SemaTemplate/instantiate-declref.cpp index 0e1e562056..1a5f216639 100644 --- a/test/SemaTemplate/instantiate-declref.cpp +++ b/test/SemaTemplate/instantiate-declref.cpp @@ -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>; |