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