Skip to content

Commit b08ff17

Browse files
aykevldeadprogram
authored andcommitted
compiler: disallow most types in //go:wasmimport
This is for compatibility with upstream Go. See golang/go#59149 for more context.
1 parent 41e787d commit b08ff17

File tree

4 files changed

+101
-14
lines changed

4 files changed

+101
-14
lines changed

compiler/symbol.go

+59-4
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"
@@ -247,14 +248,14 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
247248
linkName: f.RelString(nil),
248249
}
249250
// Check for //go: pragmas, which may change the link name (among others).
250-
info.parsePragmas(f)
251+
c.parsePragmas(&info, f)
251252
c.functionInfos[f] = info
252253
return info
253254
}
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,58 @@ 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+
if f.Signature.Results().Len() > 1 {
381+
c.addError(f.Signature.Results().At(1).Pos(), fmt.Sprintf("%s: too many return values", pragma))
382+
} else if f.Signature.Results().Len() == 1 {
383+
result := f.Signature.Results().At(0)
384+
if !isValidWasmType(result.Type(), true) {
385+
c.addError(result.Pos(), fmt.Sprintf("%s: unsupported result type %s", pragma, result.Type().String()))
386+
}
387+
}
388+
for _, param := range f.Params {
389+
// Check whether the type is allowed.
390+
// Only a very limited number of types can be mapped to WebAssembly.
391+
if !isValidWasmType(param.Type(), false) {
392+
c.addError(param.Pos(), fmt.Sprintf("%s: unsupported parameter type %s", pragma, param.Type().String()))
393+
}
394+
}
395+
}
396+
397+
// Check whether the type maps directly to a WebAssembly type, according to:
398+
// https://github.com/golang/go/issues/59149
399+
func isValidWasmType(typ types.Type, isReturn bool) bool {
400+
switch typ := typ.Underlying().(type) {
401+
case *types.Basic:
402+
switch typ.Kind() {
403+
case types.Int32, types.Uint32, types.Int64, types.Uint64:
404+
return true
405+
case types.Float32, types.Float64:
406+
return true
407+
case types.UnsafePointer:
408+
if !isReturn {
409+
return true
410+
}
411+
}
412+
}
413+
return false
414+
}
415+
361416
// getParams returns the function parameters, including the receiver at the
362417
// start. This is an alternative to the Params member of *ssa.Function, which is
363418
// not yet populated when the package has not yet been built.

compiler/testdata/errors.go

+42
Original file line numberDiff line numberDiff line change
@@ -1 +1,43 @@
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+
type Uint uint32
15+
16+
//go:wasmimport modulename validparam
17+
func validparam(a int32, b uint64, c float64, d unsafe.Pointer, e Uint)
18+
19+
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type int
20+
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type string
21+
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type []byte
22+
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type *int32
23+
//
24+
//go:wasmimport modulename invalidparam
25+
func invalidparam(a int, b string, c []byte, d *int32)
26+
27+
//go:wasmimport modulename validreturn
28+
func validreturn() int32
29+
30+
// ERROR: //go:wasmimport modulename manyreturns: too many return values
31+
//
32+
//go:wasmimport modulename manyreturns
33+
func manyreturns() (int32, int32)
34+
35+
// ERROR: //go:wasmimport modulename invalidreturn: unsupported result type int
36+
//
37+
//go:wasmimport modulename invalidreturn
38+
func invalidreturn() int
39+
40+
// ERROR: //go:wasmimport modulename invalidUnsafePointerReturn: unsupported result type unsafe.Pointer
41+
//
42+
//go:wasmimport modulename invalidUnsafePointerReturn
43+
func invalidUnsafePointerReturn() unsafe.Pointer

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)