diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1949,6 +1949,12 @@ let Documentation = [NoEscapeDocs]; } +def Escape : Attr { + let Spellings = [Clang<"escape">]; + let Subjects = SubjectList<[ParmVar]>; + let Documentation = [EscapeDocs]; +} + def AssumeAligned : InheritableAttr { let Spellings = [GCC<"assume_aligned">]; let Subjects = SubjectList<[ObjCMethod, Function]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -253,6 +253,27 @@ }]; } +def EscapeDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +``escape`` placed on a function parameter of a pointer type is used to indicate +that the pointer can escape the function. This means that a reference to the object +the pointer points to that is derived from the parameter value may survive +after the function returns. + +For example: + +.. code-block:: c + + int *gp; + + void escapingFunc(__attribute__((escape)) int *p) { + gp = p; + } + + }]; +} + def CarriesDependencyDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -1553,11 +1553,11 @@ D->addAttr(::new (S.Context) ReturnsNonNullAttr(S.Context, AL)); } -static void handleNoEscapeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { +static void handleXEscapeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (D->isInvalidDecl()) return; - // noescape only applies to pointer types. + // escape/noescape only applies to pointer types. QualType T = cast(D)->getType(); if (!S.isValidPointerAttrType(T, /* RefOkay */ true)) { S.Diag(AL.getLoc(), diag::warn_attribute_pointers_only) @@ -1565,7 +1565,16 @@ return; } - D->addAttr(::new (S.Context) NoEscapeAttr(S.Context, AL)); + switch (AL.getKind()) { + default: + llvm_unreachable("invalid escape attribute"); + case ParsedAttr::AT_NoEscape: + handleSimpleAttribute(S, D, AL); + return; + case ParsedAttr::AT_Escape: + handleSimpleAttribute(S, D, AL); + return; + } } static void handleAssumeAlignedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -8007,8 +8016,9 @@ case ParsedAttr::AT_ReturnsNonNull: handleReturnsNonNullAttr(S, D, AL); break; + case ParsedAttr::AT_Escape: case ParsedAttr::AT_NoEscape: - handleNoEscapeAttr(S, D, AL); + handleXEscapeAttr(S, D, AL); break; case ParsedAttr::AT_AssumeAligned: handleAssumeAlignedAttr(S, D, AL); diff --git a/clang/test/AST/ast-dump-attr.cpp b/clang/test/AST/ast-dump-attr.cpp --- a/clang/test/AST/ast-dump-attr.cpp +++ b/clang/test/AST/ast-dump-attr.cpp @@ -200,6 +200,15 @@ // CHECK-NEXT: NoEscapeAttr } +namespace TestEscape { + void escapeFunc(int *p0, __attribute__((escape)) int *p1) {} + // CHECK: NamespaceDecl{{.*}} TestEscape + // CHECK-NEXT: `-FunctionDecl{{.*}} escapeFunc 'void (int *, int *)' + // CHECK-NEXT: ParmVarDecl + // CHECK-NEXT: ParmVarDecl + // CHECK-NEXT: EscapeAttr +} + namespace TestSuppress { [[gsl::suppress("at-namespace")]]; // CHECK: NamespaceDecl{{.*}} TestSuppress diff --git a/clang/test/AST/ast-dump-attr.m b/clang/test/AST/ast-dump-attr.m --- a/clang/test/AST/ast-dump-attr.m +++ b/clang/test/AST/ast-dump-attr.m @@ -65,4 +65,8 @@ // CHECK-NEXT: | `-NoEscapeAttr // CHECK-NEXT: |-ParmVarDecl{{.*}} Test14 'int' // CHECK-NEXT: `-NSConsumesSelfAttr +-(void)Test15: (int *) [[clang::escape]] Test16; +// CHECK: ObjCMethodDecl{{.*}} Test15: 'void' +// CHECK-NEXT: -ParmVarDecl{{.*}} Test16 'int *' +// CHECK-NEXT: `-EscapeAttr @end