Skip to content

Commit dc48893

Browse files
committed
[clang-repl] Support statements on global scope in incremental mode.
This patch teaches clang to parse statements on the global scope to allow: ``` ./bin/clang-repl clang-repl> int i = 12; clang-repl> ++i; clang-repl> extern "C" int printf(const char*,...); clang-repl> printf("%d\n", i); 13 clang-repl> %quit ``` Generally, disambiguating between statements and declarations is a non-trivial task for a C++ parser. The challenge is to allow both standard C++ to be translated as if this patch does not exist and in the cases where the user typed a statement to be executed as if it were in a function body. Clang's Parser does pretty well in disambiguating between declarations and expressions. We have added DisambiguatingWithExpression flag which allows us to preserve the existing and optimized behavior where needed and implement the extra rules for disambiguating. Only few cases require additional attention: * Constructors/destructors -- Parser::isConstructorDeclarator was used in to disambiguate between ctor-looking declarations and statements on the global scope(eg. `Ns::f()`). * The template keyword -- the template keyword can appear in both declarations and statements. This patch considers the template keyword to be a declaration starter which breaks a few cases in incremental mode which will be tackled later. * The inline (and similar) keyword -- looking at the first token in many cases allows us to classify what is a declaration. * Other language keywords and specifiers -- ObjC/ObjC++/OpenCL/OpenMP rely on pragmas or special tokens which will be handled in subsequent patches. The patch conceptually models a "top-level" statement into a TopLevelStmtDecl. The TopLevelStmtDecl is lowered into a void function with no arguments. We attach this function to the global initializer list to execute the statement blocks in the correct order. Differential revision: https://reviews.llvm.org/D127284
1 parent e324a80 commit dc48893

32 files changed

+363
-38
lines changed

clang/include/clang/AST/ASTNodeTraverser.h

+2
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,8 @@ class ASTNodeTraverser
476476
Visit(D->getAsmString());
477477
}
478478

479+
void VisitTopLevelStmtDecl(const TopLevelStmtDecl *D) { Visit(D->getStmt()); }
480+
479481
void VisitCapturedDecl(const CapturedDecl *D) { Visit(D->getBody()); }
480482

481483
void VisitOMPThreadPrivateDecl(const OMPThreadPrivateDecl *D) {

clang/include/clang/AST/Decl.h

+28
Original file line numberDiff line numberDiff line change
@@ -4277,6 +4277,34 @@ class FileScopeAsmDecl : public Decl {
42774277
static bool classofKind(Kind K) { return K == FileScopeAsm; }
42784278
};
42794279

4280+
/// A declaration that models statements at global scope. This declaration
4281+
/// supports incremental and interactive C/C++.
4282+
///
4283+
/// \note This is used in libInterpreter, clang -cc1 -fincremental-extensions
4284+
/// and in tools such as clang-repl.
4285+
class TopLevelStmtDecl : public Decl {
4286+
friend class ASTDeclReader;
4287+
friend class ASTDeclWriter;
4288+
4289+
Stmt *Statement = nullptr;
4290+
4291+
TopLevelStmtDecl(DeclContext *DC, SourceLocation L, Stmt *S)
4292+
: Decl(TopLevelStmt, DC, L), Statement(S) {}
4293+
4294+
virtual void anchor();
4295+
4296+
public:
4297+
static TopLevelStmtDecl *Create(ASTContext &C, Stmt *Statement);
4298+
static TopLevelStmtDecl *CreateDeserialized(ASTContext &C, unsigned ID);
4299+
4300+
SourceRange getSourceRange() const override LLVM_READONLY;
4301+
Stmt *getStmt() { return Statement; }
4302+
const Stmt *getStmt() const { return Statement; }
4303+
4304+
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
4305+
static bool classofKind(Kind K) { return K == TopLevelStmt; }
4306+
};
4307+
42804308
/// Represents a block literal declaration, which is like an
42814309
/// unnamed FunctionDecl. For example:
42824310
/// ^{ statement-body } or ^(int arg1, float arg2){ statement-body }

clang/include/clang/AST/RecursiveASTVisitor.h

+2
Original file line numberDiff line numberDiff line change
@@ -1543,6 +1543,8 @@ DEF_TRAVERSE_DECL(LifetimeExtendedTemporaryDecl, {
15431543
DEF_TRAVERSE_DECL(FileScopeAsmDecl,
15441544
{ TRY_TO(TraverseStmt(D->getAsmString())); })
15451545

1546+
DEF_TRAVERSE_DECL(TopLevelStmtDecl, { TRY_TO(TraverseStmt(D->getStmt())); })
1547+
15461548
DEF_TRAVERSE_DECL(ImportDecl, {})
15471549

15481550
DEF_TRAVERSE_DECL(FriendDecl, {

clang/include/clang/Basic/DeclNodes.td

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ def LinkageSpec : DeclNode<Decl>, DeclContext;
9595
def Export : DeclNode<Decl>, DeclContext;
9696
def ObjCPropertyImpl : DeclNode<Decl>;
9797
def FileScopeAsm : DeclNode<Decl>;
98+
def TopLevelStmt : DeclNode<Decl>;
9899
def AccessSpec : DeclNode<Decl>;
99100
def Friend : DeclNode<Decl>;
100101
def FriendTemplate : DeclNode<Decl>;

clang/include/clang/Basic/LangOptions.def

+5
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,11 @@ VALUE_LANGOPT(FuchsiaAPILevel, 32, 0, "Fuchsia API level")
459459
// on large _BitInts.
460460
BENIGN_VALUE_LANGOPT(MaxBitIntWidth, 32, 128, "Maximum width of a _BitInt")
461461

462+
LANGOPT(IncrementalExtensions, 1, 0, " True if we want to process statements"
463+
"on the global scope, ignore EOF token and continue later on (thus "
464+
"avoid tearing the Lexer and etc. down). Controlled by "
465+
"-fincremental-extensions.")
466+
462467
#undef LANGOPT
463468
#undef COMPATIBLE_LANGOPT
464469
#undef BENIGN_LANGOPT

clang/include/clang/Driver/Options.td

+7
Original file line numberDiff line numberDiff line change
@@ -2320,6 +2320,13 @@ def fno_modules_validate_textual_header_includes :
23202320
HelpText<"Do not enforce -fmodules-decluse and private header restrictions for textual headers. "
23212321
"This flag will be removed in a future Clang release.">;
23222322

2323+
def fincremental_extensions :
2324+
Flag<["-"], "fincremental-extensions">,
2325+
Group<f_Group>, Flags<[CC1Option]>,
2326+
HelpText<"Enable incremental processing extensions such as processing"
2327+
"statements on the global scope.">,
2328+
MarshallingInfoFlag<LangOpts<"IncrementalExtensions">>;
2329+
23232330
def fvalidate_ast_input_files_content:
23242331
Flag <["-"], "fvalidate-ast-input-files-content">,
23252332
Group<f_Group>, Flags<[CC1Option]>,

clang/include/clang/Lex/Preprocessor.h

+5-6
Original file line numberDiff line numberDiff line change
@@ -283,10 +283,6 @@ class Preprocessor {
283283
/// Empty line handler.
284284
EmptylineHandler *Emptyline = nullptr;
285285

286-
/// True if we want to ignore EOF token and continue later on (thus
287-
/// avoid tearing the Lexer and etc. down).
288-
bool IncrementalProcessing = false;
289-
290286
public:
291287
/// The kind of translation unit we are processing.
292288
const TranslationUnitKind TUKind;
@@ -1778,11 +1774,14 @@ class Preprocessor {
17781774
void recomputeCurLexerKind();
17791775

17801776
/// Returns true if incremental processing is enabled
1781-
bool isIncrementalProcessingEnabled() const { return IncrementalProcessing; }
1777+
bool isIncrementalProcessingEnabled() const {
1778+
return getLangOpts().IncrementalExtensions;
1779+
}
17821780

17831781
/// Enables the incremental processing
17841782
void enableIncrementalProcessing(bool value = true) {
1785-
IncrementalProcessing = value;
1783+
// FIXME: Drop this interface.
1784+
const_cast<LangOptions &>(getLangOpts()).IncrementalExtensions = value;
17861785
}
17871786

17881787
/// Specify the point at which code-completion will be performed.

clang/include/clang/Parse/Parser.h

+12-7
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,9 @@ class Parser : public CodeCompletionHandler {
464464

465465
typedef Sema::FullExprArg FullExprArg;
466466

467+
/// A SmallVector of statements.
468+
typedef SmallVector<Stmt *, 32> StmtVector;
469+
467470
// Parsing methods.
468471

469472
/// Initialize - Warm up the parser.
@@ -2071,10 +2074,7 @@ class Parser : public CodeCompletionHandler {
20712074
//===--------------------------------------------------------------------===//
20722075
// C99 6.8: Statements and Blocks.
20732076

2074-
/// A SmallVector of statements, with stack size 32 (as that is the only one
2075-
/// used.)
2076-
typedef SmallVector<Stmt*, 32> StmtVector;
2077-
/// A SmallVector of expressions, with stack size 12 (the maximum used.)
2077+
/// A SmallVector of expressions.
20782078
typedef SmallVector<Expr*, 12> ExprVector;
20792079

20802080
StmtResult
@@ -2451,6 +2451,8 @@ class Parser : public CodeCompletionHandler {
24512451
ParsingDeclSpec &DS,
24522452
llvm::function_ref<void(ParsingFieldDeclarator &)> FieldsCallback);
24532453

2454+
DeclGroupPtrTy ParseTopLevelStmtDecl();
2455+
24542456
bool isDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
24552457
bool DisambiguatingWithExpression = false);
24562458
bool isTypeSpecifierQualifier();
@@ -2472,10 +2474,13 @@ class Parser : public CodeCompletionHandler {
24722474

24732475
/// isDeclarationStatement - Disambiguates between a declaration or an
24742476
/// expression statement, when parsing function bodies.
2477+
///
2478+
/// \param DisambiguatingWithExpression - True to indicate that the purpose of
2479+
/// this check is to disambiguate between an expression and a declaration.
24752480
/// Returns true for declaration, false for expression.
2476-
bool isDeclarationStatement() {
2481+
bool isDeclarationStatement(bool DisambiguatingWithExpression = false) {
24772482
if (getLangOpts().CPlusPlus)
2478-
return isCXXDeclarationStatement();
2483+
return isCXXDeclarationStatement(DisambiguatingWithExpression);
24792484
return isDeclarationSpecifier(ImplicitTypenameContext::No, true);
24802485
}
24812486

@@ -2542,7 +2547,7 @@ class Parser : public CodeCompletionHandler {
25422547
/// isCXXDeclarationStatement - C++-specialized function that disambiguates
25432548
/// between a declaration or an expression statement, when parsing function
25442549
/// bodies. Returns true for declaration, false for expression.
2545-
bool isCXXDeclarationStatement();
2550+
bool isCXXDeclarationStatement(bool DisambiguatingWithExpression = false);
25462551

25472552
/// isCXXSimpleDeclaration - C++-specialized function that disambiguates
25482553
/// between a simple-declaration or an expression-statement.

clang/include/clang/Sema/Sema.h

+2
Original file line numberDiff line numberDiff line change
@@ -3106,6 +3106,8 @@ class Sema final {
31063106
SourceLocation AsmLoc,
31073107
SourceLocation RParenLoc);
31083108

3109+
Decl *ActOnTopLevelStmtDecl(Stmt *Statement);
3110+
31093111
/// Handle a C++11 empty-declaration and attribute-declaration.
31103112
Decl *ActOnEmptyDeclaration(Scope *S, const ParsedAttributesView &AttrList,
31113113
SourceLocation SemiLoc);

clang/include/clang/Sema/Template.h

+1
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ enum class TemplateSubstitutionKind : char {
571571
// Decls which never appear inside a class or function.
572572
#define OBJCCONTAINER(DERIVED, BASE)
573573
#define FILESCOPEASM(DERIVED, BASE)
574+
#define TOPLEVELSTMT(DERIVED, BASE)
574575
#define IMPORT(DERIVED, BASE)
575576
#define EXPORT(DERIVED, BASE)
576577
#define LINKAGESPEC(DERIVED, BASE)

clang/include/clang/Serialization/ASTBitCodes.h

+3
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,9 @@ enum DeclCode {
13181318
/// A FileScopeAsmDecl record.
13191319
DECL_FILE_SCOPE_ASM,
13201320

1321+
/// A TopLevelStmtDecl record.
1322+
DECL_TOP_LEVEL_STMT_DECL,
1323+
13211324
/// A BlockDecl record.
13221325
DECL_BLOCK,
13231326

clang/lib/AST/Decl.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -5236,6 +5236,29 @@ FileScopeAsmDecl *FileScopeAsmDecl::CreateDeserialized(ASTContext &C,
52365236
SourceLocation());
52375237
}
52385238

5239+
void TopLevelStmtDecl::anchor() {}
5240+
5241+
TopLevelStmtDecl *TopLevelStmtDecl::Create(ASTContext &C, Stmt *Statement) {
5242+
assert(Statement);
5243+
assert(C.getLangOpts().IncrementalExtensions &&
5244+
"Must be used only in incremental mode");
5245+
5246+
SourceLocation BeginLoc = Statement->getBeginLoc();
5247+
DeclContext *DC = C.getTranslationUnitDecl();
5248+
5249+
return new (C, DC) TopLevelStmtDecl(DC, BeginLoc, Statement);
5250+
}
5251+
5252+
TopLevelStmtDecl *TopLevelStmtDecl::CreateDeserialized(ASTContext &C,
5253+
unsigned ID) {
5254+
return new (C, ID)
5255+
TopLevelStmtDecl(/*DC=*/nullptr, SourceLocation(), /*S=*/nullptr);
5256+
}
5257+
5258+
SourceRange TopLevelStmtDecl::getSourceRange() const {
5259+
return SourceRange(getLocation(), Statement->getEndLoc());
5260+
}
5261+
52395262
void EmptyDecl::anchor() {}
52405263

52415264
EmptyDecl *EmptyDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L) {

clang/lib/AST/DeclBase.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
843843
case LinkageSpec:
844844
case Export:
845845
case FileScopeAsm:
846+
case TopLevelStmt:
846847
case StaticAssert:
847848
case ObjCPropertyImpl:
848849
case PragmaComment:

clang/lib/AST/DeclPrinter.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ namespace {
7272
void VisitLabelDecl(LabelDecl *D);
7373
void VisitParmVarDecl(ParmVarDecl *D);
7474
void VisitFileScopeAsmDecl(FileScopeAsmDecl *D);
75+
void VisitTopLevelStmtDecl(TopLevelStmtDecl *D);
7576
void VisitImportDecl(ImportDecl *D);
7677
void VisitStaticAssertDecl(StaticAssertDecl *D);
7778
void VisitNamespaceDecl(NamespaceDecl *D);
@@ -932,6 +933,11 @@ void DeclPrinter::VisitFileScopeAsmDecl(FileScopeAsmDecl *D) {
932933
Out << ")";
933934
}
934935

936+
void DeclPrinter::VisitTopLevelStmtDecl(TopLevelStmtDecl *D) {
937+
assert(D->getStmt());
938+
D->getStmt()->printPretty(Out, nullptr, Policy, Indentation, "\n", &Context);
939+
}
940+
935941
void DeclPrinter::VisitImportDecl(ImportDecl *D) {
936942
Out << "@import " << D->getImportedModule()->getFullModuleName()
937943
<< ";\n";

clang/lib/CodeGen/CGDecl.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
9090
case Decl::Export:
9191
case Decl::ObjCPropertyImpl:
9292
case Decl::FileScopeAsm:
93+
case Decl::TopLevelStmt:
9394
case Decl::Friend:
9495
case Decl::FriendTemplate:
9596
case Decl::Block:

clang/lib/CodeGen/CodeGenModule.cpp

+45
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,14 @@ void CodeGenModule::Release() {
516516
applyGlobalValReplacements();
517517
applyReplacements();
518518
emitMultiVersionFunctions();
519+
520+
if (Context.getLangOpts().IncrementalExtensions &&
521+
GlobalTopLevelStmtBlockInFlight.first) {
522+
const TopLevelStmtDecl *TLSD = GlobalTopLevelStmtBlockInFlight.second;
523+
GlobalTopLevelStmtBlockInFlight.first->FinishFunction(TLSD->getEndLoc());
524+
GlobalTopLevelStmtBlockInFlight = {};
525+
}
526+
519527
if (CXX20ModuleInits && Primary && Primary->isInterfaceOrPartition())
520528
EmitCXXModuleInitFunc(Primary);
521529
else
@@ -6150,6 +6158,39 @@ void CodeGenModule::EmitLinkageSpec(const LinkageSpecDecl *LSD) {
61506158
EmitDeclContext(LSD);
61516159
}
61526160

6161+
void CodeGenModule::EmitTopLevelStmt(const TopLevelStmtDecl *D) {
6162+
std::unique_ptr<CodeGenFunction> &CurCGF =
6163+
GlobalTopLevelStmtBlockInFlight.first;
6164+
6165+
// We emitted a top-level stmt but after it there is initialization.
6166+
// Stop squashing the top-level stmts into a single function.
6167+
if (CurCGF && CXXGlobalInits.back() != CurCGF->CurFn) {
6168+
CurCGF->FinishFunction(D->getEndLoc());
6169+
CurCGF = nullptr;
6170+
}
6171+
6172+
if (!CurCGF) {
6173+
// void __stmts__N(void)
6174+
// FIXME: Ask the ABI name mangler to pick a name.
6175+
std::string Name = "__stmts__" + llvm::utostr(CXXGlobalInits.size());
6176+
FunctionArgList Args;
6177+
QualType RetTy = getContext().VoidTy;
6178+
const CGFunctionInfo &FnInfo =
6179+
getTypes().arrangeBuiltinFunctionDeclaration(RetTy, Args);
6180+
llvm::FunctionType *FnTy = getTypes().GetFunctionType(FnInfo);
6181+
llvm::Function *Fn = llvm::Function::Create(
6182+
FnTy, llvm::GlobalValue::InternalLinkage, Name, &getModule());
6183+
6184+
CurCGF.reset(new CodeGenFunction(*this));
6185+
GlobalTopLevelStmtBlockInFlight.second = D;
6186+
CurCGF->StartFunction(GlobalDecl(), RetTy, Fn, FnInfo, Args,
6187+
D->getBeginLoc(), D->getBeginLoc());
6188+
CXXGlobalInits.push_back(Fn);
6189+
}
6190+
6191+
CurCGF->EmitStmt(D->getStmt());
6192+
}
6193+
61536194
void CodeGenModule::EmitDeclContext(const DeclContext *DC) {
61546195
for (auto *I : DC->decls()) {
61556196
// Unlike other DeclContexts, the contents of an ObjCImplDecl at TU scope
@@ -6359,6 +6400,10 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
63596400
break;
63606401
}
63616402

6403+
case Decl::TopLevelStmt:
6404+
EmitTopLevelStmt(cast<TopLevelStmtDecl>(D));
6405+
break;
6406+
63626407
case Decl::Import: {
63636408
auto *Import = cast<ImportDecl>(D);
63646409

clang/lib/CodeGen/CodeGenModule.h

+6
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,11 @@ class CodeGenModule : public CodeGenTypeCache {
591591

592592
llvm::DenseMap<const llvm::Constant *, llvm::GlobalVariable *> RTTIProxyMap;
593593

594+
// Helps squashing blocks of TopLevelStmtDecl into a single llvm::Function
595+
// when used with -fincremental-extensions.
596+
std::pair<std::unique_ptr<CodeGenFunction>, const TopLevelStmtDecl *>
597+
GlobalTopLevelStmtBlockInFlight;
598+
594599
public:
595600
CodeGenModule(ASTContext &C, IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
596601
const HeaderSearchOptions &headersearchopts,
@@ -1590,6 +1595,7 @@ class CodeGenModule : public CodeGenTypeCache {
15901595

15911596
void EmitDeclContext(const DeclContext *DC);
15921597
void EmitLinkageSpec(const LinkageSpecDecl *D);
1598+
void EmitTopLevelStmt(const TopLevelStmtDecl *D);
15931599

15941600
/// Emit the function that initializes C++ thread_local variables.
15951601
void EmitCXXThreadLocalInitFunc();

clang/lib/CodeGen/ModuleBuilder.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ namespace {
179179
}
180180

181181
bool HandleTopLevelDecl(DeclGroupRef DG) override {
182+
// FIXME: Why not return false and abort parsing?
182183
if (Diags.hasErrorOccurred())
183184
return true;
184185

clang/lib/Interpreter/IncrementalParser.cpp

-4
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ class IncrementalAction : public WrapperFrontendAction {
101101
CompletionConsumer = &CI.getCodeCompletionConsumer();
102102

103103
Preprocessor &PP = CI.getPreprocessor();
104-
PP.enableIncrementalProcessing();
105104
PP.EnterMainSourceFile();
106105

107106
if (!CI.hasSema())
@@ -174,9 +173,6 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
174173
Sema::ModuleImportState ImportState;
175174
for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF;
176175
AtEOF = P->ParseTopLevelDecl(ADecl, ImportState)) {
177-
// If we got a null return and something *was* parsed, ignore it. This
178-
// is due to a top-level semicolon, an action override, or a parse error
179-
// skipping something.
180176
if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get()))
181177
return llvm::make_error<llvm::StringError>("Parsing failed. "
182178
"The consumer rejected a decl",

clang/lib/Interpreter/Interpreter.cpp

+5-7
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,11 @@ IncrementalCompilerBuilder::create(std::vector<const char *> &ClangArgv) {
138138
// specified. By prepending we allow users to override the default
139139
// action and use other actions in incremental mode.
140140
// FIXME: Print proper driver diagnostics if the driver flags are wrong.
141-
ClangArgv.insert(ClangArgv.begin() + 1, "-c");
142-
143-
if (!llvm::is_contained(ClangArgv, " -x")) {
144-
// We do C++ by default; append right after argv[0] if no "-x" given
145-
ClangArgv.push_back("-x");
146-
ClangArgv.push_back("c++");
147-
}
141+
// We do C++ by default; append right after argv[0] if no "-x" given
142+
ClangArgv.insert(ClangArgv.end(), "-xc++");
143+
ClangArgv.insert(ClangArgv.end(), "-Xclang");
144+
ClangArgv.insert(ClangArgv.end(), "-fincremental-extensions");
145+
ClangArgv.insert(ClangArgv.end(), "-c");
148146

149147
// Put a dummy C++ file on to ensure there's at least one compile job for the
150148
// driver to construct.

0 commit comments

Comments
 (0)