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
@@ -271,6 +271,8 @@ function normalizeDeferStmt(stmt, parentList = null, index = -1, ctx = null) {
271
271
  ...call,
272
272
  func: funcExpr,
273
273
  args: callArgs,
274
+ // A deferred native call runs the Go body, as on the JS backend.
275
+ _noNative: true,
274
276
  _multiForward: tempIdents.length > 0 ? false : call._multiForward,
275
277
  };
276
278
  const synthFuncLit = {
@@ -0,0 +1,274 @@
1
+ // src/lower/gen-types.js
2
+ // Spells checked types as Go source for the generated per-package file
3
+ // (src/lower/json-codecs.js, src/lower/errors-as.js,
4
+ // src/lower/fmt-formatters.js), and holds what those generators share: the
5
+ // imports the spelled code needs and the enumeration of the program's types.
6
+
7
+ import { defaultType } from "../typechecker/types.js";
8
+ import { forEachChild } from "./walk.js";
9
+
10
+ export function under(t) {
11
+ return t?.kind === "named" ? under(t.underlying) : t;
12
+ }
13
+
14
+ export const isEmptyIface = (t) =>
15
+ (t?.kind === "basic" && t.name === "any") ||
16
+ (under(t)?.kind === "interface" && under(t).methods?.size === 0);
17
+
18
+ // Arrays and maps of these share one WebAssembly representation (and so one
19
+ // type-switch case), so only literals may register them: a `[2]byte` conversion
20
+ // must not shadow a `[2]int32` that is stored in an interface. A slice of
21
+ // them is tagged with its Go type in an interface and registers like any other.
22
+ export const SHARED_REPR = new Set([
23
+ "bool",
24
+ "int8",
25
+ "int16",
26
+ "int32",
27
+ "uint8",
28
+ "uint16",
29
+ "uint32",
30
+ "byte",
31
+ "rune",
32
+ ]);
33
+
34
+ export function hasSharedRepr(t, depth = 0) {
35
+ if (!t || depth > 8) return false;
36
+ const u = under(t);
37
+ if (u?.kind === "basic") return SHARED_REPR.has(u.name);
38
+ if (u?.kind === "slice" || u?.kind === "array")
39
+ return hasSharedRepr(u.elem, depth + 1);
40
+ if (u?.kind === "map")
41
+ return hasSharedRepr(u.key, depth + 1) || hasSharedRepr(u.value, depth + 1);
42
+ return false;
43
+ }
44
+
45
+ // A method's results; a multi-value result is one tuple type.
46
+ export function results(m) {
47
+ const r = (m.returns ?? []).filter(
48
+ (t) => !(t?.kind === "basic" && t.name === "void"),
49
+ );
50
+ return r.length === 1 && r[0]?.kind === "tuple" ? r[0].types : r;
51
+ }
52
+
53
+ // Calls every `fn(node)` for the AST nodes under `root`.
54
+ export function walkNodes(root, fn) {
55
+ if (!root || typeof root !== "object") return;
56
+ if (Array.isArray(root)) {
57
+ for (const n of root) walkNodes(n, fn);
58
+ return;
59
+ }
60
+ fn(root);
61
+ forEachChild(root, (c) => walkNodes(c, fn));
62
+ }
63
+
64
+ // True when `t` mentions a type parameter (a generic declaration, or a type
65
+ // built from one inside a generic function).
66
+ export function mentionsTypeParam(t, seen = new Set()) {
67
+ if (!t || seen.has(t)) return false;
68
+ seen.add(t);
69
+ const m = (x) => mentionsTypeParam(x, seen);
70
+ switch (t.kind) {
71
+ case "typeParam":
72
+ return true;
73
+ case "named":
74
+ return (t.typeArgs ?? []).some(m) || m(t.underlying);
75
+ case "pointer":
76
+ return m(t.base);
77
+ case "slice":
78
+ case "array":
79
+ return m(t.elem);
80
+ case "map":
81
+ return m(t.key) || m(t.value);
82
+ case "struct":
83
+ return (t._structFields ?? []).some((f) => m(f.type));
84
+ default:
85
+ return false;
86
+ }
87
+ }
88
+
89
+ // The last element of an import path: the package's name.
90
+ const canonicalName = (modPath) => modPath.split("/").pop();
91
+
92
+ // The names the programs declare at package level.
93
+ function declaredNames(programs) {
94
+ const names = new Set();
95
+ for (const p of programs)
96
+ for (const d of p.decls ?? []) {
97
+ if (d.kind === "TypeDecl" || d.kind === "FuncDecl") names.add(d.name);
98
+ else if (d.kind === "VarDecl" || d.kind === "ConstDecl")
99
+ for (const spec of d.decls ?? [])
100
+ for (const n of spec.names ?? []) names.add(n);
101
+ }
102
+ return names;
103
+ }
104
+
105
+ // Every name an import answers to (its alias, its package name) -> its path.
106
+ function importPaths(programs) {
107
+ const paths = new Map();
108
+ for (const p of programs)
109
+ for (const imp of p.imports ?? [])
110
+ for (const spec of imp.imports) {
111
+ if (spec.alias === "_" || spec.alias === ".") continue;
112
+ paths.set(canonicalName(spec.path), spec.path);
113
+ if (spec.alias) paths.set(spec.alias, spec.path);
114
+ }
115
+ return paths;
116
+ }
117
+
118
+ // The generated file's imports for the qualifiers (name -> path) its code
119
+ // spells: a path, or { path, alias } for a qualifier renamed to stay clear of
120
+ // a package-level name.
121
+ export function importsFor(qualifiers) {
122
+ return [...qualifiers].map(([name, path]) =>
123
+ name === canonicalName(path) ? path : { path, alias: name },
124
+ );
125
+ }
126
+
127
+ // Every type a closed-world generator may need to describe: the entries
128
+ // { type, node, via } for the type arguments of generic calls (`typeArg`),
129
+ // the type of every expression (`expr`) and the package's declared named
130
+ // types (`declared`, node null).
131
+ export function collectProgramTypes(programs, checker) {
132
+ const found = [];
133
+ for (const p of programs)
134
+ walkNodes(p.decls, (node) => {
135
+ for (const a of node._typeArgs ?? [])
136
+ found.push({ type: defaultType(a), node, via: "typeArg" });
137
+ if (node._type)
138
+ found.push({ type: defaultType(node._type), node, via: "expr" });
139
+ });
140
+ for (const [name, t] of checker.types) {
141
+ if (t?.kind !== "named" || t.name !== name) continue;
142
+ if (name.includes(".") || name.includes("$$")) continue;
143
+ if (t._genericOf || t.typeArgs?.length) continue;
144
+ found.push({ type: t, node: null, via: "declared" });
145
+ }
146
+ return found;
147
+ }
148
+
149
+ export class TypeSpeller {
150
+ // `qualifiers` receives name -> import path for each package the spelled
151
+ // code names.
152
+ constructor(pkgName, qualifiers, programs = []) {
153
+ this.pkgName = pkgName;
154
+ this.qualifiers = qualifiers;
155
+ this.programs = programs;
156
+ this.importPaths = importPaths(programs);
157
+ this.declared = declaredNames(programs);
158
+ }
159
+
160
+ // Go source for the type.
161
+ spell(t) {
162
+ const sp = (x) => this.spell(x);
163
+ const byKind = {
164
+ basic: () => t.name,
165
+ named: () => this.spellNamed(t),
166
+ pointer: () => `*${sp(t.base)}`,
167
+ slice: () => `[]${sp(t.elem)}`,
168
+ array: () => `[${t.size}]${sp(t.elem)}`,
169
+ map: () => `map[${sp(t.key)}]${sp(t.value)}`,
170
+ struct: () => this.spellStruct(t),
171
+ interface: () => (t.name === "error" ? "error" : "any"),
172
+ func: () => this.funcString(t, sp),
173
+ };
174
+ if (!byKind[t.kind])
175
+ throw new Error(`json: cannot spell type kind ${t.kind}`);
176
+ return byKind[t.kind]();
177
+ }
178
+
179
+ // The package name `%T` and errors print for the bare-named type `t`: this
180
+ // package, or the local package an importer reaches it through as `p.Name`.
181
+ declaringPackage(t) {
182
+ if (this.declared.has(t.name) || !this.checker) return this.pkgName;
183
+ for (const [key, v] of this.checker.types) {
184
+ const dot = key.indexOf(".");
185
+ if (v !== t || dot <= 0 || key.slice(dot + 1) !== t.name) continue;
186
+ const modPath = this.importPaths.get(key.slice(0, dot));
187
+ if (modPath) return canonicalName(modPath);
188
+ }
189
+ return this.pkgName;
190
+ }
191
+
192
+ // The name the generated file uses for the package at `modPath`: its
193
+ // package name, or `_gf_<name>` when the package declares that name itself
194
+ // or another path took it.
195
+ qualifierFor(modPath) {
196
+ for (const [name, path] of this.qualifiers)
197
+ if (path === modPath) return name;
198
+ const base = canonicalName(modPath);
199
+ const taken = (n) => this.declared.has(n) || this.qualifiers.has(n);
200
+ let name = base;
201
+ for (let i = 1; taken(name); i++) name = `_gf_${base}${i > 1 ? i : ""}`;
202
+ this.qualifiers.set(name, modPath);
203
+ return name;
204
+ }
205
+
206
+ spellNamed(t) {
207
+ let name = t.name;
208
+ const at = name.indexOf("$$");
209
+ const dot = name.indexOf(".");
210
+ if (at >= 0) {
211
+ const orig = name.slice(0, at);
212
+ const modPath = name.slice(at + 2).replaceAll("$", "/");
213
+ name = `${this.qualifierFor(modPath)}.${orig}`;
214
+ } else if (dot > 0) {
215
+ const modPath = this.importPaths.get(name.slice(0, dot));
216
+ if (modPath) name = `${this.qualifierFor(modPath)}${name.slice(dot)}`;
217
+ }
218
+ return t.typeArgs?.length
219
+ ? `${name}[${t.typeArgs.map((a) => this.spell(a)).join(", ")}]`
220
+ : name;
221
+ }
222
+
223
+ spellStruct(t) {
224
+ const fields = (t._structFields ?? []).map((f) => {
225
+ const tag = f.tag ? ` \`${f.tag}\`` : "";
226
+ if (f.embedded) return `${this.spell(f.type)}${tag}`;
227
+ return `${f.names.join(", ")} ${this.spell(f.type)}${tag}`;
228
+ });
229
+ return `struct { ${fields.join("; ")} }`;
230
+ }
231
+
232
+ funcString(t, name) {
233
+ const rs = results(t);
234
+ const res =
235
+ rs.length === 0
236
+ ? ""
237
+ : rs.length === 1
238
+ ? ` ${name(rs[0])}`
239
+ : ` (${rs.map(name).join(", ")})`;
240
+ // The signature keeps a variadic parameter's element type.
241
+ const params = t.params.map((p, i) =>
242
+ t.variadic && i === t.params.length - 1 ? `...${name(p)}` : name(p),
243
+ );
244
+ return `func(${params.join(", ")})${res}`;
245
+ }
246
+
247
+ // True when the type can be written as Go source in the generated file.
248
+ // Subclasses decide what a named type, an interface and a struct field
249
+ // need.
250
+ spellable(t, depth = 0, seen = new Set()) {
251
+ if (!t || depth > 8) return false;
252
+ const ok = (x) => this.spellable(x, depth + 1, seen);
253
+ const byKind = {
254
+ basic: () => t.name !== "void" && t.name !== "nil",
255
+ named: () => this.spellableNamed(t, ok, seen),
256
+ pointer: () => ok(t.base),
257
+ slice: () => ok(t.elem),
258
+ array: () => ok(t.elem),
259
+ map: () => ok(t.key) && ok(t.value),
260
+ interface: () => this.spellableInterface(t),
261
+ struct: () => this.spellableStruct(t, ok),
262
+ func: () => t.params.every(ok) && results(t).every(ok),
263
+ };
264
+ return byKind[t.kind]?.() ?? false;
265
+ }
266
+
267
+ spellableInterface(_t) {
268
+ return true;
269
+ }
270
+
271
+ spellableStruct(t, ok) {
272
+ return (t._structFields ?? []).every((f) => ok(f.type));
273
+ }
274
+ }
@@ -1,6 +1,7 @@
1
1
  // src/lower/index.js
2
2
  // Lowering step: analyzes AST and types to populate side-tables for backends (JS and WASM).
3
3
 
4
+ export * from "./bounds.js";
4
5
  export * from "./boxing.js";
5
6
  export * from "./captures.js";
6
7
  export * from "./embedding.js";
@@ -17,7 +18,9 @@ import { computeEmbeddedStubs } from "./embedding.js";
17
18
  import { paramEscapes } from "./escape.js";
18
19
  import { extractNamedReturns, hasDefer, normalizeDefers } from "./functions.js";
19
20
  import { computeGlobalAnalysis } from "./globals.js";
20
- import { OwnershipContext } from "./ownership.js";
21
+ import { OwnershipContext, takesElementAddress } from "./ownership.js";
22
+
23
+ export { takesElementAddress };
21
24
 
22
25
  class LowerResult {
23
26
  constructor() {
@@ -39,9 +42,35 @@ class LowerResult {
39
42
  this.initPathFunctions = new Set();
40
43
  // Map<ASTNode, Array<string>>
41
44
  this.cachedGlobalsByFunc = new Map();
45
+ // True when some code takes `&xs[i]` (see takesElementAddress)
46
+ this.elemAddrTaken = false;
42
47
  }
43
48
  }
44
49
 
50
+ /**
51
+ * Runs the per-function analyses for `d` and records them in `res`. Also
52
+ * used for synthesized functions (the package-level initializer).
53
+ */
54
+ export function lowerFunction(res, d) {
55
+ if (!d.body) return;
56
+ const boxed = scanAddressTaken(d.body, new Set());
57
+ res.boxedVars.set(d, boxed);
58
+ res.ownership.set(d, new OwnershipContext(d.body));
59
+ res.functions.set(d, {
60
+ hasDefer: hasDefer(d.body),
61
+ namedReturns: extractNamedReturns(d),
62
+ });
63
+ res.captures.set(d, analyzeCaptures(d));
64
+
65
+ const escapingParams = new Set();
66
+ for (const p of d.params ?? []) {
67
+ if (p.name && p.name !== "_" && paramEscapes(d.body, p.name)) {
68
+ escapingParams.add(p.name);
69
+ }
70
+ }
71
+ res.escapes.set(d, escapingParams);
72
+ }
73
+
45
74
  /**
46
75
  * Runs lowering analyses across an AST or collection of programs, returning side-tables.
47
76
  * @param {Object} programOrPrograms
@@ -56,6 +85,8 @@ export function lower(programOrPrograms, checker = null) {
56
85
 
57
86
  normalizeDefers(programs);
58
87
 
88
+ res.elemAddrTaken = programs.some((p) => takesElementAddress(p?.decls));
89
+
59
90
  // 1. Aggregate methods across all files in the package
60
91
  const methodMap = new Map();
61
92
  for (const program of programs) {
@@ -74,24 +105,7 @@ export function lower(programOrPrograms, checker = null) {
74
105
 
75
106
  for (const d of program.decls) {
76
107
  if (d.kind === "FuncDecl" || d.kind === "MethodDecl") {
77
- if (d.body) {
78
- const boxed = scanAddressTaken(d.body, new Set());
79
- res.boxedVars.set(d, boxed);
80
- res.ownership.set(d, new OwnershipContext(d.body));
81
- res.functions.set(d, {
82
- hasDefer: hasDefer(d.body),
83
- namedReturns: extractNamedReturns(d),
84
- });
85
- res.captures.set(d, analyzeCaptures(d));
86
-
87
- const escapingParams = new Set();
88
- for (const p of d.params ?? []) {
89
- if (p.name && p.name !== "_" && paramEscapes(d.body, p.name)) {
90
- escapingParams.add(p.name);
91
- }
92
- }
93
- res.escapes.set(d, escapingParams);
94
- }
108
+ lowerFunction(res, d);
95
109
  }
96
110
  }
97
111