Skip to content

Commit 7389678

Browse files
committed
Reland [MS][clang] Add support for vector deleting destructors
MSVC supports and extension allowing to delete an array of objects via pointer whose static type doesn't match its dynamic type. This is done via generation of special destructors - vector deleting destructors. MSVC's virtual tables always contain pointer to vector deleting destructor for classes with virtual destructors, so not having this extension not having this extension implemented causes clang to generate code that is not compatible with the code generated by MSVC, because clang always puts pointer to a scalar deleting destructor to the vtable. As a bonus the deletion of an array of polymorphic object will work just like it does with MSVC - no memory leaks and correct destructors are called. This patch will cause clang to emit code that is compatible with code produced by MSVC but not compatible with code produced with clang of older versions, so the new behavior can be disabled via passing -fclang-abi-compat=21 (or lower). This is yet another attempt to land vector deleting destructors support originally implemented by llvm#133451. This PR contains fixes for issues reported in the original PR as well as fixes for issues related to operator delete[] search reported in several issues like llvm#133950 (comment) llvm#134265 Fixes llvm#19772
1 parent 22a10c8 commit 7389678

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+1260
-161
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ Potentially Breaking Changes
6969
call the member ``operator delete`` instead of the expected global
7070
delete operator. The old behavior is retained under ``-fclang-abi-compat=21``
7171
flag.
72+
- Clang now supports MSVC vector deleting destructors when targeting Windows.
73+
This means that vtables of classes with virtual destructors will contain a
74+
pointer to vector deleting destructor (instead of scalar deleting destructor)
75+
which in fact is a different symbol with different name and linkage. This
76+
may cause runtime failures if two binaries using the same class defining a
77+
virtual destructor are compiled with different versions of clang.
78+
7279

7380
C/C++ Language Potentially Breaking Changes
7481
-------------------------------------------
@@ -549,6 +556,8 @@ Android Support
549556
Windows Support
550557
^^^^^^^^^^^^^^^
551558

559+
- Clang now supports MSVC vector deleting destructors (GH19772).
560+
552561
LoongArch Support
553562
^^^^^^^^^^^^^^^^^
554563
- Enable linker relaxation by default for loongarch64.

clang/include/clang/AST/ASTContext.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,18 @@ class ASTContext : public RefCountedBase<ASTContext> {
370370
mutable llvm::DenseSet<const FunctionDecl *> DestroyingOperatorDeletes;
371371
mutable llvm::DenseSet<const FunctionDecl *> TypeAwareOperatorNewAndDeletes;
372372

373+
/// Global and array operators delete are only required for MSVC deleting
374+
/// destructors support. Store them here to avoid keeping 4 pointers that are
375+
/// not always used in each redeclaration of the destructor.
376+
mutable llvm::DenseMap<const CXXDestructorDecl *, FunctionDecl *>
377+
OperatorDeletesForVirtualDtor;
378+
mutable llvm::DenseMap<const CXXDestructorDecl *, FunctionDecl *>
379+
GlobalOperatorDeletesForVirtualDtor;
380+
mutable llvm::DenseMap<const CXXDestructorDecl *, FunctionDecl *>
381+
ArrayOperatorDeletesForVirtualDtor;
382+
mutable llvm::DenseMap<const CXXDestructorDecl *, FunctionDecl *>
383+
GlobalArrayOperatorDeletesForVirtualDtor;
384+
373385
/// The next string literal "version" to allocate during constant evaluation.
374386
/// This is used to distinguish between repeated evaluations of the same
375387
/// string literal.
@@ -3473,6 +3485,21 @@ class ASTContext : public RefCountedBase<ASTContext> {
34733485
bool IsTypeAware);
34743486
bool isTypeAwareOperatorNewOrDelete(const FunctionDecl *FD) const;
34753487

3488+
enum OperatorDeleteKind {
3489+
Regular,
3490+
GlobalRegular,
3491+
Array,
3492+
ArrayGlobal
3493+
};
3494+
3495+
void addOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
3496+
FunctionDecl *OperatorDelete,
3497+
OperatorDeleteKind K) const;
3498+
FunctionDecl *getOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
3499+
OperatorDeleteKind K) const;
3500+
bool dtorHasOperatorDelete(const CXXDestructorDecl *Dtor,
3501+
OperatorDeleteKind K) const;
3502+
34763503
/// Retrieve the context for computing mangling numbers in the given
34773504
/// DeclContext.
34783505
MangleNumberingContext &getManglingNumberContext(const DeclContext *DC);

clang/include/clang/AST/ASTMutationListener.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ class ASTMutationListener {
9090
virtual void ResolvedOperatorGlobDelete(const CXXDestructorDecl *DD,
9191
const FunctionDecl *GlobDelete) {}
9292

93+
/// A virtual destructor's operator array delete has been resolved.
94+
virtual void ResolvedOperatorArrayDelete(const CXXDestructorDecl *DD,
95+
const FunctionDecl *ArrayDelete) {}
96+
97+
/// A virtual destructor's operator global array delete has been resolved.
98+
virtual void
99+
ResolvedOperatorGlobArrayDelete(const CXXDestructorDecl *DD,
100+
const FunctionDecl *GlobArrayDelete) {}
101+
93102
/// An implicit member got a definition.
94103
virtual void CompletedImplicitDefinition(const FunctionDecl *D) {}
95104

clang/include/clang/AST/DeclCXX.h

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2872,8 +2872,6 @@ class CXXDestructorDecl : public CXXMethodDecl {
28722872

28732873
// FIXME: Don't allocate storage for these except in the first declaration
28742874
// of a virtual destructor.
2875-
FunctionDecl *OperatorDelete = nullptr;
2876-
FunctionDecl *OperatorGlobalDelete = nullptr;
28772875
Expr *OperatorDeleteThisArg = nullptr;
28782876

28792877
CXXDestructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
@@ -2900,14 +2898,12 @@ class CXXDestructorDecl : public CXXMethodDecl {
29002898

29012899
void setOperatorDelete(FunctionDecl *OD, Expr *ThisArg);
29022900
void setOperatorGlobalDelete(FunctionDecl *OD);
2903-
2904-
const FunctionDecl *getOperatorDelete() const {
2905-
return getCanonicalDecl()->OperatorDelete;
2906-
}
2907-
2908-
const FunctionDecl *getOperatorGlobalDelete() const {
2909-
return getCanonicalDecl()->OperatorGlobalDelete;
2910-
}
2901+
void setOperatorArrayDelete(FunctionDecl *OD);
2902+
void setGlobalOperatorArrayDelete(FunctionDecl *OD);
2903+
const FunctionDecl *getOperatorDelete() const;
2904+
const FunctionDecl *getOperatorGlobalDelete() const;
2905+
const FunctionDecl *getArrayOperatorDelete() const;
2906+
const FunctionDecl *getGlobalArrayOperatorDelete() const;
29112907

29122908
Expr *getOperatorDeleteThisArg() const {
29132909
return getCanonicalDecl()->OperatorDeleteThisArg;

clang/include/clang/AST/VTableBuilder.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ class VTableComponent {
150150

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

153-
GlobalDecl getGlobalDecl() const {
153+
GlobalDecl getGlobalDecl(bool HasVectorDeletingDtors) const {
154154
assert(isUsedFunctionPointerKind() &&
155155
"GlobalDecl can be created only from virtual function");
156156

@@ -161,7 +161,9 @@ class VTableComponent {
161161
case CK_CompleteDtorPointer:
162162
return GlobalDecl(DtorDecl, CXXDtorType::Dtor_Complete);
163163
case CK_DeletingDtorPointer:
164-
return GlobalDecl(DtorDecl, CXXDtorType::Dtor_Deleting);
164+
return GlobalDecl(DtorDecl, (HasVectorDeletingDtors)
165+
? CXXDtorType::Dtor_VectorDeleting
166+
: CXXDtorType::Dtor_Deleting);
165167
case CK_VCallOffset:
166168
case CK_VBaseOffset:
167169
case CK_OffsetToTop:

clang/include/clang/Basic/ABI.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ enum CXXDtorType {
3737
Dtor_Base, ///< Base object dtor
3838
Dtor_Comdat, ///< The COMDAT used for dtors
3939
Dtor_Unified, ///< GCC-style unified dtor
40+
Dtor_VectorDeleting, ///< Vector deleting dtor
4041
};
4142

4243
} // end namespace clang

clang/include/clang/Basic/TargetInfo.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,6 +1793,11 @@ class TargetInfo : public TransferrableTargetInfo,
17931793
/// destructor body.
17941794
virtual bool callGlobalDeleteInDeletingDtor(const LangOptions &) const;
17951795

1796+
/// Controls whether to emit MSVC vector deleting destructors. The support for
1797+
/// vector deleting affects vtable layout and therefore is an ABI breaking
1798+
/// change. The support was only implemented at Clang 22 timeframe.
1799+
virtual bool emitVectorDeletingDtors(const LangOptions &) const;
1800+
17961801
/// Controls if __builtin_longjmp / __builtin_setjmp can be lowered to
17971802
/// llvm.eh.sjlj.longjmp / llvm.eh.sjlj.setjmp.
17981803
virtual bool hasSjLjLowering() const {

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8565,7 +8565,8 @@ class Sema final : public SemaBase {
85658565
FunctionDecl *FindDeallocationFunctionForDestructor(SourceLocation StartLoc,
85668566
CXXRecordDecl *RD,
85678567
bool Diagnose,
8568-
bool LookForGlobal);
8568+
bool LookForGlobal,
8569+
DeclarationName Name);
85698570

85708571
/// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in:
85718572
/// @code ::delete ptr; @endcode

clang/include/clang/Serialization/ASTWriter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,10 @@ class ASTWriter : public ASTDeserializationListener,
951951
Expr *ThisArg) override;
952952
void ResolvedOperatorGlobDelete(const CXXDestructorDecl *DD,
953953
const FunctionDecl *Delete) override;
954+
void ResolvedOperatorArrayDelete(const CXXDestructorDecl *DD,
955+
const FunctionDecl *Delete) override;
956+
void ResolvedOperatorGlobArrayDelete(const CXXDestructorDecl *DD,
957+
const FunctionDecl *Delete) override;
954958
void CompletedImplicitDefinition(const FunctionDecl *D) override;
955959
void InstantiationRequested(const ValueDecl *D) override;
956960
void VariableDefinitionInstantiated(const VarDecl *D) override;

clang/lib/AST/ASTContext.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13328,6 +13328,76 @@ bool ASTContext::isTypeAwareOperatorNewOrDelete(const FunctionDecl *FD) const {
1332813328
return TypeAwareOperatorNewAndDeletes.contains(FD->getCanonicalDecl());
1332913329
}
1333013330

13331+
void ASTContext::addOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
13332+
FunctionDecl *OperatorDelete,
13333+
OperatorDeleteKind K) const {
13334+
switch (K) {
13335+
case OperatorDeleteKind::Regular:
13336+
OperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] = OperatorDelete;
13337+
break;
13338+
case OperatorDeleteKind::GlobalRegular:
13339+
GlobalOperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] =
13340+
OperatorDelete;
13341+
break;
13342+
case OperatorDeleteKind::Array:
13343+
ArrayOperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] =
13344+
OperatorDelete;
13345+
break;
13346+
case OperatorDeleteKind::ArrayGlobal:
13347+
GlobalArrayOperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] =
13348+
OperatorDelete;
13349+
break;
13350+
default:
13351+
llvm_unreachable("Unknown operator delete kind");
13352+
}
13353+
}
13354+
13355+
bool ASTContext::dtorHasOperatorDelete(const CXXDestructorDecl *Dtor,
13356+
OperatorDeleteKind K) const {
13357+
switch (K) {
13358+
case OperatorDeleteKind::Regular:
13359+
return OperatorDeletesForVirtualDtor.contains(Dtor->getCanonicalDecl());
13360+
case OperatorDeleteKind::GlobalRegular:
13361+
return GlobalOperatorDeletesForVirtualDtor.contains(
13362+
Dtor->getCanonicalDecl());
13363+
case OperatorDeleteKind::Array:
13364+
return ArrayOperatorDeletesForVirtualDtor.contains(
13365+
Dtor->getCanonicalDecl());
13366+
case OperatorDeleteKind::ArrayGlobal:
13367+
return GlobalArrayOperatorDeletesForVirtualDtor.contains(
13368+
Dtor->getCanonicalDecl());
13369+
default:
13370+
llvm_unreachable("Unknown operator delete kind");
13371+
}
13372+
return false;
13373+
}
13374+
13375+
FunctionDecl *ASTContext::getOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
13376+
OperatorDeleteKind K) const {
13377+
const CXXDestructorDecl *Canon = Dtor->getCanonicalDecl();
13378+
switch (K) {
13379+
case OperatorDeleteKind::Regular:
13380+
if (OperatorDeletesForVirtualDtor.contains(Canon))
13381+
return OperatorDeletesForVirtualDtor[Canon];
13382+
return nullptr;
13383+
case OperatorDeleteKind::GlobalRegular:
13384+
if (GlobalOperatorDeletesForVirtualDtor.contains(Canon))
13385+
return GlobalOperatorDeletesForVirtualDtor[Canon];
13386+
return nullptr;
13387+
case OperatorDeleteKind::Array:
13388+
if (ArrayOperatorDeletesForVirtualDtor.contains(Canon))
13389+
return ArrayOperatorDeletesForVirtualDtor[Canon];
13390+
return nullptr;
13391+
case OperatorDeleteKind::ArrayGlobal:
13392+
if (GlobalArrayOperatorDeletesForVirtualDtor.contains(Canon))
13393+
return GlobalArrayOperatorDeletesForVirtualDtor[Canon];
13394+
return nullptr;
13395+
default:
13396+
llvm_unreachable("Unknown operator delete kind");
13397+
}
13398+
return nullptr;
13399+
}
13400+
1333113401
MangleNumberingContext &
1333213402
ASTContext::getManglingNumberContext(const DeclContext *DC) {
1333313403
assert(LangOpts.CPlusPlus); // We don't need mangling numbers for plain C.

0 commit comments

Comments
 (0)