ahatanak created this revision.
ahatanak added reviewers: rsmith, rjmccall, doug.gregor.
ahatanak added a project: clang.
Herald added subscribers: dexonsmith, jkorous.

ObjC pointer members are currently not allowed in unions in either C or C++ 
mode. This patch lifts the restriction in C++ mode.

This patch essentially treats ObjC pointer members the same way a non-static 
data member of a class type that has non-trivial special functions is treated. 
The ObjC pointer member causes all of the defaulted special functions of the 
union that directly contains the member 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


Repository:
  rC Clang

https://reviews.llvm.org/D57438

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

Index: test/SemaObjCXX/objc-weak.mm
===================================================================
--- test/SemaObjCXX/objc-weak.mm
+++ test/SemaObjCXX/objc-weak.mm
@@ -13,7 +13,7 @@
 };
 
 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}}
 };
 
Index: test/SemaObjCXX/arc-0x.mm
===================================================================
--- test/SemaObjCXX/arc-0x.mm
+++ 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,141 @@
     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;
+    };
+  };
+
+  U0 *x0;
+  U1 *x1;
+  U2 *x2;
+  U3 *x3;
+  S0 *x4;
+  S1 *x5;
+
+  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}}
+  }
+
+  void testDestructor(U0 *u0, U1 *u1, U2 *u2, U3 *u3, S0 *s0, S1 *s1) {
+    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}}
+  }
+
+  void testCopyConstructor(U0 *u0, U1 *u1, U2 *u2, U3 *u3, S0 *s0, S1 *s1) {
+    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}}
+  }
+
+  void testCopyAssignment(U0 *u0, U1 *u1, U2 *u2, U3 *u3, S0 *s0, S1 *s1) {
+    *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}}
+  }
+
+  // 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) {
+    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}}
+  }
+
+  void testMoveAssignment(U0 *u0, U1 *u1, U2 *u2, U3 *u3, S0 *s0, S1 *s1) {
+    *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}}
+  }
+}
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp
+++ lib/Sema/SemaDeclCXX.cpp
@@ -6894,6 +6894,8 @@
     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); }
 
@@ -6965,13 +6967,14 @@
       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)
@@ -7023,6 +7026,26 @@
   return false;
 }
 
+bool SpecialMemberDeletionInfo::shouldDeleteForVariantObjCPtrMember(
+    FieldDecl *FD, QualType FieldType) {
+  // The defaulted special functions are defined as deleted if this is a variant
+  // member of an ObjC pointer type, except when it has an in-class initializer,
+  // it doesn't make the defaulted default constructor defined as deleted.
+  if (!FieldType.hasNonTrivialObjCLifetime() ||
+      (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) {
@@ -7043,7 +7066,8 @@
       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();
@@ -7057,6 +7081,10 @@
   QualType FieldType = S.Context.getBaseElementType(FD->getType());
   CXXRecordDecl *FieldRecord = FieldType->getAsCXXRecordDecl();
 
+  if (FD->getParent()->isUnion() &&
+      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.
@@ -7118,6 +7146,9 @@
       for (auto *UI : FieldRecord->fields()) {
         QualType UnionFieldType = S.Context.getBaseElementType(UI->getType());
 
+        if (shouldDeleteForVariantObjCPtrMember(&*UI, UnionFieldType))
+          return true;
+
         if (!UnionFieldType.isConstQualified())
           AllVariantFieldsAreConst = false;
 
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -15923,7 +15923,8 @@
       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.
Index: lib/AST/DeclCXX.cpp
===================================================================
--- lib/AST/DeclCXX.cpp
+++ lib/AST/DeclCXX.cpp
@@ -991,6 +991,17 @@
           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);
       }
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -4648,13 +4648,15 @@
   "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 "
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to