[clang] 819bd9e - [clang][Interp] IndirectMember initializers (#69900)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 18 07:25:09 PST 2024
Author: Timm Baeder
Date: 2024-01-18T16:25:05+01:00
New Revision: 819bd9e39b8c255600b7ec13ac195f726aa9a082
URL: https://github.com/llvm/llvm-project/commit/819bd9e39b8c255600b7ec13ac195f726aa9a082
DIFF: https://github.com/llvm/llvm-project/commit/819bd9e39b8c255600b7ec13ac195f726aa9a082.diff
LOG: [clang][Interp] IndirectMember initializers (#69900)
We need to look at the chain of declarations to initialize the right
field.
Added:
Modified:
clang/lib/AST/Interp/ByteCodeStmtGen.cpp
clang/lib/AST/Interp/Interp.h
clang/lib/AST/Interp/Opcodes.td
clang/test/AST/Interp/records.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
index b1ab5fcf9cb64c..38067be73e2543 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 auto *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..800dc6f910be6b 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -1133,3 +1133,90 @@ 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, "");
+
+ 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);
+
+ struct S2 {
+ int a;
+ struct {
+ int b;
+ struct {
+ int x, 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 && s2.a == 3 && s2.b == 4);
+
+#endif
+}
More information about the cfe-commits
mailing list