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/parser/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Parser type-expression methods — installed as a mixin on Parser.prototype.
|
|
2
2
|
|
|
3
|
-
import { T } from "../tokens.js";
|
|
3
|
+
import { T } from "../lexer/tokens.js";
|
|
4
4
|
|
|
5
5
|
// Token types that directly map to parseTypeName().
|
|
6
6
|
const TYPE_NAME_TOKENS = new Set([
|
|
@@ -76,8 +76,10 @@ export const typeParserMethods = {
|
|
|
76
76
|
i++;
|
|
77
77
|
continue;
|
|
78
78
|
}
|
|
79
|
-
// Reject literals — it's an index/array expression
|
|
80
|
-
|
|
79
|
+
// Reject literals — it's an index/array expression. A literal nested
|
|
80
|
+
// deeper is an array length inside a type argument: Stack[[2]int].
|
|
81
|
+
if (depth === 1 && (tt === T.INT || tt === T.FLOAT || tt === T.STRING))
|
|
82
|
+
return false;
|
|
81
83
|
i++;
|
|
82
84
|
}
|
|
83
85
|
return depth === 0;
|
|
@@ -86,11 +88,17 @@ export const typeParserMethods = {
|
|
|
86
88
|
parseTypeParamList() {
|
|
87
89
|
this.expect(T.LBRACKET);
|
|
88
90
|
const params = [];
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
+
while (!this.check(T.RBRACKET) && !this.check(T.EOF)) {
|
|
92
|
+
const names = [this.expect(T.IDENT).value];
|
|
93
|
+
while (this.match(T.COMMA)) {
|
|
94
|
+
names.push(this.expect(T.IDENT).value);
|
|
95
|
+
}
|
|
91
96
|
const constraint = this.parseConstraint();
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
for (const name of names) {
|
|
98
|
+
params.push({ kind: "TypeParam", name, constraint });
|
|
99
|
+
}
|
|
100
|
+
if (!this.match(T.COMMA)) break;
|
|
101
|
+
}
|
|
94
102
|
this.expect(T.RBRACKET);
|
|
95
103
|
return params;
|
|
96
104
|
},
|
|
@@ -185,7 +193,7 @@ export const typeParserMethods = {
|
|
|
185
193
|
}
|
|
186
194
|
const type = {
|
|
187
195
|
kind: "PointerType",
|
|
188
|
-
base:
|
|
196
|
+
base: this._embeddedTypeName(typeName, true),
|
|
189
197
|
};
|
|
190
198
|
let tag = null;
|
|
191
199
|
if (this.check(T.STRING)) {
|
|
@@ -195,6 +203,7 @@ export const typeParserMethods = {
|
|
|
195
203
|
names: [],
|
|
196
204
|
type,
|
|
197
205
|
embedded: true,
|
|
206
|
+
embeddedName: type.base.name,
|
|
198
207
|
tag,
|
|
199
208
|
});
|
|
200
209
|
this.semi();
|
|
@@ -206,14 +215,38 @@ export const typeParserMethods = {
|
|
|
206
215
|
if (names.length === 1 && this.check(T.DOT)) {
|
|
207
216
|
this.advance();
|
|
208
217
|
const typeName = `${names[0]}.${this.expect(T.IDENT).value}`;
|
|
218
|
+
const embeddedType = this._embeddedTypeName(typeName, true);
|
|
209
219
|
let tag = null;
|
|
210
220
|
if (this.check(T.STRING)) {
|
|
211
221
|
tag = this.advance().value;
|
|
212
222
|
}
|
|
213
223
|
fields.push({
|
|
214
224
|
names: [],
|
|
215
|
-
type:
|
|
225
|
+
type: embeddedType,
|
|
216
226
|
embedded: true,
|
|
227
|
+
embeddedName: typeName,
|
|
228
|
+
tag,
|
|
229
|
+
});
|
|
230
|
+
this.semi();
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Embedded generic instance: Inner[T] (not an array field `a [N]T`)
|
|
235
|
+
if (
|
|
236
|
+
names.length === 1 &&
|
|
237
|
+
this.check(T.LBRACKET) &&
|
|
238
|
+
this._bracketEndsEmbeddedField()
|
|
239
|
+
) {
|
|
240
|
+
const embeddedType = this._embeddedTypeName(names[0], true);
|
|
241
|
+
let tag = null;
|
|
242
|
+
if (this.check(T.STRING)) {
|
|
243
|
+
tag = this.advance().value;
|
|
244
|
+
}
|
|
245
|
+
fields.push({
|
|
246
|
+
names: [],
|
|
247
|
+
type: embeddedType,
|
|
248
|
+
embedded: true,
|
|
249
|
+
embeddedName: names[0],
|
|
217
250
|
tag,
|
|
218
251
|
});
|
|
219
252
|
this.semi();
|
|
@@ -236,6 +269,7 @@ export const typeParserMethods = {
|
|
|
236
269
|
names: [],
|
|
237
270
|
type: { kind: "TypeName", name: typeName },
|
|
238
271
|
embedded: true,
|
|
272
|
+
embeddedName: typeName,
|
|
239
273
|
tag,
|
|
240
274
|
});
|
|
241
275
|
this.semi();
|
|
@@ -255,6 +289,33 @@ export const typeParserMethods = {
|
|
|
255
289
|
return { kind: "StructType", fields };
|
|
256
290
|
},
|
|
257
291
|
|
|
292
|
+
// True when the `[...]` at the cursor is followed by the end of a field
|
|
293
|
+
// (`Inner[T]` embedded), rather than an element type (`a [N]T`).
|
|
294
|
+
_bracketEndsEmbeddedField() {
|
|
295
|
+
let depth = 0;
|
|
296
|
+
for (let i = this.pos; i < this.tokens.length; i++) {
|
|
297
|
+
const tt = this.tokens[i].type;
|
|
298
|
+
if (tt === T.LBRACKET) depth++;
|
|
299
|
+
else if (tt === T.RBRACKET && --depth === 0) {
|
|
300
|
+
const next = this.tokens[i + 1]?.type;
|
|
301
|
+
return next === T.SEMICOLON || next === T.RBRACE || next === T.STRING;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return false;
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
// Embedded field type: `name`, or `name[Args]` when type arguments follow.
|
|
308
|
+
_embeddedTypeName(name, allowArgs) {
|
|
309
|
+
if (allowArgs && this.check(T.LBRACKET)) {
|
|
310
|
+
this.expect(T.LBRACKET);
|
|
311
|
+
const typeArgs = [this.parseType()];
|
|
312
|
+
while (this.match(T.COMMA)) typeArgs.push(this.parseType());
|
|
313
|
+
this.expect(T.RBRACKET);
|
|
314
|
+
return { kind: "GenericTypeName", name, typeArgs };
|
|
315
|
+
}
|
|
316
|
+
return { kind: "TypeName", name };
|
|
317
|
+
},
|
|
318
|
+
|
|
258
319
|
parseInterfaceType() {
|
|
259
320
|
this.expect(T.INTERFACE);
|
|
260
321
|
this.expect(T.LBRACE);
|
package/src/resolver.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
14
14
|
import { dirname, join, resolve } from "node:path";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
15
16
|
|
|
16
17
|
// Walk up from startDir looking for a node_modules directory
|
|
17
18
|
function findNodeModules(startDir) {
|
|
@@ -25,6 +26,7 @@ function findNodeModules(startDir) {
|
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
import { EXCLUDED_STDLIB_PACKAGES } from "./typechecker/stdlib/excluded.js";
|
|
28
30
|
import { STDLIB_PACKAGES } from "./typechecker/types.js";
|
|
29
31
|
|
|
30
32
|
// Returns true for local relative paths (start with ./ or ../)
|
|
@@ -36,6 +38,22 @@ export function isBuiltinPackage(importPath) {
|
|
|
36
38
|
return STDLIB_PACKAGES.has(importPath);
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
// Go-source stdlib: `stdlib/<import path>/*.go`, written in GoFront Go
|
|
42
|
+
// and compiled into the caller's target.
|
|
43
|
+
const STDLIB_SOURCE_ROOT = fileURLToPath(
|
|
44
|
+
new URL("../stdlib/", import.meta.url),
|
|
45
|
+
);
|
|
46
|
+
const STDLIB_PATH_RE = /^[a-z0-9]+(\/[a-z0-9]+)*$/;
|
|
47
|
+
|
|
48
|
+
// Returns the source directory for a Go-source stdlib import path, or null.
|
|
49
|
+
// `root` replaces stdlib/ (tests use a temporary tree).
|
|
50
|
+
export function resolveStdlibSource(importPath, root = null) {
|
|
51
|
+
if (!STDLIB_PATH_RE.test(importPath)) return null;
|
|
52
|
+
const dir = join(root ?? STDLIB_SOURCE_ROOT, importPath);
|
|
53
|
+
if (!existsSync(dir)) return null;
|
|
54
|
+
return readdirSync(dir).some((f) => f.endsWith(".go")) ? dir : null;
|
|
55
|
+
}
|
|
56
|
+
|
|
39
57
|
// Resolve a local relative import path to an absolute directory of .go files.
|
|
40
58
|
// Returns the absolute dir path, or null if it doesn't contain any .go files.
|
|
41
59
|
export function resolveGwDir(importPath, fromFile) {
|
|
@@ -86,14 +104,16 @@ function resolveImport(importPath, fromFile) {
|
|
|
86
104
|
|
|
87
105
|
// Given an AST's import list, resolve all imports.
|
|
88
106
|
// Returns Map<importPath, { dtsPath, importPath, types, values }>
|
|
89
|
-
export function resolveAll(imports, fromFile, parseDts) {
|
|
107
|
+
export function resolveAll(imports, fromFile, parseDts, stdlibRoot = null) {
|
|
90
108
|
const resolved = new Map();
|
|
91
109
|
|
|
92
110
|
for (const imp of imports) {
|
|
93
111
|
for (const { path } of imp.imports) {
|
|
94
112
|
if (path.startsWith("js:")) continue; // handled separately
|
|
95
113
|
if (isLocalPath(path)) continue; // local .go packages — handled by compiler
|
|
114
|
+
if (EXCLUDED_STDLIB_PACKAGES.has(path)) continue; // the checker reports it
|
|
96
115
|
if (isBuiltinPackage(path)) continue; // built-in stdlib namespaces — always available
|
|
116
|
+
if (resolveStdlibSource(path, stdlibRoot)) continue; // Go-source stdlib — handled by compiler
|
|
97
117
|
|
|
98
118
|
if (resolved.has(path)) continue;
|
|
99
119
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Assignability, binary result types, and interface satisfaction.
|
|
2
2
|
// Installed as a mixin on TypeChecker.prototype.
|
|
3
3
|
|
|
4
|
+
import { constIntValue } from "./const-fold.js";
|
|
5
|
+
import { exprText, operandKind } from "./operand-text.js";
|
|
4
6
|
import { sharedInfo } from "./stdlib/shared.js";
|
|
5
7
|
import {
|
|
6
8
|
ANY,
|
|
@@ -8,9 +10,12 @@ import {
|
|
|
8
10
|
CMP_OPS,
|
|
9
11
|
COMPLEX128,
|
|
10
12
|
FLOAT64,
|
|
13
|
+
is64BitInt,
|
|
11
14
|
isAny,
|
|
12
15
|
isComplex,
|
|
16
|
+
isComplex64,
|
|
13
17
|
isComplexOrNumeric,
|
|
18
|
+
isInteger,
|
|
14
19
|
isNil,
|
|
15
20
|
isNumeric,
|
|
16
21
|
isPointer,
|
|
@@ -79,6 +84,8 @@ export const assignabilityMethods = {
|
|
|
79
84
|
if (isAny(lt) || isAny(rt)) return ANY;
|
|
80
85
|
if (isComplex(lt) || isComplex(rt))
|
|
81
86
|
return this._binaryResultTypeComplex(op, lt, rt, node);
|
|
87
|
+
if ((op === "<<" || op === ">>") && isNumeric(lt) && isNumeric(rt))
|
|
88
|
+
return this._binaryResultTypeShift(op, lt, rt, node);
|
|
82
89
|
if (isNumeric(lt) && isNumeric(rt))
|
|
83
90
|
return this._binaryResultTypeNumeric(lt, rt);
|
|
84
91
|
if (isString(lt) && isString(rt) && op === "+")
|
|
@@ -88,6 +95,37 @@ export const assignabilityMethods = {
|
|
|
88
95
|
return ANY;
|
|
89
96
|
},
|
|
90
97
|
|
|
98
|
+
// A shift has the type of its left operand; the count may be any integer
|
|
99
|
+
// type or an untyped constant representable as uint.
|
|
100
|
+
_binaryResultTypeShift(_op, lt, rt, node) {
|
|
101
|
+
if (node) this.checkShiftCount(rt, node.right);
|
|
102
|
+
if (node && lt.kind !== "untyped" && !isInteger(lt))
|
|
103
|
+
this.err(`shifted operand ${typeStr(lt)} must be integer`, node.left);
|
|
104
|
+
return lt;
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
// Reports a shift count that is not an integer or is a negative constant.
|
|
108
|
+
checkShiftCount(rt, countExpr) {
|
|
109
|
+
const v = this._constValue(countExpr);
|
|
110
|
+
if (v) {
|
|
111
|
+
const n = constIntValue(v);
|
|
112
|
+
if (n === null) {
|
|
113
|
+
this.err(
|
|
114
|
+
`invalid shift count ${typeStr(rt)} constant: not an integer`,
|
|
115
|
+
countExpr,
|
|
116
|
+
);
|
|
117
|
+
} else if (n < 0n) {
|
|
118
|
+
this.err(`invalid shift count: ${n} is negative`, countExpr);
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (!isInteger(rt))
|
|
123
|
+
this.err(
|
|
124
|
+
`invalid shift count: ${typeStr(rt)} is not an integer type`,
|
|
125
|
+
countExpr,
|
|
126
|
+
);
|
|
127
|
+
},
|
|
128
|
+
|
|
91
129
|
_binaryResultTypeString(lt, rt) {
|
|
92
130
|
if (lt.kind === "untyped" && rt.kind === "untyped") return lt;
|
|
93
131
|
if (lt.kind === "untyped") return rt;
|
|
@@ -97,6 +135,7 @@ export const assignabilityMethods = {
|
|
|
97
135
|
|
|
98
136
|
_binaryResultTypeCmp(op, lt, rt, node) {
|
|
99
137
|
if (this._checkComplexCmpOp(op, lt, rt, node)) return ANY;
|
|
138
|
+
this._checkIntegerCmpKinds(op, lt, rt, node);
|
|
100
139
|
this._checkEqualityComparable(op, lt, rt, node);
|
|
101
140
|
return BOOL;
|
|
102
141
|
},
|
|
@@ -111,6 +150,9 @@ export const assignabilityMethods = {
|
|
|
111
150
|
return ANY;
|
|
112
151
|
}
|
|
113
152
|
if (lt.kind === "untyped" && rt.kind === "untyped") return UNTYPED_COMPLEX;
|
|
153
|
+
if (lt.kind === "untyped") return rt;
|
|
154
|
+
if (rt.kind === "untyped") return lt;
|
|
155
|
+
if (isComplex64(lt) && isComplex64(rt)) return lt;
|
|
114
156
|
return COMPLEX128;
|
|
115
157
|
},
|
|
116
158
|
|
|
@@ -134,6 +176,17 @@ export const assignabilityMethods = {
|
|
|
134
176
|
return isFloat ? FLOAT64 : lt;
|
|
135
177
|
},
|
|
136
178
|
|
|
179
|
+
// Comparing two typed integers of different types (`byte != int`) is a
|
|
180
|
+
// mismatch in Go; untyped constants adapt to either side.
|
|
181
|
+
_checkIntegerCmpKinds(op, lt, rt, node) {
|
|
182
|
+
if (!node || lt.kind === "untyped" || rt.kind === "untyped") return;
|
|
183
|
+
if (!isInteger(lt) || !isInteger(rt) || typeStr(lt) === typeStr(rt)) return;
|
|
184
|
+
this.err(
|
|
185
|
+
`invalid operation: ${exprText(node)} (mismatched types ${typeStr(lt)} and ${typeStr(rt)})`,
|
|
186
|
+
node,
|
|
187
|
+
);
|
|
188
|
+
},
|
|
189
|
+
|
|
137
190
|
_checkComplexCmpOp(op, lt, rt, node) {
|
|
138
191
|
if (isComplex(lt) || isComplex(rt)) {
|
|
139
192
|
if (op !== "==" && op !== "!=") {
|
|
@@ -165,12 +218,68 @@ export const assignabilityMethods = {
|
|
|
165
218
|
|
|
166
219
|
// ── Assignability ─────────────────────────────────────────────────
|
|
167
220
|
|
|
168
|
-
|
|
221
|
+
_tag64BitNode(target, node) {
|
|
222
|
+
if (node && typeof node === "object" && is64BitInt(target)) {
|
|
223
|
+
node._is64Bit = true;
|
|
224
|
+
if (node.kind === "KeyValueExpr" && node.value)
|
|
225
|
+
node.value._is64Bit = true;
|
|
226
|
+
if (node.kind === "UnaryExpr" && node.operand)
|
|
227
|
+
node.operand._is64Bit = true;
|
|
228
|
+
if (node.kind === "ParenExpr" && node.expr) node.expr._is64Bit = true;
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
// `var m uint64 = (1 << k) - 1`: a non-constant shift whose left operand is
|
|
233
|
+
// an untyped constant takes the type its context gives it, so codegen
|
|
234
|
+
// computes it in that type (BigInt for 64-bit integers).
|
|
235
|
+
_retypeUntypedShift(target, source, valueNode) {
|
|
236
|
+
if (!isUntyped(source) || !isInteger(target)) return;
|
|
237
|
+
this._retypeShiftTree(valueNode, target);
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
_retypeShiftTree(e, target) {
|
|
241
|
+
if (!e || typeof e !== "object" || e._type?.kind !== "untyped") return;
|
|
242
|
+
if (e.kind === "ParenExpr") this._retypeShiftTree(e.expr, target);
|
|
243
|
+
else if (e.kind === "UnaryExpr") this._retypeShiftTree(e.operand, target);
|
|
244
|
+
else if (e.kind === "BinaryExpr" && !this._constValue(e)) {
|
|
245
|
+
const isShift = e.op === "<<" || e.op === ">>";
|
|
246
|
+
this._retypeShiftTree(e.left, target);
|
|
247
|
+
if (isShift && e.left._type?.kind === "untyped") {
|
|
248
|
+
e.left._type = target;
|
|
249
|
+
if (is64BitInt(target)) e.left._is64Bit = true;
|
|
250
|
+
} else if (!isShift) this._retypeShiftTree(e.right, target);
|
|
251
|
+
} else return;
|
|
252
|
+
if (this._constValue(e)) return;
|
|
253
|
+
e._type = target;
|
|
254
|
+
if (is64BitInt(target)) e._is64Bit = true;
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
// An untyped real constant flowing into a complex type becomes a complex
|
|
258
|
+
// value in codegen.
|
|
259
|
+
_tagComplexConst(target, source, valueNode) {
|
|
260
|
+
if (
|
|
261
|
+
valueNode &&
|
|
262
|
+
typeof valueNode === "object" &&
|
|
263
|
+
isComplex(target) &&
|
|
264
|
+
isUntyped(source) &&
|
|
265
|
+
!isComplex(source)
|
|
266
|
+
)
|
|
267
|
+
valueNode._asComplex = true;
|
|
268
|
+
},
|
|
269
|
+
|
|
270
|
+
// `operand` is the expression whose value flows in, when `node` (the
|
|
271
|
+
// position and codegen tag) is not that expression.
|
|
272
|
+
assertAssignable(target, source, node, ctx = "assignment", operand = node) {
|
|
169
273
|
if (!target || !source) return;
|
|
274
|
+
if (this._checkConstOverflow(target, source, node, ctx)) return;
|
|
170
275
|
target = this.resolveType(target);
|
|
171
276
|
source = this.resolveType(source);
|
|
277
|
+
this._tag64BitNode(target, node);
|
|
278
|
+
this._retypeUntypedShift(target, source, operand ?? node);
|
|
279
|
+
this._tagComplexConst(target, source, operand ?? node);
|
|
172
280
|
this._markIfaceBox(target, source, node);
|
|
173
281
|
this._checkWasmIfaceProvider(target, source, node);
|
|
282
|
+
if (this._checkIfaceToConcrete(target, source, node, ctx, operand)) return;
|
|
174
283
|
if (this._assertAssignableEarlyReturn(target, source)) return;
|
|
175
284
|
if (isPointer(target) && isPointer(source)) {
|
|
176
285
|
this.assertAssignable(
|
|
@@ -181,7 +290,7 @@ export const assignabilityMethods = {
|
|
|
181
290
|
return;
|
|
182
291
|
}
|
|
183
292
|
if (isUntyped(source) && this._isUntypedAssignable(target, source)) return;
|
|
184
|
-
if (this.
|
|
293
|
+
if (this._checkNumericCoercion(target, source, node, ctx, operand)) return;
|
|
185
294
|
if (this._checkArrayAssignable(target, source, node)) return;
|
|
186
295
|
if (this._checkTypedArrayAssignable(target, source, node)) return;
|
|
187
296
|
if (this._checkFuncAssignable(target, source, node)) return;
|
|
@@ -201,23 +310,32 @@ export const assignabilityMethods = {
|
|
|
201
310
|
)
|
|
202
311
|
return;
|
|
203
312
|
if (target.kind === "typeParam") return;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
);
|
|
313
|
+
let tBase = this.resolveType(target);
|
|
314
|
+
if (tBase?.kind === "named") tBase = this.resolveType(tBase.underlying);
|
|
207
315
|
if (!isAny(target) && tBase?.kind !== "interface") return;
|
|
316
|
+
// The static type behind the interface value: codegen derives the
|
|
317
|
+
// fmt metadata (Stringer elements of slices and maps) from it.
|
|
318
|
+
node._ifaceSrc = source;
|
|
319
|
+
const meta = this._ifaceBoxMeta(source);
|
|
320
|
+
if (!meta) return;
|
|
321
|
+
node._ifaceBox = meta.box;
|
|
322
|
+
if (meta.type) node._ifaceNamedType = meta.type;
|
|
323
|
+
if (meta.ptrFields) node._ifacePtrFields = meta.ptrFields;
|
|
324
|
+
},
|
|
325
|
+
|
|
326
|
+
// How a value of static type `source` is boxed on entering an interface:
|
|
327
|
+
// { box, type? (named kinds), ptrFields? }, or null when it stays raw.
|
|
328
|
+
// Shared by _markIfaceBox and the JS backend's multi-value fmt operands.
|
|
329
|
+
_ifaceBoxMeta(source) {
|
|
208
330
|
const named = this._namedMethodBoxSource(source);
|
|
209
|
-
if (named)
|
|
210
|
-
|
|
211
|
-
node._ifaceNamedType = named.type;
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
331
|
+
if (named)
|
|
332
|
+
return { box: named.ptr ? "named_ptr" : "named", type: named.type };
|
|
214
333
|
const sBase = this.resolveType(
|
|
215
334
|
source.kind === "named" ? source.underlying : source,
|
|
216
335
|
);
|
|
217
336
|
const info = this._classifyIfaceBox(sBase);
|
|
218
|
-
if (!info) return;
|
|
219
|
-
|
|
220
|
-
if (info.ptrFields) node._ifacePtrFields = info.ptrFields;
|
|
337
|
+
if (!info) return null;
|
|
338
|
+
return { box: info.box, ptrFields: info.ptrFields };
|
|
221
339
|
},
|
|
222
340
|
|
|
223
341
|
// A named non-struct type with methods (or a pointer to one) entering an
|
|
@@ -225,14 +343,26 @@ export const assignabilityMethods = {
|
|
|
225
343
|
// dynamic method dispatch works.
|
|
226
344
|
_namedMethodBoxSource(source) {
|
|
227
345
|
const isPtr = source?.kind === "pointer";
|
|
228
|
-
|
|
346
|
+
let t = isPtr ? source.base : source;
|
|
347
|
+
if (t?.kind === "TypeName" || t?.kind === "Ident") t = this.resolveType(t);
|
|
229
348
|
if (t?.kind !== "named") return null;
|
|
230
349
|
const u = this.resolveType(t.underlying);
|
|
231
350
|
if (!u || u.kind === "struct" || u.kind === "interface") return null;
|
|
232
351
|
const methods = t.methods ?? t._generic?.methods;
|
|
352
|
+
// A user-declared named basic type without methods is boxed as well, so
|
|
353
|
+
// a type assertion to its underlying type fails like Go.
|
|
354
|
+
if (
|
|
355
|
+
u.kind === "basic" &&
|
|
356
|
+
!t._generic &&
|
|
357
|
+
t.methods instanceof Map &&
|
|
358
|
+
t.methods.size === 0 &&
|
|
359
|
+
t._target !== undefined &&
|
|
360
|
+
t._target !== "wasm"
|
|
361
|
+
)
|
|
362
|
+
return { type: t, ptr: isPtr };
|
|
233
363
|
if (!methods || methods.size === 0) return null;
|
|
234
364
|
// Only user-declared types (methods carry _ptrRecv); stdlib named types
|
|
235
|
-
// such as
|
|
365
|
+
// such as strings.Builder have no box class.
|
|
236
366
|
if (![...methods.values()].some((m) => Object.hasOwn(m ?? {}, "_ptrRecv")))
|
|
237
367
|
return null;
|
|
238
368
|
return { type: t, ptr: isPtr };
|
|
@@ -311,6 +441,28 @@ export const assignabilityMethods = {
|
|
|
311
441
|
);
|
|
312
442
|
},
|
|
313
443
|
|
|
444
|
+
// An interface-typed operand needs an explicit type assertion before it can
|
|
445
|
+
// flow into a concrete (non-interface) type.
|
|
446
|
+
_checkIfaceToConcrete(target, source, node, ctx, operand) {
|
|
447
|
+
if (!source?._declared || !operand || typeof operand !== "object")
|
|
448
|
+
return false;
|
|
449
|
+
if (!this._isConcreteTarget(target)) return false;
|
|
450
|
+
this.err(
|
|
451
|
+
`cannot use ${exprText(operand)} (${operandKind(operand, typeStr(source))}) as ${typeStr(target)} value in ${ctx}: need type assertion`,
|
|
452
|
+
node,
|
|
453
|
+
);
|
|
454
|
+
return true;
|
|
455
|
+
},
|
|
456
|
+
|
|
457
|
+
_isConcreteTarget(target) {
|
|
458
|
+
if (!target || target.kind === "typeParam" || isAny(target)) return false;
|
|
459
|
+
if (isNil(target) || isUntyped(target)) return false;
|
|
460
|
+
const base = this.resolveType(
|
|
461
|
+
target.kind === "named" ? target.underlying : target,
|
|
462
|
+
);
|
|
463
|
+
return !!base && base.kind !== "interface" && base.kind !== "typeParam";
|
|
464
|
+
},
|
|
465
|
+
|
|
314
466
|
_assertAssignableEarlyReturn(target, source) {
|
|
315
467
|
return (
|
|
316
468
|
target?.kind === "typeParam" ||
|
|
@@ -351,7 +503,45 @@ export const assignabilityMethods = {
|
|
|
351
503
|
return;
|
|
352
504
|
}
|
|
353
505
|
if (this._identicalUnderlyingLiteral(target, source, tBase, sBase)) return;
|
|
354
|
-
this.err(
|
|
506
|
+
this.err(
|
|
507
|
+
`Cannot assign ${typeStr(source)} to ${typeStr(target)}`,
|
|
508
|
+
node,
|
|
509
|
+
this._seqToSliceHint(source, tBase, node),
|
|
510
|
+
);
|
|
511
|
+
},
|
|
512
|
+
|
|
513
|
+
// maps.Keys/Values (and every iter.Seq) no longer return a slice: point the
|
|
514
|
+
// slice-expecting caller at slices.Collect.
|
|
515
|
+
_seqToSliceHint(source, tBase, node) {
|
|
516
|
+
if (tBase?.kind !== "slice" || !this._isIterSeq(source)) return null;
|
|
517
|
+
return this._seqCollectHint(node);
|
|
518
|
+
},
|
|
519
|
+
|
|
520
|
+
_isIterSeq(t) {
|
|
521
|
+
return t?.kind === "named" && /^iter\.Seq\d*\[/.test(typeStr(t));
|
|
522
|
+
},
|
|
523
|
+
|
|
524
|
+
_seqCollectHint(node) {
|
|
525
|
+
const f = node?.kind === "CallExpr" ? node.func : null;
|
|
526
|
+
if (
|
|
527
|
+
f?.kind === "SelectorExpr" &&
|
|
528
|
+
f.expr?.kind === "Ident" &&
|
|
529
|
+
f.expr.name === "maps" &&
|
|
530
|
+
(f.field === "Keys" || f.field === "Values")
|
|
531
|
+
)
|
|
532
|
+
return `use slices.Collect(maps.${f.field}(m)) for a slice`;
|
|
533
|
+
return "use slices.Collect(seq) for a slice";
|
|
534
|
+
},
|
|
535
|
+
|
|
536
|
+
// len/cap, spread and slice conversion are lenient on func types, and an
|
|
537
|
+
// iter.Seq value is one: reject it there instead of miscompiling.
|
|
538
|
+
_rejectSeqAsSlice(type, node, use) {
|
|
539
|
+
if (!this._isIterSeq(type)) return;
|
|
540
|
+
this.err(
|
|
541
|
+
`cannot use ${typeStr(type)} in ${use}`,
|
|
542
|
+
node,
|
|
543
|
+
this._seqCollectHint(node),
|
|
544
|
+
);
|
|
355
545
|
},
|
|
356
546
|
|
|
357
547
|
// Identical underlying types are assignable when one side is an unnamed
|
|
@@ -377,18 +567,61 @@ export const assignabilityMethods = {
|
|
|
377
567
|
);
|
|
378
568
|
},
|
|
379
569
|
|
|
570
|
+
// A value computed from a generic call (`3 + Sum[float64](1, 2)`) has the
|
|
571
|
+
// exact instantiated type, so the loose int/float64 promotion that plain
|
|
572
|
+
// code enjoys does not apply to it.
|
|
573
|
+
_isGenericCallResult(node) {
|
|
574
|
+
if (!node || typeof node !== "object") return false;
|
|
575
|
+
if (node.kind === "ParenExpr") return this._isGenericCallResult(node.expr);
|
|
576
|
+
if (node.kind === "BinaryExpr")
|
|
577
|
+
return (
|
|
578
|
+
this._isGenericCallResult(node.left) ||
|
|
579
|
+
this._isGenericCallResult(node.right)
|
|
580
|
+
);
|
|
581
|
+
return node.kind === "CallExpr" && Boolean(node.func?._genericFnType);
|
|
582
|
+
},
|
|
583
|
+
|
|
380
584
|
_isNumericCoercible(target, source) {
|
|
381
585
|
if (target.kind !== "basic" || source.kind !== "basic") return false;
|
|
382
586
|
const pair = `${target.name}:${source.name}`;
|
|
383
587
|
return (
|
|
384
588
|
pair === "float64:int" ||
|
|
385
|
-
pair === "int:float64" ||
|
|
386
589
|
// rune was historically int in GoFront; keep rune/int interchangeable.
|
|
387
590
|
pair === "int:int32" ||
|
|
388
591
|
pair === "int32:int"
|
|
389
592
|
);
|
|
390
593
|
},
|
|
391
594
|
|
|
595
|
+
// The lenient numeric pairs: accepted, except that a generic call result
|
|
596
|
+
// is a mismatch and a float64 never reaches an int implicitly.
|
|
597
|
+
// Returns true when the pair was handled.
|
|
598
|
+
_checkNumericCoercion(target, source, node, ctx, operand) {
|
|
599
|
+
const floatToInt = this._isFloatToInt(target, source);
|
|
600
|
+
if (!floatToInt && !this._isNumericCoercible(target, source)) return false;
|
|
601
|
+
if (this._isGenericCallResult(node))
|
|
602
|
+
this.err(
|
|
603
|
+
`mismatched types ${typeStr(source)} and ${typeStr(target)} in ${ctx}`,
|
|
604
|
+
node,
|
|
605
|
+
);
|
|
606
|
+
else if (floatToInt)
|
|
607
|
+
this.err(
|
|
608
|
+
`cannot use ${exprText(operand)} (${operandKind(operand, typeStr(source))}) as ${typeStr(target)} value in ${ctx}`,
|
|
609
|
+
node,
|
|
610
|
+
);
|
|
611
|
+
return true;
|
|
612
|
+
},
|
|
613
|
+
|
|
614
|
+
// Go has no implicit float64 -> int conversion; only an untyped constant
|
|
615
|
+
// (handled earlier) or an explicit `int(x)` reaches an int.
|
|
616
|
+
_isFloatToInt(target, source) {
|
|
617
|
+
return (
|
|
618
|
+
target.kind === "basic" &&
|
|
619
|
+
source.kind === "basic" &&
|
|
620
|
+
target.name === "int" &&
|
|
621
|
+
source.name === "float64"
|
|
622
|
+
);
|
|
623
|
+
},
|
|
624
|
+
|
|
392
625
|
_checkArrayAssignable(target, source, node) {
|
|
393
626
|
if (target.kind === "array" && source.kind === "array") {
|
|
394
627
|
if (
|
|
@@ -465,10 +698,47 @@ export const assignabilityMethods = {
|
|
|
465
698
|
|
|
466
699
|
// ── Interface satisfaction ────────────────────────────────────────
|
|
467
700
|
|
|
701
|
+
// `interface{}` and `any` are the same type, but print differently.
|
|
702
|
+
_sigTypeStr(t) {
|
|
703
|
+
switch (t?.kind) {
|
|
704
|
+
case "interface":
|
|
705
|
+
if (
|
|
706
|
+
!t.name &&
|
|
707
|
+
t.methods.size === 0 &&
|
|
708
|
+
!t.unionConstraint &&
|
|
709
|
+
!t._typeSetEmbeds
|
|
710
|
+
)
|
|
711
|
+
return "any";
|
|
712
|
+
return typeStr(t);
|
|
713
|
+
case "slice":
|
|
714
|
+
return `[]${this._sigTypeStr(t.elem)}`;
|
|
715
|
+
case "array":
|
|
716
|
+
return `[${t.size ?? "..."}]${this._sigTypeStr(t.elem)}`;
|
|
717
|
+
case "pointer":
|
|
718
|
+
return `*${this._sigTypeStr(t.base)}`;
|
|
719
|
+
case "map":
|
|
720
|
+
return `map[${this._sigTypeStr(t.key)}]${this._sigTypeStr(t.value)}`;
|
|
721
|
+
case "tuple":
|
|
722
|
+
return `(${t.types.map((x) => this._sigTypeStr(x)).join(", ")})`;
|
|
723
|
+
case "func": {
|
|
724
|
+
const ret =
|
|
725
|
+
t.returns.length > 1
|
|
726
|
+
? `(${t.returns.map((x) => this._sigTypeStr(x)).join(", ")})`
|
|
727
|
+
: t.returns[0]
|
|
728
|
+
? this._sigTypeStr(t.returns[0])
|
|
729
|
+
: "void";
|
|
730
|
+
return `func(${t.params.map((x) => this._sigTypeStr(x)).join(", ")}) ${ret}`;
|
|
731
|
+
}
|
|
732
|
+
default:
|
|
733
|
+
return typeStr(t);
|
|
734
|
+
}
|
|
735
|
+
},
|
|
736
|
+
|
|
468
737
|
_sigParamsMatch(reqList, actList) {
|
|
469
738
|
if (reqList.length !== actList.length) return false;
|
|
470
739
|
for (let i = 0; i < reqList.length; i++) {
|
|
471
|
-
if (
|
|
740
|
+
if (this._sigTypeStr(reqList[i]) !== this._sigTypeStr(actList[i]))
|
|
741
|
+
return false;
|
|
472
742
|
}
|
|
473
743
|
return true;
|
|
474
744
|
},
|
|
@@ -511,7 +781,9 @@ export const assignabilityMethods = {
|
|
|
511
781
|
}
|
|
512
782
|
const methodMap =
|
|
513
783
|
base?.kind === "struct"
|
|
514
|
-
? base.methods
|
|
784
|
+
? base.methods?.size
|
|
785
|
+
? base.methods
|
|
786
|
+
: srcType.methods || base.methods
|
|
515
787
|
: srcType.kind === "named"
|
|
516
788
|
? srcType.methods
|
|
517
789
|
: null;
|