makepad/libs/relax/compiler/std/check/tests/vec_dedup.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

109 lines
3.1 KiB
Rust

//! Relax std Vec::dedup_by vs real std: results and panic safety (a panicking same_bucket or Drop
//! leaves the Vec valid; every element is dropped exactly once).
use relax_std_check::vec::Vec as HVec;
use std::cell::Cell;
use std::panic::{catch_unwind, AssertUnwindSafe};
thread_local! {
static DROPS: Cell<usize> = Cell::new(0);
static PANIC_DROP: Cell<u32> = Cell::new(u32::MAX);
}
#[derive(Debug, PartialEq)]
struct D(u32);
impl Drop for D {
fn drop(&mut self) {
DROPS.with(|d| d.set(d.get() + 1));
if PANIC_DROP.with(|p| p.get()) == self.0 {
PANIC_DROP.with(|p| p.set(u32::MAX));
panic!("drop boom");
}
}
}
fn drops() -> usize {
DROPS.with(|d| d.get())
}
const INPUT: [u32; 11] = [1, 1, 2, 2, 3, 4, 4, 4, 5, 6, 6];
// Runs dedup_by with a same_bucket that panics on call `at` (or a Drop that panics for value
// `drop_panic`), returns (panicked, drops during, remaining values, total drops).
fn run_real(at: usize, drop_panic: u32) -> (bool, usize, Vec<u32>, usize) {
let before = drops();
let mut v: Vec<D> = INPUT.iter().map(|&x| D(x)).collect();
let mut calls = 0usize;
PANIC_DROP.with(|p| p.set(drop_panic));
let r = catch_unwind(AssertUnwindSafe(|| {
v.dedup_by(|a, b| {
if calls == at {
panic!("boom");
}
calls += 1;
a.0 == b.0
})
}));
PANIC_DROP.with(|p| p.set(u32::MAX));
let during = drops() - before;
let left: Vec<u32> = v.iter().map(|d| d.0).collect();
drop(v);
(r.is_err(), during, left, drops() - before)
}
fn run_relax(at: usize, drop_panic: u32) -> (bool, usize, Vec<u32>, usize) {
let before = drops();
let mut v: HVec<D> = HVec::new();
for &x in INPUT.iter() {
v.push(D(x));
}
let mut calls = 0usize;
PANIC_DROP.with(|p| p.set(drop_panic));
let r = catch_unwind(AssertUnwindSafe(|| {
v.dedup_by(|a, b| {
if calls == at {
panic!("boom");
}
calls += 1;
a.0 == b.0
})
}));
PANIC_DROP.with(|p| p.set(u32::MAX));
let during = drops() - before;
let left: Vec<u32> = v.iter().map(|d| d.0).collect();
drop(v);
(r.is_err(), during, left, drops() - before)
}
#[test]
fn dedup_by_panic_safety() {
std::panic::set_hook(Box::new(|_| {}));
for at in 0..12 {
assert_eq!(run_relax(at, u32::MAX), run_real(at, u32::MAX), "same_bucket panics at call {}", at);
}
for dp in [1u32, 2, 4, 6] {
assert_eq!(run_relax(usize::MAX, dp), run_real(usize::MAX, dp), "drop of {} panics", dp);
}
let _ = std::panic::take_hook();
}
#[test]
fn dedup_results() {
let mut x = 0x2545F4914F6CDD1Du64;
for len in 0..200usize {
let mut a: Vec<u32> = Vec::new();
for _ in 0..len {
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
a.push((x % 4) as u32);
}
let mut h: HVec<u32> = HVec::new();
for &e in a.iter() {
h.push(e);
}
a.dedup();
h.dedup();
assert_eq!(&h[..], &a[..]);
}
}