Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
8 changes: 8 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ Potentially Breaking Changes
options-related code has been moved out of the Driver into a separate library.
- The ``clangFrontend`` library no longer depends on ``clangDriver``, which may
break downstream projects that relied on this transitive dependency.
- 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 @@ -646,6 +652,8 @@ Windows Support
- clang-cl now supports /arch:AVX10.1 and /arch:AVX10.2.
- clang-cl now supports /vlen, /vlen=256 and /vlen=512.

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

LoongArch Support
^^^^^^^^^^^^^^^^^
- Enable linker relaxation by default for loongarch64.
Expand Down
27 changes: 27 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,21 @@ 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;

/// To remember which types did require a vector deleting dtor.
llvm::DenseSet<const CXXRecordDecl *> RequireVectorDeletingDtor;

/// 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 @@ -3489,6 +3504,18 @@ 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;
void setClassNeedsVectorDeletingDestructor(const CXXRecordDecl *RD);
bool classNeedsVectorDeletingDestructor(const CXXRecordDecl *RD);

/// 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 @@ -1798,6 +1798,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 @@ -8586,7 +8586,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 @@ -955,6 +955,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
85 changes: 85 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13352,6 +13352,91 @@ 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;
}

bool ASTContext::classNeedsVectorDeletingDestructor(const CXXRecordDecl *RD) {
if (!getTargetInfo().emitVectorDeletingDtors(getLangOpts()))
return false;
CXXDestructorDecl *Dtor = RD->getDestructor();
// The compiler can't know if new[]/delete[] will be used outside of the DLL,
// so just force vector deleting destructor emission if dllexport is present.
// This matches MSVC behavior.
if (Dtor && Dtor->isVirtual() && Dtor->hasAttr<DLLExportAttr>())
return true;

return RequireVectorDeletingDtor.count(RD);
}

void ASTContext::setClassNeedsVectorDeletingDestructor(
const CXXRecordDecl *RD) {
if (!getTargetInfo().emitVectorDeletingDtors(getLangOpts()))
return;
RequireVectorDeletingDtor.insert(RD);
}

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
Loading