makepad/libs/relax/compiler/tests/box_temp_move.rs
Admin b22a714840 makepad run: the Rust JIT (libs/relax) and makepad-remote
The JIT that runs makepad apps from source: lexer, front end, type checker,
lazy borrow checker (Gandalf; `--check-first` opts in), codegen, session and
warm/cold caches keyed by compiler stamp, provided crates bound late,
startup images, live patching. User-facing name is `makepad run` (binary
`makepad`, MAKEPAD_RUN_* env, ~/.makepad-run); libs/relax stays the internal
library name. `makepad check` reports rustc-style diagnostics with rustc
error codes and checks a whole workspace (`--workspace`). Runs on macOS,
Linux, Windows and Android. makepad-remote: one tool schema for apps and the
WM (app/1): grabs, captures, traces, frame profiles and tweaks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 12:26:21 +02:00

108 lines
2.8 KiB
Rust

// Moving out of `*tmp` where tmp is a temporary Box (`*opt.take()?`, `*opt.take().unwrap()`, `*Box::new(..)`):
// the temporary is freed without dropping the moved contents again (rustc drop elaboration: box_free only).
// makepad-script's end_literal_trace (`let mut trace = *self.bx.literal_trace.take()?;`) double-freed its
// HashSets under Relax (makepad-stage: bus error in free() when a Motion document failed to load).
// "| " lines equal rustc's.
#[derive(Default)]
struct T {
s: Vec<u64>,
n: Vec<u64>,
}
struct B {
t: Option<Box<T>>,
}
fn fresh(i: u64) -> B {
B { t: Some(Box::new(T { s: vec![i; 3], n: vec![i; 2] })) }
}
fn via_try(b: &mut B) -> Option<T> {
let t = *b.t.take()?;
Some(t)
}
fn via_try_tail(b: &mut B) -> Option<T> {
Some(*b.t.take()?)
}
fn via_unwrap(b: &mut B) -> T {
*b.t.take().unwrap()
}
fn via_new(i: u64) -> T {
*Box::new(T { s: vec![i; 4], n: vec![i] })
}
fn field_of_temp(b: &mut B) -> Option<Vec<u64>> {
Some((*b.t.take()?).s)
}
fn let_pattern(b: &mut B) -> Option<Vec<u64>> {
let T { s, .. } = *b.t.take()?;
Some(s)
}
#[test]
fn a01_temp_box_moves() {
let mut sum = 0usize;
for i in 0..3000u64 {
let mut b = fresh(i);
let t = via_try(&mut b).unwrap();
sum += t.s.len() + t.n.len();
assert!(via_try(&mut b).is_none());
let mut b = fresh(i);
sum += via_try_tail(&mut b).map_or(0, |t| t.s.len());
let mut b = fresh(i);
sum += via_unwrap(&mut b).n.len();
sum += via_new(i).s.len();
let mut b = fresh(i);
sum += field_of_temp(&mut b).unwrap().len();
let mut b = fresh(i);
sum += let_pattern(&mut b).unwrap().len();
}
println!("| sum {}", sum);
}
#[test]
fn a02_copy_contents() {
// contents without drop glue: the temporary is dropped as before
let n: u64 = *Box::new(41u64) + 1;
let o: Option<Box<(u8, u16)>> = Some(Box::new((1, 2)));
let (a, b) = *o.unwrap();
println!("| copy {} {} {}", n, a, b);
}
static DROPS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
struct D(u32);
impl Drop for D {
fn drop(&mut self) {
DROPS.fetch_add(self.0 as usize, std::sync::atomic::Ordering::Relaxed);
}
}
struct P {
a: D,
b: D,
}
fn drops() -> usize {
DROPS.swap(0, std::sync::atomic::Ordering::Relaxed)
}
#[test]
fn a03_drop_counts() {
drops();
let mut o = Some(Box::new(P { a: D(1), b: D(10) }));
let p = *o.take().unwrap();
println!("| whole moved, before drop {}", drops());
drop(p);
println!("| whole dropped {}", drops());
let o = Some(Box::new(P { a: D(1), b: D(10) }));
let P { b, .. } = *o.unwrap();
println!("| pattern b moved: rest dropped {}", drops());
drop(b);
println!("| pattern b dropped {}", drops());
}