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
|
@@ -3,14 +3,19 @@
|
|
|
3
3
|
|
|
4
4
|
import { parseJsonTag } from "../parser/struct-tag.js";
|
|
5
5
|
import { foldConstExpr } from "./const-fold.js";
|
|
6
|
+
import { exprText, operandKind } from "./operand-text.js";
|
|
6
7
|
import { EXCLUDED_STDLIB_MEMBERS } from "./stdlib/excluded.js";
|
|
7
8
|
import {
|
|
8
9
|
ANY,
|
|
9
10
|
BASIC_TYPES,
|
|
10
11
|
COMPARABLE,
|
|
12
|
+
DECLARED_ANY,
|
|
11
13
|
defaultType,
|
|
12
14
|
displayTypeName,
|
|
15
|
+
isAmbiguousFieldSelector,
|
|
16
|
+
isAmbiguousPromotion,
|
|
13
17
|
isAny,
|
|
18
|
+
isMethodPromotedOverField,
|
|
14
19
|
isTypedArraySlice,
|
|
15
20
|
Scope,
|
|
16
21
|
TAINTED_ANY,
|
|
@@ -27,6 +32,11 @@ import {
|
|
|
27
32
|
// instantiation is reported as a cycle.
|
|
28
33
|
const MAX_INSTANTIATION_DEPTH = 32;
|
|
29
34
|
|
|
35
|
+
// Type nodes in a constraint position, where a constraint interface may
|
|
36
|
+
// appear. A property of the AST node, so it is shared by every checker: an
|
|
37
|
+
// importing package resolves the constraints of the imported generics.
|
|
38
|
+
const CONSTRAINT_POS = new WeakSet();
|
|
39
|
+
|
|
30
40
|
// Two imported packages may both declare `Pt`, so the cache key of an
|
|
31
41
|
// instance tells declarations apart by identity instead of by display name.
|
|
32
42
|
const declIds = new WeakMap();
|
|
@@ -71,6 +81,30 @@ function typeSize(t) {
|
|
|
71
81
|
}
|
|
72
82
|
}
|
|
73
83
|
|
|
84
|
+
// Whether `t` mentions one of the type parameters `params` below its root.
|
|
85
|
+
function nestsTypeParam(t, params) {
|
|
86
|
+
const mentions = (u) => {
|
|
87
|
+
switch (u?.kind) {
|
|
88
|
+
case "typeParam":
|
|
89
|
+
return params.includes(u);
|
|
90
|
+
case "pointer":
|
|
91
|
+
return mentions(u.base);
|
|
92
|
+
case "slice":
|
|
93
|
+
case "array":
|
|
94
|
+
return mentions(u.elem);
|
|
95
|
+
case "map":
|
|
96
|
+
return mentions(u.key) || mentions(u.value);
|
|
97
|
+
case "func":
|
|
98
|
+
return [...(u.params ?? []), ...(u.returns ?? [])].some(mentions);
|
|
99
|
+
case "named":
|
|
100
|
+
return (u.typeArgs ?? []).some(mentions);
|
|
101
|
+
default:
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
return t?.kind !== "typeParam" && mentions(t);
|
|
106
|
+
}
|
|
107
|
+
|
|
74
108
|
function instanceKey(t) {
|
|
75
109
|
switch (t?.kind) {
|
|
76
110
|
case "named":
|
|
@@ -133,13 +167,34 @@ const SUBSTITUTE_DISPATCH = {
|
|
|
133
167
|
fields,
|
|
134
168
|
methods,
|
|
135
169
|
_embeds: type._embeds,
|
|
136
|
-
|
|
170
|
+
// The JSON codecs read the field types from here.
|
|
171
|
+
_structFields: type._structFields?.map((f) => ({
|
|
172
|
+
...f,
|
|
173
|
+
type: s.substituteType(f.type, map),
|
|
174
|
+
})),
|
|
137
175
|
};
|
|
138
176
|
},
|
|
139
177
|
interface: (s, type, map) => {
|
|
140
178
|
const methods = new Map();
|
|
141
179
|
for (const [k, v] of type.methods) methods.set(k, s.substituteType(v, map));
|
|
142
|
-
|
|
180
|
+
const out = { kind: "interface", name: type.name, methods };
|
|
181
|
+
// The type set follows the substitution (`interface{ ~[]E }` with E=int).
|
|
182
|
+
if (type.unionConstraint)
|
|
183
|
+
out.unionConstraint = {
|
|
184
|
+
...type.unionConstraint,
|
|
185
|
+
terms: type.unionConstraint.terms.map((term) => ({
|
|
186
|
+
...term,
|
|
187
|
+
resolved: s.substituteType(
|
|
188
|
+
term.resolved ?? s.resolveTypeNode(term.type, type._unionScope),
|
|
189
|
+
map,
|
|
190
|
+
),
|
|
191
|
+
})),
|
|
192
|
+
};
|
|
193
|
+
if (type._typeSetEmbeds)
|
|
194
|
+
out._typeSetEmbeds = type._typeSetEmbeds.map((e) =>
|
|
195
|
+
s.substituteType(e, map),
|
|
196
|
+
);
|
|
197
|
+
return out;
|
|
143
198
|
},
|
|
144
199
|
named: (s, type, map) => {
|
|
145
200
|
// An instance of a generic type: re-instantiate with the substituted
|
|
@@ -167,8 +222,10 @@ export const resolveMethods = {
|
|
|
167
222
|
if (!node) return ANY;
|
|
168
223
|
switch (node.kind) {
|
|
169
224
|
case "TypeName": {
|
|
170
|
-
if (BASIC_TYPES[node.name])
|
|
171
|
-
|
|
225
|
+
if (BASIC_TYPES[node.name])
|
|
226
|
+
return node.name === "any" ? DECLARED_ANY : BASIC_TYPES[node.name];
|
|
227
|
+
if (node.name === "comparable")
|
|
228
|
+
return this._checkConstraintUse(COMPARABLE, node);
|
|
172
229
|
if (scope) {
|
|
173
230
|
const fromScope = scope.lookup(node.name);
|
|
174
231
|
if (fromScope?.kind === "typeParam") return fromScope;
|
|
@@ -176,11 +233,11 @@ export const resolveMethods = {
|
|
|
176
233
|
}
|
|
177
234
|
const named = this.types.get(node.name);
|
|
178
235
|
if (named) {
|
|
179
|
-
const wasmErr = this._checkWasmStdlibTypeMember(node);
|
|
180
|
-
if (wasmErr) return wasmErr;
|
|
181
236
|
this._useQualifiedTypeName(node, named);
|
|
182
|
-
return named;
|
|
237
|
+
return this._checkConstraintUse(named, node);
|
|
183
238
|
}
|
|
239
|
+
const jsOnly = this._jsOnlyMember(node.name);
|
|
240
|
+
if (jsOnly) return this._errJsOnly(jsOnly, node);
|
|
184
241
|
if (EXCLUDED_STDLIB_MEMBERS.has(node.name)) {
|
|
185
242
|
return this.err(
|
|
186
243
|
`${node.name} is not available: ${EXCLUDED_STDLIB_MEMBERS.get(node.name)}`,
|
|
@@ -194,6 +251,7 @@ export const resolveMethods = {
|
|
|
194
251
|
case "IndexExpr":
|
|
195
252
|
return this._resolveGenericTypeName(node, scope);
|
|
196
253
|
case "TypeParam": {
|
|
254
|
+
this._markConstraintPos(node.constraint);
|
|
197
255
|
const constraint = node.constraint
|
|
198
256
|
? this.resolveTypeNode(node.constraint, scope)
|
|
199
257
|
: ANY;
|
|
@@ -217,7 +275,17 @@ export const resolveMethods = {
|
|
|
217
275
|
case "StructType":
|
|
218
276
|
return this._resolveStructType(node, scope);
|
|
219
277
|
case "InterfaceType":
|
|
220
|
-
return this.
|
|
278
|
+
return this._checkConstraintUse(
|
|
279
|
+
this._resolveInterfaceType(node, scope),
|
|
280
|
+
node,
|
|
281
|
+
);
|
|
282
|
+
case "UnaryExpr":
|
|
283
|
+
// `new(*T)`: the parser reads the pointer type as a dereference.
|
|
284
|
+
if (node.op !== "*") return ANY;
|
|
285
|
+
return {
|
|
286
|
+
kind: "pointer",
|
|
287
|
+
base: this.resolveTypeNode(node.operand, scope),
|
|
288
|
+
};
|
|
221
289
|
case "Ident":
|
|
222
290
|
return this.resolveTypeNode(
|
|
223
291
|
{ kind: "TypeName", name: node.name },
|
|
@@ -229,10 +297,9 @@ export const resolveMethods = {
|
|
|
229
297
|
if (node.expr?.kind === "Ident" && typeof node.field === "string") {
|
|
230
298
|
const qualified = `${node.expr.name}.${node.field}`;
|
|
231
299
|
if (this.types.has(qualified)) {
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
);
|
|
300
|
+
const inner = { kind: "TypeName", name: qualified };
|
|
301
|
+
this._inheritConstraintPos(node, inner);
|
|
302
|
+
const resolved = this.resolveTypeNode(inner, scope);
|
|
236
303
|
this._useQualifiedTypeName(node, resolved);
|
|
237
304
|
return resolved;
|
|
238
305
|
}
|
|
@@ -252,23 +319,112 @@ export const resolveMethods = {
|
|
|
252
319
|
}
|
|
253
320
|
},
|
|
254
321
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
return
|
|
266
|
-
|
|
322
|
+
// ── Constraint interfaces outside constraints ───────────────────
|
|
323
|
+
|
|
324
|
+
// Records a type node that sits in a constraint position (a type
|
|
325
|
+
// parameter's constraint, an interface's embedded element or union term,
|
|
326
|
+
// the right-hand side of a type declaration), where a constraint
|
|
327
|
+
// interface is allowed.
|
|
328
|
+
_markConstraintPos(node) {
|
|
329
|
+
if (!node || typeof node !== "object") return;
|
|
330
|
+
if (node.kind === "UnionConstraint") {
|
|
331
|
+
for (const term of node.terms) this._markConstraintPos(term.type);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
CONSTRAINT_POS.add(node);
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
// `inner` stands for `node` (a rewritten qualified name).
|
|
338
|
+
_inheritConstraintPos(node, inner) {
|
|
339
|
+
if (CONSTRAINT_POS.has(node)) CONSTRAINT_POS.add(inner);
|
|
340
|
+
},
|
|
341
|
+
|
|
342
|
+
// An interface's embedded elements and union terms are constraint positions.
|
|
343
|
+
_markInterfaceElements(node) {
|
|
344
|
+
for (const embed of node.embeds ?? []) this._markConstraintPos(embed);
|
|
345
|
+
this._markConstraintPos(node.unionConstraint);
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
// Marks the constraint positions of top-level declarations before any
|
|
349
|
+
// type resolves (constraints resolve lazily from several places).
|
|
350
|
+
_markConstraintPositions(programs) {
|
|
351
|
+
for (const p of programs) {
|
|
352
|
+
for (const d of p.decls) {
|
|
353
|
+
if (d.kind === "TypeDecl") this._markConstraintPos(d.type);
|
|
354
|
+
for (const tp of d.typeParams ?? [])
|
|
355
|
+
this._markConstraintPos(tp.constraint);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
|
|
360
|
+
// A constraint interface (one with a type set, or comparable) resolved
|
|
361
|
+
// outside a constraint position is an error. A type whose declaration has
|
|
362
|
+
// not resolved yet is judged once every package-level type has.
|
|
363
|
+
_checkConstraintUse(type, node) {
|
|
364
|
+
if (CONSTRAINT_POS.has(node)) return type;
|
|
365
|
+
if (type?.kind === "named" && type.underlying == null) {
|
|
366
|
+
this._pendingConstraintUses.push({
|
|
367
|
+
type,
|
|
368
|
+
node,
|
|
369
|
+
file: this._currentFile,
|
|
370
|
+
source: this._currentSource,
|
|
371
|
+
});
|
|
372
|
+
return type;
|
|
373
|
+
}
|
|
374
|
+
this._reportConstraintUse(type, node);
|
|
375
|
+
return type;
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
_reportConstraintUse(type, node) {
|
|
379
|
+
const u = this._deepUnderlying(type);
|
|
380
|
+
if (u === COMPARABLE || (u?.kind === "interface" && u._comparable)) {
|
|
381
|
+
this.err(
|
|
382
|
+
`cannot use type ${typeStr(type)} outside a type constraint: interface is (or embeds) comparable`,
|
|
383
|
+
node,
|
|
384
|
+
);
|
|
385
|
+
} else if (
|
|
386
|
+
u?.kind === "interface" &&
|
|
387
|
+
(u.unionConstraint || u._typeSetEmbeds)
|
|
388
|
+
) {
|
|
389
|
+
this.err(
|
|
390
|
+
`cannot use type ${typeStr(type)} outside a type constraint: interface contains type constraints`,
|
|
267
391
|
node,
|
|
268
|
-
"move this call to a JS package, or use a supported alternative",
|
|
269
392
|
);
|
|
270
393
|
}
|
|
271
|
-
|
|
394
|
+
},
|
|
395
|
+
|
|
396
|
+
_flushConstraintUses() {
|
|
397
|
+
const pending = this._pendingConstraintUses;
|
|
398
|
+
if (pending.length === 0) return;
|
|
399
|
+
this._pendingConstraintUses = [];
|
|
400
|
+
const file = this._currentFile;
|
|
401
|
+
const source = this._currentSource;
|
|
402
|
+
for (const u of pending) {
|
|
403
|
+
this._currentFile = u.file;
|
|
404
|
+
this._currentSource = u.source;
|
|
405
|
+
this._reportConstraintUse(u.type, u.node);
|
|
406
|
+
}
|
|
407
|
+
this._currentFile = file;
|
|
408
|
+
this._currentSource = source;
|
|
409
|
+
},
|
|
410
|
+
|
|
411
|
+
// `pkg.Name` when `pkg` is a Go-source package whose `js` files declare
|
|
412
|
+
// Name and this package is wasm or both (those files are not loaded).
|
|
413
|
+
_jsOnlyMember(qualified) {
|
|
414
|
+
if (this.target !== "wasm" && this.target !== "both") return null;
|
|
415
|
+
const dot = qualified.indexOf(".");
|
|
416
|
+
if (dot < 0) return null;
|
|
417
|
+
const ns = this.globals.lookup(qualified.slice(0, dot));
|
|
418
|
+
return ns?._jsOnly?.has(qualified.slice(dot + 1)) ? qualified : null;
|
|
419
|
+
},
|
|
420
|
+
|
|
421
|
+
_errJsOnly(qualified, node) {
|
|
422
|
+
this.recordBlocker("unsupported stdlib member", qualified);
|
|
423
|
+
return this.err(
|
|
424
|
+
`'${qualified}' is only available in js packages`,
|
|
425
|
+
node,
|
|
426
|
+
"move this call to a JS package",
|
|
427
|
+
);
|
|
272
428
|
},
|
|
273
429
|
|
|
274
430
|
// `pkg.T` in a type position counts as using the import; a Go-source
|
|
@@ -288,20 +444,81 @@ export const resolveMethods = {
|
|
|
288
444
|
const e = depth < 16 ? scope.lookupConstExpr(name) : null;
|
|
289
445
|
return e ? foldConstExpr(e, (n) => resolve(n, depth + 1)) : undefined;
|
|
290
446
|
};
|
|
447
|
+
let n = Number.NaN;
|
|
291
448
|
try {
|
|
292
449
|
const lit = foldConstExpr(size, resolve);
|
|
293
|
-
|
|
294
|
-
return Number.isInteger(n) && n >= 0 ? n : size;
|
|
450
|
+
n = lit ? Number(lit.value) : Number.NaN;
|
|
295
451
|
} catch {
|
|
452
|
+
// fall through to the checker's evaluator
|
|
453
|
+
}
|
|
454
|
+
// `[math.MaxInt8]T`: a package constant the AST folder cannot see.
|
|
455
|
+
if (!Number.isInteger(n))
|
|
456
|
+
n = this._constIntValue(size, scope) ?? Number.NaN;
|
|
457
|
+
if (!Number.isInteger(n)) {
|
|
458
|
+
this._reportNonConstantArrayLen(size, scope);
|
|
296
459
|
return size;
|
|
297
460
|
}
|
|
461
|
+
this._useQualifiedConsts(size);
|
|
462
|
+
if (n >= 0) return n;
|
|
463
|
+
this.err(
|
|
464
|
+
`invalid array length ${exprText(size)} (untyped int constant ${n})`,
|
|
465
|
+
size,
|
|
466
|
+
);
|
|
467
|
+
return 0;
|
|
468
|
+
},
|
|
469
|
+
|
|
470
|
+
// A length that is not a constant at all (`[len(s)]T` with a slice, `[n]T`
|
|
471
|
+
// with a variable) cannot size an array. The operand's type is checked
|
|
472
|
+
// first, so it counts as used.
|
|
473
|
+
_reportNonConstantArrayLen(size, scope) {
|
|
474
|
+
if (size._type === undefined) this.checkExpr(size, scope);
|
|
475
|
+
this.err(
|
|
476
|
+
`invalid array length ${exprText(size)} (${operandKind(size, typeStr(size._type), false)}): must be constant`,
|
|
477
|
+
size,
|
|
478
|
+
);
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
// An array length must be an integer constant: `[1 == 1]T`, `["a"]T`,
|
|
482
|
+
// `[nil]T`, `[2.5]T`, `[2i]T` and a typed float constant are rejected.
|
|
483
|
+
_reportNonIntegerArrayLen(size, scope) {
|
|
484
|
+
const what = this._nonIntegerConst(size, scope);
|
|
485
|
+
if (!what) return false;
|
|
486
|
+
this.err(
|
|
487
|
+
`invalid array length ${exprText(size)} (${what}): must be integer`,
|
|
488
|
+
size,
|
|
489
|
+
);
|
|
490
|
+
return true;
|
|
491
|
+
},
|
|
492
|
+
|
|
493
|
+
// `pkg.K` inside a folded array length counts as using the import.
|
|
494
|
+
_useQualifiedConsts(node) {
|
|
495
|
+
if (!node || typeof node !== "object") return;
|
|
496
|
+
if (node.kind === "SelectorExpr" && node.expr?.kind === "Ident")
|
|
497
|
+
this.globals.lookup(node.expr.name);
|
|
498
|
+
for (const key of ["expr", "left", "right", "operand"])
|
|
499
|
+
this._useQualifiedConsts(node[key]);
|
|
500
|
+
for (const arg of node.args ?? []) this._useQualifiedConsts(arg);
|
|
298
501
|
},
|
|
299
502
|
|
|
300
503
|
_resolveArrayTypeNode(node, scope) {
|
|
504
|
+
if (
|
|
505
|
+
!node.inferLen &&
|
|
506
|
+
this._reportNonIntegerArrayLen(node._sizeExpr ?? node.size, scope)
|
|
507
|
+
)
|
|
508
|
+
return {
|
|
509
|
+
kind: "array",
|
|
510
|
+
size: 0,
|
|
511
|
+
elem: this.resolveTypeNode(node.elem, scope),
|
|
512
|
+
};
|
|
301
513
|
// Fold in place so backends that read the type node see a literal length.
|
|
302
|
-
|
|
514
|
+
// The node may come from the parse cache: keep the original expression so
|
|
515
|
+
// a later compile of the same AST still counts `pkg.K` as using `pkg`
|
|
516
|
+
// and still reports an invalid length.
|
|
517
|
+
if (node._sizeExpr) this._foldArrayLen(node._sizeExpr, scope);
|
|
518
|
+
else if (!node.inferLen && node.size?.value === undefined) {
|
|
303
519
|
const n = this._foldArrayLen(node.size, scope);
|
|
304
|
-
if (typeof n === "number")
|
|
520
|
+
if (typeof n === "number") {
|
|
521
|
+
node._sizeExpr = node.size;
|
|
305
522
|
node.size = {
|
|
306
523
|
kind: "BasicLit",
|
|
307
524
|
litKind: "INT",
|
|
@@ -309,6 +526,7 @@ export const resolveMethods = {
|
|
|
309
526
|
_line: node.size._line,
|
|
310
527
|
_col: node.size._col,
|
|
311
528
|
};
|
|
529
|
+
}
|
|
312
530
|
}
|
|
313
531
|
return {
|
|
314
532
|
kind: "array",
|
|
@@ -337,8 +555,11 @@ export const resolveMethods = {
|
|
|
337
555
|
? this.resolveTypeNode(node.expr, scope)
|
|
338
556
|
: null;
|
|
339
557
|
if (!base) return this.err(`Unknown type '${name ?? "?"}'`, node);
|
|
340
|
-
if (qualified)
|
|
341
|
-
|
|
558
|
+
if (qualified) {
|
|
559
|
+
this.globals.lookup(sel.expr.name);
|
|
560
|
+
// `iter.Seq[int](f)`: the selector names the mangled stdlib type.
|
|
561
|
+
this._useQualifiedTypeName(sel, base);
|
|
562
|
+
} else if (name)
|
|
342
563
|
this._useQualifiedTypeName(node.name ? node : node.expr, base);
|
|
343
564
|
if (base.kind === "named" && base._generic?.isAlias) {
|
|
344
565
|
const aliasArgs = typeArgs.map((n) => this.resolveTypeNode(n, scope));
|
|
@@ -417,6 +638,7 @@ export const resolveMethods = {
|
|
|
417
638
|
},
|
|
418
639
|
|
|
419
640
|
_resolveInterfaceType(node, scope) {
|
|
641
|
+
this._markInterfaceElements(node);
|
|
420
642
|
const methods = new Map();
|
|
421
643
|
for (const m of node.methods) {
|
|
422
644
|
const params = m.params.map((p) => this.resolveTypeNode(p.type, scope));
|
|
@@ -432,31 +654,41 @@ export const resolveMethods = {
|
|
|
432
654
|
// Embedded interfaces that carry a type set (`interface{ Integer }`):
|
|
433
655
|
// kept so type-parameter operator checks can follow them.
|
|
434
656
|
const typeSetEmbeds = [];
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
for (const [mName, mType] of base.methods) {
|
|
442
|
-
if (!methods.has(mName)) methods.set(mName, mType);
|
|
443
|
-
}
|
|
444
|
-
if (base.unionConstraint || base._typeSetEmbeds)
|
|
445
|
-
typeSetEmbeds.push(base);
|
|
446
|
-
} else if (!isAny(resolved)) {
|
|
447
|
-
this.err(
|
|
448
|
-
`cannot embed non-interface type ${typeStr(resolved)}`,
|
|
449
|
-
embed,
|
|
450
|
-
);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
657
|
+
// `interface{ comparable; ... }`: the type set is restricted to
|
|
658
|
+
// comparable types (directly or through an embedded constraint).
|
|
659
|
+
let comparable = false;
|
|
660
|
+
for (const embed of node.embeds ?? []) {
|
|
661
|
+
if (this._mergeInterfaceEmbed(embed, scope, methods, typeSetEmbeds))
|
|
662
|
+
comparable = true;
|
|
453
663
|
}
|
|
454
664
|
const result = { kind: "interface", methods };
|
|
455
|
-
if (node.unionConstraint)
|
|
665
|
+
if (node.unionConstraint) {
|
|
666
|
+
result.unionConstraint = node.unionConstraint;
|
|
667
|
+
result._unionScope = scope;
|
|
668
|
+
}
|
|
456
669
|
if (typeSetEmbeds.length > 0) result._typeSetEmbeds = typeSetEmbeds;
|
|
670
|
+
if (comparable) result._comparable = true;
|
|
457
671
|
return result;
|
|
458
672
|
},
|
|
459
673
|
|
|
674
|
+
// Folds one embedded element of an interface into `methods` and
|
|
675
|
+
// `typeSetEmbeds`; returns true when it makes the interface comparable-only.
|
|
676
|
+
_mergeInterfaceEmbed(embed, scope, methods, typeSetEmbeds) {
|
|
677
|
+
const resolved = this.resolveTypeNode(embed, scope);
|
|
678
|
+
const base = resolved?.kind === "named" ? resolved.underlying : resolved;
|
|
679
|
+
if (base === COMPARABLE) return true;
|
|
680
|
+
if (base?.kind !== "interface") {
|
|
681
|
+
if (!isAny(resolved))
|
|
682
|
+
this.err(`cannot embed non-interface type ${typeStr(resolved)}`, embed);
|
|
683
|
+
return false;
|
|
684
|
+
}
|
|
685
|
+
for (const [mName, mType] of base.methods) {
|
|
686
|
+
if (!methods.has(mName)) methods.set(mName, mType);
|
|
687
|
+
}
|
|
688
|
+
if (base.unionConstraint || base._typeSetEmbeds) typeSetEmbeds.push(base);
|
|
689
|
+
return Boolean(base._comparable);
|
|
690
|
+
},
|
|
691
|
+
|
|
460
692
|
// ── Generics — substitution, instantiation, inference ────────────
|
|
461
693
|
|
|
462
694
|
substituteType(type, map) {
|
|
@@ -501,8 +733,15 @@ export const resolveMethods = {
|
|
|
501
733
|
// settles (`T[X]` containing `T[[]X]`), and doubling forms such as
|
|
502
734
|
// `T[Pair[X, X]]` blow up long before a depth limit is reached.
|
|
503
735
|
const top = generic._buildSizes[generic._buildSizes.length - 1];
|
|
736
|
+
// Inside a method of `G[T]`, `G[[]T]` makes every instance ask for a
|
|
737
|
+
// larger one, even if the method is never called.
|
|
738
|
+
const growsReceiver =
|
|
739
|
+
this._recvGeneric === generic &&
|
|
740
|
+
typeArgs.some((a) => nestsTypeParam(a, generic.typeParams));
|
|
504
741
|
if (
|
|
742
|
+
growsReceiver ||
|
|
505
743
|
building.length >= MAX_INSTANTIATION_DEPTH ||
|
|
744
|
+
(generic._syncDepth ?? 0) >= MAX_INSTANTIATION_DEPTH ||
|
|
506
745
|
(top !== undefined && size > top)
|
|
507
746
|
) {
|
|
508
747
|
if (!generic._cycleReported) {
|
|
@@ -557,13 +796,20 @@ export const resolveMethods = {
|
|
|
557
796
|
const methods = named.methods;
|
|
558
797
|
if (!methods || !named._instMap || named._syncing) return;
|
|
559
798
|
// A method signature that names the instance reaches this again.
|
|
799
|
+
// `func (s Stack[T]) Wrap() Stack[[]T]` makes every instance's methods
|
|
800
|
+
// ask for a larger instance: count the nesting to diagnose it.
|
|
560
801
|
named._syncing = true;
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
802
|
+
generic._syncDepth = (generic._syncDepth ?? 0) + 1;
|
|
803
|
+
try {
|
|
804
|
+
for (const [mName, mType] of generic.methods ?? [])
|
|
805
|
+
if (!methods.has(mName))
|
|
806
|
+
methods.set(mName, this.substituteType(mType, named._instMap));
|
|
807
|
+
// Methods promoted from embedded structs (`Outer[T]` embedding `Base`).
|
|
808
|
+
this._promoteEmbeddedInto(named.underlying);
|
|
809
|
+
} finally {
|
|
810
|
+
generic._syncDepth--;
|
|
811
|
+
named._syncing = false;
|
|
812
|
+
}
|
|
567
813
|
},
|
|
568
814
|
|
|
569
815
|
// A[X] for `type A[P any] = T`: resolve T with each P bound to the
|
|
@@ -591,19 +837,76 @@ export const resolveMethods = {
|
|
|
591
837
|
|
|
592
838
|
// `explicit` seeds the leading type arguments of a partial instantiation
|
|
593
839
|
// (`F[int](x)`); the rest are inferred from the call arguments.
|
|
594
|
-
|
|
840
|
+
// `scope` resolves the type parameters of the calling function, so an
|
|
841
|
+
// argument of type `S ~[]E` unifies through its core type.
|
|
842
|
+
inferTypeArgs(genericType, argTypes, explicit = [], scope = null) {
|
|
595
843
|
const map = new Map();
|
|
844
|
+
const saved = this._inferScope;
|
|
845
|
+
this._inferScope = scope;
|
|
846
|
+
try {
|
|
847
|
+
return this._inferTypeArgs(genericType, argTypes, explicit, map);
|
|
848
|
+
} finally {
|
|
849
|
+
this._inferScope = saved;
|
|
850
|
+
}
|
|
851
|
+
},
|
|
852
|
+
|
|
853
|
+
_inferTypeArgs(genericType, argTypes, explicit, map) {
|
|
596
854
|
for (let i = 0; i < explicit.length; i++)
|
|
597
855
|
map.set(genericType.typeParams[i].name, explicit[i]);
|
|
598
856
|
const params = genericType.underlying.params;
|
|
599
857
|
for (let i = 0; i < params.length && i < argTypes.length; i++)
|
|
600
858
|
this._inferFromPair(params[i], argTypes[i], map);
|
|
859
|
+
this._inferFromCoreTypes(genericType.typeParams, map);
|
|
601
860
|
for (const tp of genericType.typeParams) {
|
|
602
861
|
if (!map.has(tp.name)) return null;
|
|
603
862
|
}
|
|
604
863
|
return genericType.typeParams.map((tp) => defaultType(map.get(tp.name)));
|
|
605
864
|
},
|
|
606
865
|
|
|
866
|
+
// Constraint type inference: a type parameter `S` constrained by `~[]E`
|
|
867
|
+
// whose argument is known unifies `E` with that argument's element type.
|
|
868
|
+
_inferFromCoreTypes(typeParams, map) {
|
|
869
|
+
const scope = new Scope(this.globals);
|
|
870
|
+
for (const tp of typeParams) scope.define(tp.name, tp);
|
|
871
|
+
for (let round = 0; round < typeParams.length; round++) {
|
|
872
|
+
const before = map.size;
|
|
873
|
+
for (const tp of typeParams) {
|
|
874
|
+
const arg = map.get(tp.name);
|
|
875
|
+
if (!tp.constraint) continue;
|
|
876
|
+
if (!arg) {
|
|
877
|
+
this._inferFromPointerCore(tp, typeParams, map, scope);
|
|
878
|
+
continue;
|
|
879
|
+
}
|
|
880
|
+
const terms = this._constraintTerms(tp.constraint, new Set(), scope);
|
|
881
|
+
if (terms?.length !== 1) continue;
|
|
882
|
+
const argUnder =
|
|
883
|
+
this._coreOfTypeParam(arg) ??
|
|
884
|
+
this._deepUnderlying(this.resolveType(arg));
|
|
885
|
+
if (argUnder) this._inferFromPair(terms[0], argUnder, map);
|
|
886
|
+
}
|
|
887
|
+
if (map.size === before) break;
|
|
888
|
+
}
|
|
889
|
+
},
|
|
890
|
+
|
|
891
|
+
// An unbound type parameter whose constraint has the single exact term
|
|
892
|
+
// `*T` (`P interface{ *T; M() }`) is the pointer type, once `T` is known.
|
|
893
|
+
_inferFromPointerCore(tp, typeParams, map, scope) {
|
|
894
|
+
const terms = this._constraintTerms(tp.constraint, new Set(), scope);
|
|
895
|
+
if (terms?.length !== 1 || terms[0]?.kind !== "pointer") return;
|
|
896
|
+
const free = new Set(
|
|
897
|
+
typeParams.filter((p) => !map.has(p.name)).map((p) => p.name),
|
|
898
|
+
);
|
|
899
|
+
const inferred = this.substituteType(terms[0], map);
|
|
900
|
+
const mentionsFree = (t) =>
|
|
901
|
+
t?.kind === "typeParam"
|
|
902
|
+
? free.has(t.name)
|
|
903
|
+
: t?.kind === "pointer"
|
|
904
|
+
? mentionsFree(t.base)
|
|
905
|
+
: false;
|
|
906
|
+
if (!mentionsFree(inferred) && inferred.kind === "pointer")
|
|
907
|
+
map.set(tp.name, inferred);
|
|
908
|
+
},
|
|
909
|
+
|
|
607
910
|
_inferFromFuncPair(paramType, argType, map) {
|
|
608
911
|
for (
|
|
609
912
|
let i = 0;
|
|
@@ -622,6 +925,10 @@ export const resolveMethods = {
|
|
|
622
925
|
_inferFromStructuredPair(paramType, argType, map) {
|
|
623
926
|
if (paramType.kind === "slice" && argType.kind === "slice")
|
|
624
927
|
this._inferFromPair(paramType.elem, argType.elem, map);
|
|
928
|
+
if (paramType.kind === "array" && argType.kind === "array")
|
|
929
|
+
this._inferFromPair(paramType.elem, argType.elem, map);
|
|
930
|
+
if (paramType.kind === "pointer" && argType.kind === "pointer")
|
|
931
|
+
this._inferFromPair(paramType.base, argType.base, map);
|
|
625
932
|
if (paramType.kind === "map" && argType.kind === "map") {
|
|
626
933
|
this._inferFromPair(paramType.key, argType.key, map);
|
|
627
934
|
this._inferFromPair(paramType.value, argType.value, map);
|
|
@@ -638,8 +945,19 @@ export const resolveMethods = {
|
|
|
638
945
|
}
|
|
639
946
|
},
|
|
640
947
|
|
|
948
|
+
// An argument whose type is a type parameter of the calling function
|
|
949
|
+
// (`S ~[]E`) unifies through its core type: that type, or null.
|
|
950
|
+
// `paramType` (optional) is the parameter it is matched against; a bare
|
|
951
|
+
// type parameter takes the argument as it is.
|
|
952
|
+
_coreOfTypeParam(argType, paramType = null) {
|
|
953
|
+
if (argType.kind !== "typeParam" || !this._inferScope) return null;
|
|
954
|
+
if (paramType?.kind === "typeParam") return null;
|
|
955
|
+
return this._coreType(argType, this._inferScope);
|
|
956
|
+
},
|
|
957
|
+
|
|
641
958
|
_inferFromPair(paramType, argType, map) {
|
|
642
959
|
if (!paramType || !argType) return;
|
|
960
|
+
argType = this._coreOfTypeParam(argType, paramType) ?? argType;
|
|
643
961
|
if (paramType.kind === "typeParam") {
|
|
644
962
|
const prev = map.get(paramType.name);
|
|
645
963
|
if (!prev || untypedWins(prev, argType)) map.set(paramType.name, argType);
|
|
@@ -662,15 +980,91 @@ export const resolveMethods = {
|
|
|
662
980
|
},
|
|
663
981
|
|
|
664
982
|
_checkUnionConstraint(typeArg, constraint, node) {
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
const termType = this.resolveTypeNode(term.type, this.globals);
|
|
668
|
-
if (typeStr(typeArg) === typeStr(termType)) return;
|
|
669
|
-
}
|
|
983
|
+
if (typeArg?._tainted || this._unionAccepts(typeArg, constraint, new Set()))
|
|
984
|
+
return;
|
|
670
985
|
this.err(`type ${typeStr(typeArg)} does not satisfy constraint`, node);
|
|
671
986
|
},
|
|
672
987
|
|
|
988
|
+
// Whether `typeArg` is in the type set of a union: some term holds it.
|
|
989
|
+
// `scope` resolves the names its terms mention.
|
|
990
|
+
_unionAccepts(typeArg, union, active, scope = this.globals) {
|
|
991
|
+
return union.terms.some((term) => {
|
|
992
|
+
// `~[]E` may mention a type parameter that is not in `scope`, so an
|
|
993
|
+
// approximation term is resolved (and compared) only when it names
|
|
994
|
+
// a type.
|
|
995
|
+
if (!term.resolved && term.approx && term.type?.kind !== "TypeName")
|
|
996
|
+
return true;
|
|
997
|
+
const t = term.resolved ?? this.resolveTypeNode(term.type, scope);
|
|
998
|
+
return this._termAccepts(typeArg, t, term.approx, active);
|
|
999
|
+
});
|
|
1000
|
+
},
|
|
1001
|
+
|
|
1002
|
+
// One union term: a named interface contributes its own type set
|
|
1003
|
+
// (`S | U` with `type S interface{ ~int }`), `~T` any type with the
|
|
1004
|
+
// underlying type of `T`, and `T` exactly `T`.
|
|
1005
|
+
_termAccepts(typeArg, t, approx, active) {
|
|
1006
|
+
const base =
|
|
1007
|
+
t?.kind === "named"
|
|
1008
|
+
? (this.resolveType(t.underlying) ?? t.underlying)
|
|
1009
|
+
: t;
|
|
1010
|
+
if (base?.kind === "interface")
|
|
1011
|
+
return this._interfaceAccepts(typeArg, base, active);
|
|
1012
|
+
if (!approx) return typeStr(typeArg) === typeStr(t);
|
|
1013
|
+
const want = this._deepUnderlying(t);
|
|
1014
|
+
if (typeArg?.kind === "typeParam" || want?.kind !== "basic") return true;
|
|
1015
|
+
return typeStr(this._deepUnderlying(typeArg)) === typeStr(want);
|
|
1016
|
+
},
|
|
1017
|
+
|
|
1018
|
+
// An interface's type set is the intersection of its own union and the
|
|
1019
|
+
// type sets of the interfaces it embeds; one with neither holds every type.
|
|
1020
|
+
_interfaceAccepts(typeArg, iface, active) {
|
|
1021
|
+
if (active.has(iface)) return true;
|
|
1022
|
+
active.add(iface);
|
|
1023
|
+
const ok =
|
|
1024
|
+
(!iface.unionConstraint ||
|
|
1025
|
+
this._unionAccepts(
|
|
1026
|
+
typeArg,
|
|
1027
|
+
iface.unionConstraint,
|
|
1028
|
+
active,
|
|
1029
|
+
iface._unionScope ?? this.globals,
|
|
1030
|
+
)) &&
|
|
1031
|
+
(iface._typeSetEmbeds ?? []).every((e) =>
|
|
1032
|
+
this._interfaceAccepts(typeArg, e, active),
|
|
1033
|
+
);
|
|
1034
|
+
active.delete(iface);
|
|
1035
|
+
return ok;
|
|
1036
|
+
},
|
|
1037
|
+
|
|
1038
|
+
// Go's comparability: slices, maps and funcs are not comparable, nor are
|
|
1039
|
+
// arrays and structs that contain them.
|
|
1040
|
+
_isComparable(t, seen = new Set()) {
|
|
1041
|
+
const base = t?.kind === "named" ? t.underlying : t;
|
|
1042
|
+
if (!base || seen.has(base)) return true;
|
|
1043
|
+
seen.add(base);
|
|
1044
|
+
switch (base.kind) {
|
|
1045
|
+
case "slice":
|
|
1046
|
+
case "map":
|
|
1047
|
+
case "func":
|
|
1048
|
+
return false;
|
|
1049
|
+
case "array":
|
|
1050
|
+
return this._isComparable(base.elem, seen);
|
|
1051
|
+
case "struct":
|
|
1052
|
+
return [...(base.fields?.values() ?? [])].every((f) =>
|
|
1053
|
+
this._isComparable(f, seen),
|
|
1054
|
+
);
|
|
1055
|
+
default:
|
|
1056
|
+
return true;
|
|
1057
|
+
}
|
|
1058
|
+
},
|
|
1059
|
+
|
|
673
1060
|
_checkInterfaceConstraint(typeArg, base, constraint, node) {
|
|
1061
|
+
if (base._comparable && !this._isComparable(typeArg)) {
|
|
1062
|
+
this.err(
|
|
1063
|
+
`type ${typeStr(typeArg)} does not satisfy constraint ${typeStr(constraint)}`,
|
|
1064
|
+
node,
|
|
1065
|
+
);
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
674
1068
|
if (base.methods.size === 0 && !base.unionConstraint) return;
|
|
675
1069
|
if (base.unionConstraint) {
|
|
676
1070
|
this.checkConstraint(typeArg, base.unionConstraint, node);
|
|
@@ -758,7 +1152,7 @@ export const resolveMethods = {
|
|
|
758
1152
|
return (
|
|
759
1153
|
baseType.kind === "pointer" &&
|
|
760
1154
|
field !== "value" &&
|
|
761
|
-
baseType.base?.kind === "named"
|
|
1155
|
+
(baseType.base?.kind === "named" || baseType.base?.kind === "struct")
|
|
762
1156
|
);
|
|
763
1157
|
},
|
|
764
1158
|
|
|
@@ -788,7 +1182,11 @@ export const resolveMethods = {
|
|
|
788
1182
|
},
|
|
789
1183
|
|
|
790
1184
|
_fieldTypeStruct(base, baseType, field, node) {
|
|
1185
|
+
if (base.fields?.has(field) && isMethodPromotedOverField(base, field))
|
|
1186
|
+
return base.methods.get(field);
|
|
791
1187
|
if (base.fields?.has(field)) {
|
|
1188
|
+
if (isAmbiguousFieldSelector(base, field))
|
|
1189
|
+
return this._errAmbiguousSelector(baseType, field, node);
|
|
792
1190
|
if (
|
|
793
1191
|
field.startsWith("_") &&
|
|
794
1192
|
(baseType?.name?.startsWith("strings.") ||
|
|
@@ -802,6 +1200,8 @@ export const resolveMethods = {
|
|
|
802
1200
|
return base.fields.get(field);
|
|
803
1201
|
}
|
|
804
1202
|
if (base.methods?.has(field)) return base.methods.get(field);
|
|
1203
|
+
if (isAmbiguousPromotion(base, field))
|
|
1204
|
+
return this._errAmbiguousSelector(baseType, field, node);
|
|
805
1205
|
const typeName = baseType?.name || base.name;
|
|
806
1206
|
if (typeName) {
|
|
807
1207
|
const dName = displayTypeName(typeName);
|
|
@@ -826,6 +1226,12 @@ export const resolveMethods = {
|
|
|
826
1226
|
return this.err(`No field '${field}' on ${typeStr(baseType)}`, node);
|
|
827
1227
|
},
|
|
828
1228
|
|
|
1229
|
+
_errAmbiguousSelector(baseType, field, node) {
|
|
1230
|
+
const text = exprText(node?.expr);
|
|
1231
|
+
const recv = text === "expression" ? typeStr(baseType) : text;
|
|
1232
|
+
return this.err(`ambiguous selector ${recv}.${field}`, node);
|
|
1233
|
+
},
|
|
1234
|
+
|
|
829
1235
|
_fieldTypeInterface(base, baseType, field, node) {
|
|
830
1236
|
if (base.methods?.has(field)) return base.methods.get(field);
|
|
831
1237
|
if (base.fields?.has(field)) return base.fields.get(field);
|
|
@@ -891,16 +1297,35 @@ export const resolveMethods = {
|
|
|
891
1297
|
return ANY;
|
|
892
1298
|
},
|
|
893
1299
|
|
|
894
|
-
|
|
1300
|
+
// An error for `field` when the namespace cannot offer it to this package:
|
|
1301
|
+
// an unexported name, or a member of a `js` file in a wasm or both package.
|
|
1302
|
+
_namespaceMemberError(base, field, node) {
|
|
895
1303
|
if (base._gofront && field.length > 0 && field[0] >= "a" && field[0] <= "z")
|
|
896
1304
|
return this.err(
|
|
897
1305
|
`cannot refer to unexported name ${base.name}.${field}`,
|
|
898
1306
|
node,
|
|
899
1307
|
);
|
|
1308
|
+
const wasm = this.target === "wasm" || this.target === "both";
|
|
1309
|
+
return wasm && base._jsOnly?.has(field)
|
|
1310
|
+
? this._errJsOnly(`${base.name}.${field}`, node)
|
|
1311
|
+
: null;
|
|
1312
|
+
},
|
|
1313
|
+
|
|
1314
|
+
_fieldTypeNamespace(base, field, node) {
|
|
1315
|
+
const bad = this._namespaceMemberError(base, field, node);
|
|
1316
|
+
if (bad) return bad;
|
|
900
1317
|
// Go-source stdlib member: backends emit the mangled top-level name.
|
|
901
1318
|
const mangled = base._mangle?.get(field);
|
|
902
1319
|
if (mangled) {
|
|
903
1320
|
node._stdName = mangled;
|
|
1321
|
+
// Lazy stdlib bodies: referencing a member checks what it reaches.
|
|
1322
|
+
const failure = base._reach?.(mangled);
|
|
1323
|
+
if (failure)
|
|
1324
|
+
this.err(
|
|
1325
|
+
`internal stdlib error in ${base._path}; please report`,
|
|
1326
|
+
node,
|
|
1327
|
+
failure,
|
|
1328
|
+
);
|
|
904
1329
|
return base.members[field];
|
|
905
1330
|
}
|
|
906
1331
|
if (field in base.members) {
|