[Sema] Check that the destructor for each element of class type is

accessible from the context where aggregate initialization occurs.

rdar://problem/38168772

Differential Revision: https://reviews.llvm.org/D45898

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@341629 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Akira Hatanaka 2018-09-07 02:38:01 +00:00
parent f0bf1c6f8d
commit b6dc2efd41
3 changed files with 160 additions and 1 deletions

View File

@ -1818,6 +1818,30 @@ bool InitListChecker::CheckFlexibleArrayInit(const InitializedEntity &Entity,
return FlexArrayDiag != diag::ext_flexible_array_init;
}
/// Check if the type of a class element has an accessible destructor.
///
/// Aggregate initialization requires a class element's destructor be
/// accessible per 11.6.1 [dcl.init.aggr]:
///
/// The destructor for each element of class type is potentially invoked
/// (15.4 [class.dtor]) from the context where the aggregate initialization
/// occurs.
static bool hasAccessibleDestructor(QualType ElementType, SourceLocation Loc,
Sema &SemaRef) {
auto *CXXRD = ElementType->getAsCXXRecordDecl();
if (!CXXRD)
return false;
CXXDestructorDecl *Destructor = SemaRef.LookupDestructor(CXXRD);
SemaRef.CheckDestructorAccess(Loc, Destructor,
SemaRef.PDiag(diag::err_access_dtor_temp)
<< ElementType);
SemaRef.MarkFunctionReferenced(Loc, Destructor);
if (SemaRef.DiagnoseUseOfDecl(Destructor, Loc))
return true;
return false;
}
void InitListChecker::CheckStructUnionTypes(
const InitializedEntity &Entity, InitListExpr *IList, QualType DeclType,
CXXRecordDecl::base_class_range Bases, RecordDecl::field_iterator Field,
@ -1838,6 +1862,15 @@ void InitListChecker::CheckStructUnionTypes(
if (DeclType->isUnionType() && IList->getNumInits() == 0) {
RecordDecl *RD = DeclType->getAs<RecordType>()->getDecl();
if (!VerifyOnly)
for (FieldDecl *FD : RD->fields()) {
QualType ET = SemaRef.Context.getBaseElementType(FD->getType());
if (hasAccessibleDestructor(ET, IList->getEndLoc(), SemaRef)) {
hadError = true;
return;
}
}
// If there's a default initializer, use it.
if (isa<CXXRecordDecl>(RD) && cast<CXXRecordDecl>(RD)->hasInClassInitializer()) {
if (VerifyOnly)
@ -1874,13 +1907,13 @@ void InitListChecker::CheckStructUnionTypes(
// If we have any base classes, they are initialized prior to the fields.
for (auto &Base : Bases) {
Expr *Init = Index < IList->getNumInits() ? IList->getInit(Index) : nullptr;
SourceLocation InitLoc = Init ? Init->getBeginLoc() : IList->getEndLoc();
// Designated inits always initialize fields, so if we see one, all
// remaining base classes have no explicit initializer.
if (Init && isa<DesignatedInitExpr>(Init))
Init = nullptr;
SourceLocation InitLoc = Init ? Init->getBeginLoc() : IList->getEndLoc();
InitializedEntity BaseEntity = InitializedEntity::InitializeBase(
SemaRef.Context, &Base, false, &Entity);
if (Init) {
@ -1890,6 +1923,12 @@ void InitListChecker::CheckStructUnionTypes(
} else if (VerifyOnly) {
CheckEmptyInitializable(BaseEntity, InitLoc);
}
if (!VerifyOnly)
if (hasAccessibleDestructor(Base.getType(), InitLoc, SemaRef)) {
hadError = true;
return;
}
}
// If structDecl is a forward declaration, this loop won't do
@ -1900,9 +1939,11 @@ void InitListChecker::CheckStructUnionTypes(
RecordDecl::field_iterator FieldEnd = RD->field_end();
bool CheckForMissingFields =
!IList->isIdiomaticZeroInitializer(SemaRef.getLangOpts());
bool HasDesignatedInit = false;
while (Index < IList->getNumInits()) {
Expr *Init = IList->getInit(Index);
SourceLocation InitLoc = Init->getBeginLoc();
if (DesignatedInitExpr *DIE = dyn_cast<DesignatedInitExpr>(Init)) {
// If we're not the subobject that matches up with the '{' for
@ -1911,6 +1952,8 @@ void InitListChecker::CheckStructUnionTypes(
if (!SubobjectIsDesignatorContext)
return;
HasDesignatedInit = true;
// Handle this designated initializer. Field will be updated to
// the next field that we'll be initializing.
if (CheckDesignatedInitializer(Entity, IList, DIE, 0,
@ -1918,6 +1961,17 @@ void InitListChecker::CheckStructUnionTypes(
StructuredList, StructuredIndex,
true, TopLevelObject))
hadError = true;
else if (!VerifyOnly) {
// Find the field named by the designated initializer.
RecordDecl::field_iterator F = RD->field_begin();
while (std::next(F) != Field)
++F;
QualType ET = SemaRef.Context.getBaseElementType(F->getType());
if (hasAccessibleDestructor(ET, InitLoc, SemaRef)) {
hadError = true;
return;
}
}
InitializedSomething = true;
@ -1960,6 +2014,14 @@ void InitListChecker::CheckStructUnionTypes(
continue;
}
if (!VerifyOnly) {
QualType ET = SemaRef.Context.getBaseElementType(Field->getType());
if (hasAccessibleDestructor(ET, InitLoc, SemaRef)) {
hadError = true;
return;
}
}
InitializedEntity MemberEntity =
InitializedEntity::InitializeMember(*Field, &Entity);
CheckSubElementType(MemberEntity, IList, Field->getType(), Index,
@ -2002,6 +2064,21 @@ void InitListChecker::CheckStructUnionTypes(
}
}
// Check that the types of the remaining fields have accessible destructors.
if (!VerifyOnly) {
// If the initializer expression has a designated initializer, check the
// elements for which a designated initializer is not provided too.
RecordDecl::field_iterator I = HasDesignatedInit ? RD->field_begin()
: Field;
for (RecordDecl::field_iterator E = RD->field_end(); I != E; ++I) {
QualType ET = SemaRef.Context.getBaseElementType(I->getType());
if (hasAccessibleDestructor(ET, IList->getEndLoc(), SemaRef)) {
hadError = true;
return;
}
}
}
if (Field == FieldEnd || !Field->getType()->isIncompleteArrayType() ||
Index >= IList->getNumInits())
return;

View File

@ -0,0 +1,34 @@
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.13.0 -std=c++1z -fobjc-arc -fobjc-exceptions -fcxx-exceptions -fexceptions -emit-llvm -o - %s | FileCheck %s
// CHECK: %[[V0:.*]] = type opaque
// CHECK: %[[STRUCT_CLASS1:.*]] = type { %[[V0]]* }
@interface Class0;
@end
struct Class1 {
Class0 *f;
};
struct Container {
Class1 a;
bool b;
};
bool getBool() {
return false;
}
Class0 *g;
// CHECK: define {{.*}} @_Z4testv()
// CHECK: invoke zeroext i1 @_Z7getBoolv()
// CHECK: landingpad { i8*, i32 }
// CHECK: call void @_ZN6Class1D1Ev(%[[STRUCT_CLASS1]]* %{{.*}})
// CHECK: br label
// CHECK: define linkonce_odr void @_ZN6Class1D1Ev(
Container test() {
return {{g}, getBool()};
}

View File

@ -186,3 +186,51 @@ namespace HugeArraysUseArrayFiller {
// amount of time.
struct A { int n; int arr[1000 * 1000 * 1000]; } a = {1, {2}};
}
namespace ElementDestructor {
// The destructor for each element of class type is potentially invoked
// (15.4 [class.dtor]) from the context where the aggregate initialization
// occurs. Produce a diagnostic if an element's destructor isn't accessible.
class X { int f; ~X(); }; // expected-note {{implicitly declared private here}}
struct Y { X x; };
void test0() {
auto *y = new Y {}; // expected-error {{temporary of type 'ElementDestructor::X' has private destructor}}
}
struct S0 { int f; ~S0() = delete; }; // expected-note 3 {{'~S0' has been explicitly marked deleted here}}
struct S1 { S0 s0; int f; };
S1 test1() {
auto *t = new S1 { .f = 1 }; // expected-error {{attempt to use a deleted function}}
return {2}; // expected-error {{attempt to use a deleted function}}
}
// Check if the type of an array element has a destructor.
struct S2 { S0 a[4]; };
void test2() {
auto *t = new S2 {1,2,3,4}; // expected-error {{attempt to use a deleted function}}
}
#if __cplusplus >= 201703L
namespace BaseDestructor {
struct S0 { int f; ~S0() = delete; }; // expected-note {{'~S0' has been explicitly marked deleted here}}
// Check destructor of base class.
struct S3 : S0 {};
void test3() {
S3 s3 = {1}; // expected-error {{attempt to use a deleted function}}
}
}
#endif
// A's destructor doesn't have to be accessible from the context of C's
// initialization.
struct A { friend struct B; private: ~A(); };
struct B { B(); A a; };
struct C { B b; };
C c = { B() };
}