74379b29e5
Schema floor for explicit reuse hints. Adds Term::ReuseAs
{ source: Box<Term>, body: Box<Term> } with serde tag "reuse-as",
form-A (reuse-as <source> <body>). Schema choice (wrapper, not
modifier-on-Ctor) and rationale recorded in DESIGN.md.
Codegen is identity under all --alloc strategies — lower body,
drop source on the floor. Iter 18d.2 will lower this as the
in-place rewrite under --alloc=rc.
User-facing diagnostics:
- typecheck reuse-as-non-allocating-body: body must be a
Term::Ctor or Term::Lam, the two AST shapes that allocate.
Other shapes (literal, var, app, ...) emit this with a
suggested_rewrite that drops the wrapper.
- linearity reuse-as-source-not-bare-var: source must be a
Term::Var referring to an in-scope binder. Anything else
(literal, nested expression) is rejected here. Suggested
rewrite drops the wrapper.
- linearity use-after-consume at a reuse-as site: source must
not have been consumed earlier in the body. Suggested rewrite
drops the wrapper.
- Visiting reuse-as marks source consumed for the rest of the
body, so a subsequent use of the same binder flags
use-after-consume against it.
Uniqueness inference treats source as Consume — the consume
shows up in the side table for the codegen consumer.
Tests:
- 4 surface parse-tests (round-trip, no-args, one-arg, full
parse_term/term_to_form_a round-trip).
- 1 typecheck unit test (non-allocating body).
- 2 linearity unit tests (non-var source, after-consume).
- 1 integration test (happy-path map_inc in all-explicit mode
is linearity-clean).
- 1 E2E test running the canonical map_inc-via-reuse-as fixture
under --alloc=gc, asserting stdout 9.
- New fixture examples/reuse_as_demo.{ailx,ail.json}.
Test deltas: E2E 55 -> 56, ailang-check unit 43 -> 46,
ailang-check workspace 8 -> 9, surface 14 -> 18. cargo test
--workspace green. No existing fixture's canonical JSON changed.
65 lines
2.1 KiB
Plaintext
65 lines
2.1 KiB
Plaintext
; Iter 18d.1 — `(reuse-as xs (term-ctor List Cons ...))` schema floor
|
|
; demo. The `(reuse-as ...)` wrapper is identity at codegen in 18d.1
|
|
; (the `body` is lowered, the `source` is dropped); 18d.2 will lower
|
|
; this as in-place rewrite under `--alloc=rc`.
|
|
;
|
|
; The fixture exercises:
|
|
; - parser + printer round-trip for `Term::ReuseAs`
|
|
; - typecheck of an all-explicit-mode fn whose body returns a
|
|
; `(reuse-as <var> <ctor>)` form (body is allocating: Term::Ctor)
|
|
; - linearity check: `xs` is consumed exactly once via reuse-as in
|
|
; the Cons arm; the Nil arm doesn't consume `xs`. Merge across
|
|
; arms is consistent.
|
|
; - codegen identity under --alloc=gc: program prints `9`
|
|
; (1+1 + 2+1 + 3+1 = 9), the same value the non-reuse-as
|
|
; `map_inc` would print.
|
|
;
|
|
; Expected stdout under --alloc=gc:
|
|
; 9
|
|
|
|
(module reuse_as_demo
|
|
|
|
(data List
|
|
(doc "Monomorphic singly-linked Int list — boxed, recursive.")
|
|
(ctor Nil)
|
|
(ctor Cons (con Int) (con List)))
|
|
|
|
(fn map_inc
|
|
(doc "Increment each Int in xs. The Cons arm uses (reuse-as xs ...) — author asserts that the freshly-allocated Cons cell should reuse xs's slot. In 18d.1 codegen treats this as identity.")
|
|
(type
|
|
(fn-type
|
|
(params (own (con List)))
|
|
(ret (own (con List)))))
|
|
(params xs)
|
|
(body
|
|
(match xs
|
|
(case (pat-ctor Nil) (term-ctor List Nil))
|
|
(case (pat-ctor Cons h t)
|
|
(reuse-as xs
|
|
(term-ctor List Cons (app + h 1) (app map_inc t)))))))
|
|
|
|
(fn sum_list
|
|
(doc "Consume xs, sum its elements.")
|
|
(type
|
|
(fn-type
|
|
(params (own (con List)))
|
|
(ret (con Int))))
|
|
(params xs)
|
|
(body
|
|
(match xs
|
|
(case (pat-ctor Nil) 0)
|
|
(case (pat-ctor Cons h t)
|
|
(app + h (app sum_list t))))))
|
|
|
|
(fn main
|
|
(doc "Build [1,2,3]; map_inc → [2,3,4]; sum_list → 9. Print 9.")
|
|
(type (fn-type (params) (ret (con Unit)) (effects IO)))
|
|
(params)
|
|
(body
|
|
(let xs
|
|
(term-ctor List Cons 1
|
|
(term-ctor List Cons 2
|
|
(term-ctor List Cons 3
|
|
(term-ctor List Nil))))
|
|
(do io/print_int (app sum_list (app map_inc xs)))))))
|