summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/AST/ASTContext.h5
-rw-r--r--clang/lib/AST/ASTContext.cpp22
-rw-r--r--clang/lib/Sema/SemaDecl.cpp36
-rw-r--r--clang/lib/Sema/SemaExpr.cpp18
-rw-r--r--clang/lib/Sema/SemaType.cpp32
-rw-r--r--clang/test/SemaObjC/Inputs/non-trivial-c-union.h19
-rw-r--r--clang/test/SemaObjC/non-trivial-c-union.m8
7 files changed, 87 insertions, 53 deletions
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 816f665fed7..4dc9a231a8a 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -2045,6 +2045,11 @@ public:
/// 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) {
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 94a5addff25..f488ef7995b 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -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.
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 279332ab93d..023d1fcff84 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -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> {
@@ -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) {}
@@ -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) {}
@@ -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,
@@ -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()) {
@@ -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);
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 7f00981d348..816d37e0b5e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -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);
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 4281a6a0b34..a5634028978 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -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.
///
@@ -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;
diff --git a/clang/test/SemaObjC/Inputs/non-trivial-c-union.h b/clang/test/SemaObjC/Inputs/non-trivial-c-union.h
new file mode 100644
index 00000000000..9738353202a
--- /dev/null
+++ b/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;
diff --git a/clang/test/SemaObjC/non-trivial-c-union.m b/clang/test/SemaObjC/non-trivial-c-union.m
index 7bd82775451..4c294764c43 100644
--- a/clang/test/SemaObjC/non-trivial-c-union.m
+++ b/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}}
@@ -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}}
OpenPOWER on IntegriCloud