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

Reply via email to