From 5d9464124dc09bde2b697d7bfc0f224b030a0bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= Date: Wed, 9 Jul 2025 15:17:26 +0200 Subject: [PATCH] [clang][bytecode] Check new/delete mismatch earlier This fixes a mismatch in diagnostic output with the current intepreter. --- clang/lib/AST/ByteCode/Interp.cpp | 32 +++++++++++++++----------- clang/test/AST/ByteCode/new-delete.cpp | 14 +++++++++++ 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 51cf0c59f0b50..be77657acabcc 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -1196,6 +1196,8 @@ bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm, if (!CheckDynamicMemoryAllocation(S, OpPC)) return false; + DynamicAllocator &Allocator = S.getAllocator(); + const Expr *Source = nullptr; const Block *BlockToDelete = nullptr; { @@ -1212,6 +1214,21 @@ bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm, while (Ptr.isBaseClass()) Ptr = Ptr.getBase(); + Source = Ptr.getDeclDesc()->asExpr(); + BlockToDelete = Ptr.block(); + + // Check that new[]/delete[] or new/delete were used, not a mixture. + const Descriptor *BlockDesc = BlockToDelete->getDescriptor(); + if (std::optional AllocForm = + Allocator.getAllocationForm(Source)) { + DynamicAllocator::Form DeleteForm = + DeleteIsArrayForm ? DynamicAllocator::Form::Array + : DynamicAllocator::Form::NonArray; + if (!CheckNewDeleteForms(S, OpPC, *AllocForm, DeleteForm, BlockDesc, + Source)) + return false; + } + // For the non-array case, the types must match if the static type // does not have a virtual destructor. if (!DeleteIsArrayForm && Ptr.getType() != InitialType && @@ -1230,9 +1247,6 @@ bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm, return false; } - Source = Ptr.getDeclDesc()->asExpr(); - BlockToDelete = Ptr.block(); - if (!CheckDeleteSource(S, OpPC, Source, Ptr)) return false; @@ -1266,11 +1280,6 @@ bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm, if (!RunDestructors(S, OpPC, BlockToDelete)) return false; - DynamicAllocator &Allocator = S.getAllocator(); - const Descriptor *BlockDesc = BlockToDelete->getDescriptor(); - std::optional AllocForm = - Allocator.getAllocationForm(Source); - if (!Allocator.deallocate(Source, BlockToDelete, S)) { // Nothing has been deallocated, this must be a double-delete. const SourceInfo &Loc = S.Current->getSource(OpPC); @@ -1278,12 +1287,7 @@ bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm, return false; } - assert(AllocForm); - DynamicAllocator::Form DeleteForm = DeleteIsArrayForm - ? DynamicAllocator::Form::Array - : DynamicAllocator::Form::NonArray; - return CheckNewDeleteForms(S, OpPC, *AllocForm, DeleteForm, BlockDesc, - Source); + return true; } void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED, diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp index 840736f332250..3c0bdbc8c99fe 100644 --- a/clang/test/AST/ByteCode/new-delete.cpp +++ b/clang/test/AST/ByteCode/new-delete.cpp @@ -171,6 +171,20 @@ namespace Arrays { } static_assert(mismatch2() == 6); // both-error {{not an integral constant expression}} \ // both-note {{in call to 'mismatch2()'}} + + constexpr int mismatch3() { // both-error {{never produces a constant expression}} + int a = 0; + struct S {}; + struct T : S {}; + T *p = new T[3]{}; // both-note 2{{heap allocation performed here}} + delete (S*)p; // both-note 2{{non-array delete used to delete pointer to array object of type 'T[3]'}} + + return 0; + + } + static_assert(mismatch3() == 0); // both-error {{not an integral constant expression}} \ + // both-note {{in call to}} + /// Array of composite elements. constexpr int foo() { S *ss = new S[12];