Skip to content

Compiled: default/optional params on class members (#705 #723 #737 #739 #738)#787

Merged
nickna merged 1 commit into
mainfrom
wrk/intelligent-franklin-bf7e19
Jun 16, 2026
Merged

Compiled: default/optional params on class members (#705 #723 #737 #739 #738)#787
nickna merged 1 commit into
mainfrom
wrk/intelligent-franklin-bf7e19

Conversation

@nickna

@nickna nickna commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Closes the compiled-mode default/optional-parameter cluster for class members. The interpreter was already correct; this brings compiled mode to parity. All run in both modes to pin interp/compiler parity.

Fixes #705, #723, #737, #739, #738.

What was broken

In compiled mode, class-member default/optional parameters were mishandled: value-type defaults never fired on methods, generator/async-generator methods ran no default prologue at all, omitted optionals read as CLR null (so typeof was "object"), and a --ref-asm function-expression value-call shifted its arguments.

Changes

#723 / #705 — value-type defaults fire on instance & static methods. ParameterTypeResolver widens a value-type-defaulted param to an object slot so the entry prologue can observe the $Undefined sentinel (a double/bool slot cannot — it coerces to NaN/false).

  • Static methods widen directly (non-virtual → always safe).
  • Instance methods widen hierarchy-consistently: a position is widened across the whole override group (root declaration + every override) when any member makes it an optional value-type param. Widening the whole group keeps every override’s CLR signature identical, so a derived override that adds a default still lands in the base’s vtable slot — virtual dispatch is preserved (guarded by Override_DerivedAddsDefault_StillOverrides). Reference-type defaults are left alone (they already fire via null-padding without a slot change). Adds a TypeMap.ClassTypes accessor for the override-group walk.

#737 — generator & async-generator method defaults. GeneratorMoveNextEmitter and AsyncGeneratorMoveNextEmitter now run a default-parameter prologue (operating on the hoisted state-machine fields), placed right after the state-dispatch switch so it runs exactly once on initial entry (resume states jump past it; no guard field needed).

#739 — omitted optional reads undefined, not null. A direct instance/static method or constructor call pads an omitted trailing optional param with the $Undefined sentinel instead of CLR null. Centralized as a shared EmitOmittedArgument(slot) helper (object slot → $Undefined, else CLR default) applied across the call-emission sites (sync + async/generator + call handlers).

#738--ref-asm value-call no longer shifts arguments. Root cause: the reference-assembly rewrite (external NickNa.PEPacker) strips parameter names, breaking the runtime params[0].Name == "__this" receiver-slot check in $TSFunction, so the synthetic __this slot was miscounted as a real argument. Adds a $ExpectsThis marker attribute (custom attributes survive the rewrite) applied at the function-expression/arrow __this sites; EmitComputeExpectsThis now OR-s IsDefined($ExpectsThis) with the name check — identical behavior under --compile, fixed under --ref-asm.

Bonus fix — generator/async ?? ignored the $Undefined sentinel. The correct #739 padding surfaced a latent state-machine bug: StateMachineEmitHelpers.EmitNullishCoalescing only tested CLR null (brfalse) and missed $Undefined (a non-null reference), so x ?? rhs returned undefined inside a generator/async body. In the real yaml parser this was an infinite loop (*pop(error) { error ?? stack.pop() } with an omitted error never popped the stack). Now mirrors the sync ILEmitter override (also tests isinst $Undefined).

Testing

  • Full unit suite green: 13173 passed, 0 failed (incl. the real-package smoke tests — yaml/lodash/minimatch — which exercise the compiled generator/parser paths).
  • New cases in ClassMethodDefaultParameterTests (instance/static/generator/async-generator value-type defaults, omitted-optional typeof, the override omit-fires-derived-default case, the generator ?? regression, and an IL-verification guard).
  • New ReferenceAssemblyTests.RefAsm_FunctionExpressionValueCall_DoesNotShiftArguments (runtime).
  • Conformance: changes are compilation/runtime only (no type-checker changes); validated against the interpreter as the reference across all new cases. The standalone-Test262/TSConformance suites were not re-baselined here.

Follow-up filed

#738)

Closes the compiled-mode default/optional-parameter cluster for class members.
The interpreter was already correct; this brings compiled mode to parity.

#723/#705 — value-type defaults now fire on instance and static methods.
ParameterTypeResolver widens a value-type-defaulted param to an `object` slot
so the entry prologue can observe the `$Undefined` sentinel. Static methods
widen directly (non-virtual). Instance methods widen *hierarchy-consistently*:
a position is widened across the whole override group when any member makes it
an optional value-type param, so every override keeps one CLR signature and
virtual dispatch is preserved (regression-guarded). Adds TypeMap.ClassTypes.

#737 — generator and async-generator methods now run a default-parameter
prologue (GeneratorMoveNextEmitter / AsyncGeneratorMoveNextEmitter), placed
after the state switch so it runs once on initial entry.

#739 — a direct instance/static method or constructor call pads an omitted
trailing optional with the `undefined` sentinel, not CLR null, so `typeof`
reads "undefined". Centralized as a shared EmitOmittedArgument helper
(object slot -> $Undefined, else CLR default) across the call-emission sites.

#738 — under `--compile --ref-asm`, a function-expression value-call shifted
its arguments because the reference-assembly rewrite strips parameter names,
breaking the runtime `params[0].Name == "__this"` receiver-slot check. Adds a
`$ExpectsThis` marker attribute (survives the rewrite) that backstops the name
check in $TSFunction's _expectsThis computation.

Also fixes a latent generator/async nullish-coalescing bug surfaced by the
correct #739 padding: StateMachineEmitHelpers.EmitNullishCoalescing only tested
CLR null and missed the `$Undefined` sentinel, so `x ?? rhs` returned undefined
inside a state machine (manifesting as an infinite loop in the real yaml
parser's `error ?? stack.pop()`). Now mirrors the sync ILEmitter override.

Filed #778 (compiled static generator methods don't compile — pre-existing).

Full suite green (13173).
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.

Compiled: value-type default parameters cannot represent an explicit/padded undefined (yields NaN)

1 participant