go expr handle referencing undeclared thread__tN#14@@ -0,0 +1,24 @@
1+// vtest build: false // This should be built with: `v -use-coroutines go_handle.v`
2+// Note: the Photon wrapper is not yet trivial enough to build/install on the CI.
3+//
4+// Demonstrates storing the handle returned by a `go` expression and waiting on
5+// it. Because the Photon coroutine wrapper exposes no joinable handle, a `go`
6+// whose handle is used is lowered to the regular `spawn` (pthread) path, so the
7+// handle can be waited on just like `spawn`.
8+module main
9+
10+struct App {
11+mut:
12+ worker thread
13+}
14+
15+fn (a App) loop() {
16+ println('hello from the worker coroutine')
17+}
18+
19+fn main() {
20+ mut a := App{}
21+ a.worker = go a.loop()
22+ a.worker.wait()
23+ println('done')
24+}
@@ -15,8 +15,19 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) {
1515 if node.call_expr.should_be_skipped {
1616 return
1717 }
18- is_spawn := mode == .spawn_
19- is_go := mode == .go_
18+ mut is_spawn := mode == .spawn_
19+ mut is_go := mode == .go_
20+ if is_go && node.is_expr {
21+ // A `go expr` whose handle is used as a value (e.g. `h := go f()` or
22+ // `obj.field = go f()`) needs a real, joinable thread handle. The photon
23+ // coroutine wrapper (`photon_thread_create*`) returns void and exposes no
24+ // such handle, so fall back to the regular `spawn` (pthread) path here,
25+ // which declares and assigns the `thread_<tmp>` handle that is read back
26+ // as the expression's value. Plain statement form `go f()` (no handle
27+ // used) keeps using the photon work-pool path below.
28+ is_spawn = true
29+ is_go = false
30+ }
2031 if is_spawn {
2132 g.writeln('/*spawn (thread) */')
2233 } else {
@@ -1075,6 +1075,20 @@ fn (mut w Walker) expr(node_ ast.Expr) {
10751075 ast.GoExpr {
10761076 if node.is_expr {
10771077 w.fn_by_name('free')
1078+ // A `go expr` whose handle is used as a value is lowered to the
1079+ // `spawn` (pthread) codegen path (see spawn_and_go_expr), since the
1080+ // photon coroutine wrapper returns no joinable handle. Mark the same
1081+ // thread-handle type and pthread error helpers that `spawn` needs,
1082+ // otherwise -skip-unused prunes them and the generated C fails to link.
1083+ w.mark_by_type(w.table.find_or_register_thread(node.call_expr.return_type))
1084+ w.uses_spawn = true
1085+ if w.pref.os == .windows {
1086+ w.fn_by_name('panic_lasterr')
1087+ w.fn_by_name('winapi_lasterr_str')
1088+ } else {
1089+ w.fn_by_name('c_error_number_str')
1090+ w.fn_by_name('panic_error_number')
1091+ }
10781092 }
10791093 w.mark_by_type(node.call_expr.return_type)
10801094 w.expr(node.call_expr)