gofront 1.8.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +6 -5
- package/src/backend/js/expressions.js +1157 -408
- package/src/backend/js/index.js +327 -153
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +559 -2552
- package/src/backend/js/statements.js +670 -158
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +298 -19
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +91 -170
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +884 -256
- package/src/backend/wasm/emit-exprs.js +185 -77
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-shared.js +4 -1
- package/src/backend/wasm/emit-stmts.js +524 -240
- package/src/backend/wasm/emit.js +176 -38
- package/src/backend/wasm/globals.js +48 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +44 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +299 -770
- package/src/backend/wasm/monomorph.js +139 -8
- package/src/backend/wasm/natives.js +600 -0
- package/src/backend/wasm/prescan.js +5 -153
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -32
- package/src/backend/wasm/types.js +97 -20
- package/src/backend/wasm/variadic.js +9 -4
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +113 -130
- package/src/lexer/lexer.js +106 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +355 -132
- package/src/link/type-names.js +204 -0
- package/src/link/wasm.js +30 -6
- package/src/lower/bounds.js +153 -0
- package/src/lower/captures.js +92 -33
- package/src/lower/embedding.js +62 -5
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +929 -0
- package/src/lower/functions.js +225 -7
- package/src/lower/gen-types.js +293 -0
- package/src/lower/index.js +14 -2
- package/src/lower/json-codecs.js +334 -245
- package/src/lower/ownership.js +25 -7
- package/src/lower/reachability.js +69 -51
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +30 -5
- package/src/parser/expressions.js +111 -57
- package/src/parser/statements.js +108 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +12 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +214 -19
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +156 -8
- package/src/typechecker/expressions.js +490 -108
- package/src/typechecker/index.js +346 -42
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +41 -0
- package/src/typechecker/resolve.js +490 -65
- package/src/typechecker/statements.js +288 -27
- package/src/typechecker/stdlib/excluded.js +9 -5
- package/src/typechecker/stdlib/index.js +25 -9
- package/src/typechecker/termination.js +10 -1
- package/src/typechecker/types.js +287 -575
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -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/json.go +62 -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/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +171 -38
- package/stdlib/internal/jsonrt/enc.go +186 -36
- package/stdlib/internal/jsonrt/scanner.go +17 -8
- package/stdlib/io/io.go +157 -1
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +16 -14
- 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 +391 -452
- 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 +235 -50
- 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 +141 -3
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/fmt-runtime.js +0 -573
- 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 -119
- package/src/backend/js/stdlib/builder.js +0 -98
- package/src/backend/js/stdlib/bytes.js +0 -368
- package/src/backend/js/stdlib/errors.js +0 -45
- package/src/backend/js/stdlib/fmt.js +0 -517
- 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 -1167
- package/src/backend/js/stdlib/maps.js +0 -37
- package/src/backend/js/stdlib/math.js +0 -85
- 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 -34
- package/src/backend/js/stdlib/slices.js +0 -107
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -79
- package/src/backend/js/stdlib/strings.js +0 -301
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -41
- package/src/backend/js/stdlib/time.js +0 -152
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/stdlib/bytes.js +0 -6716
- package/src/backend/wasm/stdlib/core.js +0 -10278
- package/src/backend/wasm/stdlib/io.js +0 -3578
- package/src/backend/wasm/stdlib/packages.js +0 -2402
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -2229
- package/src/typechecker/stdlib/core.js +0 -1079
- package/src/typechecker/stdlib/extended.js +0 -1210
- package/stdlib/bytes/iter.go +0 -192
- package/stdlib/bytes/search.go +0 -240
- package/stdlib/bytes/split.go +0 -218
- package/stdlib/errors/overlay.go +0 -12
- package/stdlib/maps/iter.go +0 -65
- package/stdlib/slices/extra.go +0 -73
- package/stdlib/slices/iter.go +0 -113
- package/stdlib/strings/extra.go +0 -35
- package/stdlib/strings/iter.go +0 -122
- package/stdlib/sync/extra.go +0 -105
- package/stdlib/unicode/utf8/bytes.go +0 -239
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
// TypeChecker expression-checking methods — installed as a mixin on TypeChecker.prototype.
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { exprText, operandKind } from "./operand-text.js";
|
|
4
|
+
import {
|
|
5
|
+
EXCLUDED_STDLIB_MEMBERS,
|
|
6
|
+
EXCLUDED_STDLIB_PACKAGES,
|
|
7
|
+
} from "./stdlib/excluded.js";
|
|
4
8
|
import { sharedInfo } from "./stdlib/shared.js";
|
|
5
9
|
import {
|
|
6
10
|
ANY,
|
|
11
|
+
BASIC_TYPES,
|
|
7
12
|
BOOL,
|
|
8
13
|
BROWSER_GLOBALS,
|
|
9
14
|
COMPLEX64,
|
|
@@ -11,9 +16,12 @@ import {
|
|
|
11
16
|
compositeLitMaxIndex,
|
|
12
17
|
displayTypeName,
|
|
13
18
|
ERROR,
|
|
19
|
+
embeddedFieldName,
|
|
14
20
|
FLOAT32,
|
|
15
21
|
FLOAT64,
|
|
22
|
+
FMT_PRINT_FUNCS,
|
|
16
23
|
INT,
|
|
24
|
+
is64BitInt,
|
|
17
25
|
isAny,
|
|
18
26
|
isArray,
|
|
19
27
|
isBool,
|
|
@@ -21,6 +29,7 @@ import {
|
|
|
21
29
|
isComplex64,
|
|
22
30
|
isComplexOrNumeric,
|
|
23
31
|
isInteger,
|
|
32
|
+
isNil,
|
|
24
33
|
isNumeric,
|
|
25
34
|
isString,
|
|
26
35
|
NIL,
|
|
@@ -30,14 +39,25 @@ import {
|
|
|
30
39
|
STRING,
|
|
31
40
|
TAINTED_ANY,
|
|
32
41
|
typeStr,
|
|
42
|
+
UINT8,
|
|
33
43
|
UNTYPED_BOOL,
|
|
34
44
|
UNTYPED_COMPLEX,
|
|
35
45
|
UNTYPED_FLOAT,
|
|
36
46
|
UNTYPED_INT,
|
|
47
|
+
UNTYPED_RUNE,
|
|
37
48
|
UNTYPED_STRING,
|
|
38
49
|
VOID,
|
|
39
50
|
} from "./types.js";
|
|
40
51
|
|
|
52
|
+
const NILABLE_KINDS = new Set([
|
|
53
|
+
"slice",
|
|
54
|
+
"map",
|
|
55
|
+
"pointer",
|
|
56
|
+
"func",
|
|
57
|
+
"interface",
|
|
58
|
+
"chan",
|
|
59
|
+
]);
|
|
60
|
+
|
|
41
61
|
/** @typedef {import('./index.js').TypeChecker} TypeChecker */
|
|
42
62
|
|
|
43
63
|
// min/max: the type of the first typed operand; with only untyped constants,
|
|
@@ -52,7 +72,7 @@ function minMaxType(at) {
|
|
|
52
72
|
// Each value is (self, name, expr, argTypes, scope) => returnType.
|
|
53
73
|
const BUILTIN_CHECK = {
|
|
54
74
|
len: (s, n, e, at) => s._checkBuiltinLen(n, e, at),
|
|
55
|
-
cap: (s, n, e, at) => s._checkBuiltinLen(n, e, at),
|
|
75
|
+
cap: (s, n, e, at, sc) => s._checkBuiltinLen(n, e, at, sc),
|
|
56
76
|
append: (s, _n, e, at) => {
|
|
57
77
|
const st = at[0] ?? ANY;
|
|
58
78
|
if (isArray(st)) {
|
|
@@ -68,15 +88,23 @@ const BUILTIN_CHECK = {
|
|
|
68
88
|
}
|
|
69
89
|
const sb = s.resolveType(st?.kind === "named" ? st.underlying : st);
|
|
70
90
|
if (sb?.kind === "slice") {
|
|
91
|
+
const is64 = is64BitInt(sb.elem);
|
|
71
92
|
for (let i = 1; i < e.args.length; i++) {
|
|
72
93
|
if (e.args[i]._spread)
|
|
73
94
|
s._rejectSeqAsSlice(at[i], e.args[i], "a spread argument");
|
|
74
|
-
if (!e.args[i]._spread && at[i])
|
|
95
|
+
if (!e.args[i]._spread && at[i]) {
|
|
96
|
+
if (is64) e.args[i]._is64Bit = true;
|
|
97
|
+
s._tagComplexConst(
|
|
98
|
+
s.resolveType(sb.elem),
|
|
99
|
+
s.resolveType(at[i]),
|
|
100
|
+
e.args[i],
|
|
101
|
+
);
|
|
75
102
|
s._markIfaceBox(
|
|
76
103
|
s.resolveType(sb.elem),
|
|
77
104
|
s.resolveType(at[i]),
|
|
78
105
|
e.args[i],
|
|
79
106
|
);
|
|
107
|
+
}
|
|
80
108
|
}
|
|
81
109
|
}
|
|
82
110
|
return st;
|
|
@@ -84,14 +112,23 @@ const BUILTIN_CHECK = {
|
|
|
84
112
|
copy: (s, _n, e, at) => s._checkBuiltinCopy(e, at),
|
|
85
113
|
delete: (s, _n, e, at) => {
|
|
86
114
|
const mt = s._deepUnderlying(at[0]);
|
|
87
|
-
if (mt?.kind === "map" && e.args[1])
|
|
115
|
+
if (mt?.kind === "map" && e.args[1]) {
|
|
116
|
+
s._tagFloat32Const(s.resolveType(mt.key), at[1], e.args[1]);
|
|
117
|
+
s._markIfaceKey(mt, e.args[1], at[1]);
|
|
118
|
+
}
|
|
88
119
|
return VOID;
|
|
89
120
|
},
|
|
90
121
|
make: (s, _n, e, _at, sc) => {
|
|
91
122
|
if (e.args.length < 1) return ANY;
|
|
92
123
|
const ta = e.args[0];
|
|
93
124
|
const tn = ta.kind === "TypeExpr" ? ta.type : ta;
|
|
94
|
-
|
|
125
|
+
const made = s.resolveTypeNode(tn, sc) ?? ANY;
|
|
126
|
+
// `make(S, n)` with S a type parameter makes its core type.
|
|
127
|
+
const core = made.kind === "typeParam" ? s._coreType(made, sc) : null;
|
|
128
|
+
if (core) ta._coreType = core;
|
|
129
|
+
for (const size of e.args.slice(1, 3))
|
|
130
|
+
s._checkIntegerConstOperand(size, "size", sc);
|
|
131
|
+
return made;
|
|
95
132
|
},
|
|
96
133
|
new: (s, _n, e, _at, sc) => {
|
|
97
134
|
if (e.args.length < 1) return ANY;
|
|
@@ -101,17 +138,44 @@ const BUILTIN_CHECK = {
|
|
|
101
138
|
},
|
|
102
139
|
print: (s, n, e, at) => s._checkPrintArgs(n, e, at),
|
|
103
140
|
println: (s, n, e, at) => s._checkPrintArgs(n, e, at),
|
|
104
|
-
panic: () =>
|
|
141
|
+
panic: (s, _n, e, at) => {
|
|
142
|
+
// The operand converts to `any`, so a defined type keeps its identity.
|
|
143
|
+
if (e.args[0] && at?.[0]) s._markIfaceBox(ANY, at[0], e.args[0]);
|
|
144
|
+
return VOID;
|
|
145
|
+
},
|
|
105
146
|
recover: () => ANY,
|
|
106
147
|
error: () => ERROR,
|
|
107
|
-
min: (
|
|
108
|
-
max: (
|
|
148
|
+
min: (s, _n, e, at) => s._checkMinMax(e, at),
|
|
149
|
+
max: (s, _n, e, at) => s._checkMinMax(e, at),
|
|
109
150
|
clear: () => VOID,
|
|
110
151
|
complex: (s, _n, e, at) => s._checkBuiltinComplex(e, at),
|
|
111
152
|
real: (s, n, e, at) => s._checkBuiltinRealImag(n, e, at),
|
|
112
153
|
imag: (s, n, e, at) => s._checkBuiltinRealImag(n, e, at),
|
|
113
154
|
};
|
|
114
155
|
|
|
156
|
+
// Type kinds that are never valid as an index, slice bound or `make` size.
|
|
157
|
+
const NON_INTEGER_KINDS = new Set([
|
|
158
|
+
"struct",
|
|
159
|
+
"pointer",
|
|
160
|
+
"slice",
|
|
161
|
+
"map",
|
|
162
|
+
"func",
|
|
163
|
+
"array",
|
|
164
|
+
"interface",
|
|
165
|
+
"simd",
|
|
166
|
+
]);
|
|
167
|
+
|
|
168
|
+
// Basic types that are never valid as an index, slice bound or `make` size.
|
|
169
|
+
const NON_INTEGER_BASICS = new Set([
|
|
170
|
+
"float32",
|
|
171
|
+
"float64",
|
|
172
|
+
"complex64",
|
|
173
|
+
"complex128",
|
|
174
|
+
"bool",
|
|
175
|
+
"string",
|
|
176
|
+
"any",
|
|
177
|
+
]);
|
|
178
|
+
|
|
115
179
|
// Method-name dispatch for _checkExpr — string values add no static call edges.
|
|
116
180
|
const CHECK_EXPR_DELEGATE = {
|
|
117
181
|
UnaryExpr: "_checkUnaryExpr",
|
|
@@ -141,7 +205,7 @@ export const expressionCheckMethods = {
|
|
|
141
205
|
case "BasicLit": {
|
|
142
206
|
switch (expr.litKind) {
|
|
143
207
|
case "INT":
|
|
144
|
-
return UNTYPED_INT;
|
|
208
|
+
return expr.rune ? UNTYPED_RUNE : UNTYPED_INT;
|
|
145
209
|
case "FLOAT":
|
|
146
210
|
return UNTYPED_FLOAT;
|
|
147
211
|
case "STRING":
|
|
@@ -157,8 +221,35 @@ export const expressionCheckMethods = {
|
|
|
157
221
|
return UNTYPED_COMPLEX;
|
|
158
222
|
case "Ident": {
|
|
159
223
|
const t = scope.lookup(expr.name);
|
|
160
|
-
|
|
161
|
-
if (
|
|
224
|
+
// `(*int)(nil)`: a predeclared type named where a type may stand.
|
|
225
|
+
if (!t && expr._typeRefOk && Object.hasOwn(BASIC_TYPES, expr.name)) {
|
|
226
|
+
expr._isTypeRef = true;
|
|
227
|
+
return BASIC_TYPES[expr.name];
|
|
228
|
+
}
|
|
229
|
+
if (!t) {
|
|
230
|
+
// The import of an excluded package already reported; its
|
|
231
|
+
// uses are not a second root cause.
|
|
232
|
+
const imported = this._fileImports
|
|
233
|
+
?.get(this._currentFile)
|
|
234
|
+
?.get(expr.name);
|
|
235
|
+
if (imported && EXCLUDED_STDLIB_PACKAGES.has(imported))
|
|
236
|
+
return TAINTED_ANY;
|
|
237
|
+
if (this._unavailableImports?.has(expr.name)) return TAINTED_ANY;
|
|
238
|
+
// `gom` needs no import in a js package; wasm packages get
|
|
239
|
+
// the same answer as an `import "gom"`.
|
|
240
|
+
if (
|
|
241
|
+
expr.name === "gom" &&
|
|
242
|
+
(this.target === "wasm" || this.target === "both")
|
|
243
|
+
) {
|
|
244
|
+
this.recordBlocker("gom usage", "gom");
|
|
245
|
+
return this.err(
|
|
246
|
+
"package 'gom' is not available in wasm packages",
|
|
247
|
+
expr,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
return this.err(`Undefined: '${expr.name}'`, expr);
|
|
251
|
+
}
|
|
252
|
+
if (this._namesType(expr.name, t, scope) || t.kind === "typeParam")
|
|
162
253
|
expr._isTypeRef = true;
|
|
163
254
|
// A Go declaration that shadows a browser global: JS output renames
|
|
164
255
|
// it so generated code keeps reaching the real global. Package
|
|
@@ -188,14 +279,6 @@ export const expressionCheckMethods = {
|
|
|
188
279
|
`(package '${this.pkgName}' is //gofront:target ${this.target} — browser globals are only available in js packages)`,
|
|
189
280
|
);
|
|
190
281
|
}
|
|
191
|
-
if (
|
|
192
|
-
(this.target === "wasm" || this.target === "both") &&
|
|
193
|
-
expr.name === "gom" &&
|
|
194
|
-
scope.lookup(expr.name) === this.globals.lookup(expr.name)
|
|
195
|
-
) {
|
|
196
|
-
this.recordBlocker("gom usage", "gom");
|
|
197
|
-
this.err("package 'gom' is not available in wasm packages", expr);
|
|
198
|
-
}
|
|
199
282
|
return t;
|
|
200
283
|
}
|
|
201
284
|
case "BinaryExpr": {
|
|
@@ -205,7 +288,21 @@ export const expressionCheckMethods = {
|
|
|
205
288
|
if (isAny(lt) || isAny(rt))
|
|
206
289
|
return this.binaryResultType(expr.op, ANY, ANY, expr);
|
|
207
290
|
this._checkConstOperands(expr.op, lt, rt, expr);
|
|
208
|
-
|
|
291
|
+
this._markTypeParamMapNil(expr, lt, rt, scope);
|
|
292
|
+
const res = this.binaryResultType(expr.op, lt, rt, expr);
|
|
293
|
+
if (expr.op !== "<<" && expr.op !== ">>") {
|
|
294
|
+
this._retypeUntypedShift(lt, rt, expr.right);
|
|
295
|
+
this._retypeUntypedShift(rt, lt, expr.left);
|
|
296
|
+
}
|
|
297
|
+
this._tagFloat32Const(lt, rt, expr.right);
|
|
298
|
+
this._tagFloat32Const(rt, lt, expr.left);
|
|
299
|
+
this._tagComplex64Const(lt, rt, expr.right);
|
|
300
|
+
this._tagComplex64Const(rt, lt, expr.left);
|
|
301
|
+
if (is64BitInt(lt) && rt?.kind === "untyped" && expr.right)
|
|
302
|
+
expr.right._is64Bit = true;
|
|
303
|
+
if (is64BitInt(rt) && lt?.kind === "untyped" && expr.left)
|
|
304
|
+
expr.left._is64Bit = true;
|
|
305
|
+
return res;
|
|
209
306
|
}
|
|
210
307
|
case "AwaitExpr": {
|
|
211
308
|
if (this.target === "wasm" || this.target === "both") {
|
|
@@ -251,7 +348,8 @@ export const expressionCheckMethods = {
|
|
|
251
348
|
|
|
252
349
|
_checkBoolUnaryOp(ot, expr) {
|
|
253
350
|
if (!isBool(ot) && !isAny(ot)) this.err("! requires bool", expr);
|
|
254
|
-
|
|
351
|
+
// `!` keeps an untyped or defined bool type.
|
|
352
|
+
return isBool(ot) ? ot : BOOL;
|
|
255
353
|
},
|
|
256
354
|
|
|
257
355
|
_checkNumericUnaryOp(ot, expr) {
|
|
@@ -281,23 +379,32 @@ export const expressionCheckMethods = {
|
|
|
281
379
|
return terms !== null && terms.length > 0 && terms.every(pred);
|
|
282
380
|
},
|
|
283
381
|
|
|
284
|
-
|
|
382
|
+
// `scope` resolves the type parameters a term mentions (`~[]E`); it
|
|
383
|
+
// defaults to the package scope.
|
|
384
|
+
_constraintTerms(c, seen, scope = this.globals) {
|
|
285
385
|
if (!c || seen.has(c)) return null;
|
|
286
386
|
seen.add(c);
|
|
287
387
|
// Receiver type parameters keep their constraint as an AST node.
|
|
288
|
-
if (
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
388
|
+
if (
|
|
389
|
+
c.kind === "TypeName" ||
|
|
390
|
+
c.kind === "InterfaceType" ||
|
|
391
|
+
c.kind === "GenericTypeName" ||
|
|
392
|
+
c.kind === "InstantiationExpr" ||
|
|
393
|
+
c.kind === "IndexExpr"
|
|
394
|
+
)
|
|
395
|
+
return this._constraintTerms(this.resolveTypeNode(c, scope), seen, scope);
|
|
396
|
+
if (c.kind === "named")
|
|
397
|
+
return this._constraintTerms(c.underlying, seen, scope);
|
|
398
|
+
if (c.kind === "interface") return this._interfaceTerms(c, seen, scope);
|
|
292
399
|
if (c.kind === "typeParam")
|
|
293
|
-
return this._constraintTerms(c.constraint, seen);
|
|
400
|
+
return this._constraintTerms(c.constraint, seen, scope);
|
|
294
401
|
if (c.kind !== "UnionConstraint") return isAny(c) ? null : [c];
|
|
295
402
|
const out = [];
|
|
296
403
|
for (const term of c.terms) {
|
|
297
|
-
const t = this.resolveTypeNode(term.type,
|
|
404
|
+
const t = term.resolved ?? this.resolveTypeNode(term.type, scope);
|
|
298
405
|
const inner =
|
|
299
406
|
t?.kind === "named" && t.underlying?.kind === "interface"
|
|
300
|
-
? this._constraintTerms(t, seen)
|
|
407
|
+
? this._constraintTerms(t, seen, scope)
|
|
301
408
|
: [t];
|
|
302
409
|
if (inner === null) return null;
|
|
303
410
|
out.push(...inner);
|
|
@@ -307,10 +414,10 @@ export const expressionCheckMethods = {
|
|
|
307
414
|
|
|
308
415
|
// An interface's type set is the intersection of its own union and the
|
|
309
416
|
// type sets of the interfaces it embeds; null when it has none.
|
|
310
|
-
_interfaceTerms(iface, seen) {
|
|
417
|
+
_interfaceTerms(iface, seen, scope) {
|
|
311
418
|
const sets = [iface.unionConstraint, ...(iface._typeSetEmbeds ?? [])]
|
|
312
419
|
.filter(Boolean)
|
|
313
|
-
.map((s) => this._constraintTerms(s, new Set(seen)));
|
|
420
|
+
.map((s) => this._constraintTerms(s, new Set(seen), scope));
|
|
314
421
|
if (sets.length === 0 || sets.includes(null)) return null;
|
|
315
422
|
const [first, ...rest] = sets;
|
|
316
423
|
return first.filter((t) =>
|
|
@@ -608,6 +715,11 @@ export const expressionCheckMethods = {
|
|
|
608
715
|
const base = this.resolveType(
|
|
609
716
|
recvT?.kind === "named" ? recvT.underlying : recvT,
|
|
610
717
|
);
|
|
718
|
+
if (base?.kind === "interface" && base.methods?.has(expr.field)) {
|
|
719
|
+
// `i.M` binds the interface value; the call dispatches when it runs.
|
|
720
|
+
expr._isMethodValue = true;
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
611
723
|
if (!recvT || !this._isStructOrNamedMethod(recvT, base, expr.field)) return;
|
|
612
724
|
expr._isMethodValue = true;
|
|
613
725
|
if (baseType.kind === "pointer")
|
|
@@ -615,6 +727,16 @@ export const expressionCheckMethods = {
|
|
|
615
727
|
else this._markPtrRecvAddress(expr, base, ft, scope);
|
|
616
728
|
},
|
|
617
729
|
|
|
730
|
+
// Whether the identifier `name`, which resolved to `t`, names a type. A
|
|
731
|
+
// local variable or parameter that shares its name with a type of the
|
|
732
|
+
// build (the type table spans packages) is a value.
|
|
733
|
+
_namesType(name, t, scope) {
|
|
734
|
+
if (!this.types.has(name)) return false;
|
|
735
|
+
return (
|
|
736
|
+
scope.lookupScope(name) === this.globals || this.types.get(name) === t
|
|
737
|
+
);
|
|
738
|
+
},
|
|
739
|
+
|
|
618
740
|
_checkSelectorExpr(expr, scope) {
|
|
619
741
|
expr.expr._typeRefOk = true;
|
|
620
742
|
const baseType = this.checkExpr(expr.expr, scope);
|
|
@@ -688,10 +810,70 @@ export const expressionCheckMethods = {
|
|
|
688
810
|
return u;
|
|
689
811
|
},
|
|
690
812
|
|
|
813
|
+
// Core type of type parameter `tp`: the one underlying type shared by every
|
|
814
|
+
// type in its constraint (`~[]E`, `~map[K]V`), or null when there is none.
|
|
815
|
+
_coreType(tp, scope) {
|
|
816
|
+
if (tp?.kind !== "typeParam") return null;
|
|
817
|
+
const terms = this._constraintTerms(tp.constraint, new Set(), scope);
|
|
818
|
+
if (!terms || terms.length === 0) return null;
|
|
819
|
+
const unders = terms.map((t) => this._deepUnderlying(this.resolveType(t)));
|
|
820
|
+
const key = typeStr(unders[0]);
|
|
821
|
+
if (unders.some((u) => !u || typeStr(u) !== key)) return null;
|
|
822
|
+
return unders[0];
|
|
823
|
+
},
|
|
824
|
+
|
|
825
|
+
// `m == nil` on a type parameter whose core type is a map: the backends
|
|
826
|
+
// compare it as a map, so record the core type on the operand.
|
|
827
|
+
_markTypeParamMapNil(expr, lt, rt, scope) {
|
|
828
|
+
if (expr.op !== "==" && expr.op !== "!=") return;
|
|
829
|
+
const [operand, other] =
|
|
830
|
+
rt === NIL
|
|
831
|
+
? [expr.left, lt]
|
|
832
|
+
: lt === NIL
|
|
833
|
+
? [expr.right, rt]
|
|
834
|
+
: [null, null];
|
|
835
|
+
if (!operand || other?.kind !== "typeParam") return;
|
|
836
|
+
const core = this._coreType(other, scope);
|
|
837
|
+
if (core?.kind === "map") operand._coreType = core;
|
|
838
|
+
},
|
|
839
|
+
|
|
840
|
+
// Underlying type of `t` for indexing, slicing, ranging and the like; a
|
|
841
|
+
// type parameter stands for its core type.
|
|
842
|
+
_coreUnderlying(t, scope) {
|
|
843
|
+
if (t?.kind === "typeParam") return this._coreType(t, scope) ?? t;
|
|
844
|
+
return this._deepUnderlying(t);
|
|
845
|
+
},
|
|
846
|
+
|
|
847
|
+
// One step of underlying type, or the core type for a type parameter.
|
|
848
|
+
_underlyingOrCore(t, scope) {
|
|
849
|
+
if (t?.kind === "typeParam") return this._coreType(t, scope) ?? t;
|
|
850
|
+
return t?.kind === "named" ? t.underlying : t;
|
|
851
|
+
},
|
|
852
|
+
|
|
853
|
+
_markCompositeKey(key, kb, node, srcType) {
|
|
854
|
+
if (!node || !srcType || isAny(key) || kb?.kind === "interface")
|
|
855
|
+
return false;
|
|
856
|
+
// Arrays, anonymous structs and pointers are not JS property names: the
|
|
857
|
+
// backend encodes them by value (`sk`) or by identity (`pk`).
|
|
858
|
+
if (kb?.kind === "pointer") node._mapKeyEnc = "pk";
|
|
859
|
+
else if (kb?.kind === "array" || (key?.kind === "struct" && kb === key))
|
|
860
|
+
node._mapKeyEnc = "sk";
|
|
861
|
+
return true;
|
|
862
|
+
},
|
|
863
|
+
|
|
691
864
|
// A key of an interface-keyed map (`map[any]T`): the backend encodes it
|
|
692
865
|
// with its dynamic type, so `L("1")`, `N(1)`, `"1"` and `1` stay distinct.
|
|
693
866
|
// A named basic value is boxed first, exactly as in any interface
|
|
694
867
|
// conversion; other values are keyed as they are.
|
|
868
|
+
// Untyped constant operands take the result type, so a float32 result
|
|
869
|
+
// rounds them like the typed operands.
|
|
870
|
+
_checkMinMax(e, at) {
|
|
871
|
+
const rt = minMaxType(at);
|
|
872
|
+
for (let i = 0; i < e.args.length; i++)
|
|
873
|
+
this._tagFloat32Const(rt, at[i], e.args[i]);
|
|
874
|
+
return rt;
|
|
875
|
+
},
|
|
876
|
+
|
|
695
877
|
_markIfaceKey(mapType, node, srcType) {
|
|
696
878
|
const key = this.resolveType(mapType.key);
|
|
697
879
|
const kb = key?.kind === "named" ? this.resolveType(key.underlying) : key;
|
|
@@ -705,8 +887,10 @@ export const expressionCheckMethods = {
|
|
|
705
887
|
}
|
|
706
888
|
return;
|
|
707
889
|
}
|
|
890
|
+
if (this._markCompositeKey(key, kb, node, srcType)) return;
|
|
708
891
|
if (!node || !srcType || (!isAny(key) && kb?.kind !== "interface")) return;
|
|
709
892
|
node._ifaceKey = true;
|
|
893
|
+
node._ifaceKeyType = src;
|
|
710
894
|
this._markIfaceBox(key, this.resolveType(srcType), node);
|
|
711
895
|
if (node._ifaceBox === "named" || node._ifaceBox === "named_ptr") return;
|
|
712
896
|
node._ifaceBox = undefined;
|
|
@@ -729,13 +913,16 @@ export const expressionCheckMethods = {
|
|
|
729
913
|
const indexType = this.checkExpr(expr.index, scope);
|
|
730
914
|
if (bt?._tainted) return TAINTED_ANY;
|
|
731
915
|
if (isAny(bt)) return ANY;
|
|
732
|
-
const btu = this.
|
|
916
|
+
const btu = this._coreUnderlying(bt, scope);
|
|
917
|
+
if (btu.kind !== "map")
|
|
918
|
+
this._checkIntegerConstOperand(expr.index, "index", scope);
|
|
733
919
|
if (btu.kind === "slice" || btu.kind === "array") {
|
|
734
920
|
this._checkArrayBoundsExpr(btu, bt, expr, scope);
|
|
735
921
|
return btu.elem;
|
|
736
922
|
}
|
|
737
923
|
if (btu.kind === "map") {
|
|
738
924
|
expr._mapValueType = btu.value;
|
|
925
|
+
this.assertAssignable(btu.key, indexType, expr.index);
|
|
739
926
|
this._markIfaceKey(btu, expr.index, indexType);
|
|
740
927
|
return btu.value;
|
|
741
928
|
}
|
|
@@ -746,7 +933,7 @@ export const expressionCheckMethods = {
|
|
|
746
933
|
this.err(`invalid index ${idx} (index must not be negative)`, expr);
|
|
747
934
|
return btu.elem;
|
|
748
935
|
}
|
|
749
|
-
if (isString(bt)) return
|
|
936
|
+
if (isString(bt)) return UINT8;
|
|
750
937
|
return this.err(`Cannot index type ${typeStr(bt)}`, expr);
|
|
751
938
|
},
|
|
752
939
|
|
|
@@ -756,6 +943,12 @@ export const expressionCheckMethods = {
|
|
|
756
943
|
_genericFuncValueAsInstantiation(expr) {
|
|
757
944
|
const toTypeNode = (n) => {
|
|
758
945
|
if (n?.kind === "TypeExpr") return n.type;
|
|
946
|
+
// `Id[*int]`: the parser sees a dereference; on a generic function
|
|
947
|
+
// it is a pointer type argument.
|
|
948
|
+
if (n?.kind === "UnaryExpr" && n.op === "*") {
|
|
949
|
+
const base = toTypeNode(n.operand);
|
|
950
|
+
return base && { kind: "PointerType", base, _line: n._line };
|
|
951
|
+
}
|
|
759
952
|
const parts = [];
|
|
760
953
|
let cur = n;
|
|
761
954
|
while (cur?.kind === "SelectorExpr") {
|
|
@@ -812,18 +1005,64 @@ export const expressionCheckMethods = {
|
|
|
812
1005
|
return t.base;
|
|
813
1006
|
},
|
|
814
1007
|
|
|
1008
|
+
// An index, slice bound or `make` size that is a constant must be an
|
|
1009
|
+
// integer (`s[1 == 1]`, `make([]int, "a")`).
|
|
1010
|
+
// A typed operand must have an integer type too (`s[f]` with `f float64`).
|
|
1011
|
+
_checkIntegerConstOperand(operand, what, scope) {
|
|
1012
|
+
const kind = this._nonIntegerConst(operand, scope);
|
|
1013
|
+
if (kind) {
|
|
1014
|
+
this.err(
|
|
1015
|
+
`invalid argument: ${what} ${exprText(operand)} (${kind}) must be integer`,
|
|
1016
|
+
operand,
|
|
1017
|
+
);
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
const t = operand?._type;
|
|
1021
|
+
if (!this._isNonIntegerType(t, scope)) return;
|
|
1022
|
+
this.err(
|
|
1023
|
+
`invalid argument: ${what} ${exprText(operand)} (${operandKind(operand, typeStr(t), false)}) must be integer`,
|
|
1024
|
+
operand,
|
|
1025
|
+
);
|
|
1026
|
+
},
|
|
1027
|
+
|
|
1028
|
+
// Whether `t` is a type (or a defined type over one) that is not an
|
|
1029
|
+
// integer: a float, complex, bool, string, `any`, struct, pointer, slice,
|
|
1030
|
+
// map, func, array, interface, simd vector or a type parameter whose type
|
|
1031
|
+
// set is not all integers. Untyped constants are judged by
|
|
1032
|
+
// `_nonIntegerConst`, so only untyped non-integers that reach here
|
|
1033
|
+
// (`s[a == b]`) count.
|
|
1034
|
+
_isNonIntegerType(t, scope) {
|
|
1035
|
+
if (!t || t._tainted) return false;
|
|
1036
|
+
// A type parameter is an integer only when every type in its type set
|
|
1037
|
+
// is (`~int | ~uint8`); `any`, `comparable` and method-only
|
|
1038
|
+
// constraints have no integer type set.
|
|
1039
|
+
if (t.kind === "typeParam") {
|
|
1040
|
+
const terms = this._constraintTerms(t.constraint, new Set(), scope);
|
|
1041
|
+
return !(terms?.length > 0 && terms.every(isInteger));
|
|
1042
|
+
}
|
|
1043
|
+
if (t.kind === "untyped")
|
|
1044
|
+
return (
|
|
1045
|
+
t.base === "bool" || t.base === "string" || t.base === "complex128"
|
|
1046
|
+
);
|
|
1047
|
+
const u = this._deepUnderlying(t);
|
|
1048
|
+
if (u?.kind === "basic") return NON_INTEGER_BASICS.has(u.name);
|
|
1049
|
+
return NON_INTEGER_KINDS.has(u?.kind);
|
|
1050
|
+
},
|
|
1051
|
+
|
|
815
1052
|
_checkSliceExpr(expr, scope) {
|
|
816
1053
|
const bt = this._derefArrayPtr(
|
|
817
1054
|
expr,
|
|
818
1055
|
"expr",
|
|
819
1056
|
this.checkExpr(expr.expr, scope),
|
|
820
1057
|
);
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
1058
|
+
for (const bound of [expr.low, expr.high, expr.max]) {
|
|
1059
|
+
if (!bound) continue;
|
|
1060
|
+
this.checkExpr(bound, scope);
|
|
1061
|
+
this._checkIntegerConstOperand(bound, "index", scope);
|
|
1062
|
+
}
|
|
824
1063
|
if (bt?._tainted) return TAINTED_ANY;
|
|
825
1064
|
if (isAny(bt)) return ANY;
|
|
826
|
-
const btu = this.
|
|
1065
|
+
const btu = this._coreUnderlying(bt, scope);
|
|
827
1066
|
if (btu.kind === "slice" || btu.kind === "array")
|
|
828
1067
|
return { kind: "slice", elem: btu.elem };
|
|
829
1068
|
if (isString(bt)) return STRING;
|
|
@@ -862,9 +1101,12 @@ export const expressionCheckMethods = {
|
|
|
862
1101
|
const ret = expr.returnType
|
|
863
1102
|
? this.resolveTypeNode(expr.returnType, scope)
|
|
864
1103
|
: VOID;
|
|
1104
|
+
const bodyRet = this._injectNamedReturns(expr.returnType, inner)
|
|
1105
|
+
? { ...ret, _hasNamedReturns: true }
|
|
1106
|
+
: ret;
|
|
865
1107
|
const savedDefer = this._deferCount;
|
|
866
1108
|
this._deferCount = 0;
|
|
867
|
-
this.checkBlock(expr.body, inner,
|
|
1109
|
+
this.checkBlock(expr.body, inner, bodyRet);
|
|
868
1110
|
this._reportUnused(inner, expr);
|
|
869
1111
|
if (this._deferCount > 0) expr.body._hasDefer = true;
|
|
870
1112
|
this._deferCount = savedDefer;
|
|
@@ -923,6 +1165,17 @@ export const expressionCheckMethods = {
|
|
|
923
1165
|
const targetType = this.resolveTypeNode(expr.targetType, scope);
|
|
924
1166
|
// `any(x)` / `error(x)`: box x as an implicit conversion would.
|
|
925
1167
|
this._markIfaceBox(targetType, srcType, expr.expr);
|
|
1168
|
+
this._rejectNilConversion(srcType, targetType, expr);
|
|
1169
|
+
if (this._checkIfaceConversion(srcType, targetType, expr, expr.expr))
|
|
1170
|
+
return targetType;
|
|
1171
|
+
if (
|
|
1172
|
+
srcType &&
|
|
1173
|
+
targetType &&
|
|
1174
|
+
!isAny(srcType) &&
|
|
1175
|
+
!isAny(targetType) &&
|
|
1176
|
+
this._checkIfaceTargetConversion(srcType, targetType, expr.expr)
|
|
1177
|
+
)
|
|
1178
|
+
return targetType;
|
|
926
1179
|
const srcResolved =
|
|
927
1180
|
srcType?.kind === "named" ? srcType.underlying : srcType;
|
|
928
1181
|
const tgtResolved =
|
|
@@ -943,6 +1196,8 @@ export const expressionCheckMethods = {
|
|
|
943
1196
|
expr,
|
|
944
1197
|
);
|
|
945
1198
|
}
|
|
1199
|
+
// `complex64(1 + 0.01i)`: the constant's components round to float32.
|
|
1200
|
+
this._tagComplex64Const(targetType, srcType, expr.expr);
|
|
946
1201
|
if (tgtResolved?.kind === "slice")
|
|
947
1202
|
this._rejectSeqAsSlice(srcType, expr.expr, "a slice conversion");
|
|
948
1203
|
if (this._isMutualArraySlice(tgtResolved, srcResolved)) {
|
|
@@ -964,12 +1219,59 @@ export const expressionCheckMethods = {
|
|
|
964
1219
|
return targetType;
|
|
965
1220
|
},
|
|
966
1221
|
|
|
1222
|
+
// `int(nil)`: only a type with a nil value converts from `nil`.
|
|
1223
|
+
_rejectNilConversion(srcType, targetType, expr) {
|
|
1224
|
+
if (!isNil(srcType) || !targetType || isAny(targetType)) return;
|
|
1225
|
+
if (targetType.kind === "typeParam") return;
|
|
1226
|
+
const kind = this._deepUnderlying(targetType)?.kind;
|
|
1227
|
+
if (!kind || NILABLE_KINDS.has(kind)) return;
|
|
1228
|
+
this.err(`cannot convert nil to ${typeStr(targetType)}`, expr);
|
|
1229
|
+
},
|
|
1230
|
+
|
|
1231
|
+
// Pointers convert when their base types share an underlying type.
|
|
1232
|
+
_pointerConversionOk(srcPtr, tgtPtr, srcType, targetType, expr) {
|
|
1233
|
+
const sBase = this._deepUnderlying(srcPtr.base);
|
|
1234
|
+
const tBase = this._deepUnderlying(tgtPtr.base);
|
|
1235
|
+
if (typeStr(sBase) === typeStr(tBase)) return true;
|
|
1236
|
+
if (!this._sameStructFields(sBase, tBase)) return false;
|
|
1237
|
+
// WebAssembly struct types are nominal: the two pointers could not
|
|
1238
|
+
// share one object.
|
|
1239
|
+
if (this.target === "wasm" || this.target === "both") {
|
|
1240
|
+
this.recordBlocker("pointer conversion between struct types");
|
|
1241
|
+
this.err(
|
|
1242
|
+
`cannot convert ${typeStr(srcType)} to ${typeStr(targetType)} in a wasm package: pointers to distinct struct types cannot share storage; keep this code in a js package`,
|
|
1243
|
+
expr,
|
|
1244
|
+
);
|
|
1245
|
+
}
|
|
1246
|
+
return true;
|
|
1247
|
+
},
|
|
1248
|
+
|
|
1249
|
+
// `I(x)` with I an interface type converts exactly when x is assignable to
|
|
1250
|
+
// I, so the method-set check (and its wording) is the assignment one.
|
|
1251
|
+
_checkIfaceTargetConversion(srcType, targetType, operand) {
|
|
1252
|
+
// `error("msg")` builds an error value (GoFront extension).
|
|
1253
|
+
if (targetType === ERROR && isString(srcType)) return false;
|
|
1254
|
+
const base = this.resolveType(
|
|
1255
|
+
targetType.kind === "named" ? targetType.underlying : targetType,
|
|
1256
|
+
);
|
|
1257
|
+
if (base?.kind !== "interface" || !operand) return false;
|
|
1258
|
+
this.assertAssignable(targetType, srcType, operand, "conversion");
|
|
1259
|
+
return true;
|
|
1260
|
+
},
|
|
1261
|
+
|
|
967
1262
|
_checkConversionValidity(srcType, targetType, expr) {
|
|
1263
|
+
if (this._checkIfaceConversion(srcType, targetType, expr, expr.args?.[0]))
|
|
1264
|
+
return;
|
|
968
1265
|
if (!srcType || !targetType || isAny(srcType) || isAny(targetType)) return;
|
|
1266
|
+
if (this._checkIfaceTargetConversion(srcType, targetType, expr.args?.[0]))
|
|
1267
|
+
return;
|
|
969
1268
|
const srcResolved = srcType.kind === "named" ? srcType.underlying : srcType;
|
|
970
1269
|
const tgtResolved =
|
|
971
1270
|
targetType.kind === "named" ? targetType.underlying : targetType;
|
|
972
1271
|
|
|
1272
|
+
// `nil` converts to any type that has a nil value.
|
|
1273
|
+
if (isNil(srcType) && NILABLE_KINDS.has(tgtResolved?.kind)) return;
|
|
1274
|
+
|
|
973
1275
|
if (isNumeric(targetType) && isComplex(srcType)) {
|
|
974
1276
|
this.err(
|
|
975
1277
|
`cannot convert ${typeStr(srcType)} to ${typeStr(targetType)} (use real() or imag())`,
|
|
@@ -1032,17 +1334,18 @@ export const expressionCheckMethods = {
|
|
|
1032
1334
|
srcResolved.elem?.name === "int32")))
|
|
1033
1335
|
)
|
|
1034
1336
|
return;
|
|
1035
|
-
if (
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1337
|
+
if (
|
|
1338
|
+
srcResolved?.kind === "pointer" &&
|
|
1339
|
+
tgtResolved?.kind === "pointer" &&
|
|
1340
|
+
this._pointerConversionOk(
|
|
1341
|
+
srcResolved,
|
|
1342
|
+
tgtResolved,
|
|
1343
|
+
srcType,
|
|
1344
|
+
targetType,
|
|
1345
|
+
expr,
|
|
1346
|
+
)
|
|
1347
|
+
)
|
|
1348
|
+
return;
|
|
1046
1349
|
this.err(
|
|
1047
1350
|
`cannot convert ${typeStr(srcType)} to ${typeStr(targetType)}`,
|
|
1048
1351
|
expr,
|
|
@@ -1052,6 +1355,7 @@ export const expressionCheckMethods = {
|
|
|
1052
1355
|
_checkTypeAssertExpr(expr, scope) {
|
|
1053
1356
|
const srcType = this.checkExpr(expr.expr, scope);
|
|
1054
1357
|
const targetType = this.resolveTypeNode(expr.type, scope);
|
|
1358
|
+
this._markDynamicType(expr.type, targetType);
|
|
1055
1359
|
|
|
1056
1360
|
// In Go, the source of a type assertion must be an interface type.
|
|
1057
1361
|
// We allow `any` (which is GoFront's basic any) and interface types.
|
|
@@ -1091,8 +1395,10 @@ export const expressionCheckMethods = {
|
|
|
1091
1395
|
"expr",
|
|
1092
1396
|
this.checkExpr(expr.expr, scope),
|
|
1093
1397
|
);
|
|
1094
|
-
const resolved =
|
|
1095
|
-
|
|
1398
|
+
const resolved = this._underlyingOrCore(collType, scope);
|
|
1399
|
+
// Backends range over the core type of a type parameter.
|
|
1400
|
+
if (collType?.kind === "typeParam" && resolved !== collType)
|
|
1401
|
+
expr.expr._coreType = resolved;
|
|
1096
1402
|
return this._rangeCollTypeTuple(resolved, collType);
|
|
1097
1403
|
},
|
|
1098
1404
|
|
|
@@ -1119,7 +1425,10 @@ export const expressionCheckMethods = {
|
|
|
1119
1425
|
) {
|
|
1120
1426
|
const tp = baseType.typeParams[i];
|
|
1121
1427
|
const constraint = tp.constraint
|
|
1122
|
-
? this.resolveTypeNode(
|
|
1428
|
+
? this.resolveTypeNode(
|
|
1429
|
+
tp.constraint,
|
|
1430
|
+
this._typeArgScope(baseType.typeParams, typeArgs, scope),
|
|
1431
|
+
)
|
|
1123
1432
|
: null;
|
|
1124
1433
|
if (constraint) this.checkConstraint(typeArgs[i], constraint, expr);
|
|
1125
1434
|
}
|
|
@@ -1298,12 +1607,12 @@ export const expressionCheckMethods = {
|
|
|
1298
1607
|
if (!resolved) return null;
|
|
1299
1608
|
fnType = resolved;
|
|
1300
1609
|
}
|
|
1301
|
-
return this._completePartialInstantiation(expr, argTypes) ?? fnType;
|
|
1610
|
+
return this._completePartialInstantiation(expr, argTypes, scope) ?? fnType;
|
|
1302
1611
|
},
|
|
1303
1612
|
|
|
1304
1613
|
// `F[int](x)` with fewer explicit type arguments than type parameters:
|
|
1305
1614
|
// infer the rest from the call arguments and record the full list.
|
|
1306
|
-
_completePartialInstantiation(expr, argTypes) {
|
|
1615
|
+
_completePartialInstantiation(expr, argTypes, scope) {
|
|
1307
1616
|
const inst = expr.func;
|
|
1308
1617
|
const generic = inst?.kind === "InstantiationExpr" && inst._genericFnType;
|
|
1309
1618
|
if (!generic || inst._typeArgs.length >= generic.typeParams.length)
|
|
@@ -1312,6 +1621,7 @@ export const expressionCheckMethods = {
|
|
|
1312
1621
|
generic,
|
|
1313
1622
|
this._inferArgTypes(expr, argTypes),
|
|
1314
1623
|
inst._typeArgs,
|
|
1624
|
+
scope,
|
|
1315
1625
|
);
|
|
1316
1626
|
if (!typeArgs) {
|
|
1317
1627
|
this.err(
|
|
@@ -1327,7 +1637,7 @@ export const expressionCheckMethods = {
|
|
|
1327
1637
|
|
|
1328
1638
|
// `T(x)` with T a type parameter: only a numeric x converts (the constraint
|
|
1329
1639
|
// decides the target at instantiation).
|
|
1330
|
-
_checkTypeParamConversion(tp, argTypes, expr) {
|
|
1640
|
+
_checkTypeParamConversion(tp, argTypes, expr, scope) {
|
|
1331
1641
|
expr._isTypeConversion = true;
|
|
1332
1642
|
expr._conversionTargetType = tp;
|
|
1333
1643
|
if (expr.args.length !== 1) {
|
|
@@ -1336,7 +1646,23 @@ export const expressionCheckMethods = {
|
|
|
1336
1646
|
expr,
|
|
1337
1647
|
);
|
|
1338
1648
|
}
|
|
1339
|
-
|
|
1649
|
+
// A constraint with a core type (`~[]E`) converts as that type does.
|
|
1650
|
+
const core = this._coreType(tp, scope);
|
|
1651
|
+
if (core) {
|
|
1652
|
+
const src = argTypes[0];
|
|
1653
|
+
const nilOk = ["slice", "map", "pointer", "func", "chan", "interface"];
|
|
1654
|
+
if (src === NIL || (src?.kind === "basic" && src.name === "nil")) {
|
|
1655
|
+
if (!nilOk.includes(core.kind))
|
|
1656
|
+
this.err(`cannot convert nil to ${typeStr(tp)}`, expr);
|
|
1657
|
+
} else if (src?.kind !== "typeParam") {
|
|
1658
|
+
this._checkConversionValidity(src, core, expr);
|
|
1659
|
+
}
|
|
1660
|
+
return tp;
|
|
1661
|
+
}
|
|
1662
|
+
if (
|
|
1663
|
+
isNumeric(argTypes[0]) &&
|
|
1664
|
+
!this._constraintHasTypeSet(tp.constraint, scope)
|
|
1665
|
+
)
|
|
1340
1666
|
return this.err(
|
|
1341
1667
|
`cannot convert ${typeStr(argTypes[0])} to type ${typeStr(tp)}: ${typeStr(tp)} is not constrained to a set of convertible types`,
|
|
1342
1668
|
expr,
|
|
@@ -1356,18 +1682,38 @@ export const expressionCheckMethods = {
|
|
|
1356
1682
|
// A conversion to a type parameter needs a constraint that lists types
|
|
1357
1683
|
// (`~int | ~float64`, or an interface embedding such a union); `any`,
|
|
1358
1684
|
// `comparable` and method-only constraints allow every type argument.
|
|
1359
|
-
_constraintHasTypeSet(constraint) {
|
|
1685
|
+
_constraintHasTypeSet(constraint, scope = this.globals) {
|
|
1360
1686
|
if (!constraint) return true;
|
|
1361
1687
|
if (constraint.kind === "UnionConstraint") return true;
|
|
1362
|
-
|
|
1688
|
+
// Receiver type parameters keep their constraint as an AST node.
|
|
1689
|
+
const c = this.resolveType(
|
|
1690
|
+
constraint.kind === "TypeName" || constraint.kind === "InterfaceType"
|
|
1691
|
+
? this.resolveTypeNode(constraint, scope)
|
|
1692
|
+
: constraint,
|
|
1693
|
+
);
|
|
1363
1694
|
const base = c?.kind === "named" ? this.resolveType(c.underlying) : c;
|
|
1364
1695
|
if (base?.kind !== "interface") return false;
|
|
1365
1696
|
if (base.unionConstraint) return true;
|
|
1366
1697
|
return (base._typeSetEmbeds ?? []).some((e) =>
|
|
1367
|
-
this._constraintHasTypeSet(e),
|
|
1698
|
+
this._constraintHasTypeSet(e, scope),
|
|
1368
1699
|
);
|
|
1369
1700
|
},
|
|
1370
1701
|
|
|
1702
|
+
// A direct call of a `//gofront:native` function is marked `_native` so a
|
|
1703
|
+
// backend may substitute host code. A function used as a value (`f :=
|
|
1704
|
+
// str.Index; f(a, b)`) is not a direct reference and calls the Go body.
|
|
1705
|
+
_markNativeCall(expr, fnType, scope) {
|
|
1706
|
+
const callee =
|
|
1707
|
+
expr.func.kind === "InstantiationExpr" ? expr.func.expr : expr.func;
|
|
1708
|
+
const native = fnType?.native ?? fnType?.underlying?.native ?? null;
|
|
1709
|
+
const direct =
|
|
1710
|
+
callee.kind === "Ident"
|
|
1711
|
+
? scope.lookupScope(callee.name) === this.globals &&
|
|
1712
|
+
Boolean(this._funcDeclNames?.has(callee.name))
|
|
1713
|
+
: callee.kind === "SelectorExpr" && Boolean(callee._stdName);
|
|
1714
|
+
expr._native = native && direct ? native : null;
|
|
1715
|
+
},
|
|
1716
|
+
|
|
1371
1717
|
checkCall(expr, scope) {
|
|
1372
1718
|
expr.func._typeRefOk = true;
|
|
1373
1719
|
let fnType = this.checkExpr(expr.func, scope);
|
|
@@ -1391,6 +1737,7 @@ export const expressionCheckMethods = {
|
|
|
1391
1737
|
return this.checkBuiltin(fnType.name, expr, argTypes, scope);
|
|
1392
1738
|
}
|
|
1393
1739
|
|
|
1740
|
+
this._markNativeCall(expr, fnType, scope);
|
|
1394
1741
|
const argTypes = this._resolveCallArgTypes(expr, scope);
|
|
1395
1742
|
|
|
1396
1743
|
if (fnType?._tainted) return TAINTED_ANY;
|
|
@@ -1401,7 +1748,12 @@ export const expressionCheckMethods = {
|
|
|
1401
1748
|
|
|
1402
1749
|
// Named type used as a constructor/conversion: Greeter(fn), NodeFunc(fn)
|
|
1403
1750
|
// The callee is a type name, not a callable variable.
|
|
1404
|
-
if (
|
|
1751
|
+
if (
|
|
1752
|
+
(fnType.kind === "named" ||
|
|
1753
|
+
fnType.kind === "pointer" ||
|
|
1754
|
+
fnType.kind === "basic") &&
|
|
1755
|
+
expr.func._isTypeRef
|
|
1756
|
+
) {
|
|
1405
1757
|
expr._isTypeConversion = true;
|
|
1406
1758
|
expr._conversionTargetType = fnType;
|
|
1407
1759
|
if (expr.args.length !== 1) {
|
|
@@ -1416,7 +1768,7 @@ export const expressionCheckMethods = {
|
|
|
1416
1768
|
}
|
|
1417
1769
|
|
|
1418
1770
|
if (fnType.kind === "typeParam" && expr.func._isTypeRef) {
|
|
1419
|
-
return this._checkTypeParamConversion(fnType, argTypes, expr);
|
|
1771
|
+
return this._checkTypeParamConversion(fnType, argTypes, expr, scope);
|
|
1420
1772
|
}
|
|
1421
1773
|
|
|
1422
1774
|
fnType = this._resolveCallFnType(fnType, argTypes, expr, scope);
|
|
@@ -1427,13 +1779,6 @@ export const expressionCheckMethods = {
|
|
|
1427
1779
|
|
|
1428
1780
|
this._checkCallArgs(fnType, argTypes, expr, scope);
|
|
1429
1781
|
|
|
1430
|
-
// Pseudo-generic stdlib helpers (maps.Keys, slices.Clone, ...) declare
|
|
1431
|
-
// how their result derives from the first argument.
|
|
1432
|
-
if (fnType._derivedReturn) {
|
|
1433
|
-
const derived = this._derivedReturnType(fnType._derivedReturn, argTypes);
|
|
1434
|
-
if (derived) return derived;
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
1782
|
const ret = fnType.returns;
|
|
1438
1783
|
if (!ret || ret.length === 0) return VOID;
|
|
1439
1784
|
if (ret.length === 1) return ret[0];
|
|
@@ -1483,19 +1828,6 @@ export const expressionCheckMethods = {
|
|
|
1483
1828
|
);
|
|
1484
1829
|
},
|
|
1485
1830
|
|
|
1486
|
-
_derivedReturnType(mode, argTypes) {
|
|
1487
|
-
const arg = argTypes[0];
|
|
1488
|
-
const under = arg?.kind === "named" ? arg.underlying : arg;
|
|
1489
|
-
switch (mode) {
|
|
1490
|
-
case "arg0":
|
|
1491
|
-
return under ? arg : null;
|
|
1492
|
-
case "elemOfArg0":
|
|
1493
|
-
return under?.elem ?? null;
|
|
1494
|
-
default:
|
|
1495
|
-
return null;
|
|
1496
|
-
}
|
|
1497
|
-
},
|
|
1498
|
-
|
|
1499
1831
|
// Argument types for inference: a spread argument `xs...` stands for its
|
|
1500
1832
|
// elements, which is what the variadic parameter type describes.
|
|
1501
1833
|
_inferArgTypes(expr, argTypes) {
|
|
@@ -1506,10 +1838,23 @@ export const expressionCheckMethods = {
|
|
|
1506
1838
|
});
|
|
1507
1839
|
},
|
|
1508
1840
|
|
|
1841
|
+
// Scope where each type parameter name stands for its type argument, so a
|
|
1842
|
+
// constraint that mentions another parameter (`S ~[]E`) resolves.
|
|
1843
|
+
_typeArgScope(typeParams, typeArgs, scope) {
|
|
1844
|
+
const out = new Scope(scope);
|
|
1845
|
+
typeParams.forEach((tp, i) => {
|
|
1846
|
+
if (typeArgs[i])
|
|
1847
|
+
out.define(tp.name, { kind: "aliasArg", type: typeArgs[i] });
|
|
1848
|
+
});
|
|
1849
|
+
return out;
|
|
1850
|
+
},
|
|
1851
|
+
|
|
1509
1852
|
_resolveGenericFnType(fnType, argTypes, expr, scope) {
|
|
1510
1853
|
const typeArgs = this.inferTypeArgs(
|
|
1511
1854
|
fnType,
|
|
1512
1855
|
this._inferArgTypes(expr, argTypes),
|
|
1856
|
+
[],
|
|
1857
|
+
scope,
|
|
1513
1858
|
);
|
|
1514
1859
|
if (!typeArgs) {
|
|
1515
1860
|
this.err(
|
|
@@ -1522,7 +1867,10 @@ export const expressionCheckMethods = {
|
|
|
1522
1867
|
for (let i = 0; i < fnType.typeParams.length && i < typeArgs.length; i++) {
|
|
1523
1868
|
const tp = fnType.typeParams[i];
|
|
1524
1869
|
const constraint = tp.constraint
|
|
1525
|
-
? this.resolveTypeNode(
|
|
1870
|
+
? this.resolveTypeNode(
|
|
1871
|
+
tp.constraint,
|
|
1872
|
+
this._typeArgScope(fnType.typeParams, typeArgs, scope),
|
|
1873
|
+
)
|
|
1526
1874
|
: null;
|
|
1527
1875
|
if (constraint) this.checkConstraint(typeArgs[i], constraint, expr);
|
|
1528
1876
|
}
|
|
@@ -1562,6 +1910,9 @@ export const expressionCheckMethods = {
|
|
|
1562
1910
|
!ns._gofront &&
|
|
1563
1911
|
!ns._mangle?.has(expr.func.field);
|
|
1564
1912
|
if (emitterBacked) return;
|
|
1913
|
+
// The generated wrapper of a Print-family call takes the results as
|
|
1914
|
+
// separate parameters (src/lower/fmt-formatters.js).
|
|
1915
|
+
if (ns?.name === "fmt" && FMT_PRINT_FUNCS.has(expr.func.field)) return;
|
|
1565
1916
|
this.err(
|
|
1566
1917
|
"passing multiple return values to a variadic function is not supported in wasm packages; assign them to variables first",
|
|
1567
1918
|
expr,
|
|
@@ -1587,6 +1938,14 @@ export const expressionCheckMethods = {
|
|
|
1587
1938
|
const node = expr._multiForward ? null : expr.args[i];
|
|
1588
1939
|
this.assertAssignable(targetParam, argTypes[i], node, "argument");
|
|
1589
1940
|
}
|
|
1941
|
+
// `g(f())`: per-element box marks for lowering to split the forward.
|
|
1942
|
+
if (
|
|
1943
|
+
expr._multiForward &&
|
|
1944
|
+
!fnType.variadic &&
|
|
1945
|
+
!this._skipIfaceMark &&
|
|
1946
|
+
fnType.params.length === argTypes.length
|
|
1947
|
+
)
|
|
1948
|
+
this._markForwardedElems(fnType.params, argTypes, expr.args[0]);
|
|
1590
1949
|
|
|
1591
1950
|
this._checkSimdLaneArgs(expr, scope);
|
|
1592
1951
|
},
|
|
@@ -1652,18 +2011,51 @@ export const expressionCheckMethods = {
|
|
|
1652
2011
|
return null;
|
|
1653
2012
|
},
|
|
1654
2013
|
|
|
1655
|
-
|
|
2014
|
+
_capOperandKind(operand, type) {
|
|
2015
|
+
const untypedConst = type?.kind === "untyped" && type.base !== "nil";
|
|
2016
|
+
return untypedConst
|
|
2017
|
+
? operandKind(operand, type.base, true)
|
|
2018
|
+
: operandKind(operand, typeStr(type), false);
|
|
2019
|
+
},
|
|
2020
|
+
|
|
2021
|
+
// `cap` takes a slice or an array (a pointer to an array is dereferenced
|
|
2022
|
+
// before this check). A type parameter needs a slice or array core type;
|
|
2023
|
+
// one with no single core type is left to instantiation. Unknown and
|
|
2024
|
+
// `any` operands (JS interop) are not judged.
|
|
2025
|
+
_capOperandOk(t, scope) {
|
|
2026
|
+
if (!t || t._tainted || isAny(t)) return true;
|
|
2027
|
+
if (t.kind === "typeParam") {
|
|
2028
|
+
const core = this._coreType(t, scope);
|
|
2029
|
+
return !core || core.kind === "slice" || core.kind === "array";
|
|
2030
|
+
}
|
|
2031
|
+
const kind = this._deepUnderlying(t)?.kind;
|
|
2032
|
+
return kind === "slice" || kind === "array";
|
|
2033
|
+
},
|
|
2034
|
+
|
|
2035
|
+
_checkBuiltinLen(name, expr, argTypes, scope) {
|
|
1656
2036
|
this._rejectSeqAsSlice(argTypes[0], expr.args?.[0], `${name}()`);
|
|
2037
|
+
const operand = expr.args?.[0];
|
|
1657
2038
|
if (expr.args?.length === 1)
|
|
1658
2039
|
argTypes[0] = this._derefArrayPtr(expr.args, 0, argTypes[0]);
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
2040
|
+
// `len` and `cap` of an array (or pointer to one) are its length.
|
|
2041
|
+
const size = this._constLenFromType(argTypes[0]);
|
|
2042
|
+
if (size != null) {
|
|
2043
|
+
expr._constLen = size;
|
|
2044
|
+
// The operand is not dereferenced: a nil `*[N]T` has length N.
|
|
2045
|
+
if (expr.args.length === 1) expr.args[0] = operand;
|
|
2046
|
+
}
|
|
2047
|
+
if (name === "cap") {
|
|
2048
|
+
if (sharedInfo(argTypes[0])) {
|
|
2049
|
+
this.err(
|
|
2050
|
+
`cannot use cap() on ${typeStr(argTypes[0])}: shared buffers have a fixed size; use len()`,
|
|
2051
|
+
expr,
|
|
2052
|
+
);
|
|
2053
|
+
} else if (!this._capOperandOk(argTypes[0], scope)) {
|
|
2054
|
+
this.err(
|
|
2055
|
+
`invalid argument: ${exprText(expr.args?.[0])} (${this._capOperandKind(expr.args?.[0], argTypes[0])}) for cap`,
|
|
2056
|
+
expr,
|
|
2057
|
+
);
|
|
2058
|
+
}
|
|
1667
2059
|
}
|
|
1668
2060
|
return INT;
|
|
1669
2061
|
},
|
|
@@ -1694,6 +2086,9 @@ export const expressionCheckMethods = {
|
|
|
1694
2086
|
expr,
|
|
1695
2087
|
);
|
|
1696
2088
|
}
|
|
2089
|
+
// An untyped operand takes type float32 (`complex(f, 0.01)`).
|
|
2090
|
+
this._tagFloat32Const(FLOAT32, crt, expr.args[0]);
|
|
2091
|
+
this._tagFloat32Const(FLOAT32, cit, expr.args[1]);
|
|
1697
2092
|
return COMPLEX64;
|
|
1698
2093
|
}
|
|
1699
2094
|
return COMPLEX128;
|
|
@@ -1778,17 +2173,7 @@ export const expressionCheckMethods = {
|
|
|
1778
2173
|
_checkStructPositionalElems(expr, scope, t, base) {
|
|
1779
2174
|
const fieldNames = base._structFields
|
|
1780
2175
|
? base._structFields.flatMap((f) =>
|
|
1781
|
-
f.embedded
|
|
1782
|
-
? [
|
|
1783
|
-
(
|
|
1784
|
-
(f.type?.kind === "pointer"
|
|
1785
|
-
? f.type.base?.name
|
|
1786
|
-
: f.type?.name) ?? ""
|
|
1787
|
-
)
|
|
1788
|
-
.split(".")
|
|
1789
|
-
.pop(),
|
|
1790
|
-
]
|
|
1791
|
-
: f.names,
|
|
2176
|
+
f.embedded ? [embeddedFieldName(f)] : f.names,
|
|
1792
2177
|
)
|
|
1793
2178
|
: [...(base.fields?.keys() ?? [])];
|
|
1794
2179
|
for (let i = 0; i < expr.elems.length; i++) {
|
|
@@ -1810,12 +2195,9 @@ export const expressionCheckMethods = {
|
|
|
1810
2195
|
for (const elem of expr.elems) {
|
|
1811
2196
|
if (elem.kind === "KeyValueExpr") {
|
|
1812
2197
|
const keyName = elem.key.name;
|
|
1813
|
-
const embed = base.
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
inner.kind === "named" ? inner.name.split(".").pop() : null;
|
|
1817
|
-
return name === keyName;
|
|
1818
|
-
});
|
|
2198
|
+
const embed = base._structFields?.find(
|
|
2199
|
+
(f) => f.embedded && embeddedFieldName(f) === keyName,
|
|
2200
|
+
)?.type;
|
|
1819
2201
|
if (embed) elem._isEmbedInit = true;
|
|
1820
2202
|
const fieldType = base.fields?.get(keyName) ?? embed;
|
|
1821
2203
|
if (fieldType) {
|