Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -1181,8 +1181,7 @@ /// argument (C++ [class.mem]p2). struct LateParsedMethodDeclaration : public LateParsedDeclaration { explicit LateParsedMethodDeclaration(Parser *P, Decl *M) - : Self(P), Method(M), TemplateScope(false), - ExceptionSpecTokens(nullptr) {} + : Self(P), Method(M), TemplateScope(false) {} void ParseLexedMethodDeclarations() override; @@ -1205,7 +1204,7 @@ /// The set of tokens that make up an exception-specification that /// has not yet been parsed. - CachedTokens *ExceptionSpecTokens; + std::unique_ptr ExceptionSpecTokens; }; /// LateParsedMemberInitializer - An initializer for a non-static class data Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -5103,7 +5103,17 @@ CXXThisScopeRAII(Sema &S, Decl *ContextDecl, unsigned CXXThisTypeQuals, bool Enabled = true); - ~CXXThisScopeRAII(); + CXXThisScopeRAII(CXXThisScopeRAII &&Other) + : S(Other.S), OldCXXThisTypeOverride(Other.OldCXXThisTypeOverride), + Enabled(Other.Enabled) { + Other.Enabled = false; + } + void operator=(CXXThisScopeRAII &&) = delete; + + /// Exit the scope early before the destructor is called. + void Exit(); + + ~CXXThisScopeRAII() { Exit(); } }; /// Make sure the value of 'this' is actually available in the current Index: lib/Parse/ParseCXXInlineMethods.cpp =================================================================== --- lib/Parse/ParseCXXInlineMethods.cpp +++ lib/Parse/ParseCXXInlineMethods.cpp @@ -209,15 +209,6 @@ // Consume everything up to (but excluding) the comma or semicolon. ConsumeAndStoreInitializer(Toks, CIK_DefaultInitializer); } - - // Store an artificial EOF token to ensure that we don't run off the end of - // the initializer when we come to parse it. - Token Eof; - Eof.startToken(); - Eof.setKind(tok::eof); - Eof.setLocation(Tok.getLocation()); - Eof.setEofData(VarD); - Toks.push_back(Eof); } Parser::LateParsedDeclaration::~LateParsedDeclaration() {} @@ -256,6 +247,57 @@ Self->ParseLexedMemberInitializer(*this); } +namespace { + +/// Object that temporarily points the parser to a list of cached tokens. +/// On construction, the current token will be the first token in the cache. +/// On destruction, the current token will be as it was before construction. +/// +/// \warning +/// The input cache will be modified to allow for some extra bookkeeing. +/// The cache shouldn't be considered usable after parsing its tokens. +class UseCachedTokensRAII { + Parser &Self; + + /// Some sort of pointer that uniquely identifies the contents of the cache. + const void *Data; + +public: + UseCachedTokensRAII(Parser &Self, CachedTokens &Cache, const void *Data) + : Self(Self), Data(Data) { + // Mark the end of the token stream so that we know when to stop. + Token EndTok; + EndTok.startToken(); + EndTok.setKind(tok::eof); + EndTok.setLocation(Cache.back().getEndLoc()); + EndTok.setEofData(Data); + Cache.push_back(EndTok); + + // Enter the token stream. + Cache.push_back(Self.getCurToken()); // Save the current token. + Self.getPreprocessor().EnterTokenStream(Cache, true); + Self.ConsumeAnyToken(true); // Consume the saved token. + } + + bool isAtEnd() const { + const Token Tok = Self.getCurToken(); + return Tok.is(tok::eof) && Tok.getEofData() == Data; + } + + ~UseCachedTokensRAII() { + // There could be leftover tokens (e.g. because of an error). + // Skip through until we reach the original token position. + while (Self.getCurToken().isNot(tok::eof)) + Self.ConsumeAnyToken(); + + // Clean up the remaining stop token. + if (isAtEnd()) + Self.ConsumeAnyToken(); + } +}; + +} // end unnamed namespace + /// ParseLexedMethodDeclarations - We finished parsing the member /// specification of a top (non-nested) C++ class. Now go over the /// stack of method declarations with some parts for which parsing was @@ -296,84 +338,71 @@ Actions.ActOnReenterTemplateScope(getCurScope(), LM.Method); ++CurTemplateDepthTracker; } + // Start the delayed C++ method declaration Actions.ActOnStartDelayedCXXMethodDeclaration(getCurScope(), LM.Method); - // Introduce the parameters into scope and parse their default - // arguments. ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope | - Scope::FunctionDeclarationScope | Scope::DeclScope); + Scope::FunctionDeclarationScope | + Scope::DeclScope); + + // Get the underlying function declaration, looking past templating. + auto *Func = cast( + isa(LM.Method) + ? cast(LM.Method)->getTemplatedDecl() + : LM.Method); + + // Introduce the parameters into scope and parse their default arguments. for (unsigned I = 0, N = LM.DefaultArgs.size(); I != N; ++I) { - auto Param = cast(LM.DefaultArgs[I].Param); - // Introduce the parameter into scope. - bool HasUnparsed = Param->hasUnparsedDefaultArg(); - Actions.ActOnDelayedCXXMethodParameter(getCurScope(), Param); - std::unique_ptr Toks = std::move(LM.DefaultArgs[I].Toks); - if (Toks) { - ParenBraceBracketBalancer BalancerRAIIObj(*this); + LateParsedDefaultArgument &DefaultArg = LM.DefaultArgs[I]; - // Mark the end of the default argument so that we know when to stop when - // we parse it later on. - Token LastDefaultArgToken = Toks->back(); - Token DefArgEnd; - DefArgEnd.startToken(); - DefArgEnd.setKind(tok::eof); - DefArgEnd.setLocation(LastDefaultArgToken.getEndLoc()); - DefArgEnd.setEofData(Param); - Toks->push_back(DefArgEnd); + auto *Param = cast(DefaultArg.Param); + const bool HasUnparsed = Param->hasUnparsedDefaultArg(); - // Parse the default argument from its saved token stream. - Toks->push_back(Tok); // So that the current token doesn't get lost - PP.EnterTokenStream(*Toks, true); + // Introduce the parameter into scope. + Actions.ActOnDelayedCXXMethodParameter(getCurScope(), Param); - // Consume the previously-pushed token. - ConsumeAnyToken(); + // If it has a default argument, parse it now. + if (std::unique_ptr Toks = std::move(DefaultArg.Toks)) { + ParenBraceBracketBalancer BalancerRAIIObj(*this); + UseCachedTokensRAII CachedTokStream(*this, *Toks, Param); // Consume the '='. assert(Tok.is(tok::equal) && "Default argument not starting with '='"); SourceLocation EqualLoc = ConsumeToken(); - // The argument isn't actually potentially evaluated unless it is - // used. + // The argument isn't actually potentially evaluated unless it's used. EnterExpressionEvaluationContext Eval( Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed, Param); + // Parse the initializer-clause. ExprResult DefArgResult; if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); DefArgResult = ParseBraceInitializer(); - } else + } else { DefArgResult = ParseAssignmentExpression(); + } + DefArgResult = Actions.CorrectDelayedTyposInExpr(DefArgResult); if (DefArgResult.isInvalid()) { Actions.ActOnParamDefaultArgumentError(Param, EqualLoc); } else { - if (Tok.isNot(tok::eof) || Tok.getEofData() != Param) { - // The last two tokens are the terminator and the saved value of - // Tok; the last token in the default argument is the one before - // those. - assert(Toks->size() >= 3 && "expected a token in default arg"); - Diag(Tok.getLocation(), diag::err_default_arg_unparsed) - << SourceRange(Tok.getLocation(), - (*Toks)[Toks->size() - 3].getLocation()); - } + if (!CachedTokStream.isAtEnd()) + Diag(Tok.getLocation(), diag::err_default_arg_unparsed); + Actions.ActOnParamDefaultArgument(Param, EqualLoc, DefArgResult.get()); } + } else if (HasUnparsed) { + assert(Param->hasInheritedDefaultArg() && + "We should've cached tokens for this default arg"); - // There could be leftover tokens (e.g. because of an error). - // Skip through until we reach the 'end of default argument' token. - while (Tok.isNot(tok::eof)) - ConsumeAnyToken(); + ParmVarDecl *OldParam = Func->getPreviousDecl()->getParamDecl(I); + assert (!OldParam->hasUnparsedDefaultArg() && + "Previous declaration should be parsed by now"); - if (Tok.is(tok::eof) && Tok.getEofData() == Param) - ConsumeAnyToken(); - } else if (HasUnparsed) { - assert(Param->hasInheritedDefaultArg()); - FunctionDecl *Old = cast(LM.Method)->getPreviousDecl(); - ParmVarDecl *OldParam = Old->getParamDecl(I); - assert (!OldParam->hasUnparsedDefaultArg()); if (OldParam->hasUninstantiatedDefaultArg()) Param->setUninstantiatedDefaultArg( OldParam->getUninstantiatedDefaultArg()); @@ -382,42 +411,27 @@ } } + // C++11 [expr.prim.general]p3: + // If a declaration declares a member function or member function + // template of a class X, the expression this is a prvalue of type + // "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq + // and the end of the function-definition, member-declarator, or + // declarator. + Sema::CXXThisScopeRAII ThisScope = [&] { + // We only need to enter a 'this' scope if we're parsing a member function. + if (auto *Method = dyn_cast(Func)) { + return Sema::CXXThisScopeRAII(Actions, Method->getParent(), + Method->getTypeQualifiers(), + getLangOpts().CPlusPlus11); + } + + return Sema::CXXThisScopeRAII(Actions, nullptr, 0, /* Enabled */false); + }(); + // Parse a delayed exception-specification, if there is one. - if (CachedTokens *Toks = LM.ExceptionSpecTokens) { + if (std::unique_ptr Toks = std::move(LM.ExceptionSpecTokens)) { ParenBraceBracketBalancer BalancerRAIIObj(*this); - - // Add the 'stop' token. - Token LastExceptionSpecToken = Toks->back(); - Token ExceptionSpecEnd; - ExceptionSpecEnd.startToken(); - ExceptionSpecEnd.setKind(tok::eof); - ExceptionSpecEnd.setLocation(LastExceptionSpecToken.getEndLoc()); - ExceptionSpecEnd.setEofData(LM.Method); - Toks->push_back(ExceptionSpecEnd); - - // Parse the default argument from its saved token stream. - Toks->push_back(Tok); // So that the current token doesn't get lost - PP.EnterTokenStream(*Toks, true); - - // Consume the previously-pushed token. - ConsumeAnyToken(); - - // C++11 [expr.prim.general]p3: - // If a declaration declares a member function or member function - // template of a class X, the expression this is a prvalue of type - // "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq - // and the end of the function-definition, member-declarator, or - // declarator. - CXXMethodDecl *Method; - if (FunctionTemplateDecl *FunTmpl - = dyn_cast(LM.Method)) - Method = cast(FunTmpl->getTemplatedDecl()); - else - Method = cast(LM.Method); - - Sema::CXXThisScopeRAII ThisScope(Actions, Method->getParent(), - Method->getTypeQualifiers(), - getLangOpts().CPlusPlus11); + UseCachedTokensRAII CachedTokStream(*this, *Toks, LM.Method); // Parse the exception-specification. SourceRange SpecificationRange; @@ -432,7 +446,7 @@ DynamicExceptionRanges, NoexceptExpr, ExceptionSpecTokens); - if (Tok.isNot(tok::eof) || Tok.getEofData() != LM.Method) + if (!CachedTokStream.isAtEnd()) Diag(Tok.getLocation(), diag::err_except_spec_unparsed); // Attach the exception-specification to the method. @@ -442,20 +456,9 @@ DynamicExceptionRanges, NoexceptExpr.isUsable()? NoexceptExpr.get() : nullptr); - - // There could be leftover tokens (e.g. because of an error). - // Skip through until we reach the original token position. - while (Tok.isNot(tok::eof)) - ConsumeAnyToken(); - - // Clean up the remaining EOF token. - if (Tok.is(tok::eof) && Tok.getEofData() == LM.Method) - ConsumeAnyToken(); - - delete Toks; - LM.ExceptionSpecTokens = nullptr; } + ThisScope.Exit(); PrototypeScope.Exit(); // Finish the delayed C++ method declaration. @@ -492,22 +495,8 @@ } ParenBraceBracketBalancer BalancerRAIIObj(*this); + UseCachedTokensRAII CachedTokStream(*this, LM.Toks, LM.D); - assert(!LM.Toks.empty() && "Empty body!"); - Token LastBodyToken = LM.Toks.back(); - Token BodyEnd; - BodyEnd.startToken(); - BodyEnd.setKind(tok::eof); - BodyEnd.setLocation(LastBodyToken.getEndLoc()); - BodyEnd.setEofData(LM.D); - LM.Toks.push_back(BodyEnd); - // Append the current token at the end of the new token stream so that it - // doesn't get lost. - LM.Toks.push_back(Tok); - PP.EnterTokenStream(LM.Toks, true); - - // Consume the previously pushed token. - ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true); assert(Tok.isOneOf(tok::l_brace, tok::colon, tok::kw_try) && "Inline method not starting with '{', ':' or 'try'"); @@ -519,12 +508,6 @@ if (Tok.is(tok::kw_try)) { ParseFunctionTryBlock(LM.D, FnScope); - - while (Tok.isNot(tok::eof)) - ConsumeAnyToken(); - - if (Tok.is(tok::eof) && Tok.getEofData() == LM.D) - ConsumeAnyToken(); return; } if (Tok.is(tok::colon)) { @@ -534,12 +517,6 @@ if (!Tok.is(tok::l_brace)) { FnScope.Exit(); Actions.ActOnFinishFunctionBody(LM.D, nullptr); - - while (Tok.isNot(tok::eof)) - ConsumeAnyToken(); - - if (Tok.is(tok::eof) && Tok.getEofData() == LM.D) - ConsumeAnyToken(); return; } } else @@ -554,12 +531,6 @@ ParseFunctionStatementBody(LM.D, FnScope); - while (Tok.isNot(tok::eof)) - ConsumeAnyToken(); - - if (Tok.is(tok::eof) && Tok.getEofData() == LM.D) - ConsumeAnyToken(); - if (auto *FD = dyn_cast_or_null(LM.D)) if (isa(FD) || FD->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)) @@ -614,14 +585,7 @@ return; ParenBraceBracketBalancer BalancerRAIIObj(*this); - - // Append the current token at the end of the new token stream so that it - // doesn't get lost. - MI.Toks.push_back(Tok); - PP.EnterTokenStream(MI.Toks, true); - - // Consume the previously pushed token. - ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true); + UseCachedTokensRAII CachedTokStream(*this, MI.Toks, MI.Field); SourceLocation EqualLoc; @@ -633,23 +597,14 @@ Actions.ActOnFinishCXXInClassMemberInitializer(MI.Field, EqualLoc, Init.get()); - // The next token should be our artificial terminating EOF token. - if (Tok.isNot(tok::eof)) { - if (!Init.isInvalid()) { - SourceLocation EndLoc = PP.getLocForEndOfToken(PrevTokLocation); - if (!EndLoc.isValid()) - EndLoc = Tok.getLocation(); - // No fixit; we can't recover as if there were a semicolon here. - Diag(EndLoc, diag::err_expected_semi_decl_list); - } + if (!CachedTokStream.isAtEnd() && !Init.isInvalid()) { + SourceLocation EndLoc = PP.getLocForEndOfToken(PrevTokLocation); + if (!EndLoc.isValid()) + EndLoc = Tok.getLocation(); - // Consume tokens until we hit the artificial EOF. - while (Tok.isNot(tok::eof)) - ConsumeAnyToken(); + // No fixit; we can't recover as if there were a semicolon here. + Diag(EndLoc, diag::err_expected_semi_decl_list); } - // Make sure this is *our* artificial EOF token. - if (Tok.getEofData() == MI.Field) - ConsumeAnyToken(); } /// ConsumeAndStoreUntil - Consume and store the token at the passed token Index: lib/Parse/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp +++ lib/Parse/ParseDeclCXX.cpp @@ -2097,43 +2097,42 @@ /// delayed, e.g., default arguments or an exception-specification, create a /// late-parsed method declaration record to handle the parsing at the end of /// the class definition. -void Parser::HandleMemberFunctionDeclDelays(Declarator& DeclaratorInfo, +void Parser::HandleMemberFunctionDeclDelays(Declarator &DeclaratorInfo, Decl *ThisDecl) { - DeclaratorChunk::FunctionTypeInfo &FTI - = DeclaratorInfo.getFunctionTypeInfo(); - // If there was a late-parsed exception-specification, we'll need a - // late parse - bool NeedLateParse = FTI.getExceptionSpecType() == EST_Unparsed; - - if (!NeedLateParse) { - // Look ahead to see if there are any default args - for (unsigned ParamIdx = 0; ParamIdx < FTI.NumParams; ++ParamIdx) { - auto Param = cast(FTI.Params[ParamIdx].Param); - if (Param->hasUnparsedDefaultArg()) { - NeedLateParse = true; - break; - } - } - } + DeclaratorChunk::FunctionTypeInfo &FTI = DeclaratorInfo.getFunctionTypeInfo(); + auto ParamInfoRange = llvm::make_range(FTI.Params, + FTI.Params + FTI.NumParams); - if (NeedLateParse) { - // Push this method onto the stack of late-parsed method - // declarations. - auto LateMethod = new LateParsedMethodDeclaration(this, ThisDecl); - getCurrentClass().LateParsedDeclarations.push_back(LateMethod); - LateMethod->TemplateScope = getCurScope()->isTemplateParamScope(); + const bool LateParseDefaultArgs = + llvm::any_of(ParamInfoRange, [](const DeclaratorChunk::ParamInfo &PI) + { return cast(PI.Param)->hasUnparsedDefaultArg(); }); - // Stash the exception-specification tokens in the late-pased method. - LateMethod->ExceptionSpecTokens = FTI.ExceptionSpecTokens; - FTI.ExceptionSpecTokens = nullptr; + const bool LateParseExceptionSpec = + FTI.getExceptionSpecType() == EST_Unparsed; + + // If we didn't delay parsing for any parts of this function, then we're done. + if (!LateParseDefaultArgs && !LateParseExceptionSpec) + return; - // Push tokens for each parameter. Those that do not have - // defaults will be NULL. - LateMethod->DefaultArgs.reserve(FTI.NumParams); - for (unsigned ParamIdx = 0; ParamIdx < FTI.NumParams; ++ParamIdx) - LateMethod->DefaultArgs.push_back(LateParsedDefaultArgument( - FTI.Params[ParamIdx].Param, - std::move(FTI.Params[ParamIdx].DefaultArgTokens))); + // Push this method onto the stack of late-parsed method declarations. + auto LateMethod = new LateParsedMethodDeclaration(this, ThisDecl); + getCurrentClass().LateParsedDeclarations.push_back(LateMethod); + LateMethod->TemplateScope = getCurScope()->isTemplateParamScope(); + + // Stash the '= initializer-clause' tokens for each parameter. We need to do + // this for every parameter, even those without a default initializer, so that + // the late-parsed method has a reference to the parameter declarations. + LateMethod->DefaultArgs.reserve(FTI.NumParams); + llvm::transform(ParamInfoRange, std::back_inserter(LateMethod->DefaultArgs), + [](DeclaratorChunk::ParamInfo &PI) { + return LateParsedDefaultArgument( + PI.Param, std::move(PI.DefaultArgTokens)); + }); + + if (LateParseExceptionSpec) { + // Stash the exception-specification tokens. + LateMethod->ExceptionSpecTokens.reset(FTI.ExceptionSpecTokens); + FTI.ExceptionSpecTokens = nullptr; } } Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -1116,10 +1116,10 @@ this->Enabled = true; } - -Sema::CXXThisScopeRAII::~CXXThisScopeRAII() { +void Sema::CXXThisScopeRAII::Exit() { if (Enabled) { S.CXXThisTypeOverride = OldCXXThisTypeOverride; + Enabled = false; } } Index: test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p4.cpp =================================================================== --- test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p4.cpp +++ test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p4.cpp @@ -61,15 +61,24 @@ struct A { struct B { static void Foo (int = 0); + + template + static void TFoo (T = 0); }; // should not hide default args friend void B::Foo (int); + + // Same as above, but with templates! + // Should not crash the compiler. + template + friend void B::TFoo (T); }; void Test () { A::B::Foo (); + A::B::TFoo (); } } // namespace