@@ -41,20 +41,41 @@ target parameter is `noescape Fn()`"). This is a deliberate review-first
4141restriction (an escaping closure that captures is a retention/aliasing concern).
4242
4343So the precise need is ** owned, escaping closures storable in a collection**
44- (` List<Fn(UOp) -> UOp> ` of rules). The VM already represents them (a ` Closure ` is
45- an ` Rc ` , trivially storable); the gap is the ** checker's escape rule** plus a
46- ** storable ` Fn ` value type** in signatures. Relaxing this is the one real language
47- decision — and it must clear RSScript's admission bar: an escaping+capturing
48- closure has to * phrase as a reviewer question* (what it captured, whether it can
49- retain), e.g. via an explicit ` own ` /move capture annotation and the ` Fn ` type
50- surfacing in the signature, so nothing is implicit.
44+ (` List<owned Fn(UOp) -> UOp> ` of rules).
45+
46+ ** Probe result (confirmed empirically).** RSScript already has ` owned Fn(...) `
47+ (escaping, move-captures owned values, lowers to ` Box<dyn FnMut> ` ), but it is
48+ gated: a ` List<owned Fn(Int)->Int> ` field / generic arg is rejected with
49+ ` RS0015 ` — * "` owned Fn(...) ` is only supported as a direct function parameter
50+ type" / "unsupported owned position"* — and consequently a closure literal in a
51+ value position is an "unsupported expression", a struct can't hold one, and a
52+ list-fetched closure ` f(x) ` "does not resolve". So closures are usable ** only
53+ inline at the call site of a function with an ` Fn ` parameter** — not as
54+ first-class values. The gap is a ** frontend restriction (parser + checker)** , NOT
55+ a missing capability: the runtime (` VmClosure ` /` MakeClosure ` /` CallClosure ` ) and
56+ the lowering (` Box<dyn FnMut> ` , ` move ` capture) substrate already exist.
57+
58+ So L1 is "lift a deliberate gate", not "build closures." The sound rule:
59+ ** storable ⇒ ` owned ` ⇒ move-captures owned values ⇒ no aliasing** , with the
60+ ` owned Fn ` type visible wherever it's stored. That preserves review-first by the
61+ same argument that already makes ` owned ` parameters sound (ownership moved in, no
62+ shared mutable state). ` noescape ` /` read Fn ` stay parameter-only (storing a
63+ borrow-capturing closure would let a borrow escape — unsound), so the relaxation
64+ is narrow and one-directional.
5165
5266## Layered plan (priority order)
5367
54- 1 . ** L1 — language (keystone): owned/escaping storable closures.** Allow a
55- closure that captures owned values to escape into a value of a first-class
56- ` Fn(args) -> ret ` type and be stored in containers. Make the capture + escape
57- explicit in the signature (review-first). Everything else rides on this.
68+ 1 . ** L1 — language (keystone): make ` owned Fn ` a first-class value.** Lift the
69+ "parameter-position-only" gate so ` owned Fn(args) -> ret ` is allowed as a
70+ generic type argument (` List<owned Fn ...> ` ), a struct field, and a
71+ ` let ` /` local ` binding; accept a closure literal as a value expression; and
72+ resolve a call on a closure-typed binding (` f(x) ` ). Keep ` noescape ` /` read Fn `
73+ parameter-only. Work is ** parser + checker** (type a closure literal as
74+ ` owned Fn ` ; allow it in storable positions; resolve closure-value calls) plus a
75+ ** lowering** glue step (emit closure values + ` Box<dyn FnMut> ` in non-parameter
76+ positions, call closure-typed loads) — all on the existing
77+ ` MakeClosure ` /` CallClosure ` + ` Box<dyn FnMut> ` substrate. Parity-gated like any
78+ feature. Everything else rides on this.
58792 . ** L2 — library/runtime: PatternMatcher + graph_rewrite.** With L1, build a
5980 ` UOp ` /` UPat ` type and a native ` graph_rewrite(sink, rules, bottom_up, name) `
6081 driver (fixpoint + memoization) plus a gated ` toposort(gate: Fn(UOp)->Bool) ` —
0 commit comments