@@ -273,129 +273,132 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
273273 if syntax == nil {
274274 return
275275 }
276+
277+ // Read all pragmas of this function.
278+ var pragmas []* ast.Comment
279+ hasWasmExport := false
276280 if decl , ok := syntax .(* ast.FuncDecl ); ok && decl .Doc != nil {
277281 for _ , comment := range decl .Doc .List {
278282 text := comment .Text
279- if strings .HasPrefix (text , "//export " ) {
280- // Rewrite '//export' to '//go:export' for compatibility with
281- // gc.
282- text = "//go:" + text [2 :]
283+ if strings .HasPrefix (text , "//go:" ) || strings .HasPrefix (text , "//export " ) {
284+ pragmas = append (pragmas , comment )
285+ if strings .HasPrefix (comment .Text , "//go:wasmexport " ) {
286+ hasWasmExport = true
287+ }
283288 }
284- if ! strings .HasPrefix (text , "//go:" ) {
289+ }
290+ }
291+
292+ // Parse each pragma.
293+ for _ , comment := range pragmas {
294+ parts := strings .Fields (comment .Text )
295+ switch parts [0 ] {
296+ case "//export" , "//go:export" :
297+ if len (parts ) != 2 {
298+ continue
299+ }
300+ if hasWasmExport {
301+ // //go:wasmexport overrides //export.
285302 continue
286303 }
287- parts := strings .Fields (text )
288- switch parts [0 ] {
289- case "//go:export" :
290- if len (parts ) != 2 {
291- continue
292- }
293304
294- info .linkName = parts [1 ]
295- info .wasmName = info .linkName
296- info .exported = true
297- case "//go:interrupt" :
298- if hasUnsafeImport (f .Pkg .Pkg ) {
299- info .interrupt = true
300- }
301- case "//go:wasm-module" :
302- // Alternative comment for setting the import module.
303- // This is deprecated, use //go:wasmimport instead.
304- if len (parts ) != 2 {
305- continue
306- }
307- info .wasmModule = parts [1 ]
308- case "//go:wasmimport" :
309- // Import a WebAssembly function, for example a WASI function.
310- // Original proposal: https://github.com/golang/go/issues/38248
311- // Allow globally: https://github.com/golang/go/issues/59149
312- if len (parts ) != 3 {
313- continue
314- }
315- if f .Blocks != nil {
316- // Defined functions cannot be exported.
317- c .addError (f .Pos (), "can only use //go:wasmimport on declarations" )
318- continue
319- }
320- c .checkWasmImportExport (f , comment .Text )
321- info .exported = true
322- info .wasmModule = parts [1 ]
323- info .wasmName = parts [2 ]
324- case "//go:wasmexport" :
325- if f .Blocks == nil {
326- c .addError (f .Pos (), "can only use //go:wasmexport on definitions" )
327- continue
328- }
329- if len (parts ) != 2 {
330- c .addError (f .Pos (), fmt .Sprintf ("expected one parameter to //go:wasmimport, not %d" , len (parts )- 1 ))
331- continue
332- }
333- name := parts [1 ]
334- if name == "_start" || name == "_initialize" {
335- c .addError (f .Pos (), fmt .Sprintf ("//go:wasmexport does not allow %#v" , name ))
336- continue
337- }
338- if c .BuildMode != "c-shared" && f .RelString (nil ) == "main.main" {
339- c .addError (f .Pos (), fmt .Sprintf ("//go:wasmexport does not allow main.main to be exported with -buildmode=%s" , c .BuildMode ))
340- continue
341- }
342- if c .archFamily () != "wasm32" {
343- c .addError (f .Pos (), "//go:wasmexport is only supported on wasm" )
344- }
345- c .checkWasmImportExport (f , comment .Text )
346- info .wasmExport = name
347- info .wasmExportPos = comment .Slash
348- case "//go:inline" :
349- info .inline = inlineHint
350- case "//go:noinline" :
305+ info .linkName = parts [1 ]
306+ info .wasmName = info .linkName
307+ info .exported = true
308+ case "//go:interrupt" :
309+ if hasUnsafeImport (f .Pkg .Pkg ) {
310+ info .interrupt = true
311+ }
312+ case "//go:wasm-module" :
313+ // Alternative comment for setting the import module.
314+ // This is deprecated, use //go:wasmimport instead.
315+ if len (parts ) != 2 {
316+ continue
317+ }
318+ info .wasmModule = parts [1 ]
319+ case "//go:wasmimport" :
320+ // Import a WebAssembly function, for example a WASI function.
321+ // Original proposal: https://github.com/golang/go/issues/38248
322+ // Allow globally: https://github.com/golang/go/issues/59149
323+ if len (parts ) != 3 {
324+ continue
325+ }
326+ if f .Blocks != nil {
327+ // Defined functions cannot be exported.
328+ c .addError (f .Pos (), "can only use //go:wasmimport on declarations" )
329+ continue
330+ }
331+ c .checkWasmImportExport (f , comment .Text )
332+ info .exported = true
333+ info .wasmModule = parts [1 ]
334+ info .wasmName = parts [2 ]
335+ case "//go:wasmexport" :
336+ if f .Blocks == nil {
337+ c .addError (f .Pos (), "can only use //go:wasmexport on definitions" )
338+ continue
339+ }
340+ if len (parts ) != 2 {
341+ c .addError (f .Pos (), fmt .Sprintf ("expected one parameter to //go:wasmimport, not %d" , len (parts )- 1 ))
342+ continue
343+ }
344+ name := parts [1 ]
345+ if name == "_start" || name == "_initialize" {
346+ c .addError (f .Pos (), fmt .Sprintf ("//go:wasmexport does not allow %#v" , name ))
347+ continue
348+ }
349+ if c .BuildMode != "c-shared" && f .RelString (nil ) == "main.main" {
350+ c .addError (f .Pos (), fmt .Sprintf ("//go:wasmexport does not allow main.main to be exported with -buildmode=%s" , c .BuildMode ))
351+ continue
352+ }
353+ if c .archFamily () != "wasm32" {
354+ c .addError (f .Pos (), "//go:wasmexport is only supported on wasm" )
355+ }
356+ c .checkWasmImportExport (f , comment .Text )
357+ info .wasmExport = name
358+ info .wasmExportPos = comment .Slash
359+ case "//go:inline" :
360+ info .inline = inlineHint
361+ case "//go:noinline" :
362+ info .inline = inlineNone
363+ case "//go:linkname" :
364+ if len (parts ) != 3 || parts [1 ] != f .Name () {
365+ continue
366+ }
367+ // Only enable go:linkname when the package imports "unsafe".
368+ // This is a slightly looser requirement than what gc uses: gc
369+ // requires the file to import "unsafe", not the package as a
370+ // whole.
371+ if hasUnsafeImport (f .Pkg .Pkg ) {
372+ info .linkName = parts [2 ]
373+ }
374+ case "//go:section" :
375+ // Only enable go:section when the package imports "unsafe".
376+ // go:section also implies go:noinline since inlining could
377+ // move the code to a different section than that requested.
378+ if len (parts ) == 2 && hasUnsafeImport (f .Pkg .Pkg ) {
379+ info .section = parts [1 ]
351380 info .inline = inlineNone
352- case "//go:linkname" :
353- if len (parts ) != 3 || parts [1 ] != f .Name () {
354- continue
355- }
356- // Only enable go:linkname when the package imports "unsafe".
357- // This is a slightly looser requirement than what gc uses: gc
358- // requires the file to import "unsafe", not the package as a
359- // whole.
360- if hasUnsafeImport (f .Pkg .Pkg ) {
361- info .linkName = parts [2 ]
362- }
363- case "//go:section" :
364- // Only enable go:section when the package imports "unsafe".
365- // go:section also implies go:noinline since inlining could
366- // move the code to a different section than that requested.
367- if len (parts ) == 2 && hasUnsafeImport (f .Pkg .Pkg ) {
368- info .section = parts [1 ]
369- info .inline = inlineNone
370- }
371- case "//go:nobounds" :
372- // Skip bounds checking in this function. Useful for some
373- // runtime functions.
374- // This is somewhat dangerous and thus only imported in packages
375- // that import unsafe.
376- if hasUnsafeImport (f .Pkg .Pkg ) {
377- info .nobounds = true
378- }
379- case "//go:variadic" :
380- // The //go:variadic pragma is emitted by the CGo preprocessing
381- // pass for C variadic functions. This includes both explicit
382- // (with ...) and implicit (no parameters in signature)
383- // functions.
384- if strings .HasPrefix (f .Name (), "C." ) {
385- // This prefix cannot naturally be created, it must have
386- // been created as a result of CGo preprocessing.
387- info .variadic = true
388- }
381+ }
382+ case "//go:nobounds" :
383+ // Skip bounds checking in this function. Useful for some
384+ // runtime functions.
385+ // This is somewhat dangerous and thus only imported in packages
386+ // that import unsafe.
387+ if hasUnsafeImport (f .Pkg .Pkg ) {
388+ info .nobounds = true
389+ }
390+ case "//go:variadic" :
391+ // The //go:variadic pragma is emitted by the CGo preprocessing
392+ // pass for C variadic functions. This includes both explicit
393+ // (with ...) and implicit (no parameters in signature)
394+ // functions.
395+ if strings .HasPrefix (f .Name (), "C." ) {
396+ // This prefix cannot naturally be created, it must have
397+ // been created as a result of CGo preprocessing.
398+ info .variadic = true
389399 }
390400 }
391401 }
392-
393- // If both //go:wasmexport and //go:export or //export are declared,
394- // only honor go:wasmexport.
395- if info .wasmExport != "" {
396- // TODO: log warning?
397- info .exported = false
398- }
399402}
400403
401404// Check whether this function can be used in //go:wasmimport or
0 commit comments