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
@@ -15,15 +15,20 @@ const CHECK_STMT_DELEGATE = {
15
15
  BranchStmt: "_checkBranchStmt",
16
16
  };
17
17
 
18
+ import { exprText, operandKind } from "./operand-text.js";
18
19
  import {
19
20
  ANY,
20
21
  BOOL,
21
22
  defaultType,
23
+ is64BitInt,
22
24
  isAny,
23
25
  isBool,
26
+ isNil,
27
+ isUntyped,
24
28
  isVoid,
25
29
  iteratorYieldParams,
26
30
  Scope,
31
+ typeStr,
27
32
  } from "./types.js";
28
33
 
29
34
  /** @typedef {import('./index.js').TypeChecker} TypeChecker */
@@ -67,9 +72,9 @@ export const statementCheckMethods = {
67
72
 
68
73
  _checkDefineCommaOkAssert(stmt, rhs, scope) {
69
74
  if (!stmt.rhs[0]?._commaOk) return false;
70
- const lhsNames = stmt.lhs.map((e) => e.name ?? e);
71
- if (lhsNames[0] !== "_") scope.defineLocal(lhsNames[0], rhs[0]);
72
- if (lhsNames[1] !== "_") scope.defineLocal(lhsNames[1], BOOL);
75
+ this._declareDefineVars(stmt, [rhs[0], BOOL], scope);
76
+ if (stmt.lhs.every((e) => e._redecl || (e.name ?? e) === "_"))
77
+ this.err("no new variables on left side of :=", stmt.lhs[0]);
73
78
  stmt._rhsTypes = [rhs[0], BOOL];
74
79
  return true;
75
80
  },
@@ -79,6 +84,12 @@ export const statementCheckMethods = {
79
84
  const name = stmt.lhs[i].name ?? stmt.lhs[i];
80
85
  if (name === "_") continue;
81
86
  if (scope.symbols.has(name)) stmt.lhs[i]._redecl = true;
87
+ if (stmt.rhs.length === stmt.lhs.length)
88
+ this._checkConstDefault(
89
+ rhsFlat[i],
90
+ stmt.rhs[i],
91
+ "variable declaration",
92
+ );
82
93
  scope.defineLocal(name, defaultType(rhsFlat[i]) ?? ANY);
83
94
  }
84
95
  },
@@ -91,10 +102,11 @@ export const statementCheckMethods = {
91
102
  )
92
103
  return false;
93
104
  const mapBaseType = this.checkExpr(stmt.rhs[0].expr, scope);
94
- const resolvedBase =
95
- mapBaseType?.kind === "named" ? mapBaseType.underlying : mapBaseType;
105
+ const resolvedBase = this._underlyingOrCore(mapBaseType, scope);
96
106
  if (resolvedBase?.kind !== "map") return false;
97
- this.checkExpr(stmt.rhs[0].index, scope);
107
+ const keyType = this.checkExpr(stmt.rhs[0].index, scope);
108
+ this.assertAssignable(resolvedBase.key, keyType, stmt.rhs[0].index);
109
+ this._markIfaceKey(resolvedBase, stmt.rhs[0].index, keyType);
98
110
  stmt._commaOkMap = true;
99
111
  stmt.rhs[0]._mapValueType = resolvedBase.value;
100
112
  this._declareDefineVars(stmt, [resolvedBase.value, BOOL], scope);
@@ -122,7 +134,8 @@ export const statementCheckMethods = {
122
134
  if (this._checkDefineCommaOkAssert(stmt, rhs, scope)) return;
123
135
  if (
124
136
  stmt.rhs[0]?.kind !== "RangeExpr" &&
125
- stmt.lhs.length !== rhsFlat.length
137
+ stmt.lhs.length !== rhsFlat.length &&
138
+ !rhsFlat.some((t) => t?._tainted)
126
139
  ) {
127
140
  const vars =
128
141
  stmt.lhs.length === 1 ? "1 variable" : `${stmt.lhs.length} variables`;
@@ -149,10 +162,11 @@ export const statementCheckMethods = {
149
162
  )
150
163
  return null;
151
164
  const mapBaseType = this.checkExpr(stmt.rhs[0].expr, scope);
152
- const resolvedBase =
153
- mapBaseType?.kind === "named" ? mapBaseType.underlying : mapBaseType;
165
+ const resolvedBase = this._underlyingOrCore(mapBaseType, scope);
154
166
  if (resolvedBase?.kind !== "map") return null;
155
- this.checkExpr(stmt.rhs[0].index, scope);
167
+ const keyType = this.checkExpr(stmt.rhs[0].index, scope);
168
+ this.assertAssignable(resolvedBase.key, keyType, stmt.rhs[0].index);
169
+ this._markIfaceKey(resolvedBase, stmt.rhs[0].index, keyType);
156
170
  stmt._commaOkMap = true;
157
171
  stmt.rhs[0]._mapValueType = resolvedBase.value;
158
172
  return [resolvedBase.value, BOOL];
@@ -191,9 +205,25 @@ export const statementCheckMethods = {
191
205
  this.resolveType(r),
192
206
  stmt.rhs[i],
193
207
  );
208
+ // A shift count is not converted to the left operand's type.
209
+ if (stmt.op === "<<=" || stmt.op === ">>=") {
210
+ if (r && !isAny(r)) this.checkShiftCount(r, stmt.rhs[i]);
211
+ continue;
212
+ }
213
+ if (
214
+ stmt.rhs.length === lhsTypes.length &&
215
+ this._checkConstOverflow(lhsTypes[i], r, stmt.rhs[i], "assignment")
216
+ )
217
+ continue;
194
218
  if (!isAny(lhsTypes[i]) && !isAny(r)) {
195
219
  this._skipIfaceMark = true;
196
- this.assertAssignable(lhsTypes[i], r, stmt.lhs[i]);
220
+ this.assertAssignable(
221
+ lhsTypes[i],
222
+ r,
223
+ stmt.lhs[i],
224
+ "assignment",
225
+ stmt.rhs.length === lhsTypes.length ? stmt.rhs[i] : undefined,
226
+ );
197
227
  this._skipIfaceMark = false;
198
228
  }
199
229
  }
@@ -213,7 +243,8 @@ export const statementCheckMethods = {
213
243
  if (
214
244
  !stmt.rhs[0]?._commaOk &&
215
245
  stmt.rhs[0]?.kind !== "RangeExpr" &&
216
- stmt.lhs.length !== rhsFlat.length
246
+ stmt.lhs.length !== rhsFlat.length &&
247
+ !rhsFlat.some((t) => t?._tainted)
217
248
  ) {
218
249
  const vars =
219
250
  stmt.lhs.length === 1 ? "1 variable" : `${stmt.lhs.length} variables`;
@@ -246,9 +277,34 @@ export const statementCheckMethods = {
246
277
  if (!isVoid(returnType) && types.length > 0) {
247
278
  const expected =
248
279
  returnType.kind === "tuple" ? returnType.types : [returnType];
280
+ // `return f()` forwards every result of a multi-value call.
281
+ if (types.length === 1 && types[0]?.kind === "tuple") {
282
+ const forwarded = types[0].types;
283
+ if (forwarded.length !== expected.length) {
284
+ this.err(
285
+ `return count mismatch: have ${forwarded.length} values, want ${expected.length}`,
286
+ stmt,
287
+ );
288
+ return;
289
+ }
290
+ for (let i = 0; i < expected.length; i++) {
291
+ this.assertAssignable(
292
+ expected[i],
293
+ forwarded[i],
294
+ stmt.values[0],
295
+ "return statement",
296
+ );
297
+ }
298
+ return;
299
+ }
249
300
  for (let i = 0; i < expected.length; i++) {
250
301
  if (types[i])
251
- this.assertAssignable(expected[i], types[i], stmt.values[i] ?? stmt);
302
+ this.assertAssignable(
303
+ expected[i],
304
+ types[i],
305
+ stmt.values[i] ?? stmt,
306
+ "return statement",
307
+ );
252
308
  }
253
309
  }
254
310
  },
@@ -278,6 +334,8 @@ export const statementCheckMethods = {
278
334
  this.err("For condition must be bool", stmt);
279
335
  } else {
280
336
  const ct = this.checkExpr(stmt.cond.expr, inner);
337
+ const core = ct?.kind === "typeParam" ? this._coreType(ct, inner) : null;
338
+ if (core) stmt.cond.expr._coreType = core;
281
339
  const info = iteratorYieldParams(ct);
282
340
  if (info) {
283
341
  stmt.cond._isIterator = true;
@@ -288,7 +346,14 @@ export const statementCheckMethods = {
288
346
  }
289
347
  },
290
348
 
291
- _checkForStmt(stmt, scope, returnType) {
349
+ // `checkBody(bodyScope)` checks the loop body; templ `for` loops pass one
350
+ // that checks their HTML nodes in place of the header's empty block.
351
+ _checkForStmt(
352
+ stmt,
353
+ scope,
354
+ returnType,
355
+ checkBody = (s) => this.checkBlock(stmt.body, s, returnType),
356
+ ) {
292
357
  const inner = new Scope(scope);
293
358
  let iterInfo = null;
294
359
  if (stmt.init?.rhs?.[0]?.kind === "RangeExpr") {
@@ -299,7 +364,7 @@ export const statementCheckMethods = {
299
364
  if (stmt.post) this.checkStmt(stmt.post, inner, returnType);
300
365
  this._loopDepth++;
301
366
  const bodyScope = new Scope(inner);
302
- this.checkBlock(stmt.body, bodyScope, returnType);
367
+ checkBody(bodyScope);
303
368
  this._reportUnused(bodyScope, stmt);
304
369
  this._loopDepth--;
305
370
  this._reportUnused(inner, stmt);
@@ -309,10 +374,17 @@ export const statementCheckMethods = {
309
374
  const inner = new Scope(scope);
310
375
  if (stmt.init) this.checkStmt(stmt.init, inner, returnType);
311
376
  if (stmt.tag) this.checkExpr(stmt.tag, inner);
377
+ const tagIs64 = stmt.tag && is64BitInt(stmt.tag._type);
312
378
  this._switchDepth++;
313
379
  for (const c of stmt.cases) {
314
380
  const caseScope = new Scope(inner);
315
- if (c.list) for (const e of c.list) this.checkExpr(e, caseScope);
381
+ if (c.list) {
382
+ for (const e of c.list) {
383
+ this.checkExpr(e, caseScope);
384
+ this._checkCaseValue(stmt.tag, e);
385
+ if (tagIs64 && e._type?.kind === "untyped") e._is64Bit = true;
386
+ }
387
+ }
316
388
  for (const s of c.stmts) this.checkStmt(s, caseScope, returnType);
317
389
  this._reportUnused(caseScope, stmt);
318
390
  }
@@ -320,6 +392,106 @@ export const statementCheckMethods = {
320
392
  this._reportUnused(inner, stmt);
321
393
  },
322
394
 
395
+ // `case e:` compares `tag == e`, so `e` must convert to the tag's type
396
+ // (or the other way round for typed values).
397
+ _checkCaseValue(tag, e) {
398
+ const caseType = this.resolveType(e._type);
399
+ if (!tag) {
400
+ this._checkTaglessCase(e, caseType);
401
+ return;
402
+ }
403
+ let tagType = this.resolveType(tag._type);
404
+ // `switch "foo" {` compares as the constant's default type.
405
+ if (isUntyped(tagType) && !isNil(tagType) && tagType.base !== "bool")
406
+ tagType = defaultType(tagType);
407
+ if (this._caseValueUnchecked(tagType, caseType)) return;
408
+ if (isUntyped(caseType)) {
409
+ if (this._isUntypedAssignable(tagType, caseType)) return;
410
+ this.err(
411
+ `cannot convert ${exprText(e)} (${operandKind(e, caseType.base, true)}) to type ${typeStr(tagType)}`,
412
+ e,
413
+ );
414
+ return;
415
+ }
416
+ if (
417
+ this._assignableQuietly(tagType, caseType) ||
418
+ this._assignableQuietly(caseType, tagType)
419
+ )
420
+ return;
421
+ this.err(
422
+ `invalid case ${exprText(e)} in switch on ${exprText(tag)} (mismatched types ${typeStr(caseType)} and ${typeStr(tagType)})`,
423
+ e,
424
+ );
425
+ },
426
+
427
+ // A tagless `switch` is `switch true`, so every case must be boolean.
428
+ _checkTaglessCase(e, caseType) {
429
+ if (!caseType || caseType._tainted || caseType.kind === "typeParam") return;
430
+ if (isAny(caseType) || isNil(caseType) || isBool(caseType)) return;
431
+ if (isUntyped(caseType) && caseType.base === "bool") return;
432
+ const under = this.resolveType(caseType.underlying ?? caseType);
433
+ if (isBool(under)) return;
434
+ this.err(
435
+ `invalid case ${exprText(e)} in switch (mismatched types ${typeStr(caseType)} and bool)`,
436
+ e,
437
+ );
438
+ },
439
+
440
+ // Operands whose comparison the checker cannot (or need not) judge: unknown
441
+ // or recovered types, `any`/interface tags, nil cases, type parameters.
442
+ _caseValueUnchecked(tagType, caseType) {
443
+ if (!tagType || !caseType || tagType._tainted || caseType._tainted)
444
+ return true;
445
+ if (isAny(tagType) || isAny(caseType) || isNil(caseType)) return true;
446
+ if (isUntyped(tagType) || tagType.kind === "typeParam") return true;
447
+ return (
448
+ this.resolveType(tagType.underlying ?? tagType)?.kind === "interface"
449
+ );
450
+ },
451
+
452
+ // Whether assertAssignable(target, source) reports nothing.
453
+ _assignableQuietly(target, source) {
454
+ const saved = this.errors;
455
+ const savedSkip = this._skipIfaceMark;
456
+ this.errors = [];
457
+ this._skipIfaceMark = true;
458
+ this.assertAssignable(target, source, null);
459
+ const ok = this.errors.length === 0;
460
+ this.errors = saved;
461
+ this._skipIfaceMark = savedSkip;
462
+ return ok;
463
+ },
464
+
465
+ // A case naming an interface (literal or declared) records the resolved
466
+ // type so backends test its method set.
467
+ _markInterfaceCaseType(t, scope) {
468
+ if (!t || (t.kind !== "InterfaceType" && t.kind !== "TypeName")) return;
469
+ if (t.kind === "TypeName" && (t.name === "nil" || t.name === "error"))
470
+ return;
471
+ const resolved = this.resolveTypeNode(t, scope);
472
+ const u = this.resolveType(
473
+ resolved?.kind === "named" ? resolved.underlying : resolved,
474
+ );
475
+ if (u?.kind === "interface") t._type = resolved;
476
+ },
477
+
478
+ // Records the resolved type of a composite type node used as an assertion
479
+ // or type-switch target (`[]int`, `map[string]any`, `*int`, `iter.Seq[int]`)
480
+ // so the backend can match it against the dynamic type of an interface value.
481
+ _markDynamicType(node, resolved) {
482
+ const kind = node?.kind;
483
+ if (
484
+ kind === "SliceType" ||
485
+ kind === "ArrayType" ||
486
+ kind === "MapType" ||
487
+ kind === "PointerType" ||
488
+ kind === "FuncType" ||
489
+ kind === "StructType" ||
490
+ kind === "GenericTypeName"
491
+ )
492
+ node._assertType = resolved;
493
+ },
494
+
323
495
  _checkTypeSwitchStmt(stmt, scope, returnType) {
324
496
  const inner = new Scope(scope);
325
497
  this.checkExpr(stmt.expr, inner);
@@ -327,10 +499,25 @@ export const statementCheckMethods = {
327
499
  this._typeSwitchDepth++;
328
500
  for (const c of stmt.cases) {
329
501
  const caseScope = new Scope(inner);
502
+ // Resolve instantiated generic case types so backends can tell
503
+ // `*Stack[int]` from `*Stack[string]`.
504
+ for (const t of c.types ?? []) {
505
+ const base = t?.kind === "PointerType" ? t.base : t;
506
+ if (base?.kind === "GenericTypeName") this.resolveTypeNode(t, inner);
507
+ this._markInterfaceCaseType(t, inner);
508
+ if (t && t.kind !== "TypeName" && t.kind !== "InterfaceType")
509
+ this._markDynamicType(t, this.resolveTypeNode(t, inner));
510
+ }
330
511
  if (stmt.assign) {
331
- // Bind the variable to the single case type, or any for multi/default
332
- const bindType =
333
- c.types?.length === 1 ? this.resolveTypeNode(c.types[0], inner) : ANY;
512
+ // Bind the variable to the single case type, or any for multi/default.
513
+ // `case nil` binds the switch expression's own type, as in Go.
514
+ const only = c.types?.length === 1 ? c.types[0] : null;
515
+ const isNilCase = only?.kind === "TypeName" && only.name === "nil";
516
+ const bindType = isNilCase
517
+ ? (stmt.expr._type ?? ANY)
518
+ : only
519
+ ? this.resolveTypeNode(only, inner)
520
+ : ANY;
334
521
  caseScope.defineLocal(stmt.assign, bindType);
335
522
  // The capture variable is always considered used — it's the whole
336
523
  // point of the switch-with-assign form. Each case body uses its own
@@ -346,7 +533,12 @@ export const statementCheckMethods = {
346
533
 
347
534
  _checkBranchStmt(stmt) {
348
535
  const kw = stmt.keyword;
349
- if (kw === "continue" && this._loopDepth === 0)
536
+ if (kw === "goto")
537
+ this.err(
538
+ "goto is not supported; restructure with labeled break/continue",
539
+ stmt,
540
+ );
541
+ else if (kw === "continue" && this._loopDepth === 0)
350
542
  this.err("continue statement outside for loop", stmt);
351
543
  else if (kw === "break" && this._loopDepth === 0 && this._switchDepth === 0)
352
544
  this.err("break statement outside for loop or switch", stmt);