Summary
With cannot construct a []mut T (mutable slice) argument from a collection. This blocks #379's buf_out fact (memset/explicit_bzero need a []mut u8). Deep-dive below; no code/spec change made pending a scope decision.
Root-cause analysis (deep dive)
[]mut T is a real, live type — TY_SLICE whose d1 field is the mutability flag. The argument compatibility check is correct and strict:
src/Sema.w:4426-4429: if exp_k==TY_SLICE and act_k==TY_SLICE: if d1(exp)!=0 and d1(act)==0: return 0 — a []mut parameter demands an actual []mut value; []T does not satisfy it.
The gap is that nothing produces a []mut T value:
- Slice-creation expressions (
a[..], a[0..3]) always build an immutable slice — they emit TY_SLICE with d1=0 regardless of source mutability or expected type (e.g. src/SemaCheck.w:414, range/subscript typing).
split_at_mut returns the Vec-specific VecRange[T], not []mut T (src/SemaCheck.w:14790-14811; mutable Vec access is .set()/.get() on VecRange).
- A bare collection does not coerce: passing
var a: [3]u8 to a []mut u8 parameter gives wrong argument type (it also fails for []u8 — only a[..]/a[0..3] coerce, and only to immutable).
Spec intent: this is a compiler bug, not a stale spec
§4.8 (spec lines 2083-2086) explicitly promises the coercion:
"in parameter position, []T and []mut T follow §3.8's borrow mode: the caller's collection coerces to the slice view … for []mut T, the exclusive borrow ends when the callee returns."
So []mut T is the intended model and VecRange is a separate Vec convenience, not a replacement. The compiler is simply missing the collection→[]mut T coercion. Per AGENTS.md ("spec says X works, compiler disagrees ⇒ compiler bug"), this is a bug to fix.
Proposed design
Produce a mutable slice (TY_SLICE d1=1) when, and only when, (a) the expected parameter type is []mut T, (b) the source place is mutable (var binding / mutable field / mutable receiver), and (c) §21.1 exclusivity holds (no overlapping live &T/[]T/[]mut T view of the same place). Touch points:
- Slice-creation typing (range/subscript expr →
TY_SLICE): thread expected-mutability + source-mutability to set d1. Today it hardcodes d1=0.
- Call-argument coercion: accept a mutable collection /
a[..] over a mutable source as []mut T (mirror of the existing []T array→slice coercion).
- Borrow/exclusivity checker (§21.1): register the exclusive borrow for the argument's lifetime — invalidate overlapping
&T/[]T views until the callee returns. This is the subtle part.
- MIR/codegen: none expected — a mutable slice has the same
(ptr,len) fat-pointer ABI as an immutable one; mutability is compile-time only. (The buf_out wrapper already uses &raw mut s[0], validated to compile.)
Size estimate
Medium. The slice machinery + fat-pointer ABI already work end-to-end for immutable slices, so (1) and (2) are small-to-medium. The risk and most of the effort concentrate in (3) — getting §21.1 exclusivity right for the slice argument (overlap invalidation, NLL end-of-borrow at callee return). Roughly comparable to the buf_in work but reaching into the borrow checker rather than binding-gen. Recommend a dedicated session with adversarial borrow-checker tests.
Once this lands
#379 buf_out is then trivial: add the curated entries (memset out:0:2, explicit_bzero out:0:1, …) to ci_buf_is_mut/ci_buf_count in src/CImport.w. The generator already emits the []mut u8 + &raw mut wrapper; only the overlay config and tests remain.
Status: analysis + design only. Awaiting maintainer decision on whether to fix the coercion (Option A) before resuming #379 buf_out. No []mut T semantics changed.
Summary
With cannot construct a
[]mut T(mutable slice) argument from a collection. This blocks #379'sbuf_outfact (memset/explicit_bzero need a[]mut u8). Deep-dive below; no code/spec change made pending a scope decision.Root-cause analysis (deep dive)
[]mut Tis a real, live type —TY_SLICEwhosed1field is the mutability flag. The argument compatibility check is correct and strict:src/Sema.w:4426-4429:if exp_k==TY_SLICE and act_k==TY_SLICE: if d1(exp)!=0 and d1(act)==0: return 0— a[]mutparameter demands an actual[]mutvalue;[]Tdoes not satisfy it.The gap is that nothing produces a
[]mut Tvalue:a[..],a[0..3]) always build an immutable slice — they emitTY_SLICEwithd1=0regardless of source mutability or expected type (e.g.src/SemaCheck.w:414, range/subscript typing).split_at_mutreturns the Vec-specificVecRange[T], not[]mut T(src/SemaCheck.w:14790-14811; mutable Vec access is.set()/.get()onVecRange).var a: [3]u8to a[]mut u8parameter giveswrong argument type(it also fails for[]u8— onlya[..]/a[0..3]coerce, and only to immutable).Spec intent: this is a compiler bug, not a stale spec
§4.8 (spec lines 2083-2086) explicitly promises the coercion:
So
[]mut Tis the intended model andVecRangeis a separate Vec convenience, not a replacement. The compiler is simply missing the collection→[]mut Tcoercion. Per AGENTS.md ("spec says X works, compiler disagrees ⇒ compiler bug"), this is a bug to fix.Proposed design
Produce a mutable slice (
TY_SLICEd1=1) when, and only when, (a) the expected parameter type is[]mut T, (b) the source place is mutable (varbinding / mutable field / mutable receiver), and (c) §21.1 exclusivity holds (no overlapping live&T/[]T/[]mut Tview of the same place). Touch points:TY_SLICE): thread expected-mutability + source-mutability to setd1. Today it hardcodesd1=0.a[..]over a mutable source as[]mut T(mirror of the existing[]Tarray→slice coercion).&T/[]Tviews until the callee returns. This is the subtle part.(ptr,len)fat-pointer ABI as an immutable one; mutability is compile-time only. (The buf_out wrapper already uses&raw mut s[0], validated to compile.)Size estimate
Medium. The slice machinery + fat-pointer ABI already work end-to-end for immutable slices, so (1) and (2) are small-to-medium. The risk and most of the effort concentrate in (3) — getting §21.1 exclusivity right for the slice argument (overlap invalidation, NLL end-of-borrow at callee return). Roughly comparable to the buf_in work but reaching into the borrow checker rather than binding-gen. Recommend a dedicated session with adversarial borrow-checker tests.
Once this lands
#379 buf_out is then trivial: add the curated entries (memset
out:0:2, explicit_bzeroout:0:1, …) toci_buf_is_mut/ci_buf_countinsrc/CImport.w. The generator already emits the[]mut u8+&raw mutwrapper; only the overlay config and tests remain.Status: analysis + design only. Awaiting maintainer decision on whether to fix the coercion (Option A) before resuming #379 buf_out. No
[]mut Tsemantics changed.