@@ -7,6 +7,22 @@ use std::process::Command;
77
88pub struct Go ;
99
10+ /// Go-specific state, stored in `Runner`, detected during `prepare`.
11+ pub struct State {
12+ /// Whether the Go toolchain in use supports the `runtime.wasiOnIdle` hook
13+ /// required for component model async support.
14+ async_supported : bool ,
15+ }
16+
17+ impl Runner {
18+ fn go_async_supported ( & self ) -> bool {
19+ self . go_state
20+ . as_ref ( )
21+ . map ( |s| s. async_supported )
22+ . unwrap_or ( false )
23+ }
24+ }
25+
1026impl LanguageMethods for Go {
1127 fn display ( & self ) -> & str {
1228 "go"
@@ -18,19 +34,43 @@ impl LanguageMethods for Go {
1834
1935 fn should_fail_verify (
2036 & self ,
37+ runner : & Runner ,
2138 name : & str ,
2239 config : & crate :: config:: WitConfig ,
2340 _args : & [ String ] ,
2441 ) -> bool {
25- // TODO: We _do_ support async, but only with a build of Go that has
26- // [this
27- // patch](https://github.com/dicej/go/commit/40fc123d5bce6448fc4e4601fd33bad4250b36a5).
28- // Once we upstream something equivalent, we can remove the ` || name ==
29- // "async-trait-function.wit"` here.
30- config. error_context
31- || name == "async-trait-function.wit"
32- || name == "named-fixed-length-list.wit"
33- || name == "issue-1598.wit"
42+ if config. error_context {
43+ return true ;
44+ }
45+ if name == "named-fixed-length-list.wit" {
46+ return true ;
47+ }
48+ if !runner. go_async_supported ( ) {
49+ return name == "async-trait-function.wit" || name == "issue-1598.wit" ;
50+ }
51+
52+ false
53+ }
54+
55+ fn should_fail_compile (
56+ & self ,
57+ runner : & Runner ,
58+ path : & Path ,
59+ config : & crate :: config:: WitConfig ,
60+ ) -> bool {
61+ // This test, even though it's part of async, compiles on any
62+ // toolchain.
63+ if path. ends_with ( "incomplete-writes/leaf.go" ) {
64+ return false ;
65+ }
66+
67+ // Bindings for async tests rely on `runtime.wasiOnIdle` (see `prepare`
68+ // below) and fail to link without a toolchain that provides it.
69+ if !runner. go_async_supported ( ) {
70+ return config. async_ ;
71+ }
72+
73+ false
3474 }
3575
3676 fn default_bindgen_args_for_codegen ( & self ) -> & [ & str ] {
@@ -56,7 +96,45 @@ impl LanguageMethods for Go {
5696 . arg ( "build" )
5797 . arg ( "-buildmode=c-shared" )
5898 . arg ( "-ldflags=-checklinkname=0" ) ,
59- )
99+ ) ?;
100+
101+ // Component model async support requires a `runtime.wasiOnIdle` hook
102+ // which, as of the time of this writing, is only available in a
103+ // patched build of Go. Detect whether the toolchain in use has
104+ // this hook by building a program that links against it, and if not
105+ // then async tests are expected to fail to build.
106+ println ! ( "Testing if `go` supports `runtime.wasiOnIdle`..." ) ;
107+ let probe_dir = dir. join ( "wasi-on-idle-probe" ) ;
108+ super :: write_if_different (
109+ & probe_dir. join ( "main.go" ) ,
110+ r#"package main
111+
112+ import _ "unsafe"
113+
114+ //go:linkname wasiOnIdle runtime.wasiOnIdle
115+ func wasiOnIdle(callback func() bool)
116+ func init() { defer wasiOnIdle(func() bool { return false }) }
117+ func main() {}
118+ "# ,
119+ ) ?;
120+ super :: write_if_different ( & probe_dir. join ( "go.mod" ) , "module probe\n \n go 1.25" ) ?;
121+ let async_supported = runner
122+ . run_command (
123+ Command :: new ( "go" )
124+ . current_dir ( & probe_dir)
125+ . env ( "GOOS" , "wasip1" )
126+ . env ( "GOARCH" , "wasm" )
127+ . arg ( "build" )
128+ . arg ( "-o" )
129+ . arg ( "probe.wasm" )
130+ . arg ( "-buildmode=c-shared" )
131+ . arg ( "-ldflags=-checklinkname=0" ) ,
132+ )
133+ . is_ok ( ) ;
134+ println ! ( "`runtime.wasiOnIdle` supported: {async_supported}" ) ;
135+ runner. go_state = Some ( State { async_supported } ) ;
136+
137+ Ok ( ( ) )
60138 }
61139
62140 fn compile ( & self , runner : & Runner , compile : & Compile < ' _ > ) -> Result < ( ) > {
0 commit comments