gofront 0.0.9 → 1.0.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 +5 -5
- package/package.json +7 -4
- package/src/cli-core.js +65 -0
- package/src/codegen/expressions.js +523 -1717
- package/src/codegen/index.js +576 -0
- package/src/codegen/runtime.js +151 -0
- package/src/codegen/statements.js +308 -296
- package/src/codegen/stdlib/builder.js +39 -0
- package/src/codegen/stdlib/bytes.js +65 -0
- package/src/codegen/stdlib/errors.js +20 -0
- package/src/codegen/stdlib/fmt.js +104 -0
- package/src/codegen/stdlib/gom.js +155 -0
- package/src/codegen/stdlib/html.js +20 -0
- package/src/codegen/stdlib/index.js +98 -0
- package/src/codegen/stdlib/io.js +36 -0
- package/src/codegen/stdlib/maps.js +45 -0
- package/src/codegen/stdlib/math.js +56 -0
- package/src/codegen/stdlib/os.js +13 -0
- package/src/codegen/stdlib/path.js +34 -0
- package/src/codegen/stdlib/rand.js +27 -0
- package/src/codegen/stdlib/regexp.js +54 -0
- package/src/codegen/stdlib/slices.js +51 -0
- package/src/codegen/stdlib/sort.js +31 -0
- package/src/codegen/stdlib/strconv.js +33 -0
- package/src/codegen/stdlib/strings.js +86 -0
- package/src/codegen/stdlib/time.js +57 -0
- package/src/codegen/stdlib/unicode.js +25 -0
- package/src/codegen/stdlib/utf8.js +26 -0
- package/src/codegen/templ.js +284 -0
- package/src/compiler.js +142 -77
- package/src/dts-parser.js +355 -371
- package/src/index.js +32 -162
- package/src/lexer.js +186 -272
- package/src/minifier.js +305 -318
- package/src/{parser.js → parser/declarations.js} +18 -175
- package/src/parser/expressions.js +51 -34
- package/src/parser/index.js +161 -0
- package/src/parser/statements.js +24 -34
- package/src/parser/types.js +31 -29
- package/src/templ-parser.js +2 -2
- package/src/tokens.js +109 -0
- package/src/typechecker/assignability.js +261 -0
- package/src/typechecker/expressions.js +568 -549
- package/src/typechecker/index.js +556 -0
- package/src/typechecker/resolve.js +428 -0
- package/src/typechecker/statements.js +270 -280
- package/src/typechecker/stdlib/core.js +592 -0
- package/src/typechecker/stdlib/extended.js +332 -0
- package/src/typechecker/stdlib.js +9 -1060
- package/src/typechecker/termination.js +86 -0
- package/src/typechecker/types.js +1 -0
- package/src/codegen.js +0 -882
- package/src/typechecker.js +0 -1299
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
NIL,
|
|
18
18
|
Scope,
|
|
19
19
|
STRING,
|
|
20
|
+
TAINTED_ANY,
|
|
20
21
|
typeStr,
|
|
21
22
|
UNTYPED_BOOL,
|
|
22
23
|
UNTYPED_COMPLEX,
|
|
@@ -26,6 +27,63 @@ import {
|
|
|
26
27
|
VOID,
|
|
27
28
|
} from "./types.js";
|
|
28
29
|
|
|
30
|
+
/** @typedef {import('./index.js').TypeChecker} TypeChecker */
|
|
31
|
+
|
|
32
|
+
// Dispatch table for builtin function type checking.
|
|
33
|
+
// Each value is (self, name, expr, argTypes, scope) => returnType.
|
|
34
|
+
const BUILTIN_CHECK = {
|
|
35
|
+
len: (s, n, e, at) => s._checkBuiltinLen(n, e, at),
|
|
36
|
+
cap: (s, n, e, at) => s._checkBuiltinLen(n, e, at),
|
|
37
|
+
append: (s, _n, e, at) => {
|
|
38
|
+
const st = at[0] ?? ANY;
|
|
39
|
+
if (isArray(st)) {
|
|
40
|
+
s.err(`cannot append to array (type ${typeStr(st)})`, e);
|
|
41
|
+
return st;
|
|
42
|
+
}
|
|
43
|
+
return st;
|
|
44
|
+
},
|
|
45
|
+
copy: () => INT,
|
|
46
|
+
delete: () => VOID,
|
|
47
|
+
make: (s, _n, e, _at, sc) => {
|
|
48
|
+
if (e.args.length < 1) return ANY;
|
|
49
|
+
const ta = e.args[0];
|
|
50
|
+
const tn = ta.kind === "TypeExpr" ? ta.type : ta;
|
|
51
|
+
return s.resolveTypeNode(tn, sc) ?? ANY;
|
|
52
|
+
},
|
|
53
|
+
new: (s, _n, e, _at, sc) => {
|
|
54
|
+
if (e.args.length < 1) return ANY;
|
|
55
|
+
const inner = s.resolveTypeNode(e.args[0], sc);
|
|
56
|
+
return { kind: "pointer", base: inner };
|
|
57
|
+
},
|
|
58
|
+
print: () => VOID,
|
|
59
|
+
println: () => VOID,
|
|
60
|
+
panic: () => VOID,
|
|
61
|
+
recover: () => ANY,
|
|
62
|
+
error: () => ERROR,
|
|
63
|
+
min: (_s, _n, _e, at) => at[0] ?? ANY,
|
|
64
|
+
max: (_s, _n, _e, at) => at[0] ?? ANY,
|
|
65
|
+
clear: () => VOID,
|
|
66
|
+
complex: (s, _n, e, at) => s._checkBuiltinComplex(e, at),
|
|
67
|
+
real: (s, n, e, at) => s._checkBuiltinRealImag(n, e, at),
|
|
68
|
+
imag: (s, n, e, at) => s._checkBuiltinRealImag(n, e, at),
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Method-name dispatch for _checkExpr — string values add no static call edges.
|
|
72
|
+
const CHECK_EXPR_DELEGATE = {
|
|
73
|
+
UnaryExpr: "_checkUnaryExpr",
|
|
74
|
+
CallExpr: "checkCall",
|
|
75
|
+
SelectorExpr: "_checkSelectorExpr",
|
|
76
|
+
IndexExpr: "_checkIndexExpr",
|
|
77
|
+
SliceExpr: "_checkSliceExpr",
|
|
78
|
+
CompositeLit: "checkCompositeLit",
|
|
79
|
+
FuncLit: "_checkFuncLit",
|
|
80
|
+
TypeConversion: "_checkTypeConversion",
|
|
81
|
+
TypeAssertExpr: "_checkTypeAssertExpr",
|
|
82
|
+
RangeExpr: "_checkRangeExpr",
|
|
83
|
+
InstantiationExpr: "_checkInstantiationExpr",
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/** @type {ThisType<TypeChecker>} */
|
|
29
87
|
export const expressionCheckMethods = {
|
|
30
88
|
checkExpr(expr, scope) {
|
|
31
89
|
const t = this._checkExpr(expr, scope);
|
|
@@ -50,335 +108,317 @@ export const expressionCheckMethods = {
|
|
|
50
108
|
}
|
|
51
109
|
break;
|
|
52
110
|
}
|
|
53
|
-
|
|
54
111
|
case "ImagLit":
|
|
55
112
|
return UNTYPED_COMPLEX;
|
|
56
|
-
|
|
57
113
|
case "Ident": {
|
|
58
114
|
const t = scope.lookup(expr.name);
|
|
59
115
|
if (!t) return this.err(`Undefined: '${expr.name}'`, expr);
|
|
60
|
-
// Mark type-name identifiers so SelectorExpr can detect method expressions
|
|
61
116
|
if (this.types.has(expr.name)) expr._isTypeRef = true;
|
|
62
117
|
return t;
|
|
63
118
|
}
|
|
64
|
-
|
|
65
|
-
case "UnaryExpr": {
|
|
66
|
-
const ot = this.checkExpr(expr.operand, scope);
|
|
67
|
-
if (expr.op === "!") {
|
|
68
|
-
if (!isBool(ot) && !isAny(ot)) this.err("! requires bool", expr);
|
|
69
|
-
return BOOL;
|
|
70
|
-
}
|
|
71
|
-
if (expr.op === "-" || expr.op === "+") {
|
|
72
|
-
if (!isNumeric(ot) && !isComplex(ot) && !isAny(ot))
|
|
73
|
-
this.err(`${expr.op} requires numeric`, expr);
|
|
74
|
-
return ot;
|
|
75
|
-
}
|
|
76
|
-
if (expr.op === "&") {
|
|
77
|
-
// Mark the operand as address-taken for codegen
|
|
78
|
-
if (expr.operand.kind === "Ident") {
|
|
79
|
-
expr.operand._addressTaken = true;
|
|
80
|
-
const sym = scope.lookup(expr.operand.name);
|
|
81
|
-
if (sym) sym._addressTaken = true;
|
|
82
|
-
}
|
|
83
|
-
return { kind: "pointer", base: ot };
|
|
84
|
-
}
|
|
85
|
-
if (expr.op === "*") {
|
|
86
|
-
if (ot === ANY) return ANY;
|
|
87
|
-
if (ot.kind === "pointer") return ot.base;
|
|
88
|
-
if (ot.kind === "named" && ot.underlying?.kind === "pointer")
|
|
89
|
-
return ot.underlying.base;
|
|
90
|
-
this.err(`cannot dereference non-pointer type ${typeStr(ot)}`, expr);
|
|
91
|
-
return ANY;
|
|
92
|
-
}
|
|
93
|
-
return ot;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
119
|
case "BinaryExpr": {
|
|
97
120
|
const lt = this.checkExpr(expr.left, scope);
|
|
98
121
|
const rt = this.checkExpr(expr.right, scope);
|
|
122
|
+
if (lt?._tainted || rt?._tainted) return TAINTED_ANY;
|
|
99
123
|
if (isAny(lt) || isAny(rt))
|
|
100
124
|
return this.binaryResultType(expr.op, ANY, ANY, expr);
|
|
101
125
|
return this.binaryResultType(expr.op, lt, rt, expr);
|
|
102
126
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
127
|
+
case "AwaitExpr":
|
|
128
|
+
return this.checkExpr(expr.expr, scope);
|
|
129
|
+
case "InstantiationExpr":
|
|
130
|
+
return this._checkInstantiationExpr(expr, scope);
|
|
131
|
+
default: {
|
|
132
|
+
const m = CHECK_EXPR_DELEGATE[expr.kind];
|
|
133
|
+
if (m) return this[m](expr, scope);
|
|
134
|
+
return ANY;
|
|
106
135
|
}
|
|
136
|
+
}
|
|
137
|
+
return ANY;
|
|
138
|
+
},
|
|
107
139
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
methodType = base.methods.get(expr.field);
|
|
117
|
-
} else if (
|
|
118
|
-
baseType.kind === "named" &&
|
|
119
|
-
baseType.methods?.has(expr.field)
|
|
120
|
-
) {
|
|
121
|
-
methodType = baseType.methods.get(expr.field);
|
|
122
|
-
}
|
|
123
|
-
if (methodType) {
|
|
124
|
-
// Return a func type with the receiver prepended to params
|
|
125
|
-
expr._isMethodExpr = true;
|
|
126
|
-
expr._methodExprRecv = baseType;
|
|
127
|
-
return {
|
|
128
|
-
kind: "func",
|
|
129
|
-
params: [baseType, ...(methodType.params ?? [])],
|
|
130
|
-
returns: methodType.returns ?? [],
|
|
131
|
-
variadic: methodType.variadic,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
const ft = this.fieldType(baseType, expr.field, expr);
|
|
136
|
-
// Mark method selectors so codegen can emit .bind() when used as a value
|
|
137
|
-
if (ft?.kind === "func") {
|
|
138
|
-
let base = baseType.kind === "named" ? baseType.underlying : baseType;
|
|
139
|
-
base = this.resolveType(base);
|
|
140
|
-
if (
|
|
141
|
-
(base?.kind === "struct" && base.methods?.has(expr.field)) ||
|
|
142
|
-
(baseType.kind === "named" && baseType.methods?.has(expr.field))
|
|
143
|
-
) {
|
|
144
|
-
expr._isMethodValue = true;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return ft;
|
|
148
|
-
}
|
|
140
|
+
_checkDeref(ot, expr) {
|
|
141
|
+
if (ot === ANY) return ANY;
|
|
142
|
+
if (ot.kind === "pointer") return ot.base;
|
|
143
|
+
if (ot.kind === "named" && ot.underlying?.kind === "pointer")
|
|
144
|
+
return ot.underlying.base;
|
|
145
|
+
this.err(`cannot dereference non-pointer type ${typeStr(ot)}`, expr);
|
|
146
|
+
return ANY;
|
|
147
|
+
},
|
|
149
148
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
// Named types (e.g. type Nums []int) are indexable via their underlying
|
|
155
|
-
const btu =
|
|
156
|
-
bt.kind === "named"
|
|
157
|
-
? (this.resolveType(bt.underlying) ?? bt.underlying)
|
|
158
|
-
: bt;
|
|
159
|
-
if (btu.kind === "slice" || btu.kind === "array") {
|
|
160
|
-
// Compile-time bounds checking for arrays
|
|
161
|
-
if (btu.kind === "array" && btu.size != null) {
|
|
162
|
-
const idx = this._constIntValue(expr.index);
|
|
163
|
-
if (idx !== null) {
|
|
164
|
-
if (idx < 0) {
|
|
165
|
-
this.err(
|
|
166
|
-
`invalid array index ${idx} (index must not be negative)`,
|
|
167
|
-
expr,
|
|
168
|
-
);
|
|
169
|
-
} else if (idx >= btu.size) {
|
|
170
|
-
this.err(
|
|
171
|
-
`invalid array index ${idx} (out of bounds for ${typeStr(bt)})`,
|
|
172
|
-
expr,
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return btu.elem;
|
|
178
|
-
}
|
|
179
|
-
if (btu.kind === "map") {
|
|
180
|
-
expr._mapValueType = btu.value; // for codegen zero-value fallback
|
|
181
|
-
return btu.value;
|
|
182
|
-
}
|
|
183
|
-
if (isString(bt)) return INT; // byte
|
|
184
|
-
return this.err(`Cannot index type ${typeStr(bt)}`, expr);
|
|
185
|
-
}
|
|
149
|
+
_checkBoolUnaryOp(ot, expr) {
|
|
150
|
+
if (!isBool(ot) && !isAny(ot)) this.err("! requires bool", expr);
|
|
151
|
+
return BOOL;
|
|
152
|
+
},
|
|
186
153
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (isAny(bt)) return ANY;
|
|
193
|
-
if (bt.kind === "slice" || bt.kind === "array")
|
|
194
|
-
return { kind: "slice", elem: bt.elem };
|
|
195
|
-
if (isString(bt)) return STRING;
|
|
196
|
-
return this.err(`Cannot slice type ${typeStr(bt)}`, expr);
|
|
197
|
-
}
|
|
154
|
+
_checkNumericUnaryOp(ot, expr) {
|
|
155
|
+
if (!isNumeric(ot) && !isComplex(ot) && !isAny(ot))
|
|
156
|
+
this.err(`${expr.op} requires numeric`, expr);
|
|
157
|
+
return ot;
|
|
158
|
+
},
|
|
198
159
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
160
|
+
_checkAddressOfExpr(expr, scope, ot) {
|
|
161
|
+
if (expr.operand.kind === "Ident") {
|
|
162
|
+
expr.operand._addressTaken = true;
|
|
163
|
+
const sym = scope.lookup(expr.operand.name);
|
|
164
|
+
if (sym) sym._addressTaken = true;
|
|
165
|
+
}
|
|
166
|
+
return { kind: "pointer", base: ot };
|
|
167
|
+
},
|
|
202
168
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
169
|
+
_checkUnaryExpr(expr, scope) {
|
|
170
|
+
const ot = this.checkExpr(expr.operand, scope);
|
|
171
|
+
if (expr.op === "!") return this._checkBoolUnaryOp(ot, expr);
|
|
172
|
+
if (expr.op === "-" || expr.op === "+")
|
|
173
|
+
return this._checkNumericUnaryOp(ot, expr);
|
|
174
|
+
if (expr.op === "&") return this._checkAddressOfExpr(expr, scope, ot);
|
|
175
|
+
if (expr.op === "*") return this._checkDeref(ot, expr);
|
|
176
|
+
return ot;
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
_isStructOrNamedMethod(baseType, base, field) {
|
|
180
|
+
return (
|
|
181
|
+
(base?.kind === "struct" && base.methods?.has(field)) ||
|
|
182
|
+
(baseType.kind === "named" && baseType.methods?.has(field))
|
|
183
|
+
);
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
_resolveMethodExprType(baseType, base, field) {
|
|
187
|
+
if (!this._isStructOrNamedMethod(baseType, base, field)) return null;
|
|
188
|
+
return base?.methods?.get(field) ?? baseType.methods?.get(field);
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
_checkSelectorExpr(expr, scope) {
|
|
192
|
+
const baseType = this.checkExpr(expr.expr, scope);
|
|
193
|
+
if (baseType?._tainted) return TAINTED_ANY;
|
|
194
|
+
if (expr.expr._isTypeRef) {
|
|
195
|
+
let base = baseType.kind === "named" ? baseType.underlying : baseType;
|
|
196
|
+
base = this.resolveType(base);
|
|
197
|
+
const methodType = this._resolveMethodExprType(
|
|
198
|
+
baseType,
|
|
199
|
+
base,
|
|
200
|
+
expr.field,
|
|
201
|
+
);
|
|
202
|
+
if (methodType) {
|
|
203
|
+
expr._isMethodExpr = true;
|
|
204
|
+
expr._methodExprRecv = baseType;
|
|
222
205
|
return {
|
|
223
206
|
kind: "func",
|
|
224
|
-
params:
|
|
225
|
-
returns: [
|
|
226
|
-
|
|
207
|
+
params: [baseType, ...(methodType.params ?? [])],
|
|
208
|
+
returns: methodType.returns ?? [],
|
|
209
|
+
variadic: methodType.variadic,
|
|
227
210
|
};
|
|
228
211
|
}
|
|
212
|
+
}
|
|
213
|
+
const ft = this.fieldType(baseType, expr.field, expr);
|
|
214
|
+
if (ft?.kind === "func") {
|
|
215
|
+
let base = baseType.kind === "named" ? baseType.underlying : baseType;
|
|
216
|
+
base = this.resolveType(base);
|
|
217
|
+
if (this._isStructOrNamedMethod(baseType, base, expr.field))
|
|
218
|
+
expr._isMethodValue = true;
|
|
219
|
+
}
|
|
220
|
+
return ft;
|
|
221
|
+
},
|
|
229
222
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
223
|
+
_checkArrayBoundsExpr(btu, bt, expr) {
|
|
224
|
+
if (btu.kind !== "array" || btu.size == null) return;
|
|
225
|
+
const idx = this._constIntValue(expr.index);
|
|
226
|
+
if (idx === null) return;
|
|
227
|
+
if (idx < 0)
|
|
228
|
+
this.err(`invalid array index ${idx} (index must not be negative)`, expr);
|
|
229
|
+
else if (idx >= btu.size)
|
|
230
|
+
this.err(
|
|
231
|
+
`invalid array index ${idx} (out of bounds for ${typeStr(bt)})`,
|
|
232
|
+
expr,
|
|
233
|
+
);
|
|
234
|
+
},
|
|
234
235
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
// Slice → array conversion: [N]T(slice)
|
|
259
|
-
if (tgtResolved?.kind === "array" && srcResolved?.kind === "slice") {
|
|
260
|
-
const tgtElem = tgtResolved.elem;
|
|
261
|
-
const srcElem = srcResolved.elem;
|
|
262
|
-
if (
|
|
263
|
-
tgtElem &&
|
|
264
|
-
srcElem &&
|
|
265
|
-
!isAny(tgtElem) &&
|
|
266
|
-
!isAny(srcElem) &&
|
|
267
|
-
typeStr(tgtElem) !== typeStr(srcElem)
|
|
268
|
-
) {
|
|
269
|
-
this.err(
|
|
270
|
-
`cannot convert ${typeStr(srcType)} to ${typeStr(targetType)}`,
|
|
271
|
-
expr,
|
|
272
|
-
);
|
|
273
|
-
}
|
|
274
|
-
return targetType;
|
|
275
|
-
}
|
|
276
|
-
// Array → slice conversion: []T(array)
|
|
277
|
-
if (tgtResolved?.kind === "slice" && srcResolved?.kind === "array") {
|
|
278
|
-
const tgtElem = tgtResolved.elem;
|
|
279
|
-
const srcElem = srcResolved.elem;
|
|
280
|
-
if (
|
|
281
|
-
tgtElem &&
|
|
282
|
-
srcElem &&
|
|
283
|
-
!isAny(tgtElem) &&
|
|
284
|
-
!isAny(srcElem) &&
|
|
285
|
-
typeStr(tgtElem) !== typeStr(srcElem)
|
|
286
|
-
) {
|
|
287
|
-
this.err(
|
|
288
|
-
`cannot convert ${typeStr(srcType)} to ${typeStr(targetType)}`,
|
|
289
|
-
expr,
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
return targetType;
|
|
293
|
-
}
|
|
294
|
-
// Reject conversions between array and incompatible types
|
|
295
|
-
if (tgtResolved?.kind === "array" && srcResolved?.kind !== "array") {
|
|
296
|
-
if (!isAny(srcType)) {
|
|
297
|
-
this.err(
|
|
298
|
-
`cannot convert ${typeStr(srcType)} to ${typeStr(targetType)}`,
|
|
299
|
-
expr,
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
return targetType;
|
|
304
|
-
}
|
|
236
|
+
_checkIndexExpr(expr, scope) {
|
|
237
|
+
const bt = this.checkExpr(expr.expr, scope);
|
|
238
|
+
this.checkExpr(expr.index, scope);
|
|
239
|
+
if (bt?._tainted) return TAINTED_ANY;
|
|
240
|
+
if (isAny(bt)) return ANY;
|
|
241
|
+
const btu =
|
|
242
|
+
bt.kind === "named"
|
|
243
|
+
? (this.resolveType(bt.underlying) ?? bt.underlying)
|
|
244
|
+
: bt;
|
|
245
|
+
if (btu.kind === "slice" || btu.kind === "array") {
|
|
246
|
+
this._checkArrayBoundsExpr(btu, bt, expr);
|
|
247
|
+
return btu.elem;
|
|
248
|
+
}
|
|
249
|
+
if (btu.kind === "map") {
|
|
250
|
+
expr._mapValueType = btu.value;
|
|
251
|
+
return btu.value;
|
|
252
|
+
}
|
|
253
|
+
if (isString(bt)) return INT;
|
|
254
|
+
return this.err(`Cannot index type ${typeStr(bt)}`, expr);
|
|
255
|
+
},
|
|
305
256
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
`invalid type assertion: ${typeStr(srcType)} is not an interface`,
|
|
319
|
-
expr,
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
257
|
+
_checkSliceExpr(expr, scope) {
|
|
258
|
+
const bt = this.checkExpr(expr.expr, scope);
|
|
259
|
+
if (expr.low) this.checkExpr(expr.low, scope);
|
|
260
|
+
if (expr.high) this.checkExpr(expr.high, scope);
|
|
261
|
+
if (expr.max) this.checkExpr(expr.max, scope);
|
|
262
|
+
if (bt?._tainted) return TAINTED_ANY;
|
|
263
|
+
if (isAny(bt)) return ANY;
|
|
264
|
+
if (bt.kind === "slice" || bt.kind === "array")
|
|
265
|
+
return { kind: "slice", elem: bt.elem };
|
|
266
|
+
if (isString(bt)) return STRING;
|
|
267
|
+
return this.err(`Cannot slice type ${typeStr(bt)}`, expr);
|
|
268
|
+
},
|
|
323
269
|
|
|
324
|
-
|
|
325
|
-
|
|
270
|
+
_checkFuncLit(expr, scope) {
|
|
271
|
+
const inner = new Scope(scope);
|
|
272
|
+
for (const p of expr.params)
|
|
273
|
+
inner.define(p.name, p.type ? this.resolveTypeNode(p.type, scope) : ANY);
|
|
274
|
+
const ret = expr.returnType
|
|
275
|
+
? this.resolveTypeNode(expr.returnType, scope)
|
|
276
|
+
: VOID;
|
|
277
|
+
const savedDefer = this._deferCount;
|
|
278
|
+
this._deferCount = 0;
|
|
279
|
+
this.checkBlock(expr.body, inner, ret);
|
|
280
|
+
this._reportUnused(inner, expr);
|
|
281
|
+
if (this._deferCount > 0) expr.body._hasDefer = true;
|
|
282
|
+
this._deferCount = savedDefer;
|
|
283
|
+
const paramTypes = expr.params.map((p) =>
|
|
284
|
+
p.type ? this.resolveTypeNode(p.type, scope) : ANY,
|
|
285
|
+
);
|
|
286
|
+
return {
|
|
287
|
+
kind: "func",
|
|
288
|
+
params: paramTypes,
|
|
289
|
+
returns: [ret],
|
|
290
|
+
async: expr.async,
|
|
291
|
+
};
|
|
292
|
+
},
|
|
326
293
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
if (resolved?.kind === "slice" || resolved?.kind === "array") {
|
|
342
|
-
return { kind: "tuple", types: [INT, resolved.elem ?? ANY] };
|
|
343
|
-
}
|
|
344
|
-
if (resolved?.kind === "map") {
|
|
345
|
-
return {
|
|
346
|
-
kind: "tuple",
|
|
347
|
-
types: [resolved.key ?? ANY, resolved.value ?? ANY],
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
// integer range or unknown — return single-element tuple
|
|
351
|
-
return { kind: "tuple", types: [collType] };
|
|
352
|
-
}
|
|
294
|
+
_checkElemTypeCompatible(tgtElem, srcElem, srcType, targetType, expr) {
|
|
295
|
+
if (
|
|
296
|
+
tgtElem &&
|
|
297
|
+
srcElem &&
|
|
298
|
+
!isAny(tgtElem) &&
|
|
299
|
+
!isAny(srcElem) &&
|
|
300
|
+
typeStr(tgtElem) !== typeStr(srcElem)
|
|
301
|
+
)
|
|
302
|
+
this.err(
|
|
303
|
+
`cannot convert ${typeStr(srcType)} to ${typeStr(targetType)}`,
|
|
304
|
+
expr,
|
|
305
|
+
);
|
|
306
|
+
},
|
|
353
307
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
}
|
|
375
|
-
|
|
308
|
+
_isMutualArraySlice(tgt, src) {
|
|
309
|
+
return (
|
|
310
|
+
(tgt?.kind === "array" && src?.kind === "slice") ||
|
|
311
|
+
(tgt?.kind === "slice" && src?.kind === "array")
|
|
312
|
+
);
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
_isInvalidArrayConv(tgt, src, srcType) {
|
|
316
|
+
return tgt?.kind === "array" && src?.kind !== "array" && !isAny(srcType);
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
_checkTypeConversion(expr, scope) {
|
|
320
|
+
const srcType = this.checkExpr(expr.expr, scope);
|
|
321
|
+
const targetType = this.resolveTypeNode(expr.targetType, scope);
|
|
322
|
+
const srcResolved =
|
|
323
|
+
srcType?.kind === "named" ? srcType.underlying : srcType;
|
|
324
|
+
const tgtResolved =
|
|
325
|
+
targetType?.kind === "named" ? targetType.underlying : targetType;
|
|
326
|
+
if (isNumeric(targetType) && isComplex(srcType)) {
|
|
327
|
+
this.err(
|
|
328
|
+
`cannot convert ${typeStr(srcType)} to ${typeStr(targetType)} (use real() or imag())`,
|
|
329
|
+
expr,
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
if (
|
|
333
|
+
isComplex(targetType) &&
|
|
334
|
+
!isComplexOrNumeric(srcType) &&
|
|
335
|
+
!isAny(srcType)
|
|
336
|
+
) {
|
|
337
|
+
this.err(
|
|
338
|
+
`cannot convert ${typeStr(srcType)} to ${typeStr(targetType)}`,
|
|
339
|
+
expr,
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
if (this._isMutualArraySlice(tgtResolved, srcResolved)) {
|
|
343
|
+
this._checkElemTypeCompatible(
|
|
344
|
+
tgtResolved.elem,
|
|
345
|
+
srcResolved.elem,
|
|
346
|
+
srcType,
|
|
347
|
+
targetType,
|
|
348
|
+
expr,
|
|
349
|
+
);
|
|
350
|
+
return targetType;
|
|
351
|
+
}
|
|
352
|
+
if (this._isInvalidArrayConv(tgtResolved, srcResolved, srcType)) {
|
|
353
|
+
this.err(
|
|
354
|
+
`cannot convert ${typeStr(srcType)} to ${typeStr(targetType)}`,
|
|
355
|
+
expr,
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
return targetType;
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
_checkTypeAssertExpr(expr, scope) {
|
|
362
|
+
const srcType = this.checkExpr(expr.expr, scope);
|
|
363
|
+
const targetType = this.resolveTypeNode(expr.type, scope);
|
|
364
|
+
|
|
365
|
+
// In Go, the source of a type assertion must be an interface type.
|
|
366
|
+
// We allow `any` (which is GoFront's basic any) and interface types.
|
|
367
|
+
if (srcType && !isAny(srcType)) {
|
|
368
|
+
let underlying = srcType.kind === "named" ? srcType.underlying : srcType;
|
|
369
|
+
underlying = this.resolveType(underlying);
|
|
370
|
+
if (underlying?.kind !== "interface") {
|
|
371
|
+
this.err(
|
|
372
|
+
`invalid type assertion: ${typeStr(srcType)} is not an interface`,
|
|
373
|
+
expr,
|
|
374
|
+
);
|
|
376
375
|
}
|
|
376
|
+
}
|
|
377
377
|
|
|
378
|
-
|
|
379
|
-
|
|
378
|
+
return targetType;
|
|
379
|
+
},
|
|
380
|
+
|
|
381
|
+
_rangeCollTypeTuple(resolved, collType) {
|
|
382
|
+
if (isString(resolved)) return { kind: "tuple", types: [INT, INT] };
|
|
383
|
+
if (resolved?.kind === "slice" || resolved?.kind === "array")
|
|
384
|
+
return { kind: "tuple", types: [INT, resolved.elem ?? ANY] };
|
|
385
|
+
if (resolved?.kind === "map")
|
|
386
|
+
return {
|
|
387
|
+
kind: "tuple",
|
|
388
|
+
types: [resolved.key ?? ANY, resolved.value ?? ANY],
|
|
389
|
+
};
|
|
390
|
+
return { kind: "tuple", types: [collType] };
|
|
391
|
+
},
|
|
392
|
+
|
|
393
|
+
_checkRangeExpr(expr, scope) {
|
|
394
|
+
const collType = this.checkExpr(expr.expr, scope);
|
|
395
|
+
const resolved =
|
|
396
|
+
collType?.kind === "named" ? collType.underlying : collType;
|
|
397
|
+
return this._rangeCollTypeTuple(resolved, collType);
|
|
398
|
+
},
|
|
399
|
+
|
|
400
|
+
_checkInstantiationExpr(expr, scope) {
|
|
401
|
+
// Generic instantiation: Foo[int] or Foo[int, string]
|
|
402
|
+
const baseType = this.checkExpr(expr.expr, scope);
|
|
403
|
+
if (baseType?.kind === "generic") {
|
|
404
|
+
const typeArgs = expr.typeArgs.map((ta) =>
|
|
405
|
+
this.resolveTypeNode(ta, scope),
|
|
406
|
+
);
|
|
407
|
+
// Check constraints
|
|
408
|
+
for (
|
|
409
|
+
let i = 0;
|
|
410
|
+
i < baseType.typeParams.length && i < typeArgs.length;
|
|
411
|
+
i++
|
|
412
|
+
) {
|
|
413
|
+
const tp = baseType.typeParams[i];
|
|
414
|
+
const constraint = tp.constraint
|
|
415
|
+
? this.resolveTypeNode(tp.constraint, scope)
|
|
416
|
+
: null;
|
|
417
|
+
if (constraint) this.checkConstraint(typeArgs[i], constraint, expr);
|
|
418
|
+
}
|
|
419
|
+
return this.instantiateGenericFunc(baseType, typeArgs);
|
|
380
420
|
}
|
|
381
|
-
return
|
|
421
|
+
return baseType;
|
|
382
422
|
},
|
|
383
423
|
|
|
384
424
|
_constIntValue(expr) {
|
|
@@ -395,11 +435,36 @@ export const expressionCheckMethods = {
|
|
|
395
435
|
return null;
|
|
396
436
|
},
|
|
397
437
|
|
|
438
|
+
_resolveCallArgTypes(expr, scope) {
|
|
439
|
+
if (expr.args.length === 1 && expr.args[0].kind === "CallExpr") {
|
|
440
|
+
const argType = this.checkExpr(expr.args[0], scope);
|
|
441
|
+
if (argType?.kind === "tuple") {
|
|
442
|
+
expr._multiForward = true;
|
|
443
|
+
return argType.types;
|
|
444
|
+
}
|
|
445
|
+
return [argType];
|
|
446
|
+
}
|
|
447
|
+
return expr.args.map((a) => this.checkExpr(a, scope));
|
|
448
|
+
},
|
|
449
|
+
|
|
450
|
+
_resolveCallFnType(fnType, argTypes, expr, scope) {
|
|
451
|
+
if (fnType.kind === "named" && fnType.underlying?.kind === "func")
|
|
452
|
+
fnType = fnType.underlying;
|
|
453
|
+
if (fnType.kind === "generic") {
|
|
454
|
+
const resolved = this._resolveGenericFnType(
|
|
455
|
+
fnType,
|
|
456
|
+
argTypes,
|
|
457
|
+
expr,
|
|
458
|
+
scope,
|
|
459
|
+
);
|
|
460
|
+
if (!resolved) return null;
|
|
461
|
+
fnType = resolved;
|
|
462
|
+
}
|
|
463
|
+
return fnType;
|
|
464
|
+
},
|
|
465
|
+
|
|
398
466
|
checkCall(expr, scope) {
|
|
399
467
|
let fnType = this.checkExpr(expr.func, scope);
|
|
400
|
-
|
|
401
|
-
// Handle built-ins before evaluating args; some (e.g. new) take type names as
|
|
402
|
-
// arguments, which would otherwise produce false "Undefined" errors.
|
|
403
468
|
if (fnType.kind === "builtin") {
|
|
404
469
|
const argTypes = expr.args.map((a, i) => {
|
|
405
470
|
// new(T) — first arg is a type name, not a value expression
|
|
@@ -409,21 +474,9 @@ export const expressionCheckMethods = {
|
|
|
409
474
|
return this.checkBuiltin(fnType.name, expr, argTypes, scope);
|
|
410
475
|
}
|
|
411
476
|
|
|
412
|
-
|
|
413
|
-
// When a single argument is a multi-return call, flatten its tuple into args.
|
|
414
|
-
let argTypes;
|
|
415
|
-
if (expr.args.length === 1 && expr.args[0].kind === "CallExpr") {
|
|
416
|
-
const argType = this.checkExpr(expr.args[0], scope);
|
|
417
|
-
if (argType?.kind === "tuple") {
|
|
418
|
-
expr._multiForward = true; // flag for codegen
|
|
419
|
-
argTypes = argType.types;
|
|
420
|
-
} else {
|
|
421
|
-
argTypes = [argType];
|
|
422
|
-
}
|
|
423
|
-
} else {
|
|
424
|
-
argTypes = expr.args.map((a) => this.checkExpr(a, scope));
|
|
425
|
-
}
|
|
477
|
+
const argTypes = this._resolveCallArgTypes(expr, scope);
|
|
426
478
|
|
|
479
|
+
if (fnType?._tainted) return TAINTED_ANY;
|
|
427
480
|
if (isAny(fnType)) return ANY;
|
|
428
481
|
|
|
429
482
|
// Named type used as a constructor/conversion: Greeter(fn), NodeFunc(fn)
|
|
@@ -434,300 +487,266 @@ export const expressionCheckMethods = {
|
|
|
434
487
|
return fnType;
|
|
435
488
|
}
|
|
436
489
|
|
|
437
|
-
|
|
438
|
-
if (fnType
|
|
439
|
-
|
|
490
|
+
fnType = this._resolveCallFnType(fnType, argTypes, expr, scope);
|
|
491
|
+
if (!fnType) return ANY;
|
|
492
|
+
if (fnType.kind !== "func") {
|
|
493
|
+
return this.err(`Cannot call non-function type ${typeStr(fnType)}`, expr);
|
|
440
494
|
}
|
|
441
495
|
|
|
442
|
-
|
|
443
|
-
if (fnType.kind === "generic") {
|
|
444
|
-
const typeArgs = this.inferTypeArgs(fnType, argTypes);
|
|
445
|
-
if (!typeArgs) {
|
|
446
|
-
return this.err(
|
|
447
|
-
`Cannot infer type arguments for generic function '${fnType.name}'`,
|
|
448
|
-
expr,
|
|
449
|
-
);
|
|
450
|
-
}
|
|
451
|
-
// Check constraints
|
|
452
|
-
for (
|
|
453
|
-
let i = 0;
|
|
454
|
-
i < fnType.typeParams.length && i < typeArgs.length;
|
|
455
|
-
i++
|
|
456
|
-
) {
|
|
457
|
-
const tp = fnType.typeParams[i];
|
|
458
|
-
const constraint = tp.constraint
|
|
459
|
-
? this.resolveTypeNode(tp.constraint, scope)
|
|
460
|
-
: null;
|
|
461
|
-
if (constraint) this.checkConstraint(typeArgs[i], constraint, expr);
|
|
462
|
-
}
|
|
463
|
-
fnType = this.instantiateGenericFunc(fnType, typeArgs);
|
|
464
|
-
}
|
|
496
|
+
this._checkCallArgs(fnType, argTypes, expr);
|
|
465
497
|
|
|
466
|
-
|
|
467
|
-
|
|
498
|
+
const ret = fnType.returns;
|
|
499
|
+
if (!ret || ret.length === 0) return VOID;
|
|
500
|
+
if (ret.length === 1) return ret[0];
|
|
501
|
+
return { kind: "tuple", types: ret };
|
|
502
|
+
},
|
|
503
|
+
|
|
504
|
+
_resolveGenericFnType(fnType, argTypes, expr, scope) {
|
|
505
|
+
const typeArgs = this.inferTypeArgs(fnType, argTypes);
|
|
506
|
+
if (!typeArgs) {
|
|
507
|
+
this.err(
|
|
508
|
+
`Cannot infer type arguments for generic function '${fnType.name}'`,
|
|
509
|
+
expr,
|
|
510
|
+
);
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
513
|
+
// Check constraints
|
|
514
|
+
for (let i = 0; i < fnType.typeParams.length && i < typeArgs.length; i++) {
|
|
515
|
+
const tp = fnType.typeParams[i];
|
|
516
|
+
const constraint = tp.constraint
|
|
517
|
+
? this.resolveTypeNode(tp.constraint, scope)
|
|
518
|
+
: null;
|
|
519
|
+
if (constraint) this.checkConstraint(typeArgs[i], constraint, expr);
|
|
468
520
|
}
|
|
521
|
+
return this.instantiateGenericFunc(fnType, typeArgs);
|
|
522
|
+
},
|
|
469
523
|
|
|
470
|
-
|
|
524
|
+
_checkArgCount(fnType, argTypes, expr) {
|
|
471
525
|
const minArgs = fnType.params.filter(
|
|
472
526
|
(_, i) => !fnType.variadic || i < fnType.params.length - 1,
|
|
473
527
|
).length;
|
|
474
|
-
if (argTypes.length < minArgs)
|
|
528
|
+
if (argTypes.length < minArgs)
|
|
475
529
|
this.err(
|
|
476
530
|
`Too few arguments: expected ${fnType.params.length}, got ${argTypes.length}`,
|
|
477
531
|
expr,
|
|
478
532
|
);
|
|
479
|
-
|
|
480
|
-
if (!fnType.variadic && argTypes.length > fnType.params.length) {
|
|
533
|
+
if (!fnType.variadic && argTypes.length > fnType.params.length)
|
|
481
534
|
this.err(
|
|
482
535
|
`Too many arguments: expected ${fnType.params.length}, got ${argTypes.length}`,
|
|
483
536
|
expr,
|
|
484
537
|
);
|
|
485
|
-
|
|
538
|
+
},
|
|
486
539
|
|
|
487
|
-
|
|
540
|
+
_checkCallArgs(fnType, argTypes, expr) {
|
|
541
|
+
this._checkArgCount(fnType, argTypes, expr);
|
|
488
542
|
for (let i = 0; i < fnType.params.length && i < argTypes.length; i++) {
|
|
489
543
|
const paramIdx =
|
|
490
544
|
fnType.variadic && i >= fnType.params.length - 1
|
|
491
545
|
? fnType.params.length - 1
|
|
492
546
|
: i;
|
|
493
|
-
// Spread arg (f(slice...)) passes a whole slice into a variadic — skip per-element check
|
|
494
547
|
if (expr.args[i]?._spread) continue;
|
|
495
548
|
this.assertAssignable(fnType.params[paramIdx], argTypes[i], expr.args[i]);
|
|
496
549
|
}
|
|
497
|
-
|
|
498
|
-
const ret = fnType.returns;
|
|
499
|
-
if (!ret || ret.length === 0) return VOID;
|
|
500
|
-
if (ret.length === 1) return ret[0];
|
|
501
|
-
return { kind: "tuple", types: ret };
|
|
502
550
|
},
|
|
503
551
|
|
|
504
552
|
checkBuiltin(name, expr, argTypes, scope) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
case "max":
|
|
558
|
-
return argTypes[0] ?? ANY;
|
|
559
|
-
case "clear":
|
|
560
|
-
return VOID;
|
|
561
|
-
case "complex": {
|
|
562
|
-
if (expr.args.length !== 2) {
|
|
563
|
-
this.err("complex() requires exactly 2 arguments", expr);
|
|
564
|
-
return ANY;
|
|
565
|
-
}
|
|
566
|
-
const crt = argTypes[0];
|
|
567
|
-
const cit = argTypes[1];
|
|
568
|
-
if (!isNumeric(crt)) {
|
|
569
|
-
this.err(`cannot use ${typeStr(crt)} as float in complex()`, expr);
|
|
570
|
-
return ANY;
|
|
571
|
-
}
|
|
572
|
-
if (!isNumeric(cit)) {
|
|
573
|
-
this.err(`cannot use ${typeStr(cit)} as float in complex()`, expr);
|
|
574
|
-
return ANY;
|
|
575
|
-
}
|
|
576
|
-
if (crt.kind === "untyped" && cit.kind === "untyped")
|
|
577
|
-
return UNTYPED_COMPLEX;
|
|
578
|
-
return COMPLEX128;
|
|
579
|
-
}
|
|
580
|
-
case "real":
|
|
581
|
-
case "imag": {
|
|
582
|
-
if (expr.args.length !== 1) {
|
|
583
|
-
this.err(`${name}() requires exactly 1 argument`, expr);
|
|
584
|
-
return ANY;
|
|
585
|
-
}
|
|
586
|
-
const zt = argTypes[0];
|
|
587
|
-
if (!isComplex(zt)) {
|
|
588
|
-
this.err(`cannot use ${typeStr(zt)} as complex in ${name}()`, expr);
|
|
589
|
-
return ANY;
|
|
590
|
-
}
|
|
591
|
-
if (zt.kind === "untyped") return UNTYPED_FLOAT;
|
|
592
|
-
return FLOAT64;
|
|
593
|
-
}
|
|
594
|
-
default:
|
|
595
|
-
return ANY;
|
|
553
|
+
const gen = BUILTIN_CHECK[name];
|
|
554
|
+
return gen ? gen(this, name, expr, argTypes, scope) : ANY;
|
|
555
|
+
},
|
|
556
|
+
|
|
557
|
+
_constLenFromType(arg0) {
|
|
558
|
+
if (arg0?.kind === "array" && arg0.size != null) return arg0.size;
|
|
559
|
+
if (
|
|
560
|
+
arg0?.kind === "named" &&
|
|
561
|
+
arg0.underlying?.kind === "array" &&
|
|
562
|
+
arg0.underlying.size != null
|
|
563
|
+
)
|
|
564
|
+
return arg0.underlying.size;
|
|
565
|
+
return null;
|
|
566
|
+
},
|
|
567
|
+
|
|
568
|
+
_checkBuiltinLen(name, expr, argTypes) {
|
|
569
|
+
if (name === "len") {
|
|
570
|
+
const size = this._constLenFromType(argTypes[0]);
|
|
571
|
+
if (size != null) expr._constLen = size;
|
|
572
|
+
}
|
|
573
|
+
return INT;
|
|
574
|
+
},
|
|
575
|
+
|
|
576
|
+
_checkBuiltinComplex(expr, argTypes) {
|
|
577
|
+
if (expr.args.length !== 2) {
|
|
578
|
+
this.err("complex() requires exactly 2 arguments", expr);
|
|
579
|
+
return ANY;
|
|
580
|
+
}
|
|
581
|
+
const crt = argTypes[0];
|
|
582
|
+
const cit = argTypes[1];
|
|
583
|
+
if (!isNumeric(crt)) {
|
|
584
|
+
this.err(`cannot use ${typeStr(crt)} as float in complex()`, expr);
|
|
585
|
+
return ANY;
|
|
586
|
+
}
|
|
587
|
+
if (!isNumeric(cit)) {
|
|
588
|
+
this.err(`cannot use ${typeStr(cit)} as float in complex()`, expr);
|
|
589
|
+
return ANY;
|
|
590
|
+
}
|
|
591
|
+
if (crt.kind === "untyped" && cit.kind === "untyped")
|
|
592
|
+
return UNTYPED_COMPLEX;
|
|
593
|
+
return COMPLEX128;
|
|
594
|
+
},
|
|
595
|
+
|
|
596
|
+
_checkBuiltinRealImag(name, expr, argTypes) {
|
|
597
|
+
if (expr.args.length !== 1) {
|
|
598
|
+
this.err(`${name}() requires exactly 1 argument`, expr);
|
|
599
|
+
return ANY;
|
|
600
|
+
}
|
|
601
|
+
const zt = argTypes[0];
|
|
602
|
+
if (!isComplex(zt)) {
|
|
603
|
+
this.err(`cannot use ${typeStr(zt)} as complex in ${name}()`, expr);
|
|
604
|
+
return ANY;
|
|
596
605
|
}
|
|
606
|
+
if (zt.kind === "untyped") return UNTYPED_FLOAT;
|
|
607
|
+
return FLOAT64;
|
|
608
|
+
},
|
|
609
|
+
|
|
610
|
+
_inferArraySize(expr, typeNode, base) {
|
|
611
|
+
if (
|
|
612
|
+
typeNode?.kind === "ArrayType" &&
|
|
613
|
+
typeNode.inferLen &&
|
|
614
|
+
base?.kind === "array"
|
|
615
|
+
)
|
|
616
|
+
base.size = computeMaxIndex(expr.elems) + 1;
|
|
617
|
+
},
|
|
618
|
+
|
|
619
|
+
_isSliceOrArray(base) {
|
|
620
|
+
return base?.kind === "slice" || base?.kind === "array";
|
|
621
|
+
},
|
|
622
|
+
|
|
623
|
+
_checkUnknownCompositeLit(expr, scope, t) {
|
|
624
|
+
for (const elem of expr.elems) {
|
|
625
|
+
if (elem.kind === "KeyValueExpr") this.checkExpr(elem.value, scope);
|
|
626
|
+
else this.checkExpr(elem, scope);
|
|
627
|
+
}
|
|
628
|
+
return t ?? ANY;
|
|
597
629
|
},
|
|
598
630
|
|
|
599
631
|
checkCompositeLit(expr, scope, hintType = null) {
|
|
600
|
-
// null typeExpr = implicit lit inside a slice/map: {X:1} in []Point{{X:1}}
|
|
601
632
|
const t =
|
|
602
633
|
expr.typeExpr === null
|
|
603
634
|
? (hintType ?? ANY)
|
|
604
635
|
: this.resolveTypeNode(expr.typeExpr, scope);
|
|
605
636
|
const base = t.kind === "named" ? t.underlying : t;
|
|
606
|
-
|
|
607
|
-
// [...]T size inference: count elements and set size
|
|
608
637
|
const typeNode = expr.typeExpr;
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
}
|
|
638
|
+
this._inferArraySize(expr, typeNode, base);
|
|
639
|
+
if (base?.kind === "struct")
|
|
640
|
+
return this._checkStructCompositeLit(expr, scope, t, base);
|
|
641
|
+
if (this._isSliceOrArray(base))
|
|
642
|
+
return this._checkSliceArrayCompositeLit(expr, scope, t, base, typeNode);
|
|
643
|
+
if (base?.kind === "map")
|
|
644
|
+
return this._checkMapCompositeLit(expr, scope, t, base);
|
|
645
|
+
return this._checkUnknownCompositeLit(expr, scope, t);
|
|
646
|
+
},
|
|
619
647
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
// Map positional elements to struct fields by declaration order
|
|
629
|
-
const fieldNames = [...(base.fields?.keys() ?? [])];
|
|
630
|
-
for (let i = 0; i < expr.elems.length; i++) {
|
|
631
|
-
const elem = expr.elems[i];
|
|
632
|
-
const fieldName = fieldNames[i];
|
|
633
|
-
if (!fieldName) {
|
|
634
|
-
this.err("too many values in struct literal", elem);
|
|
635
|
-
continue;
|
|
636
|
-
}
|
|
637
|
-
const fieldType = base.fields.get(fieldName);
|
|
638
|
-
const vt = this.checkExpr(elem, scope);
|
|
639
|
-
if (fieldType) this.assertAssignable(fieldType, vt, elem);
|
|
640
|
-
// Annotate for codegen
|
|
641
|
-
elem._positionalField = fieldName;
|
|
642
|
-
}
|
|
643
|
-
return t;
|
|
648
|
+
_checkStructPositionalElems(expr, scope, t, base) {
|
|
649
|
+
const fieldNames = [...(base.fields?.keys() ?? [])];
|
|
650
|
+
for (let i = 0; i < expr.elems.length; i++) {
|
|
651
|
+
const elem = expr.elems[i];
|
|
652
|
+
const fieldName = fieldNames[i];
|
|
653
|
+
if (!fieldName) {
|
|
654
|
+
this.err("too many values in struct literal", elem);
|
|
655
|
+
continue;
|
|
644
656
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
657
|
+
const fieldType = base.fields.get(fieldName);
|
|
658
|
+
const vt = this.checkExpr(elem, scope);
|
|
659
|
+
if (fieldType) this.assertAssignable(fieldType, vt, elem);
|
|
660
|
+
elem._positionalField = fieldName;
|
|
661
|
+
}
|
|
662
|
+
return t;
|
|
663
|
+
},
|
|
664
|
+
|
|
665
|
+
_checkStructKeyedElems(expr, scope, t, base) {
|
|
666
|
+
for (const elem of expr.elems) {
|
|
667
|
+
if (elem.kind === "KeyValueExpr") {
|
|
668
|
+
const keyName = elem.key.name;
|
|
669
|
+
const fieldType = base.fields?.get(keyName);
|
|
670
|
+
if (fieldType) {
|
|
671
|
+
const vt = this.checkExpr(elem.value, scope);
|
|
672
|
+
this.assertAssignable(fieldType, vt, elem.value);
|
|
673
|
+
} else {
|
|
674
|
+
const embed = base._embeds?.find(
|
|
675
|
+
(e) => (e.kind === "named" ? e.name : null) === keyName,
|
|
676
|
+
);
|
|
677
|
+
if (embed) {
|
|
650
678
|
const vt = this.checkExpr(elem.value, scope);
|
|
651
|
-
this.assertAssignable(
|
|
679
|
+
this.assertAssignable(embed, vt, elem.value);
|
|
680
|
+
elem._isEmbedInit = true;
|
|
652
681
|
} else {
|
|
653
|
-
|
|
654
|
-
const embed = base._embeds?.find(
|
|
655
|
-
(e) => (e.kind === "named" ? e.name : null) === keyName,
|
|
656
|
-
);
|
|
657
|
-
if (embed) {
|
|
658
|
-
const vt = this.checkExpr(elem.value, scope);
|
|
659
|
-
this.assertAssignable(embed, vt, elem.value);
|
|
660
|
-
elem._isEmbedInit = true;
|
|
661
|
-
} else {
|
|
662
|
-
this.err(`Unknown field '${keyName}'`, elem.key);
|
|
663
|
-
}
|
|
682
|
+
this.err(`Unknown field '${keyName}'`, elem.key);
|
|
664
683
|
}
|
|
665
|
-
} else {
|
|
666
|
-
this.checkExpr(elem, scope);
|
|
667
684
|
}
|
|
685
|
+
} else {
|
|
686
|
+
this.checkExpr(elem, scope);
|
|
668
687
|
}
|
|
669
|
-
return t;
|
|
670
688
|
}
|
|
689
|
+
return t;
|
|
690
|
+
},
|
|
671
691
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
const et = this.checkExpr(elem, scope);
|
|
682
|
-
this.assertAssignable(base.elem, et, elem);
|
|
683
|
-
}
|
|
684
|
-
}
|
|
692
|
+
_checkStructCompositeLit(expr, scope, t, base) {
|
|
693
|
+
const hasPositional = expr.elems.some((e) => e.kind !== "KeyValueExpr");
|
|
694
|
+
const hasKeyed = expr.elems.some((e) => e.kind === "KeyValueExpr");
|
|
695
|
+
if (hasPositional && hasKeyed)
|
|
696
|
+
this.err("mixture of field:value and value initializers", expr);
|
|
697
|
+
if (hasPositional)
|
|
698
|
+
return this._checkStructPositionalElems(expr, scope, t, base);
|
|
699
|
+
return this._checkStructKeyedElems(expr, scope, t, base);
|
|
700
|
+
},
|
|
685
701
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
702
|
+
_checkSliceArrayCompositeLit(expr, scope, t, base, typeNode) {
|
|
703
|
+
for (const elem of expr.elems) {
|
|
704
|
+
if (elem.kind === "CompositeLit" && elem.typeExpr === null) {
|
|
705
|
+
const et = this.checkCompositeLit(elem, scope, base.elem);
|
|
706
|
+
elem._type = et;
|
|
707
|
+
} else if (elem.kind === "KeyValueExpr") {
|
|
708
|
+
const vt = this.checkExpr(elem.value, scope);
|
|
709
|
+
this.assertAssignable(base.elem, vt, elem.value);
|
|
710
|
+
} else {
|
|
711
|
+
const et = this.checkExpr(elem, scope);
|
|
712
|
+
this.assertAssignable(base.elem, et, elem);
|
|
695
713
|
}
|
|
714
|
+
}
|
|
696
715
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
let vt;
|
|
706
|
-
if (
|
|
707
|
-
elem.value.kind === "CompositeLit" &&
|
|
708
|
-
elem.value.typeExpr === null
|
|
709
|
-
) {
|
|
710
|
-
vt = this.checkCompositeLit(elem.value, scope, base.value);
|
|
711
|
-
elem.value._type = vt;
|
|
712
|
-
} else {
|
|
713
|
-
vt = this.checkExpr(elem.value, scope);
|
|
714
|
-
}
|
|
715
|
-
this.assertAssignable(base.key, kt, elem.key);
|
|
716
|
-
this.assertAssignable(base.value, vt, elem.value);
|
|
717
|
-
}
|
|
716
|
+
// Composite literal element count validation for explicit-size arrays
|
|
717
|
+
if (base.kind === "array" && base.size != null && !typeNode?.inferLen) {
|
|
718
|
+
const maxIndex = computeMaxIndex(expr.elems);
|
|
719
|
+
if (maxIndex >= base.size) {
|
|
720
|
+
this.err(
|
|
721
|
+
`array index ${maxIndex} out of bounds [0:${base.size}]`,
|
|
722
|
+
expr,
|
|
723
|
+
);
|
|
718
724
|
}
|
|
719
|
-
return t;
|
|
720
725
|
}
|
|
721
726
|
|
|
722
|
-
|
|
727
|
+
return t;
|
|
728
|
+
},
|
|
729
|
+
|
|
730
|
+
_checkMapCompositeLit(expr, scope, t, base) {
|
|
723
731
|
for (const elem of expr.elems) {
|
|
724
732
|
if (elem.kind === "KeyValueExpr") {
|
|
725
|
-
this.checkExpr(elem.
|
|
726
|
-
|
|
727
|
-
|
|
733
|
+
const kt = this.checkExpr(elem.key, scope);
|
|
734
|
+
// value may be an implicit composite lit
|
|
735
|
+
let vt;
|
|
736
|
+
if (
|
|
737
|
+
elem.value.kind === "CompositeLit" &&
|
|
738
|
+
elem.value.typeExpr === null
|
|
739
|
+
) {
|
|
740
|
+
vt = this.checkCompositeLit(elem.value, scope, base.value);
|
|
741
|
+
elem.value._type = vt;
|
|
742
|
+
} else {
|
|
743
|
+
vt = this.checkExpr(elem.value, scope);
|
|
744
|
+
}
|
|
745
|
+
this.assertAssignable(base.key, kt, elem.key);
|
|
746
|
+
this.assertAssignable(base.value, vt, elem.value);
|
|
728
747
|
}
|
|
729
748
|
}
|
|
730
|
-
return t
|
|
749
|
+
return t;
|
|
731
750
|
},
|
|
732
751
|
};
|
|
733
752
|
|