Skip to content

A number, string, boolean or array is not an interface with members - #543

Merged
ASDAlexander77 merged 2 commits into
mainfrom
fix-value-to-interface-cast-check
Oct 11, 2026
Merged

ASDAlexander77 merged 2 commits into
mainfrom
fix-value-to-interface-cast-check

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Problem

A value that can't be an interface, i.e. a number, string, boolean or array, compiled without any error when passed where an interface with members is expected:

interface IKind { kind: number; }
function f(i: IKind) { return i.kind; }
print(f([1, 2]));   // compiled; f read garbage
print(f(5));        // same

TypeScript rejects all of these. docs/bugs/array_of_ifaces_cast.ts exists for this case: it says that passing IStateToken[] for an interface parameter "should produce an error". tslang never reported it. That file fails earlier on unrelated errors, but a cleaned-up version compiled, with at most <unknown>:0: warning: invalid cast from '!llvm.ptr' to '!llvm.struct<(ptr, ptr, ptr)>'.

Cause

castToInterfaceSpecialCases handles classes, tuples and objects. For anything else it returned nothing, so the value fell through to a plain ts.Cast to the interface type. Lowering can't build an interface value from an array or a scalar, so it produced garbage.

Fix

For a numeric type (any width), number, string, boolean, char, bigint, an array or a const array cast to an interface that has members (getVTableSize() > 0), report:

error: type 's32[]' can't be cast to interface 'IKind': it has none of the interface's members

An interface without members still accepts any value, as in TypeScript.

Testing

  • New error-text tests test-compile-interface-cast-{number,string,boolean,array} (each requires the message), plus test-compile-interface-cast-empty, which must compile cleanly. The current release compiles all five without an error.
  • Full ctest: 3918/3918 pass.

🤖 Generated with Claude Code

ASDAlexander77 and others added 2 commits October 11, 2026 13:26
Passed where an interface with members is expected, such a value
compiled without a word: castToInterfaceSpecialCases had no case for it,
so it became a plain cast that lowering could not make (at most a
"<unknown>:0: warning: invalid cast"), and the callee read garbage. It is
now an error naming the type and the interface. An interface without
members still takes any value, as in TypeScript.

Found by docs/bugs/array_of_ifaces_cast.ts, whose intended error
(IStateToken[] passed for an IState) was never reported.

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

# Conflicts:
#	tslang/test/tester/CMakeLists.txt
@ASDAlexander77
ASDAlexander77 merged commit f27a058 into main Oct 11, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-value-to-interface-cast-check branch October 11, 2026 13:12
ASDAlexander77 added a commit that referenced this pull request Oct 12, 2026
…558)

Every repro in docs/bugs passes on main, or fails as TypeScript does:
the native_ts_graphics findings were fixed by #535-#544 and #549 (the
`let s = 0` one by design, with a warning since #544), testFallThrough,
generic_in_generic and array_of_ifaces_cast by #538, #542 and #543, and
bug14 and bug21 aren't valid TypeScript. Each fix has its regression
test in tslang/test/tester, and the findings document is in the history
and in those PRs, so the folder goes.

Its interop notes stay, in docs/c-cpp-header-import.md: a `string`
returned by a C function is its `char*`, not a copy; `number` is a
`double`; and a nullable `Ref<T>` field is spelled `Ref<T> | null`.

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