mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-21 04:49:04 +00:00
compiler: conform to latest iteration of wasm types proposal (#4501)
compiler: align with current wasm types proposal https://github.com/golang/go/issues/66984 - Remove int and uint as allowed types in params, results, pointers, or struct fields - Only allow small integers in pointers, arrays, or struct fields - enforce structs.HostLayout usage per wasm types proposal https://github.com/golang/go/issues/66984 - require go1.23 for structs.HostLayout - use an interface to check if GoVersion() exists This permits TinyGo to compile with Go 1.21. - use goenv.Compare instead of WantGoVersion - testdata/wasmexport: use int32 instead of int - compiler/testdata: add structs.HostLayout - compiler/testdata: improve tests for structs.HostLayout
This commit is contained in:
+27
-8
@@ -12,6 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
"github.com/tinygo-org/tinygo/loader"
|
"github.com/tinygo-org/tinygo/loader"
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
@@ -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))
|
c.addError(f.Signature.Results().At(1).Pos(), fmt.Sprintf("%s: too many return values", pragma))
|
||||||
} else if f.Signature.Results().Len() == 1 {
|
} else if f.Signature.Results().Len() == 1 {
|
||||||
result := f.Signature.Results().At(0)
|
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()))
|
c.addError(result.Pos(), fmt.Sprintf("%s: unsupported result type %s", pragma, result.Type().String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, param := range f.Params {
|
for _, param := range f.Params {
|
||||||
// Check whether the type is allowed.
|
// Check whether the type is allowed.
|
||||||
// Only a very limited number of types can be mapped to WebAssembly.
|
// 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()))
|
c.addError(param.Pos(), fmt.Sprintf("%s: unsupported parameter type %s", pragma, param.Type().String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -442,13 +443,15 @@ func (c *compilerContext) checkWasmImportExport(f *ssa.Function, pragma string)
|
|||||||
//
|
//
|
||||||
// This previously reflected the additional restrictions documented here:
|
// This previously reflected the additional restrictions documented here:
|
||||||
// https://github.com/golang/go/issues/59149
|
// 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) {
|
switch typ := typ.Underlying().(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
switch typ.Kind() {
|
switch typ.Kind() {
|
||||||
case types.Bool:
|
case types.Bool:
|
||||||
return true
|
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
|
return true
|
||||||
case types.Float32, types.Float64:
|
case types.Float32, types.Float64:
|
||||||
return true
|
return true
|
||||||
@@ -459,19 +462,35 @@ func isValidWasmType(typ types.Type, site wasmSite) bool {
|
|||||||
return site == siteParam || site == siteIndirect
|
return site == siteParam || site == siteIndirect
|
||||||
}
|
}
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
return site == siteIndirect && isValidWasmType(typ.Elem(), siteIndirect)
|
return site == siteIndirect && c.isValidWasmType(typ.Elem(), siteIndirect)
|
||||||
case *types.Struct:
|
case *types.Struct:
|
||||||
if site != siteIndirect {
|
if site != siteIndirect {
|
||||||
return false
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
for i := 0; i < typ.NumFields(); i++ {
|
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 false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return hasHostLayout
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
return isValidWasmType(typ.Elem(), siteIndirect)
|
return c.isValidWasmType(typ.Elem(), siteIndirect)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+37
-7
@@ -1,6 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "unsafe"
|
import (
|
||||||
|
"structs"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
//go:wasmimport modulename empty
|
//go:wasmimport modulename empty
|
||||||
func empty()
|
func empty()
|
||||||
@@ -14,31 +17,37 @@ func implementation() {
|
|||||||
type Uint uint32
|
type Uint uint32
|
||||||
|
|
||||||
type S struct {
|
type S struct {
|
||||||
|
_ structs.HostLayout
|
||||||
a [4]uint32
|
a [4]uint32
|
||||||
b uintptr
|
b uintptr
|
||||||
c int
|
|
||||||
d float32
|
d float32
|
||||||
e float64
|
e float64
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:wasmimport modulename validparam
|
//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 [4]uint32
|
||||||
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type []byte
|
// 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 struct{a int}
|
||||||
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type chan struct{}
|
// 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 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
|
//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
|
//go:wasmimport modulename validreturn_int32
|
||||||
func validreturn_int32() int32
|
func validreturn_int32() int32
|
||||||
|
|
||||||
//go:wasmimport modulename validreturn_int
|
|
||||||
func validreturn_int() int
|
|
||||||
|
|
||||||
//go:wasmimport modulename validreturn_ptr_int32
|
//go:wasmimport modulename validreturn_ptr_int32
|
||||||
func validreturn_ptr_int32() *int32
|
func validreturn_ptr_int32() *int32
|
||||||
|
|
||||||
@@ -48,6 +57,12 @@ func validreturn_ptr_string() *string
|
|||||||
//go:wasmimport modulename validreturn_ptr_struct
|
//go:wasmimport modulename validreturn_ptr_struct
|
||||||
func validreturn_ptr_struct() *S
|
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
|
//go:wasmimport modulename validreturn_unsafe_pointer
|
||||||
func validreturn_unsafe_pointer() unsafe.Pointer
|
func validreturn_unsafe_pointer() unsafe.Pointer
|
||||||
|
|
||||||
@@ -56,11 +71,26 @@ func validreturn_unsafe_pointer() unsafe.Pointer
|
|||||||
//go:wasmimport modulename manyreturns
|
//go:wasmimport modulename manyreturns
|
||||||
func manyreturns() (int32, int32)
|
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()
|
// ERROR: //go:wasmimport modulename invalidreturn_func: unsupported result type func()
|
||||||
//
|
//
|
||||||
//go:wasmimport modulename invalidreturn_func
|
//go:wasmimport modulename invalidreturn_func
|
||||||
func invalidreturn_func() 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
|
// ERROR: //go:wasmimport modulename invalidreturn_slice_byte: unsupported result type []byte
|
||||||
//
|
//
|
||||||
//go:wasmimport modulename invalidreturn_slice_byte
|
//go:wasmimport modulename invalidreturn_slice_byte
|
||||||
|
|||||||
Vendored
+3
-3
@@ -18,16 +18,16 @@ func hello() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//go:wasmexport add
|
//go:wasmexport add
|
||||||
func add(a, b int) int {
|
func add(a, b int32) int32 {
|
||||||
println("called add:", a, b)
|
println("called add:", a, b)
|
||||||
return a + b
|
return a + b
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:wasmimport tester callOutside
|
//go:wasmimport tester callOutside
|
||||||
func callOutside(a, b int) int
|
func callOutside(a, b int32) int32
|
||||||
|
|
||||||
//go:wasmexport reentrantCall
|
//go:wasmexport reentrantCall
|
||||||
func reentrantCall(a, b int) int {
|
func reentrantCall(a, b int32) int32 {
|
||||||
println("reentrantCall:", a, b)
|
println("reentrantCall:", a, b)
|
||||||
result := callOutside(a, b)
|
result := callOutside(a, b)
|
||||||
println("reentrantCall result:", result)
|
println("reentrantCall result:", result)
|
||||||
|
|||||||
Vendored
+5
-5
@@ -21,15 +21,15 @@ func hello() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//go:wasmexport add
|
//go:wasmexport add
|
||||||
func add(a, b int) int {
|
func add(a, b int32) int32 {
|
||||||
println("called add:", a, b)
|
println("called add:", a, b)
|
||||||
addInputs <- a
|
addInputs <- a
|
||||||
addInputs <- b
|
addInputs <- b
|
||||||
return <-addOutput
|
return <-addOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
var addInputs = make(chan int)
|
var addInputs = make(chan int32)
|
||||||
var addOutput = make(chan int)
|
var addOutput = make(chan int32)
|
||||||
|
|
||||||
func adder() {
|
func adder() {
|
||||||
for {
|
for {
|
||||||
@@ -41,10 +41,10 @@ func adder() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//go:wasmimport tester callOutside
|
//go:wasmimport tester callOutside
|
||||||
func callOutside(a, b int) int
|
func callOutside(a, b int32) int32
|
||||||
|
|
||||||
//go:wasmexport reentrantCall
|
//go:wasmexport reentrantCall
|
||||||
func reentrantCall(a, b int) int {
|
func reentrantCall(a, b int32) int32 {
|
||||||
println("reentrantCall:", a, b)
|
println("reentrantCall:", a, b)
|
||||||
result := callOutside(a, b)
|
result := callOutside(a, b)
|
||||||
println("reentrantCall result:", result)
|
println("reentrantCall result:", result)
|
||||||
|
|||||||
Reference in New Issue
Block a user