https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/197416
>From 62032911518c77d884841589fe5cdf3327a85f6b Mon Sep 17 00:00:00 2001 From: Arendelle <[email protected]> Date: Wed, 13 May 2026 17:55:04 +0800 Subject: [PATCH 1/3] [clang][constexpr] Move anonymous-union check before getDestructor() in HandleDestructionImpl When HandleDestructionImpl destroys a class object during constant evaluation, it calls getDestructor() before checking whether the record is an anonymous union. For an anonymous union whose members have non-trivial destructors, getDestructor() may return null while hasTrivialDestructor() returns false. This causes us to incorrectly reject valid code with a "subexpression not valid in a constant expression" diagnostic. Anonymous unions should not have their destructors invoked directly; their lifetime is ended by the enclosing class's destructor. Move the anonymous-union short-circuit before the getDestructor() call so that we avoid the erroneous destructor lookup. This fixes cases where an object with an implicitly-defined constexpr destructor stored inside an anonymous union member was incorrectly rejected during constant evaluation. --- clang/lib/AST/ExprConstant.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 5f09c9ea4a7b8..efe9d793d2fb2 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -7370,23 +7370,26 @@ static bool HandleDestructionImpl(EvalInfo &Info, SourceRange CallRange, return false; } + // If an anonymous union would be destroyed, some enclosing destructor must + // have been explicitly defined, and the anonymous union destruction should + // have no effect. + if (RD->isAnonymousStructOrUnion() && RD->isUnion()) { + Value = APValue(); + return true; + } + const CXXDestructorDecl *DD = RD->getDestructor(); if (!DD && !RD->hasTrivialDestructor()) { Info.FFDiag(CallRange.getBegin()); return false; } - if (!DD || DD->isTrivial() || - (RD->isAnonymousStructOrUnion() && RD->isUnion())) { + if (!DD || DD->isTrivial()) { // A trivial destructor just ends the lifetime of the object. Check for // this case before checking for a body, because we might not bother // building a body for a trivial destructor. Note that it doesn't matter // whether the destructor is constexpr in this case; all trivial // destructors are constexpr. - // - // If an anonymous union would be destroyed, some enclosing destructor must - // have been explicitly defined, and the anonymous union destruction should - // have no effect. Value = APValue(); return true; } >From 698048528e629fe1806acddedfad1872455dde82 Mon Sep 17 00:00:00 2001 From: Arendelle <[email protected]> Date: Wed, 13 May 2026 18:33:29 +0800 Subject: [PATCH 2/3] add test --- .../SemaCXX/constant-expression-cxx2a.cpp | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/clang/test/SemaCXX/constant-expression-cxx2a.cpp b/clang/test/SemaCXX/constant-expression-cxx2a.cpp index 396a8df21a3e3..0748c6be0179f 100644 --- a/clang/test/SemaCXX/constant-expression-cxx2a.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx2a.cpp @@ -1518,3 +1518,23 @@ namespace GH150705 { constexpr const A& a = b; constexpr auto x = (a.*q)(); // expected-error {{constant expression}} } + +namespace GH197403 { + struct Inner { + constexpr ~Inner() noexcept {} + }; + struct Outer { + Inner inner; + }; + template<typename T> + struct BugTrigger { + union { T value; int dummy; }; + constexpr BugTrigger() : value{} {} + constexpr ~BugTrigger() noexcept { value.~T(); } + }; + consteval int test() { + BugTrigger<Outer> bt; + return 0; + } + static_assert(test() == 0); +} >From f538d13fef9adc05ca0a3d5b657b3d90be4a374c Mon Sep 17 00:00:00 2001 From: Arendelle <[email protected]> Date: Fri, 15 May 2026 08:41:11 +0800 Subject: [PATCH 3/3] test for ByteCode interprater --- clang/test/AST/ByteCode/unions.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/clang/test/AST/ByteCode/unions.cpp b/clang/test/AST/ByteCode/unions.cpp index 399c4c891be00..33ca3d64aaa42 100644 --- a/clang/test/AST/ByteCode/unions.cpp +++ b/clang/test/AST/ByteCode/unions.cpp @@ -1071,4 +1071,24 @@ namespace Revive { static_assert(h() == 20); // both-error {{not an integral constant expression}} \ // both-note {{in call to}} } + +namespace GH197403 { + struct Inner { + constexpr ~Inner() noexcept {} + }; + struct Outer { + Inner inner; + }; + template<typename T> + struct BugTrigger { + union { T value; int dummy; }; + constexpr BugTrigger() : value{} {} + constexpr ~BugTrigger() noexcept { value.~T(); } + }; + consteval int test() { + BugTrigger<Outer> bt; + return 0; + } + static_assert(test() == 0); +} #endif _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
