diff --git a/docs/content/limitations/builtin-apis/arraybuffers-and-typed-arrays.mdx b/docs/content/limitations/builtin-apis/arraybuffers-and-typed-arrays.mdx index 81dfded25..59a61518d 100644 --- a/docs/content/limitations/builtin-apis/arraybuffers-and-typed-arrays.mdx +++ b/docs/content/limitations/builtin-apis/arraybuffers-and-typed-arrays.mdx @@ -1 +1 @@ -- **ArrayBuffers and typed arrays:** Fixed-length ArrayBuffers support shared typed-array and DataView storage. Resizing, transferring, Float16 arrays, and BigInt arrays are unsupported. Numeric typed arrays and Buffers preserve their kind and shared storage through `unknown`, including when nested in records and arrays. Numeric typed-array constructors can be stored and used with `new`, including constructors read from an array's `constructor` property. JavaScript numeric-array parameters and returns preserve typed-array storage. `Buffer.from`, `Buffer.allocUnsafe`, and `Buffer.concat` return views into a per-thread pool of `Buffer.poolSize` bytes (64 KiB by default) when the size is below `Buffer.poolSize >>> 1`, as in Node; a pool slab stays in memory while any of its Buffers does. Node's own startup code allocates from the first slab, so the `byteOffset` of Buffers in that slab differs from Node's. Buffers returned by other Node APIs, such as `fs.readFileSync` and `zlib`, own their `ArrayBuffer`, where Node allocates small ones from the pool. +- **ArrayBuffers and typed arrays:** Fixed-length ArrayBuffers support shared typed-array and DataView storage. Resizing, transferring, and BigInt arrays are unsupported. `Float16Array` stores IEEE 754 half-precision values, rounding each stored number to nearest-even as Node does; `Math.f16round`, `DataView.prototype.getFloat16` and `setFloat16`, and `util.types.isFloat16Array` compile. Numeric typed arrays and Buffers preserve their kind and shared storage through `unknown`, including when nested in records and arrays. Numeric typed-array constructors can be stored and used with `new`, including constructors read from an array's `constructor` property. JavaScript numeric-array parameters and returns preserve typed-array storage. `Buffer.from`, `Buffer.allocUnsafe`, and `Buffer.concat` return views into a per-thread pool of `Buffer.poolSize` bytes (64 KiB by default) when the size is below `Buffer.poolSize >>> 1`, as in Node; a pool slab stays in memory while any of its Buffers does. Node's own startup code allocates from the first slab, so the `byteOffset` of Buffers in that slab differs from Node's. Buffers returned by other Node APIs, such as `fs.readFileSync` and `zlib`, own their `ArrayBuffer`, where Node allocates small ones from the pool. diff --git a/packages/compiler/ambient/scriptc-node-fallback.d.ts b/packages/compiler/ambient/scriptc-node-fallback.d.ts index 4eb38ab75..8d2eaab10 100644 --- a/packages/compiler/ambient/scriptc-node-fallback.d.ts +++ b/packages/compiler/ambient/scriptc-node-fallback.d.ts @@ -3252,6 +3252,7 @@ declare module "util/types" { export function isInt8Array(value: unknown): value is Int8Array; export function isInt16Array(value: unknown): value is Int16Array; export function isInt32Array(value: unknown): value is Int32Array; + export function isFloat16Array(value: unknown): value is Float16Array; export function isFloat32Array(value: unknown): value is Float32Array; export function isFloat64Array(value: unknown): value is Float64Array; export function isMap(value: unknown): value is Map; @@ -3279,7 +3280,6 @@ declare module "util/types" { export function isBoxedPrimitive(value: unknown): boolean; export function isBigInt64Array(value: unknown): value is BigInt64Array; export function isBigUint64Array(value: unknown): value is BigUint64Array; - export function isFloat16Array(value: unknown): boolean; export function isCryptoKey(value: unknown): boolean; export function isExternal(value: unknown): boolean; } diff --git a/packages/compiler/src/backend/llvm/byte-numbers.ts b/packages/compiler/src/backend/llvm/byte-numbers.ts index d12ef08b9..c51d03744 100644 --- a/packages/compiler/src/backend/llvm/byte-numbers.ts +++ b/packages/compiler/src/backend/llvm/byte-numbers.ts @@ -182,7 +182,11 @@ export function emitByteNumber( const integerType = bits <= 32 ? "i32" : "i64"; if (spec.write) { let raw: string; - if (spec.floating) { + if (spec.floating && bits === 16) { + host.declare(`declare zeroext i16 @scr_f16_from_f64(double)`); + raw = B.tmp(); + B.line(`${raw} = call zeroext i16 @scr_f16_from_f64(double ${value!.name})`); + } else if (spec.floating) { let stored = value!.name; if (bits === 32) { stored = B.tmp(); @@ -228,7 +232,10 @@ export function emitByteNumber( const raw = endianBits(host, loaded, spec.width, little); fastResult = B.tmp(); if (spec.floating) { - if (bits === 32) { + if (bits === 16) { + host.declare(`declare double @scr_f16_to_f64(i16 zeroext)`); + B.line(`${fastResult} = call double @scr_f16_to_f64(i16 zeroext ${raw})`); + } else if (bits === 32) { const float = B.tmp(); B.line(`${float} = bitcast i32 ${raw} to float`); B.line(`${fastResult} = fpext float ${float} to double`); diff --git a/packages/compiler/src/backend/llvm/common.ts b/packages/compiler/src/backend/llvm/common.ts index 0a473e65b..8a8778919 100644 --- a/packages/compiler/src/backend/llvm/common.ts +++ b/packages/compiler/src/backend/llvm/common.ts @@ -159,6 +159,7 @@ export const BYTES_ELEM_NUM: Record = { u16: 6, i16: 7, u8c: 8, + f16: 9, }; /** The payload marker of an ABSENT record field slot (IR fieldAbsent): the diff --git a/packages/compiler/src/backend/llvm/expr-bytes.ts b/packages/compiler/src/backend/llvm/expr-bytes.ts index 89a482b52..ac025df09 100644 --- a/packages/compiler/src/backend/llvm/expr-bytes.ts +++ b/packages/compiler/src/backend/llvm/expr-bytes.ts @@ -44,6 +44,7 @@ const DV_GET_KIND: Record = { dvGetFloat64: 7, dvGetBigUint64Number: 8, dvGetBigInt64Number: 9, + dvGetFloat16: 10, }; const DV_SET_KIND: Record = { @@ -55,6 +56,7 @@ const DV_SET_KIND: Record = { dvSetInt32: 5, dvSetFloat32: 6, dvSetFloat64: 7, + dvSetFloat16: 10, }; export function emitIntegerLoopIndex(host: LlvmEmitterContext, expr: IrExpr): string | null { @@ -273,7 +275,7 @@ function emitSharedOrLocalLoad( idx: string, ): LlValue { const B = host.B; - const floating = elem === "f32" || elem === "f64"; + const floating = elem === "f16" || elem === "f32" || elem === "f64"; const fn = floating ? "scr_bytes_shared_get_f64" : "scr_bytes_shared_get_int"; const ret = floating ? "double" : "i32"; host.declare(`declare ${ret} @${fn}(ptr, ${host.sizeType})`); @@ -314,6 +316,16 @@ function emitBytesLoadAt( const B = host.B; const data = host.emitBytesData(receiver); const p = B.tmp(); + if (elem === "f16") { + // Half-precision bits widen exactly; the runtime converts them. + host.declare(`declare double @scr_f16_to_f64(i16 zeroext)`); + const raw = B.tmp(); + const out = B.tmp(); + B.line(`${p} = getelementptr inbounds i16, ptr ${data}, ${host.sizeType} ${idx}`); + B.line(`${raw} = load i16, ptr ${p}, align 1`); + B.line(`${out} = call double @scr_f16_to_f64(i16 zeroext ${raw})`); + return { name: out, type: F64 }; + } if (BYTES_ELEMENT_SIZE[elem] < 4) { const bits = BYTES_ELEMENT_SIZE[elem] * 8; const signed = elem === "i8" || elem === "i16"; @@ -472,10 +484,19 @@ export function emitBytesSet( B.line(`${stored} = fptoui double ${clamped} to i32`); } else stored = - elem === "f32" || elem === "f64" ? null : host.emitToUint32(value.name, expr, value.uint32); + elem === "f16" || elem === "f32" || elem === "f64" + ? null + : host.emitToUint32(value.name, expr, value.uint32); const data = host.emitBytesData(receiver); const p = B.tmp(); - if (BYTES_ELEMENT_SIZE[elem] < 4) { + if (elem === "f16") { + // One round-to-nearest-even step from the double, in the runtime. + host.declare(`declare zeroext i16 @scr_f16_from_f64(double)`); + const narrowed = B.tmp(); + B.line(`${narrowed} = call zeroext i16 @scr_f16_from_f64(double ${value.name})`); + B.line(`${p} = getelementptr inbounds i16, ptr ${data}, ${host.sizeType} ${idx}`); + B.line(`store i16 ${narrowed}, ptr ${p}, align 1`); + } else if (BYTES_ELEMENT_SIZE[elem] < 4) { const bits = BYTES_ELEMENT_SIZE[elem] * 8; const byte = B.tmp(); B.line(`${byte} = trunc i32 ${stored!} to i${bits}`); @@ -882,6 +903,7 @@ export function emitBytesIntrinsic( case "dvGetInt16": case "dvGetUint32": case "dvGetInt32": + case "dvGetFloat16": case "dvGetFloat32": case "dvGetFloat64": case "dvGetBigUint64Number": @@ -912,6 +934,7 @@ export function emitBytesIntrinsic( case "dvSetInt16": case "dvSetUint32": case "dvSetInt32": + case "dvSetFloat16": case "dvSetFloat32": case "dvSetFloat64": { // DataView setters: [offset, value, littleEndian?] — void; throw diff --git a/packages/compiler/src/backend/llvm/lib-filesystem.ts b/packages/compiler/src/backend/llvm/lib-filesystem.ts index b1d8869fd..0e7d02a15 100644 --- a/packages/compiler/src/backend/llvm/lib-filesystem.ts +++ b/packages/compiler/src/backend/llvm/lib-filesystem.ts @@ -1039,6 +1039,16 @@ export function emitPrimitiveLibCall(host: LlvmEmitterContext, e: LibCallExpr): B.line(`${t} = sitofp i32 ${product} to double`); return { name: t, type: e.type, uint32: product }; } + if (e.fn === "math.f16round") { + const v = host.emitExpr(e.args[0]!); + host.declare(`declare zeroext i16 @scr_f16_from_f64(double)`); + host.declare(`declare double @scr_f16_to_f64(i16 zeroext)`); + const narrowed = B.tmp(); + const t = B.tmp(); + B.line(`${narrowed} = call zeroext i16 @scr_f16_from_f64(double ${v.name})`); + B.line(`${t} = call double @scr_f16_to_f64(i16 zeroext ${narrowed})`); + return { name: t, type: e.type }; + } if (e.fn === "math.fround") { const v = host.emitExpr(e.args[0]!); const narrowed = B.tmp(); diff --git a/packages/compiler/src/backend/llvm/lib-shared.ts b/packages/compiler/src/backend/llvm/lib-shared.ts index 58b9e0b80..df9316627 100644 --- a/packages/compiler/src/backend/llvm/lib-shared.ts +++ b/packages/compiler/src/backend/llvm/lib-shared.ts @@ -65,6 +65,7 @@ export const LIB_FN_SYMS: Record = { "arrayBuffer.viewU8": "scr_array_buffer_view_u8", "arrayBuffer.viewU32": "scr_array_buffer_view_u32", "arrayBuffer.viewI32": "scr_array_buffer_view_i32", + "arrayBuffer.viewF16": "scr_array_buffer_view_f16", "arrayBuffer.viewF32": "scr_array_buffer_view_f32", "arrayBuffer.viewF64": "scr_array_buffer_view_f64", "arrayBuffer.viewDV": "scr_array_buffer_view_dv", diff --git a/packages/compiler/src/frontend/lowering/containers/array-pipelines.ts b/packages/compiler/src/frontend/lowering/containers/array-pipelines.ts index 0ceb4d677..cf45a7575 100644 --- a/packages/compiler/src/frontend/lowering/containers/array-pipelines.ts +++ b/packages/compiler/src/frontend/lowering/containers/array-pipelines.ts @@ -108,6 +108,7 @@ const PURE_MATH = new Set([ "math.exp", "math.expm1", "math.floor", + "math.f16round", "math.fround", "math.imul", "math.log", diff --git a/packages/compiler/src/frontend/lowering/containers/bytes.ts b/packages/compiler/src/frontend/lowering/containers/bytes.ts index b9400cbbd..7a19927de 100644 --- a/packages/compiler/src/frontend/lowering/containers/bytes.ts +++ b/packages/compiler/src/frontend/lowering/containers/bytes.ts @@ -151,7 +151,7 @@ function lowerBytesSortCall( /* ── typed arrays / Buffer ─────────────────────────────────────────────── */ /** The typed-array constructors with a runtime representation, by lib - * interface name. Other flavors (Float16Array, BigInt64Array, ...) fall through + * interface name. Other flavors (BigInt64Array, BigUint64Array) fall through * to the generic stdlib-constructor fence. DataView is handled separately. */ const BYTES_CTORS: Record = { Uint8Array: "u8", @@ -161,6 +161,7 @@ const BYTES_CTORS: Record = { Int16Array: "i16", Uint32Array: "u32", Int32Array: "i32", + Float16Array: "f16", Float32Array: "f32", Float64Array: "f64", }; @@ -397,6 +398,7 @@ function lowerArrayBufferView( u8: "arrayBuffer.viewU8", u32: "arrayBuffer.viewU32", i32: "arrayBuffer.viewI32", + f16: "arrayBuffer.viewF16", f32: "arrayBuffer.viewF32", f64: "arrayBuffer.viewF64", dv: "arrayBuffer.viewDV", @@ -1220,6 +1222,7 @@ const DV_GETTERS: Record = new Set([ "Int8Array", "Int16Array", "Int32Array", + "Float16Array", "Float32Array", "Float64Array", "BigInt64Array", diff --git a/packages/compiler/src/frontend/lowering/nan-coded-reads.ts b/packages/compiler/src/frontend/lowering/nan-coded-reads.ts index 2a537d6de..f7023cc52 100644 --- a/packages/compiler/src/frontend/lowering/nan-coded-reads.ts +++ b/packages/compiler/src/frontend/lowering/nan-coded-reads.ts @@ -191,6 +191,7 @@ const TYPED_ARRAYS = new Set([ "Uint16Array", "Int32Array", "Uint32Array", + "Float16Array", "Float32Array", "Float64Array", ]); diff --git a/packages/compiler/src/frontend/lowering/narrowing-stability.ts b/packages/compiler/src/frontend/lowering/narrowing-stability.ts index 9c7850ceb..4ae8eec67 100644 --- a/packages/compiler/src/frontend/lowering/narrowing-stability.ts +++ b/packages/compiler/src/frontend/lowering/narrowing-stability.ts @@ -834,6 +834,7 @@ const INERT_CONSTRUCTORS = new Set([ "Int16Array", "Uint32Array", "Int32Array", + "Float16Array", "Float32Array", "Float64Array", "BigInt64Array", diff --git a/packages/compiler/src/frontend/lowering/surfaces.ts b/packages/compiler/src/frontend/lowering/surfaces.ts index db7d50b64..5f8baa6fb 100644 --- a/packages/compiler/src/frontend/lowering/surfaces.ts +++ b/packages/compiler/src/frontend/lowering/surfaces.ts @@ -664,6 +664,7 @@ export const STDLIB_SURFACE = { cosh: ISL_N1, exp: ISL_N1, expm1: ISL_N1, + f16round: ISL_N1, fround: ISL_N1, hypot: ISL_N2, imul: ISL_N2, @@ -751,6 +752,7 @@ export const STATIC_MATH_FNS: Record([ "math.sign", "math.exp", "math.expm1", + "math.f16round", "math.fround", "math.log", "math.log1p", diff --git a/packages/compiler/src/ir/builtin-effects.ts b/packages/compiler/src/ir/builtin-effects.ts index 7365eefb1..ed8a087f8 100644 --- a/packages/compiler/src/ir/builtin-effects.ts +++ b/packages/compiler/src/ir/builtin-effects.ts @@ -75,6 +75,7 @@ export const MAY_THROW_LIB_FNS: ReadonlySet = new Set([ "arrayBuffer.viewU8", "arrayBuffer.viewU32", "arrayBuffer.viewI32", + "arrayBuffer.viewF16", "arrayBuffer.viewF32", "arrayBuffer.viewF64", "arrayBuffer.viewDV", diff --git a/packages/compiler/src/ir/builtin-signatures.ts b/packages/compiler/src/ir/builtin-signatures.ts index 07e5d223d..cb75dfa73 100644 --- a/packages/compiler/src/ir/builtin-signatures.ts +++ b/packages/compiler/src/ir/builtin-signatures.ts @@ -648,6 +648,7 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ "math.sign": { argTypes: [F64], result: F64 }, "math.exp": { argTypes: [F64], result: F64 }, "math.expm1": { argTypes: [F64], result: F64 }, + "math.f16round": { argTypes: [F64], result: F64 }, "math.fround": { argTypes: [F64], result: F64 }, "math.sqrt": { argTypes: [F64], result: F64 }, "math.log": { argTypes: [F64], result: F64 }, @@ -3163,6 +3164,7 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ "arrayBuffer.viewU8": { argTypes: [DYN, DYN, DYN], result: BYTES_U8 }, "arrayBuffer.viewU32": { argTypes: [DYN, DYN, DYN], result: bytesOf("u32") }, "arrayBuffer.viewI32": { argTypes: [DYN, DYN, DYN], result: bytesOf("i32") }, + "arrayBuffer.viewF16": { argTypes: [DYN, DYN, DYN], result: bytesOf("f16") }, "arrayBuffer.viewF32": { argTypes: [DYN, DYN, DYN], result: bytesOf("f32") }, "arrayBuffer.viewF64": { argTypes: [DYN, DYN, DYN], result: bytesOf("f64") }, "arrayBuffer.viewDV": { argTypes: [DYN, DYN, DYN], result: BYTES_U8 }, diff --git a/packages/compiler/src/ir/byte-numbers.ts b/packages/compiler/src/ir/byte-numbers.ts index a6a079af6..89ffaaa98 100644 --- a/packages/compiler/src/ir/byte-numbers.ts +++ b/packages/compiler/src/ir/byte-numbers.ts @@ -25,6 +25,7 @@ const FORMATS: Readonly> = { i16: { width: 2, signed: true, floating: false }, u32: { width: 4, signed: false, floating: false }, i32: { width: 4, signed: true, floating: false }, + f16: { width: 2, signed: false, floating: true }, f32: { width: 4, signed: false, floating: true }, f64: { width: 8, signed: false, floating: true }, u64: { width: 8, signed: false, floating: false }, @@ -38,6 +39,7 @@ const VIEW_FORMATS: Readonly> = { dvGetInt16: "i16", dvGetUint32: "u32", dvGetInt32: "i32", + dvGetFloat16: "f16", dvGetFloat32: "f32", dvGetFloat64: "f64", dvGetBigUint64Number: "u64", @@ -48,6 +50,7 @@ const VIEW_FORMATS: Readonly> = { dvSetInt16: "i16", dvSetUint32: "u32", dvSetInt32: "i32", + dvSetFloat16: "f16", dvSetFloat32: "f32", dvSetFloat64: "f64", }; diff --git a/packages/compiler/src/ir/int32-slots.ts b/packages/compiler/src/ir/int32-slots.ts index 5e868793c..a6a10eaa7 100644 --- a/packages/compiler/src/ir/int32-slots.ts +++ b/packages/compiler/src/ir/int32-slots.ts @@ -336,6 +336,7 @@ const BOUNDARY_KINDS = new Set([ * property, except literal-keyed `dyn.keySet`/`dyn.keyDelete`. */ const DYN_READ_ONLY = new Set([ "arrayBuffer.new", + "arrayBuffer.viewF16", "arrayBuffer.viewF32", "arrayBuffer.viewF64", "arrayBuffer.viewI16", diff --git a/packages/compiler/src/ir/ir.ts b/packages/compiler/src/ir/ir.ts index c12aa8cdd..41284118b 100644 --- a/packages/compiler/src/ir/ir.ts +++ b/packages/compiler/src/ir/ir.ts @@ -26,9 +26,19 @@ export interface SrcLoc { /* ── types ─────────────────────────────────────────────────────────────── */ -/** Numeric typed-array element kinds with a native representation. Float16 - * and BigInt typed arrays remain frontend-fenced. */ -export type IrBytesElem = "u8" | "u8c" | "i8" | "u16" | "i16" | "u32" | "i32" | "f32" | "f64"; +/** Numeric typed-array element kinds with a native representation. BigInt + * typed arrays remain frontend-fenced. */ +export type IrBytesElem = + | "u8" + | "u8c" + | "i8" + | "u16" + | "i16" + | "u32" + | "i32" + | "f16" + | "f32" + | "f64"; export const BYTES_ELEMENT_SIZE: Record = { u8: 1, @@ -38,13 +48,14 @@ export const BYTES_ELEMENT_SIZE: Record = { i16: 2, u32: 4, i32: 4, + f16: 2, f32: 4, f64: 8, }; /** Integer element kinds: a valid element read is never NaN. */ export function isIntegerBytesElem(elem: IrBytesElem): boolean { - return elem !== "f32" && elem !== "f64"; + return elem !== "f16" && elem !== "f32" && elem !== "f64"; } export const BYTES_ELEMENT_NAME: Record = { @@ -55,6 +66,7 @@ export const BYTES_ELEMENT_NAME: Record = { i16: "Int16Array", u32: "Uint32Array", i32: "Int32Array", + f16: "Float16Array", f32: "Float32Array", f64: "Float64Array", }; @@ -2242,6 +2254,7 @@ export type IrBytesIntrinsicMethod = | "dvGetInt16" | "dvGetUint32" | "dvGetInt32" + | "dvGetFloat16" | "dvGetFloat32" | "dvGetFloat64" | "dvGetBigUint64Number" @@ -2252,6 +2265,7 @@ export type IrBytesIntrinsicMethod = | "dvSetInt16" | "dvSetUint32" | "dvSetInt32" + | "dvSetFloat16" | "dvSetFloat32" | "dvSetFloat64"; @@ -2283,6 +2297,7 @@ export const MAY_THROW_BYTES_METHODS: ReadonlySet = new "dvGetInt16", "dvGetUint32", "dvGetInt32", + "dvGetFloat16", "dvGetFloat32", "dvGetFloat64", "dvGetBigUint64Number", @@ -2293,6 +2308,7 @@ export const MAY_THROW_BYTES_METHODS: ReadonlySet = new "dvSetInt16", "dvSetUint32", "dvSetInt32", + "dvSetFloat16", "dvSetFloat32", "dvSetFloat64", ]); diff --git a/packages/compiler/surface-manifest.json b/packages/compiler/surface-manifest.json index e6924f7cd..9a0188cf3 100644 --- a/packages/compiler/surface-manifest.json +++ b/packages/compiler/surface-manifest.json @@ -4602,6 +4602,13 @@ "status": "static", "note": "compiles statically at arity 1; other declared call shapes are refused with SC2012" }, + { + "id": "stdlib.math.f16round", + "kind": "stdlib", + "name": "Math.f16round", + "status": "static", + "note": "compiles statically at arity 1; other declared call shapes are refused with SC2012" + }, { "id": "stdlib.math.floor", "kind": "stdlib", diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5900-float16-array.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5900-float16-array.ts.json new file mode 100644 index 000000000..47cf8723f --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5900-float16-array.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5900-float16-array.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5901-float16-array-js.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5901-float16-array-js.js.json new file mode 100644 index 000000000..148849dca --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5901-float16-array-js.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5901-float16-array-js.js" + ], + "diags": [] +} diff --git a/packages/runtime/src/scr_array.c b/packages/runtime/src/scr_array.c index eba0367f7..ef64be56d 100644 --- a/packages/runtime/src/scr_array.c +++ b/packages/runtime/src/scr_array.c @@ -3111,6 +3111,11 @@ static double scr_bytes_sort_load(const ScrBytes *b, size_t i) { memcpy(&v, at, sizeof v); return v; } + case SCR_BYTES_F16: { + uint16_t v; + memcpy(&v, at, sizeof v); + return scr_f16_to_f64(v); + } case SCR_BYTES_F32: { float v; memcpy(&v, at, sizeof v); @@ -3151,6 +3156,11 @@ static void scr_bytes_sort_store(ScrBytes *b, size_t i, double value) { memcpy(at, &v, sizeof v); break; } + case SCR_BYTES_F16: { + uint16_t v = scr_f16_from_f64(value); + memcpy(at, &v, sizeof v); + break; + } case SCR_BYTES_F32: { float v = (float)value; memcpy(at, &v, sizeof v); @@ -3263,6 +3273,18 @@ static int scr_bytes_f32_key(const uint8_t *at, int32_t order, uint32_t *key) { return 0; } +/* Float16 keys use the low 16 bits of a 32-bit key; a descending order + * complements all 32, which keeps the order of keys. */ +static int scr_bytes_f16_key(const uint8_t *at, int32_t order, uint32_t *key) { + uint16_t bits; + memcpy(&bits, at, sizeof bits); + if ((bits & 0x7fff) > 0x7c00) return order ? 2 : 1; + if (order && bits == 0x8000) return 2; + uint32_t k = (bits & 0x8000) ? (uint16_t)~bits : (uint32_t)bits | 0x8000; + *key = k ^ (order < 0 ? ~UINT32_C(0) : 0); + return 0; +} + static int scr_bytes_f64_key(const uint8_t *at, int32_t order, uint64_t *key) { double v; uint64_t bits; @@ -3323,6 +3345,9 @@ static bool scr_bytes_sort_refused(const ScrBytes *b, int32_t order) { if (b->elem == SCR_BYTES_F32) { uint32_t key; if (scr_bytes_f32_key(data + i * 4, order, &key)) return true; + } else if (b->elem == SCR_BYTES_F16) { + uint32_t key; + if (scr_bytes_f16_key(data + i * 2, order, &key)) return true; } else if (b->elem == SCR_BYTES_F64) { uint64_t key; if (scr_bytes_f64_key(data + i * 8, order, &key)) return true; @@ -3353,7 +3378,7 @@ bool scr_bytes_sort_numeric(ScrBytes *b, int32_t order) { size_t width = scr_bytes_elem_size(b->elem); uint8_t *data = b->data; /* Integer elements have no NaN or -0: every order is a value order. */ - if (width == 1 || (width == 2 && n >= SCR_BYTES_COUNT_SORT)) { + if (width == 1 || (width == 2 && b->elem != SCR_BYTES_F16 && n >= SCR_BYTES_COUNT_SORT)) { scr_bytes_count_sort(b, order < 0); return true; } @@ -3392,13 +3417,23 @@ bool scr_bytes_sort_numeric(ScrBytes *b, int32_t order) { uint32_t bits_ = (k_ & SCR_SORT_SIGN32) ? k_ & ~SCR_SORT_SIGN32 : ~k_; \ memcpy(data + (i) * 4, &bits_, 4); \ } while (0) +#define SCR_BYTES_READ32_F16(i, key) scr_bytes_f16_key(data + (i) * 2, order, (key)) +#define SCR_BYTES_WRITE32_F16(i, key) \ + do { \ + uint16_t k_ = (uint16_t)((key) ^ flip32); \ + uint16_t bits_ = (k_ & 0x8000) ? (uint16_t)(k_ & 0x7fff) : (uint16_t)~k_; \ + memcpy(data + (i) * 2, &bits_, 2); \ + } while (0) if (b->elem == SCR_BYTES_F32) SCR_BYTES_KEYS32(F32); + else if (b->elem == SCR_BYTES_F16) SCR_BYTES_KEYS32(F16); else SCR_BYTES_KEYS32(INT); #undef SCR_BYTES_KEYS32 #undef SCR_BYTES_READ32_INT #undef SCR_BYTES_WRITE32_INT #undef SCR_BYTES_READ32_F32 #undef SCR_BYTES_WRITE32_F32 +#undef SCR_BYTES_READ32_F16 +#undef SCR_BYTES_WRITE32_F16 free(keys); return true; } diff --git a/packages/runtime/src/scr_assert.c b/packages/runtime/src/scr_assert.c index 9e2e79418..5a4769038 100644 --- a/packages/runtime/src/scr_assert.c +++ b/packages/runtime/src/scr_assert.c @@ -791,7 +791,7 @@ static bool scr_assert_loose_error(const ScrDyn *a, const ScrDyn *b) { } static bool scr_assert_loose_float_elem(ScrBytesElem elem) { - return elem == SCR_BYTES_F32 || elem == SCR_BYTES_F64; + return elem == SCR_BYTES_F16 || elem == SCR_BYTES_F32 || elem == SCR_BYTES_F64; } /* Typed arrays and Buffers: the same tag (a Buffer is a Uint8Array here), @@ -1305,6 +1305,7 @@ static const char *scr_assert_cf_bytes_class(ScrBytesElem elem) { switch (elem) { case SCR_BYTES_U8: return "Uint8Array"; case SCR_BYTES_U32: return "Uint32Array"; + case SCR_BYTES_F16: return "Float16Array"; case SCR_BYTES_F32: return "Float32Array"; case SCR_BYTES_I32: return "Int32Array"; case SCR_BYTES_F64: return "Float64Array"; diff --git a/packages/runtime/src/scr_bytes.c b/packages/runtime/src/scr_bytes.c index e9514531b..28258a29c 100644 --- a/packages/runtime/src/scr_bytes.c +++ b/packages/runtime/src/scr_bytes.c @@ -87,10 +87,60 @@ static void scr_bytes_oom(void) { scr_trap("scriptc: out of memory\n"); } +uint16_t scr_f16_from_f64(double value) { + uint64_t bits; + memcpy(&bits, &value, sizeof bits); + uint16_t sign = (uint16_t)((bits >> 48) & 0x8000); + uint64_t magnitude = bits & UINT64_C(0x7fffffffffffffff); + if (magnitude >= UINT64_C(0x7ff0000000000000)) { + if (magnitude == UINT64_C(0x7ff0000000000000)) return sign | 0x7c00; + return sign | 0x7e00 | (uint16_t)((magnitude >> 42) & 0x3ff); + } + int exponent = (int)(magnitude >> 52) - 1023; + if (exponent > 15) return sign | 0x7c00; + uint64_t fraction = magnitude & ((UINT64_C(1) << 52) - 1); + uint64_t significand = exponent == -1023 ? fraction : fraction | (UINT64_C(1) << 52); + if (exponent == -1023) exponent = -1022; + int biased = exponent + 15; + /* Normal results keep 10 of the 52 fraction bits; subnormal results + * shift further, by how far the exponent is below the normal range. */ + int shift = biased >= 1 ? 42 : 42 + (1 - biased); + if (shift >= 54) return sign; + uint64_t q = significand >> shift; + uint64_t rest = significand & ((UINT64_C(1) << shift) - 1); + uint64_t half = UINT64_C(1) << (shift - 1); + if (rest > half || (rest == half && (q & 1))) q++; + if (biased < 1) return sign | (uint16_t)q; /* a carry into 0x400 is the smallest normal */ + if (q == 2048) { + q = 1024; + biased++; + } + if (biased >= 31) return sign | 0x7c00; + return sign | (uint16_t)(biased << 10) | (uint16_t)(q & 0x3ff); +} + +double scr_f16_to_f64(uint16_t h) { + uint64_t sign = (uint64_t)(h >> 15) << 63; + unsigned exponent = (h >> 10) & 0x1f, fraction = h & 0x3ff; + uint64_t bits; + if (exponent == 0) { + double v = (double)fraction * 5.9604644775390625e-8; /* fraction * 2^-24, exact */ + memcpy(&bits, &v, sizeof bits); + bits |= sign; + } else if (exponent == 31) { + bits = sign | UINT64_C(0x7ff0000000000000) | ((uint64_t)fraction << 42); + } else { + bits = sign | ((uint64_t)(exponent - 15 + 1023) << 52) | ((uint64_t)fraction << 42); + } + double out; + memcpy(&out, &bits, sizeof out); + return out; +} + size_t scr_bytes_elem_size(ScrBytesElem elem) { switch (elem) { case SCR_BYTES_U8: case SCR_BYTES_U8C: case SCR_BYTES_I8: return 1; - case SCR_BYTES_U16: case SCR_BYTES_I16: return 2; + case SCR_BYTES_U16: case SCR_BYTES_I16: case SCR_BYTES_F16: return 2; case SCR_BYTES_U32: case SCR_BYTES_I32: case SCR_BYTES_F32: return 4; case SCR_BYTES_F64: return 8; } @@ -106,6 +156,7 @@ const char *scr_bytes_elem_name(ScrBytesElem elem) { case SCR_BYTES_I16: return "Int16Array"; case SCR_BYTES_U32: return "Uint32Array"; case SCR_BYTES_I32: return "Int32Array"; + case SCR_BYTES_F16: return "Float16Array"; case SCR_BYTES_F32: return "Float32Array"; case SCR_BYTES_F64: return "Float64Array"; } @@ -329,6 +380,11 @@ static double scr_bytes_decode(ScrBytesElem elem, const uint8_t *p) { memcpy(&v, p, 4); return (double)v; } + case SCR_BYTES_F16: { + uint16_t v; + memcpy(&v, p, 2); + return scr_f16_to_f64(v); + } case SCR_BYTES_F32: { float v; memcpy(&v, p, 4); @@ -368,6 +424,11 @@ static void scr_bytes_encode(ScrBytesElem elem, uint8_t *p, double v) { memcpy(p, &u, 4); break; } + case SCR_BYTES_F16: { + uint16_t h = scr_f16_from_f64(v); + memcpy(p, &h, 2); + break; + } case SCR_BYTES_F32: { float f = (float)v; /* round-to-nearest-even, exactly Float32Array */ memcpy(p, &f, 4); @@ -511,6 +572,13 @@ static void scr_bytes_read_numbers(double *out, const uint8_t *src, ScrBytesElem SCR_READ_NUMBERS(SCR_BYTES_I32, int32_t); SCR_READ_NUMBERS(SCR_BYTES_F32, float); SCR_READ_NUMBERS(SCR_BYTES_F64, double); + case SCR_BYTES_F16: + for (size_t i = 0; i < count; i++) { + uint16_t value; + memcpy(&value, src + i * sizeof value, sizeof value); + out[i] = scr_f16_to_f64(value); + } + return; } #undef SCR_READ_NUMBERS } @@ -535,6 +603,9 @@ static void scr_bytes_write_numbers(uint8_t *dst, ScrBytesElem elem, const doubl case SCR_BYTES_U32: case SCR_BYTES_I32: { SCR_WRITE_NUMBERS(uint32_t, scr_numeric_to_u32(src[i])); } + case SCR_BYTES_F16: { + SCR_WRITE_NUMBERS(uint16_t, scr_f16_from_f64(src[i])); + } case SCR_BYTES_F32: { SCR_WRITE_NUMBERS(float, (float)src[i]); } @@ -936,6 +1007,7 @@ static size_t scr_dataview_get_size(ScrDataViewGet kind) { return 1; case SCR_DV_U16: case SCR_DV_I16: + case SCR_DV_F16: return 2; case SCR_DV_U32: case SCR_DV_I32: @@ -974,6 +1046,8 @@ double scr_dataview_get(const ScrBytes *b, double byte_off, ScrDataViewGet kind, return (double)(int16_t)u; case SCR_DV_I32: return (double)(int32_t)u; + case SCR_DV_F16: + return scr_f16_to_f64((uint16_t)u); case SCR_DV_F32: { uint32_t bits = (uint32_t)u; float f; @@ -1018,6 +1092,9 @@ void scr_dataview_set(ScrBytes *b, double byte_off, double value, ScrDataViewGet case SCR_DV_I32: u = (uint64_t)scr_bytes_to_u32(value); break; + case SCR_DV_F16: + u = scr_f16_from_f64(value); + break; case SCR_DV_F32: { float f = (float)value; uint32_t bits; diff --git a/packages/runtime/src/scr_bytes.h b/packages/runtime/src/scr_bytes.h index 766c24514..752f64dc8 100644 --- a/packages/runtime/src/scr_bytes.h +++ b/packages/runtime/src/scr_bytes.h @@ -20,7 +20,8 @@ * Elements are scalars only and the backing edge is acyclic by * construction (owners point at nothing): never part of a cycle, no * trace. Element reads widen to double; writes coerce JS-exactly (ToUint8 - * / ToUint32 modular truncation, double→float rounding for f32). + * / ToUint32 modular truncation, double→float rounding for f32, one + * double→binary16 rounding for f16). * Out-of-bounds typed ELEMENT reads trap like arrays; invalid writes are * ignored as in JavaScript. The Node-shaped operations * (construction lengths, set(), the read/write numeric families, the @@ -37,7 +38,16 @@ typedef enum ScrBytesElem { SCR_BYTES_U16, /* Uint16Array */ SCR_BYTES_I16, /* Int16Array */ SCR_BYTES_U8C, /* Uint8ClampedArray */ + SCR_BYTES_F16, /* Float16Array (IEEE 754 binary16 storage) */ } ScrBytesElem; +#define SCR_BYTES_ELEM_COUNT 10 + +/* Float16Array's element conversions: one round-to-nearest-even step from + * the double (Float16Array stores, Math.f16round and DataView.setFloat16 + * all round this way), and the exact widening back. NaN keeps its sign and + * top payload bits with the quiet bit set. */ +uint16_t scr_f16_from_f64(double value); +double scr_f16_to_f64(uint16_t bits); typedef struct ScrSharedBytes ScrSharedBytes; @@ -149,6 +159,7 @@ ScrBytes *scr_array_buffer_view_i16(ScrDyn *, ScrDyn *, ScrDyn *); ScrBytes *scr_array_buffer_view_u8(ScrDyn *, ScrDyn *, ScrDyn *); ScrBytes *scr_array_buffer_view_u32(ScrDyn *, ScrDyn *, ScrDyn *); ScrBytes *scr_array_buffer_view_i32(ScrDyn *, ScrDyn *, ScrDyn *); +ScrBytes *scr_array_buffer_view_f16(ScrDyn *, ScrDyn *, ScrDyn *); ScrBytes *scr_array_buffer_view_f32(ScrDyn *, ScrDyn *, ScrDyn *); ScrBytes *scr_array_buffer_view_f64(ScrDyn *, ScrDyn *, ScrDyn *); ScrBytes *scr_array_buffer_view_dv(ScrDyn *, ScrDyn *, ScrDyn *); @@ -206,7 +217,7 @@ ScrBytes *scr_dataview_new(ScrBytes *src, double byte_off, bool has_len, double * with the exception pending). */ typedef enum ScrDataViewGet { SCR_DV_U8, SCR_DV_I8, SCR_DV_U16, SCR_DV_I16, SCR_DV_U32, SCR_DV_I32, - SCR_DV_F32, SCR_DV_F64, SCR_DV_BIGU64, SCR_DV_BIGI64, + SCR_DV_F32, SCR_DV_F64, SCR_DV_BIGU64, SCR_DV_BIGI64, SCR_DV_F16, } ScrDataViewGet; double scr_dataview_get(const ScrBytes *b, double byte_off, ScrDataViewGet kind, bool le); diff --git a/packages/runtime/src/scr_dyn_invoke.c b/packages/runtime/src/scr_dyn_invoke.c index 72d277271..59cf34a10 100644 --- a/packages/runtime/src/scr_dyn_invoke.c +++ b/packages/runtime/src/scr_dyn_invoke.c @@ -1520,7 +1520,7 @@ static ScrDyn *scr_dyn_invoke_impl( if (recv->kind == SCR_DYN_BYTES) { ScrBytes *bytes = recv->v.bytes; if (bytes->is_data_view) { - static const char *const kinds[] = { "Uint8", "Int8", "Uint16", "Int16", "Uint32", "Int32", "Float32", "Float64", "BigUint64", "BigInt64" }; + static const char *const kinds[] = { "Uint8", "Int8", "Uint16", "Int16", "Uint32", "Int32", "Float32", "Float64", "BigUint64", "BigInt64", "Float16" }; const bool get = !strncmp(method, "get", 3); const bool set = !strncmp(method, "set", 3); for (size_t kind = 0; (get || set) && kind < sizeof kinds / sizeof kinds[0]; kind++) { @@ -1537,7 +1537,7 @@ static ScrDyn *scr_dyn_invoke_impl( } const bool le = argc > (get ? 1 : 2) && scr_dyn_truthy(args[get ? 1 : 2]); if (get) { - if (kind >= SCR_DV_BIGU64) { + if (kind == SCR_DV_BIGU64 || kind == SCR_DV_BIGI64) { ScrBigInt *value = scr_bigint_dataview_get(bytes, offset, kind == SCR_DV_BIGI64, le); if (scr_exc_pending()) return NULL; ScrDyn *result = scr_dyn_new_bigint(value); @@ -1547,7 +1547,7 @@ static ScrDyn *scr_dyn_invoke_impl( double value = scr_dataview_get(bytes, offset, (ScrDataViewGet)kind, le); return scr_exc_pending() ? NULL : scr_dyn_new_num(value); } - if (kind >= SCR_DV_BIGU64) { + if (kind == SCR_DV_BIGU64 || kind == SCR_DV_BIGI64) { ScrBigInt *value = scr_dyn_bigint_coerce(argc > 1 ? args[1] : scr_dyn_undefined()); if (scr_exc_pending()) return NULL; scr_bigint_dataview_set(bytes, offset, value, le); diff --git a/packages/runtime/src/scr_json.c b/packages/runtime/src/scr_json.c index d4290e0df..f43315634 100644 --- a/packages/runtime/src/scr_json.c +++ b/packages/runtime/src/scr_json.c @@ -3146,6 +3146,7 @@ bool scr_dyn_util_type_is(const ScrDyn *value, const ScrStr *probe) { if (PROBE("isInt8Array")) return scr_dyn_typed_array_is(value, SCR_BYTES_I8); if (PROBE("isInt16Array")) return scr_dyn_typed_array_is(value, SCR_BYTES_I16); if (PROBE("isInt32Array")) return scr_dyn_typed_array_is(value, SCR_BYTES_I32); + if (PROBE("isFloat16Array")) return scr_dyn_typed_array_is(value, SCR_BYTES_F16); if (PROBE("isFloat32Array")) return scr_dyn_typed_array_is(value, SCR_BYTES_F32); if (PROBE("isFloat64Array")) return scr_dyn_typed_array_is(value, SCR_BYTES_F64); if (PROBE("isMap")) return value->kind == SCR_DYN_HANDLE && value->v.handle.tag == SCR_DYNH_MAP; @@ -3162,13 +3163,11 @@ bool scr_dyn_util_type_is(const ScrDyn *value, const ScrStr *probe) { if (PROBE("isMapIterator")) return scr_native_iterator_over(value, 2); if (PROBE("isSetIterator")) return scr_native_iterator_over(value, 3); if (PROBE("isKeyObject")) return value->kind == SCR_DYN_HANDLE && value->v.handle.tag == SCR_DYNH_KEY_OBJECT; - /* Boxed primitives, BigInt64Array, BigUint64Array, Float16Array, CryptoKey - * and external values cannot be created here, so no value has these - * brands. */ + /* Boxed primitives, BigInt64Array, BigUint64Array, CryptoKey and external + * values cannot be created here, so no value has these brands. */ if (PROBE("isNumberObject") || PROBE("isStringObject") || PROBE("isBooleanObject") || PROBE("isBigIntObject") || PROBE("isSymbolObject") || PROBE("isBoxedPrimitive") || - PROBE("isBigInt64Array") || PROBE("isBigUint64Array") || PROBE("isFloat16Array") || - PROBE("isCryptoKey") || PROBE("isExternal")) + PROBE("isBigInt64Array") || PROBE("isBigUint64Array") || PROBE("isCryptoKey") || PROBE("isExternal")) return false; if (PROBE("isNativeError")) { ScrError *error = scr_errdyn_err_of(value); @@ -3184,7 +3183,7 @@ bool scr_dyn_util_type_is(const ScrDyn *value, const ScrStr *probe) { * anonymous with length zero. Keep their native closure identity intact. */ ScrDyn *scr_dyn_util_type_value(const ScrDyn *value, const ScrStr *probe) { static const char *const named[] = { "isDataView", "isTypedArray", "isUint8Array", "isUint8ClampedArray", "isUint16Array", "isUint32Array", - "isInt8Array", "isInt16Array", "isInt32Array", "isFloat32Array", "isFloat64Array" }; + "isInt8Array", "isInt16Array", "isInt32Array", "isFloat16Array", "isFloat32Array", "isFloat64Array" }; const char *name = ""; if (probe->len == 17 && memcmp(probe->data, "isArrayBufferView", 17) == 0) name = "isView"; for (size_t i = 0; i < sizeof named / sizeof named[0]; i++) @@ -3203,16 +3202,16 @@ ScrBytes *scr_dyn_bytes_unbox(const ScrDyn *d) { /* Numeric typed-array constructor values share native function identity. * The cache owns one reference until runtime cleanup; arbitrary strings or * functions cannot impersonate one of these constructors. */ -static SCR_TL ScrDyn *scr_bytes_ctors[9]; +static SCR_TL ScrDyn *scr_bytes_ctors[SCR_BYTES_ELEM_COUNT]; static void scr_bytes_ctors_cleanup(void) { - for (size_t i = 0; i < 9; i++) { + for (size_t i = 0; i < SCR_BYTES_ELEM_COUNT; i++) { scr_dyn_release(scr_bytes_ctors[i]); scr_bytes_ctors[i] = NULL; } } static ScrDyn *scr_bytes_ctor_call(ScrClosure *closure, ScrDyn *const *args, size_t argc) { (void)args; (void)argc; - for (size_t i = 0; i < 9; i++) { + for (size_t i = 0; i < SCR_BYTES_ELEM_COUNT; i++) { if (!scr_bytes_ctors[i] || scr_bytes_ctors[i]->v.fn.clo != closure) continue; ScrJsonBuf message; scr_jb_init(&message); @@ -3228,7 +3227,7 @@ static ScrDyn *scr_dyn_fn_properties(const ScrDyn *function); static ScrDyn *scr_bytes_constructor_for(ScrBytesElem elem) { if (!scr_bytes_ctors[elem]) { bool initialized = false; - for (size_t i = 0; i < 9; i++) if (scr_bytes_ctors[i]) initialized = true; + for (size_t i = 0; i < SCR_BYTES_ELEM_COUNT; i++) if (scr_bytes_ctors[i]) initialized = true; if (!initialized) scr_atexit(scr_bytes_ctors_cleanup); scr_bytes_ctors[elem] = scr_dyn_new_func(scr_closure_new(NULL, 0), scr_bytes_ctor_call, 3, "typed-array-constructor", scr_bytes_elem_name(elem)); @@ -3241,7 +3240,7 @@ static ScrDyn *scr_bytes_constructor_for(ScrBytesElem elem) { return scr_dyn_retain(scr_bytes_ctors[elem]); } ScrDyn *scr_bytes_constructor(const ScrStr *name) { - for (size_t i = 0; i < 9; i++) { + for (size_t i = 0; i < SCR_BYTES_ELEM_COUNT; i++) { const char *candidate = scr_bytes_elem_name((ScrBytesElem)i); if (strlen(candidate) == name->len && memcmp(candidate, name->data, name->len) == 0) return scr_bytes_constructor_for((ScrBytesElem)i); @@ -3308,7 +3307,7 @@ bool scr_bytes_instanceof(const ScrDyn *value, const ScrDyn *callee) { } if (scr_array_buffer_ctor && callee->v.fn.clo == scr_array_buffer_ctor->v.fn.clo) return scr_array_buffer_is(value); if (scr_array_ctor && callee->v.fn.clo == scr_array_ctor->v.fn.clo) return value->kind == SCR_DYN_ARR || scr_dyn_ref_is_array(value); - for (size_t i = 0; i < 9; i++) { + for (size_t i = 0; i < SCR_BYTES_ELEM_COUNT; i++) { if (scr_bytes_ctors[i] && callee->v.fn.clo == scr_bytes_ctors[i]->v.fn.clo) return scr_dyn_typed_array_is(value, (int)i); } @@ -3321,7 +3320,7 @@ ScrDyn *scr_bytes_construct(const ScrDyn *callee, const ScrDyn *args, const ScrS return scr_array_ctor_call(callee->v.fn.clo, args->v.arr.items, args->v.arr.len); if (callee->kind == SCR_DYN_FUNC && scr_array_buffer_ctor && callee->v.fn.clo == scr_array_buffer_ctor->v.fn.clo) return scr_array_buffer_new(args->v.arr.len ? args->v.arr.items[0] : scr_dyn_undefined()); - for (size_t i = 0; i < 9; i++) { + for (size_t i = 0; i < SCR_BYTES_ELEM_COUNT; i++) { if (callee->kind != SCR_DYN_FUNC || !scr_bytes_ctors[i] || callee->v.fn.clo != scr_bytes_ctors[i]->v.fn.clo) continue; const ScrDyn *input = args->v.arr.len > 0 ? args->v.arr.items[0] : scr_dyn_undefined(); const ScrDyn *offset = args->v.arr.len > 1 ? args->v.arr.items[1] : scr_dyn_undefined(); @@ -12377,6 +12376,7 @@ SCR_ARRAY_BUFFER_VIEW(i16, SCR_BYTES_I16) SCR_ARRAY_BUFFER_VIEW(u8, SCR_BYTES_U8) SCR_ARRAY_BUFFER_VIEW(u32, SCR_BYTES_U32) SCR_ARRAY_BUFFER_VIEW(i32, SCR_BYTES_I32) +SCR_ARRAY_BUFFER_VIEW(f16, SCR_BYTES_F16) SCR_ARRAY_BUFFER_VIEW(f32, SCR_BYTES_F32) SCR_ARRAY_BUFFER_VIEW(f64, SCR_BYTES_F64) #undef SCR_ARRAY_BUFFER_VIEW diff --git a/tests/corpus/5831-util-types-brands.ts b/tests/corpus/5831-util-types-brands.ts index 7deec967d..d38200914 100644 --- a/tests/corpus/5831-util-types-brands.ts +++ b/tests/corpus/5831-util-types-brands.ts @@ -1,9 +1,9 @@ // util.types brand checks over typed values: isDate, isSharedArrayBuffer, // isMapIterator, isSetIterator and isKeyObject answer from the value's // internal brand, and isBoxedPrimitive (with the per-kind boxed checks), -// isBigInt64Array, isBigUint64Array, isFloat16Array, isCryptoKey and -// isExternal answer false for every value a program here can create. A -// Date-typed argument answers from its static type. +// isBigInt64Array, isBigUint64Array, isCryptoKey and isExternal answer +// false for every value a program here can create. A Date-typed argument +// answers from its static type. import { types } from "node:util"; import * as direct from "node:util/types"; import { createSecretKey } from "node:crypto"; diff --git a/tests/corpus/5900-float16-array.ts b/tests/corpus/5900-float16-array.ts new file mode 100644 index 000000000..bcf57ea8d --- /dev/null +++ b/tests/corpus/5900-float16-array.ts @@ -0,0 +1,94 @@ +// Float16Array: construction from lengths, arrays, typed arrays and ArrayBuffer +// views; round-to-nearest-even stores with overflow, NaN, -0 and subnormals; +// fill, set, subarray, slice and both sort orders; DataView getFloat16 and +// setFloat16 in both byte orders; Math.f16round; util.types and instanceof; +// structuredClone, assert and Buffer views. Node.js prints the same. +import { types } from "node:util"; +import assert from "node:assert"; +const show = (label: string, a: Float16Array): void => console.log(label, a.length, Array.from(a).join(",")); +const a = new Float16Array(6); +a[0] = 1.5; +a[1] = 65519; +a[2] = 65520; +a[3] = 0.1; +a[4] = -0; +a[5] = 5.960464477539063e-8 / 2; +show("rounding", a); +const b = new Float16Array([NaN, Infinity, -Infinity, 1e-8, 2049, 2051, 4097, -1e6]); +show("specials", b); +console.log(Object.is(a[4], -0), a[6], a.length, a.byteLength, Number.isNaN(b[0])); +const c = Float16Array.from([3.14159, 2.71828, 1.41421]); +show("from", c); +const e = new Float16Array(c); +e[0] = 100; +show("copy", e); +show("source untouched", c); +const f32 = new Float32Array([1 / 3, 2 / 3]); +show("from f32", new Float16Array(f32)); +const buf = new ArrayBuffer(8); +const view = new Float16Array(buf, 2, 2); +view[0] = 1; +view[1] = -2; +console.log("view bytes", Array.from(new Uint8Array(buf)).join(","), new Uint16Array(buf)[1]); +const g = new Float16Array(5); +g.fill(0.3); +show("fill", g); +g.set([1.1, 2.2], 3); +show("set", g); +show("subarray", g.subarray(1, 4)); +show("slice", g.slice(-2)); +const h = new Float16Array([3, -0, 0, NaN, -1, 2.5, -Infinity, 0.1]); +show("sort", h.slice().sort()); +show("sort desc", h.slice().sort((x, y) => y - x)); +show("toSorted", h.toSorted()); +let sum = 0; +for (const v of c) sum += v; +console.log("for-of", sum, h.join("|"), String(c), Object.prototype.toString.call(c)); +const dv = new DataView(new ArrayBuffer(8)); +dv.setFloat16(0, 3.14159); +dv.setFloat16(2, 3.14159, true); +dv.setFloat16(4, -65504); +dv.setFloat16(6, NaN, true); +console.log("dataview", dv.getFloat16(0), dv.getFloat16(2, true), dv.getFloat16(4), dv.getFloat16(6, true), dv.getUint16(0), dv.getUint16(2)); +try { + dv.getFloat16(7); +} catch (err) { + console.log((err as Error).name, (err as Error).message); +} +console.log("f16round", Math.f16round(1.337), Math.f16round(65520), Math.f16round(-0), Math.f16round(5e-8), Math.f16round(2 ** -25), Math.f16round(1.0009765625 + 2 ** -12)); +console.log("types", types.isFloat16Array(a), types.isFloat32Array(a), types.isTypedArray(a), a instanceof Float16Array); +const u: unknown = a; +console.log("unknown", u instanceof Float16Array, u instanceof Float32Array); +const big = new Float16Array(1000); +for (let i = 0; i < big.length; i++) big[i] = Math.sin(i) * 1000; +let acc = 0; +for (let i = 0; i < big.length; i++) acc += big[i]!; +console.log("loop", acc, big[999]); +const f = new Float16Array([1.5, NaN, -0, 65504]); +const clone = structuredClone(f); +clone[0] = 2; +console.log(clone[0], f[0], clone.length); +assert.deepStrictEqual(new Float16Array([NaN, 1]), new Float16Array([NaN, 1])); +try { + assert.deepStrictEqual(new Float16Array([0]), new Float16Array([-0])); +} catch (e) { + console.log("deepStrictEqual -0", (e as Error).name); +} +assert.deepEqual(new Float16Array([0]), new Float16Array([-0])); +const raw = Buffer.from(f.buffer); +console.log(raw.length, raw.readUInt16LE(0), raw.readUInt16LE(6)); +const fromBuf = new Float16Array(f.buffer, 2, 1); +console.log(Number.isNaN(fromBuf[0])); +const list: Float16Array[] = [f, new Float16Array(2)]; +const m = new Map([["a", f]]); +console.log(list.length, m.get("a")![3], list[1]!.length); +const Ctor = Float16Array; +const x = new Ctor(3); +x[1] = 0.2; +console.log(x.length, x[1]); +const y = new Float16Array([1, 2]); +const D = y.constructor as Float16ArrayConstructor; +const z = new D(2); +console.log(z.length, z instanceof Float16Array); +const unknownView: unknown = new Float16Array([7]); +console.log(unknownView instanceof Float16Array); diff --git a/tests/corpus/5901-float16-array-js.js b/tests/corpus/5901-float16-array-js.js new file mode 100644 index 000000000..8c0f6f7bc --- /dev/null +++ b/tests/corpus/5901-float16-array-js.js @@ -0,0 +1,95 @@ +// Float16Array from JavaScript: construction from lengths, arrays, typed +// arrays and ArrayBuffer views; round-to-nearest-even stores with overflow, +// NaN, -0 and subnormals; fill, set, subarray, slice and both sort orders; +// DataView getFloat16 and setFloat16 in both byte orders; Math.f16round; +// util.types and instanceof; structuredClone, assert and Buffer views. +// Node.js prints the same. +import { types } from "node:util"; +import assert from "node:assert"; +const show = (label , a ) => console.log(label, a.length, Array.from(a).join(",")); +const a = new Float16Array(6); +a[0] = 1.5; +a[1] = 65519; +a[2] = 65520; +a[3] = 0.1; +a[4] = -0; +a[5] = 5.960464477539063e-8 / 2; +show("rounding", a); +const b = new Float16Array([NaN, Infinity, -Infinity, 1e-8, 2049, 2051, 4097, -1e6]); +show("specials", b); +console.log(Object.is(a[4], -0), a[6], a.length, a.byteLength, Number.isNaN(b[0])); +const c = Float16Array.from([3.14159, 2.71828, 1.41421]); +show("from", c); +const e = new Float16Array(c); +e[0] = 100; +show("copy", e); +show("source untouched", c); +const f32 = new Float32Array([1 / 3, 2 / 3]); +show("from f32", new Float16Array(f32)); +const buf = new ArrayBuffer(8); +const view = new Float16Array(buf, 2, 2); +view[0] = 1; +view[1] = -2; +console.log("view bytes", Array.from(new Uint8Array(buf)).join(","), new Uint16Array(buf)[1]); +const g = new Float16Array(5); +g.fill(0.3); +show("fill", g); +g.set([1.1, 2.2], 3); +show("set", g); +show("subarray", g.subarray(1, 4)); +show("slice", g.slice(-2)); +const h = new Float16Array([3, -0, 0, NaN, -1, 2.5, -Infinity, 0.1]); +show("sort", h.slice().sort()); +show("sort desc", h.slice().sort((x, y) => y - x)); +show("toSorted", h.toSorted()); +let sum = 0; +for (const v of c) sum += v; +console.log("for-of", sum, h.join("|"), String(c), Object.prototype.toString.call(c)); +const dv = new DataView(new ArrayBuffer(8)); +dv.setFloat16(0, 3.14159); +dv.setFloat16(2, 3.14159, true); +dv.setFloat16(4, -65504); +dv.setFloat16(6, NaN, true); +console.log("dataview", dv.getFloat16(0), dv.getFloat16(2, true), dv.getFloat16(4), dv.getFloat16(6, true), dv.getUint16(0), dv.getUint16(2)); +try { + dv.getFloat16(7); +} catch (err) { + console.log((err ).name, (err ).message); +} +console.log("f16round", Math.f16round(1.337), Math.f16round(65520), Math.f16round(-0), Math.f16round(5e-8), Math.f16round(2 ** -25), Math.f16round(1.0009765625 + 2 ** -12)); +console.log("types", types.isFloat16Array(a), types.isFloat32Array(a), types.isTypedArray(a), a instanceof Float16Array); +const u = a; +console.log("unknown", u instanceof Float16Array, u instanceof Float32Array); +const big = new Float16Array(1000); +for (let i = 0; i < big.length; i++) big[i] = Math.sin(i) * 1000; +let acc = 0; +for (let i = 0; i < big.length; i++) acc += big[i] ; +console.log("loop", acc, big[999]); +const f = new Float16Array([1.5, NaN, -0, 65504]); +const clone = structuredClone(f); +clone[0] = 2; +console.log(clone[0], f[0], clone.length); +assert.deepStrictEqual(new Float16Array([NaN, 1]), new Float16Array([NaN, 1])); +try { + assert.deepStrictEqual(new Float16Array([0]), new Float16Array([-0])); +} catch (e) { + console.log("deepStrictEqual -0", (e ).name); +} +assert.deepEqual(new Float16Array([0]), new Float16Array([-0])); +const raw = Buffer.from(f.buffer); +console.log(raw.length, raw.readUInt16LE(0), raw.readUInt16LE(6)); +const fromBuf = new Float16Array(f.buffer, 2, 1); +console.log(Number.isNaN(fromBuf[0])); +const list = [f, new Float16Array(2)]; +const m = new Map ([["a", f]]); +console.log(list.length, m.get("a") [3], list[1] .length); +const Ctor = Float16Array; +const x = new Ctor(3); +x[1] = 0.2; +console.log(x.length, x[1]); +const y = new Float16Array([1, 2]); +const D = y.constructor ; +const z = new D(2); +console.log(z.length, z instanceof Float16Array); +const unknownView = new Float16Array([7]); +console.log(unknownView instanceof Float16Array); diff --git a/tests/diagnostics/dataview.ts b/tests/diagnostics/dataview.ts index 01255b47c..c20193279 100644 --- a/tests/diagnostics/dataview.ts +++ b/tests/diagnostics/dataview.ts @@ -1,6 +1,8 @@ -// Stored ArrayBuffers and DataViews lower; Float16 accessors remain unsupported. +// Stored ArrayBuffers and DataViews lower, Float16 accessors included; +// detached accessor methods remain unsupported. const buffer = new ArrayBuffer(16); const view = new DataView(buffer); view.setFloat16(0, 1.5); console.log(view.getFloat16(0)); +const getter = view.getFloat16; // End of the diagnostic fixture. diff --git a/tests/diagnostics/es2025-fences.ts b/tests/diagnostics/es2025-fences.ts index 2df12226f..067da192e 100644 --- a/tests/diagnostics/es2025-fences.ts +++ b/tests/diagnostics/es2025-fences.ts @@ -1,9 +1,8 @@ // The es2025-lib surface WITHOUT a lowering fences by name: resizable // ArrayBuffers and transfer, SharedArrayBuffer.grow, Atomics.waitAsync, -// Float16Array and its Math/DataView companions, first-class iterator -// objects, and the unlowered forms of the implemented statics -// (Promise.try with fewer arguments than parameters, groupBy's -// literal-union keys, a set-like non-Set argument). +// first-class iterator objects, and the unlowered forms of the +// implemented statics (Promise.try with fewer arguments than parameters, +// groupBy's literal-union keys, a set-like non-Set argument). // Resizable ArrayBuffer + transfer (ES2024). const ab = new ArrayBuffer(8, { maxByteLength: 16 }); @@ -22,12 +21,6 @@ sab.grow(16); const ia = new Int32Array(4); const waited = Atomics.waitAsync(ia, 0, 0, 1); -// Float16 (ES2025): the typed array, Math.f16round, DataView accessors. -const f16 = new Float16Array(4); -console.log(Math.f16round(1.337)); -const dv = new DataView(new Uint8Array(8).buffer); -dv.setFloat16(0, 1.5); - // First-class iterator objects. const it = Iterator.from([1, 2, 3].values()); diff --git a/tests/harness/__snapshots__/dataview.ts.txt b/tests/harness/__snapshots__/dataview.ts.txt index f09051126..4f1401ca3 100644 --- a/tests/harness/__snapshots__/dataview.ts.txt +++ b/tests/harness/__snapshots__/dataview.ts.txt @@ -1,17 +1,8 @@ -dataview.ts:4:1 - error SC2020: 'DataView.setFloat16' is part of the standard library types but has no scriptc lowering yet +dataview.ts:7:16 - error SC2020: 'DataView.getFloat16' is part of the standard library types but has no scriptc lowering yet - 3 | const view = new DataView(buffer); - 4 | view.setFloat16(0, 1.5); - | ^~~~~~~~~~~~~~~ - 5 | console.log(view.getFloat16(0)); - - hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations) - -dataview.ts:5:13 - error SC2020: 'DataView.getFloat16' is part of the standard library types but has no scriptc lowering yet - - 4 | view.setFloat16(0, 1.5); - 5 | console.log(view.getFloat16(0)); - | ^~~~~~~~~~~~~~~ - 6 | // End of the diagnostic fixture. + 6 | console.log(view.getFloat16(0)); + 7 | const getter = view.getFloat16; + | ^~~~~~~~~~~~~~~ + 8 | // End of the diagnostic fixture. hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations) \ No newline at end of file diff --git a/tests/harness/__snapshots__/es2025-fences.ts.txt b/tests/harness/__snapshots__/es2025-fences.ts.txt index e1761f73a..e2dc5f40f 100644 --- a/tests/harness/__snapshots__/es2025-fences.ts.txt +++ b/tests/harness/__snapshots__/es2025-fences.ts.txt @@ -1,125 +1,89 @@ -es2025-fences.ts:9:12 - error SC2020: 'resizable ArrayBuffer construction' is part of the standard library types but has no scriptc lowering yet +es2025-fences.ts:8:12 - error SC2020: 'resizable ArrayBuffer construction' is part of the standard library types but has no scriptc lowering yet - 8 | // Resizable ArrayBuffer + transfer (ES2024). - 9 | const ab = new ArrayBuffer(8, { maxByteLength: 16 }); - | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 10 | ab.resize(16); + 7 | // Resizable ArrayBuffer + transfer (ES2024). + 8 | const ab = new ArrayBuffer(8, { maxByteLength: 16 }); + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 9 | ab.resize(16); hint: fixed-length ArrayBuffers accept a byte length; resizable storage is not supported yet -es2025-fences.ts:15:27 - error SC2020: 'resizable ArrayBuffer construction' is part of the standard library types but has no scriptc lowering yet +es2025-fences.ts:14:27 - error SC2020: 'resizable ArrayBuffer construction' is part of the standard library types but has no scriptc lowering yet - 14 | // fixed-length new Uint8Array(new ArrayBuffer(8)) lowers). - 15 | const rz = new Uint8Array(new ArrayBuffer(8, { maxByteLength: 16 })); + 13 | // fixed-length new Uint8Array(new ArrayBuffer(8)) lowers). + 14 | const rz = new Uint8Array(new ArrayBuffer(8, { maxByteLength: 16 })); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 16 | + 15 | hint: fixed-length ArrayBuffers accept a byte length; resizable storage is not supported yet -es2025-fences.ts:18:13 - error SC2020: 'resizable ArrayBuffer construction' is part of the standard library types but has no scriptc lowering yet +es2025-fences.ts:17:13 - error SC2020: 'resizable ArrayBuffer construction' is part of the standard library types but has no scriptc lowering yet - 17 | // SharedArrayBuffer growth. - 18 | const sab = new SharedArrayBuffer(8, { maxByteLength: 16 }); + 16 | // SharedArrayBuffer growth. + 17 | const sab = new SharedArrayBuffer(8, { maxByteLength: 16 }); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 19 | sab.grow(16); + 18 | sab.grow(16); hint: fixed-length ArrayBuffers accept a byte length; resizable storage is not supported yet -es2025-fences.ts:23:7 - error SC2009: values of type '{ async: false; value: "not-equal" | "timed-out"; } | { async: true; value: Promise<"ok" | "timed-out">; }' cannot be compiled: the union shape is supported, but its arm '{ async: false; value: "not-equal" | "timed-out"; }' does not compile +es2025-fences.ts:22:7 - error SC2009: values of type '{ async: false; value: "not-equal" | "timed-out"; } | { async: true; value: Promise<"ok" | "timed-out">; }' cannot be compiled: the union shape is supported, but its arm '{ async: false; value: "not-equal" | "timed-out"; }' does not compile - 22 | const ia = new Int32Array(4); - 23 | const waited = Atomics.waitAsync(ia, 0, 0, 1); + 21 | const ia = new Int32Array(4); + 22 | const waited = Atomics.waitAsync(ia, 0, 0, 1); | ^~~~~~ - 24 | + 23 | hint: the outer shape is supported — the named component is the blocker; restate it inside the compilable set (number, string, boolean, arrays, Maps, Sets, RegExp, functions, classes, records, unions of those, and 'unknown') -es2025-fences.ts:23:16 - error SC2020: 'Atomics.waitAsync' is part of the standard library types but has no scriptc lowering yet +es2025-fences.ts:22:16 - error SC2020: 'Atomics.waitAsync' is part of the standard library types but has no scriptc lowering yet - 22 | const ia = new Int32Array(4); - 23 | const waited = Atomics.waitAsync(ia, 0, 0, 1); + 21 | const ia = new Int32Array(4); + 22 | const waited = Atomics.waitAsync(ia, 0, 0, 1); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 24 | + 23 | hint: use integer load, store, exchange, compareExchange, add, sub, and, or, xor, wait or notify -es2025-fences.ts:26:7 - error SC2020: 'Float16Array' is part of the standard library types but has no scriptc lowering yet +es2025-fences.ts:25:7 - error SC2020: 'IteratorObject' is part of the standard library types but has no scriptc lowering yet - 25 | // Float16 (ES2025): the typed array, Math.f16round, DataView accessors. - 26 | const f16 = new Float16Array(4); - | ^~~ - 27 | console.log(Math.f16round(1.337)); - - hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations) - -es2025-fences.ts:26:13 - error SC2020: 'new Float16Array' is part of the standard library types but has no scriptc lowering yet - - 25 | // Float16 (ES2025): the typed array, Math.f16round, DataView accessors. - 26 | const f16 = new Float16Array(4); - | ^~~~~~~~~~~~~~~~~~~ - 27 | console.log(Math.f16round(1.337)); - - hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations) - -es2025-fences.ts:27:13 - error SC2020: 'Math.f16round' is part of the standard library types but has no scriptc lowering yet - - 26 | const f16 = new Float16Array(4); - 27 | console.log(Math.f16round(1.337)); - | ^~~~~~~~~~~~~ - 28 | const dv = new DataView(new Uint8Array(8).buffer); - - hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations) - -es2025-fences.ts:29:1 - error SC2020: 'DataView.setFloat16' is part of the standard library types but has no scriptc lowering yet - - 28 | const dv = new DataView(new Uint8Array(8).buffer); - 29 | dv.setFloat16(0, 1.5); - | ^~~~~~~~~~~~~ - 30 | - - hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations) - -es2025-fences.ts:32:7 - error SC2020: 'IteratorObject' is part of the standard library types but has no scriptc lowering yet - - 31 | // First-class iterator objects. - 32 | const it = Iterator.from([1, 2, 3].values()); + 24 | // First-class iterator objects. + 25 | const it = Iterator.from([1, 2, 3].values()); | ^~ - 33 | + 26 | hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations) -es2025-fences.ts:32:12 - error SC2020: 'Iterator.from' is part of the standard library types but has no scriptc lowering yet +es2025-fences.ts:25:12 - error SC2020: 'Iterator.from' is part of the standard library types but has no scriptc lowering yet - 31 | // First-class iterator objects. - 32 | const it = Iterator.from([1, 2, 3].values()); + 24 | // First-class iterator objects. + 25 | const it = Iterator.from([1, 2, 3].values()); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 33 | + 26 | hint: first-class iterator objects have no lowering — iterator helpers compile as one chain on an array iterator, consumed in place: arr.values().map(f).take(n).toArray() -es2025-fences.ts:35:27 - error SC2020: 'Promise.try over a callback of type '(a: number, b?: number | undefined) => number' with 1 argument' is part of the standard library types but has no scriptc lowering yet +es2025-fences.ts:28:27 - error SC2020: 'Promise.try over a callback of type '(a: number, b?: number | undefined) => number' with 1 argument' is part of the standard library types but has no scriptc lowering yet - 34 | // Promise.try with fewer arguments than the callback's parameters. - 35 | const tried = Promise.try((a: number, b?: number) => a + (b ?? 0), 1); + 27 | // Promise.try with fewer arguments than the callback's parameters. + 28 | const tried = Promise.try((a: number, b?: number) => a + (b ?? 0), 1); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 36 | + 29 | hint: pass one argument per callback parameter: Promise.try((a, b) => a + b, 1, 2) -es2025-fences.ts:38:17 - error SC2020: 'Object.groupBy at this result type' is part of the standard library types but has no scriptc lowering yet +es2025-fences.ts:31:17 - error SC2020: 'Object.groupBy at this result type' is part of the standard library types but has no scriptc lowering yet - 37 | // Object.groupBy at a literal-union key type (a fixed-field record). - 38 | const grouped = Object.groupBy([1, 2, 3], (n) => (n % 2 === 0 ? "even" : "odd")); + 30 | // Object.groupBy at a literal-union key type (a fixed-field record). + 31 | const grouped = Object.groupBy([1, 2, 3], (n) => (n % 2 === 0 ? "even" : "odd")); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 39 | + 32 | hint: a string-keyed result (Partial>) is the supported form — a literal-union key type makes a fixed-field record: widen the callback's key type to string (numbers stringify like JS property keys) -es2025-fences.ts:43:19 - error SC2020: 'Set.prototype.union over a set-like argument of type '{ size: number; has: (x: number) => x is 1; keys: () => ArrayIterator; }'' is part of the standard library types but has no scriptc lowering yet +es2025-fences.ts:36:19 - error SC2020: 'Set.prototype.union over a set-like argument of type '{ size: number; has: (x: number) => x is 1; keys: () => ArrayIterator; }'' is part of the standard library types but has no scriptc lowering yet - 42 | const like = { size: 1, has: (x: number) => x === 1, keys: () => [1].values() }; - 43 | const u = s.union(like); + 35 | const like = { size: 1, has: (x: number) => x === 1, keys: () => [1].values() }; + 36 | const u = s.union(like); | ^~~~ - 44 | + 37 | hint: only a real Set or Map lowers (the argument's has/size/keys must be the builtins) — build one first: new Set(...) \ No newline at end of file diff --git a/tests/harness/corpus-costs.json b/tests/harness/corpus-costs.json index a25fd802f..d41c8b6d9 100644 --- a/tests/harness/corpus-costs.json +++ b/tests/harness/corpus-costs.json @@ -1860,6 +1860,8 @@ "5886-inspect-dates.ts": 2559, "5887-inspect-typed-arrays.ts": 3448, "5888-inspect-sorted.ts": 2998, + "5900-float16-array.ts": 3000, + "5901-float16-array-js.js": 2900, "5904-async-generator-completion.ts": 2950, "5905-array-from-async-generator.ts": 2750, "5906-array-from-async-generator-js.js": 2950, @@ -4523,6 +4525,8 @@ "5886-inspect-dates.ts": 2736, "5887-inspect-typed-arrays.ts": 2997, "5888-inspect-sorted.ts": 2861, + "5900-float16-array.ts": 3750, + "5901-float16-array-js.js": 3700, "5904-async-generator-completion.ts": 3800, "5905-array-from-async-generator.ts": 3500, "5906-array-from-async-generator-js.js": 3750,