Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1b0b624
[win][clang] Align scalar deleting destructors with MSABI
Fznamznon May 12, 2025
d945d0e
Merge branch 'main' into fix-msvc-scalar-deleting-dtors
Fznamznon Jun 10, 2025
2e07469
Always use getCanonicalDecl() and avoid emitting branch with unreachable
Fznamznon Jun 11, 2025
2c51545
Bring back accidentally removed line
Fznamznon Jun 12, 2025
9493c05
Add release note for the breaking change.
Fznamznon Jun 12, 2025
ef899c2
Merge branch 'main' into fix-msvc-scalar-deleting-dtors
Fznamznon Jun 16, 2025
94f43ae
Enable changes under -fclang-abi-compat
Fznamznon Jun 16, 2025
83a8f06
Change variable's name
Fznamznon Jun 16, 2025
c9efdf8
Add a test for ::delete done on an object of a class with destroying
Fznamznon Jun 17, 2025
f5e7a45
Apply suggestions from code review
Fznamznon Jun 18, 2025
9d3c96c
Format
Fznamznon Jun 18, 2025
d71d3d4
Add comment to EmitConditionalDtorDeleteCall
Fznamznon Jun 18, 2025
c670d74
Assert that setOperatorGlobalDelete only accepts a global delete
Fznamznon Jun 18, 2025
7147603
Merge branch 'main' into fix-msvc-scalar-deleting-dtors
Fznamznon Jun 24, 2025
057ac9f
Update clang/docs/ReleaseNotes.rst
Fznamznon Jun 24, 2025
05c386a
Add serialization support and tests
Fznamznon Jun 25, 2025
d07da86
Remove trailing spaces
Fznamznon Jun 25, 2025
92bbda6
Introduce callGlobalDeleteInDeletingDtor
Fznamznon Jun 26, 2025
83665e2
Change the assertion
Fznamznon Jun 26, 2025
c214b69
Mention -fclang-abi-compat in the release note.
Fznamznon Jul 7, 2025
bc55453
Merge branch 'main' into fix-msvc-scalar-deleting-dtors
Fznamznon Jul 7, 2025
7b238c9
Merge branch 'main' into fix-msvc-scalar-deleting-dtors
Fznamznon Aug 7, 2025
6150d04
Switch to clang21
Fznamznon Aug 7, 2025
c0a863f
Merge branch 'main' into fix-msvc-scalar-deleting-dtors
Fznamznon Sep 4, 2025
d74fc8a
Merge branch 'main' into fix-msvc-scalar-deleting-dtors
Fznamznon Sep 10, 2025
a852a3e
CR feedback
Fznamznon Sep 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,24 @@ latest release, please see the `Clang Web Site <https://clang.llvm.org>`_ or the
Potentially Breaking Changes
============================

- Scalar deleting destructor support has been aligned with MSVC when
targeting the MSVC ABI. Clang previously implemented support for
``::delete`` by calling the complete object destructor and then the
appropriate global delete operator (as is done for the Itanium ABI).
The scalar deleting destructor is now called to destroy the object
and deallocate its storage. This is an ABI change that can result in
memory corruption when a program built for the MSVC ABI has
portions compiled with clang 21 or earlier and portions compiled
with a version of clang 22 (or MSVC). Consider a class ``X`` that
declares a virtual destructor and an ``operator delete`` member
with the destructor defined in library ``A`` and a call to `::delete`` in
library ``B``. If library ``A`` is compiled with clang 21 and library ``B``
is compiled with clang 22, the ``::delete`` call might dispatch to the
scalar deleting destructor emitted in library ``A`` which will erroneously
call the member ``operator delete`` instead of the expected global
delete operator. The old behavior is retained under ``-fclang-abi-compat=20``
flag.

C/C++ Language Potentially Breaking Changes
-------------------------------------------

Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/ASTMutationListener.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ class ASTMutationListener {
const FunctionDecl *Delete,
Expr *ThisArg) {}

/// A virtual destructor's operator global delete has been resolved.
virtual void ResolvedOperatorGlobDelete(const CXXDestructorDecl *DD,
const FunctionDecl *GlobDelete) {}

/// An implicit member got a definition.
virtual void CompletedImplicitDefinition(const FunctionDecl *D) {}

Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -2868,6 +2868,7 @@ class CXXDestructorDecl : public CXXMethodDecl {
// FIXME: Don't allocate storage for these except in the first declaration
// of a virtual destructor.
FunctionDecl *OperatorDelete = nullptr;
FunctionDecl *OperatorGlobalDelete = nullptr;
Expr *OperatorDeleteThisArg = nullptr;

CXXDestructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
Expand All @@ -2893,11 +2894,16 @@ class CXXDestructorDecl : public CXXMethodDecl {
static CXXDestructorDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);

void setOperatorDelete(FunctionDecl *OD, Expr *ThisArg);
void setOperatorGlobalDelete(FunctionDecl *OD);

const FunctionDecl *getOperatorDelete() const {
return getCanonicalDecl()->OperatorDelete;
}

const FunctionDecl *getOperatorGlobalDelete() const {
return getCanonicalDecl()->OperatorGlobalDelete;
}

Expr *getOperatorDeleteThisArg() const {
return getCanonicalDecl()->OperatorDeleteThisArg;
}
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,14 @@ class LangOptionsBase {
/// - Incorrectly return C++ records in AVX registers on x86_64.
Ver20,

/// Attempt to be ABI-compatible with code generated by Clang 21.0.x.
/// This causes clang to:
/// - When targeting Windows emit scalar deleting destructors that are not
// compatible with scalar deleting destructors emitted by MSVC for the
// cases when the class whose destructor is being emitted defines
// operator delete.
Ver21,

/// Conform to the underlying platform's C and C++ ABIs as closely
/// as we can.
Latest
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -1760,6 +1760,14 @@ class TargetInfo : public TransferrableTargetInfo,
/// Clang backwards compatibility rather than GCC/Itanium ABI compatibility.
virtual bool areDefaultedSMFStillPOD(const LangOptions&) const;

/// Controls whether global operator delete is called by the deleting
/// destructor or at the point where ::delete was called. Historically Clang
/// called global operator delete outside of the deleting destructor for both
/// Microsoft and Itanium ABI. In Clang 21 support for ::delete was aligned
/// with Microsoft ABI, so it will call global operator delete in the deleting
/// destructor body.
virtual bool callGlobalDeleteInDeletingDtor(const LangOptions &) const;

/// Controls if __builtin_longjmp / __builtin_setjmp can be lowered to
/// llvm.eh.sjlj.longjmp / llvm.eh.sjlj.setjmp.
virtual bool hasSjLjLowering() const {
Expand Down
10 changes: 6 additions & 4 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -8536,10 +8536,12 @@ class Sema final : public SemaBase {
bool Diagnose = true);
FunctionDecl *FindUsualDeallocationFunction(SourceLocation StartLoc,
ImplicitDeallocationParameters,
DeclarationName Name);
FunctionDecl *FindDeallocationFunctionForDestructor(SourceLocation StartLoc,
CXXRecordDecl *RD,
bool Diagnose = true);
DeclarationName Name,
bool Diagnose = true);
FunctionDecl *
FindDeallocationFunctionForDestructor(SourceLocation StartLoc,
CXXRecordDecl *RD, bool Diagnose = true,
bool LookForGlobal = false);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having multiple defaulted bool arguments is really confusing/easy to misuse; do these really need to be defaulted?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems they don't, there are only 2 callsites of the function so I changed these to be not defailted.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did something happen to the latest changes here? It looks like the version that got merged doesn't address the review comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmm, I would say it has been addressed, see

bool LookForGlobal);

Did you also mean to not add defaulted arguments to FindUsualDeallocationFunction? It has only one. Should I push a change to remove it too?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind; I was looking at the wrong version of the patch somehow.


/// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in:
/// @code ::delete ptr; @endcode
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Serialization/ASTWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,8 @@ class ASTWriter : public ASTDeserializationListener,
void ResolvedOperatorDelete(const CXXDestructorDecl *DD,
const FunctionDecl *Delete,
Expr *ThisArg) override;
void ResolvedOperatorGlobDelete(const CXXDestructorDecl *DD,
const FunctionDecl *Delete) override;
void CompletedImplicitDefinition(const FunctionDecl *D) override;
void InstantiationRequested(const ValueDecl *D) override;
void VariableDefinitionInstantiated(const VarDecl *D) override;
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3111,6 +3111,22 @@ void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) {
}
}

void CXXDestructorDecl::setOperatorGlobalDelete(FunctionDecl *OD) {
// FIXME: C++23 [expr.delete] specifies that the delete operator will be
// a usual deallocation function declared at global scope. A convenient
// function to assert that is lacking; Sema::isUsualDeallocationFunction()
// only works for CXXMethodDecl.
assert(!OD ||
(OD->getDeclName().getCXXOverloadedOperator() == OO_Delete &&
OD->getDeclContext()->getRedeclContext()->isTranslationUnit()));
auto *Canonical = cast<CXXDestructorDecl>(getCanonicalDecl());
if (!Canonical->OperatorGlobalDelete) {
Canonical->OperatorGlobalDelete = OD;
if (auto *L = getASTMutationListener())
L->ResolvedOperatorGlobDelete(Canonical, OD);
}
}

bool CXXDestructorDecl::isCalledByDelete(const FunctionDecl *OpDel) const {
// C++20 [expr.delete]p6: If the value of the operand of the delete-
// expression is not a null pointer value and the selected deallocation
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Basic/TargetInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,14 @@ TargetInfo::getCallingConvKind(bool ClangABICompat4) const {
return CCK_Default;
}

bool TargetInfo::callGlobalDeleteInDeletingDtor(
const LangOptions &LangOpts) const {
if (getCXXABI() == TargetCXXABI::Microsoft &&
LangOpts.getClangABICompat() > LangOptions::ClangABI::Ver21)
return true;
return false;
}

bool TargetInfo::areDefaultedSMFStillPOD(const LangOptions &LangOpts) const {
return LangOpts.getClangABICompat() > LangOptions::ClangABI::Ver15;
}
Expand Down
85 changes: 71 additions & 14 deletions clang/lib/CodeGen/CGClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1592,28 +1592,85 @@ namespace {
}
};

// This function implements generation of scalar deleting destructor body for
// the case when the destructor also accepts an implicit flag. Right now only
// Microsoft ABI requires deleting destructors to accept implicit flags.
// The flag indicates whether an operator delete should be called and whether
// it should be a class-specific operator delete or a global one.
void EmitConditionalDtorDeleteCall(CodeGenFunction &CGF,
llvm::Value *ShouldDeleteCondition,
bool ReturnAfterDelete) {
const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl);
const CXXRecordDecl *ClassDecl = Dtor->getParent();
const FunctionDecl *OD = Dtor->getOperatorDelete();
assert(OD->isDestroyingOperatorDelete() == ReturnAfterDelete &&
"unexpected value for ReturnAfterDelete");
auto *CondTy = cast<llvm::IntegerType>(ShouldDeleteCondition->getType());
// MSVC calls global operator delete inside of dtor body, but clang aligned
// with this behavior only after a particular version and started emitting
// code that is not ABI-compatible with previous versions.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// MSVC calls global operator delete inside of dtor body, but clang aligned
// with this behavior only after a particular version and started emitting
// code that is not ABI-compatible with previous versions.
// MSVC calls global operator delete inside of the dtor body, but clang aligned
// with this behavior only after a particular version. This is not
// ABI-compatible with previous versions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, thanks!

ASTContext &Context = CGF.getContext();
bool CallGlobDelete =
Context.getTargetInfo().callGlobalDeleteInDeletingDtor(
Context.getLangOpts());
if (CallGlobDelete && OD->isDestroyingOperatorDelete()) {
llvm::BasicBlock *CallDtor = CGF.createBasicBlock("dtor.call_dtor");
llvm::BasicBlock *DontCallDtor = CGF.createBasicBlock("dtor.entry_cont");
// Third bit set signals that global operator delete is called. That means
// despite class having destroying operator delete which is responsible
// for calling dtor, we need to call dtor because global operator delete
// won't do that.
llvm::Value *Check3rdBit = CGF.Builder.CreateAnd(
ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 4));
llvm::Value *ShouldCallDtor = CGF.Builder.CreateIsNull(Check3rdBit);
CGF.Builder.CreateCondBr(ShouldCallDtor, DontCallDtor, CallDtor);
CGF.EmitBlock(CallDtor);
QualType ThisTy = Dtor->getFunctionObjectParameterType();
CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false,
/*Delegating=*/false, CGF.LoadCXXThisAddress(),
ThisTy);
CGF.Builder.CreateBr(DontCallDtor);
CGF.EmitBlock(DontCallDtor);
}
llvm::BasicBlock *callDeleteBB = CGF.createBasicBlock("dtor.call_delete");
llvm::BasicBlock *continueBB = CGF.createBasicBlock("dtor.continue");
llvm::Value *ShouldCallDelete
= CGF.Builder.CreateIsNull(ShouldDeleteCondition);
// First bit set signals that operator delete must be called.
llvm::Value *Check1stBit = CGF.Builder.CreateAnd(
ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 1));
llvm::Value *ShouldCallDelete = CGF.Builder.CreateIsNull(Check1stBit);
CGF.Builder.CreateCondBr(ShouldCallDelete, continueBB, callDeleteBB);

CGF.EmitBlock(callDeleteBB);
const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl);
const CXXRecordDecl *ClassDecl = Dtor->getParent();
CGF.EmitDeleteCall(Dtor->getOperatorDelete(),
LoadThisForDtorDelete(CGF, Dtor),
CGF.getContext().getTagDeclType(ClassDecl));
assert(Dtor->getOperatorDelete()->isDestroyingOperatorDelete() ==
ReturnAfterDelete &&
"unexpected value for ReturnAfterDelete");
if (ReturnAfterDelete)
CGF.EmitBranchThroughCleanup(CGF.ReturnBlock);
else
CGF.Builder.CreateBr(continueBB);
auto EmitDeleteAndGoToEnd = [&](const FunctionDecl *DeleteOp) {
CGF.EmitDeleteCall(DeleteOp, LoadThisForDtorDelete(CGF, Dtor),
Context.getTagDeclType(ClassDecl));
if (ReturnAfterDelete)
CGF.EmitBranchThroughCleanup(CGF.ReturnBlock);
else
CGF.Builder.CreateBr(continueBB);
};
// If Sema only found a global operator delete previously, the dtor can
// always call it. Otherwise we need to check the third bit and call the
// appropriate operator delete, i.e. global or class-specific.
if (const FunctionDecl *GlobOD = Dtor->getOperatorGlobalDelete();
isa<CXXMethodDecl>(OD) && GlobOD && CallGlobDelete) {
// Third bit set signals that global operator delete is called, i.e.
// ::delete appears on the callsite.
llvm::Value *CheckTheBitForGlobDeleteCall = CGF.Builder.CreateAnd(
ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 4));
llvm::Value *ShouldCallGlobDelete =
CGF.Builder.CreateIsNull(CheckTheBitForGlobDeleteCall);
llvm::BasicBlock *GlobDelete =
CGF.createBasicBlock("dtor.call_glob_delete");
llvm::BasicBlock *ClassDelete =
CGF.createBasicBlock("dtor.call_class_delete");
CGF.Builder.CreateCondBr(ShouldCallGlobDelete, ClassDelete, GlobDelete);
CGF.EmitBlock(GlobDelete);

EmitDeleteAndGoToEnd(GlobOD);
CGF.EmitBlock(ClassDelete);
}
EmitDeleteAndGoToEnd(OD);

CGF.EmitBlock(continueBB);
}
Expand Down
24 changes: 17 additions & 7 deletions clang/lib/CodeGen/MicrosoftCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -900,12 +900,19 @@ void MicrosoftCXXABI::emitVirtualObjectDelete(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor) {
// FIXME: Provide a source location here even though there's no
// CXXMemberCallExpr for dtor call.
bool UseGlobalDelete = DE->isGlobalDelete();
CXXDtorType DtorType = UseGlobalDelete ? Dtor_Complete : Dtor_Deleting;
llvm::Value *MDThis = EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr, DE,
/*CallOrInvoke=*/nullptr);
if (UseGlobalDelete)
CGF.EmitDeleteCall(DE->getOperatorDelete(), MDThis, ElementType);
if (!getContext().getTargetInfo().callGlobalDeleteInDeletingDtor(
getContext().getLangOpts())) {
bool UseGlobalDelete = DE->isGlobalDelete();
CXXDtorType DtorType = UseGlobalDelete ? Dtor_Complete : Dtor_Deleting;
llvm::Value *MDThis =
EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr, DE,
/*CallOrInvoke=*/nullptr);
if (UseGlobalDelete)
CGF.EmitDeleteCall(DE->getOperatorDelete(), MDThis, ElementType);
} else {
EmitVirtualDestructorCall(CGF, Dtor, Dtor_Deleting, Ptr, DE,
/*CallOrInvoke=*/nullptr);
}
}

void MicrosoftCXXABI::emitRethrow(CodeGenFunction &CGF, bool isNoReturn) {
Expand Down Expand Up @@ -2020,7 +2027,10 @@ llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall(
ASTContext &Context = getContext();
llvm::Value *ImplicitParam = llvm::ConstantInt::get(
llvm::IntegerType::getInt32Ty(CGF.getLLVMContext()),
DtorType == Dtor_Deleting);
(DtorType == Dtor_Deleting) |
4 * (D && D->isGlobalDelete() &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really like using implicit bool->int conversion... I think I'd write something like:

bool IsDeleting = DtorType == Dtor_Deleting;
bool IsGlobalDelete = D && D->isGlobalDelete() &&
    Context.getTargetInfo().callGlobalDeleteInDeletingDtor(Context.getLangOpts());
llvm::Value *ImplicitParam = CGF.Builder.getInt32((IsDeleting ? 1 : 0) |
                               (IsGlobalDelete ? 4 : 0));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, changed.

Context.getTargetInfo().callGlobalDeleteInDeletingDtor(
Context.getLangOpts())));

QualType ThisTy;
if (CE) {
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3975,6 +3975,9 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts,
case LangOptions::ClangABI::Ver20:
GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "20.0");
break;
case LangOptions::ClangABI::Ver21:
GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "21.0");
break;
case LangOptions::ClangABI::Latest:
break;
}
Expand Down Expand Up @@ -4508,6 +4511,8 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args,
Opts.setClangABICompat(LangOptions::ClangABI::Ver19);
else if (Major <= 20)
Opts.setClangABICompat(LangOptions::ClangABI::Ver20);
else if (Major <= 21)
Opts.setClangABICompat(LangOptions::ClangABI::Ver21);
} else if (Ver != "latest") {
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Frontend/MultiplexConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ class MultiplexASTMutationListener : public ASTMutationListener {
void ResolvedOperatorDelete(const CXXDestructorDecl *DD,
const FunctionDecl *Delete,
Expr *ThisArg) override;
void ResolvedOperatorGlobDelete(const CXXDestructorDecl *DD,
const FunctionDecl *GlobDelete) override;
void CompletedImplicitDefinition(const FunctionDecl *D) override;
void InstantiationRequested(const ValueDecl *D) override;
void VariableDefinitionInstantiated(const VarDecl *D) override;
Expand Down Expand Up @@ -184,6 +186,11 @@ void MultiplexASTMutationListener::ResolvedOperatorDelete(
for (auto *L : Listeners)
L->ResolvedOperatorDelete(DD, Delete, ThisArg);
}
void MultiplexASTMutationListener::ResolvedOperatorGlobDelete(
const CXXDestructorDecl *DD, const FunctionDecl *GlobDelete) {
for (auto *L : Listeners)
L->ResolvedOperatorGlobDelete(DD, GlobDelete);
}
void MultiplexASTMutationListener::CompletedImplicitDefinition(
const FunctionDecl *D) {
for (size_t i = 0, e = Listeners.size(); i != e; ++i)
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11121,6 +11121,22 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) {
DiagnoseUseOfDecl(OperatorDelete, Loc);
MarkFunctionReferenced(Loc, OperatorDelete);
Destructor->setOperatorDelete(OperatorDelete, ThisArg);

if (isa<CXXMethodDecl>(OperatorDelete) &&
Context.getTargetInfo().callGlobalDeleteInDeletingDtor(
Context.getLangOpts())) {
// In Microsoft ABI whenever a class has a defined operator delete,
// scalar deleting destructors check the 3rd bit of the implicit
// parameter and if it is set, then, global operator delete must be
// called instead of the class-specific one. Find and save the global
// operator delete for that case. Do not diagnose at this point because
// the lack of a global operator delete is not an error if there are no
// delete calls that require it.
FunctionDecl *GlobalOperatorDelete =
FindDeallocationFunctionForDestructor(Loc, RD, /*Diagnose*/ false,
/*LookForGlobal*/ true);
Destructor->setOperatorGlobalDelete(GlobalOperatorDelete);
}
}
}

Expand Down
Loading