[clang][bytecode] Check new/delete mismatch earlier (#147732)

This fixes a mismatch in diagnostic output with the current intepreter.
This commit is contained in:
Timm Baeder 2025-07-10 07:33:33 +02:00 committed by GitHub
parent 00a85e5704
commit 36cbd43ae8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 32 additions and 14 deletions

View File

@ -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<DynamicAllocator::Form> 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<DynamicAllocator::Form> 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,

View File

@ -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];