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

Reply via email to