gofront 1.8.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +6 -5
- package/src/backend/js/expressions.js +1157 -408
- package/src/backend/js/index.js +327 -153
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +559 -2552
- package/src/backend/js/statements.js +670 -158
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +298 -19
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +91 -170
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +884 -256
- package/src/backend/wasm/emit-exprs.js +185 -77
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-shared.js +4 -1
- package/src/backend/wasm/emit-stmts.js +524 -240
- package/src/backend/wasm/emit.js +176 -38
- package/src/backend/wasm/globals.js +48 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +44 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +299 -770
- package/src/backend/wasm/monomorph.js +139 -8
- package/src/backend/wasm/natives.js +600 -0
- package/src/backend/wasm/prescan.js +5 -153
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -32
- package/src/backend/wasm/types.js +97 -20
- package/src/backend/wasm/variadic.js +9 -4
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +113 -130
- package/src/lexer/lexer.js +106 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +355 -132
- package/src/link/type-names.js +204 -0
- package/src/link/wasm.js +30 -6
- package/src/lower/bounds.js +153 -0
- package/src/lower/captures.js +92 -33
- package/src/lower/embedding.js +62 -5
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +929 -0
- package/src/lower/functions.js +225 -7
- package/src/lower/gen-types.js +293 -0
- package/src/lower/index.js +14 -2
- package/src/lower/json-codecs.js +334 -245
- package/src/lower/ownership.js +25 -7
- package/src/lower/reachability.js +69 -51
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +30 -5
- package/src/parser/expressions.js +111 -57
- package/src/parser/statements.js +108 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +12 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +214 -19
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +156 -8
- package/src/typechecker/expressions.js +490 -108
- package/src/typechecker/index.js +346 -42
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +41 -0
- package/src/typechecker/resolve.js +490 -65
- package/src/typechecker/statements.js +288 -27
- package/src/typechecker/stdlib/excluded.js +9 -5
- package/src/typechecker/stdlib/index.js +25 -9
- package/src/typechecker/termination.js +10 -1
- package/src/typechecker/types.js +287 -575
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +171 -38
- package/stdlib/internal/jsonrt/enc.go +186 -36
- package/stdlib/internal/jsonrt/scanner.go +17 -8
- package/stdlib/io/io.go +157 -1
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +16 -14
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +391 -452
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +235 -50
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +141 -3
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/fmt-runtime.js +0 -573
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -119
- package/src/backend/js/stdlib/builder.js +0 -98
- package/src/backend/js/stdlib/bytes.js +0 -368
- package/src/backend/js/stdlib/errors.js +0 -45
- package/src/backend/js/stdlib/fmt.js +0 -517
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -1167
- package/src/backend/js/stdlib/maps.js +0 -37
- package/src/backend/js/stdlib/math.js +0 -85
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -34
- package/src/backend/js/stdlib/slices.js +0 -107
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -79
- package/src/backend/js/stdlib/strings.js +0 -301
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -41
- package/src/backend/js/stdlib/time.js +0 -152
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/stdlib/bytes.js +0 -6716
- package/src/backend/wasm/stdlib/core.js +0 -10278
- package/src/backend/wasm/stdlib/io.js +0 -3578
- package/src/backend/wasm/stdlib/packages.js +0 -2402
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -2229
- package/src/typechecker/stdlib/core.js +0 -1079
- package/src/typechecker/stdlib/extended.js +0 -1210
- package/stdlib/bytes/iter.go +0 -192
- package/stdlib/bytes/search.go +0 -240
- package/stdlib/bytes/split.go +0 -218
- package/stdlib/errors/overlay.go +0 -12
- package/stdlib/maps/iter.go +0 -65
- package/stdlib/slices/extra.go +0 -73
- package/stdlib/slices/iter.go +0 -113
- package/stdlib/strings/extra.go +0 -35
- package/stdlib/strings/iter.go +0 -122
- package/stdlib/sync/extra.go +0 -105
- package/stdlib/unicode/utf8/bytes.go +0 -239
|
@@ -11,7 +11,10 @@ import {
|
|
|
11
11
|
compositeLitIndices,
|
|
12
12
|
compositeLitMaxIndex,
|
|
13
13
|
} from "../../typechecker/types.js";
|
|
14
|
+
import { goTypeName } from "../shared/fmt-types.js";
|
|
15
|
+
import { emitWasmNative } from "./natives.js";
|
|
14
16
|
import {
|
|
17
|
+
getInterfaceMethodList,
|
|
15
18
|
getMapKeyValTypes,
|
|
16
19
|
isAnyType,
|
|
17
20
|
isArrayType,
|
|
@@ -19,6 +22,7 @@ import {
|
|
|
19
22
|
isComplex64,
|
|
20
23
|
isFuncType,
|
|
21
24
|
isMapType,
|
|
25
|
+
isNonEmptyInterface,
|
|
22
26
|
isPointerToStruct,
|
|
23
27
|
isRuneSlice,
|
|
24
28
|
isScalarType,
|
|
@@ -26,7 +30,7 @@ import {
|
|
|
26
30
|
isSliceType,
|
|
27
31
|
isStringType,
|
|
28
32
|
isStructType,
|
|
29
|
-
|
|
33
|
+
isTaggedSliceType,
|
|
30
34
|
toWasmType,
|
|
31
35
|
} from "./types.js";
|
|
32
36
|
|
|
@@ -101,27 +105,38 @@ export class ExprsEmitter {
|
|
|
101
105
|
return;
|
|
102
106
|
}
|
|
103
107
|
|
|
108
|
+
// A defined func type, and every array and map type, is boxed to keep
|
|
109
|
+
// its identity: arrays and maps share one wasm type across Go types
|
|
110
|
+
// (`[2]int` and `[2]int64`), and a defined func type has methods that
|
|
111
|
+
// interface dispatch must tell from the plain func type.
|
|
112
|
+
if (this._isMethodNamedRef(goType)) {
|
|
113
|
+
const box = this.mod.getIfaceBoxType(this._resolveTestTarget(goType));
|
|
114
|
+
this._emitRawExpr(expr, box.wType);
|
|
115
|
+
// An array stored in an interface is a copy.
|
|
116
|
+
if (isArrayType(box.goType, this.mod.checker))
|
|
117
|
+
this._emitCopyIfValue(expr, goType, box.wType);
|
|
118
|
+
this._emitBoxValue(box);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
104
122
|
if (
|
|
105
123
|
isSliceType(goType, this.mod.checker) ||
|
|
106
|
-
|
|
107
|
-
isFuncType(goType, this.mod.checker) ||
|
|
108
|
-
isMapType(goType, this.mod.checker)
|
|
124
|
+
isFuncType(goType, this.mod.checker)
|
|
109
125
|
) {
|
|
110
126
|
const refWType = this.toWasmType(goType);
|
|
111
127
|
this._emitRawExpr(expr, refWType);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (
|
|
116
|
-
isFuncType(goType, this.mod.checker) ||
|
|
117
|
-
isMapType(goType, this.mod.checker)
|
|
118
|
-
)
|
|
128
|
+
if (isTaggedSliceType(goType, this.mod.checker))
|
|
129
|
+
this._emitTagSlice(goType, refWType);
|
|
130
|
+
if (isFuncType(goType, this.mod.checker))
|
|
119
131
|
this._emitNilAwareAny(goType, expr);
|
|
120
132
|
return;
|
|
121
133
|
}
|
|
122
134
|
|
|
123
135
|
// Scalar type -> box into struct
|
|
124
|
-
|
|
136
|
+
// An untyped rune constant boxes as `rune` (int32), like Go.
|
|
137
|
+
const scalarType = goType?.rune
|
|
138
|
+
? { kind: "basic", name: "int32" }
|
|
139
|
+
: (goType ?? { kind: "basic", name: "int" });
|
|
125
140
|
const box = this.mod.getIfaceBoxType(scalarType);
|
|
126
141
|
this._emitRawExpr(expr, box.wType);
|
|
127
142
|
this._emitBoxValue(box);
|
|
@@ -131,6 +146,85 @@ export class ExprsEmitter {
|
|
|
131
146
|
this._emitRawExpr(expr, targetWasmType);
|
|
132
147
|
}
|
|
133
148
|
|
|
149
|
+
// Panics with Go's failed type assertion message: the static interface
|
|
150
|
+
// type, the dynamic type of the value in `anyTmp` and the target type.
|
|
151
|
+
_emitTypeAssertPanic(expr, anyTmp) {
|
|
152
|
+
const checker = this.mod.checker;
|
|
153
|
+
const target = this._resolveTestTarget(expr._type ?? expr.type);
|
|
154
|
+
const iface = isNonEmptyInterface(target, checker);
|
|
155
|
+
this.pushInstruction({ op: "local.get", index: anyTmp });
|
|
156
|
+
if (iface) {
|
|
157
|
+
const names = getInterfaceMethodList(target.underlying ?? target)
|
|
158
|
+
.map((m) => m.name)
|
|
159
|
+
.sort();
|
|
160
|
+
this._pushStringConst(names[0] ?? "");
|
|
161
|
+
} else {
|
|
162
|
+
this._pushStringConst(this._assertSrcName(expr.expr?._type));
|
|
163
|
+
}
|
|
164
|
+
this._pushStringConst(
|
|
165
|
+
target.name === "error" ? "error" : goTypeName(target),
|
|
166
|
+
);
|
|
167
|
+
this.pushInstruction({ op: "i32.const", value: iface ? 1 : 0 });
|
|
168
|
+
this.pushInstruction({
|
|
169
|
+
op: "call",
|
|
170
|
+
funcIndex: this.mod.resolveFuncIndex("__ta_fail"),
|
|
171
|
+
});
|
|
172
|
+
this.pushInstruction("unreachable");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// The static interface type of an assertion operand as Go names it.
|
|
176
|
+
_assertSrcName(srcType) {
|
|
177
|
+
if (!srcType || !isNonEmptyInterface(srcType, this.mod.checker))
|
|
178
|
+
return "interface {}";
|
|
179
|
+
return srcType.name === "error" ? "error" : goTypeName(srcType);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Pushes the interned string `text` as an externref.
|
|
183
|
+
_pushStringConst(text) {
|
|
184
|
+
this.pushInstruction({
|
|
185
|
+
op: "i32.const",
|
|
186
|
+
value: this.mod.internString(text),
|
|
187
|
+
});
|
|
188
|
+
this.pushInstruction({
|
|
189
|
+
op: "call",
|
|
190
|
+
funcIndex: this.mod.getStringImportIndex(),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// The slice on the stack becomes the tagged subtype of its Go type, so a
|
|
195
|
+
// type test can tell `[]byte` from `[]bool` (see getTaggedSliceType). The
|
|
196
|
+
// copy shares the backing array; slice headers are immutable.
|
|
197
|
+
_emitTagSlice(goType, refWType) {
|
|
198
|
+
const tagged = this.mod.getTaggedSliceType(
|
|
199
|
+
goType,
|
|
200
|
+
this._getSliceElemType(goType),
|
|
201
|
+
);
|
|
202
|
+
const base = tagged.base;
|
|
203
|
+
const tmp = this.acquireTemp(refWType);
|
|
204
|
+
this.pushInstruction({ op: "local.tee", index: tmp });
|
|
205
|
+
this.pushInstruction("ref.is_null");
|
|
206
|
+
this.pushInstruction({
|
|
207
|
+
op: "if",
|
|
208
|
+
blockType: { kind: "ref", nullable: true, typeIndex: base.typeIndex },
|
|
209
|
+
});
|
|
210
|
+
this.pushInstruction({ op: "global.get", index: base.emptyGlobalIndex });
|
|
211
|
+
this.pushInstruction("else");
|
|
212
|
+
this.pushInstruction({ op: "local.get", index: tmp });
|
|
213
|
+
this.pushInstruction("end");
|
|
214
|
+
this.pushInstruction({ op: "local.set", index: tmp });
|
|
215
|
+
for (let i = 0; i < 4; i++) {
|
|
216
|
+
this.pushInstruction({ op: "local.get", index: tmp });
|
|
217
|
+
this.pushInstruction({
|
|
218
|
+
op: "struct.get",
|
|
219
|
+
typeIndex: base.typeIndex,
|
|
220
|
+
fieldIndex: i,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
this.pushInstruction({ op: "ref.null", heapType: tagged.tagTypeIndex });
|
|
224
|
+
this.pushInstruction({ op: "struct.new", typeIndex: tagged.typeIndex });
|
|
225
|
+
this.releaseTemp(tmp, refWType);
|
|
226
|
+
}
|
|
227
|
+
|
|
134
228
|
// The nullable ref on the stack becomes an interface value: a nil map, func
|
|
135
229
|
// or pointer turns into the marker of its type (never a nil interface).
|
|
136
230
|
_emitNilAwareAny(goType, expr) {
|
|
@@ -240,7 +334,8 @@ export class ExprsEmitter {
|
|
|
240
334
|
break;
|
|
241
335
|
|
|
242
336
|
case "CallExpr":
|
|
243
|
-
this
|
|
337
|
+
if (!emitWasmNative(this, expr, targetWasmType))
|
|
338
|
+
this.emitCallExpr(expr, targetWasmType);
|
|
244
339
|
break;
|
|
245
340
|
|
|
246
341
|
case "TypeConversion":
|
|
@@ -254,7 +349,6 @@ export class ExprsEmitter {
|
|
|
254
349
|
case "SelectorExpr": {
|
|
255
350
|
const deq = this._dequalify(expr);
|
|
256
351
|
if (deq) this.emitIdent(deq, targetWasmType);
|
|
257
|
-
else if (this._emitPkgConst(expr, targetWasmType)) break;
|
|
258
352
|
else this.emitSelectorExpr(expr);
|
|
259
353
|
break;
|
|
260
354
|
}
|
|
@@ -286,7 +380,7 @@ export class ExprsEmitter {
|
|
|
286
380
|
this._emitTypeTest(targetGoType);
|
|
287
381
|
this.pushInstruction("i32.eqz");
|
|
288
382
|
this.pushInstruction({ op: "if", blockType: "void" });
|
|
289
|
-
this.
|
|
383
|
+
this._emitTypeAssertPanic(expr, anyTmp);
|
|
290
384
|
this.pushInstruction("end");
|
|
291
385
|
|
|
292
386
|
this.pushInstruction({ op: "local.get", index: anyTmp });
|
|
@@ -316,7 +410,7 @@ export class ExprsEmitter {
|
|
|
316
410
|
if (capturedNames.length === 0) {
|
|
317
411
|
this.pushInstruction({ op: "ref.null", heapType: "any" });
|
|
318
412
|
} else {
|
|
319
|
-
const envFields = this._buildEnvFields(capturedNames);
|
|
413
|
+
const envFields = this._buildEnvFields(capturedNames, funcLit);
|
|
320
414
|
const envInfo = this.mod.getEnvType(envFields);
|
|
321
415
|
|
|
322
416
|
for (const field of envFields) {
|
|
@@ -327,6 +421,10 @@ export class ExprsEmitter {
|
|
|
327
421
|
);
|
|
328
422
|
}
|
|
329
423
|
this.pushInstruction({ op: "local.get", index: local.index });
|
|
424
|
+
// A non-escaping `range n` index lives in an i32 local, but a
|
|
425
|
+
// closure (such as a range-over-func body) sees a Go `int`.
|
|
426
|
+
if (!field.isBoxed && local.type === "i32" && field.wType === "i64")
|
|
427
|
+
this.pushInstruction("i64.extend_i32_s");
|
|
330
428
|
}
|
|
331
429
|
|
|
332
430
|
this.pushInstruction({
|
|
@@ -562,55 +660,7 @@ export class ExprsEmitter {
|
|
|
562
660
|
return this._derefStructInfo(operand) ? operand : null;
|
|
563
661
|
}
|
|
564
662
|
|
|
565
|
-
_emitPkgConst(expr, targetWasmType) {
|
|
566
|
-
if (this._isMathConst(expr)) {
|
|
567
|
-
this.emitMathConst(expr.field, targetWasmType);
|
|
568
|
-
return true;
|
|
569
|
-
}
|
|
570
|
-
if (this._isUtf8Const(expr)) {
|
|
571
|
-
this.emitUtf8Const(expr.field, targetWasmType);
|
|
572
|
-
return true;
|
|
573
|
-
}
|
|
574
|
-
if (this._isStrconvConst(expr)) {
|
|
575
|
-
this.emitStrconvConst(expr.field, targetWasmType);
|
|
576
|
-
return true;
|
|
577
|
-
}
|
|
578
|
-
if (this._isPathConst(expr)) {
|
|
579
|
-
this.emitPathConst(expr.field, targetWasmType);
|
|
580
|
-
return true;
|
|
581
|
-
}
|
|
582
|
-
if (this._isIoConst(expr)) {
|
|
583
|
-
this.emitIoConst(expr.field, targetWasmType);
|
|
584
|
-
return true;
|
|
585
|
-
}
|
|
586
|
-
if (this._isOsConst?.(expr)) {
|
|
587
|
-
this.emitOsConst(expr.field, targetWasmType);
|
|
588
|
-
return true;
|
|
589
|
-
}
|
|
590
|
-
if (this._isTimeConst(expr)) {
|
|
591
|
-
this.emitTimeConst(expr.field, targetWasmType);
|
|
592
|
-
return true;
|
|
593
|
-
}
|
|
594
|
-
if (this._isBitsConst?.(expr)) {
|
|
595
|
-
this.emitBitsConst(expr.field, targetWasmType);
|
|
596
|
-
return true;
|
|
597
|
-
}
|
|
598
|
-
if (this._isExtendedPkgConst?.(expr)) {
|
|
599
|
-
this.emitExtendedPkgConst(expr, targetWasmType);
|
|
600
|
-
return true;
|
|
601
|
-
}
|
|
602
|
-
return false;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
663
|
emitSelectorExpr(expr) {
|
|
606
|
-
if (isTestingT(expr.expr?._type) && expr.field === "N") {
|
|
607
|
-
this.emitExpr(expr.expr, "externref");
|
|
608
|
-
this.pushInstruction({
|
|
609
|
-
op: "call",
|
|
610
|
-
funcIndex: this.mod.getStdlibImportIndex("testing_n"),
|
|
611
|
-
});
|
|
612
|
-
return;
|
|
613
|
-
}
|
|
614
664
|
const structInfo = this._resolveStructInfo(expr.expr);
|
|
615
665
|
if (!structInfo) {
|
|
616
666
|
if (expr.expr?.kind === "Ident" && expr.expr._type?.kind === "namespace")
|
|
@@ -1296,12 +1346,14 @@ export class ExprsEmitter {
|
|
|
1296
1346
|
constIdx < arrayLen
|
|
1297
1347
|
) {
|
|
1298
1348
|
this.emitExpr(baseNode, arrWType);
|
|
1349
|
+
this._emitNilCheck(rawBaseType);
|
|
1299
1350
|
this.pushInstruction({ op: "i32.const", value: constIdx });
|
|
1300
1351
|
return;
|
|
1301
1352
|
}
|
|
1302
1353
|
|
|
1303
1354
|
const arrTmp = this.acquireTemp(arrWType);
|
|
1304
1355
|
this.emitExpr(baseNode, arrWType);
|
|
1356
|
+
this._emitNilCheck(rawBaseType);
|
|
1305
1357
|
this.pushInstruction({ op: "local.tee", index: arrTmp });
|
|
1306
1358
|
this._emitCheckedIndex(indexNode, () => {
|
|
1307
1359
|
this.pushInstruction({ op: "local.get", index: arrTmp });
|
|
@@ -1376,6 +1428,15 @@ export class ExprsEmitter {
|
|
|
1376
1428
|
return null;
|
|
1377
1429
|
}
|
|
1378
1430
|
|
|
1431
|
+
// True when the target wasm type is the `$complex64` struct, whether it is
|
|
1432
|
+
// named or only carries the struct's type index.
|
|
1433
|
+
_isComplex64Target(w) {
|
|
1434
|
+
return (
|
|
1435
|
+
w?.typeName === "$complex64" ||
|
|
1436
|
+
this._complexKindOfWType(w) === "complex64"
|
|
1437
|
+
);
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1379
1440
|
emitBasicLit(lit, targetWasmType) {
|
|
1380
1441
|
const targetComplex = this._complexKindOfWType(targetWasmType);
|
|
1381
1442
|
const isTargetComplex64 =
|
|
@@ -1456,7 +1517,7 @@ export class ExprsEmitter {
|
|
|
1456
1517
|
emitImagLit(lit, targetWasmType) {
|
|
1457
1518
|
const is64 =
|
|
1458
1519
|
lit._type?.name === "complex64" ||
|
|
1459
|
-
targetWasmType
|
|
1520
|
+
this._isComplex64Target(targetWasmType);
|
|
1460
1521
|
const complexName = is64 ? "complex64" : "complex128";
|
|
1461
1522
|
const info = this.mod.getComplexType(complexName);
|
|
1462
1523
|
const imVal = Number(lit.value);
|
|
@@ -1518,6 +1579,13 @@ export class ExprsEmitter {
|
|
|
1518
1579
|
this._emitNilValue(ident._type, targetWasmType);
|
|
1519
1580
|
return;
|
|
1520
1581
|
}
|
|
1582
|
+
// A named untyped complex constant that takes type complex64 is built
|
|
1583
|
+
// from its initializer, like a complex literal, not read from its
|
|
1584
|
+
// complex128 global.
|
|
1585
|
+
if (ident._complexInit && this._isComplex64Target(targetWasmType)) {
|
|
1586
|
+
this.emitExpr(ident._complexInit, targetWasmType);
|
|
1587
|
+
return;
|
|
1588
|
+
}
|
|
1521
1589
|
|
|
1522
1590
|
const localInfo = this.resolveLocal(ident.name);
|
|
1523
1591
|
if (localInfo) {
|
|
@@ -1589,10 +1657,17 @@ export class ExprsEmitter {
|
|
|
1589
1657
|
throw new Error(`Unresolved identifier: ${ident.name}`);
|
|
1590
1658
|
}
|
|
1591
1659
|
|
|
1660
|
+
// Unary arithmetic on a complex value; `&c` and `*p` of one only move
|
|
1661
|
+
// its pointer.
|
|
1662
|
+
_isComplexArith(expr, op, operand) {
|
|
1663
|
+
if (op === "&" || op === "*") return false;
|
|
1664
|
+
return isComplex(expr._type) || isComplex(operand._type);
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1592
1667
|
emitUnaryExpr(expr, targetWasmType = null) {
|
|
1593
1668
|
const { op, operand } = expr;
|
|
1594
1669
|
|
|
1595
|
-
if (
|
|
1670
|
+
if (this._isComplexArith(expr, op, operand)) {
|
|
1596
1671
|
this._emitComplexUnary(expr, op, operand, targetWasmType);
|
|
1597
1672
|
return;
|
|
1598
1673
|
}
|
|
@@ -1681,7 +1756,7 @@ export class ExprsEmitter {
|
|
|
1681
1756
|
const is64 =
|
|
1682
1757
|
isComplex64(expr._type) ||
|
|
1683
1758
|
isComplex64(operand._type) ||
|
|
1684
|
-
targetWasmType
|
|
1759
|
+
this._isComplex64Target(targetWasmType);
|
|
1685
1760
|
const info = this.mod.getComplexType(is64 ? "complex64" : "complex128");
|
|
1686
1761
|
const cWType = {
|
|
1687
1762
|
kind: "ref",
|
|
@@ -1713,19 +1788,25 @@ export class ExprsEmitter {
|
|
|
1713
1788
|
}
|
|
1714
1789
|
}
|
|
1715
1790
|
|
|
1791
|
+
// True when a pointer to a `t` variable is the value's own reference (an
|
|
1792
|
+
// array or struct) rather than a box around it (scalars and complex).
|
|
1793
|
+
_isOwnPointerValue(t) {
|
|
1794
|
+
if (this._isValueArray(t)) return true;
|
|
1795
|
+
return (
|
|
1796
|
+
t?.kind !== "pointer" &&
|
|
1797
|
+
!isComplex(t) &&
|
|
1798
|
+
isStructType(t, this.mod.checker, this.mod) &&
|
|
1799
|
+
!isPointerToStruct(t, this.mod.checker, this.mod)
|
|
1800
|
+
);
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1716
1803
|
_emitAddressOf(operand) {
|
|
1717
1804
|
if (operand.kind === "Ident") {
|
|
1718
1805
|
const loc = this.resolveLocal(operand.name);
|
|
1719
1806
|
if (loc?.isBoxed) {
|
|
1720
1807
|
this.pushInstruction({ op: "local.get", index: loc.index });
|
|
1721
1808
|
// A pointer to an array or struct is its reference itself.
|
|
1722
|
-
|
|
1723
|
-
if (
|
|
1724
|
-
this._isValueArray(t) ||
|
|
1725
|
-
(t?.kind !== "pointer" &&
|
|
1726
|
-
isStructType(t, this.mod.checker, this.mod) &&
|
|
1727
|
-
!isPointerToStruct(t, this.mod.checker, this.mod))
|
|
1728
|
-
)
|
|
1809
|
+
if (this._isOwnPointerValue(loc.goType))
|
|
1729
1810
|
this.pushInstruction({
|
|
1730
1811
|
op: "struct.get",
|
|
1731
1812
|
typeIndex: loc.boxInfo.typeIndex,
|
|
@@ -1774,7 +1855,8 @@ export class ExprsEmitter {
|
|
|
1774
1855
|
// The struct a `*operand` reads or writes, or null when the operand points
|
|
1775
1856
|
// at something else: `*pp` with `pp **T` loads a pointer, not a struct.
|
|
1776
1857
|
_derefStructInfo(operand) {
|
|
1777
|
-
|
|
1858
|
+
const base = this._ptrBaseType(operand._type);
|
|
1859
|
+
if (this._isPointerGoType(base) || isComplex(base)) return null;
|
|
1778
1860
|
return this._resolveStructInfo(operand);
|
|
1779
1861
|
}
|
|
1780
1862
|
|
|
@@ -1797,6 +1879,7 @@ export class ExprsEmitter {
|
|
|
1797
1879
|
if (!isScalarType(baseGoType, this.mod.checker, this.mod)) {
|
|
1798
1880
|
const baseWType = this.toWasmType(baseGoType);
|
|
1799
1881
|
this.emitExpr(operand, baseWType);
|
|
1882
|
+
this._emitNilCheck(operandGoType);
|
|
1800
1883
|
return;
|
|
1801
1884
|
}
|
|
1802
1885
|
const box = this.mod.getBoxType(baseGoType);
|
|
@@ -2046,7 +2129,7 @@ export class ExprsEmitter {
|
|
|
2046
2129
|
isComplex64(expr._type) ||
|
|
2047
2130
|
isComplex64(left._type) ||
|
|
2048
2131
|
isComplex64(right._type) ||
|
|
2049
|
-
targetWasmType
|
|
2132
|
+
this._isComplex64Target(targetWasmType);
|
|
2050
2133
|
const info = this.mod.getComplexType(is64 ? "complex64" : "complex128");
|
|
2051
2134
|
const f = info.elemWType;
|
|
2052
2135
|
const cWType = {
|
|
@@ -2433,10 +2516,26 @@ export class ExprsEmitter {
|
|
|
2433
2516
|
this.releaseTemp(bTmp, arrWType);
|
|
2434
2517
|
}
|
|
2435
2518
|
|
|
2519
|
+
// Equality of two complex values on the stack -> i32.
|
|
2520
|
+
_emitComplexElemEq(goType, wType) {
|
|
2521
|
+
const info = this.mod.getComplexType(
|
|
2522
|
+
isComplex64(goType) ? "complex64" : "complex128",
|
|
2523
|
+
);
|
|
2524
|
+
const bTmp = this.acquireTemp(wType);
|
|
2525
|
+
const aTmp = this.acquireTemp(wType);
|
|
2526
|
+
this.pushInstruction({ op: "local.set", index: bTmp });
|
|
2527
|
+
this.pushInstruction({ op: "local.set", index: aTmp });
|
|
2528
|
+
this._emitComplexEq(aTmp, bTmp, info, info.elemWType, "==");
|
|
2529
|
+
this.releaseTemp(aTmp, wType);
|
|
2530
|
+
this.releaseTemp(bTmp, wType);
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2436
2533
|
// Equality of two array elements on the stack -> i32.
|
|
2437
2534
|
_emitElemEq(goType, wType) {
|
|
2438
2535
|
if (this._isValueArray(goType)) {
|
|
2439
2536
|
this._emitArrayEq(goType);
|
|
2537
|
+
} else if (isComplex(goType)) {
|
|
2538
|
+
this._emitComplexElemEq(goType, wType);
|
|
2440
2539
|
} else if (
|
|
2441
2540
|
isStructType(goType, this.mod.checker, this.mod) &&
|
|
2442
2541
|
!isPointerToStruct(goType, this.mod.checker, this.mod)
|
|
@@ -2599,6 +2698,8 @@ export class ExprsEmitter {
|
|
|
2599
2698
|
emitNarrowIntWrap(goType) {
|
|
2600
2699
|
if (!goType) return;
|
|
2601
2700
|
if (goType.kind === "TypeName" || goType.kind === "Ident") {
|
|
2701
|
+
const declared = this.mod.checker?.types?.get(goType.name);
|
|
2702
|
+
if (declared?.kind === "named") return this.emitNarrowIntWrap(declared);
|
|
2602
2703
|
return this.emitNarrowIntWrap({ kind: "basic", name: goType.name });
|
|
2603
2704
|
}
|
|
2604
2705
|
if (goType.kind === "named" && goType.underlying) {
|
|
@@ -2626,7 +2727,14 @@ export class ExprsEmitter {
|
|
|
2626
2727
|
}
|
|
2627
2728
|
|
|
2628
2729
|
emitDivRem(op, left, right, targetWasmType = null) {
|
|
2629
|
-
|
|
2730
|
+
// An untyped constant left operand takes the type of the typed right
|
|
2731
|
+
// one (`(1<<31) % uint32(n)` divides unsigned).
|
|
2732
|
+
const goType =
|
|
2733
|
+
left._type?.kind === "untyped" &&
|
|
2734
|
+
right._type &&
|
|
2735
|
+
right._type.kind !== "untyped"
|
|
2736
|
+
? right._type
|
|
2737
|
+
: left._type;
|
|
2630
2738
|
const wType = targetWasmType ?? toWasmType(goType, this.mod.checker);
|
|
2631
2739
|
|
|
2632
2740
|
if (wType === "f32" || wType === "f64") {
|