Skip to main content

traceforge/
msg.rs

1//! Must's requirements for types passed as messages
2
3use dyn_clone::DynClone;
4use std::any::Any;
5
6/// This type is used to signify the type of messages
7/// exchanged between threads as well as the type of
8/// values returned by a thread.
9/// Since we do not know this type in advance, we created
10/// a trait `Message` that signifies (Clone, Debug, PartialEq)
11/// that needs to be derived on such values of type `Val`.
12///
13/// This is `pub` because monitors, which are instantiated
14/// via macros in the customer's crate, receive values of
15/// this type.
16#[derive(Clone, Debug)]
17pub struct Val {
18    pub(crate) val: Box<dyn Message>,
19    pub type_name: String,
20}
21
22impl PartialEq for Val {
23    fn eq(&self, other: &Self) -> bool {
24        self.val.msg_equals(other)
25    }
26}
27
28impl Val {
29    pub fn new<T: Message + 'static>(val: T) -> Self {
30        Val {
31            val: Box::new(val),
32            type_name: std::any::type_name::<T>().to_string(),
33        }
34    }
35
36    pub(crate) fn set_pending(&mut self) {
37        *self = Self::default();
38    }
39
40    pub(crate) fn is_pending(&self) -> bool {
41        self.val.msg_as_any_ref().is::<Unit>()
42    }
43
44    pub fn as_any(&self) -> Box<dyn Any> {
45        self.val.clone().msg_as_any()
46    }
47
48    pub fn as_any_ref(&self) -> &dyn Any {
49        self.val.msg_as_any_ref()
50    }
51}
52
53impl Default for Val {
54    fn default() -> Self {
55        Val::new(Unit)
56    }
57}
58
59/// This is a default type introduced for values of type `Val`.
60/// This comes in handy when serializing an execution graph
61/// when values of type `Val` cannot be serialized.
62/// In such situations, those values are replaced by `Unit`.
63/// This *looks like* the Rust built-in unit type () but it is a
64/// special Must type that can serve as a sentry value for show when
65/// a thread's result is missing from the graph because it was ignored
66/// during serialization.
67#[derive(Clone, PartialEq, Debug)]
68struct Unit;
69
70impl std::fmt::Display for Unit {
71    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72        write!(f, "()")
73    }
74}
75
76macro_rules! sign_msg_core {
77    () => {
78        // Prefix these types with 'msg' so that their use must be
79        // chosen explicitly rather than by accident or confusion that
80        // someone is calling Box::as_any() or similar things.
81        fn msg_as_any(self: Box<Self>) -> Box<dyn Any>;
82        fn msg_as_any_ref(&self) -> &dyn Any;
83        fn msg_equals(&self, other: &crate::Val) -> bool;
84    };
85}
86
87/// Supertrait to be satisfied by all messages exchanged in user
88/// programs. Assuming a user type `T` satisfies `Clone` and
89/// `PartialEq`, `Message` is automatically derived.
90// TODO Any way we can get around PartialEq and still get symmetry
91// reduction?
92#[cfg(not(any(feature = "print_vals", feature = "print_vals_custom")))]
93pub trait Message: Send + DynClone {
94    sign_msg_core!();
95}
96
97#[cfg(feature = "print_vals")]
98pub trait Message: Send + DynClone + std::fmt::Debug {
99    sign_msg_core!();
100    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result;
101}
102
103#[cfg(feature = "print_vals_custom")]
104pub trait Message: Send + DynClone + std::fmt::Display {
105    sign_msg_core!();
106    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result;
107}
108
109macro_rules! impl_msg_core {
110    () => {
111        fn msg_as_any(self: Box<Self>) -> Box<dyn Any> {
112            self
113        }
114
115        fn msg_as_any_ref(&self) -> &dyn Any {
116            self
117        }
118
119        fn msg_equals(&self, other: &crate::Val) -> bool {
120            match other.as_any_ref().downcast_ref::<T>() {
121                Some(a) => self == a,
122                None => false,
123            }
124        }
125    };
126}
127
128#[cfg(not(any(feature = "print_vals", feature = "print_vals_custom")))]
129impl<T: Send + PartialEq + DynClone + std::fmt::Debug + 'static> Message for T {
130    impl_msg_core!();
131}
132
133#[cfg(feature = "print_vals")]
134impl<T: Send + PartialEq + DynClone + std::fmt::Debug + 'static> Message for T {
135    impl_msg_core!();
136
137    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
138        write!(
139            f,
140            "{:?}",
141            self.msg_as_any_ref().downcast_ref::<T>().unwrap()
142        )
143    }
144}
145
146#[cfg(feature = "print_vals_custom")]
147impl<T: Send + PartialEq + DynClone + std::fmt::Display + 'static> Message for T {
148    impl_msg_core!();
149
150    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
151        write!(f, "{}", self.msg_as_any_ref().downcast_ref::<T>().unwrap())
152    }
153}
154
155#[cfg(all(feature = "print_vals", feature = "print_vals_custom"))]
156compile_error!("features `must/print_vals` and `must/print_vals_custom` are mutually exclusive");
157
158dyn_clone::clone_trait_object!(Message);
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    #[test]
165    fn test_unit_display() {
166        let unit = Unit;
167        assert_eq!(format!("{}", unit), "()");
168    }
169
170    #[test]
171    fn test_val_partial_eq() {
172        let val1 = Val::new(42);
173        let val2 = Val::new(42);
174        let val3 = Val::new(43);
175
176        assert!(val1 == val2);
177        assert!(!(val1 == val3));
178    }
179
180    #[test]
181    fn test_val_partial_eq_different_types() {
182        let val1 = Val::new(42);
183        let val2 = Val::new("hello");
184
185        assert!(!(val1 == val2));
186    }
187
188    #[test]
189    fn test_val_partial_eq_null() {
190        let val1: Val = Val::default();
191        let val2: Val = Val::default();
192
193        assert!(val1 == val2);
194    }
195}