Skip to content

Commit 0521583

Browse files
authored
Merge pull request #286 from bytecodealliance/ydnar/issue284
wit/bindgen: do not use bool for result or variant shapes
2 parents c8e7a96 + 8cf144f commit 0521583

File tree

11 files changed

+143
-6
lines changed

11 files changed

+143
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
1414
- Breaking: generated `*.wasm.go` files will now have correct WIT kebab-case base name. Interfaces or worlds with `-` in their name will require removal of the previous `*.wasm.go` files.
1515
- Dropped support for TinyGo v0.32.0.
1616

17+
### Fixed
18+
19+
- [#284](https://github.com/bytecodealliance/go-modules/issues/284): do not use `bool` for `variant` or `result` GC shapes. TinyGo returns `result` and `variant` values with `bool` as 0 or 1, which breaks the memory representation of tagged unions (variants).
20+
1721
## [v0.5.0] — 2024-12-14
1822

1923
### Changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,7 @@ test:
5353
tinygo test -target=wasip1 $(GOTESTARGS) $(GOTESTMODULES)
5454
tinygo test -target=wasip2 $(GOTESTARGS) $(GOTESTMODULES)
5555
tinygo test -target=wasip2 $(GOTESTARGS) ./tests/...
56+
57+
.PHONY: tests
58+
tests:
59+
tinygo test -target=wasip2 $(GOTESTARGS) ./tests/...

cm/result_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cm
22

33
import (
4+
"fmt"
45
"runtime"
56
"testing"
67
"unsafe"
@@ -238,3 +239,98 @@ func TestIssue95BoolInt64(t *testing.T) {
238239
t.Errorf("*res.OK(): %v, expected %v", got, want)
239240
}
240241
}
242+
243+
func TestIssue284(t *testing.T) {
244+
// Using int8 instead of bool for shape works on Go and TinyGo.
245+
// See https://github.com/bytecodealliance/go-modules/issues/284
246+
type BoolS8Result Result[int8, bool, int8]
247+
248+
tests := []struct {
249+
isErr bool
250+
ok bool
251+
err int8
252+
}{
253+
{false, false, 0},
254+
{false, true, 0},
255+
{true, false, 0},
256+
{true, false, 1},
257+
{true, false, 5},
258+
{true, false, 126},
259+
{true, false, 127},
260+
}
261+
for _, tt := range tests {
262+
if !tt.isErr {
263+
t.Run(fmt.Sprintf("OK[BoolS8Result](%t)", tt.ok), func(t *testing.T) {
264+
r := OK[BoolS8Result](tt.ok)
265+
ok, _, isErr := r.Result()
266+
if isErr != tt.isErr {
267+
t.Errorf("isErr == %t, expected %t", isErr, tt.isErr)
268+
}
269+
if ok != tt.ok {
270+
t.Errorf("ok == %t, expected %t", ok, tt.ok)
271+
}
272+
})
273+
} else {
274+
t.Run(fmt.Sprintf("Err[BoolS8Result](%d)", tt.err), func(t *testing.T) {
275+
r := Err[BoolS8Result](tt.err)
276+
_, err, isErr := r.Result()
277+
if isErr != tt.isErr {
278+
t.Errorf("isErr == %t, expected %t", isErr, tt.isErr)
279+
}
280+
if err != tt.err {
281+
t.Errorf("err == %d, expected %d", err, tt.err)
282+
}
283+
})
284+
}
285+
}
286+
}
287+
288+
func TestIssue284NotTinyGo(t *testing.T) {
289+
if runtime.Compiler == "tinygo" {
290+
return
291+
}
292+
293+
// Using bool as result shape changes how [OK] returns the result by value.
294+
// This works on Go, but breaks on TinyGo / LLVM.
295+
// See https://github.com/bytecodealliance/go-modules/issues/284
296+
type BoolS8Result Result[bool, bool, int8]
297+
298+
tests := []struct {
299+
isErr bool
300+
ok bool
301+
err int8
302+
}{
303+
{false, false, 0},
304+
{false, true, 0},
305+
{true, false, 0},
306+
{true, false, 1},
307+
{true, false, 5},
308+
{true, false, 126},
309+
{true, false, 127},
310+
}
311+
for _, tt := range tests {
312+
if !tt.isErr {
313+
t.Run(fmt.Sprintf("OK[BoolS8Result](%t)", tt.ok), func(t *testing.T) {
314+
r := OK[BoolS8Result](tt.ok)
315+
ok, _, isErr := r.Result()
316+
if isErr != tt.isErr {
317+
t.Errorf("isErr == %t, expected %t", isErr, tt.isErr)
318+
}
319+
if ok != tt.ok {
320+
t.Errorf("ok == %t, expected %t", ok, tt.ok)
321+
}
322+
})
323+
} else {
324+
t.Run(fmt.Sprintf("Err[BoolS8Result](%d)", tt.err), func(t *testing.T) {
325+
r := Err[BoolS8Result](tt.err)
326+
_, err, isErr := r.Result()
327+
if isErr != tt.isErr {
328+
t.Errorf("isErr == %t, expected %t", isErr, tt.isErr)
329+
}
330+
if err != tt.err {
331+
t.Errorf("err == %d, expected %d", err, tt.err)
332+
}
333+
})
334+
}
335+
}
336+
}

tests/generated/issues/issue284/i/i.wit.go

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/generated/issues/issue284/w/w.wit.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/generated/wasi/sockets/v0.2.0/tcp/abi.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/generated/wasi/sockets/v0.2.0/tcp/tcp.wasm.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/generated/wasi/sockets/v0.2.0/tcp/tcp.wit.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/generated/wasi/sockets/v0.2.0/udp/abi.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wit/bindgen/abi.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"go.bytecodealliance.org/wit"
77
)
88

9-
// variantShape returns the type with the greatest size.
9+
// variantShape returns the type with the greatest size that is not a bool.
1010
// If there are multiple types with the same size, it returns
1111
// the first type that contains a pointer.
1212
func variantShape(types []wit.Type) wit.Type {
@@ -19,6 +19,12 @@ func variantShape(types []wit.Type) wit.Type {
1919
return -1
2020
case a.Size() < b.Size():
2121
return 1
22+
case !isBool(a) && isBool(b):
23+
// bool cannot be used as variant shape
24+
// See https://github.com/bytecodealliance/go-modules/issues/284
25+
return -1
26+
case isBool(a) && !isBool(b):
27+
return 1
2228
case wit.HasPointer(a) && !wit.HasPointer(b):
2329
return -1
2430
case !wit.HasPointer(a) && wit.HasPointer(b):
@@ -30,6 +36,16 @@ func variantShape(types []wit.Type) wit.Type {
3036
return types[0]
3137
}
3238

39+
func isBool(t wit.TypeDefKind) bool {
40+
switch t := t.(type) {
41+
case wit.Bool:
42+
return true
43+
case *wit.TypeDef:
44+
return isBool(t.Root().Kind)
45+
}
46+
return false
47+
}
48+
3349
// variantAlign returns the type with the largest alignment.
3450
func variantAlign(types []wit.Type) wit.Type {
3551
if len(types) == 0 {

0 commit comments

Comments
 (0)