makepad/libs/relax/compiler/tests/async_box_recursion.rs
Admin d67fa511c1 makepad run / Relax: no crash after the zone swap from borrowck state; swap fixture
- the borrowck USER_TYPES buffer grows in the kept heap, not the compiler
  generation the zone swap destroys (Calculator crashed after the swap)
- tools/run/fixture/zone_swap.sh forces a swap and fails on a fault, a
  use-after-destroy, a panic or a nonzero kept-scan
- Box::pin(f(n-1)).await in an async fn lowers (no false E0733)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 09:37:24 +02:00

58 lines
1.7 KiB
Rust

// A recursive async fn through Box::pin: the boxed future is a pointer, so the future type does
// not contain itself (rustc accepts it; E0733 is only for recursion by value). The poll function
// builds the callee future as a temporary and moves it into the box, which needs the future's
// layout before its own saved locals are known.
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
fn noop_raw() -> RawWaker {
fn clone(_: *const ()) -> RawWaker { noop_raw() }
fn noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(clone, noop, noop, noop);
RawWaker::new(std::ptr::null(), &VT)
}
fn block_on<F: Future>(mut f: F) -> F::Output {
let waker = unsafe { Waker::from_raw(noop_raw()) };
let mut cx = Context::from_waker(&waker);
let mut f = unsafe { Pin::new_unchecked(&mut f) };
loop {
if let Poll::Ready(v) = f.as_mut().poll(&mut cx) {
return v;
}
}
}
struct Yield(bool);
impl Future for Yield {
type Output = ();
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
if self.0 { Poll::Ready(()) } else { self.0 = true; Poll::Pending }
}
}
async fn sum(n: u32) -> u32 {
if n == 0 {
0
} else {
Yield(false).await;
n + Box::pin(sum(n - 1)).await
}
}
async fn tree(depth: u32) -> u32 {
if depth == 0 {
return 1;
}
let left = Box::pin(tree(depth - 1)).await;
Yield(false).await;
let right = Box::pin(tree(depth - 1)).await;
left + right
}
#[test]
fn boxed_self_recursion() {
assert_eq!(block_on(sum(10)), 55);
assert_eq!(block_on(tree(5)), 32);
}