Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1218,6 +1218,12 @@ let Documentation = [Undocumented]; } +def NoEscape : InheritableAttr { + let Spellings = [GCC<"noescape">, CXX11<"clang", "noescape">]; + let Subjects = SubjectList<[ParmVar], WarnDiag, "ExpectedParameter">; + let Documentation = [NoEscapeDocs]; +} + def AssumeAligned : InheritableAttr { let Spellings = [GCC<"assume_aligned">]; let Subjects = SubjectList<[ObjCMethod, Function]>; Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -112,6 +112,32 @@ }]; } +def NoEscapeDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +``noescape`` placed on a block parameter is used to inform the compiler that the block passed to a function cannot escape: that is, the block will not be invoked after the function returns. To ensure that the block does not escape, clang imposes the following restrictions on usage of non-escaping blocks: + +* Cannot be passed to a function expecting an escaping block +* Cannot be returned from a function +* Cannot be captured by a block +* Cannot be assigned to a variable + +For example: + +.. code-block:: c + + typedef void (^BlockTy)(); + void nonescapingFunc(__attribute__((noescape)) BlockTy); + void escapingFunc(BlockTy); + + void callerFunc(__attribute__((noescape)) BlockTy block) { + nonescapingFunc(block); // OK + escapingFunc(block); // error: parameter is not annotated with noescape + } + + }]; +} + def CarriesDependencyDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -3090,6 +3090,21 @@ "code; pointer may be assumed to always convert to true">, InGroup; +def err_noescape_passed_to_call : Error< + "cannot pass non-escaping parameter '%0' to function or method expecting escaping " + "argument">; +def err_noescape_returned : Error< + "cannot return non-escaping parameter '%0' from function">; +def err_noescape_assignment : Error< + "cannot assign non-escaping parameter '%0' to escaping variable">; +def err_noescape_block_capture : Error< + "block cannot capture non-escaping parameter '%0'">; +def warn_attribute_noescape_non_pointer : Warning< + "'noescape' attribute ignored on parameter of non-pointer type %0">, + InGroup; +def note_noescape_parameter : Note< + "parameter is %select{annotated|not annotated}0 with noescape">; + def warn_null_pointer_compare : Warning< "comparison of %select{address of|function|array}0 '%1' %select{not |}2" "equal to a null pointer is always %select{true|false}2">, Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -2538,6 +2538,39 @@ } } +template +static void checkNoescapeArguments(Sema &S, const DeclType *FDecl, + ArrayRef Args, + SourceLocation CallSiteLoc) { + ArrayRef Params = FDecl->parameters(); + + for (unsigned I = 0, E = Args.size(); I < E ; ++I) { + const auto *CalleePD = I < Params.size() ? Params[I] : nullptr; + + // If this is not a variadic argument and the callee's parameter is marked + // noescape, continue. + if (CalleePD && CalleePD->hasAttr()) + continue; + + if (!Args[I]) + continue; + + if (const auto *DRE = dyn_cast(Args[I]->IgnoreImpCasts())) { + const auto *CallerPD = DRE->getDecl(); + if (CallerPD->hasAttr()) { + S.Diag(Args[I]->getExprLoc(), diag::err_noescape_passed_to_call) + << CallerPD->getName(); + S.Diag(CallerPD->getLocation(), diag::note_noescape_parameter) << 0; + if (CalleePD) + S.Diag(CalleePD->getLocation(), diag::note_noescape_parameter) << 1; + else + assert(FDecl->isVariadic() && + "Called function expected to be variadic"); + } + } + } +} + /// Handles the checks for format strings, non-POD arguments to vararg /// functions, NULL arguments passed to non-NULL parameters, and diagnose_if /// attributes. @@ -2592,6 +2625,16 @@ } } + if (FDecl) { + if (const auto *FD = dyn_cast(FDecl)) + checkNoescapeArguments(*this, FD, Args, Loc); + else if (const auto *OD = dyn_cast(FDecl)) + checkNoescapeArguments(*this, OD, Args, Loc); + else if (const auto *VD = dyn_cast(FDecl)) + if (const auto *BE = dyn_cast_or_null(VD->getInit())) + checkNoescapeArguments(*this, BE->getBlockDecl(), Args, Loc); + } + if (FD) diagnoseArgDependentDiagnoseIfAttrs(FD, ThisArg, Args, Loc); } Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -1516,6 +1516,23 @@ Attr.getAttributeSpellingListIndex())); } +static void handleNoEscapeAttr(Sema &S, Decl *D, const AttributeList &Attr) { + ParmVarDecl *PD = dyn_cast(D); + if (!PD) + return; + + // noescape only applies to pointer types. + QualType T = PD->getType(); + if (!T->isAnyPointerType() && !T->isBlockPointerType() && + !T->isReferenceType() && !T->isArrayType() && !T->isMemberPointerType()) { + S.Diag(Attr.getLoc(), diag::warn_attribute_noescape_non_pointer) << T; + return; + } + + D->addAttr(::new (S.Context) NoEscapeAttr( + Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); +} + static void handleAssumeAlignedAttr(Sema &S, Decl *D, const AttributeList &Attr) { Expr *E = Attr.getArgAsExpr(0), @@ -6049,6 +6066,9 @@ case AttributeList::AT_ReturnsNonNull: handleReturnsNonNullAttr(S, D, Attr); break; + case AttributeList::AT_NoEscape: + handleNoEscapeAttr(S, D, Attr); + break; case AttributeList::AT_AssumeAligned: handleAssumeAlignedAttr(S, D, Attr); break; Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -11174,6 +11174,18 @@ DiagnoseSelfAssignment(*this, LHS.get(), RHS.get(), OpLoc); DiagnoseSelfMove(LHS.get(), RHS.get(), OpLoc); } + + if (RHS.get()) + if (const auto *DRE = + dyn_cast_or_null(RHS.get()->IgnoreImpCasts())) { + const auto *D = DRE->getDecl(); + if (D->hasAttr()) { + Diag(DRE->getLocation(), diag::err_noescape_assignment) + << D->getName(); + Diag(D->getLocation(), diag::note_noescape_parameter) << 0; + } + } + RecordModifiableNonNullParam(*this, LHS.get()); break; case BO_PtrMemD: @@ -13666,6 +13678,11 @@ return false; } + if (Var->hasAttr()) { + S.Diag(Loc, diag::err_noescape_block_capture) << Var->getName(); + S.Diag(Var->getLocation(), diag::note_noescape_parameter) << 0; + } + // Warn about implicitly autoreleasing indirect parameters captured by blocks. if (const auto *PT = CaptureType->getAs()) { // This function finds out whether there is an AttributedType of kind Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -3199,6 +3199,14 @@ const AttrVec *Attrs = nullptr; bool isObjCMethod = false; + if (const auto *DRE = dyn_cast_or_null(RetValExp)) { + const auto *D = DRE->getDecl(); + if (D->hasAttr()) { + Diag(DRE->getLocation(), diag::err_noescape_returned) << D->getName(); + Diag(D->getLocation(), diag::note_noescape_parameter) << 0; + } + } + if (const FunctionDecl *FD = getCurFunctionDecl()) { FnRetType = FD->getReturnType(); if (FD->hasAttrs()) Index: test/SemaObjCXX/noescape.mm =================================================================== --- /dev/null +++ test/SemaObjCXX/noescape.mm @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -std=c++11 %s + +typedef void (^BlockTy)(); + +void noescapeFunc(id, __attribute__((noescape)) BlockTy); +void escapeFunc(id, BlockTy); // expected-note {{parameter is not annotated with noescape}} +void noescapeFuncId(id, __attribute__((noescape)) id); +void escapeFuncId(id, id); // expected-note {{parameter is not annotated with noescape}} +void variadicFunc(int, ...); +void invalidFunc0(int __attribute__((noescape))); // expected-warning {{'noescape' attribute ignored on parameter of non-pointer type}} + +__attribute__((objc_root_class)) @interface C +-(BlockTy)noescapeMethod:(id)a blockArg:(__attribute__((noescape)) BlockTy)b; +-(void)escapeMethod:(id)a blockArg:(BlockTy)b; // expected-note {{parameter is not annotated with noescape}} +@end + +@implementation C +-(BlockTy)noescapeMethod:(id)a blockArg:(__attribute__((noescape)) BlockTy)b { // expected-note {{parameter is annotated with noescape}} + return b; // expected-error {{cannot return non-escaping parameter}} +} +-(void)escapeMethod:(id)a blockArg:(BlockTy)b { +} +@end + +struct S { + void noescapeMethod(__attribute__((noescape)) BlockTy); + void escapeMethod(BlockTy); // expected-note {{parameter is not annotated with noescape}} +}; + +BlockTy test1([[clang::noescape]] BlockTy neb, BlockTy eb, C *c1, S *s1) { // expected-note 11 {{parameter is annotated with noescape}} + id i; + BlockTy t; + t = neb; // expected-error {{cannot assign non-escaping parameter}} + t = eb; + (void)^{ neb(); }; // expected-error 3 {{block cannot capture non-escaping parameter}} + (void)^{ eb(); }; + auto noescapeBlock = ^(__attribute__((noescape)) BlockTy neb) {}; + auto escapeBlock = ^(BlockTy eb) {}; // expected-note {{parameter is not annotated with noescape}} + noescapeBlock(neb); + noescapeBlock(eb); + escapeBlock(neb); // expected-error {{cannot pass non-escaping parameter}} + escapeBlock(eb); + noescapeFunc(i, neb); + noescapeFunc(i, eb); + escapeFunc(i, neb); // expected-error {{cannot pass non-escaping parameter}} + escapeFunc(i, eb); + noescapeFuncId(i, neb); + noescapeFuncId(i, eb); + escapeFuncId(i, neb); // expected-error {{cannot pass non-escaping parameter}} + escapeFuncId(i, eb); + variadicFunc(1, neb); // expected-error {{cannot pass non-escaping parameter}} + variadicFunc(1, eb); + [c1 noescapeMethod:i blockArg:neb]; + [c1 noescapeMethod:i blockArg:eb]; + [c1 escapeMethod:i blockArg:neb]; // expected-error {{cannot pass non-escaping parameter}} + [c1 escapeMethod:i blockArg:eb]; + s1->noescapeMethod(neb); + s1->noescapeMethod(eb); + s1->escapeMethod(neb); // expected-error {{cannot pass non-escaping parameter}} + s1->escapeMethod(eb); + return neb; // expected-error {{cannot return non-escaping parameter}} +}