gofront 1.7.0 → 1.8.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 +13 -13
- package/package.json +8 -37
- package/src/backend/js/expressions.js +1341 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +375 -57
- package/src/backend/js/runtime.js +1460 -133
- package/src/backend/js/statements.js +900 -86
- package/src/backend/js/stdlib/bits.js +87 -14
- package/src/backend/js/stdlib/builder.js +41 -6
- package/src/backend/js/stdlib/bytes.js +253 -7
- package/src/backend/js/stdlib/errors.js +2 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +8 -3
- package/src/backend/js/stdlib/json.js +247 -52
- package/src/backend/js/stdlib/maps.js +0 -8
- package/src/backend/js/stdlib/math.js +17 -1
- package/src/backend/js/stdlib/simd.js +10 -0
- package/src/backend/js/stdlib/slices.js +36 -6
- package/src/backend/js/stdlib/strconv.js +10 -5
- package/src/backend/js/stdlib/strings.js +83 -4
- package/src/backend/js/stdlib/testing.js +7 -0
- package/src/backend/js/stdlib/time.js +79 -55
- package/src/backend/js/stdlib/unicode.js +1 -1
- package/src/backend/js/stdlib/utf8.js +1 -1
- package/src/backend/js/templ.js +38 -76
- package/src/backend/js/type-info.js +272 -0
- package/src/backend/shared/fmt-types.js +188 -0
- package/src/backend/wasm/boundary.js +436 -80
- package/src/backend/wasm/emit-builtins.js +303 -13
- package/src/backend/wasm/emit-exprs.js +1397 -203
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +512 -77
- package/src/backend/wasm/encode.js +43 -13
- package/src/backend/wasm/globals.js +452 -0
- package/src/backend/wasm/index.js +194 -2489
- package/src/backend/wasm/method-expr.js +381 -0
- package/src/backend/wasm/module-emitter.js +2240 -0
- package/src/backend/wasm/monomorph.js +62 -4
- package/src/backend/wasm/prescan.js +397 -0
- package/src/backend/wasm/stdlib/bytes.js +1804 -127
- package/src/backend/wasm/stdlib/core.js +1574 -199
- package/src/backend/wasm/stdlib/index.js +0 -5
- package/src/backend/wasm/stdlib/io.js +1567 -150
- package/src/backend/wasm/stdlib/packages.js +2 -0
- package/src/backend/wasm/stdlib/simd.js +293 -28
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +200 -15
- 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} +236 -10
- package/src/{test-runner.js → cli/test-runner.js} +332 -39
- package/src/{vendor.js → cli/vendor.js} +3 -4
- package/src/compiler.js +107 -269
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +102 -55
- package/src/{tokens.js → lexer/tokens.js} +1 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +515 -0
- package/src/link/wasm.js +294 -0
- package/src/lower/bounds.js +394 -0
- package/src/lower/boxing.js +1 -1
- package/src/lower/captures.js +176 -32
- package/src/lower/index.js +26 -18
- package/src/lower/json-codecs.js +1398 -0
- package/src/lower/ownership.js +22 -1
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +132 -0
- package/src/parser/declarations.js +58 -13
- package/src/parser/expressions.js +29 -7
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +37 -22
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +69 -9
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +176 -17
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +931 -122
- package/src/typechecker/index.js +459 -67
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +451 -56
- package/src/typechecker/statements.js +90 -9
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +322 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +149 -25
- package/stdlib/bytes/iter.go +192 -0
- package/stdlib/bytes/search.go +240 -0
- package/stdlib/bytes/split.go +218 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/overlay.go +12 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/internal/jsonrt/dec.go +738 -0
- package/stdlib/internal/jsonrt/enc.go +366 -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 +649 -0
- package/stdlib/io/io.go +346 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/iter.go +65 -0
- package/stdlib/math/extra.go +270 -0
- package/stdlib/slices/extra.go +73 -0
- package/stdlib/slices/iter.go +113 -0
- package/stdlib/sort/sort.go +561 -0
- package/stdlib/strconv/quote.go +341 -0
- package/stdlib/strings/extra.go +35 -0
- package/stdlib/strings/iter.go +122 -0
- package/stdlib/sync/extra.go +105 -0
- package/stdlib/unicode/unicode.go +112 -0
- package/stdlib/unicode/utf8/bytes.go +239 -0
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- /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
- /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
package/src/typechecker/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
// Split into sub-modules under typechecker/:
|
|
5
5
|
// types.js — shared type constants, predicates, Scope
|
|
6
|
-
// stdlib.js
|
|
6
|
+
// stdlib/index.js — browser globals + all built-in package registrations
|
|
7
7
|
// statements.js — checkBlock, checkStmt
|
|
8
8
|
// expressions.js — checkExpr, checkCall, checkBuiltin, checkCompositeLit
|
|
9
9
|
// termination.js — _isTerminating* family
|
|
@@ -11,15 +11,21 @@
|
|
|
11
11
|
// assignability.js — assertAssignable, binaryResultType, implements
|
|
12
12
|
|
|
13
13
|
import { assignabilityMethods } from "./assignability.js";
|
|
14
|
+
import { constantCheckMethods } from "./constants.js";
|
|
14
15
|
import { expressionCheckMethods } from "./expressions.js";
|
|
16
|
+
import { orderPackageInits, orderVarSpecsForTyping } from "./init-order.js";
|
|
15
17
|
import { resolveMethods } from "./resolve.js";
|
|
16
18
|
import { statementCheckMethods } from "./statements.js";
|
|
17
|
-
import {
|
|
19
|
+
import { EXCLUDED_STDLIB_PACKAGES } from "./stdlib/excluded.js";
|
|
20
|
+
import { setupGlobals } from "./stdlib/index.js";
|
|
18
21
|
import { terminationMethods } from "./termination.js";
|
|
19
22
|
import {
|
|
20
23
|
ANY,
|
|
21
24
|
defaultType,
|
|
22
25
|
ERROR,
|
|
26
|
+
INTRINSICS,
|
|
27
|
+
isAny,
|
|
28
|
+
isBool,
|
|
23
29
|
isVoid,
|
|
24
30
|
Scope,
|
|
25
31
|
TAINTED_ANY,
|
|
@@ -29,6 +35,16 @@ import {
|
|
|
29
35
|
VOID,
|
|
30
36
|
} from "./types.js";
|
|
31
37
|
|
|
38
|
+
// Checker method per templ body node kind (see _checkTemplNodes).
|
|
39
|
+
const TEMPL_NODE_CHECKS = {
|
|
40
|
+
TemplElement: "_checkTemplElement",
|
|
41
|
+
TemplExpr: "_checkTemplLeaf",
|
|
42
|
+
TemplComponent: "_checkTemplLeaf",
|
|
43
|
+
TemplIf: "_checkTemplIf",
|
|
44
|
+
TemplFor: "_checkTemplFor",
|
|
45
|
+
TemplSwitch: "_checkTemplSwitch",
|
|
46
|
+
};
|
|
47
|
+
|
|
32
48
|
function rootIdentName(e) {
|
|
33
49
|
if (!e) return null;
|
|
34
50
|
if (e.kind === "Ident") return e.name;
|
|
@@ -38,12 +54,56 @@ function rootIdentName(e) {
|
|
|
38
54
|
}
|
|
39
55
|
|
|
40
56
|
// Re-export for consumers that import from typechecker.js
|
|
41
|
-
export { TypeCheckError, typeStr };
|
|
57
|
+
export { EXCLUDED_STDLIB_PACKAGES, TypeCheckError, typeStr };
|
|
58
|
+
|
|
59
|
+
// JavaScript built-ins that generated JS relies on; a JS class of the same
|
|
60
|
+
// name in the bundle would shadow them (see _checkGlobalTypeNamesPass).
|
|
61
|
+
const JS_BUILTIN_CLASSES = new Set([
|
|
62
|
+
"Array",
|
|
63
|
+
"ArrayBuffer",
|
|
64
|
+
"BigInt",
|
|
65
|
+
"BigInt64Array",
|
|
66
|
+
"BigUint64Array",
|
|
67
|
+
"Boolean",
|
|
68
|
+
"DataView",
|
|
69
|
+
"Date",
|
|
70
|
+
"Error",
|
|
71
|
+
"Float32Array",
|
|
72
|
+
"Float64Array",
|
|
73
|
+
"Int16Array",
|
|
74
|
+
"Int32Array",
|
|
75
|
+
"Int8Array",
|
|
76
|
+
"JSON",
|
|
77
|
+
"Map",
|
|
78
|
+
"Math",
|
|
79
|
+
"Number",
|
|
80
|
+
"Object",
|
|
81
|
+
"Promise",
|
|
82
|
+
"RangeError",
|
|
83
|
+
"Reflect",
|
|
84
|
+
"RegExp",
|
|
85
|
+
"Set",
|
|
86
|
+
"String",
|
|
87
|
+
"Symbol",
|
|
88
|
+
"TextDecoder",
|
|
89
|
+
"TextEncoder",
|
|
90
|
+
"TypeError",
|
|
91
|
+
"Uint16Array",
|
|
92
|
+
"Uint32Array",
|
|
93
|
+
"Uint8Array",
|
|
94
|
+
"Uint8ClampedArray",
|
|
95
|
+
"WeakMap",
|
|
96
|
+
"WebAssembly",
|
|
97
|
+
]);
|
|
42
98
|
|
|
43
99
|
export class TypeChecker {
|
|
44
100
|
constructor() {
|
|
45
101
|
this.types = new Map(); // named types
|
|
46
|
-
|
|
102
|
+
// Go's universe scope: builtins, browser globals and built-in package
|
|
103
|
+
// namespaces. Package declarations live in `globals` above it, so they
|
|
104
|
+
// shadow predeclared names (`func Array()`, `var document int`).
|
|
105
|
+
this.universe = new Scope();
|
|
106
|
+
this.globals = new Scope(this.universe);
|
|
47
107
|
this.errors = [];
|
|
48
108
|
this.warnings = []; // non-fatal diagnostics (e.g. boundary-call-in-loop hints)
|
|
49
109
|
this.target = "js";
|
|
@@ -58,21 +118,34 @@ export class TypeChecker {
|
|
|
58
118
|
this._deferCount = 0; // tracks defer usage in current function body
|
|
59
119
|
this._imports = []; // tracked imports for unused-import detection
|
|
60
120
|
this._constExprs = new Map(); // name -> AST initializer expr
|
|
121
|
+
this._importedConsts = new Map(); // dot-imported const name -> value
|
|
122
|
+
this._externalNames = new Set(); // package-scope names from .d.ts / npm definitions
|
|
61
123
|
this._setupGlobals();
|
|
62
124
|
// Register error as a named type (interface)
|
|
63
125
|
this.types.set("error", ERROR);
|
|
64
126
|
}
|
|
65
127
|
|
|
66
128
|
_setupGlobals() {
|
|
67
|
-
setupGlobals(this.
|
|
129
|
+
setupGlobals(this.universe, this.types);
|
|
68
130
|
}
|
|
69
131
|
|
|
70
|
-
|
|
132
|
+
// `external` marks values as real JS names (.d.ts / npm definitions) that
|
|
133
|
+
// must not be renamed; dot-imported GoFront packages pass false so their
|
|
134
|
+
// browser-global-named members get the same `$` renaming as local ones.
|
|
135
|
+
// `consts` (dot-imported GoFront packages) maps constant names to their
|
|
136
|
+
// folded values.
|
|
137
|
+
addDefinitions(types, values, { external = true, consts = null } = {}) {
|
|
71
138
|
for (const [name, type] of types) {
|
|
72
139
|
this.types.set(name, type);
|
|
73
140
|
}
|
|
74
141
|
for (const [name, type] of values) {
|
|
75
142
|
this.globals.define(name, type);
|
|
143
|
+
if (external) this._externalNames.add(name);
|
|
144
|
+
}
|
|
145
|
+
for (const [name, v] of consts ?? []) {
|
|
146
|
+
if (!v || !values.has(name)) continue;
|
|
147
|
+
this.globals._consts.add(name);
|
|
148
|
+
this._importedConsts.set(name, v);
|
|
76
149
|
}
|
|
77
150
|
}
|
|
78
151
|
|
|
@@ -80,7 +153,9 @@ export class TypeChecker {
|
|
|
80
153
|
// e.g. addPackageNamespace('utils', symbolsMap, typesMap)
|
|
81
154
|
// lets callers type-check `utils.Foo` via SelectorExpr.
|
|
82
155
|
// `target` is the imported package's compile target ("js"/"wasm"/"both").
|
|
83
|
-
|
|
156
|
+
// `consts` maps the package's constant names to their folded value (see
|
|
157
|
+
// typechecker/const-fold.js), or null when the folder cannot evaluate one.
|
|
158
|
+
addPackageNamespace(pkgName, symbols, types, target = null, consts = null) {
|
|
84
159
|
const members = {};
|
|
85
160
|
for (const [name, type] of symbols) members[name] = type;
|
|
86
161
|
// _gofront: true marks this as a GoFront package — exported identifier rules apply
|
|
@@ -90,8 +165,15 @@ export class TypeChecker {
|
|
|
90
165
|
members,
|
|
91
166
|
_gofront: true,
|
|
92
167
|
_target: target,
|
|
168
|
+
_consts: consts,
|
|
93
169
|
});
|
|
94
170
|
for (const [name, type] of types) {
|
|
171
|
+
const prior = this.types.get(name);
|
|
172
|
+
if (prior && prior !== type && prior.kind === "named") {
|
|
173
|
+
this._ambiguousImported ??= new WeakSet();
|
|
174
|
+
this._ambiguousImported.add(prior);
|
|
175
|
+
this._ambiguousImported.add(type);
|
|
176
|
+
}
|
|
95
177
|
this.types.set(name, type);
|
|
96
178
|
this.types.set(`${pkgName}.${name}`, type);
|
|
97
179
|
}
|
|
@@ -108,7 +190,7 @@ export class TypeChecker {
|
|
|
108
190
|
// Report any tracked imports whose package name was never referenced.
|
|
109
191
|
reportUnusedImports() {
|
|
110
192
|
for (const { name, node, filename, source } of this._imports) {
|
|
111
|
-
if (!this.globals._used.has(name)) {
|
|
193
|
+
if (!this.globals._used.has(name) && !this.universe._used.has(name)) {
|
|
112
194
|
const saved = [this._currentFile, this._currentSource];
|
|
113
195
|
this._currentFile = filename;
|
|
114
196
|
this._currentSource = source;
|
|
@@ -130,6 +212,15 @@ export class TypeChecker {
|
|
|
130
212
|
return out;
|
|
131
213
|
}
|
|
132
214
|
|
|
215
|
+
getExportedConsts() {
|
|
216
|
+
const out = new Map();
|
|
217
|
+
for (const name of this.globals._consts) {
|
|
218
|
+
if (!this.globals.lookupConstExpr(name)) continue;
|
|
219
|
+
out.set(name, this._constValue({ kind: "Ident", name }, this.globals));
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
|
|
133
224
|
getExportedTypes() {
|
|
134
225
|
return new Map(this.types);
|
|
135
226
|
}
|
|
@@ -269,11 +360,15 @@ export class TypeChecker {
|
|
|
269
360
|
}
|
|
270
361
|
}
|
|
271
362
|
|
|
363
|
+
this._checkDirectivesAndImportsPass(programs);
|
|
364
|
+
this._registerConstExprs(programs);
|
|
272
365
|
this._collectTypesPass(programs);
|
|
273
366
|
this._collectFuncsPass(programs);
|
|
367
|
+
this._checkGlobalTypeNamesPass(programs);
|
|
274
368
|
this._promoteEmbeddedMethods();
|
|
275
369
|
this._collectVarsConstsPass(programs);
|
|
276
370
|
this._checkTopDeclsPass(programs);
|
|
371
|
+
this._reportInitCycles(programs);
|
|
277
372
|
|
|
278
373
|
if (this.target === "both") {
|
|
279
374
|
this._checkMutablePackageVars(programs);
|
|
@@ -290,6 +385,42 @@ export class TypeChecker {
|
|
|
290
385
|
return this.errors;
|
|
291
386
|
}
|
|
292
387
|
|
|
388
|
+
_checkEmbedDirectives(directives) {
|
|
389
|
+
for (const { line, col } of directives ?? []) {
|
|
390
|
+
this.err("//go:embed is not supported yet", { line, col });
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
_checkDirectivesAndImportsPass(programs) {
|
|
395
|
+
this._importedPaths = new Set();
|
|
396
|
+
this._fileImports = new Map();
|
|
397
|
+
for (const p of programs) {
|
|
398
|
+
this._setCurrentFile(p);
|
|
399
|
+
const names = new Map();
|
|
400
|
+
this._fileImports.set(this._currentFile, names);
|
|
401
|
+
this._checkEmbedDirectives(p._embedDirectives);
|
|
402
|
+
for (const imp of p.imports ?? []) {
|
|
403
|
+
for (const spec of imp.imports ?? []) {
|
|
404
|
+
this._importedPaths.add(spec.path);
|
|
405
|
+
names.set(
|
|
406
|
+
spec.alias ??
|
|
407
|
+
spec.path
|
|
408
|
+
.split("/")
|
|
409
|
+
.filter((s) => !/^v\d+$/.test(s))
|
|
410
|
+
.at(-1),
|
|
411
|
+
spec.path,
|
|
412
|
+
);
|
|
413
|
+
if (EXCLUDED_STDLIB_PACKAGES.has(spec.path)) {
|
|
414
|
+
this.err(
|
|
415
|
+
`package '${spec.path}' is not available: ${EXCLUDED_STDLIB_PACKAGES.get(spec.path)}`,
|
|
416
|
+
spec,
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
293
424
|
_checkMutablePackageVars(programs) {
|
|
294
425
|
const pkgVars = new Set();
|
|
295
426
|
for (const p of programs) {
|
|
@@ -409,6 +540,18 @@ export class TypeChecker {
|
|
|
409
540
|
for (const d of p.decls) {
|
|
410
541
|
if (d.kind === "TypeDecl" && !d.isAlias && !this.types.has(d.name)) {
|
|
411
542
|
const placeholder = { kind: "named", name: d.name, underlying: null };
|
|
543
|
+
// A generic type is known to be generic before any field resolves,
|
|
544
|
+
// so types declared earlier can instantiate it.
|
|
545
|
+
if (d.typeParams) {
|
|
546
|
+
placeholder._generic = {
|
|
547
|
+
typeParams: d.typeParams.map((tp) => ({
|
|
548
|
+
kind: "typeParam",
|
|
549
|
+
name: tp.name,
|
|
550
|
+
constraint: tp.constraint,
|
|
551
|
+
})),
|
|
552
|
+
declNode: d,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
412
555
|
this.types.set(d.name, placeholder);
|
|
413
556
|
this.globals.define(d.name, placeholder);
|
|
414
557
|
}
|
|
@@ -418,6 +561,7 @@ export class TypeChecker {
|
|
|
418
561
|
for (const p of programs) {
|
|
419
562
|
for (const d of p.decls) if (d.kind === "TypeDecl") all.push({ p, d });
|
|
420
563
|
}
|
|
564
|
+
this._expandGenericStructDefs(all.map((x) => x.d));
|
|
421
565
|
// Collect in dependency order so aliases, generics, embedded and by-value
|
|
422
566
|
// field types declared later (or in other files) are ready when needed.
|
|
423
567
|
let pending = all;
|
|
@@ -436,6 +580,57 @@ export class TypeChecker {
|
|
|
436
580
|
}
|
|
437
581
|
}
|
|
438
582
|
|
|
583
|
+
// `type S[T any] Base` with `Base` a struct declared in the package: S is a
|
|
584
|
+
// new struct type with Base's fields (and none of its methods), so its
|
|
585
|
+
// declaration becomes a copy of Base's struct type.
|
|
586
|
+
_expandGenericStructDefs(decls) {
|
|
587
|
+
const byName = new Map(decls.map((d) => [d.name, d]));
|
|
588
|
+
for (const d of decls) {
|
|
589
|
+
if (!d.typeParams || d.isAlias) continue;
|
|
590
|
+
let target = d.type;
|
|
591
|
+
for (let i = 0; i < 16 && target?.kind === "TypeName"; i++) {
|
|
592
|
+
const base = byName.get(target.name);
|
|
593
|
+
if (!base || base.typeParams || base.isAlias) break;
|
|
594
|
+
target = base.type;
|
|
595
|
+
}
|
|
596
|
+
// A struct written in place is already this declaration's own copy;
|
|
597
|
+
// cloning it would also choke on an AST a previous target pass
|
|
598
|
+
// annotated with cycles.
|
|
599
|
+
if (target?.kind === "StructType" && target !== d.type)
|
|
600
|
+
d.type = JSON.parse(JSON.stringify(target));
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// A struct type, or a named type with methods, becomes a JS class of the
|
|
605
|
+
// same name in `js` and `both` packages. Functions and variables that
|
|
606
|
+
// shadow a browser global get a `$` suffix, but type names reach the
|
|
607
|
+
// output through the checker's types (class declarations, `new T`,
|
|
608
|
+
// `instanceof T`, static methods), so a class named after a JavaScript
|
|
609
|
+
// built-in the generated code calls (`Array.isArray`, `new Error`) is
|
|
610
|
+
// rejected instead. Interfaces and method-less named types emit no class.
|
|
611
|
+
_checkGlobalTypeNamesPass(programs) {
|
|
612
|
+
if (this.target === "wasm" || this.isStdlib) return;
|
|
613
|
+
for (const p of programs) {
|
|
614
|
+
for (const d of p.decls) {
|
|
615
|
+
if (
|
|
616
|
+
d.kind !== "TypeDecl" ||
|
|
617
|
+
d.isAlias ||
|
|
618
|
+
!JS_BUILTIN_CLASSES.has(d.name)
|
|
619
|
+
)
|
|
620
|
+
continue;
|
|
621
|
+
const t = this.types.get(d.name);
|
|
622
|
+
const isClass =
|
|
623
|
+
t?.underlying?.kind === "struct" || (t?.methods?.size ?? 0) > 0;
|
|
624
|
+
if (!isClass) continue;
|
|
625
|
+
this._setCurrentFile(p);
|
|
626
|
+
this.err(
|
|
627
|
+
`type ${d.name} shadows the JavaScript global ${d.name}; rename the type`,
|
|
628
|
+
d,
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
439
634
|
_typeDeclBlocked(decl, pendingNames) {
|
|
440
635
|
const visit = (node, indirect) => {
|
|
441
636
|
if (!node || typeof node !== "object") return false;
|
|
@@ -475,6 +670,22 @@ export class TypeChecker {
|
|
|
475
670
|
}
|
|
476
671
|
}
|
|
477
672
|
|
|
673
|
+
// Constant initializers are known before types are resolved, so an array
|
|
674
|
+
// length may name a constant declared later (`[N]int`, `[N + 1]T`).
|
|
675
|
+
_registerConstExprs(programs) {
|
|
676
|
+
for (const p of programs) {
|
|
677
|
+
for (const d of p.decls) {
|
|
678
|
+
if (d.kind !== "ConstDecl") continue;
|
|
679
|
+
for (const spec of d.decls) {
|
|
680
|
+
spec.names.forEach((name, i) => {
|
|
681
|
+
const expr = spec.value?.[i] ?? spec.value?.[0] ?? null;
|
|
682
|
+
if (expr) this.globals._constExprs.set(name, expr);
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
478
689
|
_collectVarsConstsPass(programs) {
|
|
479
690
|
for (const p of programs) {
|
|
480
691
|
this._setCurrentFile(p);
|
|
@@ -485,14 +696,30 @@ export class TypeChecker {
|
|
|
485
696
|
}
|
|
486
697
|
}
|
|
487
698
|
|
|
699
|
+
// Go rejects package variables whose initialisers depend on each other.
|
|
700
|
+
_reportInitCycles(programs) {
|
|
701
|
+
if (this.errors.length > 0) return;
|
|
702
|
+
const { cycles } = orderPackageInits(programs);
|
|
703
|
+
for (const c of cycles) {
|
|
704
|
+
const owner = programs.find((p) =>
|
|
705
|
+
p.decls?.some((d) => d.kind === "VarDecl" && d.decls?.includes(c.spec)),
|
|
706
|
+
);
|
|
707
|
+
if (owner) this._setCurrentFile(owner);
|
|
708
|
+
this.err(c.message, c.spec);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
488
712
|
_checkTopDeclsPass(programs) {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
713
|
+
// A constant may name one declared later, so each is typed after those.
|
|
714
|
+
for (const e of orderPackageInits(programs).consts) {
|
|
715
|
+
this._setCurrentFile(e.program);
|
|
716
|
+
this.checkTopDecl({ ...e.decl, decls: [e.spec] }, this.globals);
|
|
717
|
+
}
|
|
718
|
+
// A variable's type may come from one declared later (`var a = b + 1;
|
|
719
|
+
// var b = f()`), so initialisers are typed after what they name.
|
|
720
|
+
for (const e of orderVarSpecsForTyping(programs)) {
|
|
721
|
+
this._setCurrentFile(e.program);
|
|
722
|
+
this.checkTopDecl({ ...e.decl, decls: [e.spec] }, this.globals);
|
|
496
723
|
}
|
|
497
724
|
for (const p of programs) {
|
|
498
725
|
this._setCurrentFile(p);
|
|
@@ -512,16 +739,20 @@ export class TypeChecker {
|
|
|
512
739
|
|
|
513
740
|
_promoteEmbeddedMethods() {
|
|
514
741
|
for (const type of this.types.values()) {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
742
|
+
this._promoteEmbeddedInto(type.underlying);
|
|
743
|
+
for (const inst of type._generic?._instances?.values() ?? [])
|
|
744
|
+
this._promoteEmbeddedInto(inst.underlying);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
_promoteEmbeddedInto(struct) {
|
|
749
|
+
if (struct?.kind !== "struct" || !struct._embeds) return;
|
|
750
|
+
for (const embed of struct._embeds) {
|
|
751
|
+
const inner = embed.kind === "pointer" ? embed.base : embed;
|
|
752
|
+
const base = inner.kind === "named" ? inner.underlying : inner;
|
|
753
|
+
if (base?.kind !== "struct" || !base.methods) continue;
|
|
754
|
+
for (const [mName, mType] of base.methods.entries()) {
|
|
755
|
+
if (!struct.methods.has(mName)) struct.methods.set(mName, mType);
|
|
525
756
|
}
|
|
526
757
|
}
|
|
527
758
|
}
|
|
@@ -529,30 +760,39 @@ export class TypeChecker {
|
|
|
529
760
|
// ── Type collection ──────────────────────────────────────────
|
|
530
761
|
|
|
531
762
|
collectType(decl) {
|
|
763
|
+
if (decl.typeParams && decl.isAlias) {
|
|
764
|
+
this._collectGenericAlias(decl);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
532
767
|
if (decl.typeParams) {
|
|
533
768
|
// Generic type — resolve underlying with type params as ANY for struct fields
|
|
534
769
|
const typeScope = new Scope(this.globals);
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
770
|
+
const preset = this.types.get(decl.name)?._generic;
|
|
771
|
+
const typeParamTypes = decl.typeParams.map((tp, i) => {
|
|
772
|
+
const t =
|
|
773
|
+
preset?.declNode === decl
|
|
774
|
+
? preset.typeParams[i]
|
|
775
|
+
: {
|
|
776
|
+
kind: "typeParam",
|
|
777
|
+
name: tp.name,
|
|
778
|
+
constraint: tp.constraint,
|
|
779
|
+
};
|
|
541
780
|
typeScope.define(tp.name, t);
|
|
542
781
|
return t;
|
|
543
782
|
});
|
|
544
|
-
const underlying = this.resolveTypeNode(decl.type, typeScope);
|
|
545
783
|
const existing = this.types.get(decl.name);
|
|
546
784
|
const named =
|
|
547
785
|
existing?.kind === "named"
|
|
548
786
|
? existing
|
|
549
787
|
: { kind: "named", name: decl.name };
|
|
788
|
+
// Registered before the fields resolve so `Next *List[T]` finds the
|
|
789
|
+
// generic declaration it is part of.
|
|
790
|
+
if (preset?.declNode === decl) named._generic = preset;
|
|
791
|
+
else named._generic = { typeParams: typeParamTypes, declNode: decl };
|
|
792
|
+
this.types.set(decl.name, named);
|
|
793
|
+
const underlying = this.resolveTypeNode(decl.type, typeScope);
|
|
550
794
|
named.underlying = underlying;
|
|
551
795
|
named._target = this.target;
|
|
552
|
-
named._generic = {
|
|
553
|
-
typeParams: typeParamTypes,
|
|
554
|
-
declNode: decl,
|
|
555
|
-
};
|
|
556
796
|
if (underlying.kind === "struct") {
|
|
557
797
|
underlying.name = decl.name;
|
|
558
798
|
underlying.methods = new Map();
|
|
@@ -561,7 +801,15 @@ export class TypeChecker {
|
|
|
561
801
|
this.globals.define(decl.name, named);
|
|
562
802
|
return;
|
|
563
803
|
}
|
|
564
|
-
|
|
804
|
+
let underlying = this.resolveTypeNode(decl.type, this.globals);
|
|
805
|
+
// `type W V` with V a named array: W's underlying type is V's array.
|
|
806
|
+
if (!decl.isAlias && underlying.kind === "named") {
|
|
807
|
+
const inner = this._deepUnderlying(underlying);
|
|
808
|
+
if (inner?.kind === "array") underlying = inner;
|
|
809
|
+
// `type S Base` with Base a struct: S gets its own copy of the fields.
|
|
810
|
+
else if (inner?.kind === "struct")
|
|
811
|
+
underlying = { ...inner, name: decl.name, methods: new Map() };
|
|
812
|
+
}
|
|
565
813
|
if (decl.isAlias) {
|
|
566
814
|
// type A = B — transparent alias, A and B are identical types
|
|
567
815
|
this.types.set(decl.name, underlying);
|
|
@@ -588,6 +836,29 @@ export class TypeChecker {
|
|
|
588
836
|
this.globals.define(decl.name, named);
|
|
589
837
|
}
|
|
590
838
|
|
|
839
|
+
// type A[P any] = T[P] — Go 1.24 generic alias. The declaration is a
|
|
840
|
+
// template only: each instantiation A[X] substitutes X into the aliased
|
|
841
|
+
// type and resolves to exactly that type (no new named type).
|
|
842
|
+
_collectGenericAlias(decl) {
|
|
843
|
+
const typeScope = new Scope(this.globals);
|
|
844
|
+
const typeParamTypes = decl.typeParams.map((tp) => {
|
|
845
|
+
const t = { kind: "typeParam", name: tp.name, constraint: tp.constraint };
|
|
846
|
+
typeScope.define(tp.name, t);
|
|
847
|
+
return t;
|
|
848
|
+
});
|
|
849
|
+
// Resolve once with the parameters in scope to report errors in the
|
|
850
|
+
// aliased type at the declaration.
|
|
851
|
+
const underlying = this.resolveTypeNode(decl.type, typeScope);
|
|
852
|
+
const alias = {
|
|
853
|
+
kind: "named",
|
|
854
|
+
name: decl.name,
|
|
855
|
+
underlying,
|
|
856
|
+
_generic: { typeParams: typeParamTypes, declNode: decl, isAlias: true },
|
|
857
|
+
};
|
|
858
|
+
this.types.set(decl.name, alias);
|
|
859
|
+
this.globals.define(decl.name, alias);
|
|
860
|
+
}
|
|
861
|
+
|
|
591
862
|
_buildCollectFuncScope(decl) {
|
|
592
863
|
let resolveScope = this.globals;
|
|
593
864
|
let typeParamTypes = null;
|
|
@@ -623,6 +894,7 @@ export class TypeChecker {
|
|
|
623
894
|
name: decl.name,
|
|
624
895
|
typeParams: typeParamTypes,
|
|
625
896
|
underlying: funcType,
|
|
897
|
+
declNode: decl,
|
|
626
898
|
});
|
|
627
899
|
} else {
|
|
628
900
|
this.globals.define(decl.name, funcType);
|
|
@@ -663,14 +935,18 @@ export class TypeChecker {
|
|
|
663
935
|
const recvType = this.resolveTypeNodeName(decl.recvType, this.globals);
|
|
664
936
|
const base = recvType?.underlying ?? recvType;
|
|
665
937
|
if (base?.kind === "struct") {
|
|
938
|
+
base.methods ??= new Map();
|
|
666
939
|
base.methods.set(decl.name, funcType);
|
|
667
|
-
}
|
|
940
|
+
}
|
|
941
|
+
if (recvType?.kind === "named") {
|
|
942
|
+
recvType.methods ??= new Map();
|
|
668
943
|
recvType.methods.set(decl.name, funcType);
|
|
669
944
|
}
|
|
670
945
|
}
|
|
671
946
|
|
|
672
947
|
_attachMethod(decl, funcType) {
|
|
673
948
|
funcType._ptrRecv = Boolean(decl.recvPointer);
|
|
949
|
+
funcType._decl = decl;
|
|
674
950
|
const recvNamedType = this.types.get(decl.recvType.name);
|
|
675
951
|
if (recvNamedType?._generic)
|
|
676
952
|
this._attachGenericMethod(recvNamedType, decl, funcType);
|
|
@@ -697,42 +973,111 @@ export class TypeChecker {
|
|
|
697
973
|
case "CssDecl":
|
|
698
974
|
break;
|
|
699
975
|
case "TemplDecl":
|
|
700
|
-
this.
|
|
976
|
+
this._checkTemplDecl(decl, scope);
|
|
701
977
|
break;
|
|
702
978
|
}
|
|
703
979
|
}
|
|
704
980
|
|
|
705
|
-
|
|
981
|
+
// A templ body is checked like a function body whose parameters are the
|
|
982
|
+
// templ's: every Go expression TemplParser attached to a node (`expr`,
|
|
983
|
+
// attribute `expr`, `forStmt`) is type-checked in the enclosing scope.
|
|
984
|
+
_checkTemplDecl(decl, outer) {
|
|
985
|
+
const inner = new Scope(outer);
|
|
986
|
+
for (const p of decl.params)
|
|
987
|
+
inner.define(p.name, this._paramType(p, inner));
|
|
988
|
+
this._checkTemplNodes(decl.body, inner);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
_checkTemplNodes(nodes, scope) {
|
|
992
|
+
for (const n of nodes ?? []) {
|
|
993
|
+
const m = TEMPL_NODE_CHECKS[n.kind];
|
|
994
|
+
if (m) this[m](n, scope);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// A malformed expression has no AST: the code generator reports its parse
|
|
999
|
+
// error. Its names count as used so that error is not preceded by a
|
|
1000
|
+
// misleading `declared and not used`.
|
|
1001
|
+
_checkTemplExpr(node, tokens, scope) {
|
|
1002
|
+
if (node.exprError) {
|
|
1003
|
+
this._markTokensUsed(tokens, scope);
|
|
1004
|
+
return null;
|
|
1005
|
+
}
|
|
1006
|
+
return node.expr ? this.checkExpr(node.expr, scope) : null;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
_checkTemplElement(n, scope) {
|
|
1010
|
+
for (const a of n.attrs ?? []) {
|
|
1011
|
+
if (a.kind === "expr" || a.kind === "cond-bool")
|
|
1012
|
+
this._checkTemplExpr(a, a.tokens, scope);
|
|
1013
|
+
}
|
|
1014
|
+
this._checkTemplNodes(n.children, scope);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
_checkTemplLeaf(n, scope) {
|
|
1018
|
+
this._checkTemplExpr(n, n.tokens, scope);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
_checkTemplIf(n, scope) {
|
|
1022
|
+
const ct = this._checkTemplExpr(n, n.condTokens, scope);
|
|
1023
|
+
if (ct && !isBool(ct) && !isAny(ct))
|
|
1024
|
+
this.err("If condition must be bool", n.expr);
|
|
1025
|
+
this._checkTemplNodes(n.then, scope);
|
|
1026
|
+
this._checkTemplNodes(n.else_, scope);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
_checkTemplSwitch(n, scope) {
|
|
1030
|
+
this._checkTemplExpr(n, n.exprTokens, scope);
|
|
1031
|
+
for (const c of n.cases ?? []) {
|
|
1032
|
+
this._checkTemplExpr(c, c.caseTokens, scope);
|
|
1033
|
+
this._checkTemplNodes(c.body, scope);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// A header that did not parse is reported by the code generator; its body
|
|
1038
|
+
// is not checked, since the loop variables it would declare are unknown.
|
|
1039
|
+
_checkTemplFor(n, scope) {
|
|
1040
|
+
if (!n.forStmt) {
|
|
1041
|
+
this._markTokensUsed(n.stmtTokens, scope);
|
|
1042
|
+
this._markTemplNodesUsed(n.body, scope);
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
this._checkForStmt(n.forStmt, scope, VOID, (bodyScope) =>
|
|
1046
|
+
this._checkTemplNodes(n.body, bodyScope),
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
_markTokensUsed(tokens, scope) {
|
|
706
1051
|
if (!tokens) return;
|
|
707
1052
|
for (const t of tokens) {
|
|
708
1053
|
if (t.type === "IDENT" || t.type?.name === "IDENT") {
|
|
709
|
-
|
|
1054
|
+
scope.lookup(t.value);
|
|
710
1055
|
}
|
|
711
1056
|
}
|
|
712
1057
|
}
|
|
713
1058
|
|
|
714
|
-
_markTemplNodesUsed(nodes) {
|
|
1059
|
+
_markTemplNodesUsed(nodes, scope) {
|
|
715
1060
|
if (!nodes) return;
|
|
716
1061
|
for (const n of nodes) {
|
|
717
1062
|
if (n.kind === "TemplElement") {
|
|
718
1063
|
for (const a of n.attrs || []) {
|
|
719
|
-
if (a.tokens) this._markTokensUsed(a.tokens);
|
|
1064
|
+
if (a.tokens) this._markTokensUsed(a.tokens, scope);
|
|
720
1065
|
}
|
|
721
|
-
this._markTemplNodesUsed(n.children);
|
|
1066
|
+
this._markTemplNodesUsed(n.children, scope);
|
|
722
1067
|
} else if (n.kind === "TemplExpr" || n.kind === "TemplComponent") {
|
|
723
|
-
this._markTokensUsed(n.tokens);
|
|
1068
|
+
this._markTokensUsed(n.tokens, scope);
|
|
724
1069
|
} else if (n.kind === "TemplIf") {
|
|
725
|
-
this._markTokensUsed(n.condTokens);
|
|
726
|
-
this._markTemplNodesUsed(n.then);
|
|
727
|
-
this._markTemplNodesUsed(n.else_);
|
|
1070
|
+
this._markTokensUsed(n.condTokens, scope);
|
|
1071
|
+
this._markTemplNodesUsed(n.then, scope);
|
|
1072
|
+
this._markTemplNodesUsed(n.else_, scope);
|
|
728
1073
|
} else if (n.kind === "TemplFor") {
|
|
729
|
-
this._markTokensUsed(n.stmtTokens);
|
|
730
|
-
this._markTemplNodesUsed(n.body);
|
|
1074
|
+
this._markTokensUsed(n.stmtTokens, scope);
|
|
1075
|
+
this._markTemplNodesUsed(n.body, scope);
|
|
731
1076
|
} else if (n.kind === "TemplSwitch") {
|
|
732
|
-
this._markTokensUsed(n.exprTokens);
|
|
1077
|
+
this._markTokensUsed(n.exprTokens, scope);
|
|
733
1078
|
for (const c of n.cases || []) {
|
|
734
|
-
this._markTokensUsed(c.caseTokens);
|
|
735
|
-
this._markTemplNodesUsed(c.body);
|
|
1079
|
+
this._markTokensUsed(c.caseTokens, scope);
|
|
1080
|
+
this._markTemplNodesUsed(c.body, scope);
|
|
736
1081
|
}
|
|
737
1082
|
}
|
|
738
1083
|
}
|
|
@@ -786,7 +1131,24 @@ export class TypeChecker {
|
|
|
786
1131
|
this.globals.define(decl.name, funcType);
|
|
787
1132
|
}
|
|
788
1133
|
|
|
1134
|
+
_checkIntrinsic(decl) {
|
|
1135
|
+
const want = INTRINSICS.get(decl.intrinsic);
|
|
1136
|
+
if (!want) return this.err(`unknown intrinsic '${decl.intrinsic}'`, decl);
|
|
1137
|
+
if (!decl._bodyless)
|
|
1138
|
+
return this.err(
|
|
1139
|
+
`intrinsic '${decl.intrinsic}' must not have a body`,
|
|
1140
|
+
decl,
|
|
1141
|
+
);
|
|
1142
|
+
const got = typeStr(this.globals.symbols.get(decl.name));
|
|
1143
|
+
if (got !== want)
|
|
1144
|
+
this.err(
|
|
1145
|
+
`intrinsic '${decl.intrinsic}' must have type ${want}, not ${got}`,
|
|
1146
|
+
decl,
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
789
1150
|
checkFuncDecl(decl, outer) {
|
|
1151
|
+
if (decl.intrinsic) this._checkIntrinsic(decl);
|
|
790
1152
|
if (
|
|
791
1153
|
(this.target === "wasm" || this.target === "both") &&
|
|
792
1154
|
(decl.async || decl.isAsync)
|
|
@@ -800,9 +1162,9 @@ export class TypeChecker {
|
|
|
800
1162
|
const inner = new Scope(outer);
|
|
801
1163
|
this._injectTypeParams(decl, inner, outer);
|
|
802
1164
|
for (const p of decl.params) {
|
|
803
|
-
inner.define(p.name, this.
|
|
1165
|
+
inner.define(p.name, this._paramType(p, inner));
|
|
804
1166
|
}
|
|
805
|
-
const returnType = this._setupFuncReturnType(decl, inner
|
|
1167
|
+
const returnType = this._setupFuncReturnType(decl, inner);
|
|
806
1168
|
const savedSharedAlloc = this._sharedAllocOk;
|
|
807
1169
|
this._sharedAllocOk = decl.name === "init";
|
|
808
1170
|
this._runFuncBody(decl, inner, returnType);
|
|
@@ -828,19 +1190,26 @@ export class TypeChecker {
|
|
|
828
1190
|
: decl.recvType.name;
|
|
829
1191
|
const recvNamedType = this.types.get(recvTypeName);
|
|
830
1192
|
this._injectGenericReceiverTypeParams(inner, recvNamedType);
|
|
831
|
-
const recvType = this.
|
|
1193
|
+
const recvType = this._methodReceiverType(decl, recvNamedType, outer);
|
|
832
1194
|
inner.define(
|
|
833
1195
|
decl.recvName,
|
|
834
1196
|
decl.recvPointer ? { kind: "pointer", base: recvType } : recvType,
|
|
835
1197
|
);
|
|
836
1198
|
for (const p of decl.params) {
|
|
837
|
-
inner.define(p.name, this.
|
|
1199
|
+
inner.define(p.name, this._paramType(p, inner));
|
|
838
1200
|
}
|
|
839
|
-
const returnType = this._setupFuncReturnType(decl, inner
|
|
1201
|
+
const returnType = this._setupFuncReturnType(decl, inner);
|
|
840
1202
|
this._runFuncBody(decl, inner, returnType);
|
|
841
1203
|
this._checkMissingReturn(decl, returnType, "method");
|
|
842
1204
|
}
|
|
843
1205
|
|
|
1206
|
+
// A variadic parameter `xs ...T` is a `[]T` inside the body (the signature
|
|
1207
|
+
// keeps the element type plus the `variadic` flag).
|
|
1208
|
+
_paramType(p, scope) {
|
|
1209
|
+
const t = this.resolveTypeNode(p.type, scope);
|
|
1210
|
+
return p.variadic ? { kind: "slice", elem: t } : t;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
844
1213
|
_injectTypeParams(decl, inner, outer) {
|
|
845
1214
|
if (!decl.typeParams) return;
|
|
846
1215
|
for (const tp of decl.typeParams) {
|
|
@@ -851,6 +1220,16 @@ export class TypeChecker {
|
|
|
851
1220
|
}
|
|
852
1221
|
}
|
|
853
1222
|
|
|
1223
|
+
// Inside a method of `G[T]` the receiver is the instance `G[T]` over the
|
|
1224
|
+
// receiver's own type parameters, so it is assignable to results and locals
|
|
1225
|
+
// written as `G[T]`.
|
|
1226
|
+
_methodReceiverType(decl, recvNamedType, outer) {
|
|
1227
|
+
const generic = recvNamedType?._generic;
|
|
1228
|
+
if (!generic || generic.isAlias || !generic.typeParams?.length)
|
|
1229
|
+
return this.resolveTypeNodeName(decl.recvType, outer);
|
|
1230
|
+
return this.instantiateGenericWithArgs(generic, generic.typeParams);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
854
1233
|
_injectGenericReceiverTypeParams(inner, recvNamedType) {
|
|
855
1234
|
if (!recvNamedType?._generic) return;
|
|
856
1235
|
for (const tp of recvNamedType._generic.typeParams) {
|
|
@@ -858,15 +1237,11 @@ export class TypeChecker {
|
|
|
858
1237
|
}
|
|
859
1238
|
}
|
|
860
1239
|
|
|
861
|
-
_setupFuncReturnType(decl, inner
|
|
1240
|
+
_setupFuncReturnType(decl, inner) {
|
|
862
1241
|
let returnType = decl.returnType
|
|
863
1242
|
? this.resolveTypeNode(decl.returnType, inner)
|
|
864
1243
|
: VOID;
|
|
865
|
-
const hasNamedReturns = this._injectNamedReturns(
|
|
866
|
-
decl.returnType,
|
|
867
|
-
inner,
|
|
868
|
-
outer,
|
|
869
|
-
);
|
|
1244
|
+
const hasNamedReturns = this._injectNamedReturns(decl.returnType, inner);
|
|
870
1245
|
if (hasNamedReturns && returnType)
|
|
871
1246
|
returnType = { ...returnType, _hasNamedReturns: true };
|
|
872
1247
|
return returnType;
|
|
@@ -890,10 +1265,12 @@ export class TypeChecker {
|
|
|
890
1265
|
}
|
|
891
1266
|
}
|
|
892
1267
|
|
|
893
|
-
|
|
1268
|
+
// Named result types resolve in the function scope, so a result of a type
|
|
1269
|
+
// parameter's type (`(r T)`) sees T.
|
|
1270
|
+
_injectNamedReturns(returnTypeNode, scope) {
|
|
894
1271
|
if (!returnTypeNode?._namedReturns) return false;
|
|
895
1272
|
for (const { name, type } of returnTypeNode._namedReturns) {
|
|
896
|
-
if (name) scope.define(name, this.resolveTypeNode(type,
|
|
1273
|
+
if (name) scope.define(name, this.resolveTypeNode(type, scope));
|
|
897
1274
|
}
|
|
898
1275
|
return true;
|
|
899
1276
|
}
|
|
@@ -922,8 +1299,15 @@ export class TypeChecker {
|
|
|
922
1299
|
for (let i = 0; i < spec.names.length; i++) {
|
|
923
1300
|
const vt = valTypesFlat[i] ?? ANY;
|
|
924
1301
|
const varType = type ?? defaultType(vt) ?? ANY;
|
|
1302
|
+
if (!type && spec.names.length === spec.value.length)
|
|
1303
|
+
this._checkConstDefault(vt, spec.value[i], "variable declaration");
|
|
925
1304
|
if (type)
|
|
926
|
-
this.assertAssignable(
|
|
1305
|
+
this.assertAssignable(
|
|
1306
|
+
type,
|
|
1307
|
+
vt,
|
|
1308
|
+
spec.value[i] ?? spec.value[0],
|
|
1309
|
+
"variable declaration",
|
|
1310
|
+
);
|
|
927
1311
|
scope.defineLocal(spec.names[i], varType);
|
|
928
1312
|
}
|
|
929
1313
|
} else {
|
|
@@ -944,6 +1328,13 @@ export class TypeChecker {
|
|
|
944
1328
|
for (let i = 0; i < spec.names.length; i++) {
|
|
945
1329
|
const name = spec.names[i];
|
|
946
1330
|
const expr = spec.value?.[i] ?? spec.value?.[0] ?? null;
|
|
1331
|
+
if (spec.type && expr)
|
|
1332
|
+
this._checkConstOverflow(
|
|
1333
|
+
type,
|
|
1334
|
+
valTypes[i] ?? valTypes[0],
|
|
1335
|
+
expr,
|
|
1336
|
+
"constant declaration",
|
|
1337
|
+
);
|
|
947
1338
|
scope.defineConst(name, type, expr);
|
|
948
1339
|
}
|
|
949
1340
|
}
|
|
@@ -957,3 +1348,4 @@ Object.assign(TypeChecker.prototype, expressionCheckMethods);
|
|
|
957
1348
|
Object.assign(TypeChecker.prototype, terminationMethods);
|
|
958
1349
|
Object.assign(TypeChecker.prototype, resolveMethods);
|
|
959
1350
|
Object.assign(TypeChecker.prototype, assignabilityMethods);
|
|
1351
|
+
Object.assign(TypeChecker.prototype, constantCheckMethods);
|