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

Lines changed: 363 additions & 38 deletions

‎clang/include/clang/AST/ASTNodeTraverser.h‎

Lines changed: 2 additions & 0 deletions
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‎

Lines changed: 28 additions & 0 deletions
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‎

Lines changed: 2 additions & 0 deletions
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‎

Lines changed: 1 addition & 0 deletions
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‎

Lines changed: 5 additions & 0 deletions
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‎

Lines changed: 7 additions & 0 deletions
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‎

Lines changed: 5 additions & 6 deletions
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‎

Lines changed: 12 additions & 7 deletions
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‎

Lines changed: 2 additions & 0 deletions
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‎

Lines changed: 1 addition & 0 deletions
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)

0 commit comments

Comments
 (0)