machine_check_exec/
framework.rs

1use std::collections::BTreeMap;
2use std::ops::ControlFlow;
3
4use log::log_enabled;
5use log::trace;
6use machine_check_common::check::Conclusion;
7use machine_check_common::check::KnownConclusion;
8use machine_check_common::iir::description::IMachine;
9use machine_check_common::iir::property::IProperty;
10use machine_check_common::ExecError;
11use machine_check_common::ExecStats;
12use machine_check_common::NodeId;
13use machine_check_common::ParamValuation;
14use machine_check_common::StateId;
15use mck::concr::FullMachine;
16use work_state::WorkState;
17
18use crate::space::StateSpace;
19use crate::Strategy;
20use mck::refin::RefinementValue;
21
22mod refine;
23mod regenerate;
24mod work_state;
25
26/// Three-valued abstraction refinement framework.
27pub struct Framework<M: FullMachine> {
28    /// Abstract system.
29    abstract_system: M::Abstr,
30
31    machine: IMachine,
32
33    /// Default input precision.
34    default_input_precision: RefinementValue,
35
36    /// Default parameter precision.
37    default_param_precision: RefinementValue,
38
39    /// Default step precision.
40    default_step_precision: RefinementValue,
41
42    /// Work state containing the structures that change during verification.
43    work_state: WorkState<M>,
44}
45
46impl<M: FullMachine> Framework<M> {
47    /// Constructs the framework with a given system and strategy.
48    pub fn new(abstract_system: M::Abstr, machine: IMachine, strategy: Strategy) -> Self {
49        let input = machine.input();
50        let param = machine.param();
51        let state = machine.state();
52
53        let description = &machine.description;
54
55        // default the input and param precision to clean (inputs will be refined)
56        let (default_input_precision, default_param_precision) = if strategy.naive_inputs {
57            (
58                input.dirty_refin(description),
59                param.dirty_refin(description),
60            )
61        } else {
62            (
63                input.clean_refin(description),
64                param.clean_refin(description),
65            )
66        };
67
68        // default the step precision to dirty (steps will remain non-decayed)
69        let default_step_precision = if strategy.use_decay {
70            state.clean_refin(description)
71        } else {
72            state.dirty_refin(description)
73        };
74
75        // return the framework with empty state space, before any construction
76        Framework {
77            abstract_system,
78            machine,
79            default_input_precision,
80            default_step_precision,
81            default_param_precision,
82            work_state: WorkState::new(),
83        }
84    }
85
86    pub fn verify(&mut self, property: &IProperty) -> Result<KnownConclusion, ExecError> {
87        // loop verification steps until some conclusion is reached
88        let result = loop {
89            match self.step_verification(property) {
90                ControlFlow::Continue(()) => {}
91                ControlFlow::Break(result) => break result,
92            }
93        };
94
95        // make compact after verification for nice state space information
96        self.work_state.make_compact();
97
98        if log_enabled!(log::Level::Trace) {
99            trace!("Verification final space: {:#?}", self.work_state.space);
100        }
101        result
102    }
103
104    pub fn step_verification(
105        &mut self,
106        property: &IProperty,
107    ) -> ControlFlow<Result<KnownConclusion, ExecError>> {
108        // if the space is invalid (just after construction), regenerate it
109        if !self.work_state.space.is_valid() {
110            self.regenerate(NodeId::ROOT);
111        } else if let Some(culprit) = self.work_state.culprit.take() {
112            // we have a culprit, refine on it
113            if let Err(err) = self.refine(&culprit) {
114                // the refinement is incomplete
115                return ControlFlow::Break(Err(err));
116            }
117            // run garbage collection
118            self.work_state.garbage_collect();
119        }
120
121        if log_enabled!(log::Level::Trace) {
122            trace!("Model-checking state space: {:#?}", self.work_state.space);
123        }
124
125        // perform model-checking
126        match self.work_state.checker.check_property(
127            &self.work_state.space,
128            &self.machine,
129            property,
130        ) {
131            Ok(Conclusion::Known(conclusion)) => {
132                // conclude the result
133                ControlFlow::Break(Ok(conclusion))
134            }
135            Ok(Conclusion::Unknown(culprit)) => {
136                // we have a new culprit, continue the control flow
137                self.work_state.culprit = Some(culprit);
138                ControlFlow::Continue(())
139            }
140            Ok(Conclusion::NotCheckable) => {
141                // should never happen, the state space should be valid
142                panic!("The state space should be valid after stepping verification");
143            }
144            Err(err) => {
145                // propagate the error
146                ControlFlow::Break(Err(err))
147            }
148        }
149    }
150
151    pub fn current_labellings(
152        &mut self,
153        property: &IProperty,
154    ) -> Result<BTreeMap<usize, BTreeMap<StateId, ParamValuation>>, ExecError> {
155        self.work_state
156            .checker
157            .get_labellings(&self.machine, &self.work_state.space, property)
158    }
159
160    pub fn current_conclusion(&mut self, property: &IProperty) -> Result<Conclusion, ExecError> {
161        self.work_state
162            .checker
163            .check_property(&self.work_state.space, &self.machine, property)
164    }
165
166    pub fn find_panic_string(&mut self) -> Option<&'static str> {
167        self.work_state.find_panic_string()
168    }
169
170    pub fn reset(&mut self) {
171        // reset the work state
172        self.work_state = WorkState::new()
173    }
174
175    pub fn info(&mut self) -> ExecStats {
176        self.work_state.info()
177    }
178
179    pub fn space(&self) -> &StateSpace<M> {
180        &self.work_state.space
181    }
182
183    pub fn make_compact(&mut self) {
184        self.work_state.make_compact();
185    }
186
187    pub fn abstract_system(&self) -> &M::Abstr {
188        &self.abstract_system
189    }
190    pub fn machine(&self) -> &IMachine {
191        &self.machine
192    }
193}