Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 27 additions & 8 deletions compiler/symbol.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"strings"

"github.com/tinygo-org/tinygo/compiler/llvmutil"
"github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/loader"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
Expand Down Expand Up @@ -422,14 +423,14 @@ func (c *compilerContext) checkWasmImportExport(f *ssa.Function, pragma string)
c.addError(f.Signature.Results().At(1).Pos(), fmt.Sprintf("%s: too many return values", pragma))
} else if f.Signature.Results().Len() == 1 {
result := f.Signature.Results().At(0)
if !isValidWasmType(result.Type(), siteResult) {
if !c.isValidWasmType(result.Type(), siteResult) {
c.addError(result.Pos(), fmt.Sprintf("%s: unsupported result type %s", pragma, result.Type().String()))
}
}
for _, param := range f.Params {
// Check whether the type is allowed.
// Only a very limited number of types can be mapped to WebAssembly.
if !isValidWasmType(param.Type(), siteParam) {
if !c.isValidWasmType(param.Type(), siteParam) {
c.addError(param.Pos(), fmt.Sprintf("%s: unsupported parameter type %s", pragma, param.Type().String()))
}
}
Expand All @@ -442,13 +443,15 @@ func (c *compilerContext) checkWasmImportExport(f *ssa.Function, pragma string)
//
// This previously reflected the additional restrictions documented here:
// https://github.com/golang/go/issues/59149
func isValidWasmType(typ types.Type, site wasmSite) bool {
func (c *compilerContext) isValidWasmType(typ types.Type, site wasmSite) bool {
switch typ := typ.Underlying().(type) {
case *types.Basic:
switch typ.Kind() {
case types.Bool:
return true
case types.Int, types.Uint, types.Int8, types.Uint8, types.Int16, types.Uint16, types.Int32, types.Uint32, types.Int64, types.Uint64:
case types.Int8, types.Uint8, types.Int16, types.Uint16:
return site == siteIndirect
case types.Int32, types.Uint32, types.Int64, types.Uint64:
return true
case types.Float32, types.Float64:
return true
Expand All @@ -459,19 +462,35 @@ func isValidWasmType(typ types.Type, site wasmSite) bool {
return site == siteParam || site == siteIndirect
}
case *types.Array:
return site == siteIndirect && isValidWasmType(typ.Elem(), siteIndirect)
return site == siteIndirect && c.isValidWasmType(typ.Elem(), siteIndirect)
case *types.Struct:
if site != siteIndirect {
return false
}
// Structs with no fields do not need structs.HostLayout
if typ.NumFields() == 0 {
return true
}
hasHostLayout := true // default to true before detecting Go version
// (*types.Package).GoVersion added in go1.21
if gv, ok := any(c.pkg).(interface{ GoVersion() string }); ok {
if goenv.Compare(gv.GoVersion(), "go1.23") >= 0 {
hasHostLayout = false // package structs added in go1.23
Comment on lines +476 to +478
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting solution, I hadn't thought of that! Much cleaner than using build tags.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah! It's great when the only difference is a new method.

}
}
for i := 0; i < typ.NumFields(); i++ {
if !isValidWasmType(typ.Field(i).Type(), siteIndirect) {
ftyp := typ.Field(i).Type()
if ftyp.String() == "structs.HostLayout" {
hasHostLayout = true
continue
}
if !c.isValidWasmType(ftyp, siteIndirect) {
return false
}
}
return true
return hasHostLayout
case *types.Pointer:
return isValidWasmType(typ.Elem(), siteIndirect)
return c.isValidWasmType(typ.Elem(), siteIndirect)
}
return false
}
Expand Down
44 changes: 37 additions & 7 deletions compiler/testdata/errors.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package main

import "unsafe"
import (
"structs"
"unsafe"
)

//go:wasmimport modulename empty
func empty()
Expand All @@ -14,31 +17,37 @@ func implementation() {
type Uint uint32

type S struct {
_ structs.HostLayout
a [4]uint32
b uintptr
c int
d float32
e float64
}

//go:wasmimport modulename validparam
func validparam(a int32, b uint64, c float64, d unsafe.Pointer, e Uint, f uintptr, g string, h *int32, i *S)
func validparam(a int32, b uint64, c float64, d unsafe.Pointer, e Uint, f uintptr, g string, h *int32, i *S, j *struct{}, k *[8]uint8)

// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type [4]uint32
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type []byte
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type struct{a int}
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type chan struct{}
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type func()
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type int
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type uint
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type [8]int
//
//go:wasmimport modulename invalidparam
func invalidparam(a [4]uint32, b []byte, c struct{ a int }, d chan struct{}, e func())
func invalidparam(a [4]uint32, b []byte, c struct{ a int }, d chan struct{}, e func(), f int, g uint, h [8]int)

// ERROR: //go:wasmimport modulename invalidparam_no_hostlayout: unsupported parameter type *struct{int}
// ERROR: //go:wasmimport modulename invalidparam_no_hostlayout: unsupported parameter type *struct{string}
//
//go:wasmimport modulename invalidparam_no_hostlayout
func invalidparam_no_hostlayout(a *struct{ int }, b *struct{ string })

//go:wasmimport modulename validreturn_int32
func validreturn_int32() int32

//go:wasmimport modulename validreturn_int
func validreturn_int() int

//go:wasmimport modulename validreturn_ptr_int32
func validreturn_ptr_int32() *int32

Expand All @@ -48,6 +57,12 @@ func validreturn_ptr_string() *string
//go:wasmimport modulename validreturn_ptr_struct
func validreturn_ptr_struct() *S

//go:wasmimport modulename validreturn_ptr_struct
func validreturn_ptr_empty_struct() *struct{}

//go:wasmimport modulename validreturn_ptr_array
func validreturn_ptr_array() *[8]uint8

//go:wasmimport modulename validreturn_unsafe_pointer
func validreturn_unsafe_pointer() unsafe.Pointer

Expand All @@ -56,11 +71,26 @@ func validreturn_unsafe_pointer() unsafe.Pointer
//go:wasmimport modulename manyreturns
func manyreturns() (int32, int32)

// ERROR: //go:wasmimport modulename invalidreturn_int: unsupported result type int
//
//go:wasmimport modulename invalidreturn_int
func invalidreturn_int() int

// ERROR: //go:wasmimport modulename invalidreturn_int: unsupported result type uint
//
//go:wasmimport modulename invalidreturn_int
func invalidreturn_uint() uint

// ERROR: //go:wasmimport modulename invalidreturn_func: unsupported result type func()
//
//go:wasmimport modulename invalidreturn_func
func invalidreturn_func() func()

// ERROR: //go:wasmimport modulename invalidreturn_pointer_array_int: unsupported result type *[8]int
//
//go:wasmimport modulename invalidreturn_pointer_array_int
func invalidreturn_pointer_array_int() *[8]int

// ERROR: //go:wasmimport modulename invalidreturn_slice_byte: unsupported result type []byte
//
//go:wasmimport modulename invalidreturn_slice_byte
Expand Down
6 changes: 3 additions & 3 deletions testdata/wasmexport-noscheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ func hello() {
}

//go:wasmexport add
func add(a, b int) int {
func add(a, b int32) int32 {
println("called add:", a, b)
return a + b
}

//go:wasmimport tester callOutside
func callOutside(a, b int) int
func callOutside(a, b int32) int32

//go:wasmexport reentrantCall
func reentrantCall(a, b int) int {
func reentrantCall(a, b int32) int32 {
println("reentrantCall:", a, b)
result := callOutside(a, b)
println("reentrantCall result:", result)
Expand Down
10 changes: 5 additions & 5 deletions testdata/wasmexport.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ func hello() {
}

//go:wasmexport add
func add(a, b int) int {
func add(a, b int32) int32 {
println("called add:", a, b)
addInputs <- a
addInputs <- b
return <-addOutput
}

var addInputs = make(chan int)
var addOutput = make(chan int)
var addInputs = make(chan int32)
var addOutput = make(chan int32)

func adder() {
for {
Expand All @@ -41,10 +41,10 @@ func adder() {
}

//go:wasmimport tester callOutside
func callOutside(a, b int) int
func callOutside(a, b int32) int32

//go:wasmexport reentrantCall
func reentrantCall(a, b int) int {
func reentrantCall(a, b int32) int32 {
println("reentrantCall:", a, b)
result := callOutside(a, b)
println("reentrantCall result:", result)
Expand Down