From b6dc2efd4106d7d1a048b670bc1b69220dd4c5fd Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Fri, 7 Sep 2018 02:38:01 +0000 Subject: [PATCH] [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 --- lib/Sema/SemaInit.cpp | 79 +++++++++++++++++++- test/CodeGenObjCXX/arc-list-init-destruct.mm | 34 +++++++++ test/SemaCXX/aggregate-initialization.cpp | 48 ++++++++++++ 3 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 test/CodeGenObjCXX/arc-list-init-destruct.mm diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 634554e096..18a7aab1f5 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -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()->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(RD) && cast(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(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(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; diff --git a/test/CodeGenObjCXX/arc-list-init-destruct.mm b/test/CodeGenObjCXX/arc-list-init-destruct.mm new file mode 100644 index 0000000000..09a66458c2 --- /dev/null +++ b/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()}; +} diff --git a/test/SemaCXX/aggregate-initialization.cpp b/test/SemaCXX/aggregate-initialization.cpp index 0dfb61333e..bb03c6fb5c 100644 --- a/test/SemaCXX/aggregate-initialization.cpp +++ b/test/SemaCXX/aggregate-initialization.cpp @@ -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() }; +} -- 2.40.0