https://github.com/hnrklssn created https://github.com/llvm/llvm-project/pull/195973
This changes the documented semantics of the `noescape` attribute to disallow freeing the pointer, and allow escapes of the integer value of the memory address, as discussed in https://discourse.llvm.org/t/rfc-updating-the-semantics-of-the-noescape-attribute/90326. It also clarifies that the attribute may only be used to annotate the outermost pointer level of nested pointer parameters. >From 86f7d636f5287de881712973f5c1b4e4ac5ca793 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" <[email protected]> Date: Tue, 5 May 2026 18:13:44 -0700 Subject: [PATCH] [docs] update noescape semantics to disallow free This changes the documented semantics of the `noescape` attribute to disallow freeing the pointer, and allow escapes of the integer value of the memory address, as discussed in https://discourse.llvm.org/t/rfc-updating-the-semantics-of-the-noescape-attribute/90326. It also clarifies that the attribute may only be used to annotate the outermost pointer level of nested pointer parameters. --- clang/docs/LifetimeSafety.rst | 1 + clang/docs/ReleaseNotes.rst | 7 ++++ clang/include/clang/Basic/AttrDocs.td | 57 ++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/clang/docs/LifetimeSafety.rst b/clang/docs/LifetimeSafety.rst index c71816dd75a82..2bd44c17b4c36 100644 --- a/clang/docs/LifetimeSafety.rst +++ b/clang/docs/LifetimeSafety.rst @@ -200,6 +200,7 @@ with ``[[clang::lifetimebound]]`` annotated parameters: For more details, see `lifetimebound <https://clang.llvm.org/docs/AttributeReference.html#lifetimebound>`_. +.. _Wlifetime-safety-noescape: NoEscape -------- diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b2e62106506f0..d2994bf9ae97b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -257,6 +257,13 @@ Attribute Changes in Clang ``[[clang::always_inline]]`` with additional checks to ensure that they are only accepted in places where MSVC also does. +- The ``[[clang::noescape]]`` attribute now disallows deallocating memory + through the annotated parameter. This information is currently not exposed to + LLVM for optimization purposes, to prevent breaking existing adopters. It may + instead be used by warnings and static analyses to provide more information + about pointer lifetimes. It may be used to power optimizations in the future, + however there are no concrete plans to do so at the moment. + Improvements to Clang's diagnostics ----------------------------------- - ``-Wunused-but-set-variable`` now diagnoses file-scope variables with diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 502af9b562ef0..59e052431a7a2 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -297,8 +297,14 @@ def NoEscapeDocs : Documentation { the compiler that the pointer cannot escape: that is, no reference to the object the pointer points to that is derived from the parameter value will survive after the function returns. Users are responsible for making sure parameters -annotated with ``noescape`` do not actually escape. Calling ``free()`` on such -a parameter does not constitute an escape. +annotated with ``noescape`` do not actually escape. The optimizer may make +assumptions based on the fact that it knows that a call to the function does +not escape a certain parameter, so incorrectly annotating a parameter with +``noescape`` leads to undefined behavior. The callee is also not allowed to +deallocate memory through a ``noescape`` parameter: the optimizer does not make +assumptions based on this information at the moment, but may do so in the +future. Some cases of invalid uses of ``noescape`` can be found with +:ref:`-Wlifetime-safety-noescape <Wlifetime-safety-noescape>`. For example: @@ -314,6 +320,23 @@ For example: gp = p; // Not OK. } + void freeingFunc(__attribute__((noescape)) int *p) { + free(p); // Not OK. + } + +Since ``noescape`` is a parameter attribute and not a type attribute, it only +applies to the outermost pointer level, regardless of where in the parameter +declaration you place it: + +.. code-block:: c + + int **gp; + + void nestingEscapes(__attribute__((noescape)) int **p) { + gp = p; // Not OK. + *gp = *p; // OK, p does not escape. + } + Additionally, when the parameter is a `block pointer <https://clang.llvm.org/docs/BlockLanguageSpec.html>`, the same restriction applies to copies of the block. For example: @@ -332,6 +355,36 @@ applies to copies of the block. For example: g1 = Block_copy(block); // Not OK either. } +The function *is* allowed to leak information about the memory address of the +pointer, but not any provenance of the allocation: + +.. code-block:: c + + bool isNull(__attribute__((noescape)) void *p) { + return !p; // OK. + } + + uintptr_t gi; + + void escapingAddress(__attribute__((noescape)) int *p) { + // OK *if and only if* gi is never casted back to a pointer. + gi = (uintptr_t)p; + } + + bool usingEscapedAddress(int *p) { + return (uintptr_t)p > gi; // OK. + } + + bool usingEscapedPointer(int *p) { + return p > (int*)gi; // Not OK. + } + + int *gp; + + void escapingEndFunc(__attribute__((noescape)) int *p, size_t len) { + gp = p + len; // Not OK. + } + }]; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
