1use std::collections::BTreeMap;
2use std::collections::BTreeSet;
3use std::collections::VecDeque;
4use std::ops::ControlFlow;
5use std::time::Instant;
6
7use log::debug;
8use log::log_enabled;
9use log::trace;
10use machine_check_common::check::Conclusion;
11use machine_check_common::check::Culprit;
12use machine_check_common::check::PreparedProperty;
13use machine_check_common::ExecError;
14use machine_check_common::ExecStats;
15use machine_check_common::NodeId;
16use machine_check_common::StateId;
17use machine_check_common::ThreeValued;
18use mck::concr::FullMachine;
19use mck::misc::Meta;
20use mck::refin::Manipulatable;
21use mck::refin::{self};
22use work_state::WorkState;
23
24use crate::model_check::{self};
25use crate::space::StateSpace;
26use crate::RefinInput;
27use crate::RefinPanicState;
28use crate::Strategy;
29use mck::abstr::Machine as AbstrMachine;
30use mck::refin::Machine as RefinMachine;
31use mck::refin::Refine;
32
33mod work_state;
34
35pub struct Framework<M: FullMachine> {
37 abstract_system: M::Abstr,
39
40 default_input_precision: RefinInput<M>,
42
43 default_step_precision: RefinPanicState<M>,
45
46 work_state: WorkState<M>,
48}
49
50impl<M: FullMachine> Framework<M> {
51 pub fn new(abstract_system: M::Abstr, strategy: Strategy) -> Self {
53 let default_input_precision = if strategy.naive_inputs {
55 Refine::dirty()
56 } else {
57 Refine::clean()
58 };
59
60 let default_step_precision = if strategy.use_decay {
62 Refine::clean()
63 } else {
64 Refine::dirty()
65 };
66
67 Framework {
69 abstract_system,
70 default_input_precision,
71 default_step_precision,
72 work_state: WorkState::new(),
73 }
74 }
75
76 pub fn verify(&mut self, property: &PreparedProperty) -> Result<bool, ExecError> {
77 let result = loop {
79 match self.step_verification(property) {
80 ControlFlow::Continue(()) => {}
81 ControlFlow::Break(result) => break result,
82 }
83 };
84
85 self.work_state.make_compact();
87
88 if log_enabled!(log::Level::Trace) {
89 trace!("Verification final space: {:#?}", self.work_state.space);
90 }
91 result
92 }
93
94 pub fn step_verification(
95 &mut self,
96 property: &PreparedProperty,
97 ) -> ControlFlow<Result<bool, ExecError>> {
98 if !self.work_state.space.is_valid() {
100 self.regenerate(NodeId::ROOT);
101 } else if let Some(culprit) = self.work_state.culprit.take() {
102 if let Err(err) = self.refine(&culprit) {
104 return ControlFlow::Break(Err(err));
106 }
107 self.work_state.garbage_collect();
109 }
110
111 if log_enabled!(log::Level::Trace) {
112 trace!("Model-checking state space: {:#?}", self.work_state.space);
113 }
114
115 match model_check::check_property::<M>(&self.work_state.space, property) {
117 Ok(Conclusion::Known(conclusion)) => {
118 ControlFlow::Break(Ok(conclusion))
120 }
121 Ok(Conclusion::Unknown(culprit)) => {
122 self.work_state.culprit = Some(culprit);
124 ControlFlow::Continue(())
125 }
126 Ok(Conclusion::NotCheckable) => {
127 panic!("The state space should be valid after stepping verification");
129 }
130 Err(err) => {
131 ControlFlow::Break(Err(err))
133 }
134 }
135 }
136
137 fn refine(&mut self, culprit: &Culprit) -> Result<(), ExecError> {
139 while !self.subrefine(culprit)? {}
141 self.work_state.num_refinements += 1;
142 Ok(())
143 }
144
145 fn subrefine(&mut self, culprit: &Culprit) -> Result<bool, ExecError> {
147 let start_instant = if log_enabled!(log::Level::Debug) {
148 Some(Instant::now())
149 } else {
150 None
151 };
152 let mut current_state_mark =
154 mck::refin::PanicResult::<<M::Refin as refin::Machine<M>>::State>::clean();
155
156 if culprit.atomic_property.left().name() == "__panic" {
158 current_state_mark.panic = refin::PanicBitvector::dirty();
159 } else {
160 let manip_mark = current_state_mark
162 .result
163 .get_mut(culprit.atomic_property.left().name())
164 .expect("Culprit mark should be manipulatable");
165
166 let manip_mark = if let Some(index) = culprit.atomic_property.left().index() {
167 let Some(indexed_manip_mark) = manip_mark.index_mut(index) else {
168 panic!("Indexed culprit mark should be indexable");
169 };
170 indexed_manip_mark
171 } else {
172 manip_mark
173 };
174 manip_mark.mark();
175 }
176
177 let mut iter = culprit.path.iter().cloned().rev().peekable();
179 let mut input_precision_refinement: Option<(
181 NodeId,
182 <<M as FullMachine>::Refin as mck::refin::Machine<M>>::Input,
183 )> = None;
184
185 while let Some(current_state_id) = iter.next() {
186 let previous_node_id = match iter.peek() {
187 Some(previous_state_id) => (*previous_state_id).into(),
188 None => NodeId::ROOT,
189 };
190
191 let mut step_precision = self.work_state.step_precision.get(
193 &self.work_state.space,
194 previous_node_id,
195 &self.default_step_precision,
196 );
197
198 if step_precision.apply_refin(¤t_state_mark) {
199 self.work_state.step_precision.insert(
201 &mut self.work_state.space,
202 previous_node_id,
203 step_precision,
204 &self.default_step_precision,
205 );
206
207 return Ok(self.regenerate(previous_node_id));
208 }
209
210 let input = self
211 .work_state
212 .space
213 .representative_input(previous_node_id, current_state_id);
214
215 let (input_mark, new_state_mark) = match TryInto::<StateId>::try_into(previous_node_id)
216 {
217 Ok(previous_state_id) => {
218 trace!(
219 "Finding refinement where original step function was from {:?} to {:?}",
220 previous_state_id,
221 current_state_id
222 );
223 let previous_state = self.work_state.space.state_data(previous_state_id);
225
226 if log_enabled!(log::Level::Trace) {
227 trace!("Earlier state: {:?}", previous_state);
228 let current_state = self.work_state.space.state_data(current_state_id);
229 trace!("Later state: {:?}", current_state);
230 trace!("Later mark: {:?}", current_state_mark);
231 }
232
233 let previous_state = &previous_state.result;
235
236 let (_refinement_machine, new_state_mark, input_mark) = M::Refin::next(
237 (&self.abstract_system, previous_state, input),
238 current_state_mark,
239 );
240
241 (input_mark, Some(new_state_mark))
242 }
243 Err(_) => {
244 trace!(
245 "Finding refinement where original init function was to {:?}",
246 current_state_id
247 );
248
249 if log_enabled!(log::Level::Trace) {
250 let current_state = self.work_state.space.state_data(current_state_id);
251 trace!("Later state: {:?}", current_state);
252 trace!("Later mark: {:?}", current_state_mark);
253 }
254 let (_refinement_machine, input_mark) =
256 M::Refin::init((&self.abstract_system, input), current_state_mark);
257 (input_mark, None)
258 }
259 };
260
261 let mut input_precision = self.work_state.input_precision.get(
262 &self.work_state.space,
263 previous_node_id,
264 &self.default_input_precision,
265 );
266
267 trace!("Input mark: {:?}", input_mark);
268
269 if input_precision.apply_refin(&input_mark) {
270 if log_enabled!(log::Level::Trace) {
272 if let Ok(previous_state_id) = previous_node_id.try_into() {
273 trace!(
274 "Step candidate id: {:?} node: {:?}, input mark: {:?}",
275 previous_state_id,
276 self.work_state.space.state_data(previous_state_id),
277 input_mark
278 );
279 } else {
280 trace!("Init candidate input mark: {:?}", input_mark);
281 }
282 }
283
284 let replace_refinement =
286 if let Some(ref input_precision_refinement) = input_precision_refinement {
287 trace!(
288 "Candidate importance: {}, refinement importance: {}",
289 input_precision.importance(),
290 input_precision_refinement.1.importance()
291 );
292 input_precision.importance() >= input_precision_refinement.1.importance()
293 } else {
294 true
295 };
296
297 if replace_refinement {
298 trace!(
299 "Replacing refinement with candidate with importance {}: {:?}",
300 input_precision.importance(),
301 input_precision
302 );
303 input_precision_refinement = Some((previous_node_id, input_precision));
304 }
305 }
306 if let Some(new_state_mark) = new_state_mark {
308 current_state_mark = mck::refin::PanicResult {
311 panic: refin::PanicBitvector::new_unmarked(),
312 result: new_state_mark,
313 };
314 } else {
315 break;
318 }
319 }
320
321 let result = match input_precision_refinement {
323 Some((node_id, refined_input_precision)) => {
324 self.work_state.input_precision.insert(
326 &mut self.work_state.space,
327 node_id,
328 refined_input_precision,
329 &self.default_input_precision,
330 );
331
332 Ok(self.regenerate(node_id))
333 }
334 None => {
335 Err(ExecError::Incomplete)
337 }
338 };
339
340 if let Some(start_instant) = start_instant {
341 debug!(
342 "Refinement #{} took {:?}.",
343 self.work_state.num_refinements,
344 start_instant.elapsed()
345 );
346 }
347
348 result
349 }
350
351 pub fn regenerate(&mut self, from_node_id: NodeId) -> bool {
353 let default_input_precision = &self.default_input_precision;
354 let default_step_precision = &self.default_step_precision;
355
356 let mut queue = VecDeque::new();
357
358 self.work_state.space.clear_step(from_node_id);
360 queue.push_back(from_node_id);
361
362 let mut changed = false;
363 while let Some(node_id) = queue.pop_front() {
365 let current_has_direct_successor = self
367 .work_state
368 .space
369 .direct_successor_iter(node_id)
370 .next()
371 .is_some();
372 if current_has_direct_successor {
373 continue;
374 }
375
376 self.work_state.num_generated_states += 1;
377 let removed_direct_successors = self.work_state.space.clear_step(node_id);
379
380 let input_precision: RefinInput<M> = self.work_state.input_precision.get(
382 &self.work_state.space,
383 node_id,
384 default_input_precision,
385 );
386 let step_precision = self.work_state.step_precision.get(
387 &self.work_state.space,
388 node_id,
389 default_step_precision,
390 );
391
392 let current_state = if let Ok(state_id) = StateId::try_from(node_id) {
394 Some(self.work_state.space.state_data(state_id).clone())
395 } else {
396 None
397 };
398
399 for input in input_precision.into_proto_iter() {
401 let mut next_state = {
403 if let Some(current_state) = ¤t_state {
404 M::Abstr::next(&self.abstract_system, ¤t_state.result, &input)
405 } else {
406 M::Abstr::init(&self.abstract_system, &input)
407 }
408 };
409
410 step_precision.force_decay(&mut next_state);
412
413 self.work_state.num_generated_transitions += 1;
415 let next_state_index = self.work_state.space.add_step(node_id, next_state, &input);
416
417 let next_has_direct_successor = self
419 .work_state
420 .space
421 .direct_successor_iter(next_state_index.into())
422 .next()
423 .is_some();
424
425 if !next_has_direct_successor {
426 queue.push_back(next_state_index.into());
428 }
429 }
430
431 if !changed {
435 let direct_successors: BTreeSet<StateId> = self
437 .work_state
438 .space
439 .direct_successor_iter(node_id)
440 .collect();
441 changed = direct_successors != removed_direct_successors;
442 }
443 }
444
445 self.space().assert_left_total();
448
449 changed
450 }
451
452 pub fn check_property_with_labelling(
453 &self,
454 property: &PreparedProperty,
455 ) -> Result<(Conclusion, BTreeMap<StateId, ThreeValued>), ExecError> {
456 model_check::check_property_with_labelling(&self.work_state.space, property)
457 }
458
459 pub fn find_panic_string(&mut self) -> Option<&'static str> {
460 self.work_state.find_panic_string()
461 }
462
463 pub fn reset(&mut self) {
464 self.work_state = WorkState::new()
466 }
467
468 pub fn info(&mut self) -> ExecStats {
469 self.work_state.info()
470 }
471
472 pub fn space(&self) -> &StateSpace<M> {
473 &self.work_state.space
474 }
475
476 pub fn make_compact(&mut self) {
477 self.work_state.make_compact();
478 }
479}