Repository navigation
Make typed arrays distinct types - #9050
Conversation
Why:
Every typed array type was a public abbreviation of `TypedArray.t<_>`, so
the type checker took `Uint8Array.t` for `Int8Array.t`, while an untagged
variant recognized them by their classes: `I8(uint8Array)` type-checked
and then matched the `U8` case (#8955).
Each typed array type is `private TypedArray.t<_>`: a distinct type that
upcasts to `TypedArray.t<_>` with `:>`. The `TypedArray.Impl` functor gives
each typed array module the functions of `TypedArray` for its own type, with
their documentation, so `Int8Array.get` works on an `Int8Array.t` and
`Int8Array.slice` returns one. The functor holds only externals, so including
it emits no code; `Stdlib_TypedArray` gains an empty `Impl` function.
Typing rules changed (typed arrays):
Example:
@unboxed type ta = I8(Int8Array.t) | U8(Uint8Array.t)
let x = I8(Uint8Array.fromArray([1]))
Before (rule): `Int8Array.t` and `Uint8Array.t` are both `TypedArray.t<int>`,
so `I8(uint8Array)` type-checks and is matched as `U8` at run time
After (rule): they are distinct types; `I8(uint8Array)` is a type error
Diagnostic added (one typed array for another):
Example:
let x = I8(Uint8Array.fromArray([1]))
Before (diagnostic): none
After (diagnostic):
This has type: Uint8Array.t
But it's expected to have type: Int8Array.t
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CMkrYaktctdKc4M41WF36t
Signed-off-by: Christoph Knittel <ck@cca.io>
Why:
A class declared with @instanceof on a public abbreviation of an
abstract type made every value of that type an instance, and two such
abbreviations could declare the same values instances of two classes
while the type checker treated them as one type, as the typed arrays did.
`Typedecl.check_manifest_annotation` rejects @instanceof on a public
abbreviation of a type the compiler does not see; a subclass already had to
be private.
Typing rules changed (a class on an abbreviation):
Example:
type node
@instanceof("Element") type element = node
Before (rule): a class on a public abbreviation of an abstract type is
accepted
After (rule): the abbreviation must be private,
`@instanceOf("Element") type element = private node`
Diagnostic added (a class on a public abbreviation):
Example:
type node
@instanceof("Element") type element = node
Before (diagnostic): none
After (diagnostic):
A class must be declared on an abstract type or a private abbreviation:
write `= private ...`, so that values of the abbreviated type are not
instances of Element without a coercion, and another abbreviation cannot
declare them instances of another class.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CMkrYaktctdKc4M41WF36t
Signed-off-by: Christoph Knittel <ck@cca.io>
Changelog💥 Breaking Change
💥 A changelog entry is a breaking change, but no commit says what it breaks ( Semantic Diff3 commits · Full page Make typed arrays private abbreviations of TypedArray.t0839518 · cknitt + Claude · Oct 26, 2026, 06:36 UTC
Each typed array type is Typing rules changed (typed arrays): @unboxed type ta = I8(Int8Array.t) | U8(Uint8Array.t)
let x = I8(Uint8Array.fromArray([1]))
Diagnostic added (one typed array for another) let x = I8(Uint8Array.fromArray([1]))
-none
+This has type: Uint8Array.t
+But it's expected to have type: Int8Array.tRequire a private abbreviation for a class on an abstract typeeb66527 · cknitt + Claude · Oct 26, 2026, 06:36 UTC
Typing rules changed (a class on an abbreviation): type node
@instanceOf("Element") type element = node
Diagnostic added (a class on a public abbreviation) type node
@instanceOf("Element") type element = node
-none
+A class must be declared on an abstract type or a private abbreviation:
+write `= private ...`, so that values of the abbreviated type are not
+instances of Element without a coercion, and another abbreviation cannot
+declare them instances of another class.✨ Migrate the deprecated TypedArray functions on a TypedArray.t53a463e · cknitt + Claude · Oct 26, 2026, 06:52 UTC · new
The test's typed array is upcast to |
Why:
The migration test applied the deprecated `TypedArray.*ToEnd` functions to
a `Uint8Array.t`, which is now a type of its own; those functions exist
only on `TypedArray`.
The test's typed array is upcast to `TypedArray.t<int>`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CMkrYaktctdKc4M41WF36t
Signed-off-by: Christoph Knittel <ck@cca.io>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #9050 +/- ##
=======================================
Coverage 81.33% 81.33%
=======================================
Files 456 456
Lines 64273 64277 +4
=======================================
+ Hits 52275 52280 +5
+ Misses 11998 11997 -1
🚀 New features to boost your workflow:
|
rescript
@rescript/belt
@rescript/darwin-arm64
@rescript/linux-arm64
@rescript/linux-x64
@rescript/runtime
@rescript/win32-x64
commit: |
|
Developer playground preview: https://rescript-lang.github.io/rescript/dev-playground/?version=pr-9050 |
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. Keep it up! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Fixes #8955.
Every typed array type was a public abbreviation of
TypedArray.t<_>, so the type checker treatedUint8Array.tandInt8Array.tas the same type, while an untagged variant recognized them by their classes. As a result,@unboxed type ta = I8(Int8Array.t) | U8(Uint8Array.t)acceptedI8(uint8Array)and then matched it asU8.Typed arrays are distinct types. Each one is
@instanceOf("Int8Array") type t = private TypedArray.t<int>(and so on). It upcasts toTypedArray.t<_>with:>, so the genericTypedArrayfunctions still work through a coercion. To keepview->Int8Array.get(0)working, and to haveInt8Array.slice,map,filteretc. return anInt8Array.t, each module includes the newTypedArray.Implfunctor. It declares theTypedArrayfunctions for the module's own type, with their documentation.The functor holds only externals, so it costs nothing at run time:
Stdlib_TypedArraygains one emptyImplfunction.Editor completion on a typed array now lists the module's own functions with precise types (
(t, bigint) => intinstead of(t<'a>, 'a) => int).A general rule closes the same hole elsewhere. An
@instanceOfthat introduces a class on an abbreviation of an abstract type must beprivate, as a subclass already had to be. A public abbreviation would make every value of the abstract type an instance, and a second abbreviation could claim another class for the same values.Breaking (changelog fragment included):
TypedArray.f(view)on a specific typed array must useInt8Array.f(view)or(view :> TypedArray.t<int>).@instanceOfabbreviation of an abstract type needsprivate.The deprecated
…ToEndfunctions stay onTypedArrayonly.New tests:
super_errors/fixtures/typed_array_cases_distinct.res: the Untagged variant puts one typed array in another typed array's case #8955 repro, now a type error.instance_of_public_abbreviation.res: the new rule.untagged_runtime_annotations_test.res: a variant over three typed arrays dispatches to the right case.Stdlib_TypedArrayTests.res: the coercion toTypedArray.t.🤖 Generated with Claude Code
https://claude.ai/code/session_01CMkrYaktctdKc4M41WF36t