Timm =?utf-8?q?Bäder?= <tbae...@redhat.com> Message-ID: <llvm.org/llvm/llvm-project/pull/149...@github.com> In-Reply-To:
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/149963 The initializer itself might need the field to be active. >From 08399fdaa0468d9e272a9fdc49871d57a9691529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Mon, 21 Jul 2025 16:56:27 +0200 Subject: [PATCH 1/2] [clang][bytecode] Unions again --- clang/lib/AST/ByteCode/Compiler.cpp | 9 ++++- clang/test/AST/ByteCode/unions.cpp | 55 +++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 952a43a0ecbcf..597ec68d854b3 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -25,11 +25,18 @@ using APSInt = llvm::APSInt; namespace clang { namespace interp { +static bool hasTrivialDefaultCtorParent(const FieldDecl *FD) { + assert(FD); + assert(FD->getParent()->isUnion()); + const auto *CXXRD = dyn_cast<CXXRecordDecl>(FD->getParent()); + return !CXXRD || CXXRD->hasTrivialDefaultConstructor(); +} + static bool refersToUnion(const Expr *E) { for (;;) { if (const auto *ME = dyn_cast<MemberExpr>(E)) { if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()); - FD && FD->getParent()->isUnion()) + FD && FD->getParent()->isUnion() && hasTrivialDefaultCtorParent(FD)) return true; E = ME->getBase(); continue; diff --git a/clang/test/AST/ByteCode/unions.cpp b/clang/test/AST/ByteCode/unions.cpp index 7cfd0d677a7b3..ae71776353cf0 100644 --- a/clang/test/AST/ByteCode/unions.cpp +++ b/clang/test/AST/ByteCode/unions.cpp @@ -861,6 +861,61 @@ namespace CopyCtorMutable { // both-note {{in call}} } + +namespace NonTrivialCtor { + struct A { int x = 1; constexpr int f() { return 1; } }; + struct B : A { int y = 1; constexpr int g() { return 2; } }; + struct C { + int x; + constexpr virtual int f() = 0; + }; + struct D : C { + int y; + constexpr virtual int f() override { return 3; } + }; + + union U { + int n; + B b; + D d; + }; + + consteval int test(int which) { + if (which == 0) {} + + U u{.n = 5}; + assert_active(u); + assert_active(u.n); + assert_inactive(u.b); + + switch (which) { + case 0: + u.b.x = 10; // both-note {{assignment to member 'b' of union with active member 'n'}} + return u.b.f(); + case 1: + u.b.y = 10; // both-note {{assignment to member 'b' of union with active member 'n'}} + return u.b.g(); + case 2: + u.d.x = 10; // both-note {{assignment to member 'd' of union with active member 'n'}} + return u.d.f(); + case 3: + u.d.y = 10; // both-note {{assignment to member 'd' of union with active member 'n'}} + return u.d.f(); + } + + return 1; + } + static_assert(test(0)); // both-error {{not an integral constant expression}} \ + // both-note {{in call}} + static_assert(test(1)); // both-error {{not an integral constant expression}} \ + // both-note {{in call}} + static_assert(test(2)); // both-error {{not an integral constant expression}} \ + // both-note {{in call}} + static_assert(test(3)); // both-error {{not an integral constant expression}} \ + // both-note {{in call}} + +} + #endif namespace AddressComparison { >From 49b7370d69ff7b9c2a5f9ffc93df58c6ea26b4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Tue, 22 Jul 2025 06:59:02 +0200 Subject: [PATCH 2/2] [clang][bytecode] Activate primitive fields before initializing them The initializer itself might need the field to be active. --- clang/lib/AST/ByteCode/Compiler.cpp | 7 +++---- clang/lib/AST/ByteCode/Interp.h | 10 ++++++++++ clang/lib/AST/ByteCode/Opcodes.td | 1 + clang/test/AST/ByteCode/unions.cpp | 15 ++++++++++++--- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 597ec68d854b3..af2f69bc8573e 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -5941,16 +5941,15 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) { return false; if (std::optional<PrimType> T = this->classify(InitExpr)) { + if (Activate && !this->emitActivateThisField(FieldOffset, InitExpr)) + return false; + if (!this->visit(InitExpr)) return false; bool BitField = F->isBitField(); - if (BitField && Activate) - return this->emitInitThisBitFieldActivate(*T, F, FieldOffset, InitExpr); if (BitField) return this->emitInitThisBitField(*T, F, FieldOffset, InitExpr); - if (Activate) - return this->emitInitThisFieldActivate(*T, FieldOffset, InitExpr); return this->emitInitThisField(*T, FieldOffset, InitExpr); } // Non-primitive case. Get a pointer to the field-to-initialize diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 7f29200f8617f..a234f8ebf0c82 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -1983,6 +1983,16 @@ static inline bool Activate(InterpState &S, CodePtr OpPC) { return true; } +static inline bool ActivateThisField(InterpState &S, CodePtr OpPC, uint32_t I) { + if (S.checkingPotentialConstantExpression()) + return false; + + const Pointer &Ptr = S.Current->getThis(); + assert(Ptr.atField(I).canBeInitialized()); + Ptr.atField(I).activate(); + return true; +} + template <PrimType Name, class T = typename PrimConv<Name>::T> bool StoreActivate(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop<T>(); diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 80703ad72d954..abfed77750f87 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -510,6 +510,7 @@ def StoreBitFieldActivate : StoreBitFieldOpcode {} def StoreBitFieldActivatePop : StoreBitFieldOpcode {} def Activate : Opcode {} +def ActivateThisField : Opcode { let Args = [ArgUint32]; } // [Pointer, Value] -> [] def Init : StoreOpcode {} diff --git a/clang/test/AST/ByteCode/unions.cpp b/clang/test/AST/ByteCode/unions.cpp index ae71776353cf0..8fce2e6a738c7 100644 --- a/clang/test/AST/ByteCode/unions.cpp +++ b/clang/test/AST/ByteCode/unions.cpp @@ -79,10 +79,9 @@ namespace DefaultInit { constexpr U1 u1; /// OK. - constexpr int foo() { // expected-error {{never produces a constant expression}} + constexpr int foo() { U1 u; - return u.a; // both-note {{read of member 'a' of union with active member 'b'}} \ - // expected-note {{read of member 'a' of union with active member 'b'}} + return u.a; // both-note {{read of member 'a' of union with active member 'b'}} } static_assert(foo() == 42); // both-error {{not an integral constant expression}} \ // both-note {{in call to}} @@ -916,6 +915,16 @@ namespace NonTrivialCtor { } +namespace PrimitiveFieldInitActivates { + /// The initializer of a needs the field to be active _before_ it's visited. + template<int> struct X {}; + union V { + int a, b; + constexpr V(X<0>) : a(a = 1) {} // ok + }; + constinit V v0 = X<0>(); +} + #endif namespace AddressComparison { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits