@scriptc/compiler 0.1.6 → 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 (116) 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 +45 -1
  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-dynamic.js +10 -0
  32. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  33. package/dist/backend/llvm/lib-filesystem.js +29 -0
  34. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  35. package/dist/backend/llvm/lib-shared.js +8 -0
  36. package/dist/backend/llvm/lib-shared.js.map +1 -1
  37. package/dist/backend/llvm/split.js +4 -1
  38. package/dist/backend/llvm/split.js.map +1 -1
  39. package/dist/backend/llvm/split.test.js +26 -1
  40. package/dist/backend/llvm/split.test.js.map +1 -1
  41. package/dist/backend/native-toolchain.js +58 -8
  42. package/dist/backend/native-toolchain.js.map +1 -1
  43. package/dist/backend/native-toolchain.test.js +10 -0
  44. package/dist/backend/native-toolchain.test.js.map +1 -1
  45. package/dist/backend/source-locations.d.ts +12 -0
  46. package/dist/backend/source-locations.js +32 -0
  47. package/dist/backend/source-locations.js.map +1 -0
  48. package/dist/backend/source-locations.test.d.ts +1 -0
  49. package/dist/backend/source-locations.test.js +16 -0
  50. package/dist/backend/source-locations.test.js.map +1 -0
  51. package/dist/diagnostics/diagnostic.d.ts +4 -0
  52. package/dist/diagnostics/diagnostic.js +12 -0
  53. package/dist/diagnostics/diagnostic.js.map +1 -1
  54. package/dist/executable/executable-cache.js +28 -4
  55. package/dist/executable/executable-cache.js.map +1 -1
  56. package/dist/executable/executable-cache.test.js +23 -0
  57. package/dist/executable/executable-cache.test.js.map +1 -1
  58. package/dist/frontend/arguments-usage.d.ts +6 -0
  59. package/dist/frontend/arguments-usage.js +40 -0
  60. package/dist/frontend/arguments-usage.js.map +1 -0
  61. package/dist/frontend/binding-source.d.ts +4 -0
  62. package/dist/frontend/binding-source.js +18 -0
  63. package/dist/frontend/binding-source.js.map +1 -0
  64. package/dist/frontend/lowering/expressions/object-literals.js +13 -8
  65. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  66. package/dist/frontend/lowering/expressions/optional-chains.d.ts +5 -6
  67. package/dist/frontend/lowering/expressions/optional-chains.js +7 -6
  68. package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
  69. package/dist/frontend/lowering/lower-builtins.js +15 -4
  70. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  71. package/dist/frontend/lowering/lower-calls.d.ts +2 -9
  72. package/dist/frontend/lowering/lower-calls.js +140 -30
  73. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  74. package/dist/frontend/lowering/lower-exprs.js +116 -16
  75. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  76. package/dist/frontend/lowering/lower-island.d.ts +1 -1
  77. package/dist/frontend/lowering/lower-island.js +1 -1
  78. package/dist/frontend/lowering/lower-modules.js +19 -8
  79. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  80. package/dist/frontend/lowering/lower-stmts.js +4 -37
  81. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  82. package/dist/frontend/lowering/lowerer.js +14 -3
  83. package/dist/frontend/lowering/lowerer.js.map +1 -1
  84. package/dist/frontend/lowering/surfaces.d.ts +1 -1
  85. package/dist/frontend/lowering/surfaces.js +37 -6
  86. package/dist/frontend/lowering/surfaces.js.map +1 -1
  87. package/dist/frontend/program.d.ts +5 -0
  88. package/dist/frontend/program.js +216 -32
  89. package/dist/frontend/program.js.map +1 -1
  90. package/dist/frontend/program.test.d.ts +1 -0
  91. package/dist/frontend/program.test.js +117 -0
  92. package/dist/frontend/program.test.js.map +1 -0
  93. package/dist/frontend/type-mapper.d.ts +4 -2
  94. package/dist/frontend/type-mapper.js +249 -186
  95. package/dist/frontend/type-mapper.js.map +1 -1
  96. package/dist/frontend/type-mapper.test.d.ts +1 -0
  97. package/dist/frontend/type-mapper.test.js +86 -0
  98. package/dist/frontend/type-mapper.test.js.map +1 -0
  99. package/dist/index.d.ts +2 -1
  100. package/dist/index.js +7 -2
  101. package/dist/index.js.map +1 -1
  102. package/dist/ir/analysis.js +4 -2
  103. package/dist/ir/analysis.js.map +1 -1
  104. package/dist/ir/ir.d.ts +18 -12
  105. package/dist/ir/ir.js +3 -1
  106. package/dist/ir/ir.js.map +1 -1
  107. package/dist/ir/validate.js +14 -5
  108. package/dist/ir/validate.js.map +1 -1
  109. package/dist/library/compiler-self-identity.d.ts +1 -1
  110. package/dist/library/compiler-self-identity.js +31 -15
  111. package/dist/library/compiler-self-identity.js.map +1 -1
  112. package/dist/library/compiler-self-identity.test.d.ts +1 -0
  113. package/dist/library/compiler-self-identity.test.js +93 -0
  114. package/dist/library/compiler-self-identity.test.js.map +1 -0
  115. package/package.json +17 -17
  116. package/surface-manifest.json +120 -1
@@ -1006,6 +1006,13 @@ function lowerExprInner(lowerer, expr) {
1006
1006
  const canonical = stdlibGlobalNameOf(lowerer, expr) ?? expr.text;
1007
1007
  return { kind: "strLit", value: `[builtin ${canonical}]`, type: STRING, loc };
1008
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
+ }
1009
1016
  // The families with a WHY: each hint states what makes the
1010
1017
  // surface genuinely non-static (or what to use instead).
1011
1018
  const globalHints = {
@@ -2307,8 +2314,25 @@ export function maybeNarrow(lowerer, expr, node) {
2307
2314
  return expr;
2308
2315
  }
2309
2316
  const tag = lowerer.armTag(expr.type.unionId, narrowed);
2310
- 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
+ }
2311
2334
  return expr;
2335
+ }
2312
2336
  return {
2313
2337
  kind: "unionNarrow",
2314
2338
  unionId: expr.type.unionId,
@@ -2365,12 +2389,11 @@ function runtimeOptionalReceiverRead(lowerer, expr, local, property, loc) {
2365
2389
  }
2366
2390
  return { kind: "call", callee: helper, args: [varRef(local.id, local.type, loc)], type: narrowed, loc };
2367
2391
  }
2368
- const valueTag = lowerer.armTag(local.type.unionId, narrowed);
2369
2392
  const undefTag = lowerer.armTag(local.type.unionId, UNDEFINED_T);
2370
- if (valueTag < 0 || undefTag < 0)
2371
- throw new InternalCompilerError("runtime-optional local is missing its value or undefined arm");
2372
2393
  const def = lowerer.unions.get(local.type.unionId);
2373
- 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) {
2374
2397
  // The checker may narrow a runtime-optional capture to one arm of a
2375
2398
  // value union (`Circle | Square | undefined` -> `Circle`). The
2376
2399
  // two-arm fast path below cannot extract that arm safely: a later
@@ -2383,10 +2406,19 @@ function runtimeOptionalReceiverRead(lowerer, expr, local, property, loc) {
2383
2406
  }
2384
2407
  return { kind: "call", callee: helper, args: [varRef(local.id, local.type, loc)], type: narrowed, loc };
2385
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
+ }
2386
2418
  const message = property === null
2387
2419
  ? `${expr.text} is not a function`
2388
2420
  : `Cannot read properties of undefined (reading '${property}')`;
2389
- return {
2421
+ return lowerer.maybeNarrow({
2390
2422
  kind: "ternary",
2391
2423
  cond: {
2392
2424
  kind: "unionIsTag",
@@ -2397,18 +2429,18 @@ function runtimeOptionalReceiverRead(lowerer, expr, local, property, loc) {
2397
2429
  type: BOOL,
2398
2430
  loc,
2399
2431
  },
2400
- then: nodeThrowExpr(1, "", message, narrowed, loc),
2432
+ then: nodeThrowExpr(1, "", message, valueType, loc),
2401
2433
  else_: {
2402
2434
  kind: "unionNarrow",
2403
2435
  unionId: local.type.unionId,
2404
2436
  tag: valueTag,
2405
2437
  value: varRef(local.id, local.type, loc),
2406
- type: narrowed,
2438
+ type: valueType,
2407
2439
  loc,
2408
2440
  },
2409
- type: narrowed,
2441
+ type: valueType,
2410
2442
  loc,
2411
- };
2443
+ }, narrowedNode);
2412
2444
  }
2413
2445
  function runtimeOptionalUseOf(expr) {
2414
2446
  let receiver = expr;
@@ -3429,7 +3461,10 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3429
3461
  export function lowerArrayLiteral(lowerer, expr, expected) {
3430
3462
  const loc = locOf(expr);
3431
3463
  const ctxType = lowerer.checker.getContextualType(expr);
3432
- 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);
3433
3468
  let mapped = expected ?? lowerer.mapTypeOf(tsType);
3434
3469
  // A JS literal whose OWN inferred type is never-tainted
3435
3470
  // (neverTaintedJsType — the evolving `const gb = []`, the mixed
@@ -3542,13 +3577,13 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3542
3577
  // literal constructs the tuple's record shape — one positional field
3543
3578
  // per element, source order (which IS index order, so evaluation order
3544
3579
  // is JS-exact). tsc has already checked the arity; the recount below
3545
- // backstops `as` smuggling. Spreads have no fixed positions — fenced.
3580
+ // backstops `as` smuggling. Fixed tuple spreads have known positions.
3546
3581
  if (mapped?.kind === "record") {
3547
3582
  const shape = lowerer.shapes.get(mapped.shapeId);
3548
3583
  if (shape?.tuple) {
3549
3584
  const spread = expr.elements.find(ts.isSpreadElement);
3550
3585
  if (spread) {
3551
- lowerer.unsupported("SC1090", spread, "spread elements in tuple literals (positions must be spelled out)");
3586
+ return lowerTupleSpreadLiteral(lowerer, expr, mapped, shape);
3552
3587
  }
3553
3588
  if (expr.elements.length !== shape.fields.length) {
3554
3589
  // tsc padded an UNDER-LENGTH literal against an optional-element
@@ -3817,6 +3852,62 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3817
3852
  loc,
3818
3853
  };
3819
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
+ }
3820
3911
  /** Convert an undefined-armed numeric value at an arithmetic use. Ordinary
3821
3912
  * reads preserve their tagged value; JavaScript's ToNumber(undefined) is NaN,
3822
3913
  * so compounds and binary arithmetic use this checked one-time extraction. */
@@ -4384,6 +4475,16 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
4384
4475
  // to observe the missing value instead of throwing during that check.
4385
4476
  return includeUndefined ? read : lowerer.maybeNarrow(read, expr);
4386
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
+ }
4387
4488
  if (litKey !== null) {
4388
4489
  const field = shape.fields.find((f) => f.name === litKey);
4389
4490
  if (field) {
@@ -6619,7 +6720,7 @@ export function lowerCaughtTypeofTest(lowerer, expr, loc) {
6619
6720
  if (!ts.isStringLiteral(b)) {
6620
6721
  lowerer.unsupported("SC1090", expr, "'typeof' catch-binding tests against non-literal strings");
6621
6722
  }
6622
- if (b.text === "string" || b.text === "number" || b.text === "boolean") {
6723
+ if (b.text === "string" || b.text === "number" || b.text === "boolean" || b.text === "object") {
6623
6724
  return {
6624
6725
  kind: "caughtTest",
6625
6726
  value: { kind: "varRef", localId: local.id, type: CAUGHT, loc },
@@ -6629,8 +6730,7 @@ export function lowerCaughtTypeofTest(lowerer, expr, loc) {
6629
6730
  loc,
6630
6731
  };
6631
6732
  }
6632
- lowerer.unsupported("SC1090", b, `'typeof' catch-binding tests against "${b.text}" (only "string"/"number"/"boolean" ` +
6633
- `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)`);
6634
6734
  }
6635
6735
  return null;
6636
6736
  }