@scriptc/compiler 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend/emission/emit-exprs.js +25 -2
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-walkers.js +47 -0
- package/dist/backend/emission/emit-walkers.js.map +1 -1
- package/dist/backend/emission/may-throw.js +12 -0
- package/dist/backend/emission/may-throw.js.map +1 -1
- package/dist/backend/llvm/dyn.js +23 -1
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.js +17 -2
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/walkers.d.ts +5 -0
- package/dist/backend/llvm/walkers.js +65 -5
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/frontend/lowering/lower-assert.js +41 -1
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.d.ts +7 -0
- package/dist/frontend/lowering/lower-inspect.js +130 -28
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +1 -1
- package/dist/frontend/lowering/lowerer.js +16 -6
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/types.d.ts +68 -6
- package/dist/frontend/types.js +285 -57
- package/dist/frontend/types.js.map +1 -1
- package/dist/ir/nodes.d.ts +7 -4
- package/dist/ir/nodes.js +37 -13
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +10 -0
- package/dist/ir/validate.js.map +1 -1
- package/package.json +2 -2
- package/surface-manifest.json +1 -1
package/dist/frontend/types.d.ts
CHANGED
|
@@ -19,9 +19,49 @@ export declare class ShapeRegistry {
|
|
|
19
19
|
private readonly byId;
|
|
20
20
|
/** All interned shapes in first-seen (`r0`, `r1`, ...) order. */
|
|
21
21
|
readonly shapes: IrRecordShape[];
|
|
22
|
-
/** ts.Types currently being mapped —
|
|
23
|
-
* (`
|
|
22
|
+
/** ts.Types currently being mapped — a BACK-REFERENCE to one of these is
|
|
23
|
+
* the recursive knot (`interface TreeNode { children: TreeNode[] }`):
|
|
24
|
+
* mapType answers a NAMED RECURSIVE SHAPE (recursiveRef) whose fields
|
|
25
|
+
* fill in when the outer frame completes. */
|
|
24
26
|
readonly inProgress: Set<ts.Type>;
|
|
27
|
+
/** RECURSIVE shape ids, keyed by CHECKER TYPE identity: one shapeId per
|
|
28
|
+
* declaration site (the checker canonicalizes interface/alias types per
|
|
29
|
+
* declaration, and caches generic instantiations, so the same spelling
|
|
30
|
+
* is the same ts.Type). Cross-declaration structural unification exists
|
|
31
|
+
* only through the byKey registration finalizeRecursive performs — a
|
|
32
|
+
* one-level unfolding that references the same knot (`{ next: T }` where
|
|
33
|
+
* `type T = { next: T }`) folds into the knot's id; two INDEPENDENT
|
|
34
|
+
* structurally identical recursive declarations intern as distinct
|
|
35
|
+
* shapes, so values of one fence at the other's slots with the ordinary
|
|
36
|
+
* shape-mismatch diagnostic (tsc admits the assignment; the exact-shape
|
|
37
|
+
* stance reports it — the documented v1 width/assignability consequence
|
|
38
|
+
* of per-declaration identity). */
|
|
39
|
+
private readonly recIds;
|
|
40
|
+
/** Placeholder ids minted but not yet finalized: mid-construction, or
|
|
41
|
+
* permanently pending when the outer mapping failed (such shapes are
|
|
42
|
+
* referenced by nothing reachable and prune at module assembly). */
|
|
43
|
+
private readonly pendingRec;
|
|
44
|
+
/** The interning key of a canonical field list — shared by intern and
|
|
45
|
+
* finalizeRecursive so the two registration paths can never disagree. */
|
|
46
|
+
private keyOf;
|
|
47
|
+
/** The shape id a back-reference to an in-progress type resolves to:
|
|
48
|
+
* reuses the type's persistent recursive id or mints a PLACEHOLDER
|
|
49
|
+
* entry (empty fields) the outer frame finalizes. */
|
|
50
|
+
recursiveRef(t: ts.Type): string;
|
|
51
|
+
/** The FINALIZED recursive shape for a checker type — undefined while
|
|
52
|
+
* never mapped, mid-construction, or permanently failed. */
|
|
53
|
+
recursiveShapeFor(t: ts.Type): string | undefined;
|
|
54
|
+
/** True when a back-reference minted a placeholder for `t` that the
|
|
55
|
+
* outer frame has not (yet) finalized. */
|
|
56
|
+
recursivePending(t: ts.Type): boolean;
|
|
57
|
+
/** Completes a recursive placeholder with its computed field list and
|
|
58
|
+
* registers the structural key (first writer wins — the key may already
|
|
59
|
+
* belong to a structurally identical shape; the recursive id stays
|
|
60
|
+
* authoritative for its own checker type either way). */
|
|
61
|
+
finalizeRecursive(t: ts.Type, fields: {
|
|
62
|
+
name: string;
|
|
63
|
+
type: IrType;
|
|
64
|
+
}[], indexValue?: IrType, declaredOrder?: string[]): string;
|
|
25
65
|
/** Interns a canonical (name-sorted) field list, returning its shapeId.
|
|
26
66
|
* `tuple` shapes (fields "0".."n-1" from a tuple type) intern SEPARATELY
|
|
27
67
|
* from structurally identical numeric-keyed object records: the flag is
|
|
@@ -46,9 +86,30 @@ export declare class UnionRegistry {
|
|
|
46
86
|
private readonly byId;
|
|
47
87
|
/** All interned unions in first-seen (`u0`, `u1`, ...) order. */
|
|
48
88
|
readonly unions: IrUnionDef[];
|
|
49
|
-
/** ts.Types currently being mapped —
|
|
50
|
-
*
|
|
89
|
+
/** ts.Types currently being mapped — a back-reference to one is the
|
|
90
|
+
* recursive knot passing through a union (`type Tree = Leaf | Branch`
|
|
91
|
+
* whose Branch arm carries `Tree[]`, the optional field `a?: A` of a
|
|
92
|
+
* mutually recursive pair): mapType answers a NAMED RECURSIVE UNION
|
|
93
|
+
* (recursiveRef) whose arms fill in when the outer frame completes. */
|
|
51
94
|
readonly inProgress: Set<ts.Type>;
|
|
95
|
+
/** Recursive union ids, keyed by checker type identity — the
|
|
96
|
+
* ShapeRegistry.recIds story exactly (per-declaration identity, byKey
|
|
97
|
+
* folding one-level unfoldings in). */
|
|
98
|
+
private readonly recIds;
|
|
99
|
+
private readonly pendingRec;
|
|
100
|
+
/** The union id a back-reference to an in-progress union resolves to:
|
|
101
|
+
* reuses the type's persistent recursive id or mints a PLACEHOLDER
|
|
102
|
+
* entry (empty arms) the outer frame finalizes. */
|
|
103
|
+
recursiveRef(t: ts.Type): string;
|
|
104
|
+
/** The FINALIZED recursive union for a checker type — undefined while
|
|
105
|
+
* never mapped, mid-construction, or permanently failed. */
|
|
106
|
+
recursiveUnionFor(t: ts.Type): string | undefined;
|
|
107
|
+
/** True when a back-reference minted a placeholder for `t` that the
|
|
108
|
+
* outer frame has not (yet) finalized. */
|
|
109
|
+
recursivePending(t: ts.Type): boolean;
|
|
110
|
+
/** Completes a recursive placeholder with its canonical arm list and
|
|
111
|
+
* registers the structural key (first writer wins, like shapes). */
|
|
112
|
+
finalizeRecursive(t: ts.Type, arms: IrType[]): string;
|
|
52
113
|
/** Interns a canonical (typeKey-sorted, deduplicated) arm list, returning
|
|
53
114
|
* its unionId. */
|
|
54
115
|
intern(arms: IrType[]): string;
|
|
@@ -56,8 +117,9 @@ export declare class UnionRegistry {
|
|
|
56
117
|
}
|
|
57
118
|
/** Human-readable rendering of an IrType for diagnostics (records expand to
|
|
58
119
|
* their canonical field list, unions to their arms; `checker.typeToString`
|
|
59
|
-
* can't — it never sees IR types).
|
|
60
|
-
|
|
120
|
+
* can't — it never sees IR types). `seen` breaks recursive shapes/unions:
|
|
121
|
+
* a back-reference renders as "..." instead of expanding forever. */
|
|
122
|
+
export declare function formatIrType(t: IrType, shapes: ShapeRegistry, unions: UnionRegistry, seen?: Set<string>): string;
|
|
61
123
|
/** True when the declaration sits inside `declare module "<name>"` (or
|
|
62
124
|
* its node:-prefixed twin) — the disambiguator for builtin-module type
|
|
63
125
|
* names that repeat across modules (net.Server vs http.Server). Both
|
package/dist/frontend/types.js
CHANGED
|
@@ -23,9 +23,84 @@ export class ShapeRegistry {
|
|
|
23
23
|
byId = new Map();
|
|
24
24
|
/** All interned shapes in first-seen (`r0`, `r1`, ...) order. */
|
|
25
25
|
shapes = [];
|
|
26
|
-
/** ts.Types currently being mapped —
|
|
27
|
-
* (`
|
|
26
|
+
/** ts.Types currently being mapped — a BACK-REFERENCE to one of these is
|
|
27
|
+
* the recursive knot (`interface TreeNode { children: TreeNode[] }`):
|
|
28
|
+
* mapType answers a NAMED RECURSIVE SHAPE (recursiveRef) whose fields
|
|
29
|
+
* fill in when the outer frame completes. */
|
|
28
30
|
inProgress = new Set();
|
|
31
|
+
/** RECURSIVE shape ids, keyed by CHECKER TYPE identity: one shapeId per
|
|
32
|
+
* declaration site (the checker canonicalizes interface/alias types per
|
|
33
|
+
* declaration, and caches generic instantiations, so the same spelling
|
|
34
|
+
* is the same ts.Type). Cross-declaration structural unification exists
|
|
35
|
+
* only through the byKey registration finalizeRecursive performs — a
|
|
36
|
+
* one-level unfolding that references the same knot (`{ next: T }` where
|
|
37
|
+
* `type T = { next: T }`) folds into the knot's id; two INDEPENDENT
|
|
38
|
+
* structurally identical recursive declarations intern as distinct
|
|
39
|
+
* shapes, so values of one fence at the other's slots with the ordinary
|
|
40
|
+
* shape-mismatch diagnostic (tsc admits the assignment; the exact-shape
|
|
41
|
+
* stance reports it — the documented v1 width/assignability consequence
|
|
42
|
+
* of per-declaration identity). */
|
|
43
|
+
recIds = new Map();
|
|
44
|
+
/** Placeholder ids minted but not yet finalized: mid-construction, or
|
|
45
|
+
* permanently pending when the outer mapping failed (such shapes are
|
|
46
|
+
* referenced by nothing reachable and prune at module assembly). */
|
|
47
|
+
pendingRec = new Set();
|
|
48
|
+
/** The interning key of a canonical field list — shared by intern and
|
|
49
|
+
* finalizeRecursive so the two registration paths can never disagree. */
|
|
50
|
+
keyOf(fields, tuple, indexValue) {
|
|
51
|
+
return ((tuple ? "tuple!" : "") +
|
|
52
|
+
(indexValue ? `idx<${typeKey(indexValue)}>!` : "") +
|
|
53
|
+
JSON.stringify(fields.map((f) => [f.name, typeKey(f.type)])));
|
|
54
|
+
}
|
|
55
|
+
/** The shape id a back-reference to an in-progress type resolves to:
|
|
56
|
+
* reuses the type's persistent recursive id or mints a PLACEHOLDER
|
|
57
|
+
* entry (empty fields) the outer frame finalizes. */
|
|
58
|
+
recursiveRef(t) {
|
|
59
|
+
let id = this.recIds.get(t);
|
|
60
|
+
if (id === undefined) {
|
|
61
|
+
id = `r${this.shapes.length}`;
|
|
62
|
+
const shape = { id, fields: [] };
|
|
63
|
+
this.byId.set(id, shape);
|
|
64
|
+
this.shapes.push(shape);
|
|
65
|
+
this.recIds.set(t, id);
|
|
66
|
+
this.pendingRec.add(id);
|
|
67
|
+
}
|
|
68
|
+
return id;
|
|
69
|
+
}
|
|
70
|
+
/** The FINALIZED recursive shape for a checker type — undefined while
|
|
71
|
+
* never mapped, mid-construction, or permanently failed. */
|
|
72
|
+
recursiveShapeFor(t) {
|
|
73
|
+
const id = this.recIds.get(t);
|
|
74
|
+
return id !== undefined && !this.pendingRec.has(id) ? id : undefined;
|
|
75
|
+
}
|
|
76
|
+
/** True when a back-reference minted a placeholder for `t` that the
|
|
77
|
+
* outer frame has not (yet) finalized. */
|
|
78
|
+
recursivePending(t) {
|
|
79
|
+
const id = this.recIds.get(t);
|
|
80
|
+
return id !== undefined && this.pendingRec.has(id);
|
|
81
|
+
}
|
|
82
|
+
/** Completes a recursive placeholder with its computed field list and
|
|
83
|
+
* registers the structural key (first writer wins — the key may already
|
|
84
|
+
* belong to a structurally identical shape; the recursive id stays
|
|
85
|
+
* authoritative for its own checker type either way). */
|
|
86
|
+
finalizeRecursive(t, fields, indexValue, declaredOrder) {
|
|
87
|
+
const id = this.recIds.get(t);
|
|
88
|
+
if (id === undefined)
|
|
89
|
+
throw new Error("shape registry bug: finalizeRecursive without a placeholder");
|
|
90
|
+
if (this.pendingRec.has(id)) {
|
|
91
|
+
const shape = this.byId.get(id);
|
|
92
|
+
shape.fields = fields;
|
|
93
|
+
if (indexValue)
|
|
94
|
+
shape.indexValue = indexValue;
|
|
95
|
+
if (declaredOrder)
|
|
96
|
+
shape.declaredOrder = declaredOrder;
|
|
97
|
+
this.pendingRec.delete(id);
|
|
98
|
+
const key = this.keyOf(fields, false, indexValue);
|
|
99
|
+
if (!this.byKey.has(key))
|
|
100
|
+
this.byKey.set(key, id);
|
|
101
|
+
}
|
|
102
|
+
return id;
|
|
103
|
+
}
|
|
29
104
|
/** Interns a canonical (name-sorted) field list, returning its shapeId.
|
|
30
105
|
* `tuple` shapes (fields "0".."n-1" from a tuple type) intern SEPARATELY
|
|
31
106
|
* from structurally identical numeric-keyed object records: the flag is
|
|
@@ -35,9 +110,7 @@ export class ShapeRegistry {
|
|
|
35
110
|
* declared fields with and without an overflow portion — or with
|
|
36
111
|
* differently-typed ones — are different structs. */
|
|
37
112
|
intern(fields, tuple = false, indexValue, declaredOrder) {
|
|
38
|
-
const key = (
|
|
39
|
-
(indexValue ? `idx<${typeKey(indexValue)}>!` : "") +
|
|
40
|
-
JSON.stringify(fields.map((f) => [f.name, typeKey(f.type)]));
|
|
113
|
+
const key = this.keyOf(fields, tuple, indexValue);
|
|
41
114
|
let id = this.byKey.get(key);
|
|
42
115
|
if (id === undefined) {
|
|
43
116
|
id = `r${this.shapes.length}`;
|
|
@@ -71,9 +144,60 @@ export class UnionRegistry {
|
|
|
71
144
|
byId = new Map();
|
|
72
145
|
/** All interned unions in first-seen (`u0`, `u1`, ...) order. */
|
|
73
146
|
unions = [];
|
|
74
|
-
/** ts.Types currently being mapped —
|
|
75
|
-
*
|
|
147
|
+
/** ts.Types currently being mapped — a back-reference to one is the
|
|
148
|
+
* recursive knot passing through a union (`type Tree = Leaf | Branch`
|
|
149
|
+
* whose Branch arm carries `Tree[]`, the optional field `a?: A` of a
|
|
150
|
+
* mutually recursive pair): mapType answers a NAMED RECURSIVE UNION
|
|
151
|
+
* (recursiveRef) whose arms fill in when the outer frame completes. */
|
|
76
152
|
inProgress = new Set();
|
|
153
|
+
/** Recursive union ids, keyed by checker type identity — the
|
|
154
|
+
* ShapeRegistry.recIds story exactly (per-declaration identity, byKey
|
|
155
|
+
* folding one-level unfoldings in). */
|
|
156
|
+
recIds = new Map();
|
|
157
|
+
pendingRec = new Set();
|
|
158
|
+
/** The union id a back-reference to an in-progress union resolves to:
|
|
159
|
+
* reuses the type's persistent recursive id or mints a PLACEHOLDER
|
|
160
|
+
* entry (empty arms) the outer frame finalizes. */
|
|
161
|
+
recursiveRef(t) {
|
|
162
|
+
let id = this.recIds.get(t);
|
|
163
|
+
if (id === undefined) {
|
|
164
|
+
id = `u${this.unions.length}`;
|
|
165
|
+
const def = { id, arms: [] };
|
|
166
|
+
this.byId.set(id, def);
|
|
167
|
+
this.unions.push(def);
|
|
168
|
+
this.recIds.set(t, id);
|
|
169
|
+
this.pendingRec.add(id);
|
|
170
|
+
}
|
|
171
|
+
return id;
|
|
172
|
+
}
|
|
173
|
+
/** The FINALIZED recursive union for a checker type — undefined while
|
|
174
|
+
* never mapped, mid-construction, or permanently failed. */
|
|
175
|
+
recursiveUnionFor(t) {
|
|
176
|
+
const id = this.recIds.get(t);
|
|
177
|
+
return id !== undefined && !this.pendingRec.has(id) ? id : undefined;
|
|
178
|
+
}
|
|
179
|
+
/** True when a back-reference minted a placeholder for `t` that the
|
|
180
|
+
* outer frame has not (yet) finalized. */
|
|
181
|
+
recursivePending(t) {
|
|
182
|
+
const id = this.recIds.get(t);
|
|
183
|
+
return id !== undefined && this.pendingRec.has(id);
|
|
184
|
+
}
|
|
185
|
+
/** Completes a recursive placeholder with its canonical arm list and
|
|
186
|
+
* registers the structural key (first writer wins, like shapes). */
|
|
187
|
+
finalizeRecursive(t, arms) {
|
|
188
|
+
const id = this.recIds.get(t);
|
|
189
|
+
if (id === undefined)
|
|
190
|
+
throw new Error("union registry bug: finalizeRecursive without a placeholder");
|
|
191
|
+
if (this.pendingRec.has(id)) {
|
|
192
|
+
const def = this.byId.get(id);
|
|
193
|
+
def.arms.push(...arms);
|
|
194
|
+
this.pendingRec.delete(id);
|
|
195
|
+
const key = JSON.stringify(arms.map(typeKey));
|
|
196
|
+
if (!this.byKey.has(key))
|
|
197
|
+
this.byKey.set(key, id);
|
|
198
|
+
}
|
|
199
|
+
return id;
|
|
200
|
+
}
|
|
77
201
|
/** Interns a canonical (typeKey-sorted, deduplicated) arm list, returning
|
|
78
202
|
* its unionId. */
|
|
79
203
|
intern(arms) {
|
|
@@ -94,8 +218,9 @@ export class UnionRegistry {
|
|
|
94
218
|
}
|
|
95
219
|
/** Human-readable rendering of an IrType for diagnostics (records expand to
|
|
96
220
|
* their canonical field list, unions to their arms; `checker.typeToString`
|
|
97
|
-
* can't — it never sees IR types).
|
|
98
|
-
|
|
221
|
+
* can't — it never sees IR types). `seen` breaks recursive shapes/unions:
|
|
222
|
+
* a back-reference renders as "..." instead of expanding forever. */
|
|
223
|
+
export function formatIrType(t, shapes, unions, seen = new Set()) {
|
|
99
224
|
switch (t.kind) {
|
|
100
225
|
case "f64":
|
|
101
226
|
return "number";
|
|
@@ -117,7 +242,7 @@ export function formatIrType(t, shapes, unions) {
|
|
|
117
242
|
case "array": {
|
|
118
243
|
// Union/func elements need the parens TS syntax would ("(number |
|
|
119
244
|
// string)[]" — without them the [] reads as binding to the last arm).
|
|
120
|
-
const elem = formatIrType(t.elem, shapes, unions);
|
|
245
|
+
const elem = formatIrType(t.elem, shapes, unions, seen);
|
|
121
246
|
return t.elem.kind === "union" || t.elem.kind === "func" ? `(${elem})[]` : `${elem}[]`;
|
|
122
247
|
}
|
|
123
248
|
case "bytes":
|
|
@@ -125,11 +250,11 @@ export function formatIrType(t, shapes, unions) {
|
|
|
125
250
|
// runtime representation; the message stays honest either way).
|
|
126
251
|
return t.elem === "u8" ? "Uint8Array" : t.elem === "u32" ? "Uint32Array" : t.elem === "i32" ? "Int32Array" : "Float32Array";
|
|
127
252
|
case "map":
|
|
128
|
-
return `Map<${formatIrType(t.key, shapes, unions)}, ${formatIrType(t.value, shapes, unions)}>`;
|
|
253
|
+
return `Map<${formatIrType(t.key, shapes, unions, seen)}, ${formatIrType(t.value, shapes, unions, seen)}>`;
|
|
129
254
|
case "set":
|
|
130
|
-
return `Set<${formatIrType(t.elem, shapes, unions)}>`;
|
|
255
|
+
return `Set<${formatIrType(t.elem, shapes, unions, seen)}>`;
|
|
131
256
|
case "func":
|
|
132
|
-
return `(${t.params.map((p) => formatIrType(p, shapes, unions)).join(", ")}) => ${formatIrType(t.ret, shapes, unions)}`;
|
|
257
|
+
return `(${t.params.map((p) => formatIrType(p, shapes, unions, seen)).join(", ")}) => ${formatIrType(t.ret, shapes, unions, seen)}`;
|
|
133
258
|
case "object":
|
|
134
259
|
// Runtime-provided error classes carry '%'-prefixed IR names
|
|
135
260
|
// ("%Error") so user classes can never collide; diagnostics show the
|
|
@@ -142,33 +267,49 @@ export function formatIrType(t, shapes, unions) {
|
|
|
142
267
|
const shape = shapes.get(t.shapeId);
|
|
143
268
|
if (!shape)
|
|
144
269
|
return `{ /* unknown shape ${t.shapeId} */ }`;
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const slot = accessorSlotProp(f.name);
|
|
153
|
-
if (slot && f.type.kind === "func") {
|
|
154
|
-
return slot.kind === "get"
|
|
155
|
-
? `get ${slot.prop}(): ${formatIrType(f.type.ret, shapes, unions)}`
|
|
156
|
-
: `set ${slot.prop}(${formatIrType(f.type.params[0] ?? VOID, shapes, unions)})`;
|
|
270
|
+
if (seen.has(t.shapeId))
|
|
271
|
+
return "..."; // the recursive knot
|
|
272
|
+
seen.add(t.shapeId);
|
|
273
|
+
try {
|
|
274
|
+
if (shape.tuple) {
|
|
275
|
+
const byIndex = [...shape.fields].sort((a, b) => Number(a.name) - Number(b.name));
|
|
276
|
+
return `[${byIndex.map((f) => formatIrType(f.type, shapes, unions, seen)).join(", ")}]`;
|
|
157
277
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
278
|
+
const members = shape.fields.map((f) => {
|
|
279
|
+
// Accessor slots print in TS's accessor spelling, not the
|
|
280
|
+
// reserved '%'-field encoding.
|
|
281
|
+
const slot = accessorSlotProp(f.name);
|
|
282
|
+
if (slot && f.type.kind === "func") {
|
|
283
|
+
return slot.kind === "get"
|
|
284
|
+
? `get ${slot.prop}(): ${formatIrType(f.type.ret, shapes, unions, seen)}`
|
|
285
|
+
: `set ${slot.prop}(${formatIrType(f.type.params[0] ?? VOID, shapes, unions, seen)})`;
|
|
286
|
+
}
|
|
287
|
+
return `${f.name}: ${formatIrType(f.type, shapes, unions, seen)}`;
|
|
288
|
+
});
|
|
289
|
+
if (shape.indexValue) {
|
|
290
|
+
members.push(`[key: string]: ${formatIrType(shape.indexValue, shapes, unions, seen)}`);
|
|
291
|
+
}
|
|
292
|
+
if (members.length === 0)
|
|
293
|
+
return "{}";
|
|
294
|
+
return `{ ${members.join("; ")} }`;
|
|
295
|
+
}
|
|
296
|
+
finally {
|
|
297
|
+
seen.delete(t.shapeId); // sibling occurrences still expand
|
|
162
298
|
}
|
|
163
|
-
if (members.length === 0)
|
|
164
|
-
return "{}";
|
|
165
|
-
return `{ ${members.join("; ")} }`;
|
|
166
299
|
}
|
|
167
300
|
case "union": {
|
|
168
301
|
const def = unions.get(t.unionId);
|
|
169
302
|
if (!def)
|
|
170
303
|
return `/* union ${t.unionId} */`;
|
|
171
|
-
|
|
304
|
+
if (seen.has(t.unionId))
|
|
305
|
+
return "..."; // the recursive knot
|
|
306
|
+
seen.add(t.unionId);
|
|
307
|
+
try {
|
|
308
|
+
return def.arms.map((a) => formatIrType(a, shapes, unions, seen)).join(" | ");
|
|
309
|
+
}
|
|
310
|
+
finally {
|
|
311
|
+
seen.delete(t.unionId);
|
|
312
|
+
}
|
|
172
313
|
}
|
|
173
314
|
case "jsval":
|
|
174
315
|
return "any";
|
|
@@ -213,9 +354,9 @@ export function formatIrType(t, shapes, unions) {
|
|
|
213
354
|
case "procStream":
|
|
214
355
|
return "WriteStream";
|
|
215
356
|
case "promise":
|
|
216
|
-
return `Promise<${formatIrType(t.inner, shapes, unions)}>`;
|
|
357
|
+
return `Promise<${formatIrType(t.inner, shapes, unions, seen)}>`;
|
|
217
358
|
case "generator":
|
|
218
|
-
return `Generator<${formatIrType(t.yieldT, shapes, unions)}, ${formatIrType(t.retT, shapes, unions)}, ${formatIrType(t.nextT, shapes, unions)}>`;
|
|
359
|
+
return `Generator<${formatIrType(t.yieldT, shapes, unions, seen)}, ${formatIrType(t.retT, shapes, unions, seen)}, ${formatIrType(t.nextT, shapes, unions, seen)}>`;
|
|
219
360
|
default: {
|
|
220
361
|
const _exhaustive = t;
|
|
221
362
|
void _exhaustive;
|
|
@@ -378,6 +519,14 @@ function genChannels(yieldTs, retTs, nextTs, ctx) {
|
|
|
378
519
|
* fallback for it. */
|
|
379
520
|
const MAP_TYPE_MAX_DEPTH = 64;
|
|
380
521
|
let mapTypeDepth = 0;
|
|
522
|
+
/** Bumped whenever mapType resolves a type through CONTEXT-SENSITIVE hooks
|
|
523
|
+
* (a generic body's type parameter, a mixin instantiation) — mappings that
|
|
524
|
+
* make the same ts.Type answer differently across instantiation contexts.
|
|
525
|
+
* The recursive-shape machinery keys shape identity by checker type, which
|
|
526
|
+
* is sound only for context-FREE mappings: a recursive frame that observes
|
|
527
|
+
* a bump between entry and exit stays fenced (recursive generic-open types
|
|
528
|
+
* report SC2001 instead of interning a per-context-wrong shape). */
|
|
529
|
+
let contextResolutions = 0;
|
|
381
530
|
export function mapType(type, ctx) {
|
|
382
531
|
if (mapTypeDepth >= MAP_TYPE_MAX_DEPTH)
|
|
383
532
|
return null;
|
|
@@ -408,8 +557,10 @@ function mapTypeInner(type, ctx) {
|
|
|
408
557
|
const { checker, unions, classNamer, resolveTypeParam } = ctx;
|
|
409
558
|
if (resolveTypeParam && type.flags & ts.TypeFlags.TypeParameter) {
|
|
410
559
|
const bound = resolveTypeParam(type);
|
|
411
|
-
if (bound)
|
|
560
|
+
if (bound) {
|
|
561
|
+
contextResolutions++;
|
|
412
562
|
return bound;
|
|
563
|
+
}
|
|
413
564
|
}
|
|
414
565
|
// Narrowed type parameters: `!== undefined` / `!== null` / truthiness on
|
|
415
566
|
// a `T | undefined`-flavored value inside a generic body leaves the
|
|
@@ -420,15 +571,19 @@ function mapTypeInner(type, ctx) {
|
|
|
420
571
|
// the mapping is the binding filtered to what the narrowing kept.
|
|
421
572
|
if (resolveTypeParam) {
|
|
422
573
|
const viaNarrowedParam = mapNarrowedTypeParam(type, ctx);
|
|
423
|
-
if (viaNarrowedParam !== undefined)
|
|
574
|
+
if (viaNarrowedParam !== undefined) {
|
|
575
|
+
contextResolutions++;
|
|
424
576
|
return viaNarrowedParam;
|
|
577
|
+
}
|
|
425
578
|
// `Awaited<T>` over a bound type parameter (an async generic body's
|
|
426
579
|
// `await fn()` result): a CONDITIONAL type the checker keeps symbolic
|
|
427
580
|
// inside the body — the object-gated utility-alias hook below never
|
|
428
581
|
// sees it, so it resolves here, before the flag dispatch.
|
|
429
582
|
const viaAwaited = mapGenericAwaitedAlias(type, ctx);
|
|
430
|
-
if (viaAwaited !== null)
|
|
583
|
+
if (viaAwaited !== null) {
|
|
584
|
+
contextResolutions++;
|
|
431
585
|
return viaAwaited;
|
|
586
|
+
}
|
|
432
587
|
}
|
|
433
588
|
const widened = checker.getBaseTypeOfLiteralType(type);
|
|
434
589
|
const flags = widened.flags;
|
|
@@ -770,8 +925,10 @@ function mapTypeInner(type, ctx) {
|
|
|
770
925
|
// pinned instantiation; ambiguity stays unmapped (the hook's rules).
|
|
771
926
|
{
|
|
772
927
|
const viaMixin = ctx.mixinIntersectionInstance?.(widened);
|
|
773
|
-
if (viaMixin)
|
|
928
|
+
if (viaMixin) {
|
|
929
|
+
contextResolutions++;
|
|
774
930
|
return viaMixin;
|
|
931
|
+
}
|
|
775
932
|
}
|
|
776
933
|
}
|
|
777
934
|
// symbol, but with construct signatures — that is the STATIC side, and
|
|
@@ -797,8 +954,10 @@ function mapTypeInner(type, ctx) {
|
|
|
797
954
|
// stays unmapped (nothing names a call site).
|
|
798
955
|
{
|
|
799
956
|
const viaMixin = ctx.mixinClassInstance?.(classDecl);
|
|
800
|
-
if (viaMixin)
|
|
957
|
+
if (viaMixin) {
|
|
958
|
+
contextResolutions++;
|
|
801
959
|
return viaMixin;
|
|
960
|
+
}
|
|
802
961
|
}
|
|
803
962
|
// A class expression inside a function/static block never registers
|
|
804
963
|
// (a distinct class per evaluation): unmappable for the same reason.
|
|
@@ -825,8 +984,10 @@ function mapTypeInner(type, ctx) {
|
|
|
825
984
|
// the current instantiation's classval, like the instance type above.
|
|
826
985
|
{
|
|
827
986
|
const viaMixin = ctx.mixinClassInstance?.(classDecl);
|
|
828
|
-
if (viaMixin?.kind === "object")
|
|
987
|
+
if (viaMixin?.kind === "object") {
|
|
988
|
+
contextResolutions++;
|
|
829
989
|
return { kind: "classval", className: viaMixin.className };
|
|
990
|
+
}
|
|
830
991
|
}
|
|
831
992
|
if (classExprNeverRegisters(classDecl))
|
|
832
993
|
return null;
|
|
@@ -1811,12 +1972,21 @@ function mapTypeInner(type, ctx) {
|
|
|
1811
1972
|
// union membership is the only position where the units are
|
|
1812
1973
|
// representable. Deliberately unmapped: any other unmappable part
|
|
1813
1974
|
// (poisons the union), void parts, func arms (closure identity vs tag
|
|
1814
|
-
// interplay, later)
|
|
1815
|
-
//
|
|
1975
|
+
// interplay, later). RECURSIVE unions (`type Tree = Leaf | { children:
|
|
1976
|
+
// Tree[] }`, the optional-field spelling `a?: A` of a mutually recursive
|
|
1977
|
+
// pair) map as NAMED RECURSIVE UNIONS: a back-reference to an in-progress
|
|
1978
|
+
// union answers a placeholder id whose arms fill in when this frame
|
|
1979
|
+
// completes — the ShapeRegistry knot, mirrored (identity per checker
|
|
1980
|
+
// type; context-sensitive frames stay fenced).
|
|
1816
1981
|
if (widened.isUnionType()) {
|
|
1817
|
-
|
|
1818
|
-
|
|
1982
|
+
const knownRecursive = unions.recursiveUnionFor(widened);
|
|
1983
|
+
if (knownRecursive !== undefined)
|
|
1984
|
+
return { kind: "union", unionId: knownRecursive };
|
|
1985
|
+
if (unions.inProgress.has(widened)) {
|
|
1986
|
+
return { kind: "union", unionId: unions.recursiveRef(widened) };
|
|
1987
|
+
}
|
|
1819
1988
|
unions.inProgress.add(widened);
|
|
1989
|
+
const sensitivityAtEntry = contextResolutions;
|
|
1820
1990
|
try {
|
|
1821
1991
|
const byKey = new Map();
|
|
1822
1992
|
for (const part of widened.getTypes()) {
|
|
@@ -1859,11 +2029,17 @@ function mapTypeInner(type, ctx) {
|
|
|
1859
2029
|
const arms = [...byKey.values()];
|
|
1860
2030
|
// A single surviving UNIT arm cannot stand alone (degenerate — the
|
|
1861
2031
|
// checker collapsed everything else away); anything else single is
|
|
1862
|
-
// just that type.
|
|
2032
|
+
// just that type. A single arm UNDER A MINTED PLACEHOLDER cannot
|
|
2033
|
+
// stand either: back-references already handed out the union id, and
|
|
2034
|
+
// a one-armed union has no representation — fence the degenerate
|
|
2035
|
+
// recursive spelling honestly.
|
|
1863
2036
|
if (arms.length === 1 && isUnitType(arms[0]))
|
|
1864
2037
|
return null;
|
|
1865
|
-
if (arms.length === 1)
|
|
2038
|
+
if (arms.length === 1) {
|
|
2039
|
+
if (unions.recursivePending(widened))
|
|
2040
|
+
return null;
|
|
1866
2041
|
return arms[0];
|
|
2042
|
+
}
|
|
1867
2043
|
if (arms.some((a) => a.kind === "void" || a.kind === "union" ||
|
|
1868
2044
|
// Map/Set and regex arms stay out (like func against data
|
|
1869
2045
|
// arms: no narrowing test — no discriminant fields on them).
|
|
@@ -1885,6 +2061,16 @@ function mapTypeInner(type, ctx) {
|
|
|
1885
2061
|
return null;
|
|
1886
2062
|
}
|
|
1887
2063
|
arms.sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
|
|
2064
|
+
if (unions.recursivePending(widened)) {
|
|
2065
|
+
// The knot closed through this union. A frame that resolved
|
|
2066
|
+
// through context-sensitive hooks (generic type parameters, mixin
|
|
2067
|
+
// instantiations) cannot intern by checker-type identity — the
|
|
2068
|
+
// same ts.Type answers differently per instantiation — so
|
|
2069
|
+
// recursive generic-open unions stay fenced.
|
|
2070
|
+
if (contextResolutions !== sensitivityAtEntry)
|
|
2071
|
+
return null;
|
|
2072
|
+
return { kind: "union", unionId: unions.finalizeRecursive(widened, arms) };
|
|
2073
|
+
}
|
|
1888
2074
|
return { kind: "union", unionId: unions.intern(arms) };
|
|
1889
2075
|
}
|
|
1890
2076
|
finally {
|
|
@@ -2304,6 +2490,56 @@ export function isGenericCallableMemberType(t, checker) {
|
|
|
2304
2490
|
checker.getIndexInfosOfType(t).length === 0);
|
|
2305
2491
|
}
|
|
2306
2492
|
function mapRecordType(widened, ctx) {
|
|
2493
|
+
const { shapes } = ctx;
|
|
2494
|
+
// A type whose recursive shape already finalized answers its id straight
|
|
2495
|
+
// off the registry (identity is per checker type; remapping would walk
|
|
2496
|
+
// the same members to the same answer).
|
|
2497
|
+
const knownRecursive = shapes.recursiveShapeFor(widened);
|
|
2498
|
+
if (knownRecursive !== undefined)
|
|
2499
|
+
return { kind: "record", shapeId: knownRecursive };
|
|
2500
|
+
// A BACK-REFERENCE to a shape currently being mapped (`interface
|
|
2501
|
+
// TreeNode { label: string; children: TreeNode[] }` reaching TreeNode
|
|
2502
|
+
// through its own field walk — directly or through arrays, unions,
|
|
2503
|
+
// optionals, Maps): the recursive knot. Answer a NAMED RECURSIVE SHAPE —
|
|
2504
|
+
// a placeholder shape-table entry whose fields fill in when the outer
|
|
2505
|
+
// frame completes (finalizeRecursive below). Backends represent records
|
|
2506
|
+
// as heap references, so the self-reference is an ordinary pointer.
|
|
2507
|
+
if (shapes.inProgress.has(widened)) {
|
|
2508
|
+
return { kind: "record", shapeId: shapes.recursiveRef(widened) };
|
|
2509
|
+
}
|
|
2510
|
+
shapes.inProgress.add(widened);
|
|
2511
|
+
const sensitivityAtEntry = contextResolutions;
|
|
2512
|
+
try {
|
|
2513
|
+
const inner = mapRecordTypeInner(widened, ctx);
|
|
2514
|
+
if (inner === null)
|
|
2515
|
+
return null; // a pending placeholder, if minted, stays unfinalized (prunes as unreachable)
|
|
2516
|
+
if (!("fields" in inner)) {
|
|
2517
|
+
// A whole-type answer (the jsval/dyn absorbs, the header-family
|
|
2518
|
+
// canonical shape). If a back-reference minted a placeholder for
|
|
2519
|
+
// this very type meanwhile, the two answers disagree — fence rather
|
|
2520
|
+
// than leave references to a shape that is not this type's mapping.
|
|
2521
|
+
if (shapes.recursivePending(widened) && inner.kind !== "jsval" && inner.kind !== "dyn")
|
|
2522
|
+
return null;
|
|
2523
|
+
return inner;
|
|
2524
|
+
}
|
|
2525
|
+
if (shapes.recursivePending(widened)) {
|
|
2526
|
+
// The knot closed through this shape. Context-sensitive frames
|
|
2527
|
+
// (generic type parameters, mixin instantiations) cannot intern by
|
|
2528
|
+
// checker-type identity — fence recursive generic-open shapes.
|
|
2529
|
+
if (contextResolutions !== sensitivityAtEntry)
|
|
2530
|
+
return null;
|
|
2531
|
+
return {
|
|
2532
|
+
kind: "record",
|
|
2533
|
+
shapeId: shapes.finalizeRecursive(widened, inner.fields, inner.indexValue, inner.declaredOrder),
|
|
2534
|
+
};
|
|
2535
|
+
}
|
|
2536
|
+
return { kind: "record", shapeId: shapes.intern(inner.fields, false, inner.indexValue, inner.declaredOrder) };
|
|
2537
|
+
}
|
|
2538
|
+
finally {
|
|
2539
|
+
shapes.inProgress.delete(widened);
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
function mapRecordTypeInner(widened, ctx) {
|
|
2307
2543
|
const { checker, shapes } = ctx;
|
|
2308
2544
|
if (checker.getConstructSignatures(widened).length > 0)
|
|
2309
2545
|
return null;
|
|
@@ -2427,12 +2663,7 @@ function mapRecordType(widened, ctx) {
|
|
|
2427
2663
|
// Checker-computed shapes (no user declaration) need two extra fences in
|
|
2428
2664
|
// the member walk below; see the comments there.
|
|
2429
2665
|
const computed = widened.isIntersectionType() || isMappedShape(widened);
|
|
2430
|
-
|
|
2431
|
-
// cycles anyway): break the recursion by failing the inner reference.
|
|
2432
|
-
if (shapes.inProgress.has(widened))
|
|
2433
|
-
return null;
|
|
2434
|
-
shapes.inProgress.add(widened);
|
|
2435
|
-
try {
|
|
2666
|
+
{
|
|
2436
2667
|
const props = checker.getPropertiesOfType(widened);
|
|
2437
2668
|
// A computed shape with NO members is not a real empty record: inside a
|
|
2438
2669
|
// generic body `Partial<T>` resolves to no members at all (keyof T is
|
|
@@ -2590,10 +2821,7 @@ function mapRecordType(widened, ctx) {
|
|
|
2590
2821
|
// (OrdinaryOwnPropertyKeys) — Object.keys/JSON/inspect all read it.
|
|
2591
2822
|
const declaredOrder = esOwnKeyOrder(fields.filter((f) => accessorSlotProp(f.name) === null).map((f) => f.name));
|
|
2592
2823
|
fields.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
2593
|
-
return {
|
|
2594
|
-
}
|
|
2595
|
-
finally {
|
|
2596
|
-
shapes.inProgress.delete(widened);
|
|
2824
|
+
return { fields, ...(indexValue ? { indexValue } : {}), declaredOrder };
|
|
2597
2825
|
}
|
|
2598
2826
|
}
|
|
2599
2827
|
//# sourceMappingURL=types.js.map
|