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 +constexpr T* construct_at(T* p, Args&&... args) { return ::new((void*)p) T(static_cast(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