Found while landing #7532 (typed-shape layout declared at allocation).
The finding
HIR rewrites a closed-shape object literal into new __AnonShape_<hash>(…), and that synthesized constructor is a qualifying prologue for #7532's declaration gate. It still does not qualify — because the minted class's field types come out as Any, not Number.
Any is pointer-bearing, so class_layout_declarable_at_allocation rejects it, correctly.
But the consequence is bigger than one missed optimization:
{v: number, w: number} is currently declared to the collector as two POINTER slots.
Every closed-shape numeric literal — the single most common allocation shape in the churn family and in most real JS — tells the GC it holds two pointers when it provably holds two doubles. That costs:
- scanning: minor and full collections trace two slots per object that can never contain a reference;
- the raw-f64 store path: there is no raw-f64 store to unlock at all, because the descriptor never says raw-f64;
- layout bookkeeping: pointer slots take the mask-maintaining path rather than the pointer-free one.
Why this is a different lever from #7532
#7532 fixed an ordering problem — the layout was installed after the constructor, so stores inside it could not pass the intact-bit guard. That is now closed for declared classes: push_cls went 1.553×, and its promotion fell 210,488 → 64 bytes, matching what the equivalent literal already achieved.
This one is a type-propagation gap. The literal's element types are known at the site ({v: 1, w: 2} or {v: someNumber, w: otherNumber}) and are simply not carried into the minted class's ClassField::ty. Nothing about the ordering work reaches it.
Where to look
mint_anon_shape_class in perry-hir's lower/context.rs — what it writes into each synthesized ClassField::ty, and whether the literal's per-property inferred type is available at that point (the value-type analysis in analysis/value_types.rs already answers this for other consumers). The acceptance question is narrow: does a {v: number, w: number} literal end up with Number-typed fields and therefore a raw-f64 descriptor?
Acceptance
{v: 1, w: 2} constructs with a raw-f64 descriptor and a pointer-free layout, verified by the same measurements #7532 used: an IR census showing the stores no longer route through js_put_value_set, and PERRY_GC_DIAG promotion/copied bytes at or below the current numbers. Guard against the #6377 failure mode — more type visibility un-gates latent fast paths — with a full gap-suite run against a same-session main baseline.
Found while landing #7532 (typed-shape layout declared at allocation).
The finding
HIR rewrites a closed-shape object literal into
new __AnonShape_<hash>(…), and that synthesized constructor is a qualifying prologue for #7532's declaration gate. It still does not qualify — because the minted class's field types come out asAny, notNumber.Anyis pointer-bearing, soclass_layout_declarable_at_allocationrejects it, correctly.But the consequence is bigger than one missed optimization:
Every closed-shape numeric literal — the single most common allocation shape in the
churnfamily and in most real JS — tells the GC it holds two pointers when it provably holds two doubles. That costs:Why this is a different lever from #7532
#7532 fixed an ordering problem — the layout was installed after the constructor, so stores inside it could not pass the intact-bit guard. That is now closed for declared classes:
push_clswent 1.553×, and its promotion fell 210,488 → 64 bytes, matching what the equivalent literal already achieved.This one is a type-propagation gap. The literal's element types are known at the site (
{v: 1, w: 2}or{v: someNumber, w: otherNumber}) and are simply not carried into the minted class'sClassField::ty. Nothing about the ordering work reaches it.Where to look
mint_anon_shape_classinperry-hir'slower/context.rs— what it writes into each synthesizedClassField::ty, and whether the literal's per-property inferred type is available at that point (the value-type analysis inanalysis/value_types.rsalready answers this for other consumers). The acceptance question is narrow: does a{v: number, w: number}literal end up withNumber-typed fields and therefore a raw-f64 descriptor?Acceptance
{v: 1, w: 2}constructs with a raw-f64 descriptor and a pointer-free layout, verified by the same measurements #7532 used: an IR census showing the stores no longer route throughjs_put_value_set, andPERRY_GC_DIAGpromotion/copied bytes at or below the current numbers. Guard against the #6377 failure mode — more type visibility un-gates latent fast paths — with a full gap-suite run against a same-sessionmainbaseline.