[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
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 63501d9..d1db718 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/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,79 @@
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 @@
case ParsedAttr::AT_Uninitialized:
handleUninitializedAttr(S, D, AL);
break;
+
+ case ParsedAttr::AT_ObjCExternallyRetained:
+ handleObjCExternallyRetainedAttr(S, D, AL);
+ break;
}
}