gofront 1.9.0 → 1.9.2

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 (43) hide show
  1. package/README.md +1 -1
  2. package/package.json +3 -3
  3. package/src/backend/js/expressions.js +329 -58
  4. package/src/backend/js/index.js +90 -10
  5. package/src/backend/js/runtime.js +51 -9
  6. package/src/backend/js/statements.js +193 -84
  7. package/src/backend/js/type-info.js +123 -8
  8. package/src/backend/shared/fmt-types.js +30 -1
  9. package/src/backend/wasm/emit-builtins.js +250 -9
  10. package/src/backend/wasm/emit-exprs.js +63 -22
  11. package/src/backend/wasm/emit-shared.js +4 -1
  12. package/src/backend/wasm/emit-stmts.js +239 -205
  13. package/src/backend/wasm/emit.js +73 -26
  14. package/src/backend/wasm/globals.js +1 -0
  15. package/src/backend/wasm/index.js +8 -0
  16. package/src/backend/wasm/module-emitter.js +211 -57
  17. package/src/backend/wasm/monomorph.js +36 -2
  18. package/src/backend/wasm/natives.js +4 -0
  19. package/src/backend/wasm/types.js +32 -3
  20. package/src/backend/wasm/variadic.js +6 -1
  21. package/src/cli/minifier.js +20 -6
  22. package/src/compiler.js +6 -1
  23. package/src/lexer/lexer.js +1 -0
  24. package/src/link/stdlib.js +1 -0
  25. package/src/link/type-names.js +72 -16
  26. package/src/lower/embedding.js +43 -15
  27. package/src/lower/fmt-formatters.js +38 -14
  28. package/src/lower/functions.js +223 -7
  29. package/src/lower/gen-types.js +21 -2
  30. package/src/lower/index.js +7 -1
  31. package/src/lower/json-codecs.js +114 -6
  32. package/src/parser/declarations.js +4 -2
  33. package/src/parser/expressions.js +25 -41
  34. package/src/parser/statements.js +107 -0
  35. package/src/parser/types.js +11 -0
  36. package/src/typechecker/assignability.js +94 -12
  37. package/src/typechecker/constants.js +124 -2
  38. package/src/typechecker/expressions.js +171 -17
  39. package/src/typechecker/index.js +158 -29
  40. package/src/typechecker/operand-text.js +1 -0
  41. package/src/typechecker/resolve.js +308 -39
  42. package/src/typechecker/statements.js +171 -21
  43. package/src/typechecker/types.js +84 -2
@@ -59,8 +59,16 @@ export function walkNodes(root, fn) {
59
59
  }
60
60
  fn(root);
61
61
  forEachChild(root, (c) => walkNodes(c, fn));
62
+ // templ bodies keep their Go expressions in non-enumerable properties
63
+ // (see parser/templ.js), which forEachChild does not visit.
64
+ for (const name of TEMPL_EXPR_PROPS) {
65
+ const d = Object.getOwnPropertyDescriptor(root, name);
66
+ if (d && !d.enumerable) walkNodes(d.value, fn);
67
+ }
62
68
  }
63
69
 
70
+ const TEMPL_EXPR_PROPS = ["expr", "exprs", "forStmt"];
71
+
64
72
  // True when `t` mentions a type parameter (a generic declaration, or a type
65
73
  // built from one inside a generic function).
66
74
  export function mentionsTypeParam(t, seen = new Set()) {
@@ -126,8 +134,9 @@ export function importsFor(qualifiers) {
126
134
 
127
135
  // Every type a closed-world generator may need to describe: the entries
128
136
  // { 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).
137
+ // the type of every expression (`expr`), the static type of every value
138
+ // converted to an interface (`boxed`) and the package's declared named types
139
+ // (`declared`, node null).
131
140
  export function collectProgramTypes(programs, checker) {
132
141
  const found = [];
133
142
  for (const p of programs)
@@ -136,6 +145,14 @@ export function collectProgramTypes(programs, checker) {
136
145
  found.push({ type: defaultType(a), node, via: "typeArg" });
137
146
  if (node._type)
138
147
  found.push({ type: defaultType(node._type), node, via: "expr" });
148
+ if (node._ifaceSrc)
149
+ found.push({ type: defaultType(node._ifaceSrc), node, via: "boxed" });
150
+ // A multi-value call forwarded into interfaces (`return f()`,
151
+ // `g(f())`) keeps one mark holder per element until lowering
152
+ // splits it.
153
+ for (const h of node._fwdIface ?? [])
154
+ if (h?._ifaceSrc)
155
+ found.push({ type: defaultType(h._ifaceSrc), node, via: "boxed" });
139
156
  });
140
157
  for (const [name, t] of checker.types) {
141
158
  if (t?.kind !== "named" || t.name !== name) continue;
@@ -253,6 +270,8 @@ export class TypeSpeller {
253
270
  const byKind = {
254
271
  basic: () => t.name !== "void" && t.name !== "nil",
255
272
  named: () => this.spellableNamed(t, ok, seen),
273
+ // A JS-interop (.d.ts) type has no Go source to write.
274
+ namespace: () => false,
256
275
  pointer: () => ok(t.base),
257
276
  slice: () => ok(t.elem),
258
277
  array: () => ok(t.elem),
@@ -16,7 +16,12 @@ import { scanAddressTaken } from "./boxing.js";
16
16
  import { analyzeCaptures } from "./captures.js";
17
17
  import { computeEmbeddedStubs } from "./embedding.js";
18
18
  import { paramEscapes } from "./escape.js";
19
- import { extractNamedReturns, hasDefer, normalizeDefers } from "./functions.js";
19
+ import {
20
+ extractNamedReturns,
21
+ hasDefer,
22
+ normalizeDefers,
23
+ splitForwardedTuples,
24
+ } from "./functions.js";
20
25
  import { computeGlobalAnalysis } from "./globals.js";
21
26
  import { OwnershipContext, takesElementAddress } from "./ownership.js";
22
27
 
@@ -83,6 +88,7 @@ export function lower(programOrPrograms, checker = null) {
83
88
  ? programOrPrograms
84
89
  : [programOrPrograms];
85
90
 
91
+ splitForwardedTuples(programs);
86
92
  normalizeDefers(programs);
87
93
 
88
94
  res.elemAddrTaken = programs.some((p) => takesElementAddress(p?.decls));
@@ -220,6 +220,8 @@ export class JsonCodecGen extends TypeSpeller {
220
220
  return `map[${this.refl(t.key)}]${this.refl(t.value)}`;
221
221
  case "interface":
222
222
  return t.name === "error" ? "error" : "interface {}";
223
+ case "namespace":
224
+ return t.name;
223
225
  case "func":
224
226
  return this.funcString(t, (x) => this.refl(x));
225
227
  default:
@@ -273,9 +275,49 @@ export class JsonCodecGen extends TypeSpeller {
273
275
 
274
276
  // Statements that encode the Go expression `x` of type `t`.
275
277
  encValue(t, x, addr) {
278
+ if (holdsInterop(t)) return this.encInterop(t, x);
276
279
  return [`${this.encFn(t, addr)}(e, ${x})`];
277
280
  }
278
281
 
282
+ // A JS-interop value has no Go source to spell, so it is encoded where it
283
+ // sits: nil is `null` (an empty slice, map or array is `[]` or `{}`), and
284
+ // anything else fails with the type's name.
285
+ encInterop(t, x) {
286
+ const u = under(t);
287
+ const fail = (name) => `\te.FailType(${q(name)})`;
288
+ if (u.kind === "namespace")
289
+ return [`if ${x} == nil {`, "\te.Null()", "} else {", fail(u.name), "}"];
290
+ if (u.kind === "pointer")
291
+ return [
292
+ `if ${x} == nil {`,
293
+ "\te.Null()",
294
+ "} else {",
295
+ fail(this.refl(t)),
296
+ "}",
297
+ ];
298
+ const [empty, elem] =
299
+ u.kind === "map"
300
+ ? ["{}", holdsInterop(u.key) ? u.key : u.value]
301
+ : ["[]", u.elem];
302
+ if (u.kind === "array")
303
+ return [
304
+ `if ${u.size} == 0 {`,
305
+ `\te.Lit(${q(empty)})`,
306
+ "} else {",
307
+ fail(this.refl(elem)),
308
+ "}",
309
+ ];
310
+ return [
311
+ `if ${x} == nil {`,
312
+ "\te.Null()",
313
+ `} else if len(${x}) == 0 {`,
314
+ `\te.Lit(${q(empty)})`,
315
+ "} else {",
316
+ fail(this.refl(elem)),
317
+ "}",
318
+ ];
319
+ }
320
+
279
321
  // `mt` is the type that declares the method (the pointee for a pointer).
280
322
  marshalCall(x, t, mt = t) {
281
323
  if (!marshalerOf(mt) && textMarshalerOf(mt))
@@ -413,6 +455,7 @@ export class JsonCodecGen extends TypeSpeller {
413
455
  if (u.kind === "slice" || u.kind === "map") return `len(${x}) == 0`;
414
456
  if (u.kind === "array") return u.size === 0 ? "true" : "false";
415
457
  if (u.kind === "struct") return "false";
458
+ if (u.kind === "namespace") return `${x} == nil`;
416
459
  const n = unalias(u.name);
417
460
  if (n === "bool") return `!${x}`;
418
461
  if (n === "string") return `${x} == ""`;
@@ -779,6 +822,9 @@ export class JsonCodecGen extends TypeSpeller {
779
822
 
780
823
  decStruct(u, t, typ) {
781
824
  const fields = resolveJsonFields(u);
825
+ for (const f of fields)
826
+ if (holdsInterop(f.type))
827
+ throw new Error(`json: cannot unmarshal into ${interopName(f.type)}`);
782
828
  const sname = this.shortName(t);
783
829
  const lines = [
784
830
  "if d.Null() {",
@@ -1106,7 +1152,8 @@ export class JsonCodecGen extends TypeSpeller {
1106
1152
  this.encFn(t, false);
1107
1153
  }
1108
1154
  for (const { type: t, via } of this.collectedTypes()) {
1109
- if (via !== "declared" || mentionsTypeParam(t)) continue;
1155
+ if (via !== "boxed" || !t || mentionsTypeParam(t)) continue;
1156
+ if (!this.spellable(t)) continue;
1110
1157
  const u = under(t);
1111
1158
  if (!u || u.kind === "interface" || u.kind === "func") continue;
1112
1159
  this.encFn(t, false);
@@ -1114,12 +1161,15 @@ export class JsonCodecGen extends TypeSpeller {
1114
1161
  }
1115
1162
  }
1116
1163
 
1164
+ // A struct holding a JS-interop value (a `.d.ts` interface) cannot be
1165
+ // written as a dynamic case, so it is not spellable either.
1117
1166
  spellableNamed(t, ok) {
1167
+ if (isJsonValue(t)) return true;
1118
1168
  return (
1119
- isJsonValue(t) ||
1120
- (!t.name.includes("$$") &&
1121
- !t.name.includes("[") &&
1122
- (t.typeArgs ?? []).every(ok))
1169
+ !t.name.includes("$$") &&
1170
+ !t.name.includes("[") &&
1171
+ (t.typeArgs ?? []).every(ok) &&
1172
+ !reachesInterop(t)
1123
1173
  );
1124
1174
  }
1125
1175
 
@@ -1157,7 +1207,12 @@ export class JsonCodecGen extends TypeSpeller {
1157
1207
  }
1158
1208
  // Slices, arrays and maps reach an interface from any expression
1159
1209
  // (a function result, a variable), not only from a literal
1160
- if (!buildsValue(node) && !isCompositeType(t) && !isJsonValue(t))
1210
+ if (
1211
+ !buildsValue(node) &&
1212
+ !isCompositeType(t) &&
1213
+ !isFuncComposite(t) &&
1214
+ !isJsonValue(t)
1215
+ )
1161
1216
  continue;
1162
1217
  if (this.spellable(t) && under(t)?.kind !== "interface")
1163
1218
  found.set(this.key(t), t);
@@ -1196,6 +1251,48 @@ const isTaggedSlice = (t) =>
1196
1251
 
1197
1252
  // json.RawMessage and json.Number values can reach an interface from any
1198
1253
  // expression (a conversion, a field read).
1254
+ // True when `t` is, or holds through pointers, elements and struct fields, a
1255
+ // JS-interop type (a `.d.ts` interface) that has no Go source to spell.
1256
+ export function reachesInterop(t, seen = new Set(), viaStruct = true) {
1257
+ if (!t || seen.has(t)) return false;
1258
+ seen.add(t);
1259
+ const u = under(t) ?? t;
1260
+ switch (u.kind) {
1261
+ case "namespace":
1262
+ return true;
1263
+ case "pointer":
1264
+ return reachesInterop(u.base, seen, viaStruct);
1265
+ case "slice":
1266
+ case "array":
1267
+ return reachesInterop(u.elem, seen, viaStruct);
1268
+ case "map":
1269
+ return (
1270
+ reachesInterop(u.key, seen, viaStruct) ||
1271
+ reachesInterop(u.value, seen, viaStruct)
1272
+ );
1273
+ case "struct":
1274
+ return (
1275
+ viaStruct &&
1276
+ (u._structFields ?? []).some((f) => reachesInterop(f.type, seen))
1277
+ );
1278
+ default:
1279
+ return false;
1280
+ }
1281
+ }
1282
+
1283
+ // True when a value of type `t` is, or holds through pointers and elements
1284
+ // (not through a named struct, which has its own codec), a JS-interop value.
1285
+ const holdsInterop = (t) => reachesInterop(t, new Set(), false);
1286
+
1287
+ // The name of the JS-interop type that `t` is or wraps.
1288
+ function interopName(t) {
1289
+ const u = under(t);
1290
+ if (u.kind === "namespace") return u.name;
1291
+ if (u.kind === "map")
1292
+ return interopName(holdsInterop(u.key) ? u.key : u.value);
1293
+ return interopName(u.base ?? u.elem);
1294
+ }
1295
+
1199
1296
  const isJsonValue = (t) => isRaw(t) || isNumber(t);
1200
1297
 
1201
1298
  export const isCompositeType = (t) => {
@@ -1207,6 +1304,17 @@ export const isCompositeType = (t) => {
1207
1304
  );
1208
1305
  };
1209
1306
 
1307
+ // A slice, array or map holding funcs: it never encodes, but a nil one in an
1308
+ // interface still does, as `null`, and a non-nil one names the func.
1309
+ export const isFuncComposite = (t) => {
1310
+ const k = under(t)?.kind;
1311
+ return (
1312
+ (k === "slice" || k === "array" || k === "map") &&
1313
+ holdsFunc(t) &&
1314
+ !hasSharedRepr(t)
1315
+ );
1316
+ };
1317
+
1210
1318
  // Func values are not JSON-encodable, and `case []func():` does not parse.
1211
1319
  export function holdsFunc(t, depth = 0) {
1212
1320
  if (!t || depth > 8) return false;
@@ -227,12 +227,14 @@ export const declarationParseMethods = {
227
227
  this.check(T.EOF)
228
228
  )
229
229
  return null;
230
- // Inside a type expression (e.g. func type in a tuple or a type-switch
231
- // `case func():`), `,`, `)` and `:` also end the type
230
+ // Inside a type expression (e.g. func type in a tuple, a type-switch
231
+ // `case func():` or a type argument `f[func(int)]`), `,`, `)`, `]` and
232
+ // `:` also end the type
232
233
  if (
233
234
  inTypeExpr &&
234
235
  (this.check(T.COMMA) ||
235
236
  this.check(T.RPAREN) ||
237
+ this.check(T.RBRACKET) ||
236
238
  this.check(T.COLON) ||
237
239
  this.check(T.STRING) ||
238
240
  this.check(T.ASSIGN))
@@ -402,48 +402,20 @@ export const expressionParserMethods = {
402
402
  return { kind: "CallExpr", func: fn, args };
403
403
  },
404
404
 
405
- // Returns true if the current position looks like a composite literal argument,
406
- // e.g. []int{1,2,3} or map[string]int{"a":1}. We scan ahead past the type
407
- // tokens to check whether a { follows, without advancing this.pos permanently.
405
+ // Returns true if the current position starts a composite literal argument,
406
+ // e.g. []int{1,2,3}, map[string]int{"a":1} or []func() int{f}: the type is
407
+ // parsed speculatively and must be followed by `{`. The position is
408
+ // always restored.
408
409
  looksLikeCompositeLitArg() {
409
- let i = this.pos;
410
- const src = this.tokens;
411
- // Skip over the type tokens: [, ], map, [, K, ], V, ident, etc.
412
- // Simple approach: skip until we hit something that can't be part of a type
413
- let depth = 0;
414
- while (i < src.length) {
415
- const tt = src[i].type;
416
- if (tt === T.LBRACKET) {
417
- depth++;
418
- i++;
419
- continue;
420
- }
421
- if (tt === T.RBRACKET) {
422
- depth--;
423
- i++;
424
- continue;
425
- }
426
- if (depth > 0) {
427
- i++;
428
- continue;
429
- }
430
- if (
431
- tt === T.MAP ||
432
- tt === T.IDENT ||
433
- tt === T.DOT || // qualified types: []pkg.Type{...}
434
- tt === T.STAR ||
435
- tt === T.INT ||
436
- tt === T.FLOAT ||
437
- tt === T.STRING
438
- ) {
439
- i++;
440
- continue;
441
- }
442
- break;
410
+ const start = this.pos;
411
+ try {
412
+ this.parseType();
413
+ return this.check(T.LBRACE);
414
+ } catch {
415
+ return false;
416
+ } finally {
417
+ this.pos = start;
443
418
  }
444
- // Skip whitespace tokens (shouldn't exist post-lex, but be safe)
445
- while (i < src.length && src[i].type === T.SEMICOLON) i++;
446
- return i < src.length && src[i].type === T.LBRACE;
447
419
  },
448
420
 
449
421
  parseCompositeLit(typeExpr) {
@@ -494,7 +466,9 @@ export const expressionParserMethods = {
494
466
  // Literals
495
467
  if (t.type === T.INT) {
496
468
  this.advance();
497
- return { kind: "BasicLit", litKind: "INT", value: t.value };
469
+ const lit = { kind: "BasicLit", litKind: "INT", value: t.value };
470
+ if (t.rune) lit.rune = true;
471
+ return lit;
498
472
  }
499
473
  if (t.type === T.FLOAT) {
500
474
  this.advance();
@@ -589,6 +563,16 @@ export const expressionParserMethods = {
589
563
  return this.parseCompositeLit(typeExpr);
590
564
  }
591
565
 
566
+ // Interface conversion: interface{ M() }(v). `interface{…}{` is not a
567
+ // composite literal in Go, so only the conversion form is accepted.
568
+ if (t.type === T.INTERFACE) {
569
+ const targetType = this.parseInterfaceType();
570
+ this.expect(T.LPAREN);
571
+ const expr = this.parseExpr();
572
+ this.expect(T.RPAREN);
573
+ return { kind: "TypeConversion", targetType, expr };
574
+ }
575
+
592
576
  if (t.type === T.CHAN) {
593
577
  this.err("channels are not supported in GoFront");
594
578
  }
@@ -2,6 +2,57 @@
2
2
 
3
3
  import { T } from "../lexer/tokens.js";
4
4
 
5
+ // Statement keywords: none of them can appear inside a composite literal or a
6
+ // statement header's expressions.
7
+ const STMT_KEYWORDS = new Set([
8
+ T.VAR,
9
+ T.CONST,
10
+ T.TYPE,
11
+ T.IF,
12
+ T.ELSE,
13
+ T.FOR,
14
+ T.RETURN,
15
+ T.BREAK,
16
+ T.CONTINUE,
17
+ T.SWITCH,
18
+ T.CASE,
19
+ T.DEFAULT,
20
+ T.SELECT,
21
+ T.DEFER,
22
+ T.GO,
23
+ T.FALLTHROUGH,
24
+ ]);
25
+
26
+ // Tokens that only appear in statements: a brace pair holding one at its top
27
+ // level is a block, not a composite literal.
28
+ const STMT_ONLY_TOKENS = new Set([
29
+ ...STMT_KEYWORDS,
30
+ T.SEMICOLON,
31
+ T.ASSIGN,
32
+ T.DEFINE,
33
+ T.PLUS_ASSIGN,
34
+ T.MINUS_ASSIGN,
35
+ T.STAR_ASSIGN,
36
+ T.SLASH_ASSIGN,
37
+ T.PERCENT_ASSIGN,
38
+ T.AMP_ASSIGN,
39
+ T.PIPE_ASSIGN,
40
+ T.AND_NOT_ASSIGN,
41
+ T.CARET_ASSIGN,
42
+ T.LSHIFT_ASSIGN,
43
+ T.RSHIFT_ASSIGN,
44
+ T.INC,
45
+ T.DEC,
46
+ ]);
47
+
48
+ const OPEN_BRACKETS = new Set([T.LBRACE, T.LPAREN, T.LBRACKET]);
49
+ const CLOSE_BRACKETS = new Set([T.RBRACE, T.RPAREN, T.RBRACKET]);
50
+
51
+ function bracketDelta(tt) {
52
+ if (OPEN_BRACKETS.has(tt)) return 1;
53
+ return CLOSE_BRACKETS.has(tt) ? -1 : 0;
54
+ }
55
+
5
56
  // Method-name dispatch for pure-delegation cases in _parseStmt.
6
57
  const PARSE_STMT_DELEGATE = {
7
58
  [T.VAR]: "parseVarDecl",
@@ -130,6 +181,7 @@ export const statementParserMethods = {
130
181
  let cond;
131
182
  // if init; cond { } — use noCompLit to avoid "{" being parsed as composite literal
132
183
  const first = this.parseSimpleStmtRaw(true);
184
+ this._rejectHeaderCompositeLit("if", 0, first);
133
185
  // Only treat as "init; cond" if semicolon is followed by a non-brace token
134
186
  if (this.check(T.SEMICOLON) && this.peek2().type !== T.LBRACE) {
135
187
  this.advance(); // consume ;
@@ -166,6 +218,7 @@ export const statementParserMethods = {
166
218
  let first = null;
167
219
  if (!this.check(T.SEMICOLON)) {
168
220
  first = this.parseSimpleStmtRaw(true);
221
+ this._rejectHeaderCompositeLit("for", 1, first);
169
222
 
170
223
  if (this.check(T.LBRACE)) {
171
224
  // for k, v := range expr { } — range stmt ends right before {
@@ -201,6 +254,59 @@ export const statementParserMethods = {
201
254
  return { kind: "ForStmt", init: first, cond, post, body };
202
255
  },
203
256
 
257
+ // `if s := &T{f: 1}; s.f > 0 {`: in a header, `{` after a type name opens
258
+ // the body, so Go requires the literal to be parenthesized. Reported when
259
+ // the brace pair holds no statement tokens, is followed by an explicit `;`,
260
+ // and the header then continues to its body `{` (with at most `semis`
261
+ // further `;`, one for a `for` post statement). A bare expression or a
262
+ // range clause before the `{` (`if ok { f() };`) means it is the body.
263
+ _rejectHeaderCompositeLit(keyword, semis, first) {
264
+ if (!this.check(T.LBRACE)) return;
265
+ if (first?.kind === "ExprStmt" || this.isRangeStmt(first)) return;
266
+ const prev = this.tokens[this.pos - 1]?.type;
267
+ if (prev !== T.IDENT && prev !== T.RBRACKET) return;
268
+ const close = this._closingBrace(this.pos);
269
+ if (close < 0) return;
270
+ const semi = this.tokens[close + 1];
271
+ if (semi?.type !== T.SEMICOLON || !this._isExplicitSemi(semi)) return;
272
+ if (!this._headerContinues(close + 2, semis)) return;
273
+ this.err(`composite literal in ${keyword} header must be parenthesized`);
274
+ },
275
+
276
+ // Index of the `}` matching the `{` at `open`, or -1 when the pair holds a
277
+ // statement-only token at its top level (then it is a block).
278
+ _closingBrace(open) {
279
+ let depth = 0;
280
+ for (let i = open; i < this.tokens.length; i++) {
281
+ const tt = this.tokens[i].type;
282
+ depth += bracketDelta(tt);
283
+ if (depth === 0) return tt === T.RBRACE ? i : -1;
284
+ if (tt === T.EOF || (depth === 1 && STMT_ONLY_TOKENS.has(tt))) return -1;
285
+ }
286
+ return -1;
287
+ },
288
+
289
+ _isExplicitSemi(tok) {
290
+ if (!this.source) return false;
291
+ this._sourceLines ??= this.source.split("\n");
292
+ return this._sourceLines[tok.line - 1]?.[tok.col - 1] === ";";
293
+ },
294
+
295
+ // Whether the tokens from `i` reach a top-level `{` (the body) without a
296
+ // statement keyword and with at most `semis` semicolons on the way.
297
+ _headerContinues(i, semis) {
298
+ let depth = 0;
299
+ for (; i < this.tokens.length; i++) {
300
+ const tt = this.tokens[i].type;
301
+ if (tt === T.LBRACE && depth === 0) return true;
302
+ depth += bracketDelta(tt);
303
+ if (tt === T.SEMICOLON && depth === 0) semis--;
304
+ if (depth < 0 || semis < 0) return false;
305
+ if (tt === T.EOF || STMT_KEYWORDS.has(tt)) return false;
306
+ }
307
+ return false;
308
+ },
309
+
204
310
  isRangeStmt(stmt) {
205
311
  if (!stmt) return false;
206
312
  if (stmt.kind !== "DefineStmt" && stmt.kind !== "AssignStmt") return false;
@@ -213,6 +319,7 @@ export const statementParserMethods = {
213
319
  let tag = null;
214
320
  if (!this.check(T.LBRACE)) {
215
321
  const first = this.parseSimpleStmtRaw(true);
322
+ this._rejectHeaderCompositeLit("switch", 0, first);
216
323
  if (this.match(T.SEMICOLON)) {
217
324
  init = first;
218
325
  if (!this.check(T.LBRACE))
@@ -329,6 +329,17 @@ export const typeParserMethods = {
329
329
  this.semi();
330
330
  continue;
331
331
  }
332
+ // Type-set element that is not a bare name: *T, []int, map[K]V, ...
333
+ if (!this.check(T.IDENT)) {
334
+ const terms = [{ approx: false, type: this.parseType() }];
335
+ while (this.match(T.PIPE)) {
336
+ const approx = this.match(T.TILDE);
337
+ terms.push({ approx, type: this.parseType() });
338
+ }
339
+ unionConstraint = { kind: "UnionConstraint", terms };
340
+ this.semi();
341
+ continue;
342
+ }
332
343
  const name = this.expect(T.IDENT).value;
333
344
  if (this.check(T.LPAREN)) {
334
345
  // Method signature: Name(params) returnType