PR #7149 (#7034 §3) licenses const r = A[i] on an element-shape-proven local
array and promotes r. It deliberately covers one read form. The others
are listed here so the gap is a decision, not an oversight.
Covered
const r = A[i] under for (let i = 0; i < A.length; i++).
for (const r of A) — the desugar
(lower/stmt_loops.rs::lazy_or_index_elem) produces exactly the above, so
this is covered by the same proof rather than by a second one.
Not covered
1. Direct A[i].field with no binding. total += rows[i].weight.
ArrayWalk admits it (a declared-chain field read/write cannot transition
the shape, so it does not disqualify the array), but nothing promotes it:
FnCtx::ptr_shape_receiver_fact matches only Expr::LocalGet and
Expr::This. Extending it to Expr::IndexGet needs the fact keyed on the
site — (array root, index local) is a sound key given E5 already requires
the index local to have exactly one governing loop — and each consumption site
(property_get.rs, property_get/helpers.rs, property_set.rs,
instance_misc1.rs, lower_call/property_get/dynamic_dispatch.rs) needs the
receiver lowered per access, which is one array element read per access rather
than one reload from a slot. Worth measuring against just asking users to bind
the element.
2. A.map(cb) / A.forEach(cb) / A.reduce(cb, …) callback parameters.
This is the call argument position (#7034 §1) — it needs clone-and-route,
not a containment relaxation. It is where batch.ts's hot field traffic
actually lives (acc[idx] inside the reducer).
3. Element reads bound to a let. E1's mutable: false on the array and
the single-Let requirement on the element local are both conservative; a
let r reassigned only from A[i] is provable but not proven.
4. Non-empty array literals. const a = [new C(1), new C(2)] is refused
because a literal can carry elisions, whose slots read back as undefined and
would break E5's density argument. A literal with no holes is fine and could
be admitted.
5. Shape-preserving mutators. sort, reverse, slice, concat of two
proven arrays, and fill with a class-C value all preserve "every element
is an instance of C" but are refused outright by E2, which admits only
push. pop/shift/splice/unshift/IndexSet must stay refused — they
are what E5's in-bounds argument rests on.
Each is independently landable, and each should be measured before it is
built: on the corpus measured in #7149, the element position accounts for 1 of
746 Ptr<Shape> candidates in dependency JS, so widening the READ side is only
worth it where app-shaped TypeScript is the target.
PR #7149 (#7034 §3) licenses
const r = A[i]on an element-shape-proven localarray and promotes
r. It deliberately covers one read form. The othersare listed here so the gap is a decision, not an oversight.
Covered
const r = A[i]underfor (let i = 0; i < A.length; i++).for (const r of A)— the desugar(
lower/stmt_loops.rs::lazy_or_index_elem) produces exactly the above, sothis is covered by the same proof rather than by a second one.
Not covered
1. Direct
A[i].fieldwith no binding.total += rows[i].weight.ArrayWalkadmits it (a declared-chain field read/write cannot transitionthe shape, so it does not disqualify the array), but nothing promotes it:
FnCtx::ptr_shape_receiver_factmatches onlyExpr::LocalGetandExpr::This. Extending it toExpr::IndexGetneeds the fact keyed on thesite —
(array root, index local)is a sound key given E5 already requiresthe index local to have exactly one governing loop — and each consumption site
(
property_get.rs,property_get/helpers.rs,property_set.rs,instance_misc1.rs,lower_call/property_get/dynamic_dispatch.rs) needs thereceiver lowered per access, which is one array element read per access rather
than one reload from a slot. Worth measuring against just asking users to bind
the element.
2.
A.map(cb)/A.forEach(cb)/A.reduce(cb, …)callback parameters.This is the
call argumentposition (#7034 §1) — it needs clone-and-route,not a containment relaxation. It is where
batch.ts's hot field trafficactually lives (
acc[idx]inside the reducer).3. Element reads bound to a
let. E1'smutable: falseon the array andthe single-
Letrequirement on the element local are both conservative; alet rreassigned only fromA[i]is provable but not proven.4. Non-empty array literals.
const a = [new C(1), new C(2)]is refusedbecause a literal can carry elisions, whose slots read back as
undefinedandwould break E5's density argument. A literal with no holes is fine and could
be admitted.
5. Shape-preserving mutators.
sort,reverse,slice,concatof twoproven arrays, and
fillwith a class-Cvalue all preserve "every elementis an instance of
C" but are refused outright by E2, which admits onlypush.pop/shift/splice/unshift/IndexSetmust stay refused — theyare what E5's in-bounds argument rests on.
Each is independently landable, and each should be measured before it is
built: on the corpus measured in #7149, the element position accounts for 1 of
746
Ptr<Shape>candidates in dependency JS, so widening the READ side is onlyworth it where app-shaped TypeScript is the target.