rapx/verify/smt_check/
non_overlap.rs1use super::common::{SmtCheckResult, SmtChecker, SmtObligation, SmtTerm};
9use crate::verify::{
10 contract::Property, def_use::PlaceKey, helpers::Checkpoint, verifier::ForwardVisitResult,
11};
12use rustc_middle::mir::{Operand, Rvalue, StatementKind};
13
14pub(crate) fn check<'tcx>(
16 checker: &SmtChecker<'tcx>,
17 checkpoint: &Checkpoint<'tcx>,
18 property: &Property<'tcx>,
19 forward: &ForwardVisitResult<'tcx>,
20) -> SmtCheckResult {
21 let Some(left) = checker.property_place_arg(checkpoint, property, 0) else {
22 return SmtCheckResult::unknown("NonOverlap left pointer could not be resolved");
23 };
24 let Some(right) = checker.property_place_arg(checkpoint, property, 1) else {
25 return SmtCheckResult::unknown("NonOverlap right pointer could not be resolved");
26 };
27 let left = resolve_callsite_copy(checker, checkpoint, left);
28 let right = resolve_callsite_copy(checker, checkpoint, right);
29
30 let count = property
31 .args
32 .get(2)
33 .and_then(|_| checker.property_len_expr(checkpoint, property))
34 .and_then(|expr| checker.contract_expr_to_smt_term(checkpoint.caller, &expr))
35 .or_else(|| checker.callsite_arg_smt_term(checkpoint, 2))
36 .unwrap_or(SmtTerm::Const(1));
37
38 let elem_size = checker
39 .place_pointee_size(checkpoint.caller, &left)
40 .or_else(|| checker.place_pointee_size(checkpoint.caller, &right))
41 .unwrap_or(1);
42
43 checker.prove_obligation(
44 checkpoint,
45 forward,
46 SmtObligation::NonOverlapping {
47 left,
48 right,
49 left_count: count.clone(),
50 right_count: count,
51 elem_size,
52 },
53 )
54}
55
56fn resolve_callsite_copy<'tcx>(
57 checker: &SmtChecker<'tcx>,
58 checkpoint: &Checkpoint<'tcx>,
59 mut place: PlaceKey,
60) -> PlaceKey {
61 for _ in 0..8 {
62 if !place.fields.is_empty() {
63 return place;
64 }
65 let Some(local) = place.local() else {
66 return place;
67 };
68 let body = checker.tcx.optimized_mir(checkpoint.caller);
69 let block = &body.basic_blocks[checkpoint.block];
70 let source = block
71 .statements
72 .iter()
73 .rev()
74 .find_map(|statement| assignment_source_for_local(statement, local))
75 .or_else(|| {
76 body.basic_blocks.iter().rev().find_map(|block| {
77 block
78 .statements
79 .iter()
80 .rev()
81 .find_map(|statement| assignment_source_for_local(statement, local))
82 })
83 });
84 let Some(source) = source else {
85 return place;
86 };
87 let next = PlaceKey::from_mir_place(source);
88 if next == place {
89 return place;
90 }
91 place = next;
92 }
93 place
94}
95
96fn assignment_source_for_local<'a, 'tcx>(
97 statement: &'a rustc_middle::mir::Statement<'tcx>,
98 local: rustc_middle::mir::Local,
99) -> Option<&'a rustc_middle::mir::Place<'tcx>> {
100 let StatementKind::Assign(assign) = &statement.kind else {
101 return None;
102 };
103 let (target, rvalue) = assign.as_ref();
104 if target.local != local {
105 return None;
106 }
107 rvalue_source_place(rvalue)
108}
109
110fn rvalue_source_place<'a, 'tcx>(
111 rvalue: &'a Rvalue<'tcx>,
112) -> Option<&'a rustc_middle::mir::Place<'tcx>> {
113 match rvalue {
114 Rvalue::Use(Operand::Copy(place), ..)
115 | Rvalue::Use(Operand::Move(place), ..)
116 | Rvalue::Cast(_, Operand::Copy(place), _)
117 | Rvalue::Cast(_, Operand::Move(place), _)
118 | Rvalue::CopyForDeref(place) => Some(place),
119 _ => None,
120 }
121}