iter eob.1: effect-op args walk as Borrow; heap-Str RC discipline closes
Language rule: Term::Do.args[*] are walked in Position::Borrow by
the uniqueness + linearity passes, symmetric to Term::Ctor.args[*]
being walked in Position::Consume. The kind itself carries the
ownership default — no per-op field on EffectOpSig. Three
analyser sites flip in lockstep: uniqueness.rs:289, linearity.rs:506,
and the shared doc-comment at linearity.rs:42.
Heap-Str-specific consequences (closing hs.4's leak):
- int_to_str / float_to_str ret_mode: Implicit → Own at four lockstep
sites (builtins.rs:200,210 + synth.rs:172,179). The codegen
trackability gate (drop.rs:464-475, iter 18g.2) now fires on these
callees, so the let-binder receives a scope-close drop.
- drop_symbol_for_binder's App arm gets a Type::Con{name:"Str"} →
"ailang_rc_dec" carve-out, symmetric to field_drop_call's existing
Str arm. Without it, the new Own-trackable App binder would emit
drop_<m>_Str (undefined).
Tests: the two pre-existing RED tests at e2e.rs (commit 592d87b)
flip to GREEN unchanged with predicted numbers (allocs=1,frees=1,
live=0 and allocs=2,frees=2,live=0). Two new positive tests pin
the rule's coverage: primitive-Int arg through io/print_int produces
zero RC traffic; heap-Str through 2× io/print_str in sequence
typechecks (no use-after-consume) and balances allocs=1,frees=1,
live=0.
DESIGN.md §Decision 10 gets the arg-position-policy table for both
AST node kinds (Ctor=Consume, Do=Borrow) plus a linking paragraph
explaining how Do=Borrow cooperates with ret_mode==Own.
heap-str-abi milestone closes here: WhatsNew entry shipped (Strings
produced at runtime now release cleanly), roadmap P1 entry checked
off, Post-22-Prelude depends-on line removed.
Workspace cargo test + cross_lang.py + compile_check.py all green.
check.py noisy latency cluster + bench_list_sum.bump_s ±12% — same
precedent as the previous two audits, not coupled to this milestone.
This commit is contained in:
@@ -1396,6 +1396,31 @@ returns that are not `Type::Con` (e.g. unresolved type vars on
|
||||
a polymorphic call's pre-monomorphisation site; the
|
||||
monomorphised copies resolve to concrete drop fns).
|
||||
|
||||
#### Arg-position policy for compound AST nodes
|
||||
|
||||
The uniqueness and linearity passes walk arguments of compound
|
||||
nodes with a fixed `Position` policy. For ownership-bearing nodes:
|
||||
|
||||
| Node | Arg position | Reason |
|
||||
|----------------------|--------------|---------------------------------------------------------------------------------------|
|
||||
| `Term::Ctor.args[*]` | Consume | constructor packs values into the cell; the cell owns them afterwards |
|
||||
| `Term::Do.args[*]` | Borrow | effect-op observes its arguments; the caller still owns whatever pointer it passed in |
|
||||
|
||||
The two policies are language rules, not per-op annotations. They
|
||||
do not appear as fields on `EffectOpSig` or `Ctor`; the AST node
|
||||
kind itself carries the default. The walkers that read this policy
|
||||
live at `crates/ailang-check/src/uniqueness.rs` and
|
||||
`crates/ailang-check/src/linearity.rs` (matched arms in both).
|
||||
|
||||
The Do = Borrow rule pairs with the `ret_mode == Own` letbinder-
|
||||
trackability rule above: when a built-in such as `int_to_str` is
|
||||
declared `ret_mode: Own` and its result is fed into an effect-op
|
||||
(`io/print_str s`), the let-binder is RC-tracked for scope-close
|
||||
drop *and* the effect-op does not consume it — the slab is freed
|
||||
exactly once at scope close, never zero-times (RC leak under the
|
||||
old Consume rule, which silenced the scope-close drop) and never
|
||||
twice (double-free under a hypothetical Consume + scope-close).
|
||||
|
||||
**What this widening does NOT do.**
|
||||
|
||||
- Does not change the canonical hash. `param_modes` /
|
||||
|
||||
Reference in New Issue
Block a user