Skip to content

Commit 4d10c11

Browse files
authored
Reland [MS][clang] Add support for vector deleting destructors (#165598)
MSVC supports an 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 a pointer to the vector deleting destructor for classes with virtual destructors, so not having this extension implemented causes clang to generate code that is not compatible with the code generated by MSVC, because clang always puts a 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 #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 #133950 (comment) #134265 Fixes #19772
1 parent 295a3f7 commit 4d10c11

Some content is hidden

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

57 files changed

+1260
-164
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ Potentially Breaking Changes
8282
- Downstream projects that previously linked only against ``clangDriver`` may
8383
now (also) need to link against the new ``clangOptions`` library, since
8484
options-related code has been moved out of the Driver into a separate library.
85+
- Clang now supports MSVC vector deleting destructors when targeting Windows.
86+
This means that vtables of classes with virtual destructors will contain a
87+
pointer to vector deleting destructor (instead of scalar deleting destructor)
88+
which in fact is a different symbol with different name and linkage. This
89+
may cause runtime failures if two binaries using the same class defining a
90+
virtual destructor are compiled with different versions of clang.
8591

8692
C/C++ Language Potentially Breaking Changes
8793
-------------------------------------------
@@ -588,6 +594,8 @@ Android Support
588594
Windows Support
589595
^^^^^^^^^^^^^^^
590596

597+
- Clang now supports MSVC vector deleting destructors (GH19772).
598+
591599
LoongArch Support
592600
^^^^^^^^^^^^^^^^^
593601
- Enable linker relaxation by default for loongarch64.

clang/include/clang/AST/ASTContext.h

Lines changed: 22 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,16 @@ class ASTContext : public RefCountedBase<ASTContext> {
34733485
bool IsTypeAware);
34743486
bool isTypeAwareOperatorNewOrDelete(const FunctionDecl *FD) const;
34753487

3488+
enum OperatorDeleteKind { Regular, GlobalRegular, Array, ArrayGlobal };
3489+
3490+
void addOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
3491+
FunctionDecl *OperatorDelete,
3492+
OperatorDeleteKind K) const;
3493+
FunctionDecl *getOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
3494+
OperatorDeleteKind K) const;
3495+
bool dtorHasOperatorDelete(const CXXDestructorDecl *Dtor,
3496+
OperatorDeleteKind K) const;
3497+
34763498
/// Retrieve the context for computing mangling numbers in the given
34773499
/// DeclContext.
34783500
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: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,12 @@ enum CXXCtorType {
3232

3333
/// C++ destructor types.
3434
enum CXXDtorType {
35-
Dtor_Deleting, ///< Deleting dtor
36-
Dtor_Complete, ///< Complete object dtor
37-
Dtor_Base, ///< Base object dtor
38-
Dtor_Comdat, ///< The COMDAT used for dtors
39-
Dtor_Unified, ///< GCC-style unified dtor
35+
Dtor_Deleting, ///< Deleting dtor
36+
Dtor_Complete, ///< Complete object dtor
37+
Dtor_Base, ///< Base object dtor
38+
Dtor_Comdat, ///< The COMDAT used for dtors
39+
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
@@ -1796,6 +1796,11 @@ class TargetInfo : public TransferrableTargetInfo,
17961796
/// destructor body.
17971797
virtual bool callGlobalDeleteInDeletingDtor(const LangOptions &) const;
17981798

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

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8570,7 +8570,8 @@ class Sema final : public SemaBase {
85708570
FunctionDecl *FindDeallocationFunctionForDestructor(SourceLocation StartLoc,
85718571
CXXRecordDecl *RD,
85728572
bool Diagnose,
8573-
bool LookForGlobal);
8573+
bool LookForGlobal,
8574+
DeclarationName Name);
85748575

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

clang/include/clang/Serialization/ASTWriter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,10 @@ class ASTWriter : public ASTDeserializationListener,
955955
Expr *ThisArg) override;
956956
void ResolvedOperatorGlobDelete(const CXXDestructorDecl *DD,
957957
const FunctionDecl *Delete) override;
958+
void ResolvedOperatorArrayDelete(const CXXDestructorDecl *DD,
959+
const FunctionDecl *Delete) override;
960+
void ResolvedOperatorGlobArrayDelete(const CXXDestructorDecl *DD,
961+
const FunctionDecl *Delete) override;
958962
void CompletedImplicitDefinition(const FunctionDecl *D) override;
959963
void InstantiationRequested(const ValueDecl *D) override;
960964
void VariableDefinitionInstantiated(const VarDecl *D) override;

clang/lib/AST/ASTContext.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13333,6 +13333,71 @@ bool ASTContext::isTypeAwareOperatorNewOrDelete(const FunctionDecl *FD) const {
1333313333
return TypeAwareOperatorNewAndDeletes.contains(FD->getCanonicalDecl());
1333413334
}
1333513335

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

0 commit comments

Comments
 (0)