Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -2678,6 +2678,9 @@ /// store the data for the anonymous union or struct. bool isAnonymousStructOrUnion() const; + /// Indicates whether this field has a non-trivial C type. + bool hasNonTrivialPrimitiveCType(const ASTContext &Ctx) const; + Expr *getBitWidth() const { if (!BitField) return nullptr; Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -91,6 +91,10 @@ [{!S->isBitField()}], "non-bit-field non-static data members">; +def FieldInUnion : SubsetSubjectgetParent()->isUnion()}], + "field in union">; + def NonStaticCXXMethod : SubsetSubjectisStatic()}], "non-static member functions">; @@ -2176,6 +2180,13 @@ let LangOpts = [COnly]; } +def NonTrivialUnionMember : InheritableAttr { + let Spellings = [Clang<"non_trivial_union_member">]; + let Subjects = SubjectList<[FieldInUnion]>; + let Documentation = [NonTrivialUnionMemberDocs]; + let LangOpts = [COnly]; +} + def Unavailable : InheritableAttr { let Spellings = [Clang<"unavailable">]; let Args = [StringArgument<"Message", 1>, Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -3719,6 +3719,16 @@ }]; } +def NonTrivialUnionMemberDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +This attribute causes fields of non-trivial types (e.g., `__strong`) in C unions +to be treated as trivial fields when computing the trivialness of the +containing union. Users are responsible for patching up the code so that the +union is initialized, destructed, and copied in a functionally correct way. + }]; +} + def ObjCSubclassingRestrictedDocs : Documentation { let Category = DocCatDecl; let Content = [{ Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -3882,6 +3882,11 @@ return false; } +bool FieldDecl::hasNonTrivialPrimitiveCType(const ASTContext &Ctx) const { + return !hasAttr() && + getType().isNonTrivialPrimitiveCType(Ctx); +} + unsigned FieldDecl::getBitWidthValue(const ASTContext &Ctx) const { assert(isBitField() && "not a bitfield"); return getBitWidth()->EvaluateKnownConstInt(Ctx).getZExtValue(); Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -2309,7 +2309,8 @@ if (isa(RD)) return false; for (const FieldDecl *FD : RD->fields()) - if (this->asDerived().visit(FD->getType())) + if (!FD->hasAttr() && + this->asDerived().visit(FD->getType())) return true; return false; } Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -16038,7 +16038,7 @@ if (Record && FDTTy->getDecl()->hasVolatileMember()) Record->setHasVolatileMember(true); if (Record && Record->isUnion() && - FD->getType().isNonTrivialPrimitiveCType(Context)) + FD->hasNonTrivialPrimitiveCType(Context)) Diag(FD->getLocation(), diag::err_nontrivial_primitive_type_in_union); } else if (FDTy->isObjCObjectType()) { @@ -16049,7 +16049,8 @@ FD->setType(T); } else if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && Record && !ObjCFieldLifetimeErrReported && Record->isUnion() && - !getLangOpts().CPlusPlus) { + !getLangOpts().CPlusPlus && + !FD->hasAttr()) { // 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. @@ -16088,14 +16089,17 @@ if (Record && !getLangOpts().CPlusPlus && !FD->hasAttr()) { QualType FT = FD->getType(); - if (FT.isNonTrivialToPrimitiveDefaultInitialize()) - Record->setNonTrivialToPrimitiveDefaultInitialize(true); - QualType::PrimitiveCopyKind PCK = FT.isNonTrivialToPrimitiveCopy(); - if (PCK != QualType::PCK_Trivial && PCK != QualType::PCK_VolatileTrivial) - Record->setNonTrivialToPrimitiveCopy(true); - if (FT.isDestructedType()) { - Record->setNonTrivialToPrimitiveDestroy(true); - Record->setParamDestroyedInCallee(true); + if (!FD->hasAttr()) { + if (FT.isNonTrivialToPrimitiveDefaultInitialize()) + Record->setNonTrivialToPrimitiveDefaultInitialize(true); + QualType::PrimitiveCopyKind PCK = FT.isNonTrivialToPrimitiveCopy(); + if (PCK != QualType::PCK_Trivial && + PCK != QualType::PCK_VolatileTrivial) + Record->setNonTrivialToPrimitiveCopy(true); + if (FT.isDestructedType()) { + Record->setNonTrivialToPrimitiveDestroy(true); + Record->setParamDestroyedInCallee(true); + } } if (const auto *RT = FT->getAs()) { Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -7044,6 +7044,9 @@ case ParsedAttr::AT_TransparentUnion: handleTransparentUnionAttr(S, D, AL); break; + case ParsedAttr::AT_NonTrivialUnionMember: + handleSimpleAttribute(S, D, AL); + break; case ParsedAttr::AT_ObjCException: handleSimpleAttribute(S, D, AL); break; Index: test/CodeGenObjC/strong-in-c-struct.m =================================================================== --- test/CodeGenObjC/strong-in-c-struct.m +++ test/CodeGenObjC/strong-in-c-struct.m @@ -80,6 +80,10 @@ volatile int a[16]; } VolatileArray ; +typedef union { + id __attribute__((non_trivial_union_member)) f0; +} NonTrivialAttrUnion; + #endif #ifdef USESTRUCT @@ -712,4 +716,19 @@ VolatileArray t = *a; } +// CHECK-LABEL: define void @test_non_trivial_union_member(i64 % +// CHECK: %[[A:.*]] = alloca %[[UNION_NONTRIVIALATTRUNION:.*]], align 8 +// CHECK: %[[T:.*]] = alloca %[[UNION_NONTRIVIALATTRUNION]], align 8 +// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[UNION_NONTRIVIALATTRUNION]], %[[UNION_NONTRIVIALATTRUNION]]* %[[A]], i32 0, i32 0 +// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[A_COERCE]] to i8* +// CHECK: store i8* %[[COERCE_VAL_IP]], i8** %[[COERCE_DIVE]], align 8 +// CHECK: %[[V0:.*]] = bitcast %[[UNION_NONTRIVIALATTRUNION]]* %[[T]] to i8* +// CHECK: %[[V1:.*]] = bitcast %[[UNION_NONTRIVIALATTRUNION]]* %[[A]] to i8* +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %[[V0]], i8* align 8 %[[V1]], i64 8, i1 false) +// CHECK: ret void + +void test_non_trivial_union_member(NonTrivialAttrUnion a) { + NonTrivialAttrUnion t = a; +} + #endif /* USESTRUCT */ Index: test/SemaObjC/attr-non-trivial-union-member.m =================================================================== --- /dev/null +++ test/SemaObjC/attr-non-trivial-union-member.m @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -fsyntax-only -fblocks -fobjc-arc -verify -Wno-objc-root-class %s + +int x __attribute__((non_trivial_union_member)); // expected-warning {{'non_trivial_union_member' attribute only applies to field in union}} + +struct S0 { + int __attribute__((non_trivial_union_member)) f0; // expected-warning {{'non_trivial_union_member' attribute only applies to field in union}} +}; + +struct S1 { + id f0; +}; + +union U0 { + id f0; // expected-error {{ARC forbids Objective-C objects in union}} +}; + +union U1 { + struct S1 f0; // expected-error {{non-trivial C types are disallowed in union}} +}; + +union U2 { + id __attribute__((non_trivial_union_member)) f0; +}; + +union U3 { + union U2 f0; + struct S1 __attribute__((non_trivial_union_member)) f1; +};