[llvm] [LangRef] Do not allow free via synchronization in nofree (PR #195658)

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Mon May 4 07:13:56 PDT 2026


https://github.com/nikic created https://github.com/llvm/llvm-project/pull/195658

The nofree attribute is currently specified to only forbid direct free calls inside the function. A nofree function is still allowed to compel a pointer to be freed by a different thread through synchronization.

This is currently only spelled out for the function-level nofree attribute, but I assume the same semantics also hold for argument nofree (and this matches how the Attributor implementation infers it).

The original motivation for this definition was to keep the attributes orthogonal and independently inferable. However, the problem is that nosync is a too strong condition: It excludes *any* synchronization, not just synchronization that results in the free of a pointer.

Some frontends like Rust can guarantee that most pointer arguments cannot be freed for the duration of a function call, including via synchronization. However, they cannot guarantee that no synchronization takes place at all. The current definition of nofree makes this inexpressible in LLVM IR, which blocks further progress on dereferenceable-at-point semantics.

This PR makes a few changes to the nofree spec:

 * Function-level nofree now disallows free via synchronization.
 * Argument-level nofree now also disallows free via synchronization (it previously made not statement in either direction).
 * Argument-level nofree is now specified to only apply to the particular pointer (similar to `captures` etc). The underlying object may still be freed via a different argument to which the same pointer is passed. (I'm open to changing this, but that would essentially limit inference to noalias arguments.)

Note: This PR doesn't include the necessary inference changes to comply with the new semantics yet. I'd like to make sure we're aligned on the direction first.

>From fe869e57b0d1020f0d2c6a01d814e8222168f7fc Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Mon, 4 May 2026 15:54:09 +0200
Subject: [PATCH] [LangRef] Do not allow free via synchronization in nofree

The nofree attribute is currently specified to only forbid direct
free calls inside the function. A nofree function is still allowed
to compel a pointer to be freed by a different thread through
synchronization.

This is currently only spelled out for the function-level nofree
attribute, but I assume the same semantics also hold for argument
nofree (and this matches how the Attributor implementation infers
it).

The original motivation for this definition was to keep the
attributes orthogonal and independently inferable. However, the
problem is that nosync is a too strong condition: It excludes *any*
synchronization, not just synchronization that results in the
free of a pointer.

Some frontends like Rust can guarantee that most pointer arguments
cannot be freed for the duration of a function call, including via
synchronization. However, they cannot guarantee that no
synchronization takes place at all. The current definition of
nofree makes this inexpressible.

This PR makes a few changes to the nofree spec:

 * Function-level nofree now excludes free via synchronization.
 * Argument-level nofree now also excludes free via synchronization
   (it previously made not statement in either direction).
 * Argument-level nofree is now specified to only apply to the
   particular pointer (similar to `captures` etc). The underlying
   object may still be freed via a different argument to which the
   same pointer is passed. (I'm open to changing this, but that
   would essentially limit inference to noalias arguments.)

Note: This PR doesn't include the necessary inference changes to
comply with the new semantics yet. I'd like to make sure we're
aligned on the direction first.
---
 llvm/docs/LangRef.rst | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 912cca72e1ad5..57e5743dde39e 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1543,8 +1543,11 @@ Currently, only the following parameter attributes are defined:
       is null is captured in some other way.
 
 ``nofree``
-    This indicates that the callee does not free the pointer argument. This is not
-    a valid attribute for return values.
+    This indicates that the callee does not free the pointer argument, or
+    cause it to be freed through synchronization.
+
+    This is not a valid attribute for return values. This attribute applies
+    only to the particular copy of the pointer passed in this argument.
 
 .. _nest:
 
@@ -2328,20 +2331,17 @@ For example:
 ``nofree``
     This function attribute indicates that the function does not, directly or
     transitively, call a memory-deallocation function (``free``, for example)
-    on a memory allocation which existed before the call.
+    on a memory allocation which existed before the call, or make such a call
+    visible through synchronization.
 
-    As a result, uncaptured pointers that are known to be dereferenceable
-    prior to a call to a function with the ``nofree`` attribute are still
-    known to be dereferenceable after the call. The capturing condition is
-    necessary in environments where the function might communicate the
-    pointer to another thread which then deallocates the memory.  Alternatively,
-    ``nosync`` would ensure such communication cannot happen and even captured
-    pointers cannot be freed by the function.
+    As a result, pointers that are known to be dereferenceable prior to a call
+    to a function with the ``nofree`` attribute are still known to be
+    dereferenceable after the call.
 
     A ``nofree`` function is explicitly allowed to free memory which it
-    allocated or (if not ``nosync``) arrange for another thread to free
-    memory on its behalf.  As a result, perhaps surprisingly, a ``nofree``
-    function can return a pointer to a previously deallocated
+    allocated or arrange for another thread to free such memory on its behalf.
+    As a result, perhaps surprisingly, a ``nofree`` function can return a
+    pointer to a previously deallocated
     :ref:`allocated object<allocatedobjects>`.
 ``noimplicitfloat``
     Disallows implicit floating-point code. This inhibits optimizations that



More information about the llvm-commits mailing list