Skip to content

fix(hir): self-named native member base no longer OOM-loops codegen (#4908) - #4955

Merged
proggeramlug merged 3 commits into
mainfrom
worktree-fix-4908-super-createconnection
Jun 11, 2026
Merged

proggeramlug merged 3 commits into
mainfrom
worktree-fix-4908-super-createconnection

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Fixes #4908.

Root cause

class Agent extends http.Agent { ... } OOM-crashed codegen during "Generating code...".

The class-heritage lowering resolves a member-expression base (http.Agent) by its trailing property only — extract_member_class_name returns "Agent". The subclass registers its own name (Agent) before heritage resolution, so lookup_class("Agent") returns the class itself and both extends and extends_name end up self-referencing. Every codegen parent-chain walk (extends_name → ctx.classes by name → same class) then loops forever until the process is OOM-killed.

Confirmed by bisection: an empty class Agent extends http.Agent {} — no method, no super, no captured let — is already enough to crash. The super.createConnection() call and the captured-let ++ cited in the issue are not the trigger (both compile fine on their own; a CPU sample showed a tight recursive cycle in the parent-chain walk).

Fix

Guard both member-heritage arms (class declaration + class expression): when the member's trailing name equals the subclass's own name, leave the class parentless (extends/extends_name/native_extends/extends_expr all None) instead of self-linking.

This matches how a non-colliding native member base (class Foo extends http.Agent) already behaves — extends_name there resolves to no known class, so the class is effectively parentless and constructs cleanly. Routing through the dynamic extends_expr path was tried and rejected: it makes the class derived and demands a runtime super() into the native base, which fails (Class extends value is not a constructor / Must call super constructor).

Cross-module member-extends resolution (drizzle import_mid.SQLiteColumnBuilder, ethers ethers.JsonRpcProvider) is unchanged — the else branch is byte-identical; the new guard only fires on the name-collision case.

Native member-base inheritance (real instanceof / super.method dispatch into the base) remains unimplemented for the member-expression case generally; this PR scopes to the codegen crash per the issue's acceptance ("the four tests compile").

Validation

  • The four class Agent extends http.Agent node:http tests now compile (-abort-keep-alive-destroy-res, -abort-keep-alive-queued-tcp-socket, -abort-unix-socket, -read-in-error).
  • New regression test crates/perry/tests/issue_4908_subclass_native_member_base.rs (2 cases: empty subclass; method + super.createConnection + captured let++) — both compile and construct/run cleanly.
  • cargo test -p perry-hir green.

Metadata (version bump + CHANGELOG) intentionally omitted for the maintainer to fold at merge.

Ralph Küpper and others added 3 commits June 10, 2026 23:15
…4908)

`class Agent extends http.Agent` resolved the member base by its trailing
property only (`extract_member_class_name` -> "Agent"), which equals the
subclass's own name. The subclass registers its name before heritage
resolution, so `lookup_class("Agent")` returned the class itself and both
`extends` and `extends_name` self-referenced. Every codegen parent-chain
walk then looped forever, OOM-killing the process during "Generating
code..." — the four node:http `class Agent extends http.Agent` abort/error
tests all crashed this way.

Guard both member-heritage arms (class decl + class expr): when the
member's trailing name equals the subclass name, leave the class
parentless instead of self-linking. This matches how a non-colliding
native member base (`class Foo extends http.Agent`) already behaves and,
unlike routing through the dynamic extends_expr path, constructs cleanly
(no spurious "Must call super constructor"). Cross-module member-extends
resolution (drizzle/ethers) is unchanged — the else branch is identical.

Adds a regression test covering the empty and method+super+captured-let
shapes.
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.

codegen: subclassing http.Agent with super.createConnection() + captured mutated let fails to compile

1 participant