Skip to content

Instantly share code, notes, and snippets.

@atrick
Last active December 20, 2025 06:55
Show Gist options
  • Select an option

  • Save atrick/e331456ec9a8270e79c7348c25d5873e to your computer and use it in GitHub Desktop.

Select an option

Save atrick/e331456ec9a8270e79c7348c25d5873e to your computer and use it in GitHub Desktop.

Lifetime dependencies for conditionally Escapable types

Background

This document proposes a "requires-escapable" rule. This is a short way of saying that a dependency source can only be Escapable by requiring the dependency target to be Escapable under the same conditions. This isn't a new idea. It was informally proposed alongside the initial lifetime implementation, but the implementation was deferred because of engineering bandwidth. LifetimeAnnotation.md currently refers to it as the "same-type" rule until the implementation is complete. We want to include this in the SE proposal that we pitch in Jan 2026 month for source compatibility reasons.

The requires-escapable rule only affects the legality of function types. It does not change lifetime enforcement in the function body. It simply extends the existing lifetime rules to support conditional conformances. The lifetime rules have always prohibited copying a lifetime from an unconditionally Escapable type. Here's an example where we prohibit dependency from an unconditionally Escapable type:

struct S: Escapable {}

struct NE: ~Escapable {}

@_lifetime(copy s) // ERROR: dependency on Escapable type `S`
func foo(s: S) -> NE

The require-escapable rules extends this rule to handle conditional conformances.

Requires-Escapable Rule

Given

@_lifetime(copy a)
func foo<...>(..., a: A, ...) -> R

The lifetime annotation is valid iff: A: Escapable requires R: Escapable

Examples

Without this rule, the compiler cannot statically know whether R can depend on A inside a generic context. The following examples show how theis rule affects diagnostics:

  1. ERROR: unrelated type parameter
@_lifetime(copy t) // ERROR
func foo<T: ~Escapable, U: ~Escapable>(t: T) -> U
  1. OK: same type parameter
@_lifetime(copy t) // OK
func foo<T: ~Escapable>(t: T) -> T
  1. OK: conditionally non-escapable nominal types
struct NE1<T: ~Escapable>: ~Escapable {
  var t: T
}

extension NE1: Escapable where T: Escapable {}

@_lifetime(copy ne) // OK
func foo<T: ~Escapable>(ne: NE1<T>) -> T
  1. OK: conditionally non-escapable nominal types with related type parameters
struct NE2<T: ~Escapable>: ~Escapable {
  var t: T
}

extension NE2: Escapable where T: Escapable {}

@_lifetime(copy ne) // OK
func foo<T: ~Escapable>(ne: NE1<NE1<T>>) -> NE2<T>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment