Skip to content

Commit

Permalink
[Sema][ObjC] Mark C union fields that have non-trivial ObjC ownership
Browse files Browse the repository at this point in the history
qualifications as unavailable if the union is declared in a system
header

r365985 stopped marking those fields as unavailable, which caused the
union's NonTrivialToPrimitive* bits to be set to true. This patch
restores the behavior prior to r365985, except that users can explicitly
specify the ownership qualification of the field to instruct the
compiler not to mark it as unavailable.

rdar://problem/53420753

Differential Revision: https://reviews.llvm.org/D65256

llvm-svn: 371276
  • Loading branch information
ahatanaka committed Sep 7, 2019
1 parent 0905106 commit 3f2c991
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 53 deletions.
5 changes: 5 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Expand Up @@ -2045,6 +2045,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// types.
bool areCompatibleVectorTypes(QualType FirstVec, QualType SecondVec);

/// Return true if the type has been explicitly qualified with ObjC ownership.
/// A type may be implicitly qualified with ownership under ObjC ARC, and in
/// some cases the compiler treats these differently.
bool hasDirectOwnershipQualifier(QualType Ty) const;

/// Return true if this is an \c NSObject object with its \c NSObject
/// attribute set.
static bool isObjCNSObjectType(QualType Ty) {
Expand Down
22 changes: 22 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Expand Up @@ -7949,6 +7949,28 @@ bool ASTContext::areCompatibleVectorTypes(QualType FirstVec,
return false;
}

bool ASTContext::hasDirectOwnershipQualifier(QualType Ty) const {
while (true) {
// __strong id
if (const AttributedType *Attr = dyn_cast<AttributedType>(Ty)) {
if (Attr->getAttrKind() == attr::ObjCOwnership)
return true;

Ty = Attr->getModifiedType();

// X *__strong (...)
} else if (const ParenType *Paren = dyn_cast<ParenType>(Ty)) {
Ty = Paren->getInnerType();

// We do not want to look through typedefs, typeof(expr),
// typeof(type), or any other way that the type is somehow
// abstracted.
} else {
return false;
}
}
}

//===----------------------------------------------------------------------===//
// ObjCQualifiedIdTypesAreCompatible - Compatibility testing for qualified id's.
//===----------------------------------------------------------------------===//
Expand Down
36 changes: 32 additions & 4 deletions clang/lib/Sema/SemaDecl.cpp
Expand Up @@ -11255,6 +11255,15 @@ void Sema::checkNonTrivialCUnionInInitializer(const Expr *Init,

namespace {

bool shouldIgnoreForRecordTriviality(const FieldDecl *FD) {
// Ignore unavailable fields. A field can be marked as unavailable explicitly
// in the source code or implicitly by the compiler if it is in a union
// defined in a system header and has non-trivial ObjC ownership
// qualifications. We don't want those fields to participate in determining
// whether the containing union is non-trivial.
return FD->hasAttr<UnavailableAttr>();
}

struct DiagNonTrivalCUnionDefaultInitializeVisitor
: DefaultInitializedTypeVisitor<DiagNonTrivalCUnionDefaultInitializeVisitor,
void> {
Expand Down Expand Up @@ -11308,7 +11317,8 @@ struct DiagNonTrivalCUnionDefaultInitializeVisitor
<< 0 << 0 << QT.getUnqualifiedType() << "";

for (const FieldDecl *FD : RD->fields())
asDerived().visit(FD->getType(), FD, InNonTrivialUnion);
if (!shouldIgnoreForRecordTriviality(FD))
asDerived().visit(FD->getType(), FD, InNonTrivialUnion);
}

void visitTrivial(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) {}
Expand Down Expand Up @@ -11372,7 +11382,8 @@ struct DiagNonTrivalCUnionDestructedTypeVisitor
<< 0 << 1 << QT.getUnqualifiedType() << "";

for (const FieldDecl *FD : RD->fields())
asDerived().visit(FD->getType(), FD, InNonTrivialUnion);
if (!shouldIgnoreForRecordTriviality(FD))
asDerived().visit(FD->getType(), FD, InNonTrivialUnion);
}

void visitTrivial(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) {}
Expand Down Expand Up @@ -11437,7 +11448,8 @@ struct DiagNonTrivalCUnionCopyVisitor
<< 0 << 2 << QT.getUnqualifiedType() << "";

for (const FieldDecl *FD : RD->fields())
asDerived().visit(FD->getType(), FD, InNonTrivialUnion);
if (!shouldIgnoreForRecordTriviality(FD))
asDerived().visit(FD->getType(), FD, InNonTrivialUnion);
}

void preVisit(QualType::PrimitiveCopyKind PCK, QualType QT,
Expand Down Expand Up @@ -16509,6 +16521,21 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
<< FixItHint::CreateInsertion(FD->getLocation(), "*");
QualType T = Context.getObjCObjectPointerType(FD->getType());
FD->setType(T);
} else if (Record && Record->isUnion() &&
FD->getType().hasNonTrivialObjCLifetime() &&
getSourceManager().isInSystemHeader(FD->getLocation()) &&
!getLangOpts().CPlusPlus && !FD->hasAttr<UnavailableAttr>() &&
(FD->getType().getObjCLifetime() != Qualifiers::OCL_Strong ||
!Context.hasDirectOwnershipQualifier(FD->getType()))) {
// For backward compatibility, fields of C unions declared in system
// headers that have non-trivial ObjC ownership qualifications are marked
// as unavailable unless the qualifier is explicit and __strong. This can
// break ABI compatibility between programs compiled with ARC and MRR, but
// is a better option than rejecting programs using those unions under
// ARC.
FD->addAttr(UnavailableAttr::CreateImplicit(
Context, "", UnavailableAttr::IR_ARCFieldWithOwnership,
FD->getLocation()));
} else if (getLangOpts().ObjC &&
getLangOpts().getGC() != LangOptions::NonGC &&
Record && !Record->hasObjectMember()) {
Expand All @@ -16526,7 +16553,8 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
}
}

if (Record && !getLangOpts().CPlusPlus && !FD->hasAttr<UnavailableAttr>()) {
if (Record && !getLangOpts().CPlusPlus &&
!shouldIgnoreForRecordTriviality(FD)) {
QualType FT = FD->getType();
if (FT.isNonTrivialToPrimitiveDefaultInitialize()) {
Record->setNonTrivialToPrimitiveDefaultInitialize(true);
Expand Down
18 changes: 1 addition & 17 deletions clang/lib/Sema/SemaExpr.cpp
Expand Up @@ -15714,27 +15714,11 @@ static bool captureInBlock(BlockScopeInfo *BSI, VarDecl *Var,

// Warn about implicitly autoreleasing indirect parameters captured by blocks.
if (const auto *PT = CaptureType->getAs<PointerType>()) {
// This function finds out whether there is an AttributedType of kind
// attr::ObjCOwnership in Ty. The existence of AttributedType of kind
// attr::ObjCOwnership implies __autoreleasing was explicitly specified
// rather than being added implicitly by the compiler.
auto IsObjCOwnershipAttributedType = [](QualType Ty) {
while (const auto *AttrTy = Ty->getAs<AttributedType>()) {
if (AttrTy->getAttrKind() == attr::ObjCOwnership)
return true;

// Peel off AttributedTypes that are not of kind ObjCOwnership.
Ty = AttrTy->getModifiedType();
}

return false;
};

QualType PointeeTy = PT->getPointeeType();

if (!Invalid && PointeeTy->getAs<ObjCObjectPointerType>() &&
PointeeTy.getObjCLifetime() == Qualifiers::OCL_Autoreleasing &&
!IsObjCOwnershipAttributedType(PointeeTy)) {
!S.Context.hasDirectOwnershipQualifier(PointeeTy)) {
if (BuildAndDiagnose) {
SourceLocation VarLoc = Var->getLocation();
S.Diag(Loc, diag::warn_block_capture_autoreleasing);
Expand Down
32 changes: 1 addition & 31 deletions clang/lib/Sema/SemaType.cpp
Expand Up @@ -6027,36 +6027,6 @@ static void HandleAddressSpaceTypeAttribute(QualType &Type,
}
}

/// Does this type have a "direct" ownership qualifier? That is,
/// is it written like "__strong id", as opposed to something like
/// "typeof(foo)", where that happens to be strong?
static bool hasDirectOwnershipQualifier(QualType type) {
// Fast path: no qualifier at all.
assert(type.getQualifiers().hasObjCLifetime());

while (true) {
// __strong id
if (const AttributedType *attr = dyn_cast<AttributedType>(type)) {
if (attr->getAttrKind() == attr::ObjCOwnership)
return true;

type = attr->getModifiedType();

// X *__strong (...)
} else if (const ParenType *paren = dyn_cast<ParenType>(type)) {
type = paren->getInnerType();

// That's it for things we want to complain about. In particular,
// we do not want to look through typedefs, typeof(expr),
// typeof(type), or any other way that the type is somehow
// abstracted.
} else {

return false;
}
}
}

/// handleObjCOwnershipTypeAttr - Process an objc_ownership
/// attribute on the specified type.
///
Expand Down Expand Up @@ -6132,7 +6102,7 @@ static bool handleObjCOwnershipTypeAttr(TypeProcessingState &state,
if (Qualifiers::ObjCLifetime previousLifetime
= type.getQualifiers().getObjCLifetime()) {
// If it's written directly, that's an error.
if (hasDirectOwnershipQualifier(type)) {
if (S.Context.hasDirectOwnershipQualifier(type)) {
S.Diag(AttrLoc, diag::err_attr_objc_ownership_redundant)
<< type;
return true;
Expand Down
19 changes: 19 additions & 0 deletions clang/test/SemaObjC/Inputs/non-trivial-c-union.h
@@ -0,0 +1,19 @@
// For backward compatibility, fields of C unions declared in system headers
// that have non-trivial ObjC ownership qualifications are marked as unavailable
// unless the qualifier is explicit and __strong.

#pragma clang system_header

typedef __strong id StrongID;

typedef union {
id f0;
_Nonnull id f1;
__weak id f2;
StrongID f3;
} U0_SystemHeader;

typedef union { // expected-note {{'U1_SystemHeader' has subobjects that are non-trivial to destruct}} expected-note {{'U1_SystemHeader' has subobjects that are non-trivial to copy}}
__strong id f0; // expected-note {{f0 has type '__strong id' that is non-trivial to destruct}} expected-note {{f0 has type '__strong id' that is non-trivial to copy}}
_Nonnull id f1;
} U1_SystemHeader;
8 changes: 7 additions & 1 deletion clang/test/SemaObjC/non-trivial-c-union.m
@@ -1,4 +1,6 @@
// RUN: %clang_cc1 -fsyntax-only -fblocks -fobjc-arc -fobjc-runtime-has-weak -verify %s
// RUN: %clang_cc1 -fsyntax-only -fblocks -fobjc-arc -fobjc-runtime-has-weak -I %S/Inputs -verify %s

#include "non-trivial-c-union.h"

typedef union { // expected-note 12 {{'U0' has subobjects that are non-trivial to default-initialize}} expected-note 36 {{'U0' has subobjects that are non-trivial to destruct}} expected-note 28 {{'U0' has subobjects that are non-trivial to copy}}
id f0; // expected-note 12 {{f0 has type '__strong id' that is non-trivial to default-initialize}} expected-note 36 {{f0 has type '__strong id' that is non-trivial to destruct}} expected-note 28 {{f0 has type '__strong id' that is non-trivial to copy}}
Expand Down Expand Up @@ -80,3 +82,7 @@ void testBlockCapture(void) {
void testVolatileLValueToRValue(volatile U0 *a) {
(void)*a; // expected-error {{cannot use volatile type 'volatile U0' where it causes an lvalue-to-rvalue conversion since it is a union that is non-trivial to destruct}} // expected-error {{cannot use volatile type 'volatile U0' where it causes an lvalue-to-rvalue conversion since it is a union that is non-trivial to copy}}
}

void unionInSystemHeader0(U0_SystemHeader);

void unionInSystemHeader1(U1_SystemHeader); // expected-error {{cannot use type 'U1_SystemHeader' for a function/method parameter since it is a union that is non-trivial to destruct}} expected-error {{cannot use type 'U1_SystemHeader' for a function/method parameter since it is a union that is non-trivial to copy}}

0 comments on commit 3f2c991

Please sign in to comment.