Skip to main content

veryl_simulator/
assert_buffer.rs

1//! Thread-local buffer for `$assert` / `$assert_continue` failures.
2//!
3//! Using a thread-local lets the combinational evaluation path record
4//! failures without panicking.
5
6use std::cell::RefCell;
7
8#[derive(Default)]
9struct State {
10    fatal: Option<String>,
11    continues: Vec<String>,
12}
13
14thread_local! {
15    static STATE: RefCell<State> = const {
16        RefCell::new(State {
17            fatal: None,
18            continues: Vec::new(),
19        })
20    };
21}
22
23pub fn reset() {
24    STATE.with(|s| {
25        let mut s = s.borrow_mut();
26        s.fatal = None;
27        s.continues.clear();
28    });
29}
30
31pub fn record_fatal(msg: String) {
32    STATE.with(|s| {
33        let mut s = s.borrow_mut();
34        if s.fatal.is_none() {
35            s.fatal = Some(msg);
36        }
37    });
38}
39
40pub fn record_continue(msg: String) {
41    STATE.with(|s| s.borrow_mut().continues.push(msg));
42}
43
44pub fn has_fatal() -> bool {
45    STATE.with(|s| s.borrow().fatal.is_some())
46}
47
48pub fn take_failure() -> Option<String> {
49    STATE.with(|s| {
50        let mut s = s.borrow_mut();
51        if let Some(msg) = s.fatal.take() {
52            s.continues.clear();
53            return Some(msg);
54        }
55        if s.continues.is_empty() {
56            None
57        } else {
58            let msgs = std::mem::take(&mut s.continues);
59            Some(msgs.join("\n"))
60        }
61    })
62}