Skip to content

fix(hir): class test262 remnant (elements/dstr/private/async tail)#4888

Merged
proggeramlug merged 1 commit into
mainfrom
language-class-remnant-parity
Jun 10, 2026
Merged

fix(hir): class test262 remnant (elements/dstr/private/async tail)#4888
proggeramlug merged 1 commit into
mainfrom
language-class-remnant-parity

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Summary

Class-suite test262 tail (language/statements/class + language/expressions/class): 4839 → 5009 pass (90.1% → 93.3%, +170, zero regressions). Broad cross-suite shard (--shard 0/12 built-ins language): 91.5% → 92.0% (+15 fixed, zero regressions). Native-region gate: pass (7/7 workloads). cargo test (hir/codegen/transform/runtime): all pass.

Root causes fixed

  1. PerformEval early errors for eval in class contexts (~120 tests; biggest bucket). New crates/perry-hir/src/lower/eval_super_scan.rs: constant direct-eval bodies are scanned per spec Contains semantics (arrows transparent; function / class-member / object-method bodies opaque) for SuperCall, SuperProperty, arguments, new.target, and private-name references. Capabilities come from the lowering context: super() allowed only directly in a derived-class ctor (new current_class_is_derived), super.x in any class member / object method, arguments rejected in field initializers (new in_class_field_init, cleared inside nested plain functions), private names checked against the lexical private_scopes stack. Violations compile to a runtime SyntaxError via new js_throw_eval_syntax_error. Indirect (0, eval) evaluates as global code: any super or new.target rejected; private names valid only if declared inside the eval source.

  2. Direct-eval wrapper rebound this (~30 tests). The constant-eval scope-IIFE template was a plain function, so eval(\"this.#x\") inside a method brand-checked against undefined and eval(\"this.prop\") read undefined. The wrapper is now an arrow, giving eval'd code the caller's this / arguments / super / new.target lexically (spec: direct eval inherits all four).

  3. Allowed super.x in eval bodies failed to parse (~24 tests). SWC rejects SuperProperty at script top level, so x = eval('() => super.x') threw a bogus SyntaxError. When the call site provides super, the body re-parses inside an object-method wrapper and the method body statements are spliced into the (now-arrow) IIFE.

  4. Derived-ctor static no-super throw too eager (~10 tests). The compile-time ReferenceError: must call super now stays silent when the ctor body has (a) a value-bearing return (return-override path; bare return undefined still throws — Object/constructor-return-undefined-throws), or (b) a closure-captured super() with no direct this use (e.g. iter.f = () => super() invoked during for-of iterator close — derived-class-return-override-{for-of,finally-super,catch-finally}-arrow; the direct-this carve-out keeps privatefieldset-evaluation-order-1 throwing).

  5. arguments in parameter defaults (8 tests). method(x = arguments[2], y) {} never synthesized the arguments object (the check only scanned the body), and the synth param was appended after defaults were lowered, so arguments in a default resolved as an unknown global. Both fixed for class methods and free functions (class_members.rs, fn_decl.rs).

  6. this in static field initializers (~4 tests). static g = this.f + '262' read undefinedinit_static_fields_late now seeds the same class-ref NaN-box this slot a static method binds.

Validation

  • 4 incremental box sweeps; failure-set diffs at each step (not aggregate %); the two intermediate regressions introduced by fix 4 (privatefieldset-evaluation-order-1, constructor-return-undefined-throws) were caught by the set-diff and fixed (direct-this carve-out + return undefined exclusion) — final diff vs merge-base f32b486 is fixes-only, in-suite and cross-suite.
  • Native-region gate: compiler_output_regression.py suite --suite native-region-prooffailed_workloads: [].
  • cargo fmt --all applied; file-size gate OK (largest touched file 1706 lines).

Knowingly deferred (tracked)

No version bump / changelog edits per external-contributor flow.

…is-binding, ctor return-override, arguments-in-defaults, static-field this

language/{statements,expressions}/class: 4839->5009 pass (90.1%->93.3%, +170, zero regressions).

- PerformEval early errors (new lower/eval_super_scan.rs): direct eval bodies
  scanned per Contains semantics (arrows transparent, function/method bodies
  opaque) for SuperCall / SuperProperty / arguments / new.target / private
  names; violations throw SyntaxError at the eval call via new
  js_throw_eval_syntax_error. Indirect (0,eval) is global code: any super or
  new.target rejected, private names only if declared inside the source.
- Direct-eval IIFE wrapper is now an ARROW: eval'd code sees the caller's
  this/arguments/super/new.target lexically (this.#x brand checks, this.prop
  reads in methods/static-field inits).
- eval bodies with super.x that fail the script-level parse re-parse inside
  an object-method wrapper when the call site provides super.
- Derived-ctor static no-super ReferenceError no longer fires when the body
  has a value-bearing return (return-override) or a closure-captured super()
  with no direct this use; bare return undefined still throws.
- arguments referenced from method/function parameter DEFAULTS now triggers
  the synthetic-arguments param (and it is appended before defaults lower).
- this in static field initializers binds the class constructor (class-ref
  seed in init_static_fields_late, mirroring compile_static_method).
@proggeramlug proggeramlug merged commit 41891af into main Jun 10, 2026
12 of 13 checks passed
@proggeramlug proggeramlug deleted the language-class-remnant-parity branch June 10, 2026 08:38
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