1use crate::helpers::mir_scan::Checkpoint;
8use crate::verify::contract::{
9 ContractExpr, ContractPlace, ContractProjection, NumericBinOp, PlaceBase, Property,
10 PropertyArg, RelOp,
11};
12use crate::verify::report::{CheckResult, UnknownReason};
13use crate::verify::vm::state::{VmState, VmValue};
14use rustc_middle::mir::{Local, Operand, Rvalue, StatementKind, TerminatorKind};
15#[cfg(rapx_const_ext)]
16use rustc_middle::ty::consts::ConstExt;
17use rustc_middle::ty::{GenericArg, GenericArgKind, Ty, TyKind};
18use z3::{
19 SatResult, Solver,
20 ast::{Ast, Bool, Int},
21};
22
23use super::PropertyChecker;
24
25pub(super) fn local_param_operand<'a, 'z3, 'tcx>(
29 vm_state: &VmState<'z3, 'tcx>,
30 ck: &'a Checkpoint<'tcx>,
31 n: usize,
32) -> Option<&'a Operand<'tcx>> {
33 let callee = ck.callee?;
34 let idx = crate::helpers::mir_utils::callee_param_index_for_local(vm_state.tcx, callee, n)?;
35 ck.args.get(idx)
36}
37
38impl PropertyChecker {
39 pub(super) fn ty_arg<'tcx>(property: &Property<'tcx>, idx: usize) -> Option<Ty<'tcx>> {
40 property.args().get(idx).and_then(|a| match a {
41 PropertyArg::Ty(ty) => Some(*ty),
42 _ => None,
43 })
44 }
45
46 pub(super) fn target_value<'z3, 'tcx>(
47 &self,
48 vm_state: &VmState<'z3, 'tcx>,
49 checkpoint: &Checkpoint<'tcx>,
50 property: &Property<'tcx>,
51 ) -> Option<VmValue<'z3, 'tcx>> {
52 self.target_value_raw(vm_state, checkpoint, property)
53 }
54
55 fn target_value_raw<'z3, 'tcx>(
58 &self,
59 vm_state: &VmState<'z3, 'tcx>,
60 checkpoint: &Checkpoint<'tcx>,
61 property: &Property<'tcx>,
62 ) -> Option<VmValue<'z3, 'tcx>> {
63 let cp = match property.args().first()? {
64 PropertyArg::Expr(ContractExpr::Const(n)) => {
65 let idx = usize::try_from(*n).ok()?;
66 crate::verify::contract::ContractPlace {
67 base: PlaceBase::Arg(idx),
68 projections: vec![],
69 }
70 }
71 PropertyArg::Predicates(_) | PropertyArg::Ty(_) | PropertyArg::Ident(_) => return None,
72 PropertyArg::Expr(ContractExpr::Place(cp)) => cp.clone(),
73 PropertyArg::Expr(ContractExpr::IndexAccess { slice, .. }) => match slice.as_ref() {
74 ContractExpr::Place(cp) => cp.clone(),
75 _ => return None,
76 },
77 _ => return None,
78 };
79 if cp.projections.is_empty() {
80 return match cp.base {
81 PlaceBase::Return => vm_state.local_value(Local::from_usize(0)).cloned(),
82 PlaceBase::Arg(n) => {
83 let operand = checkpoint.args.get(n)?;
84 Some(vm_state.value_of_operand(operand))
85 }
86 PlaceBase::Local(n) => vm_state.local_value(Local::from_usize(n)).cloned(),
87 };
88 }
89 let base_local = match cp.base {
90 PlaceBase::Return => Local::from_usize(0),
91 PlaceBase::Arg(n) => {
92 let operand = checkpoint.args.get(n)?;
93 match operand {
94 Operand::Copy(place) | Operand::Move(place) => place.local,
95 _ => return None,
96 }
97 }
98 PlaceBase::Local(n) => Local::from_usize(n),
99 };
100 let mut field_path: Vec<usize> = Vec::new();
101 let mut last_field_ty: Option<Ty<'tcx>> = None;
102
103 for proj in &cp.projections {
104 match proj {
105 ContractProjection::Field { index, ty } => {
106 field_path.push(*index);
107 last_field_ty = *ty;
108 }
109 ContractProjection::Downcast { variant_index } => {
110 let base_val = vm_state
111 .field_value(base_local, &field_path)
112 .cloned()
113 .or_else(|| vm_state.local_value(base_local).cloned());
114 let Some(base_val) = base_val else {
115 return None;
116 };
117
118 let enum_ty = last_field_ty.unwrap_or(base_val.ty);
119 let inner_ty = match enum_ty.kind() {
120 TyKind::Adt(adt_def, substs) => {
121 if adt_def.is_enum() {
122 let variant = &adt_def.variants()
123 [rustc_abi::VariantIdx::from_usize(*variant_index)];
124 if !variant.fields.is_empty() {
125 Some(crate::helpers::mir_utils::field_ty(
126 vm_state.tcx,
127 &variant.fields[rustc_abi::FieldIdx::from_usize(0)],
128 substs,
129 ))
130 } else {
131 None
132 }
133 } else {
134 None
135 }
136 }
137 _ => None,
138 };
139 let inner_ty = inner_ty.unwrap_or(base_val.ty);
140
141 return Some(VmValue {
142 z3_term: base_val.z3_term.clone(),
143 ty: inner_ty,
144 provenance: base_val.provenance.clone(),
145 facts: base_val.facts,
146 source: base_val.source.field_offset_only(),
147 });
148 }
149 ContractProjection::ForEach => {
150 if let Some(val) = vm_state.field_value(base_local, &field_path) {
153 return Some(val.clone());
154 }
155 if let Some(base_val) = vm_state.local_value(base_local) {
156 if base_val.is_pointer() {
157 return Some(VmValue {
158 z3_term: base_val.z3_term.clone(),
159 ty: base_val.ty,
160 provenance: base_val.provenance.clone(),
161 facts: base_val.facts.clone(),
162 source: base_val.source.field_offset_only(),
163 });
164 }
165 }
166 return None;
167 }
168 }
169 }
170
171 if let Some(val) = vm_state.field_value(base_local, &field_path) {
173 return Some(val.clone());
174 }
175 if !field_path.is_empty() && base_local == Local::from_usize(0) {
178 for bb in vm_state.body().basic_blocks.iter() {
179 for stmt in &bb.statements {
180 if let rustc_middle::mir::StatementKind::Assign(assign) = &stmt.kind {
181 let (ref place, ref rval) = **assign;
182 if let rustc_middle::mir::Rvalue::Aggregate(_, operands) = rval {
183 if place.local == base_local {
184 if let Some(operand) =
185 operands.get(rustc_abi::FieldIdx::from_usize(field_path[0]))
186 {
187 let val = vm_state.value_of_operand(operand);
188 if field_path.len() == 1 {
189 return Some(val);
190 }
191 }
192 }
193 }
194 }
195 }
196 }
197 }
198 if let Some(base_val) = vm_state.local_value(base_local) {
199 if let Some(ref prov) = base_val.provenance {
200 return Some(VmValue {
201 z3_term: base_val.z3_term.clone(),
202 ty: base_val.ty,
203 provenance: Some(prov.clone()),
204 facts: base_val.facts.clone(),
205 source: base_val.source.field_offset_only(),
206 });
207 }
208 }
209 None
210 }
211
212 pub(super) fn resolve_pointer_provenance<'z3, 'tcx>(
218 &self,
219 vm_state: &VmState<'z3, 'tcx>,
220 mut value: VmValue<'z3, 'tcx>,
221 ) -> VmValue<'z3, 'tcx> {
222 if matches!(
223 value.ty.kind(),
224 rustc_middle::ty::TyKind::Ref(..) | rustc_middle::ty::TyKind::RawPtr(..)
225 ) {
226 if let Some(owner) = vm_state.find_local_by_address(&value.z3_term) {
227 if let Some(heap_field) = vm_state.owner_ptr_field(owner) {
228 if heap_field.is_pointer() {
229 value.z3_term = heap_field.z3_term.clone();
230 value.provenance = heap_field.provenance.clone();
231 value.facts = heap_field.facts.clone();
232 }
233 }
234 }
235 }
236 value
237 }
238
239 pub(super) fn is_vacuously_true_for_nullable<'z3, 'tcx>(
248 &self,
249 vm_state: &VmState<'z3, 'tcx>,
250 checkpoint: &Checkpoint<'tcx>,
251 property: &Property<'tcx>,
252 ) -> bool {
253 let cp = match property.args().first() {
254 Some(PropertyArg::Expr(crate::verify::contract::ContractExpr::Place(cp))) => cp,
255 _ => return false,
256 };
257 let has_nullable_proj = cp.projections.iter().any(|p| {
258 matches!(
259 p,
260 ContractProjection::Downcast { .. } | ContractProjection::ForEach
261 )
262 });
263 if !has_nullable_proj {
264 return false;
265 }
266 match self.target_value(vm_state, checkpoint, property) {
267 Some(val) => val.provenance.is_none(),
268 None => true,
269 }
270 }
271
272 pub(super) fn is_null<'z3, 'tcx>(
279 &self,
280 vm_state: &VmState<'z3, 'tcx>,
281 checkpoint: &Checkpoint<'tcx>,
282 place: &ContractPlace<'tcx>,
283 ) -> bool {
284 use crate::verify::def_use::{PlaceBaseKey, PlaceKey};
285 let key = PlaceKey::from_contract_place(place);
286 let local = match key.base {
287 PlaceBaseKey::Local(n) => Local::from_usize(n),
288 PlaceBaseKey::Arg(n) => checkpoint
289 .args
290 .get(n)
291 .and_then(|op| match op {
292 Operand::Copy(place) | Operand::Move(place) => Some(place.local),
293 _ => None,
294 })
295 .unwrap_or(Local::from_usize(n + 1)),
296 PlaceBaseKey::Return => Local::from_usize(0),
297 };
298 let val = if key.fields.is_empty() {
299 vm_state.local_value(local).cloned()
300 } else {
301 vm_state.field_value(local, &key.fields).cloned()
302 };
303 match val {
304 Some(v) => {
305 if !v.facts.non_null {
310 let possibly_null = match &v.provenance {
311 None => true,
312 Some(prov) => vm_state.alloc(prov.alloc_id).is_external(),
313 };
314 if possibly_null {
315 return true;
316 }
317 }
318 if let Some(term_zero) = v.z3_term.simplify().as_u64() {
319 if term_zero == 0 {
320 return true;
321 }
322 }
323 false
324 }
325 None => true,
326 }
327 }
328
329 pub(super) fn smt_check<'z3>(
330 &self,
331 solver: &Solver<'z3>,
332 condition: &Bool<'z3>,
333 ) -> CheckResult {
334 solver.push();
335 solver.assert(condition);
336 let r = match solver.check() {
337 SatResult::Unsat => CheckResult::ProvedBySmt,
338 SatResult::Sat => CheckResult::Failed,
339 SatResult::Unknown => CheckResult::Unknown(UnknownReason::SmtTimeout),
340 };
341 solver.pop(1);
342 r
343 }
344
345 pub(super) fn smt_check_size_split<'z3, 'tcx>(
350 vm_state: &VmState<'z3, 'tcx>,
351 elem_size: &Int<'z3>,
352 goal_negated: &Bool<'z3>,
353 on_sat: CheckResult,
354 ) -> CheckResult {
355 let solver = Solver::new(vm_state.z3_ctx);
356 let zero = Int::from_u64(vm_state.z3_ctx, 0);
357 let one = Int::from_u64(vm_state.z3_ctx, 1);
358
359 solver.push();
360 vm_state.assert_all(&solver);
361 solver.assert(&elem_size._eq(&zero));
362 solver.assert(goal_negated);
363 let r_zst = solver.check();
364 solver.pop(1);
365
366 solver.push();
367 vm_state.assert_all(&solver);
368 solver.assert(&elem_size.ge(&one));
369 solver.assert(goal_negated);
370 let r_non_zst = solver.check();
371 solver.pop(1);
372
373 match (r_zst, r_non_zst) {
374 (SatResult::Unsat, SatResult::Unsat) => CheckResult::ProvedBySmt,
375 (SatResult::Sat, _) | (_, SatResult::Sat) => on_sat,
376 _ => CheckResult::Unknown(UnknownReason::SmtTimeout),
377 }
378 }
379
380 pub(super) fn resolve_arg_term<'z3, 'tcx>(
381 &self,
382 vm_state: &VmState<'z3, 'tcx>,
383 checkpoint: &Checkpoint<'tcx>,
384 arg: &PropertyArg<'tcx>,
385 ) -> Option<Int<'z3>> {
386 match arg {
387 PropertyArg::Expr(ContractExpr::Const(n)) if *n <= u64::MAX as u128 => {
388 Some(Int::from_u64(vm_state.z3_ctx, *n as u64))
389 }
390 PropertyArg::Expr(ContractExpr::Place(cp)) => {
391 match cp.base {
392 PlaceBase::Arg(n) => {
393 let op = checkpoint.args.get(n)?;
394 Some(vm_state.value_of_operand(op).z3_term)
395 }
396 PlaceBase::Local(n) => {
397 if let Some(op) = local_param_operand(vm_state, checkpoint, n) {
403 Some(vm_state.value_of_operand(op).z3_term)
404 } else {
405 vm_state
406 .local_value(Local::from_usize(n))
407 .map(|v| v.z3_term.clone())
408 }
409 }
410 PlaceBase::Return => None,
411 }
412 }
413 PropertyArg::Expr(expr) => self.eval_contract_expr(vm_state, Some(checkpoint), expr),
414 _ => None,
415 }
416 }
417
418 pub(super) fn count_is_zero<'z3, 'tcx>(
423 &self,
424 vm_state: &VmState<'z3, 'tcx>,
425 checkpoint: &Checkpoint<'tcx>,
426 property: &Property<'tcx>,
427 count_arg: usize,
428 ) -> bool {
429 property
430 .args()
431 .get(count_arg)
432 .and_then(|a| self.resolve_arg_term(vm_state, checkpoint, a))
433 .and_then(|ct| ct.as_u64())
434 == Some(0)
435 }
436
437 pub(super) fn access_bytes<'z3, 'tcx>(
438 &self,
439 vm_state: &VmState<'z3, 'tcx>,
440 property: &Property<'tcx>,
441 ty_arg: usize,
442 count_arg: usize,
443 checkpoint: &Checkpoint<'tcx>,
444 value: &VmValue<'z3, 'tcx>,
445 ) -> Int<'z3> {
446 let elem_ty = property
451 .args()
452 .get(ty_arg)
453 .and_then(|a| {
454 if let PropertyArg::Ty(ty) = a {
455 Some(*ty)
456 } else {
457 None
458 }
459 })
460 .filter(|ty| vm_state.size_of_ty(*ty) > 0)
461 .or_else(|| {
462 crate::helpers::mir_utils::pointee_ty(value.ty).map(|ty| match ty.kind() {
466 rustc_middle::ty::TyKind::Slice(e) | rustc_middle::ty::TyKind::Array(e, _) => {
467 *e
468 }
469 _ => ty,
470 })
471 });
472 let elem_size_term = elem_ty
473 .map(|ty| vm_state.size_sym_read(ty))
474 .unwrap_or_else(|| Int::from_u64(vm_state.z3_ctx, 1));
475
476 let count_term = property
477 .args()
478 .get(count_arg)
479 .and_then(|a| self.resolve_arg_term(vm_state, checkpoint, a))
480 .unwrap_or_else(|| Int::from_u64(vm_state.z3_ctx, 1));
481 if let (Some(elem), Some(count)) = (
483 elem_size_term.simplify().as_u64(),
484 count_term.simplify().as_u64(),
485 ) {
486 return Int::from_u64(vm_state.z3_ctx, elem.max(1) * count.max(1));
487 }
488 Int::mul(vm_state.z3_ctx, &[&elem_size_term, &count_term])
489 }
490
491 pub(super) fn zst_guard<'z3, 'tcx>(
492 &self,
493 vm_state: &VmState<'z3, 'tcx>,
494 checkpoint: &Checkpoint<'tcx>,
495 property: &Property<'tcx>,
496 ) -> bool {
497 let required_ty = Self::ty_arg(property, 1);
498 self.is_zst_type(vm_state, checkpoint, required_ty)
499 }
500
501 pub(super) fn is_zst_type<'z3, 'tcx>(
502 &self,
503 vm_state: &VmState<'z3, 'tcx>,
504 checkpoint: &Checkpoint<'tcx>,
505 ty: Option<Ty<'tcx>>,
506 ) -> bool {
507 let ty = match ty {
508 Some(t) => t,
509 None => return false,
510 };
511 if self.is_concrete_zst(vm_state, ty) {
512 return true;
513 }
514 let resolved = self.instantiate_callsite_ty(vm_state, checkpoint, ty);
515 if resolved != ty {
516 return self.is_concrete_zst(vm_state, resolved);
517 }
518 false
519 }
520
521 pub(super) fn is_concrete_zst<'z3, 'tcx>(
522 &self,
523 vm_state: &VmState<'z3, 'tcx>,
524 ty: Ty<'tcx>,
525 ) -> bool {
526 !self.is_generic_ty(ty) && vm_state.size_of_ty(ty) == 0
527 }
528
529 pub(super) fn is_generic_ty<'tcx>(&self, ty: Ty<'tcx>) -> bool {
530 matches!(
531 ty.kind(),
532 TyKind::Param(_) | TyKind::Alias(..) | TyKind::Error(_)
533 )
534 }
535
536 pub(super) fn instantiate_callsite_ty<'z3, 'tcx>(
537 &self,
538 vm_state: &VmState<'z3, 'tcx>,
539 checkpoint: &Checkpoint<'tcx>,
540 ty: Ty<'tcx>,
541 ) -> Ty<'tcx> {
542 let TyKind::Param(param) = ty.kind() else {
543 return ty;
544 };
545 if checkpoint.callee.is_none() {
551 return ty;
552 };
553
554 let body = vm_state.body();
555 let terminator = body.basic_blocks[checkpoint.block].terminator();
556 let TerminatorKind::Call { func, .. } = &terminator.kind else {
557 return ty;
558 };
559 let Operand::Constant(func_constant) = func else {
560 return ty;
561 };
562 let TyKind::FnDef(_, args) = func_constant.const_.ty().kind() else {
563 return ty;
564 };
565 let Some(arg) = crate::compat::args_get(args, param.index as usize) else {
566 return ty;
567 };
568 match arg.kind() {
569 GenericArgKind::Type(actual_ty) => actual_ty,
570 _ => ty,
571 }
572 }
573
574 pub(super) fn instantiate_callsite_const<'z3, 'tcx>(
575 &self,
576 vm_state: &VmState<'z3, 'tcx>,
577 checkpoint: &Checkpoint<'tcx>,
578 index: u32,
579 ) -> Option<u128> {
580 let body = vm_state.body();
581 let terminator = body.basic_blocks[checkpoint.block].terminator();
582 let TerminatorKind::Call { func, .. } = &terminator.kind else {
583 return None;
584 };
585 let Operand::Constant(func_constant) = func else {
586 return None;
587 };
588 let TyKind::FnDef(_, args) = func_constant.const_.ty().kind() else {
589 return None;
590 };
591 let arg = crate::compat::args_get(args, index as usize)?;
592 match arg.kind() {
593 GenericArgKind::Const(actual_const) => actual_const
594 .try_to_target_usize(vm_state.tcx)
595 .map(|value| value as u128)
596 .or_else(|| {
597 crate::helpers::mir_utils::const_int_from_debug(&format!("{actual_const:?}"))
598 .map(|v| v as u128)
599 }),
600 _ => None,
601 }
602 }
603
604 pub(super) fn resolve_ty_params<'z3, 'tcx>(
605 &self,
606 vm_state: &VmState<'z3, 'tcx>,
607 checkpoint: &Checkpoint<'tcx>,
608 ty: Ty<'tcx>,
609 ) -> Ty<'tcx> {
610 match ty.kind() {
611 TyKind::Param(_) => self.instantiate_callsite_ty(vm_state, checkpoint, ty),
612 TyKind::Adt(adt_def, substs) => {
613 let mut changed = false;
614 let resolved_substs: Vec<_> = substs
615 .iter()
616 .map(|arg| match arg.kind() {
617 GenericArgKind::Type(t) => {
618 let resolved = self.resolve_ty_params(vm_state, checkpoint, t);
619 if resolved != t {
620 changed = true;
621 GenericArg::from(resolved)
622 } else {
623 arg.clone()
624 }
625 }
626 _ => arg.clone(),
627 })
628 .collect();
629 if changed {
630 Ty::new_adt(
631 vm_state.tcx,
632 *adt_def,
633 vm_state.tcx.mk_args(&resolved_substs),
634 )
635 } else {
636 ty
637 }
638 }
639 _ => ty,
640 }
641 }
642
643 pub(super) fn eval_contract_expr<'z3, 'tcx>(
644 &self,
645 vm_state: &VmState<'z3, 'tcx>,
646 checkpoint: Option<&Checkpoint<'tcx>>,
647 expr: &ContractExpr<'tcx>,
648 ) -> Option<Int<'z3>> {
649 match expr {
650 ContractExpr::Const(n) => Some(Int::from_u64(vm_state.z3_ctx, *n as u64)),
651 ContractExpr::SizeOf(ty) => {
652 let mut size = vm_state.size_of_ty(*ty);
653 if size == 0 && matches!(ty.kind(), rustc_middle::ty::TyKind::Param(_)) {
654 size = crate::helpers::mir_utils::size_of_generic_param(
655 vm_state.tcx,
656 vm_state.current_frame.current_def_id,
657 *ty,
658 );
659 if size == 0 {
660 if let Some(ck) = checkpoint {
661 if let Some(_callee) = ck.callee {
662 if !self.is_caller_type_param(vm_state, *ty) {
663 let resolved = self.instantiate_callsite_ty(vm_state, ck, *ty);
664 if resolved != *ty {
665 size = vm_state.size_of_ty(resolved);
666 }
667 }
668 }
669 }
670 }
671 }
672 if size > 0 {
673 Some(Int::from_u64(vm_state.z3_ctx, size))
674 } else {
675 Some(Int::from_u64(vm_state.z3_ctx, 0))
676 }
677 }
678 ContractExpr::AlignOf(ty) => {
679 let align = vm_state.align_of_ty(*ty);
680 if align > 0 {
681 Some(Int::from_u64(vm_state.z3_ctx, align.max(1)))
682 } else {
683 Some(Int::from_u64(vm_state.z3_ctx, 0))
684 }
685 }
686 ContractExpr::Place(cp) => self.eval_contract_place(vm_state, checkpoint, cp),
687 ContractExpr::Binary { op, lhs, rhs } => {
688 let l = self.eval_contract_expr(vm_state, checkpoint, lhs)?;
689 let r = self.eval_contract_expr(vm_state, checkpoint, rhs)?;
690 match op {
691 NumericBinOp::Add => Some(Int::add(vm_state.z3_ctx, &[&l, &r])),
692 NumericBinOp::Sub => Some(Int::sub(vm_state.z3_ctx, &[&l, &r])),
693 NumericBinOp::Mul => Some(Int::mul(vm_state.z3_ctx, &[&l, &r])),
694 NumericBinOp::Div | NumericBinOp::Rem => {
695 if r.as_u64() == Some(0) {
701 Some(Int::from_u64(vm_state.z3_ctx, 0))
702 } else if matches!(op, NumericBinOp::Div) {
703 Some(l.div(&r))
704 } else {
705 let q = l.div(&r);
706 Some(Int::sub(
707 vm_state.z3_ctx,
708 &[&l, &Int::mul(vm_state.z3_ctx, &[&q, &r])],
709 ))
710 }
711 }
712 NumericBinOp::Min => Some(l.le(&r).ite(&l, &r)),
713 NumericBinOp::Max => Some(l.ge(&r).ite(&l, &r)),
714 _ => None,
715 }
716 }
717 ContractExpr::Unary { op, expr: inner } => {
718 let v = self.eval_contract_expr(vm_state, checkpoint, inner)?;
719 match op {
720 crate::verify::contract::NumericUnaryOp::Not => {
721 Some(v._eq(&Int::from_u64(vm_state.z3_ctx, 0)).ite(
722 &Int::from_u64(vm_state.z3_ctx, 1),
723 &Int::from_u64(vm_state.z3_ctx, 0),
724 ))
725 }
726 crate::verify::contract::NumericUnaryOp::Neg => {
727 let zero = Int::from_u64(vm_state.z3_ctx, 0);
728 Some(Int::sub(vm_state.z3_ctx, &[&zero, &v]))
729 }
730 }
731 }
732 ContractExpr::Len(inner) => {
733 if let Some(ck) = checkpoint {
734 if let Some(term) = self.try_iter_len_from_fields(vm_state, ck, inner) {
735 return Some(term);
736 }
737 }
738 let val = self.eval_contract_expr_to_value(vm_state, checkpoint, inner)?;
739 if let crate::verify::contract::ContractExpr::Place(cp) = &**inner {
742 if let Some(field_path) = cp.plain_field_path() {
743 let base_local =
744 match cp.base {
745 PlaceBase::Return => Some(Local::from_usize(0)),
746 PlaceBase::Local(n) => Some(Local::from_usize(n)),
747 PlaceBase::Arg(n) => checkpoint
748 .and_then(|ck| ck.args.get(n))
749 .and_then(|op| match op {
750 Operand::Copy(p) | Operand::Move(p) => Some(p.local),
751 _ => None,
752 }),
753 };
754 if let Some(local) = base_local {
755 if let Some(len) =
756 vm_state.try_struct_nn_len_field(local, &field_path, val.ty)
757 {
758 return Some(len);
759 }
760 }
761 }
762 }
763 vm_state.len_from_value(&val)
764 }
765 ContractExpr::ConstParam { index, name: _ } => self
766 .instantiate_callsite_const(vm_state, checkpoint?, *index)
767 .and_then(|v| u64::try_from(v).ok())
768 .map(|v| Int::from_u64(vm_state.z3_ctx, v)),
769 ContractExpr::If {
770 cond,
771 then_expr,
772 else_expr,
773 } => {
774 let l = self.eval_contract_expr(vm_state, checkpoint, &cond.lhs)?;
775 let r = self.eval_contract_expr(vm_state, checkpoint, &cond.rhs)?;
776 let cond_bool = match cond.op {
777 RelOp::Eq => l._eq(&r),
778 RelOp::Ne => l._eq(&r).not(),
779 RelOp::Le => l.le(&r),
780 RelOp::Lt => l.lt(&r),
781 RelOp::Ge => l.ge(&r),
782 RelOp::Gt => l.gt(&r),
783 };
784 match cond_bool.simplify().as_bool() {
789 Some(true) => self.eval_contract_expr(vm_state, checkpoint, then_expr),
790 Some(false) => self.eval_contract_expr(vm_state, checkpoint, else_expr),
791 _ => {
792 let t = self.eval_contract_expr(vm_state, checkpoint, then_expr)?;
793 let e = self.eval_contract_expr(vm_state, checkpoint, else_expr)?;
794 Some(cond_bool.ite(&t, &e))
795 }
796 }
797 }
798 _ => None,
799 }
800 }
801
802 pub(super) fn eval_contract_expr_to_value<'z3, 'tcx>(
803 &self,
804 vm_state: &VmState<'z3, 'tcx>,
805 checkpoint: Option<&Checkpoint<'tcx>>,
806 expr: &ContractExpr<'tcx>,
807 ) -> Option<VmValue<'z3, 'tcx>> {
808 match expr {
809 ContractExpr::Place(cp) => {
810 if cp.projections.is_empty() {
811 return match cp.base {
812 PlaceBase::Return => vm_state.local_value(Local::from_usize(0)).cloned(),
813 PlaceBase::Arg(n) => checkpoint?
814 .args
815 .get(n)
816 .map(|op| vm_state.value_of_operand(op)),
817 PlaceBase::Local(n) => {
818 let ck = checkpoint?;
819 if let Some(op) = local_param_operand(vm_state, ck, n) {
820 return Some(vm_state.value_of_operand(op));
821 }
822 vm_state.local_value(Local::from_usize(n)).cloned()
823 }
824 };
825 }
826 let base_local = match cp.base {
832 PlaceBase::Return => Local::from_usize(0),
833 PlaceBase::Arg(n) => {
834 let op = checkpoint?.args.get(n)?;
835 match op {
836 Operand::Copy(p) | Operand::Move(p) => p.local,
837 _ => return None,
838 }
839 }
840 PlaceBase::Local(n) => Local::from_usize(n),
841 };
842 let field_path = cp.plain_field_path()?;
843 vm_state.field_value(base_local, &field_path).cloned()
844 }
845 _ => None,
846 }
847 }
848
849 pub(super) fn eval_contract_place<'z3, 'tcx>(
850 &self,
851 vm_state: &VmState<'z3, 'tcx>,
852 checkpoint: Option<&Checkpoint<'tcx>>,
853 cp: &crate::verify::contract::ContractPlace<'tcx>,
854 ) -> Option<Int<'z3>> {
855 let field_path = cp.plain_field_path()?;
859
860 let base_local: Option<Local> = match cp.base {
861 PlaceBase::Return => Some(Local::from_usize(0)),
862 PlaceBase::Arg(n) => {
863 if field_path.is_empty() {
864 return checkpoint.and_then(|ck| {
865 let op = ck.args.get(n)?;
866 self.eval_contract_operand(vm_state, op)
867 });
868 }
869 checkpoint
872 .and_then(|ck| ck.args.get(n))
873 .and_then(|op| match op {
874 Operand::Copy(p) | Operand::Move(p) => Some(p.local),
875 _ => None,
876 })
877 }
878 PlaceBase::Local(n) => {
879 if field_path.is_empty() {
880 if let Some(ck) = checkpoint {
881 if let Some(op) = local_param_operand(vm_state, ck, n) {
882 if let Some(v) = self.eval_contract_operand(vm_state, op) {
883 return Some(v);
884 }
885 }
886 }
887 }
888 Some(Local::from_usize(n))
889 }
890 };
891
892 let local = base_local?;
893 if field_path.is_empty() {
894 vm_state.local_value(local).map(|v| v.z3_term.clone())
895 } else {
896 vm_state
897 .field_value(local, &field_path)
898 .map(|v| v.z3_term.clone())
899 }
900 }
901
902 pub(super) fn eval_contract_operand<'z3, 'tcx>(
903 &self,
904 vm_state: &VmState<'z3, 'tcx>,
905 op: &Operand<'tcx>,
906 ) -> Option<Int<'z3>> {
907 match op {
908 Operand::Constant(c) => {
909 let const_text = format!("{:?}", c.const_);
910 let typing_env = rustc_middle::ty::TypingEnv::fully_monomorphized();
911 if let Ok(val) = c
912 .const_
913 .eval(vm_state.tcx, typing_env, rustc_span::DUMMY_SP)
914 {
915 if let Some(scalar) = val.try_to_scalar_int() {
916 let v = scalar.to_bits(scalar.size()) as u64;
917 if v == 0
918 && (const_text.contains("AlignOf")
919 || const_text.contains("SizeOf")
920 || const_text.contains("min_align_of")
921 || const_text.contains("min_size_of"))
922 {
923 } else {
927 return Some(Int::from_u64(vm_state.z3_ctx, v));
928 }
929 }
930 }
931 crate::helpers::mir_utils::const_int_from_debug(&const_text)
932 .map(|v| Int::from_u64(vm_state.z3_ctx, v))
933 }
934 Operand::Copy(p) | Operand::Move(p) if p.projection.is_empty() => {
935 vm_state.local_value(p.local).map(|v| v.z3_term.clone())
936 }
937 _ => None,
938 }
939 }
940
941 pub(super) fn trace_value<'z3, 'tcx>(
942 &self,
943 vm_state: &VmState<'z3, 'tcx>,
944 op: &Operand<'tcx>,
945 ) -> VmValue<'z3, 'tcx> {
946 let place = match op {
947 Operand::Copy(p) | Operand::Move(p) => p,
948 _ => return vm_state.value_of_operand(op),
949 };
950 if !place.projection.is_empty() {
951 return vm_state.value_of_operand(op);
952 }
953 let local = place.local;
954 if local.as_usize() <= vm_state.body().arg_count {
956 return vm_state.value_of_operand(op);
957 }
958 for block in vm_state.body().basic_blocks.iter() {
960 for stmt in &block.statements {
961 if let StatementKind::Assign(assign) = &stmt.kind {
962 let (dest, rvalue) = &**assign;
963 if dest.local == local && dest.projection.is_empty() {
964 #[cfg(rapx_rvalue_use_with_retag)]
965 if let Rvalue::Use(src_op, _) = rvalue {
966 return self.trace_value(vm_state, src_op);
967 }
968 #[cfg(not(rapx_rvalue_use_with_retag))]
969 if let Rvalue::Use(src_op) = rvalue {
970 return self.trace_value(vm_state, src_op);
971 }
972 }
973 }
974 }
975 }
976 vm_state.value_of_operand(op)
977 }
978
979 pub(super) fn alloc_elem_is_array_of<'tcx>(
980 &self,
981 alloc_elem_ty: Ty<'tcx>,
982 required_ty: Ty<'tcx>,
983 ) -> bool {
984 match alloc_elem_ty.kind() {
985 TyKind::Array(inner_ty, _) => {
986 *inner_ty == required_ty
987 || matches!(
988 (inner_ty.kind(), required_ty.kind()),
989 (TyKind::Param(_), TyKind::Param(_))
990 )
991 }
992 TyKind::Slice(inner_ty) => {
993 *inner_ty == required_ty
994 || matches!(
995 (inner_ty.kind(), required_ty.kind()),
996 (TyKind::Param(_), TyKind::Param(_))
997 )
998 }
999 _ => false,
1000 }
1001 }
1002}
1003
1004pub(super) fn maybe_uninit_inner(ty: Ty<'_>) -> Option<Ty<'_>> {
1008 if let TyKind::Adt(adt_def, substs) = ty.kind()
1009 && crate::verify::api_classify::is_maybe_uninit_type(adt_def.did())
1010 && let Some(inner) = substs.first().and_then(|s| s.as_type())
1011 {
1012 return Some(inner);
1013 }
1014 None
1015}
1016
1017pub(super) fn smart_pointer_pointee(ty: Ty<'_>) -> Option<Ty<'_>> {
1024 match ty.kind() {
1025 TyKind::RawPtr(e, _) | TyKind::Ref(_, e, _) => Some(*e),
1026 TyKind::Adt(adt, args) => {
1027 let did = adt.did();
1028 if crate::verify::api_classify::is_std_box(did)
1029 || crate::verify::api_classify::is_std_vec(did)
1030 || crate::verify::api_classify::is_std_nonnull(did)
1031 || crate::verify::api_classify::is_std_cstring(did)
1032 || crate::def_id::rc_types().contains(&did)
1033 {
1034 args.types().next()
1035 } else {
1036 None
1037 }
1038 }
1039 _ => None,
1040 }
1041}