Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1337,6 +1337,12 @@ let Documentation = [Undocumented]; } +def NoEscape : InheritableAttr { + let Spellings = [GNU<"noescape">, CXX11<"clang", "noescape">]; + let Subjects = SubjectList<[ParmVar]>; + 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,40 @@ }]; } +def NoEscapeDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +``noescape`` placed on a function parameter of block type is used to inform the +compiler that the block cannot escape: that is, no reference to the block +derived from the parameter value will survive after the function returns. + +For example: + +.. code-block:: c + + typedef void (^BlockTy)(); + BlockTy g; + + void nonescapingFunc(__attribute__((noescape)) BlockTy block) { + block(); // OK. + } + + void escapingFunc(__attribute__((noescape)) BlockTy block) { + g = block; // Not OK. g can be invoked after the function returns. + } + + void caller() { + escapingFunc(^{}); + g(); + } + +The compiler may do optimizations or issue improved diagnostics using this +knowledge. Users are responsible for making sure parameters annotated with +``noescape`` do not actuallly escape. + + }]; +} + def CarriesDependencyDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -3109,6 +3109,10 @@ "code; pointer may be assumed to always convert to true">, InGroup; +def warn_attribute_noescape_non_pointer : Warning< + "'noescape' attribute ignored on parameter of non-pointer type %0">, + InGroup; + 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/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -1516,6 +1516,19 @@ Attr.getAttributeSpellingListIndex())); } +static void handleNoEscapeAttr(Sema &S, Decl *D, const AttributeList &Attr) { + // noescape only applies to pointer types. + QualType T = cast(D)->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), @@ -6040,6 +6053,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: test/SemaObjCXX/noescape.mm =================================================================== --- /dev/null +++ test/SemaObjCXX/noescape.mm @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -std=c++11 %s + +typedef void (^BlockTy)(); + +void noescapeFunc0(id, __attribute__((noescape)) BlockTy); +void noescapeFunc1(id, [[clang::noescape]] BlockTy); +void invalidFunc(int __attribute__((noescape))); // expected-warning {{'noescape' attribute ignored on parameter of non-pointer type}} +int __attribute__((noescape)) g; // expected-warning {{'noescape' attribute only applies to parameters}}