[clang] [clang][Interp] IndirectMember initializers (PR #69900)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Tue Jan 16 20:55:03 PST 2024
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/69900 at github.com>
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/69900
>From 58ff884a20816526ea1b3e4035a65ab435e2396d 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 1/5] [clang][Interp] IndirectMember initializers
---
clang/lib/AST/Interp/ByteCodeStmtGen.cpp | 67 ++++++++++++++++--------
clang/lib/AST/Interp/Interp.h | 7 ++-
clang/lib/AST/Interp/Opcodes.td | 6 ++-
clang/test/AST/Interp/records.cpp | 60 +++++++++++++++++++++
4 files changed, 114 insertions(+), 26 deletions(-)
diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
index b1ab5fcf9cb64c..7dca7c902b1c79 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,27 @@ 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()) {
+ // FIXME: Can this *not* be a FieldDecl?
+ 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 dbbc4c09ce42a1..5321f9617feff2 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1078,15 +1078,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 e01b6b9eea7dbb..6e0f1c939460d0 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -417,7 +417,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 a1ced049dcedb8..dc1739c1b4ec5a 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -1133,3 +1133,63 @@ namespace AccessOnNullptr {
// ref-error {{not an integral constant expression}} \
// ref-note {{in call to 'a2()'}}
}
+
+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
+}
>From 92e585735350e1d0512ad3d26d5b09af30aae4cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 16 Jan 2024 17:34:16 +0100
Subject: [PATCH 2/5] add another test case
---
clang/test/AST/Interp/records.cpp | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index dc1739c1b4ec5a..fcf340af42c19d 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -1191,5 +1191,17 @@ namespace IndirectFieldInit {
};
constexpr Nested4 N4{};
static_assert(N4.first == 123, "");
+
+ struct S {
+ struct {
+ int x, y;
+ };
+
+ constexpr S(int x_, int y_) : x(x_), y(y_) {}
+ };
+
+ constexpr S s(1, 2);
+ static_assert(s.x == 1 && s.y == 2);
+
#endif
}
>From c26fae9f4a8c1f97083947c3c0ebe5c8d8049444 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 16 Jan 2024 17:35:01 +0100
Subject: [PATCH 3/5] Remove fixme comment
---
clang/lib/AST/Interp/ByteCodeStmtGen.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
index 7dca7c902b1c79..96308f7b5f0c1b 100644
--- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -204,7 +204,6 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
unsigned NestedFieldOffset = 0;
const Record::Field *NestedField = nullptr;
for (const NamedDecl *ND : IFD->chain()) {
- // FIXME: Can this *not* be a FieldDecl?
const FieldDecl *FD = cast<FieldDecl>(ND);
const Record *FieldRecord =
this->P.getOrCreateRecord(FD->getParent());
>From 3051c09eb2f8518ba3e99a8cb0a76c4eef2c2c29 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 16 Jan 2024 17:50:29 +0100
Subject: [PATCH 4/5] Address review comments
---
clang/lib/AST/Interp/ByteCodeStmtGen.cpp | 2 +-
clang/test/AST/Interp/records.cpp | 13 +++++++++++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
index 96308f7b5f0c1b..38067be73e2543 100644
--- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -204,7 +204,7 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
unsigned NestedFieldOffset = 0;
const Record::Field *NestedField = nullptr;
for (const NamedDecl *ND : IFD->chain()) {
- const FieldDecl *FD = cast<FieldDecl>(ND);
+ const auto *FD = cast<FieldDecl>(ND);
const Record *FieldRecord =
this->P.getOrCreateRecord(FD->getParent());
assert(FieldRecord);
diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index fcf340af42c19d..5f90b3ea39995e 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -1203,5 +1203,18 @@ namespace IndirectFieldInit {
constexpr S s(1, 2);
static_assert(s.x == 1 && s.y == 2);
+ struct S2 {
+ struct {
+ struct {
+ int x, y;
+ };
+ };
+
+ constexpr S2(int x_, int y_) : x(x_), y(y_) {}
+ };
+
+ constexpr S2 s2(1, 2);
+ static_assert(s2.x == 1 && s2.y == 2);
+
#endif
}
>From 25dda3a8ad9f5e99c6663999b08f4e63645819b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Wed, 17 Jan 2024 05:54:41 +0100
Subject: [PATCH 5/5] Expand on last test case
---
clang/test/AST/Interp/records.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index 5f90b3ea39995e..800dc6f910be6b 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -1204,17 +1204,19 @@ namespace IndirectFieldInit {
static_assert(s.x == 1 && s.y == 2);
struct S2 {
+ int a;
struct {
+ int b;
struct {
int x, y;
};
};
- constexpr S2(int x_, int y_) : x(x_), y(y_) {}
+ constexpr S2(int x_, int y_) : a(3), b(4), x(x_), y(y_) {}
};
constexpr S2 s2(1, 2);
- static_assert(s2.x == 1 && s2.y == 2);
+ static_assert(s2.x == 1 && s2.y == 2 && s2.a == 3 && s2.b == 4);
#endif
}
More information about the cfe-commits
mailing list