Skip to content

Commit 8f0da9b

Browse files
authored
[clang][bytecode] Disable EndLifetime op for array elements (#154119)
This breaks a ton of libc++ tests otherwise, since calling std::destroy_at will currently end the lifetime of the entire array not just the given element. See #147528
1 parent 8b52e5a commit 8f0da9b

File tree

3 files changed

+36
-7
lines changed

3 files changed

+36
-7
lines changed

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1852,6 +1852,11 @@ bool EndLifetime(InterpState &S, CodePtr OpPC) {
18521852
const auto &Ptr = S.Stk.peek<Pointer>();
18531853
if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy))
18541854
return false;
1855+
1856+
// FIXME: We need per-element lifetime information for primitive arrays.
1857+
if (Ptr.isArrayElement())
1858+
return true;
1859+
18551860
endLifetimeRecurse(Ptr.narrow());
18561861
return true;
18571862
}
@@ -1861,6 +1866,11 @@ bool EndLifetimePop(InterpState &S, CodePtr OpPC) {
18611866
const auto &Ptr = S.Stk.pop<Pointer>();
18621867
if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy))
18631868
return false;
1869+
1870+
// FIXME: We need per-element lifetime information for primitive arrays.
1871+
if (Ptr.isArrayElement())
1872+
return true;
1873+
18641874
endLifetimeRecurse(Ptr.narrow());
18651875
return true;
18661876
}

clang/test/AST/ByteCode/builtin-functions.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1789,9 +1789,11 @@ namespace WithinLifetime {
17891789
} xstd; // both-error {{is not a constant expression}} \
17901790
// both-note {{in call to}}
17911791

1792+
/// FIXME: We do not have per-element lifetime information for primitive arrays.
1793+
/// See https://github.com/llvm/llvm-project/issues/147528
17921794
consteval bool test_dynamic(bool read_after_deallocate) {
17931795
std::allocator<int> a;
1794-
int* p = a.allocate(1);
1796+
int* p = a.allocate(1); // expected-note 2{{allocation performed here was not deallocated}}
17951797
// a.allocate starts the lifetime of an array,
17961798
// the complete object of *p has started its lifetime
17971799
if (__builtin_is_within_lifetime(p))
@@ -1804,12 +1806,12 @@ namespace WithinLifetime {
18041806
return false;
18051807
a.deallocate(p, 1);
18061808
if (read_after_deallocate)
1807-
__builtin_is_within_lifetime(p); // both-note {{read of heap allocated object that has been deleted}}
1809+
__builtin_is_within_lifetime(p); // ref-note {{read of heap allocated object that has been deleted}}
18081810
return true;
18091811
}
1810-
static_assert(test_dynamic(false));
1812+
static_assert(test_dynamic(false)); // expected-error {{not an integral constant expression}}
18111813
static_assert(test_dynamic(true)); // both-error {{not an integral constant expression}} \
1812-
// both-note {{in call to}}
1814+
// ref-note {{in call to}}
18131815
}
18141816

18151817
#ifdef __SIZEOF_INT128__

clang/test/AST/ByteCode/lifetimes26.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ namespace std {
1717

1818
constexpr void *operator new(std::size_t, void *p) { return p; }
1919
namespace std {
20-
template<typename T> constexpr T *construct(T *p) { return new (p) T; }
21-
template<typename T> constexpr void destroy(T *p) { p->~T(); }
20+
template<typename T> constexpr T *construct_at(T *p) { return new (p) T; }
21+
template<typename T> constexpr void destroy_at(T *p) { p->~T(); }
2222
}
2323

2424
constexpr bool foo() {
@@ -43,7 +43,24 @@ constexpr void destroy_pointer() {
4343
using T = int*;
4444
T p;
4545
p.~T();
46-
std::construct(&p);
46+
std::construct_at(&p);
4747
}
4848
static_assert((destroy_pointer(), true));
4949

50+
51+
namespace DestroyArrayElem {
52+
/// This is proof that std::destroy_at'ing an array element
53+
/// ends the lifetime of the entire array.
54+
/// See https://github.com/llvm/llvm-project/issues/147528
55+
/// Using destroy_at on array elements is currently a no-op due to this.
56+
constexpr int test() {
57+
int a[4] = {};
58+
59+
std::destroy_at(&a[3]);
60+
int r = a[1];
61+
std::construct_at(&a[3]);
62+
63+
return r;
64+
}
65+
static_assert(test() == 0);
66+
}

0 commit comments

Comments
 (0)