@scriptc/compiler 0.0.7 → 0.0.8
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 +47 -1
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-island.js +9 -1
- package/dist/backend/emission/emit-island.js.map +1 -1
- package/dist/backend/emission/emit-stmts.js +3 -1
- package/dist/backend/emission/emit-stmts.js.map +1 -1
- package/dist/backend/emission/emit-walkers.js +22 -8
- package/dist/backend/emission/emit-walkers.js.map +1 -1
- package/dist/backend/emission/emitter.js +5 -1
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/emission/may-throw.js +4 -3
- package/dist/backend/emission/may-throw.js.map +1 -1
- package/dist/backend/llvm/emitter.js +94 -7
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +43 -1
- package/dist/diagnostics/diagnostic.js +103 -5
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/frontend/lib-contract.d.ts +24 -6
- package/dist/frontend/lib-contract.js +125 -44
- package/dist/frontend/lib-contract.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.js +16 -1
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +47 -7
- package/dist/frontend/lowering/lower-calls.js +560 -36
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +14 -1
- package/dist/frontend/lowering/lower-classes.js +85 -15
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.js +43 -1
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-emitter.js +1 -1
- package/dist/frontend/lowering/lower-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +26 -3
- package/dist/frontend/lowering/lower-exprs.js +455 -40
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +7 -0
- package/dist/frontend/lowering/lower-island.js +71 -10
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-mixins.d.ts +1 -1
- package/dist/frontend/lowering/lower-mixins.js +1 -1
- package/dist/frontend/lowering/lower-modules.js +95 -4
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +421 -43
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +19 -3
- package/dist/frontend/lowering/lowerer.js +188 -11
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/types.d.ts +28 -1
- package/dist/frontend/types.js +300 -8
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +37 -7
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +28 -2
- package/dist/ir/nodes.js +35 -3
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +35 -6
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/profile.d.ts +17 -3
- package/dist/library/profile.js +38 -9
- package/dist/library/profile.js.map +1 -1
- package/dist/library/sidecar.js +162 -43
- package/dist/library/sidecar.js.map +1 -1
- package/dist/library/trap-teaching.d.ts +11 -0
- package/dist/library/trap-teaching.js +49 -0
- package/dist/library/trap-teaching.js.map +1 -0
- package/package.json +2 -2
- package/surface-manifest.json +37 -2
package/dist/frontend/types.d.ts
CHANGED
|
@@ -140,7 +140,10 @@ export declare function containsRecord(t: IrType): boolean;
|
|
|
140
140
|
* need a runtime re-tag). */
|
|
141
141
|
export declare function containsUnion(t: IrType): boolean;
|
|
142
142
|
/** ts.Type → IrType. Returns null for anything outside the supported
|
|
143
|
-
* surface; the caller
|
|
143
|
+
* surface; the caller (badType) classifies the shape and reports the
|
|
144
|
+
* matching type fence — SC2005 generic signatures, SC2006 index
|
|
145
|
+
* signatures, SC2007 overloads, SC2008 intersections, SC2009 component
|
|
146
|
+
* fences, SC2020 library types, SC2001 for the remainder — with
|
|
144
147
|
* checker.typeToString.
|
|
145
148
|
*
|
|
146
149
|
* Literal types are widened FIRST (`const x = 1` has type `1`, `true` has
|
|
@@ -162,6 +165,13 @@ export type ClassNamer = (decl: ts.ClassLikeDeclaration) => string;
|
|
|
162
165
|
* substitution has to happen inside mapType's recursion — carried by the
|
|
163
166
|
* TypeMapperCtx exactly like ClassNamer. */
|
|
164
167
|
export type TypeParamResolver = (t: ts.Type) => IrType | null;
|
|
168
|
+
/** Resolves a type-parameter ts.Type to the CHECKER type it is bound to in
|
|
169
|
+
* the current instantiation, or null. The ts-level twin of
|
|
170
|
+
* TypeParamResolver, needed where the IR type has already lost information
|
|
171
|
+
* mapType's widening discipline drops: `T[K]` inside a generic body resolves
|
|
172
|
+
* through the BOUND checker types (K's literal names the property), which no
|
|
173
|
+
* IrType binding can carry. */
|
|
174
|
+
export type TypeParamTsResolver = (t: ts.Type) => ts.Type | null;
|
|
165
175
|
/** Everything mapType needs besides the type itself: the checker, the two
|
|
166
176
|
* interners (owned by the Lowerer), and the Lowerer-injected hooks. Built
|
|
167
177
|
* once per Lowerer and threaded through the recursion — adding a hook means
|
|
@@ -172,6 +182,10 @@ export interface TypeMapperCtx {
|
|
|
172
182
|
unions: UnionRegistry;
|
|
173
183
|
classNamer: ClassNamer;
|
|
174
184
|
resolveTypeParam?: TypeParamResolver;
|
|
185
|
+
/** The ts-level twin of resolveTypeParam (bound CHECKER types), consulted
|
|
186
|
+
* only where literal identity matters — indexed accesses (`T[K]`) inside
|
|
187
|
+
* generic bodies whose K is bound to a literal key. */
|
|
188
|
+
resolveTypeParamTs?: TypeParamTsResolver;
|
|
175
189
|
/** GENERIC program classes (monomorphization by flow): the mapping of a
|
|
176
190
|
* concrete instantiation reference (`Box<number>`) — the Lowerer
|
|
177
191
|
* registers/reuses the instantiation (`Box%0`) and answers its object
|
|
@@ -289,3 +303,16 @@ export declare function withUndefinedArm(t: IrType, unions: UnionRegistry): IrTy
|
|
|
289
303
|
* Object.keys over such a shape omits the member (a pin — every excluded
|
|
290
304
|
* member is a function the key walk would name). */
|
|
291
305
|
export declare function isGenericCallableMemberType(t: ts.Type, checker: ts.TypeChecker): boolean;
|
|
306
|
+
/** When an unmapped type is a SUPPORTED container/composite whose failure
|
|
307
|
+
* lives in a component, answer a message tail naming the component (the
|
|
308
|
+
* SC2009 story); null when the shape itself is the blocker (the caller
|
|
309
|
+
* falls through to the other fences). Stdlib container heads (Map, Set,
|
|
310
|
+
* Promise, generators) ALWAYS answer — their shapes have lowerings, so a
|
|
311
|
+
* later "no lowering" claim about them would be false. */
|
|
312
|
+
export declare function describeComponentBlocker(widened: ts.Type, ctx: TypeMapperCtx): string | null;
|
|
313
|
+
/** The record-member arm of the component fence: a USER record shape
|
|
314
|
+
* blocked by ONE member's type. Runs AFTER badType's stdlib/npm/index-
|
|
315
|
+
* signature routing, so what reaches it is a plain data shape; a null
|
|
316
|
+
* answer (no member pinpointed — provenance fences, accessor rules, shape
|
|
317
|
+
* knots) keeps the residual SC2001 story. */
|
|
318
|
+
export declare function describeRecordMemberBlocker(widened: ts.Type, ctx: TypeMapperCtx): string | null;
|
package/dist/frontend/types.js
CHANGED
|
@@ -525,7 +525,7 @@ let mapTypeDepth = 0;
|
|
|
525
525
|
* The recursive-shape machinery keys shape identity by checker type, which
|
|
526
526
|
* is sound only for context-FREE mappings: a recursive frame that observes
|
|
527
527
|
* a bump between entry and exit stays fenced (recursive generic-open types
|
|
528
|
-
*
|
|
528
|
+
* stay fenced instead of interning a per-context-wrong shape). */
|
|
529
529
|
let contextResolutions = 0;
|
|
530
530
|
export function mapType(type, ctx) {
|
|
531
531
|
if (mapTypeDepth >= MAP_TYPE_MAX_DEPTH)
|
|
@@ -584,6 +584,15 @@ function mapTypeInner(type, ctx) {
|
|
|
584
584
|
contextResolutions++;
|
|
585
585
|
return viaAwaited;
|
|
586
586
|
}
|
|
587
|
+
// `T[K]` over a bound parameter: like Awaited, a form the checker
|
|
588
|
+
// keeps symbolic inside the body. Two resolvers, tried in order: the
|
|
589
|
+
// structural read against the record binding, then the bound-checker
|
|
590
|
+
// route for a K naming one concrete key (mapBoundIndexedAccess).
|
|
591
|
+
const viaIndexed = mapGenericIndexedAccess(type, ctx) ?? mapBoundIndexedAccess(type, ctx);
|
|
592
|
+
if (viaIndexed !== null) {
|
|
593
|
+
contextResolutions++;
|
|
594
|
+
return viaIndexed;
|
|
595
|
+
}
|
|
587
596
|
}
|
|
588
597
|
const widened = checker.getBaseTypeOfLiteralType(type);
|
|
589
598
|
const flags = widened.flags;
|
|
@@ -1030,8 +1039,8 @@ function mapTypeInner(type, ctx) {
|
|
|
1030
1039
|
// provenance, not the name (a user's own `interface Map` maps as a record
|
|
1031
1040
|
// like any other). Keys are fenced to f64/string (SameValueZero hashing),
|
|
1032
1041
|
// values to the supported kinds (see isSupportedMapValue); anything
|
|
1033
|
-
// outside stays unmapped — callers report
|
|
1034
|
-
//
|
|
1042
|
+
// outside stays unmapped — callers report the component fence (SC2009)
|
|
1043
|
+
// naming the offending half, as does the `new Map` lowering per site.
|
|
1035
1044
|
const psym = widened.getSymbol();
|
|
1036
1045
|
const isStdlibInterface = (name) => psym?.name === name &&
|
|
1037
1046
|
checker.declarationsOf(psym).some((d) => ts.isInterfaceDeclaration(d) && ctx.isStdlibFile(d.getSourceFile()));
|
|
@@ -2041,9 +2050,12 @@ function mapTypeInner(type, ctx) {
|
|
|
2041
2050
|
return arms[0];
|
|
2042
2051
|
}
|
|
2043
2052
|
if (arms.some((a) => a.kind === "void" || a.kind === "union" ||
|
|
2044
|
-
// Map/Set
|
|
2045
|
-
//
|
|
2046
|
-
|
|
2053
|
+
// Map/Set arms stay out (like func against data arms: no
|
|
2054
|
+
// narrowing test — no discriminant fields on them). REGEX
|
|
2055
|
+
// arms map: `x instanceof RegExp` is their narrowing test
|
|
2056
|
+
// (the skip-utility `string | RegExp` shape), and the arm
|
|
2057
|
+
// rides the ref machinery like array regex elements.
|
|
2058
|
+
a.kind === "map" || a.kind === "set" || a.kind === "dyn" ||
|
|
2047
2059
|
// Generator arms follow the map/set rule: no narrowing test.
|
|
2048
2060
|
a.kind === "generator" ||
|
|
2049
2061
|
// Func arms map beside ANY sibling: `typeof x === "function"`
|
|
@@ -2145,6 +2157,36 @@ function mapNarrowedTypeParam(type, ctx) {
|
|
|
2145
2157
|
// Filtering preserves the canonical (typeKey-sorted) arm order.
|
|
2146
2158
|
return { kind: "union", unionId: unions.intern(arms) };
|
|
2147
2159
|
}
|
|
2160
|
+
/** `T[K]` inside a generic body, resolved through the instantiation's BOUND
|
|
2161
|
+
* checker types (the checker keeps indexed accesses over type parameters
|
|
2162
|
+
* symbolic). Object and index sides each resolve through resolveTypeParamTs
|
|
2163
|
+
* when they are type parameters; when the index lands on ONE literal key
|
|
2164
|
+
* (`K extends keyof T` bound to `"a"` — inference preserves the literal
|
|
2165
|
+
* there), the named property's type maps like a concrete member read. A
|
|
2166
|
+
* non-literal index (K bound to `string` or a key union) answers null: the
|
|
2167
|
+
* access has no one property type, and the per-site keyed-read machinery
|
|
2168
|
+
* (or its fences) owns the story. */
|
|
2169
|
+
function mapBoundIndexedAccess(type, ctx) {
|
|
2170
|
+
const { checker, resolveTypeParamTs } = ctx;
|
|
2171
|
+
if (!resolveTypeParamTs || !type.isIndexedAccessType())
|
|
2172
|
+
return null;
|
|
2173
|
+
const resolveSide = (t) => t.flags & ts.TypeFlags.TypeParameter ? resolveTypeParamTs(t) : t;
|
|
2174
|
+
const objT = resolveSide(type.getObjectType());
|
|
2175
|
+
const idxT = resolveSide(type.getIndexType());
|
|
2176
|
+
if (!objT || !idxT)
|
|
2177
|
+
return null;
|
|
2178
|
+
const key = idxT.isStringLiteralType()
|
|
2179
|
+
? idxT.value
|
|
2180
|
+
: idxT.isNumberLiteralType()
|
|
2181
|
+
? String(idxT.value)
|
|
2182
|
+
: null;
|
|
2183
|
+
if (key === null)
|
|
2184
|
+
return null;
|
|
2185
|
+
const prop = checker.getPropertyOfType(objT, key);
|
|
2186
|
+
if (!prop)
|
|
2187
|
+
return null;
|
|
2188
|
+
return mapType(checker.getTypeOfSymbol(prop), ctx);
|
|
2189
|
+
}
|
|
2148
2190
|
/** `Partial<T>` / `Readonly<T>` where T is a STILL-GENERIC type parameter —
|
|
2149
2191
|
* the one mapped-type spelling a monomorphized body can contain that the
|
|
2150
2192
|
* checker cannot resolve for us (`keyof T` is unknown inside the body, so
|
|
@@ -2227,6 +2269,52 @@ function mapGenericAwaitedAlias(type, ctx) {
|
|
|
2227
2269
|
r = r.inner;
|
|
2228
2270
|
return r;
|
|
2229
2271
|
}
|
|
2272
|
+
/** `T[K]` where T is a STILL-GENERIC type parameter, resolved against the
|
|
2273
|
+
* lowerer's binding — the checker keeps the access symbolic inside a
|
|
2274
|
+
* generic body (the mockable-clock module shape: a JSDoc-generic factory
|
|
2275
|
+
* whose declared return carries `implementation: T[K]` members). The
|
|
2276
|
+
* binding decides which field types the access can name: a string-LITERAL
|
|
2277
|
+
* index picks its one declared field (the index signature's value type for
|
|
2278
|
+
* an undeclared name); an all-keys index — `keyof T` over the same
|
|
2279
|
+
* parameter, or an index parameter whose own binding widened to plain
|
|
2280
|
+
* string — covers every declared field. The mapping answers only when
|
|
2281
|
+
* every covered field agrees on ONE IR type (the call boundary resolved
|
|
2282
|
+
* the same access against the concrete T, so the shapes intern equal);
|
|
2283
|
+
* disagreeing fields would need a union whose arms this relation cannot
|
|
2284
|
+
* vouch for — those stay unmapped, keeping their fences. */
|
|
2285
|
+
function mapGenericIndexedAccess(type, ctx) {
|
|
2286
|
+
const { resolveTypeParam, shapes } = ctx;
|
|
2287
|
+
if (!resolveTypeParam || !type.isIndexedAccessType())
|
|
2288
|
+
return null;
|
|
2289
|
+
const obj = type.getObjectType();
|
|
2290
|
+
if (!(obj.flags & ts.TypeFlags.TypeParameter))
|
|
2291
|
+
return null;
|
|
2292
|
+
const bound = resolveTypeParam(obj);
|
|
2293
|
+
if (!bound || bound.kind !== "record")
|
|
2294
|
+
return null;
|
|
2295
|
+
const shape = shapes.get(bound.shapeId);
|
|
2296
|
+
if (!shape || shape.tuple)
|
|
2297
|
+
return null;
|
|
2298
|
+
const idx = type.getIndexType();
|
|
2299
|
+
let covered = null;
|
|
2300
|
+
if (idx.isStringLiteralType()) {
|
|
2301
|
+
const f = shape.fields.find((x) => x.name === idx.value);
|
|
2302
|
+
covered = f ? [f.type] : shape.indexValue ? [shape.indexValue] : null;
|
|
2303
|
+
}
|
|
2304
|
+
else {
|
|
2305
|
+
const allKeys = (idx.flags & ts.TypeFlags.TypeParameter && resolveTypeParam(idx)?.kind === "string") ||
|
|
2306
|
+
(idx.isIndexType() && idx.getTarget() === obj);
|
|
2307
|
+
if (allKeys) {
|
|
2308
|
+
covered = shape.fields.map((f) => f.type);
|
|
2309
|
+
if (shape.indexValue)
|
|
2310
|
+
covered.push(shape.indexValue);
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
if (!covered || covered.length === 0)
|
|
2314
|
+
return null;
|
|
2315
|
+
const first = covered[0];
|
|
2316
|
+
return covered.every((t) => typeEquals(t, first)) ? first : null;
|
|
2317
|
+
}
|
|
2230
2318
|
function mapGenericUtilityAlias(widened, ctx) {
|
|
2231
2319
|
const { resolveTypeParam, shapes, unions } = ctx;
|
|
2232
2320
|
if (!resolveTypeParam)
|
|
@@ -2369,8 +2457,7 @@ export function withUndefinedArm(t, unions) {
|
|
|
2369
2457
|
arms.sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
|
|
2370
2458
|
return { kind: "union", unionId: unions.intern(arms) };
|
|
2371
2459
|
}
|
|
2372
|
-
if (t.kind === "void" || t.kind === "map" ||
|
|
2373
|
-
t.kind === "regex" || t.kind === "dyn" ||
|
|
2460
|
+
if (t.kind === "void" || t.kind === "map" || t.kind === "dyn" ||
|
|
2374
2461
|
// A bare unit field type cannot occur (units live only inside unions),
|
|
2375
2462
|
// but guard against constructing a single-arm union from one.
|
|
2376
2463
|
isUnitType(t)) {
|
|
@@ -2824,4 +2911,209 @@ function mapRecordTypeInner(widened, ctx) {
|
|
|
2824
2911
|
return { fields, ...(indexValue ? { indexValue } : {}), declaredOrder };
|
|
2825
2912
|
}
|
|
2826
2913
|
}
|
|
2914
|
+
/* ── Component-fence classification (SC2009) ───────────────────────────────
|
|
2915
|
+
* badType's post-hoc classifiers: given a type mapType REJECTED, decide
|
|
2916
|
+
* whether the failure lives in a COMPONENT of an otherwise supported shape
|
|
2917
|
+
* — and if so, name it. Pure description: these run only on the failure
|
|
2918
|
+
* path (the build already carries a diagnostic for the site), mirror
|
|
2919
|
+
* mapType's own rules, and never change what maps. Probing through mapType
|
|
2920
|
+
* here is safe for the same reason badType's dynamic probe is: anything
|
|
2921
|
+
* interned on the way belongs to a build that is not emitted. */
|
|
2922
|
+
/** The recognized stdlib containers and the slot name each type-argument
|
|
2923
|
+
* position plays in their messages. */
|
|
2924
|
+
const STDLIB_CONTAINERS = {
|
|
2925
|
+
Map: { role: (i) => (i === 0 ? "key" : "value") },
|
|
2926
|
+
ReadonlyMap: { role: (i) => (i === 0 ? "key" : "value") },
|
|
2927
|
+
Set: { role: () => "element" },
|
|
2928
|
+
ReadonlySet: { role: () => "element" },
|
|
2929
|
+
Promise: { role: () => "value" },
|
|
2930
|
+
Generator: { role: (i) => ["yield", "return", "next"][i] ?? "channel" },
|
|
2931
|
+
AsyncGenerator: { role: (i) => ["yield", "return", "next"][i] ?? "channel" },
|
|
2932
|
+
};
|
|
2933
|
+
/** Arm kinds with no home in a compiled union (mapTypeInner's union rule):
|
|
2934
|
+
* no runtime narrowing test exists against sibling data arms. */
|
|
2935
|
+
function armHasUnionHome(arm, siblingCount) {
|
|
2936
|
+
switch (arm.kind) {
|
|
2937
|
+
case "void":
|
|
2938
|
+
case "union":
|
|
2939
|
+
case "map":
|
|
2940
|
+
case "set":
|
|
2941
|
+
case "regex":
|
|
2942
|
+
case "generator":
|
|
2943
|
+
case "dyn":
|
|
2944
|
+
return false;
|
|
2945
|
+
// Promise arms map only beside unit siblings (the promise-or-absent
|
|
2946
|
+
// shape); a data sibling has no narrowing test against them.
|
|
2947
|
+
case "promise":
|
|
2948
|
+
return siblingCount === 0;
|
|
2949
|
+
default:
|
|
2950
|
+
return true;
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
/** When an unmapped type is a SUPPORTED container/composite whose failure
|
|
2954
|
+
* lives in a component, answer a message tail naming the component (the
|
|
2955
|
+
* SC2009 story); null when the shape itself is the blocker (the caller
|
|
2956
|
+
* falls through to the other fences). Stdlib container heads (Map, Set,
|
|
2957
|
+
* Promise, generators) ALWAYS answer — their shapes have lowerings, so a
|
|
2958
|
+
* later "no lowering" claim about them would be false. */
|
|
2959
|
+
export function describeComponentBlocker(widened, ctx) {
|
|
2960
|
+
const { checker } = ctx;
|
|
2961
|
+
const text = (t) => checker.typeToString(t);
|
|
2962
|
+
// Stdlib container references (provenance, not the name — a user's own
|
|
2963
|
+
// `interface Map` is a record shape and keeps the record stories).
|
|
2964
|
+
const psym = widened.getSymbol();
|
|
2965
|
+
const container = psym !== undefined &&
|
|
2966
|
+
Object.prototype.hasOwnProperty.call(STDLIB_CONTAINERS, psym.name) &&
|
|
2967
|
+
checker.declarationsOf(psym).some((d) => ts.isInterfaceDeclaration(d) && ctx.isStdlibFile(d.getSourceFile()))
|
|
2968
|
+
? psym.name
|
|
2969
|
+
: undefined;
|
|
2970
|
+
if (container !== undefined) {
|
|
2971
|
+
const spec = STDLIB_CONTAINERS[container];
|
|
2972
|
+
const args = checker.getTypeArguments(widened);
|
|
2973
|
+
for (let i = 0; i < args.length; i++) {
|
|
2974
|
+
const arg = args[i];
|
|
2975
|
+
const role = spec.role(i);
|
|
2976
|
+
const mapped = mapType(arg, ctx);
|
|
2977
|
+
if (!mapped) {
|
|
2978
|
+
return `the ${container} shape is supported, but its ${role} type '${text(arg)}' does not compile`;
|
|
2979
|
+
}
|
|
2980
|
+
if ((container === "Map" || container === "ReadonlyMap") && i === 0 && !isSupportedMapKey(mapped)) {
|
|
2981
|
+
return `the ${container} shape is supported, but keys are limited to numbers and strings — '${text(arg)}' is outside that domain`;
|
|
2982
|
+
}
|
|
2983
|
+
if ((container === "Map" || container === "ReadonlyMap") && i === 1 && !isSupportedMapValue(mapped)) {
|
|
2984
|
+
return `the ${container} shape is supported, but '${text(arg)}' values have no Map slot yet (functions, promises, and nested Maps stay out)`;
|
|
2985
|
+
}
|
|
2986
|
+
if ((container === "Set" || container === "ReadonlySet") && !isSupportedSetElem(mapped)) {
|
|
2987
|
+
return `the ${container} shape is supported, but elements are limited to numbers and strings — '${text(arg)}' is outside that domain`;
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
// Every argument passed the per-slot checks and the type still failed:
|
|
2991
|
+
// a composition rule the slots alone don't show (a generator's channel
|
|
2992
|
+
// interplay, a promise wrapper rule). Still the container's story.
|
|
2993
|
+
return `the ${container} shape is supported, but this instantiation's type arguments are outside the supported set`;
|
|
2994
|
+
}
|
|
2995
|
+
// Arrays: the element is the failure by construction (a mappable element
|
|
2996
|
+
// makes the array map).
|
|
2997
|
+
if (checker.isArrayType(widened)) {
|
|
2998
|
+
const elemTs = checker.getTypeArguments(widened)[0];
|
|
2999
|
+
if (elemTs === undefined)
|
|
3000
|
+
return null;
|
|
3001
|
+
const elem = mapType(elemTs, ctx);
|
|
3002
|
+
if (!elem) {
|
|
3003
|
+
return `the array shape is supported, but its element type '${text(elemTs)}' does not compile`;
|
|
3004
|
+
}
|
|
3005
|
+
return `the array shape is supported, but '${text(elemTs)}' elements have no array representation yet`;
|
|
3006
|
+
}
|
|
3007
|
+
// Tuples: name the first element whose type does not map. Optional/rest
|
|
3008
|
+
// tuples in static builds are dynamic-representable and never get here
|
|
3009
|
+
// (badType's dynamic probe speaks first).
|
|
3010
|
+
if (checker.isTupleType(widened)) {
|
|
3011
|
+
for (const arg of checker.getTypeArguments(widened)) {
|
|
3012
|
+
const et = mapType(arg, ctx);
|
|
3013
|
+
if (et?.kind === "void" && isUnitOnlyTsType(arg))
|
|
3014
|
+
continue;
|
|
3015
|
+
if (!et || et.kind === "void") {
|
|
3016
|
+
return `the tuple shape is supported, but its element type '${text(arg)}' does not compile`;
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
return null;
|
|
3020
|
+
}
|
|
3021
|
+
// Unions: name the first arm that does not compile, or the first mapped
|
|
3022
|
+
// arm kind with no union home. Unions have no symbol, so a null answer
|
|
3023
|
+
// falls through to the residual fence, never to a false lib claim.
|
|
3024
|
+
if (widened.isUnionType()) {
|
|
3025
|
+
const parts = widened.getTypes();
|
|
3026
|
+
const UNIT = ts.TypeFlags.Undefined | ts.TypeFlags.Void | ts.TypeFlags.Null;
|
|
3027
|
+
const dataArms = parts.filter((p) => (p.flags & UNIT) === 0);
|
|
3028
|
+
for (const part of dataArms) {
|
|
3029
|
+
const mapped = mapType(part, ctx);
|
|
3030
|
+
if (!mapped) {
|
|
3031
|
+
return `the union shape is supported, but its arm '${text(part)}' does not compile`;
|
|
3032
|
+
}
|
|
3033
|
+
if (!armHasUnionHome(mapped, dataArms.length - 1)) {
|
|
3034
|
+
return `the union shape is supported, but '${text(part)}' arms have no home in a compiled union yet (no runtime narrowing test exists against sibling arms)`;
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
return null;
|
|
3038
|
+
}
|
|
3039
|
+
// Single-signature, non-generic function types: rest parameters, a
|
|
3040
|
+
// parameter type, or the return type carries the failure. (Generic and
|
|
3041
|
+
// overloaded signatures have their own fences — SC2005/SC2007 — and
|
|
3042
|
+
// badType runs those first.)
|
|
3043
|
+
const callSigs = checker.getCallSignatures(widened);
|
|
3044
|
+
if (callSigs.length === 1) {
|
|
3045
|
+
const sig = callSigs[0];
|
|
3046
|
+
if (sig.getTypeParameters().length > 0)
|
|
3047
|
+
return null;
|
|
3048
|
+
const sigDecl = checker.signatureDeclaration(sig);
|
|
3049
|
+
if (sigDecl !== undefined &&
|
|
3050
|
+
ts.isFunctionLike(sigDecl) &&
|
|
3051
|
+
(sigDecl.parameters.length !== sig.getParameters().length ||
|
|
3052
|
+
bodyReadsArgumentsLocal(sigDecl))) {
|
|
3053
|
+
return `the function shape is supported, but its signature is variadic ('arguments'-reading), and a compiled signature is fixed-arity`;
|
|
3054
|
+
}
|
|
3055
|
+
for (const p of sig.getParameters()) {
|
|
3056
|
+
const decl = checker.valueDeclarationOf(p);
|
|
3057
|
+
if (decl !== undefined && ts.isParameter(decl) && decl.dotDotDotToken !== undefined) {
|
|
3058
|
+
return `the function shape is supported, but its rest parameter '${p.name}' has no compiled calling convention yet (a compiled signature is fixed-arity)`;
|
|
3059
|
+
}
|
|
3060
|
+
const pTs = checker.getTypeOfSymbol(p);
|
|
3061
|
+
if (!mapType(pTs, ctx)) {
|
|
3062
|
+
return `the function shape is supported, but its parameter '${p.name}' has type '${text(pTs)}', which does not compile`;
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
const retTs = checker.getReturnTypeOfSignature(sig);
|
|
3066
|
+
if ((retTs.flags & ts.TypeFlags.Never) === 0 && !mapType(retTs, ctx)) {
|
|
3067
|
+
return `the function shape is supported, but its return type '${text(retTs)}' does not compile`;
|
|
3068
|
+
}
|
|
3069
|
+
return null;
|
|
3070
|
+
}
|
|
3071
|
+
return null;
|
|
3072
|
+
}
|
|
3073
|
+
/** The record-member arm of the component fence: a USER record shape
|
|
3074
|
+
* blocked by ONE member's type. Runs AFTER badType's stdlib/npm/index-
|
|
3075
|
+
* signature routing, so what reaches it is a plain data shape; a null
|
|
3076
|
+
* answer (no member pinpointed — provenance fences, accessor rules, shape
|
|
3077
|
+
* knots) keeps the residual SC2001 story. */
|
|
3078
|
+
export function describeRecordMemberBlocker(widened, ctx) {
|
|
3079
|
+
const { checker } = ctx;
|
|
3080
|
+
if ((widened.flags & ts.TypeFlags.Object) === 0)
|
|
3081
|
+
return null;
|
|
3082
|
+
if (checker.getCallSignatures(widened).length > 0)
|
|
3083
|
+
return null;
|
|
3084
|
+
if (checker.getConstructSignatures(widened).length > 0)
|
|
3085
|
+
return null;
|
|
3086
|
+
if (checker.isTupleType(widened) || checker.isArrayLikeType(widened))
|
|
3087
|
+
return null;
|
|
3088
|
+
if (checker.getIndexInfosOfType(widened).length > 0)
|
|
3089
|
+
return null;
|
|
3090
|
+
const widenedSym = widened.getSymbol();
|
|
3091
|
+
if (widenedSym !== undefined && (widenedSym.flags & ts.SymbolFlags.Enum) !== 0)
|
|
3092
|
+
return null;
|
|
3093
|
+
const computed = isMappedShape(widened);
|
|
3094
|
+
for (const p of checker.getPropertiesOfType(widened)) {
|
|
3095
|
+
// Accessor members and symbol-keyed members have their own multi-shaped
|
|
3096
|
+
// rules (mapRecordTypeInner) — no single-member claim is honest there.
|
|
3097
|
+
if ((p.flags & (ts.SymbolFlags.GetAccessor | ts.SymbolFlags.SetAccessor)) !== 0)
|
|
3098
|
+
continue;
|
|
3099
|
+
if (p.name.startsWith("__@"))
|
|
3100
|
+
continue;
|
|
3101
|
+
// Computed shapes over LIBRARY members (`Readonly<Date>`): the record
|
|
3102
|
+
// fence there is per-member PROVENANCE (mapRecordTypeInner's rule), not
|
|
3103
|
+
// any one member's type — that story stays with the residual fence.
|
|
3104
|
+
if (computed && checker.declarationsOf(p).some((d) => d.getSourceFile().isDeclarationFile)) {
|
|
3105
|
+
return null;
|
|
3106
|
+
}
|
|
3107
|
+
const fieldTs = checker.getTypeOfSymbol(p);
|
|
3108
|
+
if (isGenericCallableMemberType(fieldTs, checker))
|
|
3109
|
+
continue;
|
|
3110
|
+
let pt = mapType(fieldTs, ctx);
|
|
3111
|
+
if (pt?.kind === "void" && isUnitOnlyTsType(fieldTs))
|
|
3112
|
+
pt = unitOnlyUnion(ctx.unions);
|
|
3113
|
+
if (!pt || pt.kind === "void") {
|
|
3114
|
+
return `the record shape is supported, but its member '${p.name}' has type '${checker.typeToString(fieldTs)}', which does not compile`;
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
return null;
|
|
3118
|
+
}
|
|
2827
3119
|
//# sourceMappingURL=types.js.map
|