Repository navigation
fix(hir): synthesized capture ctor on a derived class must call super() (#4972) - #4980
Merged
Merged
Conversation
…() (#4972) A derived class with NO user-written constructor whose members capture an enclosing-scope local threw "Must call super constructor in derived class before accessing 'this'" at construction time (test-http-client-readable). synthesize_class_captures (#212/#740) materializes a capture-stashing constructor (__perry_cap_<id> params + this-PropertySets) when class members reference outer locals. That synthesized ctor contained no super() call, so codegen's static derived-ctor TDZ check in lower_call/new.rs (own ctor + heritage + no super() => unconditional ReferenceError) fired for a class the user never wrote a ctor for: const Duplex = require('stream').Duplex; class FakeAgent extends http.Agent { createConnection() { return new Duplex(); } // captures Duplex } new FakeAgent(); // threw pre-fix Fix: when the fresh (non-user) ctor is synthesized for a class with any heritage (extends / extends_name / native_extends / extends_expr), seed its body with super() - mirroring the spec default ctor constructor(...args) { super(...args) }. The existing insertion logic already places the capture assignments after the first SuperCall, so TDZ ordering is preserved. This both suppresses the bogus static throw and routes known user-class parents through the inline-parent-ctor arm so the parent body actually runs (pure-TS shape: function-nested class C extends P { m() { return capturedLocal; } } previously threw; now constructs and runs P's ctor, matching Node). SuperCall codegen is a safe no-op for unknown/native member bases ("Agent"), so the corpus shape stays effectively parentless, same as the no-capture case (#4908). test-http-client-readable now passes (exit 0) staged against the node-core shim common.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4972.
Problem
test-http-client-readablefailed with:As the issue predicted, this is general class codegen, not http-specific. The trigger is the combination derived class + captured outer binding + no user-written constructor:
Pure-TS minimal repro (also threw; Node prints
1 42):Root cause
synthesize_class_captures(#212/#740) materializes a capture-stashing constructor (__perry_cap_<id>params +thisPropertySets) when class members reference enclosing-scope locals. That synthesized ctor contained nosuper()call. Codegen's static derived-ctor TDZ check inlower_call/new.rs(own ctor + heritage + nosuper()⇒ unconditionaljs_throw_reference_error_this_before_super) then fired for a class the user never wrote a constructor for — the captured binding alone forced the ctor into existence and flipped the class from the (working) no-own-ctornewpath onto the own-ctor path.Fix
When the fresh (non-user) ctor is synthesized for a class with any heritage (
extends/extends_name/native_extends/extends_expr), seed its body withsuper()— mirroring the spec default ctorconstructor(...args) { super(...args) }. The existing insertion logic already places capture assignments after the firstSuperCall, so this-after-super ordering is preserved. User-written ctors are untouched; the static TDZ check still fires for genuinely missingsuper().Effects:
http.Agent):SuperCallcodegen is a safe no-op for unknown parent names, so the class stays effectively parentless — same behavior as the no-capture case (codegen: subclassing http.Agent with super.createConnection() + captured mutated let fails to compile #4908).super()routes through the inline-parent-ctor arm, so the parent body now actually runs (c.p === 1above) — previously this shape threw, so nothing working could regress.Validation
test-http-client-readablestaged against the node-core shimcommon: exits 0 (was: ReferenceError at startup).crates/perry/tests/issue_4972_derived_class_capture_super.rs: corpus shape, the pure-TS nested-class repro the issue asked for, and a base-class-with-captures guard.cargo test --release -p perry -p perry-hir: 21 suites, 0 failures (includes the codegen: subclassing http.Agent with super.createConnection() + captured mutated let fails to compile #4908issue_4908_subclass_native_member_baseneighbors).Code-only PR — no version bump / changelog per contributor flow.