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
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
// Backend-independent evaluation of Go constant expressions.
|
|
2
|
+
//
|
|
3
|
+
// A constant value is `{ k, v, t, i }`:
|
|
4
|
+
// k — "INT" | "FLOAT" | "STRING" | "BOOL"
|
|
5
|
+
// v — BigInt (INT), Number (FLOAT), string or boolean
|
|
6
|
+
// t — basic type name of a typed constant ("int8", "float64", …); absent
|
|
7
|
+
// for untyped constants, which are exact (BigInt) and may exceed every
|
|
8
|
+
// Go type until they are converted or assigned
|
|
9
|
+
// i — for an untyped integral FLOAT, its exact BigInt value, so that
|
|
10
|
+
// `1e18 + 1` stays exact when it later becomes an integer
|
|
11
|
+
// r — for an untyped decimal FLOAT, its exact value `{ n, e }` = n * 10^-e
|
|
12
|
+
// (BigInt n), so `0.2 + 0.1` is exactly 0.3 before it rounds to float64
|
|
13
|
+
//
|
|
14
|
+
// The checker evaluates constant expressions with these helpers and attaches
|
|
15
|
+
// the result to the AST node (`_const`); both backends emit that value.
|
|
16
|
+
// `foldConstExpr` folds an AST directly (the WASM backend uses it for
|
|
17
|
+
// package-level constants).
|
|
18
|
+
|
|
19
|
+
const INT = "INT";
|
|
20
|
+
const FLOAT = "FLOAT";
|
|
21
|
+
const STRING = "STRING";
|
|
22
|
+
const BOOL = "BOOL";
|
|
23
|
+
|
|
24
|
+
// Shift counts beyond this are left to the runtime (Go allows larger shifts
|
|
25
|
+
// of untyped constants, but no program needs a 4096-bit intermediate).
|
|
26
|
+
const MAX_SHIFT = 4096n;
|
|
27
|
+
const MAX_DECIMAL_EXP = 400;
|
|
28
|
+
|
|
29
|
+
// Value ranges of the integer types.
|
|
30
|
+
export const INT_RANGES = {
|
|
31
|
+
int8: [-(2n ** 7n), 2n ** 7n - 1n],
|
|
32
|
+
int16: [-(2n ** 15n), 2n ** 15n - 1n],
|
|
33
|
+
int32: [-(2n ** 31n), 2n ** 31n - 1n],
|
|
34
|
+
rune: [-(2n ** 31n), 2n ** 31n - 1n],
|
|
35
|
+
int64: [-(2n ** 63n), 2n ** 63n - 1n],
|
|
36
|
+
int: [-(2n ** 63n), 2n ** 63n - 1n],
|
|
37
|
+
uint8: [0n, 2n ** 8n - 1n],
|
|
38
|
+
byte: [0n, 2n ** 8n - 1n],
|
|
39
|
+
uint16: [0n, 2n ** 16n - 1n],
|
|
40
|
+
uint32: [0n, 2n ** 32n - 1n],
|
|
41
|
+
uint64: [0n, 2n ** 64n - 1n],
|
|
42
|
+
uint: [0n, 2n ** 64n - 1n],
|
|
43
|
+
uintptr: [0n, 2n ** 64n - 1n],
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const FLOAT_TYPES = new Set(["float32", "float64"]);
|
|
47
|
+
|
|
48
|
+
export const intConst = (v) => ({ k: INT, v });
|
|
49
|
+
|
|
50
|
+
// Exact integer value of a decimal float literal (`1e18`, `2.50e1`), or null.
|
|
51
|
+
function exactIntOfFloatText(text) {
|
|
52
|
+
const m = /^(\d*)(?:\.(\d*))?(?:[eE]([+-]?\d+))?$/.exec(text);
|
|
53
|
+
if (!m) return null;
|
|
54
|
+
const frac = m[2] ?? "";
|
|
55
|
+
const digits = `${m[1]}${frac}` || "0";
|
|
56
|
+
const exp = Number(m[3] ?? 0) - frac.length;
|
|
57
|
+
if (exp > 400) return null;
|
|
58
|
+
if (exp >= 0) return BigInt(digits) * 10n ** BigInt(exp);
|
|
59
|
+
const cut = digits.length + exp;
|
|
60
|
+
if (!/^0*$/.test(digits.slice(Math.max(cut, 0)))) return null;
|
|
61
|
+
return cut > 0 ? BigInt(digits.slice(0, cut)) : 0n;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Exact decimal value `{ n, e }` (n * 10^-e) of a decimal float literal, or null.
|
|
65
|
+
function exactDecimalOfFloatText(text) {
|
|
66
|
+
const m = /^(\d*)(?:\.(\d*))?(?:[eE]([+-]?\d+))?$/.exec(text);
|
|
67
|
+
if (!m) return null;
|
|
68
|
+
const frac = m[2] ?? "";
|
|
69
|
+
const digits = `${m[1]}${frac}` || "0";
|
|
70
|
+
const e = frac.length - Number(m[3] ?? 0);
|
|
71
|
+
if (Math.abs(e) > MAX_DECIMAL_EXP) return null;
|
|
72
|
+
return e >= 0
|
|
73
|
+
? { n: BigInt(digits), e }
|
|
74
|
+
: { n: BigInt(digits) * 10n ** BigInt(-e), e: 0 };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Exact decimal value of an untyped INT or FLOAT constant, or null.
|
|
78
|
+
function exactDecimal(val) {
|
|
79
|
+
if (val.t) return null;
|
|
80
|
+
if (val.r) return val.r;
|
|
81
|
+
const i = constIntValue(val);
|
|
82
|
+
return i === null ? null : { n: i, e: 0 };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function decimalToFloat({ n, e }) {
|
|
86
|
+
return Number(`${n}e-${e}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function floatConstFromText(s) {
|
|
90
|
+
const n = Number(s);
|
|
91
|
+
if (!Number.isFinite(n)) return null;
|
|
92
|
+
const i = exactIntOfFloatText(s);
|
|
93
|
+
const r = exactDecimalOfFloatText(s);
|
|
94
|
+
const out = i === null ? { k: FLOAT, v: n } : { k: FLOAT, v: n, i };
|
|
95
|
+
return r === null ? out : { ...out, r };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function constFromLit(lit) {
|
|
99
|
+
switch (lit.litKind) {
|
|
100
|
+
case INT: {
|
|
101
|
+
const s = String(lit.value).replaceAll("_", "");
|
|
102
|
+
const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
|
|
103
|
+
return { k: INT, v: BigInt(clean) };
|
|
104
|
+
}
|
|
105
|
+
case FLOAT:
|
|
106
|
+
return floatConstFromText(String(lit.value).replaceAll("_", ""));
|
|
107
|
+
case STRING:
|
|
108
|
+
return { k: STRING, v: String(lit.value ?? "") };
|
|
109
|
+
case BOOL:
|
|
110
|
+
return { k: BOOL, v: lit.value === "true" || lit.value === true };
|
|
111
|
+
default:
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function constToLit(val) {
|
|
117
|
+
switch (val.k) {
|
|
118
|
+
case INT:
|
|
119
|
+
return { kind: "BasicLit", litKind: INT, value: val.v.toString() };
|
|
120
|
+
case FLOAT:
|
|
121
|
+
return {
|
|
122
|
+
kind: "BasicLit",
|
|
123
|
+
litKind: FLOAT,
|
|
124
|
+
value: Object.is(val.v, -0) ? "-0" : String(val.v),
|
|
125
|
+
};
|
|
126
|
+
case STRING:
|
|
127
|
+
return { kind: "BasicLit", litKind: STRING, value: val.v };
|
|
128
|
+
default:
|
|
129
|
+
return {
|
|
130
|
+
kind: "BasicLit",
|
|
131
|
+
litKind: BOOL,
|
|
132
|
+
value: val.v ? "true" : "false",
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Exact integer value of an INT or integral FLOAT, else null.
|
|
138
|
+
export function constIntValue(val) {
|
|
139
|
+
if (val?.k === INT) return val.v;
|
|
140
|
+
if (val?.k !== FLOAT) return null;
|
|
141
|
+
if (val.i !== undefined) return val.i;
|
|
142
|
+
return Number.isInteger(val.v) ? BigInt(val.v) : null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function asFloat(val) {
|
|
146
|
+
return val.k === INT ? Number(val.v) : val.v;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// An integer result typed `t` that does not fit is an overflow.
|
|
150
|
+
function typedInt(v, t) {
|
|
151
|
+
const range = t ? INT_RANGES[t] : null;
|
|
152
|
+
if (range && (v < range[0] || v > range[1]))
|
|
153
|
+
return { k: INT, v, t, overflow: true };
|
|
154
|
+
return t ? { k: INT, v, t } : { k: INT, v };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function typedFloat(v, t) {
|
|
158
|
+
if (!Number.isFinite(v)) return null;
|
|
159
|
+
const r = t === "float32" ? Math.fround(v) : v;
|
|
160
|
+
return t ? { k: FLOAT, v: r, t } : { k: FLOAT, v: r };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function negateFloat(x) {
|
|
164
|
+
const r = typedFloat(-x.v, x.t);
|
|
165
|
+
if (r && x.i !== undefined) r.i = -x.i;
|
|
166
|
+
if (r && x.r && !x.t) r.r = { n: -x.r.n, e: x.r.e };
|
|
167
|
+
return r;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function foldUnary(op, x) {
|
|
171
|
+
if (op === "+" && (x.k === INT || x.k === FLOAT)) return x;
|
|
172
|
+
if (op === "!" && x.k === BOOL) return { ...x, v: !x.v };
|
|
173
|
+
if (op === "-" && x.k === INT) return typedInt(-x.v, x.t);
|
|
174
|
+
if (op === "-" && x.k === FLOAT) {
|
|
175
|
+
return negateFloat(x);
|
|
176
|
+
}
|
|
177
|
+
if (op === "^" && x.k === INT) {
|
|
178
|
+
// Unsigned: flip the bits of the type's width; otherwise -x-1.
|
|
179
|
+
const range = x.t ? INT_RANGES[x.t] : null;
|
|
180
|
+
if (range && range[0] === 0n) return { k: INT, v: range[1] ^ x.v, t: x.t };
|
|
181
|
+
return typedInt(~x.v, x.t);
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function compare(op, a, b) {
|
|
187
|
+
switch (op) {
|
|
188
|
+
case "==":
|
|
189
|
+
return a === b;
|
|
190
|
+
case "!=":
|
|
191
|
+
return a !== b;
|
|
192
|
+
case "<":
|
|
193
|
+
return a < b;
|
|
194
|
+
case "<=":
|
|
195
|
+
return a <= b;
|
|
196
|
+
case ">":
|
|
197
|
+
return a > b;
|
|
198
|
+
case ">=":
|
|
199
|
+
return a >= b;
|
|
200
|
+
default:
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function foldInts(op, a, b, t) {
|
|
206
|
+
switch (op) {
|
|
207
|
+
case "+":
|
|
208
|
+
return typedInt(a + b, t);
|
|
209
|
+
case "-":
|
|
210
|
+
return typedInt(a - b, t);
|
|
211
|
+
case "*":
|
|
212
|
+
return typedInt(a * b, t);
|
|
213
|
+
case "/":
|
|
214
|
+
if (b === 0n) throw new Error("invalid operation: division by zero");
|
|
215
|
+
return typedInt(a / b, t);
|
|
216
|
+
case "%":
|
|
217
|
+
if (b === 0n) throw new Error("invalid operation: division by zero");
|
|
218
|
+
return typedInt(a % b, t);
|
|
219
|
+
case "&":
|
|
220
|
+
return typedInt(a & b, t);
|
|
221
|
+
case "|":
|
|
222
|
+
return typedInt(a | b, t);
|
|
223
|
+
case "^":
|
|
224
|
+
return typedInt(a ^ b, t);
|
|
225
|
+
case "&^":
|
|
226
|
+
return typedInt(a & ~b, t);
|
|
227
|
+
default: {
|
|
228
|
+
const r = compare(op, a, b);
|
|
229
|
+
return r === null ? null : { k: BOOL, v: r };
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// `x op y` on exact decimals for + - *.
|
|
235
|
+
function combineDecimals(op, x, y) {
|
|
236
|
+
if (op === "*") return { n: x.n * y.n, e: x.e + y.e };
|
|
237
|
+
const e = Math.max(x.e, y.e);
|
|
238
|
+
const xn = x.n * 10n ** BigInt(e - x.e);
|
|
239
|
+
const yn = y.n * 10n ** BigInt(e - y.e);
|
|
240
|
+
return { n: op === "+" ? xn + yn : xn - yn, e };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Untyped decimal + - * in exact arithmetic, rounded to float64 once at the end.
|
|
244
|
+
function foldExactDecimals(op, a, b) {
|
|
245
|
+
if (op !== "+" && op !== "-" && op !== "*") return null;
|
|
246
|
+
const x = exactDecimal(a);
|
|
247
|
+
const y = exactDecimal(b);
|
|
248
|
+
if (!x || !y) return null;
|
|
249
|
+
const r = combineDecimals(op, x, y);
|
|
250
|
+
if (r.e > MAX_DECIMAL_EXP) return null;
|
|
251
|
+
const v = decimalToFloat(r);
|
|
252
|
+
return Number.isFinite(v) ? { k: FLOAT, v, r } : null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function foldFloats(op, a, b, t) {
|
|
256
|
+
// Untyped integral operands stay exact for + - * (`1e18 + 1`).
|
|
257
|
+
const ai = t ? null : constIntValue(a);
|
|
258
|
+
const bi = t ? null : constIntValue(b);
|
|
259
|
+
if (ai !== null && bi !== null && (op === "+" || op === "-" || op === "*")) {
|
|
260
|
+
const i = op === "+" ? ai + bi : op === "-" ? ai - bi : ai * bi;
|
|
261
|
+
return { k: FLOAT, v: Number(i), i };
|
|
262
|
+
}
|
|
263
|
+
const exact = t ? null : foldExactDecimals(op, a, b);
|
|
264
|
+
if (exact) return exact;
|
|
265
|
+
const x = asFloat(a);
|
|
266
|
+
const y = asFloat(b);
|
|
267
|
+
switch (op) {
|
|
268
|
+
case "+":
|
|
269
|
+
return typedFloat(x + y, t);
|
|
270
|
+
case "-":
|
|
271
|
+
return typedFloat(x - y, t);
|
|
272
|
+
case "*":
|
|
273
|
+
return typedFloat(x * y, t);
|
|
274
|
+
case "/":
|
|
275
|
+
if (y === 0) throw new Error("invalid operation: division by zero");
|
|
276
|
+
return typedFloat(x / y, t);
|
|
277
|
+
default: {
|
|
278
|
+
const r = compare(op, x, y);
|
|
279
|
+
return r === null ? null : { k: BOOL, v: r };
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function foldShift(op, a, b) {
|
|
285
|
+
const n = constIntValue(b);
|
|
286
|
+
const x = constIntValue(a);
|
|
287
|
+
if (n === null || x === null) return null;
|
|
288
|
+
if (n < 0n) throw new Error("invalid shift count: negative shift count");
|
|
289
|
+
if (n > MAX_SHIFT) return null;
|
|
290
|
+
return typedInt(op === "<<" ? x << n : x >> n, a.t);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function foldBools(op, a, b) {
|
|
294
|
+
if (op === "&&") return { k: BOOL, v: a.v && b.v };
|
|
295
|
+
if (op === "||") return { k: BOOL, v: a.v || b.v };
|
|
296
|
+
const r = op === "==" || op === "!=" ? compare(op, a.v, b.v) : null;
|
|
297
|
+
return r === null ? null : { k: BOOL, v: r };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function foldStrings(op, a, b) {
|
|
301
|
+
if (op === "+") return { k: STRING, v: a.v + b.v, t: a.t ?? b.t };
|
|
302
|
+
const r = compare(op, a.v, b.v);
|
|
303
|
+
return r === null ? null : { k: BOOL, v: r };
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function foldBinary(op, a, b) {
|
|
307
|
+
if (op === "<<" || op === ">>") return foldShift(op, a, b);
|
|
308
|
+
if (a.k === BOOL && b.k === BOOL) return foldBools(op, a, b);
|
|
309
|
+
if (a.k === STRING && b.k === STRING) return foldStrings(op, a, b);
|
|
310
|
+
const t = a.t ?? b.t;
|
|
311
|
+
const numeric = (x) => x.k === INT || x.k === FLOAT;
|
|
312
|
+
if (!numeric(a) || !numeric(b)) return null;
|
|
313
|
+
if (FLOAT_TYPES.has(t) || (!t && (a.k === FLOAT || b.k === FLOAT)))
|
|
314
|
+
return foldFloats(op, a, b, t);
|
|
315
|
+
const ai = constIntValue(a);
|
|
316
|
+
const bi = constIntValue(b);
|
|
317
|
+
if (ai === null || bi === null) return foldFloats(op, a, b, t);
|
|
318
|
+
return foldInts(op, ai, bi, t);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Converts `val` to the basic type `name`. Returns the typed value, a value
|
|
322
|
+
// with `overflow: true` when an integer does not fit, or null when the
|
|
323
|
+
// conversion is not a constant one this folder handles.
|
|
324
|
+
export function convertConst(val, name) {
|
|
325
|
+
if (INT_RANGES[name]) {
|
|
326
|
+
const i = constIntValue(val);
|
|
327
|
+
return i === null ? null : typedInt(i, name);
|
|
328
|
+
}
|
|
329
|
+
if (FLOAT_TYPES.has(name)) {
|
|
330
|
+
if (val.k !== INT && val.k !== FLOAT) return null;
|
|
331
|
+
return typedFloat(asFloat(val), name);
|
|
332
|
+
}
|
|
333
|
+
if (name === "string") {
|
|
334
|
+
if (val.k === STRING) return { k: STRING, v: val.v, t: name };
|
|
335
|
+
const i = constIntValue(val);
|
|
336
|
+
if (val.k !== INT || i === null) return null;
|
|
337
|
+
const valid = i >= 0n && i <= 0x10ffffn && !(i >= 0xd800n && i <= 0xdfffn);
|
|
338
|
+
return {
|
|
339
|
+
k: STRING,
|
|
340
|
+
v: valid ? String.fromCodePoint(Number(i)) : "�",
|
|
341
|
+
t: name,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
if (name === "bool" && val.k === BOOL) return { k: BOOL, v: val.v, t: name };
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// min/max over constants of one kind.
|
|
349
|
+
export function foldMinMax(isMin, vals) {
|
|
350
|
+
let best = vals[0];
|
|
351
|
+
for (const x of vals.slice(1)) {
|
|
352
|
+
const less = foldBinary("<", x, best);
|
|
353
|
+
if (!less) return null;
|
|
354
|
+
if (less.v === isMin) best = x;
|
|
355
|
+
}
|
|
356
|
+
return best;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function foldExpr(expr, resolve) {
|
|
360
|
+
switch (expr?.kind) {
|
|
361
|
+
case "BasicLit":
|
|
362
|
+
return constFromLit(expr);
|
|
363
|
+
case "Ident": {
|
|
364
|
+
if (expr.name === "true" || expr.name === "false")
|
|
365
|
+
return { k: BOOL, v: expr.name === "true" };
|
|
366
|
+
const lit = resolve(expr.name);
|
|
367
|
+
return lit ? constFromLit(lit) : null;
|
|
368
|
+
}
|
|
369
|
+
case "ParenExpr":
|
|
370
|
+
return foldExpr(expr.expr, resolve);
|
|
371
|
+
case "UnaryExpr": {
|
|
372
|
+
const x = foldExpr(expr.operand, resolve);
|
|
373
|
+
return x ? foldUnary(expr.op, x) : null;
|
|
374
|
+
}
|
|
375
|
+
case "BinaryExpr": {
|
|
376
|
+
const a = foldExpr(expr.left, resolve);
|
|
377
|
+
const b = a ? foldExpr(expr.right, resolve) : null;
|
|
378
|
+
return a && b ? foldBinary(expr.op, a, b) : null;
|
|
379
|
+
}
|
|
380
|
+
case "CallExpr": {
|
|
381
|
+
if (expr.func?.kind !== "Ident" || expr.func.name !== "len") return null;
|
|
382
|
+
if (expr.args?.length !== 1) return null;
|
|
383
|
+
const s = foldExpr(expr.args[0], resolve);
|
|
384
|
+
if (s?.k !== STRING) return null;
|
|
385
|
+
// Gofront strings across JS and WASM runtime use UTF-16 code units (s.length).
|
|
386
|
+
return { k: INT, v: BigInt(s.v.length) };
|
|
387
|
+
}
|
|
388
|
+
default:
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Folds `expr` into a BasicLit, or returns null when it needs the runtime.
|
|
394
|
+
// `resolve(name)` yields the BasicLit of an already-folded constant, if any.
|
|
395
|
+
// Throws on a constant division by zero or a negative shift count.
|
|
396
|
+
export function foldConstExpr(expr, resolve) {
|
|
397
|
+
const val = foldExpr(expr, resolve);
|
|
398
|
+
return val && !val.overflow ? constToLit(val) : null;
|
|
399
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
// TypeChecker constant evaluation — installed as a mixin on TypeChecker.prototype.
|
|
2
|
+
//
|
|
3
|
+
// Every constant expression gets its exact value from the shared folder
|
|
4
|
+
// (src/typechecker/const-fold.js) and carries it on the AST node as `_const`, so
|
|
5
|
+
// both backends emit the same value. Typed constant overflow and constant
|
|
6
|
+
// division by zero are reported here.
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
constFromLit,
|
|
10
|
+
constIntValue,
|
|
11
|
+
convertConst,
|
|
12
|
+
foldBinary,
|
|
13
|
+
foldMinMax,
|
|
14
|
+
foldUnary,
|
|
15
|
+
INT_RANGES,
|
|
16
|
+
intConst,
|
|
17
|
+
} from "./const-fold.js";
|
|
18
|
+
import { defaultType, isAny, typeStr, VOID } from "./types.js";
|
|
19
|
+
|
|
20
|
+
// Basic type names usable as conversion functions (`int32(x)`).
|
|
21
|
+
const BASIC_CONVERSIONS = new Set([
|
|
22
|
+
...Object.keys(INT_RANGES),
|
|
23
|
+
"float32",
|
|
24
|
+
"float64",
|
|
25
|
+
"string",
|
|
26
|
+
"bool",
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
// Node kinds that can be constant expressions.
|
|
30
|
+
const CONST_KINDS = new Set([
|
|
31
|
+
"BasicLit",
|
|
32
|
+
"Ident",
|
|
33
|
+
"ParenExpr",
|
|
34
|
+
"UnaryExpr",
|
|
35
|
+
"BinaryExpr",
|
|
36
|
+
"CallExpr",
|
|
37
|
+
"TypeConversion",
|
|
38
|
+
"SelectorExpr",
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
const NEGATIVE_SHIFT_ERROR = "invalid shift count: negative shift count";
|
|
42
|
+
|
|
43
|
+
/** @type {ThisType<import('./index.js').TypeChecker>} */
|
|
44
|
+
export const constantCheckMethods = {
|
|
45
|
+
// The value of a constant expression, or null when `expr` is not one (or
|
|
46
|
+
// is an overflowing or erroneous one).
|
|
47
|
+
_constValue(expr, scope = null) {
|
|
48
|
+
const raw = this._constRaw(expr, scope ?? this.globals);
|
|
49
|
+
return raw && !raw.overflow && !raw.error ? raw : null;
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
// Integer value of an integer (or untyped integral float) constant as a
|
|
53
|
+
// Number, or null.
|
|
54
|
+
_constIntValue(expr, scope = null) {
|
|
55
|
+
const v = this._constValue(expr, scope);
|
|
56
|
+
if (!v || (v.k === "FLOAT" && v.t)) return null;
|
|
57
|
+
const i = constIntValue(v);
|
|
58
|
+
return i === null ? null : Number(i);
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// Raw evaluation result, memoized on checked nodes: a value, a value with
|
|
62
|
+
// `overflow`, `{ error }`, or null.
|
|
63
|
+
_constRaw(expr, scope) {
|
|
64
|
+
if (!expr || typeof expr !== "object" || !CONST_KINDS.has(expr.kind))
|
|
65
|
+
return null;
|
|
66
|
+
if (expr._constRaw !== undefined) return expr._constRaw;
|
|
67
|
+
if (expr._constBusy) return null; // cyclic constant
|
|
68
|
+
expr._constBusy = true;
|
|
69
|
+
let raw;
|
|
70
|
+
try {
|
|
71
|
+
raw = this._evalConst(expr, scope);
|
|
72
|
+
} catch (err) {
|
|
73
|
+
raw = { error: err.message };
|
|
74
|
+
} finally {
|
|
75
|
+
expr._constBusy = false;
|
|
76
|
+
}
|
|
77
|
+
// Conversions are only resolved once the node has been checked.
|
|
78
|
+
if (expr._type !== undefined) expr._constRaw = raw;
|
|
79
|
+
return raw;
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
// Called by checkExpr after typing `expr`: attaches `_const` and reports
|
|
83
|
+
// overflow / division by zero once per node.
|
|
84
|
+
_noteConst(expr, scope) {
|
|
85
|
+
if (!CONST_KINDS.has(expr.kind)) return;
|
|
86
|
+
const raw = this._constRaw(expr, scope);
|
|
87
|
+
expr._const = raw && !raw.overflow && !raw.error ? raw : null;
|
|
88
|
+
if (!raw || expr._constReported) return;
|
|
89
|
+
if (raw.error) {
|
|
90
|
+
expr._constReported = true;
|
|
91
|
+
// checkShiftCount already reports a negative count at its operand.
|
|
92
|
+
if (raw.error === NEGATIVE_SHIFT_ERROR) return;
|
|
93
|
+
this.err(raw.error, expr);
|
|
94
|
+
} else if (raw.overflow) {
|
|
95
|
+
expr._constReported = true;
|
|
96
|
+
this.err(`constant ${raw.v} overflows ${raw.typeName ?? raw.t}`, expr);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
_evalConst(expr, scope) {
|
|
101
|
+
switch (expr.kind) {
|
|
102
|
+
case "BasicLit":
|
|
103
|
+
return constFromLit(expr);
|
|
104
|
+
case "ParenExpr":
|
|
105
|
+
return this._constValue(expr.expr, scope);
|
|
106
|
+
case "UnaryExpr": {
|
|
107
|
+
const x = this._constValue(expr.operand, scope);
|
|
108
|
+
return x ? foldUnary(expr.op, x) : null;
|
|
109
|
+
}
|
|
110
|
+
case "BinaryExpr": {
|
|
111
|
+
const a = this._constValue(expr.left, scope);
|
|
112
|
+
const b = a ? this._constValue(expr.right, scope) : null;
|
|
113
|
+
return a && b ? foldBinary(expr.op, a, b) : null;
|
|
114
|
+
}
|
|
115
|
+
case "Ident":
|
|
116
|
+
return this._evalConstIdent(expr, scope);
|
|
117
|
+
case "SelectorExpr":
|
|
118
|
+
return this._evalConstSelector(expr, scope);
|
|
119
|
+
case "TypeConversion": {
|
|
120
|
+
const v = this._constValue(expr.expr, scope);
|
|
121
|
+
const target = v ? this.resolveTypeNode(expr.targetType, scope) : null;
|
|
122
|
+
return target ? this._convertConstTo(v, target) : null;
|
|
123
|
+
}
|
|
124
|
+
default:
|
|
125
|
+
return this._evalConstCall(expr, scope);
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
_evalConstIdent(expr, scope) {
|
|
130
|
+
const owner = scope.lookupScope(expr.name);
|
|
131
|
+
if (!owner?._consts.has(expr.name)) return null;
|
|
132
|
+
const init = owner.lookupConstExpr(expr.name);
|
|
133
|
+
const v = init
|
|
134
|
+
? this._constValue(init, owner)
|
|
135
|
+
: (this._importedConsts?.get(expr.name) ?? null);
|
|
136
|
+
if (!v) return null;
|
|
137
|
+
const declared = owner.symbols.get(expr.name);
|
|
138
|
+
if (!declared || declared.kind === "untyped" || declared.kind === "any")
|
|
139
|
+
return v;
|
|
140
|
+
const typed = this._convertConstTo(v, declared);
|
|
141
|
+
return typed?.overflow ? null : typed;
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
// `pkg.K` of an imported GoFront package.
|
|
145
|
+
_evalConstSelector(expr, scope) {
|
|
146
|
+
if (expr.expr?.kind !== "Ident") return null;
|
|
147
|
+
const ns = scope.lookup(expr.expr.name);
|
|
148
|
+
if (ns?.kind !== "namespace") return null;
|
|
149
|
+
return ns._gofront ? (ns._consts?.get(expr.field) ?? null) : null;
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
// Conversions, `len` of a constant string, and `min`/`max` of constants.
|
|
153
|
+
_evalConstCall(expr, scope) {
|
|
154
|
+
const target = this._constConversionTarget(expr, scope);
|
|
155
|
+
if (target) {
|
|
156
|
+
const v = this._constValue(expr.args[0], scope);
|
|
157
|
+
return v ? this._convertConstTo(v, target) : null;
|
|
158
|
+
}
|
|
159
|
+
const fn = expr.func;
|
|
160
|
+
if (fn?.kind !== "Ident" || scope.lookup(fn.name)?.kind !== "builtin")
|
|
161
|
+
return null;
|
|
162
|
+
const vals = (expr.args ?? []).map((a) => this._constValue(a, scope));
|
|
163
|
+
if (vals.length === 0 || vals.some((v) => !v)) return null;
|
|
164
|
+
if (fn.name === "min" || fn.name === "max")
|
|
165
|
+
return foldMinMax(fn.name === "min", vals);
|
|
166
|
+
// GoFront strings use UTF-16 code units on both backends.
|
|
167
|
+
const isLen = fn.name === "len" && vals[0].k === "STRING";
|
|
168
|
+
return isLen ? intConst(BigInt(vals[0].v.length)) : null;
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
// Target type of a conversion call (`MyInt(x)`, `int32(x)`), or null.
|
|
172
|
+
_constConversionTarget(expr, scope) {
|
|
173
|
+
if (expr.args?.length !== 1) return null;
|
|
174
|
+
if (expr._isTypeConversion) return expr._conversionTargetType ?? null;
|
|
175
|
+
const fn = expr.func;
|
|
176
|
+
if (fn?.kind !== "Ident" || !BASIC_CONVERSIONS.has(fn.name)) return null;
|
|
177
|
+
const owner = scope.lookupScope(fn.name);
|
|
178
|
+
return !owner || owner === this.universe
|
|
179
|
+
? { kind: "basic", name: fn.name }
|
|
180
|
+
: null;
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
// Converts a constant to Go type `t` (basic or named over a basic type).
|
|
184
|
+
_convertConstTo(v, t) {
|
|
185
|
+
const u = this.resolveType(t?.kind === "named" ? t.underlying : t);
|
|
186
|
+
if (u?.kind !== "basic") return null;
|
|
187
|
+
const r = convertConst(v, u.name);
|
|
188
|
+
return r?.overflow ? { ...r, typeName: typeStr(t) } : r;
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
// `var x int8 = 200`: an untyped integer constant must fit the target. An
|
|
192
|
+
// interface target takes the constant's default type (`var v any = 1 << 64`).
|
|
193
|
+
// A null `ctx` reports an operand converted to the other operand's type.
|
|
194
|
+
_checkConstOverflow(target, source, node, ctx) {
|
|
195
|
+
if (source?.kind !== "untyped" || !node?._const) return false;
|
|
196
|
+
const base = this.resolveType(
|
|
197
|
+
target?.kind === "named" ? target.underlying : target,
|
|
198
|
+
);
|
|
199
|
+
if (base?.kind === "interface" || isAny(base)) target = defaultType(source);
|
|
200
|
+
const i = constIntValue(node._const);
|
|
201
|
+
const range = this._intRange(target);
|
|
202
|
+
if (i === null && range)
|
|
203
|
+
return this._reportTruncatedConst(target, node, ctx);
|
|
204
|
+
// A float or complex target holds the constant as a Number: Go rounds
|
|
205
|
+
// the same way, so the JS backend has nothing to warn about.
|
|
206
|
+
if (!range && node.kind) {
|
|
207
|
+
const u = this.resolveType(
|
|
208
|
+
target?.kind === "named" ? target.underlying : target,
|
|
209
|
+
);
|
|
210
|
+
if (u?.kind === "basic" && /^(float|complex)/.test(u.name))
|
|
211
|
+
node._floatConst = true;
|
|
212
|
+
}
|
|
213
|
+
if (!range) return false;
|
|
214
|
+
if (i >= range[0] && i <= range[1]) {
|
|
215
|
+
node._intConst = true; // the JS backend's precision warning reads this
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
const isInt = node._const.k === "INT";
|
|
219
|
+
const text = isInt ? String(i) : String(node._const.v);
|
|
220
|
+
const what = `untyped ${isInt ? "int" : "float"} constant`;
|
|
221
|
+
const shown =
|
|
222
|
+
node.kind === "BasicLit"
|
|
223
|
+
? `${node.value} (${what})`
|
|
224
|
+
: `${text} (${what} ${text})`;
|
|
225
|
+
this.err(
|
|
226
|
+
ctx
|
|
227
|
+
? `cannot use ${shown} as ${typeStr(target)} value in ${ctx} (overflows)`
|
|
228
|
+
: `${shown} overflows ${typeStr(target)}`,
|
|
229
|
+
node,
|
|
230
|
+
);
|
|
231
|
+
return true;
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
// `var i int = 1.5`: an untyped float constant with a fractional part
|
|
235
|
+
// cannot become an integer.
|
|
236
|
+
_reportTruncatedConst(target, node, ctx) {
|
|
237
|
+
const c = node._const;
|
|
238
|
+
if (c.k !== "FLOAT" || c.t) return false;
|
|
239
|
+
const shown =
|
|
240
|
+
node.kind === "BasicLit"
|
|
241
|
+
? `${node.value} (untyped float constant)`
|
|
242
|
+
: `${c.v} (untyped float constant ${c.v})`;
|
|
243
|
+
this.err(
|
|
244
|
+
ctx
|
|
245
|
+
? `cannot use ${shown} as ${typeStr(target)} value in ${ctx} (truncated)`
|
|
246
|
+
: `${shown} truncated to ${typeStr(target)}`,
|
|
247
|
+
node,
|
|
248
|
+
);
|
|
249
|
+
return true;
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
// `x := 1 << 63`, `println(1 << 64)`: the constant takes its default type.
|
|
253
|
+
_checkConstDefault(source, node, ctx) {
|
|
254
|
+
return this._checkConstOverflow(defaultType(source), source, node, ctx);
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
// print/println arguments take their default type.
|
|
258
|
+
_checkPrintArgs(name, expr, argTypes) {
|
|
259
|
+
for (let i = 0; i < expr.args.length; i++)
|
|
260
|
+
this._checkConstDefault(
|
|
261
|
+
argTypes[i],
|
|
262
|
+
expr.args[i],
|
|
263
|
+
`argument to built-in ${name}`,
|
|
264
|
+
);
|
|
265
|
+
return VOID;
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
// `x + 300` with `x int8`: the untyped operand is converted to the other
|
|
269
|
+
// operand's type.
|
|
270
|
+
_checkConstOperands(op, lt, rt, expr) {
|
|
271
|
+
if (op === "<<" || op === ">>" || op === "&&" || op === "||") return;
|
|
272
|
+
if (rt?.kind === "untyped" && lt?.kind !== "untyped")
|
|
273
|
+
this._checkConstOverflow(lt, rt, expr.right, null);
|
|
274
|
+
else if (lt?.kind === "untyped" && rt?.kind !== "untyped")
|
|
275
|
+
this._checkConstOverflow(rt, lt, expr.left, null);
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
_intRange(t) {
|
|
279
|
+
const u = this.resolveType(t?.kind === "named" ? t.underlying : t);
|
|
280
|
+
return u?.kind === "basic" ? (INT_RANGES[u.name] ?? null) : null;
|
|
281
|
+
},
|
|
282
|
+
};
|