microsoft / microsoft/pxt

[compiler] Default parameter values are not applied on dynamic dispatch

Open
#11,562 0 comments 0 reactions 0 assignees View on GitHub

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.