gofront 1.7.0 → 1.9.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.
Files changed (194) hide show
  1. package/README.md +14 -14
  2. package/package.json +10 -38
  3. package/src/backend/js/expressions.js +2060 -389
  4. package/src/backend/js/index.js +586 -174
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +936 -1644
  7. package/src/backend/js/statements.js +1349 -132
  8. package/src/backend/js/stdlib/index.js +51 -94
  9. package/src/backend/js/templ.js +67 -118
  10. package/src/backend/js/type-info.js +436 -0
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +80 -0
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +753 -872
  15. package/src/backend/wasm/emit-builtins.js +914 -237
  16. package/src/backend/wasm/emit-exprs.js +1456 -195
  17. package/src/backend/wasm/emit-maps.js +78 -380
  18. package/src/backend/wasm/emit-stmts.js +1659 -214
  19. package/src/backend/wasm/emit.js +614 -88
  20. package/src/backend/wasm/encode.js +43 -13
  21. package/src/backend/wasm/globals.js +491 -0
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +406 -3195
  24. package/src/backend/wasm/method-expr.js +392 -0
  25. package/src/backend/wasm/module-emitter.js +1615 -0
  26. package/src/backend/wasm/monomorph.js +164 -9
  27. package/src/backend/wasm/natives.js +596 -0
  28. package/src/backend/wasm/prescan.js +249 -0
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -37
  31. package/src/backend/wasm/stdlib/simd.js +293 -28
  32. package/src/backend/wasm/types.js +260 -27
  33. package/src/backend/wasm/variadic.js +93 -0
  34. package/src/backend/wasm/wat.js +47 -20
  35. package/src/{cli-core.js → cli/cli-core.js} +241 -11
  36. package/src/{test-runner.js → cli/test-runner.js} +188 -122
  37. package/src/{vendor.js → cli/vendor.js} +3 -4
  38. package/src/compiler.js +202 -386
  39. package/src/index.js +15 -169
  40. package/src/{lexer.js → lexer/lexer.js} +203 -62
  41. package/src/{templ-lexer.js → lexer/templ-lexer.js} +61 -18
  42. package/src/{tokens.js → lexer/tokens.js} +2 -0
  43. package/src/link/sourcemap.js +97 -0
  44. package/src/link/stdlib.js +737 -0
  45. package/src/link/type-names.js +148 -0
  46. package/src/link/wasm.js +318 -0
  47. package/src/lower/bounds.js +547 -0
  48. package/src/lower/boxing.js +1 -1
  49. package/src/lower/captures.js +267 -64
  50. package/src/lower/embedding.js +32 -3
  51. package/src/lower/errors-as.js +196 -0
  52. package/src/lower/fmt-formatters.js +905 -0
  53. package/src/lower/functions.js +2 -0
  54. package/src/lower/gen-types.js +274 -0
  55. package/src/lower/index.js +33 -19
  56. package/src/lower/json-codecs.js +1379 -0
  57. package/src/lower/ownership.js +47 -8
  58. package/src/lower/range.js +86 -1
  59. package/src/lower/reachability.js +150 -0
  60. package/src/lower/walk.js +12 -0
  61. package/src/parser/declarations.js +83 -15
  62. package/src/parser/expressions.js +110 -18
  63. package/src/parser/index.js +2 -1
  64. package/src/parser/statements.js +38 -22
  65. package/src/{templ-parser.js → parser/templ.js} +179 -17
  66. package/src/parser/types.js +70 -9
  67. package/src/resolver.js +21 -1
  68. package/src/typechecker/assignability.js +291 -19
  69. package/src/typechecker/const-fold.js +399 -0
  70. package/src/typechecker/constants.js +282 -0
  71. package/src/typechecker/expressions.js +1220 -183
  72. package/src/typechecker/index.js +659 -92
  73. package/src/typechecker/init-order.js +426 -0
  74. package/src/typechecker/operand-text.js +40 -0
  75. package/src/typechecker/resolve.js +613 -62
  76. package/src/typechecker/statements.js +212 -20
  77. package/src/typechecker/stdlib/excluded.js +902 -0
  78. package/src/typechecker/stdlib/index.js +38 -0
  79. package/src/typechecker/stdlib/simd.js +65 -15
  80. package/src/typechecker/termination.js +11 -2
  81. package/src/typechecker/types.js +268 -514
  82. package/stdlib/bytes/buffer.go +479 -0
  83. package/stdlib/bytes/bytes.go +1010 -0
  84. package/stdlib/bytes/reader.go +161 -0
  85. package/stdlib/cmp/cmp.go +65 -0
  86. package/stdlib/context/context.go +115 -0
  87. package/stdlib/crypto/rand/rand.go +34 -0
  88. package/stdlib/crypto/sha256/sha256.go +249 -0
  89. package/stdlib/encoding/base64/base64.go +331 -0
  90. package/stdlib/encoding/binary/binary.go +332 -0
  91. package/stdlib/encoding/hex/hex.go +140 -0
  92. package/stdlib/encoding/json/errors.go +31 -0
  93. package/stdlib/encoding/json/indent.go +71 -0
  94. package/stdlib/encoding/json/json.go +62 -0
  95. package/stdlib/encoding/json/number.go +12 -0
  96. package/stdlib/encoding/json/stream.go +173 -0
  97. package/stdlib/errors/errors.go +174 -0
  98. package/stdlib/fmt/errors.go +110 -0
  99. package/stdlib/fmt/format.go +732 -0
  100. package/stdlib/fmt/print.go +1232 -0
  101. package/stdlib/gom/gom.go +293 -0
  102. package/stdlib/hash/hash.go +53 -0
  103. package/stdlib/html/html.go +251 -0
  104. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  105. package/stdlib/internal/jsonrt/dec.go +871 -0
  106. package/stdlib/internal/jsonrt/enc.go +516 -0
  107. package/stdlib/internal/jsonrt/indent.go +135 -0
  108. package/stdlib/internal/jsonrt/number.go +27 -0
  109. package/stdlib/internal/jsonrt/reader.go +73 -0
  110. package/stdlib/internal/jsonrt/scanner.go +658 -0
  111. package/stdlib/io/io.go +502 -0
  112. package/stdlib/iter/iter.go +18 -0
  113. package/stdlib/maps/maps.go +131 -0
  114. package/stdlib/math/bits/bits.go +507 -0
  115. package/stdlib/math/extra.go +272 -0
  116. package/stdlib/math/math.go +439 -0
  117. package/stdlib/math/rand/rand.go +297 -0
  118. package/stdlib/os/os.go +93 -0
  119. package/stdlib/path/filepath/filepath.go +47 -0
  120. package/stdlib/path/path.go +394 -0
  121. package/stdlib/regexp/regexp.go +231 -0
  122. package/stdlib/slices/slices.go +526 -0
  123. package/stdlib/slices/sort.go +209 -0
  124. package/stdlib/slices/zsort_cmp.go +480 -0
  125. package/stdlib/slices/zsort_ordered.go +482 -0
  126. package/stdlib/sort/sort.go +500 -0
  127. package/stdlib/sort/zsort_func.go +480 -0
  128. package/stdlib/sort/zsort_iface.go +480 -0
  129. package/stdlib/strconv/float.go +1699 -0
  130. package/stdlib/strconv/isprint.go +752 -0
  131. package/stdlib/strconv/quote.go +526 -0
  132. package/stdlib/strconv/strconv.go +616 -0
  133. package/stdlib/strings/builder.go +95 -0
  134. package/stdlib/strings/reader.go +163 -0
  135. package/stdlib/strings/replacer.go +102 -0
  136. package/stdlib/strings/strings.go +807 -0
  137. package/stdlib/sync/sync.go +268 -0
  138. package/stdlib/testing/async_js.go +61 -0
  139. package/stdlib/testing/benchmark.go +297 -0
  140. package/stdlib/testing/testing.go +523 -0
  141. package/stdlib/time/duration.go +233 -0
  142. package/stdlib/time/format.go +745 -0
  143. package/stdlib/time/parse.go +790 -0
  144. package/stdlib/time/sleep_js.go +63 -0
  145. package/stdlib/time/time.go +834 -0
  146. package/stdlib/unicode/unicode.go +250 -0
  147. package/stdlib/unicode/utf8/utf8.go +464 -0
  148. package/src/backend/js/stdlib/base64.js +0 -12
  149. package/src/backend/js/stdlib/binary.js +0 -102
  150. package/src/backend/js/stdlib/bits.js +0 -46
  151. package/src/backend/js/stdlib/builder.js +0 -63
  152. package/src/backend/js/stdlib/bytes.js +0 -122
  153. package/src/backend/js/stdlib/errors.js +0 -44
  154. package/src/backend/js/stdlib/fmt.js +0 -178
  155. package/src/backend/js/stdlib/gom.js +0 -155
  156. package/src/backend/js/stdlib/hex.js +0 -17
  157. package/src/backend/js/stdlib/html.js +0 -20
  158. package/src/backend/js/stdlib/io.js +0 -137
  159. package/src/backend/js/stdlib/json.js +0 -972
  160. package/src/backend/js/stdlib/maps.js +0 -45
  161. package/src/backend/js/stdlib/math.js +0 -69
  162. package/src/backend/js/stdlib/os.js +0 -13
  163. package/src/backend/js/stdlib/path.js +0 -48
  164. package/src/backend/js/stdlib/rand.js +0 -32
  165. package/src/backend/js/stdlib/regexp.js +0 -54
  166. package/src/backend/js/stdlib/sha256.js +0 -12
  167. package/src/backend/js/stdlib/shared.js +0 -17
  168. package/src/backend/js/stdlib/simd.js +0 -24
  169. package/src/backend/js/stdlib/slices.js +0 -77
  170. package/src/backend/js/stdlib/sort.js +0 -30
  171. package/src/backend/js/stdlib/strconv.js +0 -74
  172. package/src/backend/js/stdlib/strings.js +0 -222
  173. package/src/backend/js/stdlib/sync.js +0 -13
  174. package/src/backend/js/stdlib/testing.js +0 -34
  175. package/src/backend/js/stdlib/time.js +0 -128
  176. package/src/backend/js/stdlib/unicode.js +0 -25
  177. package/src/backend/js/stdlib/utf8.js +0 -26
  178. package/src/backend/wasm/const-fold.js +0 -205
  179. package/src/backend/wasm/emit-stdlib.js +0 -13
  180. package/src/backend/wasm/stdlib/bytes.js +0 -5039
  181. package/src/backend/wasm/stdlib/core.js +0 -8903
  182. package/src/backend/wasm/stdlib/io.js +0 -2161
  183. package/src/backend/wasm/stdlib/packages.js +0 -2400
  184. package/src/backend/wasm/stdlib/pure.js +0 -720
  185. package/src/backend/wasm/stdlib/time.js +0 -1200
  186. package/src/typechecker/stdlib/core.js +0 -801
  187. package/src/typechecker/stdlib/extended.js +0 -914
  188. package/src/typechecker/stdlib.js +0 -22
  189. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  190. /package/src/{colors.js → cli/colors.js} +0 -0
  191. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  192. /package/src/{minifier.js → cli/minifier.js} +0 -0
  193. /package/src/{project-config.js → cli/project-config.js} +0 -0
  194. /package/src/{pwa.js → cli/pwa.js} +0 -0
@@ -0,0 +1,426 @@
1
+ // Package initialisation order (Go spec, "Package initialization").
2
+ //
3
+ // A package-level variable is initialised once every variable its initialiser
4
+ // refers to has been, where "refers to" follows references to functions and
5
+ // methods transitively. Each step picks the earliest variable in declaration
6
+ // order that is ready; a step with none left over is an initialisation cycle.
7
+ // Constants are folded by value, so they only need ordering among themselves
8
+ // (a constant may name one declared later).
9
+ //
10
+ // The checker reports cycles; both backends emit initialisers in this order.
11
+ // Shadowing follows Go's block scoping: a name declared by a parameter, result,
12
+ // receiver or statement hides the package-level one from the next statement to
13
+ // the end of its block, and not outside it.
14
+
15
+ function forEachChildNode(node, fn) {
16
+ for (const key of Object.keys(node)) {
17
+ if (key.charCodeAt(0) === 95) continue; // "_" side tables
18
+ const v = node[key];
19
+ if (v && typeof v === "object") fn(v);
20
+ }
21
+ }
22
+
23
+ // A lexical scope of locally declared names.
24
+ class Shadow {
25
+ constructor(parent = null) {
26
+ this.parent = parent;
27
+ this.names = new Set();
28
+ }
29
+
30
+ has(name) {
31
+ for (let s = this; s; s = s.parent) if (s.names.has(name)) return true;
32
+ return false;
33
+ }
34
+
35
+ add(...names) {
36
+ for (const n of names) if (n) this.names.add(n);
37
+ }
38
+ }
39
+
40
+ // Names a function-like node declares for its body: receiver, parameters and
41
+ // named results.
42
+ function signatureNames(node) {
43
+ const out = [];
44
+ if (node.recvName) out.push(node.recvName);
45
+ for (const p of node.params ?? []) if (p?.name) out.push(p.name);
46
+ for (const r of node.returnType?._namedReturns ?? []) out.push(r.name);
47
+ return out;
48
+ }
49
+
50
+ const lhsName = (l) => (typeof l === "string" ? l : l?.name);
51
+
52
+ // The `Type.method` key of the declaration `name` selects on a value of type
53
+ // `t`, found the way the checker finds it: on the type itself, else on an
54
+ // embedded type (through pointers too). Interface methods have no body to
55
+ // follow, so a call through an interface adds no dependency (Go does the same).
56
+ function methodKeyOf(t, name, funcs, depth = 0) {
57
+ let x = t;
58
+ if (x?.kind === "pointer") x = x.base;
59
+ if (x?.kind !== "named" || depth > 8) return null;
60
+ for (const typeName of [x.name, x._generic?.name]) {
61
+ const key = `${typeName}.${name}`;
62
+ if (typeName && funcs.has(key)) return key;
63
+ }
64
+ for (const e of x.underlying?._embeds ?? []) {
65
+ const key = methodKeyOf(e, name, funcs, depth + 1);
66
+ if (key) return key;
67
+ }
68
+ return null;
69
+ }
70
+
71
+ // Whether the keys of a composite literal are expressions (map, slice and
72
+ // array literals) rather than field names (struct literals). Before the
73
+ // literal is checked its written type decides; an unnamed one stays ambiguous
74
+ // and its keys are skipped.
75
+ function litHasExprKeys(lit) {
76
+ let t = lit._type;
77
+ for (let i = 0; t && i < 8; i++) {
78
+ if (t.kind === "named") t = t.underlying;
79
+ else if (t.kind === "pointer") t = t.base;
80
+ else break;
81
+ }
82
+ if (t?.kind) return ["map", "slice", "array"].includes(t.kind);
83
+ return ["MapType", "SliceType", "ArrayType"].includes(lit.typeExpr?.kind);
84
+ }
85
+
86
+ // Statements that open a scope and declare names as they run. Each handler
87
+ // walks the node's children in order, adding declarations to `shadow` after
88
+ // the expressions that cannot see them.
89
+ const SCOPED = {
90
+ FuncDecl: walkFunc,
91
+ MethodDecl: walkFunc,
92
+ FuncLit: walkFunc,
93
+ Block: (node, shadow, ctx, out) =>
94
+ walkRefs(node.stmts, new Shadow(shadow), ctx, out),
95
+ DefineStmt: (node, shadow, ctx, out) => {
96
+ walkRefs(node.rhs, shadow, ctx, out);
97
+ shadow.add(...[node.lhs].flat().map(lhsName));
98
+ },
99
+ VarDecl: walkDeclSpecs,
100
+ ConstDecl: walkDeclSpecs,
101
+ IfStmt: (node, shadow, ctx, out) => {
102
+ const inner = new Shadow(shadow);
103
+ for (const c of [node.init, node.cond, node.body, node.elseBody])
104
+ walkRefs(c, inner, ctx, out);
105
+ },
106
+ ForStmt: (node, shadow, ctx, out) => {
107
+ const inner = new Shadow(shadow);
108
+ for (const c of [node.init, node.cond, node.post, node.body])
109
+ walkRefs(c, inner, ctx, out);
110
+ },
111
+ SwitchStmt: walkSwitch,
112
+ TypeSwitchStmt: walkSwitch,
113
+ };
114
+
115
+ function walkFunc(node, shadow, ctx, out) {
116
+ const inner = new Shadow(shadow);
117
+ inner.add(...signatureNames(node));
118
+ walkRefs(node.body, inner, ctx, out);
119
+ }
120
+
121
+ function walkDeclSpecs(node, shadow, ctx, out) {
122
+ for (const spec of node.decls ?? []) {
123
+ walkRefs(spec.value, shadow, ctx, out);
124
+ shadow.add(...(spec.names ?? []));
125
+ }
126
+ }
127
+
128
+ function walkSwitch(node, shadow, ctx, out) {
129
+ const inner = new Shadow(shadow);
130
+ walkRefs(node.init, inner, ctx, out);
131
+ walkRefs(node.tag ?? node.expr, inner, ctx, out);
132
+ for (const c of node.cases ?? []) {
133
+ const clause = new Shadow(inner);
134
+ if (node.assign) clause.add(node.assign);
135
+ walkRefs(c.list, clause, ctx, out);
136
+ walkRefs(c.stmts, clause, ctx, out);
137
+ }
138
+ }
139
+
140
+ function walkRefs(node, shadow, ctx, out) {
141
+ if (!node || typeof node !== "object") return;
142
+ if (Array.isArray(node)) {
143
+ for (const n of node) walkRefs(n, shadow, ctx, out);
144
+ return;
145
+ }
146
+ if (node.kind === "Ident") {
147
+ // The blank identifier names nothing, whatever `var _` the package has.
148
+ if (
149
+ node.name !== "_" &&
150
+ ctx.pkgNames.has(node.name) &&
151
+ !shadow.has(node.name)
152
+ )
153
+ out.names.add(node.name);
154
+ return;
155
+ }
156
+ const scoped = SCOPED[node.kind];
157
+ if (scoped) return scoped(node, shadow, ctx, out);
158
+ if (node.kind === "SelectorExpr") {
159
+ const key = methodKeyOf(node.expr?._type, node.field, ctx.funcs);
160
+ if (key) out.methods.add(key);
161
+ }
162
+ if (node.kind === "CompositeLit") {
163
+ const exprKeys = litHasExprKeys(node);
164
+ for (const el of node.elems ?? []) {
165
+ if (el?.kind !== "KeyValueExpr") walkRefs(el, shadow, ctx, out);
166
+ else {
167
+ // A struct literal's key is a field name, not a reference.
168
+ if (exprKeys || el.key?.kind !== "Ident")
169
+ walkRefs(el.key, shadow, ctx, out);
170
+ walkRefs(el.value, shadow, ctx, out);
171
+ }
172
+ }
173
+ walkRefs(node.typeExpr, shadow, ctx, out);
174
+ return;
175
+ }
176
+ forEachChildNode(node, (c) => walkRefs(c, shadow, ctx, out));
177
+ }
178
+
179
+ /**
180
+ * Collects what `root` (an initialiser expression or a function declaration)
181
+ * refers to: package-level names (`names`) and declarations selected as methods
182
+ * (`methods`, keyed `Type.method`), honouring block-scoped shadowing.
183
+ */
184
+ function collectRefs(root, ctx, out) {
185
+ walkRefs(root, new Shadow(), ctx, out);
186
+ }
187
+
188
+ const emptyRefs = () => ({ names: new Set(), methods: new Set() });
189
+
190
+ // Package-level specs and callable declarations of all files.
191
+ function collectDecls(programs) {
192
+ const varEntries = [];
193
+ const constEntries = [];
194
+ const funcs = new Map(); // name | "Type.method" -> declaration
195
+ for (const program of programs) {
196
+ for (const d of program.decls ?? []) {
197
+ if (d.kind === "VarDecl" || d.kind === "ConstDecl") {
198
+ const into = d.kind === "VarDecl" ? varEntries : constEntries;
199
+ for (const spec of d.decls ?? [])
200
+ into.push({ decl: d, spec, names: spec.names ?? [], program });
201
+ } else if (d.kind === "FuncDecl") {
202
+ funcs.set(d.name, d);
203
+ } else if (d.kind === "MethodDecl") {
204
+ const t = d.recvType?.name ?? d.recvType?.base?.name;
205
+ if (t) funcs.set(`${t}.${d.name}`, d);
206
+ }
207
+ }
208
+ }
209
+ return { varEntries, constEntries, funcs };
210
+ }
211
+
212
+ // For each var spec, the other var specs its initialiser depends on (through
213
+ // functions and methods too), with the name chain leading to each.
214
+ function buildVarDeps({ varEntries, constEntries, funcs }) {
215
+ const varOwner = new Map(); // var name -> entry
216
+ for (const e of varEntries) for (const n of e.names) varOwner.set(n, e);
217
+ const pkgNames = new Set([
218
+ ...varOwner.keys(),
219
+ ...funcs.keys(),
220
+ ...constEntries.flatMap((e) => e.names),
221
+ ]);
222
+ const ctx = { pkgNames, funcs };
223
+ const funcRefs = new Map();
224
+ const refsOfFunc = (key) => {
225
+ if (funcRefs.has(key)) return funcRefs.get(key);
226
+ const decl = funcs.get(key);
227
+ const out = emptyRefs();
228
+ funcRefs.set(key, out);
229
+ if (decl) collectRefs(decl, ctx, out);
230
+ return out;
231
+ };
232
+ const depsOf = (entry) => {
233
+ const deps = new Map(); // owner entry -> path of names
234
+ const seen = new Set();
235
+ const follow = (key, path) => {
236
+ if (!funcs.has(key) || seen.has(key)) return;
237
+ seen.add(key);
238
+ visit(refsOfFunc(key), [...path, key]);
239
+ };
240
+ const visit = (refs, path) => {
241
+ for (const name of refs.names) {
242
+ const owner = varOwner.get(name);
243
+ if (owner) {
244
+ if (!deps.has(owner)) deps.set(owner, [...path, name]);
245
+ } else follow(name, path);
246
+ }
247
+ for (const m of refs.methods) follow(m, path);
248
+ };
249
+ const refs = emptyRefs();
250
+ for (const v of entry.spec.value ?? []) collectRefs(v, ctx, refs);
251
+ visit(refs, []);
252
+ return deps;
253
+ };
254
+ return new Map(varEntries.map((e) => [e, depsOf(e)]));
255
+ }
256
+
257
+ // A chain of names leading from `start` back to itself through the specs in
258
+ // `pending`, e.g. ["a", "f", "b", "a"]; null when `start` is on no cycle.
259
+ function findCycle(start, depsOf, pending) {
260
+ const visited = new Set();
261
+ const walk = (entry, chain) => {
262
+ for (const [dep, path] of depsOf.get(entry)) {
263
+ const next = [...chain, ...path];
264
+ if (dep === start) return next;
265
+ if (!pending.has(dep) || visited.has(dep)) continue;
266
+ visited.add(dep);
267
+ const found = walk(dep, next);
268
+ if (found) return found;
269
+ }
270
+ return null;
271
+ };
272
+ const chain = walk(start, [start.names[0]]);
273
+ return chain && chain.length === 2 ? [chain[0]] : chain;
274
+ }
275
+
276
+ function cycleMessage(entry, chain) {
277
+ const name = entry.names[0];
278
+ if (!chain) return `initialization cycle for ${name}`;
279
+ if (chain.length === 1)
280
+ return `initialization cycle: ${name} refers to itself`;
281
+ return `initialization cycle for ${name}: ${chain.join(" refers to ")}`;
282
+ }
283
+
284
+ // Go's rule: repeatedly take the earliest spec whose dependencies are done.
285
+ function orderVars(varEntries, depsOf) {
286
+ const done = new Set();
287
+ const vars = [];
288
+ const ready = (e) => {
289
+ for (const dep of depsOf.get(e).keys()) {
290
+ if (dep === e && e.names.length > 1) continue;
291
+ if (!done.has(dep)) return false;
292
+ }
293
+ return true;
294
+ };
295
+ const remaining = varEntries.slice();
296
+ while (remaining.length > 0) {
297
+ const idx = remaining.findIndex(ready);
298
+ if (idx === -1) {
299
+ // Everything left is on or behind a cycle: report the first spec
300
+ // that is on one.
301
+ const pending = new Set(remaining);
302
+ let culprit = remaining[0];
303
+ let chain = null;
304
+ for (const e of remaining) {
305
+ chain = findCycle(e, depsOf, pending);
306
+ if (chain) {
307
+ culprit = e;
308
+ break;
309
+ }
310
+ }
311
+ vars.push(...remaining);
312
+ return {
313
+ vars,
314
+ cycles: [{ spec: culprit.spec, message: cycleMessage(culprit, chain) }],
315
+ };
316
+ }
317
+ const [e] = remaining.splice(idx, 1);
318
+ done.add(e);
319
+ vars.push(e);
320
+ }
321
+ return { vars, cycles: [] };
322
+ }
323
+
324
+ // Constants depend on other constants only; a constant that reaches itself is
325
+ // a cycle (reported once, at the constant where the walk closed it).
326
+ function orderConsts(constEntries) {
327
+ const constNames = new Set(constEntries.flatMap((e) => e.names));
328
+ const ctx = { pkgNames: constNames, funcs: new Map() };
329
+ const owner = new Map();
330
+ for (const e of constEntries) for (const n of e.names) owner.set(n, e);
331
+ const consts = [];
332
+ const cycles = [];
333
+ const state = new Map(); // entry -> 1 while on the walk's path, 2 when done
334
+ const path = [];
335
+ const visit = (e) => {
336
+ state.set(e, 1);
337
+ path.push(e);
338
+ const refs = emptyRefs();
339
+ for (const v of e.spec.value ?? []) collectRefs(v, ctx, refs);
340
+ for (const n of refs.names) {
341
+ const dep = owner.get(n);
342
+ if (!dep) continue;
343
+ if (state.get(dep) === 1) {
344
+ const names = path.slice(path.indexOf(dep)).map((p) => p.names[0]);
345
+ const chain = names.length === 1 ? names : [...names, names[0]];
346
+ cycles.push({ spec: dep.spec, message: cycleMessage(dep, chain) });
347
+ } else if (!state.has(dep)) visit(dep);
348
+ }
349
+ path.pop();
350
+ state.set(e, 2);
351
+ consts.push(e);
352
+ };
353
+ for (const e of constEntries) if (!state.has(e)) visit(e);
354
+ return { consts, cycles };
355
+ }
356
+
357
+ // A literal initialiser (or none: the zero value) reads nothing and has no
358
+ // effect, and gc initialises it statically, so a function that runs during
359
+ // another variable's initialiser sees it set even when it is declared later
360
+ // (an interface method call adds no dependency in the spec).
361
+ const isStaticValue = (v) =>
362
+ v?.kind === "BasicLit" ||
363
+ (v?.kind === "UnaryExpr" &&
364
+ ["-", "+"].includes(v.op) &&
365
+ isStaticValue(v.operand));
366
+
367
+ function staticInitsFirst(vars) {
368
+ const isStatic = (e) => (e.spec.value ?? []).every(isStaticValue);
369
+ return [...vars.filter(isStatic), ...vars.filter((e) => !isStatic(e))];
370
+ }
371
+
372
+ /**
373
+ * @param {object[]} programs parsed (and, for method resolution, checked) files
374
+ * @returns {{ consts: object[], vars: object[], cycles: object[] }}
375
+ * `consts` / `vars`: entries `{ decl, spec, names, program }` in initialisation
376
+ * order; `cycles`: `{ spec, message }` for each initialisation cycle.
377
+ */
378
+ export function orderPackageInits(programs) {
379
+ const decls = collectDecls(programs);
380
+ const depsOf = buildVarDeps(decls);
381
+ const ordered = orderVars(decls.varEntries, depsOf);
382
+ const { cycles } = ordered;
383
+ const vars =
384
+ cycles.length === 0 ? staticInitsFirst(ordered.vars) : ordered.vars;
385
+ const constOrder = orderConsts(decls.constEntries);
386
+ return {
387
+ consts: constOrder.consts,
388
+ vars,
389
+ cycles: [...constOrder.cycles, ...cycles],
390
+ };
391
+ }
392
+
393
+ /**
394
+ * Package-level `var` specs in the order their initialisers must be *typed*:
395
+ * a spec that names another package variable (directly, even inside a func
396
+ * literal) comes after it, since `var a = b + 1; var b = f()` infers `a` from
397
+ * `b`. Declaration order is kept otherwise.
398
+ */
399
+ export function orderVarSpecsForTyping(programs) {
400
+ const entries = [];
401
+ for (const program of programs)
402
+ for (const d of program.decls ?? [])
403
+ if (d.kind === "VarDecl")
404
+ for (const spec of d.decls ?? [])
405
+ entries.push({ program, decl: d, spec });
406
+ const owner = new Map();
407
+ for (const e of entries) for (const n of e.spec.names) owner.set(n, e);
408
+ const ctx = { pkgNames: new Set(owner.keys()), funcs: new Map() };
409
+ const out = [];
410
+ const state = new Set();
411
+ const visit = (e) => {
412
+ if (state.has(e)) return;
413
+ state.add(e);
414
+ if (!e.spec.type) {
415
+ const refs = emptyRefs();
416
+ for (const v of e.spec.value ?? []) collectRefs(v, ctx, refs);
417
+ for (const n of refs.names) {
418
+ const o = owner.get(n);
419
+ if (o && o !== e) visit(o);
420
+ }
421
+ }
422
+ out.push(e);
423
+ };
424
+ for (const e of entries) visit(e);
425
+ return out;
426
+ }
@@ -0,0 +1,40 @@
1
+ // Source-like text of an expression for diagnostics ("cannot use y (variable
2
+ // of type float64) as int value"). Only the common shapes are printed in full;
3
+ // anything else falls back to "expression".
4
+
5
+ function litText(node) {
6
+ switch (node.litKind) {
7
+ case "STRING":
8
+ return JSON.stringify(node.value);
9
+ case "NIL":
10
+ return "nil";
11
+ default:
12
+ return String(node.value);
13
+ }
14
+ }
15
+
16
+ const PRINTERS = {
17
+ Ident: (n) => n.name,
18
+ BasicLit: litText,
19
+ ParenExpr: (n) => `(${exprText(n.expr)})`,
20
+ SelectorExpr: (n) => `${exprText(n.expr)}.${n.field}`,
21
+ UnaryExpr: (n) => `${n.op}${exprText(n.operand)}`,
22
+ BinaryExpr: (n) => `${exprText(n.left)} ${n.op} ${exprText(n.right)}`,
23
+ CallExpr: (n) =>
24
+ `${exprText(n.func)}(${(n.args ?? []).map(exprText).join(", ")})`,
25
+ IndexExpr: (n) => `${exprText(n.expr)}[${exprText(n.index)}]`,
26
+ };
27
+
28
+ export function exprText(node) {
29
+ const print = node && PRINTERS[node.kind];
30
+ return print ? print(node) : "expression";
31
+ }
32
+
33
+ // "variable of type float64", "untyped int constant", "value of type float64".
34
+ export function operandKind(node, typeName, isUntypedConst) {
35
+ if (isUntypedConst)
36
+ return `untyped ${typeName === "float64" ? "float" : typeName} constant`;
37
+ return node?.kind === "Ident"
38
+ ? `variable of type ${typeName}`
39
+ : `value of type ${typeName}`;
40
+ }