Skip to content

Fix #751, #753, #754: typed-array index IL-verify, string rest element, assignment destructuring#785

Merged
nickna merged 1 commit into
mainfrom
wrk/clever-brown-34bb01
Jun 16, 2026
Merged

Fix #751, #753, #754: typed-array index IL-verify, string rest element, assignment destructuring#785
nickna merged 1 commit into
mainfrom
wrk/clever-brown-34bb01

Conversation

@nickna

@nickna nickna commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Completes three issues from the #685 array-destructuring cluster.

#754 — Array/object assignment destructuring (without declaration)

[a, b] = rhs and ({ a, b } = rhs) (assigning to existing l-values) now parse and run in both interpreter and compiled mode.

  • New Expr.DestructuringAssign carries the parser-lowered assignment statements (reusing the Array destructuring desugars to index access, so it fails over non-indexable iterables (generators, Set, Map) #685 __arrayDestructure iterator path and __objectRest) plus the original rhs as the result value. Every backend lowers it identically — run the statements, yield the result — so async/generator contexts work for free (each statement goes through the normal EmitStatement/ExecuteStatementAsync path).
  • Supported: variables, member/index/private targets, leaf & member defaults, holes, rest (binds a real Array), nested patterns (no default), object shorthand/rename/rest/computed-key/string-key, iterator sources (Set/Map/generators), expression position, chaining ([a,b]=[c,d]=x), and await in the rhs.
  • Spec semantics verified: the rhs is evaluated once, targets are written left-to-right after their receivers are evaluated, and the expression evaluates to the original rhs (([a,b]="hi") === "hi").

#753 — String rest element binds a substring instead of an array

const [a, ...rest] = "hello" now binds rest = ["e","l","l","o"] (a fresh Array), matching ECMA-262, instead of the substring "ello". String is dropped from the index-addressable pass-through in the type checker, interpreter (NormalizeArrayDestructureSource), and compiled ArrayDestructureSource/handler, so it materializes through the iterator path. Non-rest character bindings are byte-for-byte unchanged.

#751 — Numeric Set/Map spread/destructure fails --verify

Root cause was broader than the title: every typed-array (number[]/boolean[]) index read/write emitted IL that fails ILVerify. The typed List<T> fast path left a native double/bool on the stack where the sibling $Array/List<object>/fallback paths — and every consumer, which reads the clobbered StackType=Unknown — expected an object ref (StackUnexpected {Found=Double, Expected=ref 'object'}). It only ran because the typed branch is dead code for an $Array-backed value. The fast path now boxes its result at all four sites (GetIndex/SetIndex × hoisted/non-hoisted), converging every branch on object. The fix follows this codebase's established "box a typed value before a generic object slot" pattern (#279/#344/#367). Behavior- and performance-neutral: the unboxed value was never actually consumed unboxed (the consumer always required a boxed object). The numeric Set/Map spread/destructure repro inherits the fix.

Tests

Conformance

The TSConformance subset (assignmentCompatibility + conditional) is unrelated to destructuring/parsing. Test262 runs .js with type-checking off; the runtime/parser changes here were validated via the spec-semantics checks above and the both-mode unit tests rather than the heavy, uninitialized-submodule + stale-baseline Test262 isolation.

Follow-ups filed

#779 (nested pattern with a default in assignment destructuring), #780 ({a = 5} object-shorthand-default parse), #781 (typed-array rest → Array, interpreter), #782 (new Uint8Array([...]) crashes compiled — pre-existing), #783 (mixed nested array-literal tuple inference — pre-existing, affects declaration too), #784 (destructuring default applies on null via ?? instead of undefined-only — pre-existing).

…nment destructuring

#751 — Indexing a typed-array (number[]/boolean[]) variable emitted an
unverifiable merge: the typed List<T> fast path left a native double/bool on
the stack where the sibling $Array/List<object>/fallback paths (and every
consumer, which reads the clobbered StackType=Unknown) expected an object ref.
It ran only because the typed branch is dead for an $Array-backed value. The
fast path now boxes its result at all four sites (GetIndex/SetIndex ×
hoisted/non-hoisted) so every branch converges on object. Behavior- and
perf-neutral (the unboxed value was never consumed unboxed). The numeric
Set/Map spread/destructure repro inherits the fix.

#753 — A rest element over a STRING source now materializes a fresh character
array (via the #685 __arrayDestructure iterator path) instead of binding the
trailing substring, matching ECMA-262. String dropped from the index-addressable
pass-through in the type checker, interpreter, and compiled ArrayDestructureSource/
handler. Non-rest character bindings are unchanged.

#754 — Assignment destructuring to existing l-values ([a, b] = rhs /
({a, b} = rhs)) now parses and runs in both modes. New Expr.DestructuringAssign
carries the parser-lowered assignment statements (reusing __arrayDestructure /
__objectRest) plus the original-rhs result value; every backend runs the
statements then yields the result. Supports variables, member/index targets,
defaults, holes, rest (array), nested patterns, object rename/rest/computed/
string keys, iterator sources, expression position, chaining, and await in the
rhs. rhs is evaluated once and the expression yields the original rhs per spec.

Follow-ups filed: #779 (nested pattern with default), #780 ({a=5} parse),
#781/#782 (typed-array rest / Uint8Array compiled), #783 (mixed nested literal
tuple inference), #784 (default on null vs undefined-only).
@nickna nickna merged commit c5970a0 into main Jun 16, 2026
3 checks passed
nickna added a commit that referenced this pull request Jun 16, 2026
…gaps

#784 - destructuring defaults applied on `null` instead of `undefined`-only.
Replace the `?? default` (`Expr.NullishCoalescing`, triggers on null) at all six
desugaring sites with a shared DefaultIfUndefined helper (`v = access;
v === undefined ? default : v`), spilling the access so it is read exactly once
(getter-safe) and the default stays lazy. Also fix a latent #754 compiled bug it
exposed: an assignment-destructuring value-type default over an ABSENT element
produced NaN, because the lowered spill temps live inside an expression and so
escaped the whole-body numeric-slot taint pass, getting an unboxed double slot
that coerced the undefined sentinel. Flag them in VisitDestructuringAssign.

#781 - an array-destructuring rest over a typed array / Buffer bound a typed
array, not a fresh Array. Materialize typed arrays/buffers element-by-element
into a SharpTSArray in Interpreter.NormalizeArrayDestructureSource (they are not
GetIterableElements-iterable, so they cannot route through it). Interpreter-only;
compiled typed-array construction is blocked by #782.

#780 - the object-literal parser rejected the cover-grammar `{ a = 5 }`. Accept
it (stored as `{ a: (a = 5) }`) so it round-trips to the #754 assignment-
destructuring lowering; an IsShorthandDefault flag on Expr.Property keeps a
pure-expression `{ a = 5 }` a type error (TS1312), matching tsc.

#779 - a nested pattern WITH a default in an assignment destructuring
(`[[a] = []]`, `{p: {x} = {}}`) was rejected. DestructuringAssign now retains the
un-lowered (RawTarget, RawDefault); the outer pattern walk re-lowers the inner
pattern against the defaulted access.

#753 (string rest -> array) was already fixed in main by PR #785; verified and
covered by tests here.

Adds 14 destructuring test cases (both modes where applicable). Representative
compiled program passes --verify.

Filed #796 (object destructuring with a default over a source lacking the
property reports a spurious "Property does not exist" - pre-existing, shared root
cause with #783). #783 left open with findings; its tuple-inference fix did not
reach the headline nested case and was reverted.
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