From 44d861c1f7099970b74af95998dc36e99eeb1f08 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Sat, 2 Feb 2019 02:23:40 +0000 Subject: [PATCH] [Sema][ObjC] Allow declaring ObjC pointer members with non-trivial ownership qualifications in C++ unions under ARC. An ObjC pointer member with non-trivial ownership qualifications causes all of the defaulted special functions of the enclosing union to be defined as deleted, except when the member has an in-class initializer, the default constructor isn't defined as deleted. rdar://problem/34213306 Differential Revision: https://reviews.llvm.org/D57438 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@352949 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 6 +- lib/AST/DeclCXX.cpp | 11 ++ lib/Sema/SemaDecl.cpp | 3 +- lib/Sema/SemaDeclCXX.cpp | 40 +++++- test/SemaObjCXX/arc-0x.mm | 159 ++++++++++++++++++++- test/SemaObjCXX/objc-weak.mm | 2 +- 6 files changed, 213 insertions(+), 8 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index cf4b71a2af..0896be7edf 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -4649,13 +4649,15 @@ def note_deleted_special_member_class_subobject : Note< "copy assignment operator of|move assignment operator of|destructor of|" "constructor inherited by}0 " "%1 is implicitly deleted because " - "%select{base class %3|%select{||||variant }4field %3}2 has " + "%select{base class %3|%select{||||variant }4field %3}2 " + "%select{has " "%select{no|a deleted|multiple|an inaccessible|a non-trivial}4 " "%select{%select{default constructor|copy constructor|move constructor|copy " "assignment operator|move assignment operator|destructor|" "%select{default|corresponding|default|default|default}4 constructor}0|" "destructor}5" - "%select{||s||}4">; + "%select{||s||}4" + "|is an ObjC pointer}6">; def note_deleted_default_ctor_uninit_field : Note< "%select{default constructor of|constructor inherited by}0 " "%1 is implicitly deleted because field %2 of " diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 76e1cf5496..22ec400571 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -991,6 +991,17 @@ void CXXRecordDecl::addedMember(Decl *D) { setArgPassingRestrictions(RecordDecl::APK_CanNeverPassInRegs); Data.HasIrrelevantDestructor = false; + + if (isUnion()) { + data().DefaultedCopyConstructorIsDeleted = true; + data().DefaultedMoveConstructorIsDeleted = true; + data().DefaultedMoveAssignmentIsDeleted = true; + data().DefaultedDestructorIsDeleted = true; + data().NeedOverloadResolutionForCopyConstructor = true; + data().NeedOverloadResolutionForMoveConstructor = true; + data().NeedOverloadResolutionForMoveAssignment = true; + data().NeedOverloadResolutionForDestructor = true; + } } else if (!Context.getLangOpts().ObjCAutoRefCount) { setHasObjectMember(true); } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 5b6fd880ca..c8a69c4d53 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -15923,7 +15923,8 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, QualType T = Context.getObjCObjectPointerType(FD->getType()); FD->setType(T); } else if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && - Record && !ObjCFieldLifetimeErrReported && Record->isUnion()) { + Record && !ObjCFieldLifetimeErrReported && Record->isUnion() && + !getLangOpts().CPlusPlus) { // It's an error in ARC or Weak if a field has lifetime. // We don't want to report this in a system header, though, // so we just make the field unavailable. diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index de87f985d6..c54e700f51 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -6891,6 +6891,8 @@ struct SpecialMemberDeletionInfo return ICI ? Sema::CXXInvalid : CSM; } + bool shouldDeleteForVariantObjCPtrMember(FieldDecl *FD, QualType FieldType); + bool visitBase(CXXBaseSpecifier *Base) { return shouldDeleteForBase(Base); } bool visitField(FieldDecl *Field) { return shouldDeleteForField(Field); } @@ -6962,13 +6964,14 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall( S.Diag(Field->getLocation(), diag::note_deleted_special_member_class_subobject) << getEffectiveCSM() << MD->getParent() << /*IsField*/true - << Field << DiagKind << IsDtorCallInCtor; + << Field << DiagKind << IsDtorCallInCtor << /*IsObjCPtr*/false; } else { CXXBaseSpecifier *Base = Subobj.get(); S.Diag(Base->getBeginLoc(), diag::note_deleted_special_member_class_subobject) << getEffectiveCSM() << MD->getParent() << /*IsField*/ false - << Base->getType() << DiagKind << IsDtorCallInCtor; + << Base->getType() << DiagKind << IsDtorCallInCtor + << /*IsObjCPtr*/false; } if (DiagKind == 1) @@ -7020,6 +7023,30 @@ bool SpecialMemberDeletionInfo::shouldDeleteForClassSubobject( return false; } +bool SpecialMemberDeletionInfo::shouldDeleteForVariantObjCPtrMember( + FieldDecl *FD, QualType FieldType) { + // The defaulted special functions are defined as deleted if this is a variant + // member with a non-trivial ownership type, e.g., ObjC __strong or __weak + // type under ARC. + if (!FieldType.hasNonTrivialObjCLifetime()) + return false; + + // Don't make the defaulted default constructor defined as deleted if the + // member has an in-class initializer. + if (CSM == Sema::CXXDefaultConstructor && FD->hasInClassInitializer()) + return false; + + if (Diagnose) { + auto *ParentClass = cast(FD->getParent()); + S.Diag(FD->getLocation(), + diag::note_deleted_special_member_class_subobject) + << getEffectiveCSM() << ParentClass << /*IsField*/true + << FD << 4 << /*IsDtorCallInCtor*/false << /*IsObjCPtr*/true; + } + + return true; +} + /// Check whether we should delete a special member function due to the class /// having a particular direct or virtual base class. bool SpecialMemberDeletionInfo::shouldDeleteForBase(CXXBaseSpecifier *Base) { @@ -7040,7 +7067,8 @@ bool SpecialMemberDeletionInfo::shouldDeleteForBase(CXXBaseSpecifier *Base) { S.Diag(Base->getBeginLoc(), diag::note_deleted_special_member_class_subobject) << getEffectiveCSM() << MD->getParent() << /*IsField*/ false - << Base->getType() << /*Deleted*/ 1 << /*IsDtorCallInCtor*/ false; + << Base->getType() << /*Deleted*/ 1 << /*IsDtorCallInCtor*/ false + << /*IsObjCPtr*/false; S.NoteDeletedFunction(BaseCtor); } return BaseCtor->isDeleted(); @@ -7054,6 +7082,9 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) { QualType FieldType = S.Context.getBaseElementType(FD->getType()); CXXRecordDecl *FieldRecord = FieldType->getAsCXXRecordDecl(); + if (inUnion() && shouldDeleteForVariantObjCPtrMember(FD, FieldType)) + return true; + if (CSM == Sema::CXXDefaultConstructor) { // For a default constructor, all references must be initialized in-class // and, if a union, it must have a non-const member. @@ -7115,6 +7146,9 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) { for (auto *UI : FieldRecord->fields()) { QualType UnionFieldType = S.Context.getBaseElementType(UI->getType()); + if (shouldDeleteForVariantObjCPtrMember(&*UI, UnionFieldType)) + return true; + if (!UnionFieldType.isConstQualified()) AllVariantFieldsAreConst = false; diff --git a/test/SemaObjCXX/arc-0x.mm b/test/SemaObjCXX/arc-0x.mm index d0f3284a18..052c99ac13 100644 --- a/test/SemaObjCXX/arc-0x.mm +++ b/test/SemaObjCXX/arc-0x.mm @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fobjc-arc -verify -fblocks -fobjc-exceptions %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fobjc-arc -fobjc-runtime-has-weak -fobjc-weak -verify -fblocks -fobjc-exceptions %s // "Move" semantics, trivial version. void move_it(__strong id &&from) { @@ -111,3 +111,160 @@ namespace test_err_arc_array_param_no_ownership { func(^(A *a[]){}); // expected-error{{must explicitly describe intended ownership of an object array parameter}} } } + +namespace test_union { + // Implicitly-declared special functions of a union are deleted by default if + // ARC is enabled and the union has an ObjC pointer field. + union U0 { + id f0; // expected-note 6 {{'U0' is implicitly deleted because variant field 'f0' is an ObjC pointer}} + }; + + union U1 { + __weak id f0; // expected-note 12 {{'U1' is implicitly deleted because variant field 'f0' is an ObjC pointer}} + U1() = default; // expected-warning {{explicitly defaulted default constructor is implicitly deleted}} expected-note {{explicitly defaulted function was implicitly deleted here}} + ~U1() = default; // expected-warning {{explicitly defaulted destructor is implicitly deleted}} expected-note {{explicitly defaulted function was implicitly deleted here}} + U1(const U1 &) = default; // expected-warning {{explicitly defaulted copy constructor is implicitly deleted}} expected-note 2 {{explicitly defaulted function was implicitly deleted here}} + U1(U1 &&) = default; // expected-warning {{explicitly defaulted move constructor is implicitly deleted}} + U1 & operator=(const U1 &) = default; // expected-warning {{explicitly defaulted copy assignment operator is implicitly deleted}} expected-note 2 {{explicitly defaulted function was implicitly deleted here}} + U1 & operator=(U1 &&) = default; // expected-warning {{explicitly defaulted move assignment operator is implicitly deleted}} + }; + + id getStrong(); + + // If the ObjC pointer field of a union has a default member initializer, the + // implicitly-declared default constructor of the union is not deleted by + // default. + union U2 { + id f0 = getStrong(); // expected-note 4 {{'U2' is implicitly deleted because variant field 'f0' is an ObjC pointer}} + ~U2(); + }; + + // It's fine if the user has explicitly defined the special functions. + union U3 { + id f0; + U3(); + ~U3(); + U3(const U3 &); + U3(U3 &&); + U3 & operator=(const U3 &); + U3 & operator=(U3 &&); + }; + + // ObjC pointer fields in anonymous union fields delete the defaulted special + // functions of the containing class. + struct S0 { + union { + id f0; // expected-note 6 {{'' is implicitly deleted because variant field 'f0' is an ObjC pointer}} + char f1; + }; + }; + + struct S1 { + union { + union { // expected-note 2 {{'S1' is implicitly deleted because variant field '' has a non-trivial}} expected-note 4 {{'S1' is implicitly deleted because field '' has a deleted}} + id f0; // expected-note 2 {{'' is implicitly deleted because variant field 'f0' is an ObjC pointer}} + char f1; + }; + int f2; + }; + }; + + struct S2 { + union { + // FIXME: the note should say 'f0' is causing the special functions to be deleted. + struct { // expected-note 6 {{'S2' is implicitly deleted because variant field '' has a non-trivial}} + id f0; + int f1; + }; + int f2; + }; + int f3; + }; + + U0 *x0; + U1 *x1; + U2 *x2; + U3 *x3; + S0 *x4; + S1 *x5; + S2 *x6; + + static union { // expected-error {{call to implicitly-deleted default constructor of}} + id g0; // expected-note {{default constructor of '' is implicitly deleted because variant field 'g0' is an ObjC pointer}} + }; + + static union { // expected-error {{call to implicitly-deleted default constructor of}} + union { // expected-note {{default constructor of '' is implicitly deleted because field '' has a deleted default constructor}} + union { // expected-note {{default constructor of '' is implicitly deleted because field '' has a deleted default constructor}} + __weak id g1; // expected-note {{default constructor of '' is implicitly deleted because variant field 'g1' is an ObjC pointer}} + int g2; + }; + int g3; + }; + int g4; + }; + + void testDefaultConstructor() { + U0 t0; // expected-error {{call to implicitly-deleted default constructor}} + U1 t1; // expected-error {{call to implicitly-deleted default constructor}} + U2 t2; + U3 t3; + S0 t4; // expected-error {{call to implicitly-deleted default constructor}} + S1 t5; // expected-error {{call to implicitly-deleted default constructor}} + S2 t6; // expected-error {{call to implicitly-deleted default constructor}} + } + + void testDestructor(U0 *u0, U1 *u1, U2 *u2, U3 *u3, S0 *s0, S1 *s1, S2 *s2) { + delete u0; // expected-error {{attempt to use a deleted function}} + delete u1; // expected-error {{attempt to use a deleted function}} + delete u2; + delete u3; + delete s0; // expected-error {{attempt to use a deleted function}} + delete s1; // expected-error {{attempt to use a deleted function}} + delete s2; // expected-error {{attempt to use a deleted function}} + } + + void testCopyConstructor(U0 *u0, U1 *u1, U2 *u2, U3 *u3, S0 *s0, S1 *s1, S2 *s2) { + U0 t0(*u0); // expected-error {{call to implicitly-deleted copy constructor}} + U1 t1(*u1); // expected-error {{call to implicitly-deleted copy constructor}} + U2 t2(*u2); // expected-error {{call to implicitly-deleted copy constructor}} + U3 t3(*u3); + S0 t4(*s0); // expected-error {{call to implicitly-deleted copy constructor}} + S1 t5(*s1); // expected-error {{call to implicitly-deleted copy constructor}} + S2 t6(*s2); // expected-error {{call to implicitly-deleted copy constructor}} + } + + void testCopyAssignment(U0 *u0, U1 *u1, U2 *u2, U3 *u3, S0 *s0, S1 *s1, S2 *s2) { + *x0 = *u0; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x1 = *u1; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x2 = *u2; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x3 = *u3; + *x4 = *s0; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x5 = *s1; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x6 = *s2; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + } + + // The diagnostics below refer to the deleted copy constructors and assignment + // operators since defaulted move constructors and assignment operators that are + // defined as deleted are ignored by overload resolution. + + void testMoveConstructor(U0 *u0, U1 *u1, U2 *u2, U3 *u3, S0 *s0, S1 *s1, S2 *s2) { + U0 t0(static_cast(*u0)); // expected-error {{call to implicitly-deleted copy constructor}} + U1 t1(static_cast(*u1)); // expected-error {{call to implicitly-deleted copy constructor}} + U2 t2(static_cast(*u2)); // expected-error {{call to implicitly-deleted copy constructor}} + U3 t3(static_cast(*u3)); + S0 t4(static_cast(*s0)); // expected-error {{call to implicitly-deleted copy constructor}} + S1 t5(static_cast(*s1)); // expected-error {{call to implicitly-deleted copy constructor}} + S2 t6(static_cast(*s2)); // expected-error {{call to implicitly-deleted copy constructor}} + } + + void testMoveAssignment(U0 *u0, U1 *u1, U2 *u2, U3 *u3, S0 *s0, S1 *s1, S2 *s2) { + *x0 = static_cast(*u0); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x1 = static_cast(*u1); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x2 = static_cast(*u2); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x3 = static_cast(*u3); + *x4 = static_cast(*s0); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x5 = static_cast(*s1); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x6 = static_cast(*s2); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + } +} diff --git a/test/SemaObjCXX/objc-weak.mm b/test/SemaObjCXX/objc-weak.mm index 93c6af1aa3..2671dc104c 100644 --- a/test/SemaObjCXX/objc-weak.mm +++ b/test/SemaObjCXX/objc-weak.mm @@ -13,7 +13,7 @@ struct S { }; union U { - __weak id a; // expected-error {{ARC forbids Objective-C objects in union}} + __weak id a; S b; // expected-error {{union member 'b' has a non-trivial copy constructor}} }; -- 2.40.0