[Accepted] SE-0528: Non-`Copyable` `Continuation`

Hi everyone. The review of SE-0528, Non-Copyable Continuation, concluded on April 28, 2026. The Language Steering Group has decided the proposal should be accepted with modifications. In order to maintain naming consistency with related APIs in the standard library, the proposed withContinuation(of:throws:) top-level function shall be named withContinuation(of:throwing:).

The review discussion raised other questions about the proposed design, for which the language steering group accepts the proposed design as is:

  • There was some concern that, as a non-Copyable type, Continuation will be difficult to use due to various limitations and missing features of non-Copyable types today. Non-Copyable types are an area of active development, and the language steering group does not see any missing features as directly inhibiting the use of Continuation today. Developers can continue to use CheckedContinuation where Continuation is too difficult to use, and the proposal provides a straightforward conversion for that purpose.
  • Reviewers aired concern that Continuation would benefit from the ability to specify types whose values must be explicitly consumed, which might be modeled similar to ~Copyable by a suppressible ~Discardable protocol, and that by delivering the type today as only ~Copyable, we would be foreclosing on the ability to take advantage of ~Discardable in the future. However, the language steering group does not foresee us beginning work on such a feature anytime soon, and the ability of non-Copyable types to enforce uniqueness and single resumption is already a massive improvement in static safety over the existing continuation variants, that shouldn't need to wait an indeterminate amount of time for a slightly more ideal variant to become possible. If we did introduce ~Discardable at some point in the future, ~Discardable types would be subject to even stricter usage constraints that ~Copyable types, and there would likely be a multi-year transition to catch the language and standard library up to them as we are currently undergoing with ~Copyable now. We can also pursue limited diagnostics that diagnose obvious misuse of Continuation or other similar types that should not be allowed to implicitly deinit; a limited diagnostic feature like that should be readily adoptable by Continuation as is.
  • In response to pitch feedback, the reviewed proposal removed captured source location information from the Continuation type, leaving its representation as only a single pointer to the suspended task. Reviewers asked whether this would lead to difficulties when debugging runtime crashes due to continuation misuse. The language steering group believes that keeping the Continuation type lightweight is the correct tradeoff; since Continuation statically enforces single ownership and resumption, the primary remaining source of runtime failure would be allowing a Continuation to be deinit-ed without resuming. In that situation, the interesting context is most likely the place where the program crashed, rather than where the continuation was formed, since the crashing context should generally be the one that was responsible for either resuming the continuation or transferring ownership to someone else. From a debugger, the source of the continuation should also be readily available by looking for the suspended task and seeing what source location it is suspended at.
  • There was some debate as to whether allowing a Continuation to be deinit-ed should cause a runtime failure or only "leak" the continuation and let the program keep running (possibly with a warning logged to the console, as CheckedContinuation does today). The language steering group agrees with the proposal that deinit should trigger a runtime failure. This should make it easier to catch mistakes during development, and in the future if Swift does gain new diagnostics or type system features for suppressing implicit deinits, runtime failures also reduce the likelihood of "Hyrum's law" situations where existing code becomes dependent on leaked continuations for its expected behavior, which could become an impediment to Continuation adopting those features when they become available.

Thank you to everybody who participated in the review!

17 Likes

Towards the end of the review I mentioned a potential solution that avoids closing off the ~Discardable future direction. The justification in the acceptance sounds like it boils down to "it would be a number of years before we regret this decision", which isn't really comforting, at least to me. Is my suggestion of temporarily giving this type a different name not viable? Or is there a stronger argument as to why we won't regret accepting this proposal with this name?

If it comes to us needing to swap a new type in under the same name, we have other features that can do that as well, such as @abi. I'm not personally too concerned about phasing in ~Discardable over existing types, as long as they're already non-Copyable and trap in their deinit; existing correct code would not be able to dynamically reach such a type's deinit, and integrating a newly-~Discardable type into existing code would likely require a wrapper that adds a trapping deinit, so having an in-between mode where an existing type can be made ~Discardable but only warn when used statically in a situation that has a Discardable requirement seems like a reasonable migration path.

3 Likes

I could also imagine the ~Discardable enforcement behaving like non-Sendable types. Perhaps new types would always have compile-time enforcement, and a new language mode would assert correct handling of all types at compile time. But previously-existing types in an older language modes could just emit a warning and a runtime trap.

4 Likes