aboutsummaryrefslogtreecommitdiff
path: root/clang
diff options
context:
space:
mode:
authorErik Pilkington <erik.pilkington@gmail.com>2019-01-04 18:33:06 +0000
committerErik Pilkington <erik.pilkington@gmail.com>2019-01-04 18:33:06 +0000
commit1e36882b5291d5a7209be4f9c99b9713828afac4 (patch)
treecd23171fec5d69fd0390b0ad9303f51396072b5c /clang
parent6153565511338f33506f4615dd5a7d2e4c4ffaa6 (diff)
[ObjCARC] Add an new attribute, objc_externally_retained
This attribute, called "objc_externally_retained", exposes clang's notion of pseudo-__strong variables in ARC. Pseudo-strong variables "borrow" their initializer, meaning that they don't retain/release it, instead assuming that someone else is keeping their value alive. If a function is annotated with this attribute, implicitly strong parameters of that function aren't implicitly retained/released in the function body, and are implicitly const. This is useful to expose for performance reasons, most functions don't need the extra safety of the retain/release, so programmers can opt out as needed. This attribute can also apply to declarations of local variables, with similar effect. Differential revision: https://reviews.llvm.org/D55865 llvm-svn: 350422
Diffstat (limited to 'clang')
-rw-r--r--clang/docs/AutomaticReferenceCounting.rst87
-rw-r--r--clang/include/clang/AST/Decl.h30
-rw-r--r--clang/include/clang/AST/DeclObjC.h8
-rw-r--r--clang/include/clang/Basic/Attr.td8
-rw-r--r--clang/include/clang/Basic/AttrDocs.td28
-rw-r--r--clang/include/clang/Basic/DiagnosticSemaKinds.td8
-rw-r--r--clang/lib/CodeGen/CGDecl.cpp28
-rw-r--r--clang/lib/CodeGen/CGExpr.cpp8
-rw-r--r--clang/lib/Sema/SemaDeclAttr.cpp96
-rw-r--r--clang/lib/Sema/SemaExpr.cpp12
-rw-r--r--clang/lib/Serialization/ASTReaderDecl.cpp2
-rw-r--r--clang/lib/Serialization/ASTWriterDecl.cpp5
-rw-r--r--clang/test/CodeGenObjC/externally-retained.m115
-rw-r--r--clang/test/Misc/pragma-attribute-supported-attributes-list.test1
-rw-r--r--clang/test/SemaObjC/externally-retained-no-arc.m7
-rw-r--r--clang/test/SemaObjC/externally-retained.m114
16 files changed, 500 insertions, 57 deletions
diff --git a/clang/docs/AutomaticReferenceCounting.rst b/clang/docs/AutomaticReferenceCounting.rst
index d8cda0c05f9e..3e51d2f5d79e 100644
--- a/clang/docs/AutomaticReferenceCounting.rst
+++ b/clang/docs/AutomaticReferenceCounting.rst
@@ -1734,20 +1734,78 @@ A program is ill-formed if it refers to the ``NSAutoreleasePool`` class.
rest of the language. Not draining the pool during an unwind is apparently
required by the Objective-C exceptions implementation.
+.. _arc.misc.externally_retained:
+
+Externally-Retained Variables
+-----------------------------
+
+In some situations, variables with strong ownership are considered
+externally-retained by the implementation. This means that the variable is
+retained elsewhere, and therefore the implementation can elide retaining and
+releasing its value. Such a variable is implicitly ``const`` for safety. In
+contrast with ``__unsafe_unretained``, an externally-retained variable still
+behaves as a strong variable outside of initialization and destruction. For
+instance, when an externally-retained variable is captured in a block the value
+of the variable is retained and released on block capture and destruction. It
+also affects C++ features such as lambda capture, ``decltype``, and template
+argument deduction.
+
+Implicitly, the implementation assumes that the :ref:`self parameter in a
+non-init method <arc.misc.self>` and the :ref:`variable in a for-in loop
+<arc.misc.enumeration>` are externally-retained.
+
+Externally-retained semantics can also be opted into with the
+``objc_externally_retained`` attribute. This attribute can apply to strong local
+variables, functions, methods, or blocks:
+
+.. code-block:: objc
+
+ @class WobbleAmount;
+
+ @interface Widget : NSObject
+ -(void)wobble:(WobbleAmount *)amount;
+ @end
+
+ @implementation Widget
+
+ -(void)wobble:(WobbleAmount *)amount
+ __attribute__((objc_externally_retained)) {
+ // 'amount' and 'alias' aren't retained on entry, nor released on exit.
+ __attribute__((objc_externally_retained)) WobbleAmount *alias = amount;
+ }
+ @end
+
+Annotating a function with this attribute makes every parameter with strong
+retainable object pointer type externally-retained, unless the variable was
+explicitly qualified with ``__strong``. For instance, ``first_param`` is
+externally-retained (and therefore ``const``) below, but not ``second_param``:
+
+.. code-block:: objc
+
+ __attribute__((objc_externally_retained))
+ void f(NSArray *first_param, __strong NSArray *second_param) {
+ // ...
+ }
+
+You can test if your compiler has support for ``objc_externally_retained`` with
+``__has_attribute``:
+
+.. code-block:: objc
+
+ #if __has_attribute(objc_externally_retained)
+ // Use externally retained...
+ #endif
+
.. _arc.misc.self:
``self``
--------
-The ``self`` parameter variable of an Objective-C method is never actually
-retained by the implementation. It is undefined behavior, or at least
-dangerous, to cause an object to be deallocated during a message send to that
-object.
-
-To make this safe, for Objective-C instance methods ``self`` is implicitly
-``const`` unless the method is in the :ref:`init family
-<arc.family.semantics.init>`. Further, ``self`` is **always** implicitly
-``const`` within a class method.
+The ``self`` parameter variable of an non-init Objective-C method is considered
+:ref:`externally-retained <arc.misc.externally_retained>` by the implementation.
+It is undefined behavior, or at least dangerous, to cause an object to be
+deallocated during a message send to that object. In an init method, ``self``
+follows the :ref:``init family rules <arc.family.semantics.init>``.
.. admonition:: Rationale
@@ -1758,9 +1816,9 @@ To make this safe, for Objective-C instance methods ``self`` is implicitly
without this retain and release. Since it's extremely uncommon to actually
do so, even unintentionally, and since there's no natural way for the
programmer to remove this retain/release pair otherwise (as there is for
- other parameters by, say, making the variable ``__unsafe_unretained``), we
- chose to make this optimizing assumption and shift some amount of risk to the
- user.
+ other parameters by, say, making the variable ``objc_externally_retained`` or
+ qualifying it with ``__unsafe_unretained``), we chose to make this optimizing
+ assumption and shift some amount of risk to the user.
.. _arc.misc.enumeration:
@@ -1769,8 +1827,9 @@ Fast enumeration iteration variables
If a variable is declared in the condition of an Objective-C fast enumeration
loop, and the variable has no explicit ownership qualifier, then it is
-qualified with ``const __strong`` and objects encountered during the
-enumeration are not actually retained.
+implicitly :ref:`externally-retained <arc.misc.externally_retained>` so that
+objects encountered during the enumeration are not actually retained and
+released.
.. admonition:: Rationale
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 52703e60aa28..ac641881ae2d 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -866,8 +866,12 @@ private:
unsigned SClass : 3;
unsigned TSCSpec : 2;
unsigned InitStyle : 2;
+
+ /// Whether this variable is an ARC pseudo-__strong variable; see
+ /// isARCPseudoStrong() for details.
+ unsigned ARCPseudoStrong : 1;
};
- enum { NumVarDeclBits = 7 };
+ enum { NumVarDeclBits = 8 };
protected:
enum { NumParameterIndexBits = 8 };
@@ -940,10 +944,6 @@ protected:
/// Whether this variable is the for-in loop declaration in Objective-C.
unsigned ObjCForDecl : 1;
- /// Whether this variable is an ARC pseudo-__strong
- /// variable; see isARCPseudoStrong() for details.
- unsigned ARCPseudoStrong : 1;
-
/// Whether this variable is (C++1z) inline.
unsigned IsInline : 1;
@@ -1349,17 +1349,15 @@ public:
NonParmVarDeclBits.ObjCForDecl = FRD;
}
- /// Determine whether this variable is an ARC pseudo-__strong
- /// variable. A pseudo-__strong variable has a __strong-qualified
- /// type but does not actually retain the object written into it.
- /// Generally such variables are also 'const' for safety.
- bool isARCPseudoStrong() const {
- return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.ARCPseudoStrong;
- }
- void setARCPseudoStrong(bool ps) {
- assert(!isa<ParmVarDecl>(this));
- NonParmVarDeclBits.ARCPseudoStrong = ps;
- }
+ /// Determine whether this variable is an ARC pseudo-__strong variable. A
+ /// pseudo-__strong variable has a __strong-qualified type but does not
+ /// actually retain the object written into it. Generally such variables are
+ /// also 'const' for safety. There are 3 cases where this will be set, 1) if
+ /// the variable is annotated with the objc_externally_retained attribute, 2)
+ /// if its 'self' in a non-init method, or 3) if its the variable in an for-in
+ /// loop.
+ bool isARCPseudoStrong() const { return VarDeclBits.ARCPseudoStrong; }
+ void setARCPseudoStrong(bool PS) { VarDeclBits.ARCPseudoStrong = PS; }
/// Whether this variable is (C++1z) inline.
bool isInline() const {
diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h
index bc10ba42c6a3..5b57411f9785 100644
--- a/clang/include/clang/AST/DeclObjC.h
+++ b/clang/include/clang/AST/DeclObjC.h
@@ -369,6 +369,14 @@ public:
NumParams);
}
+ ParmVarDecl *getParamDecl(unsigned Idx) {
+ assert(Idx < NumParams && "Index out of bounds!");
+ return getParams()[Idx];
+ }
+ const ParmVarDecl *getParamDecl(unsigned Idx) const {
+ return const_cast<ObjCMethodDecl *>(this)->getParamDecl(Idx);
+ }
+
/// Sets the method's parameters and selector source locations.
/// If the method is implicit (not coming from source) \p SelLocs is
/// ignored.
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 271f979f8ac3..311cf943dfab 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -300,6 +300,7 @@ def OpenCL : LangOpt<"OpenCL">;
def RenderScript : LangOpt<"RenderScript">;
def ObjC : LangOpt<"ObjC">;
def BlocksSupported : LangOpt<"Blocks">;
+def ObjCAutoRefCount : LangOpt<"ObjCAutoRefCount">;
// Defines targets for target-specific attributes. Empty lists are unchecked.
class TargetSpec {
@@ -3141,3 +3142,10 @@ def Uninitialized : InheritableAttr {
let Subjects = SubjectList<[LocalVar]>;
let Documentation = [UninitializedDocs];
}
+
+def ObjCExternallyRetained : InheritableAttr {
+ let LangOpts = [ObjCAutoRefCount];
+ let Spellings = [Clang<"objc_externally_retained">];
+ let Subjects = SubjectList<[NonParmVar, Function, Block, ObjCMethod]>;
+ let Documentation = [ObjCExternallyRetainedDocs];
+}
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 532773fc95c4..aae2d1ddfc9e 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3824,3 +3824,31 @@ def SpeculativeLoadHardeningDocs : Documentation {
(even after inlining) end up hardened.
}];
}
+
+def ObjCExternallyRetainedDocs : Documentation {
+ let Category = DocCatVariable;
+ let Content = [{
+The ``objc_externally_retained`` attribute can be applied to strong local
+variables, functions, methods, or blocks to opt into
+`externally-retained semantics
+<https://clang.llvm.org/docs/AutomaticReferenceCounting.html#externally-retained-variables>`_.
+
+When applied to the definition of a function, method, or block, every parameter
+of the function with implicit strong retainable object pointer type is
+considered externally-retained, and becomes ``const``. By explicitly annotating
+a parameter with ``__strong``, you can opt back into the default
+non-externally-retained behaviour for that parameter. For instance,
+``first_param`` is externally-retained below, but not ``second_param``:
+
+.. code-block:: objc
+
+ __attribute__((objc_externally_retained))
+ void f(NSArray *first_param, __strong NSArray *second_param) {
+ // ...
+ }
+
+Likewise, when applied to a strong local variable, that variable becomes
+``const`` and is considered externally-retained.
+
+When compiled without ``-fobjc-arc``, this attribute is ignored.
+}]; }
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 375f9515b26d..0f30aed96049 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3485,6 +3485,11 @@ def err_objc_bridged_related_known_method : Error<
def err_objc_attr_protocol_requires_definition : Error<
"attribute %0 can only be applied to @protocol definitions, not forward declarations">;
+def warn_ignored_objc_externally_retained : Warning<
+ "'objc_externally_retained' can only be applied to local variables "
+ "%select{of retainable type|with strong ownership}0">,
+ InGroup<IgnoredAttributes>;
+
// Function Parameter Semantic Analysis.
def err_param_with_void_type : Error<"argument may not have 'void' type">;
def err_void_only_param : Error<
@@ -5254,6 +5259,9 @@ def err_typecheck_arc_assign_self_class_method : Error<
def err_typecheck_arr_assign_enumeration : Error<
"fast enumeration variables cannot be modified in ARC by default; "
"declare the variable __strong to allow this">;
+def err_typecheck_arc_assign_externally_retained : Error<
+ "variable declared with 'objc_externally_retained' "
+ "cannot be modified in ARC">;
def warn_arc_retained_assign : Warning<
"assigning retained object to %select{weak|unsafe_unretained}0 "
"%select{property|variable}1"
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 2262c998af0e..5959d889b455 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -797,15 +797,21 @@ void CodeGenFunction::EmitScalarInit(const Expr *init, const ValueDecl *D,
case Qualifiers::OCL_None:
llvm_unreachable("present but none");
+ case Qualifiers::OCL_Strong: {
+ if (!D || !isa<VarDecl>(D) || !cast<VarDecl>(D)->isARCPseudoStrong()) {
+ value = EmitARCRetainScalarExpr(init);
+ break;
+ }
+ // If D is pseudo-strong, treat it like __unsafe_unretained here. This means
+ // that we omit the retain, and causes non-autoreleased return values to be
+ // immediately released.
+ LLVM_FALLTHROUGH;
+ }
+
case Qualifiers::OCL_ExplicitNone:
value = EmitARCUnsafeUnretainedScalarExpr(init);
break;
- case Qualifiers::OCL_Strong: {
- value = EmitARCRetainScalarExpr(init);
- break;
- }
-
case Qualifiers::OCL_Weak: {
// If it's not accessed by the initializer, try to emit the
// initialization with a copy or move.
@@ -2324,15 +2330,11 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, ParamValue Arg,
// cleanup to do the release at the end of the function.
bool isConsumed = D.hasAttr<NSConsumedAttr>();
- // 'self' is always formally __strong, but if this is not an
- // init method then we don't want to retain it.
+ // If a parameter is pseudo-strong then we can omit the implicit retain.
if (D.isARCPseudoStrong()) {
- const ObjCMethodDecl *method = cast<ObjCMethodDecl>(CurCodeDecl);
- assert(&D == method->getSelfDecl());
- assert(lt == Qualifiers::OCL_Strong);
- assert(qs.hasConst());
- assert(method->getMethodFamily() != OMF_init);
- (void) method;
+ assert(lt == Qualifiers::OCL_Strong &&
+ "pseudo-strong variable isn't strong?");
+ assert(qs.hasConst() && "pseudo-strong variable should be const!");
lt = Qualifiers::OCL_ExplicitNone;
}
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 6ef1091cc0dd..34a921e2dc00 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -419,8 +419,12 @@ LValue CodeGenFunction::
EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *M) {
const Expr *E = M->GetTemporaryExpr();
- // FIXME: ideally this would use EmitAnyExprToMem, however, we cannot do so
- // as that will cause the lifetime adjustment to be lost for ARC
+ assert((!M->getExtendingDecl() || !isa<VarDecl>(M->getExtendingDecl()) ||
+ !cast<VarDecl>(M->getExtendingDecl())->isARCPseudoStrong()) &&
+ "Reference should never be pseudo-strong!");
+
+ // FIXME: ideally this would use EmitAnyExprToMem, however, we cannot do so
+ // as that will cause the lifetime adjustment to be lost for ARC
auto ownership = M->getType().getObjCLifetime();
if (ownership != Qualifiers::OCL_None &&
ownership != Qualifiers::OCL_ExplicitNone) {
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 63501d92915a..d1db71838fed 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -93,6 +93,17 @@ static unsigned getFunctionOrMethodNumParams(const Decl *D) {
return cast<ObjCMethodDecl>(D)->param_size();
}
+static const ParmVarDecl *getFunctionOrMethodParam(const Decl *D,
+ unsigned Idx) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(D))
+ return FD->getParamDecl(Idx);
+ if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
+ return MD->getParamDecl(Idx);
+ if (const auto *BD = dyn_cast<BlockDecl>(D))
+ return BD->getParamDecl(Idx);
+ return nullptr;
+}
+
static QualType getFunctionOrMethodParamType(const Decl *D, unsigned Idx) {
if (const FunctionType *FnTy = D->getFunctionType())
return cast<FunctionProtoType>(FnTy)->getParamType(Idx);
@@ -103,12 +114,8 @@ static QualType getFunctionOrMethodParamType(const Decl *D, unsigned Idx) {
}
static SourceRange getFunctionOrMethodParamRange(const Decl *D, unsigned Idx) {
- if (const auto *FD = dyn_cast<FunctionDecl>(D))
- return FD->getParamDecl(Idx)->getSourceRange();
- if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
- return MD->parameters()[Idx]->getSourceRange();
- if (const auto *BD = dyn_cast<BlockDecl>(D))
- return BD->getParamDecl(Idx)->getSourceRange();
+ if (auto *PVD = getFunctionOrMethodParam(D, Idx))
+ return PVD->getSourceRange();
return SourceRange();
}
@@ -6093,6 +6100,79 @@ static void handleUninitializedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
UninitializedAttr(AL.getLoc(), S.Context, Index));
}
+static bool tryMakeVariablePseudoStrong(Sema &S, VarDecl *VD,
+ bool DiagnoseFailure) {
+ QualType Ty = VD->getType();
+ if (!Ty->isObjCRetainableType()) {
+ if (DiagnoseFailure) {
+ S.Diag(VD->getBeginLoc(), diag::warn_ignored_objc_externally_retained)
+ << 0;
+ }
+ return false;
+ }
+
+ Qualifiers::ObjCLifetime LifetimeQual = Ty.getQualifiers().getObjCLifetime();
+
+ // Sema::inferObjCARCLifetime must run after processing decl attributes
+ // (because __block lowers to an attribute), so if the lifetime hasn't been
+ // explicitly specified, infer it locally now.
+ if (LifetimeQual == Qualifiers::OCL_None)
+ LifetimeQual = Ty->getObjCARCImplicitLifetime();
+
+ // The attributes only really makes sense for __strong variables; ignore any
+ // attempts to annotate a parameter with any other lifetime qualifier.
+ if (LifetimeQual != Qualifiers::OCL_Strong) {
+ if (DiagnoseFailure) {
+ S.Diag(VD->getBeginLoc(), diag::warn_ignored_objc_externally_retained)
+ << 1;
+ }
+ return false;
+ }
+
+ // Tampering with the type of a VarDecl here is a bit of a hack, but we need
+ // to ensure that the variable is 'const' so that we can error on
+ // modification, which can otherwise over-release.
+ VD->setType(Ty.withConst());
+ VD->setARCPseudoStrong(true);
+ return true;
+}
+
+static void handleObjCExternallyRetainedAttr(Sema &S, Decl *D,
+ const ParsedAttr &AL) {
+ if (auto *VD = dyn_cast<VarDecl>(D)) {
+ assert(!isa<ParmVarDecl>(VD) && "should be diagnosed automatically");
+ if (!VD->hasLocalStorage()) {
+ S.Diag(D->getBeginLoc(), diag::warn_ignored_objc_externally_retained)
+ << 0;
+ return;
+ }
+
+ if (!tryMakeVariablePseudoStrong(S, VD, /*DiagnoseFailure=*/true))
+ return;
+
+ handleSimpleAttribute<ObjCExternallyRetainedAttr>(S, D, AL);
+ return;
+ }
+
+ // If D is a function-like declaration (method, block, or function), then we
+ // make every parameter psuedo-strong.
+ for (unsigned I = 0, E = getFunctionOrMethodNumParams(D); I != E; ++I) {
+ auto *PVD = const_cast<ParmVarDecl *>(getFunctionOrMethodParam(D, I));
+ QualType Ty = PVD->getType();
+
+ // If a user wrote a parameter with __strong explicitly, then assume they
+ // want "real" strong semantics for that parameter. This works because if
+ // the parameter was written with __strong, then the strong qualifier will
+ // be non-local.
+ if (Ty.getLocalUnqualifiedType().getQualifiers().getObjCLifetime() ==
+ Qualifiers::OCL_Strong)
+ continue;
+
+ tryMakeVariablePseudoStrong(S, PVD, /*DiagnoseFailure=*/false);
+ }
+ handleSimpleAttribute<ObjCExternallyRetainedAttr>(S, D, AL);
+}
+
//===----------------------------------------------------------------------===//
// Top Level Sema Entry Points
//===----------------------------------------------------------------------===//
@@ -6788,6 +6868,10 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case ParsedAttr::AT_Uninitialized:
handleUninitializedAttr(S, D, AL);
break;
+
+ case ParsedAttr::AT_ObjCExternallyRetained:
+ handleObjCExternallyRetainedAttr(S, D, AL);
+ break;
}
}
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index aa4b23b15c55..eddf90178bce 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -11214,17 +11214,23 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
if (var->isARCPseudoStrong() &&
(!var->getTypeSourceInfo() ||
!var->getTypeSourceInfo()->getType().isConstQualified())) {
- // There are two pseudo-strong cases:
+ // There are three pseudo-strong cases:
// - self
ObjCMethodDecl *method = S.getCurMethodDecl();
- if (method && var == method->getSelfDecl())
+ if (method && var == method->getSelfDecl()) {
DiagID = method->isClassMethod()
? diag::err_typecheck_arc_assign_self_class_method
: diag::err_typecheck_arc_assign_self;
+ // - Objective-C externally_retained attribute.
+ } else if (var->hasAttr<ObjCExternallyRetainedAttr>() ||
+ isa<ParmVarDecl>(var)) {
+ DiagID = diag::err_typecheck_arc_assign_externally_retained;
+
// - fast enumeration variables
- else
+ } else {
DiagID = diag::err_typecheck_arr_assign_enumeration;
+ }
SourceRange Assign;
if (Loc != OrigLoc)
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 8c1710f6608c..763ab527570d 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -1350,6 +1350,7 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) {
VD->VarDeclBits.SClass = (StorageClass)Record.readInt();
VD->VarDeclBits.TSCSpec = Record.readInt();
VD->VarDeclBits.InitStyle = Record.readInt();
+ VD->VarDeclBits.ARCPseudoStrong = Record.readInt();
if (!isa<ParmVarDecl>(VD)) {
VD->NonParmVarDeclBits.IsThisDeclarationADemotedDefinition =
Record.readInt();
@@ -1357,7 +1358,6 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) {
VD->NonParmVarDeclBits.NRVOVariable = Record.readInt();
VD->NonParmVarDeclBits.CXXForRangeDecl = Record.readInt();
VD->NonParmVarDeclBits.ObjCForDecl = Record.readInt();
- VD->NonParmVarDeclBits.ARCPseudoStrong = Record.readInt();
VD->NonParmVarDeclBits.IsInline = Record.readInt();
VD->NonParmVarDeclBits.IsInlineSpecified = Record.readInt();
VD->NonParmVarDeclBits.IsConstexpr = Record.readInt();
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 5ec11dda8a96..002b43f81121 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -922,13 +922,13 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) {
Record.push_back(D->getStorageClass());
Record.push_back(D->getTSCSpec());
Record.push_back(D->getInitStyle());
+ Record.push_back(D->isARCPseudoStrong());
if (!isa<ParmVarDecl>(D)) {
Record.push_back(D->isThisDeclarationADemotedDefinition());
Record.push_back(D->isExceptionVariable());
Record.push_back(D->isNRVOVariable());
Record.push_back(D->isCXXForRangeDecl());
Record.push_back(D->isObjCForDecl());
- Record.push_back(D->isARCPseudoStrong());
Record.push_back(D->isInline());
Record.push_back(D->isInlineSpecified());
Record.push_back(D->isConstexpr());
@@ -1986,6 +1986,7 @@ void ASTWriter::WriteDeclAbbrevs() {
Abv->Add(BitCodeAbbrevOp(0)); // SClass
Abv->Add(BitCodeAbbrevOp(0)); // TSCSpec
Abv->Add(BitCodeAbbrevOp(0)); // InitStyle
+ Abv->Add(BitCodeAbbrevOp(0)); // ARCPseudoStrong
Abv->Add(BitCodeAbbrevOp(0)); // Linkage
Abv->Add(BitCodeAbbrevOp(0)); // HasInit
Abv->Add(BitCodeAbbrevOp(0)); // HasMemberSpecializationInfo
@@ -2062,12 +2063,12 @@ void ASTWriter::WriteDeclAbbrevs() {
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // SClass
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // TSCSpec
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // InitStyle
+ Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isARCPseudoStrong
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsThisDeclarationADemotedDefinition
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isExceptionVariable
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isNRVOVariable
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isCXXForRangeDecl
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isObjCForDecl
- Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isARCPseudoStrong
Abv->Add(BitCodeAbbrevOp(0)); // isInline
Abv->Add(BitCodeAbbrevOp(0)); // isInlineSpecified
Abv->Add(BitCodeAbbrevOp(0)); // isConstexpr
diff --git a/clang/test/CodeGenObjC/externally-retained.m b/clang/test/CodeGenObjC/externally-retained.m
new file mode 100644
index 000000000000..0b4d0d648b44
--- /dev/null
+++ b/clang/test/CodeGenObjC/externally-retained.m
@@ -0,0 +1,115 @@
+// RUN: %clang_cc1 -triple x86_64-apple-macosx10.13.0 -fobjc-arc -fblocks -Wno-objc-root-class -O0 %s -S -emit-llvm -o - | FileCheck %s --dump-input-on-failure
+// RUN: %clang_cc1 -triple x86_64-apple-macosx10.13.0 -fobjc-arc -fblocks -Wno-objc-root-class -O0 -xobjective-c++ -std=c++11 %s -S -emit-llvm -o - | FileCheck %s --check-prefix CHECKXX --dump-input-on-failure
+
+#define EXT_RET __attribute__((objc_externally_retained))
+
+@interface ObjTy @end
+
+ObjTy *global;
+
+#if __cplusplus
+// Suppress name mangling in C++ mode for the sake of check lines.
+extern "C" void param(ObjTy *p);
+extern "C" void local();
+extern "C" void in_init();
+extern "C" void anchor();
+extern "C" void block_capture(ObjTy *);
+extern "C" void esc(void (^)());
+extern "C" void escp(void (^)(ObjTy *));
+extern "C" void block_param();
+#endif
+
+void param(ObjTy *p) EXT_RET {
+ // CHECK-LABEL: define void @param
+ // CHECK-NOT: llvm.objc.
+ // CHECK ret
+}
+
+void local() {
+ EXT_RET ObjTy *local = global;
+ // CHECK-LABEL: define void @local
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+}
+
+void in_init() {
+ // Test that we do the right thing when a variable appears in it's own
+ // initializer. Here, we release the value stored in 'wat' after overwriting
+ // it, in case it was somehow set to point to a non-null object while it's
+ // initializer is being evaluated.
+ EXT_RET ObjTy *wat = 0 ? wat : global;
+
+ // CHECK-LABEL: define void @in_init
+ // CHECK: [[WAT:%.*]] = alloca
+ // CHECK-NEXT: store {{.*}} null, {{.*}} [[WAT]]
+ // CHECK-NEXT: [[GLOBAL:%.*]] = load {{.*}} @global
+ // CHECK-NEXT: [[WAT_LOAD:%.*]] = load {{.*}} [[WAT]]
+ // CHECK-NEXT: store {{.*}} [[GLOBAL]], {{.*}} [[WAT]]
+ // CHECK-NEXT: [[CASTED:%.*]] = bitcast {{.*}} [[WAT_LOAD]] to
+ // CHECK-NEXT: call void @llvm.objc.release(i8* [[CASTED]])
+
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+}
+
+void esc(void (^)());
+
+void block_capture(ObjTy *obj) EXT_RET {
+ esc(^{ (void)obj; });
+
+ // CHECK-LABEL: define void @block_capture
+ // CHECK-NOT: llvm.objc.
+ // CHECK: call i8* @llvm.objc.retain
+ // CHECK-NOT: llvm.objc.
+ // CHECK: call void @esc
+ // CHECK-NOT: llvm.objc.
+ // CHECK: call void @llvm.objc.storeStrong({{.*}} null)
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+
+ // CHECK-LABEL: define {{.*}} void @__copy_helper_block_
+ // CHECK-NOT: llvm.objc.
+ // CHECK: llvm.objc.storeStrong
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+
+ // CHECK-LABEL: define {{.*}} void @__destroy_helper_block_
+ // CHECK-NOT: llvm.objc.
+ // CHECK: llvm.objc.storeStrong({{.*}} null)
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+}
+
+void escp(void (^)(ObjTy *));
+
+void block_param() {
+ escp(^(ObjTy *p) EXT_RET {});
+
+ // CHECK-LABEL: define internal void @__block_param_block_invoke
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+}
+
+@interface Inter
+-(void)m1: (ObjTy *)w;
+@end
+
+@implementation Inter
+-(void)m1: (ObjTy *) w EXT_RET {
+ // CHECK-LABEL: define internal void @"\01-[Inter m1:]"
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+}
+-(void)m2: (ObjTy *) w EXT_RET {
+ // CHECK-LABEL: define internal void @"\01-[Inter m2:]"
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+}
+@end
+
+#if __cplusplus
+// Verify that the decltype(p) is resolved before 'p' is made implicitly const.
+__attribute__((objc_externally_retained))
+void foo(ObjTy *p, decltype(p) *) {}
+// CHECKXX: _Z3fooP5ObjTyPU8__strongS0_
+#endif
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 48a6bac18752..2f4aaa200c55 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -94,6 +94,7 @@
// CHECK-NEXT: ObjCBridgeRelated (SubjectMatchRule_record)
// CHECK-NEXT: ObjCException (SubjectMatchRule_objc_interface)
// CHECK-NEXT: ObjCExplicitProtocolImpl (SubjectMatchRule_objc_protocol)
+// CHECK-NEXT: ObjCExternallyRetained (SubjectMatchRule_variable_not_is_parameter, SubjectMatchRule_function, SubjectMatchRule_block, SubjectMatchRule_objc_method)
// CHECK-NEXT: ObjCMethodFamily (SubjectMatchRule_objc_method)
// CHECK-NEXT: ObjCPreciseLifetime (SubjectMatchRule_variable)
// CHECK-NEXT: ObjCRequiresPropertyDefs (SubjectMatchRule_objc_interface)
diff --git a/clang/test/SemaObjC/externally-retained-no-arc.m b/clang/test/SemaObjC/externally-retained-no-arc.m
new file mode 100644
index 000000000000..a548d6b88ddb
--- /dev/null
+++ b/clang/test/SemaObjC/externally-retained-no-arc.m
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 %s -verify
+
+@interface NSWidget @end
+
+__attribute__((objc_externally_retained)) void f(NSWidget *p) { // expected-warning{{'objc_externally_retained' attribute ignored}}
+ __attribute__((objc_externally_retained)) NSWidget *w; // expected-warning{{'objc_externally_retained' attribute ignored}}
+}
diff --git a/clang/test/SemaObjC/externally-retained.m b/clang/test/SemaObjC/externally-retained.m
new file mode 100644
index 000000000000..2708fc8eefe1
--- /dev/null
+++ b/clang/test/SemaObjC/externally-retained.m
@@ -0,0 +1,114 @@
+// RUN: %clang_cc1 -triple x86_64-apple-macosx10.13.0 -fobjc-runtime=macosx-10.13.0 -fblocks -fobjc-arc %s -verify
+// RUN: %clang_cc1 -triple x86_64-apple-macosx10.13.0 -fobjc-runtime=macosx-10.13.0 -fblocks -fobjc-arc -xobjective-c++ %s -verify
+
+#define EXT_RET __attribute__((objc_externally_retained))
+
+@interface ObjCTy
+@end
+
+void test1() {
+ EXT_RET int a; // expected-warning{{'objc_externally_retained' can only be applied to}}
+ EXT_RET __weak ObjCTy *b; // expected-warning{{'objc_externally_retained' can only be applied to}}
+ EXT_RET __weak int (^c)(); // expected-warning{{'objc_externally_retained' can only be applied to}}
+
+ EXT_RET int (^d)() = ^{return 0;};
+ EXT_RET ObjCTy *e = 0;
+ EXT_RET __strong ObjCTy *f = 0;
+
+ e = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+ f = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+ d = ^{ return 0; }; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+}
+
+void test2(ObjCTy *a);
+
+void test2(ObjCTy *a) EXT_RET {
+ a = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+}
+
+EXT_RET ObjCTy *test3; // expected-warning{{'objc_externally_retained' can only be applied to}}
+
+@interface X // expected-warning{{defined without specifying a base class}} expected-note{{add a super class}}
+-(void)m: (ObjCTy *) p;
+@end
+@implementation X
+-(void)m: (ObjCTy *) p EXT_RET {
+ p = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+}
+@end
+
+void test4() {
+ __attribute__((objc_externally_retained(0))) ObjCTy *a; // expected-error{{'objc_externally_retained' attribute takes no arguments}}
+}
+
+void test5(ObjCTy *first, __strong ObjCTy *second) EXT_RET {
+ first = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+ second = 0; // fine
+}
+
+void test6(ObjCTy *first,
+ __strong ObjCTy *second) EXT_RET {
+ first = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+ second = 0;
+}
+
+__attribute__((objc_root_class)) @interface Y @end
+
+@implementation Y
+- (void)test7:(__strong ObjCTy *)first
+ withThird:(ObjCTy *)second EXT_RET {
+ first = 0;
+ second = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+}
+@end
+
+void (^blk)(ObjCTy *, ObjCTy *) =
+ ^(__strong ObjCTy *first, ObjCTy *second) EXT_RET {
+ first = 0;
+ second = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+};
+
+void test8(EXT_RET ObjCTy *x) {} // expected-warning{{'objc_externally_retained' attribute only applies to variables}}
+
+#pragma clang attribute ext_ret.push(__attribute__((objc_externally_retained)), apply_to=any(function, block, objc_method))
+void test9(ObjCTy *first, __strong ObjCTy *second) {
+ first = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+ second = 0;
+}
+void (^test10)(ObjCTy *first, ObjCTy *second) = ^(ObjCTy *first, __strong ObjCTy *second) {
+ first = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+ second = 0;
+};
+__attribute__((objc_root_class)) @interface Test11 @end
+@implementation Test11
+-(void)meth: (ObjCTy *)first withSecond:(__strong ObjCTy *)second {
+ first = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+ second = 0;
+}
++(void)othermeth: (ObjCTy *)first withSecond:(__strong ObjCTy *)second {
+ first = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+ second = 0;
+}
+@end
+
+#if __cplusplus
+class Test12 {
+ void inline_member(ObjCTy *first, __strong ObjCTy *second) {
+ first = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+ second = 0;
+ }
+ static void static_inline_member(ObjCTy *first, __strong ObjCTy *second) {
+ first = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+ second = 0;
+ }
+};
+#endif
+
+void test13(ObjCTy *first, __weak ObjCTy *second, __unsafe_unretained ObjCTy *third, __strong ObjCTy *fourth) {
+ first = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+ second = 0;
+ third = 0;
+ fourth = 0;
+}
+
+#pragma clang attribute ext_ret.pop