Author: Timm Baeder
Date: 2025-08-18T16:32:50+02:00
New Revision: 8f0da9b8bd342f200a8b97cb19c2ca1588175299

URL: 
https://github.com/llvm/llvm-project/commit/8f0da9b8bd342f200a8b97cb19c2ca1588175299
DIFF: 
https://github.com/llvm/llvm-project/commit/8f0da9b8bd342f200a8b97cb19c2ca1588175299.diff

LOG: [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 https://github.com/llvm/llvm-project/issues/147528

Added: 
    

Modified: 
    clang/lib/AST/ByteCode/Interp.cpp
    clang/test/AST/ByteCode/builtin-functions.cpp
    clang/test/AST/ByteCode/lifetimes26.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 931d3879f0ff8..aeab9ff381711 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1852,6 +1852,11 @@ bool EndLifetime(InterpState &S, CodePtr OpPC) {
   const auto &Ptr = S.Stk.peek<Pointer>();
   if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy))
     return false;
+
+  // FIXME: We need per-element lifetime information for primitive arrays.
+  if (Ptr.isArrayElement())
+    return true;
+
   endLifetimeRecurse(Ptr.narrow());
   return true;
 }
@@ -1861,6 +1866,11 @@ bool EndLifetimePop(InterpState &S, CodePtr OpPC) {
   const auto &Ptr = S.Stk.pop<Pointer>();
   if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy))
     return false;
+
+  // FIXME: We need per-element lifetime information for primitive arrays.
+  if (Ptr.isArrayElement())
+    return true;
+
   endLifetimeRecurse(Ptr.narrow());
   return true;
 }

diff  --git a/clang/test/AST/ByteCode/builtin-functions.cpp 
b/clang/test/AST/ByteCode/builtin-functions.cpp
index 878c0d1a40f26..3277ef65a880b 100644
--- a/clang/test/AST/ByteCode/builtin-functions.cpp
+++ b/clang/test/AST/ByteCode/builtin-functions.cpp
@@ -1789,9 +1789,11 @@ namespace WithinLifetime {
   } xstd; // both-error {{is not a constant expression}} \
           // both-note {{in call to}}
 
+  /// FIXME: We do not have per-element lifetime information for primitive 
arrays.
+  /// See https://github.com/llvm/llvm-project/issues/147528
   consteval bool test_dynamic(bool read_after_deallocate) {
     std::allocator<int> a;
-    int* p = a.allocate(1);
+    int* p = a.allocate(1); // expected-note 2{{allocation performed here was 
not deallocated}}
     // a.allocate starts the lifetime of an array,
     // the complete object of *p has started its lifetime
     if (__builtin_is_within_lifetime(p))
@@ -1804,12 +1806,12 @@ namespace WithinLifetime {
       return false;
     a.deallocate(p, 1);
     if (read_after_deallocate)
-      __builtin_is_within_lifetime(p); // both-note {{read of heap allocated 
object that has been deleted}}
+      __builtin_is_within_lifetime(p); // ref-note {{read of heap allocated 
object that has been deleted}}
     return true;
   }
-  static_assert(test_dynamic(false));
+  static_assert(test_dynamic(false)); // expected-error {{not an integral 
constant expression}}
   static_assert(test_dynamic(true)); // both-error {{not an integral constant 
expression}} \
-                                     // both-note {{in call to}}
+                                     // ref-note {{in call to}}
 }
 
 #ifdef __SIZEOF_INT128__

diff  --git a/clang/test/AST/ByteCode/lifetimes26.cpp 
b/clang/test/AST/ByteCode/lifetimes26.cpp
index a5203ae77bc13..c3163f8a562bf 100644
--- a/clang/test/AST/ByteCode/lifetimes26.cpp
+++ b/clang/test/AST/ByteCode/lifetimes26.cpp
@@ -17,8 +17,8 @@ namespace std {
 
 constexpr void *operator new(std::size_t, void *p) { return p; }
 namespace std {
-  template<typename T> constexpr T *construct(T *p) { return new (p) T; }
-  template<typename T> constexpr void destroy(T *p) { p->~T(); }
+  template<typename T> constexpr T *construct_at(T *p) { return new (p) T; }
+  template<typename T> constexpr void destroy_at(T *p) { p->~T(); }
 }
 
 constexpr bool foo() {
@@ -43,7 +43,24 @@ constexpr void destroy_pointer() {
   using T = int*;
   T p;
   p.~T();
-  std::construct(&p);
+  std::construct_at(&p);
 }
 static_assert((destroy_pointer(), true));
 
+
+namespace DestroyArrayElem {
+  /// This is proof that std::destroy_at'ing an array element
+  /// ends the lifetime of the entire array.
+  /// See https://github.com/llvm/llvm-project/issues/147528
+  /// Using destroy_at on array elements is currently a no-op due to this.
+  constexpr int test() {
+    int a[4] = {};
+
+    std::destroy_at(&a[3]);
+    int r = a[1];
+    std::construct_at(&a[3]);
+
+    return r;
+  }
+  static_assert(test() == 0);
+}


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to