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.
- package/package.json +1 -1
- package/src/backend/js/expressions.js +238 -109
- package/src/backend/js/index.js +38 -1
- package/src/backend/js/runtime.js +953 -53
- package/src/backend/js/statements.js +55 -67
- package/src/backend/js/stdlib/base64.js +12 -0
- package/src/backend/js/stdlib/binary.js +102 -0
- package/src/backend/js/stdlib/bits.js +15 -1
- package/src/backend/js/stdlib/builder.js +28 -9
- package/src/backend/js/stdlib/bytes.js +75 -18
- package/src/backend/js/stdlib/errors.js +20 -1
- package/src/backend/js/stdlib/fmt.js +70 -13
- package/src/backend/js/stdlib/hex.js +17 -0
- package/src/backend/js/stdlib/html.js +1 -1
- package/src/backend/js/stdlib/index.js +16 -0
- package/src/backend/js/stdlib/io.js +105 -3
- package/src/backend/js/stdlib/math.js +13 -1
- package/src/backend/js/stdlib/path.js +18 -4
- package/src/backend/js/stdlib/rand.js +10 -5
- package/src/backend/js/stdlib/sha256.js +12 -0
- package/src/backend/js/stdlib/slices.js +52 -26
- package/src/backend/js/stdlib/strconv.js +21 -5
- package/src/backend/js/stdlib/strings.js +148 -15
- package/src/backend/js/stdlib/sync.js +13 -0
- package/src/backend/js/stdlib/time.js +90 -19
- package/src/backend/js/stdlib/unicode.js +2 -2
- package/src/backend/wasm/boundary.js +1207 -41
- package/src/backend/wasm/const-fold.js +205 -0
- package/src/backend/wasm/emit-builtins.js +203 -76
- package/src/backend/wasm/emit-exprs.js +464 -276
- package/src/backend/wasm/emit-maps.js +144 -8
- package/src/backend/wasm/emit-stdlib.js +12 -3005
- package/src/backend/wasm/emit-stmts.js +264 -270
- package/src/backend/wasm/emit.js +115 -87
- package/src/backend/wasm/encode.js +55 -29
- package/src/backend/wasm/index.js +1001 -32
- package/src/backend/wasm/optimize.js +13 -4
- package/src/backend/wasm/stdlib/bytes.js +5039 -0
- package/src/backend/wasm/stdlib/core.js +8903 -0
- package/src/backend/wasm/stdlib/index.js +40 -0
- package/src/backend/wasm/stdlib/io.js +2161 -0
- package/src/backend/wasm/stdlib/packages.js +2400 -0
- package/src/backend/wasm/stdlib/pure.js +720 -0
- package/src/backend/wasm/stdlib/time.js +1200 -0
- package/src/backend/wasm/types.js +153 -1
- package/src/cli-core.js +1 -1
- package/src/compiler.js +18 -4
- package/src/dts-parser.js +79 -126
- package/src/index.js +2 -6
- package/src/lexer.js +80 -29
- package/src/lower/boxing.js +1 -2
- package/src/lower/captures.js +2 -6
- package/src/lower/ownership.js +30 -9
- package/src/minifier.js +20 -68
- package/src/parser/declarations.js +3 -6
- package/src/parser/expressions.js +3 -8
- package/src/parser/index.js +18 -3
- package/src/parser/statements.js +25 -38
- package/src/parser/types.js +1 -2
- package/src/resolver.js +2 -27
- package/src/templ-lexer.js +29 -63
- package/src/templ-parser.js +22 -45
- package/src/typechecker/assignability.js +81 -13
- package/src/typechecker/expressions.js +102 -2
- package/src/typechecker/resolve.js +36 -6
- package/src/typechecker/stdlib/core.js +280 -89
- package/src/typechecker/stdlib/extended.js +453 -1
- package/src/typechecker/types.js +344 -6
package/src/templ-lexer.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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) { <
|
|
208
|
-
|
|
204
|
+
// Called right after consuming `templ` or `css` keyword.
|
|
205
|
+
// Lexes: Name(params) { <body> }
|
|
206
|
+
_lexBlockDecl(kw) {
|
|
209
207
|
this.skipWhitespace();
|
|
210
208
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
235
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
645
|
+
this.advance();
|
|
678
646
|
}
|
|
679
|
-
const trimmed =
|
|
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
|
-
|
|
733
|
-
while (!this.eof() && /[a-zA-Z0-9_-]/.test(this.peek()))
|
|
734
|
-
|
|
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
|
-
|
|
740
|
-
while (!this.eof() && /[a-zA-Z0-9_:.-]/.test(this.peek()))
|
|
741
|
-
|
|
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
|
-
|
|
760
|
-
while (!this.eof() && /\w/.test(this.peek()))
|
|
761
|
-
return
|
|
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() {
|
package/src/templ-parser.js
CHANGED
|
@@ -48,55 +48,31 @@ export class TemplParser extends Parser {
|
|
|
48
48
|
|
|
49
49
|
// ── CssDecl ───────────────────────────────────────────────────
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
_parseDecl(kind, bodyFn) {
|
|
52
52
|
const _line = this.peek().line;
|
|
53
|
-
this.advance();
|
|
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
|
-
|
|
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
|
-
|
|
81
|
-
|
|
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({
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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 (
|
|
186
|
-
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
102
|
-
|
|
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
|
-
|
|
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 '${
|
|
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))
|
|
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
|
},
|