Author: Arendelle Date: 2026-07-15T12:56:01+02:00 New Revision: d9b56d3e5e012a618f55318662e3e5c9ba2c4abf
URL: https://github.com/llvm/llvm-project/commit/d9b56d3e5e012a618f55318662e3e5c9ba2c4abf DIFF: https://github.com/llvm/llvm-project/commit/d9b56d3e5e012a618f55318662e3e5c9ba2c4abf.diff LOG: [clang][constexpr] Move anonymous-union check before getDestructor() in HandleDestructionImpl (#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. Added: Modified: clang/lib/AST/ExprConstant.cpp clang/test/AST/ByteCode/unions.cpp clang/test/SemaCXX/constant-expression-cxx2a.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 7d9dba8cd33fc..a6f56f3cfd1fb 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -7420,23 +7420,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; } diff --git a/clang/test/AST/ByteCode/unions.cpp b/clang/test/AST/ByteCode/unions.cpp index 4c72af986c333..9d11a984ace1b 100644 --- a/clang/test/AST/ByteCode/unions.cpp +++ b/clang/test/AST/ByteCode/unions.cpp @@ -1083,6 +1083,26 @@ 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 namespace TrivialDtorInEvaluateDtor{ diff --git a/clang/test/SemaCXX/constant-expression-cxx2a.cpp b/clang/test/SemaCXX/constant-expression-cxx2a.cpp index 94ecafc081302..870865646a61e 100644 --- a/clang/test/SemaCXX/constant-expression-cxx2a.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx2a.cpp @@ -1524,3 +1524,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
