Skip to content

Accessors fill abstract and interface fields; object-literal lambdas typed from the receiver - #719

Merged
ASDAlexander77 merged 3 commits into
mainfrom
fix-accessor-implements-field
Oct 28, 2026
Merged

ASDAlexander77 merged 3 commits into
mainfrom
fix-accessor-implements-field

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Two docs/not_impl items, then the folder removed.

Accessors fill abstract fields and interface fields (not_impl15)

A getter/setter implementing abstract prop: T was read as a field: through the base, print(b.prop) printed null and b.prop = x skipped the setter. A class implementing an interface field with accessors failed to compile ("field type not matching", or "can't find reference for field" through an abstract base).

  • abstract prop: T has no storage now: it is an abstract get/set pair (getter only when readonly). A subclass fills it with its own accessors, or with a field, which gets accessors over it. This also fixes a plain subclass field filling an abstract field, which used to be a second slot the base never read.
  • An interface field (not optional, not function-typed) filled by accessors has a slot holding the address of a {getter, setter} cell plus the sign bit (INTERFACE_ACCESSOR_CELL_TAG). Loads and stores through an interface field test the slot and call the accessor; RetainSlot/ReleaseSlot skip the cell, and a store releases the value the setter took.
  • Cost: one runtime branch on every load and store of such an interface field.
  • Not supported: an optional interface field (prop?: T) filled by accessors is an error. On 32-bit the cell must sit below 2 GB.

Untyped lambdas in object literals (not_impl28)

const o: { w: (x: number) => number } = { w: x => x + 1 } failed with "can't find type by name: TGenParam_x" (never worked).

  • An object literal's function members take their parameter types (and an unannotated result type) from the receiver's field of that name, or its interface's method less this.
  • A generic call with such a literal (foo({ read: () => s, write: value => s = value }) for foo<T>(x: Computed<T>)) first infers, with no code generated, from the other arguments and the literal's other members; the parameter's type with those type parameters known becomes the literal's receiver.
  • Inference now matches an interface template against an object (Computed<T> from { read: () => string }), and () => T infers from () => string.
  • Under -mm=own the f1 shape still hits a borrow error that main gives with fully typed lambdas too; the new test isn't run under own.

Tests

  • New: 00class_accessor_implements_field.ts, 00object_literal_contextual_lambda.ts (compile, jit, TSLANG_CORPUS).
  • ctest -C Release: 6121/6121.
  • DefaultLib (main 362f556) release tests built with this compiler: gc 175/175, rc and none 174/175 (1 gc-only skip), own 164/175 (11 not-own skips).

🤖 Generated with Claude Code

ASDAlexander77 and others added 3 commits October 28, 2026 18:08
A getter/setter implementing `abstract prop: T`, or an interface's `prop: T`,
was read as a field: through the base a read printed null, and a class
implementing an interface field with accessors did not compile
(docs/not_impl/not_impl15.ts).

- `abstract prop: T` has no storage now: it is an abstract get/set pair
  (getter only when readonly). A subclass fills it with accessors of its own,
  or with a field, which gets accessors over it. A plain field filling an
  abstract field used to be a second slot the base never read.
- An interface field (not optional, not function-typed) a class fills with
  accessors has a slot holding the address of a {getter, setter} cell plus the
  sign bit (INTERFACE_ACCESSOR_CELL_TAG). Loads and stores through an
  interface field test the slot and call the accessor; RetainSlot/ReleaseSlot
  skip the cell, and a store releases the value the setter took. This adds a
  branch to every load and store of such an interface field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
not_impl15 (accessors filling abstract and interface fields) and not_impl28
(contextually typed lambdas in object literals) are implemented on this branch
and covered by tests; the rest are dropped by request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e receiver

`const o: { w: (x: number) => number } = { w: x => x + 1 }` failed with "can't
find type by name: TGenParam_x": an object literal's function members never
saw the receiver, so an untyped parameter had no type (docs/not_impl/not_impl28.ts).

- A function member of an object literal takes its parameter types (and an
  unannotated result type) from the receiver's field of that name, or its
  interface's method less `this`.
- A generic call with such a literal for an argument
  (`foo({ read: () => s, write: value => s = value })` for
  `foo<T>(x: Computed<T>)`) infers first, with no code made, from the other
  arguments and the literal's other members (an expression-bodied lambda's
  type is its parameters' and its expression's); the parameter's type with
  those type parameters known is the literal's receiver. One still holding a
  type parameter (`U` of `w: (x: T) => U`) types the members' parameters only.
- Inference matches an interface template against an object (`Computed<T>`
  from `{ read: () => string }`), and a function with no parameters infers
  from its result (`() => T` from `() => string`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 379f038 into main Oct 28, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-accessor-implements-field branch October 28, 2026 19:10
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