Skip to content

Commit 921bd20

Browse files
committed
Ensure that we delete destructors in the right cases. Specifically:
- variant members with nontrivial destructors make the containing class's destructor deleted - check for a virtual destructor after checking for overridden methods in the base class(es) - check for an inaccessible operator delete for a class with a virtual destructor. Do not try to call an anonymous union field's destructor from the destructor of the containing class. llvm-svn: 151483
1 parent af3c209 commit 921bd20

File tree

4 files changed

+203
-105
lines changed

4 files changed

+203
-105
lines changed

clang/lib/CodeGen/CGClass.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,10 @@ void CodeGenFunction::EnterDtorCleanups(const CXXDestructorDecl *DD,
10621062
QualType::DestructionKind dtorKind = type.isDestructedType();
10631063
if (!dtorKind) continue;
10641064

1065+
// Anonymous union members do not have their destructors called.
1066+
const RecordType *RT = type->getAsUnionType();
1067+
if (RT && RT->getDecl()->isAnonymousStructOrUnion()) continue;
1068+
10651069
CleanupKind cleanupKind = getCleanupKind(dtorKind);
10661070
EHStack.pushCleanup<DestroyField>(cleanupKind, field,
10671071
getDestroyer(dtorKind),

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 86 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -3293,6 +3293,9 @@ Sema::MarkBaseAndMemberDestructorsReferenced(SourceLocation Location,
32933293
continue;
32943294
if (FieldClassDecl->hasIrrelevantDestructor())
32953295
continue;
3296+
// The destructor for an implicit anonymous union member is never invoked.
3297+
if (FieldClassDecl->isUnion() && FieldClassDecl->isAnonymousStructOrUnion())
3298+
continue;
32963299

32973300
CXXDestructorDecl *Dtor = LookupDestructor(FieldClassDecl);
32983301
assert(Dtor && "No dtor found for FieldClassDecl!");
@@ -4372,31 +4375,33 @@ struct SpecialMemberDeletionInfo {
43724375
TQ & Qualifiers::Volatile);
43734376
}
43744377

4378+
bool shouldDeleteForClassSubobject(CXXRecordDecl *Class, FieldDecl *Field);
4379+
43754380
bool shouldDeleteForBase(CXXRecordDecl *BaseDecl, bool IsVirtualBase);
43764381
bool shouldDeleteForField(FieldDecl *FD);
43774382
bool shouldDeleteForAllConstMembers();
43784383
};
43794384
}
43804385

4381-
/// Check whether we should delete a special member function due to the class
4382-
/// having a particular direct or virtual base class.
4383-
bool SpecialMemberDeletionInfo::shouldDeleteForBase(CXXRecordDecl *BaseDecl,
4384-
bool IsVirtualBase) {
4385-
// C++11 [class.copy]p23:
4386-
// -- for the move assignment operator, any direct or indirect virtual
4387-
// base class.
4388-
if (CSM == Sema::CXXMoveAssignment && IsVirtualBase)
4389-
return true;
4390-
4386+
/// Check whether we should delete a special member function due to having a
4387+
/// direct or virtual base class or static data member of class type M.
4388+
bool SpecialMemberDeletionInfo::shouldDeleteForClassSubobject(
4389+
CXXRecordDecl *Class, FieldDecl *Field) {
43914390
// C++11 [class.ctor]p5, C++11 [class.copy]p11, C++11 [class.dtor]p5:
43924391
// -- any direct or virtual base class [...] has a type with a destructor
43934392
// that is deleted or inaccessible
43944393
if (!IsAssignment) {
4395-
CXXDestructorDecl *BaseDtor = S.LookupDestructor(BaseDecl);
4396-
if (BaseDtor->isDeleted())
4394+
CXXDestructorDecl *Dtor = S.LookupDestructor(Class);
4395+
if (Dtor->isDeleted())
43974396
return true;
4398-
if (S.CheckDestructorAccess(Loc, BaseDtor, S.PDiag())
4399-
!= Sema::AR_accessible)
4397+
if (S.CheckDestructorAccess(Loc, Dtor, S.PDiag()) != Sema::AR_accessible)
4398+
return true;
4399+
4400+
// C++11 [class.dtor]p5:
4401+
// -- X is a union-like class that has a variant member with a non-trivial
4402+
// destructor
4403+
if (CSM == Sema::CXXDestructor && Field && Field->getParent()->isUnion() &&
4404+
!Dtor->isTrivial())
44004405
return true;
44014406
}
44024407

@@ -4410,50 +4415,58 @@ bool SpecialMemberDeletionInfo::shouldDeleteForBase(CXXRecordDecl *BaseDecl,
44104415
// overload resolution, as applied to B's corresponding special member,
44114416
// results in an ambiguity or a function that is deleted or inaccessible
44124417
// from the defaulted special member
4418+
// FIXME: in-class initializers should be handled here
44134419
if (CSM != Sema::CXXDestructor) {
4414-
Sema::SpecialMemberOverloadResult *SMOR = lookupIn(BaseDecl);
4420+
Sema::SpecialMemberOverloadResult *SMOR = lookupIn(Class);
44154421
if (!SMOR->hasSuccess())
44164422
return true;
44174423

4418-
CXXMethodDecl *BaseMember = SMOR->getMethod();
4424+
CXXMethodDecl *Member = SMOR->getMethod();
4425+
// A member of a union must have a trivial corresponding special member.
4426+
if (Field && Field->getParent()->isUnion() && !Member->isTrivial())
4427+
return true;
4428+
44194429
if (IsConstructor) {
4420-
CXXConstructorDecl *BaseCtor = cast<CXXConstructorDecl>(BaseMember);
4421-
if (S.CheckConstructorAccess(Loc, BaseCtor, BaseCtor->getAccess(),
4422-
S.PDiag()) != Sema::AR_accessible)
4430+
CXXConstructorDecl *Ctor = cast<CXXConstructorDecl>(Member);
4431+
if (S.CheckConstructorAccess(Loc, Ctor, Ctor->getAccess(), S.PDiag())
4432+
!= Sema::AR_accessible)
44234433
return true;
44244434

44254435
// -- for the move constructor, a [...] direct or virtual base class with
44264436
// a type that does not have a move constructor and is not trivially
44274437
// copyable.
4428-
if (IsMove && !BaseCtor->isMoveConstructor() &&
4429-
!BaseDecl->isTriviallyCopyable())
4438+
if (IsMove && !Ctor->isMoveConstructor() && !Class->isTriviallyCopyable())
44304439
return true;
44314440
} else {
44324441
assert(IsAssignment && "unexpected kind of special member");
4433-
if (S.CheckDirectMemberAccess(Loc, BaseMember, S.PDiag())
4442+
if (S.CheckDirectMemberAccess(Loc, Member, S.PDiag())
44344443
!= Sema::AR_accessible)
44354444
return true;
44364445

44374446
// -- for the move assignment operator, a direct base class with a type
44384447
// that does not have a move assignment operator and is not trivially
44394448
// copyable.
4440-
if (IsMove && !BaseMember->isMoveAssignmentOperator() &&
4441-
!BaseDecl->isTriviallyCopyable())
4449+
if (IsMove && !Member->isMoveAssignmentOperator() &&
4450+
!Class->isTriviallyCopyable())
44424451
return true;
44434452
}
44444453
}
44454454

4446-
// C++11 [class.dtor]p5:
4447-
// -- for a virtual destructor, lookup of the non-array deallocation function
4448-
// results in an ambiguity or in a function that is deleted or inaccessible
4449-
if (CSM == Sema::CXXDestructor && MD->isVirtual()) {
4450-
FunctionDecl *OperatorDelete = 0;
4451-
DeclarationName Name =
4452-
S.Context.DeclarationNames.getCXXOperatorName(OO_Delete);
4453-
if (S.FindDeallocationFunction(Loc, MD->getParent(), Name,
4454-
OperatorDelete, false))
4455-
return true;
4456-
}
4455+
return false;
4456+
}
4457+
4458+
/// Check whether we should delete a special member function due to the class
4459+
/// having a particular direct or virtual base class.
4460+
bool SpecialMemberDeletionInfo::shouldDeleteForBase(CXXRecordDecl *BaseDecl,
4461+
bool IsVirtualBase) {
4462+
// C++11 [class.copy]p23:
4463+
// -- for the move assignment operator, any direct or indirect virtual
4464+
// base class.
4465+
if (CSM == Sema::CXXMoveAssignment && IsVirtualBase)
4466+
return true;
4467+
4468+
if (shouldDeleteForClassSubobject(BaseDecl, 0))
4469+
return true;
44574470

44584471
return false;
44594472
}
@@ -4500,58 +4513,14 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
45004513
UE = FieldRecord->field_end();
45014514
UI != UE; ++UI) {
45024515
QualType UnionFieldType = S.Context.getBaseElementType(UI->getType());
4503-
CXXRecordDecl *UnionFieldRecord =
4504-
UnionFieldType->getAsCXXRecordDecl();
45054516

45064517
if (!UnionFieldType.isConstQualified())
45074518
AllVariantFieldsAreConst = false;
45084519

4509-
if (UnionFieldRecord) {
4510-
// FIXME: Checking for accessibility and validity of this
4511-
// destructor is technically going beyond the
4512-
// standard, but this is believed to be a defect.
4513-
if (!IsAssignment) {
4514-
CXXDestructorDecl *FieldDtor = S.LookupDestructor(UnionFieldRecord);
4515-
if (FieldDtor->isDeleted())
4516-
return true;
4517-
if (S.CheckDestructorAccess(Loc, FieldDtor, S.PDiag()) !=
4518-
Sema::AR_accessible)
4519-
return true;
4520-
if (!FieldDtor->isTrivial())
4521-
return true;
4522-
}
4523-
4524-
// FIXME: in-class initializers should be handled here
4525-
if (CSM != Sema::CXXDestructor) {
4526-
Sema::SpecialMemberOverloadResult *SMOR =
4527-
lookupIn(UnionFieldRecord);
4528-
// FIXME: Checking for accessibility and validity of this
4529-
// corresponding member is technically going beyond the
4530-
// standard, but this is believed to be a defect.
4531-
if (!SMOR->hasSuccess())
4532-
return true;
4533-
4534-
CXXMethodDecl *FieldMember = SMOR->getMethod();
4535-
// A member of a union must have a trivial corresponding
4536-
// special member.
4537-
if (!FieldMember->isTrivial())
4538-
return true;
4539-
4540-
if (IsConstructor) {
4541-
CXXConstructorDecl *FieldCtor =
4542-
cast<CXXConstructorDecl>(FieldMember);
4543-
if (S.CheckConstructorAccess(Loc, FieldCtor,
4544-
FieldCtor->getAccess(),
4545-
S.PDiag()) != Sema::AR_accessible)
4546-
return true;
4547-
} else {
4548-
assert(IsAssignment && "unexpected kind of special member");
4549-
if (S.CheckDirectMemberAccess(Loc, FieldMember, S.PDiag())
4550-
!= Sema::AR_accessible)
4551-
return true;
4552-
}
4553-
}
4554-
}
4520+
CXXRecordDecl *UnionFieldRecord = UnionFieldType->getAsCXXRecordDecl();
4521+
if (UnionFieldRecord &&
4522+
shouldDeleteForClassSubobject(UnionFieldRecord, *UI))
4523+
return true;
45554524
}
45564525

45574526
// At least one member in each anonymous union must be non-const
@@ -4564,9 +4533,9 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
45644533
return false;
45654534
}
45664535

4567-
// Unless we're doing assignment, the field's destructor must be
4568-
// accessible and not deleted.
4569-
if (!IsAssignment) {
4536+
// When checking a constructor, the field's destructor must be accessible
4537+
// and not deleted.
4538+
if (IsConstructor) {
45704539
CXXDestructorDecl *FieldDtor = S.LookupDestructor(FieldRecord);
45714540
if (FieldDtor->isDeleted())
45724541
return true;
@@ -4578,13 +4547,18 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
45784547
// Check that the corresponding member of the field is accessible,
45794548
// unique, and non-deleted. We don't do this if it has an explicit
45804549
// initialization when default-constructing.
4581-
if (CSM != Sema::CXXDestructor &&
4582-
!(CSM == Sema::CXXDefaultConstructor && FD->hasInClassInitializer())) {
4550+
if (!(CSM == Sema::CXXDefaultConstructor && FD->hasInClassInitializer())) {
45834551
Sema::SpecialMemberOverloadResult *SMOR = lookupIn(FieldRecord);
45844552
if (!SMOR->hasSuccess())
45854553
return true;
45864554

45874555
CXXMethodDecl *FieldMember = SMOR->getMethod();
4556+
4557+
// We need the corresponding member of a union to be trivial so that
4558+
// we can safely process all members simultaneously.
4559+
if (inUnion() && !FieldMember->isTrivial())
4560+
return true;
4561+
45884562
if (IsConstructor) {
45894563
CXXConstructorDecl *FieldCtor = cast<CXXConstructorDecl>(FieldMember);
45904564
if (S.CheckConstructorAccess(Loc, FieldCtor, FieldCtor->getAccess(),
@@ -4597,6 +4571,13 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
45974571
if (IsMove && !FieldCtor->isMoveConstructor() &&
45984572
!FieldRecord->isTriviallyCopyable())
45994573
return true;
4574+
} else if (CSM == Sema::CXXDestructor) {
4575+
CXXDestructorDecl *FieldDtor = S.LookupDestructor(FieldRecord);
4576+
if (FieldDtor->isDeleted())
4577+
return true;
4578+
if (S.CheckDestructorAccess(Loc, FieldDtor, S.PDiag()) !=
4579+
Sema::AR_accessible)
4580+
return true;
46004581
} else {
46014582
assert(IsAssignment && "unexpected kind of special member");
46024583
if (S.CheckDirectMemberAccess(Loc, FieldMember, S.PDiag())
@@ -4610,14 +4591,6 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
46104591
!FieldRecord->isTriviallyCopyable())
46114592
return true;
46124593
}
4613-
4614-
// We need the corresponding member of a union to be trivial so that
4615-
// we can safely copy them all simultaneously.
4616-
// FIXME: Note that performing the check here (where we rely on the lack
4617-
// of an in-class initializer) is technically ill-formed. However, this
4618-
// seems most obviously to be a bug in the standard.
4619-
if (inUnion() && !FieldMember->isTrivial())
4620-
return true;
46214594
}
46224595
} else if (CSM == Sema::CXXDefaultConstructor && !inUnion() &&
46234596
FieldType.isConstQualified() && !FD->hasInClassInitializer()) {
@@ -4652,6 +4625,8 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM) {
46524625
if (!LangOpts.CPlusPlus0x || RD->isInvalidDecl())
46534626
return false;
46544627

4628+
// FIXME: Provide the ability to diagnose why a special member was deleted.
4629+
46554630
// C++11 [expr.lambda.prim]p19:
46564631
// The closure type associated with a lambda-expression has a
46574632
// deleted (8.4.3) default constructor and a deleted copy
@@ -4660,6 +4635,18 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM) {
46604635
(CSM == CXXDefaultConstructor || CSM == CXXCopyAssignment))
46614636
return true;
46624637

4638+
// C++11 [class.dtor]p5:
4639+
// -- for a virtual destructor, lookup of the non-array deallocation function
4640+
// results in an ambiguity or in a function that is deleted or inaccessible
4641+
if (CSM == Sema::CXXDestructor && MD->isVirtual()) {
4642+
FunctionDecl *OperatorDelete = 0;
4643+
DeclarationName Name =
4644+
Context.DeclarationNames.getCXXOperatorName(OO_Delete);
4645+
if (FindDeallocationFunction(MD->getLocation(), MD->getParent(), Name,
4646+
OperatorDelete, false))
4647+
return true;
4648+
}
4649+
46634650
// For an anonymous struct or union, the copy and assignment special members
46644651
// will never be used, so skip the check. For an anonymous union declared at
46654652
// namespace scope, the constructor and destructor are used.
@@ -4672,8 +4659,6 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM) {
46724659

46734660
SpecialMemberDeletionInfo SMI(*this, MD, CSM);
46744661

4675-
// FIXME: We should put some diagnostic logic right into this function.
4676-
46774662
for (CXXRecordDecl::base_class_iterator BI = RD->bases_begin(),
46784663
BE = RD->bases_end(); BI != BE; ++BI)
46794664
if (!BI->isVirtual() &&
@@ -7216,11 +7201,11 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) {
72167201
// This could be uniqued if it ever proves significant.
72177202
Destructor->setTypeSourceInfo(Context.getTrivialTypeSourceInfo(Ty));
72187203

7204+
AddOverriddenMethods(ClassDecl, Destructor);
7205+
72197206
if (ShouldDeleteSpecialMember(Destructor, CXXDestructor))
72207207
Destructor->setDeletedAsWritten();
7221-
7222-
AddOverriddenMethods(ClassDecl, Destructor);
7223-
7208+
72247209
return Destructor;
72257210
}
72267211

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,9 +1666,13 @@ bool Sema::FindAllocationOverload(SourceLocation StartLoc, SourceRange Range,
16661666

16671667
Args[i] = Result.takeAs<Expr>();
16681668
}
1669+
16691670
Operator = FnDecl;
1670-
CheckAllocationAccess(StartLoc, Range, R.getNamingClass(), Best->FoundDecl,
1671-
Diagnose);
1671+
1672+
if (CheckAllocationAccess(StartLoc, Range, R.getNamingClass(),
1673+
Best->FoundDecl, Diagnose) == AR_inaccessible)
1674+
return true;
1675+
16721676
return false;
16731677
}
16741678

@@ -1895,8 +1899,10 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
18951899
return true;
18961900
}
18971901

1898-
CheckAllocationAccess(StartLoc, SourceRange(), Found.getNamingClass(),
1899-
Matches[0], Diagnose);
1902+
if (CheckAllocationAccess(StartLoc, SourceRange(), Found.getNamingClass(),
1903+
Matches[0], Diagnose) == AR_inaccessible)
1904+
return true;
1905+
19001906
return false;
19011907

19021908
// We found multiple suitable operators; complain about the ambiguity.

0 commit comments

Comments
 (0)