gofront 1.8.0 → 1.9.1

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 (178) hide show
  1. package/README.md +3 -3
  2. package/package.json +6 -5
  3. package/src/backend/js/expressions.js +1157 -408
  4. package/src/backend/js/index.js +327 -153
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +559 -2552
  7. package/src/backend/js/statements.js +670 -158
  8. package/src/backend/js/stdlib/index.js +45 -93
  9. package/src/backend/js/templ.js +32 -45
  10. package/src/backend/js/type-info.js +298 -19
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +91 -170
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +490 -965
  15. package/src/backend/wasm/emit-builtins.js +884 -256
  16. package/src/backend/wasm/emit-exprs.js +185 -77
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-shared.js +4 -1
  19. package/src/backend/wasm/emit-stmts.js +524 -240
  20. package/src/backend/wasm/emit.js +176 -38
  21. package/src/backend/wasm/globals.js +48 -8
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +44 -530
  24. package/src/backend/wasm/method-expr.js +12 -1
  25. package/src/backend/wasm/module-emitter.js +299 -770
  26. package/src/backend/wasm/monomorph.js +139 -8
  27. package/src/backend/wasm/natives.js +600 -0
  28. package/src/backend/wasm/prescan.js +5 -153
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -32
  31. package/src/backend/wasm/types.js +97 -20
  32. package/src/backend/wasm/variadic.js +9 -4
  33. package/src/cli/cli-core.js +5 -1
  34. package/src/cli/test-runner.js +115 -342
  35. package/src/compiler.js +113 -130
  36. package/src/lexer/lexer.js +106 -11
  37. package/src/lexer/templ-lexer.js +61 -18
  38. package/src/lexer/tokens.js +1 -0
  39. package/src/link/stdlib.js +355 -132
  40. package/src/link/type-names.js +204 -0
  41. package/src/link/wasm.js +30 -6
  42. package/src/lower/bounds.js +153 -0
  43. package/src/lower/captures.js +92 -33
  44. package/src/lower/embedding.js +62 -5
  45. package/src/lower/errors-as.js +196 -0
  46. package/src/lower/fmt-formatters.js +929 -0
  47. package/src/lower/functions.js +225 -7
  48. package/src/lower/gen-types.js +293 -0
  49. package/src/lower/index.js +14 -2
  50. package/src/lower/json-codecs.js +334 -245
  51. package/src/lower/ownership.js +25 -7
  52. package/src/lower/reachability.js +69 -51
  53. package/src/lower/walk.js +12 -0
  54. package/src/parser/declarations.js +30 -5
  55. package/src/parser/expressions.js +111 -57
  56. package/src/parser/statements.js +108 -0
  57. package/src/parser/templ.js +44 -5
  58. package/src/parser/types.js +12 -0
  59. package/src/resolver.js +1 -2
  60. package/src/typechecker/assignability.js +214 -19
  61. package/src/typechecker/const-fold.js +70 -27
  62. package/src/typechecker/constants.js +156 -8
  63. package/src/typechecker/expressions.js +490 -108
  64. package/src/typechecker/index.js +346 -42
  65. package/src/typechecker/init-order.js +136 -40
  66. package/src/typechecker/operand-text.js +41 -0
  67. package/src/typechecker/resolve.js +490 -65
  68. package/src/typechecker/statements.js +288 -27
  69. package/src/typechecker/stdlib/excluded.js +9 -5
  70. package/src/typechecker/stdlib/index.js +25 -9
  71. package/src/typechecker/termination.js +10 -1
  72. package/src/typechecker/types.js +287 -575
  73. package/stdlib/bytes/buffer.go +479 -0
  74. package/stdlib/bytes/bytes.go +1010 -0
  75. package/stdlib/bytes/reader.go +161 -0
  76. package/stdlib/context/context.go +115 -0
  77. package/stdlib/crypto/rand/rand.go +34 -0
  78. package/stdlib/crypto/sha256/sha256.go +249 -0
  79. package/stdlib/encoding/base64/base64.go +331 -0
  80. package/stdlib/encoding/binary/binary.go +332 -0
  81. package/stdlib/encoding/hex/hex.go +140 -0
  82. package/stdlib/encoding/json/json.go +62 -0
  83. package/stdlib/errors/errors.go +174 -0
  84. package/stdlib/fmt/errors.go +110 -0
  85. package/stdlib/fmt/format.go +732 -0
  86. package/stdlib/fmt/print.go +1232 -0
  87. package/stdlib/gom/gom.go +293 -0
  88. package/stdlib/html/html.go +251 -0
  89. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  90. package/stdlib/internal/jsonrt/dec.go +171 -38
  91. package/stdlib/internal/jsonrt/enc.go +186 -36
  92. package/stdlib/internal/jsonrt/scanner.go +17 -8
  93. package/stdlib/io/io.go +157 -1
  94. package/stdlib/maps/maps.go +131 -0
  95. package/stdlib/math/bits/bits.go +507 -0
  96. package/stdlib/math/extra.go +16 -14
  97. package/stdlib/math/math.go +439 -0
  98. package/stdlib/math/rand/rand.go +297 -0
  99. package/stdlib/os/os.go +93 -0
  100. package/stdlib/path/filepath/filepath.go +47 -0
  101. package/stdlib/path/path.go +394 -0
  102. package/stdlib/regexp/regexp.go +231 -0
  103. package/stdlib/slices/slices.go +526 -0
  104. package/stdlib/slices/sort.go +209 -0
  105. package/stdlib/slices/zsort_cmp.go +480 -0
  106. package/stdlib/slices/zsort_ordered.go +482 -0
  107. package/stdlib/sort/sort.go +391 -452
  108. package/stdlib/sort/zsort_func.go +480 -0
  109. package/stdlib/sort/zsort_iface.go +480 -0
  110. package/stdlib/strconv/float.go +1699 -0
  111. package/stdlib/strconv/isprint.go +752 -0
  112. package/stdlib/strconv/quote.go +235 -50
  113. package/stdlib/strconv/strconv.go +616 -0
  114. package/stdlib/strings/builder.go +95 -0
  115. package/stdlib/strings/reader.go +163 -0
  116. package/stdlib/strings/replacer.go +102 -0
  117. package/stdlib/strings/strings.go +807 -0
  118. package/stdlib/sync/sync.go +268 -0
  119. package/stdlib/testing/async_js.go +61 -0
  120. package/stdlib/testing/benchmark.go +297 -0
  121. package/stdlib/testing/testing.go +523 -0
  122. package/stdlib/time/duration.go +233 -0
  123. package/stdlib/time/format.go +745 -0
  124. package/stdlib/time/parse.go +790 -0
  125. package/stdlib/time/sleep_js.go +63 -0
  126. package/stdlib/time/time.go +834 -0
  127. package/stdlib/unicode/unicode.go +141 -3
  128. package/stdlib/unicode/utf8/utf8.go +464 -0
  129. package/src/backend/js/fmt-runtime.js +0 -573
  130. package/src/backend/js/stdlib/base64.js +0 -12
  131. package/src/backend/js/stdlib/binary.js +0 -102
  132. package/src/backend/js/stdlib/bits.js +0 -119
  133. package/src/backend/js/stdlib/builder.js +0 -98
  134. package/src/backend/js/stdlib/bytes.js +0 -368
  135. package/src/backend/js/stdlib/errors.js +0 -45
  136. package/src/backend/js/stdlib/fmt.js +0 -517
  137. package/src/backend/js/stdlib/gom.js +0 -155
  138. package/src/backend/js/stdlib/hex.js +0 -17
  139. package/src/backend/js/stdlib/html.js +0 -20
  140. package/src/backend/js/stdlib/io.js +0 -137
  141. package/src/backend/js/stdlib/json.js +0 -1167
  142. package/src/backend/js/stdlib/maps.js +0 -37
  143. package/src/backend/js/stdlib/math.js +0 -85
  144. package/src/backend/js/stdlib/os.js +0 -13
  145. package/src/backend/js/stdlib/path.js +0 -48
  146. package/src/backend/js/stdlib/rand.js +0 -32
  147. package/src/backend/js/stdlib/regexp.js +0 -54
  148. package/src/backend/js/stdlib/sha256.js +0 -12
  149. package/src/backend/js/stdlib/shared.js +0 -17
  150. package/src/backend/js/stdlib/simd.js +0 -34
  151. package/src/backend/js/stdlib/slices.js +0 -107
  152. package/src/backend/js/stdlib/sort.js +0 -30
  153. package/src/backend/js/stdlib/strconv.js +0 -79
  154. package/src/backend/js/stdlib/strings.js +0 -301
  155. package/src/backend/js/stdlib/sync.js +0 -13
  156. package/src/backend/js/stdlib/testing.js +0 -41
  157. package/src/backend/js/stdlib/time.js +0 -152
  158. package/src/backend/js/stdlib/unicode.js +0 -25
  159. package/src/backend/js/stdlib/utf8.js +0 -26
  160. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  161. package/src/backend/wasm/stdlib/core.js +0 -10278
  162. package/src/backend/wasm/stdlib/io.js +0 -3578
  163. package/src/backend/wasm/stdlib/packages.js +0 -2402
  164. package/src/backend/wasm/stdlib/pure.js +0 -720
  165. package/src/backend/wasm/stdlib/time.js +0 -2229
  166. package/src/typechecker/stdlib/core.js +0 -1079
  167. package/src/typechecker/stdlib/extended.js +0 -1210
  168. package/stdlib/bytes/iter.go +0 -192
  169. package/stdlib/bytes/search.go +0 -240
  170. package/stdlib/bytes/split.go +0 -218
  171. package/stdlib/errors/overlay.go +0 -12
  172. package/stdlib/maps/iter.go +0 -65
  173. package/stdlib/slices/extra.go +0 -73
  174. package/stdlib/slices/iter.go +0 -113
  175. package/stdlib/strings/extra.go +0 -35
  176. package/stdlib/strings/iter.go +0 -122
  177. package/stdlib/sync/extra.go +0 -105
  178. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -1,26 +1,83 @@
1
1
  // src/lower/embedding.js
2
2
  // Computes promoted methods and delegation stubs for embedded structs.
3
3
 
4
+ import {
5
+ ambiguousPromotions,
6
+ embeddedFieldName,
7
+ } from "../typechecker/types.js";
8
+
9
+ // A method promoted from an embedded struct value but declared on its
10
+ // pointer receiver is outside the method set of the outer struct's value.
11
+ function isPtrOnlyPromotion(rawEmbed, embed, mName, mType) {
12
+ if (rawEmbed.kind === "pointer") return false;
13
+ return Boolean((mType ?? embed.methods?.get(mName))?._ptrRecv);
14
+ }
15
+
16
+ // A pointer-receiver method reached through an embedded pointer, which may
17
+ // be nil.
18
+ function isNilablePtrRecv(rawEmbed, mType) {
19
+ return rawEmbed.kind === "pointer" && Boolean(mType?._ptrRecv);
20
+ }
21
+
22
+ // An embedded interface (named, literal or `error`) forwards each method of
23
+ // its method set to the interface value in the field.
24
+ function pushInterfaceStubs(stubs, declared, iface, fieldName) {
25
+ for (const mName of iface.methods?.keys() ?? []) {
26
+ if (declared.has(mName)) continue;
27
+ stubs.push({ methodName: mName, fieldName, ifaceEmbed: true });
28
+ declared.add(mName);
29
+ }
30
+ }
31
+
32
+ function pushValueEmbedStubs(stubs, declared, embed, embedName, fieldName) {
33
+ for (const [mName, mType] of embed.methods?.entries() ?? []) {
34
+ if (declared.has(mName) || mType?._ptrRecv) continue;
35
+ stubs.push({ methodName: mName, embedName, fieldName, valueEmbed: true });
36
+ declared.add(mName);
37
+ }
38
+ }
39
+
4
40
  export function computeEmbeddedStubs(name, methodDecls, checker) {
5
41
  if (!checker) return [];
6
42
  const resolvedType = checker.types.get(name)?.underlying;
7
43
  if (resolvedType?.kind !== "struct" || !resolvedType._embeds) return [];
8
44
 
9
- const declared = new Set(methodDecls.map((m) => m.name));
45
+ // A method two embeds declare at the same depth is in neither method set:
46
+ // no stub, so the value does not satisfy an interface that needs it.
47
+ const declared = new Set([
48
+ ...methodDecls.map((m) => m.name),
49
+ ...ambiguousPromotions(resolvedType),
50
+ ]);
10
51
  const stubs = [];
11
52
 
12
- for (const rawEmbed of resolvedType._embeds) {
53
+ const embedded = (resolvedType._structFields ?? []).filter((f) => f.embedded);
54
+ for (const [i, rawEmbed] of resolvedType._embeds.entries()) {
55
+ const fieldName = embedded[i] ? embeddedFieldName(embedded[i]) : null;
13
56
  const embed = rawEmbed.kind === "pointer" ? rawEmbed.base : rawEmbed;
57
+ const embedBase = embed.kind === "named" ? embed.underlying : embed;
58
+ if (embedBase?.kind === "interface") {
59
+ if (rawEmbed.kind !== "pointer" && fieldName)
60
+ pushInterfaceStubs(stubs, declared, embedBase, fieldName);
61
+ continue;
62
+ }
14
63
  const embedName = embed.kind === "named" ? embed.name : null;
15
64
  if (!embedName) continue;
16
- const embedBase = embed.kind === "named" ? embed.underlying : embed;
17
- if (embedBase?.kind !== "struct" || !embedBase.methods) continue;
65
+ if (embedBase?.kind !== "struct" || !embedBase.methods) {
66
+ // A named non-struct type (`time.Duration`) promotes its value
67
+ // methods; the stub passes the embedded value as the receiver.
68
+ if (rawEmbed.kind !== "pointer")
69
+ pushValueEmbedStubs(stubs, declared, embed, embedName, fieldName);
70
+ continue;
71
+ }
18
72
 
19
- for (const [mName] of embedBase.methods.entries()) {
73
+ for (const [mName, mType] of embedBase.methods.entries()) {
20
74
  if (!declared.has(mName)) {
21
75
  stubs.push({
22
76
  methodName: mName,
23
77
  embedName,
78
+ fieldName,
79
+ ptrOnly: isPtrOnlyPromotion(rawEmbed, embed, mName, mType),
80
+ nilPtrRecv: isNilablePtrRecv(rawEmbed, mType),
24
81
  });
25
82
  declared.add(mName); // Prevent duplicate stubs for diamond embeds
26
83
  }
@@ -0,0 +1,196 @@
1
+ // src/lower/errors-as.js
2
+ // errors.As as generated Go source.
3
+ //
4
+ // `errors.As(err, &x)` stores through a pointer whose type only the checker
5
+ // knows. Each call site gets a function generated for its static target
6
+ // type; it walks the error tree like the real package and honours custom
7
+ // `As(any) bool` methods. The functions are ordinary Go, parsed and checked
8
+ // as part of the package's generated file (src/link/stdlib.js), so both
9
+ // backends compile the same code.
10
+
11
+ import {
12
+ importsFor,
13
+ mentionsTypeParam,
14
+ TypeSpeller,
15
+ walkNodes,
16
+ } from "./gen-types.js";
17
+
18
+ const ERRORS_PATH = "errors";
19
+ const NOT_POINTER =
20
+ "errors.As: second argument must be a non-nil pointer to a type that implements error or to any interface type";
21
+
22
+ // The names the file imports `errors` as.
23
+ function errorsAliases(program) {
24
+ const aliases = new Set();
25
+ for (const imp of program.imports ?? [])
26
+ for (const spec of imp.imports)
27
+ if (spec.path === ERRORS_PATH && spec.alias !== "_")
28
+ aliases.add(spec.alias ?? "errors");
29
+ return aliases;
30
+ }
31
+
32
+ const isAsRef = (node, aliases) =>
33
+ node.kind === "SelectorExpr" &&
34
+ node.expr?.kind === "Ident" &&
35
+ node.expr._type?.kind === "namespace" &&
36
+ aliases.has(node.expr.name) &&
37
+ node.field === "As";
38
+
39
+ const isAsCall = (node, aliases) =>
40
+ node.kind === "CallExpr" && isAsRef(node.func, aliases);
41
+
42
+ // Marks `call` to call the generated function `name`. The function takes the
43
+ // typed pointer: the target is not boxed into an interface the way
44
+ // errors.As's `any` parameter asked.
45
+ function markCall(call, name) {
46
+ call.func._genEntry = name;
47
+ const arg = call.args[1];
48
+ arg._ifaceBox = undefined;
49
+ arg._ifaceSrc = undefined;
50
+ arg._ifaceNamedType = undefined;
51
+ arg._ifacePtrFields = undefined;
52
+ }
53
+
54
+ // One function per distinct target type; `tparams` ("[T any]") makes it the
55
+ // shared function for targets that are a type parameter.
56
+ function asFunction(name, target, tparams = "") {
57
+ return `func ${name}${tparams}(err error, target *${target}) bool {
58
+ if target == nil {
59
+ panic("errors: target must be a non-nil pointer")
60
+ }
61
+ for err != nil {
62
+ if v, ok := err.(${target}); ok {
63
+ *target = v
64
+ return true
65
+ }
66
+ if a, ok := err.(interface{ As(any) bool }); ok && a.As(target) {
67
+ return true
68
+ }
69
+ switch u := err.(type) {
70
+ case interface{ Unwrap() error }:
71
+ err = u.Unwrap()
72
+ case interface{ Unwrap() []error }:
73
+ for _, e := range u.Unwrap() {
74
+ if e != nil && ${name}${tparams ? "[T]" : ""}(e, target) {
75
+ return true
76
+ }
77
+ }
78
+ return false
79
+ default:
80
+ return false
81
+ }
82
+ }
83
+ return false
84
+ }`;
85
+ }
86
+
87
+ // The `errors.As` calls of the programs, and a diagnostic for each use of
88
+ // errors.As that is not a call.
89
+ function findSites(programs) {
90
+ const sites = [];
91
+ const diagnostics = [];
92
+ for (const program of programs) {
93
+ const aliases = errorsAliases(program);
94
+ if (aliases.size === 0) continue;
95
+ const callees = new Set();
96
+ const refs = [];
97
+ walkNodes(program.decls, (node) => {
98
+ if (isAsCall(node, aliases)) {
99
+ sites.push({ call: node, program });
100
+ callees.add(node.func);
101
+ } else if (isAsRef(node, aliases)) {
102
+ refs.push(node);
103
+ }
104
+ });
105
+ for (const node of refs)
106
+ if (!callees.has(node))
107
+ diagnostics.push({
108
+ program,
109
+ node: node.expr,
110
+ message: "errors.As cannot be used as a value",
111
+ hint: "call errors.As directly",
112
+ });
113
+ }
114
+ return { sites, diagnostics };
115
+ }
116
+
117
+ // What a call's target type allows: { base } for a target to generate for, or
118
+ // { message, hint } for one the generator rejects.
119
+ function classifyTarget(call) {
120
+ const argType = call.args[1]?._type;
121
+ if (argType?.kind !== "pointer") return { message: NOT_POINTER };
122
+ const base = argType.base;
123
+ if (base?.kind === "interface" && !base.name && base.methods?.size > 0)
124
+ return {
125
+ message:
126
+ "errors.As with a target of an anonymous interface type is not yet available",
127
+ hint: "declare a named interface type for the target",
128
+ };
129
+ if (base?.kind !== "typeParam" && mentionsTypeParam(base))
130
+ return {
131
+ message:
132
+ "errors.As with a target that mentions a type parameter is not yet available",
133
+ hint: "use a target that is a type parameter itself, or a concrete type",
134
+ };
135
+ return { base };
136
+ }
137
+
138
+ // Generates the `errors.As` functions for a package's call sites. Returns
139
+ // null when it has none; otherwise { body, imports, diagnostics, link } and
140
+ // marks each call's SelectorExpr with `_genEntry`.
141
+ export function generateErrorsAs(programs, _checker, pkgName) {
142
+ const { sites, diagnostics } = findSites(programs);
143
+ if (sites.length + diagnostics.length === 0) return null;
144
+ const qualifiers = new Map();
145
+ const speller = new TypeSpeller(pkgName, qualifiers, programs);
146
+ const prefix = `gfe_${String(pkgName).replace(/[^A-Za-z0-9_]/g, "_")}_as_`;
147
+ const names = new Map(); // spelled target -> function name
148
+ const out = [];
149
+ const sharedSites = []; // targets that are a type parameter
150
+ for (const { call, program } of sites) {
151
+ const { base, message, hint } = classifyTarget(call);
152
+ let target;
153
+ try {
154
+ if (base?.kind === "typeParam") sharedSites.push(call);
155
+ else if (base) target = speller.spell(base);
156
+ } catch (e) {
157
+ diagnostics.push({
158
+ program,
159
+ node: call.func.expr,
160
+ message: `errors.As: ${e.message}`,
161
+ });
162
+ continue;
163
+ }
164
+ if (!base) {
165
+ diagnostics.push({ program, node: call.func.expr, message, hint });
166
+ continue;
167
+ }
168
+ if (target === undefined) continue;
169
+ if (!names.has(target)) {
170
+ names.set(target, `${prefix}${names.size}`);
171
+ out.push(asFunction(names.get(target), target));
172
+ }
173
+ markCall(call, names.get(target));
174
+ }
175
+ const sharedName = `${prefix}t`;
176
+ if (sharedSites.length > 0) out.push(asFunction(sharedName, "T", "[T any]"));
177
+ for (const call of sharedSites) markCall(call, sharedName);
178
+ // The shared function is generic: once it is checked, its call sites take
179
+ // the type arguments the checker would have inferred.
180
+ const link = (chk) => {
181
+ const generic = chk.globals.symbols.get(sharedName);
182
+ for (const call of sharedSites) {
183
+ const typeArgs = [call.args[1]._type.base];
184
+ for (const n of [call, call.func]) {
185
+ n._typeArgs = typeArgs;
186
+ n._genericFnType = generic;
187
+ }
188
+ }
189
+ };
190
+ return {
191
+ body: out.join("\n\n"),
192
+ imports: importsFor(qualifiers),
193
+ diagnostics,
194
+ link,
195
+ };
196
+ }