gofront 1.8.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 +3 -3
- package/package.json +5 -4
- package/src/backend/js/expressions.js +849 -371
- package/src/backend/js/index.js +241 -147
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +512 -2547
- package/src/backend/js/statements.js +506 -103
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +177 -13
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +63 -171
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +643 -256
- package/src/backend/wasm/emit-exprs.js +124 -57
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-stmts.js +325 -75
- package/src/backend/wasm/emit.js +104 -13
- package/src/backend/wasm/globals.js +47 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +36 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +92 -717
- package/src/backend/wasm/monomorph.js +103 -6
- package/src/backend/wasm/natives.js +596 -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 +68 -20
- package/src/backend/wasm/variadic.js +3 -3
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +108 -130
- package/src/lexer/lexer.js +105 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +354 -132
- package/src/link/type-names.js +148 -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 +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 +7 -1
- package/src/lower/json-codecs.js +228 -247
- 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 +28 -5
- package/src/parser/expressions.js +86 -16
- package/src/parser/statements.js +1 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +1 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +124 -11
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +32 -6
- package/src/typechecker/expressions.js +326 -98
- package/src/typechecker/index.js +208 -33
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +192 -36
- package/src/typechecker/statements.js +122 -11
- 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 +203 -573
- 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,6 +39,7 @@ 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,
|
|
@@ -38,6 +48,15 @@ import {
|
|
|
38
48
|
VOID,
|
|
39
49
|
} from "./types.js";
|
|
40
50
|
|
|
51
|
+
const NILABLE_KINDS = new Set([
|
|
52
|
+
"slice",
|
|
53
|
+
"map",
|
|
54
|
+
"pointer",
|
|
55
|
+
"func",
|
|
56
|
+
"interface",
|
|
57
|
+
"chan",
|
|
58
|
+
]);
|
|
59
|
+
|
|
41
60
|
/** @typedef {import('./index.js').TypeChecker} TypeChecker */
|
|
42
61
|
|
|
43
62
|
// min/max: the type of the first typed operand; with only untyped constants,
|
|
@@ -68,15 +87,18 @@ const BUILTIN_CHECK = {
|
|
|
68
87
|
}
|
|
69
88
|
const sb = s.resolveType(st?.kind === "named" ? st.underlying : st);
|
|
70
89
|
if (sb?.kind === "slice") {
|
|
90
|
+
const is64 = is64BitInt(sb.elem);
|
|
71
91
|
for (let i = 1; i < e.args.length; i++) {
|
|
72
92
|
if (e.args[i]._spread)
|
|
73
93
|
s._rejectSeqAsSlice(at[i], e.args[i], "a spread argument");
|
|
74
|
-
if (!e.args[i]._spread && at[i])
|
|
94
|
+
if (!e.args[i]._spread && at[i]) {
|
|
95
|
+
if (is64) e.args[i]._is64Bit = true;
|
|
75
96
|
s._markIfaceBox(
|
|
76
97
|
s.resolveType(sb.elem),
|
|
77
98
|
s.resolveType(at[i]),
|
|
78
99
|
e.args[i],
|
|
79
100
|
);
|
|
101
|
+
}
|
|
80
102
|
}
|
|
81
103
|
}
|
|
82
104
|
return st;
|
|
@@ -91,7 +113,11 @@ const BUILTIN_CHECK = {
|
|
|
91
113
|
if (e.args.length < 1) return ANY;
|
|
92
114
|
const ta = e.args[0];
|
|
93
115
|
const tn = ta.kind === "TypeExpr" ? ta.type : ta;
|
|
94
|
-
|
|
116
|
+
const made = s.resolveTypeNode(tn, sc) ?? ANY;
|
|
117
|
+
// `make(S, n)` with S a type parameter makes its core type.
|
|
118
|
+
const core = made.kind === "typeParam" ? s._coreType(made, sc) : null;
|
|
119
|
+
if (core) ta._coreType = core;
|
|
120
|
+
return made;
|
|
95
121
|
},
|
|
96
122
|
new: (s, _n, e, _at, sc) => {
|
|
97
123
|
if (e.args.length < 1) return ANY;
|
|
@@ -101,7 +127,11 @@ const BUILTIN_CHECK = {
|
|
|
101
127
|
},
|
|
102
128
|
print: (s, n, e, at) => s._checkPrintArgs(n, e, at),
|
|
103
129
|
println: (s, n, e, at) => s._checkPrintArgs(n, e, at),
|
|
104
|
-
panic: () =>
|
|
130
|
+
panic: (s, _n, e, at) => {
|
|
131
|
+
// The operand converts to `any`, so a defined type keeps its identity.
|
|
132
|
+
if (e.args[0] && at?.[0]) s._markIfaceBox(ANY, at[0], e.args[0]);
|
|
133
|
+
return VOID;
|
|
134
|
+
},
|
|
105
135
|
recover: () => ANY,
|
|
106
136
|
error: () => ERROR,
|
|
107
137
|
min: (_s, _n, _e, at) => minMaxType(at),
|
|
@@ -157,8 +187,35 @@ export const expressionCheckMethods = {
|
|
|
157
187
|
return UNTYPED_COMPLEX;
|
|
158
188
|
case "Ident": {
|
|
159
189
|
const t = scope.lookup(expr.name);
|
|
160
|
-
|
|
161
|
-
if (
|
|
190
|
+
// `(*int)(nil)`: a predeclared type named where a type may stand.
|
|
191
|
+
if (!t && expr._typeRefOk && Object.hasOwn(BASIC_TYPES, expr.name)) {
|
|
192
|
+
expr._isTypeRef = true;
|
|
193
|
+
return BASIC_TYPES[expr.name];
|
|
194
|
+
}
|
|
195
|
+
if (!t) {
|
|
196
|
+
// The import of an excluded package already reported; its
|
|
197
|
+
// uses are not a second root cause.
|
|
198
|
+
const imported = this._fileImports
|
|
199
|
+
?.get(this._currentFile)
|
|
200
|
+
?.get(expr.name);
|
|
201
|
+
if (imported && EXCLUDED_STDLIB_PACKAGES.has(imported))
|
|
202
|
+
return TAINTED_ANY;
|
|
203
|
+
if (this._unavailableImports?.has(expr.name)) return TAINTED_ANY;
|
|
204
|
+
// `gom` needs no import in a js package; wasm packages get
|
|
205
|
+
// the same answer as an `import "gom"`.
|
|
206
|
+
if (
|
|
207
|
+
expr.name === "gom" &&
|
|
208
|
+
(this.target === "wasm" || this.target === "both")
|
|
209
|
+
) {
|
|
210
|
+
this.recordBlocker("gom usage", "gom");
|
|
211
|
+
return this.err(
|
|
212
|
+
"package 'gom' is not available in wasm packages",
|
|
213
|
+
expr,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
return this.err(`Undefined: '${expr.name}'`, expr);
|
|
217
|
+
}
|
|
218
|
+
if (this._namesType(expr.name, t, scope) || t.kind === "typeParam")
|
|
162
219
|
expr._isTypeRef = true;
|
|
163
220
|
// A Go declaration that shadows a browser global: JS output renames
|
|
164
221
|
// it so generated code keeps reaching the real global. Package
|
|
@@ -188,14 +245,6 @@ export const expressionCheckMethods = {
|
|
|
188
245
|
`(package '${this.pkgName}' is //gofront:target ${this.target} — browser globals are only available in js packages)`,
|
|
189
246
|
);
|
|
190
247
|
}
|
|
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
248
|
return t;
|
|
200
249
|
}
|
|
201
250
|
case "BinaryExpr": {
|
|
@@ -205,7 +254,17 @@ export const expressionCheckMethods = {
|
|
|
205
254
|
if (isAny(lt) || isAny(rt))
|
|
206
255
|
return this.binaryResultType(expr.op, ANY, ANY, expr);
|
|
207
256
|
this._checkConstOperands(expr.op, lt, rt, expr);
|
|
208
|
-
|
|
257
|
+
this._markTypeParamMapNil(expr, lt, rt, scope);
|
|
258
|
+
const res = this.binaryResultType(expr.op, lt, rt, expr);
|
|
259
|
+
if (expr.op !== "<<" && expr.op !== ">>") {
|
|
260
|
+
this._retypeUntypedShift(lt, rt, expr.right);
|
|
261
|
+
this._retypeUntypedShift(rt, lt, expr.left);
|
|
262
|
+
}
|
|
263
|
+
if (is64BitInt(lt) && rt?.kind === "untyped" && expr.right)
|
|
264
|
+
expr.right._is64Bit = true;
|
|
265
|
+
if (is64BitInt(rt) && lt?.kind === "untyped" && expr.left)
|
|
266
|
+
expr.left._is64Bit = true;
|
|
267
|
+
return res;
|
|
209
268
|
}
|
|
210
269
|
case "AwaitExpr": {
|
|
211
270
|
if (this.target === "wasm" || this.target === "both") {
|
|
@@ -281,23 +340,32 @@ export const expressionCheckMethods = {
|
|
|
281
340
|
return terms !== null && terms.length > 0 && terms.every(pred);
|
|
282
341
|
},
|
|
283
342
|
|
|
284
|
-
|
|
343
|
+
// `scope` resolves the type parameters a term mentions (`~[]E`); it
|
|
344
|
+
// defaults to the package scope.
|
|
345
|
+
_constraintTerms(c, seen, scope = this.globals) {
|
|
285
346
|
if (!c || seen.has(c)) return null;
|
|
286
347
|
seen.add(c);
|
|
287
348
|
// Receiver type parameters keep their constraint as an AST node.
|
|
288
|
-
if (
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
349
|
+
if (
|
|
350
|
+
c.kind === "TypeName" ||
|
|
351
|
+
c.kind === "InterfaceType" ||
|
|
352
|
+
c.kind === "GenericTypeName" ||
|
|
353
|
+
c.kind === "InstantiationExpr" ||
|
|
354
|
+
c.kind === "IndexExpr"
|
|
355
|
+
)
|
|
356
|
+
return this._constraintTerms(this.resolveTypeNode(c, scope), seen, scope);
|
|
357
|
+
if (c.kind === "named")
|
|
358
|
+
return this._constraintTerms(c.underlying, seen, scope);
|
|
359
|
+
if (c.kind === "interface") return this._interfaceTerms(c, seen, scope);
|
|
292
360
|
if (c.kind === "typeParam")
|
|
293
|
-
return this._constraintTerms(c.constraint, seen);
|
|
361
|
+
return this._constraintTerms(c.constraint, seen, scope);
|
|
294
362
|
if (c.kind !== "UnionConstraint") return isAny(c) ? null : [c];
|
|
295
363
|
const out = [];
|
|
296
364
|
for (const term of c.terms) {
|
|
297
|
-
const t = this.resolveTypeNode(term.type,
|
|
365
|
+
const t = term.resolved ?? this.resolveTypeNode(term.type, scope);
|
|
298
366
|
const inner =
|
|
299
367
|
t?.kind === "named" && t.underlying?.kind === "interface"
|
|
300
|
-
? this._constraintTerms(t, seen)
|
|
368
|
+
? this._constraintTerms(t, seen, scope)
|
|
301
369
|
: [t];
|
|
302
370
|
if (inner === null) return null;
|
|
303
371
|
out.push(...inner);
|
|
@@ -307,10 +375,10 @@ export const expressionCheckMethods = {
|
|
|
307
375
|
|
|
308
376
|
// An interface's type set is the intersection of its own union and the
|
|
309
377
|
// type sets of the interfaces it embeds; null when it has none.
|
|
310
|
-
_interfaceTerms(iface, seen) {
|
|
378
|
+
_interfaceTerms(iface, seen, scope) {
|
|
311
379
|
const sets = [iface.unionConstraint, ...(iface._typeSetEmbeds ?? [])]
|
|
312
380
|
.filter(Boolean)
|
|
313
|
-
.map((s) => this._constraintTerms(s, new Set(seen)));
|
|
381
|
+
.map((s) => this._constraintTerms(s, new Set(seen), scope));
|
|
314
382
|
if (sets.length === 0 || sets.includes(null)) return null;
|
|
315
383
|
const [first, ...rest] = sets;
|
|
316
384
|
return first.filter((t) =>
|
|
@@ -608,6 +676,11 @@ export const expressionCheckMethods = {
|
|
|
608
676
|
const base = this.resolveType(
|
|
609
677
|
recvT?.kind === "named" ? recvT.underlying : recvT,
|
|
610
678
|
);
|
|
679
|
+
if (base?.kind === "interface" && base.methods?.has(expr.field)) {
|
|
680
|
+
// `i.M` binds the interface value; the call dispatches when it runs.
|
|
681
|
+
expr._isMethodValue = true;
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
611
684
|
if (!recvT || !this._isStructOrNamedMethod(recvT, base, expr.field)) return;
|
|
612
685
|
expr._isMethodValue = true;
|
|
613
686
|
if (baseType.kind === "pointer")
|
|
@@ -615,6 +688,16 @@ export const expressionCheckMethods = {
|
|
|
615
688
|
else this._markPtrRecvAddress(expr, base, ft, scope);
|
|
616
689
|
},
|
|
617
690
|
|
|
691
|
+
// Whether the identifier `name`, which resolved to `t`, names a type. A
|
|
692
|
+
// local variable or parameter that shares its name with a type of the
|
|
693
|
+
// build (the type table spans packages) is a value.
|
|
694
|
+
_namesType(name, t, scope) {
|
|
695
|
+
if (!this.types.has(name)) return false;
|
|
696
|
+
return (
|
|
697
|
+
scope.lookupScope(name) === this.globals || this.types.get(name) === t
|
|
698
|
+
);
|
|
699
|
+
},
|
|
700
|
+
|
|
618
701
|
_checkSelectorExpr(expr, scope) {
|
|
619
702
|
expr.expr._typeRefOk = true;
|
|
620
703
|
const baseType = this.checkExpr(expr.expr, scope);
|
|
@@ -688,6 +771,57 @@ export const expressionCheckMethods = {
|
|
|
688
771
|
return u;
|
|
689
772
|
},
|
|
690
773
|
|
|
774
|
+
// Core type of type parameter `tp`: the one underlying type shared by every
|
|
775
|
+
// type in its constraint (`~[]E`, `~map[K]V`), or null when there is none.
|
|
776
|
+
_coreType(tp, scope) {
|
|
777
|
+
if (tp?.kind !== "typeParam") return null;
|
|
778
|
+
const terms = this._constraintTerms(tp.constraint, new Set(), scope);
|
|
779
|
+
if (!terms || terms.length === 0) return null;
|
|
780
|
+
const unders = terms.map((t) => this._deepUnderlying(this.resolveType(t)));
|
|
781
|
+
const key = typeStr(unders[0]);
|
|
782
|
+
if (unders.some((u) => !u || typeStr(u) !== key)) return null;
|
|
783
|
+
return unders[0];
|
|
784
|
+
},
|
|
785
|
+
|
|
786
|
+
// `m == nil` on a type parameter whose core type is a map: the backends
|
|
787
|
+
// compare it as a map, so record the core type on the operand.
|
|
788
|
+
_markTypeParamMapNil(expr, lt, rt, scope) {
|
|
789
|
+
if (expr.op !== "==" && expr.op !== "!=") return;
|
|
790
|
+
const [operand, other] =
|
|
791
|
+
rt === NIL
|
|
792
|
+
? [expr.left, lt]
|
|
793
|
+
: lt === NIL
|
|
794
|
+
? [expr.right, rt]
|
|
795
|
+
: [null, null];
|
|
796
|
+
if (!operand || other?.kind !== "typeParam") return;
|
|
797
|
+
const core = this._coreType(other, scope);
|
|
798
|
+
if (core?.kind === "map") operand._coreType = core;
|
|
799
|
+
},
|
|
800
|
+
|
|
801
|
+
// Underlying type of `t` for indexing, slicing, ranging and the like; a
|
|
802
|
+
// type parameter stands for its core type.
|
|
803
|
+
_coreUnderlying(t, scope) {
|
|
804
|
+
if (t?.kind === "typeParam") return this._coreType(t, scope) ?? t;
|
|
805
|
+
return this._deepUnderlying(t);
|
|
806
|
+
},
|
|
807
|
+
|
|
808
|
+
// One step of underlying type, or the core type for a type parameter.
|
|
809
|
+
_underlyingOrCore(t, scope) {
|
|
810
|
+
if (t?.kind === "typeParam") return this._coreType(t, scope) ?? t;
|
|
811
|
+
return t?.kind === "named" ? t.underlying : t;
|
|
812
|
+
},
|
|
813
|
+
|
|
814
|
+
_markCompositeKey(key, kb, node, srcType) {
|
|
815
|
+
if (!node || !srcType || isAny(key) || kb?.kind === "interface")
|
|
816
|
+
return false;
|
|
817
|
+
// Arrays, anonymous structs and pointers are not JS property names: the
|
|
818
|
+
// backend encodes them by value (`sk`) or by identity (`pk`).
|
|
819
|
+
if (kb?.kind === "pointer") node._mapKeyEnc = "pk";
|
|
820
|
+
else if (kb?.kind === "array" || (key?.kind === "struct" && kb === key))
|
|
821
|
+
node._mapKeyEnc = "sk";
|
|
822
|
+
return true;
|
|
823
|
+
},
|
|
824
|
+
|
|
691
825
|
// A key of an interface-keyed map (`map[any]T`): the backend encodes it
|
|
692
826
|
// with its dynamic type, so `L("1")`, `N(1)`, `"1"` and `1` stay distinct.
|
|
693
827
|
// A named basic value is boxed first, exactly as in any interface
|
|
@@ -705,8 +839,10 @@ export const expressionCheckMethods = {
|
|
|
705
839
|
}
|
|
706
840
|
return;
|
|
707
841
|
}
|
|
842
|
+
if (this._markCompositeKey(key, kb, node, srcType)) return;
|
|
708
843
|
if (!node || !srcType || (!isAny(key) && kb?.kind !== "interface")) return;
|
|
709
844
|
node._ifaceKey = true;
|
|
845
|
+
node._ifaceKeyType = src;
|
|
710
846
|
this._markIfaceBox(key, this.resolveType(srcType), node);
|
|
711
847
|
if (node._ifaceBox === "named" || node._ifaceBox === "named_ptr") return;
|
|
712
848
|
node._ifaceBox = undefined;
|
|
@@ -729,13 +865,14 @@ export const expressionCheckMethods = {
|
|
|
729
865
|
const indexType = this.checkExpr(expr.index, scope);
|
|
730
866
|
if (bt?._tainted) return TAINTED_ANY;
|
|
731
867
|
if (isAny(bt)) return ANY;
|
|
732
|
-
const btu = this.
|
|
868
|
+
const btu = this._coreUnderlying(bt, scope);
|
|
733
869
|
if (btu.kind === "slice" || btu.kind === "array") {
|
|
734
870
|
this._checkArrayBoundsExpr(btu, bt, expr, scope);
|
|
735
871
|
return btu.elem;
|
|
736
872
|
}
|
|
737
873
|
if (btu.kind === "map") {
|
|
738
874
|
expr._mapValueType = btu.value;
|
|
875
|
+
this.assertAssignable(btu.key, indexType, expr.index);
|
|
739
876
|
this._markIfaceKey(btu, expr.index, indexType);
|
|
740
877
|
return btu.value;
|
|
741
878
|
}
|
|
@@ -746,7 +883,7 @@ export const expressionCheckMethods = {
|
|
|
746
883
|
this.err(`invalid index ${idx} (index must not be negative)`, expr);
|
|
747
884
|
return btu.elem;
|
|
748
885
|
}
|
|
749
|
-
if (isString(bt)) return
|
|
886
|
+
if (isString(bt)) return UINT8;
|
|
750
887
|
return this.err(`Cannot index type ${typeStr(bt)}`, expr);
|
|
751
888
|
},
|
|
752
889
|
|
|
@@ -756,6 +893,12 @@ export const expressionCheckMethods = {
|
|
|
756
893
|
_genericFuncValueAsInstantiation(expr) {
|
|
757
894
|
const toTypeNode = (n) => {
|
|
758
895
|
if (n?.kind === "TypeExpr") return n.type;
|
|
896
|
+
// `Id[*int]`: the parser sees a dereference; on a generic function
|
|
897
|
+
// it is a pointer type argument.
|
|
898
|
+
if (n?.kind === "UnaryExpr" && n.op === "*") {
|
|
899
|
+
const base = toTypeNode(n.operand);
|
|
900
|
+
return base && { kind: "PointerType", base, _line: n._line };
|
|
901
|
+
}
|
|
759
902
|
const parts = [];
|
|
760
903
|
let cur = n;
|
|
761
904
|
while (cur?.kind === "SelectorExpr") {
|
|
@@ -823,7 +966,7 @@ export const expressionCheckMethods = {
|
|
|
823
966
|
if (expr.max) this.checkExpr(expr.max, scope);
|
|
824
967
|
if (bt?._tainted) return TAINTED_ANY;
|
|
825
968
|
if (isAny(bt)) return ANY;
|
|
826
|
-
const btu = this.
|
|
969
|
+
const btu = this._coreUnderlying(bt, scope);
|
|
827
970
|
if (btu.kind === "slice" || btu.kind === "array")
|
|
828
971
|
return { kind: "slice", elem: btu.elem };
|
|
829
972
|
if (isString(bt)) return STRING;
|
|
@@ -862,9 +1005,12 @@ export const expressionCheckMethods = {
|
|
|
862
1005
|
const ret = expr.returnType
|
|
863
1006
|
? this.resolveTypeNode(expr.returnType, scope)
|
|
864
1007
|
: VOID;
|
|
1008
|
+
const bodyRet = this._injectNamedReturns(expr.returnType, inner)
|
|
1009
|
+
? { ...ret, _hasNamedReturns: true }
|
|
1010
|
+
: ret;
|
|
865
1011
|
const savedDefer = this._deferCount;
|
|
866
1012
|
this._deferCount = 0;
|
|
867
|
-
this.checkBlock(expr.body, inner,
|
|
1013
|
+
this.checkBlock(expr.body, inner, bodyRet);
|
|
868
1014
|
this._reportUnused(inner, expr);
|
|
869
1015
|
if (this._deferCount > 0) expr.body._hasDefer = true;
|
|
870
1016
|
this._deferCount = savedDefer;
|
|
@@ -923,6 +1069,7 @@ export const expressionCheckMethods = {
|
|
|
923
1069
|
const targetType = this.resolveTypeNode(expr.targetType, scope);
|
|
924
1070
|
// `any(x)` / `error(x)`: box x as an implicit conversion would.
|
|
925
1071
|
this._markIfaceBox(targetType, srcType, expr.expr);
|
|
1072
|
+
this._rejectNilConversion(srcType, targetType, expr);
|
|
926
1073
|
const srcResolved =
|
|
927
1074
|
srcType?.kind === "named" ? srcType.underlying : srcType;
|
|
928
1075
|
const tgtResolved =
|
|
@@ -964,12 +1111,42 @@ export const expressionCheckMethods = {
|
|
|
964
1111
|
return targetType;
|
|
965
1112
|
},
|
|
966
1113
|
|
|
1114
|
+
// `int(nil)`: only a type with a nil value converts from `nil`.
|
|
1115
|
+
_rejectNilConversion(srcType, targetType, expr) {
|
|
1116
|
+
if (!isNil(srcType) || !targetType || isAny(targetType)) return;
|
|
1117
|
+
if (targetType.kind === "typeParam") return;
|
|
1118
|
+
const kind = this._deepUnderlying(targetType)?.kind;
|
|
1119
|
+
if (!kind || NILABLE_KINDS.has(kind)) return;
|
|
1120
|
+
this.err(`cannot convert nil to ${typeStr(targetType)}`, expr);
|
|
1121
|
+
},
|
|
1122
|
+
|
|
1123
|
+
// Pointers convert when their base types share an underlying type.
|
|
1124
|
+
_pointerConversionOk(srcPtr, tgtPtr, srcType, targetType, expr) {
|
|
1125
|
+
const sBase = this._deepUnderlying(srcPtr.base);
|
|
1126
|
+
const tBase = this._deepUnderlying(tgtPtr.base);
|
|
1127
|
+
if (typeStr(sBase) === typeStr(tBase)) return true;
|
|
1128
|
+
if (!this._sameStructFields(sBase, tBase)) return false;
|
|
1129
|
+
// WebAssembly struct types are nominal: the two pointers could not
|
|
1130
|
+
// share one object.
|
|
1131
|
+
if (this.target === "wasm" || this.target === "both") {
|
|
1132
|
+
this.recordBlocker("pointer conversion between struct types");
|
|
1133
|
+
this.err(
|
|
1134
|
+
`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`,
|
|
1135
|
+
expr,
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
return true;
|
|
1139
|
+
},
|
|
1140
|
+
|
|
967
1141
|
_checkConversionValidity(srcType, targetType, expr) {
|
|
968
1142
|
if (!srcType || !targetType || isAny(srcType) || isAny(targetType)) return;
|
|
969
1143
|
const srcResolved = srcType.kind === "named" ? srcType.underlying : srcType;
|
|
970
1144
|
const tgtResolved =
|
|
971
1145
|
targetType.kind === "named" ? targetType.underlying : targetType;
|
|
972
1146
|
|
|
1147
|
+
// `nil` converts to any type that has a nil value.
|
|
1148
|
+
if (isNil(srcType) && NILABLE_KINDS.has(tgtResolved?.kind)) return;
|
|
1149
|
+
|
|
973
1150
|
if (isNumeric(targetType) && isComplex(srcType)) {
|
|
974
1151
|
this.err(
|
|
975
1152
|
`cannot convert ${typeStr(srcType)} to ${typeStr(targetType)} (use real() or imag())`,
|
|
@@ -1032,17 +1209,18 @@ export const expressionCheckMethods = {
|
|
|
1032
1209
|
srcResolved.elem?.name === "int32")))
|
|
1033
1210
|
)
|
|
1034
1211
|
return;
|
|
1035
|
-
if (
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1212
|
+
if (
|
|
1213
|
+
srcResolved?.kind === "pointer" &&
|
|
1214
|
+
tgtResolved?.kind === "pointer" &&
|
|
1215
|
+
this._pointerConversionOk(
|
|
1216
|
+
srcResolved,
|
|
1217
|
+
tgtResolved,
|
|
1218
|
+
srcType,
|
|
1219
|
+
targetType,
|
|
1220
|
+
expr,
|
|
1221
|
+
)
|
|
1222
|
+
)
|
|
1223
|
+
return;
|
|
1046
1224
|
this.err(
|
|
1047
1225
|
`cannot convert ${typeStr(srcType)} to ${typeStr(targetType)}`,
|
|
1048
1226
|
expr,
|
|
@@ -1052,6 +1230,7 @@ export const expressionCheckMethods = {
|
|
|
1052
1230
|
_checkTypeAssertExpr(expr, scope) {
|
|
1053
1231
|
const srcType = this.checkExpr(expr.expr, scope);
|
|
1054
1232
|
const targetType = this.resolveTypeNode(expr.type, scope);
|
|
1233
|
+
this._markDynamicType(expr.type, targetType);
|
|
1055
1234
|
|
|
1056
1235
|
// In Go, the source of a type assertion must be an interface type.
|
|
1057
1236
|
// We allow `any` (which is GoFront's basic any) and interface types.
|
|
@@ -1091,8 +1270,10 @@ export const expressionCheckMethods = {
|
|
|
1091
1270
|
"expr",
|
|
1092
1271
|
this.checkExpr(expr.expr, scope),
|
|
1093
1272
|
);
|
|
1094
|
-
const resolved =
|
|
1095
|
-
|
|
1273
|
+
const resolved = this._underlyingOrCore(collType, scope);
|
|
1274
|
+
// Backends range over the core type of a type parameter.
|
|
1275
|
+
if (collType?.kind === "typeParam" && resolved !== collType)
|
|
1276
|
+
expr.expr._coreType = resolved;
|
|
1096
1277
|
return this._rangeCollTypeTuple(resolved, collType);
|
|
1097
1278
|
},
|
|
1098
1279
|
|
|
@@ -1119,7 +1300,10 @@ export const expressionCheckMethods = {
|
|
|
1119
1300
|
) {
|
|
1120
1301
|
const tp = baseType.typeParams[i];
|
|
1121
1302
|
const constraint = tp.constraint
|
|
1122
|
-
? this.resolveTypeNode(
|
|
1303
|
+
? this.resolveTypeNode(
|
|
1304
|
+
tp.constraint,
|
|
1305
|
+
this._typeArgScope(baseType.typeParams, typeArgs, scope),
|
|
1306
|
+
)
|
|
1123
1307
|
: null;
|
|
1124
1308
|
if (constraint) this.checkConstraint(typeArgs[i], constraint, expr);
|
|
1125
1309
|
}
|
|
@@ -1298,12 +1482,12 @@ export const expressionCheckMethods = {
|
|
|
1298
1482
|
if (!resolved) return null;
|
|
1299
1483
|
fnType = resolved;
|
|
1300
1484
|
}
|
|
1301
|
-
return this._completePartialInstantiation(expr, argTypes) ?? fnType;
|
|
1485
|
+
return this._completePartialInstantiation(expr, argTypes, scope) ?? fnType;
|
|
1302
1486
|
},
|
|
1303
1487
|
|
|
1304
1488
|
// `F[int](x)` with fewer explicit type arguments than type parameters:
|
|
1305
1489
|
// infer the rest from the call arguments and record the full list.
|
|
1306
|
-
_completePartialInstantiation(expr, argTypes) {
|
|
1490
|
+
_completePartialInstantiation(expr, argTypes, scope) {
|
|
1307
1491
|
const inst = expr.func;
|
|
1308
1492
|
const generic = inst?.kind === "InstantiationExpr" && inst._genericFnType;
|
|
1309
1493
|
if (!generic || inst._typeArgs.length >= generic.typeParams.length)
|
|
@@ -1312,6 +1496,7 @@ export const expressionCheckMethods = {
|
|
|
1312
1496
|
generic,
|
|
1313
1497
|
this._inferArgTypes(expr, argTypes),
|
|
1314
1498
|
inst._typeArgs,
|
|
1499
|
+
scope,
|
|
1315
1500
|
);
|
|
1316
1501
|
if (!typeArgs) {
|
|
1317
1502
|
this.err(
|
|
@@ -1327,7 +1512,7 @@ export const expressionCheckMethods = {
|
|
|
1327
1512
|
|
|
1328
1513
|
// `T(x)` with T a type parameter: only a numeric x converts (the constraint
|
|
1329
1514
|
// decides the target at instantiation).
|
|
1330
|
-
_checkTypeParamConversion(tp, argTypes, expr) {
|
|
1515
|
+
_checkTypeParamConversion(tp, argTypes, expr, scope) {
|
|
1331
1516
|
expr._isTypeConversion = true;
|
|
1332
1517
|
expr._conversionTargetType = tp;
|
|
1333
1518
|
if (expr.args.length !== 1) {
|
|
@@ -1336,7 +1521,23 @@ export const expressionCheckMethods = {
|
|
|
1336
1521
|
expr,
|
|
1337
1522
|
);
|
|
1338
1523
|
}
|
|
1339
|
-
|
|
1524
|
+
// A constraint with a core type (`~[]E`) converts as that type does.
|
|
1525
|
+
const core = this._coreType(tp, scope);
|
|
1526
|
+
if (core) {
|
|
1527
|
+
const src = argTypes[0];
|
|
1528
|
+
const nilOk = ["slice", "map", "pointer", "func", "chan", "interface"];
|
|
1529
|
+
if (src === NIL || (src?.kind === "basic" && src.name === "nil")) {
|
|
1530
|
+
if (!nilOk.includes(core.kind))
|
|
1531
|
+
this.err(`cannot convert nil to ${typeStr(tp)}`, expr);
|
|
1532
|
+
} else if (src?.kind !== "typeParam") {
|
|
1533
|
+
this._checkConversionValidity(src, core, expr);
|
|
1534
|
+
}
|
|
1535
|
+
return tp;
|
|
1536
|
+
}
|
|
1537
|
+
if (
|
|
1538
|
+
isNumeric(argTypes[0]) &&
|
|
1539
|
+
!this._constraintHasTypeSet(tp.constraint, scope)
|
|
1540
|
+
)
|
|
1340
1541
|
return this.err(
|
|
1341
1542
|
`cannot convert ${typeStr(argTypes[0])} to type ${typeStr(tp)}: ${typeStr(tp)} is not constrained to a set of convertible types`,
|
|
1342
1543
|
expr,
|
|
@@ -1356,18 +1557,38 @@ export const expressionCheckMethods = {
|
|
|
1356
1557
|
// A conversion to a type parameter needs a constraint that lists types
|
|
1357
1558
|
// (`~int | ~float64`, or an interface embedding such a union); `any`,
|
|
1358
1559
|
// `comparable` and method-only constraints allow every type argument.
|
|
1359
|
-
_constraintHasTypeSet(constraint) {
|
|
1560
|
+
_constraintHasTypeSet(constraint, scope = this.globals) {
|
|
1360
1561
|
if (!constraint) return true;
|
|
1361
1562
|
if (constraint.kind === "UnionConstraint") return true;
|
|
1362
|
-
|
|
1563
|
+
// Receiver type parameters keep their constraint as an AST node.
|
|
1564
|
+
const c = this.resolveType(
|
|
1565
|
+
constraint.kind === "TypeName" || constraint.kind === "InterfaceType"
|
|
1566
|
+
? this.resolveTypeNode(constraint, scope)
|
|
1567
|
+
: constraint,
|
|
1568
|
+
);
|
|
1363
1569
|
const base = c?.kind === "named" ? this.resolveType(c.underlying) : c;
|
|
1364
1570
|
if (base?.kind !== "interface") return false;
|
|
1365
1571
|
if (base.unionConstraint) return true;
|
|
1366
1572
|
return (base._typeSetEmbeds ?? []).some((e) =>
|
|
1367
|
-
this._constraintHasTypeSet(e),
|
|
1573
|
+
this._constraintHasTypeSet(e, scope),
|
|
1368
1574
|
);
|
|
1369
1575
|
},
|
|
1370
1576
|
|
|
1577
|
+
// A direct call of a `//gofront:native` function is marked `_native` so a
|
|
1578
|
+
// backend may substitute host code. A function used as a value (`f :=
|
|
1579
|
+
// str.Index; f(a, b)`) is not a direct reference and calls the Go body.
|
|
1580
|
+
_markNativeCall(expr, fnType, scope) {
|
|
1581
|
+
const callee =
|
|
1582
|
+
expr.func.kind === "InstantiationExpr" ? expr.func.expr : expr.func;
|
|
1583
|
+
const native = fnType?.native ?? fnType?.underlying?.native ?? null;
|
|
1584
|
+
const direct =
|
|
1585
|
+
callee.kind === "Ident"
|
|
1586
|
+
? scope.lookupScope(callee.name) === this.globals &&
|
|
1587
|
+
Boolean(this._funcDeclNames?.has(callee.name))
|
|
1588
|
+
: callee.kind === "SelectorExpr" && Boolean(callee._stdName);
|
|
1589
|
+
expr._native = native && direct ? native : null;
|
|
1590
|
+
},
|
|
1591
|
+
|
|
1371
1592
|
checkCall(expr, scope) {
|
|
1372
1593
|
expr.func._typeRefOk = true;
|
|
1373
1594
|
let fnType = this.checkExpr(expr.func, scope);
|
|
@@ -1391,6 +1612,7 @@ export const expressionCheckMethods = {
|
|
|
1391
1612
|
return this.checkBuiltin(fnType.name, expr, argTypes, scope);
|
|
1392
1613
|
}
|
|
1393
1614
|
|
|
1615
|
+
this._markNativeCall(expr, fnType, scope);
|
|
1394
1616
|
const argTypes = this._resolveCallArgTypes(expr, scope);
|
|
1395
1617
|
|
|
1396
1618
|
if (fnType?._tainted) return TAINTED_ANY;
|
|
@@ -1401,7 +1623,12 @@ export const expressionCheckMethods = {
|
|
|
1401
1623
|
|
|
1402
1624
|
// Named type used as a constructor/conversion: Greeter(fn), NodeFunc(fn)
|
|
1403
1625
|
// The callee is a type name, not a callable variable.
|
|
1404
|
-
if (
|
|
1626
|
+
if (
|
|
1627
|
+
(fnType.kind === "named" ||
|
|
1628
|
+
fnType.kind === "pointer" ||
|
|
1629
|
+
fnType.kind === "basic") &&
|
|
1630
|
+
expr.func._isTypeRef
|
|
1631
|
+
) {
|
|
1405
1632
|
expr._isTypeConversion = true;
|
|
1406
1633
|
expr._conversionTargetType = fnType;
|
|
1407
1634
|
if (expr.args.length !== 1) {
|
|
@@ -1416,7 +1643,7 @@ export const expressionCheckMethods = {
|
|
|
1416
1643
|
}
|
|
1417
1644
|
|
|
1418
1645
|
if (fnType.kind === "typeParam" && expr.func._isTypeRef) {
|
|
1419
|
-
return this._checkTypeParamConversion(fnType, argTypes, expr);
|
|
1646
|
+
return this._checkTypeParamConversion(fnType, argTypes, expr, scope);
|
|
1420
1647
|
}
|
|
1421
1648
|
|
|
1422
1649
|
fnType = this._resolveCallFnType(fnType, argTypes, expr, scope);
|
|
@@ -1427,13 +1654,6 @@ export const expressionCheckMethods = {
|
|
|
1427
1654
|
|
|
1428
1655
|
this._checkCallArgs(fnType, argTypes, expr, scope);
|
|
1429
1656
|
|
|
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
1657
|
const ret = fnType.returns;
|
|
1438
1658
|
if (!ret || ret.length === 0) return VOID;
|
|
1439
1659
|
if (ret.length === 1) return ret[0];
|
|
@@ -1483,19 +1703,6 @@ export const expressionCheckMethods = {
|
|
|
1483
1703
|
);
|
|
1484
1704
|
},
|
|
1485
1705
|
|
|
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
1706
|
// Argument types for inference: a spread argument `xs...` stands for its
|
|
1500
1707
|
// elements, which is what the variadic parameter type describes.
|
|
1501
1708
|
_inferArgTypes(expr, argTypes) {
|
|
@@ -1506,10 +1713,23 @@ export const expressionCheckMethods = {
|
|
|
1506
1713
|
});
|
|
1507
1714
|
},
|
|
1508
1715
|
|
|
1716
|
+
// Scope where each type parameter name stands for its type argument, so a
|
|
1717
|
+
// constraint that mentions another parameter (`S ~[]E`) resolves.
|
|
1718
|
+
_typeArgScope(typeParams, typeArgs, scope) {
|
|
1719
|
+
const out = new Scope(scope);
|
|
1720
|
+
typeParams.forEach((tp, i) => {
|
|
1721
|
+
if (typeArgs[i])
|
|
1722
|
+
out.define(tp.name, { kind: "aliasArg", type: typeArgs[i] });
|
|
1723
|
+
});
|
|
1724
|
+
return out;
|
|
1725
|
+
},
|
|
1726
|
+
|
|
1509
1727
|
_resolveGenericFnType(fnType, argTypes, expr, scope) {
|
|
1510
1728
|
const typeArgs = this.inferTypeArgs(
|
|
1511
1729
|
fnType,
|
|
1512
1730
|
this._inferArgTypes(expr, argTypes),
|
|
1731
|
+
[],
|
|
1732
|
+
scope,
|
|
1513
1733
|
);
|
|
1514
1734
|
if (!typeArgs) {
|
|
1515
1735
|
this.err(
|
|
@@ -1522,7 +1742,10 @@ export const expressionCheckMethods = {
|
|
|
1522
1742
|
for (let i = 0; i < fnType.typeParams.length && i < typeArgs.length; i++) {
|
|
1523
1743
|
const tp = fnType.typeParams[i];
|
|
1524
1744
|
const constraint = tp.constraint
|
|
1525
|
-
? this.resolveTypeNode(
|
|
1745
|
+
? this.resolveTypeNode(
|
|
1746
|
+
tp.constraint,
|
|
1747
|
+
this._typeArgScope(fnType.typeParams, typeArgs, scope),
|
|
1748
|
+
)
|
|
1526
1749
|
: null;
|
|
1527
1750
|
if (constraint) this.checkConstraint(typeArgs[i], constraint, expr);
|
|
1528
1751
|
}
|
|
@@ -1562,6 +1785,9 @@ export const expressionCheckMethods = {
|
|
|
1562
1785
|
!ns._gofront &&
|
|
1563
1786
|
!ns._mangle?.has(expr.func.field);
|
|
1564
1787
|
if (emitterBacked) return;
|
|
1788
|
+
// The generated wrapper of a Print-family call takes the results as
|
|
1789
|
+
// separate parameters (src/lower/fmt-formatters.js).
|
|
1790
|
+
if (ns?.name === "fmt" && FMT_PRINT_FUNCS.has(expr.func.field)) return;
|
|
1565
1791
|
this.err(
|
|
1566
1792
|
"passing multiple return values to a variadic function is not supported in wasm packages; assign them to variables first",
|
|
1567
1793
|
expr,
|
|
@@ -1654,16 +1880,31 @@ export const expressionCheckMethods = {
|
|
|
1654
1880
|
|
|
1655
1881
|
_checkBuiltinLen(name, expr, argTypes) {
|
|
1656
1882
|
this._rejectSeqAsSlice(argTypes[0], expr.args?.[0], `${name}()`);
|
|
1883
|
+
const operand = expr.args?.[0];
|
|
1657
1884
|
if (expr.args?.length === 1)
|
|
1658
1885
|
argTypes[0] = this._derefArrayPtr(expr.args, 0, argTypes[0]);
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1886
|
+
// `len` and `cap` of an array (or pointer to one) are its length.
|
|
1887
|
+
const size = this._constLenFromType(argTypes[0]);
|
|
1888
|
+
if (size != null) {
|
|
1889
|
+
expr._constLen = size;
|
|
1890
|
+
// The operand is not dereferenced: a nil `*[N]T` has length N.
|
|
1891
|
+
if (expr.args.length === 1) expr.args[0] = operand;
|
|
1892
|
+
}
|
|
1893
|
+
if (name === "cap") {
|
|
1894
|
+
if (
|
|
1895
|
+
(argTypes[0]?.underlying ?? argTypes[0])?.kind === "map" ||
|
|
1896
|
+
isString(argTypes[0])
|
|
1897
|
+
) {
|
|
1898
|
+
this.err(
|
|
1899
|
+
`invalid argument: ${exprText(expr.args?.[0])} (${operandKind(expr.args?.[0], typeStr(argTypes[0]), false)}) for cap`,
|
|
1900
|
+
expr,
|
|
1901
|
+
);
|
|
1902
|
+
} else if (sharedInfo(argTypes[0])) {
|
|
1903
|
+
this.err(
|
|
1904
|
+
`cannot use cap() on ${typeStr(argTypes[0])}: shared buffers have a fixed size; use len()`,
|
|
1905
|
+
expr,
|
|
1906
|
+
);
|
|
1907
|
+
}
|
|
1667
1908
|
}
|
|
1668
1909
|
return INT;
|
|
1669
1910
|
},
|
|
@@ -1778,17 +2019,7 @@ export const expressionCheckMethods = {
|
|
|
1778
2019
|
_checkStructPositionalElems(expr, scope, t, base) {
|
|
1779
2020
|
const fieldNames = base._structFields
|
|
1780
2021
|
? 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,
|
|
2022
|
+
f.embedded ? [embeddedFieldName(f)] : f.names,
|
|
1792
2023
|
)
|
|
1793
2024
|
: [...(base.fields?.keys() ?? [])];
|
|
1794
2025
|
for (let i = 0; i < expr.elems.length; i++) {
|
|
@@ -1810,12 +2041,9 @@ export const expressionCheckMethods = {
|
|
|
1810
2041
|
for (const elem of expr.elems) {
|
|
1811
2042
|
if (elem.kind === "KeyValueExpr") {
|
|
1812
2043
|
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
|
-
});
|
|
2044
|
+
const embed = base._structFields?.find(
|
|
2045
|
+
(f) => f.embedded && embeddedFieldName(f) === keyName,
|
|
2046
|
+
)?.type;
|
|
1819
2047
|
if (embed) elem._isEmbedInit = true;
|
|
1820
2048
|
const fieldType = base.fields?.get(keyName) ?? embed;
|
|
1821
2049
|
if (fieldType) {
|