[clang] [clang][Interp] IndirectMember initializers (PR #69900)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 23 00:30:35 PDT 2023
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/69900
We need to look at the chain of declarations to initialize the right field.
>From 5b48d59166b013f375682eced512a4da937c458f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sun, 22 Oct 2023 19:47:33 +0200
Subject: [PATCH] [clang][Interp] IndirectMember initializers
---
clang/lib/AST/Interp/ByteCodeStmtGen.cpp | 66 +++++++++++++++---------
clang/lib/AST/Interp/Interp.h | 7 ++-
clang/lib/AST/Interp/Opcodes.td | 6 ++-
clang/test/AST/Interp/records.cpp | 60 +++++++++++++++++++++
4 files changed, 113 insertions(+), 26 deletions(-)
diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
index 509abe3ae867f93..1b9b3069a8f4347 100644
--- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -142,6 +142,27 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
// Classify the return type.
ReturnType = this->classify(F->getReturnType());
+ auto emitFieldInitializer = [&](const Record::Field *F, unsigned FieldOffset,
+ const Expr *InitExpr) -> bool {
+ if (std::optional<PrimType> T = this->classify(InitExpr)) {
+ if (!this->visit(InitExpr))
+ return false;
+
+ if (F->isBitField())
+ 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.
+ if (!this->emitGetPtrThisField(FieldOffset, InitExpr))
+ return false;
+
+ if (!this->visitInitializer(InitExpr))
+ return false;
+
+ return this->emitPopPtr(InitExpr);
+ };
+
// Emit custom code if this is a lambda static invoker.
if (const auto *MD = dyn_cast<CXXMethodDecl>(F);
MD && MD->isLambdaStaticInvoker())
@@ -162,29 +183,8 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
if (const FieldDecl *Member = Init->getMember()) {
const Record::Field *F = R->getField(Member);
- if (std::optional<PrimType> T = this->classify(InitExpr)) {
- if (!this->visit(InitExpr))
- return false;
-
- if (F->isBitField()) {
- if (!this->emitInitThisBitField(*T, F, InitExpr))
- return false;
- } else {
- if (!this->emitInitThisField(*T, F->Offset, InitExpr))
- return false;
- }
- } else {
- // Non-primitive case. Get a pointer to the field-to-initialize
- // on the stack and call visitInitialzer() for it.
- if (!this->emitGetPtrThisField(F->Offset, InitExpr))
- return false;
-
- if (!this->visitInitializer(InitExpr))
- return false;
-
- if (!this->emitPopPtr(InitExpr))
- return false;
- }
+ if (!emitFieldInitializer(F, F->Offset, InitExpr))
+ return false;
} else if (const Type *Base = Init->getBaseClass()) {
// Base class initializer.
// Get This Base and call initializer on it.
@@ -198,6 +198,26 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
return false;
if (!this->emitInitPtrPop(InitExpr))
return false;
+ } else if (const IndirectFieldDecl *IFD = Init->getIndirectMember()) {
+ assert(IFD->getChainingSize() >= 2);
+
+ unsigned NestedFieldOffset = 0;
+ const Record::Field *NestedField = nullptr;
+ for (const NamedDecl *ND : IFD->chain()) {
+ const FieldDecl *FD = cast<FieldDecl>(ND);
+ const Record *FieldRecord =
+ this->P.getOrCreateRecord(FD->getParent());
+ assert(FieldRecord);
+
+ NestedField = FieldRecord->getField(FD);
+ assert(NestedField);
+
+ NestedFieldOffset += NestedField->Offset;
+ }
+ assert(NestedField);
+
+ if (!emitFieldInitializer(NestedField, NestedFieldOffset, InitExpr))
+ return false;
} else {
assert(Init->isDelegatingInitializer());
if (!this->emitThis(InitExpr))
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 7ef1e344224a3c3..b2777c8a007ea7f 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1069,15 +1069,18 @@ bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
return true;
}
+// FIXME: The Field pointer here is too much IMO and we could instead just
+// pass an Offset + BitWidth pair.
template <PrimType Name, class T = typename PrimConv<Name>::T>
-bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
+bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F,
+ uint32_t FieldOffset) {
assert(F->isBitField());
if (S.checkingPotentialConstantExpression())
return false;
const Pointer &This = S.Current->getThis();
if (!CheckThis(S, OpPC, This))
return false;
- const Pointer &Field = This.atField(F->Offset);
+ const Pointer &Field = This.atField(FieldOffset);
const auto &Value = S.Stk.pop<T>();
Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx()));
Field.initialize();
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index e1e7e5e2efbb059..709de4755af82e1 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -416,7 +416,11 @@ def InitThisField : AccessOpcode;
// [Value] -> []
def InitThisFieldActive : AccessOpcode;
// [Value] -> []
-def InitThisBitField : BitFieldOpcode;
+def InitThisBitField : Opcode {
+ let Types = [AluTypeClass];
+ let Args = [ArgRecordField, ArgUint32];
+ let HasGroup = 1;
+}
// [Pointer, Value] -> []
def InitField : AccessOpcode;
// [Pointer, Value] -> []
diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index a2e878f6132d0ac..f30722a2ef146ff 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -1105,3 +1105,63 @@ namespace DelegatingConstructors {
static_assert(d4.a == 10, "");
static_assert(d4.b == 12, "");
}
+
+namespace IndirectFieldInit {
+#if __cplusplus >= 202002L
+ /// Primitive.
+ struct Nested1 {
+ struct {
+ int first;
+ };
+ int x;
+ constexpr Nested1(int x) : first(12), x() { x = 4; }
+ constexpr Nested1() : Nested1(42) {}
+ };
+ constexpr Nested1 N1{};
+ static_assert(N1.first == 12, "");
+
+ /// Composite.
+ struct Nested2 {
+ struct First { int x = 42; };
+ struct {
+ First first;
+ };
+ int x;
+ constexpr Nested2(int x) : first(12), x() { x = 4; }
+ constexpr Nested2() : Nested2(42) {}
+ };
+ constexpr Nested2 N2{};
+ static_assert(N2.first.x == 12, "");
+
+ /// Bitfield.
+ struct Nested3 {
+ struct {
+ unsigned first : 2;
+ };
+ int x;
+ constexpr Nested3(int x) : first(3), x() { x = 4; }
+ constexpr Nested3() : Nested3(42) {}
+ };
+
+ constexpr Nested3 N3{};
+ static_assert(N3.first == 3, "");
+
+ /// Test that we get the offset right if the
+ /// record has a base.
+ struct Nested4Base {
+ int a;
+ int b;
+ char c;
+ };
+ struct Nested4 : Nested4Base{
+ struct {
+ int first;
+ };
+ int x;
+ constexpr Nested4(int x) : first(123), x() { a = 1; b = 2; c = 3; x = 4; }
+ constexpr Nested4() : Nested4(42) {}
+ };
+ constexpr Nested4 N4{};
+ static_assert(N4.first == 123, "");
+#endif
+}
More information about the cfe-commits
mailing list