@scriptc/compiler 0.1.5 → 0.1.7

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 (139) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +0 -1
  2. package/ambient/scriptc.d.ts +13 -5
  3. package/dist/backend/c/c-emitter.d.ts +5 -0
  4. package/dist/backend/c/c-emitter.js +15 -0
  5. package/dist/backend/c/c-emitter.js.map +1 -1
  6. package/dist/backend/c/exprs.js +81 -9
  7. package/dist/backend/c/exprs.js.map +1 -1
  8. package/dist/backend/c/stmts.js +25 -1
  9. package/dist/backend/c/stmts.js.map +1 -1
  10. package/dist/backend/c/walkers.js +2 -3
  11. package/dist/backend/c/walkers.js.map +1 -1
  12. package/dist/backend/debug-symbols.d.ts +8 -0
  13. package/dist/backend/debug-symbols.js +52 -0
  14. package/dist/backend/debug-symbols.js.map +1 -0
  15. package/dist/backend/link-plan.d.ts +1 -0
  16. package/dist/backend/link-plan.js +2 -0
  17. package/dist/backend/link-plan.js.map +1 -1
  18. package/dist/backend/linker.js +8 -0
  19. package/dist/backend/linker.js.map +1 -1
  20. package/dist/backend/llvm/blocks.d.ts +2 -0
  21. package/dist/backend/llvm/blocks.js +18 -2
  22. package/dist/backend/llvm/blocks.js.map +1 -1
  23. package/dist/backend/llvm/debug-info.d.ts +32 -0
  24. package/dist/backend/llvm/debug-info.js +203 -0
  25. package/dist/backend/llvm/debug-info.js.map +1 -0
  26. package/dist/backend/llvm/dyn.js +4 -10
  27. package/dist/backend/llvm/dyn.js.map +1 -1
  28. package/dist/backend/llvm/emitter.d.ts +2 -0
  29. package/dist/backend/llvm/emitter.js +49 -4
  30. package/dist/backend/llvm/emitter.js.map +1 -1
  31. package/dist/backend/llvm/expr-containers.js +28 -2
  32. package/dist/backend/llvm/expr-containers.js.map +1 -1
  33. package/dist/backend/llvm/expr-dynamic.js +10 -0
  34. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  35. package/dist/backend/llvm/lib-filesystem.js +37 -0
  36. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  37. package/dist/backend/llvm/lib-process.js +4 -3
  38. package/dist/backend/llvm/lib-process.js.map +1 -1
  39. package/dist/backend/llvm/lib-shared.js +9 -0
  40. package/dist/backend/llvm/lib-shared.js.map +1 -1
  41. package/dist/backend/llvm/split.js +4 -1
  42. package/dist/backend/llvm/split.js.map +1 -1
  43. package/dist/backend/llvm/split.test.js +26 -1
  44. package/dist/backend/llvm/split.test.js.map +1 -1
  45. package/dist/backend/native-toolchain.js +58 -8
  46. package/dist/backend/native-toolchain.js.map +1 -1
  47. package/dist/backend/native-toolchain.test.js +10 -0
  48. package/dist/backend/native-toolchain.test.js.map +1 -1
  49. package/dist/backend/source-locations.d.ts +12 -0
  50. package/dist/backend/source-locations.js +32 -0
  51. package/dist/backend/source-locations.js.map +1 -0
  52. package/dist/backend/source-locations.test.d.ts +1 -0
  53. package/dist/backend/source-locations.test.js +16 -0
  54. package/dist/backend/source-locations.test.js.map +1 -0
  55. package/dist/coverage/surface-manifest.js +6 -1
  56. package/dist/coverage/surface-manifest.js.map +1 -1
  57. package/dist/diagnostics/diagnostic.d.ts +4 -0
  58. package/dist/diagnostics/diagnostic.js +12 -0
  59. package/dist/diagnostics/diagnostic.js.map +1 -1
  60. package/dist/executable/executable-cache.js +28 -4
  61. package/dist/executable/executable-cache.js.map +1 -1
  62. package/dist/executable/executable-cache.test.js +23 -0
  63. package/dist/executable/executable-cache.test.js.map +1 -1
  64. package/dist/frontend/arguments-usage.d.ts +6 -0
  65. package/dist/frontend/arguments-usage.js +40 -0
  66. package/dist/frontend/arguments-usage.js.map +1 -0
  67. package/dist/frontend/binding-source.d.ts +4 -0
  68. package/dist/frontend/binding-source.js +18 -0
  69. package/dist/frontend/binding-source.js.map +1 -0
  70. package/dist/frontend/lowering/array-indexed-mutation.d.ts +8 -0
  71. package/dist/frontend/lowering/array-indexed-mutation.js +176 -0
  72. package/dist/frontend/lowering/array-indexed-mutation.js.map +1 -0
  73. package/dist/frontend/lowering/containers/string-and-regexp.d.ts +7 -0
  74. package/dist/frontend/lowering/containers/string-and-regexp.js +190 -13
  75. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  76. package/dist/frontend/lowering/expressions/object-literals.js +13 -8
  77. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  78. package/dist/frontend/lowering/expressions/optional-chains.d.ts +5 -6
  79. package/dist/frontend/lowering/expressions/optional-chains.js +7 -6
  80. package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
  81. package/dist/frontend/lowering/indexed-comparison.d.ts +6 -0
  82. package/dist/frontend/lowering/indexed-comparison.js +30 -0
  83. package/dist/frontend/lowering/indexed-comparison.js.map +1 -0
  84. package/dist/frontend/lowering/lower-builtins.d.ts +1 -1
  85. package/dist/frontend/lowering/lower-builtins.js +21 -10
  86. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  87. package/dist/frontend/lowering/lower-calls.d.ts +2 -9
  88. package/dist/frontend/lowering/lower-calls.js +406 -40
  89. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  90. package/dist/frontend/lowering/lower-classes.js +3 -27
  91. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  92. package/dist/frontend/lowering/lower-containers.d.ts +8 -2
  93. package/dist/frontend/lowering/lower-containers.js +454 -104
  94. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  95. package/dist/frontend/lowering/lower-exprs.js +134 -18
  96. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  97. package/dist/frontend/lowering/lower-island.d.ts +1 -1
  98. package/dist/frontend/lowering/lower-island.js +1 -1
  99. package/dist/frontend/lowering/lower-modules.js +19 -8
  100. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  101. package/dist/frontend/lowering/lower-stmts.js +4 -37
  102. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  103. package/dist/frontend/lowering/lowerer.js +14 -3
  104. package/dist/frontend/lowering/lowerer.js.map +1 -1
  105. package/dist/frontend/lowering/surfaces.d.ts +3 -1
  106. package/dist/frontend/lowering/surfaces.js +50 -7
  107. package/dist/frontend/lowering/surfaces.js.map +1 -1
  108. package/dist/frontend/program.d.ts +5 -0
  109. package/dist/frontend/program.js +216 -32
  110. package/dist/frontend/program.js.map +1 -1
  111. package/dist/frontend/program.test.d.ts +1 -0
  112. package/dist/frontend/program.test.js +117 -0
  113. package/dist/frontend/program.test.js.map +1 -0
  114. package/dist/frontend/type-mapper.d.ts +4 -2
  115. package/dist/frontend/type-mapper.js +249 -186
  116. package/dist/frontend/type-mapper.js.map +1 -1
  117. package/dist/frontend/type-mapper.test.d.ts +1 -0
  118. package/dist/frontend/type-mapper.test.js +86 -0
  119. package/dist/frontend/type-mapper.test.js.map +1 -0
  120. package/dist/index.d.ts +2 -1
  121. package/dist/index.js +7 -2
  122. package/dist/index.js.map +1 -1
  123. package/dist/ir/analysis.js +4 -2
  124. package/dist/ir/analysis.js.map +1 -1
  125. package/dist/ir/ir.d.ts +33 -14
  126. package/dist/ir/ir.js +5 -1
  127. package/dist/ir/ir.js.map +1 -1
  128. package/dist/ir/validate.js +57 -31
  129. package/dist/ir/validate.js.map +1 -1
  130. package/dist/ir/validate.test.js +17 -0
  131. package/dist/ir/validate.test.js.map +1 -1
  132. package/dist/library/compiler-self-identity.d.ts +1 -1
  133. package/dist/library/compiler-self-identity.js +31 -15
  134. package/dist/library/compiler-self-identity.js.map +1 -1
  135. package/dist/library/compiler-self-identity.test.d.ts +1 -0
  136. package/dist/library/compiler-self-identity.test.js +93 -0
  137. package/dist/library/compiler-self-identity.test.js.map +1 -0
  138. package/package.json +17 -17
  139. package/surface-manifest.json +155 -4
@@ -11,11 +11,12 @@ import { pathToFileURL } from "node:url";
11
11
  import { wasiGuestPath } from "../../wasi-paths.js";
12
12
  import { BIGINT_T, BOOL, CAUGHT, DYN, DYN_HANDLE_KINDS, F64, JSVAL, NULL_T, REF_TRUTHY_KINDS, REGEX, RUNTIME_ERROR_CLASSES, SEARCH_PARAMS_T, STRING, UNDEFINED_T, VOID, arrayOf, canAdaptDynFuncTo, canBoxFuncIntoDyn, funcOf, isDynTypedRefType, isJsonSafeType, isSupportedArrayElem, isUnitType, jsOpResultKind, shapeHasAccessorSlots, typeEquals, typeKey, unionFuncSetArmsOk } from "../../ir/ir.js";
13
13
  import { cjsClassExprWholeExportOf, cjsExportAssignmentOf, cjsExportDiscardReason, isCjsExportTableLiteral, isCjsJsFile, isJsSourceFile, isModuleExportsAccess, isNodeEsmFile, locOf } from "../program.js";
14
- import { ARRAY_METHODS, builtinConstLit, builtinFenceHintOf, builtinModuleConstOf, builtinModulesArrayLit, builtinModuleFnOf, COMPOUND_ASSIGN_OPS, ISLAND_SURFACE, isChildSurfaceMember, MAP_METHODS, NARROW_FIRST, SET_METHODS, STR_METHODS, UNSUPPORTED_EXPR, sideEffectFreeOptionValue, stdlibGlobalNameOf } from "./surfaces.js";
14
+ import { ARRAY_METHODS, builtinConstLit, builtinFenceHintOf, builtinModuleConstOf, builtinModulesArrayLit, builtinModuleFnOf, COMPOUND_ASSIGN_OPS, ISLAND_SURFACE, isChildSurfaceMember, MAP_METHODS, NARROW_FIRST, SET_METHODS, STRING_INDEX_METHODS, STR_METHODS, UNSUPPORTED_EXPR, sideEffectFreeOptionValue, stdlibGlobalNameOf } from "./surfaces.js";
15
15
  import { blockedBindingUseDiag, requiresDynamicPackageDiag, unsupportedDiag } from "../../diagnostics/diagnostic.js";
16
16
  import { PoisonError, dynUndefinedExpr, jsFuncNameOf, neverTaintedJsType, nodeThrowExpr, own } from "./lowerer.js";
17
17
  import { lowerNpmStaticSafeIndexRead, lowerSafeIndexRead, strCharsCall, tryLowerNumericIndexRead } from "./lower-containers.js";
18
18
  import { arrayValueRead, arrayValueStore } from "./array-values.js";
19
+ import { tryLowerIndexedComparison } from "./indexed-comparison.js";
19
20
  import { npmStaticPackageOfPath } from "../npm-static.js";
20
21
  import { unsupportedModuleFeatureOf } from "../builtin-modules.js";
21
22
  import { fenceEnumObjectValue, lowerEnumAccess } from "./lower-enums.js";
@@ -554,6 +555,18 @@ function lowerExprInner(lowerer, expr) {
554
555
  return { kind: "strLit", value: "object", type: STRING, loc };
555
556
  }
556
557
  if (ts.isPropertyAccessExpression(expr.expression)) {
558
+ const member = expr.expression;
559
+ const prototype = member.expression;
560
+ if (STRING_INDEX_METHODS.has(member.name.text) && ts.isPropertyAccessExpression(prototype) &&
561
+ prototype.name.text === "prototype" && ts.isIdentifier(prototype.expression) &&
562
+ lowerer.isStdlibGlobal(prototype.expression, "String") && lowerer.isStdlibMember(member)) {
563
+ return { kind: "strLit", value: "function", type: STRING, loc };
564
+ }
565
+ if (member.name.text === "at" && ts.isPropertyAccessExpression(prototype) &&
566
+ prototype.name.text === "prototype" && ts.isIdentifier(prototype.expression) &&
567
+ lowerer.isStdlibGlobal(prototype.expression, "Array") && lowerer.isStdlibMember(member)) {
568
+ return { kind: "strLit", value: "function", type: STRING, loc };
569
+ }
557
570
  const presence = lowerPromiseThenPresence(lowerer, expr.expression, { kind: "strLit", value: "function", type: STRING, loc });
558
571
  if (presence !== null)
559
572
  return presence;
@@ -993,6 +1006,13 @@ function lowerExprInner(lowerer, expr) {
993
1006
  const canonical = stdlibGlobalNameOf(lowerer, expr) ?? expr.text;
994
1007
  return { kind: "strLit", value: `[builtin ${canonical}]`, type: STRING, loc };
995
1008
  }
1009
+ // In a dynamic TypeScript build, the real global object is the
1010
+ // escape hatch for host capabilities without static lowering.
1011
+ // Direct globalThis.member uses still take their static surface
1012
+ // paths; an explicit `as any` or `: any` can reach engine ops.
1013
+ if (lowerer.dynamic && expr.text === "globalThis") {
1014
+ return { kind: "jsOp", op: "globalGet", name: "globalThis", args: [], type: JSVAL, loc };
1015
+ }
996
1016
  // The families with a WHY: each hint states what makes the
997
1017
  // surface genuinely non-static (or what to use instead).
998
1018
  const globalHints = {
@@ -2294,8 +2314,25 @@ export function maybeNarrow(lowerer, expr, node) {
2294
2314
  return expr;
2295
2315
  }
2296
2316
  const tag = lowerer.armTag(expr.type.unionId, narrowed);
2297
- if (tag < 0)
2317
+ if (tag < 0) {
2318
+ // A proven-present optional base class can narrow further to a
2319
+ // subclass. The payload is still stored under the base class tag;
2320
+ // unwrap it before applying the ordinary class downcast.
2321
+ const arms = lowerer.unions.get(expr.type.unionId)?.arms ?? [];
2322
+ const valueTag = arms.findIndex((arm) => !isUnitType(arm));
2323
+ const valueType = arms[valueTag];
2324
+ if (narrowed.kind === "object" && valueType?.kind === "object" &&
2325
+ arms.every((arm, i) => i === valueTag || isUnitType(arm)) &&
2326
+ lowerer.isSubclassOf(narrowed.className, valueType.className)) {
2327
+ return {
2328
+ kind: "downcast",
2329
+ value: { kind: "unionNarrow", unionId: expr.type.unionId, tag: valueTag, value: expr, type: valueType, loc: expr.loc },
2330
+ type: narrowed,
2331
+ loc: expr.loc,
2332
+ };
2333
+ }
2298
2334
  return expr;
2335
+ }
2299
2336
  return {
2300
2337
  kind: "unionNarrow",
2301
2338
  unionId: expr.type.unionId,
@@ -2352,12 +2389,11 @@ function runtimeOptionalReceiverRead(lowerer, expr, local, property, loc) {
2352
2389
  }
2353
2390
  return { kind: "call", callee: helper, args: [varRef(local.id, local.type, loc)], type: narrowed, loc };
2354
2391
  }
2355
- const valueTag = lowerer.armTag(local.type.unionId, narrowed);
2356
2392
  const undefTag = lowerer.armTag(local.type.unionId, UNDEFINED_T);
2357
- if (valueTag < 0 || undefTag < 0)
2358
- throw new InternalCompilerError("runtime-optional local is missing its value or undefined arm");
2359
2393
  const def = lowerer.unions.get(local.type.unionId);
2360
- if (!def || def.arms.length !== 2) {
2394
+ if (!def || undefTag < 0)
2395
+ throw new InternalCompilerError("runtime-optional local is missing its undefined arm");
2396
+ if (def.arms.length !== 2) {
2361
2397
  // The checker may narrow a runtime-optional capture to one arm of a
2362
2398
  // value union (`Circle | Square | undefined` -> `Circle`). The
2363
2399
  // two-arm fast path below cannot extract that arm safely: a later
@@ -2370,10 +2406,19 @@ function runtimeOptionalReceiverRead(lowerer, expr, local, property, loc) {
2370
2406
  }
2371
2407
  return { kind: "call", callee: helper, args: [varRef(local.id, local.type, loc)], type: narrowed, loc };
2372
2408
  }
2409
+ // A predicate can strengthen a record's fields or narrow a base class
2410
+ // to a subclass without changing the slot's stored value arm. Extract
2411
+ // that actual arm first: record reads need its original field layout,
2412
+ // and class reads apply the ordinary downcast bridge afterward.
2413
+ const valueTag = undefTag === 0 ? 1 : 0;
2414
+ const valueType = def.arms[valueTag];
2415
+ if (!valueType || isUnitType(valueType)) {
2416
+ lowerer.unsupported("SC1090", expr, "a runtime-optional receiver without a representable value arm");
2417
+ }
2373
2418
  const message = property === null
2374
2419
  ? `${expr.text} is not a function`
2375
2420
  : `Cannot read properties of undefined (reading '${property}')`;
2376
- return {
2421
+ return lowerer.maybeNarrow({
2377
2422
  kind: "ternary",
2378
2423
  cond: {
2379
2424
  kind: "unionIsTag",
@@ -2384,18 +2429,18 @@ function runtimeOptionalReceiverRead(lowerer, expr, local, property, loc) {
2384
2429
  type: BOOL,
2385
2430
  loc,
2386
2431
  },
2387
- then: nodeThrowExpr(1, "", message, narrowed, loc),
2432
+ then: nodeThrowExpr(1, "", message, valueType, loc),
2388
2433
  else_: {
2389
2434
  kind: "unionNarrow",
2390
2435
  unionId: local.type.unionId,
2391
2436
  tag: valueTag,
2392
2437
  value: varRef(local.id, local.type, loc),
2393
- type: narrowed,
2438
+ type: valueType,
2394
2439
  loc,
2395
2440
  },
2396
- type: narrowed,
2441
+ type: valueType,
2397
2442
  loc,
2398
- };
2443
+ }, narrowedNode);
2399
2444
  }
2400
2445
  function runtimeOptionalUseOf(expr) {
2401
2446
  let receiver = expr;
@@ -3399,7 +3444,7 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3399
3444
  : { kind: "arrIntrinsic", method: "length", receiver, args: [], type: F64, loc: locOf(expr) };
3400
3445
  }
3401
3446
  if (kind === "string"
3402
- ? own(STR_METHODS, name) !== undefined || own(ISLAND_SURFACE.string, name) !== undefined
3447
+ ? own(STR_METHODS, name) !== undefined || STRING_INDEX_METHODS.has(name) || own(ISLAND_SURFACE.string, name) !== undefined
3403
3448
  : ARRAY_METHODS.has(name)) {
3404
3449
  lowerer.unsupported("SC1090", expr, `${kind} methods as values (call '${name}' directly)`);
3405
3450
  }
@@ -3416,7 +3461,10 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3416
3461
  export function lowerArrayLiteral(lowerer, expr, expected) {
3417
3462
  const loc = locOf(expr);
3418
3463
  const ctxType = lowerer.checker.getContextualType(expr);
3419
- const tsType = ctxType ?? lowerer.typeOf(expr);
3464
+ // `as const satisfies readonly T[]` checks against an array but keeps
3465
+ // the literal's inferred tuple type. Build that shape; an actual array
3466
+ // destination can use the ordinary tuple-to-array coercion afterward.
3467
+ const tsType = underConstAssertion(expr) ? lowerer.typeOf(expr) : ctxType ?? lowerer.typeOf(expr);
3420
3468
  let mapped = expected ?? lowerer.mapTypeOf(tsType);
3421
3469
  // A JS literal whose OWN inferred type is never-tainted
3422
3470
  // (neverTaintedJsType — the evolving `const gb = []`, the mixed
@@ -3529,13 +3577,13 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3529
3577
  // literal constructs the tuple's record shape — one positional field
3530
3578
  // per element, source order (which IS index order, so evaluation order
3531
3579
  // is JS-exact). tsc has already checked the arity; the recount below
3532
- // backstops `as` smuggling. Spreads have no fixed positions — fenced.
3580
+ // backstops `as` smuggling. Fixed tuple spreads have known positions.
3533
3581
  if (mapped?.kind === "record") {
3534
3582
  const shape = lowerer.shapes.get(mapped.shapeId);
3535
3583
  if (shape?.tuple) {
3536
3584
  const spread = expr.elements.find(ts.isSpreadElement);
3537
3585
  if (spread) {
3538
- lowerer.unsupported("SC1090", spread, "spread elements in tuple literals (positions must be spelled out)");
3586
+ return lowerTupleSpreadLiteral(lowerer, expr, mapped, shape);
3539
3587
  }
3540
3588
  if (expr.elements.length !== shape.fields.length) {
3541
3589
  // tsc padded an UNDER-LENGTH literal against an optional-element
@@ -3804,6 +3852,62 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3804
3852
  loc,
3805
3853
  };
3806
3854
  }
3855
+ /** Build a fixed tuple from positional elements and fixed tuple spreads.
3856
+ * Capture each value in source order: deferring the reads until recordLit
3857
+ * would observe a later element's mutations of the spread source. */
3858
+ function lowerTupleSpreadLiteral(lowerer, expr, type, shape) {
3859
+ const loc = locOf(expr);
3860
+ const byName = new Map(shape.fields.map((field) => [field.name, field.type]));
3861
+ const stmts = [];
3862
+ const fields = [];
3863
+ const append = (node, value) => {
3864
+ const name = String(fields.length);
3865
+ const expected = byName.get(name);
3866
+ if (!expected)
3867
+ lowerer.badType(expr, lowerer.typeOf(expr));
3868
+ const coerced = lowerer.coerceInto(node, value, expected);
3869
+ const temp = lowerer.declareHiddenLocal("%tupleElement", expected);
3870
+ const at = locOf(node);
3871
+ stmts.push({ kind: "varDecl", localId: temp.id, init: coerced, loc: at });
3872
+ fields.push({ name, value: varRef(temp.id, expected, at) });
3873
+ };
3874
+ for (const element of expr.elements) {
3875
+ if (!ts.isSpreadElement(element)) {
3876
+ const expected = byName.get(String(fields.length));
3877
+ if (!expected)
3878
+ lowerer.badType(element, lowerer.typeOf(element));
3879
+ append(element, lowerer.lowerExprExpecting(element, expected));
3880
+ continue;
3881
+ }
3882
+ const source = lowerer.lowerExpr(element.expression);
3883
+ const sourceShape = source.type.kind === "record" ? lowerer.shapes.get(source.type.shapeId) : undefined;
3884
+ if (!sourceShape?.tuple) {
3885
+ // Empty tuples use a zero-length array representation. Preserve a
3886
+ // producing call's effects even though there are no positions to copy.
3887
+ const sourceTs = lowerer.typeOf(element.expression);
3888
+ if (source.type.kind === "array" && lowerer.checker.isTupleType(sourceTs) &&
3889
+ lowerer.checker.getTypeArguments(sourceTs).length === 0) {
3890
+ stmts.push({ kind: "exprStmt", expr: source, loc: locOf(element) });
3891
+ continue;
3892
+ }
3893
+ lowerer.unsupported("SC1090", element, "spreading a variable-length value into a fixed tuple literal");
3894
+ }
3895
+ const temp = lowerer.declareHiddenLocal("%tupleSpread", source.type);
3896
+ const at = locOf(element);
3897
+ stmts.push({ kind: "varDecl", localId: temp.id, init: source, loc: at });
3898
+ const receiver = varRef(temp.id, source.type, at);
3899
+ const positions = [...sourceShape.fields].sort((a, b) => Number(a.name) - Number(b.name));
3900
+ for (const field of positions) {
3901
+ append(element.expression, {
3902
+ kind: "recordGet", obj: receiver, shapeId: sourceShape.id,
3903
+ field: field.name, type: field.type, loc: at,
3904
+ });
3905
+ }
3906
+ }
3907
+ if (fields.length !== shape.fields.length)
3908
+ lowerer.badType(expr, lowerer.typeOf(expr));
3909
+ return { kind: "seqExpr", stmts, result: { kind: "recordLit", fields, type, loc }, type, loc };
3910
+ }
3807
3911
  /** Convert an undefined-armed numeric value at an arithmetic use. Ordinary
3808
3912
  * reads preserve their tagged value; JavaScript's ToNumber(undefined) is NaN,
3809
3913
  * so compounds and binary arithmetic use this checked one-time extraction. */
@@ -4371,6 +4475,16 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
4371
4475
  // to observe the missing value instead of throwing during that check.
4372
4476
  return includeUndefined ? read : lowerer.maybeNarrow(read, expr);
4373
4477
  }
4478
+ // Predicates can strengthen optional fields without changing the
4479
+ // receiver's stored layout. Use that layout for bracket reads just as
4480
+ // fieldTarget does for the corresponding dot reads.
4481
+ if (obj.type.kind === "record") {
4482
+ const actualShape = lowerer.shapes.get(obj.type.shapeId);
4483
+ if (!actualShape)
4484
+ throw new InternalCompilerError("record receiver is missing its stored shape");
4485
+ shapeId = obj.type.shapeId;
4486
+ shape = actualShape;
4487
+ }
4374
4488
  if (litKey !== null) {
4375
4489
  const field = shape.fields.find((f) => f.name === litKey);
4376
4490
  if (field) {
@@ -6276,6 +6390,9 @@ export function lowerBinary(lowerer, expr) {
6276
6390
  case ts.SyntaxKind.EqualsEqualsEqualsToken:
6277
6391
  case ts.SyntaxKind.ExclamationEqualsEqualsToken: {
6278
6392
  const negated = op === ts.SyntaxKind.ExclamationEqualsEqualsToken;
6393
+ const indexed = tryLowerIndexedComparison(lowerer, left, right, negated, loc);
6394
+ if (indexed)
6395
+ return indexed;
6279
6396
  if (plainBothNum)
6280
6397
  return { kind: "bin", op: negated ? "!==" : "===", left, right, type: BOOL, loc };
6281
6398
  if (bothStr)
@@ -6603,7 +6720,7 @@ export function lowerCaughtTypeofTest(lowerer, expr, loc) {
6603
6720
  if (!ts.isStringLiteral(b)) {
6604
6721
  lowerer.unsupported("SC1090", expr, "'typeof' catch-binding tests against non-literal strings");
6605
6722
  }
6606
- if (b.text === "string" || b.text === "number" || b.text === "boolean") {
6723
+ if (b.text === "string" || b.text === "number" || b.text === "boolean" || b.text === "object") {
6607
6724
  return {
6608
6725
  kind: "caughtTest",
6609
6726
  value: { kind: "varRef", localId: local.id, type: CAUGHT, loc },
@@ -6613,8 +6730,7 @@ export function lowerCaughtTypeofTest(lowerer, expr, loc) {
6613
6730
  loc,
6614
6731
  };
6615
6732
  }
6616
- lowerer.unsupported("SC1090", b, `'typeof' catch-binding tests against "${b.text}" (only "string"/"number"/"boolean" ` +
6617
- `narrow a catch binding; use 'instanceof' for objects)`);
6733
+ lowerer.unsupported("SC1090", b, `'typeof' catch-binding tests against "${b.text}" (only "string"/"number"/"boolean"/"object" are supported)`);
6618
6734
  }
6619
6735
  return null;
6620
6736
  }