@@ -4,6 +4,7 @@ package compiler
4
4
// pragmas, determines the link name, etc.
5
5
6
6
import (
7
+ "fmt"
7
8
"go/ast"
8
9
"go/token"
9
10
"go/types"
@@ -247,14 +248,14 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
247
248
linkName : f .RelString (nil ),
248
249
}
249
250
// Check for //go: pragmas, which may change the link name (among others).
250
- info .parsePragmas (f )
251
+ c .parsePragmas (& info , f )
251
252
c .functionInfos [f ] = info
252
253
return info
253
254
}
254
255
255
256
// parsePragmas is used by getFunctionInfo to parse function pragmas such as
256
257
// //export or //go:noinline.
257
- func (info * functionInfo ) parsePragmas (f * ssa.Function ) {
258
+ func (c * compilerContext ) parsePragmas (info * functionInfo , f * ssa.Function ) {
258
259
if f .Syntax () == nil {
259
260
return
260
261
}
@@ -294,10 +295,12 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) {
294
295
info .module = parts [1 ]
295
296
case "//go:wasmimport" :
296
297
// 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 {
299
301
continue
300
302
}
303
+ c .checkWasmImport (f , comment .Text )
301
304
info .exported = true
302
305
info .module = parts [1 ]
303
306
info .importName = parts [2 ]
@@ -358,6 +361,58 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) {
358
361
}
359
362
}
360
363
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
+
361
416
// getParams returns the function parameters, including the receiver at the
362
417
// start. This is an alternative to the Params member of *ssa.Function, which is
363
418
// not yet populated when the package has not yet been built.
0 commit comments