Skip to content

Commit 4b70d17

Browse files
[clang-repl] Names declared in if conditions and for-init statements are local to the inner context (#84150)
Make TopLevelStmtDecl a DeclContext so that variables defined in statements are attached to the TopLevelDeclContext. This fixes redefinition errors from variables declared in if conditions and for-init statements. These must be local to the inner context (C++ 3.3.2p4), but they had generated definitions on global scope instead. This PR makes the TopLevelStmtDecl looking more like a FunctionDecl and that's fine because the FunctionDecl is very close in terms of semantics. Additionally, ActOnForStmt() requires a CompoundScope when processing a NullStmt body. --------- Co-authored-by: Vassil Vassilev <v.g.vassilev@gmail.com>
1 parent 464d9d9 commit 4b70d17

File tree

9 files changed

+75
-23
lines changed

9 files changed

+75
-23
lines changed

clang/include/clang/AST/Decl.h

+10-6
Original file line numberDiff line numberDiff line change
@@ -4419,15 +4419,15 @@ class FileScopeAsmDecl : public Decl {
44194419
///
44204420
/// \note This is used in libInterpreter, clang -cc1 -fincremental-extensions
44214421
/// and in tools such as clang-repl.
4422-
class TopLevelStmtDecl : public Decl {
4422+
class TopLevelStmtDecl : public Decl, public DeclContext {
44234423
friend class ASTDeclReader;
44244424
friend class ASTDeclWriter;
44254425

44264426
Stmt *Statement = nullptr;
44274427
bool IsSemiMissing = false;
44284428

44294429
TopLevelStmtDecl(DeclContext *DC, SourceLocation L, Stmt *S)
4430-
: Decl(TopLevelStmt, DC, L), Statement(S) {}
4430+
: Decl(TopLevelStmt, DC, L), DeclContext(TopLevelStmt), Statement(S) {}
44314431

44324432
virtual void anchor();
44334433

@@ -4438,15 +4438,19 @@ class TopLevelStmtDecl : public Decl {
44384438
SourceRange getSourceRange() const override LLVM_READONLY;
44394439
Stmt *getStmt() { return Statement; }
44404440
const Stmt *getStmt() const { return Statement; }
4441-
void setStmt(Stmt *S) {
4442-
assert(IsSemiMissing && "Operation supported for printing values only!");
4443-
Statement = S;
4444-
}
4441+
void setStmt(Stmt *S);
44454442
bool isSemiMissing() const { return IsSemiMissing; }
44464443
void setSemiMissing(bool Missing = true) { IsSemiMissing = Missing; }
44474444

44484445
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
44494446
static bool classofKind(Kind K) { return K == TopLevelStmt; }
4447+
4448+
static DeclContext *castToDeclContext(const TopLevelStmtDecl *D) {
4449+
return static_cast<DeclContext *>(const_cast<TopLevelStmtDecl *>(D));
4450+
}
4451+
static TopLevelStmtDecl *castFromDeclContext(const DeclContext *DC) {
4452+
return static_cast<TopLevelStmtDecl *>(const_cast<DeclContext *>(DC));
4453+
}
44504454
};
44514455

44524456
/// Represents a block literal declaration, which is like an

clang/include/clang/AST/DeclBase.h

+1
Original file line numberDiff line numberDiff line change
@@ -2120,6 +2120,7 @@ class DeclContext {
21202120
case Decl::Block:
21212121
case Decl::Captured:
21222122
case Decl::ObjCMethod:
2123+
case Decl::TopLevelStmt:
21232124
return true;
21242125
default:
21252126
return getDeclKind() >= Decl::firstFunction &&

clang/include/clang/Basic/DeclNodes.td

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +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>;
98+
def TopLevelStmt : DeclNode<Decl>, DeclContext;
9999
def AccessSpec : DeclNode<Decl>;
100100
def Friend : DeclNode<Decl>;
101101
def FriendTemplate : DeclNode<Decl>;

clang/include/clang/Sema/Sema.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -3263,7 +3263,8 @@ class Sema final {
32633263
Decl *ActOnFileScopeAsmDecl(Expr *expr, SourceLocation AsmLoc,
32643264
SourceLocation RParenLoc);
32653265

3266-
Decl *ActOnTopLevelStmtDecl(Stmt *Statement);
3266+
TopLevelStmtDecl *ActOnStartTopLevelStmtDecl(Scope *S);
3267+
void ActOnFinishTopLevelStmtDecl(TopLevelStmtDecl *D, Stmt *Statement);
32673268

32683269
void ActOnPopScope(SourceLocation Loc, Scope *S);
32693270

clang/lib/AST/Decl.cpp

+8-3
Original file line numberDiff line numberDiff line change
@@ -5552,14 +5552,13 @@ FileScopeAsmDecl *FileScopeAsmDecl::CreateDeserialized(ASTContext &C,
55525552
void TopLevelStmtDecl::anchor() {}
55535553

55545554
TopLevelStmtDecl *TopLevelStmtDecl::Create(ASTContext &C, Stmt *Statement) {
5555-
assert(Statement);
55565555
assert(C.getLangOpts().IncrementalExtensions &&
55575556
"Must be used only in incremental mode");
55585557

5559-
SourceLocation BeginLoc = Statement->getBeginLoc();
5558+
SourceLocation Loc = Statement ? Statement->getBeginLoc() : SourceLocation();
55605559
DeclContext *DC = C.getTranslationUnitDecl();
55615560

5562-
return new (C, DC) TopLevelStmtDecl(DC, BeginLoc, Statement);
5561+
return new (C, DC) TopLevelStmtDecl(DC, Loc, Statement);
55635562
}
55645563

55655564
TopLevelStmtDecl *TopLevelStmtDecl::CreateDeserialized(ASTContext &C,
@@ -5572,6 +5571,12 @@ SourceRange TopLevelStmtDecl::getSourceRange() const {
55725571
return SourceRange(getLocation(), Statement->getEndLoc());
55735572
}
55745573

5574+
void TopLevelStmtDecl::setStmt(Stmt *S) {
5575+
assert(S);
5576+
Statement = S;
5577+
setLocation(Statement->getBeginLoc());
5578+
}
5579+
55755580
void EmptyDecl::anchor() {}
55765581

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

clang/lib/AST/DeclBase.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1352,6 +1352,7 @@ DeclContext *DeclContext::getPrimaryContext() {
13521352
case Decl::ExternCContext:
13531353
case Decl::LinkageSpec:
13541354
case Decl::Export:
1355+
case Decl::TopLevelStmt:
13551356
case Decl::Block:
13561357
case Decl::Captured:
13571358
case Decl::OMPDeclareReduction:

clang/lib/Parse/ParseDecl.cpp

+16-8
Original file line numberDiff line numberDiff line change
@@ -5678,24 +5678,32 @@ Parser::DeclGroupPtrTy Parser::ParseTopLevelStmtDecl() {
56785678
// Parse a top-level-stmt.
56795679
Parser::StmtVector Stmts;
56805680
ParsedStmtContext SubStmtCtx = ParsedStmtContext();
5681-
Actions.PushFunctionScope();
5681+
ParseScope FnScope(this, Scope::FnScope | Scope::DeclScope |
5682+
Scope::CompoundStmtScope);
5683+
TopLevelStmtDecl *TLSD = Actions.ActOnStartTopLevelStmtDecl(getCurScope());
56825684
StmtResult R = ParseStatementOrDeclaration(Stmts, SubStmtCtx);
5683-
Actions.PopFunctionScopeInfo();
56845685
if (!R.isUsable())
56855686
return nullptr;
56865687

5687-
SmallVector<Decl *, 2> DeclsInGroup;
5688-
DeclsInGroup.push_back(Actions.ActOnTopLevelStmtDecl(R.get()));
5688+
Actions.ActOnFinishTopLevelStmtDecl(TLSD, R.get());
56895689

56905690
if (Tok.is(tok::annot_repl_input_end) &&
56915691
Tok.getAnnotationValue() != nullptr) {
56925692
ConsumeAnnotationToken();
5693-
cast<TopLevelStmtDecl>(DeclsInGroup.back())->setSemiMissing();
5693+
TLSD->setSemiMissing();
56945694
}
56955695

5696-
// Currently happens for things like -fms-extensions and use `__if_exists`.
5697-
for (Stmt *S : Stmts)
5698-
DeclsInGroup.push_back(Actions.ActOnTopLevelStmtDecl(S));
5696+
SmallVector<Decl *, 2> DeclsInGroup;
5697+
DeclsInGroup.push_back(TLSD);
5698+
5699+
// Currently happens for things like -fms-extensions and use `__if_exists`.
5700+
for (Stmt *S : Stmts) {
5701+
// Here we should be safe as `__if_exists` and friends are not introducing
5702+
// new variables which need to live outside file scope.
5703+
TopLevelStmtDecl *D = Actions.ActOnStartTopLevelStmtDecl(getCurScope());
5704+
Actions.ActOnFinishTopLevelStmtDecl(D, S);
5705+
DeclsInGroup.push_back(D);
5706+
}
56995707

57005708
return Actions.BuildDeclaratorGroup(DeclsInGroup);
57015709
}

clang/lib/Sema/SemaDecl.cpp

+13-3
Original file line numberDiff line numberDiff line change
@@ -20519,12 +20519,22 @@ Decl *Sema::ActOnFileScopeAsmDecl(Expr *expr,
2051920519
return New;
2052020520
}
2052120521

20522-
Decl *Sema::ActOnTopLevelStmtDecl(Stmt *Statement) {
20523-
auto *New = TopLevelStmtDecl::Create(Context, Statement);
20524-
Context.getTranslationUnitDecl()->addDecl(New);
20522+
TopLevelStmtDecl *Sema::ActOnStartTopLevelStmtDecl(Scope *S) {
20523+
auto *New = TopLevelStmtDecl::Create(Context, /*Statement=*/nullptr);
20524+
CurContext->addDecl(New);
20525+
PushDeclContext(S, New);
20526+
PushFunctionScope();
20527+
PushCompoundScope(false);
2052520528
return New;
2052620529
}
2052720530

20531+
void Sema::ActOnFinishTopLevelStmtDecl(TopLevelStmtDecl *D, Stmt *Statement) {
20532+
D->setStmt(Statement);
20533+
PopCompoundScope();
20534+
PopFunctionScopeInfo();
20535+
PopDeclContext();
20536+
}
20537+
2052820538
void Sema::ActOnPragmaRedefineExtname(IdentifierInfo* Name,
2052920539
IdentifierInfo* AliasName,
2053020540
SourceLocation PragmaLoc,

clang/test/Interpreter/execute-stmts.cpp

+23-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
//CODEGEN-CHECK-COUNT-2: define internal void @__stmts__
1010
//CODEGEN-CHECK-NOT: define internal void @__stmts__
1111

12-
1312
extern "C" int printf(const char*,...);
1413

1514
template <typename T> T call() { printf("called\n"); return T(); }
@@ -41,3 +40,26 @@ for (; i > 4; --i) { printf("i = %d\n", i); };
4140

4241
int j = i; printf("j = %d\n", j);
4342
// CHECK-NEXT: j = 4
43+
44+
{i = 0; printf("i = %d (global scope)\n", i);}
45+
// CHECK-NEXT: i = 0
46+
47+
while (int i = 1) { printf("i = %d (while condition)\n", i--); break; }
48+
// CHECK-NEXT: i = 1
49+
50+
if (int i = 2) printf("i = %d (if condition)\n", i);
51+
// CHECK-NEXT: i = 2
52+
53+
switch (int i = 3) { default: printf("i = %d (switch condition)\n", i); }
54+
// CHECK-NEXT: i = 3
55+
56+
for (int i = 4; i > 3; --i) printf("i = %d (for-init)\n", i);
57+
// CHECK-NEXT: i = 4
58+
59+
for (const auto &i : "5") printf("i = %c (range-based for-init)\n", i);
60+
// CHECK-NEXT: i = 5
61+
62+
int *aa=nullptr;
63+
if (auto *b=aa) *b += 1;
64+
while (auto *b=aa) ;
65+
for (auto *b=aa; b; *b+=1) ;

0 commit comments

Comments
 (0)