Harden linearity for universal activation, part 2 — #55 cutover precondition #57
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Discovered while executing the #55 cutover (plan 0121, Task 4 Phase A): the cutover's central precondition — that deleting
ParamMode::Implicitturns the strict linearity check on universally and the migrated corpus passes cleanly — is false. #56 (spec 0063) closed two false-positive classes (value-type params; application-is-a-borrow). Universal activation surfaces at least three more false-positive classes plus one genuine corpus over-consume. Migrating the corpus to explicit modes (so the check activates everywhere) and applying the plan's mechanicalown→borrowrule clears only the read-only-heap params; 11 fixtures stay hard-RED on shapes no mode annotation can fix.The cutover was correctly stopped before the irreversible variant deletion (main untouched). This is the "#56 part 2" hardening that must land before #55 can ship.
Blocking classes
Local HOF function params.
callee_arg_modesresolves only global fns, never local function-typed params (crates/ailang-check/src/linearity.rs:755— "Locals never carry fn-types in 18c.2"). So(app p h)withpa local(borrow a)predicate param treatshas consumed regardless ofp's declared borrow slot.std_list.filterreuseshafter(app p h)→ falseuse-after-consume. Blocksstd_list.ail+ 7 importers.Let-alias of a borrowed value.
Term::Letwalks its value inPosition::Consume(linearity.rs:506);design/contracts/0008-memory-model.md:340documents this propagation as explicitly not shipped.rc_let_alias_implicit_param.pin_aliaseddoes(let a t (match a …))over a value the recursive loop must reuse → falseconsume-while-borrowed.Inferred value-typed let-binders. Conservatively tracked as heap (
aaa70d4scope decision: "value-typed let-binders stay conservatively tracked as heap; their type is inferred, not annotated, and the linearity walk does not re-run inference").eqord_3_newton_sqrt.iteratebinds(let xnew (app / …) …)(a Float) and uses it twice → falseuse-after-consume. Not a param; no mode annotation applies.Genuine double-consume needing a body rewrite (corpus bug, not a false positive).
std_either_list.partition_eithersprojects both(app Pair.fst rest)and(app Pair.snd rest)from one ownedPair;fst/sndmove their field out, so each consumes the Pair. The fix is a body rewrite ((match rest (case (MkPair ls rs) …))— destructure once), out of the migration's mechanical scope.Classes 1–3 are check false positives (the check is not yet precise enough for shapes that Implicit's gated-off activation was hiding); class 4 is a real fixture over-consume.
Direction (for brainstorm, not decided here)
Re-brainstorm spec 0062 (or a dedicated hardening spec). The cutover needs check-hardening for classes 1–3 (local-HOF-param mode resolution + let-alias / value-let-binder propagation) and the class-4 body rewrite scoped in, before the irreversible deletion. Do not patch a fifth phase onto plan 0121 (project rule: 2+ blocking classes = spec defect).
Plan 0121's Tasks 1 (borrow-return reject) and 3 (throwaway
ail migrate-modestool) already landed on main (e1c9089,39b674c) and are unaffected — they are additive and green. Only Task 4 (the cutover) is blocked on this.Blocks #55.
Resolved. Spec 0064 (the #56 part-2 hardening) landed in four iterations plus a drift-clean audit (
dfdc65f):47964ab): teed a (def,binder)->Type table out of the typecheck pass and exempted value-typed let-binders from consume-tracking (class 3).cc5991e): resolve local function-typed binder modes in callee_arg_modes (class 1, local HOF function params).be259f9): let-alias borrow propagation via an alias-redirect map; closed the design/contracts/0008-memory-model.md let-alias carve-out (class 2).2769e50): rewrote std_either_list.partition_eithers to destructure its recursive result once, removing the one genuine corpus double-consume (class 4); pinned both the rejected and accepted shapes.All four false-positive / over-consume classes that universal linearity activation surfaced are closed. The analysis is now correct on its own terms — it fixed real misfires in the linearity check — independent of the cutover it was scoped as a precondition for. Bench gates green (check.py 34/34, compile_check.py 24/24).
Context: #55 (the narrow ParamMode::Implicit-deletion cutover) is closed; the broader runtime-axis rethink is captured in design/models/0009-explicit-rc.md (a design exploration, not an approved milestone). This hardening stands on its own merits regardless of how or when the implicit-ownership default is eventually removed.