Skip to content

Fix #784, #781, #780, #779: destructuring default/rest/cover-grammar gaps#797

Merged
nickna merged 1 commit into
mainfrom
wrk/amazing-bartik-38549e
Jun 16, 2026
Merged

Fix #784, #781, #780, #779: destructuring default/rest/cover-grammar gaps#797
nickna merged 1 commit into
mainfrom
wrk/amazing-bartik-38549e

Conversation

@nickna

@nickna nickna commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Completes a cluster of array/object destructuring conformance gaps. Both interpreter and compiled modes; a representative compiled program passes --verify. Rebased onto current main (the build break that #769×#786 introduced was already repaired on main by 3564041, so no build-fix is included here).

Fixes

#784 — defaults apply on null instead of undefined-only

All six destructuring desugaring sites used access ?? default (Expr.NullishCoalescing), which also triggers on null. Replaced with a shared DefaultIfUndefined helper — v = access; v === undefined ? default : v — that spills the access so it is read exactly once (getter-safe) while keeping the default lazy. Reuses existing nodes, so no new emitter/visitor surface.

Also fixes a latent #754 compiled bug this exposed: an assignment-destructuring value-type default over an absent element ([c = 5] = arr, ({z: d = 5} = {})) produced NaN. The lowered spill temps live inside an Expr.DestructuringAssign expression, so they escaped the whole-body numeric-slot taint pass and got an unboxed double slot that coerced the runtime undefined sentinel. Flagged in VisitDestructuringAssign.

#781 — typed-array rest binds a typed array (interpreter)

const [a, ...rest] = new Uint8Array([1,2,3]) bound a Uint8Array slice (Array.isArray(rest) === false). NormalizeArrayDestructureSource now materializes typed arrays/buffers into a fresh SharpTSArray (they expose index access but no [Symbol.iterator] in this runtime, so they cannot route through GetIterableElements). Compiled mode is blocked by #782 (typed-array construction).

#780 — object shorthand-with-default {a = 5} unparsed

The object-literal parser rejected the cover grammar. Now accepted (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, while {a: x = 5} stays valid.

#779 — nested pattern with a default in assignment destructuring

[[a] = []] = [[1]] / ({p: {x} = {}} = ...) were a parse error. Expr.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

Already fixed in main by #785; verified in both modes and covered by tests.

Tests

14 new destructuring cases (both modes where applicable): undefined-only defaults (declaration + assignment), value-type default over absent (the compiled NaN guard), laziness + single-evaluation, typed-array rest, object shorthand-default, the pure-expression cover-grammar error, and nested array/object patterns with defaults.

Follow-ups filed

…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.
@nickna nickna force-pushed the wrk/amazing-bartik-38549e branch from f996dfa to b7e8f8b Compare June 16, 2026 19:22
@nickna nickna changed the title Fix #784, #781, #780, #779: destructuring default/rest/cover-grammar gaps (+ repair main build) Fix #784, #781, #780, #779: destructuring default/rest/cover-grammar gaps Jun 16, 2026
@nickna nickna merged commit b046a54 into main Jun 16, 2026
3 checks passed
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