Skip to content

Merge train: #9566 #9567 #9568 - #9572

Merged
proggeramlug merged 7 commits into
mainfrom
land-train81
Sep 2, 2026
Merged

Merge train: #9566 #9567 #9568#9572
proggeramlug merged 7 commits into
mainfrom
land-train81

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Lands the validated train: #9566 (Linux callback-deopt GC coverage restored — the fixture now builds a release panic=abort runtime per the #8479 exception-transport contract, replacing the Linux ignore from #9488), #9567 (dynamic parent of a mixin-of-a-mixin registered, #9079), #9568 (child-process null-byte validation, #9537).

Validation: release build green; RUST_TEST_THREADS=1 perry-runtime green; perry-hir green; the restored deopt test passes (both evacuation arms, 175s); both gap fixtures byte-identical to node; lint gates green.

Rebase-merge preserving authorship.

proggeramlug and others added 7 commits September 2, 2026 22:01
`const Mixed2 = mixin(Mixed)` — a mixin applied to a previous mixin's
RESULT — SIGSEGVed as soon as anything derived from it was constructed.
Node prints `4` for the issue's reproducer; Perry exited 139 after
unbounded recursion.

The HIR mixin fast path in `lower/stmt.rs` synthesizes a real class for
`const M = mixinFn(Base)`. At the second level the base is `Mixed`, a
lexical VALUE binding, so `lower_class_from_ast` takes its
locally-shadowed arm and captures the parent as `extends_expr` — a
dynamic parent — rather than a static class link. That is correct. What
was missing is the other half: unlike the sibling `const X = class {…}`
path immediately above it, this arm bound the synthesized class without
emitting the declaration-time `RegisterClassParentDynamic`. The
generated `Mixed2_constructor` therefore called
`js_get_dynamic_parent_value` for its class id with no registration to
answer it; with an undefined parent `js_fetch_or_value_super` fell back
to the most-derived receiver, re-selected `Mixed2`, and recursed until
the stack overflowed.

Emit the registration here too, in source order after the parent's own
value binding and before this class's — exactly where the sibling path
puts it. A single-level `mixin(Root)` extends a real class, keeps
`extends_expr` at `None`, and is unchanged: that is why one level
already worked (#9073) and two did not.

Verified on Linux (perrymaster) with a fresh
`PERRY_NO_AUTO_OPTIMIZE=1 cargo build --profile perry-dev -p perry
-p perry-runtime-static -p perry-stdlib-static`:

- Baseline binary built from this tree before the patch: exit 139.
- After: the gap fixture is byte-identical to the pinned Node 26.5.1
  oracle under both `PERRY_NO_AUTO_OPTIMIZE=1` and the default
  auto-optimize pipeline.
- LLVM for the fixture: every `js_get_dynamic_parent_value(i32 N)` in
  the module now has a matching `js_register_class_parent_dynamic(i32
  N, …)` — zero orphans; `Mixed2_constructor`'s id is among them.
- `cargo test -p perry-hir`: all green. The new lowering unit test was
  confirmed to FAIL against the unpatched lowering.

The gap fixture keeps the issue's reproducer verbatim and adds the
assertions it left open: inherited `Root` state and the mixin method
through both synthesized levels, `instanceof` across the whole chain, a
leaf with no own constructor, the still-working one-level case, and a
three-level chain built from three distinct mixins so a dropped level
shows up as a missing method rather than being masked by identical
bodies.

Closes #9079

Claude-Session: https://claude.ai/code/session_01SNcEDcviLvFMta5oL7Zxig
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.

1 participant