Skip to content

fix(hir): non-class expression-semantics remnant test262 parity#4922

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

fix(hir): non-class expression-semantics remnant test262 parity#4922
proggeramlug merged 1 commit into
mainfrom
language-expr-remnant-parity

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Summary

Five surgical fixes to non-class expression semantics in language/expressions/*. +9 test262 cases, 0 regressions, verified same-mode (PERRY_NO_AUTO_OPTIMIZE=1) against a binary built at this branch's parent commit across all 11 target directories (super object yield generators async-generator call delete new property-accessors instanceof compound-assignment): pass 2001 → 2010, parity 88.6% → 89.0%.

Also separately verified 0 regressions on iterable-heavy directories (built-ins/Map, built-ins/Set, built-ins/Array/from, language/statements/for-of, built-ins/GeneratorPrototype) to bound the global is_iterable change.

Root causes & fixes

1. super.<prop> / super['<lit>'] data-property reads miss dynamic parent-prototype writes

super.fromA in a class method routed js_super_accessor_get through the older overloaded CLASS_PROTOTYPE_OBJECTS table, which can hold a distinct synthetic prototype from the stable declared-class prototype that Parent.prototype.foo = v actually writes to — so the read returned undefined. Now prefers the declared prototype object (class_decl_prototype_object), falling back to the old table.
super/prop-dot-cls-val, prop-dot-cls-val-from-arrow

2. Computed super['<string-literal>'] read/call in a class method

The computed super arm only handled object-literal methods (home stack); class methods fell back to this[index], reading the child instance and shadowing the parent. String-literal computed keys now route to the ident-form SuperPropertyGet (reads) and SuperMethodCall (calls — super['m']() binds the current this as receiver).
super/prop-expr-cls-val, prop-expr-cls-val-from-arrow (and guards prop-expr-cls-ref-this from regressing)

3. x instanceof undefined folded to false

The undefined identifier on the RHS resolved to no class and codegen silently produced false. It now lowers to the undefined value and routes through js_instanceof_dynamic, which throws TypeError (RHS not an object).
instanceof/S11.8.6_A3

4. Generator spread into call / new not iterated

A Perry generator object is a plain object with own closure-valued next/return/throw and no [Symbol.iterator] symbol property, so is_iterable missed it and js_array_like_to_array (call / new / super spread) fell through to the array-reinterpret garbage path — f(...g()) silently passed a malformed arg and generator-body throws never propagated. is_iterable now recognizes generator objects (and built-in iterator objects), so spread drives the iterator protocol. Also fixes new Map/Set(g()).
call/spread-err-{mult,sngl}-err-expr-throws, new/spread-err-{mult,sngl}-err-expr-throws

Files

  • crates/perry-hir/src/lower/expr_misc.rs — computed super string-literal read
  • crates/perry-hir/src/lower/expr_call/mod.rs — computed super string-literal method call
  • crates/perry-hir/src/lower/lower_expr.rsinstanceof undefined dynamic routing
  • crates/perry-runtime/src/object/property_key.rs — super read via declared prototype
  • crates/perry-runtime/src/collection_iter.rsis_iterable recognizes generators / built-in iterators

Out of scope (deferred, high-risk / architectural)

  • super(...spread) — conflicts with Perry's positional inline-constructor model (runtime-dynamic spread length).
  • yield* full abrupt-completion (return/throw) delegation — large state-machine change.
  • compound-assignment A7 single-eval / RequireObjectCoercible-before-ToPropertyKey — core member-access evaluation order, broad risk.
  • new <non-ctor object/number> TypeError — blocked by NaN-box pointer/number ambiguity.

Five surgical fixes across language/expressions, +9 test262, 0 regressions
(verified same-mode against the branch-parent baseline):

- super.<prop> / super['<lit>'] data-property reads in a class method now
  resolve through the *declared* prototype object (stable heap identity), so
  a property added to a parent prototype after the class declaration is
  found. Previously the older overloaded CLASS_PROTOTYPE_OBJECTS table held a
  distinct synthetic prototype that never saw such writes -> undefined.
  (super/prop-dot-cls-val, prop-dot-cls-val-from-arrow)

- super['<string-literal>'] in a class method routes to the ident-form super
  read/call (SuperPropertyGet / SuperMethodCall) instead of the this[index]
  approximation that read the CHILD instance and shadowed the parent value.
  (super/prop-expr-cls-val, prop-expr-cls-val-from-arrow; guarded against a
  computed super-method-call regression via SuperMethodCall routing)

- x instanceof undefined now evaluates the RHS and throws TypeError (RHS is
  not an object) instead of folding to false. (instanceof/S11.8.6_A3)

- is_iterable() recognizes generator objects (own next/return/throw closures)
  and built-in iterator objects, so call / new / super spread of a generator
  (f(...g()), new C(...g())) and new Map/Set(g()) drive the iterator protocol
  instead of falling through to the array-reinterpret garbage path. Errors
  thrown by the generator body now propagate.
  (call/spread-err-*-expr-throws, new/spread-err-*-expr-throws)
@proggeramlug proggeramlug merged commit b447f8a into main Jun 10, 2026
12 of 13 checks passed
@proggeramlug proggeramlug deleted the language-expr-remnant-parity branch June 10, 2026 13:26
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