gofront 1.9.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 +1 -1
- package/package.json +2 -2
- package/src/backend/js/expressions.js +329 -58
- package/src/backend/js/index.js +90 -10
- package/src/backend/js/runtime.js +51 -9
- package/src/backend/js/statements.js +193 -84
- package/src/backend/js/type-info.js +123 -8
- package/src/backend/shared/fmt-types.js +30 -1
- package/src/backend/wasm/emit-builtins.js +250 -9
- package/src/backend/wasm/emit-exprs.js +63 -22
- package/src/backend/wasm/emit-shared.js +4 -1
- package/src/backend/wasm/emit-stmts.js +239 -205
- package/src/backend/wasm/emit.js +73 -26
- package/src/backend/wasm/globals.js +1 -0
- package/src/backend/wasm/index.js +8 -0
- package/src/backend/wasm/module-emitter.js +211 -57
- package/src/backend/wasm/monomorph.js +36 -2
- package/src/backend/wasm/natives.js +4 -0
- package/src/backend/wasm/types.js +32 -3
- package/src/backend/wasm/variadic.js +6 -1
- package/src/compiler.js +6 -1
- package/src/lexer/lexer.js +1 -0
- package/src/link/stdlib.js +1 -0
- package/src/link/type-names.js +72 -16
- package/src/lower/embedding.js +43 -15
- package/src/lower/fmt-formatters.js +38 -14
- package/src/lower/functions.js +223 -7
- package/src/lower/gen-types.js +21 -2
- package/src/lower/index.js +7 -1
- package/src/lower/json-codecs.js +114 -6
- package/src/parser/declarations.js +4 -2
- package/src/parser/expressions.js +25 -41
- package/src/parser/statements.js +107 -0
- package/src/parser/types.js +11 -0
- package/src/typechecker/assignability.js +94 -12
- package/src/typechecker/constants.js +124 -2
- package/src/typechecker/expressions.js +171 -17
- package/src/typechecker/index.js +158 -29
- package/src/typechecker/operand-text.js +1 -0
- package/src/typechecker/resolve.js +308 -39
- package/src/typechecker/statements.js +171 -21
- package/src/typechecker/types.js +84 -2
|
@@ -15,7 +15,28 @@ import {
|
|
|
15
15
|
INT_RANGES,
|
|
16
16
|
intConst,
|
|
17
17
|
} from "./const-fold.js";
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
compositeLitMaxIndex,
|
|
20
|
+
defaultType,
|
|
21
|
+
isAny,
|
|
22
|
+
isComplex,
|
|
23
|
+
isComplex64,
|
|
24
|
+
isUntyped,
|
|
25
|
+
typeStr,
|
|
26
|
+
VOID,
|
|
27
|
+
} from "./types.js";
|
|
28
|
+
|
|
29
|
+
// True when evaluating `node` runs a call or a channel receive (conversions
|
|
30
|
+
// excluded): such an operand makes `len` of an array a non-constant.
|
|
31
|
+
function hasCallOrReceive(node) {
|
|
32
|
+
if (!node || typeof node !== "object") return false;
|
|
33
|
+
if (Array.isArray(node)) return node.some(hasCallOrReceive);
|
|
34
|
+
if (node.kind === "CallExpr" && !node._isTypeConversion) return true;
|
|
35
|
+
if (node.kind === "UnaryExpr" && node.op === "<-") return true;
|
|
36
|
+
return Object.keys(node).some(
|
|
37
|
+
(key) => key.charCodeAt(0) !== 95 && hasCallOrReceive(node[key]),
|
|
38
|
+
);
|
|
39
|
+
}
|
|
19
40
|
|
|
20
41
|
// Basic type names usable as conversion functions (`int32(x)`).
|
|
21
42
|
const BASIC_CONVERSIONS = new Set([
|
|
@@ -38,6 +59,17 @@ const CONST_KINDS = new Set([
|
|
|
38
59
|
"SelectorExpr",
|
|
39
60
|
]);
|
|
40
61
|
|
|
62
|
+
// Describes a constant that is not integer-valued (`float64 constant`), or
|
|
63
|
+
// null for an integer, an untyped float with an integer value, or a rune.
|
|
64
|
+
function nonIntegerConstKind(v) {
|
|
65
|
+
if (v.k === "INT") return null;
|
|
66
|
+
if (v.k === "FLOAT") {
|
|
67
|
+
if (v.t) return `${v.t} constant`;
|
|
68
|
+
return constIntValue(v) === null ? "untyped float constant" : null;
|
|
69
|
+
}
|
|
70
|
+
return `${v.k.toLowerCase()} constant`;
|
|
71
|
+
}
|
|
72
|
+
|
|
41
73
|
const NEGATIVE_SHIFT_ERROR = "invalid shift count: negative shift count";
|
|
42
74
|
|
|
43
75
|
/** @type {ThisType<import('./index.js').TypeChecker>} */
|
|
@@ -49,6 +81,23 @@ export const constantCheckMethods = {
|
|
|
49
81
|
return raw && !raw.overflow && !raw.error ? raw : null;
|
|
50
82
|
},
|
|
51
83
|
|
|
84
|
+
// What makes `expr` a constant that cannot be an array length, index or
|
|
85
|
+
// size (`untyped bool constant`, `untyped nil`), or null when it is an
|
|
86
|
+
// integer constant or not a constant at all.
|
|
87
|
+
_nonIntegerConst(expr, scope = null) {
|
|
88
|
+
let e = expr;
|
|
89
|
+
while (e?.kind === "ParenExpr") e = e.expr;
|
|
90
|
+
if (e?.kind === "ImagLit") return "untyped complex constant";
|
|
91
|
+
if (e?.kind === "BasicLit" && e.litKind === "NIL") return "untyped nil";
|
|
92
|
+
let v = null;
|
|
93
|
+
try {
|
|
94
|
+
v = this._constValue(expr, scope);
|
|
95
|
+
} catch {
|
|
96
|
+
// not a constant the evaluator understands
|
|
97
|
+
}
|
|
98
|
+
return v ? nonIntegerConstKind(v) : null;
|
|
99
|
+
},
|
|
100
|
+
|
|
52
101
|
// Integer value of an integer (or untyped integral float) constant as a
|
|
53
102
|
// Number, or null.
|
|
54
103
|
_constIntValue(expr, scope = null) {
|
|
@@ -58,6 +107,23 @@ export const constantCheckMethods = {
|
|
|
58
107
|
return i === null ? null : Number(i);
|
|
59
108
|
},
|
|
60
109
|
|
|
110
|
+
// A named untyped complex constant has no folded value (`_const`): its
|
|
111
|
+
// initializer goes on the identifier as `_complexInit`, so the backends
|
|
112
|
+
// can fold it where it takes type complex64.
|
|
113
|
+
// A named `complex64` constant is tagged `_c64Const` as well, so a use
|
|
114
|
+
// takes float32 components as in Go.
|
|
115
|
+
_noteComplexConstInit(expr, scope) {
|
|
116
|
+
if (expr.kind !== "Ident") return;
|
|
117
|
+
const typed64 = !isUntyped(expr._type) && isComplex64(expr._type);
|
|
118
|
+
if (!typed64 && !(isUntyped(expr._type) && isComplex(expr._type))) return;
|
|
119
|
+
const owner = scope.lookupScope(expr.name);
|
|
120
|
+
if (!owner?._consts.has(expr.name)) return;
|
|
121
|
+
const init = owner.lookupConstExpr(expr.name);
|
|
122
|
+
if (!init) return;
|
|
123
|
+
expr._complexInit = init;
|
|
124
|
+
if (typed64) expr._c64Const = true;
|
|
125
|
+
},
|
|
126
|
+
|
|
61
127
|
// Raw evaluation result, memoized on checked nodes: a value, a value with
|
|
62
128
|
// `overflow`, `{ error }`, or null.
|
|
63
129
|
_constRaw(expr, scope) {
|
|
@@ -83,6 +149,7 @@ export const constantCheckMethods = {
|
|
|
83
149
|
// overflow / division by zero once per node.
|
|
84
150
|
_noteConst(expr, scope) {
|
|
85
151
|
if (!CONST_KINDS.has(expr.kind)) return;
|
|
152
|
+
this._noteComplexConstInit(expr, scope ?? this.globals);
|
|
86
153
|
const raw = this._constRaw(expr, scope);
|
|
87
154
|
expr._const = raw && !raw.overflow && !raw.error ? raw : null;
|
|
88
155
|
if (!raw || expr._constReported) return;
|
|
@@ -128,7 +195,13 @@ export const constantCheckMethods = {
|
|
|
128
195
|
|
|
129
196
|
_evalConstIdent(expr, scope) {
|
|
130
197
|
const owner = scope.lookupScope(expr.name);
|
|
131
|
-
|
|
198
|
+
// A constant declared after the type that uses it (`type B [k]int`) has
|
|
199
|
+
// no symbol yet, but its initializer is registered.
|
|
200
|
+
if (!owner) {
|
|
201
|
+
const early = scope.lookupConstExpr(expr.name);
|
|
202
|
+
return early ? this._constValue(early, scope) : null;
|
|
203
|
+
}
|
|
204
|
+
if (!owner._consts.has(expr.name)) return null;
|
|
132
205
|
const init = owner.lookupConstExpr(expr.name);
|
|
133
206
|
const v = init
|
|
134
207
|
? this._constValue(init, owner)
|
|
@@ -159,6 +232,8 @@ export const constantCheckMethods = {
|
|
|
159
232
|
const fn = expr.func;
|
|
160
233
|
if (fn?.kind !== "Ident" || scope.lookup(fn.name)?.kind !== "builtin")
|
|
161
234
|
return null;
|
|
235
|
+
const arrayLen = this._constArrayLen(fn.name, expr.args, scope);
|
|
236
|
+
if (arrayLen !== null) return intConst(BigInt(arrayLen));
|
|
162
237
|
const vals = (expr.args ?? []).map((a) => this._constValue(a, scope));
|
|
163
238
|
if (vals.length === 0 || vals.some((v) => !v)) return null;
|
|
164
239
|
if (fn.name === "min" || fn.name === "max")
|
|
@@ -168,6 +243,53 @@ export const constantCheckMethods = {
|
|
|
168
243
|
return isLen ? intConst(BigInt(vals[0].v.length)) : null;
|
|
169
244
|
},
|
|
170
245
|
|
|
246
|
+
// A package variable named in an array length may be declared after the
|
|
247
|
+
// type (or variable) that uses it, so it is not defined yet: its declared
|
|
248
|
+
// type, or the type of its composite literal, is resolved from the spec.
|
|
249
|
+
_earlyPackageVarType(arg, scope) {
|
|
250
|
+
let e = arg;
|
|
251
|
+
while (e?.kind === "ParenExpr") e = e.expr;
|
|
252
|
+
if (e?.kind !== "Ident") return null;
|
|
253
|
+
// An untyped package variable is `any` until its initializer is typed.
|
|
254
|
+
const known = scope.lookup(e.name);
|
|
255
|
+
if (known && !isAny(known)) return null;
|
|
256
|
+
const entry = this._pkgVarSpecs?.get(e.name);
|
|
257
|
+
if (!entry || entry.busy) return null;
|
|
258
|
+
const { spec, index } = entry;
|
|
259
|
+
const lit = spec.value?.[index];
|
|
260
|
+
const typeNode =
|
|
261
|
+
spec.type ?? (lit?.kind === "CompositeLit" ? lit.typeExpr : null);
|
|
262
|
+
if (!typeNode) return null;
|
|
263
|
+
entry.busy = true;
|
|
264
|
+
try {
|
|
265
|
+
const t = this.resolveTypeNode(typeNode, this.globals);
|
|
266
|
+
// `[...]T{...}`: the length is the literal's highest index + 1.
|
|
267
|
+
return t?.kind === "array" && t.size === null && lit?.elems
|
|
268
|
+
? { ...t, size: compositeLitMaxIndex(lit.elems) + 1 }
|
|
269
|
+
: t;
|
|
270
|
+
} finally {
|
|
271
|
+
entry.busy = false;
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
|
|
275
|
+
// `len(a)` and `cap(a)` of an array (or pointer to an array) are constants
|
|
276
|
+
// unless `a` contains a function call or channel receive; `a` itself is not
|
|
277
|
+
// evaluated. Returns the length, or null.
|
|
278
|
+
_constArrayLen(name, args, scope) {
|
|
279
|
+
if ((name !== "len" && name !== "cap") || args?.length !== 1) return null;
|
|
280
|
+
const arg = args[0];
|
|
281
|
+
if (hasCallOrReceive(arg)) return null;
|
|
282
|
+
let t = this._earlyPackageVarType(arg, scope);
|
|
283
|
+
if (!t) {
|
|
284
|
+
if (arg._type === undefined) this.checkExpr(arg, scope);
|
|
285
|
+
t = arg._type;
|
|
286
|
+
}
|
|
287
|
+
t = this.resolveType(t);
|
|
288
|
+
if (t?.kind === "pointer") t = this.resolveType(t.base);
|
|
289
|
+
if (t?.kind === "named") t = this.resolveType(t.underlying);
|
|
290
|
+
return t?.kind === "array" && Number.isInteger(t.size) ? t.size : null;
|
|
291
|
+
},
|
|
292
|
+
|
|
171
293
|
// Target type of a conversion call (`MyInt(x)`, `int32(x)`), or null.
|
|
172
294
|
_constConversionTarget(expr, scope) {
|
|
173
295
|
if (expr.args?.length !== 1) return null;
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
UNTYPED_COMPLEX,
|
|
45
45
|
UNTYPED_FLOAT,
|
|
46
46
|
UNTYPED_INT,
|
|
47
|
+
UNTYPED_RUNE,
|
|
47
48
|
UNTYPED_STRING,
|
|
48
49
|
VOID,
|
|
49
50
|
} from "./types.js";
|
|
@@ -71,7 +72,7 @@ function minMaxType(at) {
|
|
|
71
72
|
// Each value is (self, name, expr, argTypes, scope) => returnType.
|
|
72
73
|
const BUILTIN_CHECK = {
|
|
73
74
|
len: (s, n, e, at) => s._checkBuiltinLen(n, e, at),
|
|
74
|
-
cap: (s, n, e, at) => s._checkBuiltinLen(n, e, at),
|
|
75
|
+
cap: (s, n, e, at, sc) => s._checkBuiltinLen(n, e, at, sc),
|
|
75
76
|
append: (s, _n, e, at) => {
|
|
76
77
|
const st = at[0] ?? ANY;
|
|
77
78
|
if (isArray(st)) {
|
|
@@ -93,6 +94,11 @@ const BUILTIN_CHECK = {
|
|
|
93
94
|
s._rejectSeqAsSlice(at[i], e.args[i], "a spread argument");
|
|
94
95
|
if (!e.args[i]._spread && at[i]) {
|
|
95
96
|
if (is64) e.args[i]._is64Bit = true;
|
|
97
|
+
s._tagComplexConst(
|
|
98
|
+
s.resolveType(sb.elem),
|
|
99
|
+
s.resolveType(at[i]),
|
|
100
|
+
e.args[i],
|
|
101
|
+
);
|
|
96
102
|
s._markIfaceBox(
|
|
97
103
|
s.resolveType(sb.elem),
|
|
98
104
|
s.resolveType(at[i]),
|
|
@@ -106,7 +112,10 @@ const BUILTIN_CHECK = {
|
|
|
106
112
|
copy: (s, _n, e, at) => s._checkBuiltinCopy(e, at),
|
|
107
113
|
delete: (s, _n, e, at) => {
|
|
108
114
|
const mt = s._deepUnderlying(at[0]);
|
|
109
|
-
if (mt?.kind === "map" && e.args[1])
|
|
115
|
+
if (mt?.kind === "map" && e.args[1]) {
|
|
116
|
+
s._tagFloat32Const(s.resolveType(mt.key), at[1], e.args[1]);
|
|
117
|
+
s._markIfaceKey(mt, e.args[1], at[1]);
|
|
118
|
+
}
|
|
110
119
|
return VOID;
|
|
111
120
|
},
|
|
112
121
|
make: (s, _n, e, _at, sc) => {
|
|
@@ -117,6 +126,8 @@ const BUILTIN_CHECK = {
|
|
|
117
126
|
// `make(S, n)` with S a type parameter makes its core type.
|
|
118
127
|
const core = made.kind === "typeParam" ? s._coreType(made, sc) : null;
|
|
119
128
|
if (core) ta._coreType = core;
|
|
129
|
+
for (const size of e.args.slice(1, 3))
|
|
130
|
+
s._checkIntegerConstOperand(size, "size", sc);
|
|
120
131
|
return made;
|
|
121
132
|
},
|
|
122
133
|
new: (s, _n, e, _at, sc) => {
|
|
@@ -134,14 +145,37 @@ const BUILTIN_CHECK = {
|
|
|
134
145
|
},
|
|
135
146
|
recover: () => ANY,
|
|
136
147
|
error: () => ERROR,
|
|
137
|
-
min: (
|
|
138
|
-
max: (
|
|
148
|
+
min: (s, _n, e, at) => s._checkMinMax(e, at),
|
|
149
|
+
max: (s, _n, e, at) => s._checkMinMax(e, at),
|
|
139
150
|
clear: () => VOID,
|
|
140
151
|
complex: (s, _n, e, at) => s._checkBuiltinComplex(e, at),
|
|
141
152
|
real: (s, n, e, at) => s._checkBuiltinRealImag(n, e, at),
|
|
142
153
|
imag: (s, n, e, at) => s._checkBuiltinRealImag(n, e, at),
|
|
143
154
|
};
|
|
144
155
|
|
|
156
|
+
// Type kinds that are never valid as an index, slice bound or `make` size.
|
|
157
|
+
const NON_INTEGER_KINDS = new Set([
|
|
158
|
+
"struct",
|
|
159
|
+
"pointer",
|
|
160
|
+
"slice",
|
|
161
|
+
"map",
|
|
162
|
+
"func",
|
|
163
|
+
"array",
|
|
164
|
+
"interface",
|
|
165
|
+
"simd",
|
|
166
|
+
]);
|
|
167
|
+
|
|
168
|
+
// Basic types that are never valid as an index, slice bound or `make` size.
|
|
169
|
+
const NON_INTEGER_BASICS = new Set([
|
|
170
|
+
"float32",
|
|
171
|
+
"float64",
|
|
172
|
+
"complex64",
|
|
173
|
+
"complex128",
|
|
174
|
+
"bool",
|
|
175
|
+
"string",
|
|
176
|
+
"any",
|
|
177
|
+
]);
|
|
178
|
+
|
|
145
179
|
// Method-name dispatch for _checkExpr — string values add no static call edges.
|
|
146
180
|
const CHECK_EXPR_DELEGATE = {
|
|
147
181
|
UnaryExpr: "_checkUnaryExpr",
|
|
@@ -171,7 +205,7 @@ export const expressionCheckMethods = {
|
|
|
171
205
|
case "BasicLit": {
|
|
172
206
|
switch (expr.litKind) {
|
|
173
207
|
case "INT":
|
|
174
|
-
return UNTYPED_INT;
|
|
208
|
+
return expr.rune ? UNTYPED_RUNE : UNTYPED_INT;
|
|
175
209
|
case "FLOAT":
|
|
176
210
|
return UNTYPED_FLOAT;
|
|
177
211
|
case "STRING":
|
|
@@ -260,6 +294,10 @@ export const expressionCheckMethods = {
|
|
|
260
294
|
this._retypeUntypedShift(lt, rt, expr.right);
|
|
261
295
|
this._retypeUntypedShift(rt, lt, expr.left);
|
|
262
296
|
}
|
|
297
|
+
this._tagFloat32Const(lt, rt, expr.right);
|
|
298
|
+
this._tagFloat32Const(rt, lt, expr.left);
|
|
299
|
+
this._tagComplex64Const(lt, rt, expr.right);
|
|
300
|
+
this._tagComplex64Const(rt, lt, expr.left);
|
|
263
301
|
if (is64BitInt(lt) && rt?.kind === "untyped" && expr.right)
|
|
264
302
|
expr.right._is64Bit = true;
|
|
265
303
|
if (is64BitInt(rt) && lt?.kind === "untyped" && expr.left)
|
|
@@ -310,7 +348,8 @@ export const expressionCheckMethods = {
|
|
|
310
348
|
|
|
311
349
|
_checkBoolUnaryOp(ot, expr) {
|
|
312
350
|
if (!isBool(ot) && !isAny(ot)) this.err("! requires bool", expr);
|
|
313
|
-
|
|
351
|
+
// `!` keeps an untyped or defined bool type.
|
|
352
|
+
return isBool(ot) ? ot : BOOL;
|
|
314
353
|
},
|
|
315
354
|
|
|
316
355
|
_checkNumericUnaryOp(ot, expr) {
|
|
@@ -826,6 +865,15 @@ export const expressionCheckMethods = {
|
|
|
826
865
|
// with its dynamic type, so `L("1")`, `N(1)`, `"1"` and `1` stay distinct.
|
|
827
866
|
// A named basic value is boxed first, exactly as in any interface
|
|
828
867
|
// conversion; other values are keyed as they are.
|
|
868
|
+
// Untyped constant operands take the result type, so a float32 result
|
|
869
|
+
// rounds them like the typed operands.
|
|
870
|
+
_checkMinMax(e, at) {
|
|
871
|
+
const rt = minMaxType(at);
|
|
872
|
+
for (let i = 0; i < e.args.length; i++)
|
|
873
|
+
this._tagFloat32Const(rt, at[i], e.args[i]);
|
|
874
|
+
return rt;
|
|
875
|
+
},
|
|
876
|
+
|
|
829
877
|
_markIfaceKey(mapType, node, srcType) {
|
|
830
878
|
const key = this.resolveType(mapType.key);
|
|
831
879
|
const kb = key?.kind === "named" ? this.resolveType(key.underlying) : key;
|
|
@@ -866,6 +914,8 @@ export const expressionCheckMethods = {
|
|
|
866
914
|
if (bt?._tainted) return TAINTED_ANY;
|
|
867
915
|
if (isAny(bt)) return ANY;
|
|
868
916
|
const btu = this._coreUnderlying(bt, scope);
|
|
917
|
+
if (btu.kind !== "map")
|
|
918
|
+
this._checkIntegerConstOperand(expr.index, "index", scope);
|
|
869
919
|
if (btu.kind === "slice" || btu.kind === "array") {
|
|
870
920
|
this._checkArrayBoundsExpr(btu, bt, expr, scope);
|
|
871
921
|
return btu.elem;
|
|
@@ -955,15 +1005,61 @@ export const expressionCheckMethods = {
|
|
|
955
1005
|
return t.base;
|
|
956
1006
|
},
|
|
957
1007
|
|
|
1008
|
+
// An index, slice bound or `make` size that is a constant must be an
|
|
1009
|
+
// integer (`s[1 == 1]`, `make([]int, "a")`).
|
|
1010
|
+
// A typed operand must have an integer type too (`s[f]` with `f float64`).
|
|
1011
|
+
_checkIntegerConstOperand(operand, what, scope) {
|
|
1012
|
+
const kind = this._nonIntegerConst(operand, scope);
|
|
1013
|
+
if (kind) {
|
|
1014
|
+
this.err(
|
|
1015
|
+
`invalid argument: ${what} ${exprText(operand)} (${kind}) must be integer`,
|
|
1016
|
+
operand,
|
|
1017
|
+
);
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
const t = operand?._type;
|
|
1021
|
+
if (!this._isNonIntegerType(t, scope)) return;
|
|
1022
|
+
this.err(
|
|
1023
|
+
`invalid argument: ${what} ${exprText(operand)} (${operandKind(operand, typeStr(t), false)}) must be integer`,
|
|
1024
|
+
operand,
|
|
1025
|
+
);
|
|
1026
|
+
},
|
|
1027
|
+
|
|
1028
|
+
// Whether `t` is a type (or a defined type over one) that is not an
|
|
1029
|
+
// integer: a float, complex, bool, string, `any`, struct, pointer, slice,
|
|
1030
|
+
// map, func, array, interface, simd vector or a type parameter whose type
|
|
1031
|
+
// set is not all integers. Untyped constants are judged by
|
|
1032
|
+
// `_nonIntegerConst`, so only untyped non-integers that reach here
|
|
1033
|
+
// (`s[a == b]`) count.
|
|
1034
|
+
_isNonIntegerType(t, scope) {
|
|
1035
|
+
if (!t || t._tainted) return false;
|
|
1036
|
+
// A type parameter is an integer only when every type in its type set
|
|
1037
|
+
// is (`~int | ~uint8`); `any`, `comparable` and method-only
|
|
1038
|
+
// constraints have no integer type set.
|
|
1039
|
+
if (t.kind === "typeParam") {
|
|
1040
|
+
const terms = this._constraintTerms(t.constraint, new Set(), scope);
|
|
1041
|
+
return !(terms?.length > 0 && terms.every(isInteger));
|
|
1042
|
+
}
|
|
1043
|
+
if (t.kind === "untyped")
|
|
1044
|
+
return (
|
|
1045
|
+
t.base === "bool" || t.base === "string" || t.base === "complex128"
|
|
1046
|
+
);
|
|
1047
|
+
const u = this._deepUnderlying(t);
|
|
1048
|
+
if (u?.kind === "basic") return NON_INTEGER_BASICS.has(u.name);
|
|
1049
|
+
return NON_INTEGER_KINDS.has(u?.kind);
|
|
1050
|
+
},
|
|
1051
|
+
|
|
958
1052
|
_checkSliceExpr(expr, scope) {
|
|
959
1053
|
const bt = this._derefArrayPtr(
|
|
960
1054
|
expr,
|
|
961
1055
|
"expr",
|
|
962
1056
|
this.checkExpr(expr.expr, scope),
|
|
963
1057
|
);
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1058
|
+
for (const bound of [expr.low, expr.high, expr.max]) {
|
|
1059
|
+
if (!bound) continue;
|
|
1060
|
+
this.checkExpr(bound, scope);
|
|
1061
|
+
this._checkIntegerConstOperand(bound, "index", scope);
|
|
1062
|
+
}
|
|
967
1063
|
if (bt?._tainted) return TAINTED_ANY;
|
|
968
1064
|
if (isAny(bt)) return ANY;
|
|
969
1065
|
const btu = this._coreUnderlying(bt, scope);
|
|
@@ -1070,6 +1166,16 @@ export const expressionCheckMethods = {
|
|
|
1070
1166
|
// `any(x)` / `error(x)`: box x as an implicit conversion would.
|
|
1071
1167
|
this._markIfaceBox(targetType, srcType, expr.expr);
|
|
1072
1168
|
this._rejectNilConversion(srcType, targetType, expr);
|
|
1169
|
+
if (this._checkIfaceConversion(srcType, targetType, expr, expr.expr))
|
|
1170
|
+
return targetType;
|
|
1171
|
+
if (
|
|
1172
|
+
srcType &&
|
|
1173
|
+
targetType &&
|
|
1174
|
+
!isAny(srcType) &&
|
|
1175
|
+
!isAny(targetType) &&
|
|
1176
|
+
this._checkIfaceTargetConversion(srcType, targetType, expr.expr)
|
|
1177
|
+
)
|
|
1178
|
+
return targetType;
|
|
1073
1179
|
const srcResolved =
|
|
1074
1180
|
srcType?.kind === "named" ? srcType.underlying : srcType;
|
|
1075
1181
|
const tgtResolved =
|
|
@@ -1090,6 +1196,8 @@ export const expressionCheckMethods = {
|
|
|
1090
1196
|
expr,
|
|
1091
1197
|
);
|
|
1092
1198
|
}
|
|
1199
|
+
// `complex64(1 + 0.01i)`: the constant's components round to float32.
|
|
1200
|
+
this._tagComplex64Const(targetType, srcType, expr.expr);
|
|
1093
1201
|
if (tgtResolved?.kind === "slice")
|
|
1094
1202
|
this._rejectSeqAsSlice(srcType, expr.expr, "a slice conversion");
|
|
1095
1203
|
if (this._isMutualArraySlice(tgtResolved, srcResolved)) {
|
|
@@ -1138,8 +1246,25 @@ export const expressionCheckMethods = {
|
|
|
1138
1246
|
return true;
|
|
1139
1247
|
},
|
|
1140
1248
|
|
|
1249
|
+
// `I(x)` with I an interface type converts exactly when x is assignable to
|
|
1250
|
+
// I, so the method-set check (and its wording) is the assignment one.
|
|
1251
|
+
_checkIfaceTargetConversion(srcType, targetType, operand) {
|
|
1252
|
+
// `error("msg")` builds an error value (GoFront extension).
|
|
1253
|
+
if (targetType === ERROR && isString(srcType)) return false;
|
|
1254
|
+
const base = this.resolveType(
|
|
1255
|
+
targetType.kind === "named" ? targetType.underlying : targetType,
|
|
1256
|
+
);
|
|
1257
|
+
if (base?.kind !== "interface" || !operand) return false;
|
|
1258
|
+
this.assertAssignable(targetType, srcType, operand, "conversion");
|
|
1259
|
+
return true;
|
|
1260
|
+
},
|
|
1261
|
+
|
|
1141
1262
|
_checkConversionValidity(srcType, targetType, expr) {
|
|
1263
|
+
if (this._checkIfaceConversion(srcType, targetType, expr, expr.args?.[0]))
|
|
1264
|
+
return;
|
|
1142
1265
|
if (!srcType || !targetType || isAny(srcType) || isAny(targetType)) return;
|
|
1266
|
+
if (this._checkIfaceTargetConversion(srcType, targetType, expr.args?.[0]))
|
|
1267
|
+
return;
|
|
1143
1268
|
const srcResolved = srcType.kind === "named" ? srcType.underlying : srcType;
|
|
1144
1269
|
const tgtResolved =
|
|
1145
1270
|
targetType.kind === "named" ? targetType.underlying : targetType;
|
|
@@ -1813,6 +1938,14 @@ export const expressionCheckMethods = {
|
|
|
1813
1938
|
const node = expr._multiForward ? null : expr.args[i];
|
|
1814
1939
|
this.assertAssignable(targetParam, argTypes[i], node, "argument");
|
|
1815
1940
|
}
|
|
1941
|
+
// `g(f())`: per-element box marks for lowering to split the forward.
|
|
1942
|
+
if (
|
|
1943
|
+
expr._multiForward &&
|
|
1944
|
+
!fnType.variadic &&
|
|
1945
|
+
!this._skipIfaceMark &&
|
|
1946
|
+
fnType.params.length === argTypes.length
|
|
1947
|
+
)
|
|
1948
|
+
this._markForwardedElems(fnType.params, argTypes, expr.args[0]);
|
|
1816
1949
|
|
|
1817
1950
|
this._checkSimdLaneArgs(expr, scope);
|
|
1818
1951
|
},
|
|
@@ -1878,7 +2011,28 @@ export const expressionCheckMethods = {
|
|
|
1878
2011
|
return null;
|
|
1879
2012
|
},
|
|
1880
2013
|
|
|
1881
|
-
|
|
2014
|
+
_capOperandKind(operand, type) {
|
|
2015
|
+
const untypedConst = type?.kind === "untyped" && type.base !== "nil";
|
|
2016
|
+
return untypedConst
|
|
2017
|
+
? operandKind(operand, type.base, true)
|
|
2018
|
+
: operandKind(operand, typeStr(type), false);
|
|
2019
|
+
},
|
|
2020
|
+
|
|
2021
|
+
// `cap` takes a slice or an array (a pointer to an array is dereferenced
|
|
2022
|
+
// before this check). A type parameter needs a slice or array core type;
|
|
2023
|
+
// one with no single core type is left to instantiation. Unknown and
|
|
2024
|
+
// `any` operands (JS interop) are not judged.
|
|
2025
|
+
_capOperandOk(t, scope) {
|
|
2026
|
+
if (!t || t._tainted || isAny(t)) return true;
|
|
2027
|
+
if (t.kind === "typeParam") {
|
|
2028
|
+
const core = this._coreType(t, scope);
|
|
2029
|
+
return !core || core.kind === "slice" || core.kind === "array";
|
|
2030
|
+
}
|
|
2031
|
+
const kind = this._deepUnderlying(t)?.kind;
|
|
2032
|
+
return kind === "slice" || kind === "array";
|
|
2033
|
+
},
|
|
2034
|
+
|
|
2035
|
+
_checkBuiltinLen(name, expr, argTypes, scope) {
|
|
1882
2036
|
this._rejectSeqAsSlice(argTypes[0], expr.args?.[0], `${name}()`);
|
|
1883
2037
|
const operand = expr.args?.[0];
|
|
1884
2038
|
if (expr.args?.length === 1)
|
|
@@ -1891,17 +2045,14 @@ export const expressionCheckMethods = {
|
|
|
1891
2045
|
if (expr.args.length === 1) expr.args[0] = operand;
|
|
1892
2046
|
}
|
|
1893
2047
|
if (name === "cap") {
|
|
1894
|
-
if (
|
|
1895
|
-
(argTypes[0]?.underlying ?? argTypes[0])?.kind === "map" ||
|
|
1896
|
-
isString(argTypes[0])
|
|
1897
|
-
) {
|
|
2048
|
+
if (sharedInfo(argTypes[0])) {
|
|
1898
2049
|
this.err(
|
|
1899
|
-
`
|
|
2050
|
+
`cannot use cap() on ${typeStr(argTypes[0])}: shared buffers have a fixed size; use len()`,
|
|
1900
2051
|
expr,
|
|
1901
2052
|
);
|
|
1902
|
-
} else if (
|
|
2053
|
+
} else if (!this._capOperandOk(argTypes[0], scope)) {
|
|
1903
2054
|
this.err(
|
|
1904
|
-
`
|
|
2055
|
+
`invalid argument: ${exprText(expr.args?.[0])} (${this._capOperandKind(expr.args?.[0], argTypes[0])}) for cap`,
|
|
1905
2056
|
expr,
|
|
1906
2057
|
);
|
|
1907
2058
|
}
|
|
@@ -1935,6 +2086,9 @@ export const expressionCheckMethods = {
|
|
|
1935
2086
|
expr,
|
|
1936
2087
|
);
|
|
1937
2088
|
}
|
|
2089
|
+
// An untyped operand takes type float32 (`complex(f, 0.01)`).
|
|
2090
|
+
this._tagFloat32Const(FLOAT32, crt, expr.args[0]);
|
|
2091
|
+
this._tagFloat32Const(FLOAT32, cit, expr.args[1]);
|
|
1938
2092
|
return COMPLEX64;
|
|
1939
2093
|
}
|
|
1940
2094
|
return COMPLEX128;
|