@@ -964,7 +964,7 @@ static bool runRecordDestructor(InterpState &S, CodePtr OpPC,
964964 return true ;
965965}
966966
967- bool RunDestructors (InterpState &S, CodePtr OpPC, const Block *B) {
967+ static bool RunDestructors (InterpState &S, CodePtr OpPC, const Block *B) {
968968 assert (B);
969969 const Descriptor *Desc = B->getDescriptor ();
970970
@@ -989,6 +989,89 @@ bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) {
989989 return runRecordDestructor (S, OpPC, Pointer (const_cast <Block *>(B)), Desc);
990990}
991991
992+ bool Free (InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm,
993+ bool IsGlobalDelete) {
994+ if (!CheckDynamicMemoryAllocation (S, OpPC))
995+ return false ;
996+
997+ const Expr *Source = nullptr ;
998+ const Block *BlockToDelete = nullptr ;
999+ {
1000+ // Extra scope for this so the block doesn't have this pointer
1001+ // pointing to it when we destroy it.
1002+ Pointer Ptr = S.Stk .pop <Pointer>();
1003+
1004+ // Deleteing nullptr is always fine.
1005+ if (Ptr.isZero ())
1006+ return true ;
1007+
1008+ // Remove base casts.
1009+ while (Ptr.isBaseClass ())
1010+ Ptr = Ptr.getBase ();
1011+
1012+ if (!Ptr.isRoot () || Ptr.isOnePastEnd () || Ptr.isArrayElement ()) {
1013+ const SourceInfo &Loc = S.Current ->getSource (OpPC);
1014+ S.FFDiag (Loc, diag::note_constexpr_delete_subobject)
1015+ << Ptr.toDiagnosticString (S.getASTContext ()) << Ptr.isOnePastEnd ();
1016+ return false ;
1017+ }
1018+
1019+ Source = Ptr.getDeclDesc ()->asExpr ();
1020+ BlockToDelete = Ptr.block ();
1021+
1022+ if (!CheckDeleteSource (S, OpPC, Source, Ptr))
1023+ return false ;
1024+
1025+ // For a class type with a virtual destructor, the selected operator delete
1026+ // is the one looked up when building the destructor.
1027+ QualType AllocType = Ptr.getType ();
1028+ if (!DeleteIsArrayForm && !IsGlobalDelete) {
1029+ auto getVirtualOperatorDelete = [](QualType T) -> const FunctionDecl * {
1030+ if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl ())
1031+ if (const CXXDestructorDecl *DD = RD->getDestructor ())
1032+ return DD->isVirtual () ? DD->getOperatorDelete () : nullptr ;
1033+ return nullptr ;
1034+ };
1035+
1036+ AllocType->dump ();
1037+ if (const FunctionDecl *VirtualDelete =
1038+ getVirtualOperatorDelete (AllocType);
1039+ VirtualDelete &&
1040+ !VirtualDelete->isReplaceableGlobalAllocationFunction ()) {
1041+ S.FFDiag (S.Current ->getSource (OpPC),
1042+ diag::note_constexpr_new_non_replaceable)
1043+ << isa<CXXMethodDecl>(VirtualDelete) << VirtualDelete;
1044+ return false ;
1045+ }
1046+ }
1047+ }
1048+ assert (Source);
1049+ assert (BlockToDelete);
1050+
1051+ // Invoke destructors before deallocating the memory.
1052+ if (!RunDestructors (S, OpPC, BlockToDelete))
1053+ return false ;
1054+
1055+ DynamicAllocator &Allocator = S.getAllocator ();
1056+ const Descriptor *BlockDesc = BlockToDelete->getDescriptor ();
1057+ std::optional<DynamicAllocator::Form> AllocForm =
1058+ Allocator.getAllocationForm (Source);
1059+
1060+ if (!Allocator.deallocate (Source, BlockToDelete, S)) {
1061+ // Nothing has been deallocated, this must be a double-delete.
1062+ const SourceInfo &Loc = S.Current ->getSource (OpPC);
1063+ S.FFDiag (Loc, diag::note_constexpr_double_delete);
1064+ return false ;
1065+ }
1066+
1067+ assert (AllocForm);
1068+ DynamicAllocator::Form DeleteForm = DeleteIsArrayForm
1069+ ? DynamicAllocator::Form::Array
1070+ : DynamicAllocator::Form::NonArray;
1071+ return CheckNewDeleteForms (S, OpPC, *AllocForm, DeleteForm, BlockDesc,
1072+ Source);
1073+ }
1074+
9921075void diagnoseEnumValue (InterpState &S, CodePtr OpPC, const EnumDecl *ED,
9931076 const APSInt &Value) {
9941077 llvm::APInt Min;
0 commit comments