Skip to content

A derived class's implicit constructor passes its arguments to the base - #557

Merged
ASDAlexander77 merged 1 commit into
mainfrom
fix-implicit-derived-constructor
Oct 12, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
fix-implicit-derived-constructor

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

TypeScript gives a derived class with no constructor an implicit constructor(...args) { super(...args); }. tslang generated one only when the class had field initializers, and as constructor() { super(); }:

class Base {
    constructor(protected a: int, protected b: int = 0) {}
    sum(): int { return this.a + this.b; }
}
class Derived extends Base {
    extra = 1;                    // no constructor
}
new Derived(2, 3);                // error: Expected 1-2 arguments, but got 0. (at 1:1)
  • Implicit constructor (mlirGenClassDefaultConstructor): it takes the base constructor's parameters, typed from its function type, so a generic base is already specialized. Optional and rest parameters are included. It passes them to super, and the base applies its own defaults. The parameters are named .argN, so a field initializer, which runs in this constructor, still sees an outer variable of the same name.
  • super(a, ...rest) passed an empty rest parameter. The callee of a super call is the base class, so its arguments were generated with no parameter list, and a spread had no count to fill. They are now generated against the base constructor's function type.
  • Locations: errors in the generated constructor, and in the assignment generated for a field initializer, were reported at 1:1 because those nodes had no position. They are now reported at the class and at the field. For example, protected argv: Ref<string> = null; now reports at 6:5.

Tests: 00class_implicit_constructor.ts (compile + JIT, and the rc/none corpus) covers defaults, optional and rest parameters, a two-level chain, generic and specialized bases, and the shadowing case. Two diagnostic-location tests check the error positions. I also checked by hand a base imported from another module, with and without the JIT cache. Full suite: 4020/4020 passed (Release).

Found while porting native_ts_graphics samples (issues 1 and 2 of the native_ts_graphics findings; the location part of issue 4). With this, the forwarding constructors added in native_ts_graphics PR #48 can be deleted.

🤖 Generated with Claude Code

TypeScript gives a derived class with no constructor an implicit
`constructor(...args) { super(...args); }`. tslang generated one only
for a class with field initializers, and as `constructor() { super(); }`,
so `new Derived(2, 3)` failed with "Expected 1-2 arguments, but got 0"
when the base constructor takes parameters. The generated constructor
now takes the base constructor's parameters, typed as its function is
(a generic base's already specialized; optional and rest parameters
included), and passes them to `super`; the base applies its defaults.

That needed `super(a, ...rest)` to work: a super call's arguments were
generated without the constructor's parameters (the callee is the base
class), so a spread had no count to fill and the base got an empty rest
parameter. They are generated against the base constructor now.

Errors in the generated constructor and in the assignments generated
for field initializers were reported at 1:1, as those nodes had no
position. They are reported at the class and at the field now:
`argv: Ref<string> = null` says 6:5, not 1:1.

Found while porting native_ts_graphics samples (TSLANG_ISSUES.md 1, 2
and the location in 4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit a764731 into main Oct 12, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-implicit-derived-constructor branch October 12, 2026 16:06
ASDAlexander77 added a commit that referenced this pull request Oct 17, 2026
…de for (#581)

A node the compiler builds (a for...of's or for...in's loop, the call of
`[Symbol.iterator]`, a `using`'s dispose call) has no position, so an
error in it was reported at 1:1:

    for (const x of 5) {}   // 1:1: Can't resolve property 'length' of type s32
    using x = 5;            // 1:1: Can't resolve property 'dispose' of type s32

Each statement now records where it is (without the debug scope), and
loc() gives a node with no position that location, in the current scope;
a statement the compiler made keeps the anchor of the one it was made
for. The dispose call, made at the scope's exit, is anchored at its
declaration. #541 and #557 had stamped positions on the generated cast
and constructor nodes one at a time; this covers the rest.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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