mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-26 06:34:20 +08:00
Read ThreadSlots values with the layout they are stored with
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
||||
threadSlotsMemberOf,
|
||||
} from "../experimental-import.js";
|
||||
import * as ts from "../ts7/adapter.js";
|
||||
import { withUndefinedArm } from "../type-mapper.js";
|
||||
import type { ClassInfo } from "./lower-classes.js";
|
||||
import type { Lowerer } from "./lowerer.js";
|
||||
import { locOf } from "../program.js";
|
||||
@@ -238,10 +239,16 @@ export function lowerThreadSlotsCall(lowerer: Lowerer, expr: ts.CallExpression):
|
||||
const args = [name, receiver, key];
|
||||
switch (member) {
|
||||
case "get": {
|
||||
// The slots hold values in the layout of the type the constructor registered (`family.value`,
|
||||
// which `set` stores with). The receiver's annotation can name a structurally wider value
|
||||
// type, so the value read is converted to the call's type like any value flowing into it.
|
||||
const resultTs = lowerer.typeOf(expr);
|
||||
const result = lowerer.mapTypeOf(resultTs);
|
||||
if (result === null) lowerer.badType(expr, resultTs);
|
||||
return { kind: "intrinsic", name: "slots.get", args, type: result!, loc };
|
||||
const stored = withUndefinedArm(family.value, lowerer.unions);
|
||||
if (stored === null) lowerer.badType(expr, resultTs);
|
||||
const read: IrExpr = { kind: "intrinsic", name: "slots.get", args, type: stored!, loc };
|
||||
return typeKey(stored!) === typeKey(result!) ? read : lowerer.coerceInto(expr, read, result!);
|
||||
}
|
||||
case "has":
|
||||
case "delete":
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5730-thread-slots-widened-values.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { ThreadSlots } from "@scriptc/experimental";
|
||||
|
||||
// A ThreadSlots const annotated with a structurally wider value type than
|
||||
// its constructor's: the values are stored with the constructor's layout and
|
||||
// read back through the annotation's. Node.js prints the same.
|
||||
|
||||
class Key {
|
||||
readonly id: number;
|
||||
constructor(id: number) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
interface Counted {
|
||||
count: number;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface Labeled {
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface Tagged {
|
||||
label: string;
|
||||
tag?: string;
|
||||
}
|
||||
|
||||
const counted: ThreadSlots<Key, Labeled> = new ThreadSlots<Key, Counted>("widened.counted");
|
||||
const tagged: ThreadSlots<Key, Tagged> = new ThreadSlots<Key, Counted>("widened.tagged");
|
||||
|
||||
const first = new Key(1);
|
||||
const second = new Key(2);
|
||||
const value: Counted = { count: 7, label: "seven" };
|
||||
counted.set(first, value);
|
||||
const read = counted.get(first)!;
|
||||
console.log(read.label, counted.get(second), counted.has(first));
|
||||
|
||||
tagged.set(second, { count: 2, label: "two" } as Counted);
|
||||
const t = tagged.get(second);
|
||||
console.log(t?.label, t?.tag, tagged.get(first));
|
||||
|
||||
function labelOf(slots: Labeled | undefined): string {
|
||||
return slots === undefined ? "none" : slots.label;
|
||||
}
|
||||
console.log(labelOf(counted.get(first)), labelOf(counted.get(second)));
|
||||
@@ -27,3 +27,8 @@ function local(slots: ThreadSlots<Key, State>, key: Key): State | undefined {
|
||||
const ctor = ThreadSlots;
|
||||
const getter = first.get;
|
||||
console.log(named, second, shaped, callbacks, local(first, new Key()), ctor, getter);
|
||||
|
||||
// A read through an annotated value type the stored values can't be converted
|
||||
// to: they are stored as records, which a read as the class can't convert.
|
||||
const records: ThreadSlots<Key, State> = new ThreadSlots<Key, { count: number }>("fences.records");
|
||||
console.log(records.get(new Key()));
|
||||
|
||||
@@ -59,4 +59,13 @@ thread-slots-fences.ts:28:16 - error SC2020: 'ThreadSlots.get (@scriptc/experime
|
||||
| ^~~~~~~~~
|
||||
29 | console.log(named, second, shaped, callbacks, local(first, new Key()), ctor, getter);
|
||||
|
||||
hint: call slots.get(...) directly
|
||||
hint: call slots.get(...) directly
|
||||
|
||||
thread-slots-fences.ts:34:13 - error SC2003: union conversion has no unambiguous layout mapping: expected 'State | undefined', got '{ count: number } | undefined' (each source arm needs an identical destination arm, a unique compatible record/array layout, or a shared literal discriminant selecting compatible layouts)
|
||||
|
||||
33 | const records: ThreadSlots<Key, State> = new ThreadSlots<Key, { count: number }>("fences.records");
|
||||
34 | console.log(records.get(new Key()));
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~
|
||||
35 |
|
||||
|
||||
hint: narrow the value to a SINGLE arm first (a discriminant check, or '!== undefined'/'!== null' for unit arms); a plain arm value widens into any union that contains it, and a whole union widens into any union whose arms include the source's
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 2849, "san": 4164 }
|
||||
Reference in New Issue
Block a user