Skip to content

perf(hir): type a for-head binding so a loop-built object literal stops being declared as pointer slots (#7544) - #7550

Merged
proggeramlug merged 4 commits into
mainfrom
perf/7544-anon-shape-field-types
Aug 6, 2026
Merged

perf(hir): type a for-head binding so a loop-built object literal stops being declared as pointer slots (#7544)#7550
proggeramlug merged 4 commits into
mainfrom
perf/7544-anon-shape-field-types

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Closes #7544.

The issue's premise is wrong, and the real gap is one line away

#7544 (and the note #7532 left behind) says mint_anon_shape_class "gives every
synthesized field type Any". It does not. It writes each property's inferred
type through verbatim, and { v: 1, w: 2 } already mints two Number
fields, already gets a raw-f64 mask with a null pointer mask, and already takes
#7532's allocation-site declaration. Measured on main, one literal in a file:

@perry_typed_shape_raw_f64_mask_..._AnonShape_68383f57f2ed7340 = [1 x i64] [i64 3]
call void @js_gc_declare_typed_shape_layout(i64 %r12, i32 2, ptr @…raw_f64_mask…, i32 1, ptr null, i32 0)

What is actually Any is the loop form. for (let i = 0; …) hardcoded
Type::Any for its head binding, while the statement-level let i = 0; runs
infer_decl_type and gets Number. So infer_type_from_expr saw i: Any,
i + 1 inherited it, and { v: i, w: i + 1 } minted two Any fields. Any is
pointer-bearing, so on main that literal compiles to:

@perry_typed_shape_mask_churn_ts____AnonShape_c42fad28a2551dcc = [1 x i64] [i64 3]   ← POINTER mask
call void @js_gc_init_typed_shape_layout(i64 %r44, i32 2, ptr null, i32 0, ptr @…mask…, i32 1)

Two pointer slots holding two doubles, installed after the constructor. That
is #7544's headline claim, and it is real — it just isn't
mint_anon_shape_class's doing.

Here is the full map of what the boundary was, measured by lowering each form
and reading the minted ClassField::ty:

literal before after
{a: 1} Number Number
let x = 0; {a: x} Number Number
while counter Number Number
for (let i = 0; …) {a: i} Any Number
for (let i = 0; …) {a: i + 1} Any Number
for (let i = 0; …) {a: i * 2} Any Number
for (var i = 0; …) {a: i} Any Any
function g(p) { {a: p} } Any Any
{a: someAny} Any Any
{a: "s"} String String

The change

Both for-head declarator sites in lower/stmt.rs now call the same
infer_decl_type the statement-level declarator uses, instead of hardcoding
Type::Any. That is the whole diff; type_infer and its parent module go from
private to pub(crate) to allow the call.

var heads are deliberately left at Type::Any: a var head binding is
function-scoped and var-hoisted, so its declarator is not the only writer of the
name and the statement-level parity argument does not carry over.

What is propagated, and its exact boundary

The propagated fact is the initializer-inferred type of the head binding
byte-for-byte the same computation let i = 0; has always performed. Nothing
new is invented and no new kind of fact enters the layout: the annotation
channel (for (let i: number = 0; …)) is the one let y: number = 0; already
carried into anon-shape fields on main, which the table above shows.

This is not a runtime type assertion, and the PR does not treat it as one.
Perry validates no declared type at runtime, so a Number-typed field can still
receive a pointer through a later dynamic write. That is discharged exactly
where #7532 discharged it for declared classes: the raw-f64 store guard rejects
the non-double bits, falls back to the boxed setter, and layout_note_slot
downgrades the descriptor to GC_LAYOUT_UNKNOWN so the collector scans the slot
conservatively from then on. Verified, not assumed — see below.

Evidence

IR census — { v: i, w: i + 1 } in a loop, identical source, only the compiler differs

call before after
js_gc_init_typed_shape_layout (post-constructor) 1 0
js_gc_declare_typed_shape_layout (at allocation) 0 1
js_gc_note_slot_layout 3 1
js_write_barrier_slot 3 1
js_string_addref_if_heap_string 3 1
js_dynamic_string_or_number_add 1 0

and the mask flips from pointer_mask = [i64 3] to raw_f64_mask = [i64 3], pointer_mask = null — a pointer-free layout, which is #7544's stated
acceptance.

Two corrections to the expected evidence, both worth recording. The anon-shape
constructor's stores never routed through js_put_value_set — that was #7532's
declared-class path; the synthesized ctor's stores are direct-GEP, and what
they shed here is the three-call bookkeeping preamble above. And the collector's
byte counters do not move: copied_bytes 361760, promoted_bytes 91216,
8 cycles, byte-identical across both arms. That is expected on reflection —
mark_field_into_worklist re-validates every slot word and rejects a double, so
declaring the slots pointers cost a visit and a reject, never retention. The
win is scan work and store bookkeeping, not retained bytes. "At or below
current" holds; "below" would have been a wrong claim.

Program output is unchanged (1999999000000 1954 on both arms).

Survival and self-healing

test-files/test_gap_7544_anon_shape_numeric_fields.ts is byte-diffed against
the pinned node oracle and asserts, in one program:

  • 40 000 mixed { v: number, w: number, tag: string } literals built in a loop,
    10 retained across forced collections — every one readable, heap-string child
    included (a wrongly-declared pointer-free layout would strand it);
  • 20 000 pure { a: number, b: number } literals — the shape that actually gets
    the raw-f64 pointer-free descriptor;
  • 20 000 objects constructed with a numeric field that then receive freshly
    allocated heap strings over it, collected twice, read back — the self-healing
    path;
  • a mixed contradiction where half the objects keep numbers and half take
    strings out of the same shape.

The subject is verified live in the emitted IR rather than assumed: three
js_gc_declare_typed_shape_layout calls, raw-f64 masks on the pure-numeric
shapes and on the two contradicted ones, and a pointer mask ([i64 4], slot 2)
only on the mixed {v, w, tag} shape — which correctly does not qualify for
the allocation-site declaration.

Run under the sharpest instruments the repo has, compiled and run with
PERRY_GC_MOVING_LOOP_POLLS=1:

PERRY_GC_ZEAL=1 PERRY_GC_PROTECT_FROMSPACE=1 PERRY_GC_PROTECT_FROMSPACE_DEPTH=800
  → 100 131 copying minors, 100 131 quarantined from-space page-sets, exit 0,
    output identical to the node oracle
PERRY_GC_ZEAL=1 PERRY_GC_VERIFY_EVACUATION=1
  → exit 0

The retired_set= count is checked, not just the exit code: a run with zero
copying minors protects nothing.

Sabotage-tested

crates/perry-hir/tests/anon_shape_field_types.rs has 7 tests. Replacing
for_init_binding_type's body with Type::Any turns 4 of 7 red (the
positive cases) and leaves the 3 boundary cases green — verified by hand. A test
that passes with the change removed would not be evidence of anything.

Gates

raw_handle_debt.py 999 (baseline 999) · gc_store_site_inventory.py pass ·
check_file_size.sh pass · cargo fmt --all -- --check clean.

addr_class_inventory.py exits 0 but reports a stale ratchet baseline:
lone-valid-obj-ptr | crates/perry-runtime/src/object/field_get_set.rs: baseline says 1, found 0. That file is not in this diff — it is pre-existing on main
and wants --write-baseline in a separate commit.

Summary by CodeRabbit

  • Bug Fixes

    • Improved type inference for let and const bindings in for loops.
    • Preserved accurate field types for synthesized anonymous objects, including numeric, string, mixed, and unknown values.
    • Improved handling of numeric object fields when values change dynamically, including during garbage collection.
  • Tests

    • Added coverage for inferred field types, shadowed bindings, mixed values, and runtime object updates.
    • Added garbage-collection validation for numeric and string fields.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6abb0542-f65e-467d-9474-5f8029cc0de9

📥 Commits

Reviewing files that changed from the base of the PR and between 7346528 and 4e382a3.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • CLAUDE.md
  • Cargo.toml

📝 Walkthrough

Walkthrough

let and const for-loop bindings now use initializer-inferred types for anonymous-shape fields. HIR and runtime tests cover numeric, mixed, unknown, shadowed, and dynamically overwritten fields under garbage collection.

Changes

For-initializer type inference

Layer / File(s) Summary
Expose declaration inference
crates/perry-hir/src/destructuring/mod.rs, crates/perry-hir/src/destructuring/var_decl.rs, crates/perry-hir/src/lower/stmt.rs
Declaration inference modules are crate-visible. A helper infers identifier binding types for let and const for initializers.
Apply inferred loop binding types
crates/perry-hir/src/lower/stmt.rs, changelog.d/7550-anon-shape-numeric-field-types.md
Primary and secondary non-var declarators use inferred types instead of Type::Any. Numeric layouts use allocation-time metadata, while dynamic pointer writes downgrade the layout.
Validate anonymous-shape field inference
crates/perry-hir/tests/anon_shape_field_types.rs
Tests cover numeric, string, unknown, any, var, mixed, and shadowed bindings.
Validate numeric layouts and dynamic writes
test-files/test_gap_7544_anon_shape_numeric_fields.ts, changelog.d/7550-anon-shape-numeric-field-types.md, Cargo.toml, CLAUDE.md
Runtime tests cover numeric layouts, mixed objects, moving-GC survival, string overwrites that trigger layout downgrade, and the version update to 0.5.1312.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ForLowering
  participant infer_decl_type
  participant AnonymousShape
  participant GarbageCollector
  ForLowering->>infer_decl_type: infer let or const initializer type
  infer_decl_type-->>ForLowering: return inferred Type
  ForLowering->>AnonymousShape: create typed anonymous-shape fields
  AnonymousShape->>GarbageCollector: publish numeric or pointer-bearing layout
  GarbageCollector-->>AnonymousShape: retain values across collection
Loading

Possibly related issues

  • PerryTS/perry issue 7547: The PR infers let and const types in for initializers to enable numeric anonymous-shape fields.

Possibly related PRs

  • PerryTS/perry#7552: Directly extends the for-initializer type inference changes for anonymous-shape field typing.
  • PerryTS/perry#6877: Both changes modify HIR lowering for loop-scoped let and const declarations.
  • PerryTS/perry#6916: The HIR inference supports numeric representation-selection behavior in a separate compiler phase.

Suggested labels: bug, ready

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the HIR change and its effect on loop-built object literals.
Description check ✅ Passed The description thoroughly covers the change, issue, test evidence, regression checks, and known pre-existing failures despite not using every template heading.
Linked Issues check ✅ Passed The changes satisfy issue #7544 by propagating inferred types for loop-head let and const bindings and enabling verified pointer-free numeric layouts without regressions.
Out of Scope Changes check ✅ Passed The modified modules, changelog, HIR tests, and runtime regression test directly support the linked issue and stated implementation objectives.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/7544-anon-shape-field-types

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@changelog.d/7550-anon-shape-numeric-field-types.md`:
- Around line 75-76: Clarify the two GC test counts in the changelog entry by
separating them with commas if each is a single count, or by labeling each value
if they represent distinct measurements; preserve the surrounding test
conditions and results.

In `@crates/perry-hir/tests/anon_shape_field_types.rs`:
- Around line 215-218: Update the test loop in anon_shape_field_types to declare
the inner shadowing binding as const i = z and construct the object with { r: i
}, replacing the i2 binding and reference so it verifies same-name shadowing of
the for-loop counter.

In `@test-files/test_gap_7544_anon_shape_numeric_fields.ts`:
- Around line 76-79: The dynamic-write test cases currently annotate object
literals, potentially preventing the raw-f64 initial shape from being exercised.
In test-files/test_gap_7544_anon_shape_numeric_fields.ts at lines 76-79 and
99-102, remove the type annotations from both objects (the `o` declarations) so
`{ n: i }` and `{ m: i }` are inferred naturally, while retaining the array type
`number | string`.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8637f5fb-5dce-4f12-b157-b9f5d6f1b712

📥 Commits

Reviewing files that changed from the base of the PR and between 9a77a26 and 8595988.

📒 Files selected for processing (6)
  • changelog.d/7550-anon-shape-numeric-field-types.md
  • crates/perry-hir/src/destructuring/mod.rs
  • crates/perry-hir/src/destructuring/var_decl.rs
  • crates/perry-hir/src/lower/stmt.rs
  • crates/perry-hir/tests/anon_shape_field_types.rs
  • test-files/test_gap_7544_anon_shape_numeric_fields.ts

Comment on lines +75 to +76
compile and run time — 100 131 copying minors, 100 131 quarantined from-space
page-sets, exit 0 — and under `PERRY_GC_VERIFY_EVACUATION=1`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Clarify the GC test counts.

100 131 copying minors and 100 131 quarantined from-space page-sets are ambiguous. If each value is one count, use a separator such as 100,131. If they are separate values, label both values.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@changelog.d/7550-anon-shape-numeric-field-types.md` around lines 75 - 76,
Clarify the two GC test counts in the changelog entry by separating them with
commas if each is a single count, or by labeling each value if they represent
distinct measurements; preserve the surrounding test conditions and results.

Comment on lines +215 to +218
declare const z: any;
for (let i = 0; i < 10; i++) {
const i2 = z;
const o = { r: i2 };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Test actual same-name shadowing.

Line 217 declares i2, so this test only covers an unrelated local. Declare an inner const i = z and use { r: i }. This verifies that lookup resolves the shadowing binding instead of the for counter.

Proposed test correction
-          const i2 = z;
-          const o = { r: i2 };
+          const i = z;
+          const o = { r: i };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
declare const z: any;
for (let i = 0; i < 10; i++) {
const i2 = z;
const o = { r: i2 };
declare const z: any;
for (let i = 0; i < 10; i++) {
const i = z;
const o = { r: i };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry-hir/tests/anon_shape_field_types.rs` around lines 215 - 218,
Update the test loop in anon_shape_field_types to declare the inner shadowing
binding as const i = z and construct the object with { r: i }, replacing the i2
binding and reference so it verifies same-name shadowing of the for-loop
counter.

Comment on lines +76 to +79
const healed: { n: number | string }[] = [];
for (let i = 0; i < 20000; i++) {
const o: { n: number | string } = { n: i };
if (i % 2000 === 0) healed.push(o);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Start both dynamic-write cases from unannotated numeric literals.

The annotations can make the initial shape pointer-bearing before the string write. The test can then pass without exercising layout downgrade from a raw-f64 numeric field. Keep the array type as number | string, but infer each object literal from { n: i } or { m: i }.

  • test-files/test_gap_7544_anon_shape_numeric_fields.ts#L76-L79: Remove the annotation from o.
  • test-files/test_gap_7544_anon_shape_numeric_fields.ts#L99-L102: Remove the annotation from o.
Proposed test correction
-  const o: { n: number | string } = { n: i };
+  const o = { n: i };
...
-  const o: { m: number | string } = { m: i };
+  const o = { m: i };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const healed: { n: number | string }[] = [];
for (let i = 0; i < 20000; i++) {
const o: { n: number | string } = { n: i };
if (i % 2000 === 0) healed.push(o);
const healed: { n: number | string }[] = [];
for (let i = 0; i < 20000; i++) {
const o = { n: i };
if (i % 2000 === 0) healed.push(o);
Suggested change
const healed: { n: number | string }[] = [];
for (let i = 0; i < 20000; i++) {
const o: { n: number | string } = { n: i };
if (i % 2000 === 0) healed.push(o);
const mixed: { m: number | string }[] = [];
for (let i = 0; i < 20000; i++) {
const o = { m: i };
if (i % 2000 === 0) mixed.push(o);
📍 Affects 1 file
  • test-files/test_gap_7544_anon_shape_numeric_fields.ts#L76-L79 (this comment)
  • test-files/test_gap_7544_anon_shape_numeric_fields.ts#L99-L102
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test-files/test_gap_7544_anon_shape_numeric_fields.ts` around lines 76 - 79,
The dynamic-write test cases currently annotate object literals, potentially
preventing the raw-f64 initial shape from being exercised. In
test-files/test_gap_7544_anon_shape_numeric_fields.ts at lines 76-79 and 99-102,
remove the type annotations from both objects (the `o` declarations) so `{ n: i
}` and `{ m: i }` are inferred naturally, while retaining the array type `number
| string`.

@proggeramlug

Copy link
Copy Markdown
Contributor Author

Unrelated breakage found on main (reporting, not fixing)

Two things turned up while validating this change. Neither is caused by it, and
neither is fixed here.

1. perry-codegen/tests/native_proof_buffer_views.rs — 2 tests red on main

proven_buffer_and_typed_array_reads_are_numeric_operands ... FAILED
reassigned_typed_array_store_records_runtime_fallback ... FAILED
test result: FAILED. 30 passed; 2 failed

Proved pre-existing, not inferred: I stubbed for_init_binding_type's body
back to Type::Any, rebuilt, and got the identical two failures. Both tests
construct their HIR by hand (Stmt::For { init: Some(number_let(3, "i", …)) }),
so they never traverse the AST→HIR lowering this PR touches.

This is the hazard CLAUDE.md names directly: "Integration suites under
crates/*/tests/*.rs do not run per-PR (nightly/tag only) — a regression there
can land green and sit red for days."
The most recent commit to that file is
#7509. Worth its own issue.

perry-hir (275 unit + every integration suite) and perry-transform are
clean; perry-codegen is clean apart from those two.

2. addr_class_inventory.py ratchet baseline is stale on main

Ratchet baseline is stale (sites were fixed but not recorded):
  lone-valid-obj-ptr | crates/perry-runtime/src/object/field_get_set.rs:
    baseline says 1, found 0 — lower it to 0

The script still exits 0, so lint stays green; the file is not in this diff.
Wants a --write-baseline commit of its own.

Additional validation: the forms this change could plausibly break

Typing the for-head binding is a "more type visibility" change, so the #6377
failure mode — un-gating a latent fast path the microbench never exercises — is
the real risk. Beyond the full gap suite (running), I diffed 17 hazardous head
shapes against the pinned node oracle, on both compilers:

counter reassigned to a string mid-loop · counter assigned a string through
any · string head · bigint head · object head · array head · multiple
declarators of mixed type · null/undefined heads · float head · counter
captured per-iteration by a closure · counter feeding an object literal ·
counter in string-coercion context · NaN/Infinity heads · head shadowing an
outer binding of a different type · const head · destructuring head · nested
loops feeding a literal from two counters.

All 17 match node byte-for-byte before and after. The two arms produce
identical output.

Ralph Küpper added 3 commits August 7, 2026 00:00
)

`for (let i = 0; …)` hardcoded `Type::Any` for the head binding while the
statement-level `let i = 0;` runs `infer_decl_type` and gets `Number`. The
gap is GC-visible: a closed-shape object literal lowers to
`new __AnonShape_<hash>(…)` whose `ClassField::ty` comes from
`infer_type_from_expr` over each property's value, so inside a `for` loop
`{ v: i, w: i + 1 }` minted two `Any` fields — and `Any` is pointer-bearing,
so the most common allocation shape in the language was declared to the
collector as two POINTER slots.

Both for-head declarator sites now delegate to the same `infer_decl_type`
the statement-level declarator uses. `var` heads keep `Type::Any` (they are
function-scoped and var-hoisted, so the declarator is not the only writer).

Claude-Session: https://claude.ai/code/session_019EHcmXKArA7m42SihYCcgH
…ling (#7544)

`crates/perry-hir/tests/anon_shape_field_types.rs` pins both directions of
what a closed-shape literal mints. The four positive cases fail when the
change is stubbed out (verified by hand: replace `for_init_binding_type`'s
body with `Type::Any` and 4 of 7 go red); the three boundary cases — a bare
parameter, an explicit `any`, a `var` head, and an inner shadow — pass either
way, which is the point: a value we cannot type must still mint `Any`.

`test-files/test_gap_7544_anon_shape_numeric_fields.ts` is the runtime
witness, byte-diffed against the node oracle. It builds literals through the
new path, forces collections, and asserts every element survives; then it
writes freshly allocated heap strings over already-constructed *numeric*
fields, collects again, and reads them back. The subject is verified live in
the emitted IR: three `js_gc_declare_typed_shape_layout` calls and a raw-f64
mask on the pure-numeric shapes, with a pointer mask only on the mixed one.

Claude-Session: https://claude.ai/code/session_019EHcmXKArA7m42SihYCcgH
@proggeramlug
proggeramlug force-pushed the perf/7544-anon-shape-field-types branch from 8595988 to 7346528 Compare August 6, 2026 22:02
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@proggeramlug

Copy link
Copy Markdown
Contributor Author

Full gap suite, both arms — zero status differences across all 490 tests

The #6377 gate. Two complete runs of the whole gap suite, same session, same
host, same node oracle (.node-version = 26.5.1), compared per test from the
result journals
, not by eyeballing the summaries.

baseline (main) this branch
tests 490 490
pass 465 465
parity_fail 18 18
crash 7 7
compile_fail 0 0
parity rate 94.8% 94.8%
STATUS DIFFERENCES: 0

Not just equal totals — the named sets are identical: the same 18 output
mismatches and the same 7 crashes, test for test. And the new witness
(test_gap_7544_anon_shape_numeric_fields) passes on both arms, which is
the right result: it is a node-parity test, so it must pass with or without the
change. Its subject-liveness is asserted separately, in the IR.

Mechanics, for the record: the baseline arm ran from this worktree at
82be8545, the branch arm from an isolated copy with its own
CARGO_TARGET_DIR and test-parity/output/ (the two share those paths, so they
cannot run concurrently in one tree). Both were SIGTERM'd once mid-run by the
harness and resumed from their journals — the recorded results carry over
unchanged, and the counts above are complete.

More unrelated breakage: gap_snapshot.json has drifted on main

The committed snapshot records 16 non-pass tests. main currently produces
25. All nine extras are present in both arms, so none is caused by this
PR:

test status on main
test_gap_6908_proxy_array_mutators crash
test_gap_fetch_request_from_node_incoming_message crash
test_gap_http_client_no_redirect_follow crash
test_gap_http_overloads_3226plus crash
test_gap_http_req_async_iterator crash
test_gap_http_res_socket_writable_onfinished crash
test_gap_net_connect_bound_value crash
test_gap_diagchannel_3082_3084_3085_3086 parity_fail
test_gap_zlib_3285_params parity_fail

Nothing in the snapshot has started passing, so this is drift in one direction
only. scripts/run_gap_tests.sh would exit non-zero on main today.

test_gap_6908_proxy_array_mutators is not a load artefact — I reproduced it
standalone with the main binary on an idle box: it hangs (killed at a 120 s
timeout) while node completes normally. The http_*/net_*/fetch_* cluster
and zlib_3285_params look host-local (the harness binds a fixed port, 17891),
but they reproduced in the arm that ran alone, so that explanation is not
established either. Each wants triage on its own.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

hir: anon-shape literals mint Any-typed fields, so {v: number, w: number} is declared to the GC as two POINTER slots

1 participant