Author: cakgok Date: 2026-05-06T10:03:27+02:00 New Revision: 7234297bf52c0acc4ac0798a50b566fc17041f15
URL: https://github.com/llvm/llvm-project/commit/7234297bf52c0acc4ac0798a50b566fc17041f15 DIFF: https://github.com/llvm/llvm-project/commit/7234297bf52c0acc4ac0798a50b566fc17041f15.diff LOG: [clang][bytecode] Fix sized builtin operator delete handling (#195741) **Problem:** A crash happens with std::allocator triggered sized/aligned delete operations with new constant evaluator. `interp__builtin_operator_delete` currently consumes the top of the interpreter stack as a `Pointer`. This is correct for unsized delete: ```cpp __builtin_operator_delete(p); ``` but not for sized/aligned delete reached through `std::allocator<T>::deallocate`: ```cpp __builtin_operator_delete(p, size); __builtin_operator_delete(p, size, align); ``` In those cases, the trailing operands are pushed after the pointer, so the stack top is the size/alignment operand rather than the pointer. The delete builtin handler must discard those trailing operands first. Tested versions: 22.1.3, Trunk (x86_64-pc-linux-gnu) **How to reproduce:** ```cpp #include <vector> std::vector<int> v(1); ``` Without any additional headers: ```cpp typedef __SIZE_TYPE__ size_t; namespace std { template <class T> struct allocator { constexpr T *allocate(size_t n) { return static_cast<T *>(__builtin_operator_new(n * sizeof(T))); } constexpr void deallocate(T *p, size_t n) { __builtin_operator_delete(p, n * sizeof(T)); } }; } constexpr bool f() { int *p = std::allocator<int>().allocate(1); std::allocator<int>().deallocate(p, 1); return true; } static_assert(f()); ``` Crashes with: ```bash clang++ -std=c++20 -fexperimental-new-constant-interpreter test.cpp ``` **Fix:** Discard all trailing arguments before popping the main pointer argument. **Testing:** * Added a regression test. * The regression goes through `std::allocator<T>::deallocate` rather than calling `__builtin_operator_delete` directly. Because a direct user call is caught before the deallocation logic that consumes the pointer operand. Assisted-by: gemini-cli @tbaederr --------- Co-authored-by: Timm Baeder <[email protected]> Added: Modified: clang/lib/AST/ByteCode/InterpBuiltin.cpp clang/test/AST/ByteCode/new-delete.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 5bdd3a7824a17..11ca93c251380 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -1630,6 +1630,14 @@ static bool interp__builtin_operator_delete(InterpState &S, CodePtr OpPC, const Expr *Source = nullptr; const Block *BlockToDelete = nullptr; + unsigned NumArgs = Call->getNumArgs(); + assert(NumArgs >= 1); + + // Args are pushed in source order. The trailing sized/aligned delete + // operands are above the pointer on the stack. + for (unsigned I = NumArgs - 1; I != 0; --I) + discard(S.Stk, *S.getContext().classify(Call->getArg(I))); + if (S.checkingPotentialConstantExpression()) { S.Stk.discard<Pointer>(); return false; diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp index 4ade50b7c02e4..ac2c2ff4a73c6 100644 --- a/clang/test/AST/ByteCode/new-delete.cpp +++ b/clang/test/AST/ByteCode/new-delete.cpp @@ -643,6 +643,9 @@ namespace std { // both-note {{used to delete a null pointer}} \ // both-note {{delete of pointer '&no_deallocate_nonalloc' that does not point to a heap-allocated object}} } + constexpr void deallocate(void *p, size_t N) { + __builtin_operator_delete(p, sizeof(T) * N); + } }; template<typename T, typename ...Args> constexpr void construct_at(void *p, Args &&...args) { // #construct @@ -767,6 +770,13 @@ namespace OperatorNewDelete { // both-note {{in call}} static_assert((std::allocator<float>().deallocate(std::allocator<float>().allocate(10)), 1) == 1); + + constexpr bool sizedDeallocate() { + int *p = std::allocator<int>().allocate(1); + std::allocator<int>().deallocate(p, 1); + return true; + } + static_assert(sizedDeallocate()); } namespace Limits { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
