Iter 18a: (borrow T) / (own T) mode annotations on fn signatures

Adds form-A surface (borrow T) / (own T) wrappers in fn-type
params and ret slots. Internally these are not new Type variants
but per-position metadata on Type::Fn:

  Type::Fn { params, param_modes, ret, ret_mode, effects }
  enum ParamMode { Implicit, Own, Borrow }

This keeps Type itself unchanged, so unification, occurs, apply,
and ~190 other Type match-arms in the typechecker need no new
branch. Fields use serde skip-if-default predicates so canonical
JSON hashes for every pre-18a fixture stay bit-identical (sum,
list, hof, closure, list_map, etc.: zero diff under git).

Iter 18a is purely additive: typechecker treats modes as
transparent (mode-compat check deferred to 18c), no codegen
change (--alloc=gc / Boehm path unchanged), no linearity
enforcement. The annotation info flows into the JSON side-table
that 18c will consume.

Equality of mode slices is length-tolerant: a vec![] (the form
written by mechanically-updated construction sites that elide
modes) compares equal to vec![Implicit; n]. This kept the patch
from being a 100-site mechanical migration through the
typechecker. 18c will choose: keep the slack, or normalise to
full-length on construction.

New fixture examples/borrow_own_demo.{ailx,ail.json} exercises
both modes. list_length declares (borrow (con List)); sum_list
declares (own (con List)). Stdout: 3 then 6.

Documentation: DESIGN.md schema-additions block clarified that
modes are Type::Fn metadata (not Type variants); migration plan
flag rename --memory=rc → --alloc=rc to match the existing CLI
flag.

Verification:
- cargo build --workspace green
- cargo test --workspace green (154 tests, +1 vs baseline 153)
- git diff examples/{sum,list,hof,closure,list_map,...}.ail.json: empty
- borrow_own_demo runtime stdout: "3\n6\n"
- canonical JSON contains "param_modes":["borrow"] and ["own"]
This commit is contained in:
2026-05-08 02:07:07 +02:00
parent 52b129f558
commit b9ca8894a5
15 changed files with 606 additions and 41 deletions
+4 -4
View File
@@ -972,9 +972,9 @@ Memory layout (Iter 18b):
types, the recursion is replaced by a worklist loop (Iter 18e).
Codegen for `Term::Ctor` / `Term::Lam` env / closure pair under
`--memory=rc` calls `ailang_rc_alloc(SIZE)`. Iter 18b stops there
`--alloc=rc` calls `ailang_rc_alloc(SIZE)`. Iter 18b stops there
— inc/dec instrumentation is added in Iter 18c, once the
inference is wired up. Until then, `--memory=rc` deliberately
inference is wired up. Until then, `--alloc=rc` deliberately
leaks like the pre-Boehm era; this is purely about plumbing.
### Migration plan
@@ -984,8 +984,8 @@ leaks like the pre-Boehm era; this is purely about plumbing.
codegen change. `(con T)` ≡ `(own T)`. Existing fixtures
unchanged.
2. **Iter 18b:** RC runtime. `runtime/rc.c` with header layout +
alloc/inc/dec. Codegen `--memory=rc` routes allocation through
`rc_alloc`; **no inc/dec yet**. `--memory=gc` remains default.
alloc/inc/dec. Codegen `--alloc=rc` routes allocation through
`rc_alloc`; **no inc/dec yet**. `--alloc=gc` remains default.
3. **Iter 18c:** uniqueness inference + codegen inc/dec
instrumentation. Combines 18a's annotations with intra-fn
dataflow. Linear-by-default enforcement turns on. `(clone X)`