Skip to content

Commit 3eaca31

Browse files
authored
[clang][Interp] Simplify and fix variable scope handling (#101788)
Change scope handling to allow multiple Destroy calls for a given scope, provided it is preceeded by a InitScope call. This is necessary to properly allow nested scopes in loops.
1 parent 5d64b37 commit 3eaca31

File tree

8 files changed

+101
-75
lines changed

8 files changed

+101
-75
lines changed

clang/lib/AST/Interp/Compiler.cpp

Lines changed: 15 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2226,7 +2226,7 @@ bool Compiler<Emitter>::VisitExprWithCleanups(const ExprWithCleanups *E) {
22262226

22272227
assert(E->getNumObjects() == 0 && "TODO: Implement cleanups");
22282228

2229-
return this->delegate(SubExpr) && ES.destroyLocals();
2229+
return this->delegate(SubExpr) && ES.destroyLocals(E);
22302230
}
22312231

22322232
template <class Emitter>
@@ -2537,13 +2537,8 @@ bool Compiler<Emitter>::VisitCXXConstructExpr(const CXXConstructExpr *E) {
25372537
return false;
25382538
}
25392539

2540-
// Immediately call the destructor if we have to.
2541-
if (DiscardResult) {
2542-
if (!this->emitRecordDestruction(getRecord(E->getType())))
2543-
return false;
2544-
if (!this->emitPopPtr(E))
2545-
return false;
2546-
}
2540+
if (DiscardResult)
2541+
return this->emitPopPtr(E);
25472542
return true;
25482543
}
25492544

@@ -4222,22 +4217,6 @@ template <class Emitter> bool Compiler<Emitter>::visitStmt(const Stmt *S) {
42224217
}
42234218
}
42244219

4225-
/// Visits the given statment without creating a variable
4226-
/// scope for it in case it is a compound statement.
4227-
template <class Emitter> bool Compiler<Emitter>::visitLoopBody(const Stmt *S) {
4228-
if (isa<NullStmt>(S))
4229-
return true;
4230-
4231-
if (const auto *CS = dyn_cast<CompoundStmt>(S)) {
4232-
for (const auto *InnerStmt : CS->body())
4233-
if (!visitStmt(InnerStmt))
4234-
return false;
4235-
return true;
4236-
}
4237-
4238-
return this->visitStmt(S);
4239-
}
4240-
42414220
template <class Emitter>
42424221
bool Compiler<Emitter>::visitCompoundStmt(const CompoundStmt *S) {
42434222
BlockScope<Emitter> Scope(this);
@@ -4300,8 +4279,6 @@ bool Compiler<Emitter>::visitReturnStmt(const ReturnStmt *RS) {
43004279
}
43014280

43024281
template <class Emitter> bool Compiler<Emitter>::visitIfStmt(const IfStmt *IS) {
4303-
BlockScope<Emitter> IfScope(this);
4304-
43054282
if (IS->isNonNegatedConsteval())
43064283
return visitStmt(IS->getThen());
43074284
if (IS->isNegatedConsteval())
@@ -4340,7 +4317,7 @@ template <class Emitter> bool Compiler<Emitter>::visitIfStmt(const IfStmt *IS) {
43404317
this->emitLabel(LabelEnd);
43414318
}
43424319

4343-
return IfScope.destroyLocals();
4320+
return true;
43444321
}
43454322

43464323
template <class Emitter>
@@ -4364,12 +4341,8 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) {
43644341
if (!this->jumpFalse(EndLabel))
43654342
return false;
43664343

4367-
LocalScope<Emitter> Scope(this);
4368-
{
4369-
DestructorScope<Emitter> DS(Scope);
4370-
if (!this->visitLoopBody(Body))
4371-
return false;
4372-
}
4344+
if (!this->visitStmt(Body))
4345+
return false;
43734346

43744347
if (!this->jump(CondLabel))
43754348
return false;
@@ -4387,14 +4360,11 @@ template <class Emitter> bool Compiler<Emitter>::visitDoStmt(const DoStmt *S) {
43874360
LabelTy EndLabel = this->getLabel();
43884361
LabelTy CondLabel = this->getLabel();
43894362
LoopScope<Emitter> LS(this, EndLabel, CondLabel);
4390-
LocalScope<Emitter> Scope(this);
43914363

43924364
this->fallthrough(StartLabel);
43934365
this->emitLabel(StartLabel);
43944366
{
4395-
DestructorScope<Emitter> DS(Scope);
4396-
4397-
if (!this->visitLoopBody(Body))
4367+
if (!this->visitStmt(Body))
43984368
return false;
43994369
this->fallthrough(CondLabel);
44004370
this->emitLabel(CondLabel);
@@ -4421,10 +4391,10 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
44214391
LabelTy CondLabel = this->getLabel();
44224392
LabelTy IncLabel = this->getLabel();
44234393
LoopScope<Emitter> LS(this, EndLabel, IncLabel);
4424-
LocalScope<Emitter> Scope(this);
44254394

44264395
if (Init && !this->visitStmt(Init))
44274396
return false;
4397+
44284398
this->fallthrough(CondLabel);
44294399
this->emitLabel(CondLabel);
44304400

@@ -4440,10 +4410,9 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
44404410
}
44414411

44424412
{
4443-
DestructorScope<Emitter> DS(Scope);
4444-
4445-
if (Body && !this->visitLoopBody(Body))
4413+
if (Body && !this->visitStmt(Body))
44464414
return false;
4415+
44474416
this->fallthrough(IncLabel);
44484417
this->emitLabel(IncLabel);
44494418
if (Inc && !this->discard(Inc))
@@ -4495,13 +4464,11 @@ bool Compiler<Emitter>::visitCXXForRangeStmt(const CXXForRangeStmt *S) {
44954464
return false;
44964465

44974466
// Body.
4498-
LocalScope<Emitter> Scope(this);
44994467
{
4500-
DestructorScope<Emitter> DS(Scope);
4501-
4502-
if (!this->visitLoopBody(Body))
4468+
if (!this->visitStmt(Body))
45034469
return false;
4504-
this->fallthrough(IncLabel);
4470+
4471+
this->fallthrough(IncLabel);
45054472
this->emitLabel(IncLabel);
45064473
if (!this->discard(Inc))
45074474
return false;
@@ -4520,7 +4487,7 @@ bool Compiler<Emitter>::visitBreakStmt(const BreakStmt *S) {
45204487
if (!BreakLabel)
45214488
return false;
45224489

4523-
this->VarScope->emitDestructors();
4490+
this->emitCleanup();
45244491
return this->jump(*BreakLabel);
45254492
}
45264493

@@ -4529,7 +4496,7 @@ bool Compiler<Emitter>::visitContinueStmt(const ContinueStmt *S) {
45294496
if (!ContinueLabel)
45304497
return false;
45314498

4532-
this->VarScope->emitDestructors();
4499+
this->emitCleanup();
45334500
return this->jump(*ContinueLabel);
45344501
}
45354502

clang/lib/AST/Interp/Compiler.h

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,6 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
202202

203203
// Statements.
204204
bool visitCompoundStmt(const CompoundStmt *S);
205-
bool visitLoopBody(const Stmt *S);
206205
bool visitDeclStmt(const DeclStmt *DS);
207206
bool visitReturnStmt(const ReturnStmt *RS);
208207
bool visitIfStmt(const IfStmt *IS);
@@ -452,11 +451,15 @@ template <class Emitter> class VariableScope {
452451
}
453452

454453
// Use the parent scope.
455-
addExtended(Local);
454+
if (this->Parent)
455+
this->Parent->addLocal(Local);
456+
else
457+
this->addLocal(Local);
456458
}
457459

458460
virtual void emitDestruction() {}
459-
virtual bool emitDestructors() { return true; }
461+
virtual bool emitDestructors(const Expr *E = nullptr) { return true; }
462+
virtual bool destroyLocals(const Expr *E = nullptr) { return true; }
460463
VariableScope *getParent() const { return Parent; }
461464

462465
protected:
@@ -483,16 +486,21 @@ template <class Emitter> class LocalScope : public VariableScope<Emitter> {
483486
}
484487

485488
/// Overriden to support explicit destruction.
486-
void emitDestruction() override { destroyLocals(); }
489+
void emitDestruction() override {
490+
if (!Idx)
491+
return;
492+
493+
this->emitDestructors();
494+
this->Ctx->emitDestroy(*Idx, SourceInfo{});
495+
}
487496

488497
/// Explicit destruction of local variables.
489-
bool destroyLocals() {
498+
bool destroyLocals(const Expr *E = nullptr) override {
490499
if (!Idx)
491500
return true;
492501

493-
bool Success = this->emitDestructors();
494-
this->Ctx->emitDestroy(*Idx, SourceInfo{});
495-
removeStoredOpaqueValues();
502+
bool Success = this->emitDestructors(E);
503+
this->Ctx->emitDestroy(*Idx, E);
496504
this->Idx = std::nullopt;
497505
return Success;
498506
}
@@ -501,25 +509,26 @@ template <class Emitter> class LocalScope : public VariableScope<Emitter> {
501509
if (!Idx) {
502510
Idx = this->Ctx->Descriptors.size();
503511
this->Ctx->Descriptors.emplace_back();
512+
this->Ctx->emitInitScope(*Idx, {});
504513
}
505514

506515
this->Ctx->Descriptors[*Idx].emplace_back(Local);
507516
}
508517

509-
bool emitDestructors() override {
518+
bool emitDestructors(const Expr *E = nullptr) override {
510519
if (!Idx)
511520
return true;
512521
// Emit destructor calls for local variables of record
513522
// type with a destructor.
514523
for (Scope::Local &Local : this->Ctx->Descriptors[*Idx]) {
515524
if (!Local.Desc->isPrimitive() && !Local.Desc->isPrimitiveArray()) {
516-
if (!this->Ctx->emitGetPtrLocal(Local.Offset, SourceInfo{}))
525+
if (!this->Ctx->emitGetPtrLocal(Local.Offset, E))
517526
return false;
518527

519528
if (!this->Ctx->emitDestruction(Local.Desc))
520529
return false;
521530

522-
if (!this->Ctx->emitPopPtr(SourceInfo{}))
531+
if (!this->Ctx->emitPopPtr(E))
523532
return false;
524533
removeIfStoredOpaqueValue(Local);
525534
}
@@ -549,19 +558,6 @@ template <class Emitter> class LocalScope : public VariableScope<Emitter> {
549558
std::optional<unsigned> Idx;
550559
};
551560

552-
/// Emits the destructors of the variables of \param OtherScope
553-
/// when this scope is destroyed. Does not create a Scope in the bytecode at
554-
/// all, this is just a RAII object to emit destructors.
555-
template <class Emitter> class DestructorScope final {
556-
public:
557-
DestructorScope(LocalScope<Emitter> &OtherScope) : OtherScope(OtherScope) {}
558-
559-
~DestructorScope() { OtherScope.emitDestructors(); }
560-
561-
private:
562-
LocalScope<Emitter> &OtherScope;
563-
};
564-
565561
/// Scope for storage declared in a compound statement.
566562
template <class Emitter> class BlockScope final : public LocalScope<Emitter> {
567563
public:

clang/lib/AST/Interp/Interp.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2012,6 +2012,11 @@ inline bool Destroy(InterpState &S, CodePtr OpPC, uint32_t I) {
20122012
return true;
20132013
}
20142014

2015+
inline bool InitScope(InterpState &S, CodePtr OpPC, uint32_t I) {
2016+
S.Current->initScope(I);
2017+
return true;
2018+
}
2019+
20152020
//===----------------------------------------------------------------------===//
20162021
// Cast, CastFP
20172022
//===----------------------------------------------------------------------===//

clang/lib/AST/Interp/InterpFrame.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ InterpFrame::InterpFrame(InterpState &S, const Function *Func,
3737
Locals = std::make_unique<char[]>(FrameSize);
3838
for (auto &Scope : Func->scopes()) {
3939
for (auto &Local : Scope.locals()) {
40-
Block *B =
41-
new (localBlock(Local.Offset)) Block(S.Ctx.getEvalID(), Local.Desc);
42-
B->invokeCtor();
40+
new (localBlock(Local.Offset)) Block(S.Ctx.getEvalID(), Local.Desc);
41+
// Note that we are NOT calling invokeCtor() here, since that is done
42+
// via the InitScope op.
4343
new (localInlineDesc(Local.Offset)) InlineDescriptor(Local.Desc);
4444
}
4545
}
@@ -81,6 +81,14 @@ InterpFrame::~InterpFrame() {
8181
}
8282
}
8383

84+
void InterpFrame::initScope(unsigned Idx) {
85+
if (!Func)
86+
return;
87+
for (auto &Local : Func->getScope(Idx).locals()) {
88+
localBlock(Local.Offset)->invokeCtor();
89+
}
90+
}
91+
8492
void InterpFrame::destroy(unsigned Idx) {
8593
for (auto &Local : Func->getScope(Idx).locals()) {
8694
S.deallocate(localBlock(Local.Offset));

clang/lib/AST/Interp/InterpFrame.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class InterpFrame final : public Frame {
4444

4545
/// Invokes the destructors for a scope.
4646
void destroy(unsigned Idx);
47+
void initScope(unsigned Idx);
4748

4849
/// Pops the arguments off the stack.
4950
void popArgs();

clang/lib/AST/Interp/Opcodes.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,9 @@ def Destroy : Opcode {
232232
let Args = [ArgUint32];
233233
let HasCustomEval = 1;
234234
}
235+
def InitScope : Opcode {
236+
let Args = [ArgUint32];
237+
}
235238

236239
//===----------------------------------------------------------------------===//
237240
// Constants

clang/test/AST/Interp/if.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,21 @@ constexpr char g(char const (&x)[2]) {
5858
;
5959
}
6060
static_assert(g("x") == 'x');
61+
62+
namespace IfScope {
63+
struct Inc {
64+
int &a;
65+
constexpr Inc(int &a) : a(a) {}
66+
constexpr ~Inc() { ++a; }
67+
};
68+
69+
constexpr int foo() {
70+
int a= 0;
71+
int b = 12;
72+
if (Inc{a}; true) {
73+
b += a;
74+
}
75+
return b;
76+
}
77+
static_assert(foo() == 13, "");
78+
}

clang/test/AST/Interp/loops.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,3 +324,31 @@ namespace RangeForLoop {
324324
// ref-note {{semicolon on a separate line}}
325325
}
326326
}
327+
328+
namespace Scopes {
329+
constexpr int foo() {
330+
int n = 0;
331+
{
332+
int m = 12;
333+
for (int i = 0;i < 10;++i) {
334+
335+
{
336+
int a = 10;
337+
{
338+
int b = 20;
339+
{
340+
int c = 30;
341+
continue;
342+
}
343+
}
344+
}
345+
}
346+
++m;
347+
n = m;
348+
}
349+
350+
++n;
351+
return n;
352+
}
353+
static_assert(foo() == 14, "");
354+
}

0 commit comments

Comments
 (0)