@scriptc/compiler 0.0.31 → 0.0.33

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 (55) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +1 -1
  2. package/dist/backend/cc-cache.test.js +308 -1
  3. package/dist/backend/cc-cache.test.js.map +1 -1
  4. package/dist/backend/cc.d.ts +17 -5
  5. package/dist/backend/cc.js +745 -75
  6. package/dist/backend/cc.js.map +1 -1
  7. package/dist/backend/emission/emit-exprs.js +53 -2
  8. package/dist/backend/emission/emit-exprs.js.map +1 -1
  9. package/dist/backend/emission/emitter.d.ts +12 -5
  10. package/dist/backend/emission/emitter.js +160 -22
  11. package/dist/backend/emission/emitter.js.map +1 -1
  12. package/dist/backend/emission/may-throw.js +7 -4
  13. package/dist/backend/emission/may-throw.js.map +1 -1
  14. package/dist/backend/ffi-callbacks.d.ts +38 -1
  15. package/dist/backend/ffi-callbacks.js +76 -3
  16. package/dist/backend/ffi-callbacks.js.map +1 -1
  17. package/dist/backend/llvm/emitter.js +243 -12
  18. package/dist/backend/llvm/emitter.js.map +1 -1
  19. package/dist/diagnostics/diagnostic.js +2 -0
  20. package/dist/diagnostics/diagnostic.js.map +1 -1
  21. package/dist/ffi/profile.d.ts +17 -5
  22. package/dist/ffi/profile.js +169 -22
  23. package/dist/ffi/profile.js.map +1 -1
  24. package/dist/frontend/lowering/lower-calls.d.ts +1 -0
  25. package/dist/frontend/lowering/lower-calls.js +89 -23
  26. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  27. package/dist/frontend/lowering/lower-containers.js +65 -19
  28. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  29. package/dist/frontend/lowering/lowerer.d.ts +12 -0
  30. package/dist/frontend/lowering/lowerer.js +20 -1
  31. package/dist/frontend/lowering/lowerer.js.map +1 -1
  32. package/dist/frontend/ts7/checker.d.ts +2 -0
  33. package/dist/frontend/ts7/checker.js +20 -3
  34. package/dist/frontend/ts7/checker.js.map +1 -1
  35. package/dist/frontend/types.d.ts +8 -0
  36. package/dist/frontend/types.js +33 -5
  37. package/dist/frontend/types.js.map +1 -1
  38. package/dist/index.js +35 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/ir/nodes.d.ts +22 -9
  41. package/dist/ir/nodes.js +7 -1
  42. package/dist/ir/nodes.js.map +1 -1
  43. package/dist/ir/serialize.d.ts +1 -1
  44. package/dist/ir/serialize.js +1 -1
  45. package/dist/ir/validate.js +74 -1
  46. package/dist/ir/validate.js.map +1 -1
  47. package/dist/library/int-infer.js +7 -1
  48. package/dist/library/int-infer.js.map +1 -1
  49. package/dist/library/int-infer.test.js +73 -4
  50. package/dist/library/int-infer.test.js.map +1 -1
  51. package/dist/library/profile.d.ts +3 -0
  52. package/dist/library/profile.js +9 -1
  53. package/dist/library/profile.js.map +1 -1
  54. package/package.json +2 -2
  55. package/surface-manifest.json +1 -1
@@ -8,7 +8,7 @@ import { ARRAY_METHODS, MAP_METHODS, SET_COMBINE_METHODS, SET_METHODS, STR_METHO
8
8
  import { droppableStatic, isRequireMainFilename, lowerDynObjectLiteral, probeLower, pureReemittable } from "./lower-exprs.js";
9
9
  import { forOfVarTarget, lowerDestructuringAssign } from "./lower-stmts.js";
10
10
  import { isJsSourceFile, locOf } from "../program.js";
11
- import { DYN_DISPATCH_METHODS, islandPrimitiveExit } from "./lower-calls.js";
11
+ import { islandPrimitiveExit, lowerDynDispatchMethodCall } from "./lower-calls.js";
12
12
  import { typeKey } from "../types.js";
13
13
  import { dynUndefinedExpr, own } from "./lowerer.js";
14
14
  /** Lower an expression whose checker type is statically `undefined`/`void`.
@@ -142,7 +142,11 @@ export function lowerArrayMethodCall(L, call, access) {
142
142
  {
143
143
  const receiver = L.lowerExpr(access.expression);
144
144
  if (receiver.type.kind === "jsval") {
145
- const args = call.arguments.map((a) => L.jsvalIn(L.lowerExpr(a), a));
145
+ const loweredArgs = call.arguments.map((a) => L.lowerExpr(a));
146
+ if (name === "filter" && loweredArgs[0]?.type.kind === "func" && loweredArgs[0].type.ret.kind === "void") {
147
+ L.unsupported("SC1090", call.arguments[0], "'.filter()' with a void-returning predicate (the callback return value is erased before its truthiness can be tested)");
148
+ }
149
+ const args = loweredArgs.map((arg, i) => L.jsvalIn(arg, call.arguments[i]));
146
150
  const result = { kind: "jsOp", op: "callMethod", name, args: [receiver, ...args], type: JSVAL, loc };
147
151
  return islandPrimitiveExit(L, call, result);
148
152
  }
@@ -155,21 +159,9 @@ export function lowerArrayMethodCall(L, call, access) {
155
159
  // ICE). Consumers validate the dyn result where a static type is
156
160
  // required (dynCheck — the member-read discipline).
157
161
  if (receiver.type.kind === "dyn") {
158
- if (DYN_DISPATCH_METHODS.has(name) && !call.questionDotToken && !access.questionDotToken) {
159
- if (call.arguments.some((a) => ts.isSpreadElement(a))) {
160
- L.unsupported("SC1090", call, "spread arguments in calls through 'unknown' values");
161
- }
162
- const args = call.arguments.map((a) => L.lowerExprExpecting(a, DYN));
163
- return {
164
- kind: "dynInvoke",
165
- recv: receiver,
166
- method: name,
167
- calleeName: access.getText(),
168
- args,
169
- type: DYN,
170
- loc,
171
- };
172
- }
162
+ const dispatched = lowerDynDispatchMethodCall(L, call, access, receiver, true);
163
+ if (dispatched)
164
+ return dispatched;
173
165
  L.noLowering(`.${name} on an array value held in a checked-dynamic binding`, call, "assign it to an array-typed binding first (the validated extraction), then call the method");
174
166
  }
175
167
  // An evolving-`any` array binding under --dynamic whose flow type
@@ -596,12 +588,64 @@ export function lowerArrayHofCall(L, call, access, method, elem) {
596
588
  // declaration, so the honest answer is the fence).
597
589
  L.unsupported("SC1090", call, "'.map()' with a callback returning 'unknown'-typed values (the result array has no static element type — annotate the callback's return)");
598
590
  }
599
- if (method === "filter" && fnRet.kind !== "bool")
591
+ // JS applies ToBoolean to whatever the predicate answers, so a non-bool
592
+ // result is not an error — the filter loop wraps the call in the same
593
+ // toBool an `if` statement would apply. The island/dyn shapes, whose
594
+ // truthiness needs the engine, and void, whose real answer the ABI has
595
+ // already discarded, keep the fence. No separate
596
+ // requireTruthyUnion call belongs here: its check (no dyn/caught arm)
597
+ // IS filterPredicateOk's union branch, so it could never speak.
598
+ if (method === "filter" && !filterPredicateOk(L, fnRet)) {
599
+ if (fnRet.kind === "void") {
600
+ L.unsupported("SC1090", argNode, "'.filter()' with a void-returning predicate (the callback return value is erased before its truthiness can be tested)");
601
+ }
600
602
  L.badType(argNode, L.typeOf(argNode));
603
+ }
601
604
  const helper = arrayHofHelper(L, method, elem, fnRet, arity, loc);
602
605
  const resultType = method === "map" ? arrayOf(fnRet) : method === "filter" ? arrayOf(elem) : VOID;
603
606
  return { kind: "call", callee: helper, args: [receiver, fnArg], type: resultType, loc };
604
607
  }
608
+ /** The predicate result kinds `.filter()` accepts. JS applies ToBoolean to
609
+ * whatever the callback answers, so a bool is not required: the scalars and
610
+ * the reference kinds have constant or by-value answers, and a union is fine
611
+ * when every arm does. void/dyn/jsval/caught stay out — see below. */
612
+ function filterPredicateOk(L, ret) {
613
+ if (ret.kind === "bool")
614
+ return true;
615
+ // VOID is a TYPE erasure, not a runtime value: TS lets a value-returning
616
+ // function sit in a void-returning slot (`const p: (n: number) => void =
617
+ // (n) => n`), so the predicate's real answer can be truthy while the
618
+ // compiled ABI has already discarded it. Treating void as constantly
619
+ // falsy would silently answer [] where Node answers [1]. Fenced until
620
+ // the returned value can be preserved through the void ABI.
621
+ // dyn/jsval/caught stay out too: no native ToBoolean to compile against.
622
+ if (ret.kind === "void")
623
+ return false;
624
+ if (ret.kind === "dyn" || ret.kind === "jsval" || ret.kind === "caught")
625
+ return false;
626
+ if (ret.kind === "union") {
627
+ const def = L.unions.get(ret.unionId);
628
+ return def !== undefined && def.arms.every((a) => a.kind !== "dyn" && a.kind !== "caught");
629
+ }
630
+ return true;
631
+ }
632
+ /** The filter loop's condition: the predicate's result put through JS
633
+ * ToBoolean. A bool answer is already the condition; everything else takes
634
+ * the same `toBool` wrapper an `if` statement would apply (a union answer
635
+ * routes through its interned per-arm truthy helper). */
636
+ function filterCond(call, fnRet, loc) {
637
+ if (fnRet.kind === "bool")
638
+ return call;
639
+ // No constant-false arm here: void is fenced at the call site, and a
640
+ // bare undefinedT/nullT return cannot arise (mapType sends `undefined`/
641
+ // `void` returns to void and a standalone `null` return to the unit-ONLY
642
+ // UNION, which routes through toBool below; ir/validate.ts rejects a
643
+ // bare unit return type outright). filterPredicateOk already rejected
644
+ // the arms with no native ToBoolean (dyn/caught) — which is exactly what
645
+ // requireTruthyUnion checks — and it did so at the call site, where a
646
+ // real node exists for the diagnostic.
647
+ return { kind: "toBool", operand: call, type: BOOL, loc };
648
+ }
605
649
  /** Interned synthetic loop function for one (method, elem, fnRet, arity)
606
650
  * combo. Named `%arr.<method>.<n>` ('%' keeps it out of the user
607
651
  * namespace); rides `liftedFns` into the module like a lifted lambda (it
@@ -934,7 +978,9 @@ export function buildArrayHofFn(L, name, method, elem, fnRet, arity, loc) {
934
978
  { kind: "varDecl", localId: "v.0", init: getElem, loc },
935
979
  {
936
980
  kind: "if",
937
- cond: callF(ref("v.0", elem)),
981
+ // ToBoolean over the predicate's answer — inert when it already
982
+ // returned bool, the per-union helper when it returned a union.
983
+ cond: filterCond(callF(ref("v.0", elem)), fnRet, loc),
938
984
  then: [push(arrT, ref("v.0", elem))],
939
985
  else_: null,
940
986
  loc,