@scriptc/compiler 0.0.33 → 0.0.35

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 (133) hide show
  1. package/dist/backend/cc-cache.test.js +1091 -7
  2. package/dist/backend/cc-cache.test.js.map +1 -1
  3. package/dist/backend/cc.d.ts +109 -18
  4. package/dist/backend/cc.js +1057 -289
  5. package/dist/backend/cc.js.map +1 -1
  6. package/dist/backend/emission/emit-exprs.js +26 -1
  7. package/dist/backend/emission/emit-exprs.js.map +1 -1
  8. package/dist/backend/emission/emit-shapes.js +37 -4
  9. package/dist/backend/emission/emit-shapes.js.map +1 -1
  10. package/dist/backend/emission/emitter.d.ts +11 -2
  11. package/dist/backend/emission/emitter.js +11 -5
  12. package/dist/backend/emission/emitter.js.map +1 -1
  13. package/dist/backend/library-identity.d.ts +22 -0
  14. package/dist/backend/library-identity.js +103 -0
  15. package/dist/backend/library-identity.js.map +1 -0
  16. package/dist/backend/llvm/emitter.d.ts +3 -0
  17. package/dist/backend/llvm/emitter.js +35 -5
  18. package/dist/backend/llvm/emitter.js.map +1 -1
  19. package/dist/backend/llvm/shapes.d.ts +1 -0
  20. package/dist/backend/llvm/shapes.js +25 -1
  21. package/dist/backend/llvm/shapes.js.map +1 -1
  22. package/dist/backend/llvm/split.d.ts +43 -0
  23. package/dist/backend/llvm/split.js +227 -0
  24. package/dist/backend/llvm/split.js.map +1 -0
  25. package/dist/backend/llvm/split.test.d.ts +1 -0
  26. package/dist/backend/llvm/split.test.js +102 -0
  27. package/dist/backend/llvm/split.test.js.map +1 -0
  28. package/dist/backend/mangle.d.ts +1 -0
  29. package/dist/backend/mangle.js +3 -0
  30. package/dist/backend/mangle.js.map +1 -1
  31. package/dist/backend/object-localize.d.ts +5 -5
  32. package/dist/backend/object-localize.js +19 -16
  33. package/dist/backend/object-localize.js.map +1 -1
  34. package/dist/backend/object-localize.test.js +57 -13
  35. package/dist/backend/object-localize.test.js.map +1 -1
  36. package/dist/executable/early-cache.d.ts +86 -0
  37. package/dist/executable/early-cache.js +460 -0
  38. package/dist/executable/early-cache.js.map +1 -0
  39. package/dist/executable/early-cache.test.d.ts +1 -0
  40. package/dist/executable/early-cache.test.js +300 -0
  41. package/dist/executable/early-cache.test.js.map +1 -0
  42. package/dist/ffi/profile.d.ts +7 -2
  43. package/dist/ffi/profile.js +1 -0
  44. package/dist/ffi/profile.js.map +1 -1
  45. package/dist/frontend/input-tracker.d.ts +82 -0
  46. package/dist/frontend/input-tracker.js +380 -0
  47. package/dist/frontend/input-tracker.js.map +1 -0
  48. package/dist/frontend/input-tracker.test.d.ts +1 -0
  49. package/dist/frontend/input-tracker.test.js +137 -0
  50. package/dist/frontend/input-tracker.test.js.map +1 -0
  51. package/dist/frontend/lowering/lower-builtins.js +4 -10
  52. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  53. package/dist/frontend/lowering/lower-calls.js +169 -144
  54. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  55. package/dist/frontend/lowering/lower-classes.js +3 -0
  56. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  57. package/dist/frontend/lowering/lower-containers.js +66 -32
  58. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  59. package/dist/frontend/lowering/lower-exprs.js +89 -11
  60. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  61. package/dist/frontend/lowering/lower-inspect.js +45 -26
  62. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  63. package/dist/frontend/lowering/lower-island.js +3 -3
  64. package/dist/frontend/lowering/lower-island.js.map +1 -1
  65. package/dist/frontend/lowering/lower-mixins.js +1 -1
  66. package/dist/frontend/lowering/lower-mixins.js.map +1 -1
  67. package/dist/frontend/lowering/lower-modules.js +1 -1
  68. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  69. package/dist/frontend/lowering/lower-stmts.js +49 -4
  70. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  71. package/dist/frontend/lowering/lowerer.d.ts +73 -29
  72. package/dist/frontend/lowering/lowerer.js +588 -116
  73. package/dist/frontend/lowering/lowerer.js.map +1 -1
  74. package/dist/frontend/lowering/surfaces.js +1 -1
  75. package/dist/frontend/lowering/surfaces.js.map +1 -1
  76. package/dist/frontend/npm-static-rewrite.js +25 -26
  77. package/dist/frontend/npm-static-rewrite.js.map +1 -1
  78. package/dist/frontend/npm-static.js +29 -30
  79. package/dist/frontend/npm-static.js.map +1 -1
  80. package/dist/frontend/npm-static.test.d.ts +1 -0
  81. package/dist/frontend/npm-static.test.js +15 -0
  82. package/dist/frontend/npm-static.test.js.map +1 -0
  83. package/dist/frontend/npm.js +5 -17
  84. package/dist/frontend/npm.js.map +1 -1
  85. package/dist/frontend/program.js +44 -19
  86. package/dist/frontend/program.js.map +1 -1
  87. package/dist/frontend/resolve.js +8 -29
  88. package/dist/frontend/resolve.js.map +1 -1
  89. package/dist/frontend/ts7/checker.d.ts +40 -2
  90. package/dist/frontend/ts7/checker.js +194 -32
  91. package/dist/frontend/ts7/checker.js.map +1 -1
  92. package/dist/frontend/ts7/program.js +17 -18
  93. package/dist/frontend/ts7/program.js.map +1 -1
  94. package/dist/frontend/types.d.ts +33 -0
  95. package/dist/frontend/types.js +134 -45
  96. package/dist/frontend/types.js.map +1 -1
  97. package/dist/index.d.ts +5 -2
  98. package/dist/index.js +458 -141
  99. package/dist/index.js.map +1 -1
  100. package/dist/ir/nodes.d.ts +27 -4
  101. package/dist/ir/nodes.js +28 -0
  102. package/dist/ir/nodes.js.map +1 -1
  103. package/dist/ir/serialize.d.ts +1 -1
  104. package/dist/ir/serialize.js +1 -1
  105. package/dist/ir/validate.js +37 -1
  106. package/dist/ir/validate.js.map +1 -1
  107. package/dist/library/early-cache.d.ts +65 -0
  108. package/dist/library/early-cache.js +361 -0
  109. package/dist/library/early-cache.js.map +1 -0
  110. package/dist/library/early-cache.test.d.ts +1 -0
  111. package/dist/library/early-cache.test.js +528 -0
  112. package/dist/library/early-cache.test.js.map +1 -0
  113. package/dist/library/implementation-identity.d.ts +23 -0
  114. package/dist/library/implementation-identity.js +84 -0
  115. package/dist/library/implementation-identity.js.map +1 -0
  116. package/dist/library/int-infer.js +14 -0
  117. package/dist/library/int-infer.js.map +1 -1
  118. package/dist/library/int-infer.test.js +21 -4
  119. package/dist/library/int-infer.test.js.map +1 -1
  120. package/dist/library/semantic-source.d.ts +13 -0
  121. package/dist/library/semantic-source.js +257 -0
  122. package/dist/library/semantic-source.js.map +1 -0
  123. package/dist/library/semantic-source.test.d.ts +1 -0
  124. package/dist/library/semantic-source.test.js +113 -0
  125. package/dist/library/semantic-source.test.js.map +1 -0
  126. package/dist/library/sidecar.d.ts +6 -0
  127. package/dist/library/sidecar.js +11 -0
  128. package/dist/library/sidecar.js.map +1 -1
  129. package/dist/startup-cache.d.ts +6 -0
  130. package/dist/startup-cache.js +6 -0
  131. package/dist/startup-cache.js.map +1 -0
  132. package/package.json +3 -2
  133. package/surface-manifest.json +1 -1
@@ -625,7 +625,7 @@ export function declaredReturnType(L, decl, blame) {
625
625
  * instead of the generic supported-types recitation; a no-op for every
626
626
  * other unmappable type (the caller's badType reports those). */
627
627
  function fenceGenericSignatureResult(L, blame, t) {
628
- const parts = t.isUnionType() ? t.getTypes() : [t];
628
+ const parts = t.isUnionType() ? ts.constituentTypes(t) : [t];
629
629
  if (!parts.some((p) => L.checker.getCallSignatures(p).some((s) => (s.typeParameters?.length ?? 0) > 0))) {
630
630
  return;
631
631
  }
@@ -931,6 +931,7 @@ export function internGenericInstance(L, blame, info, params, returnType, makeBi
931
931
  info.instances.set(key, inst);
932
932
  L.instantiationQueue.push({ info, inst });
933
933
  }
934
+ L.noteGenericInstanceDemand(inst);
934
935
  return inst;
935
936
  }
936
937
  /** The type parameters of `info` declared with a `keyof` CONSTRAINT
@@ -1045,8 +1046,8 @@ export function unifySignatureBindings(L, info, rsig, bindings, tsBindings) {
1045
1046
  // unbound type parameter surfaces as a mapping diagnostic later).
1046
1047
  if (declared.isUnionType()) {
1047
1048
  const unitFlags = ts.TypeFlags.Undefined | ts.TypeFlags.Null;
1048
- const dParts = declared.getTypes().filter((t) => !(t.flags & unitFlags));
1049
- const iParts = inst.isUnionType() ? inst.getTypes().filter((t) => !(t.flags & unitFlags)) : [inst];
1049
+ const dParts = ts.constituentTypes(declared).filter((t) => !(t.flags & unitFlags));
1050
+ const iParts = inst.isUnionType() ? ts.constituentTypes(inst).filter((t) => !(t.flags & unitFlags)) : [inst];
1050
1051
  if (dParts.length === 1 && iParts.length === 1)
1051
1052
  unify(dParts[0], iParts[0], depth + 1);
1052
1053
  return;
@@ -1442,7 +1443,7 @@ export function lowerGenericFnValue(L, ref, info) {
1442
1443
  // A `Fn | undefined`-flavored slot: the value can only inhabit the
1443
1444
  // one callable arm — judge by it (the requireExactArityValue union
1444
1445
  // rule).
1445
- const callable = pinT.getTypes().map((t) => L.checker.getCallSignatures(t)).filter((s) => s.length === 1);
1446
+ const callable = ts.constituentTypes(pinT).map((t) => L.checker.getCallSignatures(t)).filter((s) => s.length === 1);
1446
1447
  if (callable.length === 1)
1447
1448
  target = callable[0][0];
1448
1449
  }
@@ -1781,6 +1782,16 @@ function internImplicitInstance(L, blame, info, shapes, argTypes) {
1781
1782
  // the pinned signature over a missing body, which the linker never
1782
1783
  // sees because the poison also fenced the call statement.
1783
1784
  try {
1785
+ // Implicit-any instances lower EAGERLY at the call site so their
1786
+ // inferred return type is available immediately. They therefore do
1787
+ // not pass through emitReachable's queued generic-instance wave,
1788
+ // which normally batches checker work before lowering a body. Prime
1789
+ // this exact committed instance body here; repeated instantiations of
1790
+ // the same declaration find the facade memos warm.
1791
+ L.checker.prefetchRoots([
1792
+ ...info.decl.parameters.flatMap((param) => param.initializer ? [param.initializer] : []),
1793
+ ...(info.decl.body ? [info.decl.body] : []),
1794
+ ]);
1784
1795
  const fn = L.lowerGenericInstance(info, inst);
1785
1796
  L.implicitFns.push(fn);
1786
1797
  }
@@ -6409,48 +6420,8 @@ export function recordKeysArrayCall(L, receiver, argIr, shape, loc) {
6409
6420
  helper = `%obj.keys.${L.arrHofHelpers.size}`;
6410
6421
  const ref = { kind: "varRef", localId: "r.0", type: argIr, loc };
6411
6422
  const outRef = { kind: "varRef", localId: "out.0", type: resultT, loc };
6412
- const body = [
6413
- { kind: "varDecl", localId: "out.0", init: { kind: "arrayLit", elems: [], type: resultT, loc }, loc },
6414
- ];
6415
- const order = shape.declaredOrder ?? shape.fields.map((f) => f.name);
6416
- for (const name of order) {
6417
- const f = shape.fields.find((x) => x.name === name);
6418
- const pushStmt = {
6419
- kind: "exprStmt",
6420
- expr: {
6421
- kind: "arrIntrinsic",
6422
- method: "push",
6423
- receiver: outRef,
6424
- args: [{ kind: "strLit", value: f.name, type: STRING, loc }],
6425
- type: F64,
6426
- loc,
6427
- },
6428
- loc,
6429
- };
6430
- // Undefined-armed fields: the push is guarded by a tag test (the
6431
- // key exists exactly when the arm is not undefined).
6432
- const utag = f.type.kind === "union" ? L.armTag(f.type.unionId, UNDEFINED_T) : -1;
6433
- body.push(utag >= 0 && f.type.kind === "union"
6434
- ? {
6435
- kind: "if",
6436
- cond: {
6437
- kind: "unionIsTag",
6438
- unionId: f.type.unionId,
6439
- tag: utag,
6440
- negated: true,
6441
- value: { kind: "recordGet", obj: ref, shapeId: argIr.shapeId, field: f.name, type: f.type, loc },
6442
- type: BOOL,
6443
- loc,
6444
- },
6445
- then: [pushStmt],
6446
- else_: null,
6447
- loc,
6448
- }
6449
- : pushStmt);
6450
- }
6451
- body.push({ kind: "return", value: outRef, loc });
6452
6423
  L.arrHofHelpers.set(key, helper);
6453
- L.liftedFns.push({
6424
+ const fn = {
6454
6425
  name: helper,
6455
6426
  params: [{ localId: "r.0", name: "r", type: argIr }],
6456
6427
  returnType: resultT,
@@ -6458,9 +6429,56 @@ export function recordKeysArrayCall(L, receiver, argIr, shape, loc) {
6458
6429
  { id: "r.0", name: "r", type: argIr, mutable: true },
6459
6430
  { id: "out.0", name: "out", type: resultT, mutable: false },
6460
6431
  ],
6461
- body,
6432
+ body: [],
6462
6433
  loc,
6463
- });
6434
+ };
6435
+ const finalize = () => {
6436
+ const current = L.shapes.get(argIr.shapeId) ?? shape;
6437
+ const body = [
6438
+ { kind: "varDecl", localId: "out.0", init: { kind: "arrayLit", elems: [], type: resultT, loc }, loc },
6439
+ ];
6440
+ const order = current.declaredOrder ?? current.fields.map((f) => f.name);
6441
+ for (const name of order) {
6442
+ const f = current.fields.find((x) => x.name === name);
6443
+ const pushStmt = {
6444
+ kind: "exprStmt",
6445
+ expr: {
6446
+ kind: "arrIntrinsic",
6447
+ method: "push",
6448
+ receiver: outRef,
6449
+ args: [{ kind: "strLit", value: f.name, type: STRING, loc }],
6450
+ type: F64,
6451
+ loc,
6452
+ },
6453
+ loc,
6454
+ };
6455
+ // Undefined-armed fields: the push is guarded by a tag test (the
6456
+ // key exists exactly when Object.keys would list it).
6457
+ const utag = f.type.kind === "union" ? L.armTag(f.type.unionId, UNDEFINED_T) : -1;
6458
+ body.push(utag >= 0 && f.type.kind === "union"
6459
+ ? {
6460
+ kind: "if",
6461
+ cond: {
6462
+ kind: "unionIsTag",
6463
+ unionId: f.type.unionId,
6464
+ tag: utag,
6465
+ negated: true,
6466
+ value: { kind: "recordGet", obj: ref, shapeId: argIr.shapeId, field: f.name, type: f.type, loc },
6467
+ type: BOOL,
6468
+ loc,
6469
+ },
6470
+ then: [pushStmt],
6471
+ else_: null,
6472
+ loc,
6473
+ }
6474
+ : pushStmt);
6475
+ }
6476
+ body.push({ kind: "return", value: outRef, loc });
6477
+ fn.body = body;
6478
+ };
6479
+ finalize();
6480
+ L.shapeOrderHelperFinalizers.push(finalize);
6481
+ L.liftedFns.push(fn);
6464
6482
  }
6465
6483
  return { kind: "call", callee: helper, args: [receiver], type: resultT, loc };
6466
6484
  }
@@ -7170,109 +7188,15 @@ function lowerObjectStaticCall(L, call, access) {
7170
7188
  L.badType(call, L.typeOf(call));
7171
7189
  valueT = tupleShape.fields.find((f) => f.name === "1").type;
7172
7190
  }
7173
- const order = shape.declaredOrder ?? shape.fields.map((f) => f.name);
7174
7191
  const key = `obj.${member}:${argIr.shapeId}:${typeKey(resultT)}`;
7175
7192
  let helper = L.arrHofHelpers.get(key);
7176
7193
  if (!helper) {
7177
7194
  helper = `%obj.${member}.${L.arrHofHelpers.size}`;
7178
7195
  const recT = argIr;
7179
7196
  const ref = { kind: "varRef", localId: "r.0", type: recT, loc };
7180
- const body = [
7181
- { kind: "varDecl", localId: "out.0", init: { kind: "arrayLit", elems: [], type: resultT, loc }, loc },
7182
- ];
7183
7197
  const outRef = { kind: "varRef", localId: "out.0", type: resultT, loc };
7184
- for (const name of order) {
7185
- const f = shape.fields.find((x) => x.name === name);
7186
- const raw = { kind: "recordGet", obj: ref, shapeId: argIr.shapeId, field: f.name, type: f.type, loc };
7187
- // The pushed element per member; null when the field's value
7188
- // cannot flow into the result element type.
7189
- const elemOf = (value, vt) => {
7190
- if (!valueT)
7191
- return null;
7192
- if (typeEquals(vt, valueT))
7193
- return value;
7194
- if (valueT.kind === "union" && vt.kind !== "union") {
7195
- const tag = L.armTag(valueT.unionId, vt);
7196
- if (tag >= 0) {
7197
- return { kind: "unionWrap", unionId: valueT.unionId, tag, value, type: valueT, loc };
7198
- }
7199
- }
7200
- return null;
7201
- };
7202
- // Undefined-armed fields: the push is guarded by a tag test, and
7203
- // the pushed value is the narrowed non-undefined arm.
7204
- let guardUndefTag = null;
7205
- let value = raw;
7206
- let vt = f.type;
7207
- if (f.type.kind === "union") {
7208
- const undefTag = L.armTag(f.type.unionId, UNDEFINED_T);
7209
- if (undefTag >= 0) {
7210
- guardUndefTag = undefTag;
7211
- const arms = L.unions.get(f.type.unionId)?.arms ?? [];
7212
- const others = arms.filter((a) => a.kind !== "undefinedT");
7213
- if (typeEquals(f.type, valueT ?? f.type)) {
7214
- // The field union IS the result union (single-field shapes):
7215
- // push the raw box — but then the undefined skip must NOT
7216
- // narrow. Handled below via vt === valueT.
7217
- value = raw;
7218
- vt = f.type;
7219
- }
7220
- else if (others.length === 1) {
7221
- vt = others[0];
7222
- // A UNIT other arm (`null | undefined` fields — the mixed-
7223
- // defaults spread idiom; undefined was filtered above, so
7224
- // the unit is null): units carry no payload, so the guarded
7225
- // push writes the unit LITERAL — unionNarrow to a unit arm
7226
- // (and unionWrap of a narrowed unit) is malformed IR; the
7227
- // literal is the one legal unit spelling.
7228
- value = isUnitType(vt)
7229
- ? { kind: "unitLit", unit: "null", type: vt, loc }
7230
- : { kind: "unionNarrow", unionId: f.type.unionId, tag: L.armTag(f.type.unionId, vt), value: raw, type: vt, loc };
7231
- }
7232
- else {
7233
- L.unsupported("SC1090", call, `Object.${member} over '${L.fmt(argIr)}' (field '${f.name}' is a multi-arm union that ` +
7234
- "cannot re-tag into the result element type — read the fields directly)");
7235
- }
7236
- }
7237
- else if (!typeEquals(f.type, valueT ?? f.type)) {
7238
- L.unsupported("SC1090", call, `Object.${member} over '${L.fmt(argIr)}' (field '${f.name}' is a union that cannot ` +
7239
- "re-tag into the result element type — read the fields directly)");
7240
- }
7241
- }
7242
- const coerced = elemOf(value, vt);
7243
- if (!coerced) {
7244
- L.unsupported("SC1090", call, `Object.${member} over '${L.fmt(argIr)}' (field '${f.name}' of type '${L.fmt(f.type)}' ` +
7245
- `cannot flow into the '${L.fmt(valueT)}' result element — read the fields directly)`);
7246
- }
7247
- const pushed = member === "values"
7248
- ? coerced
7249
- : {
7250
- kind: "recordLit",
7251
- fields: [
7252
- { name: "0", value: { kind: "strLit", value: f.name, type: STRING, loc } },
7253
- { name: "1", value: coerced },
7254
- ],
7255
- type: tupleT,
7256
- loc,
7257
- };
7258
- const pushStmt = {
7259
- kind: "exprStmt",
7260
- expr: { kind: "arrIntrinsic", method: "push", receiver: outRef, args: [pushed], type: F64, loc },
7261
- loc,
7262
- };
7263
- body.push(guardUndefTag !== null && f.type.kind === "union"
7264
- ? {
7265
- kind: "if",
7266
- cond: { kind: "unionIsTag", unionId: f.type.unionId, tag: guardUndefTag, negated: true, value: raw, type: BOOL, loc },
7267
- then: [pushStmt],
7268
- else_: null,
7269
- loc,
7270
- }
7271
- : pushStmt);
7272
- }
7273
- body.push({ kind: "return", value: outRef, loc });
7274
7198
  L.arrHofHelpers.set(key, helper);
7275
- L.liftedFns.push({
7199
+ const fn = {
7276
7200
  name: helper,
7277
7201
  params: [{ localId: "r.0", name: "r", type: recT }],
7278
7202
  returnType: resultT,
@@ -7280,9 +7204,110 @@ function lowerObjectStaticCall(L, call, access) {
7280
7204
  { id: "r.0", name: "r", type: recT, mutable: true },
7281
7205
  { id: "out.0", name: "out", type: resultT, mutable: false },
7282
7206
  ],
7283
- body,
7207
+ body: [],
7284
7208
  loc,
7285
- });
7209
+ };
7210
+ const finalize = () => {
7211
+ const current = L.shapes.get(argIr.shapeId) ?? shape;
7212
+ const body = [
7213
+ { kind: "varDecl", localId: "out.0", init: { kind: "arrayLit", elems: [], type: resultT, loc }, loc },
7214
+ ];
7215
+ const order = current.declaredOrder ?? current.fields.map((f) => f.name);
7216
+ for (const name of order) {
7217
+ const f = current.fields.find((x) => x.name === name);
7218
+ const raw = { kind: "recordGet", obj: ref, shapeId: argIr.shapeId, field: f.name, type: f.type, loc };
7219
+ // The pushed element per member; null when the field's value
7220
+ // cannot flow into the result element type.
7221
+ const elemOf = (value, vt) => {
7222
+ if (!valueT)
7223
+ return null;
7224
+ if (typeEquals(vt, valueT))
7225
+ return value;
7226
+ if (valueT.kind === "union" && vt.kind !== "union") {
7227
+ const tag = L.armTag(valueT.unionId, vt);
7228
+ if (tag >= 0) {
7229
+ return { kind: "unionWrap", unionId: valueT.unionId, tag, value, type: valueT, loc };
7230
+ }
7231
+ }
7232
+ return null;
7233
+ };
7234
+ // Undefined-armed fields: the push is guarded by a tag test, and
7235
+ // the pushed value is the narrowed non-undefined arm.
7236
+ let guardUndefTag = null;
7237
+ let value = raw;
7238
+ let vt = f.type;
7239
+ if (f.type.kind === "union") {
7240
+ const undefTag = L.armTag(f.type.unionId, UNDEFINED_T);
7241
+ if (undefTag >= 0) {
7242
+ guardUndefTag = undefTag;
7243
+ const arms = L.unions.get(f.type.unionId)?.arms ?? [];
7244
+ const others = arms.filter((a) => a.kind !== "undefinedT");
7245
+ if (typeEquals(f.type, valueT ?? f.type)) {
7246
+ // The field union IS the result union (single-field shapes):
7247
+ // push the raw box — but then the undefined skip must NOT
7248
+ // narrow. Handled below via vt === valueT.
7249
+ value = raw;
7250
+ vt = f.type;
7251
+ }
7252
+ else if (others.length === 1) {
7253
+ vt = others[0];
7254
+ // A UNIT other arm (`null | undefined` fields — the mixed-
7255
+ // defaults spread idiom; undefined was filtered above, so
7256
+ // the unit is null): units carry no payload, so the guarded
7257
+ // push writes the unit LITERAL — unionNarrow to a unit arm
7258
+ // (and unionWrap of a narrowed unit) is malformed IR; the
7259
+ // literal is the one legal unit spelling.
7260
+ value = isUnitType(vt)
7261
+ ? { kind: "unitLit", unit: "null", type: vt, loc }
7262
+ : { kind: "unionNarrow", unionId: f.type.unionId, tag: L.armTag(f.type.unionId, vt), value: raw, type: vt, loc };
7263
+ }
7264
+ else {
7265
+ L.unsupported("SC1090", call, `Object.${member} over '${L.fmt(argIr)}' (field '${f.name}' is a multi-arm union that ` +
7266
+ "cannot re-tag into the result element type — read the fields directly)");
7267
+ }
7268
+ }
7269
+ else if (!typeEquals(f.type, valueT ?? f.type)) {
7270
+ L.unsupported("SC1090", call, `Object.${member} over '${L.fmt(argIr)}' (field '${f.name}' is a union that cannot ` +
7271
+ "re-tag into the result element type — read the fields directly)");
7272
+ }
7273
+ }
7274
+ const coerced = elemOf(value, vt);
7275
+ if (!coerced) {
7276
+ L.unsupported("SC1090", call, `Object.${member} over '${L.fmt(argIr)}' (field '${f.name}' of type '${L.fmt(f.type)}' ` +
7277
+ `cannot flow into the '${L.fmt(valueT)}' result element — read the fields directly)`);
7278
+ }
7279
+ const pushed = member === "values"
7280
+ ? coerced
7281
+ : {
7282
+ kind: "recordLit",
7283
+ fields: [
7284
+ { name: "0", value: { kind: "strLit", value: f.name, type: STRING, loc } },
7285
+ { name: "1", value: coerced },
7286
+ ],
7287
+ type: tupleT,
7288
+ loc,
7289
+ };
7290
+ const pushStmt = {
7291
+ kind: "exprStmt",
7292
+ expr: { kind: "arrIntrinsic", method: "push", receiver: outRef, args: [pushed], type: F64, loc },
7293
+ loc,
7294
+ };
7295
+ body.push(guardUndefTag !== null && f.type.kind === "union"
7296
+ ? {
7297
+ kind: "if",
7298
+ cond: { kind: "unionIsTag", unionId: f.type.unionId, tag: guardUndefTag, negated: true, value: raw, type: BOOL, loc },
7299
+ then: [pushStmt],
7300
+ else_: null,
7301
+ loc,
7302
+ }
7303
+ : pushStmt);
7304
+ }
7305
+ body.push({ kind: "return", value: outRef, loc });
7306
+ fn.body = body;
7307
+ };
7308
+ finalize();
7309
+ L.shapeOrderHelperFinalizers.push(finalize);
7310
+ L.liftedFns.push(fn);
7286
7311
  }
7287
7312
  return { kind: "call", callee: helper, args: [receiver], type: resultT, loc };
7288
7313
  }