[clang] [clang][bytecode] Don't implicitly begin union member lifetime... (PR #192212)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 16 02:04:17 PDT 2026


Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/192212 at github.com>


https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/192212

>From fa08f80be0059ec0b6ec040e70cdc677b5a5a16e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 16 Apr 2026 08:41:07 +0200
Subject: [PATCH 1/2] woops

---
 clang/lib/AST/ByteCode/Compiler.cpp |  6 +++++-
 clang/test/AST/ByteCode/unions.cpp  | 23 +++++++++++++++++++++++
 2 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 59510612d9617..022c59d5e651b 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -5601,12 +5601,16 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
   SmallVector<const Expr *, 8> Args(ArrayRef(E->getArgs(), E->getNumArgs()));
 
   bool IsAssignmentOperatorCall = false;
+  bool ActivateLHS = false;
   if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(E);
       OCE && OCE->isAssignmentOp()) {
     // Just like with regular assignments, we need to special-case assignment
     // operators here and evaluate the RHS (the second arg) before the LHS (the
     // first arg). We fix this by using a Flip op later.
     assert(Args.size() == 2);
+    const CXXRecordDecl *LHSRecord = Args[0]->getType()->getAsCXXRecordDecl();
+    assert(LHSRecord);
+    ActivateLHS = LHSRecord->hasTrivialDefaultConstructor();
     IsAssignmentOperatorCall = true;
     std::reverse(Args.begin(), Args.end());
   }
@@ -5684,7 +5688,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
       return false;
   }
 
-  if (!this->visitCallArgs(Args, FuncDecl, IsAssignmentOperatorCall,
+  if (!this->visitCallArgs(Args, FuncDecl, ActivateLHS,
                            isa<CXXOperatorCallExpr>(E)))
     return false;
 
diff --git a/clang/test/AST/ByteCode/unions.cpp b/clang/test/AST/ByteCode/unions.cpp
index 2123a932fce10..2422b76a8bd6e 100644
--- a/clang/test/AST/ByteCode/unions.cpp
+++ b/clang/test/AST/ByteCode/unions.cpp
@@ -1007,4 +1007,27 @@ namespace NonTrivialUnionCtor {
   static_assert(j()); // both-error {{not an integral constant expression}} \
                       // both-note {{in call to}}
 }
+
+/// u.a should not implicitly get activated when assigning to it, since A
+/// does not have a non-deleted trivial default constructor.
+namespace NoTrivialCtor {
+  struct A {
+   int i;
+   constexpr A() : i(0) {}
+   constexpr A(int i) : i(i) {}
+  };
+
+  constexpr auto test() {
+   union U {
+    int i;
+    A a;
+   } u{.i = 0};
+
+   u.a = {123}; // both-note {{member call on member 'a' of union with active member 'i'}}
+   return u.a.i;
+  }
+
+  constexpr auto r = test(); // both-error {{must be initialized by a constant expression}} \
+                             // both-note {{in call to}}
+}
 #endif

>From 007dc48b9ff203a4288a6c67c7e59e17ab115a75 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 16 Apr 2026 10:38:03 +0200
Subject: [PATCH 2/2] Handle non-record LHS types.

---
 clang/lib/AST/ByteCode/Compiler.cpp |  3 +--
 clang/test/AST/ByteCode/unions.cpp  | 12 ++++++++++++
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 022c59d5e651b..03bd1f48addc7 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -5609,8 +5609,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
     // first arg). We fix this by using a Flip op later.
     assert(Args.size() == 2);
     const CXXRecordDecl *LHSRecord = Args[0]->getType()->getAsCXXRecordDecl();
-    assert(LHSRecord);
-    ActivateLHS = LHSRecord->hasTrivialDefaultConstructor();
+    ActivateLHS = LHSRecord && LHSRecord->hasTrivialDefaultConstructor();
     IsAssignmentOperatorCall = true;
     std::reverse(Args.begin(), Args.end());
   }
diff --git a/clang/test/AST/ByteCode/unions.cpp b/clang/test/AST/ByteCode/unions.cpp
index 2422b76a8bd6e..608b6b433fcd5 100644
--- a/clang/test/AST/ByteCode/unions.cpp
+++ b/clang/test/AST/ByteCode/unions.cpp
@@ -1029,5 +1029,17 @@ namespace NoTrivialCtor {
 
   constexpr auto r = test(); // both-error {{must be initialized by a constant expression}} \
                              // both-note {{in call to}}
+
+  /// Should still work if the LHS is not a record type.
+  enum byte {};
+  constexpr byte operator|=(byte a, byte b) {
+    return byte{};
+  }
+  constexpr int foo() {
+    byte b{};
+    b |= byte{};
+    return 10;
+  }
+  static_assert(foo() == 10);
 }
 #endif



More information about the cfe-commits mailing list