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
@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 )
@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 " ) .
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)
the value live? → @FooActor @MainActor is not used in handler 😛 Isolation Domain (capability, execution context) and Isolation Region (value isolation) are different!
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) ( ) @φ @κ @σ @ι ⋅ ⋅ @κ ::= @κ ⊆ @ι
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
ρ 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
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)
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
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
: 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 ρ
α ⊢ 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 Γ)
: 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} Γ
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