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 @@ -1943,12 +1943,18 @@ let Documentation = [Undocumented]; } -def NoEscape : Attr { +def NoEscape : InheritableAttr { let Spellings = [Clang<"noescape">]; let Subjects = SubjectList<[ParmVar]>; let Documentation = [NoEscapeDocs]; } +def Escape : InheritableAttr { + 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,19 @@ return; } - D->addAttr(::new (S.Context) NoEscapeAttr(S.Context, AL)); + switch (AL.getKind()) { + default: + llvm_unreachable("invalid escape attribute"); + case ParsedAttr::AT_NoEscape: + // escape/noescape are mutually exclusive attributes. + if (!checkAttrMutualExclusion(S, D, AL)) + handleSimpleAttribute(S, D, AL); + return; + case ParsedAttr::AT_Escape: + if (!checkAttrMutualExclusion(S, D, AL)) + handleSimpleAttribute(S, D, AL); + return; + } } static void handleAssumeAlignedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -8007,8 +8019,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 diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -61,6 +61,7 @@ // CHECK-NEXT: EnforceTCB (SubjectMatchRule_function) // CHECK-NEXT: EnforceTCBLeaf (SubjectMatchRule_function) // CHECK-NEXT: EnumExtensibility (SubjectMatchRule_enum) +// CHECK-NEXT: Escape (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: ExcludeFromExplicitInstantiation (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record) // CHECK-NEXT: ExternalSourceSymbol ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable)) // CHECK-NEXT: FlagEnum (SubjectMatchRule_enum) diff --git a/clang/test/SemaObjCXX/escape.mm b/clang/test/SemaObjCXX/escape.mm new file mode 100644 --- /dev/null +++ b/clang/test/SemaObjCXX/escape.mm @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -std=c++1z %s + +struct S {}; + +template +void escapeFunc0(__attribute__((noescape)) T); // expected-warning {{'noescape' attribute only applies to pointer arguments}} +template +void escapeFunc1(__attribute__((escape)) const T &); +template +void escapeFunc2(__attribute__((escape)) T &&); + +void escapeFunc3(__attribute__((escape)) int *); +void escapeFunc4(__attribute__((escape)) void *); +void escapeFunc5(__attribute__((escape)) int **); + +void invalidFunc0(int __attribute__((escape))); // expected-warning {{'escape' attribute only applies to pointer arguments}} +void invalidFunc1(int __attribute__((escape(0)))); // expected-error {{'escape' attribute takes no arguments}} +void invalidFunc2(__attribute__((escape)) int (S::*Ty)); // expected-warning {{'escape' attribute only applies to pointer arguments}} +void invalidFunc3(__attribute__((escape)) void (S::*Ty)()); // expected-warning {{'escape' attribute only applies to pointer arguments}} + +void invalidFunc4(__attribute__((escape)) __attribute__((noescape)) int *); // expected-error {{'noescape' and 'escape' attributes are not compatible}} \ +// expected-note {{conflicting attribute is here}} +void invalidFunc5(__attribute__((noescape)) __attribute__((escape)) int *); // expected-error {{'escape' and 'noescape' attributes are not compatible}} \ +// expected-note {{conflicting attribute is here}} + +int __attribute__((escape)) g; // expected-warning {{'escape' attribute only applies to parameters}} +int * __attribute__((escape)) h; // expected-warning {{'escape' attribute only applies to parameters}}