From 8387e2a41eef6fa17fb140a18c29b6eee9dd2b8a Mon Sep 17 00:00:00 2001 From: Francois Pichet Date: Fri, 22 Apr 2011 22:18:13 +0000 Subject: [PATCH] Add -fdelayed-template-parsing option. Using this option all templated function definitions are parsed at the end of the translation unit only if it is required by an actual instantiation. As such all the symbols of the TU are available during name lookup. Using this flag is necessary for compatibility with Microsoft template code. This also provides some parsing speed improvement. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@130022 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Decl.h | 12 ++- include/clang/Basic/LangOptions.h | 2 + include/clang/Driver/CC1Options.td | 3 + include/clang/Driver/Options.td | 2 + include/clang/Parse/Parser.h | 21 ++++ include/clang/Sema/Sema.h | 13 +++ lib/AST/Decl.cpp | 5 +- lib/CodeGen/CodeGenModule.cpp | 8 +- lib/Driver/Tools.cpp | 6 ++ lib/Frontend/CompilerInvocation.cpp | 6 ++ lib/Parse/ParseCXXInlineMethods.cpp | 31 ++++++ lib/Parse/ParseTemplate.cpp | 124 +++++++++++++++++++++++ lib/Parse/Parser.cpp | 47 +++++++++ lib/Sema/Sema.cpp | 1 + lib/Sema/SemaDecl.cpp | 7 +- lib/Sema/SemaDeclCXX.cpp | 19 ++++ lib/Sema/SemaTemplate.cpp | 21 ++++ lib/Sema/SemaTemplateInstantiateDecl.cpp | 15 +++ test/Parser/DelayedTemplateParsing.cpp | 31 ++++++ tools/libclang/CIndex.cpp | 2 +- 20 files changed, 369 insertions(+), 7 deletions(-) create mode 100644 test/Parser/DelayedTemplateParsing.cpp diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 9255b280d6..5d2a374f72 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -1294,6 +1294,7 @@ private: bool IsDeleted : 1; bool IsTrivial : 1; // sunk from CXXMethodDecl bool HasImplicitReturnZero : 1; + bool IsLateTemplateParsed : 1; /// \brief End part of this FunctionDecl's source range. /// @@ -1375,7 +1376,8 @@ protected: IsInline(isInlineSpecified), IsInlineSpecified(isInlineSpecified), IsVirtualAsWritten(false), IsPure(false), HasInheritedPrototype(false), HasWrittenPrototype(true), IsDeleted(false), IsTrivial(false), - HasImplicitReturnZero(false), EndRangeLoc(NameInfo.getEndLoc()), + HasImplicitReturnZero(false), IsLateTemplateParsed(false), + EndRangeLoc(NameInfo.getEndLoc()), TemplateOrSpecialization(), DNLoc(NameInfo.getInfo()) {} @@ -1458,7 +1460,9 @@ public: /// previous definition); for that information, use getBody. /// FIXME: Should return true if function is deleted or defaulted. However, /// CodeGenModule.cpp uses it, and I don't know if this would break it. - bool isThisDeclarationADefinition() const { return Body; } + bool isThisDeclarationADefinition() const { + return Body || IsLateTemplateParsed; + } void setBody(Stmt *B); void setLazyBody(uint64_t Offset) { Body = Offset; } @@ -1475,6 +1479,10 @@ public: bool isPure() const { return IsPure; } void setPure(bool P = true); + /// Whether this templated function will be late parsed. + bool isLateTemplateParsed() const { return IsLateTemplateParsed; } + void setLateTemplateParsed(bool ILT = true) { IsLateTemplateParsed = ILT; } + /// Whether this function is "trivial" in some specialized C++ senses. /// Can only be true for default constructors, copy constructors, /// copy assignment operators, and destructors. Not meaningful until diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h index 3a739eaf4a..91287815cd 100644 --- a/include/clang/Basic/LangOptions.h +++ b/include/clang/Basic/LangOptions.h @@ -130,6 +130,7 @@ public: // testing languages such as OpenCL. unsigned MRTD : 1; // -mrtd calling convention + unsigned DelayedTemplateParsing : 1; // Delayed template parsing private: // We declare multibit enums as unsigned because MSVC insists on making enums @@ -225,6 +226,7 @@ public: NoBitFieldTypeAlign = 0; FakeAddressSpaceMap = 0; MRTD = 0; + DelayedTemplateParsing = 0; ParseUnknownAnytype = 0; } diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index 0c2a077a93..cef94968d0 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -532,6 +532,9 @@ def traditional_cpp : Flag<"-traditional-cpp">, HelpText<"Enable some traditional CPP emulation">; def ffake_address_space_map : Flag<"-ffake-address-space-map">, HelpText<"Use a fake address space map; OpenCL testing purposes only">; +def fdelayed_template_parsing : Flag<"-fdelayed-template-parsing">, + HelpText<"Parse templated function definitions at the end of the " + "translation unit ">; def funknown_anytype : Flag<"-funknown-anytype">, HelpText<"Enable parser support for the __unknown_anytype type; for testing purposes only">; diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 542728f2f1..6374eebb61 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -314,6 +314,7 @@ def fmerge_all_constants : Flag<"-fmerge-all-constants">, Group; def fmessage_length_EQ : Joined<"-fmessage-length=">, Group; def fms_extensions : Flag<"-fms-extensions">, Group; def fmsc_version : Joined<"-fmsc-version=">, Group; +def fdelayed_template_parsing : Flag<"-fdelayed-template-parsing">, Group; def fmudflapth : Flag<"-fmudflapth">, Group; def fmudflap : Flag<"-fmudflap">, Group; def fnested_functions : Flag<"-fnested-functions">, Group; @@ -349,6 +350,7 @@ def fno_lax_vector_conversions : Flag<"-fno-lax-vector-conversions">, Group, Group; def fno_merge_all_constants : Flag<"-fno-merge-all-constants">, Group; def fno_ms_extensions : Flag<"-fno-ms-extensions">, Group; +def fno_delayed_template_parsing : Flag<"-fno-delayed-template-parsing">, Group; def fno_objc_default_synthesize_properties : Flag<"-fno-objc-default-synthesize-properties">, Group; def fno_objc_exceptions: Flag<"-fno-objc-exceptions">, Group; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index efc622cc0d..0880e5416e 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -919,6 +919,27 @@ private: SourceRange getSourceRange() const; }; + /// \brief Contains a late templated function. + /// Will be parsed at the end of the translation unit. + struct LateParsedTemplatedFunction { + explicit LateParsedTemplatedFunction(Parser* P, Decl *MD) + : D(MD) {} + + CachedTokens Toks; + + /// \brief The template function declaration to be late parsed. + Decl *D; + }; + + void LexTemplateFunctionForLateParsing(CachedTokens &Toks); + void ParseLateTemplatedFuncDef(LateParsedTemplatedFunction &LMT); + typedef llvm::DenseMap + LateParsedTemplateMapT; + LateParsedTemplateMapT LateParsedTemplateMap; + + static void LateTemplateParserCallback(void *P, FunctionDecl *FD); + void LateTemplateParser(FunctionDecl *FD); + Sema::ParsingClassState PushParsingClass(Decl *TagOrTemplate, bool TopLevelClass); void DeallocateParsedClasses(ParsingClass *Class); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index f42fe3d610..7cb0ad9970 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -307,6 +307,16 @@ public: /// and must warn if not used. Only contains the first declaration. llvm::SmallVector UnusedFileScopedDecls; + /// \brief Callback to the parser to parse templated functions when needed. + typedef void LateTemplateParserCB(void *P, FunctionDecl *FD); + LateTemplateParserCB *LateTemplateParser; + void *OpaqueParser; + + void SetLateTemplateParser(LateTemplateParserCB *LTP, void *P) { + LateTemplateParser = LTP; + OpaqueParser = P; + } + class DelayedDiagnostics; class ParsingDeclState { @@ -2974,11 +2984,14 @@ public: AttributeList *AttrList); void ActOnReenterTemplateScope(Scope *S, Decl *Template); + void ActOnReenterDeclaratorTemplateScope(Scope *S, DeclaratorDecl *D); void ActOnStartDelayedMemberDeclarations(Scope *S, Decl *Record); void ActOnStartDelayedCXXMethodDeclaration(Scope *S, Decl *Method); void ActOnDelayedCXXMethodParameter(Scope *S, Decl *Param); void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *Method); void ActOnFinishDelayedMemberDeclarations(Scope *S, Decl *Record); + void MarkAsLateParsedTemplate(FunctionDecl *FD, bool Flag = true); + bool IsInsideALocalClassWithinATemplateFunction(); Decl *ActOnStaticAssertDeclaration(SourceLocation StaticAssertLoc, Expr *AssertExpr, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 156e017aa8..d37099f864 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1413,7 +1413,7 @@ bool FunctionDecl::isVariadic() const { bool FunctionDecl::hasBody(const FunctionDecl *&Definition) const { for (redecl_iterator I = redecls_begin(), E = redecls_end(); I != E; ++I) { - if (I->Body) { + if (I->Body || I->IsLateTemplateParsed) { Definition = *I; return true; } @@ -1427,6 +1427,9 @@ Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const { if (I->Body) { Definition = *I; return I->Body.get(getASTContext().getExternalSource()); + } else if (I->IsLateTemplateParsed) { + Definition = *I; + return 0; } } diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index e73a0cffdb..83e927fcad 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -2031,7 +2031,8 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { case Decl::CXXMethod: case Decl::Function: // Skip function templates - if (cast(D)->getDescribedFunctionTemplate()) + if (cast(D)->getDescribedFunctionTemplate() || + cast(D)->isLateTemplateParsed()) return; EmitGlobal(cast(D)); @@ -2060,12 +2061,15 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { break; case Decl::CXXConstructor: // Skip function templates - if (cast(D)->getDescribedFunctionTemplate()) + if (cast(D)->getDescribedFunctionTemplate() || + cast(D)->isLateTemplateParsed()) return; EmitCXXConstructors(cast(D)); break; case Decl::CXXDestructor: + if (cast(D)->isLateTemplateParsed()) + return; EmitCXXDestructors(cast(D)); break; diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index c1f7e15c88..4e94346de1 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -1605,6 +1605,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_fno_borland_extensions, false)) CmdArgs.push_back("-fborland-extensions"); + // -fno-delayed-template-parsing is default. + if (Args.hasFlag(options::OPT_fdelayed_template_parsing, + options::OPT_fno_delayed_template_parsing, + false)) + CmdArgs.push_back("-fdelayed-template-parsing"); + // -fgnu-keywords default varies depending on language; only pass if // specified. if (Arg *A = Args.getLastArg(options::OPT_fgnu_keywords, diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 5449df2b03..3f3c1d8cce 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -692,6 +692,8 @@ static void LangOptsToArgs(const LangOptions &Opts, Res.push_back("-ffake-address-space-map"); if (Opts.ParseUnknownAnytype) Res.push_back("-funknown-anytype"); + if (Opts.DelayedTemplateParsing) + Res.push_back("-fdelayed-template-parsing"); } static void PreprocessorOptsToArgs(const PreprocessorOptions &Opts, @@ -1436,6 +1438,9 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, if (Args.hasArg(OPT_pthread)) Opts.POSIXThreads = 1; + if (Args.hasArg(OPT_fdelayed_template_parsing)) + Opts.DelayedTemplateParsing = 1; + llvm::StringRef Vis = Args.getLastArgValue(OPT_fvisibility, "default"); if (Vis == "default") Opts.setVisibilityMode(DefaultVisibility); @@ -1495,6 +1500,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.MathErrno = Args.hasArg(OPT_fmath_errno); Opts.InstantiationDepth = Args.getLastArgIntValue(OPT_ftemplate_depth, 1024, Diags); + Opts.DelayedTemplateParsing = Args.hasArg(OPT_fdelayed_template_parsing); Opts.NumLargeByValueCopy = Args.getLastArgIntValue(OPT_Wlarge_by_value_copy, 0, Diags); Opts.MSBitfields = Args.hasArg(OPT_mms_bitfields); diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp index 93568efebc..778aa11087 100644 --- a/lib/Parse/ParseCXXInlineMethods.cpp +++ b/lib/Parse/ParseCXXInlineMethods.cpp @@ -15,6 +15,7 @@ #include "clang/Parse/Parser.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/Scope.h" +#include "clang/AST/DeclTemplate.h" using namespace clang; /// ParseCXXInlineMethodDef - We parsed and verified that the specified @@ -46,6 +47,36 @@ Decl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, ParsingDeclarator &D, D.complete(FnD); + // In delayed template parsing mode, if we are within a class template + // or if we are about to parse function member template then consume + // the tokens and store them for parsing at the end of the translation unit. + if (getLang().DelayedTemplateParsing && + ((Actions.CurContext->isDependentContext() || + TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate) && + !Actions.IsInsideALocalClassWithinATemplateFunction()) && + !D.getDeclSpec().isFriendSpecified()) { + + if (FnD) { + LateParsedTemplatedFunction *LPT = + new LateParsedTemplatedFunction(this, FnD); + + FunctionDecl *FD = 0; + if (FunctionTemplateDecl *FunTmpl = dyn_cast(FnD)) + FD = FunTmpl->getTemplatedDecl(); + else + FD = cast(FnD); + + LateParsedTemplateMap[FD] = LPT; + Actions.MarkAsLateParsedTemplate(FD); + LexTemplateFunctionForLateParsing(LPT->Toks); + } else { + CachedTokens Toks; + LexTemplateFunctionForLateParsing(Toks); + } + + return FnD; + } + // Consume the tokens and store them for later parsing. LexedMethod* LM = new LexedMethod(this, FnD); diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index 84b37889f4..f21e09152b 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -17,6 +17,8 @@ #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" #include "RAIIObjectsForParser.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/ASTConsumer.h" using namespace clang; /// \brief Parse a template declaration, explicit instantiation, or @@ -1125,3 +1127,125 @@ SourceRange Parser::ParsedTemplateInfo::getSourceRange() const { R.setBegin(ExternLoc); return R; } + +void Parser::LateTemplateParserCallback(void *P, FunctionDecl *FD) { + ((Parser*)P)->LateTemplateParser(FD); +} + + +void Parser::LateTemplateParser(FunctionDecl *FD) { + LateParsedTemplatedFunction *LPT = LateParsedTemplateMap[FD]; + if (LPT) { + ParseLateTemplatedFuncDef(*LPT); + return; + } + + llvm_unreachable("Late templated function without associated lexed tokens"); +} + +/// \brief Late parse a C++ function template in Microsoft mode. +void Parser::ParseLateTemplatedFuncDef(LateParsedTemplatedFunction &LMT) { + if(!LMT.D) + return; + + // If this is a member template, introduce the template parameter scope. + ParseScope TemplateScope(this, Scope::TemplateParamScope); + + // Get the FunctionDecl. + FunctionDecl *FD = 0; + if (FunctionTemplateDecl *FunTmpl = dyn_cast(LMT.D)) + FD = FunTmpl->getTemplatedDecl(); + else + FD = cast(LMT.D); + + // Reinject the template parameters. + DeclaratorDecl* Declarator = dyn_cast(FD); + if (Declarator && Declarator->getNumTemplateParameterLists() != 0) { + Actions.ActOnReenterDeclaratorTemplateScope(getCurScope(), Declarator); + Actions.ActOnReenterTemplateScope(getCurScope(), LMT.D); + } else { + Actions.ActOnReenterTemplateScope(getCurScope(), LMT.D); + + DeclContext *DD = FD->getLexicalParent(); + while (DD && DD->isRecord()) { + if (ClassTemplatePartialSpecializationDecl* MD = + dyn_cast_or_null(DD)) + Actions.ActOnReenterTemplateScope(getCurScope(), MD); + else if (CXXRecordDecl* MD = dyn_cast_or_null(DD)) + Actions.ActOnReenterTemplateScope(getCurScope(), + MD->getDescribedClassTemplate()); + + DD = DD->getLexicalParent(); + } + } + assert(!LMT.Toks.empty() && "Empty body!"); + + // Append the current token at the end of the new token stream so that it + // doesn't get lost. + LMT.Toks.push_back(Tok); + PP.EnterTokenStream(LMT.Toks.data(), LMT.Toks.size(), true, false); + + // Consume the previously pushed token. + ConsumeAnyToken(); + assert((Tok.is(tok::l_brace) || Tok.is(tok::colon) || Tok.is(tok::kw_try)) + && "Inline method not starting with '{', ':' or 'try'"); + + // Parse the method body. Function body parsing code is similar enough + // to be re-used for method bodies as well. + ParseScope FnScope(this, Scope::FnScope|Scope::DeclScope); + + // Recreate the DeclContext. + Sema::ContextRAII SavedContext(Actions, Actions.getContainingDC(FD)); + + if (FunctionTemplateDecl *FunctionTemplate + = dyn_cast_or_null(LMT.D)) + Actions.ActOnStartOfFunctionDef(getCurScope(), + FunctionTemplate->getTemplatedDecl()); + if (FunctionDecl *Function = dyn_cast_or_null(LMT.D)) + Actions.ActOnStartOfFunctionDef(getCurScope(), Function); + + + if (Tok.is(tok::kw_try)) { + ParseFunctionTryBlock(LMT.D, FnScope); + return; + } + if (Tok.is(tok::colon)) { + ParseConstructorInitializer(LMT.D); + + // Error recovery. + if (!Tok.is(tok::l_brace)) { + Actions.ActOnFinishFunctionBody(LMT.D, 0); + return; + } + } else + Actions.ActOnDefaultCtorInitializers(LMT.D); + + ParseFunctionStatementBody(LMT.D, FnScope); + Actions.MarkAsLateParsedTemplate(FD, false); + + DeclGroupPtrTy grp = Actions.ConvertDeclToDeclGroup(LMT.D); + if (grp) + Actions.getASTConsumer().HandleTopLevelDecl(grp.get()); +} + +/// \brief Lex a delayed template function for late parsing. +void Parser::LexTemplateFunctionForLateParsing(CachedTokens &Toks) { + tok::TokenKind kind = Tok.getKind(); + // We may have a constructor initializer or function-try-block here. + if (kind == tok::colon || kind == tok::kw_try) + ConsumeAndStoreUntil(tok::l_brace, Toks); + else { + Toks.push_back(Tok); + ConsumeBrace(); + } + // Consume everything up to (and including) the matching right brace. + ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false); + + // If we're in a function-try-block, we need to store all the catch blocks. + if (kind == tok::kw_try) { + while (Tok.is(tok::kw_catch)) { + ConsumeAndStoreUntil(tok::l_brace, Toks, /*StopAtSemi=*/false); + ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false); + } + } +} diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 492b8f5309..6522306dce 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -19,6 +19,7 @@ #include "llvm/Support/raw_ostream.h" #include "RAIIObjectsForParser.h" #include "ParsePragma.h" +#include "clang/AST/DeclTemplate.h" using namespace clang; Parser::Parser(Preprocessor &pp, Sema &actions) @@ -362,6 +363,11 @@ Parser::~Parser() { for (unsigned i = 0, e = NumCachedScopes; i != e; ++i) delete ScopeCache[i]; + // Free LateParsedTemplatedFunction nodes. + for (LateParsedTemplateMapT::iterator it = LateParsedTemplateMap.begin(); + it != LateParsedTemplateMap.end(); ++it) + delete it->second; + // Remove the pragma handlers we installed. PP.RemovePragmaHandler(AlignHandler.get()); AlignHandler.reset(); @@ -438,6 +444,10 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result) { Result = DeclGroupPtrTy(); if (Tok.is(tok::eof)) { + // Late template parsing can begin. + if (getLang().DelayedTemplateParsing) + Actions.SetLateTemplateParser(LateTemplateParserCallback, this); + Actions.ActOnEndOfTranslationUnit(); return true; } @@ -786,6 +796,43 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, return 0; } + // In delayed template parsing mode, for function template we consume the + // tokens and store them for late parsing at the end of the translation unit. + if (getLang().DelayedTemplateParsing && + TemplateInfo.Kind == ParsedTemplateInfo::Template) { + MultiTemplateParamsArg TemplateParameterLists(Actions, + TemplateInfo.TemplateParams->data(), + TemplateInfo.TemplateParams->size()); + + ParseScope BodyScope(this, Scope::FnScope|Scope::DeclScope); + Scope *ParentScope = getCurScope()->getParent(); + + Decl *DP = Actions.HandleDeclarator(ParentScope, D, + move(TemplateParameterLists), + /*IsFunctionDefinition=*/true); + D.complete(DP); + D.getMutableDeclSpec().abort(); + + if (DP) { + LateParsedTemplatedFunction *LPT = new LateParsedTemplatedFunction(this, DP); + + FunctionDecl *FnD = 0; + if (FunctionTemplateDecl *FunTmpl = dyn_cast(DP)) + FnD = FunTmpl->getTemplatedDecl(); + else + FnD = cast(DP); + + LateParsedTemplateMap[FnD] = LPT; + Actions.MarkAsLateParsedTemplate(FnD); + LexTemplateFunctionForLateParsing(LPT->Toks); + } else { + CachedTokens Toks; + LexTemplateFunctionForLateParsing(Toks); + } + return DP; + } + + // Enter a scope for the function body. ParseScope BodyScope(this, Scope::FnScope|Scope::DeclScope); diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 403cf6246c..dc1270243d 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -142,6 +142,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()), ExternalSource(0), CodeCompleter(CodeCompleter), CurContext(0), PackContext(0), VisContext(0), + LateTemplateParser(0), OpaqueParser(0), IdResolver(pp.getLangOptions()), CXXTypeInfoDecl(0), MSVCGuidDecl(0), GlobalNewDeleteDeclared(false), CompleteTranslationUnit(CompleteTranslationUnit), diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index e506dd502a..2abc6dafa8 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -5748,8 +5748,13 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D) { // See if this is a redefinition. // But don't complain if we're in GNU89 mode and the previous definition // was an extern inline function. + + // FIXME: This code doesn't complain about multiple definition for late + // parsed template function. + bool IsLateParsingRedefinition = LateTemplateParser && + FD->isLateTemplateParsed(); const FunctionDecl *Definition; - if (FD->hasBody(Definition) && + if (FD->hasBody(Definition) && !IsLateParsingRedefinition && !canRedefineFunction(Definition, getLangOptions())) { if (getLangOptions().GNUMode && Definition->isInlineSpecified() && Definition->getStorageClass() == SC_Extern) diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 53a7c73998..5f3f600c8c 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -3166,6 +3166,25 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { } } +void Sema::ActOnReenterDeclaratorTemplateScope(Scope *S, DeclaratorDecl *D) { + if (!D) + return; + + int NumParamList = D->getNumTemplateParameterLists(); + for (int i = 0; i < NumParamList; i++) { + TemplateParameterList* Params = D->getTemplateParameterList(i); + for (TemplateParameterList::iterator Param = Params->begin(), + ParamEnd = Params->end(); + Param != ParamEnd; ++Param) { + NamedDecl *Named = cast(*Param); + if (Named->getDeclName()) { + S->AddDecl(Named); + IdResolver.AddDecl(Named); + } + } + } +} + void Sema::ActOnReenterTemplateScope(Scope *S, Decl *D) { if (!D) return; diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 49e4a87fd1..08eb654236 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -6381,3 +6381,24 @@ Sema::getTemplateArgumentBindingsText(const TemplateParameterList *Params, Out << ']'; return Out.str(); } + +void Sema::MarkAsLateParsedTemplate(FunctionDecl *FD, bool Flag) { + if (!FD) + return; + FD->setLateTemplateParsed(Flag); +} + +bool Sema::IsInsideALocalClassWithinATemplateFunction() { + DeclContext *DC = CurContext; + + while (DC) { + if (CXXRecordDecl *RD = dyn_cast(CurContext)) { + const FunctionDecl *FD = RD->isLocalClass(); + return (FD && FD->getTemplatedKind() != FunctionDecl::TK_NonTemplate); + } else if (DC->isTranslationUnit() || DC->isNamespace()) + return false; + + DC = DC->getParent(); + } + return false; +} diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 0ff7ff4d40..588501f50e 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2262,6 +2262,21 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, if (PatternDecl) Pattern = PatternDecl->getBody(PatternDecl); + // Postpone late parsed template instantiations. + if (PatternDecl->isLateTemplateParsed() && !LateTemplateParser) { + PendingInstantiations.push_back( + std::make_pair(Function, PointOfInstantiation)); + return; + } + + // Call the LateTemplateParser callback if there a need to late parse + // a templated function definition. + if (!Pattern && PatternDecl && PatternDecl->isLateTemplateParsed() && + LateTemplateParser) { + LateTemplateParser(OpaqueParser, (FunctionDecl*)PatternDecl); + Pattern = PatternDecl->getBody(PatternDecl); + } + if (!Pattern) { if (DefinitionRequired) { if (Function->getPrimaryTemplate()) diff --git a/test/Parser/DelayedTemplateParsing.cpp b/test/Parser/DelayedTemplateParsing.cpp new file mode 100644 index 0000000000..355250e4b0 --- /dev/null +++ b/test/Parser/DelayedTemplateParsing.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -fdelayed-template-parsing -fsyntax-only -verify %s + +template +class A { + + void foo() { + undeclared(); + } + + void foo2(); +}; + +template +void A::foo2() { + undeclared(); +} + + +template +void foo3() { + undeclared(); +} + +template void A::foo2(); + + +void undeclared() +{ + +} + diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 870e6b2018..c1d48388c8 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -788,7 +788,7 @@ bool CursorVisitor::VisitFunctionDecl(FunctionDecl *ND) { // FIXME: Attributes? } - if (ND->isThisDeclarationADefinition()) { + if (ND->isThisDeclarationADefinition() && !ND->isLateTemplateParsed()) { if (CXXConstructorDecl *Constructor = dyn_cast(ND)) { // Find the initializers that were written in the source. llvm::SmallVector WrittenInits;