Index: clang/include/clang/AST/ASTNodeTraverser.h =================================================================== --- clang/include/clang/AST/ASTNodeTraverser.h +++ clang/include/clang/AST/ASTNodeTraverser.h @@ -479,6 +479,11 @@ Visit(D->getAsmString()); } + void VisitTopLevelStmtDecl(const TopLevelStmtDecl *D) { + for (const Stmt *S : D->statements()) + Visit(S); + } + void VisitCapturedDecl(const CapturedDecl *D) { Visit(D->getBody()); } void VisitOMPThreadPrivateDecl(const OMPThreadPrivateDecl *D) { Index: clang/include/clang/AST/Decl.h =================================================================== --- clang/include/clang/AST/Decl.h +++ clang/include/clang/AST/Decl.h @@ -4236,6 +4236,44 @@ static bool classofKind(Kind K) { return K == FileScopeAsm; } }; +/// A declaration that models statements on the global scope. This declaration +/// supports incremental and interactive C/C++. +/// +///\note This is used in libInterpreter, clang -cc1 -fincremental-extensions and +/// in tools such as clang-repl. +class TopLevelStmtDecl : public Decl { + friend class ASTDeclReader; + friend class ASTDeclWriter; + + Stmt **Stmts = nullptr; + unsigned NumStmts = 0; + FunctionDecl *FD = nullptr; + + TopLevelStmtDecl(DeclContext *DC, SourceLocation L) + : Decl(TopLevelStmt, DC, L) {} + + void setStmts(ASTContext &C, ArrayRef Statements) { + if (!Statements.empty()) { + Stmts = new (C) Stmt *[Statements.size()]; + std::copy(Statements.begin(), Statements.end(), Stmts); + NumStmts = Statements.size(); + } + } + + virtual void anchor(); + +public: + static TopLevelStmtDecl *Create(ASTContext &C, ArrayRef Stmts); + static TopLevelStmtDecl *CreateDeserialized(ASTContext &C, unsigned ID); + + SourceRange getSourceRange() const override LLVM_READONLY; + FunctionDecl *getOrConvertToFunction(); + ArrayRef statements() const { return {Stmts, NumStmts}; } + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == TopLevelStmt; } +}; + /// Represents a block literal declaration, which is like an /// unnamed FunctionDecl. For example: /// ^{ statement-body } or ^(int arg1, float arg2){ statement-body } Index: clang/include/clang/AST/RecursiveASTVisitor.h =================================================================== --- clang/include/clang/AST/RecursiveASTVisitor.h +++ clang/include/clang/AST/RecursiveASTVisitor.h @@ -1547,6 +1547,11 @@ DEF_TRAVERSE_DECL(FileScopeAsmDecl, { TRY_TO(TraverseStmt(D->getAsmString())); }) +DEF_TRAVERSE_DECL(TopLevelStmtDecl, { + for (auto *I : D->statements()) + TRY_TO(TraverseStmt(I)); +}) + DEF_TRAVERSE_DECL(ImportDecl, {}) DEF_TRAVERSE_DECL(FriendDecl, { Index: clang/include/clang/Basic/DeclNodes.td =================================================================== --- clang/include/clang/Basic/DeclNodes.td +++ clang/include/clang/Basic/DeclNodes.td @@ -94,6 +94,7 @@ def Export : DeclNode, DeclContext; def ObjCPropertyImpl : DeclNode; def FileScopeAsm : DeclNode; +def TopLevelStmt : DeclNode; def AccessSpec : DeclNode; def Friend : DeclNode; def FriendTemplate : DeclNode; Index: clang/include/clang/Basic/LangOptions.def =================================================================== --- clang/include/clang/Basic/LangOptions.def +++ clang/include/clang/Basic/LangOptions.def @@ -456,6 +456,11 @@ // on large _BitInts. BENIGN_VALUE_LANGOPT(MaxBitIntWidth, 32, 128, "Maximum width of a _BitInt") +LANGOPT(IncrementalExtensions, 1, 0, " True if we want to process statements" + "on the global scope, ignore EOF token and continue later on (thus " + "avoid tearing the Lexer and etc. down). Controlled by " + "-fincremental-extensions.") + #undef LANGOPT #undef COMPATIBLE_LANGOPT #undef BENIGN_LANGOPT Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -2311,6 +2311,14 @@ HelpText<"Do not enforce -fmodules-decluse and private header restrictions for textual headers. " "This flag will be removed in a future Clang release.">; +def fincremental_extensions : + Flag<["-"], "fincremental-extensions">, + Group, Flags<[CC1Option]>, + HelpText<"Enable incremental processing extensions such as processing" + "statements on the global scope.">, + MarshallingInfoFlag>; + + def fvalidate_ast_input_files_content: Flag <["-"], "fvalidate-ast-input-files-content">, Group, Flags<[CC1Option]>, Index: clang/include/clang/Lex/Preprocessor.h =================================================================== --- clang/include/clang/Lex/Preprocessor.h +++ clang/include/clang/Lex/Preprocessor.h @@ -282,10 +282,6 @@ /// Empty line handler. EmptylineHandler *Emptyline = nullptr; - /// True if we want to ignore EOF token and continue later on (thus - /// avoid tearing the Lexer and etc. down). - bool IncrementalProcessing = false; - public: /// The kind of translation unit we are processing. const TranslationUnitKind TUKind; @@ -1785,11 +1781,14 @@ void recomputeCurLexerKind(); /// Returns true if incremental processing is enabled - bool isIncrementalProcessingEnabled() const { return IncrementalProcessing; } + bool isIncrementalProcessingEnabled() const { + return getLangOpts().IncrementalExtensions; + } /// Enables the incremental processing void enableIncrementalProcessing(bool value = true) { - IncrementalProcessing = value; + // FIXME: Drop this interface. + const_cast(getLangOpts()).IncrementalExtensions = value; } /// Specify the point at which code-completion will be performed. Index: clang/include/clang/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -464,6 +464,9 @@ typedef Sema::FullExprArg FullExprArg; + /// A SmallVector of statements. + typedef SmallVector StmtVector; + // Parsing methods. /// Initialize - Warm up the parser. @@ -2073,10 +2076,7 @@ //===--------------------------------------------------------------------===// // 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.) + /// A SmallVector of expressions. typedef SmallVector ExprVector; /// A SmallVector of types. typedef SmallVector TypeVector; @@ -2455,6 +2455,8 @@ ParsingDeclSpec &DS, llvm::function_ref FieldsCallback); + Decl *ParseTopLevelStmtDecl(); + bool isDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename, bool DisambiguatingWithExpression = false); bool isTypeSpecifierQualifier(); @@ -2476,10 +2478,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(ImplicitTypenameContext::No, true); } @@ -2508,7 +2513,8 @@ /// this is a constructor declarator. bool isConstructorDeclarator( bool Unqualified, bool DeductionGuide = false, - DeclSpec::FriendSpecified IsFriend = DeclSpec::FriendSpecified::No); + DeclSpec::FriendSpecified IsFriend = DeclSpec::FriendSpecified::No, + bool DisambiguatingWithExpression = false); /// Specifies the context in which type-id/expression /// disambiguation will occur. @@ -2546,7 +2552,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/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -3105,6 +3105,8 @@ SourceLocation AsmLoc, SourceLocation RParenLoc); + Decl *ActOnTopLevelStmtDecl(const SmallVectorImpl &Stmts); + /// Handle a C++11 empty-declaration and attribute-declaration. Decl *ActOnEmptyDeclaration(Scope *S, const ParsedAttributesView &AttrList, SourceLocation SemiLoc); Index: clang/include/clang/Sema/Template.h =================================================================== --- clang/include/clang/Sema/Template.h +++ clang/include/clang/Sema/Template.h @@ -542,6 +542,7 @@ // Decls which never appear inside a class or function. #define OBJCCONTAINER(DERIVED, BASE) #define FILESCOPEASM(DERIVED, BASE) +#define TOPLEVELSTMT(DERIVED, BASE) #define IMPORT(DERIVED, BASE) #define EXPORT(DERIVED, BASE) #define LINKAGESPEC(DERIVED, BASE) Index: clang/include/clang/Serialization/ASTBitCodes.h =================================================================== --- clang/include/clang/Serialization/ASTBitCodes.h +++ clang/include/clang/Serialization/ASTBitCodes.h @@ -1315,6 +1315,9 @@ /// A FileScopeAsmDecl record. DECL_FILE_SCOPE_ASM, + /// A TopLevelStmtDecl record. + DECL_TOP_LEVEL_STMT_DECL, + /// A BlockDecl record. DECL_BLOCK, Index: clang/lib/AST/Decl.cpp =================================================================== --- clang/lib/AST/Decl.cpp +++ clang/lib/AST/Decl.cpp @@ -5202,6 +5202,54 @@ SourceLocation()); } +void TopLevelStmtDecl::anchor() {} + +TopLevelStmtDecl *TopLevelStmtDecl::Create(ASTContext &C, + llvm::ArrayRef Stmts) { + assert(Stmts.size()); + assert(C.getLangOpts().IncrementalExtensions && + "Must be used only in incremental mode"); + + SourceLocation BeginLoc = Stmts[0]->getBeginLoc(); + DeclContext *DC = C.getTranslationUnitDecl(); + + TopLevelStmtDecl *TLSD = new (C, DC) TopLevelStmtDecl(DC, BeginLoc); + TLSD->setStmts(C, Stmts); + return TLSD; +} + +TopLevelStmtDecl *TopLevelStmtDecl::CreateDeserialized(ASTContext &C, + unsigned ID) { + return new (C, ID) TopLevelStmtDecl(/*DC=*/nullptr, SourceLocation()); +} + +SourceRange TopLevelStmtDecl::getSourceRange() const { + return SourceRange(getLocation(), Stmts[NumStmts - 1]->getEndLoc()); +} + +FunctionDecl *TopLevelStmtDecl::getOrConvertToFunction() { + if (FD) + return FD; + + ASTContext &C = getASTContext(); + IdentifierInfo *name = + &C.Idents.get("_stmts_" + llvm::utostr((uintptr_t)this)); + SourceLocation noLoc; + SourceLocation beginLoc = Stmts[0]->getBeginLoc(); + FunctionProtoType::ExtProtoInfo EPI; + QualType FunctionTy = C.getFunctionType(C.VoidTy, llvm::None, EPI); + auto *TSI = C.getTrivialTypeSourceInfo(FunctionTy); + TranslationUnitDecl *TUDecl = cast(getDeclContext()); + FD = FunctionDecl::Create(C, TUDecl, getBeginLoc(), noLoc, name, FunctionTy, + TSI, SC_None); + + auto StmtArrayRef = llvm::makeArrayRef(Stmts, NumStmts); + CompoundStmt *Body = CompoundStmt::Create( + C, StmtArrayRef, FPOptionsOverride(), beginLoc, getEndLoc()); + FD->setBody(Body); + return FD; +} + void EmptyDecl::anchor() {} EmptyDecl *EmptyDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L) { Index: clang/lib/AST/DeclBase.cpp =================================================================== --- clang/lib/AST/DeclBase.cpp +++ clang/lib/AST/DeclBase.cpp @@ -830,6 +830,7 @@ case LinkageSpec: case Export: case FileScopeAsm: + case TopLevelStmt: case StaticAssert: case ObjCPropertyImpl: case PragmaComment: Index: clang/lib/AST/DeclPrinter.cpp =================================================================== --- clang/lib/AST/DeclPrinter.cpp +++ clang/lib/AST/DeclPrinter.cpp @@ -72,6 +72,7 @@ void VisitLabelDecl(LabelDecl *D); void VisitParmVarDecl(ParmVarDecl *D); void VisitFileScopeAsmDecl(FileScopeAsmDecl *D); + void VisitTopLevelStmtDecl(TopLevelStmtDecl *D); void VisitImportDecl(ImportDecl *D); void VisitStaticAssertDecl(StaticAssertDecl *D); void VisitNamespaceDecl(NamespaceDecl *D); @@ -932,6 +933,11 @@ Out << ")"; } +void DeclPrinter::VisitTopLevelStmtDecl(TopLevelStmtDecl *D) { + for (const Stmt *S : D->statements()) + S->printPretty(Out, nullptr, Policy, Indentation, "\n", &Context); +} + void DeclPrinter::VisitImportDecl(ImportDecl *D) { Out << "@import " << D->getImportedModule()->getFullModuleName() << ";\n"; Index: clang/lib/CodeGen/CGDecl.cpp =================================================================== --- clang/lib/CodeGen/CGDecl.cpp +++ clang/lib/CodeGen/CGDecl.cpp @@ -90,6 +90,7 @@ case Decl::Export: case Decl::ObjCPropertyImpl: case Decl::FileScopeAsm: + case Decl::TopLevelStmt: case Decl::Friend: case Decl::FriendTemplate: case Decl::Block: Index: clang/lib/CodeGen/CodeGenModule.cpp =================================================================== --- clang/lib/CodeGen/CodeGenModule.cpp +++ clang/lib/CodeGen/CodeGenModule.cpp @@ -6354,6 +6354,14 @@ getModule().appendModuleInlineAsm(AD->getAsmString()->getString()); break; } + case Decl::TopLevelStmt: { + FunctionDecl *FD = cast(D)->getOrConvertToFunction(); + EmitTopLevelDecl(FD); + GlobalDecl GD(FD); + auto *Fn = cast(GetGlobalValue(getMangledName(GD))); + CXXGlobalInits.push_back(Fn); + break; + } case Decl::Import: { auto *Import = cast(D); Index: clang/lib/CodeGen/ModuleBuilder.cpp =================================================================== --- clang/lib/CodeGen/ModuleBuilder.cpp +++ clang/lib/CodeGen/ModuleBuilder.cpp @@ -179,6 +179,7 @@ } 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 @@ -101,7 +101,6 @@ CompletionConsumer = &CI.getCodeCompletionConsumer(); Preprocessor &PP = CI.getPreprocessor(); - PP.enableIncrementalProcessing(); PP.EnterMainSourceFile(); if (!CI.hasSema()) @@ -174,9 +173,6 @@ Sema::ModuleImportState ImportState; for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF; AtEOF = P->ParseTopLevelDecl(ADecl, ImportState)) { - // 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 (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get())) return llvm::make_error("Parsing failed. " "The consumer rejected a decl", Index: clang/lib/Interpreter/Interpreter.cpp =================================================================== --- clang/lib/Interpreter/Interpreter.cpp +++ clang/lib/Interpreter/Interpreter.cpp @@ -139,6 +139,8 @@ // action and use other actions in incremental mode. // FIXME: Print proper driver diagnostics if the driver flags are wrong. ClangArgv.insert(ClangArgv.begin() + 1, "-c"); + ClangArgv.insert(ClangArgv.begin() + 2, "-Xclang"); + ClangArgv.insert(ClangArgv.begin() + 3, "-fincremental-extensions"); if (!llvm::is_contained(ClangArgv, " -x")) { // We do C++ by default; append right after argv[0] if no "-x" given Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -5356,6 +5356,32 @@ } } +Decl *Parser::ParseTopLevelStmtDecl() { + if (!PP.isIncrementalProcessingEnabled()) + return nullptr; + + // FIXME: Remove the incremental processing pre-condition and verify clang + // still can pass its test suite, which will harden `isDeclarationStatement`. + // Parse a block of top-level-stmts. + Parser::StmtVector Stmts; + while (!isDeclarationStatement(/*DisambiguatingWithExpression=*/true)) { + ParsedStmtContext SubStmtCtx = ParsedStmtContext(); + StmtResult R = ParseStatementOrDeclaration(Stmts, SubStmtCtx); + if (!R.isUsable()) + return nullptr; + + Stmts.push_back(R.get()); + + if (Tok.isOneOf(tok::eof, tok::annot_module_end)) + break; + } + + if (!Stmts.empty()) + return Actions.ActOnTopLevelStmtDecl(Stmts); + + return nullptr; +} + /// isDeclarationSpecifier() - Return true if the current token is part of a /// declaration specifier. /// @@ -5592,8 +5618,9 @@ } } -bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide, - DeclSpec::FriendSpecified IsFriend) { +bool Parser::isConstructorDeclarator( + bool IsUnqualified, bool DeductionGuide, DeclSpec::FriendSpecified IsFriend, + bool DisambiguatingWithExpression /*=false*/) { TentativeParsingAction TPA(*this); // Parse the C++ scope specifier. @@ -5605,10 +5632,12 @@ return false; } + IdentifierInfo *CtorII = nullptr; // Parse the constructor name. if (Tok.is(tok::identifier)) { // We already know that we have a constructor name; just consume // the token. + CtorII = Tok.getIdentifierInfo(); ConsumeToken(); } else if (Tok.is(tok::annot_template_id)) { ConsumeAnnotationToken(); @@ -5628,12 +5657,31 @@ } ConsumeParen(); - // 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 (SS.isNotEmpty() && SS.getScopeRep()) { + NestedNameSpecifier *NNS = SS.getScopeRep(); + if (NNS->getAsNamespace() || NNS->getAsNamespaceAlias()) { + TPA.Revert(); + return false; + } + if (CtorII) { + if (NamedDecl *CXXRD = NNS->getAsRecordDecl()) { + bool IsCtor = CXXRD->getIdentifier() == CtorII; + TPA.Revert(); + return IsCtor; + } + } + } + + // FIXME: This case is only valid in a member declarator contexts where we + // declare a constructor in the class body. + if (!DisambiguatingWithExpression) { + // 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; + } } // A C++11 attribute here signals that we have a constructor, and is an Index: clang/lib/Parse/ParseTentative.cpp =================================================================== --- clang/lib/Parse/ParseTentative.cpp +++ clang/lib/Parse/ParseTentative.cpp @@ -46,7 +46,32 @@ /// 'using' 'namespace' '::'[opt] nested-name-specifier[opt] /// namespace-name ';' /// -bool Parser::isCXXDeclarationStatement() { +bool Parser::isCXXDeclarationStatement( + bool DisambiguatingWithExpression /*=false*/) { + assert(getLangOpts().CPlusPlus && "Must be called for C++ only."); + if (DisambiguatingWithExpression) { + switch (Tok.getKind()) { + case tok::semi: // Not a stmt but can be extra terminator in `namespace{};` + case tok::kw_template: // FIXME: Stmts such as `t::template f();`? + case tok::kw_inline: + case tok::annot_pragma_vis: // FIXME: How to add a generic pragma + return true; + case tok::identifier: + if (isConstructorDeclarator(/*Unqualified=*/false, + /*DeductionGuide=*/false, + /*IsFriend=*/DeclSpec::FriendSpecified::No, + /*DisambiguatingWithExpression=*/true)) + return true; + // Check if this is a dtor. + if (!TryAnnotateTypeOrScopeToken() && Tok.is(tok::annot_cxxscope) && + NextToken().is(tok::tilde)) + return true; + break; + default: + break; + } + } + switch (Tok.getKind()) { // asm-definition case tok::kw_asm: Index: clang/lib/Parse/Parser.cpp =================================================================== --- clang/lib/Parse/Parser.cpp +++ clang/lib/Parse/Parser.cpp @@ -1025,8 +1025,12 @@ ConsumeToken(); return nullptr; } + + SingleDecl = ParseTopLevelStmtDecl(); + // We can't tell whether this is a function-definition or declaration yet. - return ParseDeclarationOrFunctionDefinition(Attrs, DS); + if (!SingleDecl) + return ParseDeclarationOrFunctionDefinition(Attrs, DS); } // This routine returns a DeclGroup, if the thing we parsed only contains a Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -19441,6 +19441,12 @@ return New; } +Decl *Sema::ActOnTopLevelStmtDecl(const SmallVectorImpl &Stmts) { + TopLevelStmtDecl *New = TopLevelStmtDecl::Create(Context, Stmts); + Context.getTranslationUnitDecl()->addDecl(New); + return New; +} + void Sema::ActOnPragmaRedefineExtname(IdentifierInfo* Name, IdentifierInfo* AliasName, SourceLocation PragmaLoc, Index: clang/lib/Serialization/ASTCommon.cpp =================================================================== --- clang/lib/Serialization/ASTCommon.cpp +++ clang/lib/Serialization/ASTCommon.cpp @@ -412,6 +412,7 @@ case Decl::PragmaComment: case Decl::PragmaDetectMismatch: case Decl::FileScopeAsm: + case Decl::TopLevelStmt: case Decl::AccessSpec: case Decl::Friend: case Decl::FriendTemplate: Index: clang/lib/Serialization/ASTReaderDecl.cpp =================================================================== --- clang/lib/Serialization/ASTReaderDecl.cpp +++ clang/lib/Serialization/ASTReaderDecl.cpp @@ -400,6 +400,7 @@ void VisitLinkageSpecDecl(LinkageSpecDecl *D); void VisitExportDecl(ExportDecl *D); void VisitFileScopeAsmDecl(FileScopeAsmDecl *AD); + void VisitTopLevelStmtDecl(TopLevelStmtDecl *D); void VisitImportDecl(ImportDecl *D); void VisitAccessSpecDecl(AccessSpecDecl *D); void VisitFriendDecl(FriendDecl *D); @@ -1642,6 +1643,16 @@ AD->setRParenLoc(readSourceLocation()); } +void ASTDeclReader::VisitTopLevelStmtDecl(TopLevelStmtDecl *D) { + VisitDecl(D); + unsigned NumStmts = Record.readInt(); + llvm::SmallVector Statements; + Statements.reserve(NumStmts); + for (unsigned I = 0; I != NumStmts; ++I) + Statements.push_back(Record.readStmt()); + D->setStmts(Reader.getContext(), llvm::makeArrayRef(Statements)); +} + void ASTDeclReader::VisitBlockDecl(BlockDecl *BD) { VisitDecl(BD); BD->setBody(cast_or_null(Record.readStmt())); @@ -2977,12 +2988,9 @@ return false; } - if (isa(D) || - isa(D) || - isa(D) || - isa(D) || - isa(D) || - isa(D)) + if (isa(D) || isa(D) || + isa(D) || isa(D) || isa(D) || + isa(D) || isa(D)) return true; if (isa(D) || isa(D) || isa(D) || isa(D) || @@ -3789,6 +3797,9 @@ case DECL_FILE_SCOPE_ASM: D = FileScopeAsmDecl::CreateDeserialized(Context, ID); break; + case DECL_TOP_LEVEL_STMT_DECL: + D = TopLevelStmtDecl::CreateDeserialized(Context, ID); + break; case DECL_BLOCK: D = BlockDecl::CreateDeserialized(Context, ID); break; Index: clang/lib/Serialization/ASTWriterDecl.cpp =================================================================== --- clang/lib/Serialization/ASTWriterDecl.cpp +++ clang/lib/Serialization/ASTWriterDecl.cpp @@ -122,6 +122,7 @@ void VisitLinkageSpecDecl(LinkageSpecDecl *D); void VisitExportDecl(ExportDecl *D); void VisitFileScopeAsmDecl(FileScopeAsmDecl *D); + void VisitTopLevelStmtDecl(TopLevelStmtDecl *D); void VisitImportDecl(ImportDecl *D); void VisitAccessSpecDecl(AccessSpecDecl *D); void VisitFriendDecl(FriendDecl *D); @@ -1165,6 +1166,14 @@ Code = serialization::DECL_FILE_SCOPE_ASM; } +void ASTDeclWriter::VisitTopLevelStmtDecl(TopLevelStmtDecl *D) { + VisitDecl(D); + Record.push_back(D->statements().size()); + for (Stmt *S : D->statements()) + Record.AddStmt(S); + Code = serialization::DECL_TOP_LEVEL_STMT_DECL; +} + void ASTDeclWriter::VisitEmptyDecl(EmptyDecl *D) { VisitDecl(D); Code = serialization::DECL_EMPTY; @@ -2401,7 +2410,8 @@ // File scoped assembly or obj-c or OMP declare target implementation must be // seen. - if (isa(D) || isa(D)) + if (isa(D) || isa(D) || + isa(D)) return true; if (WritingModule && isPartOfPerModuleInitializer(D)) { Index: clang/test/Interpreter/disambiguate-decl-stmt.cpp =================================================================== --- /dev/null +++ clang/test/Interpreter/disambiguate-decl-stmt.cpp @@ -0,0 +1,52 @@ +// REQUIRES: host-supports-jit +// UNSUPPORTED: system-aix +// RUN: cat %s | clang-repl -Xcc -Xclang -Xcc -verify | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -verify -fincremental-extensions %s + +// expected-no-diagnostics + +extern "C" int printf(const char*,...); + +// Decls which are hard to disambiguate + +// FIXME: Support operators. +// struct S1 { operator int(); }; +// S1::operator int() { return 0; } + + +// Dtors +using I = int; +I x = 10; +x.I::~I(); +x = 20; + +// Ctors + +// FIXME: Support deduction guide +// template struct A { A(); A(T); }; +// A() -> A; + +struct S2 { S2(); }; +S2::S2() = default; + +namespace N { struct S { S(); }; } +N::S::S() { printf("N::S::S()\n"); } +N::S s; +// CHECK: N::S::S() + +namespace Ns {namespace Ns { void Ns(); void Fs();}} +void Ns::Ns::Ns() { printf("void Ns::Ns::Ns()\n"); } +void Ns::Ns::Fs() {} + +Ns::Ns::Fs(); +Ns::Ns::Ns(); +// CHECK-NEXT: void Ns::Ns::Ns() + +struct Attrs1 { Attrs1(); }; +Attrs1::Attrs1() __attribute((pure)) = default; + +struct Attrs2 { Attrs2(); }; +__attribute((pure)) Attrs2::Attrs2() = default; + +// Extra semicolon +namespace N {}; Index: clang/test/Interpreter/execute-stmts.cpp =================================================================== --- /dev/null +++ clang/test/Interpreter/execute-stmts.cpp @@ -0,0 +1,34 @@ +// REQUIRES: host-supports-jit +// UNSUPPORTED: system-aix +// RUN: cat %s | clang-repl -Xcc -Xclang -Xcc -verify | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -verify -fincremental-extensions %s + +// expected-no-diagnostics + +extern "C" int printf(const char*,...); + +// FIXME: Support template instantiation calls. +//template T call() { printf("call\n"); return T(); } +//call(); +//// C: call + +int i = 1; +++i; +printf("i = %d\n", i); +// CHECK: i = 2 + +namespace Ns { void f(){ i++; } } +Ns::f(); + +void g() { ++i; } +g(); +::g(); + +printf("i = %d\n", i); +// CHECK-NEXT: i = 5 + +for (; i > 4; --i) printf("i = %d\n", i); +// CHECK-NEXT: i = 5 + +int j = i; printf("j = %d\n", j); +// CHECK-NEXT: j = 4 Index: clang/test/Interpreter/stmt-serialization.cpp =================================================================== --- /dev/null +++ clang/test/Interpreter/stmt-serialization.cpp @@ -0,0 +1,19 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -std=c++20 -fincremental-extensions -fmodules-cache-path=%t \ +// RUN: -x c++ %s -verify +// expected-no-diagnostics + +#pragma clang module build TopLevelStmt +module TopLevelStmt { module Statements {} } +#pragma clang module contents + +#pragma clang module begin TopLevelStmt.Statements +extern "C" int printf(const char*,...); +int i = 0; +i++; +#pragma clang module end /*TopLevelStmt.Statements*/ +#pragma clang module endbuild /*TopLevelStmt*/ + +#pragma clang module import TopLevelStmt.Statements + +printf("Value of i is '%d'", i); Index: clang/unittests/Interpreter/InterpreterTest.cpp =================================================================== --- clang/unittests/Interpreter/InterpreterTest.cpp +++ clang/unittests/Interpreter/InterpreterTest.cpp @@ -126,12 +126,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) {