Skip to content

Commit b2f6694

Browse files
committed
compiler: disallow most types in //go:wasmimport
This is for compatibility with upstream Go. See golang/go#59149 for more context.
1 parent 6c59bdd commit b2f6694

File tree

4 files changed

+66
-13
lines changed

4 files changed

+66
-13
lines changed

compiler/symbol.go

+44-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package compiler
44
// pragmas, determines the link name, etc.
55

66
import (
7+
"fmt"
78
"go/ast"
89
"go/token"
910
"go/types"
@@ -254,7 +255,7 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
254255

255256
// parsePragmas is used by getFunctionInfo to parse function pragmas such as
256257
// //export or //go:noinline.
257-
func (info *functionInfo) parsePragmas(f *ssa.Function) {
258+
func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
258259
if f.Syntax() == nil {
259260
return
260261
}
@@ -294,10 +295,12 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) {
294295
info.module = parts[1]
295296
case "//go:wasmimport":
296297
// Import a WebAssembly function, for example a WASI function.
297-
// For details, see: https://github.com/golang/go/issues/38248
298-
if len(parts) != 3 || len(f.Blocks) != 0 {
298+
// Original proposal: https://github.com/golang/go/issues/38248
299+
// Allow globally: https://github.com/golang/go/issues/59149
300+
if len(parts) != 3 {
299301
continue
300302
}
303+
c.checkWasmImport(f, comment.Text)
301304
info.exported = true
302305
info.module = parts[1]
303306
info.importName = parts[2]
@@ -358,6 +361,44 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) {
358361
}
359362
}
360363

364+
// Check whether this function cannot be used in //go:wasmimport. It will add an
365+
// error if this is the case.
366+
//
367+
// The list of allowed types is based on this proposal:
368+
// https://github.com/golang/go/issues/59149
369+
func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
370+
if c.pkg.Path() == "runtime" {
371+
// The runtime is a special case. Allow all kinds of parameters
372+
// (importantly, including pointers).
373+
return
374+
}
375+
if f.Blocks != nil {
376+
// Defined functions cannot be exported.
377+
c.addError(f.Pos(), fmt.Sprintf("can only use //go:wasmimport on declarations"))
378+
return
379+
}
380+
for _, param := range f.Params {
381+
typ := param.Type().Underlying()
382+
// Check whether the type is allowed.
383+
// Only a very limited number of types can be mapped to WebAssembly.
384+
allowedType := false
385+
switch typ := typ.(type) {
386+
case *types.Basic:
387+
switch typ.Kind() {
388+
case types.Int32, types.Uint32, types.Int64, types.Uint64:
389+
allowedType = true
390+
case types.Float32, types.Float64:
391+
allowedType = true
392+
case types.UnsafePointer:
393+
allowedType = true
394+
}
395+
}
396+
if !allowedType {
397+
c.addError(param.Pos(), fmt.Sprintf("%s: unsupported parameter type %s", pragma, param.Type().String()))
398+
}
399+
}
400+
}
401+
361402
// getParams returns the function parameters, including the receiver at the
362403
// start. This is an alternative to the Params member of *ssa.Function, which is
363404
// not yet populated when the package has not yet been built.

compiler/testdata/errors.go

+22
Original file line numberDiff line numberDiff line change
@@ -1 +1,23 @@
11
package main
2+
3+
import "unsafe"
4+
5+
//go:wasmimport modulename empty
6+
func empty()
7+
8+
// ERROR: can only use //go:wasmimport on declarations
9+
//
10+
//go:wasmimport modulename implementation
11+
func implementation() {
12+
}
13+
14+
//go:wasmimport modulename validparam
15+
func validparam(a int32, b uint64, c float64, d unsafe.Pointer)
16+
17+
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type int
18+
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type string
19+
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type []byte
20+
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type *int32
21+
//
22+
//go:wasmimport modulename invalidparam
23+
func invalidparam(a int, b string, c []byte, d *int32)

compiler/testdata/pragma.go

-4
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,6 @@ func exportedFunctionInSection() {
6262
//go:wasmimport modulename import1
6363
func declaredImport()
6464

65-
//go:wasmimport modulename import2
66-
func definedImport() {
67-
}
68-
6965
// This function should not: it's only a declaration and not a definition.
7066
//
7167
//go:section .special_function_section

compiler/testdata/pragma.ll

-6
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,6 @@ entry:
6262

6363
declare void @main.declaredImport() #7
6464

65-
; Function Attrs: nounwind
66-
define hidden void @main.definedImport(ptr %context) unnamed_addr #2 {
67-
entry:
68-
ret void
69-
}
70-
7165
declare void @main.undefinedFunctionNotInSection(ptr) #1
7266

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

0 commit comments

Comments
 (0)