@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.
Files changed (68) hide show
  1. package/dist/backend/emission/emit-exprs.js +47 -1
  2. package/dist/backend/emission/emit-exprs.js.map +1 -1
  3. package/dist/backend/emission/emit-island.js +9 -1
  4. package/dist/backend/emission/emit-island.js.map +1 -1
  5. package/dist/backend/emission/emit-stmts.js +3 -1
  6. package/dist/backend/emission/emit-stmts.js.map +1 -1
  7. package/dist/backend/emission/emit-walkers.js +22 -8
  8. package/dist/backend/emission/emit-walkers.js.map +1 -1
  9. package/dist/backend/emission/emitter.js +5 -1
  10. package/dist/backend/emission/emitter.js.map +1 -1
  11. package/dist/backend/emission/may-throw.js +4 -3
  12. package/dist/backend/emission/may-throw.js.map +1 -1
  13. package/dist/backend/llvm/emitter.js +94 -7
  14. package/dist/backend/llvm/emitter.js.map +1 -1
  15. package/dist/diagnostics/diagnostic.d.ts +43 -1
  16. package/dist/diagnostics/diagnostic.js +103 -5
  17. package/dist/diagnostics/diagnostic.js.map +1 -1
  18. package/dist/frontend/lib-contract.d.ts +24 -6
  19. package/dist/frontend/lib-contract.js +125 -44
  20. package/dist/frontend/lib-contract.js.map +1 -1
  21. package/dist/frontend/lowering/lower-builtins.js +16 -1
  22. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  23. package/dist/frontend/lowering/lower-calls.d.ts +47 -7
  24. package/dist/frontend/lowering/lower-calls.js +560 -36
  25. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  26. package/dist/frontend/lowering/lower-classes.d.ts +14 -1
  27. package/dist/frontend/lowering/lower-classes.js +85 -15
  28. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  29. package/dist/frontend/lowering/lower-containers.js +43 -1
  30. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  31. package/dist/frontend/lowering/lower-emitter.js +1 -1
  32. package/dist/frontend/lowering/lower-emitter.js.map +1 -1
  33. package/dist/frontend/lowering/lower-exprs.d.ts +26 -3
  34. package/dist/frontend/lowering/lower-exprs.js +455 -40
  35. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  36. package/dist/frontend/lowering/lower-island.d.ts +7 -0
  37. package/dist/frontend/lowering/lower-island.js +71 -10
  38. package/dist/frontend/lowering/lower-island.js.map +1 -1
  39. package/dist/frontend/lowering/lower-mixins.d.ts +1 -1
  40. package/dist/frontend/lowering/lower-mixins.js +1 -1
  41. package/dist/frontend/lowering/lower-modules.js +95 -4
  42. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  43. package/dist/frontend/lowering/lower-stmts.js +421 -43
  44. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  45. package/dist/frontend/lowering/lowerer.d.ts +19 -3
  46. package/dist/frontend/lowering/lowerer.js +188 -11
  47. package/dist/frontend/lowering/lowerer.js.map +1 -1
  48. package/dist/frontend/types.d.ts +28 -1
  49. package/dist/frontend/types.js +300 -8
  50. package/dist/frontend/types.js.map +1 -1
  51. package/dist/index.d.ts +2 -1
  52. package/dist/index.js +37 -7
  53. package/dist/index.js.map +1 -1
  54. package/dist/ir/nodes.d.ts +28 -2
  55. package/dist/ir/nodes.js +35 -3
  56. package/dist/ir/nodes.js.map +1 -1
  57. package/dist/ir/validate.js +35 -6
  58. package/dist/ir/validate.js.map +1 -1
  59. package/dist/library/profile.d.ts +17 -3
  60. package/dist/library/profile.js +38 -9
  61. package/dist/library/profile.js.map +1 -1
  62. package/dist/library/sidecar.js +162 -43
  63. package/dist/library/sidecar.js.map +1 -1
  64. package/dist/library/trap-teaching.d.ts +11 -0
  65. package/dist/library/trap-teaching.js +49 -0
  66. package/dist/library/trap-teaching.js.map +1 -0
  67. package/package.json +2 -2
  68. package/surface-manifest.json +37 -2
@@ -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 turns that into a SC2001 diagnostic with
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;
@@ -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
- * report SC2001 instead of interning a per-context-wrong shape). */
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 SC2001 with the full type
1034
- // text, and the `new Map` lowering names the offending half specifically.
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 and regex arms stay out (like func against data
2045
- // arms: no narrowing test — no discriminant fields on them).
2046
- a.kind === "map" || a.kind === "set" || a.kind === "regex" || a.kind === "dyn" ||
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