Skip to content

repsel: Ptr<Shape> element reads — the four forms #7034 §3 does not cover #7151

Description

@proggeramlug

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions