Commit Graph

119 Commits

Author SHA1 Message Date
Aidan Hall 5149dbcd29 Lifetimes: Infer copy dependence kind on @noescape closures (#88879)
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>
2026-05-15 11:34:36 +01:00
Aidan Hall 56de5913ed LifetimeDependenceInfo::partialApply: allow fewer indices than formal params 2026-05-13 15:48:26 +01:00
Andrew Trick 7e3b90b719 Add a FIXME in LifetimeDependenceInfo::partialApply.
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.
2026-05-13 15:48:26 +01:00
Andrew Trick ad949f8ff3 Fix an assert in LifetimeDependenceInfo::partialApply.
Allow partial_applies with no captures.
2026-05-13 15:48:26 +01:00
Aidan Hall 280eed8b7d Lifetimes: Replace deps on partial_apply parameters with 'captures' 2026-05-13 15:48:23 +01:00
Aidan Hall d01dcfe628 Lifetimes: Refactor SIL & Swift lifetime printing into a unified implementation 2026-05-06 11:02:48 +01:00
Aidan Hall d779a13f2d LifetimeDependence: Closure capture dependencies 2026-04-24 16:14:21 +01:00
Aidan Hall 0659f07ab9 LifetimeDependence: Lifetime subtyping for type and witness matching
- 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)
2026-03-23 12:53:09 +00:00
Aidan Hall 372ef0f470 LifetimeDependence: Don't infer immortal for Escapable result. 2026-03-04 17:08:21 +00:00
Andrew Trick 0c05634156 Fix lifetime type checking to infer immortal static accessors
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)
2026-02-26 16:32:30 -08:00
Andrew Trick cc7e4ab33a Fix lifetime type checking for nonescaping closures
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)
2026-02-24 20:05:12 -08:00
Aidan Hall 27d1d9c266 LifetimeDependence: static method type lifetimes with uncurrying
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.
2026-02-23 14:15:55 +00:00
Andrew Trick df82d78d5c Infer lifetime defaults for conditionally Escapable dependencies
Example:

    /* DEFAULT @_lifetime(copy t) */
    func foo<T: ~Escapable>(t: T?) -> T

See Same-type default lifetime:
https://github.com/swiftlang/swift/blob/main/docs/ReferenceGuides/LifetimeAnnotation.md#same-type-default-lifetime

Fixes rdar://150959598 ([nonescapable] infer & check same-type
dependencies for functions returning non-Escapable types)
2026-02-14 17:35:23 -08:00
Andrew Trick b7e2186f7d Support @_lifetime(immortal) to suppress inout default dependency
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)
2026-02-11 05:55:26 -08:00
Andrew Trick d4b0c713d4 Tweak lifetime dependencies for 'inout' parameters
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)
2026-02-06 16:11:26 -08:00
Aidan Hall 7b9db38984 LifetimeDependence: Support function types
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.
2026-02-05 14:50:27 +00:00
Aidan Hall fbb2da1526 Serialization: Add LifetimeDependenceInfo.isFromAnnotation to module format
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.
2026-02-05 14:50:04 +00:00
Aidan Hall f39359eb35 LifetimeDependence: Fix issues from earlier refactor
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.
2026-02-05 12:53:38 +00:00
Andrew Trick 124053a0e1 Prepare for strict enforcement of conditionally Escapable
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.
2026-01-30 11:07:37 -08:00
Aidan Hall 9497dbb7e9 LifetimeDependence: Centralized ParamInfo type & prefetch SourceFile
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.
2026-01-14 16:21:02 +00:00
Andrew Trick 123791a132 Merge pull request #86386 from aidan-hall/lifetime-dependence-generic-refactor
LifetimeDependence: Minimise use of AbstractFunctionDecl interface
2026-01-12 13:35:47 -08:00
Aidan Hall 4f7dae090c LifetimeDependence: Minimise use of AbstractFunctionDecl interface
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.
2026-01-09 13:00:53 +00:00
Tim Kientzle 8eabeeb8ca [SE-0474] Read2/Modify2 => YieldingBorrow/YieldingMutate
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.
2026-01-03 16:05:12 -08:00
Andrew Trick 6aaf9034ae [lifetimes] add same-type default lifetime inference
Infer @lifetime(result: copy arg) for every (result: R, arg: A) pair
such that R == A and 'arg' is not 'inout'.
2025-12-12 21:21:09 -08:00
Andrew Trick 885d14eee9 [NFC] lifetime inference: check for prior defaults before error
Don't diagnose a lifetime error if a previous default already handled the
non-Escapable output in question.
2025-12-12 20:58:55 -08:00
Aidan Hall df1de0961a Make LifetimeDependenceChecker::checkEnumElementDecl a static method
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.
2025-11-28 17:23:33 +00:00
Slava Pestov 819738c83e AST: Rename mapTypeIntoContext() => mapTypeIntoEnvironment(), mapTypeOutOfContext() => mapTypeOutOfEnvironment() 2025-11-12 14:48:19 -05:00
Henrik G. Olsson 33a059f689 Merge pull request #84136 from hnrklssn/print-lifetime-arg-backticks
[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
2025-09-08 15:29:16 -07:00
Henrik G. Olsson d7d839e9f3 [ASTPrinter] Escape @_lifetime arguments when needed
rdar://159992995
2025-09-05 23:03:46 -07:00
Henrik G. Olsson fe14d5d636 [ASTPrinter] Move LifetimeDescriptor::getString and add test (NFC)
This refactor and test case are preparations for the implementation in
the next commit.
2025-09-05 23:03:26 -07:00
Andrew Trick 37acd5781e Infer @_lifetime for mutating methods without experimental feature
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() {...}
    }
2025-09-03 22:42:13 -07:00
Andrew Trick ada46a0cf0 Lifetime inference: restructure to support multiple dependencies.
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)
2025-09-03 22:42:12 -07:00
Andrew Trick 52b990a008 Lifetime inference: fix a crash on implicit _modify for subscripts.
Fixes rdar://155976839 (Compiler crash when _modify is not specified for
~Escapable type)
2025-08-12 22:02:17 -07:00
Andrew Trick 60ce851d21 Lifetime inference: give subscripts an implicit dependency on 'self'
Allow a subscript getter to return a non-Escapable type without an explicit
'@'lifetime annotation.

    subscript(_ index: Int) -> Span {
      get {} // OK
    }
2025-08-12 22:02:17 -07:00
Meghana Gupta 222ee7389a Diagnose @_lifetime on targets that are Escapable
Lifetime dependencies can be attached to ~Escapable types only
2025-07-22 06:00:38 -07:00
Meghana Gupta 8387b7db14 Update callers of getLoweredOwnership() and add a test 2025-07-16 11:34:14 -07:00
Meghana Gupta c2836af597 Fix getLoweredOwnership() for setters of ~Escapable types 2025-07-16 10:47:02 -07:00
Meghana Gupta 61fc2e052a Merge pull request #82787 from meg-gupta/lifetimediag
Fix a lifetime dependence diagnostic
2025-07-04 21:11:45 -07:00
Meghana Gupta 6d0a6d2760 Fix a lifetime dependence diagnostic
`LifetimeDescriptor::getName()` can crash if the descriptor had a `self`.
Replace with `LifetimeDescriptor::getString()`
2025-07-03 15:20:23 -07:00
Andrew Trick 7abe2222f9 Disable surprising lifetime inference of implicit initializers
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)
2025-06-26 12:47:01 -07:00
Andrew Trick 87f2510a27 Diagnostic note for invalid @_lifetime annotations on inout params
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)
2025-06-25 16:34:43 -07:00
Andrew Trick 05fa82b7a7 Fix misleading Lifetime diagnostics for inout parameters
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) {}
2025-06-25 16:34:43 -07:00
Andrew Trick df0b81c88d Lifetime diagnostics: clarify @_lifetime usage for inout parameters
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.
2025-06-25 16:34:42 -07:00
Andrew Trick 465d6a82e7 Fix LifetimeDependence diagnostic formatting
Remove incorrectly nested single quotes from the suggested fix.
2025-06-25 15:24:12 -07:00
Meghana Gupta 0eb32f6943 Fixes after merge conflict 2025-06-23 16:52:18 -07:00
Meghana Gupta 2a2deea77b Avoid circular reference errors by adding an early bailout for imported enums 2025-06-23 13:42:55 -07:00
Meghana Gupta db24b6f758 Support lifetime dependence inference on enum elements 2025-06-23 13:42:53 -07:00
Meghana Gupta ccda38b513 [NFC] Prepare LifetimeDependenceInfo to hold EnumElementDecl* 2025-06-23 13:42:48 -07:00
Andrew Trick 855b3e4446 [nonescapable] remove '@_lifetime' requirement on implicit accessors
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')
2025-06-22 23:27:09 -07:00
Andrew Trick 125a0862a9 LifetimeDependence type check: infer trivial _read accessor
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)
2025-06-16 10:41:06 -07:00