@scriptc/compiler 0.1.4 → 0.1.5

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 (150) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +75 -11
  2. package/dist/backend/c/c-emitter.d.ts +5 -1
  3. package/dist/backend/c/c-emitter.js +22 -9
  4. package/dist/backend/c/c-emitter.js.map +1 -1
  5. package/dist/backend/c/exprs.d.ts +2 -2
  6. package/dist/backend/c/exprs.js +264 -21
  7. package/dist/backend/c/exprs.js.map +1 -1
  8. package/dist/backend/c/stmts.js +5 -3
  9. package/dist/backend/c/stmts.js.map +1 -1
  10. package/dist/backend/c/types.d.ts +2 -3
  11. package/dist/backend/c/types.js +8 -1
  12. package/dist/backend/c/types.js.map +1 -1
  13. package/dist/backend/c/walkers.js +1 -1
  14. package/dist/backend/c/walkers.js.map +1 -1
  15. package/dist/backend/link-plan.d.ts +1 -0
  16. package/dist/backend/link-plan.js +2 -1
  17. package/dist/backend/link-plan.js.map +1 -1
  18. package/dist/backend/llvm/dyn.js +1 -1
  19. package/dist/backend/llvm/dyn.js.map +1 -1
  20. package/dist/backend/llvm/emitter.js +25 -9
  21. package/dist/backend/llvm/emitter.js.map +1 -1
  22. package/dist/backend/llvm/expr-bytes.d.ts +1 -2
  23. package/dist/backend/llvm/expr-bytes.js +22 -17
  24. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  25. package/dist/backend/llvm/expr-containers.d.ts +1 -0
  26. package/dist/backend/llvm/expr-containers.js +32 -4
  27. package/dist/backend/llvm/expr-containers.js.map +1 -1
  28. package/dist/backend/llvm/expr-context.d.ts +6 -2
  29. package/dist/backend/llvm/expr-primitives.js +22 -7
  30. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  31. package/dist/backend/llvm/lib-filesystem.js +12 -0
  32. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  33. package/dist/backend/llvm/lib-network.js +53 -10
  34. package/dist/backend/llvm/lib-network.js.map +1 -1
  35. package/dist/backend/llvm/lib-shared.js +64 -0
  36. package/dist/backend/llvm/lib-shared.js.map +1 -1
  37. package/dist/backend/native-toolchain.d.ts +2 -0
  38. package/dist/backend/native-toolchain.js +2 -1
  39. package/dist/backend/native-toolchain.js.map +1 -1
  40. package/dist/backend/targets.d.ts +3 -0
  41. package/dist/backend/targets.js +13 -0
  42. package/dist/backend/targets.js.map +1 -1
  43. package/dist/coverage/surface-manifest.js +6 -4
  44. package/dist/coverage/surface-manifest.js.map +1 -1
  45. package/dist/executable/executable-cache.d.ts +2 -0
  46. package/dist/executable/executable-cache.js +3 -1
  47. package/dist/executable/executable-cache.js.map +1 -1
  48. package/dist/executable/executable-cache.test.js +3 -3
  49. package/dist/executable/executable-cache.test.js.map +1 -1
  50. package/dist/frontend/lowering/containers/bytes.d.ts +1 -1
  51. package/dist/frontend/lowering/containers/bytes.js +6 -5
  52. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  53. package/dist/frontend/lowering/containers/string-and-regexp.d.ts +7 -6
  54. package/dist/frontend/lowering/containers/string-and-regexp.js +237 -28
  55. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  56. package/dist/frontend/lowering/expressions/object-literals.js +8 -0
  57. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  58. package/dist/frontend/lowering/lower-builtins.d.ts +4 -5
  59. package/dist/frontend/lowering/lower-builtins.js +18 -10
  60. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  61. package/dist/frontend/lowering/lower-calls.d.ts +3 -2
  62. package/dist/frontend/lowering/lower-calls.js +265 -88
  63. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  64. package/dist/frontend/lowering/lower-classes.js +2 -2
  65. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  66. package/dist/frontend/lowering/lower-containers.d.ts +7 -9
  67. package/dist/frontend/lowering/lower-containers.js +143 -51
  68. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  69. package/dist/frontend/lowering/lower-exprs.d.ts +12 -0
  70. package/dist/frontend/lowering/lower-exprs.js +128 -10
  71. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  72. package/dist/frontend/lowering/lower-island.js +17 -6
  73. package/dist/frontend/lowering/lower-island.js.map +1 -1
  74. package/dist/frontend/lowering/lower-server.d.ts +2 -4
  75. package/dist/frontend/lowering/lower-server.js +288 -32
  76. package/dist/frontend/lowering/lower-server.js.map +1 -1
  77. package/dist/frontend/lowering/lower-stmts.js +4 -4
  78. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  79. package/dist/frontend/lowering/lowerer.js +37 -2
  80. package/dist/frontend/lowering/lowerer.js.map +1 -1
  81. package/dist/frontend/lowering/optional-arguments.d.ts +9 -1
  82. package/dist/frontend/lowering/optional-arguments.js +67 -1
  83. package/dist/frontend/lowering/optional-arguments.js.map +1 -1
  84. package/dist/frontend/lowering/pairs-snapshot.d.ts +3 -1
  85. package/dist/frontend/lowering/pairs-snapshot.js +12 -4
  86. package/dist/frontend/lowering/pairs-snapshot.js.map +1 -1
  87. package/dist/frontend/lowering/surfaces.d.ts +8 -14
  88. package/dist/frontend/lowering/surfaces.js +37 -27
  89. package/dist/frontend/lowering/surfaces.js.map +1 -1
  90. package/dist/frontend/npm-static-prune.d.ts +6 -0
  91. package/dist/frontend/npm-static-prune.js +203 -0
  92. package/dist/frontend/npm-static-prune.js.map +1 -0
  93. package/dist/frontend/npm-static-prune.test.d.ts +1 -0
  94. package/dist/frontend/npm-static-prune.test.js +48 -0
  95. package/dist/frontend/npm-static-prune.test.js.map +1 -0
  96. package/dist/frontend/npm.js +7 -6
  97. package/dist/frontend/npm.js.map +1 -1
  98. package/dist/frontend/program.js +15 -4
  99. package/dist/frontend/program.js.map +1 -1
  100. package/dist/frontend/provenance.js +8 -3
  101. package/dist/frontend/provenance.js.map +1 -1
  102. package/dist/frontend/type-mapper.js +16 -5
  103. package/dist/frontend/type-mapper.js.map +1 -1
  104. package/dist/index.d.ts +3 -3
  105. package/dist/index.js +13 -41
  106. package/dist/index.js.map +1 -1
  107. package/dist/ir/analysis.d.ts +5 -3
  108. package/dist/ir/analysis.js +25 -11
  109. package/dist/ir/analysis.js.map +1 -1
  110. package/dist/ir/analysis.test.js +50 -2
  111. package/dist/ir/analysis.test.js.map +1 -1
  112. package/dist/ir/constant-tables.d.ts +17 -0
  113. package/dist/ir/constant-tables.js +99 -0
  114. package/dist/ir/constant-tables.js.map +1 -0
  115. package/dist/ir/constant-tables.test.d.ts +1 -0
  116. package/dist/ir/constant-tables.test.js +78 -0
  117. package/dist/ir/constant-tables.test.js.map +1 -0
  118. package/dist/ir/integer-ranges.d.ts +13 -0
  119. package/dist/ir/integer-ranges.js +122 -0
  120. package/dist/ir/integer-ranges.js.map +1 -0
  121. package/dist/ir/integer-ranges.test.d.ts +1 -0
  122. package/dist/ir/integer-ranges.test.js +63 -0
  123. package/dist/ir/integer-ranges.test.js.map +1 -0
  124. package/dist/ir/ir.d.ts +42 -34
  125. package/dist/ir/ir.js +7 -1
  126. package/dist/ir/ir.js.map +1 -1
  127. package/dist/ir/ir.test.js +6 -0
  128. package/dist/ir/ir.test.js.map +1 -1
  129. package/dist/ir/scalar-records.d.ts +8 -0
  130. package/dist/ir/scalar-records.js +216 -0
  131. package/dist/ir/scalar-records.js.map +1 -0
  132. package/dist/ir/scalar-records.test.d.ts +1 -0
  133. package/dist/ir/scalar-records.test.js +142 -0
  134. package/dist/ir/scalar-records.test.js.map +1 -0
  135. package/dist/ir/validate.js +92 -8
  136. package/dist/ir/validate.js.map +1 -1
  137. package/dist/ir/validate.test.d.ts +1 -0
  138. package/dist/ir/validate.test.js +31 -0
  139. package/dist/ir/validate.test.js.map +1 -0
  140. package/dist/library/cache-primitives.d.ts +1 -1
  141. package/dist/library/cache-primitives.js +3 -3
  142. package/dist/library/cache-primitives.js.map +1 -1
  143. package/dist/library/fence-eval.js +11 -4
  144. package/dist/library/fence-eval.js.map +1 -1
  145. package/dist/library/library-cache.js +0 -1
  146. package/dist/library/library-cache.js.map +1 -1
  147. package/dist/library/library-cache.test.js +3 -3
  148. package/dist/library/library-cache.test.js.map +1 -1
  149. package/package.json +17 -17
  150. package/surface-manifest.json +55 -42
@@ -164,10 +164,22 @@ export declare function lowerRecordKeyRead(lowerer: Lowerer, expr: ts.ElementAcc
164
164
  * around the read are erased here because this context handles the missing
165
165
  * arm instead of consuming it as the asserted type. */
166
166
  export declare function lowerAbsenceProbe(lowerer: Lowerer, node: ts.Expression): IrExpr | null;
167
+ /** `a[i] op= rhs` over native arrays and byte views. A compound assignment
168
+ * evaluates the receiver and index, reads the old element, evaluates rhs,
169
+ * then writes. Capture each stage so a call in the index or rhs cannot
170
+ * change the target or the value used by the operator. The expression yields
171
+ * the computed value before a typed array or Buffer coerces it for storage. */
172
+ export declare function lowerElementCompound(lowerer: Lowerer, expr: ts.BinaryExpression, op: CompoundOp): IrExpr;
167
173
  /** `a[i] = v` in statement position → arraySet (element writes, like local
168
174
  * assignment, produce no value in our subset). */
169
175
  export declare function lowerElementWrite(lowerer: Lowerer, expr: ts.BinaryExpression): IrStmt;
170
176
  export declare function ensureString(lowerer: Lowerer, e: IrExpr, node: ts.Node): IrExpr;
177
+ /** `new String(x)` (stdlib provenance, ≤1 argument) in a ToString
178
+ * position: the wrapper's only distinguishers are typeof and identity —
179
+ * neither is observable where the value immediately stringifies — so the
180
+ * span lowers as the argument's own ToString (`new String()` is "").
181
+ * Every other position keeps the wrapper-object constructor fence. */
182
+ export declare function stringWrapperToString(lowerer: Lowerer, node: ts.Expression): IrExpr | null;
171
183
  export declare function lowerTemplate(lowerer: Lowerer, expr: ts.TemplateExpression): IrExpr;
172
184
  /** `x as T`. Non-dyn inner values ordinarily erase; a record assertion
173
185
  * changing monomorphic shape rebuilds through the established width-copy
@@ -11,11 +11,11 @@ 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, 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, 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
- import { lowerNpmStaticSafeIndexRead, lowerSafeIndexRead, strCharsCall } from "./lower-containers.js";
18
- import { arrayValueStore } from "./array-values.js";
17
+ import { lowerNpmStaticSafeIndexRead, lowerSafeIndexRead, strCharsCall, tryLowerNumericIndexRead } from "./lower-containers.js";
18
+ import { arrayValueRead, arrayValueStore } from "./array-values.js";
19
19
  import { npmStaticPackageOfPath } from "../npm-static.js";
20
20
  import { unsupportedModuleFeatureOf } from "../builtin-modules.js";
21
21
  import { fenceEnumObjectValue, lowerEnumAccess } from "./lower-enums.js";
@@ -35,6 +35,7 @@ import { conditionalSpreadOf, foldedStringKeyOf } from "./expressions/object-lit
35
35
  import { tryLowerExpression } from "./expressions/try-lower-expression.js";
36
36
  import { fenceNodeModuleMutation, isNodeModuleValue, lowerNodeModuleIdentifier, lowerNodeModuleProperty, lowerRequireCacheElement, lowerRequireCacheHas, lowerRequireMainProperty } from "./lower-node-module.js";
37
37
  import { lowerAbstractEquality } from "./abstract-equality.js";
38
+ import { coerceStringSearchValue, defaultAfterUndefined, lowerStaticallyUndefinedArgument } from "./optional-arguments.js";
38
39
  /** A template piece's RAW text (String.raw's contract: escapes stay
39
40
  * characters). 7's client AST ships no rawText at runtime (the typing
40
41
  * declares it; the serialized node data omits it), so the raw span comes
@@ -871,6 +872,12 @@ function lowerExprInner(lowerer, expr) {
871
872
  if (bi.module === "module" && bi.member === "builtinModules") {
872
873
  return builtinModulesArrayLit(loc);
873
874
  }
875
+ if (bi.module === "http" && bi.member === "METHODS") {
876
+ return { kind: "libCall", fn: "http.methods", args: [], type: arrayOf(STRING), loc };
877
+ }
878
+ if (bi.module === "http" && bi.member === "STATUS_CODES") {
879
+ return { kind: "libCall", fn: "http.statusCodes", args: [], type: DYN, loc };
880
+ }
874
881
  // tls.rootCertificates: a runtime-valued module constant (the
875
882
  // cached bundled-CA array) — the one member read that lowers
876
883
  // to a libCall instead of a baked literal.
@@ -3804,6 +3811,11 @@ export function lowerOptionalNumber(lowerer, operand, loc, narrowedNode) {
3804
3811
  if (operand.type.kind !== "union" || lowerer.armTag(operand.type.unionId, UNDEFINED_T) < 0)
3805
3812
  return operand;
3806
3813
  const directNumber = lowerer.stripUndefinedArm(operand.type).kind === "f64";
3814
+ if (directNumber) {
3815
+ const scalarRead = tryLowerNumericIndexRead(lowerer, operand, loc);
3816
+ if (scalarRead)
3817
+ return scalarRead;
3818
+ }
3807
3819
  const checkerNumber = narrowedNode !== undefined && lowerer.mapTypeOf(lowerer.typeOf(narrowedNode))?.kind === "f64";
3808
3820
  if (!directNumber && (!checkerNumber || lowerer.armTag(operand.type.unionId, F64) < 0))
3809
3821
  return operand;
@@ -4582,6 +4594,78 @@ function tupleLiteralIndex(node) {
4582
4594
  const n = Number(e.text);
4583
4595
  return Number.isInteger(n) && n >= 0 ? n : null;
4584
4596
  }
4597
+ /** `a[i] op= rhs` over native arrays and byte views. A compound assignment
4598
+ * evaluates the receiver and index, reads the old element, evaluates rhs,
4599
+ * then writes. Capture each stage so a call in the index or rhs cannot
4600
+ * change the target or the value used by the operator. The expression yields
4601
+ * the computed value before a typed array or Buffer coerces it for storage. */
4602
+ export function lowerElementCompound(lowerer, expr, op) {
4603
+ const target = expr.left;
4604
+ fenceNodeModuleMutation(lowerer, target, "assignment");
4605
+ const loc = locOf(expr);
4606
+ const receiverType = lowerer.mapTypeOf(lowerer.typeOf(target.expression));
4607
+ if (receiverType?.kind !== "array" && receiverType?.kind !== "bytes") {
4608
+ lowerer.unsupported("SC1090", target, "compound assignment to non-array elements");
4609
+ }
4610
+ let receiver = lowerer.lowerExpr(target.expression);
4611
+ if (receiver.type.kind === "union" && lowerer.armTag(receiver.type.unionId, UNDEFINED_T) >= 0) {
4612
+ const present = lowerer.stripUndefinedArm(receiver.type);
4613
+ const helper = present.kind === receiverType.kind
4614
+ ? lowerer.narrowedArmHelper(receiver.type.unionId, present, locOf(target.expression))
4615
+ : null;
4616
+ if (helper)
4617
+ receiver = { kind: "call", callee: helper, args: [receiver], type: present, loc: locOf(target.expression) };
4618
+ }
4619
+ if (receiver.type.kind !== receiverType.kind) {
4620
+ lowerer.unsupported("SC1090", target.expression, "compound assignment through a non-native array or byte view");
4621
+ }
4622
+ const index = lowerer.lowerExpr(target.argumentExpression);
4623
+ if (index.type.kind !== "f64")
4624
+ lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-number keys");
4625
+ const receiverLocal = lowerer.declareHiddenLocal("%compoundArray", receiver.type);
4626
+ const indexLocal = lowerer.declareHiddenLocal("%compoundIndex", F64);
4627
+ const receiverRef = () => varRef(receiverLocal.id, receiver.type, loc);
4628
+ const indexRef = () => varRef(indexLocal.id, F64, loc);
4629
+ const oldValue = receiver.type.kind === "array"
4630
+ ? arrayValueRead(lowerer, receiverRef(), indexRef(), receiver.type.elem, locOf(target))
4631
+ : { kind: "bytesIntrinsic", method: "get", receiver: receiverRef(), args: [indexRef()], type: F64, loc: locOf(target) };
4632
+ const oldLocal = lowerer.declareHiddenLocal("%compoundOld", oldValue.type);
4633
+ const oldRef = () => varRef(oldLocal.id, oldValue.type, loc);
4634
+ const rhs = lowerer.lowerExpr(expr.right);
4635
+ const numericRhs = lowerOptionalNumber(lowerer, rhs, loc);
4636
+ const elementType = receiver.type.kind === "array" ? receiver.type.elem : F64;
4637
+ const valueType = elementType.kind === "union" && lowerer.armTag(elementType.unionId, UNDEFINED_T) >= 0
4638
+ ? lowerer.stripUndefinedArm(elementType)
4639
+ : elementType;
4640
+ let computed;
4641
+ if (op === "+" && valueType.kind === "string") {
4642
+ computed = { kind: "strConcat", left: lowerer.ensureString(oldRef(), target), right: lowerer.ensureString(rhs, expr.right), type: STRING, loc };
4643
+ }
4644
+ else if (valueType.kind === "f64" && numericRhs.type.kind === "f64") {
4645
+ computed = { kind: "bin", op, left: lowerOptionalNumber(lowerer, oldRef(), loc), right: numericRhs, type: F64, loc };
4646
+ }
4647
+ else {
4648
+ lowerer.unsupported("SC1043", expr);
4649
+ }
4650
+ const resultLocal = lowerer.declareHiddenLocal("%compoundResult", computed.type);
4651
+ const resultRef = () => varRef(resultLocal.id, computed.type, loc);
4652
+ const write = receiver.type.kind === "array"
4653
+ ? arrayValueStore(lowerer, receiverRef(), indexRef(), resultRef(), receiver.type.elem, loc)
4654
+ : { kind: "bytesSet", arr: receiverRef(), index: indexRef(), value: resultRef(), loc };
4655
+ return {
4656
+ kind: "seqExpr",
4657
+ stmts: [
4658
+ { kind: "varDecl", localId: receiverLocal.id, init: receiver, loc },
4659
+ { kind: "varDecl", localId: indexLocal.id, init: index, loc },
4660
+ { kind: "varDecl", localId: oldLocal.id, init: oldValue, loc },
4661
+ { kind: "varDecl", localId: resultLocal.id, init: computed, loc },
4662
+ write,
4663
+ ],
4664
+ result: resultRef(),
4665
+ type: computed.type,
4666
+ loc,
4667
+ };
4668
+ }
4585
4669
  /** `a[i] = v` in statement position → arraySet (element writes, like local
4586
4670
  * assignment, produce no value in our subset). */
4587
4671
  export function lowerElementWrite(lowerer, expr) {
@@ -4953,6 +5037,18 @@ export function ensureString(lowerer, e, node) {
4953
5037
  if (stringable) {
4954
5038
  return { kind: "toString", operand: e, type: STRING, loc: e.loc };
4955
5039
  }
5040
+ // Runtime-optional locals can retain their stored union after the
5041
+ // checker has narrowed this use to a primitive arm. Validate that
5042
+ // arm before converting it; an unguarded object arm stays fenced.
5043
+ const narrowed = lowerer.mapTypeOf(lowerer.typeOf(node));
5044
+ if (narrowed &&
5045
+ (narrowed.kind === "string" || narrowed.kind === "f64" ||
5046
+ narrowed.kind === "bool" || narrowed.kind === "bigint")) {
5047
+ const helper = lowerer.narrowedArmHelper(e.type.unionId, narrowed, e.loc);
5048
+ if (helper) {
5049
+ return ensureString(lowerer, { kind: "call", callee: helper, args: [e], type: narrowed, loc: e.loc }, node);
5050
+ }
5051
+ }
4956
5052
  lowerer.unsupported("SC1090", node, `string conversions of unions with object arms (${NARROW_FIRST})`);
4957
5053
  }
4958
5054
  if (e.type.kind === "symbol") {
@@ -5004,7 +5100,7 @@ export function ensureString(lowerer, e, node) {
5004
5100
  * neither is observable where the value immediately stringifies — so the
5005
5101
  * span lowers as the argument's own ToString (`new String()` is "").
5006
5102
  * Every other position keeps the wrapper-object constructor fence. */
5007
- function stringWrapperToString(lowerer, node) {
5103
+ export function stringWrapperToString(lowerer, node) {
5008
5104
  let e = node;
5009
5105
  while (ts.isParenthesizedExpression(e))
5010
5106
  e = e.expression;
@@ -5018,7 +5114,11 @@ function stringWrapperToString(lowerer, node) {
5018
5114
  return null;
5019
5115
  if (args.length === 0)
5020
5116
  return { kind: "strLit", value: "", type: STRING, loc: locOf(e) };
5021
- return lowerer.caughtToString(args[0]) ?? lowerer.ensureString(lowerer.lowerExpr(args[0]), args[0]);
5117
+ const undefinedArg = lowerStaticallyUndefinedArgument(lowerer, args[0]);
5118
+ if (undefinedArg)
5119
+ return defaultAfterUndefined(undefinedArg, { kind: "strLit", value: "undefined", type: STRING, loc: locOf(e) });
5120
+ return lowerer.caughtToString(args[0]) ??
5121
+ coerceStringSearchValue(lowerer, lowerer.lowerExpr(args[0]), args[0], locOf(e));
5022
5122
  }
5023
5123
  export function lowerTemplate(lowerer, expr) {
5024
5124
  const loc = locOf(expr);
@@ -5410,8 +5510,9 @@ export function lowerBinary(lowerer, expr) {
5410
5510
  // globals, captured/boxed included); the RHS coerces into the binding's
5411
5511
  // type exactly like statement position, and the expression's value is
5412
5512
  // the coerced binding-typed value (representation change only — never
5413
- // observably different from JS's raw-RHS yield). Compound operators,
5414
- // property/element targets, and destructuring targets stay fenced.
5513
+ // observably different from JS's raw-RHS yield). Indexed compounds
5514
+ // use a sequenced read/modify/write; other member targets and
5515
+ // destructuring targets stay fenced.
5415
5516
  if (op === ts.SyntaxKind.EqualsToken && ts.isIdentifier(expr.left)) {
5416
5517
  const target = lowerer.resolveWritable(expr.left);
5417
5518
  if (!target) {
@@ -5423,6 +5524,10 @@ export function lowerBinary(lowerer, expr) {
5423
5524
  lowerer.runtimeOptionalLocals.add(runtimeOptionalRoot);
5424
5525
  return { kind: "assignExpr", localId: target.id, value, type: target.type, loc };
5425
5526
  }
5527
+ const indexedCompound = COMPOUND_ASSIGN_OPS[op];
5528
+ if (indexedCompound !== undefined && ts.isElementAccessExpression(expr.left)) {
5529
+ return lowerElementCompound(lowerer, expr, indexedCompound);
5530
+ }
5426
5531
  // `events.defaultMaxListeners = v` — the module-property write
5427
5532
  // Node validates (validateNumber(n, 'defaultMaxListeners', 0)):
5428
5533
  // the value crosses into the checked-dynamic tree and the runtime ladder throws
@@ -8739,7 +8844,20 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
8739
8844
  // construction), typed as the index value armed with undefined under
8740
8845
  // noUncheckedIndexedAccess (mirroring lowerRecordKeyRead).
8741
8846
  if (target.container === "recordOvf") {
8742
- let t = target.fieldType;
8847
+ let obj = target.obj;
8848
+ if (obj.type.kind === "union" && ts.isPropertyAccessExpression(blame)) {
8849
+ const present = lowerer.stripUndefinedArm(obj.type);
8850
+ if (present.kind === "record") {
8851
+ obj = lowerer.runtimeOptionalPropertyReceiver(blame.expression, obj, present, target.field) ?? obj;
8852
+ }
8853
+ }
8854
+ if (obj.type.kind !== "record") {
8855
+ lowerer.unsupported("SC1090", blame, `reading '${target.field}' on a non-record receiver (narrow first)`);
8856
+ }
8857
+ const shape = lowerer.shapes.get(obj.type.shapeId);
8858
+ if (!shape?.indexValue)
8859
+ lowerer.unsupported("SC1090", blame, `reading '${target.field}' on a record without an index signature`);
8860
+ let t = shape.indexValue;
8743
8861
  if (lowerer.program.getCompilerOptions().noUncheckedIndexedAccess) {
8744
8862
  const armed = lowerer.withUndefinedArmOf(t);
8745
8863
  if (!armed)
@@ -8748,8 +8866,8 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
8748
8866
  }
8749
8867
  return {
8750
8868
  kind: "recordKeyGet",
8751
- obj: target.obj,
8752
- shapeId: target.shapeId,
8869
+ obj,
8870
+ shapeId: obj.type.shapeId,
8753
8871
  key: { kind: "strLit", value: target.field, type: STRING, loc },
8754
8872
  overflowOnly: true,
8755
8873
  type: t,