Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ Potentially Breaking Changes
call the member ``operator delete`` instead of the expected global
delete operator. The old behavior is retained under ``-fclang-abi-compat=21``
flag.
- Clang now supports MSVC vector deleting destructors when targeting Windows.
This means that vtables of classes with virtual destructors will contain a
pointer to vector deleting destructor (instead of scalar deleting destructor)
which in fact is a different symbol with different name and linkage. This
may cause runtime failures if two binaries using the same class defining a
virtual destructor are compiled with different versions of clang.


C/C++ Language Potentially Breaking Changes
-------------------------------------------
Expand Down Expand Up @@ -549,6 +556,8 @@ Android Support
Windows Support
^^^^^^^^^^^^^^^

- Clang now supports MSVC vector deleting destructors (GH19772).

LoongArch Support
^^^^^^^^^^^^^^^^^
- Enable linker relaxation by default for loongarch64.
Expand Down
22 changes: 22 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,18 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable llvm::DenseSet<const FunctionDecl *> DestroyingOperatorDeletes;
mutable llvm::DenseSet<const FunctionDecl *> TypeAwareOperatorNewAndDeletes;

/// Global and array operators delete are only required for MSVC deleting
/// destructors support. Store them here to avoid keeping 4 pointers that are
/// not always used in each redeclaration of the destructor.
mutable llvm::DenseMap<const CXXDestructorDecl *, FunctionDecl *>
OperatorDeletesForVirtualDtor;
mutable llvm::DenseMap<const CXXDestructorDecl *, FunctionDecl *>
GlobalOperatorDeletesForVirtualDtor;
mutable llvm::DenseMap<const CXXDestructorDecl *, FunctionDecl *>
ArrayOperatorDeletesForVirtualDtor;
mutable llvm::DenseMap<const CXXDestructorDecl *, FunctionDecl *>
GlobalArrayOperatorDeletesForVirtualDtor;

/// The next string literal "version" to allocate during constant evaluation.
/// This is used to distinguish between repeated evaluations of the same
/// string literal.
Expand Down Expand Up @@ -3473,6 +3485,16 @@ class ASTContext : public RefCountedBase<ASTContext> {
bool IsTypeAware);
bool isTypeAwareOperatorNewOrDelete(const FunctionDecl *FD) const;

enum OperatorDeleteKind { Regular, GlobalRegular, Array, ArrayGlobal };

void addOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
FunctionDecl *OperatorDelete,
OperatorDeleteKind K) const;
FunctionDecl *getOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
OperatorDeleteKind K) const;
bool dtorHasOperatorDelete(const CXXDestructorDecl *Dtor,
OperatorDeleteKind K) const;

/// Retrieve the context for computing mangling numbers in the given
/// DeclContext.
MangleNumberingContext &getManglingNumberContext(const DeclContext *DC);
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/AST/ASTMutationListener.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ class ASTMutationListener {
virtual void ResolvedOperatorGlobDelete(const CXXDestructorDecl *DD,
const FunctionDecl *GlobDelete) {}

/// A virtual destructor's operator array delete has been resolved.
virtual void ResolvedOperatorArrayDelete(const CXXDestructorDecl *DD,
const FunctionDecl *ArrayDelete) {}

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

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

Expand Down
16 changes: 6 additions & 10 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -2872,8 +2872,6 @@ 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 @@ -2900,14 +2898,12 @@ class CXXDestructorDecl : public CXXMethodDecl {

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

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

const FunctionDecl *getOperatorGlobalDelete() const {
return getCanonicalDecl()->OperatorGlobalDelete;
}
void setOperatorArrayDelete(FunctionDecl *OD);
void setGlobalOperatorArrayDelete(FunctionDecl *OD);
const FunctionDecl *getOperatorDelete() const;
const FunctionDecl *getOperatorGlobalDelete() const;
const FunctionDecl *getArrayOperatorDelete() const;
const FunctionDecl *getGlobalArrayOperatorDelete() const;

Expr *getOperatorDeleteThisArg() const {
return getCanonicalDecl()->OperatorDeleteThisArg;
Expand Down
6 changes: 4 additions & 2 deletions clang/include/clang/AST/VTableBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ class VTableComponent {

bool isRTTIKind() const { return isRTTIKind(getKind()); }

GlobalDecl getGlobalDecl() const {
GlobalDecl getGlobalDecl(bool HasVectorDeletingDtors) const {
assert(isUsedFunctionPointerKind() &&
"GlobalDecl can be created only from virtual function");

Expand All @@ -161,7 +161,9 @@ class VTableComponent {
case CK_CompleteDtorPointer:
return GlobalDecl(DtorDecl, CXXDtorType::Dtor_Complete);
case CK_DeletingDtorPointer:
return GlobalDecl(DtorDecl, CXXDtorType::Dtor_Deleting);
return GlobalDecl(DtorDecl, (HasVectorDeletingDtors)
? CXXDtorType::Dtor_VectorDeleting
: CXXDtorType::Dtor_Deleting);
case CK_VCallOffset:
case CK_VBaseOffset:
case CK_OffsetToTop:
Expand Down
11 changes: 6 additions & 5 deletions clang/include/clang/Basic/ABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ enum CXXCtorType {

/// C++ destructor types.
enum CXXDtorType {
Dtor_Deleting, ///< Deleting dtor
Dtor_Complete, ///< Complete object dtor
Dtor_Base, ///< Base object dtor
Dtor_Comdat, ///< The COMDAT used for dtors
Dtor_Unified, ///< GCC-style unified dtor
Dtor_Deleting, ///< Deleting dtor
Dtor_Complete, ///< Complete object dtor
Dtor_Base, ///< Base object dtor
Dtor_Comdat, ///< The COMDAT used for dtors
Dtor_Unified, ///< GCC-style unified dtor
Dtor_VectorDeleting, ///< Vector deleting dtor
};

} // end namespace clang
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -1793,6 +1793,11 @@ class TargetInfo : public TransferrableTargetInfo,
/// destructor body.
virtual bool callGlobalDeleteInDeletingDtor(const LangOptions &) const;

/// Controls whether to emit MSVC vector deleting destructors. The support for
/// vector deleting affects vtable layout and therefore is an ABI breaking
/// change. The support was only implemented at Clang 22 timeframe.
virtual bool emitVectorDeletingDtors(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
3 changes: 2 additions & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -8565,7 +8565,8 @@ class Sema final : public SemaBase {
FunctionDecl *FindDeallocationFunctionForDestructor(SourceLocation StartLoc,
CXXRecordDecl *RD,
bool Diagnose,
bool LookForGlobal);
bool LookForGlobal,
DeclarationName Name);

/// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in:
/// @code ::delete ptr; @endcode
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Serialization/ASTWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,10 @@ class ASTWriter : public ASTDeserializationListener,
Expr *ThisArg) override;
void ResolvedOperatorGlobDelete(const CXXDestructorDecl *DD,
const FunctionDecl *Delete) override;
void ResolvedOperatorArrayDelete(const CXXDestructorDecl *DD,
const FunctionDecl *Delete) override;
void ResolvedOperatorGlobArrayDelete(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
65 changes: 65 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13328,6 +13328,71 @@ bool ASTContext::isTypeAwareOperatorNewOrDelete(const FunctionDecl *FD) const {
return TypeAwareOperatorNewAndDeletes.contains(FD->getCanonicalDecl());
}

void ASTContext::addOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
FunctionDecl *OperatorDelete,
OperatorDeleteKind K) const {
switch (K) {
case OperatorDeleteKind::Regular:
OperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] = OperatorDelete;
break;
case OperatorDeleteKind::GlobalRegular:
GlobalOperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] =
OperatorDelete;
break;
case OperatorDeleteKind::Array:
ArrayOperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] =
OperatorDelete;
break;
case OperatorDeleteKind::ArrayGlobal:
GlobalArrayOperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] =
OperatorDelete;
break;
}
}

bool ASTContext::dtorHasOperatorDelete(const CXXDestructorDecl *Dtor,
OperatorDeleteKind K) const {
switch (K) {
case OperatorDeleteKind::Regular:
return OperatorDeletesForVirtualDtor.contains(Dtor->getCanonicalDecl());
case OperatorDeleteKind::GlobalRegular:
return GlobalOperatorDeletesForVirtualDtor.contains(
Dtor->getCanonicalDecl());
case OperatorDeleteKind::Array:
return ArrayOperatorDeletesForVirtualDtor.contains(
Dtor->getCanonicalDecl());
case OperatorDeleteKind::ArrayGlobal:
return GlobalArrayOperatorDeletesForVirtualDtor.contains(
Dtor->getCanonicalDecl());
}
return false;
}

FunctionDecl *
ASTContext::getOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
OperatorDeleteKind K) const {
const CXXDestructorDecl *Canon = Dtor->getCanonicalDecl();
switch (K) {
case OperatorDeleteKind::Regular:
if (OperatorDeletesForVirtualDtor.contains(Canon))
return OperatorDeletesForVirtualDtor[Canon];
return nullptr;
case OperatorDeleteKind::GlobalRegular:
if (GlobalOperatorDeletesForVirtualDtor.contains(Canon))
return GlobalOperatorDeletesForVirtualDtor[Canon];
return nullptr;
case OperatorDeleteKind::Array:
if (ArrayOperatorDeletesForVirtualDtor.contains(Canon))
return ArrayOperatorDeletesForVirtualDtor[Canon];
return nullptr;
case OperatorDeleteKind::ArrayGlobal:
if (GlobalArrayOperatorDeletesForVirtualDtor.contains(Canon))
return GlobalArrayOperatorDeletesForVirtualDtor[Canon];
return nullptr;
}
return nullptr;
}

MangleNumberingContext &
ASTContext::getManglingNumberContext(const DeclContext *DC) {
assert(LangOpts.CPlusPlus); // We don't need mangling numbers for plain C.
Expand Down
73 changes: 63 additions & 10 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3110,12 +3110,15 @@ CXXDestructorDecl *CXXDestructorDecl::Create(
}

void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) {
auto *First = cast<CXXDestructorDecl>(getFirstDecl());
if (OD && !First->OperatorDelete) {
First->OperatorDelete = OD;
First->OperatorDeleteThisArg = ThisArg;
assert(!OD || (OD->getDeclName().getCXXOverloadedOperator() == OO_Delete));
if (OD && !getASTContext().dtorHasOperatorDelete(
this, ASTContext::OperatorDeleteKind::Regular)) {
getASTContext().addOperatorDeleteForVDtor(
this, OD, ASTContext::OperatorDeleteKind::Regular);
getCanonicalDecl()->OperatorDeleteThisArg = ThisArg;
if (auto *L = getASTMutationListener())
L->ResolvedOperatorDelete(First, OD, ThisArg);
L->ResolvedOperatorDelete(cast<CXXDestructorDecl>(getCanonicalDecl()), OD,
ThisArg);
}
}

Expand All @@ -3127,14 +3130,63 @@ void CXXDestructorDecl::setOperatorGlobalDelete(FunctionDecl *OD) {
assert(!OD ||
(OD->getDeclName().getCXXOverloadedOperator() == OO_Delete &&
OD->getDeclContext()->getRedeclContext()->isTranslationUnit()));
auto *Canonical = cast<CXXDestructorDecl>(getCanonicalDecl());
if (!Canonical->OperatorGlobalDelete) {
Canonical->OperatorGlobalDelete = OD;
if (OD && !getASTContext().dtorHasOperatorDelete(
this, ASTContext::OperatorDeleteKind::GlobalRegular)) {
getASTContext().addOperatorDeleteForVDtor(
this, OD, ASTContext::OperatorDeleteKind::GlobalRegular);
if (auto *L = getASTMutationListener())
L->ResolvedOperatorGlobDelete(Canonical, OD);
L->ResolvedOperatorGlobDelete(cast<CXXDestructorDecl>(getCanonicalDecl()),
OD);
}
}

void CXXDestructorDecl::setOperatorArrayDelete(FunctionDecl *OD) {
assert(!OD ||
(OD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete));
if (OD && !getASTContext().dtorHasOperatorDelete(
this, ASTContext::OperatorDeleteKind::Array)) {
getASTContext().addOperatorDeleteForVDtor(
this, OD, ASTContext::OperatorDeleteKind::Array);
if (auto *L = getASTMutationListener())
L->ResolvedOperatorArrayDelete(
cast<CXXDestructorDecl>(getCanonicalDecl()), OD);
}
}

void CXXDestructorDecl::setGlobalOperatorArrayDelete(FunctionDecl *OD) {
assert(!OD ||
(OD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete &&
OD->getDeclContext()->getRedeclContext()->isTranslationUnit()));
if (OD && !getASTContext().dtorHasOperatorDelete(
this, ASTContext::OperatorDeleteKind::ArrayGlobal)) {
getASTContext().addOperatorDeleteForVDtor(
this, OD, ASTContext::OperatorDeleteKind::ArrayGlobal);
if (auto *L = getASTMutationListener())
L->ResolvedOperatorGlobArrayDelete(
cast<CXXDestructorDecl>(getCanonicalDecl()), OD);
}
}

const FunctionDecl *CXXDestructorDecl::getOperatorDelete() const {
return getASTContext().getOperatorDeleteForVDtor(
this, ASTContext::OperatorDeleteKind::Regular);
}

const FunctionDecl *CXXDestructorDecl::getOperatorGlobalDelete() const {
return getASTContext().getOperatorDeleteForVDtor(
this, ASTContext::OperatorDeleteKind::GlobalRegular);
}

const FunctionDecl *CXXDestructorDecl::getArrayOperatorDelete() const {
return getASTContext().getOperatorDeleteForVDtor(
this, ASTContext::OperatorDeleteKind::Array);
}

const FunctionDecl *CXXDestructorDecl::getGlobalArrayOperatorDelete() const {
return getASTContext().getOperatorDeleteForVDtor(
this, ASTContext::OperatorDeleteKind::ArrayGlobal);
}

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 All @@ -3146,7 +3198,8 @@ bool CXXDestructorDecl::isCalledByDelete(const FunctionDecl *OpDel) const {
// delete operator, as that destructor is never called, unless the
// destructor is virtual (see [expr.delete]p8.1) because then the
// selected operator depends on the dynamic type of the pointer.
const FunctionDecl *SelectedOperatorDelete = OpDel ? OpDel : OperatorDelete;
const FunctionDecl *SelectedOperatorDelete =
OpDel ? OpDel : getOperatorDelete();
if (!SelectedOperatorDelete)
return true;

Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ const CXXRecordDecl *Expr::getBestDynamicClassType() const {
if (const PointerType *PTy = DerivedType->getAs<PointerType>())
DerivedType = PTy->getPointeeType();

while (const ArrayType *ATy = DerivedType->getAsArrayTypeUnsafe())
DerivedType = ATy->getElementType();

if (DerivedType->isDependentType())
return nullptr;

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/ItaniumMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6040,6 +6040,8 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
case Dtor_Comdat:
Out << "D5";
break;
case Dtor_VectorDeleting:
llvm_unreachable("Itanium ABI does not use vector deleting dtors");
}
}

Expand Down
Loading