Skip to main content

go_lib/
go_macro.rs

1// SPDX-License-Identifier: Apache-2.0
2//! `go!` and `select!` macros — public spawn/multiplex syntax.
3//!
4//! ## `go!`
5//!
6//! Spawns a closure as a new goroutine.  Equivalent to Go's `go f()`.
7//!
8//! ```no_run
9//! go_lib::run(|| {
10//!     go_lib::go!(|| println!("hello from goroutine"));
11//! });
12//! ```
13//!
14//! ## `select!`
15//!
16//! Multiplexes channel operations, picking the first ready case at random
17//! (Go's fairness guarantee).  Syntax mirrors Go's `select` statement:
18//!
19//! ```text
20//! select! {
21//!     recv(rx)     -> v => { /* v: Option<T> */ }
22//!     send(tx, val)    => { /* val was sent    */ }
23//!     default          => { /* nothing ready   */ }
24//! }
25//! ```
26//!
27//! - Recv arms bind the variable name given after `->` as `Option<T>`:
28//!   `Some(v)` on a normal receive, `None` if the channel was closed.
29//! - Send arms use `ManuallyDrop<T>` internally; the value is consumed when
30//!   the arm wins and dropped when the arm loses.
31//! - `default` makes the select non-blocking (taken when no other arm fires).
32//! - Without `default`, `select!` blocks until at least one case is ready.
33//!
34//! Arms may appear in any order. At most 4 recv and 2 send arms are supported
35//! per invocation.
36
37/// Spawn a closure as a new goroutine.
38///
39/// # Example
40///
41/// ```no_run
42/// go_lib::run(|| {
43///     go_lib::go!(|| {
44///         println!("running in a goroutine");
45///     });
46/// });
47/// ```
48#[macro_export]
49macro_rules! go {
50    ($body:expr) => {{
51        $crate::__spawn($body)
52    }};
53}
54
55// ---------------------------------------------------------------------------
56// Internal helper — shared dispatch pattern for recv-only selects.
57//
58// The macro_rules below are explicit-rule based (not tt-munching) so the
59// generated code is always in a single hygiene scope, giving each arm its
60// own uniquely-named stack slot without any counting trick.
61// ---------------------------------------------------------------------------
62
63/// Multiplex channel operations.
64///
65/// See [module-level documentation][crate::go_macro] for full syntax and
66/// semantics.
67///
68/// # Example — nonblocking recv with default
69///
70/// ```no_run
71/// use go_lib::chan::chan;
72/// go_lib::run(|| {
73///     let (tx, rx) = chan::<i32>(1);
74///     tx.send(42);
75///     go_lib::select! {
76///         recv(rx) -> v => {
77///             println!("received {:?}", v);
78///         }
79///         default => {
80///             println!("nothing ready");
81///         }
82///     }
83/// });
84/// ```
85#[macro_export]
86macro_rules! select {
87
88    // ─── A1: single recv, blocking ────────────────────────────────────────────
89    ( recv($r:expr) -> $v:ident => $b:block $(,)? ) => {{
90        let mut __r0: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
91        let mut __sel = ::std::vec![
92            $crate::select::recv_case_of(&($r), &mut __r0),
93        ];
94        let (_i, __ok) = unsafe { $crate::select::selectgo(&mut __sel, false) };
95        let $v: ::std::option::Option<_> = if __ok {
96            ::std::option::Option::Some(unsafe { __r0.assume_init() })
97        } else {
98            ::std::option::Option::None
99        };
100        $b
101    }};
102
103    // ─── B1: single recv + default ────────────────────────────────────────────
104    ( recv($r:expr) -> $v:ident => $b:block $(,)? default => $d:block $(,)? ) => {{
105        let mut __r0: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
106        let mut __sel = ::std::vec![
107            $crate::select::recv_case_of(&($r), &mut __r0),
108        ];
109        let (__idx, __ok) = unsafe { $crate::select::selectgo(&mut __sel, true) };
110        if __idx == 0 {
111            let $v: ::std::option::Option<_> = if __ok {
112                ::std::option::Option::Some(unsafe { __r0.assume_init() })
113            } else {
114                ::std::option::Option::None
115            };
116            $b
117        } else { $d }
118    }};
119
120    // ─── A2: two recv, blocking ───────────────────────────────────────────────
121    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
122      recv($r2:expr) -> $v2:ident => $b2:block $(,)? ) => {{
123        let mut __r0: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
124        let mut __r1: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
125        let mut __sel = ::std::vec![
126            $crate::select::recv_case_of(&($r1), &mut __r0),
127            $crate::select::recv_case_of(&($r2), &mut __r1),
128        ];
129        let (__idx, __ok) = unsafe { $crate::select::selectgo(&mut __sel, false) };
130        if __idx == 0 {
131            let $v1: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r0.assume_init() }) } else { ::std::option::Option::None };
132            $b1
133        } else {
134            let $v2: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r1.assume_init() }) } else { ::std::option::Option::None };
135            $b2
136        }
137    }};
138
139    // ─── B2: two recv + default ───────────────────────────────────────────────
140    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
141      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
142      default => $d:block $(,)? ) => {{
143        let mut __r0: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
144        let mut __r1: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
145        let mut __sel = ::std::vec![
146            $crate::select::recv_case_of(&($r1), &mut __r0),
147            $crate::select::recv_case_of(&($r2), &mut __r1),
148        ];
149        let (__idx, __ok) = unsafe { $crate::select::selectgo(&mut __sel, true) };
150        if __idx == 0 {
151            let $v1: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r0.assume_init() }) } else { ::std::option::Option::None };
152            $b1
153        } else if __idx == 1 {
154            let $v2: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r1.assume_init() }) } else { ::std::option::Option::None };
155            $b2
156        } else { $d }
157    }};
158
159    // ─── A3: three recv, blocking ─────────────────────────────────────────────
160    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
161      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
162      recv($r3:expr) -> $v3:ident => $b3:block $(,)? ) => {{
163        let mut __r0: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
164        let mut __r1: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
165        let mut __r2: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
166        let mut __sel = ::std::vec![
167            $crate::select::recv_case_of(&($r1), &mut __r0),
168            $crate::select::recv_case_of(&($r2), &mut __r1),
169            $crate::select::recv_case_of(&($r3), &mut __r2),
170        ];
171        let (__idx, __ok) = unsafe { $crate::select::selectgo(&mut __sel, false) };
172        if __idx == 0 {
173            let $v1: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r0.assume_init() }) } else { ::std::option::Option::None };
174            $b1
175        } else if __idx == 1 {
176            let $v2: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r1.assume_init() }) } else { ::std::option::Option::None };
177            $b2
178        } else {
179            let $v3: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r2.assume_init() }) } else { ::std::option::Option::None };
180            $b3
181        }
182    }};
183
184    // ─── B3: three recv + default ─────────────────────────────────────────────
185    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
186      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
187      recv($r3:expr) -> $v3:ident => $b3:block $(,)?
188      default => $d:block $(,)? ) => {{
189        let mut __r0: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
190        let mut __r1: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
191        let mut __r2: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
192        let mut __sel = ::std::vec![
193            $crate::select::recv_case_of(&($r1), &mut __r0),
194            $crate::select::recv_case_of(&($r2), &mut __r1),
195            $crate::select::recv_case_of(&($r3), &mut __r2),
196        ];
197        let (__idx, __ok) = unsafe { $crate::select::selectgo(&mut __sel, true) };
198        if __idx == 0 {
199            let $v1: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r0.assume_init() }) } else { ::std::option::Option::None };
200            $b1
201        } else if __idx == 1 {
202            let $v2: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r1.assume_init() }) } else { ::std::option::Option::None };
203            $b2
204        } else if __idx == 2 {
205            let $v3: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r2.assume_init() }) } else { ::std::option::Option::None };
206            $b3
207        } else { $d }
208    }};
209
210    // ─── A4: four recv, blocking ──────────────────────────────────────────────
211    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
212      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
213      recv($r3:expr) -> $v3:ident => $b3:block $(,)?
214      recv($r4:expr) -> $v4:ident => $b4:block $(,)? ) => {{
215        let mut __r0: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
216        let mut __r1: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
217        let mut __r2: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
218        let mut __r3: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
219        let mut __sel = ::std::vec![
220            $crate::select::recv_case_of(&($r1), &mut __r0),
221            $crate::select::recv_case_of(&($r2), &mut __r1),
222            $crate::select::recv_case_of(&($r3), &mut __r2),
223            $crate::select::recv_case_of(&($r4), &mut __r3),
224        ];
225        let (__idx, __ok) = unsafe { $crate::select::selectgo(&mut __sel, false) };
226        if __idx == 0 {
227            let $v1: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r0.assume_init() }) } else { ::std::option::Option::None };
228            $b1
229        } else if __idx == 1 {
230            let $v2: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r1.assume_init() }) } else { ::std::option::Option::None };
231            $b2
232        } else if __idx == 2 {
233            let $v3: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r2.assume_init() }) } else { ::std::option::Option::None };
234            $b3
235        } else {
236            let $v4: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r3.assume_init() }) } else { ::std::option::Option::None };
237            $b4
238        }
239    }};
240
241    // ─── B4: four recv + default ──────────────────────────────────────────────
242    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
243      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
244      recv($r3:expr) -> $v3:ident => $b3:block $(,)?
245      recv($r4:expr) -> $v4:ident => $b4:block $(,)?
246      default => $d:block $(,)? ) => {{
247        let mut __r0: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
248        let mut __r1: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
249        let mut __r2: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
250        let mut __r3: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
251        let mut __sel = ::std::vec![
252            $crate::select::recv_case_of(&($r1), &mut __r0),
253            $crate::select::recv_case_of(&($r2), &mut __r1),
254            $crate::select::recv_case_of(&($r3), &mut __r2),
255            $crate::select::recv_case_of(&($r4), &mut __r3),
256        ];
257        let (__idx, __ok) = unsafe { $crate::select::selectgo(&mut __sel, true) };
258        if __idx == 0 {
259            let $v1: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r0.assume_init() }) } else { ::std::option::Option::None };
260            $b1
261        } else if __idx == 1 {
262            let $v2: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r1.assume_init() }) } else { ::std::option::Option::None };
263            $b2
264        } else if __idx == 2 {
265            let $v3: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r2.assume_init() }) } else { ::std::option::Option::None };
266            $b3
267        } else if __idx == 3 {
268            let $v4: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r3.assume_init() }) } else { ::std::option::Option::None };
269            $b4
270        } else { $d }
271    }};
272
273    // ─── C1: single send + default (nonblocking send) ─────────────────────────
274    ( send($tx:expr, $sv:expr) => $sb:block $(,)? default => $d:block $(,)? ) => {{
275        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv);
276        let mut __sel = ::std::vec![
277            $crate::select::send_case_of(&($tx), &mut __s0),
278        ];
279        let (__idx, _ok) = unsafe { $crate::select::selectgo(&mut __sel, true) };
280        if __idx == 0 {
281            // Send won — value consumed, do NOT drop __s0.
282            $sb
283        } else {
284            // Default — drop the unsent value.
285            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
286            $d
287        }
288    }};
289
290    // ─── D1: 1 recv + 1 send, blocking ───────────────────────────────────────
291    ( recv($r1:expr) -> $v1:ident => $rb:block $(,)?
292      send($tx:expr, $sv:expr)    => $sb:block $(,)? ) => {{
293        let mut __r0: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
294        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv);
295        let mut __sel = ::std::vec![
296            $crate::select::recv_case_of(&($r1), &mut __r0),
297            $crate::select::send_case_of(&($tx), &mut __s0),
298        ];
299        let (__idx, __ok) = unsafe { $crate::select::selectgo(&mut __sel, false) };
300        if __idx == 0 {
301            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
302            let $v1: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r0.assume_init() }) } else { ::std::option::Option::None };
303            $rb
304        } else {
305            // Send won — value consumed.
306            $sb
307        }
308    }};
309
310    // ─── D2: 1 recv + 1 send + default ───────────────────────────────────────
311    ( recv($r1:expr) -> $v1:ident => $rb:block $(,)?
312      send($tx:expr, $sv:expr)    => $sb:block $(,)?
313      default                     => $d:block $(,)? ) => {{
314        let mut __r0: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
315        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv);
316        let mut __sel = ::std::vec![
317            $crate::select::recv_case_of(&($r1), &mut __r0),
318            $crate::select::send_case_of(&($tx), &mut __s0),
319        ];
320        let (__idx, __ok) = unsafe { $crate::select::selectgo(&mut __sel, true) };
321        if __idx == 0 {
322            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
323            let $v1: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r0.assume_init() }) } else { ::std::option::Option::None };
324            $rb
325        } else if __idx == 1 {
326            $sb
327        } else {
328            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
329            $d
330        }
331    }};
332
333    // ─── D3: 2 recv + 1 send, blocking ───────────────────────────────────────
334    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
335      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
336      send($tx:expr, $sv:expr)    => $sb:block $(,)? ) => {{
337        let mut __r0: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
338        let mut __r1: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
339        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv);
340        let mut __sel = ::std::vec![
341            $crate::select::recv_case_of(&($r1), &mut __r0),
342            $crate::select::recv_case_of(&($r2), &mut __r1),
343            $crate::select::send_case_of(&($tx), &mut __s0),
344        ];
345        let (__idx, __ok) = unsafe { $crate::select::selectgo(&mut __sel, false) };
346        if __idx == 0 {
347            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
348            let $v1: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r0.assume_init() }) } else { ::std::option::Option::None };
349            $b1
350        } else if __idx == 1 {
351            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
352            let $v2: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r1.assume_init() }) } else { ::std::option::Option::None };
353            $b2
354        } else {
355            $sb
356        }
357    }};
358
359    // ─── D4: 2 recv + 1 send + default ───────────────────────────────────────
360    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
361      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
362      send($tx:expr, $sv:expr)    => $sb:block $(,)?
363      default                     => $d:block $(,)? ) => {{
364        let mut __r0: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
365        let mut __r1: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
366        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv);
367        let mut __sel = ::std::vec![
368            $crate::select::recv_case_of(&($r1), &mut __r0),
369            $crate::select::recv_case_of(&($r2), &mut __r1),
370            $crate::select::send_case_of(&($tx), &mut __s0),
371        ];
372        let (__idx, __ok) = unsafe { $crate::select::selectgo(&mut __sel, true) };
373        if __idx == 0 {
374            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
375            let $v1: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r0.assume_init() }) } else { ::std::option::Option::None };
376            $b1
377        } else if __idx == 1 {
378            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
379            let $v2: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r1.assume_init() }) } else { ::std::option::Option::None };
380            $b2
381        } else if __idx == 2 {
382            $sb
383        } else {
384            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
385            $d
386        }
387    }};
388
389    // ─── D5: 3 recv + 1 send, blocking ───────────────────────────────────────
390    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
391      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
392      recv($r3:expr) -> $v3:ident => $b3:block $(,)?
393      send($tx:expr, $sv:expr)    => $sb:block $(,)? ) => {{
394        let mut __r0: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
395        let mut __r1: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
396        let mut __r2: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
397        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv);
398        let mut __sel = ::std::vec![
399            $crate::select::recv_case_of(&($r1), &mut __r0),
400            $crate::select::recv_case_of(&($r2), &mut __r1),
401            $crate::select::recv_case_of(&($r3), &mut __r2),
402            $crate::select::send_case_of(&($tx), &mut __s0),
403        ];
404        let (__idx, __ok) = unsafe { $crate::select::selectgo(&mut __sel, false) };
405        if __idx == 0 {
406            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
407            let $v1: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r0.assume_init() }) } else { ::std::option::Option::None };
408            $b1
409        } else if __idx == 1 {
410            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
411            let $v2: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r1.assume_init() }) } else { ::std::option::Option::None };
412            $b2
413        } else if __idx == 2 {
414            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
415            let $v3: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r2.assume_init() }) } else { ::std::option::Option::None };
416            $b3
417        } else {
418            $sb
419        }
420    }};
421
422    // ─── D6: 1 recv + 2 send, blocking ───────────────────────────────────────
423    ( recv($r1:expr) -> $v1:ident => $rb:block $(,)?
424      send($tx1:expr, $sv1:expr)  => $sb1:block $(,)?
425      send($tx2:expr, $sv2:expr)  => $sb2:block $(,)? ) => {{
426        let mut __r0: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
427        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv1);
428        let mut __s1: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv2);
429        let mut __sel = ::std::vec![
430            $crate::select::recv_case_of(&($r1), &mut __r0),
431            $crate::select::send_case_of(&($tx1), &mut __s0),
432            $crate::select::send_case_of(&($tx2), &mut __s1),
433        ];
434        let (__idx, __ok) = unsafe { $crate::select::selectgo(&mut __sel, false) };
435        if __idx == 0 {
436            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0); ::std::mem::ManuallyDrop::drop(&mut __s1) };
437            let $v1: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r0.assume_init() }) } else { ::std::option::Option::None };
438            $rb
439        } else if __idx == 1 {
440            // __s0 consumed; drop __s1
441            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s1) };
442            $sb1
443        } else {
444            // __s1 consumed; drop __s0
445            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
446            $sb2
447        }
448    }};
449
450    // ─── D7: 1 recv + 2 send + default ───────────────────────────────────────
451    ( recv($r1:expr) -> $v1:ident => $rb:block $(,)?
452      send($tx1:expr, $sv1:expr)  => $sb1:block $(,)?
453      send($tx2:expr, $sv2:expr)  => $sb2:block $(,)?
454      default                     => $d:block $(,)? ) => {{
455        let mut __r0: ::std::mem::MaybeUninit<_> = ::std::mem::MaybeUninit::uninit();
456        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv1);
457        let mut __s1: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv2);
458        let mut __sel = ::std::vec![
459            $crate::select::recv_case_of(&($r1), &mut __r0),
460            $crate::select::send_case_of(&($tx1), &mut __s0),
461            $crate::select::send_case_of(&($tx2), &mut __s1),
462        ];
463        let (__idx, __ok) = unsafe { $crate::select::selectgo(&mut __sel, true) };
464        if __idx == 0 {
465            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0); ::std::mem::ManuallyDrop::drop(&mut __s1) };
466            let $v1: ::std::option::Option<_> = if __ok { ::std::option::Option::Some(unsafe { __r0.assume_init() }) } else { ::std::option::Option::None };
467            $rb
468        } else if __idx == 1 {
469            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s1) };
470            $sb1
471        } else if __idx == 2 {
472            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
473            $sb2
474        } else {
475            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0); ::std::mem::ManuallyDrop::drop(&mut __s1) };
476            $d
477        }
478    }};
479}
480
481// ---------------------------------------------------------------------------
482// Tests
483// ---------------------------------------------------------------------------
484
485#[cfg(all(test, not(loom)))]
486#[allow(unused_assignments)] // sentinel initial values are always overwritten by select arms
487mod tests {
488    use crate::chan::chan;
489    use crate::runtime::sched::run_impl;
490    use std::sync::atomic::{AtomicI32, Ordering};
491    use std::sync::Arc;
492
493    // ── go! ───────────────────────────────────────────────────────────────────
494
495    /// go! spawns a goroutine that runs its closure.
496    #[test]
497    fn go_macro_spawns() {
498        let count = Arc::new(AtomicI32::new(0));
499        let c2    = Arc::clone(&count);
500        run_impl(move || {
501            go!(move || { c2.fetch_add(1, Ordering::Relaxed); });
502            // Yield enough times for the goroutine to run.
503            for _ in 0..50 { crate::gosched(); }
504        });
505        assert_eq!(count.load(Ordering::Acquire), 1);
506    }
507
508    // ── select! fast-path (no park) ───────────────────────────────────────────
509
510    /// B1: recv+default — data ready → recv arm taken.
511    #[test]
512    fn select_recv_default_data_ready() {
513        run_impl(|| {
514            let (tx, rx) = chan::<i32>(1);
515            tx.send(7);
516            let mut got = -1_i32;
517            select! {
518                recv(rx) -> v => { got = v.unwrap(); }
519                default      => { panic!("default should not fire"); }
520            }
521            assert_eq!(got, 7);
522        });
523    }
524
525    /// B1: recv+default — channel empty → default taken.
526    #[test]
527    fn select_default_when_empty() {
528        run_impl(|| {
529            let (_tx, rx) = chan::<i32>(1);
530            let mut took_default = false;
531            select! {
532                recv(rx) -> _v => { panic!("should not recv"); }
533                default        => { took_default = true; }
534            }
535            assert!(took_default);
536        });
537    }
538
539    /// B2: two recv + default — first channel has data.
540    #[test]
541    fn select_two_recv_first_ready() {
542        run_impl(|| {
543            let (tx1, rx1) = chan::<i32>(1);
544            let (_tx2, rx2) = chan::<i32>(1);
545            tx1.send(42);
546            let mut winner = -1_i32;
547            select! {
548                recv(rx1) -> v => { winner = v.unwrap(); }
549                recv(rx2) -> _v => { panic!("rx2 should not fire"); }
550                default => {}
551            }
552            assert_eq!(winner, 42);
553        });
554    }
555
556    /// C1: send+default — buffer has space → send arm taken.
557    #[test]
558    fn select_send_default_space_available() {
559        run_impl(|| {
560            let (tx, rx) = chan::<i32>(1);
561            let mut sent = false;
562            select! {
563                send(tx, 99_i32) => { sent = true; }
564                default          => { panic!("default should not fire"); }
565            }
566            assert!(sent);
567            assert_eq!(rx.recv(), Some(99));
568        });
569    }
570
571    /// C1: send+default — buffer full → default taken.
572    #[test]
573    fn select_send_default_buffer_full() {
574        run_impl(|| {
575            let (tx, rx) = chan::<i32>(1);
576            tx.send(1);   // fill the buffer
577            let mut took_default = false;
578            select! {
579                send(tx, 2_i32) => { panic!("should not send"); }
580                default         => { took_default = true; }
581            }
582            assert!(took_default);
583            assert_eq!(rx.recv(), Some(1));
584        });
585    }
586
587    /// D2: recv+send+default — recv channel has data, send buffer has space;
588    /// one of them fires, the other does not panic.
589    #[test]
590    fn select_recv_send_default() {
591        run_impl(|| {
592            let (tx1, rx1) = chan::<i32>(1);
593            let (tx2, rx2) = chan::<i32>(1);
594            tx1.send(10);
595            let mut recv_val = -1_i32;
596            let mut send_ok  = false;
597            // Both cases are ready; at least one fires.
598            select! {
599                recv(rx1) -> v  => { recv_val = v.unwrap(); }
600                send(tx2, 20_i32) => { send_ok = true; }
601                default         => {}
602            }
603            // At least one of recv_val or send_ok should have changed.
604            assert!(recv_val == 10 || send_ok);
605            let _ = rx2.try_recv(); // drain if sent
606        });
607    }
608
609    // ── select! blocking path ─────────────────────────────────────────────────
610
611    /// A1: single recv blocking — goroutine parks until sender fires.
612    #[test]
613    fn select_blocking_recv() {
614        let result = Arc::new(AtomicI32::new(-1));
615        let r2 = Arc::clone(&result);
616        run_impl(move || {
617            let (tx, rx) = chan::<i32>(0);
618            go!(move || { tx.send(55); });
619            select! {
620                recv(rx) -> v => { r2.store(v.unwrap(), Ordering::Relaxed); }
621            }
622        });
623        assert_eq!(result.load(Ordering::Acquire), 55);
624    }
625
626    /// A2: two recv blocking — whichever sender fires first wins.
627    #[test]
628    fn select_blocking_two_recv() {
629        let winner = Arc::new(AtomicI32::new(-1));
630        let w2 = Arc::clone(&winner);
631        run_impl(move || {
632            let (tx1, rx1) = chan::<i32>(0);
633            let (tx2, rx2) = chan::<i32>(0);
634            go!(move || { tx1.send(1); });
635            go!(move || { tx2.send(2); });
636            select! {
637                recv(rx1) -> v => { w2.store(v.unwrap(), Ordering::Relaxed); }
638                recv(rx2) -> v => { w2.store(v.unwrap(), Ordering::Relaxed); }
639            }
640        });
641        let w = winner.load(Ordering::Acquire);
642        assert!(w == 1 || w == 2, "winner should be 1 or 2, got {w}");
643    }
644
645    /// D1: recv+send blocking — one goroutine sends, one receives; select picks.
646    #[test]
647    fn select_blocking_recv_send() {
648        let recv_val = Arc::new(AtomicI32::new(-1));
649        let rv2 = Arc::clone(&recv_val);
650        run_impl(move || {
651            let (tx1, rx1) = chan::<i32>(0); // recv from this
652            let (tx2, rx2) = chan::<i32>(0); // send to this
653            // Goroutine that will satisfy the recv arm.
654            go!(move || { tx1.send(77); });
655            // Goroutine that drains if the send arm fires instead.
656            go!(move || {
657                // Give the main goroutine time to block.
658                crate::gosched();
659                let _ = rx2.recv();
660            });
661            select! {
662                recv(rx1) -> v      => { rv2.store(v.unwrap(), Ordering::Relaxed); }
663                send(tx2, 99_i32)   => {}
664            }
665        });
666        // Either recv gave us 77 or send fired (recv_val stays -1 → we got -1).
667        let v = recv_val.load(Ordering::Acquire);
668        assert!(v == 77 || v == -1, "unexpected value {v}");
669    }
670
671    /// recv from closed channel yields None via select.
672    #[test]
673    fn select_recv_closed_yields_none() {
674        run_impl(|| {
675            let (tx, rx) = chan::<i32>(0);
676            tx.close();
677            let mut ok_flag = true;
678            select! {
679                recv(rx) -> v => { ok_flag = v.is_some(); }
680            }
681            assert!(!ok_flag, "should be None for closed channel");
682        });
683    }
684}