makepad/libs/relax/compiler/tests/unwind_drops.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

306 lines
7.2 KiB
Rust

// R11: a panic runs the drops of every frame it unwinds through (rustc MIR cleanup blocks),
// in the order rustc runs them, before catch_unwind (or the thread's start) catches it.
// Each test prints its drop log as "| " lines; tests/std/core_diff.sh-style comparison with
// rustc: the lines must be identical.
use std::cell::RefCell;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
thread_local! { static LOG: RefCell<Vec<String>> = RefCell::new(Vec::new()); }
fn log(s: &str) {
LOG.with(|l| l.borrow_mut().push(s.to_string()));
}
fn take() -> String {
LOG.with(|l| std::mem::take(&mut *l.borrow_mut())).join(",")
}
struct D(&'static str);
impl Drop for D {
fn drop(&mut self) {
log(self.0);
}
}
fn quiet() {
std::panic::set_hook(Box::new(|_| {}));
}
fn inner(n: u32) -> u32 {
let _a = D("inner.a");
let _b = D("inner.b");
if n > 2 {
panic!("boom {}", n);
}
n
}
fn middle(n: u32) -> u32 {
let _m = D("middle.m");
let v = vec![D("vec.0"), D("vec.1")];
let r = inner(n) + v.len() as u32;
drop(v);
r
}
#[test]
fn a01_frames_and_locals() {
quiet();
let r = catch_unwind(|| {
let _o = D("outer.o");
middle(5)
});
println!("| caught {} {}", r.is_err(), take());
let r = catch_unwind(|| middle(1));
println!("| ok {:?} {}", r.ok(), take());
}
fn boom() -> u32 {
panic!("tmp")
}
fn two(_a: &D, b: u32) -> u32 {
b
}
#[test]
fn a02_statement_temporaries() {
quiet();
let r = catch_unwind(|| {
let _x = D("x");
two(&D("temp"), boom())
});
println!("| temps {} {}", r.is_err(), take());
}
#[test]
fn a03_moved_and_conditional() {
quiet();
let r = catch_unwind(|| {
let a = D("a");
let b = D("b");
let c = D("c");
drop(b);
let keep = if take_len() > 100 { Some(a) } else { None };
let _ = keep;
let moved = c;
std::mem::forget(moved);
let _d = D("d");
boom()
});
println!("| moved {} {}", r.is_err(), take());
}
fn take_len() -> usize {
LOG.with(|l| l.borrow().len())
}
#[test]
fn a04_loop_and_match() {
quiet();
let r = catch_unwind(|| {
let _outer = D("outer");
for i in 0..3 {
let _it = D(["it0", "it1", "it2"][i]);
match Some(D("m")) {
Some(_x) if i == 1 => {
boom();
}
_ => {}
}
}
});
println!("| loop {} {}", r.is_err(), take());
}
#[test]
fn a05_rc_box_dyn() {
quiet();
let rc = Rc::new(5);
let r = catch_unwind(AssertUnwindSafe(|| {
let _c1 = rc.clone();
let _c2 = rc.clone();
let _b: Box<dyn std::any::Any> = Box::new(D("boxed"));
boom()
}));
println!("| rc {} count {} {}", r.is_err(), Rc::strong_count(&rc), take());
}
#[test]
fn a06_mutex_guard_poison() {
quiet();
let m = Mutex::new(1);
let r = catch_unwind(AssertUnwindSafe(|| {
let mut g = m.lock().unwrap();
*g += 1;
let _t = D("held");
boom();
}));
let v = match m.lock() {
Ok(g) => *g,
Err(p) => *p.into_inner(),
};
println!("| mutex {} poisoned {} value {} {}", r.is_err(), m.is_poisoned(), v, take());
}
#[test]
fn a07_thread_join() {
quiet();
let shared = Arc::new(Mutex::new(Vec::<&'static str>::new()));
struct T(Arc<Mutex<Vec<&'static str>>>, &'static str);
impl Drop for T {
fn drop(&mut self) {
self.0.lock().unwrap().push(self.1);
}
}
let s2 = shared.clone();
let h = std::thread::spawn(move || {
let _a = T(s2.clone(), "thread.a");
let _b = T(s2, "thread.b");
boom()
});
let r = h.join();
println!("| thread {} {:?} strong {}", r.is_err(), shared.lock().unwrap(), Arc::strong_count(&shared));
}
#[test]
fn a08_closure_state_and_nested_catch() {
quiet();
let r = catch_unwind(|| {
let _a = D("a");
let inner = catch_unwind(|| {
let _b = D("b");
boom()
});
log(if inner.is_err() { "inner caught" } else { "inner ok" });
let f = move || {
let _c = D("c");
boom()
};
f()
});
println!("| nested {} {}", r.is_err(), take());
}
#[test]
fn a09_panicking_in_drop() {
quiet();
struct P(&'static str);
impl Drop for P {
fn drop(&mut self) {
log(&format!("{} panicking={}", self.0, std::thread::panicking()));
}
}
let _ = catch_unwind(|| {
let _p = P("p");
boom()
});
{
let _q = P("q");
}
println!("| panicking {} now={}", take(), std::thread::panicking());
}
struct PD(&'static str);
impl Drop for PD {
fn drop(&mut self) {
log(self.0);
if self.0.starts_with('p') {
panic!("drop {}", self.0);
}
}
}
#[test]
fn a10_panicking_drop_at_scope_end() {
quiet();
let r = catch_unwind(|| {
let _a = D("a");
let _p = PD("p1");
let _b = D("b");
let _t = (D("t0"), PD("q"), D("t2"));
});
println!("| scope end {} {}", r.is_err(), take());
let r = catch_unwind(|| {
let _x = D("x");
keep(&PD("ptemp"), 3);
log("after");
});
println!("| temp end {} {}", r.is_err(), take());
}
fn take2(_a: D, _b: u32) {}
impl D {
fn consume(self, _n: u32) {}
}
#[test]
fn a11_evaluated_operands() {
quiet();
let r = catch_unwind(|| take2(D("arg"), boom()));
println!("| args {} {}", r.is_err(), take());
let r = catch_unwind(|| D("recv").consume(boom()));
println!("| recv {} {}", r.is_err(), take());
let r = catch_unwind(|| {
let v = vec![D("e0"), D("e1")];
let t = (D("tup"), boom());
drop(v);
t.1
});
println!("| aggregate {} {}", r.is_err(), take());
}
fn keep<T>(_a: &T, b: u32) -> u32 {
b
}
struct Q(&'static str, bool);
impl Drop for Q {
fn drop(&mut self) {
log(self.0);
if self.1 {
panic!("drop {}", self.0);
}
}
}
struct TwoQ(Q, Q);
#[test]
fn a12_drop_and_replace() {
quiet();
let r = catch_unwind(|| {
let mut v = Q("old", true);
v = Q("new", false);
let _ = &v;
});
println!("| local {} {}", r.is_err(), take());
let r = catch_unwind(|| {
let mut x = (Q("a", false), Q("b", true));
x.1 = Q("c", false);
let _ = &x;
});
println!("| field {} {}", r.is_err(), take());
}
#[test]
fn a13_glue_continues() {
quiet();
let r = catch_unwind(|| {
let _t = TwoQ(Q("s0", true), Q("s1", false));
});
println!("| struct {} {}", r.is_err(), take());
let r = catch_unwind(|| {
let _a = [Q("e0", false), Q("e1", true), Q("e2", false)];
});
println!("| array {} {}", r.is_err(), take());
let r = catch_unwind(|| {
let _v = vec![Q("v0", true), Q("v1", false), Q("v2", false)];
});
println!("| vec {} {}", r.is_err(), take());
}
#[test]
fn a14_subslice_moves() {
{
let ar = [D("x0"), D("x1"), D("x2")];
let [_, _y @ ..] = ar;
log("bound");
drop(_y);
log("after");
}
println!("| {}", take());
}