mirror of https://github.com/microsoft/clang.git
[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:
parent
f0bf1c6f8d
commit
b6dc2efd41
|
@ -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;
|
||||
|
|
|
@ -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()};
|
||||
}
|
|
@ -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() };
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue