Skip to content

Commit d714a6c

Browse files
authored
Reland [MS][clang] Add support for vector deleting destructors (#170337)
This reverts commit 54a4da9. 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). Fixes #19772
1 parent a318c50 commit d714a6c

File tree

61 files changed

+1364
-170
lines changed

Some content is hidden

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

61 files changed

+1364
-170
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ Potentially Breaking Changes
8686
options-related code has been moved out of the Driver into a separate library.
8787
- The ``clangFrontend`` library no longer depends on ``clangDriver``, which may
8888
break downstream projects that relied on this transitive dependency.
89+
- Clang now supports MSVC vector deleting destructors when targeting Windows.
90+
This means that vtables of classes with virtual destructors will contain a
91+
pointer to vector deleting destructor (instead of scalar deleting destructor)
92+
which in fact is a different symbol with different name and linkage. This
93+
may cause runtime failures if two binaries using the same class defining a
94+
virtual destructor are compiled with different versions of clang.
8995

9096
C/C++ Language Potentially Breaking Changes
9197
-------------------------------------------
@@ -654,6 +660,8 @@ Windows Support
654660
- clang-cl now supports /arch:AVX10.1 and /arch:AVX10.2.
655661
- clang-cl now supports /vlen, /vlen=256 and /vlen=512.
656662

663+
- Clang now supports MSVC vector deleting destructors (GH19772).
664+
657665
LoongArch Support
658666
^^^^^^^^^^^^^^^^^
659667
- 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,21 @@ 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+
385+
/// To remember which types did require a vector deleting dtor.
386+
llvm::DenseSet<const CXXRecordDecl *> RequireVectorDeletingDtor;
387+
373388
/// The next string literal "version" to allocate during constant evaluation.
374389
/// This is used to distinguish between repeated evaluations of the same
375390
/// string literal.
@@ -3489,6 +3504,18 @@ class ASTContext : public RefCountedBase<ASTContext> {
34893504
bool IsTypeAware);
34903505
bool isTypeAwareOperatorNewOrDelete(const FunctionDecl *FD) const;
34913506

3507+
enum OperatorDeleteKind { Regular, GlobalRegular, Array, ArrayGlobal };
3508+
3509+
void addOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
3510+
FunctionDecl *OperatorDelete,
3511+
OperatorDeleteKind K) const;
3512+
FunctionDecl *getOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
3513+
OperatorDeleteKind K) const;
3514+
bool dtorHasOperatorDelete(const CXXDestructorDecl *Dtor,
3515+
OperatorDeleteKind K) const;
3516+
void setClassNeedsVectorDeletingDestructor(const CXXRecordDecl *RD);
3517+
bool classNeedsVectorDeletingDestructor(const CXXRecordDecl *RD);
3518+
34923519
/// Retrieve the context for computing mangling numbers in the given
34933520
/// DeclContext.
34943521
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
@@ -1798,6 +1798,11 @@ class TargetInfo : public TransferrableTargetInfo,
17981798
/// destructor body.
17991799
virtual bool callGlobalDeleteInDeletingDtor(const LangOptions &) const;
18001800

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

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8586,7 +8586,8 @@ class Sema final : public SemaBase {
85868586
FunctionDecl *FindDeallocationFunctionForDestructor(SourceLocation StartLoc,
85878587
CXXRecordDecl *RD,
85888588
bool Diagnose,
8589-
bool LookForGlobal);
8589+
bool LookForGlobal,
8590+
DeclarationName Name);
85908591

85918592
/// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in:
85928593
/// @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: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13348,6 +13348,91 @@ bool ASTContext::isTypeAwareOperatorNewOrDelete(const FunctionDecl *FD) const {
1334813348
return TypeAwareOperatorNewAndDeletes.contains(FD->getCanonicalDecl());
1334913349
}
1335013350

13351+
void ASTContext::addOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
13352+
FunctionDecl *OperatorDelete,
13353+
OperatorDeleteKind K) const {
13354+
switch (K) {
13355+
case OperatorDeleteKind::Regular:
13356+
OperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] = OperatorDelete;
13357+
break;
13358+
case OperatorDeleteKind::GlobalRegular:
13359+
GlobalOperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] =
13360+
OperatorDelete;
13361+
break;
13362+
case OperatorDeleteKind::Array:
13363+
ArrayOperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] =
13364+
OperatorDelete;
13365+
break;
13366+
case OperatorDeleteKind::ArrayGlobal:
13367+
GlobalArrayOperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] =
13368+
OperatorDelete;
13369+
break;
13370+
}
13371+
}
13372+
13373+
bool ASTContext::dtorHasOperatorDelete(const CXXDestructorDecl *Dtor,
13374+
OperatorDeleteKind K) const {
13375+
switch (K) {
13376+
case OperatorDeleteKind::Regular:
13377+
return OperatorDeletesForVirtualDtor.contains(Dtor->getCanonicalDecl());
13378+
case OperatorDeleteKind::GlobalRegular:
13379+
return GlobalOperatorDeletesForVirtualDtor.contains(
13380+
Dtor->getCanonicalDecl());
13381+
case OperatorDeleteKind::Array:
13382+
return ArrayOperatorDeletesForVirtualDtor.contains(
13383+
Dtor->getCanonicalDecl());
13384+
case OperatorDeleteKind::ArrayGlobal:
13385+
return GlobalArrayOperatorDeletesForVirtualDtor.contains(
13386+
Dtor->getCanonicalDecl());
13387+
}
13388+
return false;
13389+
}
13390+
13391+
FunctionDecl *
13392+
ASTContext::getOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
13393+
OperatorDeleteKind K) const {
13394+
const CXXDestructorDecl *Canon = Dtor->getCanonicalDecl();
13395+
switch (K) {
13396+
case OperatorDeleteKind::Regular:
13397+
if (OperatorDeletesForVirtualDtor.contains(Canon))
13398+
return OperatorDeletesForVirtualDtor[Canon];
13399+
return nullptr;
13400+
case OperatorDeleteKind::GlobalRegular:
13401+
if (GlobalOperatorDeletesForVirtualDtor.contains(Canon))
13402+
return GlobalOperatorDeletesForVirtualDtor[Canon];
13403+
return nullptr;
13404+
case OperatorDeleteKind::Array:
13405+
if (ArrayOperatorDeletesForVirtualDtor.contains(Canon))
13406+
return ArrayOperatorDeletesForVirtualDtor[Canon];
13407+
return nullptr;
13408+
case OperatorDeleteKind::ArrayGlobal:
13409+
if (GlobalArrayOperatorDeletesForVirtualDtor.contains(Canon))
13410+
return GlobalArrayOperatorDeletesForVirtualDtor[Canon];
13411+
return nullptr;
13412+
}
13413+
return nullptr;
13414+
}
13415+
13416+
bool ASTContext::classNeedsVectorDeletingDestructor(const CXXRecordDecl *RD) {
13417+
if (!getTargetInfo().emitVectorDeletingDtors(getLangOpts()))
13418+
return false;
13419+
CXXDestructorDecl *Dtor = RD->getDestructor();
13420+
// The compiler can't know if new[]/delete[] will be used outside of the DLL,
13421+
// so just force vector deleting destructor emission if dllexport is present.
13422+
// This matches MSVC behavior.
13423+
if (Dtor && Dtor->isVirtual() && Dtor->hasAttr<DLLExportAttr>())
13424+
return true;
13425+
13426+
return RequireVectorDeletingDtor.count(RD);
13427+
}
13428+
13429+
void ASTContext::setClassNeedsVectorDeletingDestructor(
13430+
const CXXRecordDecl *RD) {
13431+
if (!getTargetInfo().emitVectorDeletingDtors(getLangOpts()))
13432+
return;
13433+
RequireVectorDeletingDtor.insert(RD);
13434+
}
13435+
1335113436
MangleNumberingContext &
1335213437
ASTContext::getManglingNumberContext(const DeclContext *DC) {
1335313438
assert(LangOpts.CPlusPlus); // We don't need mangling numbers for plain C.

0 commit comments

Comments
 (0)