gofront 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/package.json +10 -38
- package/src/backend/js/expressions.js +2060 -389
- package/src/backend/js/index.js +586 -174
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +936 -1644
- package/src/backend/js/statements.js +1349 -132
- package/src/backend/js/stdlib/index.js +51 -94
- package/src/backend/js/templ.js +67 -118
- package/src/backend/js/type-info.js +436 -0
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +80 -0
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +753 -872
- package/src/backend/wasm/emit-builtins.js +914 -237
- package/src/backend/wasm/emit-exprs.js +1456 -195
- package/src/backend/wasm/emit-maps.js +78 -380
- package/src/backend/wasm/emit-stmts.js +1659 -214
- package/src/backend/wasm/emit.js +614 -88
- package/src/backend/wasm/encode.js +43 -13
- package/src/backend/wasm/globals.js +491 -0
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +406 -3195
- package/src/backend/wasm/method-expr.js +392 -0
- package/src/backend/wasm/module-emitter.js +1615 -0
- package/src/backend/wasm/monomorph.js +164 -9
- package/src/backend/wasm/natives.js +596 -0
- package/src/backend/wasm/prescan.js +249 -0
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -37
- package/src/backend/wasm/stdlib/simd.js +293 -28
- package/src/backend/wasm/types.js +260 -27
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +47 -20
- package/src/{cli-core.js → cli/cli-core.js} +241 -11
- package/src/{test-runner.js → cli/test-runner.js} +188 -122
- package/src/{vendor.js → cli/vendor.js} +3 -4
- package/src/compiler.js +202 -386
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +203 -62
- package/src/{templ-lexer.js → lexer/templ-lexer.js} +61 -18
- package/src/{tokens.js → lexer/tokens.js} +2 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +737 -0
- package/src/link/type-names.js +148 -0
- package/src/link/wasm.js +318 -0
- package/src/lower/bounds.js +547 -0
- package/src/lower/boxing.js +1 -1
- package/src/lower/captures.js +267 -64
- package/src/lower/embedding.js +32 -3
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +905 -0
- package/src/lower/functions.js +2 -0
- package/src/lower/gen-types.js +274 -0
- package/src/lower/index.js +33 -19
- package/src/lower/json-codecs.js +1379 -0
- package/src/lower/ownership.js +47 -8
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +150 -0
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +83 -15
- package/src/parser/expressions.js +110 -18
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +38 -22
- package/src/{templ-parser.js → parser/templ.js} +179 -17
- package/src/parser/types.js +70 -9
- package/src/resolver.js +21 -1
- package/src/typechecker/assignability.js +291 -19
- package/src/typechecker/const-fold.js +399 -0
- package/src/typechecker/constants.js +282 -0
- package/src/typechecker/expressions.js +1220 -183
- package/src/typechecker/index.js +659 -92
- package/src/typechecker/init-order.js +426 -0
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +613 -62
- package/src/typechecker/statements.js +212 -20
- package/src/typechecker/stdlib/excluded.js +902 -0
- package/src/typechecker/stdlib/index.js +38 -0
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +11 -2
- package/src/typechecker/types.js +268 -514
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +871 -0
- package/stdlib/internal/jsonrt/enc.go +516 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +658 -0
- package/stdlib/io/io.go +502 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +272 -0
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +500 -0
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +526 -0
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +250 -0
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -46
- package/src/backend/js/stdlib/builder.js +0 -63
- package/src/backend/js/stdlib/bytes.js +0 -122
- package/src/backend/js/stdlib/errors.js +0 -44
- package/src/backend/js/stdlib/fmt.js +0 -178
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -972
- package/src/backend/js/stdlib/maps.js +0 -45
- package/src/backend/js/stdlib/math.js +0 -69
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -24
- package/src/backend/js/stdlib/slices.js +0 -77
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -74
- package/src/backend/js/stdlib/strings.js +0 -222
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -34
- package/src/backend/js/stdlib/time.js +0 -128
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- package/src/backend/wasm/stdlib/bytes.js +0 -5039
- package/src/backend/wasm/stdlib/core.js +0 -8903
- package/src/backend/wasm/stdlib/io.js +0 -2161
- package/src/backend/wasm/stdlib/packages.js +0 -2400
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -1200
- package/src/typechecker/stdlib/core.js +0 -801
- package/src/typechecker/stdlib/extended.js +0 -914
- package/src/typechecker/stdlib.js +0 -22
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
// Installed as a mixin on TypeChecker.prototype.
|
|
3
3
|
|
|
4
4
|
import { parseJsonTag } from "../parser/struct-tag.js";
|
|
5
|
+
import { foldConstExpr } from "./const-fold.js";
|
|
6
|
+
import { EXCLUDED_STDLIB_MEMBERS } from "./stdlib/excluded.js";
|
|
5
7
|
import {
|
|
6
8
|
ANY,
|
|
7
9
|
BASIC_TYPES,
|
|
8
10
|
COMPARABLE,
|
|
11
|
+
DECLARED_ANY,
|
|
9
12
|
defaultType,
|
|
13
|
+
displayTypeName,
|
|
10
14
|
isAny,
|
|
11
15
|
isTypedArraySlice,
|
|
12
16
|
Scope,
|
|
@@ -20,6 +24,97 @@ import {
|
|
|
20
24
|
/** @typedef {import('./index.js').TypeChecker} TypeChecker */
|
|
21
25
|
|
|
22
26
|
// Dispatch table for substituteType — keyed by type.kind.
|
|
27
|
+
// Nesting depth of in-progress instances of one generic type before the
|
|
28
|
+
// instantiation is reported as a cycle.
|
|
29
|
+
const MAX_INSTANTIATION_DEPTH = 32;
|
|
30
|
+
|
|
31
|
+
// Two imported packages may both declare `Pt`, so the cache key of an
|
|
32
|
+
// instance tells declarations apart by identity instead of by display name.
|
|
33
|
+
const declIds = new WeakMap();
|
|
34
|
+
let nextDeclId = 0;
|
|
35
|
+
function declId(t) {
|
|
36
|
+
if (!declIds.has(t)) declIds.set(t, ++nextDeclId);
|
|
37
|
+
return declIds.get(t);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Go infers T from the "largest" untyped constant kind (int < rune < float <
|
|
41
|
+
// complex), and a typed argument beats any untyped one.
|
|
42
|
+
const UNTYPED_RANK = { int: 0, int32: 1, float64: 2, complex128: 3 };
|
|
43
|
+
function untypedWins(prev, next) {
|
|
44
|
+
if (prev.kind !== "untyped") return false;
|
|
45
|
+
if (next.kind !== "untyped") return true;
|
|
46
|
+
return (UNTYPED_RANK[next.base] ?? 0) > (UNTYPED_RANK[prev.base] ?? 0);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Node count of a type, so a request that keeps growing can be told from one
|
|
50
|
+
// that merely repeats.
|
|
51
|
+
function typeSize(t) {
|
|
52
|
+
switch (t?.kind) {
|
|
53
|
+
case "named":
|
|
54
|
+
return 1 + (t.typeArgs ?? []).reduce((n, a) => n + typeSize(a), 0);
|
|
55
|
+
case "pointer":
|
|
56
|
+
return 1 + typeSize(t.base);
|
|
57
|
+
case "slice":
|
|
58
|
+
case "array":
|
|
59
|
+
return 1 + typeSize(t.elem);
|
|
60
|
+
case "map":
|
|
61
|
+
return 1 + typeSize(t.key) + typeSize(t.value);
|
|
62
|
+
case "func":
|
|
63
|
+
return (
|
|
64
|
+
1 +
|
|
65
|
+
[...(t.params ?? []), ...(t.returns ?? [])].reduce(
|
|
66
|
+
(n, a) => n + typeSize(a),
|
|
67
|
+
0,
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
default:
|
|
71
|
+
return 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Whether `t` mentions one of the type parameters `params` below its root.
|
|
76
|
+
function nestsTypeParam(t, params) {
|
|
77
|
+
const mentions = (u) => {
|
|
78
|
+
switch (u?.kind) {
|
|
79
|
+
case "typeParam":
|
|
80
|
+
return params.includes(u);
|
|
81
|
+
case "pointer":
|
|
82
|
+
return mentions(u.base);
|
|
83
|
+
case "slice":
|
|
84
|
+
case "array":
|
|
85
|
+
return mentions(u.elem);
|
|
86
|
+
case "map":
|
|
87
|
+
return mentions(u.key) || mentions(u.value);
|
|
88
|
+
case "func":
|
|
89
|
+
return [...(u.params ?? []), ...(u.returns ?? [])].some(mentions);
|
|
90
|
+
case "named":
|
|
91
|
+
return (u.typeArgs ?? []).some(mentions);
|
|
92
|
+
default:
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
return t?.kind !== "typeParam" && mentions(t);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function instanceKey(t) {
|
|
100
|
+
switch (t?.kind) {
|
|
101
|
+
case "named":
|
|
102
|
+
return t.typeArgs?.length
|
|
103
|
+
? `${typeStr(t)}`
|
|
104
|
+
: `${typeStr(t)}#${declId(t)}`;
|
|
105
|
+
case "pointer":
|
|
106
|
+
return `*${instanceKey(t.base)}`;
|
|
107
|
+
case "slice":
|
|
108
|
+
return `[]${instanceKey(t.elem)}`;
|
|
109
|
+
case "array":
|
|
110
|
+
return `[${t.size ?? "..."}]${instanceKey(t.elem)}`;
|
|
111
|
+
case "map":
|
|
112
|
+
return `map[${instanceKey(t.key)}]${instanceKey(t.value)}`;
|
|
113
|
+
default:
|
|
114
|
+
return typeStr(t);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
23
118
|
const SUBSTITUTE_DISPATCH = {
|
|
24
119
|
basic: (_s, type) => type,
|
|
25
120
|
untyped: (_s, type) => type,
|
|
@@ -63,18 +158,53 @@ const SUBSTITUTE_DISPATCH = {
|
|
|
63
158
|
fields,
|
|
64
159
|
methods,
|
|
65
160
|
_embeds: type._embeds,
|
|
161
|
+
// The JSON codecs read the field types from here.
|
|
162
|
+
_structFields: type._structFields?.map((f) => ({
|
|
163
|
+
...f,
|
|
164
|
+
type: s.substituteType(f.type, map),
|
|
165
|
+
})),
|
|
66
166
|
};
|
|
67
167
|
},
|
|
68
168
|
interface: (s, type, map) => {
|
|
69
169
|
const methods = new Map();
|
|
70
170
|
for (const [k, v] of type.methods) methods.set(k, s.substituteType(v, map));
|
|
71
|
-
|
|
171
|
+
const out = { kind: "interface", name: type.name, methods };
|
|
172
|
+
// The type set follows the substitution (`interface{ ~[]E }` with E=int).
|
|
173
|
+
if (type.unionConstraint)
|
|
174
|
+
out.unionConstraint = {
|
|
175
|
+
...type.unionConstraint,
|
|
176
|
+
terms: type.unionConstraint.terms.map((term) => ({
|
|
177
|
+
...term,
|
|
178
|
+
resolved: s.substituteType(
|
|
179
|
+
term.resolved ?? s.resolveTypeNode(term.type, type._unionScope),
|
|
180
|
+
map,
|
|
181
|
+
),
|
|
182
|
+
})),
|
|
183
|
+
};
|
|
184
|
+
if (type._typeSetEmbeds)
|
|
185
|
+
out._typeSetEmbeds = type._typeSetEmbeds.map((e) =>
|
|
186
|
+
s.substituteType(e, map),
|
|
187
|
+
);
|
|
188
|
+
return out;
|
|
189
|
+
},
|
|
190
|
+
named: (s, type, map) => {
|
|
191
|
+
// An instance of a generic type: re-instantiate with the substituted
|
|
192
|
+
// arguments (through the instance cache) rather than copying its
|
|
193
|
+
// possibly cyclic underlying type.
|
|
194
|
+
if (type._genericOf)
|
|
195
|
+
return s.instantiateGenericWithArgs(
|
|
196
|
+
type._genericOf,
|
|
197
|
+
type.typeArgs.map((t) => s.substituteType(t, map)),
|
|
198
|
+
);
|
|
199
|
+
return {
|
|
200
|
+
kind: "named",
|
|
201
|
+
name: type.name,
|
|
202
|
+
...(type.typeArgs && {
|
|
203
|
+
typeArgs: type.typeArgs.map((t) => s.substituteType(t, map)),
|
|
204
|
+
}),
|
|
205
|
+
underlying: s.substituteType(type.underlying, map),
|
|
206
|
+
};
|
|
72
207
|
},
|
|
73
|
-
named: (s, type, map) => ({
|
|
74
|
-
kind: "named",
|
|
75
|
-
name: type.name,
|
|
76
|
-
underlying: s.substituteType(type.underlying, map),
|
|
77
|
-
}),
|
|
78
208
|
};
|
|
79
209
|
|
|
80
210
|
/** @type {ThisType<TypeChecker>} */
|
|
@@ -83,45 +213,33 @@ export const resolveMethods = {
|
|
|
83
213
|
if (!node) return ANY;
|
|
84
214
|
switch (node.kind) {
|
|
85
215
|
case "TypeName": {
|
|
86
|
-
if (BASIC_TYPES[node.name])
|
|
216
|
+
if (BASIC_TYPES[node.name])
|
|
217
|
+
return node.name === "any" ? DECLARED_ANY : BASIC_TYPES[node.name];
|
|
87
218
|
if (node.name === "comparable") return COMPARABLE;
|
|
88
219
|
if (scope) {
|
|
89
220
|
const fromScope = scope.lookup(node.name);
|
|
90
221
|
if (fromScope?.kind === "typeParam") return fromScope;
|
|
222
|
+
if (fromScope?.kind === "aliasArg") return fromScope.type;
|
|
91
223
|
}
|
|
92
224
|
const named = this.types.get(node.name);
|
|
93
225
|
if (named) {
|
|
94
|
-
|
|
95
|
-
const dot = node.name.indexOf(".");
|
|
96
|
-
if (dot > 0) this.globals.lookup(node.name.slice(0, dot));
|
|
226
|
+
this._useQualifiedTypeName(node, named);
|
|
97
227
|
return named;
|
|
98
228
|
}
|
|
229
|
+
const jsOnly = this._jsOnlyMember(node.name);
|
|
230
|
+
if (jsOnly) return this._errJsOnly(jsOnly, node);
|
|
231
|
+
if (EXCLUDED_STDLIB_MEMBERS.has(node.name)) {
|
|
232
|
+
return this.err(
|
|
233
|
+
`${node.name} is not available: ${EXCLUDED_STDLIB_MEMBERS.get(node.name)}`,
|
|
234
|
+
node,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
99
237
|
return this.err(`Unknown type '${node.name}'`, node);
|
|
100
238
|
}
|
|
101
239
|
case "GenericTypeName":
|
|
102
240
|
case "InstantiationExpr":
|
|
103
|
-
case "IndexExpr":
|
|
104
|
-
|
|
105
|
-
node.name ?? (node.expr?.kind === "Ident" ? node.expr.name : null);
|
|
106
|
-
const typeArgs = node.typeArgs ?? (node.index ? [node.index] : []);
|
|
107
|
-
const base = name
|
|
108
|
-
? this.types.get(name)
|
|
109
|
-
: node.expr
|
|
110
|
-
? this.resolveTypeNode(node.expr, scope)
|
|
111
|
-
: null;
|
|
112
|
-
if (!base) return this.err(`Unknown type '${name ?? "?"}'`, node);
|
|
113
|
-
if (base.kind === "named" && base._generic) {
|
|
114
|
-
const res = this.instantiateGenericType(
|
|
115
|
-
base._generic,
|
|
116
|
-
typeArgs,
|
|
117
|
-
scope,
|
|
118
|
-
);
|
|
119
|
-
node._typeArgs = res.typeArgs;
|
|
120
|
-
node._genericType = base;
|
|
121
|
-
return res;
|
|
122
|
-
}
|
|
123
|
-
return this.err(`Type '${name ?? "?"}' is not generic`, node);
|
|
124
|
-
}
|
|
241
|
+
case "IndexExpr":
|
|
242
|
+
return this._resolveGenericTypeName(node, scope);
|
|
125
243
|
case "TypeParam": {
|
|
126
244
|
const constraint = node.constraint
|
|
127
245
|
? this.resolveTypeNode(node.constraint, scope)
|
|
@@ -147,6 +265,13 @@ export const resolveMethods = {
|
|
|
147
265
|
return this._resolveStructType(node, scope);
|
|
148
266
|
case "InterfaceType":
|
|
149
267
|
return this._resolveInterfaceType(node, scope);
|
|
268
|
+
case "UnaryExpr":
|
|
269
|
+
// `new(*T)`: the parser reads the pointer type as a dereference.
|
|
270
|
+
if (node.op !== "*") return ANY;
|
|
271
|
+
return {
|
|
272
|
+
kind: "pointer",
|
|
273
|
+
base: this.resolveTypeNode(node.operand, scope),
|
|
274
|
+
};
|
|
150
275
|
case "Ident":
|
|
151
276
|
return this.resolveTypeNode(
|
|
152
277
|
{ kind: "TypeName", name: node.name },
|
|
@@ -157,11 +282,14 @@ export const resolveMethods = {
|
|
|
157
282
|
// SelectorExpr; resolve it like a qualified TypeName.
|
|
158
283
|
if (node.expr?.kind === "Ident" && typeof node.field === "string") {
|
|
159
284
|
const qualified = `${node.expr.name}.${node.field}`;
|
|
160
|
-
if (this.types.has(qualified))
|
|
161
|
-
|
|
285
|
+
if (this.types.has(qualified)) {
|
|
286
|
+
const resolved = this.resolveTypeNode(
|
|
162
287
|
{ kind: "TypeName", name: qualified },
|
|
163
288
|
scope,
|
|
164
289
|
);
|
|
290
|
+
this._useQualifiedTypeName(node, resolved);
|
|
291
|
+
return resolved;
|
|
292
|
+
}
|
|
165
293
|
}
|
|
166
294
|
return ANY;
|
|
167
295
|
case "ArrayType":
|
|
@@ -178,24 +306,134 @@ export const resolveMethods = {
|
|
|
178
306
|
}
|
|
179
307
|
},
|
|
180
308
|
|
|
309
|
+
// `pkg.Name` when `pkg` is a Go-source package whose `js` files declare
|
|
310
|
+
// Name and this package is wasm or both (those files are not loaded).
|
|
311
|
+
_jsOnlyMember(qualified) {
|
|
312
|
+
if (this.target !== "wasm" && this.target !== "both") return null;
|
|
313
|
+
const dot = qualified.indexOf(".");
|
|
314
|
+
if (dot < 0) return null;
|
|
315
|
+
const ns = this.globals.lookup(qualified.slice(0, dot));
|
|
316
|
+
return ns?._jsOnly?.has(qualified.slice(dot + 1)) ? qualified : null;
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
_errJsOnly(qualified, node) {
|
|
320
|
+
this.recordBlocker("unsupported stdlib member", qualified);
|
|
321
|
+
return this.err(
|
|
322
|
+
`'${qualified}' is only available in js packages`,
|
|
323
|
+
node,
|
|
324
|
+
"move this call to a JS package",
|
|
325
|
+
);
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
// `pkg.T` in a type position counts as using the import; a Go-source
|
|
329
|
+
// stdlib type is emitted under its mangled name.
|
|
330
|
+
_useQualifiedTypeName(node, named) {
|
|
331
|
+
const name =
|
|
332
|
+
node.name ?? (node.expr ? `${node.expr.name}.${node.field}` : "");
|
|
333
|
+
const dot = name.indexOf(".");
|
|
334
|
+
if (dot <= 0) return;
|
|
335
|
+
this.globals.lookup(name.slice(0, dot));
|
|
336
|
+
if (named.name?.includes("$$")) node._stdName = named.name;
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
// `[N]T` with a named constant (or constant expression) length folds to a number.
|
|
340
|
+
_foldArrayLen(size, scope) {
|
|
341
|
+
const resolve = (name, depth = 0) => {
|
|
342
|
+
const e = depth < 16 ? scope.lookupConstExpr(name) : null;
|
|
343
|
+
return e ? foldConstExpr(e, (n) => resolve(n, depth + 1)) : undefined;
|
|
344
|
+
};
|
|
345
|
+
try {
|
|
346
|
+
const lit = foldConstExpr(size, resolve);
|
|
347
|
+
const n = lit ? Number(lit.value) : Number.NaN;
|
|
348
|
+
if (Number.isInteger(n) && n >= 0) return n;
|
|
349
|
+
} catch {
|
|
350
|
+
// fall through to the checker's evaluator
|
|
351
|
+
}
|
|
352
|
+
// `[math.MaxInt8]T`: a package constant the AST folder cannot see.
|
|
353
|
+
const q = this._constIntValue(size, scope);
|
|
354
|
+
if (q === null || !Number.isInteger(q) || q < 0) return size;
|
|
355
|
+
this._useQualifiedConsts(size);
|
|
356
|
+
return q;
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
// `pkg.K` inside a folded array length counts as using the import.
|
|
360
|
+
_useQualifiedConsts(node) {
|
|
361
|
+
if (!node || typeof node !== "object") return;
|
|
362
|
+
if (node.kind === "SelectorExpr" && node.expr?.kind === "Ident")
|
|
363
|
+
this.globals.lookup(node.expr.name);
|
|
364
|
+
for (const key of ["expr", "left", "right", "operand"])
|
|
365
|
+
this._useQualifiedConsts(node[key]);
|
|
366
|
+
for (const arg of node.args ?? []) this._useQualifiedConsts(arg);
|
|
367
|
+
},
|
|
368
|
+
|
|
181
369
|
_resolveArrayTypeNode(node, scope) {
|
|
370
|
+
// Fold in place so backends that read the type node see a literal length.
|
|
371
|
+
if (!node.inferLen && node.size?.value === undefined) {
|
|
372
|
+
const n = this._foldArrayLen(node.size, scope);
|
|
373
|
+
if (typeof n === "number")
|
|
374
|
+
node.size = {
|
|
375
|
+
kind: "BasicLit",
|
|
376
|
+
litKind: "INT",
|
|
377
|
+
value: String(n),
|
|
378
|
+
_line: node.size._line,
|
|
379
|
+
_col: node.size._col,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
182
382
|
return {
|
|
183
383
|
kind: "array",
|
|
184
384
|
size: node.inferLen
|
|
185
385
|
? null
|
|
186
386
|
: node.size?.value !== undefined
|
|
187
387
|
? Number(node.size.value)
|
|
188
|
-
: node.size,
|
|
388
|
+
: this._foldArrayLen(node.size, scope),
|
|
189
389
|
elem: this.resolveTypeNode(node.elem, scope),
|
|
190
390
|
};
|
|
191
391
|
},
|
|
192
392
|
|
|
393
|
+
// `G[X]`: instantiate a generic named type, or expand a generic alias.
|
|
394
|
+
_resolveGenericTypeName(node, scope) {
|
|
395
|
+
const sel = node.expr?.kind === "SelectorExpr" ? node.expr : null;
|
|
396
|
+
const qualified =
|
|
397
|
+
sel?.expr?.kind === "Ident" ? `${sel.expr.name}.${sel.field}` : null;
|
|
398
|
+
const name =
|
|
399
|
+
node.name ??
|
|
400
|
+
(node.expr?.kind === "Ident" ? node.expr.name : null) ??
|
|
401
|
+
qualified;
|
|
402
|
+
const typeArgs = node.typeArgs ?? (node.index ? [node.index] : []);
|
|
403
|
+
const base = name
|
|
404
|
+
? this.types.get(name)
|
|
405
|
+
: node.expr
|
|
406
|
+
? this.resolveTypeNode(node.expr, scope)
|
|
407
|
+
: null;
|
|
408
|
+
if (!base) return this.err(`Unknown type '${name ?? "?"}'`, node);
|
|
409
|
+
if (qualified) {
|
|
410
|
+
this.globals.lookup(sel.expr.name);
|
|
411
|
+
// `iter.Seq[int](f)`: the selector names the mangled stdlib type.
|
|
412
|
+
this._useQualifiedTypeName(sel, base);
|
|
413
|
+
} else if (name)
|
|
414
|
+
this._useQualifiedTypeName(node.name ? node : node.expr, base);
|
|
415
|
+
if (base.kind === "named" && base._generic?.isAlias) {
|
|
416
|
+
const aliasArgs = typeArgs.map((n) => this.resolveTypeNode(n, scope));
|
|
417
|
+
node._typeArgs = aliasArgs;
|
|
418
|
+
return this._instantiateGenericAlias(base._generic, aliasArgs);
|
|
419
|
+
}
|
|
420
|
+
if (base.kind === "named" && base._generic) {
|
|
421
|
+
const res = this.instantiateGenericType(base._generic, typeArgs, scope);
|
|
422
|
+
node._typeArgs = res.typeArgs;
|
|
423
|
+
node._genericType = base;
|
|
424
|
+
return res;
|
|
425
|
+
}
|
|
426
|
+
return this.err(`Type '${name ?? "?"}' is not generic`, node);
|
|
427
|
+
},
|
|
428
|
+
|
|
193
429
|
_resolveFuncTypeNode(node, scope) {
|
|
194
430
|
const params = node.params.map((p) => this.resolveTypeNode(p.type, scope));
|
|
195
431
|
const returns = node.returnType
|
|
196
432
|
? [this.resolveTypeNode(node.returnType, scope)]
|
|
197
433
|
: [VOID];
|
|
198
|
-
|
|
434
|
+
const fn = { kind: "func", params, returns };
|
|
435
|
+
if (node.params.at(-1)?.variadic) fn.variadic = true;
|
|
436
|
+
return fn;
|
|
199
437
|
},
|
|
200
438
|
|
|
201
439
|
resolveTypeNodeName(node, scope) {
|
|
@@ -223,6 +461,7 @@ export const resolveMethods = {
|
|
|
223
461
|
if (f.embedded) {
|
|
224
462
|
const inner = ft.kind === "pointer" ? ft.base : ft;
|
|
225
463
|
const embedName = (
|
|
464
|
+
f.embeddedName ??
|
|
226
465
|
f.astType?.name ??
|
|
227
466
|
f.type?.name ??
|
|
228
467
|
f.type?.base?.name ??
|
|
@@ -262,6 +501,9 @@ export const resolveMethods = {
|
|
|
262
501
|
if (isVariadic) mType.variadic = true;
|
|
263
502
|
methods.set(m.name, mType);
|
|
264
503
|
}
|
|
504
|
+
// Embedded interfaces that carry a type set (`interface{ Integer }`):
|
|
505
|
+
// kept so type-parameter operator checks can follow them.
|
|
506
|
+
const typeSetEmbeds = [];
|
|
265
507
|
if (node.embeds) {
|
|
266
508
|
for (const embed of node.embeds) {
|
|
267
509
|
const resolved = this.resolveTypeNode(embed, scope);
|
|
@@ -271,6 +513,8 @@ export const resolveMethods = {
|
|
|
271
513
|
for (const [mName, mType] of base.methods) {
|
|
272
514
|
if (!methods.has(mName)) methods.set(mName, mType);
|
|
273
515
|
}
|
|
516
|
+
if (base.unionConstraint || base._typeSetEmbeds)
|
|
517
|
+
typeSetEmbeds.push(base);
|
|
274
518
|
} else if (!isAny(resolved)) {
|
|
275
519
|
this.err(
|
|
276
520
|
`cannot embed non-interface type ${typeStr(resolved)}`,
|
|
@@ -280,7 +524,11 @@ export const resolveMethods = {
|
|
|
280
524
|
}
|
|
281
525
|
}
|
|
282
526
|
const result = { kind: "interface", methods };
|
|
283
|
-
if (node.unionConstraint)
|
|
527
|
+
if (node.unionConstraint) {
|
|
528
|
+
result.unionConstraint = node.unionConstraint;
|
|
529
|
+
result._unionScope = scope;
|
|
530
|
+
}
|
|
531
|
+
if (typeSetEmbeds.length > 0) result._typeSetEmbeds = typeSetEmbeds;
|
|
284
532
|
return result;
|
|
285
533
|
},
|
|
286
534
|
|
|
@@ -301,45 +549,184 @@ export const resolveMethods = {
|
|
|
301
549
|
},
|
|
302
550
|
|
|
303
551
|
instantiateGenericType(generic, typeArgNodes, scope) {
|
|
304
|
-
const map = new Map();
|
|
305
552
|
const typeArgs = typeArgNodes.map((n) => this.resolveTypeNode(n, scope));
|
|
553
|
+
return this.instantiateGenericWithArgs(generic, typeArgs);
|
|
554
|
+
},
|
|
555
|
+
|
|
556
|
+
// One instance per type-argument list. The instance is cached before its
|
|
557
|
+
// fields are resolved, so a field that names the instance again (`Next
|
|
558
|
+
// *List[T]`, `Children []Tree[T]`, or a cycle through another generic type)
|
|
559
|
+
// reaches the same object instead of recursing forever.
|
|
560
|
+
instantiateGenericWithArgs(generic, typeArgs) {
|
|
561
|
+
const key = typeArgs.map(instanceKey).join(",");
|
|
562
|
+
generic._instances ??= new Map();
|
|
563
|
+
const cached = generic._instances.get(key);
|
|
564
|
+
if (cached) {
|
|
565
|
+
this._syncInstanceMethods(generic, cached);
|
|
566
|
+
return cached;
|
|
567
|
+
}
|
|
568
|
+
// `type T[X any] struct{ n *T[[]X] }` asks for T[[]X], then T[[][]X], and
|
|
569
|
+
// so on: an instance still being built that keeps asking for a larger
|
|
570
|
+
// one never settles.
|
|
571
|
+
generic._building ??= [];
|
|
572
|
+
generic._buildSizes ??= [];
|
|
573
|
+
const building = generic._building;
|
|
574
|
+
const size = typeArgs.reduce((n, a) => n + typeSize(a), 0);
|
|
575
|
+
// A request for a larger instance while one is being built never
|
|
576
|
+
// settles (`T[X]` containing `T[[]X]`), and doubling forms such as
|
|
577
|
+
// `T[Pair[X, X]]` blow up long before a depth limit is reached.
|
|
578
|
+
const top = generic._buildSizes[generic._buildSizes.length - 1];
|
|
579
|
+
// Inside a method of `G[T]`, `G[[]T]` makes every instance ask for a
|
|
580
|
+
// larger one, even if the method is never called.
|
|
581
|
+
const growsReceiver =
|
|
582
|
+
this._recvGeneric === generic &&
|
|
583
|
+
typeArgs.some((a) => nestsTypeParam(a, generic.typeParams));
|
|
584
|
+
if (
|
|
585
|
+
growsReceiver ||
|
|
586
|
+
building.length >= MAX_INSTANTIATION_DEPTH ||
|
|
587
|
+
(generic._syncDepth ?? 0) >= MAX_INSTANTIATION_DEPTH ||
|
|
588
|
+
(top !== undefined && size > top)
|
|
589
|
+
) {
|
|
590
|
+
if (!generic._cycleReported) {
|
|
591
|
+
generic._cycleReported = true;
|
|
592
|
+
this.err(
|
|
593
|
+
`instantiation cycle: ${generic.declNode.name} instantiates itself with ever larger type arguments`,
|
|
594
|
+
generic.declNode,
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
return ANY;
|
|
598
|
+
}
|
|
599
|
+
const map = new Map();
|
|
306
600
|
for (let i = 0; i < generic.typeParams.length; i++)
|
|
307
601
|
map.set(generic.typeParams[i].name, typeArgs[i] ?? ANY);
|
|
602
|
+
const named = {
|
|
603
|
+
kind: "named",
|
|
604
|
+
name: generic.declNode.name,
|
|
605
|
+
typeArgs,
|
|
606
|
+
underlying: null,
|
|
607
|
+
_genericOf: generic,
|
|
608
|
+
};
|
|
609
|
+
generic._instances.set(key, named);
|
|
308
610
|
// Resolve the declaration with its type params in scope (as typeParams,
|
|
309
611
|
// never as value symbols), then substitute the concrete arguments.
|
|
310
612
|
const typeScope = new Scope(this.globals);
|
|
311
613
|
for (const tp of generic.typeParams) typeScope.define(tp.name, tp);
|
|
312
|
-
|
|
313
|
-
|
|
614
|
+
building.push(key);
|
|
615
|
+
generic._buildSizes.push(size);
|
|
616
|
+
let instantiated;
|
|
617
|
+
try {
|
|
618
|
+
const underlying = this.resolveTypeNode(generic.declNode.type, typeScope);
|
|
619
|
+
instantiated = this.substituteType(underlying, map);
|
|
620
|
+
} finally {
|
|
621
|
+
building.pop();
|
|
622
|
+
generic._buildSizes.pop();
|
|
623
|
+
}
|
|
314
624
|
if (instantiated.kind === "struct") {
|
|
315
625
|
instantiated.name = generic.declNode.name;
|
|
316
626
|
instantiated.typeArgs = typeArgs;
|
|
317
627
|
if (!instantiated.methods) instantiated.methods = new Map();
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
628
|
+
}
|
|
629
|
+
named.underlying = instantiated;
|
|
630
|
+
named.methods = instantiated.methods;
|
|
631
|
+
named._instMap = map;
|
|
632
|
+
this._syncInstanceMethods(generic, named);
|
|
633
|
+
return named;
|
|
634
|
+
},
|
|
635
|
+
|
|
636
|
+
// Methods are collected after types, so an instance created earlier picks
|
|
637
|
+
// up the methods declared since.
|
|
638
|
+
_syncInstanceMethods(generic, named) {
|
|
639
|
+
const methods = named.methods;
|
|
640
|
+
if (!methods || !named._instMap || named._syncing) return;
|
|
641
|
+
// A method signature that names the instance reaches this again.
|
|
642
|
+
// `func (s Stack[T]) Wrap() Stack[[]T]` makes every instance's methods
|
|
643
|
+
// ask for a larger instance: count the nesting to diagnose it.
|
|
644
|
+
named._syncing = true;
|
|
645
|
+
generic._syncDepth = (generic._syncDepth ?? 0) + 1;
|
|
646
|
+
try {
|
|
647
|
+
for (const [mName, mType] of generic.methods ?? [])
|
|
648
|
+
if (!methods.has(mName))
|
|
649
|
+
methods.set(mName, this.substituteType(mType, named._instMap));
|
|
650
|
+
// Methods promoted from embedded structs (`Outer[T]` embedding `Base`).
|
|
651
|
+
this._promoteEmbeddedInto(named.underlying);
|
|
652
|
+
} finally {
|
|
653
|
+
generic._syncDepth--;
|
|
654
|
+
named._syncing = false;
|
|
655
|
+
}
|
|
656
|
+
},
|
|
657
|
+
|
|
658
|
+
// A[X] for `type A[P any] = T`: resolve T with each P bound to the
|
|
659
|
+
// concrete X, so A[int] is exactly T[int] (generic named types inside T
|
|
660
|
+
// are instantiated normally and keep their identity).
|
|
661
|
+
_instantiateGenericAlias(generic, typeArgs) {
|
|
662
|
+
const { typeParams, declNode } = generic;
|
|
663
|
+
if (typeArgs.length !== typeParams.length) {
|
|
664
|
+
return this.err(
|
|
665
|
+
`got ${typeArgs.length} type arguments but ${declNode.name} has ${typeParams.length} type parameters`,
|
|
666
|
+
declNode,
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
const aliasScope = new Scope(this.globals);
|
|
670
|
+
for (let i = 0; i < typeParams.length; i++) {
|
|
671
|
+
const tp = typeParams[i];
|
|
672
|
+
if (tp.constraint) {
|
|
673
|
+
const constraint = this.resolveTypeNode(tp.constraint, this.globals);
|
|
674
|
+
this.checkConstraint(typeArgs[i], constraint, declNode);
|
|
321
675
|
}
|
|
676
|
+
aliasScope.define(tp.name, { kind: "aliasArg", type: typeArgs[i] });
|
|
322
677
|
}
|
|
323
|
-
return
|
|
324
|
-
kind: "named",
|
|
325
|
-
name: generic.declNode.name,
|
|
326
|
-
typeArgs,
|
|
327
|
-
underlying: instantiated,
|
|
328
|
-
methods: instantiated.methods,
|
|
329
|
-
};
|
|
678
|
+
return this.resolveTypeNode(declNode.type, aliasScope);
|
|
330
679
|
},
|
|
331
680
|
|
|
332
|
-
|
|
681
|
+
// `explicit` seeds the leading type arguments of a partial instantiation
|
|
682
|
+
// (`F[int](x)`); the rest are inferred from the call arguments.
|
|
683
|
+
// `scope` resolves the type parameters of the calling function, so an
|
|
684
|
+
// argument of type `S ~[]E` unifies through its core type.
|
|
685
|
+
inferTypeArgs(genericType, argTypes, explicit = [], scope = null) {
|
|
333
686
|
const map = new Map();
|
|
687
|
+
const saved = this._inferScope;
|
|
688
|
+
this._inferScope = scope;
|
|
689
|
+
try {
|
|
690
|
+
return this._inferTypeArgs(genericType, argTypes, explicit, map);
|
|
691
|
+
} finally {
|
|
692
|
+
this._inferScope = saved;
|
|
693
|
+
}
|
|
694
|
+
},
|
|
695
|
+
|
|
696
|
+
_inferTypeArgs(genericType, argTypes, explicit, map) {
|
|
697
|
+
for (let i = 0; i < explicit.length; i++)
|
|
698
|
+
map.set(genericType.typeParams[i].name, explicit[i]);
|
|
334
699
|
const params = genericType.underlying.params;
|
|
335
700
|
for (let i = 0; i < params.length && i < argTypes.length; i++)
|
|
336
701
|
this._inferFromPair(params[i], argTypes[i], map);
|
|
702
|
+
this._inferFromCoreTypes(genericType.typeParams, map);
|
|
337
703
|
for (const tp of genericType.typeParams) {
|
|
338
704
|
if (!map.has(tp.name)) return null;
|
|
339
705
|
}
|
|
340
706
|
return genericType.typeParams.map((tp) => defaultType(map.get(tp.name)));
|
|
341
707
|
},
|
|
342
708
|
|
|
709
|
+
// Constraint type inference: a type parameter `S` constrained by `~[]E`
|
|
710
|
+
// whose argument is known unifies `E` with that argument's element type.
|
|
711
|
+
_inferFromCoreTypes(typeParams, map) {
|
|
712
|
+
const scope = new Scope(this.globals);
|
|
713
|
+
for (const tp of typeParams) scope.define(tp.name, tp);
|
|
714
|
+
for (let round = 0; round < typeParams.length; round++) {
|
|
715
|
+
const before = map.size;
|
|
716
|
+
for (const tp of typeParams) {
|
|
717
|
+
const arg = map.get(tp.name);
|
|
718
|
+
if (!arg || !tp.constraint) continue;
|
|
719
|
+
const terms = this._constraintTerms(tp.constraint, new Set(), scope);
|
|
720
|
+
if (terms?.length !== 1) continue;
|
|
721
|
+
const argUnder =
|
|
722
|
+
this._coreOfTypeParam(arg) ??
|
|
723
|
+
this._deepUnderlying(this.resolveType(arg));
|
|
724
|
+
if (argUnder) this._inferFromPair(terms[0], argUnder, map);
|
|
725
|
+
}
|
|
726
|
+
if (map.size === before) break;
|
|
727
|
+
}
|
|
728
|
+
},
|
|
729
|
+
|
|
343
730
|
_inferFromFuncPair(paramType, argType, map) {
|
|
344
731
|
for (
|
|
345
732
|
let i = 0;
|
|
@@ -358,27 +745,63 @@ export const resolveMethods = {
|
|
|
358
745
|
_inferFromStructuredPair(paramType, argType, map) {
|
|
359
746
|
if (paramType.kind === "slice" && argType.kind === "slice")
|
|
360
747
|
this._inferFromPair(paramType.elem, argType.elem, map);
|
|
748
|
+
if (paramType.kind === "pointer" && argType.kind === "pointer")
|
|
749
|
+
this._inferFromPair(paramType.base, argType.base, map);
|
|
361
750
|
if (paramType.kind === "map" && argType.kind === "map") {
|
|
362
751
|
this._inferFromPair(paramType.key, argType.key, map);
|
|
363
752
|
this._inferFromPair(paramType.value, argType.value, map);
|
|
364
753
|
}
|
|
365
754
|
if (paramType.kind === "func" && argType.kind === "func")
|
|
366
755
|
this._inferFromFuncPair(paramType, argType, map);
|
|
756
|
+
if (paramType.kind === "tuple" && argType.kind === "tuple") {
|
|
757
|
+
for (
|
|
758
|
+
let i = 0;
|
|
759
|
+
i < paramType.types.length && i < argType.types.length;
|
|
760
|
+
i++
|
|
761
|
+
)
|
|
762
|
+
this._inferFromPair(paramType.types[i], argType.types[i], map);
|
|
763
|
+
}
|
|
764
|
+
},
|
|
765
|
+
|
|
766
|
+
// An argument whose type is a type parameter of the calling function
|
|
767
|
+
// (`S ~[]E`) unifies through its core type: that type, or null.
|
|
768
|
+
// `paramType` (optional) is the parameter it is matched against; a bare
|
|
769
|
+
// type parameter takes the argument as it is.
|
|
770
|
+
_coreOfTypeParam(argType, paramType = null) {
|
|
771
|
+
if (argType.kind !== "typeParam" || !this._inferScope) return null;
|
|
772
|
+
if (paramType?.kind === "typeParam") return null;
|
|
773
|
+
return this._coreType(argType, this._inferScope);
|
|
367
774
|
},
|
|
368
775
|
|
|
369
776
|
_inferFromPair(paramType, argType, map) {
|
|
370
777
|
if (!paramType || !argType) return;
|
|
778
|
+
argType = this._coreOfTypeParam(argType, paramType) ?? argType;
|
|
371
779
|
if (paramType.kind === "typeParam") {
|
|
372
|
-
|
|
780
|
+
const prev = map.get(paramType.name);
|
|
781
|
+
if (!prev || untypedWins(prev, argType)) map.set(paramType.name, argType);
|
|
373
782
|
return;
|
|
374
783
|
}
|
|
784
|
+
if (paramType.kind === "named" && paramType.typeArgs) {
|
|
785
|
+
// `iter.Seq[E]` against `iter.Seq[int]`: unify the type arguments;
|
|
786
|
+
// against any other type, its underlying type.
|
|
787
|
+
if (argType.kind === "named" && argType.name === paramType.name) {
|
|
788
|
+
const args = argType.typeArgs ?? [];
|
|
789
|
+
for (let i = 0; i < paramType.typeArgs.length; i++)
|
|
790
|
+
this._inferFromPair(paramType.typeArgs[i], args[i], map);
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
paramType = paramType.underlying;
|
|
794
|
+
}
|
|
795
|
+
if (argType.kind === "named" && argType.underlying)
|
|
796
|
+
argType = argType.underlying;
|
|
375
797
|
this._inferFromStructuredPair(paramType, argType, map);
|
|
376
798
|
},
|
|
377
799
|
|
|
378
800
|
_checkUnionConstraint(typeArg, constraint, node) {
|
|
379
801
|
for (const term of constraint.terms) {
|
|
380
802
|
if (term.approx) return;
|
|
381
|
-
const termType =
|
|
803
|
+
const termType =
|
|
804
|
+
term.resolved ?? this.resolveTypeNode(term.type, this.globals);
|
|
382
805
|
if (typeStr(typeArg) === typeStr(termType)) return;
|
|
383
806
|
}
|
|
384
807
|
this.err(`type ${typeStr(typeArg)} does not satisfy constraint`, node);
|
|
@@ -422,11 +845,37 @@ export const resolveMethods = {
|
|
|
422
845
|
(!t.underlying || !t.methods)
|
|
423
846
|
) {
|
|
424
847
|
const found = this.types.get(t.name);
|
|
425
|
-
|
|
848
|
+
// Two imported packages can both declare `Pt`; the bare name only
|
|
849
|
+
// reaches the last one registered, so a declaration that came from
|
|
850
|
+
// an import's own type table stays itself.
|
|
851
|
+
if (found && !(t.underlying && this._ambiguousImported?.has(t)))
|
|
852
|
+
return found;
|
|
426
853
|
}
|
|
854
|
+
if (t.kind === "named" && t.typeArgs?.length)
|
|
855
|
+
return this._withGenericMethods(t);
|
|
427
856
|
return t;
|
|
428
857
|
},
|
|
429
858
|
|
|
859
|
+
// A substituted generic instance (e.g. Stack[int] from an inferred call)
|
|
860
|
+
// drops the methods map; re-derive it from the generic declaration.
|
|
861
|
+
_withGenericMethods(t) {
|
|
862
|
+
const decl = this.types.get(t.name);
|
|
863
|
+
const generic = decl?._generic;
|
|
864
|
+
// Struct generics only: codegen has no receiver support for generic
|
|
865
|
+
// slice/map/func named types, so those keep the baseline diagnostics.
|
|
866
|
+
if (this.resolveType(decl?.underlying)?.kind !== "struct") return t;
|
|
867
|
+
if (!generic?.methods || generic.methods.size === 0) return t;
|
|
868
|
+
if (t.methods && generic.methods.size <= t.methods.size) return t;
|
|
869
|
+
const map = new Map();
|
|
870
|
+
for (let i = 0; i < generic.typeParams.length; i++)
|
|
871
|
+
map.set(generic.typeParams[i].name, t.typeArgs[i] ?? ANY);
|
|
872
|
+
const methods = new Map(t.methods);
|
|
873
|
+
for (const [name, mType] of generic.methods)
|
|
874
|
+
if (!methods.has(name))
|
|
875
|
+
methods.set(name, this.substituteType(mType, map));
|
|
876
|
+
return { ...t, methods };
|
|
877
|
+
},
|
|
878
|
+
|
|
430
879
|
// ── Field type lookup ─────────────────────────────────────────────
|
|
431
880
|
|
|
432
881
|
fieldType(baseType, field, node) {
|
|
@@ -446,7 +895,7 @@ export const resolveMethods = {
|
|
|
446
895
|
return (
|
|
447
896
|
baseType.kind === "pointer" &&
|
|
448
897
|
field !== "value" &&
|
|
449
|
-
baseType.base?.kind === "named"
|
|
898
|
+
(baseType.base?.kind === "named" || baseType.base?.kind === "struct")
|
|
450
899
|
);
|
|
451
900
|
},
|
|
452
901
|
|
|
@@ -490,12 +939,54 @@ export const resolveMethods = {
|
|
|
490
939
|
return base.fields.get(field);
|
|
491
940
|
}
|
|
492
941
|
if (base.methods?.has(field)) return base.methods.get(field);
|
|
942
|
+
const typeName = baseType?.name || base.name;
|
|
943
|
+
if (typeName) {
|
|
944
|
+
const dName = displayTypeName(typeName);
|
|
945
|
+
const shortName = typeName.includes("$$")
|
|
946
|
+
? typeName.split("$$")[0]
|
|
947
|
+
: typeName.includes(".")
|
|
948
|
+
? typeName.split(".").pop()
|
|
949
|
+
: typeName;
|
|
950
|
+
for (const key of [
|
|
951
|
+
`${dName}.${field}`,
|
|
952
|
+
`${typeName}.${field}`,
|
|
953
|
+
`${shortName}.${field}`,
|
|
954
|
+
]) {
|
|
955
|
+
if (EXCLUDED_STDLIB_MEMBERS.has(key)) {
|
|
956
|
+
return this.err(
|
|
957
|
+
`${dName}.${field} is not available: ${EXCLUDED_STDLIB_MEMBERS.get(key)}`,
|
|
958
|
+
node,
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
493
963
|
return this.err(`No field '${field}' on ${typeStr(baseType)}`, node);
|
|
494
964
|
},
|
|
495
965
|
|
|
496
966
|
_fieldTypeInterface(base, baseType, field, node) {
|
|
497
967
|
if (base.methods?.has(field)) return base.methods.get(field);
|
|
498
968
|
if (base.fields?.has(field)) return base.fields.get(field);
|
|
969
|
+
const typeName = baseType?.name || base.name;
|
|
970
|
+
if (typeName) {
|
|
971
|
+
const dName = displayTypeName(typeName);
|
|
972
|
+
const shortName = typeName.includes("$$")
|
|
973
|
+
? typeName.split("$$")[0]
|
|
974
|
+
: typeName.includes(".")
|
|
975
|
+
? typeName.split(".").pop()
|
|
976
|
+
: typeName;
|
|
977
|
+
for (const key of [
|
|
978
|
+
`${dName}.${field}`,
|
|
979
|
+
`${typeName}.${field}`,
|
|
980
|
+
`${shortName}.${field}`,
|
|
981
|
+
]) {
|
|
982
|
+
if (EXCLUDED_STDLIB_MEMBERS.has(key)) {
|
|
983
|
+
return this.err(
|
|
984
|
+
`${dName}.${field} is not available: ${EXCLUDED_STDLIB_MEMBERS.get(key)}`,
|
|
985
|
+
node,
|
|
986
|
+
);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
499
990
|
if (base._excluded?.has(field))
|
|
500
991
|
return this.err(`No member '${field}' on ${typeStr(baseType)}`, node);
|
|
501
992
|
if (base._isOpen) return ANY;
|
|
@@ -537,12 +1028,37 @@ export const resolveMethods = {
|
|
|
537
1028
|
return ANY;
|
|
538
1029
|
},
|
|
539
1030
|
|
|
540
|
-
|
|
1031
|
+
// An error for `field` when the namespace cannot offer it to this package:
|
|
1032
|
+
// an unexported name, or a member of a `js` file in a wasm or both package.
|
|
1033
|
+
_namespaceMemberError(base, field, node) {
|
|
541
1034
|
if (base._gofront && field.length > 0 && field[0] >= "a" && field[0] <= "z")
|
|
542
1035
|
return this.err(
|
|
543
1036
|
`cannot refer to unexported name ${base.name}.${field}`,
|
|
544
1037
|
node,
|
|
545
1038
|
);
|
|
1039
|
+
const wasm = this.target === "wasm" || this.target === "both";
|
|
1040
|
+
return wasm && base._jsOnly?.has(field)
|
|
1041
|
+
? this._errJsOnly(`${base.name}.${field}`, node)
|
|
1042
|
+
: null;
|
|
1043
|
+
},
|
|
1044
|
+
|
|
1045
|
+
_fieldTypeNamespace(base, field, node) {
|
|
1046
|
+
const bad = this._namespaceMemberError(base, field, node);
|
|
1047
|
+
if (bad) return bad;
|
|
1048
|
+
// Go-source stdlib member: backends emit the mangled top-level name.
|
|
1049
|
+
const mangled = base._mangle?.get(field);
|
|
1050
|
+
if (mangled) {
|
|
1051
|
+
node._stdName = mangled;
|
|
1052
|
+
// Lazy stdlib bodies: referencing a member checks what it reaches.
|
|
1053
|
+
const failure = base._reach?.(mangled);
|
|
1054
|
+
if (failure)
|
|
1055
|
+
this.err(
|
|
1056
|
+
`internal stdlib error in ${base._path}; please report`,
|
|
1057
|
+
node,
|
|
1058
|
+
failure,
|
|
1059
|
+
);
|
|
1060
|
+
return base.members[field];
|
|
1061
|
+
}
|
|
546
1062
|
if (field in base.members) {
|
|
547
1063
|
if (
|
|
548
1064
|
(this.target === "wasm" || this.target === "both") &&
|
|
@@ -561,6 +1077,12 @@ export const resolveMethods = {
|
|
|
561
1077
|
}
|
|
562
1078
|
return base.members[field];
|
|
563
1079
|
}
|
|
1080
|
+
const excluded = EXCLUDED_STDLIB_MEMBERS.get(`${base.name}.${field}`);
|
|
1081
|
+
if (excluded)
|
|
1082
|
+
return this.err(
|
|
1083
|
+
`${base.name}.${field} is not available: ${excluded}`,
|
|
1084
|
+
node,
|
|
1085
|
+
);
|
|
564
1086
|
return this.err(`No member '${field}' in namespace ${base.name}`, node);
|
|
565
1087
|
},
|
|
566
1088
|
|
|
@@ -575,7 +1097,16 @@ function isExported(name) {
|
|
|
575
1097
|
return code >= 65 && code <= 90;
|
|
576
1098
|
}
|
|
577
1099
|
|
|
578
|
-
function _processJsonField(
|
|
1100
|
+
function _processJsonField(
|
|
1101
|
+
f,
|
|
1102
|
+
depth,
|
|
1103
|
+
path,
|
|
1104
|
+
visited,
|
|
1105
|
+
queue,
|
|
1106
|
+
candidates,
|
|
1107
|
+
index,
|
|
1108
|
+
pos,
|
|
1109
|
+
) {
|
|
579
1110
|
const tagInfo = parseJsonTag(f.tag);
|
|
580
1111
|
if (tagInfo.ignored) return;
|
|
581
1112
|
|
|
@@ -602,6 +1133,7 @@ function _processJsonField(f, depth, path, visited, queue, candidates) {
|
|
|
602
1133
|
depth,
|
|
603
1134
|
tagged: true,
|
|
604
1135
|
isEmbedded: true,
|
|
1136
|
+
index: [...index, pos],
|
|
605
1137
|
path: [...path, { name: embedName, isPtr, typeName: embedName }],
|
|
606
1138
|
});
|
|
607
1139
|
}
|
|
@@ -612,6 +1144,7 @@ function _processJsonField(f, depth, path, visited, queue, candidates) {
|
|
|
612
1144
|
queue.push({
|
|
613
1145
|
struct: embedStruct,
|
|
614
1146
|
depth: depth + 1,
|
|
1147
|
+
index: [...index, pos],
|
|
615
1148
|
path: [...path, { name: embedName, isPtr, typeName: embedName }],
|
|
616
1149
|
visited: nextVisited,
|
|
617
1150
|
});
|
|
@@ -620,9 +1153,10 @@ function _processJsonField(f, depth, path, visited, queue, candidates) {
|
|
|
620
1153
|
return;
|
|
621
1154
|
}
|
|
622
1155
|
|
|
623
|
-
for (const name of f.names) {
|
|
1156
|
+
for (const [i, name] of f.names.entries()) {
|
|
624
1157
|
if (!isExported(name)) continue;
|
|
625
1158
|
candidates.push({
|
|
1159
|
+
index: [...index, pos + i],
|
|
626
1160
|
jsonKey: tagInfo.name !== null ? tagInfo.name : name,
|
|
627
1161
|
goName: name,
|
|
628
1162
|
type: f.type,
|
|
@@ -658,10 +1192,18 @@ function _resolveCandidateCollisions(candidates) {
|
|
|
658
1192
|
}
|
|
659
1193
|
}
|
|
660
1194
|
|
|
661
|
-
|
|
1195
|
+
// Go orders the fields of a struct by their index sequence, so a promoted
|
|
1196
|
+
// field sits where its embedded struct is declared.
|
|
1197
|
+
winningFields.sort((a, b) => compareIndex(a.index, b.index));
|
|
662
1198
|
return winningFields;
|
|
663
1199
|
}
|
|
664
1200
|
|
|
1201
|
+
function compareIndex(a, b) {
|
|
1202
|
+
for (let i = 0; i < a.length && i < b.length; i++)
|
|
1203
|
+
if (a[i] !== b[i]) return a[i] - b[i];
|
|
1204
|
+
return a.length - b.length;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
665
1207
|
export function resolveJsonFields(structType) {
|
|
666
1208
|
if (!structType) return [];
|
|
667
1209
|
const underlying =
|
|
@@ -670,16 +1212,25 @@ export function resolveJsonFields(structType) {
|
|
|
670
1212
|
if (underlying._jsonFields) return underlying._jsonFields;
|
|
671
1213
|
|
|
672
1214
|
const queue = [
|
|
673
|
-
{
|
|
1215
|
+
{
|
|
1216
|
+
struct: underlying,
|
|
1217
|
+
depth: 0,
|
|
1218
|
+
path: [],
|
|
1219
|
+
index: [],
|
|
1220
|
+
visited: new Set([underlying]),
|
|
1221
|
+
},
|
|
674
1222
|
];
|
|
675
1223
|
const candidates = [];
|
|
676
1224
|
|
|
677
1225
|
while (queue.length > 0) {
|
|
678
|
-
const { struct: curr, depth, path, visited } = queue.shift();
|
|
1226
|
+
const { struct: curr, depth, path, index, visited } = queue.shift();
|
|
679
1227
|
if (curr?.kind !== "struct") continue;
|
|
680
1228
|
|
|
1229
|
+
// Each name of a `A, B int` field is its own field, as in Go.
|
|
1230
|
+
let pos = 0;
|
|
681
1231
|
for (const f of curr._structFields || []) {
|
|
682
|
-
_processJsonField(f, depth, path, visited, queue, candidates);
|
|
1232
|
+
_processJsonField(f, depth, path, visited, queue, candidates, index, pos);
|
|
1233
|
+
pos += f.embedded ? 1 : (f.names?.length ?? 1);
|
|
683
1234
|
}
|
|
684
1235
|
}
|
|
685
1236
|
|