llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Timm Baeder (tbaederr) <details> <summary>Changes</summary> We need to look at the chain of declarations to initialize the right field. --- Full diff: https://github.com/llvm/llvm-project/pull/69900.diff 4 Files Affected: - (modified) clang/lib/AST/Interp/ByteCodeStmtGen.cpp (+44-23) - (modified) clang/lib/AST/Interp/Interp.h (+5-2) - (modified) clang/lib/AST/Interp/Opcodes.td (+5-1) - (modified) clang/test/AST/Interp/records.cpp (+60) ``````````diff diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp index 509abe3ae867f93..c22bbd8623e5326 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 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 +} `````````` </details> https://github.com/llvm/llvm-project/pull/69900 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits