[clang] 67f5312 - [clang][Interp] Nested ThisExprs that don't refer to the frame this ptr
Timm Bäder via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 20 07:35:08 PDT 2024
Author: Timm Bäder
Date: 2024-06-20T16:34:34+02:00
New Revision: 67f5312c41a072aaa725b5943cce2aa0f1643781
URL: https://github.com/llvm/llvm-project/commit/67f5312c41a072aaa725b5943cce2aa0f1643781
DIFF: https://github.com/llvm/llvm-project/commit/67f5312c41a072aaa725b5943cce2aa0f1643781.diff
LOG: [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.
Added:
Modified:
clang/lib/AST/Interp/ByteCodeExprGen.cpp
clang/lib/AST/Interp/ByteCodeExprGen.h
clang/lib/AST/Interp/ByteCodeStmtGen.cpp
clang/lib/AST/Interp/Interp.h
clang/test/AST/Interp/records.cpp
clang/test/SemaCXX/uninitialized.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index d26b2ab3f8243..b18873287ffcc 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -34,11 +34,13 @@ template <class Emitter> class DeclScope final : public VariableScope<Emitter> {
OldInitializingDecl(Ctx->InitializingDecl) {
Ctx->GlobalDecl = Context::shouldBeGloballyIndexed(VD);
Ctx->InitializingDecl = VD;
+ Ctx->InitStack.push_back(InitLink::Decl(VD));
}
~DeclScope() {
this->Ctx->GlobalDecl = OldGlobalDecl;
this->Ctx->InitializingDecl = OldInitializingDecl;
+ this->Ctx->InitStack.pop_back();
}
private:
@@ -72,6 +74,20 @@ template <class Emitter> class OptionScope final {
bool OldInitializing;
};
+template <class Emitter>
+bool InitLink::emit(ByteCodeExprGen<Emitter> *Ctx, const Expr *E) const {
+ switch (Kind) {
+ case K_This:
+ return Ctx->emitThis(E);
+ case K_Field:
+ // We're assuming there's a base pointer on the stack already.
+ return Ctx->emitGetPtrFieldPop(Offset, E);
+ case K_Decl:
+ return Ctx->visitDeclRef(D, E);
+ }
+ return true;
+}
+
} // namespace interp
} // namespace clang
@@ -3732,7 +3748,12 @@ template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitCXXDefaultInitExpr(
const CXXDefaultInitExpr *E) {
SourceLocScope<Emitter> SLS(this, E);
- return this->delegate(E->getExpr());
+
+ bool Old = InitStackActive;
+ InitStackActive = !isa<FunctionDecl>(E->getUsedContext());
+ bool Result = this->delegate(E->getExpr());
+ InitStackActive = Old;
+ return Result;
}
template <class Emitter>
@@ -3788,6 +3809,17 @@ bool ByteCodeExprGen<Emitter>::VisitCXXThisExpr(const CXXThisExpr *E) {
return this->emitGetPtrThisField(this->LambdaThisCapture.Offset, E);
}
+ // In some circumstances, the 'this' pointer does not actually refer to the
+ // instance pointer of the current function frame, but e.g. to the declaration
+ // currently being initialized. Here we emit the necessary instruction(s) for
+ // this scenario.
+ if (InitStackActive && !InitStack.empty()) {
+ for (const InitLink &IL : InitStack) {
+ if (!IL.emit<Emitter>(this, E))
+ return false;
+ }
+ return true;
+ }
return this->emitThis(E);
}
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index 1a62d236b74b9..eef8cae6e38cd 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -32,10 +32,44 @@ template <class Emitter> class LocalScope;
template <class Emitter> class DestructorScope;
template <class Emitter> class VariableScope;
template <class Emitter> class DeclScope;
+template <class Emitter> class InitLinkScope;
template <class Emitter> class OptionScope;
template <class Emitter> class ArrayIndexScope;
template <class Emitter> class SourceLocScope;
+template <class Emitter> class ByteCodeExprGen;
+struct InitLink {
+public:
+ enum {
+ K_This = 0,
+ K_Field = 1,
+ K_Decl = 2,
+ };
+
+ static InitLink This() { return InitLink{K_This}; }
+ static InitLink Field(unsigned Offset) {
+ InitLink IL{K_Field};
+ IL.Offset = Offset;
+ return IL;
+ }
+ static InitLink Decl(const ValueDecl *D) {
+ InitLink IL{K_Decl};
+ IL.D = D;
+ return IL;
+ }
+
+ InitLink(uint8_t Kind) : Kind(Kind) {}
+ template <class Emitter>
+ bool emit(ByteCodeExprGen<Emitter> *Ctx, const Expr *E) const;
+
+private:
+ uint32_t Kind;
+ union {
+ unsigned Offset;
+ const ValueDecl *D;
+ };
+};
+
/// Compilation context for expressions.
template <class Emitter>
class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
@@ -254,9 +288,11 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
friend class LocalScope<Emitter>;
friend class DestructorScope<Emitter>;
friend class DeclScope<Emitter>;
+ friend class InitLinkScope<Emitter>;
friend class OptionScope<Emitter>;
friend class ArrayIndexScope<Emitter>;
friend class SourceLocScope<Emitter>;
+ friend struct InitLink;
/// Emits a zero initializer.
bool visitZeroInitializer(PrimType T, QualType QT, const Expr *E);
@@ -325,6 +361,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
bool Initializing = false;
const ValueDecl *InitializingDecl = nullptr;
+ llvm::SmallVector<InitLink> InitStack;
+ bool InitStackActive = false;
+
/// Flag indicating if we're initializing a global variable.
bool GlobalDecl = false;
};
@@ -548,6 +587,18 @@ template <class Emitter> class SourceLocScope final {
bool Enabled = false;
};
+template <class Emitter> class InitLinkScope final {
+public:
+ InitLinkScope(ByteCodeExprGen<Emitter> *Ctx, InitLink &&Link) : Ctx(Ctx) {
+ Ctx->InitStack.push_back(std::move(Link));
+ }
+
+ ~InitLinkScope() { this->Ctx->InitStack.pop_back(); }
+
+private:
+ ByteCodeExprGen<Emitter> *Ctx;
+};
+
} // namespace interp
} // namespace clang
diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
index 6ee7898f228de..0618ec1aa8f58 100644
--- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -155,8 +155,10 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
return this->emitInitThisBitField(*T, F, FieldOffset, InitExpr);
return this->emitInitThisField(*T, FieldOffset, InitExpr);
}
+
// Non-primitive case. Get a pointer to the field-to-initialize
// on the stack and call visitInitialzer() for it.
+ InitLinkScope<Emitter> FieldScope(this, InitLink::Field(F->Offset));
if (!this->emitGetPtrThisField(FieldOffset, InitExpr))
return false;
@@ -178,6 +180,7 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
if (!R)
return false;
+ InitLinkScope<Emitter> InitScope(this, InitLink::This());
for (const auto *Init : Ctor->inits()) {
// Scope needed for the initializers.
BlockScope<Emitter> Scope(this);
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index fea83de829261..916d268aa4f09 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1432,7 +1432,6 @@ inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
if (Ptr.isBlockPointer() && Off > Ptr.block()->getSize())
return false;
-
S.Stk.push<Pointer>(Ptr.atField(Off));
return true;
}
diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index 8a18f7a2a4890..9f341f5bc6d1d 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -468,19 +468,12 @@ namespace ConditionalInit {
static_assert(getS(true).a == 12, "");
static_assert(getS(false).a == 13, "");
};
-/// FIXME: The following tests are broken.
-/// They are using CXXDefaultInitExprs which contain a CXXThisExpr. The This pointer
-/// in those refers to the declaration we are currently initializing, *not* the
-/// This pointer of the current stack frame. This is something we haven't
-/// implemented in the new interpreter yet.
namespace DeclRefs {
- struct A{ int m; const int &f = m; }; // expected-note {{implicit use of 'this'}}
+ struct A{ int m; const int &f = m; };
- constexpr A a{10}; // expected-error {{must be initialized by a constant expression}} \
- // expected-note {{declared here}}
+ constexpr A a{10};
static_assert(a.m == 10, "");
- static_assert(a.f == 10, ""); // expected-error {{not an integral constant expression}} \
- // expected-note {{initializer of 'a' is not a constant expression}}
+ static_assert(a.f == 10, "");
class Foo {
public:
@@ -499,12 +492,8 @@ namespace DeclRefs {
A a = A{100};
};
constexpr B b;
- /// FIXME: The following two lines don't work because we don't get the
- /// pointers on the LHS correct. They make us run into an assertion
- /// in CheckEvaluationResult. However, this may just be caused by the
- /// problems in the previous examples.
- //static_assert(b.a.m == 100, "");
- //static_assert(b.a.f == 100, "");
+ static_assert(b.a.m == 100, "");
+ static_assert(b.a.f == 100, "");
}
namespace PointerArith {
diff --git a/clang/test/SemaCXX/uninitialized.cpp b/clang/test/SemaCXX/uninitialized.cpp
index c83c2e795824a..8a640c9691b32 100644
--- a/clang/test/SemaCXX/uninitialized.cpp
+++ b/clang/test/SemaCXX/uninitialized.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -Wall -Wuninitialized -Wno-unused-value -Wno-unused-lambda-capture -Wno-uninitialized-const-reference -std=c++1z -verify %s
+// 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
// definitions for std::move
namespace std {
More information about the cfe-commits
mailing list