Skip to content

Commit ec395e1

Browse files
momo5502frederik-h
authored andcommitted
[Sema] Instantiate destructors for initialized members (llvm#128866)
Initializing fields, that are part of an anonymous union, in a constructor, requires their destructors to be instantiated. In general, initialized members within non-delegating constructors, need their destructor instantiated. This fixes llvm#93251
1 parent a33810f commit ec395e1

File tree

5 files changed

+177
-108
lines changed

5 files changed

+177
-108
lines changed

clang/docs/ReleaseNotes.rst

+1
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ Bug Fixes to C++ Support
309309
not in the last position.
310310
- Clang now correctly parses ``if constexpr`` expressions in immediate function context. (#GH123524)
311311
- Fixed an assertion failure affecting code that uses C++23 "deducing this". (#GH130272)
312+
- Clang now properly instantiates destructors for initialized members within non-delegating constructors. (#GH93251)
312313

313314
Bug Fixes to AST Handling
314315
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Sema/Sema.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -5451,7 +5451,8 @@ class Sema final : public SemaBase {
54515451
/// destructor is referenced.
54525452
void MarkVirtualBaseDestructorsReferenced(
54535453
SourceLocation Location, CXXRecordDecl *ClassDecl,
5454-
llvm::SmallPtrSetImpl<const RecordType *> *DirectVirtualBases = nullptr);
5454+
llvm::SmallPtrSetImpl<const CXXRecordDecl *> *DirectVirtualBases =
5455+
nullptr);
54555456

54565457
/// Do semantic checks to allow the complete destructor variant to be emitted
54575458
/// when the destructor is defined in another translation unit. In the Itanium

clang/lib/Sema/SemaDeclCXX.cpp

+121-104
Original file line numberDiff line numberDiff line change
@@ -5289,6 +5289,100 @@ Sema::SetDelegatingInitializer(CXXConstructorDecl *Constructor,
52895289
return false;
52905290
}
52915291

5292+
static CXXDestructorDecl *LookupDestructorIfRelevant(Sema &S,
5293+
CXXRecordDecl *Class) {
5294+
if (Class->isInvalidDecl())
5295+
return nullptr;
5296+
if (Class->hasIrrelevantDestructor())
5297+
return nullptr;
5298+
5299+
// Dtor might still be missing, e.g because it's invalid.
5300+
return S.LookupDestructor(Class);
5301+
}
5302+
5303+
static void MarkFieldDestructorReferenced(Sema &S, SourceLocation Location,
5304+
FieldDecl *Field) {
5305+
if (Field->isInvalidDecl())
5306+
return;
5307+
5308+
// Don't destroy incomplete or zero-length arrays.
5309+
if (isIncompleteOrZeroLengthArrayType(S.Context, Field->getType()))
5310+
return;
5311+
5312+
QualType FieldType = S.Context.getBaseElementType(Field->getType());
5313+
5314+
auto *FieldClassDecl = FieldType->getAsCXXRecordDecl();
5315+
if (!FieldClassDecl)
5316+
return;
5317+
5318+
// The destructor for an implicit anonymous union member is never invoked.
5319+
if (FieldClassDecl->isUnion() && FieldClassDecl->isAnonymousStructOrUnion())
5320+
return;
5321+
5322+
auto *Dtor = LookupDestructorIfRelevant(S, FieldClassDecl);
5323+
if (!Dtor)
5324+
return;
5325+
5326+
S.CheckDestructorAccess(Field->getLocation(), Dtor,
5327+
S.PDiag(diag::err_access_dtor_field)
5328+
<< Field->getDeclName() << FieldType);
5329+
5330+
S.MarkFunctionReferenced(Location, Dtor);
5331+
S.DiagnoseUseOfDecl(Dtor, Location);
5332+
}
5333+
5334+
static void MarkBaseDestructorsReferenced(Sema &S, SourceLocation Location,
5335+
CXXRecordDecl *ClassDecl) {
5336+
if (ClassDecl->isDependentContext())
5337+
return;
5338+
5339+
// We only potentially invoke the destructors of potentially constructed
5340+
// subobjects.
5341+
bool VisitVirtualBases = !ClassDecl->isAbstract();
5342+
5343+
// If the destructor exists and has already been marked used in the MS ABI,
5344+
// then virtual base destructors have already been checked and marked used.
5345+
// Skip checking them again to avoid duplicate diagnostics.
5346+
if (S.Context.getTargetInfo().getCXXABI().isMicrosoft()) {
5347+
CXXDestructorDecl *Dtor = ClassDecl->getDestructor();
5348+
if (Dtor && Dtor->isUsed())
5349+
VisitVirtualBases = false;
5350+
}
5351+
5352+
llvm::SmallPtrSet<const CXXRecordDecl *, 8> DirectVirtualBases;
5353+
5354+
// Bases.
5355+
for (const auto &Base : ClassDecl->bases()) {
5356+
auto *BaseClassDecl = Base.getType()->getAsCXXRecordDecl();
5357+
if (!BaseClassDecl)
5358+
continue;
5359+
5360+
// Remember direct virtual bases.
5361+
if (Base.isVirtual()) {
5362+
if (!VisitVirtualBases)
5363+
continue;
5364+
DirectVirtualBases.insert(BaseClassDecl);
5365+
}
5366+
5367+
auto *Dtor = LookupDestructorIfRelevant(S, BaseClassDecl);
5368+
if (!Dtor)
5369+
continue;
5370+
5371+
// FIXME: caret should be on the start of the class name
5372+
S.CheckDestructorAccess(Base.getBeginLoc(), Dtor,
5373+
S.PDiag(diag::err_access_dtor_base)
5374+
<< Base.getType() << Base.getSourceRange(),
5375+
S.Context.getTypeDeclType(ClassDecl));
5376+
5377+
S.MarkFunctionReferenced(Location, Dtor);
5378+
S.DiagnoseUseOfDecl(Dtor, Location);
5379+
}
5380+
5381+
if (VisitVirtualBases)
5382+
S.MarkVirtualBaseDestructorsReferenced(Location, ClassDecl,
5383+
&DirectVirtualBases);
5384+
}
5385+
52925386
bool Sema::SetCtorInitializers(CXXConstructorDecl *Constructor, bool AnyErrors,
52935387
ArrayRef<CXXCtorInitializer *> Initializers) {
52945388
if (Constructor->isDependentContext()) {
@@ -5456,10 +5550,24 @@ bool Sema::SetCtorInitializers(CXXConstructorDecl *Constructor, bool AnyErrors,
54565550
NumInitializers * sizeof(CXXCtorInitializer*));
54575551
Constructor->setCtorInitializers(baseOrMemberInitializers);
54585552

5553+
SourceLocation Location = Constructor->getLocation();
5554+
54595555
// Constructors implicitly reference the base and member
54605556
// destructors.
5461-
MarkBaseAndMemberDestructorsReferenced(Constructor->getLocation(),
5462-
Constructor->getParent());
5557+
5558+
for (CXXCtorInitializer *Initializer : Info.AllToInit) {
5559+
FieldDecl *Field = Initializer->getAnyMember();
5560+
if (!Field)
5561+
continue;
5562+
5563+
// C++ [class.base.init]p12:
5564+
// In a non-delegating constructor, the destructor for each
5565+
// potentially constructed subobject of class type is potentially
5566+
// invoked.
5567+
MarkFieldDestructorReferenced(*this, Location, Field);
5568+
}
5569+
5570+
MarkBaseDestructorsReferenced(*this, Location, Constructor->getParent());
54635571
}
54645572

54655573
return HadError;
@@ -5764,9 +5872,8 @@ void Sema::ActOnMemInitializers(Decl *ConstructorDecl,
57645872
DiagnoseUninitializedFields(*this, Constructor);
57655873
}
57665874

5767-
void
5768-
Sema::MarkBaseAndMemberDestructorsReferenced(SourceLocation Location,
5769-
CXXRecordDecl *ClassDecl) {
5875+
void Sema::MarkBaseAndMemberDestructorsReferenced(SourceLocation Location,
5876+
CXXRecordDecl *ClassDecl) {
57705877
// Ignore dependent contexts. Also ignore unions, since their members never
57715878
// have destructors implicitly called.
57725879
if (ClassDecl->isDependentContext() || ClassDecl->isUnion())
@@ -5779,119 +5886,29 @@ Sema::MarkBaseAndMemberDestructorsReferenced(SourceLocation Location,
57795886

57805887
// Non-static data members.
57815888
for (auto *Field : ClassDecl->fields()) {
5782-
if (Field->isInvalidDecl())
5783-
continue;
5784-
5785-
// Don't destroy incomplete or zero-length arrays.
5786-
if (isIncompleteOrZeroLengthArrayType(Context, Field->getType()))
5787-
continue;
5788-
5789-
QualType FieldType = Context.getBaseElementType(Field->getType());
5790-
5791-
const RecordType* RT = FieldType->getAs<RecordType>();
5792-
if (!RT)
5793-
continue;
5794-
5795-
CXXRecordDecl *FieldClassDecl = cast<CXXRecordDecl>(RT->getDecl());
5796-
if (FieldClassDecl->isInvalidDecl())
5797-
continue;
5798-
if (FieldClassDecl->hasIrrelevantDestructor())
5799-
continue;
5800-
// The destructor for an implicit anonymous union member is never invoked.
5801-
if (FieldClassDecl->isUnion() && FieldClassDecl->isAnonymousStructOrUnion())
5802-
continue;
5803-
5804-
CXXDestructorDecl *Dtor = LookupDestructor(FieldClassDecl);
5805-
// Dtor might still be missing, e.g because it's invalid.
5806-
if (!Dtor)
5807-
continue;
5808-
CheckDestructorAccess(Field->getLocation(), Dtor,
5809-
PDiag(diag::err_access_dtor_field)
5810-
<< Field->getDeclName()
5811-
<< FieldType);
5812-
5813-
MarkFunctionReferenced(Location, Dtor);
5814-
DiagnoseUseOfDecl(Dtor, Location);
5815-
}
5816-
5817-
// We only potentially invoke the destructors of potentially constructed
5818-
// subobjects.
5819-
bool VisitVirtualBases = !ClassDecl->isAbstract();
5820-
5821-
// If the destructor exists and has already been marked used in the MS ABI,
5822-
// then virtual base destructors have already been checked and marked used.
5823-
// Skip checking them again to avoid duplicate diagnostics.
5824-
if (Context.getTargetInfo().getCXXABI().isMicrosoft()) {
5825-
CXXDestructorDecl *Dtor = ClassDecl->getDestructor();
5826-
if (Dtor && Dtor->isUsed())
5827-
VisitVirtualBases = false;
5889+
MarkFieldDestructorReferenced(*this, Location, Field);
58285890
}
58295891

5830-
llvm::SmallPtrSet<const RecordType *, 8> DirectVirtualBases;
5831-
5832-
// Bases.
5833-
for (const auto &Base : ClassDecl->bases()) {
5834-
const RecordType *RT = Base.getType()->getAs<RecordType>();
5835-
if (!RT)
5836-
continue;
5837-
5838-
// Remember direct virtual bases.
5839-
if (Base.isVirtual()) {
5840-
if (!VisitVirtualBases)
5841-
continue;
5842-
DirectVirtualBases.insert(RT);
5843-
}
5844-
5845-
CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(RT->getDecl());
5846-
// If our base class is invalid, we probably can't get its dtor anyway.
5847-
if (BaseClassDecl->isInvalidDecl())
5848-
continue;
5849-
if (BaseClassDecl->hasIrrelevantDestructor())
5850-
continue;
5851-
5852-
CXXDestructorDecl *Dtor = LookupDestructor(BaseClassDecl);
5853-
// Dtor might still be missing, e.g because it's invalid.
5854-
if (!Dtor)
5855-
continue;
5856-
5857-
// FIXME: caret should be on the start of the class name
5858-
CheckDestructorAccess(Base.getBeginLoc(), Dtor,
5859-
PDiag(diag::err_access_dtor_base)
5860-
<< Base.getType() << Base.getSourceRange(),
5861-
Context.getTypeDeclType(ClassDecl));
5862-
5863-
MarkFunctionReferenced(Location, Dtor);
5864-
DiagnoseUseOfDecl(Dtor, Location);
5865-
}
5866-
5867-
if (VisitVirtualBases)
5868-
MarkVirtualBaseDestructorsReferenced(Location, ClassDecl,
5869-
&DirectVirtualBases);
5892+
MarkBaseDestructorsReferenced(*this, Location, ClassDecl);
58705893
}
58715894

58725895
void Sema::MarkVirtualBaseDestructorsReferenced(
58735896
SourceLocation Location, CXXRecordDecl *ClassDecl,
5874-
llvm::SmallPtrSetImpl<const RecordType *> *DirectVirtualBases) {
5897+
llvm::SmallPtrSetImpl<const CXXRecordDecl *> *DirectVirtualBases) {
58755898
// Virtual bases.
58765899
for (const auto &VBase : ClassDecl->vbases()) {
5877-
// Bases are always records in a well-formed non-dependent class.
5878-
const RecordType *RT = VBase.getType()->castAs<RecordType>();
5879-
5880-
// Ignore already visited direct virtual bases.
5881-
if (DirectVirtualBases && DirectVirtualBases->count(RT))
5900+
auto *BaseClassDecl = VBase.getType()->getAsCXXRecordDecl();
5901+
if (!BaseClassDecl)
58825902
continue;
58835903

5884-
CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(RT->getDecl());
5885-
// If our base class is invalid, we probably can't get its dtor anyway.
5886-
if (BaseClassDecl->isInvalidDecl())
5887-
continue;
5888-
if (BaseClassDecl->hasIrrelevantDestructor())
5904+
// Ignore already visited direct virtual bases.
5905+
if (DirectVirtualBases && DirectVirtualBases->count(BaseClassDecl))
58895906
continue;
58905907

5891-
CXXDestructorDecl *Dtor = LookupDestructor(BaseClassDecl);
5892-
// Dtor might still be missing, e.g because it's invalid.
5908+
auto *Dtor = LookupDestructorIfRelevant(*this, BaseClassDecl);
58935909
if (!Dtor)
58945910
continue;
5911+
58955912
if (CheckDestructorAccess(
58965913
ClassDecl->getLocation(), Dtor,
58975914
PDiag(diag::err_access_dtor_vbase)

clang/test/SemaCXX/cxx0x-nontrivial-union.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,18 @@ namespace disabled_dtor {
5151
union disable_dtor {
5252
T val;
5353
template<typename...U>
54-
disable_dtor(U &&...u) : val(forward<U>(u)...) {}
54+
disable_dtor(U &&...u) : val(forward<U>(u)...) {} // expected-error {{attempt to use a deleted function}}
5555
~disable_dtor() {}
5656
};
5757

5858
struct deleted_dtor {
5959
deleted_dtor(int n, char c) : n(n), c(c) {}
6060
int n;
6161
char c;
62-
~deleted_dtor() = delete;
62+
~deleted_dtor() = delete; // expected-note {{'~deleted_dtor' has been explicitly marked deleted here}}
6363
};
6464

65-
disable_dtor<deleted_dtor> dd(4, 'x');
65+
disable_dtor<deleted_dtor> dd(4, 'x'); // expected-note {{in instantiation of function template specialization 'disabled_dtor::disable_dtor<disabled_dtor::deleted_dtor>::disable_dtor<int, char>' requested here}}
6666
}
6767

6868
namespace optional {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
2+
3+
namespace t1 {
4+
template <class T> struct VSX {
5+
~VSX() { static_assert(sizeof(T) != 4, ""); } // expected-error {{static assertion failed due to requirement 'sizeof(int) != 4':}} \
6+
// expected-note {{expression evaluates to '4 != 4'}}
7+
};
8+
struct VS {
9+
union {
10+
VSX<int> _Tail;
11+
};
12+
~VS() { }
13+
VS(short);
14+
VS();
15+
};
16+
VS::VS() : VS(0) { } // delegating constructors should not produce errors
17+
VS::VS(short) : _Tail() { } // expected-note {{in instantiation of member function 't1::VSX<int>::~VSX' requested here}}
18+
}
19+
20+
21+
namespace t2 {
22+
template <class T> struct VSX {
23+
~VSX() { static_assert(sizeof(T) != 4, ""); } // expected-error {{static assertion failed due to requirement 'sizeof(int) != 4':}} \
24+
// expected-note {{expression evaluates to '4 != 4'}}
25+
};
26+
struct VS {
27+
union {
28+
struct {
29+
VSX<int> _Tail;
30+
};
31+
};
32+
~VS() { }
33+
VS(short);
34+
};
35+
VS::VS(short) : _Tail() { } // expected-note {{in instantiation of member function 't2::VSX<int>::~VSX' requested here}}
36+
}
37+
38+
39+
namespace t3 {
40+
template <class T> struct VSX {
41+
~VSX() { static_assert(sizeof(T) != 4, ""); } // expected-error {{static assertion failed due to requirement 'sizeof(int) != 4':}} \
42+
// expected-note {{expression evaluates to '4 != 4'}}
43+
};
44+
union VS {
45+
VSX<int> _Tail;
46+
~VS() { }
47+
VS(short);
48+
};
49+
VS::VS(short) : _Tail() { } // expected-note {{in instantiation of member function 't3::VSX<int>::~VSX' requested here}}
50+
}

0 commit comments

Comments
 (0)