[clang] [clang][bytecode] Fix activating nested unions (PR #147338)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 7 21:13:53 PDT 2025
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/147338
>From 8b42dbd7659ce5e424fb107a0d375f449a8d44a9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 7 Jul 2025 18:06:04 +0200
Subject: [PATCH] [clang][bytecode] Fix activating nested unions
When activating the new pointer, we need to de-activate pointers of
all parent unions, not just the topmost one.
---
clang/lib/AST/ByteCode/Pointer.cpp | 42 ++++++++--------------------
clang/test/AST/ByteCode/cxx23.cpp | 45 ++++++++++++++++++++++++++++++
2 files changed, 57 insertions(+), 30 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp
index e915316272178..6769e51a8ad00 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -520,39 +520,21 @@ void Pointer::activate() const {
}
};
- // Unions might be nested etc., so find the topmost Pointer that's
- // not in a union anymore.
- Pointer UnionPtr = getBase();
- while (!UnionPtr.isRoot() && UnionPtr.inUnion())
- UnionPtr = UnionPtr.getBase();
-
- assert(UnionPtr.getFieldDesc()->isUnion());
- const Record *UnionRecord = UnionPtr.getRecord();
-
- // The direct child pointer of the union that's on the path from
- // this pointer to the union.
- Pointer ChildPtr = *this;
- assert(ChildPtr != UnionPtr);
- while (true) {
- if (ChildPtr.getBase() == UnionPtr)
- break;
- ChildPtr = ChildPtr.getBase();
- }
- assert(ChildPtr.getBase() == UnionPtr);
-
- for (const Record::Field &F : UnionRecord->fields()) {
- Pointer FieldPtr = UnionPtr.atField(F.Offset);
- if (FieldPtr == ChildPtr) {
- // No need to deactivate, will be activated in the next loop.
- } else {
- deactivate(FieldPtr);
- }
- }
-
Pointer B = *this;
- while (B != UnionPtr) {
+ while (!B.isRoot() && B.inUnion()) {
activate(B);
+
+ // When walking up the pointer chain, deactivate
+ // all union child pointers that aren't on our path.
+ Pointer Cur = B;
B = B.getBase();
+ if (const Record *BR = B.getRecord(); BR && BR->isUnion()) {
+ for (const Record::Field &F : BR->fields()) {
+ Pointer FieldPtr = B.atField(F.Offset);
+ if (FieldPtr != Cur)
+ deactivate(FieldPtr);
+ }
+ }
}
}
diff --git a/clang/test/AST/ByteCode/cxx23.cpp b/clang/test/AST/ByteCode/cxx23.cpp
index 417d35dbca946..2856b872d44ab 100644
--- a/clang/test/AST/ByteCode/cxx23.cpp
+++ b/clang/test/AST/ByteCode/cxx23.cpp
@@ -4,6 +4,16 @@
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected20,all,all20 %s -fexperimental-new-constant-interpreter
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=expected23,all,all23 %s -fexperimental-new-constant-interpreter
+
+#define assert_active(F) if (!__builtin_is_within_lifetime(&F)) (1/0);
+#define assert_inactive(F) if ( __builtin_is_within_lifetime(&F)) (1/0);
+
+inline constexpr void* operator new(__SIZE_TYPE__, void* p) noexcept { return p; }
+namespace std {
+template<typename T, typename... Args>
+constexpr T* construct_at(T* p, Args&&... args) { return ::new((void*)p) T(static_cast<Args&&>(args)...); }
+}
+
constexpr int f(int n) { // all20-error {{constexpr function never produces a constant expression}}
static const int m = n; // all-note {{control flows through the definition of a static variable}} \
// all20-note {{control flows through the definition of a static variable}} \
@@ -328,3 +338,38 @@ namespace VoidCast {
constexpr int *f = (int*)(void*)b; // all-error {{must be initialized by a constant expression}} \
// all-note {{cast from 'void *' is not allowed in a constant expression}}
}
+
+#if __cplusplus >= 202302L
+namespace NestedUnions {
+ consteval bool test_nested() {
+ union {
+ union { int i; char c; } u;
+ long l;
+ };
+ std::construct_at(&l);
+ assert_active(l);
+ assert_inactive(u);
+
+ std::construct_at(&u);
+ assert_active(u);
+ assert_inactive(l);
+ assert_active(u.i);
+ assert_inactive(u.c);
+
+ std::construct_at(&u.i);
+ assert_active(u);
+ assert_inactive(u.c);
+
+
+ std::construct_at(&u.c);
+ assert_active(u);
+ assert_inactive(u.i);
+ assert_active(u.c);
+ assert_inactive(l);
+ return true;
+ }
+ static_assert(test_nested());
+
+
+}
+#endif
More information about the cfe-commits
mailing list