Follow-up to https://github.com/swiftlang/swift/pull/88733,
enabling the example in rdar://172511809 ([nonescapable] Allow a
nonescaping function to be a lifetime dependency source):
```swift
@_lifetime(body) // Inferred dependence kind: copy
func foo(body: () -> Span<Int>) { body() }
```
or
```swift
// Inferred: @_lifetime(copy body)
func foo(body: () -> Span<Int>) { body() }
```
Follow-up: Consider also disallowing borrow dependence on `@noescape`
closures.
<!--
If this pull request is targeting a release branch, please fill out the
following form:
https://github.com/swiftlang/.github/blob/main/PULL_REQUEST_TEMPLATE/release.md?plain=1
Otherwise, replace this comment with a description of your changes and
rationale. Provide links to external references/discussions if
appropriate.
If this pull request resolves any GitHub issues, link them like so:
Resolves <link to issue>, resolves <link to another issue>.
For more information about linking a pull request to an issue, see:
https://docs.github.com/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue
-->
<!--
Before merging this pull request, you must run the Swift continuous
integration tests.
For information about triggering CI builds via @swift-ci, see:
https://github.com/apple/swift/blob/main/docs/ContinuousIntegration.md#swift-ci
Thank you for your contribution to Swift!
-->
---------
Co-authored-by: Andrew Trick <atrick@apple.com>
We need to follow up to avoid linear memory growth. This is not a major problem
today because AllocateCopy will almost always be called with zero size.
- rdar://169975618 ([nonescapable] Type conversion for function types with lifetime dependencies)
- rdar://160970376 ([nonescapable] [type checker] [source compat] protocol requirements should require matching methods signatures, including @lifetime)
A recent change to lifetime type checking affected the way lifetimes are
inferred for static methods resulting in diagnostic errors for accessors that
returned a non-Escapable type.
Add a special lifetime default for accessors, such as:
/* DEFAULT: @_lifetime(immortal) */
static let globalSpan = globalArray.span
Explicitly writing the lifetime annotation for accessors is much to
cumbersome (it forces a computed property with a getter). This also fixes a
regression in compiler behavior, even though the original behavior was
unintentional.
Fixes rdar://171283240 error: the 'get' accessor with a ~Escapable result needs
a parameter to depend on)
A nonescaping closure that returns a ~Escapable result and has no @_lifetime
annotation is assumed to depend on its closure context.
This fixes overly strict type checking introduced by:
commit e98a7a6bf8
Date: Fri Feb 6 02:09:25 2026
Merge pull request #86842
LifetimeDependence: Support function types
Fix:
1. Disable missing dependency diagnostics for ~Escapable results
Continue allow the most basic nonescaping closure types without annotation:
func f(body: () -> NE) -> NE
2. Disable single-parameter inference for function types
Do *not* infer a default parameter dependency here:
func f(body: (AnyObject) -> NE) -> NE
The single-parameter rule should only apply to unambiguous function
declarations. It never applies to function types. Function types should instead
infer a dependency on the closure context.
Fixes rdar://171031632 ([nonescapable] allow nonescaping closures to return
non-Escapable types)
The formal types of all methods are curried:
- Instance method: (Self) -> (Params...) -> Result
- Static method: (Self.Type) -> (Params...) -> Result
To be able to express lifetime dependencies on the self parameter, the lifetime
dependence information is computed as though the type has been uncurried (which
it will be during SILGen), and attached to the outer function type:
(Params..., Self) -> Result
This causes the lifetime dependence information to be lost when referring to the
inner function type (which is of more interest in most cases). Representing the
lifetimes this way is (mostly) unnecessary for static methods, since there is no
instance of the type to depend on.
We now attach lifetime dependence information to the inner function type for
static methods. This allows us to convert static methods to their inner function
types without losing their lifetime dependence information.
Once we add support for dependencies on the captured context, we will be able to
use a similar representation for instance methods.
The only complication is that the lifetime information attached to the inner
function type must be "uncurried" when lowering static methods to SIL. This will
also be necessary for closures when we add support for dependencies on the
closure context. See LifetimeDependenceInfo::uncurry.
Example:
struct S {
static func f(ne: NE) -> NE { ne }
}
- Before: S.f had type @_lifetime(2: copy 0) (S.Type) -> (NE) -> NE.
- Now: S.f has type (S.Type) -> @_lifetime(copy 0) (NE) -> NE.
In the short term, this makes it impossible to express lifetime dependencies on
the self parameter of a static method (i.e. S.Type), which should almost never
be necessary.
Non-Escapable 'inout' arguments have a default self-dependency, regardless of
any other annotations. For example:
@_lifetime(dest: copy source)
/* DEFAULT: @_lifetime(dest: copy dest, copy source) */
func foo<T: ~Escapable>(dest: inout T, source: T)
An immortal lifetime specifier now suppresses that default. For example:
@_lifetime(dest: immortal, copy source)
/* DEFAULT: @_lifetime(dest: copy source) */
func foo<T: ~Escapable>(dest: inout T, source: T)
This is necessary because there is otherwise no other way to suppress the
default lifetime.
Fixes rdar://170016708 ([nonescapable] Support @_lifetime(immortal) to suppress
the usual inout default self-dependency)
Assume a default dependency for all 'inout' parameters regardless of the
presence of an explicit annotation.
Default to `@lifetime(inoutArg: copy inoutArg)` even if an explicit annotation
exists.
This way API authors do not need to make the inout dependency explicit when they
probably always want that and don't normally need to write it.
Now:
```
@_lifetime(a: copy b)
func foo(a: inout T, b: T)
```
implies:
```
@_lifetime(a: copy a, copy b)
func foo(a: inout T, b: T)
```
Fixes rdar://169835235 (Always default inout dependencies)
To a function type's lifetimes, a base version of the type is first created with
no lifetime dependence info. This is then passed to the dependence checker, and
the resulting dependencies are added to it.
It would be possible to do this analysis by passing just the parameter list and
result type (which are available before the type is created), but this approach
lets us avoid dealing with a header inclusion cycle between Types.h, ExtInfo.h,
and LifetimeDependence.h, since it does not require AnyFunctionType::Param to be
defined in LifetimeDependence.h.
The isFromAnnotation flag is set if and only if the lifetime originates from a
@lifetime or @_lifetime annotation in the source program.
isFromAnnotation==false means that the lifetime dependence checker would infer
the same lifetime if the Swift type or decl was printed without an annotation
for that dependency. More specifically, it means that the depenence was inferred
by the lifetime dependence checker.
Some dependencies on imported C/C++ decls are "inferred", but they either
correspond to explicit lifetime information in the source (smart pointers,
lifetimebound attribute) or are likely to differ from what the dependence
checker would infer. As such, we set the flag to true for all of them.
This addresses feedback from #86560:
- Remove redundant collectParameterInfo call
- Consistent naming of ParamInfo variables
- Explicit type name instead of decltype
Also add nullptr checks on afd, as appropriate.
The only intended functional change is that a diagnostic message is split into
an error and a note:
error: cannot copy the lifetime of an Escapable type
note: use '@_lifetime(borrow arg)' instead
Reorganize the LifetimeDependenceChecker to support adding the functionality
required for strict enforcement.
Adds parameter index to ParamInfo.
Adds a few (temporarily unused) helper methods.
All relevant information about each parameter, including the implicit self
parameter, is now represented by an instance of the new ParamInfo type.
By pre-computing the "contextualized" parameter types and the source file, we
eliminate the last few cases where a DeclContext is needed, aside from those
where an AbstractFunctionDecl is needed.
This allows us to eliminate functionDC, and instead store a
AbstractFunctionDecl*, which can be set to NULL when checking lifetimes of
non-AFD types. This removes the potential for subtle bugs when using dynamic
casts to determine whether we are checking a particular subtype of AFD (e.g.
when checking a function type declared inside a constructor).
This partly reverts #86386 by re-introducing the afd member variable, but it is
now necessary to check if afd is NULL before using it.
This provides a basis for extending LifetimeDependence to work with closures and
function types (see #85414).
As much information as possible is extracted from the AbstractFunctionDecl in
the constructor, so most of the business logic of the pass can be reused.
Parameters are converted to AnyFunctionType::Param because
AnyFunctionType::Param is used function type parameters and ParamDecl can be
converted to it but not vice versa.
This updates a large number of internal symbols, function names,
and types to match the final approved terminology. Matching the
surface language terminology and the compiler internals should
make the code easier for people to understand into the future.
Lifetime dependence checking for enum elements is simple, and almost entirely
contained within this method. Making it a static method separates it from the
mostly unrelated code for AbstractFunctionDecl.
We could have made it a stand-alone function; this just minimises the diff size.
This is a preliminary step in refactoring LifetimeDependenceChecker to support
closures.
[ASTPrinter] Escape @_lifetime arguments when needed
Printing a LifetimeDescriptor would never wrap it in backticks (even if originally wrapped in backticks). This would result in the output not being able to be parsed
rdar://159992995
Infer @_lifetime(self: copy self) for mutating methods without requiring the
experimental Lifetimes feature to be disabled. Treatment of mutating 'self' is
now consistent with other 'inout' parameters.
Example:
extension MutableSpan {
mutating func mutatingMethod() {...}
}
Restructure the inference logic to allow the same function declaration to
independently annotate or infer different lifetime targets.
For example:
@_lifetime(borrow a)
/* DEFAULT: @_lifetime(b: copy b)
func f(a: A, b: inout B) -> NE
The fact that we prevented this was somewhat accidental and surprising.
This restructuring simplifies the code but also gives us much more control over
the inference logic.
Fixes: rdar://159288750 ("A function cannot have a ~Escapable 'inout' parameter
in addition to other ~Escapable parameters" is not actionable)
Non-escapable struct definitions often have inicidental integer fields that are
unrelated to lifetime. Without an explicit initializer, the compiler would infer
these fields to be borrowed by the implicit intializer.
struct CountedSpan: ~Escapable {
let span: Span<Int>
let i: Int
/* infer: @lifetime(copy span, borrow i) init(...) */
}
This was done because
- we always want to infer lifetimes of synthesized code if possible
- inferring a borrow dependence is always conservative
But this was the wrong decision because it inevitabely results in lifetime
diagnostic errors elsewhere in the code that can't be tracked down at the use
site:
let span = CountedSpan(span: span, i: 3) // ERROR: span depends on the lifetime of this value
Instead, force the author of the data type to specify whether the type actually
depends on trivial fields or not. Such as:
struct CountedSpan: ~Escapable {
let span: Span<Int>
let i: Int
@lifetime(copy span) init(...) { ... }
}
This fix enables stricter diagnostics, so we need it in 6.2.
Fixes rdar://152130977 ([nonescapable] confusing diagnostic message when a
synthesized initializer generates dependence on an Int parameter)
Users commonly try to write a lifetime dependency on an 'inout' parameters as:
@_lifetime(a: &a)
func f_inout_useless(a: inout MutableRawSpan) {}
This is useless. Guide them toward what they really wanted:
@_lifetime(a: copy a)
Fixes rdar://151618856 (@lifetime(..) gives inconsistent error messages)
Correctly diagnose this as:
"invalid use of inout dependence on the same inout parameter
@_lifetime(a: &a)
func f_inout_useless(a: inout MutableRawSpan) {}
Correctly diagnose this as:
"lifetime-dependent parameter must be 'inout'":
@_lifetime(a: borrow a)
func f_inout_useless(a: borrowing MutableRawSpan) {}
This comes up often when passing a MutableSpan as an 'inout' argument. The
vague diagnostic was causing developers to attempt incorrect @_lifetime
annotations. Be clear about why the annotation is needed and which annotation
should be used.
This avoids diagnostic errors on synthesized accessors, which are impossible for developers to understand.
Fixes rdar://153793344 (Lifetime-dependent value returned by generated accessor '_read')
This fixes a small oversight in the type checker's LifetimeDependence
inference. Allow inference on _read accessors even when 'self' is a trivial
type. This is needed because the compiler synthesizes a _read accessor even when
the user defines a getter (this is probably a mistake, but it's easire to just
fix inference at this point). There is no workaround because it defining both a
getter and '_read' is illegal!
extension UnsafeMutableRawBufferPointer {
var mutableBytes: MutableRawSpan {
@_lifetime(borrow self)
get {
unsafe MutableRawSpan(_unsafeBytes: self)
}
}
}
Fixes rdar://153346478 (Can't compile the
UnsafeMutableRawBufferPointer.mutableBytes property)