@suss/resolution 0.34.1 → 0.35.0

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.
package/dist/index.cjs CHANGED
@@ -24,6 +24,7 @@ __export(index_exports, {
24
24
  ASKING_RELATIONS: () => ASKING_RELATIONS,
25
25
  BASE_CLASS_RULE: () => BASE_CLASS_RULE,
26
26
  DEFAULT_IMPORT_NAME: () => DEFAULT_IMPORT_NAME,
27
+ DISPATCH_QUESTION: () => DISPATCH_QUESTION,
27
28
  DependencyLedger: () => DependencyLedger,
28
29
  EntryReuse: () => EntryReuse,
29
30
  FACT_CONTRACT_CASES: () => FACT_CONTRACT_CASES,
@@ -41,14 +42,18 @@ __export(index_exports, {
41
42
  RESOLUTION_QUESTIONS: () => RESOLUTION_QUESTIONS,
42
43
  RESOLUTION_RULES: () => RESOLUTION_RULES,
43
44
  RESULT_STEP: () => RESULT_STEP,
45
+ SPREAD_ENTRY: () => SPREAD_ENTRY,
44
46
  UNDER_ANSWER_RELATIONS: () => UNDER_ANSWER_RELATIONS,
45
47
  UNDER_QUESTION_ROW_BUDGET: () => UNDER_QUESTION_ROW_BUDGET,
46
48
  UNDER_RUN_ROW_BUDGET: () => UNDER_RUN_ROW_BUDGET,
47
49
  UNPLACED_STORE_NAME: () => UNPLACED_STORE_NAME,
48
50
  USES_QUESTION: () => USES_QUESTION,
51
+ VALUE_ENTRY: () => VALUE_ENTRY,
49
52
  VALUE_STEP: () => VALUE_STEP,
50
53
  WatchedMap: () => WatchedMap,
54
+ addEntryRows: () => addEntryRows,
51
55
  addPackWords: () => addPackWords,
56
+ addTemplateRows: () => addTemplateRows,
52
57
  agreedMountPrefix: () => agreedMountPrefix,
53
58
  allocationSitesOf: () => allocationSitesOf,
54
59
  alsoSteps: () => alsoSteps,
@@ -65,6 +70,7 @@ __export(index_exports, {
65
70
  comesToUnder: () => comesToUnder,
66
71
  couldBeSettled: () => couldBeSettled,
67
72
  declaredTypesOf: () => declaredTypesOf,
73
+ dispatchOutcomes: () => dispatchOutcomes,
68
74
  exitCodeFunctions: () => exitCodeFunctions,
69
75
  explainResolutionProof: () => explainResolutionProof,
70
76
  explainResolvedKey: () => explainResolvedKey,
@@ -86,6 +92,7 @@ __export(index_exports, {
86
92
  packHashes: () => packHashes,
87
93
  proofRules: () => proofRules,
88
94
  queryFacts: () => queryFacts,
95
+ readDispatchOutcomes: () => readDispatchOutcomes,
89
96
  readUses: () => readUses,
90
97
  recordsToReplay: () => recordsToReplay,
91
98
  renderExplanation: () => renderExplanation,
@@ -94,6 +101,7 @@ __export(index_exports, {
94
101
  resolvedFunctions: () => resolvedFunctions,
95
102
  settledFunction: () => settledFunction,
96
103
  sourceLeavesOf: () => sourceLeavesOf,
104
+ spanOfKey: () => spanOfKey,
97
105
  startsAtName: () => startsAtName,
98
106
  staysInItsFunction: () => staysInItsFunction,
99
107
  stillValid: () => stillValid,
@@ -121,6 +129,7 @@ var ASKING_RELATIONS = [
121
129
  "wantedEnvObject",
122
130
  "wantedStreamObject",
123
131
  "wantedExitSink",
132
+ "wantedDispatch",
124
133
  "wantedOrigin",
125
134
  "wantedCallOrigin",
126
135
  "wantedExportsOf",
@@ -638,6 +647,29 @@ function classMemberOf(name) {
638
647
  return name.startsWith(CLASS_MEMBER_PREFIX) ? name.slice(CLASS_MEMBER_PREFIX.length) : null;
639
648
  }
640
649
 
650
+ // src/valuePositions.ts
651
+ var VALUE_ENTRY = "value";
652
+ var SPREAD_ENTRY = "spread";
653
+ function addEntryRows(add, container, entries) {
654
+ entries.forEach((entry, position) => {
655
+ add("writtenEntryAt", [container, String(position), entry.key, entry.form]);
656
+ });
657
+ }
658
+ function addTemplateRows(add, template, pieces) {
659
+ let position = 0;
660
+ for (const piece of pieces) {
661
+ if ("hole" in piece) {
662
+ add("holeAt", [template, String(position), piece.hole]);
663
+ position += 1;
664
+ continue;
665
+ }
666
+ if (piece.text !== "") {
667
+ add("partAt", [template, String(position), piece.text]);
668
+ position += 1;
669
+ }
670
+ }
671
+ }
672
+
641
673
  // src/contract.ts
642
674
  var distinct = (values) => new Set(values).size;
643
675
  function leadsToOneOf(facts, key, targets) {
@@ -655,6 +687,30 @@ function leadsToOneOf(facts, key, targets) {
655
687
  return false;
656
688
  }
657
689
  var RELATIONS_THAT_CAN_CROSS = ["imports", "binds", "reExports"];
690
+ function someValueLeadsTo(facts, key) {
691
+ const target = /* @__PURE__ */ new Set([key]);
692
+ return ["returnsValue", "holdsUnderKey"].some(
693
+ (relation) => facts(relation).some((row) => leadsToOneOf(facts, row[1] ?? "", target))
694
+ );
695
+ }
696
+ function holdsValueThenSpread(facts, name) {
697
+ const entries = facts("writtenEntryAt");
698
+ const held = facts("holdsProperty");
699
+ return [...new Set(entries.map((row) => row[0]))].some((container) => {
700
+ const own = entries.filter((row) => row[0] === container);
701
+ const value = own.find((row) => row[1] === "0");
702
+ const spread2 = own.find((row) => row[1] === "1");
703
+ return own.length === 2 && value?.[3] === VALUE_ENTRY && spread2?.[3] === SPREAD_ENTRY && held.some(
704
+ (row) => row[0] === container && row[1] === name && row[2] === value[2]
705
+ ) && !held.some((row) => row[0] === container && row[2] === spread2[2]);
706
+ });
707
+ }
708
+ function valueThenSpread(facts, name) {
709
+ if (facts("writtenEntryAt").length === 0) {
710
+ return "no list or object states its entries, so nothing says the order they are written in";
711
+ }
712
+ return holdsValueThenSpread(facts, name) ? null : `no container's entries are the value under ${name} at 0, keyed as holdsProperty keys it, and then a spread at 1 keyed on the collection it unpacks`;
713
+ }
658
714
  var FACT_CONTRACT_CASES = [
659
715
  {
660
716
  name: "two functions, one parameter name",
@@ -853,6 +909,122 @@ var FACT_CONTRACT_CASES = [
853
909
  return stores.some((row) => leadsToOneOf(facts, row[0] ?? "", sites)) ? null : "the write is keyed so that nothing leads from it to the construction, so the rules cannot find the object it lands on";
854
910
  }
855
911
  },
912
+ {
913
+ name: "a table of functions called by a key worked out at run time",
914
+ requires: "a module-level table written out with a function under the name created and another under deleted, and a function that calls the table's entry under its parameter, `TABLE[kind](payload)` or the language's own spelling",
915
+ // The rules take the entries off the object the read's container
916
+ // leads to, so the container has to be keyed the way `binds` keys it,
917
+ // and each entry under the name the source writes, without quotes.
918
+ check: (facts) => {
919
+ const entries = facts("holdsProperty");
920
+ const tables = new Set(
921
+ entries.filter((row) => row[1] === "created").map((row) => row[0] ?? "").filter(
922
+ (table) => entries.some((row) => row[0] === table && row[1] === "deleted")
923
+ )
924
+ );
925
+ if (tables.size === 0) {
926
+ return "no object contains a function under created and another under deleted, written without quotes";
927
+ }
928
+ const reads = facts("choosesEntry");
929
+ if (reads.length === 0) {
930
+ return "the read at a key worked out at run time is not written down as choosesEntry, so no call through it finds the table's functions";
931
+ }
932
+ return reads.some((row) => leadsToOneOf(facts, row[1] ?? "", tables)) ? null : "the read's container is keyed so that nothing leads from it to the table";
933
+ }
934
+ },
935
+ {
936
+ name: "a function written into a table under a computed key",
937
+ requires: "a module-level empty table, and a function that writes its second parameter into it under its first, `REGISTRY[name] = handler` or the language's own spelling",
938
+ // A registry fills its table from a function its callers pass handlers
939
+ // to, so the write has to say the parameter went in, keyed so that
940
+ // `binds` leads from where it went to the table.
941
+ check: (facts) => {
942
+ const params = new Set(facts("paramOf").map((row) => row[2] ?? ""));
943
+ const writes = facts("holdsUnderKey").filter(
944
+ (row) => leadsToOneOf(facts, row[1] ?? "", params)
945
+ );
946
+ if (writes.length === 0) {
947
+ return "the write of a parameter into a table under a computed key is not written down as holdsUnderKey";
948
+ }
949
+ const objects = new Set(facts("objectValue").map((row) => row[0] ?? ""));
950
+ return writes.some((row) => leadsToOneOf(facts, row[0] ?? "", objects)) ? null : "the write is keyed so that nothing leads from it to the table";
951
+ }
952
+ },
953
+ {
954
+ name: "a list written with a spread",
955
+ requires: "a module-level name assigned a written-out list of a name and then a spread of another name, `[first, ...rest]` or the language's own spelling",
956
+ check: (facts) => valueThenSpread(facts, "0")
957
+ },
958
+ {
959
+ name: "an object written with a spread",
960
+ requires: "a module-level name assigned an object or a dictionary written out with a name under the key a, written as a string where the language writes keys that way, and then a spread of another name",
961
+ check: (facts) => valueThenSpread(facts, "a")
962
+ },
963
+ {
964
+ name: "a template string with a hole",
965
+ requires: "a module-level name assigned the result of a call, and a template string that puts that name between two pieces of text, `/users/${id}/posts` or the language's own spelling",
966
+ check: (facts) => {
967
+ const parts = facts("partAt");
968
+ const [hole, ...moreHoles] = facts("holeAt");
969
+ if (parts.length !== 2 || hole === void 0 || moreHoles.length > 0) {
970
+ return `expected two parts and one hole, found ${parts.length} and ${moreHoles.length + (hole === void 0 ? 0 : 1)}`;
971
+ }
972
+ const positions = [...parts, hole].filter((row) => row[0] === hole[0]).map((row) => row[1]).sort();
973
+ if (positions.join() !== "0,1,2" || hole[1] !== "1") {
974
+ return "the parts and the hole of one template are not numbered together in source order from 0";
975
+ }
976
+ const calls = new Set(facts("call").map((row) => row[0] ?? ""));
977
+ return leadsToOneOf(facts, hole[2] ?? "", calls) ? null : "the hole is keyed so that nothing leads from it to the value the name is given";
978
+ }
979
+ },
980
+ {
981
+ name: "a callback given each element of a list",
982
+ requires: "a function that maps its parameter through a callback returning the element it is given, `users.map((user) => user)`, a comprehension or the language's own spelling",
983
+ // The collection is a read of the function's parameter, and the
984
+ // callback's own read of its variable joins the key elementOf states.
985
+ check: (facts) => {
986
+ const [row, ...more] = facts("elementOf");
987
+ if (row === void 0 || more.length > 0) {
988
+ return `expected one elementOf row, found ${more.length + (row === void 0 ? 0 : 1)}`;
989
+ }
990
+ const params = new Set(facts("paramOf").map((one) => one[2] ?? ""));
991
+ if (!leadsToOneOf(facts, row[1] ?? "", params)) {
992
+ return "the collection is keyed so that nothing leads from it to the parameter it reads";
993
+ }
994
+ return someValueLeadsTo(facts, row[0] ?? "") ? null : "no read of the variable leads to the key elementOf gives it, so nothing knows what the read is given";
995
+ }
996
+ },
997
+ {
998
+ name: "a callback given the parts of each element of a list",
999
+ requires: "a function that maps its parameter through a callback taking each element apart into two names and returning the first, `pairs.map(([name, kind]) => name)`, `[name for name, kind in pairs]` or the language's own spelling",
1000
+ check: (facts) => {
1001
+ const parts = facts("elementPartOf");
1002
+ const first = parts.find((row) => row[2] === "0");
1003
+ const second = parts.find((row) => row[2] === "1");
1004
+ if (first === void 0 || second === void 0) {
1005
+ return "no two names are given parts 0 and 1 of each element, so nothing says what a name taken apart is given";
1006
+ }
1007
+ const params = new Set(facts("paramOf").map((one) => one[2] ?? ""));
1008
+ if (first[1] !== second[1] || !leadsToOneOf(facts, first[1] ?? "", params)) {
1009
+ return "the two parts name different collections, or one that nothing leads from to the parameter it reads";
1010
+ }
1011
+ return someValueLeadsTo(facts, first[0] ?? "") ? null : "no read of the first name leads to the key elementPartOf gives it";
1012
+ }
1013
+ },
1014
+ {
1015
+ name: "a callback given each position in a list",
1016
+ requires: "a function that maps its parameter through a callback taking the element and then its position and returning the position, `users.map((user, at) => at)`, `enumerate` in a comprehension, or the language's own spelling",
1017
+ check: (facts) => {
1018
+ const [row, ...more] = facts("positionOf");
1019
+ if (row === void 0 || more.length > 0) {
1020
+ return `expected one positionOf row, found ${more.length + (row === void 0 ? 0 : 1)}`;
1021
+ }
1022
+ if (!facts("elementOf").some((one) => one[0] === row[1])) {
1023
+ return "the position is tied to a variable that no elementOf row gives an element, so nothing says what it counts through";
1024
+ }
1025
+ return someValueLeadsTo(facts, row[0] ?? "") ? null : "no read of the position leads to the key positionOf gives it";
1026
+ }
1027
+ },
856
1028
  {
857
1029
  name: "a value another file declares",
858
1030
  requires: "two files, one declaring a value and the other reading it by name",
@@ -1255,13 +1427,13 @@ var FactLog = class {
1255
1427
  hashesByKey(relation, row) {
1256
1428
  const hashes = /* @__PURE__ */ new Map();
1257
1429
  const atoms = row.map((atom) => this.hashesOf(String(atom)));
1258
- const term = (hash, column) => spread(hash + Math.imul(column + 1, 2654435761) >>> 0);
1430
+ const term = (hash, column2) => spread(hash + Math.imul(column2 + 1, 2654435761) >>> 0);
1259
1431
  let loose = hashString(relation);
1260
- atoms.forEach((atom, column) => {
1261
- loose = loose + term(atom.loose, column) >>> 0;
1432
+ atoms.forEach((atom, column2) => {
1433
+ loose = loose + term(atom.loose, column2) >>> 0;
1262
1434
  });
1263
- atoms.forEach((atom, column) => {
1264
- const key = String(row[column]);
1435
+ atoms.forEach((atom, column2) => {
1436
+ const key = String(row[column2]);
1265
1437
  if (!atom.key || hashes.has(key)) {
1266
1438
  return;
1267
1439
  }
@@ -1358,6 +1530,107 @@ var FactLog = class {
1358
1530
  }
1359
1531
  };
1360
1532
 
1533
+ // src/dispatch.ts
1534
+ function dispatchOutcomes(db, calls, program = resolutionProgram()) {
1535
+ const outcomes = /* @__PURE__ */ new Map();
1536
+ if (calls.length === 0) {
1537
+ return outcomes;
1538
+ }
1539
+ askResolution(db, calls, DISPATCH_QUESTION, program);
1540
+ return readDispatchOutcomes(db, calls);
1541
+ }
1542
+ var DISPATCH_QUESTION = "wantedDispatch";
1543
+ function readDispatchOutcomes(db, calls) {
1544
+ const outcomes = /* @__PURE__ */ new Map();
1545
+ for (const call of new Set(calls)) {
1546
+ const sites = column(db, "wantedDispatchSite", call, 1);
1547
+ if (sites.length === 0) {
1548
+ continue;
1549
+ }
1550
+ outcomes.set(call, outcomeOf(db, call, sites));
1551
+ }
1552
+ return outcomes;
1553
+ }
1554
+ function outcomeOf(db, call, sites) {
1555
+ const byEntry = /* @__PURE__ */ new Map();
1556
+ for (const entry of column(db, "wantedDispatchEntry", call, 1)) {
1557
+ byEntry.set(entry, /* @__PURE__ */ new Set());
1558
+ }
1559
+ for (const row of db.lookup("wantedDispatchLikeEntry", 0, call)) {
1560
+ const held = String(row[3]);
1561
+ if (!byEntry.has(held) && matchesWrittenPart(db, String(row[1]), String(row[2]))) {
1562
+ byEntry.set(held, /* @__PURE__ */ new Set());
1563
+ }
1564
+ }
1565
+ for (const relation of ENTRY_FUNCTIONS) {
1566
+ for (const row of db.lookup(relation, 0, call)) {
1567
+ byEntry.get(String(row[1]))?.add(String(row[2]));
1568
+ }
1569
+ }
1570
+ const functions = [
1571
+ ...new Set([...byEntry.values()].flatMap((found) => [...found]))
1572
+ ];
1573
+ const unresolved = [...byEntry.values()].filter(
1574
+ (found) => found.size === 0
1575
+ ).length;
1576
+ const through = /* @__PURE__ */ new Map();
1577
+ for (const row of db.lookup("wantedDispatchThrough", 0, call)) {
1578
+ const inner = through.get(String(row[1])) ?? [];
1579
+ inner.push(String(row[2]));
1580
+ through.set(String(row[1]), inner);
1581
+ }
1582
+ return {
1583
+ functions,
1584
+ certain: functions.length === 1 && unresolved === 0 && narrowedToOneName(db, sites) && column(db, "wantedDispatchComputed", call, 1).length === 0 && column(db, "wantedDispatchInBranch", call, 1).length === 0,
1585
+ unresolved,
1586
+ through,
1587
+ containers: [
1588
+ ...new Set(
1589
+ sites.flatMap(
1590
+ (site) => ENTRY_READS.flatMap((relation) => column(db, relation, site, 1))
1591
+ )
1592
+ )
1593
+ ]
1594
+ };
1595
+ }
1596
+ var ENTRY_READS = [
1597
+ "choosesEntry",
1598
+ "choosesNamedEntry",
1599
+ "choosesEntryLike",
1600
+ "readsEntry"
1601
+ ];
1602
+ var ENTRY_FUNCTIONS = [
1603
+ "wantedDispatchFunction",
1604
+ "wantedDispatchLikeFunction"
1605
+ ];
1606
+ function matchesWrittenPart(db, site, name) {
1607
+ return db.lookup("choosesEntryLike", 0, site).some((row) => {
1608
+ const prefix = String(row[2]);
1609
+ const suffix = String(row[3]);
1610
+ return name.length >= prefix.length + suffix.length && name.startsWith(prefix) && name.endsWith(suffix);
1611
+ });
1612
+ }
1613
+ function narrowedToOneName(db, sites) {
1614
+ const names = /* @__PURE__ */ new Set();
1615
+ for (const site of sites) {
1616
+ if (db.lookup("choosesEntry", 0, site).length > 0) {
1617
+ return false;
1618
+ }
1619
+ for (const relation of NAMED_READS) {
1620
+ for (const row of db.lookup(relation, 0, site)) {
1621
+ names.add(String(row[2]));
1622
+ }
1623
+ }
1624
+ }
1625
+ return names.size === 1;
1626
+ }
1627
+ var NAMED_READS = ["choosesNamedEntry", "readsEntry"];
1628
+ function column(db, relation, key, at) {
1629
+ return [
1630
+ ...new Set(db.lookup(relation, 0, key).map((row) => String(row[at])))
1631
+ ];
1632
+ }
1633
+
1361
1634
  // src/entryReuse.ts
1362
1635
  function fileOfKey(key) {
1363
1636
  return key.slice(0, key.lastIndexOf(":"));
@@ -1887,14 +2160,21 @@ var SPAN_KEY = /^(.*):(\d+)-(\d+)$/;
1887
2160
  function spanFileOf(key) {
1888
2161
  return key.match(SPAN_KEY)?.[1] ?? null;
1889
2162
  }
1890
- function nodeOfKey(rootsByFile, key) {
2163
+ function spanOfKey(key) {
1891
2164
  const span = key.match(SPAN_KEY);
2165
+ return span === null ? null : {
2166
+ file: span[1],
2167
+ start: Number(span[2]),
2168
+ end: Number(span[3])
2169
+ };
2170
+ }
2171
+ function nodeOfKey(rootsByFile, key) {
2172
+ const span = spanOfKey(key);
1892
2173
  if (span === null) {
1893
2174
  return null;
1894
2175
  }
1895
- const root = rootsByFile.get(span[1]);
1896
- const start = Number(span[2]);
1897
- const end = Number(span[3]);
2176
+ const root = rootsByFile.get(span.file);
2177
+ const { start, end } = span;
1898
2178
  if (root === void 0 || end <= start) {
1899
2179
  return null;
1900
2180
  }
@@ -2465,6 +2745,165 @@ var STEP_LATTICE = [
2465
2745
  { soFar: RESULT_STEP, next: INSTANCE_STEP, took: RESULT_STEP },
2466
2746
  { soFar: RESULT_STEP, next: RESULT_STEP, took: RESULT_STEP }
2467
2747
  ];
2748
+ var DISPATCH_RULES = [
2749
+ (0, import_datalog5.rule)(
2750
+ "entryRead",
2751
+ [(0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o")],
2752
+ [(0, import_datalog5.lit)("choosesEntry", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o"))]
2753
+ ),
2754
+ (0, import_datalog5.rule)(
2755
+ "entryRead",
2756
+ [(0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o")],
2757
+ [(0, import_datalog5.lit)("namedEntryRead", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("n"))]
2758
+ ),
2759
+ (0, import_datalog5.rule)(
2760
+ "entryRead",
2761
+ [(0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o")],
2762
+ [(0, import_datalog5.lit)("choosesEntryLike", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("p"), (0, import_datalog5.variable)("s"))]
2763
+ ),
2764
+ // Every entry of a read whose key is partly written out, by name. A rule
2765
+ // cannot compare strings, so dispatch.ts keeps the names that match, and
2766
+ // these stay out of `dispatchesTo`.
2767
+ (0, import_datalog5.rule)(
2768
+ "likeEntryAt",
2769
+ [(0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("n"), (0, import_datalog5.variable)("held")],
2770
+ [
2771
+ (0, import_datalog5.lit)("choosesEntryLike", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("p"), (0, import_datalog5.variable)("s")),
2772
+ (0, import_datalog5.lit)("containerOf", (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("obj")),
2773
+ (0, import_datalog5.lit)("contains", (0, import_datalog5.variable)("obj"), (0, import_datalog5.variable)("n"), (0, import_datalog5.variable)("held"))
2774
+ ]
2775
+ ),
2776
+ (0, import_datalog5.rule)(
2777
+ "namedEntryRead",
2778
+ [(0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("n")],
2779
+ [(0, import_datalog5.lit)("choosesNamedEntry", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("n"))]
2780
+ ),
2781
+ (0, import_datalog5.rule)(
2782
+ "namedEntryRead",
2783
+ [(0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("n")],
2784
+ [(0, import_datalog5.lit)("readsEntry", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("n"))]
2785
+ ),
2786
+ // The container a read takes its entry from: an object written out, or
2787
+ // a collection the program fills through its methods.
2788
+ (0, import_datalog5.rule)("containerOf", [(0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("obj")], [(0, import_datalog5.lit)("objectOf", (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("obj"))]),
2789
+ (0, import_datalog5.rule)(
2790
+ "containerOf",
2791
+ [(0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("c")],
2792
+ [
2793
+ (0, import_datalog5.lit)("reaches", (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("c"), VALUE_STEP),
2794
+ (0, import_datalog5.lit)("collectionValue", (0, import_datalog5.variable)("c"))
2795
+ ]
2796
+ ),
2797
+ (0, import_datalog5.rule)("containerOf", [(0, import_datalog5.variable)("c"), (0, import_datalog5.variable)("c")], [(0, import_datalog5.lit)("collectionValue", (0, import_datalog5.variable)("c"))]),
2798
+ (0, import_datalog5.rule)(
2799
+ "entryAt",
2800
+ [(0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("held")],
2801
+ [
2802
+ (0, import_datalog5.lit)("choosesEntry", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o")),
2803
+ (0, import_datalog5.lit)("containerOf", (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("obj")),
2804
+ (0, import_datalog5.lit)("contains", (0, import_datalog5.variable)("obj"), (0, import_datalog5.variable)("n"), (0, import_datalog5.variable)("held"))
2805
+ ],
2806
+ "any entry"
2807
+ ),
2808
+ (0, import_datalog5.rule)(
2809
+ "entryAt",
2810
+ [(0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("held")],
2811
+ [
2812
+ (0, import_datalog5.lit)("namedEntryRead", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("n")),
2813
+ (0, import_datalog5.lit)("containerOf", (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("obj")),
2814
+ (0, import_datalog5.lit)("contains", (0, import_datalog5.variable)("obj"), (0, import_datalog5.variable)("n"), (0, import_datalog5.variable)("held"))
2815
+ ],
2816
+ "named entry"
2817
+ ),
2818
+ (0, import_datalog5.rule)(
2819
+ "entryAt",
2820
+ [(0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("held")],
2821
+ [(0, import_datalog5.lit)("computedEntryAt", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("held"))]
2822
+ ),
2823
+ // A write under a key the source computes, `T[name] = fn` or a map's
2824
+ // `set`, could be under any name, so every read of the container may
2825
+ // come back with it, a read narrowed to one name included.
2826
+ (0, import_datalog5.rule)(
2827
+ "computedEntryAt",
2828
+ [(0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("held")],
2829
+ [
2830
+ (0, import_datalog5.lit)("entryRead", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o")),
2831
+ (0, import_datalog5.lit)("containerOf", (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("c")),
2832
+ (0, import_datalog5.lit)("refersToObject", (0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("c")),
2833
+ (0, import_datalog5.lit)("holdsUnderKey", (0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("held"))
2834
+ ],
2835
+ "entry under a computed key"
2836
+ ),
2837
+ // The read a call takes its function from: the callee itself, a name
2838
+ // for it, or the call when the call is the read, as Ruby's `send` is.
2839
+ (0, import_datalog5.rule)(
2840
+ "callsEntryAt",
2841
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site")],
2842
+ [(0, import_datalog5.lit)("call", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site")), (0, import_datalog5.lit)("entryRead", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o"))]
2843
+ ),
2844
+ (0, import_datalog5.rule)(
2845
+ "callsEntryAt",
2846
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site")],
2847
+ [
2848
+ (0, import_datalog5.lit)("call", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("c")),
2849
+ (0, import_datalog5.lit)("reaches", (0, import_datalog5.variable)("c"), (0, import_datalog5.variable)("site"), VALUE_STEP),
2850
+ (0, import_datalog5.lit)("entryRead", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o"))
2851
+ ]
2852
+ ),
2853
+ (0, import_datalog5.rule)("callsEntryAt", [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("r")], [(0, import_datalog5.lit)("callsChosenEntry", (0, import_datalog5.variable)("r"))]),
2854
+ (0, import_datalog5.rule)(
2855
+ "callsEntryAt",
2856
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site")],
2857
+ [(0, import_datalog5.lit)("callsEntryInBranch", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site"))]
2858
+ ),
2859
+ // `c ? T[k] : undefined` is one of its branches, so a call through it
2860
+ // may run what the read gives. The other branch may be some other
2861
+ // function, so dispatch.ts never counts such a call as certain.
2862
+ (0, import_datalog5.rule)(
2863
+ "callsEntryInBranch",
2864
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site")],
2865
+ [(0, import_datalog5.lit)("call", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("c")), (0, import_datalog5.lit)("branchReadsEntry", (0, import_datalog5.variable)("c"), (0, import_datalog5.variable)("site"))]
2866
+ ),
2867
+ (0, import_datalog5.rule)(
2868
+ "callsEntryInBranch",
2869
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site")],
2870
+ [
2871
+ (0, import_datalog5.lit)("call", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("c")),
2872
+ (0, import_datalog5.lit)("reaches", (0, import_datalog5.variable)("c"), (0, import_datalog5.variable)("x"), VALUE_STEP),
2873
+ (0, import_datalog5.lit)("branchReadsEntry", (0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("site"))
2874
+ ]
2875
+ ),
2876
+ (0, import_datalog5.rule)(
2877
+ "branchReadsEntry",
2878
+ [(0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("site")],
2879
+ [
2880
+ (0, import_datalog5.lit)("conditionalBranch", (0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("site")),
2881
+ (0, import_datalog5.lit)("entryRead", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o"))
2882
+ ]
2883
+ ),
2884
+ (0, import_datalog5.rule)(
2885
+ "branchReadsEntry",
2886
+ [(0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("site")],
2887
+ [
2888
+ (0, import_datalog5.lit)("conditionalBranch", (0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("b")),
2889
+ (0, import_datalog5.lit)("reaches", (0, import_datalog5.variable)("b"), (0, import_datalog5.variable)("site"), VALUE_STEP),
2890
+ (0, import_datalog5.lit)("entryRead", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("o"))
2891
+ ]
2892
+ ),
2893
+ (0, import_datalog5.rule)(
2894
+ "dispatchesTo",
2895
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("f")],
2896
+ [
2897
+ (0, import_datalog5.lit)("callsEntryAt", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site")),
2898
+ (0, import_datalog5.lit)("entryAt", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("held")),
2899
+ (0, import_datalog5.lit)("comesTo", (0, import_datalog5.variable)("held"), (0, import_datalog5.variable)("f")),
2900
+ (0, import_datalog5.lit)("func", (0, import_datalog5.variable)("f"))
2901
+ ]
2902
+ ),
2903
+ // A collection a program fills is an object the way a written one is,
2904
+ // so a write through a name for it lands on it.
2905
+ (0, import_datalog5.rule)("refersToObject", [(0, import_datalog5.variable)("c"), (0, import_datalog5.variable)("c")], [(0, import_datalog5.lit)("collectionValue", (0, import_datalog5.variable)("c"))])
2906
+ ];
2468
2907
  var STATED_RULES = [
2469
2908
  // Aliasing: const x = y, or an identifier referencing a declaration.
2470
2909
  // Every chain that follows a name takes the name hops, so a hop added
@@ -3567,6 +4006,17 @@ var STATED_RULES = [
3567
4006
  (0, import_datalog5.lit)("callsNamed", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("r0"))
3568
4007
  ]
3569
4008
  ),
4009
+ // The same, called straight off the factory's call, as
4010
+ // `register("created")(handler)` or a Python decorator written that way.
4011
+ (0, import_datalog5.rule)(
4012
+ "callsFunction",
4013
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("f")],
4014
+ [
4015
+ (0, import_datalog5.lit)("returnsValue", (0, import_datalog5.variable)("g"), (0, import_datalog5.variable)("f")),
4016
+ (0, import_datalog5.lit)("callsFunction", (0, import_datalog5.variable)("r0"), (0, import_datalog5.variable)("g")),
4017
+ (0, import_datalog5.lit)("call", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("r0"))
4018
+ ]
4019
+ ),
3570
4020
  // The expressions that refer to an object under another name, through
3571
4021
  // a name hop or an import. Asked from the object, so it visits only
3572
4022
  // them.
@@ -4012,6 +4462,7 @@ var STATED_RULES = [
4012
4462
  [(0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("z")],
4013
4463
  [(0, import_datalog5.lit)("comesTo", (0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("z")), (0, import_datalog5.lit)("func", (0, import_datalog5.variable)("z"))]
4014
4464
  ),
4465
+ ...DISPATCH_RULES,
4015
4466
  ...PASSED_ON_RULES
4016
4467
  ];
4017
4468
  function alsoSteps(rules) {
@@ -4215,18 +4666,112 @@ var RESOLUTION_QUESTIONS = [
4215
4666
  ),
4216
4667
  // The functions whose return becomes the exit code: a call the value
4217
4668
  // that ends up as the code is, or one it reaches. DESIGN.md has the seeds.
4669
+ (0, import_datalog5.rule)("exitStart", [(0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("x")], [(0, import_datalog5.lit)("wantedExitSink", (0, import_datalog5.variable)("x"))]),
4670
+ // `reaches` takes no step through a call that picks its function from a
4671
+ // table, so the walk starts again from what each function there returns.
4672
+ (0, import_datalog5.rule)(
4673
+ "exitStart",
4674
+ [(0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("ret")],
4675
+ [
4676
+ (0, import_datalog5.lit)("exitReached", (0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("y")),
4677
+ (0, import_datalog5.lit)("dispatchesTo", (0, import_datalog5.variable)("y"), (0, import_datalog5.variable)("f")),
4678
+ (0, import_datalog5.lit)("returnsValue", (0, import_datalog5.variable)("f"), (0, import_datalog5.variable)("ret"))
4679
+ ]
4680
+ ),
4681
+ (0, import_datalog5.rule)("exitReached", [(0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("s")], [(0, import_datalog5.lit)("exitStart", (0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("s"))]),
4682
+ (0, import_datalog5.rule)(
4683
+ "exitReached",
4684
+ [(0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("y")],
4685
+ [
4686
+ (0, import_datalog5.lit)("exitStart", (0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("s")),
4687
+ (0, import_datalog5.lit)("reaches", (0, import_datalog5.variable)("s"), (0, import_datalog5.variable)("y"), (0, import_datalog5.variable)("kind"))
4688
+ ]
4689
+ ),
4218
4690
  (0, import_datalog5.rule)(
4219
4691
  "wantedExitCodeFrom",
4220
4692
  [(0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("f")],
4221
- [(0, import_datalog5.lit)("wantedExitSink", (0, import_datalog5.variable)("x")), (0, import_datalog5.lit)("invokes", (0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("f"))]
4693
+ [(0, import_datalog5.lit)("exitReached", (0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("y")), (0, import_datalog5.lit)("invokes", (0, import_datalog5.variable)("y"), (0, import_datalog5.variable)("f"))]
4222
4694
  ),
4223
4695
  (0, import_datalog5.rule)(
4224
4696
  "wantedExitCodeFrom",
4225
4697
  [(0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("f")],
4698
+ [(0, import_datalog5.lit)("exitReached", (0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("y")), (0, import_datalog5.lit)("dispatchesTo", (0, import_datalog5.variable)("y"), (0, import_datalog5.variable)("f"))]
4699
+ ),
4700
+ // The functions a call may run when it takes one from a table, with the
4701
+ // entry each came from, and the entries that come to no function. The
4702
+ // reader in dispatch.ts decides whether the call runs exactly one.
4703
+ (0, import_datalog5.rule)(
4704
+ "wantedDispatchSite",
4705
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site")],
4706
+ [(0, import_datalog5.lit)("wantedDispatch", (0, import_datalog5.variable)("r")), (0, import_datalog5.lit)("callsEntryAt", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site"))]
4707
+ ),
4708
+ (0, import_datalog5.rule)(
4709
+ "wantedDispatchEntry",
4710
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("held")],
4711
+ [
4712
+ (0, import_datalog5.lit)("wantedDispatch", (0, import_datalog5.variable)("r")),
4713
+ (0, import_datalog5.lit)("callsEntryAt", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site")),
4714
+ (0, import_datalog5.lit)("entryAt", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("held"))
4715
+ ]
4716
+ ),
4717
+ (0, import_datalog5.rule)(
4718
+ "wantedDispatchInBranch",
4719
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site")],
4720
+ [
4721
+ (0, import_datalog5.lit)("wantedDispatch", (0, import_datalog5.variable)("r")),
4722
+ (0, import_datalog5.lit)("callsEntryInBranch", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site"))
4723
+ ]
4724
+ ),
4725
+ (0, import_datalog5.rule)(
4726
+ "wantedDispatchComputed",
4727
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("held")],
4728
+ [
4729
+ (0, import_datalog5.lit)("wantedDispatch", (0, import_datalog5.variable)("r")),
4730
+ (0, import_datalog5.lit)("callsEntryAt", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site")),
4731
+ (0, import_datalog5.lit)("computedEntryAt", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("held"))
4732
+ ]
4733
+ ),
4734
+ (0, import_datalog5.rule)(
4735
+ "wantedDispatchFunction",
4736
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("held"), (0, import_datalog5.variable)("f")],
4737
+ [
4738
+ (0, import_datalog5.lit)("wantedDispatch", (0, import_datalog5.variable)("r")),
4739
+ (0, import_datalog5.lit)("callsEntryAt", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site")),
4740
+ (0, import_datalog5.lit)("entryAt", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("held")),
4741
+ (0, import_datalog5.lit)("comesTo", (0, import_datalog5.variable)("held"), (0, import_datalog5.variable)("f")),
4742
+ (0, import_datalog5.lit)("func", (0, import_datalog5.variable)("f"))
4743
+ ]
4744
+ ),
4745
+ (0, import_datalog5.rule)(
4746
+ "wantedDispatchLikeEntry",
4747
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("n"), (0, import_datalog5.variable)("held")],
4748
+ [
4749
+ (0, import_datalog5.lit)("wantedDispatch", (0, import_datalog5.variable)("r")),
4750
+ (0, import_datalog5.lit)("callsEntryAt", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site")),
4751
+ (0, import_datalog5.lit)("likeEntryAt", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("n"), (0, import_datalog5.variable)("held"))
4752
+ ]
4753
+ ),
4754
+ (0, import_datalog5.rule)(
4755
+ "wantedDispatchLikeFunction",
4756
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("held"), (0, import_datalog5.variable)("f")],
4757
+ [
4758
+ (0, import_datalog5.lit)("wantedDispatch", (0, import_datalog5.variable)("r")),
4759
+ (0, import_datalog5.lit)("callsEntryAt", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("site")),
4760
+ (0, import_datalog5.lit)("likeEntryAt", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("n"), (0, import_datalog5.variable)("held")),
4761
+ (0, import_datalog5.lit)("comesTo", (0, import_datalog5.variable)("held"), (0, import_datalog5.variable)("f")),
4762
+ (0, import_datalog5.lit)("func", (0, import_datalog5.variable)("f"))
4763
+ ]
4764
+ ),
4765
+ // What a function there calls in its own body, for an entry written as
4766
+ // a lambda, which no summary describes.
4767
+ (0, import_datalog5.rule)(
4768
+ "wantedDispatchThrough",
4769
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("f"), (0, import_datalog5.variable)("g")],
4226
4770
  [
4227
- (0, import_datalog5.lit)("wantedExitSink", (0, import_datalog5.variable)("x")),
4228
- (0, import_datalog5.lit)("reaches", (0, import_datalog5.variable)("x"), (0, import_datalog5.variable)("y"), (0, import_datalog5.variable)("kind")),
4229
- (0, import_datalog5.lit)("invokes", (0, import_datalog5.variable)("y"), (0, import_datalog5.variable)("f"))
4771
+ (0, import_datalog5.lit)("wantedDispatch", (0, import_datalog5.variable)("r")),
4772
+ (0, import_datalog5.lit)("dispatchesTo", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("f")),
4773
+ (0, import_datalog5.lit)("makesCall", (0, import_datalog5.variable)("f"), (0, import_datalog5.variable)("r2")),
4774
+ (0, import_datalog5.lit)("invokes", (0, import_datalog5.variable)("r2"), (0, import_datalog5.variable)("g"))
4230
4775
  ]
4231
4776
  ),
4232
4777
  // The calls that hand a stream on, so the asker can read the callee's file.
@@ -4661,6 +5206,7 @@ var UNDER_ANSWER_RELATIONS = [
4661
5206
  ASKING_RELATIONS,
4662
5207
  BASE_CLASS_RULE,
4663
5208
  DEFAULT_IMPORT_NAME,
5209
+ DISPATCH_QUESTION,
4664
5210
  DependencyLedger,
4665
5211
  EntryReuse,
4666
5212
  FACT_CONTRACT_CASES,
@@ -4678,14 +5224,18 @@ var UNDER_ANSWER_RELATIONS = [
4678
5224
  RESOLUTION_QUESTIONS,
4679
5225
  RESOLUTION_RULES,
4680
5226
  RESULT_STEP,
5227
+ SPREAD_ENTRY,
4681
5228
  UNDER_ANSWER_RELATIONS,
4682
5229
  UNDER_QUESTION_ROW_BUDGET,
4683
5230
  UNDER_RUN_ROW_BUDGET,
4684
5231
  UNPLACED_STORE_NAME,
4685
5232
  USES_QUESTION,
5233
+ VALUE_ENTRY,
4686
5234
  VALUE_STEP,
4687
5235
  WatchedMap,
5236
+ addEntryRows,
4688
5237
  addPackWords,
5238
+ addTemplateRows,
4689
5239
  agreedMountPrefix,
4690
5240
  allocationSitesOf,
4691
5241
  alsoSteps,
@@ -4702,6 +5252,7 @@ var UNDER_ANSWER_RELATIONS = [
4702
5252
  comesToUnder,
4703
5253
  couldBeSettled,
4704
5254
  declaredTypesOf,
5255
+ dispatchOutcomes,
4705
5256
  exitCodeFunctions,
4706
5257
  explainResolutionProof,
4707
5258
  explainResolvedKey,
@@ -4723,6 +5274,7 @@ var UNDER_ANSWER_RELATIONS = [
4723
5274
  packHashes,
4724
5275
  proofRules,
4725
5276
  queryFacts,
5277
+ readDispatchOutcomes,
4726
5278
  readUses,
4727
5279
  recordsToReplay,
4728
5280
  renderExplanation,
@@ -4731,6 +5283,7 @@ var UNDER_ANSWER_RELATIONS = [
4731
5283
  resolvedFunctions,
4732
5284
  settledFunction,
4733
5285
  sourceLeavesOf,
5286
+ spanOfKey,
4734
5287
  startsAtName,
4735
5288
  staysInItsFunction,
4736
5289
  stillValid,