r341629 - [Sema] Check that the destructor for each element of class type is
Akira Hatanaka via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 6 19:38:01 PDT 2018
Author: ahatanak
Date: Thu Sep 6 19:38:01 2018
New Revision: 341629
URL: http://llvm.org/viewvc/llvm-project?rev=341629&view=rev
Log:
[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
Added:
cfe/trunk/test/CodeGenObjCXX/arc-list-init-destruct.mm
Modified:
cfe/trunk/lib/Sema/SemaInit.cpp
cfe/trunk/test/SemaCXX/aggregate-initialization.cpp
Modified: cfe/trunk/lib/Sema/SemaInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=341629&r1=341628&r2=341629&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaInit.cpp (original)
+++ cfe/trunk/lib/Sema/SemaInit.cpp Thu Sep 6 19:38:01 2018
@@ -1818,6 +1818,30 @@ bool InitListChecker::CheckFlexibleArray
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::CheckStructUnionTy
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::CheckStructUnionTy
// 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::CheckStructUnionTy
} 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::CheckStructUnionTy
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::CheckStructUnionTy
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::CheckStructUnionTy
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::CheckStructUnionTy
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::CheckStructUnionTy
}
}
+ // 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;
Added: cfe/trunk/test/CodeGenObjCXX/arc-list-init-destruct.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjCXX/arc-list-init-destruct.mm?rev=341629&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenObjCXX/arc-list-init-destruct.mm (added)
+++ cfe/trunk/test/CodeGenObjCXX/arc-list-init-destruct.mm Thu Sep 6 19:38:01 2018
@@ -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]]* }
+
+ at interface Class0;
+ at 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()};
+}
Modified: cfe/trunk/test/SemaCXX/aggregate-initialization.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/aggregate-initialization.cpp?rev=341629&r1=341628&r2=341629&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/aggregate-initialization.cpp (original)
+++ cfe/trunk/test/SemaCXX/aggregate-initialization.cpp Thu Sep 6 19:38:01 2018
@@ -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() };
+}
More information about the cfe-commits
mailing list