Skip to main content

cvlr_spec/
formula.rs

1//! Boolean expression types and traits.
2
3use cvlr_asserts::{cvlr_assert, cvlr_assume};
4
5/// A Boolean expression that can be evaluated, assumed, or asserted.
6///
7/// This trait represents a boolean expression with an associated context type.
8/// Expressions implementing this trait can be:
9/// - Evaluated to a boolean value via [`eval`](CvlrFormula::eval) (single state)
10/// - Evaluated over two states via [`eval_with_states`](CvlrFormula::eval_with_states) (pre-state and post-state)
11/// - Asserted (checked) via [`assert`](CvlrFormula::assert) or [`assert_with_states`](CvlrFormula::assert_with_states)
12/// - Assumed (taken as a precondition) via [`assume`](CvlrFormula::assume) or [`assume_with_states`](CvlrFormula::assume_with_states)
13///
14/// # Associated Types
15///
16/// * [`Context`](CvlrFormula::Context) - The context type used to evaluate the expression. This typically
17///   represents the state or environment in which the expression is evaluated.
18///
19/// # Examples
20///
21/// ```
22/// use cvlr_spec::CvlrFormula;
23///
24/// struct MyContext {
25///     value: i32,
26/// }
27///
28/// struct IsPositive;
29///
30/// impl CvlrFormula for IsPositive {
31///     type Context = MyContext;
32///     fn eval(&self, ctx: &Self::Context) -> bool {
33///         ctx.value > 0
34///     }
35/// }
36/// ```
37pub trait CvlrFormula {
38    type Context;
39
40    /// Evaluates the expression in the given context.
41    ///
42    /// Returns `true` if the expression holds, `false` otherwise.
43    fn eval(&self, ctx: &Self::Context) -> bool;
44
45    /// Asserts that the expression holds in the given context.
46    ///
47    /// This will cause a verification failure if the expression evaluates to `false`.
48    /// The default implementation uses [`cvlr_assert!`] to check the result of [`eval`](CvlrFormula::eval).
49    fn assert(&self, ctx: &Self::Context) {
50        cvlr_assert!(self.eval(ctx));
51    }
52
53    /// Assumes that the expression holds in the given context.
54    ///
55    /// This adds the expression as a precondition that the verifier will assume to be true.
56    /// The default implementation uses [`cvlr_assume!`] to assume the result of [`eval`](CvlrFormula::eval).
57    fn assume(&self, ctx: &Self::Context) {
58        cvlr_assume!(self.eval(ctx));
59    }
60
61    /// Evaluates the expression over two states (pre-state and post-state).
62    ///
63    /// This method allows expressions to be evaluated in contexts that require
64    /// comparing values from two different states, such as checking invariants
65    /// across state transitions or comparing before and after values.
66    ///
67    /// # Parameters
68    ///
69    /// * `ctx0` - The pre-state context (before the transition)
70    /// * `ctx1` - The post-state context (after the transition)
71    ///
72    /// # Returns
73    ///
74    /// Returns `true` if the expression holds across both states, `false` otherwise.
75    ///
76    /// # Default Implementation
77    ///
78    /// The default implementation evaluates the expression using only the pre-state
79    /// context (`ctx0`). Expressions that need to compare both states should override
80    /// this method.
81    fn eval_with_states(&self, ctx0: &Self::Context, _: &Self::Context) -> bool {
82        self.eval(ctx0)
83    }
84
85    /// Asserts that the expression holds across two states (pre-state and post-state).
86    ///
87    /// This will cause a verification failure if the expression evaluates to `false`
88    /// when considering both the pre-state and post-state contexts.
89    ///
90    /// # Parameters
91    ///
92    /// * `ctx0` - The pre-state context (before the transition)
93    /// * `ctx1` - The post-state context (after the transition)
94    ///
95    /// # Default Implementation
96    ///
97    /// The default implementation asserts the expression using only the pre-state
98    /// context (`ctx0`). Expressions that need to compare both states should override
99    /// this method.
100    fn assert_with_states(&self, ctx0: &Self::Context, _: &Self::Context) {
101        self.assert(ctx0);
102    }
103
104    /// Assumes that the expression holds across two states (pre-state and post-state).
105    ///
106    /// This adds the expression as a precondition that the verifier will assume to be true
107    /// when considering both the pre-state and post-state contexts.
108    ///
109    /// # Parameters
110    ///
111    /// * `ctx0` - The pre-state context (before the transition)
112    /// * `ctx1` - The post-state context (after the transition)
113    ///
114    /// # Default Implementation
115    ///
116    /// The default implementation assumes the expression using only the pre-state
117    /// context (`ctx0`). Expressions that need to compare both states should override
118    /// this method.
119    fn assume_with_states(&self, ctx0: &Self::Context, _: &Self::Context) {
120        self.assume(ctx0);
121    }
122}
123
124pub trait CvlrPredicate: CvlrFormula {}
125
126/// A boolean expression that always evaluates to `true`.
127///
128/// This is a constant expression that can be used as a base case or placeholder
129/// in boolean expression compositions.
130#[derive(Copy, Clone)]
131pub struct CvlrTrue<Ctx>(core::marker::PhantomData<Ctx>);
132
133impl<Ctx> CvlrFormula for CvlrTrue<Ctx> {
134    type Context = Ctx;
135    fn eval(&self, _ctx: &Self::Context) -> bool {
136        true
137    }
138    fn assert(&self, _ctx: &Self::Context) {}
139    fn assume(&self, _ctx: &Self::Context) {}
140}
141
142pub fn cvlr_true<Ctx>() -> impl CvlrFormula<Context = Ctx> {
143    CvlrTrue(core::marker::PhantomData)
144}