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.
- package/README.md +14 -14
- package/package.json +10 -38
- package/src/backend/js/expressions.js +2060 -389
- package/src/backend/js/index.js +586 -174
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +936 -1644
- package/src/backend/js/statements.js +1349 -132
- package/src/backend/js/stdlib/index.js +51 -94
- package/src/backend/js/templ.js +67 -118
- package/src/backend/js/type-info.js +436 -0
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +80 -0
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +753 -872
- package/src/backend/wasm/emit-builtins.js +914 -237
- package/src/backend/wasm/emit-exprs.js +1456 -195
- package/src/backend/wasm/emit-maps.js +78 -380
- package/src/backend/wasm/emit-stmts.js +1659 -214
- package/src/backend/wasm/emit.js +614 -88
- package/src/backend/wasm/encode.js +43 -13
- package/src/backend/wasm/globals.js +491 -0
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +406 -3195
- package/src/backend/wasm/method-expr.js +392 -0
- package/src/backend/wasm/module-emitter.js +1615 -0
- package/src/backend/wasm/monomorph.js +164 -9
- package/src/backend/wasm/natives.js +596 -0
- package/src/backend/wasm/prescan.js +249 -0
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -37
- package/src/backend/wasm/stdlib/simd.js +293 -28
- package/src/backend/wasm/types.js +260 -27
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +47 -20
- package/src/{cli-core.js → cli/cli-core.js} +241 -11
- package/src/{test-runner.js → cli/test-runner.js} +188 -122
- package/src/{vendor.js → cli/vendor.js} +3 -4
- package/src/compiler.js +202 -386
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +203 -62
- package/src/{templ-lexer.js → lexer/templ-lexer.js} +61 -18
- package/src/{tokens.js → lexer/tokens.js} +2 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +737 -0
- package/src/link/type-names.js +148 -0
- package/src/link/wasm.js +318 -0
- package/src/lower/bounds.js +547 -0
- package/src/lower/boxing.js +1 -1
- package/src/lower/captures.js +267 -64
- package/src/lower/embedding.js +32 -3
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +905 -0
- package/src/lower/functions.js +2 -0
- package/src/lower/gen-types.js +274 -0
- package/src/lower/index.js +33 -19
- package/src/lower/json-codecs.js +1379 -0
- package/src/lower/ownership.js +47 -8
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +150 -0
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +83 -15
- package/src/parser/expressions.js +110 -18
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +38 -22
- package/src/{templ-parser.js → parser/templ.js} +179 -17
- package/src/parser/types.js +70 -9
- package/src/resolver.js +21 -1
- package/src/typechecker/assignability.js +291 -19
- package/src/typechecker/const-fold.js +399 -0
- package/src/typechecker/constants.js +282 -0
- package/src/typechecker/expressions.js +1220 -183
- package/src/typechecker/index.js +659 -92
- package/src/typechecker/init-order.js +426 -0
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +613 -62
- package/src/typechecker/statements.js +212 -20
- package/src/typechecker/stdlib/excluded.js +902 -0
- package/src/typechecker/stdlib/index.js +38 -0
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +11 -2
- package/src/typechecker/types.js +268 -514
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +871 -0
- package/stdlib/internal/jsonrt/enc.go +516 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +658 -0
- package/stdlib/io/io.go +502 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +272 -0
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +500 -0
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +526 -0
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +250 -0
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -46
- package/src/backend/js/stdlib/builder.js +0 -63
- package/src/backend/js/stdlib/bytes.js +0 -122
- package/src/backend/js/stdlib/errors.js +0 -44
- package/src/backend/js/stdlib/fmt.js +0 -178
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -972
- package/src/backend/js/stdlib/maps.js +0 -45
- package/src/backend/js/stdlib/math.js +0 -69
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -24
- package/src/backend/js/stdlib/slices.js +0 -77
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -74
- package/src/backend/js/stdlib/strings.js +0 -222
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -34
- package/src/backend/js/stdlib/time.js +0 -128
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- package/src/backend/wasm/stdlib/bytes.js +0 -5039
- package/src/backend/wasm/stdlib/core.js +0 -8903
- package/src/backend/wasm/stdlib/io.js +0 -2161
- package/src/backend/wasm/stdlib/packages.js +0 -2400
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -1200
- package/src/typechecker/stdlib/core.js +0 -801
- package/src/typechecker/stdlib/extended.js +0 -914
- package/src/typechecker/stdlib.js +0 -22
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /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
|
-
|
|
71
|
-
if (
|
|
72
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
333
|
-
|
|
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 === "
|
|
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);
|