ahatanak created this revision. ahatanak added a reviewer: rjmccall. ahatanak added a project: clang. Herald added subscribers: dexonsmith, jkorous.
clang currently disallows fields of non-trivial types (e.g., `__strong`) in unions in C mode since it's not possible for the compiler to determine how the unions should be initialized, destructed, or copied. This patch adds support for a new attribute `non_trivial_union_member`, which causes fields annotated with the attribute to be trivial when computing the trivialness of the containing union. This means the users are responsible for patching up the code so that the union is initialized, destructed, and copied in a functionally correct way. rdar://problem/50591731 Repository: rC Clang https://reviews.llvm.org/D62988 Files: include/clang/AST/Decl.h include/clang/Basic/Attr.td include/clang/Basic/AttrDocs.td lib/AST/Decl.cpp lib/AST/Type.cpp lib/Sema/SemaDecl.cpp lib/Sema/SemaDeclAttr.cpp test/CodeGenObjC/strong-in-c-struct.m test/SemaObjC/attr-non-trivial-union-member.m
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; +}; 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: 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<NonTrivialUnionMemberAttr>(S, D, AL); + break; case ParsedAttr::AT_ObjCException: handleSimpleAttribute<ObjCExceptionAttr>(S, D, AL); break; 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<NonTrivialUnionMemberAttr>()) { // 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<UnavailableAttr>()) { 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<NonTrivialUnionMemberAttr>()) { + 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<RecordType>()) { Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -2309,7 +2309,8 @@ if (isa<CXXRecordDecl>(RD)) return false; for (const FieldDecl *FD : RD->fields()) - if (this->asDerived().visit(FD->getType())) + if (!FD->hasAttr<NonTrivialUnionMemberAttr>() && + this->asDerived().visit(FD->getType())) return true; return false; } 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<NonTrivialUnionMemberAttr>() && + getType().isNonTrivialPrimitiveCType(Ctx); +} + unsigned FieldDecl::getBitWidthValue(const ASTContext &Ctx) const { assert(isBitField() && "not a bitfield"); return getBitWidth()->EvaluateKnownConstInt(Ctx).getZExtValue(); 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: 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 : SubsetSubject<Field, + [{S->getParent()->isUnion()}], + "field in union">; + def NonStaticCXXMethod : SubsetSubject<CXXMethod, [{!S->isStatic()}], "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/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;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits