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
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,16 +11,24 @@
|
|
|
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,
|
|
24
|
+
BOOL,
|
|
21
25
|
defaultType,
|
|
22
26
|
ERROR,
|
|
27
|
+
INTRINSICS,
|
|
28
|
+
isAny,
|
|
29
|
+
isBool,
|
|
23
30
|
isVoid,
|
|
31
|
+
NATIVES,
|
|
24
32
|
Scope,
|
|
25
33
|
TAINTED_ANY,
|
|
26
34
|
TypeCheckError,
|
|
@@ -29,6 +37,17 @@ import {
|
|
|
29
37
|
VOID,
|
|
30
38
|
} from "./types.js";
|
|
31
39
|
|
|
40
|
+
// Checker method per templ body node kind (see _checkTemplNodes).
|
|
41
|
+
const TEMPL_NODE_CHECKS = {
|
|
42
|
+
TemplElement: "_checkTemplElement",
|
|
43
|
+
TemplExpr: "_checkTemplLeaf",
|
|
44
|
+
TemplChildren: "_checkTemplChildren",
|
|
45
|
+
TemplComponent: "_checkTemplLeaf",
|
|
46
|
+
TemplIf: "_checkTemplIf",
|
|
47
|
+
TemplFor: "_checkTemplFor",
|
|
48
|
+
TemplSwitch: "_checkTemplSwitch",
|
|
49
|
+
};
|
|
50
|
+
|
|
32
51
|
function rootIdentName(e) {
|
|
33
52
|
if (!e) return null;
|
|
34
53
|
if (e.kind === "Ident") return e.name;
|
|
@@ -38,13 +57,58 @@ function rootIdentName(e) {
|
|
|
38
57
|
}
|
|
39
58
|
|
|
40
59
|
// Re-export for consumers that import from typechecker.js
|
|
41
|
-
export { TypeCheckError, typeStr };
|
|
60
|
+
export { EXCLUDED_STDLIB_PACKAGES, TypeCheckError, typeStr };
|
|
61
|
+
|
|
62
|
+
// JavaScript built-ins that generated JS relies on; a JS class of the same
|
|
63
|
+
// name in the bundle would shadow them (see _checkGlobalTypeNamesPass).
|
|
64
|
+
const JS_BUILTIN_CLASSES = new Set([
|
|
65
|
+
"Array",
|
|
66
|
+
"ArrayBuffer",
|
|
67
|
+
"BigInt",
|
|
68
|
+
"BigInt64Array",
|
|
69
|
+
"BigUint64Array",
|
|
70
|
+
"Boolean",
|
|
71
|
+
"DataView",
|
|
72
|
+
"Date",
|
|
73
|
+
"Error",
|
|
74
|
+
"Float32Array",
|
|
75
|
+
"Float64Array",
|
|
76
|
+
"Int16Array",
|
|
77
|
+
"Int32Array",
|
|
78
|
+
"Int8Array",
|
|
79
|
+
"JSON",
|
|
80
|
+
"Map",
|
|
81
|
+
"Math",
|
|
82
|
+
"Number",
|
|
83
|
+
"Object",
|
|
84
|
+
"Promise",
|
|
85
|
+
"RangeError",
|
|
86
|
+
"Reflect",
|
|
87
|
+
"RegExp",
|
|
88
|
+
"Set",
|
|
89
|
+
"String",
|
|
90
|
+
"Symbol",
|
|
91
|
+
"TextDecoder",
|
|
92
|
+
"TextEncoder",
|
|
93
|
+
"TypeError",
|
|
94
|
+
"Uint16Array",
|
|
95
|
+
"Uint32Array",
|
|
96
|
+
"Uint8Array",
|
|
97
|
+
"Uint8ClampedArray",
|
|
98
|
+
"WeakMap",
|
|
99
|
+
"WebAssembly",
|
|
100
|
+
]);
|
|
42
101
|
|
|
43
102
|
export class TypeChecker {
|
|
44
103
|
constructor() {
|
|
45
104
|
this.types = new Map(); // named types
|
|
46
|
-
|
|
105
|
+
// Go's universe scope: builtins, browser globals and built-in package
|
|
106
|
+
// namespaces. Package declarations live in `globals` above it, so they
|
|
107
|
+
// shadow predeclared names (`func Array()`, `var document int`).
|
|
108
|
+
this.universe = new Scope();
|
|
109
|
+
this.globals = new Scope(this.universe);
|
|
47
110
|
this.errors = [];
|
|
111
|
+
this._reportedAt = new WeakMap();
|
|
48
112
|
this.warnings = []; // non-fatal diagnostics (e.g. boundary-call-in-loop hints)
|
|
49
113
|
this.target = "js";
|
|
50
114
|
this.pkgName = null;
|
|
@@ -57,22 +121,41 @@ export class TypeChecker {
|
|
|
57
121
|
this._typeSwitchDepth = 0; // for rejecting fallthrough in type switch
|
|
58
122
|
this._deferCount = 0; // tracks defer usage in current function body
|
|
59
123
|
this._imports = []; // tracked imports for unused-import detection
|
|
124
|
+
// Lazy body checking (Go-source stdlib): with `lazyBodies` set, checkAll
|
|
125
|
+
// types declarations only and `checkBody` types a function or method
|
|
126
|
+
// body when it is first reached.
|
|
127
|
+
this.lazyBodies = false;
|
|
128
|
+
this.stats = { bodiesChecked: 0 };
|
|
129
|
+
this._lazyBodies = new Map(); // FuncDecl/MethodDecl -> its program
|
|
60
130
|
this._constExprs = new Map(); // name -> AST initializer expr
|
|
131
|
+
this._importedConsts = new Map(); // dot-imported const name -> value
|
|
132
|
+
this._externalNames = new Set(); // package-scope names from .d.ts / npm definitions
|
|
61
133
|
this._setupGlobals();
|
|
62
134
|
// Register error as a named type (interface)
|
|
63
135
|
this.types.set("error", ERROR);
|
|
64
136
|
}
|
|
65
137
|
|
|
66
138
|
_setupGlobals() {
|
|
67
|
-
setupGlobals(this.
|
|
139
|
+
setupGlobals(this.universe, this.types);
|
|
68
140
|
}
|
|
69
141
|
|
|
70
|
-
|
|
142
|
+
// `external` marks values as real JS names (.d.ts / npm definitions) that
|
|
143
|
+
// must not be renamed; dot-imported GoFront packages pass false so their
|
|
144
|
+
// browser-global-named members get the same `$` renaming as local ones.
|
|
145
|
+
// `consts` (dot-imported GoFront packages) maps constant names to their
|
|
146
|
+
// folded values.
|
|
147
|
+
addDefinitions(types, values, { external = true, consts = null } = {}) {
|
|
71
148
|
for (const [name, type] of types) {
|
|
72
149
|
this.types.set(name, type);
|
|
73
150
|
}
|
|
74
151
|
for (const [name, type] of values) {
|
|
75
152
|
this.globals.define(name, type);
|
|
153
|
+
if (external) this._externalNames.add(name);
|
|
154
|
+
}
|
|
155
|
+
for (const [name, v] of consts ?? []) {
|
|
156
|
+
if (!v || !values.has(name)) continue;
|
|
157
|
+
this.globals._consts.add(name);
|
|
158
|
+
this._importedConsts.set(name, v);
|
|
76
159
|
}
|
|
77
160
|
}
|
|
78
161
|
|
|
@@ -80,7 +163,9 @@ export class TypeChecker {
|
|
|
80
163
|
// e.g. addPackageNamespace('utils', symbolsMap, typesMap)
|
|
81
164
|
// lets callers type-check `utils.Foo` via SelectorExpr.
|
|
82
165
|
// `target` is the imported package's compile target ("js"/"wasm"/"both").
|
|
83
|
-
|
|
166
|
+
// `consts` maps the package's constant names to their folded value (see
|
|
167
|
+
// typechecker/const-fold.js), or null when the folder cannot evaluate one.
|
|
168
|
+
addPackageNamespace(pkgName, symbols, types, target = null, consts = null) {
|
|
84
169
|
const members = {};
|
|
85
170
|
for (const [name, type] of symbols) members[name] = type;
|
|
86
171
|
// _gofront: true marks this as a GoFront package — exported identifier rules apply
|
|
@@ -90,9 +175,19 @@ export class TypeChecker {
|
|
|
90
175
|
members,
|
|
91
176
|
_gofront: true,
|
|
92
177
|
_target: target,
|
|
178
|
+
_consts: consts,
|
|
93
179
|
});
|
|
94
180
|
for (const [name, type] of types) {
|
|
95
|
-
this.types.
|
|
181
|
+
const prior = this.types.get(name);
|
|
182
|
+
if (prior && prior !== type && prior.kind === "named") {
|
|
183
|
+
this._ambiguousImported ??= new WeakSet();
|
|
184
|
+
this._ambiguousImported.add(prior);
|
|
185
|
+
this._ambiguousImported.add(type);
|
|
186
|
+
}
|
|
187
|
+
// A renamed type (`Pt` declared as `Pt$$b`) never takes the bare name:
|
|
188
|
+
// another package owns it, and this package's own declaration would
|
|
189
|
+
// otherwise reuse the imported placeholder. It is reached as `pkg.Pt`.
|
|
190
|
+
if (type.name === name) this.types.set(name, type);
|
|
96
191
|
this.types.set(`${pkgName}.${name}`, type);
|
|
97
192
|
}
|
|
98
193
|
}
|
|
@@ -108,7 +203,7 @@ export class TypeChecker {
|
|
|
108
203
|
// Report any tracked imports whose package name was never referenced.
|
|
109
204
|
reportUnusedImports() {
|
|
110
205
|
for (const { name, node, filename, source } of this._imports) {
|
|
111
|
-
if (!this.globals._used.has(name)) {
|
|
206
|
+
if (!this.globals._used.has(name) && !this.universe._used.has(name)) {
|
|
112
207
|
const saved = [this._currentFile, this._currentSource];
|
|
113
208
|
this._currentFile = filename;
|
|
114
209
|
this._currentSource = source;
|
|
@@ -130,6 +225,15 @@ export class TypeChecker {
|
|
|
130
225
|
return out;
|
|
131
226
|
}
|
|
132
227
|
|
|
228
|
+
getExportedConsts() {
|
|
229
|
+
const out = new Map();
|
|
230
|
+
for (const name of this.globals._consts) {
|
|
231
|
+
if (!this.globals.lookupConstExpr(name)) continue;
|
|
232
|
+
out.set(name, this._constValue({ kind: "Ident", name }, this.globals));
|
|
233
|
+
}
|
|
234
|
+
return out;
|
|
235
|
+
}
|
|
236
|
+
|
|
133
237
|
getExportedTypes() {
|
|
134
238
|
return new Map(this.types);
|
|
135
239
|
}
|
|
@@ -164,6 +268,14 @@ export class TypeChecker {
|
|
|
164
268
|
}
|
|
165
269
|
|
|
166
270
|
err(msg, node, hint = null) {
|
|
271
|
+
// One diagnostic per root cause: a node revisited by a later pass (a
|
|
272
|
+
// signature resolved at declaration and again in the body) reports once.
|
|
273
|
+
if (node && typeof node === "object") {
|
|
274
|
+
const seen = this._reportedAt.get(node) ?? new Set();
|
|
275
|
+
this._reportedAt.set(node, seen);
|
|
276
|
+
if (seen.has(msg)) return TAINTED_ANY;
|
|
277
|
+
seen.add(msg);
|
|
278
|
+
}
|
|
167
279
|
const e = new TypeCheckError(
|
|
168
280
|
msg,
|
|
169
281
|
node,
|
|
@@ -269,11 +381,15 @@ export class TypeChecker {
|
|
|
269
381
|
}
|
|
270
382
|
}
|
|
271
383
|
|
|
384
|
+
this._checkDirectivesAndImportsPass(programs);
|
|
385
|
+
this._registerConstExprs(programs);
|
|
272
386
|
this._collectTypesPass(programs);
|
|
273
387
|
this._collectFuncsPass(programs);
|
|
388
|
+
this._checkGlobalTypeNamesPass(programs);
|
|
274
389
|
this._promoteEmbeddedMethods();
|
|
275
390
|
this._collectVarsConstsPass(programs);
|
|
276
391
|
this._checkTopDeclsPass(programs);
|
|
392
|
+
this._reportInitCycles(programs);
|
|
277
393
|
|
|
278
394
|
if (this.target === "both") {
|
|
279
395
|
this._checkMutablePackageVars(programs);
|
|
@@ -290,6 +406,42 @@ export class TypeChecker {
|
|
|
290
406
|
return this.errors;
|
|
291
407
|
}
|
|
292
408
|
|
|
409
|
+
_checkEmbedDirectives(directives) {
|
|
410
|
+
for (const { line, col } of directives ?? []) {
|
|
411
|
+
this.err("//go:embed is not supported yet", { line, col });
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
_checkDirectivesAndImportsPass(programs) {
|
|
416
|
+
this._importedPaths = new Set();
|
|
417
|
+
this._fileImports = new Map();
|
|
418
|
+
for (const p of programs) {
|
|
419
|
+
this._setCurrentFile(p);
|
|
420
|
+
const names = new Map();
|
|
421
|
+
this._fileImports.set(this._currentFile, names);
|
|
422
|
+
this._checkEmbedDirectives(p._embedDirectives);
|
|
423
|
+
for (const imp of p.imports ?? []) {
|
|
424
|
+
for (const spec of imp.imports ?? []) {
|
|
425
|
+
this._importedPaths.add(spec.path);
|
|
426
|
+
names.set(
|
|
427
|
+
spec.alias ??
|
|
428
|
+
spec.path
|
|
429
|
+
.split("/")
|
|
430
|
+
.filter((s) => !/^v\d+$/.test(s))
|
|
431
|
+
.at(-1),
|
|
432
|
+
spec.path,
|
|
433
|
+
);
|
|
434
|
+
if (EXCLUDED_STDLIB_PACKAGES.has(spec.path)) {
|
|
435
|
+
this.err(
|
|
436
|
+
`package '${spec.path}' is not available: ${EXCLUDED_STDLIB_PACKAGES.get(spec.path)}`,
|
|
437
|
+
spec,
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
293
445
|
_checkMutablePackageVars(programs) {
|
|
294
446
|
const pkgVars = new Set();
|
|
295
447
|
for (const p of programs) {
|
|
@@ -409,6 +561,18 @@ export class TypeChecker {
|
|
|
409
561
|
for (const d of p.decls) {
|
|
410
562
|
if (d.kind === "TypeDecl" && !d.isAlias && !this.types.has(d.name)) {
|
|
411
563
|
const placeholder = { kind: "named", name: d.name, underlying: null };
|
|
564
|
+
// A generic type is known to be generic before any field resolves,
|
|
565
|
+
// so types declared earlier can instantiate it.
|
|
566
|
+
if (d.typeParams) {
|
|
567
|
+
placeholder._generic = {
|
|
568
|
+
typeParams: d.typeParams.map((tp) => ({
|
|
569
|
+
kind: "typeParam",
|
|
570
|
+
name: tp.name,
|
|
571
|
+
constraint: tp.constraint,
|
|
572
|
+
})),
|
|
573
|
+
declNode: d,
|
|
574
|
+
};
|
|
575
|
+
}
|
|
412
576
|
this.types.set(d.name, placeholder);
|
|
413
577
|
this.globals.define(d.name, placeholder);
|
|
414
578
|
}
|
|
@@ -418,6 +582,7 @@ export class TypeChecker {
|
|
|
418
582
|
for (const p of programs) {
|
|
419
583
|
for (const d of p.decls) if (d.kind === "TypeDecl") all.push({ p, d });
|
|
420
584
|
}
|
|
585
|
+
this._expandGenericStructDefs(all.map((x) => x.d));
|
|
421
586
|
// Collect in dependency order so aliases, generics, embedded and by-value
|
|
422
587
|
// field types declared later (or in other files) are ready when needed.
|
|
423
588
|
let pending = all;
|
|
@@ -436,6 +601,57 @@ export class TypeChecker {
|
|
|
436
601
|
}
|
|
437
602
|
}
|
|
438
603
|
|
|
604
|
+
// `type S[T any] Base` with `Base` a struct declared in the package: S is a
|
|
605
|
+
// new struct type with Base's fields (and none of its methods), so its
|
|
606
|
+
// declaration becomes a copy of Base's struct type.
|
|
607
|
+
_expandGenericStructDefs(decls) {
|
|
608
|
+
const byName = new Map(decls.map((d) => [d.name, d]));
|
|
609
|
+
for (const d of decls) {
|
|
610
|
+
if (!d.typeParams || d.isAlias) continue;
|
|
611
|
+
let target = d.type;
|
|
612
|
+
for (let i = 0; i < 16 && target?.kind === "TypeName"; i++) {
|
|
613
|
+
const base = byName.get(target.name);
|
|
614
|
+
if (!base || base.typeParams || base.isAlias) break;
|
|
615
|
+
target = base.type;
|
|
616
|
+
}
|
|
617
|
+
// A struct written in place is already this declaration's own copy;
|
|
618
|
+
// cloning it would also choke on an AST a previous target pass
|
|
619
|
+
// annotated with cycles.
|
|
620
|
+
if (target?.kind === "StructType" && target !== d.type)
|
|
621
|
+
d.type = JSON.parse(JSON.stringify(target));
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// A struct type, or a named type with methods, becomes a JS class of the
|
|
626
|
+
// same name in `js` and `both` packages. Functions and variables that
|
|
627
|
+
// shadow a browser global get a `$` suffix, but type names reach the
|
|
628
|
+
// output through the checker's types (class declarations, `new T`,
|
|
629
|
+
// `instanceof T`, static methods), so a class named after a JavaScript
|
|
630
|
+
// built-in the generated code calls (`Array.isArray`, `new Error`) is
|
|
631
|
+
// rejected instead. Interfaces and method-less named types emit no class.
|
|
632
|
+
_checkGlobalTypeNamesPass(programs) {
|
|
633
|
+
if (this.target === "wasm" || this.isStdlib) return;
|
|
634
|
+
for (const p of programs) {
|
|
635
|
+
for (const d of p.decls) {
|
|
636
|
+
if (
|
|
637
|
+
d.kind !== "TypeDecl" ||
|
|
638
|
+
d.isAlias ||
|
|
639
|
+
!JS_BUILTIN_CLASSES.has(d.name)
|
|
640
|
+
)
|
|
641
|
+
continue;
|
|
642
|
+
const t = this.types.get(d.name);
|
|
643
|
+
const isClass =
|
|
644
|
+
t?.underlying?.kind === "struct" || (t?.methods?.size ?? 0) > 0;
|
|
645
|
+
if (!isClass) continue;
|
|
646
|
+
this._setCurrentFile(p);
|
|
647
|
+
this.err(
|
|
648
|
+
`type ${d.name} shadows the JavaScript global ${d.name}; rename the type`,
|
|
649
|
+
d,
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
439
655
|
_typeDeclBlocked(decl, pendingNames) {
|
|
440
656
|
const visit = (node, indirect) => {
|
|
441
657
|
if (!node || typeof node !== "object") return false;
|
|
@@ -475,6 +691,22 @@ export class TypeChecker {
|
|
|
475
691
|
}
|
|
476
692
|
}
|
|
477
693
|
|
|
694
|
+
// Constant initializers are known before types are resolved, so an array
|
|
695
|
+
// length may name a constant declared later (`[N]int`, `[N + 1]T`).
|
|
696
|
+
_registerConstExprs(programs) {
|
|
697
|
+
for (const p of programs) {
|
|
698
|
+
for (const d of p.decls) {
|
|
699
|
+
if (d.kind !== "ConstDecl") continue;
|
|
700
|
+
for (const spec of d.decls) {
|
|
701
|
+
spec.names.forEach((name, i) => {
|
|
702
|
+
const expr = spec.value?.[i] ?? spec.value?.[0] ?? null;
|
|
703
|
+
if (expr) this.globals._constExprs.set(name, expr);
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
478
710
|
_collectVarsConstsPass(programs) {
|
|
479
711
|
for (const p of programs) {
|
|
480
712
|
this._setCurrentFile(p);
|
|
@@ -485,19 +717,44 @@ export class TypeChecker {
|
|
|
485
717
|
}
|
|
486
718
|
}
|
|
487
719
|
|
|
720
|
+
// Go rejects package variables whose initialisers depend on each other.
|
|
721
|
+
_reportInitCycles(programs) {
|
|
722
|
+
if (this.errors.length > 0) return;
|
|
723
|
+
const { cycles } = orderPackageInits(programs);
|
|
724
|
+
for (const c of cycles) {
|
|
725
|
+
const owner = programs.find((p) =>
|
|
726
|
+
p.decls?.some(
|
|
727
|
+
(d) =>
|
|
728
|
+
(d.kind === "VarDecl" || d.kind === "ConstDecl") &&
|
|
729
|
+
d.decls?.includes(c.spec),
|
|
730
|
+
),
|
|
731
|
+
);
|
|
732
|
+
if (owner) this._setCurrentFile(owner);
|
|
733
|
+
this.err(c.message, c.spec);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
488
737
|
_checkTopDeclsPass(programs) {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
738
|
+
// A constant may name one declared later, so each is typed after those.
|
|
739
|
+
for (const e of orderPackageInits(programs).consts) {
|
|
740
|
+
this._setCurrentFile(e.program);
|
|
741
|
+
this.checkTopDecl({ ...e.decl, decls: [e.spec] }, this.globals);
|
|
742
|
+
}
|
|
743
|
+
// A variable's type may come from one declared later (`var a = b + 1;
|
|
744
|
+
// var b = f()`), so initialisers are typed after what they name.
|
|
745
|
+
for (const e of orderVarSpecsForTyping(programs)) {
|
|
746
|
+
this._setCurrentFile(e.program);
|
|
747
|
+
this.checkTopDecl({ ...e.decl, decls: [e.spec] }, this.globals);
|
|
496
748
|
}
|
|
497
749
|
for (const p of programs) {
|
|
498
750
|
this._setCurrentFile(p);
|
|
499
751
|
for (const d of p.decls) {
|
|
500
752
|
if (
|
|
753
|
+
this.lazyBodies &&
|
|
754
|
+
(d.kind === "FuncDecl" || d.kind === "MethodDecl")
|
|
755
|
+
)
|
|
756
|
+
this._deferBody(d, p);
|
|
757
|
+
else if (
|
|
501
758
|
d.kind === "FuncDecl" ||
|
|
502
759
|
d.kind === "MethodDecl" ||
|
|
503
760
|
d.kind === "TemplDecl"
|
|
@@ -508,20 +765,71 @@ export class TypeChecker {
|
|
|
508
765
|
}
|
|
509
766
|
}
|
|
510
767
|
|
|
768
|
+
// Lazy mode: validate what does not need the body now, check it on demand.
|
|
769
|
+
_deferBody(decl, program) {
|
|
770
|
+
if (decl.intrinsic) this._checkIntrinsic(decl);
|
|
771
|
+
if (decl.native) this._checkNative(decl);
|
|
772
|
+
this._lazyBodies.set(decl, program);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// Types the body of a function or method deferred by `lazyBodies`; a
|
|
776
|
+
// declaration checked already (or not deferred) is left alone.
|
|
777
|
+
checkBody(decl) {
|
|
778
|
+
const program = this._lazyBodies.get(decl);
|
|
779
|
+
if (!program) return;
|
|
780
|
+
this._lazyBodies.delete(decl);
|
|
781
|
+
this._setCurrentFile(program);
|
|
782
|
+
this.checkTopDecl(decl, this.globals);
|
|
783
|
+
}
|
|
784
|
+
|
|
511
785
|
// ── Shared pass helpers ──────────────────────────────────────
|
|
512
786
|
|
|
787
|
+
// Instances made while types were collected (`struct{ Inner[int] }`) have
|
|
788
|
+
// no methods yet, and a type embedding another embedder (`Deep` over `E`
|
|
789
|
+
// over `Base`) needs the inner promotion first, so repeat until nothing
|
|
790
|
+
// new is promoted.
|
|
513
791
|
_promoteEmbeddedMethods() {
|
|
792
|
+
const structs = [];
|
|
514
793
|
for (const type of this.types.values()) {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
794
|
+
structs.push(type.underlying);
|
|
795
|
+
for (const inst of type._generic?._instances?.values() ?? []) {
|
|
796
|
+
this._syncInstanceMethods(type._generic, inst);
|
|
797
|
+
structs.push(inst.underlying);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
const count = () =>
|
|
801
|
+
structs.reduce(
|
|
802
|
+
(n, st) => n + (st?.methods?.size ?? 0) + (st?.fields?.size ?? 0),
|
|
803
|
+
0,
|
|
804
|
+
);
|
|
805
|
+
let before;
|
|
806
|
+
do {
|
|
807
|
+
before = count();
|
|
808
|
+
for (const st of structs) this._promoteEmbeddedInto(st);
|
|
809
|
+
} while (count() !== before);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
_promoteEmbeddedInto(struct) {
|
|
813
|
+
if (struct?.kind !== "struct" || !struct._embeds) return;
|
|
814
|
+
for (const embed of struct._embeds) {
|
|
815
|
+
const inner = embed.kind === "pointer" ? embed.base : embed;
|
|
816
|
+
const base = inner.kind === "named" ? inner.underlying : inner;
|
|
817
|
+
// Fields of an embedded type declared after the embedder were not
|
|
818
|
+
// there when the embedder's fields were collected.
|
|
819
|
+
if (base?.kind === "struct" && struct.fields)
|
|
820
|
+
for (const [k, v] of base.fields)
|
|
821
|
+
if (!struct.fields.has(k)) struct.fields.set(k, v);
|
|
822
|
+
// A named non-struct type (`time.Duration`) keeps its methods on
|
|
823
|
+
// the named type itself.
|
|
824
|
+
const methods =
|
|
825
|
+
base?.kind === "struct"
|
|
826
|
+
? base.methods
|
|
827
|
+
: inner.kind === "named" && base?.kind !== "interface"
|
|
828
|
+
? inner.methods
|
|
829
|
+
: null;
|
|
830
|
+
if (!methods) continue;
|
|
831
|
+
for (const [mName, mType] of methods.entries()) {
|
|
832
|
+
if (!struct.methods.has(mName)) struct.methods.set(mName, mType);
|
|
525
833
|
}
|
|
526
834
|
}
|
|
527
835
|
}
|
|
@@ -529,30 +837,39 @@ export class TypeChecker {
|
|
|
529
837
|
// ── Type collection ──────────────────────────────────────────
|
|
530
838
|
|
|
531
839
|
collectType(decl) {
|
|
840
|
+
if (decl.typeParams && decl.isAlias) {
|
|
841
|
+
this._collectGenericAlias(decl);
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
532
844
|
if (decl.typeParams) {
|
|
533
845
|
// Generic type — resolve underlying with type params as ANY for struct fields
|
|
534
846
|
const typeScope = new Scope(this.globals);
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
847
|
+
const preset = this.types.get(decl.name)?._generic;
|
|
848
|
+
const typeParamTypes = decl.typeParams.map((tp, i) => {
|
|
849
|
+
const t =
|
|
850
|
+
preset?.declNode === decl
|
|
851
|
+
? preset.typeParams[i]
|
|
852
|
+
: {
|
|
853
|
+
kind: "typeParam",
|
|
854
|
+
name: tp.name,
|
|
855
|
+
constraint: tp.constraint,
|
|
856
|
+
};
|
|
541
857
|
typeScope.define(tp.name, t);
|
|
542
858
|
return t;
|
|
543
859
|
});
|
|
544
|
-
const underlying = this.resolveTypeNode(decl.type, typeScope);
|
|
545
860
|
const existing = this.types.get(decl.name);
|
|
546
861
|
const named =
|
|
547
862
|
existing?.kind === "named"
|
|
548
863
|
? existing
|
|
549
864
|
: { kind: "named", name: decl.name };
|
|
865
|
+
// Registered before the fields resolve so `Next *List[T]` finds the
|
|
866
|
+
// generic declaration it is part of.
|
|
867
|
+
if (preset?.declNode === decl) named._generic = preset;
|
|
868
|
+
else named._generic = { typeParams: typeParamTypes, declNode: decl };
|
|
869
|
+
this.types.set(decl.name, named);
|
|
870
|
+
const underlying = this.resolveTypeNode(decl.type, typeScope);
|
|
550
871
|
named.underlying = underlying;
|
|
551
872
|
named._target = this.target;
|
|
552
|
-
named._generic = {
|
|
553
|
-
typeParams: typeParamTypes,
|
|
554
|
-
declNode: decl,
|
|
555
|
-
};
|
|
556
873
|
if (underlying.kind === "struct") {
|
|
557
874
|
underlying.name = decl.name;
|
|
558
875
|
underlying.methods = new Map();
|
|
@@ -561,7 +878,18 @@ export class TypeChecker {
|
|
|
561
878
|
this.globals.define(decl.name, named);
|
|
562
879
|
return;
|
|
563
880
|
}
|
|
564
|
-
|
|
881
|
+
// A type declared inside a function is registered before its fields
|
|
882
|
+
// resolve so `next *L` finds it; package-level types already are.
|
|
883
|
+
this._registerLocalTypePlaceholder(decl);
|
|
884
|
+
let underlying = this.resolveTypeNode(decl.type, this.globals);
|
|
885
|
+
// `type W V` with V a named array: W's underlying type is V's array.
|
|
886
|
+
if (!decl.isAlias && underlying.kind === "named") {
|
|
887
|
+
const inner = this._deepUnderlying(underlying);
|
|
888
|
+
if (inner?.kind === "array") underlying = inner;
|
|
889
|
+
// `type S Base` with Base a struct: S gets its own copy of the fields.
|
|
890
|
+
else if (inner?.kind === "struct")
|
|
891
|
+
underlying = { ...inner, name: decl.name, methods: new Map() };
|
|
892
|
+
}
|
|
565
893
|
if (decl.isAlias) {
|
|
566
894
|
// type A = B — transparent alias, A and B are identical types
|
|
567
895
|
this.types.set(decl.name, underlying);
|
|
@@ -588,6 +916,36 @@ export class TypeChecker {
|
|
|
588
916
|
this.globals.define(decl.name, named);
|
|
589
917
|
}
|
|
590
918
|
|
|
919
|
+
_registerLocalTypePlaceholder(decl) {
|
|
920
|
+
if (decl.isAlias || this.types.has(decl.name)) return;
|
|
921
|
+
const placeholder = { kind: "named", name: decl.name, underlying: null };
|
|
922
|
+
this.types.set(decl.name, placeholder);
|
|
923
|
+
this.globals.define(decl.name, placeholder);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// type A[P any] = T[P] — Go 1.24 generic alias. The declaration is a
|
|
927
|
+
// template only: each instantiation A[X] substitutes X into the aliased
|
|
928
|
+
// type and resolves to exactly that type (no new named type).
|
|
929
|
+
_collectGenericAlias(decl) {
|
|
930
|
+
const typeScope = new Scope(this.globals);
|
|
931
|
+
const typeParamTypes = decl.typeParams.map((tp) => {
|
|
932
|
+
const t = { kind: "typeParam", name: tp.name, constraint: tp.constraint };
|
|
933
|
+
typeScope.define(tp.name, t);
|
|
934
|
+
return t;
|
|
935
|
+
});
|
|
936
|
+
// Resolve once with the parameters in scope to report errors in the
|
|
937
|
+
// aliased type at the declaration.
|
|
938
|
+
const underlying = this.resolveTypeNode(decl.type, typeScope);
|
|
939
|
+
const alias = {
|
|
940
|
+
kind: "named",
|
|
941
|
+
name: decl.name,
|
|
942
|
+
underlying,
|
|
943
|
+
_generic: { typeParams: typeParamTypes, declNode: decl, isAlias: true },
|
|
944
|
+
};
|
|
945
|
+
this.types.set(decl.name, alias);
|
|
946
|
+
this.globals.define(decl.name, alias);
|
|
947
|
+
}
|
|
948
|
+
|
|
591
949
|
_buildCollectFuncScope(decl) {
|
|
592
950
|
let resolveScope = this.globals;
|
|
593
951
|
let typeParamTypes = null;
|
|
@@ -617,12 +975,16 @@ export class TypeChecker {
|
|
|
617
975
|
_registerFuncDecl(decl, funcType, typeParamTypes) {
|
|
618
976
|
if (this.globals.symbols.has(decl.name) && decl.name !== "init")
|
|
619
977
|
this.err(`${decl.name} redeclared in this block`, decl);
|
|
978
|
+
if (decl.native) funcType.native = decl.native;
|
|
979
|
+
this._funcDeclNames ??= new Set();
|
|
980
|
+
this._funcDeclNames.add(decl.name);
|
|
620
981
|
if (typeParamTypes) {
|
|
621
982
|
this.globals.define(decl.name, {
|
|
622
983
|
kind: "generic",
|
|
623
984
|
name: decl.name,
|
|
624
985
|
typeParams: typeParamTypes,
|
|
625
986
|
underlying: funcType,
|
|
987
|
+
declNode: decl,
|
|
626
988
|
});
|
|
627
989
|
} else {
|
|
628
990
|
this.globals.define(decl.name, funcType);
|
|
@@ -660,17 +1022,24 @@ export class TypeChecker {
|
|
|
660
1022
|
}
|
|
661
1023
|
|
|
662
1024
|
_attachNonGenericMethod(decl, funcType) {
|
|
1025
|
+
// Codegen of an importing package reads the body of a Go-source
|
|
1026
|
+
// method (does it accept a nil receiver?).
|
|
1027
|
+
funcType._methodDecl = decl;
|
|
663
1028
|
const recvType = this.resolveTypeNodeName(decl.recvType, this.globals);
|
|
664
1029
|
const base = recvType?.underlying ?? recvType;
|
|
665
1030
|
if (base?.kind === "struct") {
|
|
1031
|
+
base.methods ??= new Map();
|
|
666
1032
|
base.methods.set(decl.name, funcType);
|
|
667
|
-
}
|
|
1033
|
+
}
|
|
1034
|
+
if (recvType?.kind === "named") {
|
|
1035
|
+
recvType.methods ??= new Map();
|
|
668
1036
|
recvType.methods.set(decl.name, funcType);
|
|
669
1037
|
}
|
|
670
1038
|
}
|
|
671
1039
|
|
|
672
1040
|
_attachMethod(decl, funcType) {
|
|
673
1041
|
funcType._ptrRecv = Boolean(decl.recvPointer);
|
|
1042
|
+
funcType._decl = decl;
|
|
674
1043
|
const recvNamedType = this.types.get(decl.recvType.name);
|
|
675
1044
|
if (recvNamedType?._generic)
|
|
676
1045
|
this._attachGenericMethod(recvNamedType, decl, funcType);
|
|
@@ -697,42 +1066,130 @@ export class TypeChecker {
|
|
|
697
1066
|
case "CssDecl":
|
|
698
1067
|
break;
|
|
699
1068
|
case "TemplDecl":
|
|
700
|
-
this.
|
|
1069
|
+
this._checkTemplDecl(decl, scope);
|
|
701
1070
|
break;
|
|
702
1071
|
}
|
|
703
1072
|
}
|
|
704
1073
|
|
|
705
|
-
|
|
1074
|
+
// A templ body is checked like a function body whose parameters are the
|
|
1075
|
+
// templ's: every Go expression TemplParser attached to a node (`expr`,
|
|
1076
|
+
// attribute `expr`, `forStmt`) is type-checked in the enclosing scope.
|
|
1077
|
+
_checkTemplDecl(decl, outer) {
|
|
1078
|
+
const inner = new Scope(outer);
|
|
1079
|
+
for (const p of decl.params)
|
|
1080
|
+
inner.define(p.name, this._paramType(p, inner));
|
|
1081
|
+
this._checkTemplNodes(decl.body, inner);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
_checkTemplNodes(nodes, scope) {
|
|
1085
|
+
for (const n of nodes ?? []) {
|
|
1086
|
+
const m = TEMPL_NODE_CHECKS[n.kind];
|
|
1087
|
+
if (m) this[m](n, scope);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// A malformed expression has no AST: the code generator reports its parse
|
|
1092
|
+
// error. Its names count as used so that error is not preceded by a
|
|
1093
|
+
// misleading `declared and not used`.
|
|
1094
|
+
_checkTemplExpr(node, tokens, scope) {
|
|
1095
|
+
if (node.exprError) {
|
|
1096
|
+
this._markTokensUsed(tokens, scope);
|
|
1097
|
+
return null;
|
|
1098
|
+
}
|
|
1099
|
+
return node.expr ? this.checkExpr(node.expr, scope) : null;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
_checkTemplElement(n, scope) {
|
|
1103
|
+
for (const a of n.attrs ?? []) {
|
|
1104
|
+
if (a.kind === "expr") this._checkTemplExpr(a, a.tokens, scope);
|
|
1105
|
+
else if (a.kind === "cond-bool") this._checkTemplCondAttr(a, scope);
|
|
1106
|
+
}
|
|
1107
|
+
this._checkTemplNodes(n.children, scope);
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// `name?={ cond }` sets the attribute when `cond` is true.
|
|
1111
|
+
_checkTemplCondAttr(a, scope) {
|
|
1112
|
+
const t = this._checkTemplExpr(a, a.tokens, scope);
|
|
1113
|
+
if (t && !isBool(t) && !isAny(t))
|
|
1114
|
+
this.err(`Conditional attribute '${a.name}' must be bool`, a.expr);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
// `{ name... }` names a variadic parameter. A templ without a `children`
|
|
1118
|
+
// parameter may still write `{ children... }` (it mounts nothing).
|
|
1119
|
+
_checkTemplChildren(n, scope) {
|
|
1120
|
+
if (n.name === "children" && !scope.lookup("children")) return;
|
|
1121
|
+
this._checkTemplExpr(n, [], scope);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
_checkTemplLeaf(n, scope) {
|
|
1125
|
+
this._checkTemplExpr(n, n.tokens, scope);
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
_checkTemplIf(n, scope) {
|
|
1129
|
+
const ct = this._checkTemplExpr(n, n.condTokens, scope);
|
|
1130
|
+
if (ct && !isBool(ct) && !isAny(ct))
|
|
1131
|
+
this.err("If condition must be bool", n.expr);
|
|
1132
|
+
this._checkTemplNodes(n.then, scope);
|
|
1133
|
+
this._checkTemplNodes(n.else_, scope);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
_checkTemplSwitch(n, scope) {
|
|
1137
|
+
this._checkTemplExpr(n, n.exprTokens, scope);
|
|
1138
|
+
// A tag that failed to parse leaves `n.expr` unset: not a tagless switch.
|
|
1139
|
+
const tagless = !n.exprTokens?.length;
|
|
1140
|
+
for (const c of n.cases ?? []) {
|
|
1141
|
+
this._checkTemplExpr(c, c.caseTokens, scope);
|
|
1142
|
+
for (const e of c.exprs?.slice(1) ?? []) this.checkExpr(e, scope);
|
|
1143
|
+
if (n.expr || tagless)
|
|
1144
|
+
for (const e of c.exprs ?? []) this._checkCaseValue(n.expr, e);
|
|
1145
|
+
this._checkTemplNodes(c.body, scope);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// A header that did not parse is reported by the code generator; its body
|
|
1150
|
+
// is not checked, since the loop variables it would declare are unknown.
|
|
1151
|
+
_checkTemplFor(n, scope) {
|
|
1152
|
+
if (!n.forStmt) {
|
|
1153
|
+
this._markTokensUsed(n.stmtTokens, scope);
|
|
1154
|
+
this._markTemplNodesUsed(n.body, scope);
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
this._checkForStmt(n.forStmt, scope, VOID, (bodyScope) =>
|
|
1158
|
+
this._checkTemplNodes(n.body, bodyScope),
|
|
1159
|
+
);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
_markTokensUsed(tokens, scope) {
|
|
706
1163
|
if (!tokens) return;
|
|
707
1164
|
for (const t of tokens) {
|
|
708
1165
|
if (t.type === "IDENT" || t.type?.name === "IDENT") {
|
|
709
|
-
|
|
1166
|
+
scope.lookup(t.value);
|
|
710
1167
|
}
|
|
711
1168
|
}
|
|
712
1169
|
}
|
|
713
1170
|
|
|
714
|
-
_markTemplNodesUsed(nodes) {
|
|
1171
|
+
_markTemplNodesUsed(nodes, scope) {
|
|
715
1172
|
if (!nodes) return;
|
|
716
1173
|
for (const n of nodes) {
|
|
717
1174
|
if (n.kind === "TemplElement") {
|
|
718
1175
|
for (const a of n.attrs || []) {
|
|
719
|
-
if (a.tokens) this._markTokensUsed(a.tokens);
|
|
1176
|
+
if (a.tokens) this._markTokensUsed(a.tokens, scope);
|
|
720
1177
|
}
|
|
721
|
-
this._markTemplNodesUsed(n.children);
|
|
1178
|
+
this._markTemplNodesUsed(n.children, scope);
|
|
722
1179
|
} else if (n.kind === "TemplExpr" || n.kind === "TemplComponent") {
|
|
723
|
-
this._markTokensUsed(n.tokens);
|
|
1180
|
+
this._markTokensUsed(n.tokens, scope);
|
|
724
1181
|
} else if (n.kind === "TemplIf") {
|
|
725
|
-
this._markTokensUsed(n.condTokens);
|
|
726
|
-
this._markTemplNodesUsed(n.then);
|
|
727
|
-
this._markTemplNodesUsed(n.else_);
|
|
1182
|
+
this._markTokensUsed(n.condTokens, scope);
|
|
1183
|
+
this._markTemplNodesUsed(n.then, scope);
|
|
1184
|
+
this._markTemplNodesUsed(n.else_, scope);
|
|
728
1185
|
} else if (n.kind === "TemplFor") {
|
|
729
|
-
this._markTokensUsed(n.stmtTokens);
|
|
730
|
-
this._markTemplNodesUsed(n.body);
|
|
1186
|
+
this._markTokensUsed(n.stmtTokens, scope);
|
|
1187
|
+
this._markTemplNodesUsed(n.body, scope);
|
|
731
1188
|
} else if (n.kind === "TemplSwitch") {
|
|
732
|
-
this._markTokensUsed(n.exprTokens);
|
|
1189
|
+
this._markTokensUsed(n.exprTokens, scope);
|
|
733
1190
|
for (const c of n.cases || []) {
|
|
734
|
-
this._markTokensUsed(c.caseTokens);
|
|
735
|
-
this._markTemplNodesUsed(c.body);
|
|
1191
|
+
this._markTokensUsed(c.caseTokens, scope);
|
|
1192
|
+
this._markTemplNodesUsed(c.body, scope);
|
|
736
1193
|
}
|
|
737
1194
|
}
|
|
738
1195
|
}
|
|
@@ -786,7 +1243,43 @@ export class TypeChecker {
|
|
|
786
1243
|
this.globals.define(decl.name, funcType);
|
|
787
1244
|
}
|
|
788
1245
|
|
|
1246
|
+
_checkIntrinsic(decl) {
|
|
1247
|
+
const want = INTRINSICS.get(decl.intrinsic);
|
|
1248
|
+
if (!want) return this.err(`unknown intrinsic '${decl.intrinsic}'`, decl);
|
|
1249
|
+
if (!decl._bodyless)
|
|
1250
|
+
return this.err(
|
|
1251
|
+
`intrinsic '${decl.intrinsic}' must not have a body`,
|
|
1252
|
+
decl,
|
|
1253
|
+
);
|
|
1254
|
+
const got = typeStr(this.globals.symbols.get(decl.name));
|
|
1255
|
+
if (got !== want)
|
|
1256
|
+
this.err(
|
|
1257
|
+
`intrinsic '${decl.intrinsic}' must have type ${want}, not ${got}`,
|
|
1258
|
+
decl,
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
_checkNative(decl) {
|
|
1263
|
+
if (!this.isStdlib && !this.nativesChecked)
|
|
1264
|
+
return this.err(
|
|
1265
|
+
"//gofront:native is reserved for the standard library",
|
|
1266
|
+
decl,
|
|
1267
|
+
);
|
|
1268
|
+
if (decl.kind !== "FuncDecl")
|
|
1269
|
+
return this.err(
|
|
1270
|
+
`native '${decl.native}' must mark a function, not a method`,
|
|
1271
|
+
decl,
|
|
1272
|
+
);
|
|
1273
|
+
if (!NATIVES.has(decl.native))
|
|
1274
|
+
return this.err(`unknown native '${decl.native}'`, decl);
|
|
1275
|
+
if (decl._bodyless)
|
|
1276
|
+
this.err(`native '${decl.native}' must have a Go body`, decl);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
789
1279
|
checkFuncDecl(decl, outer) {
|
|
1280
|
+
this.stats.bodiesChecked++;
|
|
1281
|
+
if (decl.intrinsic) this._checkIntrinsic(decl);
|
|
1282
|
+
if (decl.native) this._checkNative(decl);
|
|
790
1283
|
if (
|
|
791
1284
|
(this.target === "wasm" || this.target === "both") &&
|
|
792
1285
|
(decl.async || decl.isAsync)
|
|
@@ -800,9 +1293,9 @@ export class TypeChecker {
|
|
|
800
1293
|
const inner = new Scope(outer);
|
|
801
1294
|
this._injectTypeParams(decl, inner, outer);
|
|
802
1295
|
for (const p of decl.params) {
|
|
803
|
-
inner.define(p.name, this.
|
|
1296
|
+
inner.define(p.name, this._paramType(p, inner));
|
|
804
1297
|
}
|
|
805
|
-
const returnType = this._setupFuncReturnType(decl, inner
|
|
1298
|
+
const returnType = this._setupFuncReturnType(decl, inner);
|
|
806
1299
|
const savedSharedAlloc = this._sharedAllocOk;
|
|
807
1300
|
this._sharedAllocOk = decl.name === "init";
|
|
808
1301
|
this._runFuncBody(decl, inner, returnType);
|
|
@@ -811,6 +1304,8 @@ export class TypeChecker {
|
|
|
811
1304
|
}
|
|
812
1305
|
|
|
813
1306
|
checkMethodDecl(decl, outer) {
|
|
1307
|
+
this.stats.bodiesChecked++;
|
|
1308
|
+
if (decl.native) this._checkNative(decl);
|
|
814
1309
|
if (
|
|
815
1310
|
(this.target === "wasm" || this.target === "both") &&
|
|
816
1311
|
(decl.async || decl.isAsync)
|
|
@@ -828,27 +1323,55 @@ export class TypeChecker {
|
|
|
828
1323
|
: decl.recvType.name;
|
|
829
1324
|
const recvNamedType = this.types.get(recvTypeName);
|
|
830
1325
|
this._injectGenericReceiverTypeParams(inner, recvNamedType);
|
|
831
|
-
const recvType = this.
|
|
1326
|
+
const recvType = this._methodReceiverType(decl, recvNamedType, outer);
|
|
832
1327
|
inner.define(
|
|
833
1328
|
decl.recvName,
|
|
834
1329
|
decl.recvPointer ? { kind: "pointer", base: recvType } : recvType,
|
|
835
1330
|
);
|
|
836
1331
|
for (const p of decl.params) {
|
|
837
|
-
inner.define(p.name, this.
|
|
1332
|
+
inner.define(p.name, this._paramType(p, inner));
|
|
1333
|
+
}
|
|
1334
|
+
const returnType = this._setupFuncReturnType(decl, inner);
|
|
1335
|
+
const savedRecvGeneric = this._recvGeneric;
|
|
1336
|
+
this._recvGeneric = recvNamedType?._generic;
|
|
1337
|
+
try {
|
|
1338
|
+
this._runFuncBody(decl, inner, returnType);
|
|
1339
|
+
} finally {
|
|
1340
|
+
this._recvGeneric = savedRecvGeneric;
|
|
838
1341
|
}
|
|
839
|
-
const returnType = this._setupFuncReturnType(decl, inner, inner);
|
|
840
|
-
this._runFuncBody(decl, inner, returnType);
|
|
841
1342
|
this._checkMissingReturn(decl, returnType, "method");
|
|
842
1343
|
}
|
|
843
1344
|
|
|
844
|
-
|
|
1345
|
+
// A variadic parameter `xs ...T` is a `[]T` inside the body (the signature
|
|
1346
|
+
// keeps the element type plus the `variadic` flag).
|
|
1347
|
+
_paramType(p, scope) {
|
|
1348
|
+
const t = this.resolveTypeNode(p.type, scope);
|
|
1349
|
+
return p.variadic ? { kind: "slice", elem: t } : t;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
_injectTypeParams(decl, inner, _outer) {
|
|
845
1353
|
if (!decl.typeParams) return;
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
inner.define(tp.name,
|
|
851
|
-
|
|
1354
|
+
// Declare every parameter first so a constraint may mention one that
|
|
1355
|
+
// comes later in the list (`[S ~[]E, E any]`).
|
|
1356
|
+
const params = decl.typeParams.map((tp) => {
|
|
1357
|
+
const t = { kind: "typeParam", name: tp.name, constraint: ANY };
|
|
1358
|
+
inner.define(tp.name, t);
|
|
1359
|
+
return t;
|
|
1360
|
+
});
|
|
1361
|
+
decl.typeParams.forEach((tp, i) => {
|
|
1362
|
+
if (tp.constraint)
|
|
1363
|
+
params[i].constraint = this.resolveTypeNode(tp.constraint, inner);
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// Inside a method of `G[T]` the receiver is the instance `G[T]` over the
|
|
1368
|
+
// receiver's own type parameters, so it is assignable to results and locals
|
|
1369
|
+
// written as `G[T]`.
|
|
1370
|
+
_methodReceiverType(decl, recvNamedType, outer) {
|
|
1371
|
+
const generic = recvNamedType?._generic;
|
|
1372
|
+
if (!generic || generic.isAlias || !generic.typeParams?.length)
|
|
1373
|
+
return this.resolveTypeNodeName(decl.recvType, outer);
|
|
1374
|
+
return this.instantiateGenericWithArgs(generic, generic.typeParams);
|
|
852
1375
|
}
|
|
853
1376
|
|
|
854
1377
|
_injectGenericReceiverTypeParams(inner, recvNamedType) {
|
|
@@ -858,15 +1381,11 @@ export class TypeChecker {
|
|
|
858
1381
|
}
|
|
859
1382
|
}
|
|
860
1383
|
|
|
861
|
-
_setupFuncReturnType(decl, inner
|
|
1384
|
+
_setupFuncReturnType(decl, inner) {
|
|
862
1385
|
let returnType = decl.returnType
|
|
863
1386
|
? this.resolveTypeNode(decl.returnType, inner)
|
|
864
1387
|
: VOID;
|
|
865
|
-
const hasNamedReturns = this._injectNamedReturns(
|
|
866
|
-
decl.returnType,
|
|
867
|
-
inner,
|
|
868
|
-
outer,
|
|
869
|
-
);
|
|
1388
|
+
const hasNamedReturns = this._injectNamedReturns(decl.returnType, inner);
|
|
870
1389
|
if (hasNamedReturns && returnType)
|
|
871
1390
|
returnType = { ...returnType, _hasNamedReturns: true };
|
|
872
1391
|
return returnType;
|
|
@@ -883,17 +1402,23 @@ export class TypeChecker {
|
|
|
883
1402
|
}
|
|
884
1403
|
|
|
885
1404
|
_checkMissingReturn(decl, returnType, kind) {
|
|
886
|
-
if (
|
|
1405
|
+
if (
|
|
1406
|
+
!decl._bodyless &&
|
|
1407
|
+
!isVoid(returnType) &&
|
|
1408
|
+
!returnType._hasNamedReturns
|
|
1409
|
+
) {
|
|
887
1410
|
if (!this._isTerminating(decl.body)) {
|
|
888
1411
|
this.err(`missing return at end of ${kind} '${decl.name}'`, decl);
|
|
889
1412
|
}
|
|
890
1413
|
}
|
|
891
1414
|
}
|
|
892
1415
|
|
|
893
|
-
|
|
1416
|
+
// Named result types resolve in the function scope, so a result of a type
|
|
1417
|
+
// parameter's type (`(r T)`) sees T.
|
|
1418
|
+
_injectNamedReturns(returnTypeNode, scope) {
|
|
894
1419
|
if (!returnTypeNode?._namedReturns) return false;
|
|
895
1420
|
for (const { name, type } of returnTypeNode._namedReturns) {
|
|
896
|
-
if (name) scope.define(name, this.resolveTypeNode(type,
|
|
1421
|
+
if (name) scope.define(name, this.resolveTypeNode(type, scope));
|
|
897
1422
|
}
|
|
898
1423
|
return true;
|
|
899
1424
|
}
|
|
@@ -904,26 +1429,19 @@ export class TypeChecker {
|
|
|
904
1429
|
for (const spec of decl.decls) {
|
|
905
1430
|
let type = spec.type ? this.resolveTypeNode(spec.type, scope) : null;
|
|
906
1431
|
if (spec.value) {
|
|
907
|
-
const
|
|
908
|
-
const isTuple = valTypes.length === 1 && valTypes[0]?.kind === "tuple";
|
|
909
|
-
const valTypesFlat = isTuple ? valTypes[0].types : valTypes;
|
|
910
|
-
if (spec.names.length !== valTypesFlat.length) {
|
|
911
|
-
const vars =
|
|
912
|
-
spec.names.length === 1
|
|
913
|
-
? "1 variable"
|
|
914
|
-
: `${spec.names.length} variables`;
|
|
915
|
-
const vals = isTuple
|
|
916
|
-
? `returns ${valTypesFlat.length} values`
|
|
917
|
-
: valTypesFlat.length === 1
|
|
918
|
-
? "1 value"
|
|
919
|
-
: `${valTypesFlat.length} values`;
|
|
920
|
-
this.err(`assignment mismatch: ${vars} but ${vals}`, spec);
|
|
921
|
-
}
|
|
1432
|
+
const valTypesFlat = this._varSpecValueTypes(spec, scope);
|
|
922
1433
|
for (let i = 0; i < spec.names.length; i++) {
|
|
923
1434
|
const vt = valTypesFlat[i] ?? ANY;
|
|
924
1435
|
const varType = type ?? defaultType(vt) ?? ANY;
|
|
1436
|
+
if (!type && spec.names.length === spec.value.length)
|
|
1437
|
+
this._checkConstDefault(vt, spec.value[i], "variable declaration");
|
|
925
1438
|
if (type)
|
|
926
|
-
this.assertAssignable(
|
|
1439
|
+
this.assertAssignable(
|
|
1440
|
+
type,
|
|
1441
|
+
vt,
|
|
1442
|
+
spec.value[i] ?? spec.value[0],
|
|
1443
|
+
"variable declaration",
|
|
1444
|
+
);
|
|
927
1445
|
scope.defineLocal(spec.names[i], varType);
|
|
928
1446
|
}
|
|
929
1447
|
} else {
|
|
@@ -934,6 +1452,45 @@ export class TypeChecker {
|
|
|
934
1452
|
this._sharedAllocOk = savedSharedAlloc;
|
|
935
1453
|
}
|
|
936
1454
|
|
|
1455
|
+
// The type of each value a `var` spec binds (a multi-value call or a
|
|
1456
|
+
// comma-ok form spreads over several names); reports a count mismatch.
|
|
1457
|
+
_varSpecValueTypes(spec, scope) {
|
|
1458
|
+
const commaOk = this._checkVarCommaOk(spec, scope);
|
|
1459
|
+
const valTypes = commaOk ?? spec.value.map((v) => this.checkExpr(v, scope));
|
|
1460
|
+
const isTuple =
|
|
1461
|
+
!commaOk && valTypes.length === 1 && valTypes[0]?.kind === "tuple";
|
|
1462
|
+
const flat = isTuple ? valTypes[0].types : valTypes;
|
|
1463
|
+
if (spec.names.length !== flat.length && !flat.some((t) => t?._tainted))
|
|
1464
|
+
this._reportAssignMismatch(spec.names.length, flat.length, isTuple, spec);
|
|
1465
|
+
return flat;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
_reportAssignMismatch(nameCount, valueCount, isTuple, node) {
|
|
1469
|
+
const vars = nameCount === 1 ? "1 variable" : `${nameCount} variables`;
|
|
1470
|
+
const vals = isTuple
|
|
1471
|
+
? `returns ${valueCount} values`
|
|
1472
|
+
: valueCount === 1
|
|
1473
|
+
? "1 value"
|
|
1474
|
+
: `${valueCount} values`;
|
|
1475
|
+
this.err(`assignment mismatch: ${vars} but ${vals}`, node);
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
// `var v, ok = m[k]` / `var v, ok = x.(T)`: the comma-ok forms yield the
|
|
1479
|
+
// value and a bool. Returns their types, or null for any other spec.
|
|
1480
|
+
_checkVarCommaOk(spec, scope) {
|
|
1481
|
+
if (spec.names.length !== 2 || spec.value.length !== 1) return null;
|
|
1482
|
+
const value = spec.value[0];
|
|
1483
|
+
if (value.kind === "TypeAssertExpr") {
|
|
1484
|
+
value._commaOk = true;
|
|
1485
|
+
spec._commaOk = true;
|
|
1486
|
+
return [this.checkExpr(value, scope), BOOL];
|
|
1487
|
+
}
|
|
1488
|
+
const stmt = { lhs: spec.names, rhs: spec.value };
|
|
1489
|
+
const types = this._checkAssignCommaOkMap(stmt, scope);
|
|
1490
|
+
if (types) spec._commaOk = true;
|
|
1491
|
+
return types;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
937
1494
|
checkConstDecl(decl, scope) {
|
|
938
1495
|
for (const spec of decl.decls) {
|
|
939
1496
|
const valTypes = spec.value.map((v) => this.checkExpr(v, scope));
|
|
@@ -944,6 +1501,15 @@ export class TypeChecker {
|
|
|
944
1501
|
for (let i = 0; i < spec.names.length; i++) {
|
|
945
1502
|
const name = spec.names[i];
|
|
946
1503
|
const expr = spec.value?.[i] ?? spec.value?.[0] ?? null;
|
|
1504
|
+
if (spec.type && expr)
|
|
1505
|
+
this._checkConstOverflow(
|
|
1506
|
+
type,
|
|
1507
|
+
valTypes[i] ?? valTypes[0],
|
|
1508
|
+
expr,
|
|
1509
|
+
"constant declaration",
|
|
1510
|
+
);
|
|
1511
|
+
// `const D Duration = 1` is a BigInt in JS.
|
|
1512
|
+
if (spec.type && expr) this._tag64BitNode(this.resolveType(type), expr);
|
|
947
1513
|
scope.defineConst(name, type, expr);
|
|
948
1514
|
}
|
|
949
1515
|
}
|
|
@@ -957,3 +1523,4 @@ Object.assign(TypeChecker.prototype, expressionCheckMethods);
|
|
|
957
1523
|
Object.assign(TypeChecker.prototype, terminationMethods);
|
|
958
1524
|
Object.assign(TypeChecker.prototype, resolveMethods);
|
|
959
1525
|
Object.assign(TypeChecker.prototype, assignabilityMethods);
|
|
1526
|
+
Object.assign(TypeChecker.prototype, constantCheckMethods);
|