Skip to content

Commit

Permalink
[Sema] Check that the destructor for each element of class type is
Browse files Browse the repository at this point in the history
accessible from the context where aggregate initialization occurs.

rdar://problem/38168772

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

llvm-svn: 341629
  • Loading branch information
ahatanaka committed Sep 7, 2018
1 parent b9dd61c commit 2ccb319
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 1 deletion.
79 changes: 78 additions & 1 deletion clang/lib/Sema/SemaInit.cpp
Expand Up @@ -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,
Expand All @@ -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)
Expand Down Expand Up @@ -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) {
Expand All @@ -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
Expand All @@ -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
Expand All @@ -1911,13 +1952,26 @@ 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,
DeclType, &Field, nullptr, Index,
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;

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
34 changes: 34 additions & 0 deletions clang/test/CodeGenObjCXX/arc-list-init-destruct.mm
@@ -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()};
}
48 changes: 48 additions & 0 deletions clang/test/SemaCXX/aggregate-initialization.cpp
Expand Up @@ -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() };
}

0 comments on commit 2ccb319

Please sign in to comment.