Timm =?utf-8?q?Bäder?= <tbae...@redhat.com> Message-ID: In-Reply-To: <llvm.org/llvm/llvm-project/pull/69...@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?= <tbae...@redhat.com> Date: Sun, 22 Oct 2023 19:47:33 +0200 Subject: [PATCH 1/2] [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?= <tbae...@redhat.com> Date: Tue, 16 Jan 2024 17:34:16 +0100 Subject: [PATCH 2/2] 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 } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits