From: Douglas Gregor Date: Sat, 16 Jan 2010 20:52:59 +0000 (+0000) Subject: While determining when to parse inline member functions of a class, X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=26997fd58c9560584edd154618f2f2c15ee68af4;p=clang While determining when to parse inline member functions of a class, distinguish between nested classes (whose member functions cannot be parsed until the innermost non-nested class is complete) and local classes (that are defined within a function but are not necessarily nested). The upshot of this change, which fixes PR5764, is that the bodies of member functions of local (non-nested) classes need to be parsed when the local class is complete (and no later), since they may refer to function-local static variables, typedefs, enums, etc. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@93653 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 90040c54bf..a5ef817d43 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -1398,15 +1398,35 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, PP.getSourceManager(), "parsing struct/union/class body"); - // Determine whether this is a top-level (non-nested) class. - bool TopLevelClass = ClassStack.empty() || - CurScope->isInCXXInlineMethodScope(); + // Determine whether this is a non-nested class. Note that local + // classes are *not* considered to be nested classes. + bool NonNestedClass = true; + if (!ClassStack.empty()) { + for (const Scope *S = CurScope; S; S = S->getParent()) { + if (S->isClassScope()) { + // We're inside a class scope, so this is a nested class. + NonNestedClass = false; + break; + } + + if ((S->getFlags() & Scope::FnScope)) { + // If we're in a function or function template declared in the + // body of a class, then this is a local class rather than a + // nested class. + const Scope *Parent = S->getParent(); + if (Parent->isTemplateParamScope()) + Parent = Parent->getParent(); + if (Parent->isClassScope()) + break; + } + } + } // 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); + ParsingClassDefinition ParsingDef(*this, TagDecl, NonNestedClass); if (TagDecl) Actions.ActOnTagStartDefinition(CurScope, TagDecl); @@ -1484,7 +1504,7 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, // // FIXME: Only function bodies and constructor ctor-initializers are // parsed correctly, fix the rest. - if (TopLevelClass) { + if (NonNestedClass) { // 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. @@ -1666,10 +1686,10 @@ bool Parser::ParseExceptionSpecification(SourceLocation &EndLoc, /// \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()) && +void Parser::PushParsingClass(DeclPtrTy ClassDecl, bool NonNestedClass) { + assert((NonNestedClass || !ClassStack.empty()) && "Nested class without outer class"); - ClassStack.push(new ParsingClass(ClassDecl, TopLevelClass)); + ClassStack.push(new ParsingClass(ClassDecl, NonNestedClass)); } /// \brief Deallocate the given parsed class and all of its nested diff --git a/test/SemaTemplate/instantiate-local-class.cpp b/test/SemaTemplate/instantiate-local-class.cpp index 5cb63b49ee..eaaae9b6ee 100644 --- a/test/SemaTemplate/instantiate-local-class.cpp +++ b/test/SemaTemplate/instantiate-local-class.cpp @@ -10,3 +10,22 @@ void f0() { } template void f0(); + +// PR5764 +namespace PR5764 { + class X { + template + void Bar() { + class Y { + Y() {} + }; + + Y y; + } + }; + + void test(X x) { + x.Bar(); + } +} +