https://github.com/SekaiArendelle created https://github.com/llvm/llvm-project/pull/197416
When HandleDestructionImpl destroys a class object during constant evaluation, it attempts to look up the class's destructor via getDestructor() before checking whether the record is an anonymous union. Anonymous unions should not have their destructors invoked directly during constant evaluation — their lifetime is managed by the enclosing class's destructor. Move the anonymous-union short-circuit before the getDestructor() call so anonymous unions are handled early, regardless of whether getDestructor() returns null or not. This fixes cases where an object with an implicitly-defined constexpr destructor stored inside an anonymous union member was incorrectly rejected during constant evaluation. >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/2] [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/2] 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); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
