Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Swift Concurrency Type System

Swift Concurrency Type System

Swift Concurrency Type System / try! Swift Tokyo 2026
Paper: https://github.com/inamiy/swift-concurrency-type-system/

Avatar for Yasuhiro Inami

Yasuhiro Inami

April 13, 2026

More Decks by Yasuhiro Inami

Other Decks in Programming

Transcript

  1. Swift Concurrency Keywords async await actor @globalActor @MainActor Task TaskGroup

    @TaskLocal AsyncSequence Continuation isolated nonisolated nonisolated(unsafe) nonisolated(nonsending) @isolated(any) @concurrent #isolation Sendable @Sendable sending 20+ concurrency-related types, keywords, and attributes
  2. Closure Conversion This is subtyping / coercion between concurrency annotations

    Given f: @A () -> Void , can we assign to / use in g: @B () -> Void ? N o t e : @ A a n d @ B c a n b e A N Y c o n c u r re n c y a t t r i b u t e s , n o t o n l y a b o u t g l o b a l a c t o r i s o l a t i o n
  3. Sync Conversion Matrix From ↓ \ To → ~iso @S

    @MA @MA @S @iso? @iso? @S iso(a) iso(a) @S ~iso ✅ ❌ ❌ ❌ ✅ ❌ ❌ ❌ @S ✅ ✅ ✅ ✅ ✅ ✅ ✅ ✅ @MA ❌ ❌ ✅ ✅ ✅ ✅ ❌ ❌ @MA @S ❌ ❌ ✅ ✅ ✅ ✅ ❌ ❌ @iso? ⚠️ ❌ ❌ ❌ ✅ ❌ ❌ ❌ @iso? @S ⚠️ ⚠️ ❌ ❌ ✅ ✅ ❌ ❌ iso(a) ❌ ❌ ❌ ❌ ❌ ❌ ✅ ❌ iso(a) @S ❌ ❌ ❌ ❌ ❌ ❌ ✅ ✅ ~ i s o = n o n i s o l a t e d , @ S = @ S e n d a b l e , @ M A = @ M a i n A c t o r, @ i s o ? = @ i s o l a t e d ( a n y ) , i s o ( a ) = i s o l a t e d Lo c a l A c t o r, ⚠️ = w a r n i n g ( f u t u re e r ro r )
  4. Sync Conversion Sendable Sync nonisolated @Sendable sync @MainActor (@Sendable) sync

    @isolated(any) @Sendable sync isolated LocalActor @Sendable sync @isolated(any) sync nonisolated sync isolated LocalActor sync ⚠ deprecated
  5. Async Conversion Matrix From ↓ \ To → ~iso @S

    @MA @MA @S @iso? @iso? @S @conc @conc @S ~iso ✅ ❌ ❌ ❌ ✅ ❌ ✅ ❌ @S ✅ ✅ ✅ ✅ ✅ ✅ ✅ ✅ @MA ✅ ✅ ✅ ✅ ✅ ✅ ✅ ✅ @MA @S ✅ ✅ ✅ ✅ ✅ ✅ ✅ ✅ @iso? ✅ ❌ ❌ ❌ ✅ ❌ ✅ ❌ @iso? @S ✅ ✅ ✅ ✅ ✅ ✅ ✅ ✅ @conc ✅ ❌ ❌ ❌ ✅ ❌ ✅ ❌ @conc @S ✅ ✅ ✅ ✅ ✅ ✅ ✅ ✅ @ c o n c = @ c o n c u r re n t . i s o ( a ) o m i t t e d i n t h i s t a b l e ( s a m e a s " s y n c " ) .
  6. Async Conversion Sendable Async Non-Sendable Async nonisolated @Sendable async @MainActor

    (@Sendable) async @concurrent @Sendable async @isolated(any) @Sendable async isolated LocalActor @Sendable async nonisolated async @concurrent async @isolated(any) async isolated LocalActor async
  7. Isolation Conversion Sync Conversion Async Conversion Sendable Sync Sendable Async

    Non-Sendable Async nonisolated @Sendable sync @MainActor (@Sendable) sync @isolated(any) @Sendable sync isolated LocalActor @Sendable sync @isolated(any) sync nonisolated sync isolated LocalActor sync nonisolated @Sendable async @MainActor (@Sendable) async @concurrent @Sendable async @isolated(any) @Sendable async isolated LocalActor @Sendable async nonisolated async @concurrent async @isolated(any) async isolated LocalActor async ⚠ deprecated Async Lift
  8. Closure Conversion Rules @Sendable is stronger closure than non-@Sendable nonisolated

    is stronger closure than isolated @isolated(any) is the weakest closure (most generic) isolated LocalActor is tied to extra isolated param, which can be added but cannot be reduced for conversion Sync closure conversion is the most painful due to await being impossible Async closure conversion is easier to achieve via await (actor hop) Sync to Async conversion is always possible (but not vice-versa)
  9. Answer Where does the code run? → @BarActor Where does

    the value live? → @FooActor @MainActor is not used in handler 😛 Isolation Domain (capability, execution context) and Isolation Region (value isolation) are different!
  10. Axis 1: Capability ( ) Where code runs — Actor's

    execution context Determines what values actor can access Controls whether await is needed for cross-isolation calls @κ
  11. Function Annotation ( ) & Capability ( ) @φ ::=

    ​ ​ s e n d a b i l i t y ​ @σ i s o l a t i o n ​ @ι (Sendability) (Isolation Domain) (none) (none) ( @nonisolated ) @Sendable @isolated(a) (e.g. @MainActor ) @isolated(any) @concurrent (async only) Capability @nonisolated | @isolated(a) ( ) @φ @κ @σ @ι ⋅ ⋅ @κ ::= @κ ⊆ @ι
  12. Axis 2: Region ( ) Where data lives — Actor's

    value isolation (SE-0414) ρ ​ ::= ns disconnected ∣ isolated(a) ∣ task ∣ invalid Meaning Free to travel (not yet bound) Bound to actor Bound to current async task Tracks which isolation a NonSendable value belongs to. ρ ρ ​ n s disconnected isolated(a) a task
  13. Region in Action Region merge: Once bound, a value cannot

    be sent across isolation boundaries. disconnected ⊔ isolated(a) = isolated(a)
  14. Region Merge ( ) invalid invalid ρ ​ ⊔ 1

    ρ ​ 2 ρ ​ 1 ρ ​ 2 ρ ​ ⊔ 1 ρ ​ 2 disconnected disconnected disconnected disconnected isolated(a) isolated(a) disconnected task task isolated(a) isolated(a) isolated(a) isolated(a) isolated(b) (a =  b) isolated(a) task task task task S e e a l s o : S E - 0 4 1 4 : R e g i o n b a s e d I s o l a t i o n
  15. Region Merge as Join Semilattice disconnected (⊥) isolated(a) task invalid

    (⊤) = bottom of = top (compile error) disconnected ρ ​ ns invalid
  16. The Two Axes Together Aspect Capability Region What Execution context

    Value location Question Where does code run? Where does data live? Examples @nonisolated , @MainActor , Changes Fixed per scope Flow-sensitive Controls async / await sending @κ ρ disconnected isolated(a)
  17. Swift Ownership Keyword Description Copyable Can be copied and used

    multiple times ~Copyable Cannot be implicitly copied (consume once) consuming param Takes ownership borrowing param Temporary read-only access
  18. Affine Types A ⊬ A ⊗ A ← can NOT

    copy A ⊢ 1 ← can discard Linear Types: use exactly once (no copy, must use) Affine Types: use at most once (no copy, can skip use) No copy = Enforced consumption
  19. Region-based Isolation is like Ownership Sendable is like Copyable Value

    and reference can be copied and used in multiple isolation domains NonSendable is like ~Copyable Cannot be used after transferring to another isolation domain sending is like consuming (but not always) sending param may still not get consumed in the same region condition
  20. Formalizing Swift Concurrency Type System D i s c l

    a i m e r : Th i s s e c t i o n i s a n A I v i b e - f o r m a l i z a t i o n .
  21. The Judgment Form ​ ; ​ ; ​ ​ ​

    : env ​ in ​ Γ capability ​ @κ mode ​ α proves ​ ⊢ expr ​ e at ​ ​ type T region ​ ρ outputs ​ ⊣ env ​ out ​ Γ ′ / : what variables are available (input / output) e.g. Every expression transforms the type environment from to (Affinity) : current capability (where we're running) : (asynchrony) : which region the expression result lives in Γ Γ′ Γ = {e ​ : 1 T ​ at ρ ​ , e ​ : 1 1 2 T ​ at ρ ​ , …} 2 2 Γ Γ′ @κ α sync ∣ async ρ
  22. Key Rules: Variables Access-only variable rule: ​ (var) Γ; @κ;

    α ⊢ x : T at ρ ⊣ Γ x : T at ρ ∈ Γ ​ ρ ∈ accessible(@κ) accessible(@κ) : P (Regions) accessible(@nonisolated) = {disconnected, task, _} accessible(@isolated(a)) = {disconnected, isolated(a), task, _} T : Sendable ⇔ (ρ = _) (normal form in Γ)
  23. Key Rules: Transfer & Merge call-nonsendable-consume — cross-isolation consumption: ​

    (call-nonsendable-consume) Γ; @κ; async ⊢ await f (arg) : B at _ ⊣ ​ Γ ​ ∖ {arg} 2 ​ f : @σ @ι (A) α → B arg : A at disconnected ∈ Γ ​ 2 A : ∼Sendable B : Sendable @κ = @ι  region-merge — e.g. binding via assignment: ​ (region-merge) Γ; @κ; α ⊢ (e ​ .field = e ​ ) : () at _ ⊣ ​ 1 2 Γ ​ [ρ ​ ↦ ρ, ρ ​ ↦ ρ] 2 1 2 ​ Γ; @κ; α ⊢ e ​ : T ​ at ρ ​ ⊣ Γ ​ Γ ​ ; @κ; α ⊢ e ​ : T ​ at ρ ​ ⊣ Γ ​ 1 1 1 1 1 2 2 2 2 T ​ , T ​ : ∼Sendable ρ ​ , ρ ​ ∈ ρ ​ ρ = ρ ​ ⊔ ρ ​ 1 2 1 2 ns 1 2 Cross-isolation shrinks . Region-merge refines it. Γ
  24. Key Rules: Closures — Isolation Inference Non-@Sendable — closure inherits

    parent's capability : ​ (closure-inherit-parent) Γ; @κ; α ⊢ {e} : ​ at ρ ​ ⊣ Γ @κ () α → B ′ closure ​ Γ ⊆ Γ cap Γ ​ ; @κ; α ⊢ e : B at ρ ​ ⊣ Γ ​ cap ′ ret cl ′ @Sendable — No inheritance, uses instead: ​ (closure-no-inherit-parent) Γ; @κ; α ⊢ {e} : ​ at _ ⊣ Γ @Sendable @ι () α → B ′ ​ Γ ​ ⊆ Γ isAllSendable(Γ ​ ) cap cap @κ ​ = toCapability(@ι) cl Γ ​ ; @κ ​ ; α ⊢ e : B at ρ ​ ⊣ Γ ​ cap cl ′ ret cl ′ @κ @ι
  25. Key Rules: Closures — sending Capture transfer case — consumes

    NonSendable captures: ​ (closure-sending-consume) Γ; @κ; α ⊢ f ({e}) : () at _ ⊣ Γ′ ​ f : (sending (@κ ​ () α → T )) → () Γ ​ ⊆ Γ cl ′ cap ¬(⋯ ∧ isActorIsolated(@κ) ∧ @κ ​ = @κ) ⋯ cl Γ ​ ; @κ ​ ; α ⊢ e : T at ρ ​ ⊣ Γ ​ cap cl ′ ret cl ′ Γ = Γ ∖ {x ∣ (x : U at disconnected) ∈ Γ ​ , U : ∼Sendable} ′ cap
  26. Recap (Formalization) Γ; @κ; α ⊢ e : T at

    ρ ⊣ Γ′ Component Role Details WHERE code runs @nonisolated , @MainActor , ... WHERE data lives Affine discipline Merge refines, transfer shrinks @κ ρ disconnected → isolated(a) Γ → Γ′
  27. Wrap Up 1. Swift Concurrency = Capability × Region @κ

    : where code runs / ρ : where data lives Covers the mystery of closure conversion rules 2. sending = Affine transfer (in many cases) Precondition: x : T at disconnected Effect: (use-after-send is rejected) 3. Region Merge = Context refinement e.g. disconnected ⊔ isolated(a) = isolated(a) Γ = ′ Γ ∖ {x} Γ
  28. References Swift Evolution SE-0414: Region-based Isolation SE-0430: sending parameter and

    result values SE-0431: @isolated(any) function types SE-0461: Run nonisolated async functions on the caller's actor Papers Tofte & Talpin (1994), Region-Based Memory Management Walker, Crary & Morrisett (2000), Typed Memory Management via Static Capabilities Walker & Watkins (2001), On Regions and Linear Types Grossman et al. (2002), Region-Based Memory Management in Cyclone Charguéraud & Pottier (2008), Functional Translation of a Calculus of Capabilities Milano, Turcotti & Myers (2022), A Flexible Type System for Fearless Concurrency