erik.pilkington updated this revision to Diff 179964.
erik.pilkington marked 10 inline comments as done.
erik.pilkington added a comment.

Address review comments. Also, make the attribute apply to parameters via the 
function declaration instead to parameters directly. i.e.:

  __attribute__((objc_externally_retained(1)))
  void foo(Widget *w) { ... }

The rule being that when this is applied to a non-param variable, it behaves as 
before, but when applied to a function it takes zero or more indices that 
correspond to which parameters are pseudo-strong. If no indices are specified, 
then it applies to every parameter that isn't explicitly __strong. This has a 
couple of advantages:

1. It fixes a more "theoretical" ABI break when used with objective-c++ 
decltype:

  void foo(Widget *w, decltype(w) *) {}

Previously, adding an attribute to `w` would have caused the mangling of `foo` 
to change because `decltype(w)` was `Widget * __strong const`. Now, we wait 
until the `FunctionType` is constructed and the `decltype(w)` is resolved 
before tampering with the parameter type, so this gets the same mangling, but 
we still get the extra safety.

2. It makes it more obvious what this attribute can/can't be applied to. i.e. 
it now is impossible to annotate parameters of function types, which doesn't 
make any sense.

3. We don't have to add special-case hacks to `#pragma clang attribute` to make 
this work. i.e.:

  #pragma clang attribute push (__attribute__((objc_externally_retained)), 
apply_to=any(function,objc_method,block))
  void f(Widget *w, __strong Widget *other) {} // w is externally-retained, 
other is not
  #pragma clang attribute pop

Whereas previously we would have had to add something like 
`apply_to=is_objc_retainable_pointer_parameter_of_function_definition_that_is_implicitly_strong`
 in order to get these semantics.

Thanks for taking a look!


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D55865/new/

https://reviews.llvm.org/D55865

Files:
  clang/docs/AutomaticReferenceCounting.rst
  clang/include/clang/AST/Decl.h
  clang/include/clang/AST/DeclObjC.h
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/CodeGen/CGDecl.cpp
  clang/lib/CodeGen/CGExpr.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaExpr.cpp
  clang/lib/Serialization/ASTReaderDecl.cpp
  clang/lib/Serialization/ASTWriterDecl.cpp
  clang/test/CodeGenObjC/externally-retained.m
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/SemaObjC/externally-retained-no-arc.m
  clang/test/SemaObjC/externally-retained.m

Index: clang/test/SemaObjC/externally-retained.m
===================================================================
--- /dev/null
+++ clang/test/SemaObjC/externally-retained.m
@@ -0,0 +1,127 @@
+// 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))
+#define CALL_RET(...) __attribute__((objc_externally_retained(__VA_ARGS__)))
+
+@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) CALL_RET(1) {
+  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 CALL_RET(1) {
+  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) CALL_RET() {
+  first = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+  second = 0; // fine
+}
+
+void test6(ObjCTy *first,
+           ObjCTy *second,
+           __strong ObjCTy *third) CALL_RET(1, 3) {
+  first = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+  second = 0;
+  third = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+}
+
+__attribute__((objc_root_class)) @interface Y @end
+
+@implementation Y
+- (void)test7:(ObjCTy *)first
+   withSecond:(ObjCTy *)second
+    withThird:(ObjCTy *)third
+  CALL_RET(1, 2) {
+  first = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+  second = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+  third = 0;
+}
+@end
+
+void (^blk)() = ^CALL_RET(1) {}; // expected-error{{'objc_externally_retained' attribute parameter 1 is out of bounds}}
+void (^blk2)(ObjCTy *, ObjCTy *, ObjCTy *) = ^(ObjCTy *first, ObjCTy *second,
+                                               ObjCTy *third) CALL_RET(2, 3) {
+  first = 0;
+  second = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+  third = 0;  // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+};
+
+CALL_RET(1) void test7(__strong ObjCTy *first, ObjCTy *second) {
+  first = 0; // expected-error{{variable declared with 'objc_externally_retained' cannot be modified in ARC}}
+  second = 0;
+}
+
+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
Index: clang/test/SemaObjC/externally-retained-no-arc.m
===================================================================
--- /dev/null
+++ clang/test/SemaObjC/externally-retained-no-arc.m
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 %s -verify
+
+@interface NSWidget @end
+
+__attribute__((objc_externally_retained(1))) void f(NSWidget *p) { // expected-warning{{'objc_externally_retained' attribute ignored}}
+  __attribute__((objc_externally_retained)) NSWidget *w; // expected-warning{{'objc_externally_retained' attribute ignored}}
+}
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ 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)
Index: clang/test/CodeGenObjC/externally-retained.m
===================================================================
--- /dev/null
+++ clang/test/CodeGenObjC/externally-retained.m
@@ -0,0 +1,125 @@
+// 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))
+#define CALL_RET(...) __attribute__((objc_externally_retained(__VA_ARGS__)))
+
+@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) CALL_RET(1) {
+  // 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) CALL_RET(1) {
+  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) CALL_RET(1) {});
+
+  // 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 CALL_RET(1) {
+  // CHECK-LABEL: define internal void @"\01-[Inter m1:]"
+  // CHECK-NOT: llvm.objc.
+  // CHECK: ret
+}
+-(void)m2: (__strong ObjTy *) w CALL_RET(1) {
+  // CHECK-LABEL: define internal void @"\01-[Inter m2:]"
+  // CHECK-NOT: llvm.objc.
+  // CHECK: ret
+}
+@end
+
+#if __cplusplus
+// Verify that the implicit "const" doesn't make it into the name mangling in
+// C++ mode.
+int takes_p(ObjTy *p);
+int call_it = takes_p(0);
+int takes_p(EXT_RET ObjTy *p) {
+  return 0;
+}
+// CHECKXX: _Z7takes_pP5ObjTy
+
+// Verify that the decltype(p) is resolved before 'p' is made implicitly const.
+__attribute__((objc_externally_retained(1))) void foo(ObjTy *p, decltype(p)) {}
+// CHECKXX: _Z3fooP5ObjTyS0_
+
+#endif
Index: clang/lib/Serialization/ASTWriterDecl.cpp
===================================================================
--- clang/lib/Serialization/ASTWriterDecl.cpp
+++ clang/lib/Serialization/ASTWriterDecl.cpp
@@ -922,13 +922,13 @@
   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 @@
   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 @@
   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
Index: clang/lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderDecl.cpp
+++ clang/lib/Serialization/ASTReaderDecl.cpp
@@ -1350,6 +1350,7 @@
   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 @@
     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();
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -11223,17 +11223,23 @@
         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)
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -93,6 +93,17 @@
   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 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,103 @@
                  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 (AL.getNumArgs() > 0) {
+      S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments)
+          << AL << 0;
+      return;
+    }
+
+    if (!VD->hasLocalStorage()) {
+      S.Diag(D->getBeginLoc(), diag::warn_ignored_objc_externally_retained)
+          << 0 << 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), and no
+  // index arguments were used, then we make every parameter psuedo-strong.
+  if (AL.getNumArgs() == 0) {
+    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);
+    return;
+  }
+
+  // Otherwise, walk through the parameters that correspond to the attribute's
+  // arguments, marking them pseudo-strong.
+  for (size_t I = 0, E = AL.getNumArgs(); I != E; ++I) {
+    Expr *AsExpr = AL.getArgAsExpr(I);
+    ParamIdx Idx;
+    if (!checkFunctionOrMethodParameterIndex(S, D, AL, I + 1, AsExpr, Idx))
+      return;
+
+    auto *PVD = const_cast<ParmVarDecl *>(
+        getFunctionOrMethodParam(D, Idx.getASTIndex()));
+    tryMakeVariablePseudoStrong(S, PVD, /*DiagnoseFailure=*/true);
+  }
+
+  handleSimpleAttribute<ObjCExternallyRetainedAttr>(S, D, AL);
+}
+
 //===----------------------------------------------------------------------===//
 // Top Level Sema Entry Points
 //===----------------------------------------------------------------------===//
@@ -6788,6 +6892,10 @@
   case ParsedAttr::AT_Uninitialized:
     handleUninitializedAttr(S, D, AL);
     break;
+
+  case ParsedAttr::AT_ObjCExternallyRetained:
+    handleObjCExternallyRetainedAttr(S, D, AL);
+    break;
   }
 }
 
Index: clang/lib/CodeGen/CGExpr.cpp
===================================================================
--- clang/lib/CodeGen/CGExpr.cpp
+++ clang/lib/CodeGen/CGExpr.cpp
@@ -419,8 +419,12 @@
 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) {
Index: clang/lib/CodeGen/CGDecl.cpp
===================================================================
--- clang/lib/CodeGen/CGDecl.cpp
+++ clang/lib/CodeGen/CGDecl.cpp
@@ -797,15 +797,21 @@
   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 @@
       // 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;
       }
 
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3489,6 +3489,12 @@
 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 or "
+  "functions with parameters "
+  "%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<
@@ -5262,6 +5268,9 @@
 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"
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -3774,3 +3774,47 @@
   (even after inlining) end up hardened.
   }];
 }
+
+def ObjCExternallyRetainedDocs : Documentation {
+  let Category = DocCatVariable;
+  let Content = [{
+The ``objc_externally_retained`` attribute can be applied to strong parameters
+via functions (or blocks or methods) or directly to local variables in ARC mode.
+When applied to parameters, it causes clang to omit the implicit calls to
+``objc_retain`` and ``objc_release`` on a parameter on function entry and exit
+respectively. When applied to local variables, it causes clang to omit the call
+to ``objc_retain`` on variable initialization, and ``objc_release`` when the
+variable goes out of scope. Parameters and variables annotated with this
+attribute are implicitly ``const``. For instance:
+
+.. code-block: objc
+
+  @class WobbleAmount;
+
+  @interface Widget : NSObject
+  -(void)wobble:(WobbleAmount *)amount;
+  @end
+
+  @implementation Widget
+  -(void)wobble:(WobbleAmount *)amount
+      __attribute__((objc_externally_retained(1))) {
+    // 'amount' and 'alias' aren't retained on entry, nor released on exit.
+    __attribute__((objc_externally_retained)) WobbleAmount *alias = amount;
+  }
+  @end
+
+Unlike an ``__unsafe_unretained`` variable, an externally-retained variable
+remains semantically ``__strong``. This affects features like block capture
+which copy the current value of the variable into a capture field of the same
+type: the block's capture field will still be ``__strong``, and so the value
+will be retained as part of capturing it and released when the block is
+destroyed. It also affects C++ features such as lambda capture, ``decltype``,
+and template argument deduction.
+
+This attribute is meant to be used to opt out of the additional safety that ARC
+provides when the programmer knows that the safety isn't necessary. You can read
+more about ARC `here
+<https://clang.llvm.org/docs/AutomaticReferenceCounting.html>`_.
+
+  }];
+}
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -300,6 +300,7 @@
 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 {
@@ -3137,3 +3138,11 @@
   let Subjects = SubjectList<[LocalVar]>;
   let Documentation = [UninitializedDocs];
 }
+
+def ObjCExternallyRetained : InheritableAttr {
+  let LangOpts = [ObjCAutoRefCount];
+  let Spellings = [Clang<"objc_externally_retained">];
+  let Args = [VariadicParamIdxArgument<"Args">];
+  let Subjects = SubjectList<[NonParmVar, Function, Block, ObjCMethod]>;
+  let Documentation = [ObjCExternallyRetainedDocs];
+}
Index: clang/include/clang/AST/DeclObjC.h
===================================================================
--- clang/include/clang/AST/DeclObjC.h
+++ clang/include/clang/AST/DeclObjC.h
@@ -369,6 +369,14 @@
                               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.
Index: clang/include/clang/AST/Decl.h
===================================================================
--- clang/include/clang/AST/Decl.h
+++ clang/include/clang/AST/Decl.h
@@ -866,8 +866,12 @@
     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 @@
     /// 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 @@
     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 {
Index: clang/docs/AutomaticReferenceCounting.rst
===================================================================
--- clang/docs/AutomaticReferenceCounting.rst
+++ clang/docs/AutomaticReferenceCounting.rst
@@ -1734,20 +1734,29 @@
   rest of the language.  Not draining the pool during an unwind is apparently
   required by the Objective-C exceptions implementation.
 
+.. _arc.misc.externally_retained:
+
+``objc_externally_retained`` attribute
+--------------------------------------
+
+If a strong variable is annotated with the ``objc_externally_retained``
+attribute, then the implementation considers the variable to be
+externally-retained. This means that the implementation can assume that the
+variable is retained elsewhere, and therefore can elide retaining and releasing
+it's value. Such a variable is implicitly ``const`` for safety.
+``objc_externally_retained`` can apply to local variables, or to parameters via
+a function definitions.
+
 .. _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 +1767,9 @@
   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 +1778,9 @@
 
 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
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to