ahatanak created this revision.
ahatanak added a reviewer: rjmccall.
ahatanak added a project: clang.
Herald added subscribers: dexonsmith, jkorous.

This patch fixes a bug where clang doesn’t reject union fields of non-trivial 
struct types:

  struct S0 {
    id x;
  };
  
  struct S1 {
    id y;
  };
  
  union U0 {
    struct S0 s0;  // no diagnostics.
    struct S1 s1;  // no diagnostics.
  };
  
  union U1 {
    id x;  // clang rejects ObjC pointer fields in unions.
  };
  
  void test(union U0 a) {
    // Both ‘S0::x’ and ‘S1::y' are destructed in the IR.
  }

rdar://problem/46677858


Repository:
  rC Clang

https://reviews.llvm.org/D55659

Files:
  include/clang/AST/Type.h
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/Sema/SemaDecl.cpp
  test/SemaObjC/arc-decls.m
  test/SemaObjCXX/objc-weak.mm

Index: test/SemaObjCXX/objc-weak.mm
===================================================================
--- test/SemaObjCXX/objc-weak.mm
+++ test/SemaObjCXX/objc-weak.mm
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -fsyntax-only -fobjc-runtime-has-weak -fobjc-weak -fblocks -Wno-objc-root-class -std=c++98 -Wno-c++0x-extensions -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime-has-weak -fobjc-weak -fblocks -Wno-objc-root-class -std=c++11 -Wno-c++0x-extensions -verify %s
 
 @interface AnObject
 @property(weak) id value;
@@ -9,14 +10,19 @@
 @end
 
 struct S {
-  __weak id a; // expected-note {{because type 'S' has a member with __weak ownership}}
+  __weak id a;
 };
 
 union U {
   __weak id a; // expected-error {{ARC forbids Objective-C objects in union}}
-  S b;         // expected-error {{union member 'b' has a non-trivial copy constructor}}
+  S b;
 };
 
+#if __cplusplus < 201103L
+// expected-note@-9 {{because type 'S' has a member with __weak ownership}}
+// expected-error@-5 {{union member 'b' has a non-trivial copy constructor}}
+#endif
+
 void testCast(AnObject *o) {
   __weak id a = reinterpret_cast<__weak NOWEAK *>(o); // expected-error {{class is incompatible with __weak references}} \
                                                       // expected-error {{explicit ownership qualifier on cast result has no effect}} \
Index: test/SemaObjC/arc-decls.m
===================================================================
--- test/SemaObjC/arc-decls.m
+++ test/SemaObjC/arc-decls.m
@@ -10,6 +10,10 @@
     id u; // expected-error {{ARC forbids Objective-C objects in union}}
 };
 
+union u_nontrivial_c {
+    struct A a; // expected-error {{ARC forbids non-trivial C types in union}}
+};
+
 @interface I {
    struct A a; 
    struct B {
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -15760,13 +15760,13 @@
   // Verify that all the fields are okay.
   SmallVector<FieldDecl*, 32> RecFields;
 
-  bool ObjCFieldLifetimeErrReported = false;
+  bool NonTrivialPrimitiveFieldErrReported = false;
   for (ArrayRef<Decl *>::iterator i = Fields.begin(), end = Fields.end();
        i != end; ++i) {
     FieldDecl *FD = cast<FieldDecl>(*i);
 
     // Get the type for the field.
-    const Type *FDTy = FD->getType().getTypePtr();
+    QualType FDTy = FD->getType();
 
     if (!FD->isAnonymousStructOrUnion()) {
       // Remember all fields written by the user.
@@ -15868,6 +15868,40 @@
       FD->setInvalidDecl();
       EnclosingDecl->setInvalidDecl();
       continue;
+    } else if (FDTy->isObjCObjectType()) {
+      /// A field cannot be an Objective-c object
+      Diag(FD->getLocation(), diag::err_statically_allocated_object)
+        << FixItHint::CreateInsertion(FD->getLocation(), "*");
+      QualType T = Context.getObjCObjectPointerType(FD->getType());
+      FD->setType(T);
+    } else if (Record && !NonTrivialPrimitiveFieldErrReported &&
+               Record->isUnion() &&
+               (FDTy.hasNonTrivialObjCLifetime() ||
+                (!getLangOpts().CPlusPlus &&
+                 (FDTy.isNonTrivialToPrimitiveDefaultInitialize() ||
+                  !FDTy.isTrivialOrTrivialVolatileToPrimitiveCopy() ||
+                  !FDTy.isTrivialOrTrivialVolatileToPrimitiveMove() ||
+                  FDTy.isDestructedType())))) {
+      // It's an error to have a field that has a non-trivial ObjC lifetime or
+      // a non-trivial C type in a union.
+      // We don't want to report this in a system header, though,
+      // so we just make the field unavailable.
+      // FIXME: that's really not sufficient; we need to make the type
+      // itself invalid to, say, initialize or copy.
+      SourceLocation loc = FD->getLocation();
+      if (getSourceManager().isInSystemHeader(loc)) {
+        if (!FD->hasAttr<UnavailableAttr>()) {
+          FD->addAttr(UnavailableAttr::CreateImplicit(Context, "",
+                        UnavailableAttr::IR_ARCFieldWithOwnership, loc));
+        }
+      } else if (FDTy.hasNonTrivialObjCLifetime()) {
+        Diag(FD->getLocation(), diag::err_arc_objc_object_in_tag)
+          << FDTy->isBlockPointerType() << Record->getTagKind();
+      } else {
+        Diag(FD->getLocation(),
+             diag::err_arc_nontrivial_primitive_type_in_union);
+      }
+      NonTrivialPrimitiveFieldErrReported = true;
     } else if (const RecordType *FDTTy = FDTy->getAs<RecordType>()) {
       if (Record && FDTTy->getDecl()->hasFlexibleArrayMember()) {
         // A type which contains a flexible array member is considered to be a
@@ -15899,33 +15933,6 @@
         Record->setHasObjectMember(true);
       if (Record && FDTTy->getDecl()->hasVolatileMember())
         Record->setHasVolatileMember(true);
-    } else if (FDTy->isObjCObjectType()) {
-      /// A field cannot be an Objective-c object
-      Diag(FD->getLocation(), diag::err_statically_allocated_object)
-        << FixItHint::CreateInsertion(FD->getLocation(), "*");
-      QualType T = Context.getObjCObjectPointerType(FD->getType());
-      FD->setType(T);
-    } else if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() &&
-               Record && !ObjCFieldLifetimeErrReported && Record->isUnion()) {
-      // 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.
-      // FIXME: that's really not sufficient; we need to make the type
-      // itself invalid to, say, initialize or copy.
-      QualType T = FD->getType();
-      if (T.hasNonTrivialObjCLifetime()) {
-        SourceLocation loc = FD->getLocation();
-        if (getSourceManager().isInSystemHeader(loc)) {
-          if (!FD->hasAttr<UnavailableAttr>()) {
-            FD->addAttr(UnavailableAttr::CreateImplicit(Context, "",
-                          UnavailableAttr::IR_ARCFieldWithOwnership, loc));
-          }
-        } else {
-          Diag(FD->getLocation(), diag::err_arc_objc_object_in_tag)
-            << T->isBlockPointerType() << Record->getTagKind();
-        }
-        ObjCFieldLifetimeErrReported = true;
-      }
     } else if (getLangOpts().ObjC &&
                getLangOpts().getGC() != LangOptions::NonGC &&
                Record && !Record->hasObjectMember()) {
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -5280,6 +5280,8 @@
 def err_arc_objc_object_in_tag : Error<
   "ARC forbids %select{Objective-C objects|blocks}0 in "
   "%select{struct|interface|union|<<ERROR>>|enum}1">;
+def err_arc_nontrivial_primitive_type_in_union : Error<
+  "ARC forbids non-trivial C types in union">;
 def err_arc_objc_property_default_assign_on_object : Error<
   "ARC forbids synthesizing a property of an Objective-C object "
   "with unspecified ownership or storage attribute">;
Index: include/clang/AST/Type.h
===================================================================
--- include/clang/AST/Type.h
+++ include/clang/AST/Type.h
@@ -1137,6 +1137,11 @@
   /// kind.
   PrimitiveCopyKind isNonTrivialToPrimitiveCopy() const;
 
+  bool isTrivialOrTrivialVolatileToPrimitiveCopy() const {
+    PrimitiveCopyKind Kind = isNonTrivialToPrimitiveCopy();
+    return Kind == PCK_Trivial || Kind == PCK_VolatileTrivial;
+  }
+
   /// Check if this is a non-trivial type that would cause a C struct
   /// transitively containing this type to be non-trivial to destructively
   /// move and return the kind. Destructive move in this context is a C++-style
@@ -1145,6 +1150,10 @@
   /// source object is placed in an uninitialized state.
   PrimitiveCopyKind isNonTrivialToPrimitiveDestructiveMove() const;
 
+  bool isTrivialOrTrivialVolatileToPrimitiveMove() const {
+    return isTrivialOrTrivialVolatileToPrimitiveCopy();
+  }
+
   enum DestructionKind {
     DK_none,
     DK_cxx_destructor,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D55659: [Sema][ObjC... Akira Hatanaka via Phabricator via cfe-commits

Reply via email to