https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/174745
When activating a union member, none of the unions in that path can have a non-trivial constructor. Unfortunately, this is something we have to do when evaluating the bytecode, not while compiling it. >From 400e7d19d12860624c4a542463ac3c99a934b8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Tue, 6 Jan 2026 16:00:00 +0100 Subject: [PATCH] [clang][bytecode] Check for non-trivial default ctors in unions When activating a union member, none of the unions in that path can have a non-trivial constructor. Unfortunately, this is something we have to do when evaluating the bytecode, not while compiling it. --- clang/lib/AST/ByteCode/Compiler.cpp | 3 ++- clang/lib/AST/ByteCode/Interp.cpp | 25 +++++++++++++++++++++++-- clang/lib/AST/ByteCode/Interp.h | 10 +++++----- clang/test/AST/ByteCode/unions.cpp | 21 +++++++++++++++++++++ 4 files changed, 51 insertions(+), 8 deletions(-) diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index b4449def1c6f0..93e80de37ab68 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -5570,7 +5570,8 @@ bool Compiler<Emitter>::maybeEmitDeferredVarInit(const VarDecl *VD) { static bool hasTrivialDefaultCtorParent(const FieldDecl *FD) { assert(FD); assert(FD->getParent()->isUnion()); - const auto *CXXRD = dyn_cast<CXXRecordDecl>(FD->getParent()); + const CXXRecordDecl *CXXRD = + FD->getType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); return !CXXRD || CXXRD->hasTrivialDefaultConstructor(); } diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 889ac1e1a9a7e..888543bb9c021 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -326,12 +326,13 @@ bool CheckBCPResult(InterpState &S, const Pointer &Ptr) { } bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - AccessKinds AK) { + AccessKinds AK, bool WillActivate) { if (Ptr.isActive()) return true; assert(Ptr.inUnion()); + // Find the outermost union. Pointer U = Ptr.getBase(); Pointer C = Ptr; while (!U.isRoot() && !U.isActive()) { @@ -346,6 +347,7 @@ bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, U = U.getBase(); } assert(C.isField()); + assert(C.getBase() == U); // Consider: // union U { @@ -362,6 +364,25 @@ bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, if (!U.getFieldDesc()->isUnion()) return true; + // When we will activate Ptr, check that none of the unions in its path have a + // non-trivial default constructor. + if (WillActivate) { + bool Fails = false; + Pointer It = Ptr; + while (!It.isRoot() && !It.isActive()) { + if (const Record *R = It.getRecord(); R && R->isUnion()) { + if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(R->getDecl()); + CXXRD && !CXXRD->hasTrivialDefaultConstructor()) { + Fails = true; + break; + } + } + It = It.getBase(); + } + if (!Fails) + return true; + } + // Get the inactive field descriptor. assert(!C.isActive()); const FieldDecl *InactiveField = C.getField(); @@ -885,7 +906,7 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr, return false; if (!CheckRange(S, OpPC, Ptr, AK_Assign)) return false; - if (!WillBeActivated && !CheckActive(S, OpPC, Ptr, AK_Assign)) + if (!CheckActive(S, OpPC, Ptr, AK_Assign, WillBeActivated)) return false; if (!CheckGlobal(S, OpPC, Ptr)) return false; diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 155d96fc1652b..7ccd690d22fb7 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -94,7 +94,7 @@ bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source, const Pointer &Ptr); bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - AccessKinds AK); + AccessKinds AK, bool WillActivate = false); /// Sets the given integral value to the pointer, which is of /// a std::{weak,partial,strong}_ordering type. @@ -2017,7 +2017,7 @@ bool StoreActivate(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop<T>(); const Pointer &Ptr = S.Stk.peek<Pointer>(); - if (!CheckStore(S, OpPC, Ptr, /*WilLBeActivated=*/true)) + if (!CheckStore(S, OpPC, Ptr, /*WillBeActivated=*/true)) return false; if (Ptr.canBeInitialized()) { Ptr.initialize(); @@ -2032,7 +2032,7 @@ bool StoreActivatePop(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop<T>(); const Pointer &Ptr = S.Stk.pop<Pointer>(); - if (!CheckStore(S, OpPC, Ptr, /*WilLBeActivated=*/true)) + if (!CheckStore(S, OpPC, Ptr, /*WillBeActivated=*/true)) return false; if (Ptr.canBeInitialized()) { Ptr.initialize(); @@ -2047,7 +2047,7 @@ bool StoreBitField(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop<T>(); const Pointer &Ptr = S.Stk.peek<Pointer>(); - if (!CheckStore(S, OpPC, Ptr, /*WilLBeActivated=*/true)) + if (!CheckStore(S, OpPC, Ptr)) return false; if (Ptr.canBeInitialized()) Ptr.initialize(); @@ -2078,7 +2078,7 @@ bool StoreBitFieldActivate(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop<T>(); const Pointer &Ptr = S.Stk.peek<Pointer>(); - if (!CheckStore(S, OpPC, Ptr, /*WilLBeActivated=*/true)) + if (!CheckStore(S, OpPC, Ptr, /*WillBeActivated=*/true)) return false; if (Ptr.canBeInitialized()) { Ptr.initialize(); diff --git a/clang/test/AST/ByteCode/unions.cpp b/clang/test/AST/ByteCode/unions.cpp index 414070417a02a..2123a932fce10 100644 --- a/clang/test/AST/ByteCode/unions.cpp +++ b/clang/test/AST/ByteCode/unions.cpp @@ -986,4 +986,25 @@ namespace ActicvateInvalidPtr { foo.a[1] = 0; // both-note {{assignment to dereferenced one-past-the-end pointer}} } } + +namespace NonTrivialUnionCtor { + union A { + int y = 5; + }; + union D { + constexpr D(int n) : n(n) {} + constexpr D() : n(3) {} + + A a; + int n; + }; + + constexpr bool j() { + D d; + d.a.y = 3; // both-note {{assignment to member 'a' of union with active member 'n'}} + return d.a.y == 3; + } + static_assert(j()); // both-error {{not an integral constant expression}} \ + // both-note {{in call to}} +} #endif _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
