Index: clang/include/clang/AST/ASTConsumer.h =================================================================== --- clang/include/clang/AST/ASTConsumer.h +++ clang/include/clang/AST/ASTConsumer.h @@ -13,6 +13,10 @@ #ifndef LLVM_CLANG_AST_ASTCONSUMER_H #define LLVM_CLANG_AST_ASTCONSUMER_H +namespace llvm { +template class SmallVectorImpl; +} + namespace clang { class ASTContext; class CXXMethodDecl; @@ -22,6 +26,7 @@ class ASTMutationListener; class ASTDeserializationListener; // layering violation because void* is ugly class SemaConsumer; // layering violation required for safe SemaConsumer + class Stmt; class TagDecl; class VarDecl; class FunctionDecl; @@ -46,6 +51,15 @@ /// ASTContext. virtual void Initialize(ASTContext &Context) {} + /// HandleTopLevelStmts - Handle the specified top-level statements. This is + /// called by the parser to process every top-level Stmt* in incremental + /// compilation mode. + /// + /// \returns true to continue parsing, or false to abort parsing. + virtual bool HandleTopLevelStmts(const llvm::SmallVectorImpl &Stmts) { + return true; + } + /// HandleTopLevelDecl - Handle the specified top-level declaration. This is /// called by the parser to process every top-level Decl*. /// Index: clang/include/clang/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -462,6 +462,10 @@ typedef Sema::FullExprArg FullExprArg; + /// A SmallVector of statements, with stack size 32 (as that is the only one + /// used.) + typedef SmallVector StmtVector; + // Parsing methods. /// Initialize - Warm up the parser. @@ -470,12 +474,14 @@ /// Parse the first top-level declaration in a translation unit. bool ParseFirstTopLevelDecl(DeclGroupPtrTy &Result, - Sema::ModuleImportState &ImportState); + Sema::ModuleImportState &ImportState, + StmtVector *Stmts = nullptr); /// ParseTopLevelDecl - Parse one top-level declaration. Returns true if /// the EOF was encountered. bool ParseTopLevelDecl(DeclGroupPtrTy &Result, - Sema::ModuleImportState &ImportState); + Sema::ModuleImportState &ImportState, + StmtVector *Stmts = nullptr); bool ParseTopLevelDecl() { DeclGroupPtrTy Result; Sema::ModuleImportState IS = Sema::ModuleImportState::NotACXX20Module; @@ -2061,9 +2067,6 @@ //===--------------------------------------------------------------------===// // C99 6.8: Statements and Blocks. - /// A SmallVector of statements, with stack size 32 (as that is the only one - /// used.) - typedef SmallVector StmtVector; /// A SmallVector of expressions, with stack size 12 (the maximum used.) typedef SmallVector ExprVector; /// A SmallVector of types. @@ -2410,10 +2413,13 @@ /// isDeclarationStatement - Disambiguates between a declaration or an /// expression statement, when parsing function bodies. + /// + /// \param DisambiguatingWithExpression - True to indicate that the purpose of + /// this check is to disambiguate between an expression and a declaration. /// Returns true for declaration, false for expression. - bool isDeclarationStatement() { + bool isDeclarationStatement(bool DisambiguatingWithExpression = false) { if (getLangOpts().CPlusPlus) - return isCXXDeclarationStatement(); + return isCXXDeclarationStatement(DisambiguatingWithExpression); return isDeclarationSpecifier(true); } @@ -2440,7 +2446,8 @@ /// Starting with a scope specifier, identifier, or /// template-id that refers to the current class, determine whether /// this is a constructor declarator. - bool isConstructorDeclarator(bool Unqualified, bool DeductionGuide = false); + bool isConstructorDeclarator(bool Unqualified, bool DeductionGuide = false, + bool DisambiguatingWithExpression = false); /// Specifies the context in which type-id/expression /// disambiguation will occur. @@ -2478,7 +2485,7 @@ /// isCXXDeclarationStatement - C++-specialized function that disambiguates /// between a declaration or an expression statement, when parsing function /// bodies. Returns true for declaration, false for expression. - bool isCXXDeclarationStatement(); + bool isCXXDeclarationStatement(bool DisambiguatingWithExpression = false); /// isCXXSimpleDeclaration - C++-specialized function that disambiguates /// between a simple-declaration or an expression-statement. Index: clang/lib/CodeGen/CodeGenAction.cpp =================================================================== --- clang/lib/CodeGen/CodeGenAction.cpp +++ clang/lib/CodeGen/CodeGenAction.cpp @@ -237,6 +237,10 @@ return true; } + bool + HandleTopLevelStmts(const llvm::SmallVectorImpl &Stmts) override { + return Gen->HandleTopLevelStmts(Stmts); + } void HandleInlineFunctionDefinition(FunctionDecl *D) override { PrettyStackTraceDecl CrashInfo(D, SourceLocation(), Index: clang/lib/CodeGen/ModuleBuilder.cpp =================================================================== --- clang/lib/CodeGen/ModuleBuilder.cpp +++ clang/lib/CodeGen/ModuleBuilder.cpp @@ -175,7 +175,30 @@ Builder->HandleCXXStaticMemberVarInstantiation(VD); } + bool + HandleTopLevelStmts(const llvm::SmallVectorImpl &Stmts) override { + if (Diags.hasErrorOccurred()) + return false; // Abort parsing. + + // FIXME: We should reimplement this in a proper way where we append the + // statements to the global init function. + static unsigned id = 0; + // Make sure to emit all elements of a Decl. + for (Stmt *S : Stmts) { + Expr *E = cast(S); + IdentifierInfo *name = &Ctx->Idents.get("_stmt" + std::to_string(id++)); + VarDecl *VD = VarDecl::Create(*Ctx, Ctx->getTranslationUnitDecl(), + E->getBeginLoc(), SourceLocation(), name, + E->getType(), /*TSI*/ nullptr, SC_None); + VD->setInit(E); + HandleTopLevelDecl(DeclGroupRef(VD)); + } + + return true; + } + bool HandleTopLevelDecl(DeclGroupRef DG) override { + // FIXME: Why not return false and abort parsing? if (Diags.hasErrorOccurred()) return true; Index: clang/lib/Interpreter/IncrementalParser.cpp =================================================================== --- clang/lib/Interpreter/IncrementalParser.cpp +++ clang/lib/Interpreter/IncrementalParser.cpp @@ -168,11 +168,17 @@ Parser::DeclGroupPtrTy ADecl; Sema::ModuleImportState ImportState; - for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF; - AtEOF = P->ParseTopLevelDecl(ADecl, ImportState)) { + Parser::StmtVector Stmts; + for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState, &Stmts); + !AtEOF; AtEOF = P->ParseTopLevelDecl(ADecl, ImportState, &Stmts)) { // If we got a null return and something *was* parsed, ignore it. This // is due to a top-level semicolon, an action override, or a parse error // skipping something. + if (!Stmts.empty() && !Consumer->HandleTopLevelStmts(Stmts)) + return llvm::make_error("Parsing failed. " + "The consumer rejected a stmt", + std::error_code()); + if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get())) return llvm::make_error("Parsing failed. " "The consumer rejected a decl", Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -5291,6 +5291,10 @@ switch (Tok.getKind()) { default: return false; + // OpenCL + case tok::kw___kernel: + return getLangOpts().OpenCL; + // OpenCL 2.0 and later define this keyword. case tok::kw_pipe: return getLangOpts().OpenCL && @@ -5513,7 +5517,9 @@ } } -bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide) { +bool Parser::isConstructorDeclarator( + bool IsUnqualified, bool DeductionGuide, + bool DisambiguatingWithExpression /*=false*/) { TentativeParsingAction TPA(*this); // Parse the C++ scope specifier. @@ -5550,11 +5556,12 @@ // A right parenthesis, or ellipsis followed by a right parenthesis signals // that we have a constructor. - if (Tok.is(tok::r_paren) || - (Tok.is(tok::ellipsis) && NextToken().is(tok::r_paren))) { - TPA.Revert(); - return true; - } + if (!DisambiguatingWithExpression) + if (Tok.is(tok::r_paren) || + (Tok.is(tok::ellipsis) && NextToken().is(tok::r_paren))) { + TPA.Revert(); + return true; + } // A C++11 attribute here signals that we have a constructor, and is an // attribute on the first constructor parameter. Index: clang/lib/Parse/ParseTentative.cpp =================================================================== --- clang/lib/Parse/ParseTentative.cpp +++ clang/lib/Parse/ParseTentative.cpp @@ -22,6 +22,7 @@ /// /// declaration-statement: /// block-declaration +/// template-declaration /// /// block-declaration: /// simple-declaration @@ -46,12 +47,25 @@ /// 'using' 'namespace' '::'[opt] nested-name-specifier[opt] /// namespace-name ';' /// -bool Parser::isCXXDeclarationStatement() { +/// template-declaration: +/// 'template' declaration +/// 'template' concept-definition +/// +bool Parser::isCXXDeclarationStatement( + bool DisambiguatingWithExpression /*=false*/) { switch (Tok.getKind()) { - // asm-definition + // OpenCL + case tok::kw___kernel: + return getLangOpts().OpenCL; + case tok::kw_asm: + case tok::kw_export: + case tok::kw_import: // namespace-alias-definition + case tok::kw_inline: // inline namespace case tok::kw_namespace: + // template-declaration + case tok::kw_template: // using-declaration // using-directive case tok::kw_using: @@ -59,7 +73,20 @@ case tok::kw_static_assert: case tok::kw__Static_assert: return true; + + case tok::identifier: + if (DisambiguatingWithExpression && getLangOpts().CPlusPlus) { + if (isConstructorDeclarator(/*Unqualified=*/false, + /*DeductionGuide=*/false, + DisambiguatingWithExpression)) + return true; + // Check if this is a dtor. + if (!TryAnnotateTypeOrScopeToken() && Tok.is(tok::annot_cxxscope) && + NextToken().is(tok::tilde)) + return true; + } // simple-declaration + LLVM_FALLTHROUGH; default: return isCXXSimpleDeclaration(/*AllowForRangeDecl=*/false); } Index: clang/lib/Parse/Parser.cpp =================================================================== --- clang/lib/Parse/Parser.cpp +++ clang/lib/Parse/Parser.cpp @@ -582,13 +582,14 @@ /// /// Note that in C, it is an error if there is no first declaration. bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result, - Sema::ModuleImportState &ImportState) { + Sema::ModuleImportState &ImportState, + StmtVector *Stmts) { Actions.ActOnStartOfTranslationUnit(); // For C++20 modules, a module decl must be the first in the TU. We also // need to track module imports. ImportState = Sema::ModuleImportState::FirstDecl; - bool NoTopLevelDecls = ParseTopLevelDecl(Result, ImportState); + bool NoTopLevelDecls = ParseTopLevelDecl(Result, ImportState, Stmts); // C11 6.9p1 says translation units must have at least one top-level // declaration. C++ doesn't have this restriction. We also don't want to @@ -609,7 +610,8 @@ /// declaration /// [C++20] module-import-declaration bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result, - Sema::ModuleImportState &ImportState) { + Sema::ModuleImportState &ImportState, + StmtVector *Stmts /*=nullptr*/) { DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(*this); // Skip over the EOF token, flagging end of previous input for incremental @@ -724,6 +726,23 @@ ParsedAttributes attrs(AttrFactory); MaybeParseCXX11Attributes(attrs); + // FIXME: Remove the incremental processing pre-condition and verify clang + // still can pass its test suite, which will harden `isDeclarationStatement`. + // It is known to have several weaknesses, for example in + // isConstructorDeclarator, infinite loop in c-index-test, etc.. + // Parse a block of top-level-stmts. + while (PP.isIncrementalProcessingEnabled() && Stmts && + !isDeclarationStatement(/*IsParsingDecl=*/false)) { + // isStmtExpr ? ParsedStmtContext::InStmtExpr : ParsedStmtContext() + ParsedStmtContext SubStmtCtx = ParsedStmtContext(); + auto R = ParseStatementOrDeclaration(*Stmts, SubStmtCtx); + if (!R.isUsable()) + return true; + Stmts->push_back(R.get()); + if (Tok.is(tok::eof)) + return false; + } + Result = ParseExternalDeclaration(attrs); // An empty Result might mean a line with ';' or some parsing error, ignore // it. Index: clang/test/Interpreter/execute-stmts.cpp =================================================================== --- /dev/null +++ clang/test/Interpreter/execute-stmts.cpp @@ -0,0 +1,22 @@ +// REQUIRES: host-supports-jit +// UNSUPPORTED: system-aix +// RUN: cat %s | clang-repl | FileCheck %s + +int i = 12; +++i; +extern "C" int printf(const char *, ...); +printf("i = %d\n", i); +// CHECK: i = 13 + +namespace Ns { +void f() { i++; } +} // namespace Ns +Ns::f(); + +void g() { ++i; } +g(); + +printf("i = %d\n", i); +// CHECK: i = 15 + +% quit Index: clang/unittests/Interpreter/InterpreterTest.cpp =================================================================== --- clang/unittests/Interpreter/InterpreterTest.cpp +++ clang/unittests/Interpreter/InterpreterTest.cpp @@ -120,12 +120,7 @@ // FIXME: Add support for wrapping and running statements. auto R2 = Interp->Parse("var1++; printf(\"var1 value %d\\n\", var1);"); - EXPECT_FALSE(!!R2); - using ::testing::HasSubstr; - EXPECT_THAT(DiagnosticsOS.str(), - HasSubstr("error: unknown type name 'var1'")); - auto Err = R2.takeError(); - EXPECT_EQ("Parsing failed.", llvm::toString(std::move(Err))); + EXPECT_TRUE(!!R2); } TEST(InterpreterTest, UndoCommand) {