gofront 1.6.0 → 1.6.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 (68) hide show
  1. package/package.json +1 -1
  2. package/src/backend/js/expressions.js +238 -109
  3. package/src/backend/js/index.js +38 -1
  4. package/src/backend/js/runtime.js +953 -53
  5. package/src/backend/js/statements.js +55 -67
  6. package/src/backend/js/stdlib/base64.js +12 -0
  7. package/src/backend/js/stdlib/binary.js +102 -0
  8. package/src/backend/js/stdlib/bits.js +15 -1
  9. package/src/backend/js/stdlib/builder.js +28 -9
  10. package/src/backend/js/stdlib/bytes.js +75 -18
  11. package/src/backend/js/stdlib/errors.js +20 -1
  12. package/src/backend/js/stdlib/fmt.js +70 -13
  13. package/src/backend/js/stdlib/hex.js +17 -0
  14. package/src/backend/js/stdlib/html.js +1 -1
  15. package/src/backend/js/stdlib/index.js +16 -0
  16. package/src/backend/js/stdlib/io.js +105 -3
  17. package/src/backend/js/stdlib/math.js +13 -1
  18. package/src/backend/js/stdlib/path.js +18 -4
  19. package/src/backend/js/stdlib/rand.js +10 -5
  20. package/src/backend/js/stdlib/sha256.js +12 -0
  21. package/src/backend/js/stdlib/slices.js +52 -26
  22. package/src/backend/js/stdlib/strconv.js +21 -5
  23. package/src/backend/js/stdlib/strings.js +148 -15
  24. package/src/backend/js/stdlib/sync.js +13 -0
  25. package/src/backend/js/stdlib/time.js +90 -19
  26. package/src/backend/js/stdlib/unicode.js +2 -2
  27. package/src/backend/wasm/boundary.js +1207 -41
  28. package/src/backend/wasm/const-fold.js +205 -0
  29. package/src/backend/wasm/emit-builtins.js +203 -76
  30. package/src/backend/wasm/emit-exprs.js +464 -276
  31. package/src/backend/wasm/emit-maps.js +144 -8
  32. package/src/backend/wasm/emit-stdlib.js +12 -3005
  33. package/src/backend/wasm/emit-stmts.js +264 -270
  34. package/src/backend/wasm/emit.js +115 -87
  35. package/src/backend/wasm/encode.js +55 -29
  36. package/src/backend/wasm/index.js +1001 -32
  37. package/src/backend/wasm/optimize.js +13 -4
  38. package/src/backend/wasm/stdlib/bytes.js +5039 -0
  39. package/src/backend/wasm/stdlib/core.js +8903 -0
  40. package/src/backend/wasm/stdlib/index.js +40 -0
  41. package/src/backend/wasm/stdlib/io.js +2161 -0
  42. package/src/backend/wasm/stdlib/packages.js +2400 -0
  43. package/src/backend/wasm/stdlib/pure.js +720 -0
  44. package/src/backend/wasm/stdlib/time.js +1200 -0
  45. package/src/backend/wasm/types.js +153 -1
  46. package/src/cli-core.js +1 -1
  47. package/src/compiler.js +18 -4
  48. package/src/dts-parser.js +79 -126
  49. package/src/index.js +2 -6
  50. package/src/lexer.js +80 -29
  51. package/src/lower/boxing.js +1 -2
  52. package/src/lower/captures.js +2 -6
  53. package/src/lower/ownership.js +30 -9
  54. package/src/minifier.js +20 -68
  55. package/src/parser/declarations.js +3 -6
  56. package/src/parser/expressions.js +3 -8
  57. package/src/parser/index.js +18 -3
  58. package/src/parser/statements.js +25 -38
  59. package/src/parser/types.js +1 -2
  60. package/src/resolver.js +2 -27
  61. package/src/templ-lexer.js +29 -63
  62. package/src/templ-parser.js +22 -45
  63. package/src/typechecker/assignability.js +81 -13
  64. package/src/typechecker/expressions.js +102 -2
  65. package/src/typechecker/resolve.js +36 -6
  66. package/src/typechecker/stdlib/core.js +280 -89
  67. package/src/typechecker/stdlib/extended.js +453 -1
  68. package/src/typechecker/types.js +344 -6
@@ -118,17 +118,15 @@ export class TemplLexer {
118
118
  continue;
119
119
  }
120
120
 
121
- // Detect `templ` keyword at identifier boundaries
121
+ // Detect `templ` or `css` keyword at identifier boundaries
122
122
  if (this._matchKeyword("templ")) {
123
123
  this.tokens.push(new Token(TT.TEMPL_KW, "templ", this.line, this.col));
124
- this._lexTemplDecl();
124
+ this._lexBlockDecl("templ");
125
125
  continue;
126
126
  }
127
-
128
- // Detect `css` keyword at identifier boundaries
129
127
  if (this._matchKeyword("css")) {
130
128
  this.tokens.push(new Token(TT.CSS_KW, "css", this.line, this.col));
131
- this._lexCssDecl();
129
+ this._lexBlockDecl("css");
132
130
  continue;
133
131
  }
134
132
 
@@ -201,24 +199,25 @@ export class TemplLexer {
201
199
  for (const t of this._goTokens(chunk)) this.tokens.push(t);
202
200
  }
203
201
 
204
- // ── Templ declaration lexing ─────────────────────────────────
202
+ // ── Templ / CSS declaration lexing ───────────────────────────
205
203
 
206
- // Called right after consuming `templ` keyword.
207
- // Lexes: Name(params) { <HTML body> }
208
- _lexTemplDecl() {
204
+ // Called right after consuming `templ` or `css` keyword.
205
+ // Lexes: Name(params) { <body> }
206
+ _lexBlockDecl(kw) {
209
207
  this.skipWhitespace();
210
208
 
211
- // Name
212
- const nameStart = this.pos;
213
- while (!this.eof() && /\w/.test(this.peek())) this.advance();
214
- const name = this.src.slice(nameStart, this.pos);
215
- if (!name) this.err("expected component name after 'templ'");
209
+ const name = this._readWord();
210
+ if (!name) {
211
+ this.err(
212
+ `expected ${kw === "templ" ? "component name" : "name"} after '${kw}'`,
213
+ );
214
+ }
216
215
  this.tokens.push(new Token(T.IDENT, name, this.line, this.col));
217
216
 
218
217
  this.skipWhitespace();
219
218
 
220
219
  // Parameter list ( ... ) — lex as Go
221
- if (this.peek() !== "(") this.err("expected '(' after templ name");
220
+ if (this.peek() !== "(") this.err(`expected '(' after ${kw} name`);
222
221
  const paramStart = this.pos;
223
222
  this._skipBalanced("(", ")");
224
223
  const paramSrc = this.src.slice(paramStart, this.pos);
@@ -227,43 +226,12 @@ export class TemplLexer {
227
226
  this.skipWhitespace();
228
227
 
229
228
  // Opening { of body
230
- if (this.peek() !== "{") this.err("expected '{' after templ params");
229
+ if (this.peek() !== "{") this.err(`expected '{' after ${kw} params`);
231
230
  this.advance(); // consume {
232
231
  this.tokens.push(new Token(T.LBRACE, "{", this.line, this.col));
233
232
 
234
- // HTML body
235
- this._lexHtmlBody(1);
236
- }
237
-
238
- // ── CSS declaration lexing ───────────────────────────────────
239
-
240
- _lexCssDecl() {
241
- this.skipWhitespace();
242
-
243
- // Name (identifier)
244
- const nameStart = this.pos;
245
- while (!this.eof() && /\w/.test(this.peek())) this.advance();
246
- const name = this.src.slice(nameStart, this.pos);
247
- if (!name) this.err("expected name after 'css'");
248
- this.tokens.push(new Token(T.IDENT, name, this.line, this.col));
249
-
250
- this.skipWhitespace();
251
-
252
- // Parameter list (balanced parens)
253
- if (this.peek() !== "(") this.err("expected '(' after css name");
254
- const paramStart = this.pos;
255
- this._skipBalanced("(", ")");
256
- const paramSrc = this.src.slice(paramStart, this.pos);
257
- for (const t of this._goTokens(paramSrc)) this.tokens.push(t);
258
-
259
- this.skipWhitespace();
260
-
261
- // Opening { of body
262
- if (this.peek() !== "{") this.err("expected '{' after css params");
263
- this.advance(); // consume {
264
- this.tokens.push(new Token(T.LBRACE, "{", this.line, this.col));
265
-
266
- this._lexCssBody();
233
+ if (kw === "templ") this._lexHtmlBody(1);
234
+ else this._lexCssBody();
267
235
  }
268
236
 
269
237
  _skipCssString(quote) {
@@ -660,7 +628,7 @@ export class TemplLexer {
660
628
  // ── HTML text content ─────────────────────────────────────────
661
629
 
662
630
  _lexHtmlText() {
663
- let text = "";
631
+ const start = this.pos;
664
632
  const line = this.line;
665
633
  while (!this.eof()) {
666
634
  const c = this.peek();
@@ -674,9 +642,9 @@ export class TemplLexer {
674
642
  (c === "d" && this._peekKeyword("default"))
675
643
  )
676
644
  break;
677
- text += this.advance();
645
+ this.advance();
678
646
  }
679
- const trimmed = text.trim();
647
+ const trimmed = this.src.slice(start, this.pos).trim();
680
648
  if (trimmed)
681
649
  this.tokens.push(new Token(TT.HTML_TEXT, trimmed, line, this.col));
682
650
  }
@@ -729,17 +697,15 @@ export class TemplLexer {
729
697
  }
730
698
 
731
699
  _readHtmlName() {
732
- let name = "";
733
- while (!this.eof() && /[a-zA-Z0-9_-]/.test(this.peek()))
734
- name += this.advance();
735
- return name;
700
+ const start = this.pos;
701
+ while (!this.eof() && /[a-zA-Z0-9_-]/.test(this.peek())) this.advance();
702
+ return this.src.slice(start, this.pos);
736
703
  }
737
704
 
738
705
  _readHtmlAttrName() {
739
- let name = "";
740
- while (!this.eof() && /[a-zA-Z0-9_:.-]/.test(this.peek()))
741
- name += this.advance();
742
- return name;
706
+ const start = this.pos;
707
+ while (!this.eof() && /[a-zA-Z0-9_:.-]/.test(this.peek())) this.advance();
708
+ return this.src.slice(start, this.pos);
743
709
  }
744
710
 
745
711
  _readQuotedString() {
@@ -756,9 +722,9 @@ export class TemplLexer {
756
722
  }
757
723
 
758
724
  _readWord() {
759
- let w = "";
760
- while (!this.eof() && /\w/.test(this.peek())) w += this.advance();
761
- return w;
725
+ const start = this.pos;
726
+ while (!this.eof() && /\w/.test(this.peek())) this.advance();
727
+ return this.src.slice(start, this.pos);
762
728
  }
763
729
 
764
730
  _skipToOpenBrace() {
@@ -48,55 +48,31 @@ export class TemplParser extends Parser {
48
48
 
49
49
  // ── CssDecl ───────────────────────────────────────────────────
50
50
 
51
- parseCssDecl() {
51
+ _parseDecl(kind, bodyFn) {
52
52
  const _line = this.peek().line;
53
- this.advance(); // consume CSS_KW
54
-
53
+ this.advance();
55
54
  const name = this.expect(T.IDENT).value;
56
-
57
- // Parse parameter list (standard Go params: LPAREN...RPAREN)
58
55
  const params = this.parseParamList();
59
-
60
- // Optional semicolon between ) and {
61
56
  this.semi();
62
-
63
- // Consume opening {
64
57
  this.expect(T.LBRACE);
65
-
66
- // Consume CSS_BODY
67
- const bodyToken = this.expect(TT.CSS_BODY);
68
- const cssText = bodyToken.value;
69
-
70
- // Consume closing }
71
- this.expect(T.RBRACE);
58
+ const extra = bodyFn();
72
59
  this.semi();
73
-
74
- return { kind: "CssDecl", name, params, cssText, _line };
60
+ return { kind, name, params, ...extra, _line };
75
61
  }
76
62
 
77
- // ── TemplDecl ─────────────────────────────────────────────────
63
+ parseCssDecl() {
64
+ return this._parseDecl("CssDecl", () => {
65
+ const bodyToken = this.expect(TT.CSS_BODY);
66
+ const cssText = bodyToken.value;
67
+ this.expect(T.RBRACE);
68
+ return { cssText };
69
+ });
70
+ }
78
71
 
79
72
  parseTemplDecl() {
80
- const _line = this.peek().line;
81
- this.advance(); // consume TEMPL_KW
82
-
83
- const name = this.expect(T.IDENT).value;
84
-
85
- // Parse parameter list (standard Go params: LPAREN...RPAREN)
86
- const params = this.parseParamList();
87
-
88
- // Optional semicolon between ) and {
89
- this.semi();
90
-
91
- // Consume the opening { of the HTML body
92
- this.expect(T.LBRACE);
93
-
94
- // Parse the HTML body; parseHtmlChildren consumes the closing RBRACE
95
- const body = this.parseHtmlChildren();
96
-
97
- this.semi();
98
-
99
- return { kind: "TemplDecl", name, params, body, _line };
73
+ return this._parseDecl("TemplDecl", () => ({
74
+ body: this.parseHtmlChildren(),
75
+ }));
100
76
  }
101
77
 
102
78
  // ── HTML node parsing ─────────────────────────────────────────
@@ -229,14 +205,15 @@ export class TemplParser extends Parser {
229
205
  !this.check(T.RBRACE) &&
230
206
  !this.check(T.EOF)
231
207
  ) {
232
- if (this.check(TT.TEMPL_CASE)) {
208
+ if (this.check(TT.TEMPL_CASE) || this.check(TT.TEMPL_DEFAULT)) {
209
+ const isDefault = this.check(TT.TEMPL_DEFAULT);
233
210
  const caseTok = this.advance();
234
211
  const body = this.parseTemplCaseBody();
235
- cases.push({ kind: "TemplCase", caseTokens: caseTok.value, body });
236
- } else if (this.check(TT.TEMPL_DEFAULT)) {
237
- this.advance();
238
- const body = this.parseTemplCaseBody();
239
- cases.push({ kind: "TemplCase", caseTokens: null, body });
212
+ cases.push({
213
+ kind: "TemplCase",
214
+ caseTokens: isDefault ? null : caseTok.value,
215
+ body,
216
+ });
240
217
  } else {
241
218
  this.advance(); // skip unexpected
242
219
  }
@@ -182,24 +182,81 @@ export const assignabilityMethods = {
182
182
  // Tags struct values / struct pointers flowing into an interface so codegen can
183
183
  // tell `T` from `*T` at runtime (both compile to the same class instance).
184
184
  _markIfaceBox(target, source, node) {
185
- if (this._skipIfaceMark) return;
186
- if (!node || typeof node !== "object" || !target || !source) return;
185
+ if (
186
+ this._skipIfaceMark ||
187
+ !node ||
188
+ typeof node !== "object" ||
189
+ !target ||
190
+ !source
191
+ )
192
+ return;
187
193
  if (target.kind === "typeParam") return;
188
194
  const tBase = this.resolveType(
189
195
  target.kind === "named" ? target.underlying : target,
190
196
  );
191
197
  if (!isAny(target) && tBase?.kind !== "interface") return;
192
- const structOf = (t) => {
193
- const r = this.resolveType(t);
194
- const b = r?.kind === "named" ? this.resolveType(r.underlying) : r;
195
- return b?.kind === "struct";
196
- };
197
198
  const sBase = this.resolveType(
198
199
  source.kind === "named" ? source.underlying : source,
199
200
  );
200
- if (sBase?.kind === "struct") node._ifaceBox = "value";
201
- else if (sBase?.kind === "pointer" && structOf(sBase.base))
202
- node._ifaceBox = "ptr";
201
+ const info = this._classifyIfaceBox(sBase);
202
+ if (!info) return;
203
+ node._ifaceBox = info.box;
204
+ if (info.ptrFields) node._ifacePtrFields = info.ptrFields;
205
+ },
206
+
207
+ _getStructPtrFields(st, prefix = "", seen = new Set()) {
208
+ if (st?.kind !== "struct" || !st?.fields) return [];
209
+ if (seen.has(st)) return [];
210
+ const nextSeen = new Set(seen);
211
+ nextSeen.add(st);
212
+ const ptrs = [];
213
+ for (const [fname, ftype] of st.fields.entries()) {
214
+ const full = prefix ? `${prefix}.${fname}` : fname;
215
+ const rf = this.resolveType(ftype);
216
+ const bf = rf?.kind === "named" ? this.resolveType(rf.underlying) : rf;
217
+ if (isPointer(bf)) {
218
+ ptrs.push(full);
219
+ } else if (bf?.kind === "struct") {
220
+ ptrs.push(...this._getStructPtrFields(bf, full, nextSeen));
221
+ }
222
+ }
223
+ return ptrs;
224
+ },
225
+
226
+ _classifyIfaceBox(sBase) {
227
+ if (sBase?.kind === "struct") {
228
+ const pf = this._getStructPtrFields(sBase);
229
+ return { box: "value", ptrFields: pf.length > 0 ? pf : null };
230
+ }
231
+ if (sBase?.kind === "pointer") {
232
+ const st = this.resolveType(
233
+ sBase.base?.kind === "named" ? sBase.base.underlying : sBase.base,
234
+ );
235
+ if (st?.kind === "struct") {
236
+ const pf = this._getStructPtrFields(st);
237
+ return { box: "ptr", ptrFields: pf.length > 0 ? pf : null };
238
+ }
239
+ return null;
240
+ }
241
+ if (sBase?.kind === "map") {
242
+ const valType = this.resolveType(sBase.value ?? sBase.val ?? sBase.elem);
243
+ const keyType = this.resolveType(sBase.key);
244
+ return {
245
+ box: isPointer(valType) || isPointer(keyType) ? "ptr_map" : "map",
246
+ };
247
+ }
248
+ if (sBase?.kind === "slice") {
249
+ const elemType = this.resolveType(sBase.elem);
250
+ if (isPointer(elemType)) return { box: "ptr_slice" };
251
+ const elemBase =
252
+ elemType?.kind === "named"
253
+ ? this.resolveType(elemType.underlying)
254
+ : elemType;
255
+ const pf =
256
+ elemBase?.kind === "struct" ? this._getStructPtrFields(elemBase) : [];
257
+ return pf.length > 0 ? { box: "slice", ptrFields: pf } : null;
258
+ }
259
+ return null;
203
260
  },
204
261
 
205
262
  // A js package cannot provide an implementation of a wasm interface: the
@@ -371,12 +428,23 @@ export const assignabilityMethods = {
371
428
  return true;
372
429
  },
373
430
 
431
+ _unpackReturns(rList) {
432
+ if (
433
+ rList.length === 1 &&
434
+ (rList[0]?.kind === "tuple" || rList[0]?.kind === "TupleType") &&
435
+ rList[0].types
436
+ ) {
437
+ return rList[0].types;
438
+ }
439
+ return rList;
440
+ },
441
+
374
442
  _implementsMethod(required, actual) {
375
443
  if (!actual) return false;
376
444
  const rp = required.params ?? [],
377
445
  ap = actual.params ?? [];
378
- const rr = required.returns ?? [],
379
- ar = actual.returns ?? [];
446
+ const rr = this._unpackReturns(required.returns ?? []),
447
+ ar = this._unpackReturns(actual.returns ?? []);
380
448
  if (!this._sigParamsMatch(rp, ap)) return false;
381
449
  if (!!required.variadic !== !!actual.variadic) return false;
382
450
  return this._sigParamsMatch(rr, ar);
@@ -398,7 +466,7 @@ export const assignabilityMethods = {
398
466
  }
399
467
  const methodMap =
400
468
  base?.kind === "struct"
401
- ? base.methods
469
+ ? base.methods || srcType.methods
402
470
  : srcType.kind === "named"
403
471
  ? srcType.methods
404
472
  : null;
@@ -266,6 +266,16 @@ export const expressionCheckMethods = {
266
266
  if (this._isStructOrNamedMethod(baseType, base, expr.field))
267
267
  expr._isMethodValue = true;
268
268
  }
269
+ if (
270
+ baseType?.kind === "namespace" &&
271
+ this.types.has(`${baseType.name}.${expr.field}`) &&
272
+ !(
273
+ baseType.name === "binary" &&
274
+ (expr.field === "LittleEndian" || expr.field === "BigEndian")
275
+ )
276
+ ) {
277
+ expr._isTypeRef = true;
278
+ }
269
279
  return ft;
270
280
  },
271
281
 
@@ -464,6 +474,81 @@ export const expressionCheckMethods = {
464
474
  return targetType;
465
475
  },
466
476
 
477
+ _checkConversionValidity(srcType, targetType, expr) {
478
+ if (!srcType || !targetType || isAny(srcType) || isAny(targetType)) return;
479
+ const srcResolved = srcType.kind === "named" ? srcType.underlying : srcType;
480
+ const tgtResolved =
481
+ targetType.kind === "named" ? targetType.underlying : targetType;
482
+
483
+ if (isNumeric(targetType) && isComplex(srcType)) {
484
+ this.err(
485
+ `cannot convert ${typeStr(srcType)} to ${typeStr(targetType)} (use real() or imag())`,
486
+ expr,
487
+ );
488
+ return;
489
+ }
490
+ if (isComplex(targetType) && !isComplexOrNumeric(srcType)) {
491
+ this.err(
492
+ `cannot convert ${typeStr(srcType)} to ${typeStr(targetType)}`,
493
+ expr,
494
+ );
495
+ return;
496
+ }
497
+ if (this._isMutualArraySlice(tgtResolved, srcResolved)) {
498
+ this._checkElemTypeCompatible(
499
+ tgtResolved.elem,
500
+ srcResolved.elem,
501
+ srcType,
502
+ targetType,
503
+ expr,
504
+ );
505
+ return;
506
+ }
507
+ if (this._isInvalidArrayConv(tgtResolved, srcResolved, srcType)) {
508
+ this.err(
509
+ `cannot convert ${typeStr(srcType)} to ${typeStr(targetType)}`,
510
+ expr,
511
+ );
512
+ return;
513
+ }
514
+ if (typeStr(srcResolved) === typeStr(tgtResolved)) return;
515
+ if (isNumeric(srcResolved) && isNumeric(tgtResolved)) return;
516
+ if (
517
+ isString(srcResolved) &&
518
+ tgtResolved?.kind === "slice" &&
519
+ (tgtResolved.elem?.name === "byte" ||
520
+ tgtResolved.elem?.name === "rune" ||
521
+ tgtResolved.elem?.name === "uint8" ||
522
+ tgtResolved.elem?.name === "int32")
523
+ )
524
+ return;
525
+ if (
526
+ isString(tgtResolved) &&
527
+ (isNumeric(srcResolved) ||
528
+ (srcResolved?.kind === "slice" &&
529
+ (srcResolved.elem?.name === "byte" ||
530
+ srcResolved.elem?.name === "rune" ||
531
+ srcResolved.elem?.name === "uint8" ||
532
+ srcResolved.elem?.name === "int32")))
533
+ )
534
+ return;
535
+ if (srcResolved?.kind === "pointer" && tgtResolved?.kind === "pointer") {
536
+ const sBase =
537
+ srcResolved.base?.kind === "named"
538
+ ? srcResolved.base.underlying
539
+ : srcResolved.base;
540
+ const tBase =
541
+ tgtResolved.base?.kind === "named"
542
+ ? tgtResolved.base.underlying
543
+ : tgtResolved.base;
544
+ if (typeStr(sBase) === typeStr(tBase)) return;
545
+ }
546
+ this.err(
547
+ `cannot convert ${typeStr(srcType)} to ${typeStr(targetType)}`,
548
+ expr,
549
+ );
550
+ },
551
+
467
552
  _checkTypeAssertExpr(expr, scope) {
468
553
  const srcType = this.checkExpr(expr.expr, scope);
469
554
  const targetType = this.resolveTypeNode(expr.type, scope);
@@ -629,6 +714,14 @@ export const expressionCheckMethods = {
629
714
  if (fnType.kind === "named" && expr.func._isTypeRef) {
630
715
  expr._isTypeConversion = true;
631
716
  expr._conversionTargetType = fnType;
717
+ if (expr.args.length !== 1) {
718
+ return this.err(
719
+ `cannot convert ${expr.args.length} arguments to type ${typeStr(fnType)}`,
720
+ expr,
721
+ );
722
+ }
723
+ const argType = argTypes[0];
724
+ this._checkConversionValidity(argType, fnType, expr);
632
725
  return fnType;
633
726
  }
634
727
 
@@ -702,6 +795,8 @@ export const expressionCheckMethods = {
702
795
  switch (mode) {
703
796
  case "arg0":
704
797
  return under ? arg : null;
798
+ case "elemOfArg0":
799
+ return under?.elem ?? null;
705
800
  case "keysOfArg0":
706
801
  return under?.key ? { kind: "slice", elem: under.key } : null;
707
802
  case "valuesOfArg0": {
@@ -755,13 +850,18 @@ export const expressionCheckMethods = {
755
850
 
756
851
  _checkCallArgs(fnType, argTypes, expr) {
757
852
  this._checkArgCount(fnType, argTypes, expr);
758
- for (let i = 0; i < fnType.params.length && i < argTypes.length; i++) {
853
+ const maxLen = fnType.variadic
854
+ ? argTypes.length
855
+ : Math.min(fnType.params.length, argTypes.length);
856
+ for (let i = 0; i < maxLen; i++) {
759
857
  const paramIdx =
760
858
  fnType.variadic && i >= fnType.params.length - 1
761
859
  ? fnType.params.length - 1
762
860
  : i;
763
861
  if (expr.args[i]?._spread) continue;
764
- this.assertAssignable(fnType.params[paramIdx], argTypes[i], expr.args[i]);
862
+ const targetParam = fnType.params[paramIdx];
863
+ const node = expr._multiForward ? null : expr.args[i];
864
+ this.assertAssignable(targetParam, argTypes[i], node);
765
865
  }
766
866
  },
767
867
 
@@ -97,20 +97,29 @@ export const resolveMethods = {
97
97
  }
98
98
  return this.err(`Unknown type '${node.name}'`, node);
99
99
  }
100
- case "GenericTypeName": {
101
- const base = this.types.get(node.name);
102
- if (!base) return this.err(`Unknown type '${node.name}'`, node);
100
+ case "GenericTypeName":
101
+ case "InstantiationExpr":
102
+ case "IndexExpr": {
103
+ const name =
104
+ node.name ?? (node.expr?.kind === "Ident" ? node.expr.name : null);
105
+ const typeArgs = node.typeArgs ?? (node.index ? [node.index] : []);
106
+ const base = name
107
+ ? this.types.get(name)
108
+ : node.expr
109
+ ? this.resolveTypeNode(node.expr, scope)
110
+ : null;
111
+ if (!base) return this.err(`Unknown type '${name ?? "?"}'`, node);
103
112
  if (base.kind === "named" && base._generic) {
104
113
  const res = this.instantiateGenericType(
105
114
  base._generic,
106
- node.typeArgs,
115
+ typeArgs,
107
116
  scope,
108
117
  );
109
118
  node._typeArgs = res.typeArgs;
110
119
  node._genericType = base;
111
120
  return res;
112
121
  }
113
- return this.err(`Type '${node.name}' is not generic`, node);
122
+ return this.err(`Type '${name ?? "?"}' is not generic`, node);
114
123
  }
115
124
  case "TypeParam": {
116
125
  const constraint = node.constraint
@@ -291,6 +300,7 @@ export const resolveMethods = {
291
300
  name: generic.declNode.name,
292
301
  typeArgs,
293
302
  underlying: instantiated,
303
+ methods: instantiated.methods,
294
304
  };
295
305
  },
296
306
 
@@ -381,6 +391,14 @@ export const resolveMethods = {
381
391
  const found = this.types.get(t.alias);
382
392
  if (found) return found;
383
393
  }
394
+ if (
395
+ t.kind === "named" &&
396
+ (!t.typeArgs || t.typeArgs.length === 0) &&
397
+ (!t.underlying || !t.methods)
398
+ ) {
399
+ const found = this.types.get(t.name);
400
+ if (found) return found;
401
+ }
384
402
  return t;
385
403
  },
386
404
 
@@ -432,7 +450,19 @@ export const resolveMethods = {
432
450
  },
433
451
 
434
452
  _fieldTypeStruct(base, baseType, field, node) {
435
- if (base.fields?.has(field)) return base.fields.get(field);
453
+ if (base.fields?.has(field)) {
454
+ if (
455
+ field.startsWith("_") &&
456
+ (baseType?.name?.startsWith("strings.") ||
457
+ baseType?.name?.startsWith("bytes."))
458
+ ) {
459
+ return this.err(
460
+ `Cannot refer to unexported field '${field}' on ${typeStr(baseType)}`,
461
+ node,
462
+ );
463
+ }
464
+ return base.fields.get(field);
465
+ }
436
466
  if (base.methods?.has(field)) return base.methods.get(field);
437
467
  return this.err(`No field '${field}' on ${typeStr(baseType)}`, node);
438
468
  },