Skip to content

Commit

Permalink
compiler: disallow most types in //go:wasmimport
Browse files Browse the repository at this point in the history
This is for compatibility with upstream Go.
See golang/go#59149 for more context.
  • Loading branch information
aykevl committed May 19, 2023
1 parent 6c59bdd commit b2f6694
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 13 deletions.
47 changes: 44 additions & 3 deletions compiler/symbol.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package compiler
// pragmas, determines the link name, etc.

import (
"fmt"
"go/ast"
"go/token"
"go/types"
Expand Down Expand Up @@ -254,7 +255,7 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {

// parsePragmas is used by getFunctionInfo to parse function pragmas such as
// //export or //go:noinline.
func (info *functionInfo) parsePragmas(f *ssa.Function) {
func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
if f.Syntax() == nil {
return
}
Expand Down Expand Up @@ -294,10 +295,12 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) {
info.module = parts[1]
case "//go:wasmimport":
// Import a WebAssembly function, for example a WASI function.
// For details, see: https://github.com/golang/go/issues/38248
if len(parts) != 3 || len(f.Blocks) != 0 {
// Original proposal: https://github.com/golang/go/issues/38248
// Allow globally: https://github.com/golang/go/issues/59149
if len(parts) != 3 {
continue
}
c.checkWasmImport(f, comment.Text)
info.exported = true
info.module = parts[1]
info.importName = parts[2]
Expand Down Expand Up @@ -358,6 +361,44 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) {
}
}

// Check whether this function cannot be used in //go:wasmimport. It will add an
// error if this is the case.
//
// The list of allowed types is based on this proposal:
// https://github.com/golang/go/issues/59149
func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
if c.pkg.Path() == "runtime" {
// The runtime is a special case. Allow all kinds of parameters
// (importantly, including pointers).
return
}
if f.Blocks != nil {
// Defined functions cannot be exported.
c.addError(f.Pos(), fmt.Sprintf("can only use //go:wasmimport on declarations"))
return
}
for _, param := range f.Params {
typ := param.Type().Underlying()
// Check whether the type is allowed.
// Only a very limited number of types can be mapped to WebAssembly.
allowedType := false
switch typ := typ.(type) {
case *types.Basic:
switch typ.Kind() {
case types.Int32, types.Uint32, types.Int64, types.Uint64:
allowedType = true
case types.Float32, types.Float64:
allowedType = true
case types.UnsafePointer:
allowedType = true
}
}
if !allowedType {
c.addError(param.Pos(), fmt.Sprintf("%s: unsupported parameter type %s", pragma, param.Type().String()))
}
}
}

// getParams returns the function parameters, including the receiver at the
// start. This is an alternative to the Params member of *ssa.Function, which is
// not yet populated when the package has not yet been built.
Expand Down
22 changes: 22 additions & 0 deletions compiler/testdata/errors.go
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
package main

import "unsafe"

//go:wasmimport modulename empty
func empty()

// ERROR: can only use //go:wasmimport on declarations
//
//go:wasmimport modulename implementation
func implementation() {
}

//go:wasmimport modulename validparam
func validparam(a int32, b uint64, c float64, d unsafe.Pointer)

// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type int
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type string
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type []byte
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type *int32
//
//go:wasmimport modulename invalidparam
func invalidparam(a int, b string, c []byte, d *int32)
4 changes: 0 additions & 4 deletions compiler/testdata/pragma.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,6 @@ func exportedFunctionInSection() {
//go:wasmimport modulename import1
func declaredImport()

//go:wasmimport modulename import2
func definedImport() {
}

// This function should not: it's only a declaration and not a definition.
//
//go:section .special_function_section
Expand Down
6 changes: 0 additions & 6 deletions compiler/testdata/pragma.ll
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,6 @@ entry:

declare void @main.declaredImport() #7

; Function Attrs: nounwind
define hidden void @main.definedImport(ptr %context) unnamed_addr #2 {
entry:
ret void
}

declare void @main.undefinedFunctionNotInSection(ptr) #1

attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
Expand Down

0 comments on commit b2f6694

Please sign in to comment.