Commit 7f55eb0
committed
feat(borrow): NLL last-use expiry for ref-bindings (CORE-01 pt3 Slice A, Refs #177)
Replaces the lexical "ref-binding lives until block exit" rule with a
proper non-lexical lifetime: a ref-binding's underlying borrow is now
released after the binder's *last use* statement, not at block exit.
The lexical rule made `let r = &x; print(*r); x = 2` an unavoidable
`MoveWhileBorrowed` even though `r` is provably dead before the
assignment. Under NLL the borrow on `x` has expired by the time the
assignment is checked, so the program type-checks — matching Rust's
NLL semantics for the same shape.
Mechanics in lib/borrow.ml:
- `compute_last_use_index` (new): forward pre-pass that returns a
`(sym_id -> int)` table mapping each symbol to the greatest statement
index that mentions it (the block's tail expression counts as the
n-th statement, where n = |stmts|). Visits every expression / stmt /
block / lambda body / handler arm / unsafe op so a use buried inside
a nested block or an `if`/`match` branch still pins the outer stmt's
index. Symbols never mentioned are absent → callers treat as -1.
- `check_block` now runs the pre-pass and, after each statement, calls
`expire_dead_ref_bindings i` to release any ref-binding *introduced
in this block* whose binder's last use is ≤ i. Outer-block
ref-bindings are deliberately preserved — they expire only at their
own block's exit. Expiry both `end_borrow`s the held borrow and
drops the (sym, b) entry from `state.ref_bindings`, so subsequent
checks see a clean borrow set.
Soundness preserved: the anti-aliasing rules
(`MoveWhileBorrowed` / `UseWhileExclusivelyBorrowed` / `ConflictingBorrow`)
still fire for any statement that reads or assigns the owner *before*
the binder's last use, because the borrow is still on `state.borrows`
at that point. The new `borrow_nll_still_rejects_live_borrow.affine`
fixture pins this — a binder used in the tail must keep the borrow
alive across the intervening assignment.
Tests (E2E Borrow Graph, +3):
- `borrow_nll_assign_after_last_use.affine` — assign after a shared
borrow's last use is now OK
- `borrow_nll_read_after_mut_last_use.affine` — read after an
exclusive borrow's last use is now OK
- `borrow_nll_still_rejects_live_borrow.affine` — assign while the
binder is still live (mentioned in tail) is still rejected
Pre-existing fixtures audited end-to-end against the new flow:
return-escape (`borrow_return_escape_{local,param}`) still fires
because `check_return_escape` runs against `state.ref_bindings` *during*
check_stmt for `StmtExpr (ExprReturn ...)`, before that statement's
expire pass; block-tail `&local` (`borrow_outlives_owner`) still fires
because the in-block tail check uses `ref_target`, not `ref_bindings`;
the `&mut` conflict/use-while fixtures still fire because the
violations are detected before the binder's last-use index. No change
to the over-rejection corpus.
Multi-day Phase-3 plan (Slices B–D remaining):
B — flow-sensitive escape via `outer = &x` (assignment must update
the borrow graph the way `let r = &x` already does)
C — origin/region variables (Polonius surface) + loan-live-at-point
dataflow across CFG joins
D — tighter quantity-checker integration for captured linears
Docs: STATE.a2ml `borrow-checker`, CAPABILITY-MATRIX.adoc, TECH-DEBT.adoc
CORE-01 row updated to "pts 1–3 Slice A landed".
NOTE: this commit was authored in a remote container without an OCaml
toolchain installed, so `dune build` / `dune runtest` were not run
locally; CI is the source of truth. The change is mechanically scoped
to the borrow checker, with the AST visitor covering all 24 expr / 5
stmt / 5 unsafe_op variants.1 parent 8137236 commit 7f55eb0
8 files changed
Lines changed: 281 additions & 18 deletions
File tree
- .machine_readable/6a2
- docs
- lib
- test
- e2e/fixtures
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
68 | 68 | | |
69 | 69 | | |
70 | 70 | | |
71 | | - | |
| 71 | + | |
72 | 72 | | |
73 | 73 | | |
74 | 74 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
84 | 84 | | |
85 | 85 | | |
86 | 86 | | |
87 | | - | |
| 87 | + | |
| 88 | + | |
88 | 89 | | |
89 | 90 | | |
90 | 91 | | |
91 | 92 | | |
92 | 93 | | |
93 | 94 | | |
94 | 95 | | |
95 | | - | |
96 | | - | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
97 | 107 | | |
98 | 108 | | |
99 | 109 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
110 | 110 | | |
111 | 111 | | |
112 | 112 | | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | | - | |
118 | | - | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
119 | 130 | | |
120 | 131 | | |
121 | 132 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
562 | 562 | | |
563 | 563 | | |
564 | 564 | | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
565 | 672 | | |
566 | 673 | | |
567 | 674 | | |
| |||
1000 | 1107 | | |
1001 | 1108 | | |
1002 | 1109 | | |
1003 | | - | |
1004 | | - | |
1005 | | - | |
1006 | | - | |
| 1110 | + | |
| 1111 | + | |
| 1112 | + | |
| 1113 | + | |
| 1114 | + | |
| 1115 | + | |
| 1116 | + | |
| 1117 | + | |
| 1118 | + | |
| 1119 | + | |
| 1120 | + | |
| 1121 | + | |
| 1122 | + | |
| 1123 | + | |
| 1124 | + | |
| 1125 | + | |
| 1126 | + | |
| 1127 | + | |
| 1128 | + | |
| 1129 | + | |
| 1130 | + | |
| 1131 | + | |
| 1132 | + | |
| 1133 | + | |
| 1134 | + | |
| 1135 | + | |
| 1136 | + | |
| 1137 | + | |
| 1138 | + | |
| 1139 | + | |
| 1140 | + | |
| 1141 | + | |
| 1142 | + | |
| 1143 | + | |
| 1144 | + | |
1007 | 1145 | | |
1008 | 1146 | | |
1009 | 1147 | | |
| |||
1190 | 1328 | | |
1191 | 1329 | | |
1192 | 1330 | | |
1193 | | - | |
1194 | | - | |
| 1331 | + | |
| 1332 | + | |
| 1333 | + | |
| 1334 | + | |
| 1335 | + | |
| 1336 | + | |
| 1337 | + | |
| 1338 | + | |
| 1339 | + | |
| 1340 | + | |
| 1341 | + | |
| 1342 | + | |
| 1343 | + | |
| 1344 | + | |
1195 | 1345 | | |
1196 | | - | |
1197 | | - | |
| 1346 | + | |
| 1347 | + | |
| 1348 | + | |
| 1349 | + | |
1198 | 1350 | | |
1199 | 1351 | | |
Lines changed: 19 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
Lines changed: 17 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
Lines changed: 18 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
0 commit comments