Skip to content

Commit 67f5312

Browse files
committed
[clang][Interp] Nested ThisExprs that don't refer to the frame this ptr
Use a series of ops in that case, getting us to the right declaration field.
1 parent 2ae6889 commit 67f5312

File tree

6 files changed

+93
-18
lines changed

6 files changed

+93
-18
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@ template <class Emitter> class DeclScope final : public VariableScope<Emitter> {
3434
OldInitializingDecl(Ctx->InitializingDecl) {
3535
Ctx->GlobalDecl = Context::shouldBeGloballyIndexed(VD);
3636
Ctx->InitializingDecl = VD;
37+
Ctx->InitStack.push_back(InitLink::Decl(VD));
3738
}
3839

3940
~DeclScope() {
4041
this->Ctx->GlobalDecl = OldGlobalDecl;
4142
this->Ctx->InitializingDecl = OldInitializingDecl;
43+
this->Ctx->InitStack.pop_back();
4244
}
4345

4446
private:
@@ -72,6 +74,20 @@ template <class Emitter> class OptionScope final {
7274
bool OldInitializing;
7375
};
7476

77+
template <class Emitter>
78+
bool InitLink::emit(ByteCodeExprGen<Emitter> *Ctx, const Expr *E) const {
79+
switch (Kind) {
80+
case K_This:
81+
return Ctx->emitThis(E);
82+
case K_Field:
83+
// We're assuming there's a base pointer on the stack already.
84+
return Ctx->emitGetPtrFieldPop(Offset, E);
85+
case K_Decl:
86+
return Ctx->visitDeclRef(D, E);
87+
}
88+
return true;
89+
}
90+
7591
} // namespace interp
7692
} // namespace clang
7793

@@ -3732,7 +3748,12 @@ template <class Emitter>
37323748
bool ByteCodeExprGen<Emitter>::VisitCXXDefaultInitExpr(
37333749
const CXXDefaultInitExpr *E) {
37343750
SourceLocScope<Emitter> SLS(this, E);
3735-
return this->delegate(E->getExpr());
3751+
3752+
bool Old = InitStackActive;
3753+
InitStackActive = !isa<FunctionDecl>(E->getUsedContext());
3754+
bool Result = this->delegate(E->getExpr());
3755+
InitStackActive = Old;
3756+
return Result;
37363757
}
37373758

37383759
template <class Emitter>
@@ -3788,6 +3809,17 @@ bool ByteCodeExprGen<Emitter>::VisitCXXThisExpr(const CXXThisExpr *E) {
37883809
return this->emitGetPtrThisField(this->LambdaThisCapture.Offset, E);
37893810
}
37903811

3812+
// In some circumstances, the 'this' pointer does not actually refer to the
3813+
// instance pointer of the current function frame, but e.g. to the declaration
3814+
// currently being initialized. Here we emit the necessary instruction(s) for
3815+
// this scenario.
3816+
if (InitStackActive && !InitStack.empty()) {
3817+
for (const InitLink &IL : InitStack) {
3818+
if (!IL.emit<Emitter>(this, E))
3819+
return false;
3820+
}
3821+
return true;
3822+
}
37913823
return this->emitThis(E);
37923824
}
37933825

clang/lib/AST/Interp/ByteCodeExprGen.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,44 @@ template <class Emitter> class LocalScope;
3232
template <class Emitter> class DestructorScope;
3333
template <class Emitter> class VariableScope;
3434
template <class Emitter> class DeclScope;
35+
template <class Emitter> class InitLinkScope;
3536
template <class Emitter> class OptionScope;
3637
template <class Emitter> class ArrayIndexScope;
3738
template <class Emitter> class SourceLocScope;
3839

40+
template <class Emitter> class ByteCodeExprGen;
41+
struct InitLink {
42+
public:
43+
enum {
44+
K_This = 0,
45+
K_Field = 1,
46+
K_Decl = 2,
47+
};
48+
49+
static InitLink This() { return InitLink{K_This}; }
50+
static InitLink Field(unsigned Offset) {
51+
InitLink IL{K_Field};
52+
IL.Offset = Offset;
53+
return IL;
54+
}
55+
static InitLink Decl(const ValueDecl *D) {
56+
InitLink IL{K_Decl};
57+
IL.D = D;
58+
return IL;
59+
}
60+
61+
InitLink(uint8_t Kind) : Kind(Kind) {}
62+
template <class Emitter>
63+
bool emit(ByteCodeExprGen<Emitter> *Ctx, const Expr *E) const;
64+
65+
private:
66+
uint32_t Kind;
67+
union {
68+
unsigned Offset;
69+
const ValueDecl *D;
70+
};
71+
};
72+
3973
/// Compilation context for expressions.
4074
template <class Emitter>
4175
class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
@@ -254,9 +288,11 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
254288
friend class LocalScope<Emitter>;
255289
friend class DestructorScope<Emitter>;
256290
friend class DeclScope<Emitter>;
291+
friend class InitLinkScope<Emitter>;
257292
friend class OptionScope<Emitter>;
258293
friend class ArrayIndexScope<Emitter>;
259294
friend class SourceLocScope<Emitter>;
295+
friend struct InitLink;
260296

261297
/// Emits a zero initializer.
262298
bool visitZeroInitializer(PrimType T, QualType QT, const Expr *E);
@@ -325,6 +361,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
325361
bool Initializing = false;
326362
const ValueDecl *InitializingDecl = nullptr;
327363

364+
llvm::SmallVector<InitLink> InitStack;
365+
bool InitStackActive = false;
366+
328367
/// Flag indicating if we're initializing a global variable.
329368
bool GlobalDecl = false;
330369
};
@@ -548,6 +587,18 @@ template <class Emitter> class SourceLocScope final {
548587
bool Enabled = false;
549588
};
550589

590+
template <class Emitter> class InitLinkScope final {
591+
public:
592+
InitLinkScope(ByteCodeExprGen<Emitter> *Ctx, InitLink &&Link) : Ctx(Ctx) {
593+
Ctx->InitStack.push_back(std::move(Link));
594+
}
595+
596+
~InitLinkScope() { this->Ctx->InitStack.pop_back(); }
597+
598+
private:
599+
ByteCodeExprGen<Emitter> *Ctx;
600+
};
601+
551602
} // namespace interp
552603
} // namespace clang
553604

clang/lib/AST/Interp/ByteCodeStmtGen.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,10 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
155155
return this->emitInitThisBitField(*T, F, FieldOffset, InitExpr);
156156
return this->emitInitThisField(*T, FieldOffset, InitExpr);
157157
}
158+
158159
// Non-primitive case. Get a pointer to the field-to-initialize
159160
// on the stack and call visitInitialzer() for it.
161+
InitLinkScope<Emitter> FieldScope(this, InitLink::Field(F->Offset));
160162
if (!this->emitGetPtrThisField(FieldOffset, InitExpr))
161163
return false;
162164

@@ -178,6 +180,7 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
178180
if (!R)
179181
return false;
180182

183+
InitLinkScope<Emitter> InitScope(this, InitLink::This());
181184
for (const auto *Init : Ctor->inits()) {
182185
// Scope needed for the initializers.
183186
BlockScope<Emitter> Scope(this);

clang/lib/AST/Interp/Interp.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1432,7 +1432,6 @@ inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
14321432

14331433
if (Ptr.isBlockPointer() && Off > Ptr.block()->getSize())
14341434
return false;
1435-
14361435
S.Stk.push<Pointer>(Ptr.atField(Off));
14371436
return true;
14381437
}

clang/test/AST/Interp/records.cpp

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -468,19 +468,12 @@ namespace ConditionalInit {
468468
static_assert(getS(true).a == 12, "");
469469
static_assert(getS(false).a == 13, "");
470470
};
471-
/// FIXME: The following tests are broken.
472-
/// They are using CXXDefaultInitExprs which contain a CXXThisExpr. The This pointer
473-
/// in those refers to the declaration we are currently initializing, *not* the
474-
/// This pointer of the current stack frame. This is something we haven't
475-
/// implemented in the new interpreter yet.
476471
namespace DeclRefs {
477-
struct A{ int m; const int &f = m; }; // expected-note {{implicit use of 'this'}}
472+
struct A{ int m; const int &f = m; };
478473

479-
constexpr A a{10}; // expected-error {{must be initialized by a constant expression}} \
480-
// expected-note {{declared here}}
474+
constexpr A a{10};
481475
static_assert(a.m == 10, "");
482-
static_assert(a.f == 10, ""); // expected-error {{not an integral constant expression}} \
483-
// expected-note {{initializer of 'a' is not a constant expression}}
476+
static_assert(a.f == 10, "");
484477

485478
class Foo {
486479
public:
@@ -499,12 +492,8 @@ namespace DeclRefs {
499492
A a = A{100};
500493
};
501494
constexpr B b;
502-
/// FIXME: The following two lines don't work because we don't get the
503-
/// pointers on the LHS correct. They make us run into an assertion
504-
/// in CheckEvaluationResult. However, this may just be caused by the
505-
/// problems in the previous examples.
506-
//static_assert(b.a.m == 100, "");
507-
//static_assert(b.a.f == 100, "");
495+
static_assert(b.a.m == 100, "");
496+
static_assert(b.a.f == 100, "");
508497
}
509498

510499
namespace PointerArith {

clang/test/SemaCXX/uninitialized.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -fsyntax-only -Wall -Wuninitialized -Wno-unused-value -Wno-unused-lambda-capture -Wno-uninitialized-const-reference -std=c++1z -verify %s
2+
// RUN: %clang_cc1 -fsyntax-only -Wall -Wuninitialized -Wno-unused-value -Wno-unused-lambda-capture -Wno-uninitialized-const-reference -std=c++1z -verify %s -fexperimental-new-constant-interpreter
23

34
// definitions for std::move
45
namespace std {

0 commit comments

Comments
 (0)