rapx/verify/smt_check/
init.rs1use super::common::{SmtCheckResult, SmtChecker, SmtObligation, SmtTerm};
16
17use crate::verify::{contract::Property, helpers::Checkpoint, verifier::ForwardVisitResult};
18use rustc_middle::ty::{Ty, TyKind};
19
20pub(crate) fn check<'tcx>(
22 checker: &SmtChecker<'tcx>,
23 checkpoint: &Checkpoint<'tcx>,
24 property: &Property<'tcx>,
25 forward: &ForwardVisitResult<'tcx>,
26) -> SmtCheckResult {
27 let Some(target) = checker.property_target(checkpoint, property) else {
28 return SmtCheckResult::unknown("Init target could not be resolved");
29 };
30 let Some(required_ty) = checker.property_required_ty(checkpoint, property) else {
31 return SmtCheckResult::unknown("Init type could not be resolved");
32 };
33 let Some(elements_expr) = checker.property_len_expr(checkpoint, property) else {
34 return SmtCheckResult::unknown("Init element-count argument could not be resolved");
35 };
36 let Some(elements_term) = checker.contract_expr_to_smt_term(checkpoint.caller, &elements_expr)
37 else {
38 return SmtCheckResult::unknown("Init element-count argument could not be lowered to SMT");
39 };
40
41 let elem_size = compute_elem_size(checker, checkpoint.caller, required_ty);
42
43 checker.prove_obligation(
44 checkpoint,
45 forward,
46 SmtObligation::Initialized {
47 place: target,
48 ty_name: format!("{required_ty:?}"),
49 elements: elements_term,
50 elem_size,
51 array_elem_size: array_elem_size(checker, checkpoint.caller, required_ty),
52 array_len_term: array_len_term(checker, required_ty),
53 },
54 )
55}
56
57fn compute_elem_size<'tcx>(
58 checker: &SmtChecker<'tcx>,
59 caller: rustc_hir::def_id::DefId,
60 ty: Ty<'tcx>,
61) -> Option<u64> {
62 if let Some((_, size)) = checker.type_layout(caller, ty) {
63 return Some(size);
64 }
65 None
66}
67
68fn array_elem_size<'tcx>(
69 checker: &SmtChecker<'tcx>,
70 caller: rustc_hir::def_id::DefId,
71 ty: Ty<'tcx>,
72) -> Option<u64> {
73 if let TyKind::Array(elem, _) = ty.kind() {
74 checker.type_layout(caller, *elem).map(|(_, s)| s)
75 } else {
76 None
77 }
78}
79
80fn array_len_term<'tcx>(
81 checker: &SmtChecker<'tcx>,
82 ty: Ty<'tcx>,
83) -> Option<SmtTerm> {
84 use rustc_middle::ty::ConstKind;
85 if let TyKind::Array(_, len) = ty.kind() {
86 if let Some(val) = len.try_to_target_usize(checker.tcx) {
87 return Some(SmtTerm::Const(val));
88 }
89 if let ConstKind::Param(param) = len.kind() {
90 return Some(SmtTerm::ConstParam(param.name.to_string()));
91 }
92 Some(SmtTerm::ConstParam(format!("Ty(usize, {len})")))
93 } else {
94 None
95 }
96}
97
98pub(crate) fn check_for_checkpoint<'tcx>(
100 checker: &SmtChecker<'tcx>,
101 caller: rustc_hir::def_id::DefId,
102 property: &Property<'tcx>,
103 forward: &ForwardVisitResult<'tcx>,
104) -> SmtCheckResult {
105 let Some(target) = checker.property_target_direct(property) else {
106 return SmtCheckResult::unknown("Init target could not be resolved");
107 };
108 let Some(required_ty) = checker.property_required_ty_direct(property) else {
109 return SmtCheckResult::unknown("Init type could not be resolved");
110 };
111 let Some(elements_expr) = checker.property_len_expr_direct(property) else {
112 return SmtCheckResult::unknown("Init element-count argument could not be resolved");
113 };
114 let Some(elements) = checker.contract_expr_to_smt_term(caller, &elements_expr) else {
115 return SmtCheckResult::unknown("Init element-count argument could not be lowered to SMT");
116 };
117
118 checker.prove_obligation_for_checkpoint(
119 caller,
120 forward,
121 SmtObligation::Initialized {
122 place: target,
123 ty_name: format!("{required_ty:?}"),
124 elements,
125 elem_size: compute_elem_size(checker, caller, required_ty),
126 array_elem_size: array_elem_size(checker, caller, required_ty),
127 array_len_term: array_len_term(checker, required_ty),
128 },
129 )
130}