JOURNAL: Iter 18c.1 — Term::Clone schema
Records that 18c.1 ran zero-surprise (mechanical recursion at ~10 sites across 6 files), the future-work seam in codegen for 18c.3 is a single line (emit one rc_inc call before returning the inner SSA reg), and 18c.2's linearity check should start as 'green for borrow_own_demo' since that fixture's shape is exactly what the check needs to accept.
This commit is contained in:
+115
@@ -5983,3 +5983,118 @@ schema), 18c.2 (linearity check + suggested_rewrites), 18c.3
|
||||
(inference + codegen) sequentially. Don't attempt all three in
|
||||
one pass — each builds on the previous and wants its own
|
||||
verification cycle.
|
||||
|
||||
## Iter 18c.1 — `Term::Clone` schema (no inc emission yet)
|
||||
|
||||
Schema floor for explicit RC inc. Adds `Term::Clone { value }`
|
||||
with serde tag `"clone"`, form-A `(clone X)`. Pure additive
|
||||
schema — typechecker treats it as identity, codegen lowers it
|
||||
exactly like its inner term. The runtime semantics of `(clone X)`
|
||||
in 18c.1 are: nothing happens; it's an identity wrapper.
|
||||
|
||||
The variant is the author-visible alternative to implicit
|
||||
sharing under the LLM-aware RC design (Decision 10's
|
||||
mechanism (2)). When 18c.2 ships linearity enforcement, an LLM
|
||||
that wants two `own`-mode uses of the same binder will be
|
||||
required to spell one of them as `(clone X)`. When 18c.3 ships
|
||||
codegen, the `Term::Clone` arm in `lower_term` will emit a
|
||||
single `call void @ailang_rc_inc(ptr %v)` before delegating
|
||||
to the inner term's lowering.
|
||||
|
||||
The 18c.3 emission seam is documented in the codegen arm with a
|
||||
comment so the future-work site is greppable.
|
||||
|
||||
### Sites touched
|
||||
|
||||
The match-arm count was higher than the brief estimated
|
||||
(~10 across 6 files) but every site was pure structural
|
||||
recursion through `value`. Pretty.rs needed nothing — it has no
|
||||
`match t {}` over Term. The non-obvious sites:
|
||||
|
||||
- `crates/ailang-check/src/lib.rs::verify_tail_positions` —
|
||||
records that `(clone tail-call)` is itself a tail call. Under
|
||||
18c.1's identity-of-clone semantics, the inner term keeps its
|
||||
tail-position. (Under 18c.3, the inserted `inc` happens before
|
||||
the call returns, so the tail-position story is preserved
|
||||
there too — `inc` is not a function call from the LLVM-IR
|
||||
tail-position perspective; it's an inline call after the value
|
||||
is computed.)
|
||||
|
||||
- `crates/ailang-codegen/src/escape.rs` — three sites in the
|
||||
escape analysis: `walk`, `escapes`, `collect_free_vars`. The
|
||||
pattern is the same as everywhere else — recurse through the
|
||||
inner value.
|
||||
|
||||
- `crates/ail/src/main.rs::walk_term` — the deps walker that
|
||||
finds cross-module references for the workspace loader. Pass-
|
||||
through.
|
||||
|
||||
### Fixture
|
||||
|
||||
`examples/clone_demo.{ailx,ail.json}`:
|
||||
|
||||
```
|
||||
(module clone_demo
|
||||
(fn main
|
||||
(type (fn-type (params) (ret (con Unit)) (effects IO)))
|
||||
(params)
|
||||
(body
|
||||
(let x 42
|
||||
(do io/print_int (clone x))))))
|
||||
```
|
||||
|
||||
Stdout: `42` under both `--alloc=gc` and `--alloc=rc`. The
|
||||
fixture is intentionally minimal — the schema/parser/printer/
|
||||
round-trip path is exercised end-to-end, but the value being
|
||||
"cloned" is a primitive `Int` where RC inc is meaningless even
|
||||
in 18c.3. A richer fixture wrapping a list-bound clone is a
|
||||
18c.3 concern (where clone actually does something).
|
||||
|
||||
### Build / test
|
||||
|
||||
`cargo build --workspace` clean. `cargo test --workspace` green:
|
||||
52 E2E (+1), 14 surface parse (+2), all other buckets unchanged.
|
||||
Hash regression test in `crates/ailang-core/src/hash.rs` passes
|
||||
unchanged (the new serde tag `"clone"` appears in zero existing
|
||||
fixtures). `git diff examples/` is empty for pre-existing
|
||||
fixtures.
|
||||
|
||||
### Did anything surprise
|
||||
|
||||
- **Zero surprises.** The 18c.1 iter ran exactly like 18a in
|
||||
shape: new variant + ~10 mechanical recursion sites + parser
|
||||
+ printer + fixture + test. Both iters lean on the same
|
||||
underlying structure (an additive AST extension that
|
||||
typechecker / codegen / desugar passes traverse without
|
||||
semantic effect). When 18c.2 / 18c.3 land they will be
|
||||
fundamentally different — those are real analysis passes
|
||||
with semantic consequences. The schema-floor iter pattern is
|
||||
rich because the project has good structural discipline
|
||||
(every Term-traversal site uses pattern matching, not
|
||||
reflection), and that's worth recording.
|
||||
|
||||
- **The future-work seam is one line.** 18c.3 only needs to
|
||||
flip the codegen `Term::Clone` arm from "lower inner, return
|
||||
the SSA reg" to "lower inner, emit one `inc` call, return the
|
||||
SSA reg". That's the entire emission cost of explicit clone.
|
||||
The hard work in 18c.3 is everywhere else (uniqueness
|
||||
inference + dec instrumentation across all binder-going-out-
|
||||
of-scope sites); explicit clone is comparatively cheap.
|
||||
|
||||
### Next
|
||||
|
||||
Iter 18c.2: linearity check. Walk every fn body whose params
|
||||
are all explicit-mode (`Borrow` or `Own`, no `Implicit`); track
|
||||
each binder's consumption state through the AST; emit
|
||||
structured `use-after-consume` / `consume-while-borrowed`
|
||||
diagnostics with form-A `suggested_rewrites`. Functions with any
|
||||
`Implicit` param stay exempt — that's the back-compat lane
|
||||
while existing fixtures stay unannotated.
|
||||
|
||||
The check is purely an addition to the diagnostic pipeline; it
|
||||
emits no IR change. Existing fixtures (all `Implicit`) are
|
||||
untouched. The new `borrow_own_demo` fixture (which has all-
|
||||
explicit modes) becomes the first program subject to the check;
|
||||
its current shape (borrow-then-own on `xs`) is exactly what the
|
||||
check should accept, so 18c.2 starts as a "green for the
|
||||
existing test" iter.
|
||||
|
||||
Reference in New Issue
Block a user