Skip to content

Lifetime dependency proposal - internal review#6

Open
atrick wants to merge 2 commits intomainfrom
lifetime-dependency
Open

Lifetime dependency proposal - internal review#6
atrick wants to merge 2 commits intomainfrom
lifetime-dependency

Conversation

@atrick
Copy link
Owner

@atrick atrick commented Feb 4, 2026

Consolidate and expand lifetime dependency proposal

  • Incorporate content from Tim's and Joe's repositories
  • Reorganize proposal structure and flow
  • Add sections covering implicit defaults, function types, and lifetime requirements

Comment on lines +395 to +400
2. A function type alias that supports dependence on closure captures:

`typealias SpanGetter = @lifetime(self) () -> Span<T>`

Here, we propose repurposing the `self` keyword to refer to the closure context. Use of `self` would obviously be problematic when the function type is a method parameter, but should be reasonably clear when declaring a typealias.

Copy link

@aidan-hall aidan-hall Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we reuse some other keyword here?
How about func? That would avoid colliding with parameter names, be unambiguous on method parameters, and still conveys a dependence on "this function/closure".

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to propose using func in this context but it would be great to air all these alternative on the forums pitch thread once this is posted.

Here's what I have for now:

  1. A function type alias that supports dependence on closure captures:

typealias SpanGetter = @lifetime(<captures_identifier>) () -> Span<T>

Here, we need to choose a keyword for the <capture_identifier>. We could repurpose the self keyword to refer to the closure context. Use of self would obviously be problematic when the function type is a method parameter, but should be reasonably clear when declaring a typealias. Alternatively, we could introduce a new captures keyword for use in the context.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed, let's completely avoid the problem of choosing a "captures identifier" by always assuming that nonescapying function types may depend on captures... I can't come up with a case where it would be important to suppress that dependency

Copy link

@aidan-hall aidan-hall Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I came up with this example, where it could be useful to require that the result only depends on the parameters:

struct NE: ~Escapable {}
func takePicker(picker: @_lifetime(copy ne0, copy ne1) (_ ne0: NE, _ ne1: NE) -> NE) {
    let x = NE()
    let y = NE()
    _ = picker(x, y) // Expected behaviour: pick one of x or y.
}

let ne3 = NE()
takePicker { ne0, ne1 in ne3 }

I suppose this is more an issue of correctness than safety, since ne3's lifetime can extend past the call to takePicker. For this purpose it would probably always make more sense to use a predicate ((NE, NE) -> Bool) anyway.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aidan-hall it's not uncommon for closures to depend only on their arguments. But those closures can still be passed to a function type that depends on its context. Lifetime enforcement only needs to know about the capture dependencies within the scope that contains the closure definition. For function type parameters, it's always safe to assume a dependency on the context even if that dependency disappears in the caller.

@lifetime(copy self)
get { ... }

@lifetime(self: copy newValue)
Copy link

@tbkka tbkka Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not following this. Mustn't the elements outlive the container? I would expect self: borrow newValue here.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the setter makes a copy of newValue. It doesn't borrow the value passed in.

init(element: Element) { ... }
}
```

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the container case really be correctly specified without some form of lifetime variable? If not, then the above example could be a little misleading. We might need a disclaimer here that elaborates how the above is conservative, it fails to permit uses that should be permitted, and that the Future Directions will clarify how this might be improved in the future.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example here is not conservative because the "Container" is conditionally Escapable based on its element type, just like Optional. The problem was the use of the term Container, I changed it to "Wrapper"

@atrick atrick changed the title Consolidate and expand lifetime dependency proposal Lifetime dependency proposal - internal review Feb 4, 2026
@atrick atrick force-pushed the lifetime-dependency branch 4 times, most recently from babb49d to de61433 Compare February 25, 2026 04:46
- Incorporate content from previous pitches
- Reorganize proposal structure and flow
- Add sections covering implicit defaults, closures, and lifetime requirements
@atrick atrick force-pushed the lifetime-dependency branch from de61433 to 1ad4fd1 Compare February 25, 2026 05:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants