r352949 - [Sema][ObjC] Allow declaring ObjC pointer members with non-trivial

Akira Hatanaka via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 1 18:23:41 PST 2019


Author: ahatanak
Date: Fri Feb  1 18:23:40 2019
New Revision: 352949

URL: http://llvm.org/viewvc/llvm-project?rev=352949&view=rev
Log:
[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

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/AST/DeclCXX.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/test/SemaObjCXX/arc-0x.mm
    cfe/trunk/test/SemaObjCXX/objc-weak.mm

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=352949&r1=352948&r2=352949&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Feb  1 18:23:40 2019
@@ -4649,13 +4649,15 @@ def note_deleted_special_member_class_su
   "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 "

Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=352949&r1=352948&r2=352949&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Fri Feb  1 18:23:40 2019
@@ -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);
       }

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=352949&r1=352948&r2=352949&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Fri Feb  1 18:23:40 2019
@@ -15923,7 +15923,8 @@ void Sema::ActOnFields(Scope *S, SourceL
       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.

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=352949&r1=352948&r2=352949&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Fri Feb  1 18:23:40 2019
@@ -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::shouldDe
       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<CXXBaseSpecifier*>();
       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::shouldDe
   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<CXXRecordDecl>(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::shouldDe
       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::shouldDe
   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::shouldDe
       for (auto *UI : FieldRecord->fields()) {
         QualType UnionFieldType = S.Context.getBaseElementType(UI->getType());
 
+        if (shouldDeleteForVariantObjCPtrMember(&*UI, UnionFieldType))
+          return true;
+
         if (!UnionFieldType.isConstQualified())
           AllVariantFieldsAreConst = false;
 

Modified: cfe/trunk/test/SemaObjCXX/arc-0x.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjCXX/arc-0x.mm?rev=352949&r1=352948&r2=352949&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjCXX/arc-0x.mm (original)
+++ cfe/trunk/test/SemaObjCXX/arc-0x.mm Fri Feb  1 18:23:40 2019
@@ -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_ow
     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 &&>(*u0)); // expected-error {{call to implicitly-deleted copy constructor}}
+    U1 t1(static_cast<U1 &&>(*u1)); // expected-error {{call to implicitly-deleted copy constructor}}
+    U2 t2(static_cast<U2 &&>(*u2)); // expected-error {{call to implicitly-deleted copy constructor}}
+    U3 t3(static_cast<U3 &&>(*u3));
+    S0 t4(static_cast<S0 &&>(*s0)); // expected-error {{call to implicitly-deleted copy constructor}}
+    S1 t5(static_cast<S1 &&>(*s1)); // expected-error {{call to implicitly-deleted copy constructor}}
+    S2 t6(static_cast<S2 &&>(*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 &&>(*u0); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
+    *x1 = static_cast<U1 &&>(*u1); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
+    *x2 = static_cast<U2 &&>(*u2); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
+    *x3 = static_cast<U3 &&>(*u3);
+    *x4 = static_cast<S0 &&>(*s0); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
+    *x5 = static_cast<S1 &&>(*s1); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
+    *x6 = static_cast<S2 &&>(*s2); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
+  }
+}

Modified: cfe/trunk/test/SemaObjCXX/objc-weak.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjCXX/objc-weak.mm?rev=352949&r1=352948&r2=352949&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjCXX/objc-weak.mm (original)
+++ cfe/trunk/test/SemaObjCXX/objc-weak.mm Fri Feb  1 18:23:40 2019
@@ -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}}
 };
 




More information about the cfe-commits mailing list