rapx/verify/smt_check/
in_bound.rs1use super::common::{SmtCheckResult, SmtChecker, SmtObligation, SmtTerm};
17use crate::verify::{
18 contract::Property, helpers::Checkpoint, primitive::PrimitiveCall, verifier::ForwardVisitResult,
19};
20
21pub(crate) fn check<'tcx>(
23 checker: &SmtChecker<'tcx>,
24 checkpoint: &Checkpoint<'tcx>,
25 property: &Property<'tcx>,
26 forward: &ForwardVisitResult<'tcx>,
27) -> SmtCheckResult {
28 if let Some(predicates) =
29 checker.property_index_access_in_bound_predicates(checkpoint, property)
30 {
31 return checker.prove_obligation(
32 checkpoint,
33 forward,
34 SmtObligation::Predicate { predicates },
35 );
36 }
37
38 let Some(target) = checker.property_target(checkpoint, property) else {
39 rap_debug!(" [SMT InBound] target could not be resolved");
40 return SmtCheckResult::unknown("InBound target could not be resolved");
41 };
42 let Some(required_ty) = checker
43 .property_required_ty(checkpoint, property)
44 .or_else(|| checker.infer_pointee_ty(checkpoint.caller, &target))
45 else {
46 rap_debug!(" [SMT InBound] type could not be resolved");
47 return SmtCheckResult::unknown("InBound type could not be resolved");
48 };
49 let elem_size = checker
50 .type_layout(checkpoint.caller, required_ty)
51 .map(|(_, s)| s)
52 .unwrap_or(0);
53 let access_count = checker
54 .property_len_expr(checkpoint, property)
55 .and_then(|expr| checker.contract_expr_to_smt_term(checkpoint.caller, &expr))
56 .unwrap_or(SmtTerm::Const(1));
57
58 if let Some(obligation) =
59 pointer_arithmetic_obligation(checker, checkpoint, required_ty, access_count.clone())
60 {
61 return checker.prove_obligation(checkpoint, forward, obligation);
62 }
63
64 checker.prove_obligation(
65 checkpoint,
66 forward,
67 SmtObligation::InBounds {
68 place: target,
69 ty_name: format!("{required_ty:?}"),
70 elem_size,
71 access_count,
72 },
73 )
74}
75
76fn pointer_arithmetic_obligation<'tcx>(
77 checker: &SmtChecker<'tcx>,
78 checkpoint: &Checkpoint<'tcx>,
79 required_ty: rustc_middle::ty::Ty<'tcx>,
80 count: SmtTerm,
81) -> Option<SmtObligation> {
82 let callee_name = checker.tcx.def_path_str(checkpoint.callee?);
83 let primitive = PrimitiveCall::classify(&callee_name)?;
84 if !primitive.is_pointer_arithmetic() {
85 return None;
86 }
87
88 let base = checker.callsite_arg_place(checkpoint, 0)?;
89 let zero = SmtTerm::Const(0);
90 let negative_count = SmtTerm::Sub(Box::new(zero.clone()), Box::new(count.clone()));
91 let (lower_delta, upper_delta) = if primitive.is_pointer_sub_like() {
92 (negative_count, zero)
93 } else if primitive.is_signed_pointer_arithmetic() {
94 (count.clone(), count)
95 } else {
96 (zero, count)
97 };
98
99 Some(SmtObligation::PointerRangeInBounds {
100 place: base,
101 ty_name: format!("{required_ty:?}"),
102 lower_delta,
103 upper_delta,
104 })
105}
106
107pub(crate) fn check_for_checkpoint<'tcx>(
109 checker: &SmtChecker<'tcx>,
110 caller: rustc_hir::def_id::DefId,
111 property: &Property<'tcx>,
112 forward: &ForwardVisitResult<'tcx>,
113) -> SmtCheckResult {
114 let Some(target) = checker.property_target_direct(property) else {
115 return SmtCheckResult::unknown("InBound target could not be resolved");
116 };
117 let Some(required_ty) = checker.property_required_ty_direct(property) else {
118 return SmtCheckResult::unknown("InBound type could not be resolved");
119 };
120 let elem_size = checker
121 .type_layout(caller, required_ty)
122 .map(|(_, s)| s)
123 .unwrap_or(0);
124 let Some(access_count_expr) = checker.property_len_expr_direct(property) else {
125 return SmtCheckResult::unknown("InBound length argument could not be resolved");
126 };
127 let Some(access_count) = checker.contract_expr_to_smt_term(caller, &access_count_expr) else {
128 return SmtCheckResult::unknown("InBound length argument could not be lowered to SMT");
129 };
130
131 checker.prove_obligation_for_checkpoint(
132 caller,
133 forward,
134 SmtObligation::InBounds {
135 place: target,
136 ty_name: format!("{required_ty:?}"),
137 elem_size,
138 access_count,
139 },
140 )
141}