Skip to content

An object literal field the receiver's type cannot hold is an error - #517

Merged
ASDAlexander77 merged 2 commits into
mainfrom
fix-mismatched-literal-assignment
Oct 5, 2026
Merged

ASDAlexander77 merged 2 commits into
mainfrom
fix-mismatched-literal-assignment

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Closes #513.

let s: Sq = { kind: "sq", side: 4 }; s = { kind: "ci", r: 1 } (with Sq = { kind: "sq"; side: number }) compiled with no error and left s unchanged. The same literal used elsewhere was also wrong:

  • in a declaration (let t: Sq = { kind: "ci", r: 1 }) it compiled and held garbage;
  • as an argument it was rejected, but with a misleading "Expected 1 arguments, but got 0" next to the real error.

Cause

addObjectFieldInfo casts each field of an object literal to the receiver's field type and took the result as a value. The cast of "ci" to the literal type "sq" failed and reported it. But a failed cast is a null value:

  1. The field was queued with no value.
  2. The literal came out with no value at all.
  3. An assignment of no value stores nothing and still succeeds.

MLIRGen postpones its messages and shows them only if the module fails to compile. Nothing failed, so the cast's error was never shown.

Change

A field whose cast fails now fails the literal, so the existing error is shown:

error: can't cast from literal type: '"ci"' to '"sq"'

One existing test relied on the dropped cast

export_object_literal_with_class_types.ts initializes left: Point with null. Strict null checks are on by default, and under them that is an error, in TypeScript and in tslang (let p: Point = null is rejected the same way). It compiled only because the field's cast was dropped and the field read null by default.

The test is about a cross-module object literal with class-typed fields, not null checking. So it now starts with // @strict-null false (as 28boolcasts.ts does), in a commit of its own.

Tests

literal-field-mismatch/ has four files:

  • assign.ts, declare.ts, argument.ts, return.ts: { kind: "ci", r: 1 } where an Sq is expected. Each must report the cast error, and no arity error. On main, the assignment and the declaration compile with no error.
  • positive.ts: matching literals assign, declare, pass and return, and a union receiver still picks its member.

Verification

  • ctest -C Release, full run: 7 failures, all the test above (its 6 registrations plus its ownership-verifier shard). With the pragma, all 7 pass. The other 3875 passed in the full run. gtest unittests are not built in this tree.
  • DefaultLib (origin/main) built with this compiler, since the change turns silent failures into errors: gc and rc build with no errors. Release tests: gc 160/160, rc 159/160 (1 gc-only skip). src/lib.ts also compiles directly under both models.
  • Branch is on c358ba2 and merges cleanly with main after -mm=rc: a parameter the body assigns owns what it is assigned #515.

Not changed

Two other mismatches still compile, and neither drops a store:

  • side: "x" where a number is expected is converted (it reads 0). That is tslang's conversion rule.
  • An extra field ({ kind: "sq", side: 5, extra: 1 }) is accepted and stored. TypeScript reports excess properties in a literal assigned to a declared type.

🤖 Generated with Claude Code

ASDAlexander77 and others added 2 commits October 5, 2026 17:53
`let s: Sq = ...; s = { kind: "ci", r: 1 }` (Sq = { kind: "sq"; side:
number }) compiled with no error and left `s` unchanged (#513). The same
literal in a declaration, `let t: Sq = { kind: "ci", r: 1 }`, compiled
and held garbage.

addObjectFieldInfo cast each field to the receiver's field type and
took the result as a value. The cast of "ci" to the literal type "sq"
failed and reported it, but a failed cast is a null value: the field
was queued with no value, the literal came out with no value at all,
and an assignment of no value stores nothing and still succeeds.
MLIRGen postpones its messages and shows them only if the module fails
to compile. Nothing failed, so the cast's error was never shown.

A field whose cast fails now fails the literal, so its error is shown.
As an argument the literal was already an error, with a misleading
"Expected 1 arguments, but got 0" next to the real one; that message
is gone.

literal-field-mismatch/: an assignment, a declaration, an argument and
a return of `{ kind: "ci", r: 1 }` where an Sq is expected. Each must
report the cast error, and no arity error. On main the assignment and
the declaration compile with no error. positive.ts assigns, declares,
passes and returns matching literals, and a union receiver still picks
its member.

Closes #513

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

`{ left: null }` for a `left: Point` is an error under strict null
checks, which are on by default: null is not a Point, as in
`let p: Point = null`. The test compiled only because the field's
failed cast was dropped (#513), and the field read null by default.
With that cast now an error, the file says what it relies on. The test
is about a cross-module object literal with class-typed fields, not
null checking.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 73c5b15 into main Oct 5, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-mismatched-literal-assignment branch October 5, 2026 17:48
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.

An object literal that does not match the variable's type is silently not assigned

1 participant