https://github.com/tbaederr created 
https://github.com/llvm/llvm-project/pull/206424

See the comment.

>From a4202831efb1c0765fc53b05c76779e5feb273e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Mon, 29 Jun 2026 10:01:52 +0200
Subject: [PATCH] [clang][bytecode] Fix an assertion failure with double
 deletions

See the comment.
---
 clang/lib/AST/ByteCode/DynamicAllocator.cpp |  7 +++++--
 clang/lib/AST/ByteCode/DynamicAllocator.h   |  3 +--
 clang/lib/AST/ByteCode/Interp.cpp           |  2 +-
 clang/lib/AST/ByteCode/InterpBuiltin.cpp    |  2 +-
 clang/test/AST/ByteCode/new-delete.cpp      | 16 ++++++++++++++++
 5 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/clang/lib/AST/ByteCode/DynamicAllocator.cpp 
b/clang/lib/AST/ByteCode/DynamicAllocator.cpp
index a3a0dcd489a76..d70202b92e6e9 100644
--- a/clang/lib/AST/ByteCode/DynamicAllocator.cpp
+++ b/clang/lib/AST/ByteCode/DynamicAllocator.cpp
@@ -110,7 +110,7 @@ Block *DynamicAllocator::allocate(const Descriptor *D, 
unsigned EvalID,
 }
 
 bool DynamicAllocator::deallocate(const Expr *Source,
-                                  const Block *BlockToDelete, InterpState &S) {
+                                  const Block *BlockToDelete) {
   auto It = AllocationSites.find(Source);
   if (It == AllocationSites.end())
     return false;
@@ -123,7 +123,10 @@ bool DynamicAllocator::deallocate(const Expr *Source,
     return BlockToDelete == A.block();
   });
 
-  assert(AllocIt != Site.Allocations.end());
+  // The allocation site it fine, but this block doesn't belong to it. Must've
+  // already been deleted.
+  if (AllocIt == Site.Allocations.end())
+    return false;
 
   Block *B = AllocIt->block();
   assert(B->isInitialized());
diff --git a/clang/lib/AST/ByteCode/DynamicAllocator.h 
b/clang/lib/AST/ByteCode/DynamicAllocator.h
index ab1058bdf9f22..2336c3f3316c3 100644
--- a/clang/lib/AST/ByteCode/DynamicAllocator.h
+++ b/clang/lib/AST/ByteCode/DynamicAllocator.h
@@ -80,8 +80,7 @@ class DynamicAllocator final {
 
   /// Deallocate the given source+block combination.
   /// Returns \c true if anything has been deallocatd, \c false otherwise.
-  bool deallocate(const Expr *Source, const Block *BlockToDelete,
-                  InterpState &S);
+  bool deallocate(const Expr *Source, const Block *BlockToDelete);
 
   /// Checks whether the allocation done at the given source is an array
   /// allocation.
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 2f09c19178f09..0396482517046 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1485,7 +1485,7 @@ bool Free(InterpState &S, CodePtr OpPC, bool 
DeleteIsArrayForm,
   if (!RunDestructors(S, OpPC, BlockToDelete))
     return false;
 
-  if (!Allocator.deallocate(Source, BlockToDelete, S)) {
+  if (!Allocator.deallocate(Source, BlockToDelete)) {
     // Nothing has been deallocated, this must be a double-delete.
     const SourceInfo &Loc = S.Current->getSource(OpPC);
     S.FFDiag(Loc, diag::note_constexpr_double_delete);
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index b76f13833da14..73952e032f1eb 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -1681,7 +1681,7 @@ static bool interp__builtin_operator_delete(InterpState 
&S, CodePtr OpPC,
   std::optional<DynamicAllocator::Form> AllocForm =
       Allocator.getAllocationForm(Source);
 
-  if (!Allocator.deallocate(Source, BlockToDelete, S)) {
+  if (!Allocator.deallocate(Source, BlockToDelete)) {
     // Nothing has been deallocated, this must be a double-delete.
     const SourceInfo &Loc = S.Current->getSource(OpPC);
     S.FFDiag(Loc, diag::note_constexpr_double_delete);
diff --git a/clang/test/AST/ByteCode/new-delete.cpp 
b/clang/test/AST/ByteCode/new-delete.cpp
index b9c1e089e6536..24195d77962b8 100644
--- a/clang/test/AST/ByteCode/new-delete.cpp
+++ b/clang/test/AST/ByteCode/new-delete.cpp
@@ -112,6 +112,22 @@ consteval int doubleDelete() { // both-error {{never 
produces a constant express
 static_assert(doubleDelete() == 1); // both-error {{not an integral constant 
expression}} \
                                     // both-note {{in call to 
'doubleDelete()'}}
 
+template <int N>
+constexpr bool doubleDelete2(const char (&x)[N]) {
+  int *p[N];
+  for (int i = 0; i < N; i++)
+    p[i] = new int(x[i]);
+
+  delete p[0];
+  delete p[0]; // both-note {{delete of pointer that has already been deleted}}
+
+  return true;
+}
+static_assert(doubleDelete2("foo")); // both-error {{not an integral constant 
expression}} \
+                                     // both-note {{in call to}}
+
+
+
 constexpr int AutoArray() {
   auto array = new int[]{0, 1, 2, 3};
   int ret = array[3];

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to