[compiler] Default parameter values are not applied on dynamic dispatch
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 2.3k
- Forks
- 641
- Avg merge
- 12h 4m
- Merged PRs (30d)
- 57
Description
Default parameter values are not applied when a method is called through dynamic dispatch. A call through an interface-typed or any-typed reference passes only the arguments written at the call site; the missing ones arrive as undefined and the parameter initializer never runs.
Repro: https://makecode.com/_EjWTcAMaqhyX
interface Shape {
scaled(x: number, factor?: number): number;
}
class Thing implements Shape {
scaled(x: number, factor = 5) {
return x * factor;
}
}
const t = new Thing();
const s: Shape = t;
console.log(t.scaled(2)); // 10 -- static call, default applied
console.log(s.scaled(2)); // NaN -- same object via interface: default not applied
Root cause is in the emitter, not a backend: defaults are filled in at the call site from the statically known signature (addDefaultParametersAndTypeCheck). A dynamically dispatched call site has no signature, so nothing fills the argument, and there is no callee-side default application to catch it -- the arity wrapper pads with undefined. A fix likely means emitting callee-side default application for methods reachable through dynamic dispatch.
It seems like a not-recent bug: reproduces on pxt v12.3.14 (the release prior to my recent optimizations), and the call-site filling mechanism dates to 2017.
A ready-made red test is checked in disabled: uncomment the REPRO block in tests/compile-test/lang-test0/57defaultparamdispatch.ts (added in https://github.com/microsoft/pxt/pull/11561) and gulp testlang fails with qzdp:iface. The file's active assertions pin the behavior that must hold regardless of the fix (concrete-path defaults, explicit-argument calls, and agreement between the interface and any paths).
Workaround: pass every argument explicitly at dynamic call sites, or have the callee test for undefined itself.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the emitter's addDefaultParametersAndTypeCheck path and the disabled REPRO block in tests/compile-test/lang-test0/57defaultparamdispatch.ts. Uncomment the block and run gulp testlang, then trace the qzdp:iface failure through dynamic interface and any dispatch. Done means the red test passes while the file's active assertions for concrete calls, explicit arguments, and both dynamic paths still hold.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 52/100