@scriptc/compiler 0.0.17 → 0.0.19

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 (62) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +4 -4
  2. package/dist/backend/cc.d.ts +5 -0
  3. package/dist/backend/cc.js +2 -0
  4. package/dist/backend/cc.js.map +1 -1
  5. package/dist/backend/emission/emit-async.js +35 -2
  6. package/dist/backend/emission/emit-async.js.map +1 -1
  7. package/dist/backend/emission/emit-exprs.js +40 -4
  8. package/dist/backend/emission/emit-exprs.js.map +1 -1
  9. package/dist/backend/emission/emitter.js +69 -11
  10. package/dist/backend/emission/emitter.js.map +1 -1
  11. package/dist/backend/emission/may-throw.d.ts +3 -3
  12. package/dist/backend/emission/may-throw.js +15 -4
  13. package/dist/backend/emission/may-throw.js.map +1 -1
  14. package/dist/backend/llvm/emitter.js +199 -21
  15. package/dist/backend/llvm/emitter.js.map +1 -1
  16. package/dist/frontend/lib-contract.d.ts +1 -0
  17. package/dist/frontend/lib-contract.js +2 -2
  18. package/dist/frontend/lib-contract.js.map +1 -1
  19. package/dist/frontend/lowering/lower-builtins.js +4 -7
  20. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  21. package/dist/frontend/lowering/lower-calls.js +17 -9
  22. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  23. package/dist/frontend/lowering/lower-containers.d.ts +22 -17
  24. package/dist/frontend/lowering/lower-containers.js +628 -51
  25. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  26. package/dist/frontend/lowering/lower-exprs.js +19 -1
  27. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  28. package/dist/frontend/lowering/lower-island.js +55 -7
  29. package/dist/frontend/lowering/lower-island.js.map +1 -1
  30. package/dist/frontend/lowering/lower-modules.js +144 -10
  31. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  32. package/dist/frontend/lowering/lower-stmts.d.ts +8 -0
  33. package/dist/frontend/lowering/lower-stmts.js +408 -21
  34. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  35. package/dist/frontend/lowering/lowerer.d.ts +38 -0
  36. package/dist/frontend/lowering/lowerer.js +149 -2
  37. package/dist/frontend/lowering/lowerer.js.map +1 -1
  38. package/dist/frontend/lowering/surfaces.js +4 -0
  39. package/dist/frontend/lowering/surfaces.js.map +1 -1
  40. package/dist/index.js +97 -21
  41. package/dist/index.js.map +1 -1
  42. package/dist/ir/nodes.d.ts +46 -12
  43. package/dist/ir/nodes.js +41 -0
  44. package/dist/ir/nodes.js.map +1 -1
  45. package/dist/ir/serialize.d.ts +1 -1
  46. package/dist/ir/serialize.js +1 -1
  47. package/dist/ir/validate.js +98 -39
  48. package/dist/ir/validate.js.map +1 -1
  49. package/dist/library/int-infer.d.ts +6 -1
  50. package/dist/library/int-infer.js +306 -53
  51. package/dist/library/int-infer.js.map +1 -1
  52. package/dist/library/int-infer.test.js +271 -1
  53. package/dist/library/int-infer.test.js.map +1 -1
  54. package/dist/library/profile.d.ts +6 -5
  55. package/dist/library/profile.js.map +1 -1
  56. package/dist/library/sidecar-validate.js +4 -1
  57. package/dist/library/sidecar-validate.js.map +1 -1
  58. package/dist/library/sidecar.d.ts +32 -4
  59. package/dist/library/sidecar.js +264 -47
  60. package/dist/library/sidecar.js.map +1 -1
  61. package/package.json +2 -2
  62. package/surface-manifest.json +25 -1
@@ -930,12 +930,13 @@ export function lowerVarStatement(L, stmt) {
930
930
  return [];
931
931
  return L.lowerDestructuringDecl(decl, isLet);
932
932
  }
933
- // A STORED matchAll drain (`const rows = s.matchAll(re)`): the drain
934
- // runs through matchAllInto with a hidden companion index array so a
935
- // later for-of over `rows` in the same function can serve `m.index`.
936
- // Plain local consts only — module globals, TDZ-predeclared consts,
937
- // and annotated non-drain types keep the ordinary path (and stored-
938
- // row .index keeps its fence).
933
+ // STORED protocol values with statically closed representations:
934
+ // numeric value iterators retain source/cursor/done hidden locals; a
935
+ // matchAll drain retains its companion index array so a later
936
+ // for-of can serve `m.index`. Both are const-only interceptions.
937
+ const numericIterator = lowerNumericIteratorDecl(L, decl, isConst);
938
+ if (numericIterator)
939
+ return numericIterator;
939
940
  const drained = lowerMatchAllDrainDecl(L, decl, isConst);
940
941
  if (drained)
941
942
  return drained;
@@ -943,6 +944,88 @@ export function lowerVarStatement(L, stmt) {
943
944
  return lowered ? [lowered] : [];
944
945
  });
945
946
  }
947
+ /** The numeric indexed source of a built-in value-iterator expression:
948
+ * `numbers.values()` or the equivalent
949
+ * `numbers[Symbol.iterator]()`, where numbers is number[] or one of the
950
+ * represented typed arrays (including Uint8Array). This is deliberately
951
+ * a syntax + provenance test rather than a mapping for the iterator
952
+ * object: it has observable mutable protocol state, represented by
953
+ * hidden locals only while a stored binding stays in one function. */
954
+ export function numericIteratorSourceOf(L, expr) {
955
+ if (expr === undefined)
956
+ return null;
957
+ let init = expr;
958
+ while (ts.isParenthesizedExpression(init))
959
+ init = init.expression;
960
+ if (!ts.isCallExpression(init) || init.questionDotToken || init.arguments.length !== 0)
961
+ return null;
962
+ let receiver = null;
963
+ if (ts.isPropertyAccessExpression(init.expression) &&
964
+ !init.expression.questionDotToken &&
965
+ init.expression.name.text === "values" &&
966
+ L.isStdlibMember(init.expression)) {
967
+ receiver = init.expression.expression;
968
+ }
969
+ else if (ts.isElementAccessExpression(init.expression) &&
970
+ !init.expression.questionDotToken &&
971
+ init.expression.argumentExpression !== undefined) {
972
+ let key = init.expression.argumentExpression;
973
+ while (ts.isParenthesizedExpression(key))
974
+ key = key.expression;
975
+ if (ts.isPropertyAccessExpression(key) &&
976
+ L.stdlibGlobalMember(key, "Symbol") === "iterator") {
977
+ receiver = init.expression.expression;
978
+ }
979
+ }
980
+ if (receiver === null)
981
+ return null;
982
+ const sourceT = L.mapTypeOf(L.typeOf(receiver));
983
+ return ((sourceT?.kind === "array" && sourceT.elem.kind === "f64") ||
984
+ sourceT?.kind === "bytes") ? receiver : null;
985
+ }
986
+ /** A stored numeric value iterator does not become a first-class IR
987
+ * value. Its standard-library provenance proves the built-in iterator
988
+ * shape, so the declaration snapshots the indexed source and initializes
989
+ * the exact mutable protocol state:
990
+ *
991
+ * const %source = numbers; let %index = 0; let %done = false;
992
+ *
993
+ * A same-function for-of resolves the binding symbol through
994
+ * numericIterators and drives these locals. Other value uses retain the
995
+ * iterator-object fence. */
996
+ function lowerNumericIteratorDecl(L, decl, isConst) {
997
+ if (!isConst || !ts.isIdentifier(decl.name))
998
+ return null;
999
+ const sourceNode = numericIteratorSourceOf(L, decl.initializer);
1000
+ if (sourceNode === null)
1001
+ return null;
1002
+ const sym = L.checker.getSymbolAtLocation(decl.name);
1003
+ if (!sym || L.tdzPredeclared.has(sym))
1004
+ return null;
1005
+ const source = L.lowerExpr(sourceNode);
1006
+ if (!((source.type.kind === "array" && source.type.elem.kind === "f64") ||
1007
+ source.type.kind === "bytes")) {
1008
+ L.badType(sourceNode, L.typeOf(sourceNode));
1009
+ }
1010
+ const loc = locOf(decl);
1011
+ const indexedSource = L.declareHiddenLocal("%numiterSource", source.type);
1012
+ const index = L.declareHiddenLocal("%numiterIndex", F64);
1013
+ const done = L.declareHiddenLocal("%numiterDone", BOOL);
1014
+ index.mutable = true;
1015
+ done.mutable = true;
1016
+ L.numericIterators.set(sym, {
1017
+ sourceLocalId: indexedSource.id,
1018
+ sourceType: source.type,
1019
+ indexLocalId: index.id,
1020
+ doneLocalId: done.id,
1021
+ ctx: L.ctx,
1022
+ });
1023
+ return [
1024
+ { kind: "varDecl", localId: indexedSource.id, init: source, loc },
1025
+ { kind: "varDecl", localId: index.id, init: { kind: "numLit", value: 0, type: F64, loc }, loc },
1026
+ { kind: "varDecl", localId: done.id, init: { kind: "boolLit", value: false, type: BOOL, loc }, loc },
1027
+ ];
1028
+ }
946
1029
  /** The stored-drain interception behind lowerVarStatement: lowers
947
1030
  * `const rows = s.matchAll(re)` (a direct stdlib matchAll initializer on
948
1031
  * a plain const identifier local) as the companion-index drain and
@@ -1338,6 +1421,53 @@ export function lowerBindingPattern(L, pattern, srcRef, srcType, isLet, out, dyn
1338
1421
  });
1339
1422
  return;
1340
1423
  }
1424
+ // Represented typed arrays are dense numeric iterables. Destructuring
1425
+ // reads their elements in index order; a rest binding drains to a
1426
+ // fresh number[] (never another typed array), exactly the iterator
1427
+ // protocol's Array accumulation.
1428
+ if (srcType.kind === "bytes") {
1429
+ pattern.elements.forEach((el, i) => {
1430
+ if (ts.isOmittedExpression(el) || el.name === undefined)
1431
+ return;
1432
+ const loc = locOf(el);
1433
+ if (el.dotDotDotToken) {
1434
+ if (el.initializer) {
1435
+ L.unsupported("SC1031", el, "defaults on rest elements");
1436
+ }
1437
+ const all = {
1438
+ kind: "bytesIntrinsic",
1439
+ method: "toArray",
1440
+ receiver: srcRef(),
1441
+ args: [],
1442
+ type: arrayOf(F64),
1443
+ loc,
1444
+ };
1445
+ const value = {
1446
+ kind: "arrIntrinsic",
1447
+ method: "slice",
1448
+ receiver: all,
1449
+ args: [{ kind: "numLit", value: i, type: F64, loc }],
1450
+ type: arrayOf(F64),
1451
+ loc,
1452
+ };
1453
+ L.bindPatternTarget(el.name, value, isLet, out);
1454
+ return;
1455
+ }
1456
+ let value = {
1457
+ kind: "bytesIntrinsic",
1458
+ method: "get",
1459
+ receiver: srcRef(),
1460
+ args: [{ kind: "numLit", value: i, type: F64, loc }],
1461
+ type: F64,
1462
+ loc,
1463
+ };
1464
+ if (el.initializer) {
1465
+ value = arrayPositionDefault(L, el, value, srcRef, i, F64, out);
1466
+ }
1467
+ L.bindPatternTarget(el.name, value, isLet, out);
1468
+ });
1469
+ return;
1470
+ }
1341
1471
  if (srcType.kind !== "array") {
1342
1472
  L.unsupported("SC1031", pattern, `array destructuring of non-array values (the source is ${srcType.kind}-typed)`);
1343
1473
  }
@@ -1979,11 +2109,29 @@ function arrayPositionDefaultValue(L, blame, init, read, srcRef, index, elemT, b
1979
2109
  if (isUnitType(elemT)) {
1980
2110
  return L.lowerExprExpecting(init, bodyT);
1981
2111
  }
2112
+ const source = srcRef();
2113
+ const length = source.type.kind === "bytes"
2114
+ ? {
2115
+ kind: "bytesIntrinsic",
2116
+ method: "length",
2117
+ receiver: source,
2118
+ args: [],
2119
+ type: F64,
2120
+ loc,
2121
+ }
2122
+ : {
2123
+ kind: "arrIntrinsic",
2124
+ method: "length",
2125
+ receiver: source,
2126
+ args: [],
2127
+ type: F64,
2128
+ loc,
2129
+ };
1982
2130
  const inRange = {
1983
2131
  kind: "bin",
1984
2132
  op: "<",
1985
2133
  left: { kind: "numLit", value: index, type: F64, loc },
1986
- right: { kind: "arrIntrinsic", method: "length", receiver: srcRef(), args: [], type: F64, loc },
2134
+ right: length,
1987
2135
  type: BOOL,
1988
2136
  loc,
1989
2137
  };
@@ -5195,8 +5343,8 @@ function lowerArrayAssignInto(L, out, target, tmpRef, blame, loc) {
5195
5343
  });
5196
5344
  elemsRef = () => ({ kind: "varRef", localId: iterTmp.id, type: srcType, loc });
5197
5345
  }
5198
- else if (srcType.kind !== "array" && !isTuple) {
5199
- if (elements.length === 0 && (srcType.kind === "string" || srcType.kind === "bytes" || srcType.kind === "map" || srcType.kind === "set")) {
5346
+ else if (srcType.kind !== "array" && srcType.kind !== "bytes" && !isTuple) {
5347
+ if (elements.length === 0 && (srcType.kind === "string" || srcType.kind === "map" || srcType.kind === "set")) {
5200
5348
  // `[] = e` over a statically-iterable source: GetIterator +
5201
5349
  // immediate close — no user code can observe it, so evaluating
5202
5350
  // the RHS is the whole statement.
@@ -5220,6 +5368,16 @@ function lowerArrayAssignInto(L, out, target, tmpRef, blame, loc) {
5220
5368
  }
5221
5369
  return { kind: "recordGet", obj: tmpRef(), shapeId: srcType.shapeId, field: String(i), type: fieldType, loc: locOf(at) };
5222
5370
  }
5371
+ if (srcType.kind === "bytes") {
5372
+ return {
5373
+ kind: "bytesIntrinsic",
5374
+ method: "get",
5375
+ receiver: tmpRef(),
5376
+ args: [{ kind: "numLit", value: i, type: F64, loc: locOf(at) }],
5377
+ type: F64,
5378
+ loc: locOf(at),
5379
+ };
5380
+ }
5223
5381
  // Plain arrays read by index — a pattern wider than the runtime
5224
5382
  // array traps (the arrayGet discipline, divergence 4's policy; JS
5225
5383
  // would answer undefined).
@@ -5257,6 +5415,24 @@ function lowerArrayAssignInto(L, out, target, tmpRef, blame, loc) {
5257
5415
  // defaults on nested targets fence in the shared machinery).
5258
5416
  const readOf = (targetT) => {
5259
5417
  if (isRest) {
5418
+ if (srcType.kind === "bytes") {
5419
+ const all = {
5420
+ kind: "bytesIntrinsic",
5421
+ method: "toArray",
5422
+ receiver: tmpRef(),
5423
+ args: [],
5424
+ type: arrayOf(F64),
5425
+ loc: locOf(el),
5426
+ };
5427
+ return {
5428
+ kind: "arrIntrinsic",
5429
+ method: "slice",
5430
+ receiver: all,
5431
+ args: [{ kind: "numLit", value: i, type: F64, loc: locOf(el) }],
5432
+ type: arrayOf(F64),
5433
+ loc: locOf(el),
5434
+ };
5435
+ }
5260
5436
  return isTuple
5261
5437
  ? tupleTailValue(L, el, tmpRef, srcType, shape, i, targetT)
5262
5438
  : { kind: "arrIntrinsic", method: "slice", receiver: tmpRef(), args: [{ kind: "numLit", value: i, type: F64, loc: locOf(el) }], type: srcType, loc: locOf(el) };
@@ -5295,7 +5471,9 @@ function lowerArrayAssignInto(L, out, target, tmpRef, blame, loc) {
5295
5471
  else if (targetT !== null) {
5296
5472
  read = isTuple
5297
5473
  ? undefArmDefault(L, el, defaultNode, read, targetT)
5298
- : arrayPositionDefaultValue(L, el, defaultNode, read, tmpRef, i, srcType.elem, targetT, out);
5474
+ : arrayPositionDefaultValue(L, el, defaultNode, read, tmpRef, i, srcType.kind === "bytes"
5475
+ ? F64
5476
+ : srcType.elem, targetT, out);
5299
5477
  }
5300
5478
  }
5301
5479
  return read;
@@ -5351,6 +5529,22 @@ export function lowerForOf(L, stmt) {
5351
5529
  }
5352
5530
  L.unsupported("SC1070", stmt, "'for await' (async iteration over anything but process.stdin and readable streams)");
5353
5531
  }
5532
+ // A stored numeric value iterator declared in this function keeps its
5533
+ // built-in protocol state in hidden locals (lowerVarStatement). The
5534
+ // iterator's own [Symbol.iterator]() returns itself, so for-of resumes
5535
+ // that cursor rather than starting another indexed-source walk.
5536
+ {
5537
+ let src = stmt.expression;
5538
+ while (ts.isParenthesizedExpression(src))
5539
+ src = src.expression;
5540
+ if (ts.isIdentifier(src)) {
5541
+ const sym = L.resolveValueSymbol(src);
5542
+ const state = sym ? L.numericIterators.get(sym) : undefined;
5543
+ if (state?.ctx === L.ctx) {
5544
+ return lowerForOfStoredNumericIterator(L, stmt, state, labels);
5545
+ }
5546
+ }
5547
+ }
5354
5548
  // `for (const k of m.keys())` / `.values()` / `.entries()`: the
5355
5549
  // iterator methods consumed DIRECTLY by a for-of head ride the
5356
5550
  // container's live walk — exactly `for..of m` with the projection
@@ -5396,6 +5590,15 @@ export function lowerForOf(L, stmt) {
5396
5590
  return lowerForOfArrayIter(L, stmt, container, proj);
5397
5591
  }
5398
5592
  }
5593
+ // Typed arrays expose the same live indexed iterator projections.
5594
+ // Their fixed length makes the walk simpler, but the yielded keys
5595
+ // and [key, numeric value] pairs are identical to Array's.
5596
+ if (recv?.kind === "bytes" && (proj === "keys" || proj === "entries")) {
5597
+ const container = L.lowerExpr(src.expression.expression);
5598
+ if (container.type.kind === "bytes") {
5599
+ return lowerForOfArrayIter(L, stmt, container, proj);
5600
+ }
5601
+ }
5399
5602
  }
5400
5603
  }
5401
5604
  // `for (const m of s.matchAll(re))` — direct call or a stored-const
@@ -5429,25 +5632,32 @@ export function lowerForOf(L, stmt) {
5429
5632
  }
5430
5633
  }
5431
5634
  }
5432
- // `for (v of xs.values())` over an ARRAY receiver: Array.prototype
5433
- // .values() iterates the array's elements exactly — the iterator
5434
- // object never needs to exist, so the loop runs over the receiver
5435
- // itself (matchAll's eager-drain stance). Only the DIRECT for-of
5436
- // position unwraps; a stored iterator value keeps its fence.
5437
- let iterSrc = stmt.expression;
5438
- {
5635
+ // A DIRECT built-in value-iterator expression needs no first-class
5636
+ // iterator object. The numeric recognizer covers number[] / typed
5637
+ // arrays and both values/default spellings; retain the established
5638
+ // generic Array.prototype.values() unwrap for every other T[] too.
5639
+ // Stored numeric values took the retained-state path above.
5640
+ let iterSrc = numericIteratorSourceOf(L, stmt.expression);
5641
+ if (iterSrc === null) {
5439
5642
  let e = stmt.expression;
5440
5643
  while (ts.isParenthesizedExpression(e))
5441
5644
  e = e.expression;
5442
- if (ts.isCallExpression(e) && !e.questionDotToken && e.arguments.length === 0 &&
5443
- ts.isPropertyAccessExpression(e.expression) && !e.expression.questionDotToken &&
5645
+ if (ts.isCallExpression(e) &&
5646
+ !e.questionDotToken &&
5647
+ e.arguments.length === 0 &&
5648
+ ts.isPropertyAccessExpression(e.expression) &&
5649
+ !e.expression.questionDotToken &&
5444
5650
  e.expression.name.text === "values" &&
5445
5651
  L.isStdlibMember(e.expression) &&
5446
5652
  L.mapTypeOf(L.typeOf(e.expression.expression))?.kind === "array") {
5447
5653
  iterSrc = e.expression.expression;
5448
5654
  }
5449
5655
  }
5656
+ iterSrc ??= stmt.expression;
5450
5657
  let iterable = L.lowerExpr(iterSrc);
5658
+ if (iterable.type.kind === "bytes") {
5659
+ return lowerForOfBytes(L, stmt, iterable, labels);
5660
+ }
5451
5661
  if (iterable.type.kind !== "array") {
5452
5662
  // The lib types strings as iterable too, so those for-ofs typecheck;
5453
5663
  // only arrays, Maps, and Sets have a lowering. Named specifically —
@@ -5665,6 +5875,182 @@ export function lowerForOf(L, stmt) {
5665
5875
  L.scopes.pop();
5666
5876
  }
5667
5877
  }
5878
+ /** Direct for-of over a represented typed array. The implicit iterator is
5879
+ * unobservable, so instantiate its source/cursor/done state as ordinary
5880
+ * hidden locals and share the retained numeric-iterator driver. */
5881
+ function lowerForOfBytes(L, stmt, iterable, labels) {
5882
+ const loc = locOf(stmt);
5883
+ const source = L.declareHiddenLocal("%numiterSource", iterable.type);
5884
+ const index = L.declareHiddenLocal("%numiterIndex", F64);
5885
+ const done = L.declareHiddenLocal("%numiterDone", BOOL);
5886
+ index.mutable = true;
5887
+ done.mutable = true;
5888
+ const loop = lowerForOfStoredNumericIterator(L, stmt, {
5889
+ sourceLocalId: source.id,
5890
+ sourceType: iterable.type,
5891
+ indexLocalId: index.id,
5892
+ doneLocalId: done.id,
5893
+ }, labels);
5894
+ return {
5895
+ kind: "block",
5896
+ body: [
5897
+ { kind: "varDecl", localId: source.id, init: iterable, loc },
5898
+ { kind: "varDecl", localId: index.id, init: { kind: "numLit", value: 0, type: F64, loc }, loc },
5899
+ { kind: "varDecl", localId: done.id, init: { kind: "boolLit", value: false, type: BOOL, loc }, loc },
5900
+ loop,
5901
+ ],
5902
+ loc,
5903
+ };
5904
+ }
5905
+ /** Drive a stored built-in numeric value iterator through its retained
5906
+ * protocol state. Unlike the ordinary array for-of IR, the cursor
5907
+ * advances BEFORE the source body: breaking or returning from the body
5908
+ * leaves the iterator positioned at the next element. Natural
5909
+ * exhaustion sets the sticky done bit; breaking early does not.
5910
+ *
5911
+ * while (true) {
5912
+ * if (%done) break;
5913
+ * if (%index >= %source.length) { %done = true; break; }
5914
+ * const value = %source[%index];
5915
+ * %index += 1;
5916
+ * <assign/bind head>; <body>
5917
+ * }
5918
+ */
5919
+ function lowerForOfStoredNumericIterator(L, stmt, state, labels) {
5920
+ let exprTarget = null;
5921
+ let exprTargetNode = null;
5922
+ if (!ts.isVariableDeclarationList(stmt.initializer)) {
5923
+ let target = stmt.initializer;
5924
+ while (ts.isParenthesizedExpression(target))
5925
+ target = target.expression;
5926
+ if (ts.isIdentifier(target)) {
5927
+ exprTarget = L.resolveWritable(target);
5928
+ exprTargetNode = target;
5929
+ }
5930
+ if (!exprTarget) {
5931
+ L.unsupported("SC1090", stmt.initializer, "for-of over a stored numeric iterator assigning anything but a pre-declared identifier");
5932
+ }
5933
+ }
5934
+ const list = ts.isVariableDeclarationList(stmt.initializer) ? stmt.initializer : null;
5935
+ if (list && (list.flags & ts.NodeFlags.Using) !== 0) {
5936
+ L.unsupported("SC1090", list, "'using' declarations (dispose-at-scope-exit semantics)");
5937
+ }
5938
+ const isLet = list !== null && (list.flags & ts.NodeFlags.Let) !== 0;
5939
+ const decl = list ? list.declarations[0] : null;
5940
+ if (decl && !ts.isIdentifier(decl.name))
5941
+ L.unsupported("SC1031", decl.name);
5942
+ const loc = locOf(stmt);
5943
+ const sourceT = state.sourceType;
5944
+ if (!((sourceT.kind === "array" && sourceT.elem.kind === "f64") ||
5945
+ sourceT.kind === "bytes")) {
5946
+ throw new Error("internal: retained numeric iterator has a non-numeric source");
5947
+ }
5948
+ const ref = (localId, type) => ({ kind: "varRef", localId, type, loc });
5949
+ const indexRef = () => ref(state.indexLocalId, F64);
5950
+ const doneRef = () => ref(state.doneLocalId, BOOL);
5951
+ const sourceRef = () => ref(state.sourceLocalId, sourceT);
5952
+ const sourceLength = () => sourceT.kind === "array"
5953
+ ? {
5954
+ kind: "arrIntrinsic",
5955
+ method: "length",
5956
+ receiver: sourceRef(),
5957
+ args: [],
5958
+ type: F64,
5959
+ loc,
5960
+ }
5961
+ : {
5962
+ kind: "bytesIntrinsic",
5963
+ method: "length",
5964
+ receiver: sourceRef(),
5965
+ args: [],
5966
+ type: F64,
5967
+ loc,
5968
+ };
5969
+ const sourceValue = () => sourceT.kind === "array"
5970
+ ? { kind: "arrayGet", arr: sourceRef(), index: indexRef(), type: F64, loc }
5971
+ : {
5972
+ kind: "bytesIntrinsic",
5973
+ method: "get",
5974
+ receiver: sourceRef(),
5975
+ args: [indexRef()],
5976
+ type: F64,
5977
+ loc,
5978
+ };
5979
+ const one = { kind: "numLit", value: 1, type: F64, loc };
5980
+ L.scopes.push(new Map());
5981
+ try {
5982
+ const varTarget = exprTarget ?? (decl ? forOfVarTarget(L, decl) : null);
5983
+ const value = varTarget
5984
+ ? L.declareHiddenLocal("%numiterValue", F64)
5985
+ : L.declareLocal(decl.name, decl.name.text, F64, isLet);
5986
+ const valueRef = ref(value.id, F64);
5987
+ const blame = decl?.name ?? exprTargetNode ?? stmt.initializer;
5988
+ const head = [
5989
+ {
5990
+ kind: "if",
5991
+ cond: doneRef(),
5992
+ then: [{ kind: "break", loc }],
5993
+ else_: null,
5994
+ loc,
5995
+ },
5996
+ {
5997
+ kind: "if",
5998
+ cond: {
5999
+ kind: "bin",
6000
+ op: ">=",
6001
+ left: indexRef(),
6002
+ right: sourceLength(),
6003
+ type: BOOL,
6004
+ loc,
6005
+ },
6006
+ then: [
6007
+ {
6008
+ kind: "assign",
6009
+ localId: state.doneLocalId,
6010
+ value: { kind: "boolLit", value: true, type: BOOL, loc },
6011
+ loc,
6012
+ },
6013
+ { kind: "break", loc },
6014
+ ],
6015
+ else_: null,
6016
+ loc,
6017
+ },
6018
+ {
6019
+ kind: "varDecl",
6020
+ localId: value.id,
6021
+ init: sourceValue(),
6022
+ loc,
6023
+ },
6024
+ {
6025
+ kind: "assign",
6026
+ localId: state.indexLocalId,
6027
+ value: { kind: "bin", op: "+", left: indexRef(), right: one, type: F64, loc },
6028
+ loc,
6029
+ },
6030
+ ...(varTarget
6031
+ ? [
6032
+ {
6033
+ kind: "assign",
6034
+ localId: varTarget.id,
6035
+ value: L.coerceInto(blame, valueRef, varTarget.type),
6036
+ loc,
6037
+ },
6038
+ ]
6039
+ : []),
6040
+ ];
6041
+ const body = L.inCtl("loop", () => L.lowerScopedBlock(stmt.statement), labels);
6042
+ return {
6043
+ kind: "while",
6044
+ cond: { kind: "boolLit", value: true, type: BOOL, loc },
6045
+ body: [...head, ...body],
6046
+ ...(labels && { labels }),
6047
+ loc,
6048
+ };
6049
+ }
6050
+ finally {
6051
+ L.scopes.pop();
6052
+ }
6053
+ }
5668
6054
  /** A DIRECT `s.matchAll(re)` call: a stdlib matchAll property call on a
5669
6055
  * string-typed receiver with one regex-typed argument (parens stripped).
5670
6056
  * The shape the companion-index machinery serves. */
@@ -6561,8 +6947,9 @@ function lowerForInArray(L, stmt, labels) {
6561
6947
  * POSIX read never yields an empty data chunk, so EOF is the only way
6562
6948
  * to see one. Awaiting parks the fiber like any await, so timers and
6563
6949
  * other fibers interleave with the reads exactly as in Node; the loop
6564
- * variable is a fresh const Uint8Array binding per iteration. Only
6565
- * async-function bodies can host it (the await parks a fiber). */
6950
+ * variable is a fresh const Uint8Array binding per iteration. Async
6951
+ * function bodies and async module initializers can host it (the await
6952
+ * parks their fiber). */
6566
6953
  function lowerForAwaitStdin(L, stmt) {
6567
6954
  if (!L.ctx.isAsync) {
6568
6955
  L.unsupported("SC1090", stmt, "top-level 'for await' (await outside async functions)");