1use rustc_middle::{
8 mir::{
9 BasicBlock, BinOp, Local, Operand, Place, Rvalue,
10 Statement, StatementKind, Terminator, TerminatorKind, UnOp,
11 },
12 ty::Ty,
13};
14#[cfg(not(rapx_has_skip_norm_wip))]
15use crate::compat::SkipNormWip;
16use rustc_hir::def_id::DefId;
17use z3::ast::{Ast, Bool, Int};
18
19use crate::{
20 compat::{FxHashMap, FxHashSet},
21 verify::{
22 contract::{ContractExpr, ContractKind, PlaceBase, Property, PropertyArg, PropertyKind},
23 def_use::PlaceKey,
24 path_extractor::{Path, PathStep},
25 slicer::RelevantItem,
26 },
27};
28
29use super::state::{AllocId, InlineFrame, Provenance, VmState, VmValue, ValueInvariants};
30
31use crate::helpers::api_classify;
32
33impl<'ctx, 'tcx> VmState<'ctx, 'tcx> {
34 pub fn execute_items(
36 &mut self,
37 items: &[RelevantItem<'tcx>],
38 ) {
39 self.init_parameters();
42
43 for item in items {
44 match item {
45 RelevantItem::CalleeEntry { callee, args } => {
46 self.handle_callee_entry(*callee, args);
47 }
48 RelevantItem::Statement {
49 block,
50 statement_index,
51 } => {
52 let statement =
53 &self.body.basic_blocks[*block].statements[*statement_index];
54 self.exec_statement(*block, *statement_index, statement);
55 }
56 RelevantItem::Terminator { block } => {
57 let occ = self
58 .block_occurrences
59 .get(block)
60 .map(|c| c + 1)
61 .unwrap_or(1);
62 self.block_occurrences.insert(*block, occ);
63 let terminator = self.body.basic_blocks[*block].terminator();
64 self.exec_terminator(*block, terminator, occ);
65 }
66 RelevantItem::ContractFact { property } => {
67 self.assert_contract_fact(property);
68 }
69 RelevantItem::Forget => {
70 self.notes.push("forget: unsupported call".to_string());
71 }
72 RelevantItem::CalleeExit { dest } => {
73 self.handle_callee_exit(*dest);
74 }
75 }
76 }
77 }
78
79 fn handle_callee_entry(
83 &mut self,
84 callee_def_id: DefId,
85 arg_locals: &[Local],
86 ) {
87 let callee_body = self.tcx.optimized_mir(callee_def_id);
88
89 let saved_locals = std::mem::take(&mut self.locals);
91
92 let arg_vals: Vec<Option<VmValue<'ctx, 'tcx>>> = arg_locals.iter()
94 .map(|&local| saved_locals.get(&local).cloned())
95 .collect();
96
97 self.inline_frames.push(InlineFrame {
99 body: self.body,
100 def_id: self.caller_def_id,
101 saved_locals,
102 });
103 self.body = callee_body;
104 self.caller_def_id = callee_def_id;
105
106 for (i, arg_val) in arg_vals.into_iter().enumerate() {
108 let callee_local = Local::from_usize(i + 1);
109 if let Some(val) = arg_val {
110 self.ensure_local_allocation(callee_local);
111 self.set_local(callee_local, val);
112 }
113 }
114 for (i, caller_arg) in arg_locals.iter().enumerate() {
118 let callee_param = Local::from_usize(i + 1);
119 let caller_field_keys: Vec<Vec<usize>> = self.field_values.keys()
120 .filter(|(l, _)| *l == *caller_arg)
121 .map(|(_, f)| f.clone())
122 .collect();
123 for fields in caller_field_keys {
124 if let Some(fv) = self.field_value(*caller_arg, &fields).cloned() {
125 self.set_field_value(callee_param, fields, fv);
126 }
127 }
128 }
129 }
130
131 fn handle_callee_exit(
135 &mut self,
136 dest: Local,
137 ) {
138 let return_val = self.locals.get(&Local::from_usize(0)).cloned();
139
140 let return_fields: Vec<(Vec<usize>, VmValue<'ctx, 'tcx>)> = self
147 .field_values
148 .keys()
149 .filter(|(l, _)| *l == Local::from_usize(0))
150 .cloned()
151 .collect::<Vec<_>>()
152 .into_iter()
153 .filter_map(|(_, fields)| {
154 self.field_value(Local::from_usize(0), &fields)
155 .cloned()
156 .map(|v| (fields, v))
157 })
158 .collect();
159
160 if let Some(frame) = self.inline_frames.last() {
163 let name = self.tcx.def_path_str(frame.def_id);
164 if api_classify::is_iter_ptr_adj(&name) {
165 self.track_iter_ptr_after_inline();
166 }
167 }
168
169 if let Some(frame) = self.inline_frames.pop() {
171 self.body = frame.body;
172 self.caller_def_id = frame.def_id;
173 self.locals = frame.saved_locals;
174 }
175
176 if let Some(mut val) = return_val {
178 let dest_ty = self.body.local_decls[dest].ty;
179 val.ty = dest_ty;
180 if let Some(ref prov) = val.provenance {
181 if prov.offset.as_u64() == Some(0) {
182 val.invariants.non_null = true;
183 val.invariants.init = true;
184 val.invariants.aligned = true;
185 self.alloc_mut(prov.alloc_id).initialized = true;
186 }
187 }
188 self.set_local(dest, val);
189 }
190
191 if let Some(dest_alloc_id) = self.local_alloc_ids.get(&dest).copied() {
197 self.alloc_mut(dest_alloc_id).initialized = true;
198 }
199
200 for (fields, field_val) in return_fields {
202 self.set_field_value(dest, fields, field_val);
203 }
204 }
205
206 fn init_parameters(&mut self) {
209 let arg_count = self.body.arg_count;
210 let local_count = self.body.local_decls.len();
211
212 for local_idx in 1..local_count {
214 let local = Local::from_usize(local_idx);
215 if self.locals.contains_key(&local) {
216 continue;
217 }
218 let decl = &self.body.local_decls[local];
219 let ty = decl.ty;
220
221 self.ensure_local_allocation(local);
222
223 let mut invariants = ValueInvariants::default();
224 if local_idx <= arg_count {
225 if let rustc_middle::ty::TyKind::Adt(adt_def, _) = ty.kind() {
227 let def_path = self.tcx.def_path_str(adt_def.did());
228 let is_vec = api_classify::is_std_vec(&def_path);
229 if api_classify::is_std_box(&def_path)
230 || is_vec
231 || api_classify::is_std_cstring(&def_path)
232 {
233 let heap_ty = if let rustc_middle::ty::TyKind::Adt(_, substs) = ty.kind() {
234 if let Some(first) = substs.first() {
235 first.as_type()
236 } else {
237 None
238 }
239 } else {
240 None
241 };
242 let heap_ty = heap_ty.unwrap_or(ty);
243 let heap_size = self.size_of_ty(heap_ty) as u64;
244 let heap_align = self.align_of_ty(heap_ty);
245 let heap_size_term = Int::from_u64(self.ctx, heap_size.max(1));
246 let (heap_alloc_id, heap_base) = if is_vec {
250 let max_size = Int::from_u64(self.ctx, i64::MAX as u64);
251 let (id, base) = self.allocate_external(max_size, heap_align, Some(heap_ty));
252 (id, base)
253 } else {
254 self.allocate(heap_size_term, heap_align, Some(heap_ty))
255 };
256 invariants.non_null = true;
257 invariants.init = true;
258 invariants.aligned = true;
259 self.alloc_mut(heap_alloc_id).initialized = true;
260 self.set_field_value(local, vec![0, 0], VmValue {
266 term: heap_base.clone(),
267 ty,
268 provenance: Some(Provenance {
269 alloc_id: heap_alloc_id,
270 offset: Int::from_u64(self.ctx, 0),
271 is_field_offset: false,
272 }),
273 invariants: ValueInvariants {
274 non_null: true,
275 init: true,
276 aligned: true,
277 ..Default::default()
278 },
279 });
280 self.set_local(local, VmValue {
281 term: heap_base,
282 ty,
283 provenance: Some(Provenance {
284 alloc_id: heap_alloc_id,
285 offset: Int::from_u64(self.ctx, 0),
286 is_field_offset: false,
287 }),
288 invariants,
289 });
290 continue;
291 }
292 }
293 if let rustc_middle::ty::TyKind::Adt(adt_def, substs) = ty.kind() {
296 if adt_def.is_enum() {
297 let term = self.fresh_int(&format!("param_{}", local_idx));
298 self.set_local(local, VmValue {
299 term, ty, provenance: None, invariants,
300 });
301 continue;
302 }
303 let variant = adt_def.non_enum_variant();
304 let mut elem_alloc: FxHashMap<Ty<'tcx>, (AllocId, Int<'ctx>)> =
305 FxHashMap::default();
306 for (idx, field_def) in variant.fields.iter().enumerate() {
307 let field_ty: Ty<'tcx> = field_def.ty(self.tcx, substs).skip_norm_wip();
308 if let rustc_middle::ty::TyKind::RawPtr(inner, _) = field_ty.kind() {
309 self.init_ptr_field(local, vec![idx], field_ty, *inner, local_idx, idx, &mut elem_alloc, true, "field_nn");
310 } else if let Some(pointee) = self.find_nn_pointee(field_ty) {
311 self.init_ptr_field(local, vec![idx], field_ty, pointee, local_idx, idx, &mut elem_alloc, false, "field_nn");
312 } else if let rustc_middle::ty::TyKind::Adt(inner_adt, _) = field_ty.kind() {
313 if !inner_adt.is_enum() {
314 self.decompose_adt_fields(local, vec![idx], field_ty, local_idx, &mut elem_alloc, 1);
315 } else {
316 let field_term = self.fresh_int(&format!("field_{}_{}", local_idx, idx));
317 self.set_field_value(local, vec![idx], VmValue {
318 term: field_term, ty: field_ty, provenance: None,
319 invariants: ValueInvariants { init: true, ..Default::default() },
320 });
321 }
322 } else {
323 let field_term = self.fresh_int(
324 &format!("field_{}_{}", local_idx, idx)
325 );
326 self.set_field_value(local, vec![idx], VmValue {
327 term: field_term,
328 ty: field_ty,
329 provenance: None,
330 invariants: ValueInvariants { init: true, ..Default::default() },
331 });
332 }
333 }
334 let term = self.fresh_int(&format!("param_{}", local_idx));
335 self.set_local(local, VmValue {
336 term,
337 ty,
338 provenance: None,
339 invariants: ValueInvariants { init: true, ..Default::default() },
340 });
341 continue;
342 }
343 if let rustc_middle::ty::TyKind::Ref(..) = ty.kind() {
348 invariants.non_null = true;
349 invariants.init = true;
350 invariants.aligned = true;
351
352 let pointee_ty = if let rustc_middle::ty::TyKind::Ref(_, inner_ty, _) = ty.kind() {
353 *inner_ty
354 } else {
355 ty
356 };
357
358 if let rustc_middle::ty::TyKind::Slice(elem_ty) = pointee_ty.kind() {
359 let elem_size = self.size_of_ty(*elem_ty) as u64;
360 let len = self.fresh_int(&format!("slice_len_{}", local_idx));
361 let zero = Int::from_u64(self.ctx, 0);
362 self.path_conditions.push(len.ge(&zero));
363 let isize_max = Int::from_i64(self.ctx, i64::MAX);
364 let elem_sz = if elem_size > 0 {
365 elem_size
366 } else {
367 crate::helpers::mir_utils::size_of_generic_param(self.tcx, self.caller_def_id, *elem_ty).max(1)
368 };
369 let elem_sz_term = Int::from_u64(self.ctx, elem_sz);
370 self.path_conditions.push(
371 Int::mul(self.ctx, &[&len, &elem_sz_term]).le(&isize_max));
372 let data_size = Int::mul(self.ctx, &[
373 &len,
374 &Int::from_u64(self.ctx, elem_size.max(1)),
375 ]);
376 let (data_alloc_id, data_base) = self.allocate(
377 data_size,
378 self.align_of_ty(*elem_ty),
379 Some(*elem_ty),
380 );
381 if let Some(ref_alloc_id) = self.alloc_for_local(local) {
382 self.alloc_mut(ref_alloc_id).slice_data = Some(data_alloc_id);
383 }
384 self.alloc_mut(data_alloc_id).initialized = true;
385 self.set_local(local, VmValue {
386 term: data_base,
387 ty,
388 provenance: Some(Provenance {
389 alloc_id: data_alloc_id,
390 offset: Int::from_u64(self.ctx, 0),
391 is_field_offset: false,
392 }),
393 invariants,
394 });
395 continue;
396 }
397
398 let pointee_size = self.size_of_ty(pointee_ty) as u64;
400 let pointee_align = self.align_of_ty(pointee_ty);
401 let pointee_size_term = Int::from_u64(self.ctx, pointee_size.max(1));
402 let (pointee_alloc_id, pointee_base) = self.allocate(
403 pointee_size_term,
404 pointee_align,
405 Some(pointee_ty),
406 );
407 self.alloc_mut(pointee_alloc_id).initialized = true;
408 self.set_local(local, VmValue {
409 term: pointee_base,
410 ty,
411 provenance: Some(Provenance {
412 alloc_id: pointee_alloc_id,
413 offset: Int::from_u64(self.ctx, 0),
414 is_field_offset: false,
415 }),
416 invariants,
417 });
418
419 if let rustc_middle::ty::TyKind::Adt(adt_def, substs) = pointee_ty.kind() {
422 if !adt_def.is_enum() {
423 let variant = adt_def.non_enum_variant();
424 let mut elem_alloc: FxHashMap<Ty<'tcx>, (AllocId, Int<'ctx>)> =
430 FxHashMap::default();
431 for (idx, field_def) in variant.fields.iter().enumerate() {
432 let field_ty: Ty<'tcx> = field_def.ty(self.tcx, substs).skip_norm_wip();
433 if let rustc_middle::ty::TyKind::RawPtr(inner, _) = field_ty.kind() {
434 self.init_ptr_field(local, vec![idx], field_ty, *inner, local_idx, idx, &mut elem_alloc, true, "field_nn");
435 } else if let Some(pointee) = self.find_nn_pointee(field_ty) {
436 self.init_ptr_field(local, vec![idx], field_ty, pointee, local_idx, idx, &mut elem_alloc, false, "ref_field");
439 } else if let rustc_middle::ty::TyKind::Ref(_, pointee, _) = field_ty.kind() {
440 if let rustc_middle::ty::TyKind::Slice(elem_ty) = pointee.kind() {
444 let elem_align = 1u64.max(self.align_of_ty(*elem_ty));
445 let max_size = Int::from_u64(self.ctx, i64::MAX as u64);
446 let (data_alloc_id, data_base) = self.allocate_external(
447 max_size, elem_align, Some(*elem_ty),
448 );
449 self.alloc_mut(data_alloc_id).initialized = true;
450 self.alloc_mut(data_alloc_id).alive_assumed = true;
451 self.set_field_value(local, vec![idx], VmValue {
452 term: data_base,
453 ty: field_ty,
454 provenance: Some(Provenance {
455 alloc_id: data_alloc_id,
456 offset: Int::from_u64(self.ctx, 0),
457 is_field_offset: false,
458 }),
459 invariants: ValueInvariants {
460 non_null: true, init: true, ..Default::default()
461 },
462 });
463 } else {
464 let pointee_align = 1u64.max(self.align_of_ty(*pointee));
465 let max_size = Int::from_u64(self.ctx, i64::MAX as u64);
466 let (field_alloc_id, field_base) = self.allocate_external(
467 max_size, pointee_align, Some(*pointee),
468 );
469 self.alloc_mut(field_alloc_id).initialized = true;
470 self.alloc_mut(field_alloc_id).alive_assumed = true;
471 self.set_field_value(local, vec![idx], VmValue {
472 term: field_base,
473 ty: field_ty,
474 provenance: Some(Provenance {
475 alloc_id: field_alloc_id,
476 offset: Int::from_u64(self.ctx, 0),
477 is_field_offset: false,
478 }),
479 invariants: ValueInvariants {
480 non_null: true, init: true, ..Default::default()
481 },
482 });
483 }
484 } else if matches!(
485 field_ty.kind(),
486 rustc_middle::ty::TyKind::Uint(_)
487 | rustc_middle::ty::TyKind::Int(_)
488 | rustc_middle::ty::TyKind::Float(_)
489 | rustc_middle::ty::TyKind::Bool
490 | rustc_middle::ty::TyKind::Char
491 ) {
492 let field_term = self.fresh_int(
500 &format!("ref_field_{}_{}", local_idx, idx)
501 );
502 self.set_field_value(local, vec![idx], VmValue {
503 term: field_term,
504 ty: field_ty,
505 provenance: None,
506 invariants: ValueInvariants { init: true, ..Default::default() },
507 });
508 }
509 }
510 }
511 }
512 continue;
513 }
514 let is_scalar = matches!(
516 ty.kind(),
517 rustc_middle::ty::TyKind::Uint(_)
518 | rustc_middle::ty::TyKind::Int(_)
519 | rustc_middle::ty::TyKind::Bool
520 | rustc_middle::ty::TyKind::Char
521 );
522 if is_scalar {
523 let val = self.fresh_int(&format!("arg_{}", local_idx));
524 self.set_local(local, VmValue {
525 term: val,
526 ty,
527 provenance: None,
528 invariants,
529 });
530 continue;
531 }
532 if let rustc_middle::ty::TyKind::RawPtr(pointee, _mutbl) = ty.kind() {
538 let max_size = Int::from_u64(self.ctx, i64::MAX as u64);
539 let pointee_align = self.align_of_ty(*pointee);
540 let (alloc_id, base) = self.allocate_external(max_size, pointee_align, Some(*pointee));
541 self.set_local(local, VmValue {
542 term: base,
543 ty,
544 provenance: Some(Provenance {
545 alloc_id,
546 offset: Int::from_u64(self.ctx, 0),
547 is_field_offset: false,
548 }),
549 invariants,
550 });
551 continue;
552 }
553 if let rustc_middle::ty::TyKind::Array(elem_ty, const_len) = ty.kind() {
558 let n: Option<usize> = const_len
559 .try_to_target_usize(self.tcx)
560 .map(|v| v as usize);
561 let elem_size = self.size_of_ty(*elem_ty) as u64;
562 let step = (elem_size.max(1)) as usize;
563 let align = self.align_of_ty(*elem_ty);
564 let (alloc_id, base) = if let Some(n) = n {
565 let total = Int::from_u64(self.ctx, (step as u64).saturating_mul(n as u64));
566 self.allocate(total, align, Some(*elem_ty))
567 } else {
568 let max_size = Int::from_u64(self.ctx, i64::MAX as u64);
570 self.allocate_external(max_size, align, Some(*elem_ty))
571 };
572 self.alloc_mut(alloc_id).initialized = true;
573 self.local_alloc_ids.insert(local, alloc_id);
574 if let Some(n) = n {
575 for i in 0..n {
576 let off = i * step;
577 let elem_term = self.fresh_int(&format!(
578 "array_{}_idx_{}",
579 local_idx, i
580 ));
581 self.record_byte_value(alloc_id, off, elem_term);
582 }
583 } else {
584 let m = 16usize;
588 for i in 0..m {
589 let off = i * step;
590 let elem_term = self.fresh_int(&format!(
591 "array_{}_idx_{}",
592 local_idx, i
593 ));
594 self.record_byte_value(alloc_id, off, elem_term);
595 }
596 }
597 self.set_local(
598 local,
599 VmValue {
600 term: base,
601 ty,
602 provenance: Some(Provenance {
603 alloc_id,
604 offset: Int::from_u64(self.ctx, 0),
605 is_field_offset: false,
606 }),
607 invariants: ValueInvariants {
608 init: true,
609 ..invariants
610 },
611 },
612 );
613 continue;
614 }
615 let term = self.fresh_int(&format!("param_{}", local_idx));
617 self.set_local(local, VmValue {
618 term,
619 ty,
620 provenance: None,
621 invariants,
622 });
623 continue;
624 }
625 let term = match ty.kind() {
631 rustc_middle::ty::TyKind::Ref(..) | rustc_middle::ty::TyKind::RawPtr(..) => {
632 self.local_address(local)
633 }
634 _ => self.fresh_int(&format!("local_{}", local_idx)),
635 };
636 self.set_local(local, VmValue {
637 term,
638 ty,
639 provenance: None,
640 invariants,
641 });
642 }
643
644 if let Some(entry_bb) = self.body.basic_blocks.iter().next() {
650 for stmt in &entry_bb.statements {
651 if let StatementKind::Assign(assign) = &stmt.kind {
652 let (dest, rvalue) = &**assign;
653 let dest_local = dest.local;
654 let src = match rvalue {
655 #[cfg(rapx_rvalue_use_with_retag)]
656 Rvalue::Use(operand, _) => Some(operand),
657 #[cfg(not(rapx_rvalue_use_with_retag))]
658 Rvalue::Use(operand) => Some(operand),
659 Rvalue::Cast(_, operand, _) => Some(operand),
660 _ => None,
661 }.and_then(|operand| {
662 match operand {
663 Operand::Copy(place) | Operand::Move(place) if place.projection.is_empty() => {
664 Some(place.local)
665 }
666 _ => None,
667 }
668 });
669 if let Some(src_local) = src {
670 if let Some(src_val) = self.locals.get(&src_local) {
671 let has_better_prov = src_val.provenance.is_some()
672 && src_val.invariants.non_null
673 && self.locals.get(&dest_local).map_or(true, |d| {
674 d.provenance.is_none() || !d.invariants.non_null
675 });
676 if has_better_prov {
677 self.set_local(dest_local, VmValue {
678 term: src_val.term.clone(),
679 ty: dest.ty(self.body, self.tcx).ty,
680 provenance: src_val.provenance.clone(),
681 invariants: src_val.invariants,
682 });
683 }
684 }
685 }
686 }
687 }
688 }
689 }
690
691 #[allow(clippy::too_many_arguments)]
697 fn init_ptr_field(
698 &mut self,
699 local: Local,
700 path: Vec<usize>,
701 field_ty: Ty<'tcx>,
702 pointee: Ty<'tcx>,
703 local_idx: usize,
704 idx: usize,
705 elem_alloc: &mut FxHashMap<Ty<'tcx>, (AllocId, Int<'ctx>)>,
706 is_raw_ptr: bool,
707 nn_fresh_prefix: &str,
708 ) {
709 let invariants = if is_raw_ptr {
710 ValueInvariants { non_null: true, init: true, ..Default::default() }
711 } else {
712 ValueInvariants { init: true, ..Default::default() }
713 };
714 if let Some(&(existing_alloc, ref base)) = elem_alloc.get(&pointee) {
715 let elem_size = self.size_of_ty(pointee).max(1) as u64;
716 let len_term = self.fresh_int(&format!("field_len_{}_{}", local_idx, idx));
717 self.path_conditions.push(len_term.ge(&Int::from_u64(self.ctx, 0)));
718 let prost_offset = Int::mul(self.ctx, &[&len_term, &Int::from_u64(self.ctx, elem_size)]);
719 let field_term = Int::add(self.ctx, &[base, &prost_offset]);
720 self.set_field_value(local, path.clone(), VmValue {
721 term: field_term,
722 ty: field_ty,
723 provenance: Some(Provenance {
724 alloc_id: existing_alloc,
725 offset: prost_offset.clone(),
726 is_field_offset: false,
727 }),
728 invariants,
729 });
730 } else {
731 let field_align = 1u64.max(self.align_of_ty(pointee));
732 let max_size = Int::from_u64(self.ctx, i64::MAX as u64);
733 let (field_alloc_id, field_base) =
734 self.allocate_external(max_size, field_align, Some(pointee));
735 self.alloc_mut(field_alloc_id).initialized = true;
736 elem_alloc.insert(pointee, (field_alloc_id, field_base.clone()));
737 let field_term = if is_raw_ptr {
738 field_base
739 } else {
740 self.fresh_int(&format!("{}_{}_{}", nn_fresh_prefix, local_idx, idx))
741 };
742 self.set_field_value(local, path.clone(), VmValue {
743 term: field_term,
744 ty: field_ty,
745 provenance: Some(Provenance {
746 alloc_id: field_alloc_id,
747 offset: Int::from_u64(self.ctx, 0),
748 is_field_offset: false,
749 }),
750 invariants,
751 });
752 }
753 }
754
755 fn decompose_adt_fields(
762 &mut self,
763 local: Local,
764 prefix: Vec<usize>,
765 ty: Ty<'tcx>,
766 local_idx: usize,
767 elem_alloc: &mut FxHashMap<Ty<'tcx>, (AllocId, Int<'ctx>)>,
768 depth: usize,
769 ) {
770 if depth > 4 {
771 return;
772 }
773 let rustc_middle::ty::TyKind::Adt(adt_def, substs) = ty.kind() else { return };
774 if adt_def.is_enum() {
775 return;
776 }
777 let variant = adt_def.non_enum_variant();
778 for (idx, field_def) in variant.fields.iter().enumerate() {
779 let field_ty: Ty<'tcx> = field_def.ty(self.tcx, substs).skip_norm_wip();
780 let mut path = prefix.clone();
781 path.push(idx);
782 if let rustc_middle::ty::TyKind::RawPtr(inner, _) = field_ty.kind() {
783 self.init_ptr_field(local, path, field_ty, *inner, local_idx, idx, elem_alloc, true, "field_nn");
784 } else if let Some(pointee) = self.find_nn_pointee(field_ty) {
785 self.init_ptr_field(local, path, field_ty, pointee, local_idx, idx, elem_alloc, false, "field_nn");
786 } else if matches!(field_ty.kind(), rustc_middle::ty::TyKind::Adt(_, _)) {
787 self.decompose_adt_fields(local, path, field_ty, local_idx, elem_alloc, depth + 1);
788 } else {
789 let field_term = self.fresh_int(&format!("field_{}_{}", local_idx, idx));
790 self.set_field_value(local, path.clone(), VmValue {
791 term: field_term,
792 ty: field_ty,
793 provenance: None,
794 invariants: ValueInvariants { init: true, ..Default::default() },
795 });
796 }
797 }
798 }
799
800 pub(crate) fn propagate_from_checkpoint(&mut self, checkpoint_block: BasicBlock) {
805 let path_blocks: FxHashSet<BasicBlock> = self.path.as_ref()
806 .map(|p| {
807 let mut blocks: FxHashSet<BasicBlock> = p.steps.iter()
808 .filter_map(|s| match s {
809 crate::verify::path_extractor::PathStep::Block(b) => Some(*b),
810 _ => None,
811 })
812 .collect();
813 blocks.insert(checkpoint_block);
814 blocks
815 })
816 .unwrap_or_default();
817
818 if path_blocks.is_empty() {
819 self.propagate_pass(checkpoint_block, None, false);
820 return;
821 }
822
823 let block_steps: Vec<BasicBlock> = self.path.as_ref()
827 .map(|p| p.steps.iter()
828 .filter_map(|s| match s {
829 crate::verify::path_extractor::PathStep::Block(b) => Some(*b),
830 _ => None,
831 })
832 .collect())
833 .unwrap_or_default();
834 let has_duplicates = {
835 let mut seen = FxHashSet::default();
836 block_steps.iter().any(|b| !seen.insert(*b))
837 };
838
839 if has_duplicates {
840 self.propagate_pass(checkpoint_block, Some(&path_blocks), false);
841 return;
842 }
843
844 self.propagate_pass(checkpoint_block, Some(&path_blocks), false);
845 self.propagate_pass(checkpoint_block, Some(&path_blocks), true);
846 }
847
848 fn propagate_pass(&mut self, checkpoint_block: BasicBlock,
849 path_blocks: Option<&FxHashSet<BasicBlock>>, use_only: bool) {
850 let mut visited = FxHashSet::default();
853 let mut worklist: Vec<BasicBlock> = vec![checkpoint_block];
854 let mut max_depth = 32usize;
855 while let Some(block) = worklist.pop() {
856 if max_depth == 0 { break; }
857 max_depth -= 1;
858 if !visited.insert(block) { continue; }
859 if let Some(blocks) = path_blocks {
860 if !blocks.contains(&block) {
861 continue;
862 }
863 }
864 for pred in self.body.basic_blocks.predecessors()[block].to_vec() {
865 if path_blocks.map_or(true, |b| b.contains(&pred)) {
866 worklist.push(pred);
867 }
868 }
869 for stmt in &self.body.basic_blocks[block].statements {
870 if let StatementKind::Assign(assign) = &stmt.kind {
871 let (dest, rvalue) = &**assign;
872 if dest.projection.is_empty() {
873 if use_only && !Self::is_propagate_use_kind(rvalue) {
874 continue;
875 }
876 self.propagate_single_assign(dest.local, rvalue);
877 }
878 }
879 }
880 if use_only {
882 continue;
883 }
884 let terminator = self.body.basic_blocks[block].terminator();
888 if let TerminatorKind::Call { destination, args, func, .. } = &terminator.kind {
889 let dest = destination.local;
890 let needs_fallback = match self.locals.get(&dest) {
892 Some(dv) => dv.provenance.is_none(),
893 None => true,
894 };
895 let mut fallback_applied = false;
896 if let Some(mut dv) = self.locals.get(&dest).cloned() {
898 let mut found = false;
899 for arg in args {
900 self.try_materialize_const_bytes(&mut dv, &arg.node);
901 if dv.provenance.is_some() {
902 self.set_local(dest, dv);
903 found = true;
904 break;
905 }
906 }
907 if !found && needs_fallback {
908 if let Some(first) = args.first() {
909 fallback_applied = self.try_as_ptr_fallback(dest, func, self.value_of_operand(&first.node), &first.node);
910 }
911 }
912 }
913 if !fallback_applied && needs_fallback && self.locals.get(&dest).is_none() {
914 if let Some(first) = args.first() {
915 self.try_as_ptr_fallback(dest, func, self.value_of_operand(&first.node), &first.node);
916 }
917 }
918 if self.locals.contains_key(&dest) {
922 let cmpr_name = crate::helpers::mir_utils::call_name(self.tcx, func);
923 if api_classify::is_eq_or_partial_eq(&cmpr_name) {
924 self.propagate_const_bytes_to_tracked(args);
925 }
926 }
927 }
928 }
929 }
930
931 fn is_propagate_use_kind(rvalue: &Rvalue<'tcx>) -> bool {
934 matches!(rvalue,
935 Rvalue::Use(..) | Rvalue::Cast(..) | Rvalue::CopyForDeref(..))
936 }
937
938 fn propagate_single_assign(&mut self, dest_local: Local, rvalue: &Rvalue<'tcx>) {
941 if self.locals.contains_key(&dest_local) {
943 return;
944 }
945
946 let src_local = match rvalue {
947 #[cfg(rapx_rvalue_use_with_retag)]
948 Rvalue::Use(operand, _) => crate::helpers::mir_utils::extract_local(operand),
949 #[cfg(not(rapx_rvalue_use_with_retag))]
950 Rvalue::Use(operand) => crate::helpers::mir_utils::extract_local(operand),
951 Rvalue::Cast(_, operand, _) => crate::helpers::mir_utils::extract_local(operand),
952 Rvalue::CopyForDeref(place) if place.projection.is_empty() => Some(place.local),
953 _ => None,
954 };
955
956 if let Some(src) = src_local {
957 if let Some(src_val) = self.locals.get(&src).cloned() {
958 let dest_ty = self.body.local_decls[dest_local].ty;
959 let is_cast = matches!(rvalue, Rvalue::Cast(..));
960 let is_ptr_arith = matches!(
961 rvalue,
962 Rvalue::BinaryOp(BinOp::Add | BinOp::AddWithOverflow | BinOp::AddUnchecked
963 | BinOp::Sub | BinOp::SubWithOverflow | BinOp::SubUnchecked
964 | BinOp::Offset, _)
965 );
966 self.set_local(dest_local, VmValue {
967 term: src_val.term,
968 ty: dest_ty,
969 provenance: src_val.provenance,
970 invariants: ValueInvariants {
971 aligned: src_val.invariants.aligned,
972 in_bounds: src_val.invariants.in_bounds,
973 align_n: if is_cast || is_ptr_arith { src_val.invariants.align_n } else { None },
974 ..src_val.invariants
975 },
976 });
977 }
978 return;
979 }
980
981 let src_place = match rvalue {
985 #[cfg(rapx_rvalue_use_with_retag)]
986 Rvalue::Use(Operand::Copy(p) | Operand::Move(p), _) => Some(p),
987 #[cfg(not(rapx_rvalue_use_with_retag))]
988 Rvalue::Use(Operand::Copy(p) | Operand::Move(p)) => Some(p),
989 Rvalue::CopyForDeref(p) => Some(p),
990 _ => None,
991 };
992 if let Some(place) = src_place {
993 if !place.projection.is_empty() {
994 if let Some(val) = self.value_of_place(place) {
995 let dest_ty = self.body.local_decls[dest_local].ty;
996 self.set_local(dest_local, VmValue {
997 term: val.term,
998 ty: dest_ty,
999 provenance: val.provenance,
1000 invariants: val.invariants,
1001 });
1002 }
1003 }
1004 return;
1005 }
1006
1007 if let Rvalue::Ref(_, _, place) = rvalue {
1009 if let Some(addr) = self.address_of_place(place) {
1010 let dest_ty = self.body.local_decls[dest_local].ty;
1011 let alloc_align = addr.provenance.as_ref()
1012 .map(|p| self.alloc(p.alloc_id).align)
1013 .filter(|&a| a > 1);
1014 let has_deref = place.projection.iter().any(|p| {
1015 matches!(p.kind(), rustc_middle::mir::ProjectionElem::Deref)
1016 });
1017 let src_ty = self.body.local_decls[place.local].ty;
1018 let is_from_raw_parts_like = matches!(src_ty.kind(),
1019 rustc_middle::ty::TyKind::RawPtr(_, _));
1020 let is_slice_ref = if let rustc_middle::ty::TyKind::Ref(_, inner, _) = dest_ty.kind() {
1021 matches!(inner.kind(), rustc_middle::ty::TyKind::Slice(_))
1022 } else {
1023 false
1024 };
1025 let src_in_bounds = if is_slice_ref && is_from_raw_parts_like && has_deref {
1026 addr.provenance.is_some()
1027 } else {
1028 self.locals.get(&place.local)
1029 .map_or(false, |v| v.invariants.in_bounds)
1030 };
1031 self.set_local(dest_local, VmValue {
1032 term: addr.term,
1033 ty: dest_ty,
1034 provenance: addr.provenance,
1035 invariants: ValueInvariants {
1036 non_null: true, aligned: true, init: true,
1037 in_bounds: src_in_bounds,
1038 align_n: alloc_align,
1039 is_field_offset: false,
1040 },
1041 });
1042 }
1043 return;
1044 }
1045
1046 if let Rvalue::RawPtr(_, place) = rvalue {
1048 if let Some(addr) = self.address_of_place(place) {
1049 let dest_ty = self.body.local_decls[dest_local].ty;
1050 let alloc_align = addr.provenance.as_ref()
1051 .map(|p| self.alloc(p.alloc_id).align)
1052 .filter(|&a| a > 1);
1053 let src_in_bounds = self.locals.get(&place.local)
1054 .map_or(false, |v| v.invariants.in_bounds);
1055 self.set_local(dest_local, VmValue {
1056 term: addr.term,
1057 ty: dest_ty,
1058 provenance: addr.provenance,
1059 invariants: ValueInvariants {
1060 non_null: true, in_bounds: src_in_bounds, align_n: alloc_align, ..Default::default()
1061 },
1062 });
1063 }
1064 return;
1065 }
1066
1067 if let Rvalue::BinaryOp(op, pair) = rvalue {
1069 let (lhs_op, rhs_op) = &**pair;
1070 if matches!(op,
1071 BinOp::Add | BinOp::AddWithOverflow | BinOp::AddUnchecked
1072 | BinOp::Sub | BinOp::SubWithOverflow | BinOp::SubUnchecked
1073 | BinOp::Offset)
1074 {
1075 let lhs = crate::helpers::mir_utils::extract_local(lhs_op);
1076 let rhs = crate::helpers::mir_utils::extract_local(rhs_op);
1077 if let Some(src) = lhs {
1078 if let Some(src_val) = self.locals.get(&src).cloned() {
1079 let rhs_val = rhs.and_then(|r| self.locals.get(&r))
1080 .map(|v| VmValue { term: v.term.clone(), ty: v.ty, provenance: None, invariants: ValueInvariants::default() })
1081 .unwrap_or(VmValue { term: Int::from_u64(self.ctx, 0), ty: self.body.local_decls[dest_local].ty, provenance: None, invariants: ValueInvariants::default() });
1082 let prov = self.provenance_for_binary_op(*op, &src_val, &rhs_val);
1083 let dest_ty = self.body.local_decls[dest_local].ty;
1084 self.set_local(dest_local, VmValue {
1085 term: src_val.term,
1086 ty: dest_ty,
1087 provenance: prov,
1088 invariants: ValueInvariants {
1089 aligned: src_val.invariants.aligned,
1090 in_bounds: false,
1091 align_n: src_val.invariants.align_n,
1092 ..src_val.invariants
1093 },
1094 });
1095 }
1096 }
1097 }
1098 }
1099 }
1100
1101 pub(crate) fn exec_statement(
1104 &mut self,
1105 block: BasicBlock,
1106 statement_index: usize,
1107 statement: &Statement<'tcx>,
1108 ) {
1109 match &statement.kind {
1110 StatementKind::Assign(assign) => {
1111 let (place, rvalue) = &**assign;
1112 self.exec_assign(place, rvalue);
1113 }
1114 StatementKind::StorageLive(local) => {
1115 self.exec_storage_live(*local);
1116 }
1117 StatementKind::StorageDead(local) => {
1118 self.exec_storage_dead(*local);
1119 }
1120 StatementKind::FakeRead(..)
1121 | StatementKind::SetDiscriminant { .. }
1122 | StatementKind::AscribeUserType(..)
1123 | StatementKind::Coverage(..)
1124 | StatementKind::PlaceMention(..)
1125 | StatementKind::Intrinsic(..)
1126 | StatementKind::ConstEvalCounter
1127 | StatementKind::Nop => {}
1128 #[cfg(not(rapx_ge_99))]
1129 StatementKind::Retag(..) => {}
1130 _ => {
1131 self.notes.push(format!(
1132 "unsupported statement at bb{}#{}",
1133 block.as_usize(),
1134 statement_index
1135 ));
1136 }
1137 }
1138 }
1139
1140 fn exec_assign(
1141 &mut self,
1142 place: &Place<'tcx>,
1143 rvalue: &Rvalue<'tcx>,
1144 ) {
1145 let value = self.eval_rvalue(place, rvalue);
1146
1147 let has_deref = place.projection.iter().any(|p| {
1148 matches!(p.kind(), rustc_middle::mir::ProjectionElem::Deref)
1149 });
1150
1151 if !place.projection.is_empty() {
1152 self.record_projected_store(place, &value);
1153 self.record_indexed_store_for_vm(place, &value);
1154 }
1155
1156 self.record_definition();
1157
1158 if place.projection.is_empty() {
1159 let mut value = value;
1160 value.invariants.init = true;
1161 self.set_local(place.local, value);
1162 let src_place: Option<&Place<'tcx>> = match rvalue {
1169 #[cfg(rapx_rvalue_use_with_retag)]
1170 Rvalue::Use(operand, _) => match operand {
1171 Operand::Copy(p) | Operand::Move(p) => Some(p),
1172 _ => None,
1173 },
1174 #[cfg(not(rapx_rvalue_use_with_retag))]
1175 Rvalue::Use(operand) => match operand {
1176 Operand::Copy(p) | Operand::Move(p) => Some(p),
1177 _ => None,
1178 },
1179 Rvalue::CopyForDeref(p) => Some(p),
1180 _ => None,
1181 };
1182 if let Some(sp) = src_place {
1183 let field_prefix: Vec<usize> = sp.projection.iter()
1184 .filter_map(|p| match p.kind() {
1185 rustc_middle::mir::ProjectionElem::Field(fi, _) => Some(fi.as_usize()),
1186 _ => None,
1187 })
1188 .collect();
1189 let only_field = sp.projection.iter().all(|p| {
1190 matches!(p.kind(), rustc_middle::mir::ProjectionElem::Field(..))
1191 });
1192 if only_field {
1193 let keys: Vec<Vec<usize>> = self.field_values.keys()
1194 .filter(|(l, _)| *l == sp.local)
1195 .map(|(_, f)| f.clone())
1196 .collect();
1197 for k in keys {
1198 let rest = if field_prefix.is_empty() {
1199 Some(k.clone())
1200 } else if k.len() > field_prefix.len()
1201 && k[..field_prefix.len()] == field_prefix[..]
1202 {
1203 Some(k[field_prefix.len()..].to_vec())
1204 } else {
1205 None
1206 };
1207 if let Some(rest) = rest {
1208 if let Some(fv) = self.field_value(sp.local, &k).cloned() {
1209 self.set_field_value(place.local, rest, fv);
1210 }
1211 }
1212 }
1213 }
1214 }
1215 } else if !has_deref {
1216 let field_indices: Vec<usize> = place.projection.iter()
1218 .filter_map(|p| match p.kind() {
1219 rustc_middle::mir::ProjectionElem::Field(idx, _) => Some(idx.as_usize()),
1220 _ => None,
1221 })
1222 .collect();
1223 if !field_indices.is_empty() {
1224 let track_iter = field_indices == [0];
1226 let mut write_value = value;
1227 write_value.invariants.init = true;
1228 self.set_field_value(place.local, field_indices, write_value);
1229 if track_iter {
1230 self.track_iter_ptr_update(place.local);
1231 }
1232 }
1233 }
1234 }
1238
1239 fn record_projected_store(
1242 &mut self,
1243 place: &Place<'tcx>,
1244 value: &VmValue<'ctx, 'tcx>,
1245 ) {
1246 let Some(alloc_id) = self.locals.get(&place.local)
1249 .and_then(|v| v.provenance_alloc_id())
1250 .or_else(|| self.local_alloc_ids.get(&place.local).copied())
1251 else {
1252 return;
1253 };
1254
1255 let value_ty = value.ty;
1256 let value_size = self.size_of_ty(value_ty) as usize;
1257
1258 let mut byte_offset: usize = 0;
1259 let mut concrete = true;
1260
1261 let base_ty = self.body.local_decls[place.local].ty;
1262 let mut cur_ty = base_ty;
1263
1264 for proj in place.projection.iter() {
1265 match proj.kind() {
1266 rustc_middle::mir::ProjectionElem::Field(field_idx, _) => {
1267 let off = self.field_offset_in_bytes(cur_ty, field_idx.as_usize()) as usize;
1268 byte_offset += off;
1269 if let rustc_middle::ty::TyKind::Adt(adt_def, substs) = cur_ty.kind() {
1270 if !adt_def.is_enum() {
1271 let variant = adt_def.non_enum_variant();
1272 if let Some(field_def) = variant.fields.get(field_idx) {
1273 let unnorm = field_def.ty(self.tcx, substs);
1274 cur_ty = unnorm.skip_norm_wip();
1275 }
1276 }
1277 }
1278 }
1279 rustc_middle::mir::ProjectionElem::Deref => {
1280 if let rustc_middle::ty::TyKind::Ref(_, inner, _) = cur_ty.kind() {
1281 cur_ty = *inner;
1282 }
1283 }
1284 rustc_middle::mir::ProjectionElem::Index(_local) => {
1285 concrete = false;
1286 break;
1287 }
1288 rustc_middle::mir::ProjectionElem::Subslice { from, to: _, from_end: _ } => {
1289 byte_offset += from as usize;
1290 }
1291 _ => {}
1292 }
1293 }
1294
1295 if concrete && value_size > 0 {
1296 self.alloc_mut(alloc_id).initialized = true;
1297
1298 let is_u8_write = matches!(value_ty.kind(),
1299 rustc_middle::ty::TyKind::Uint(rustc_middle::ty::UintTy::U8));
1300
1301 if is_u8_write {
1302 self.record_byte_value(alloc_id, byte_offset, value.term.clone());
1303 if let Some(term_val) = value.term.as_u64() {
1304 if term_val == 0 {
1305 self.mark_byte_nul(alloc_id, byte_offset);
1306 } else {
1307 self.mark_byte_non_nul(alloc_id, byte_offset);
1308 }
1309 }
1310 }
1311 }
1312 }
1313
1314 fn record_indexed_store_for_vm(
1317 &mut self,
1318 place: &Place<'tcx>,
1319 value: &VmValue<'ctx, 'tcx>,
1320 ) {
1321 let is_u8 = matches!(value.ty.kind(),
1322 rustc_middle::ty::TyKind::Uint(rustc_middle::ty::UintTy::U8));
1323 if !is_u8 {
1324 return;
1325 }
1326 let has_index_with_concrete = place.projection.iter().any(|p| {
1327 if let rustc_middle::mir::ProjectionElem::Index(local) = p {
1328 self.locals.get(&local)
1329 .and_then(|v| v.term.simplify().as_u64())
1330 .is_some()
1331 } else {
1332 false
1333 }
1334 });
1335 if !has_index_with_concrete {
1336 return;
1337 }
1338 if let Some(addr) = self.address_of_place(place) {
1339 if let Some(ref prov) = addr.provenance {
1340 let alloc_id = prov.alloc_id;
1341 let byte_offset = prov.offset.as_u64().map(|v| v as usize).unwrap_or(0);
1342 self.alloc_mut(alloc_id).initialized = true;
1343 self.record_byte_value(alloc_id, byte_offset, value.term.clone());
1344 if let Some(term_val) = value.term.as_u64() {
1345 if term_val == 0 {
1346 self.mark_byte_nul(alloc_id, byte_offset);
1347 } else {
1348 self.mark_byte_non_nul(alloc_id, byte_offset);
1349 }
1350 }
1351 }
1352 }
1353 }
1354
1355 fn inject_layout_constraints(&mut self, operand: &Operand<'tcx>, val: &VmValue<'ctx, 'tcx>) {
1357 if let Operand::Constant(constant) = operand {
1358 let text = format!("{:?}", constant.const_);
1359 if crate::helpers::mir_utils::const_int_from_debug(&text).is_none() {
1360 let is_align_or_size = text.starts_with("AlignOf(") || text.starts_with("SizeOf(");
1361 if is_align_or_size {
1362 let one = Int::from_u64(self.ctx, 1);
1363 self.path_conditions.push(val.term.ge(&one));
1364 }
1365 }
1366 }
1367 }
1368
1369 fn eval_rvalue(
1371 &mut self,
1372 dest_place: &Place<'tcx>,
1373 rvalue: &Rvalue<'tcx>,
1374 ) -> VmValue<'ctx, 'tcx> {
1375 let dest_ty = dest_place.ty(self.body, self.tcx).ty;
1376
1377 match rvalue {
1378 #[cfg(rapx_rvalue_use_with_retag)]
1379 Rvalue::Use(operand, _retag) => {
1380 let mut val = self.value_of_operand(operand);
1381 self.try_materialize_const_bytes(&mut val, operand);
1382 self.inject_layout_constraints(operand, &val);
1383 val
1384 }
1385 #[cfg(not(rapx_rvalue_use_with_retag))]
1386 Rvalue::Use(operand) => {
1387 let mut val = self.value_of_operand(operand);
1388 self.try_materialize_const_bytes(&mut val, operand);
1389 self.inject_layout_constraints(operand, &val);
1390 val
1391 }
1392 Rvalue::Ref(_, _borrow_kind, place) => {
1393 if let Some(addr) = self.address_of_place(place) {
1394 let alloc_align = addr.provenance.as_ref()
1395 .map(|p| self.alloc(p.alloc_id).align)
1396 .filter(|&a| a > 1);
1397 let has_deref = place.projection.iter().any(|p| {
1401 matches!(p.kind(), rustc_middle::mir::ProjectionElem::Deref)
1402 });
1403 let src_ty = self.body.local_decls[place.local].ty;
1404 let is_from_raw_parts_like = matches!(src_ty.kind(),
1405 rustc_middle::ty::TyKind::RawPtr(_, _));
1406 let is_slice_ref = if let rustc_middle::ty::TyKind::Ref(_, inner, _) = dest_ty.kind() {
1407 matches!(inner.kind(), rustc_middle::ty::TyKind::Slice(_))
1408 } else {
1409 false
1410 };
1411 let src_in_bounds = if is_slice_ref && is_from_raw_parts_like && has_deref {
1412 addr.provenance.is_some()
1413 } else {
1414 self.locals.get(&place.local)
1415 .map_or(false, |v| v.invariants.in_bounds)
1416 };
1417 let val = VmValue {
1418 term: addr.term,
1419 ty: dest_ty,
1420 provenance: addr.provenance,
1421 invariants: ValueInvariants {
1422 non_null: true,
1423 aligned: self.check_place_alignment(place),
1424 init: true,
1425 in_bounds: src_in_bounds,
1426 align_n: alloc_align,
1427 is_field_offset: false,
1428 },
1429 };
1430 self.propagate_byte_values_to_ref(place, &val);
1431 self.propagate_field_values_to_ref(place, dest_place.local);
1432 val
1433 } else {
1434 let term = self.fresh_int("ref_addr");
1435 VmValue {
1436 term,
1437 ty: dest_ty,
1438 provenance: None,
1439 invariants: ValueInvariants {
1440 non_null: true,
1441 init: true,
1442 ..Default::default()
1443 },
1444 }
1445 }
1446 }
1447 Rvalue::RawPtr(_, place) => {
1448 if let Some(addr) = self.address_of_place(place) {
1449 let alloc_align = addr.provenance.as_ref()
1450 .map(|p| self.alloc(p.alloc_id).align)
1451 .filter(|&a| a > 1);
1452 let source_in_bounds = self.locals.get(&place.local)
1453 .map_or(false, |v| v.invariants.in_bounds);
1454 let val = VmValue {
1455 term: addr.term,
1456 ty: dest_ty,
1457 provenance: addr.provenance,
1458 invariants: ValueInvariants {
1459 non_null: true,
1460 in_bounds: source_in_bounds,
1461 align_n: alloc_align,
1462 ..Default::default()
1463 },
1464 };
1465 val
1466 } else {
1467 let term = self.fresh_int("rawptr_addr");
1468 VmValue {
1469 term,
1470 ty: dest_ty,
1471 provenance: None,
1472 invariants: ValueInvariants {
1473 non_null: true,
1474 ..Default::default()
1475 },
1476 }
1477 }
1478 }
1479 Rvalue::BinaryOp(op, pair) => {
1480 let (lhs_op, rhs_op) = &**pair;
1481 let lhs = self.value_of_operand(lhs_op);
1482 let rhs = self.value_of_operand(rhs_op);
1483 let term = self.eval_binary_op(*op, &lhs.term, &rhs.term);
1484 let provenance = self.provenance_for_binary_op(*op, &lhs, &rhs);
1485 let invariants = self.invariants_for_binary_op(*op, &lhs, &rhs, &provenance);
1486 let dest_pk = PlaceKey::from_mir_place(dest_place);
1487 let lhs_pk = crate::helpers::mir_utils::operand_place(lhs_op);
1488 let rhs_pk = crate::helpers::mir_utils::operand_place(rhs_op);
1489 self.binary_op_sources.insert(dest_pk.clone(), (lhs_pk, rhs_pk));
1490 let cmp_cond = match *op {
1495 BinOp::Le => Some(lhs.term.le(&rhs.term)),
1496 BinOp::Lt => Some(lhs.term.lt(&rhs.term)),
1497 BinOp::Ge => Some(lhs.term.ge(&rhs.term)),
1498 BinOp::Gt => Some(lhs.term.gt(&rhs.term)),
1499 BinOp::Eq => Some(lhs.term._eq(&rhs.term)),
1500 BinOp::Ne => Some(lhs.term._eq(&rhs.term).not()),
1501 _ => None,
1502 };
1503 if let Some(cond) = cmp_cond {
1504 self.comparison_conds.insert(dest_pk.clone(), cond);
1505 }
1506 if matches!(*op, BinOp::Div | BinOp::Rem) {
1514 let quot = if matches!(*op, BinOp::Div) { &term } else { &lhs.term.div(&rhs.term) };
1515 let rem = lhs.term.rem(&rhs.term);
1516 let mul_term = Int::mul(self.ctx, &[quot, &rhs.term]);
1517 let sum_term = Int::add(self.ctx, &[&mul_term, &rem]);
1518 self.path_conditions.push(lhs.term._eq(&sum_term));
1519 let zero = Int::from_u64(self.ctx, 0);
1520 self.path_conditions.push(rem.ge(&zero));
1521 if rhs.term.as_u64().map_or(true, |r| r >= 1) {
1524 self.path_conditions.push(rem.lt(&rhs.term));
1525 }
1526 self.path_conditions.push(rem.le(&lhs.term));
1527 self.path_conditions.push(quot.ge(&zero));
1528 self.path_conditions.push(mul_term.le(&lhs.term));
1530 if rhs.term.as_u64().map_or(false, |r| r >= 2) {
1533 let one = Int::from_u64(self.ctx, 1);
1534 let qp1 = Int::add(self.ctx, &[quot, &one]);
1535 self.path_conditions.push(qp1.le(&lhs.term));
1537 } else {
1538 if rhs.term.as_u64().map_or(false, |r| r >= 1) {
1540 self.path_conditions.push(quot.le(&lhs.term));
1541 }
1542 }
1543 }
1544 if let rustc_middle::ty::TyKind::Tuple(fields) = dest_ty.kind() {
1550 if fields.len() == 2 {
1551 let result_val = VmValue {
1552 term: term.clone(),
1553 ty: fields[0],
1554 provenance: provenance.clone(),
1555 invariants,
1556 };
1557 self.set_field_value(dest_place.local, vec![0], result_val);
1558 let overflow_term = self.fresh_int("overflow_flag");
1559 let overflow_val = VmValue::new(overflow_term, fields[1]);
1560 self.set_field_value(dest_place.local, vec![1], overflow_val);
1561 }
1562 }
1563 VmValue {
1564 term,
1565 ty: dest_ty,
1566 provenance,
1567 invariants,
1568 }
1569 }
1570 Rvalue::UnaryOp(op, operand) => {
1571 let val = self.value_of_operand(operand);
1572 let is_bool = matches!(val.ty.kind(), rustc_middle::ty::TyKind::Bool);
1573 let term = if matches!(op, UnOp::PtrMetadata) {
1574 self.slice_len_from_value(&val)
1580 .unwrap_or_else(|| self.fresh_int("ptr_metadata"))
1581 } else {
1582 self.eval_unary_op(*op, &val.term, is_bool)
1583 };
1584 VmValue {
1585 term,
1586 ty: dest_ty,
1587 provenance: val.provenance,
1588 invariants: val.invariants,
1589 }
1590 }
1591 Rvalue::Cast(_kind, operand, cast_ty) => {
1592 let src_val = self.value_of_operand(operand);
1593 let src_ty = src_val.ty;
1594 let is_src_ref = matches!(src_ty.kind(),
1595 rustc_middle::ty::TyKind::Ref(..));
1596 let dest_is_ptr = matches!(cast_ty.kind(),
1597 rustc_middle::ty::TyKind::RawPtr(..));
1598 let aligned = if dest_is_ptr && is_src_ref {
1599 true
1600 } else {
1601 src_val.invariants.aligned
1602 };
1603 let term = crate::helpers::mir_utils::extract_local(operand)
1607 .and_then(|l| self.field_value(l, &[0]).map(|v| v.term.clone()))
1608 .unwrap_or(src_val.term);
1609 VmValue {
1610 term,
1611 ty: *cast_ty,
1612 provenance: src_val.provenance,
1613 invariants: ValueInvariants {
1614 non_null: src_val.invariants.non_null,
1615 init: src_val.invariants.init,
1616 aligned,
1617 in_bounds: src_val.invariants.in_bounds,
1618 align_n: src_val.invariants.align_n,
1619 is_field_offset: false,
1620 },
1621 }
1622 }
1623 Rvalue::Aggregate(_kind, operands) => {
1624 let data_variant = match &**_kind {
1629 rustc_middle::mir::AggregateKind::Adt(did, variant_idx, ..) => {
1630 if self.tcx.is_diagnostic_item(rustc_span::sym::Result, *did) {
1631 Some(variant_idx.as_usize() == 0)
1632 } else if self.tcx.is_diagnostic_item(rustc_span::sym::Option, *did) {
1633 Some(variant_idx.as_usize() == 1)
1634 } else {
1635 None
1636 }
1637 }
1638 _ => None,
1639 };
1640 if operands.len() == 1 && self.find_nn_pointee(dest_ty).is_some() {
1648 let field_val = self.value_of_operand(operands.iter().next().unwrap());
1649 let dest_local = dest_place.local;
1650 self.set_field_value(dest_local, vec![0], field_val.clone());
1651 if let Some(alloc_id) = self.local_alloc_ids.get(&dest_local).copied() {
1652 self.alloc_mut(alloc_id).initialized = true;
1653 }
1654 return VmValue {
1655 term: field_val.term,
1656 ty: dest_ty,
1657 provenance: field_val.provenance,
1658 invariants: field_val.invariants,
1659 };
1660 }
1661 let term = self.fresh_int("aggregate");
1662 let dest_local = dest_place.local;
1663 let dest_alloc_id = self.local_alloc_ids.get(&dest_local).copied();
1664 let is_byte_array = crate::helpers::mir_utils::is_u8_array_or_slice(dest_ty);
1665 let field_types: Vec<_> = self.aggregate_field_tys(dest_ty);
1666 let mut byte_offset = 0usize;
1667 for (i, operand) in operands.iter().enumerate() {
1668 let mut field_val = self.value_of_operand(operand);
1669 if let Some(field_ty) = field_types.get(i) {
1670 let src_is_ref = matches!(field_val.ty.kind(), rustc_middle::ty::TyKind::Ref(..));
1671 let dst_is_raw = matches!(field_ty.kind(), rustc_middle::ty::TyKind::RawPtr(..));
1672 if src_is_ref && dst_is_raw {
1673 field_val.invariants.in_bounds = true;
1674 field_val.ty = *field_ty;
1675 } else if dst_is_raw && field_val.invariants.non_null {
1676 field_val.invariants.in_bounds = true;
1677 field_val.ty = *field_ty;
1678 }
1679 }
1680 let field_sz = field_types.get(i).copied()
1681 .map(|ty| self.size_of_ty(ty) as usize)
1682 .unwrap_or(1);
1683 let field_term = field_val.term.clone();
1684 self.set_field_value(dest_local, vec![i], field_val);
1685 if data_variant != Some(false) {
1695 if let Some(op_place) = operand.place() {
1696 if op_place.projection.is_empty() {
1697 let nested: Vec<(Vec<usize>, VmValue<'ctx, 'tcx>)> = self
1698 .field_values
1699 .iter()
1700 .filter(|((l, _), _)| *l == op_place.local)
1701 .map(|((_, p), v)| (p.clone(), v.clone()))
1702 .collect();
1703 for (nested_path, nested_val) in nested {
1704 let mut full = vec![i];
1705 full.extend_from_slice(&nested_path);
1706 self.set_field_value(dest_local, full, nested_val);
1707 }
1708 }
1709 }
1710 }
1711 if let Some(alloc_id) = dest_alloc_id {
1712 self.alloc_mut(alloc_id).initialized = true;
1713 if is_byte_array && field_sz == 1 {
1714 self.record_byte_value(alloc_id, byte_offset, field_term.clone());
1715 }
1716 if let Some(int_val) = crate::helpers::mir_utils::extract_operand_const(operand) {
1718 if field_sz == 1 {
1719 if int_val == 0 {
1720 self.mark_byte_nul(alloc_id, byte_offset);
1721 if !is_byte_array {
1722 self.record_byte_value(alloc_id, byte_offset,
1723 Int::from_u64(self.ctx, 0));
1724 }
1725 } else {
1726 self.mark_byte_non_nul(alloc_id, byte_offset);
1727 if !is_byte_array {
1728 self.record_byte_value(alloc_id, byte_offset,
1729 Int::from_u64(self.ctx, int_val));
1730 }
1731 }
1732 }
1733 for b in 0..field_sz.min(8) {
1735 let byte_off = byte_offset + b;
1736 let byte_val = (int_val >> (b * 8)) & 0xFF;
1737 if byte_val == 0 {
1738 self.mark_byte_nul(alloc_id, byte_off);
1739 } else {
1740 self.mark_byte_non_nul(alloc_id, byte_off);
1741 }
1742 self.record_byte_value(alloc_id, byte_off,
1743 Int::from_u64(self.ctx, byte_val));
1744 }
1745 }
1746 }
1747 byte_offset += field_sz;
1748 }
1749 let is_slice_ptr = matches!(dest_ty.kind(),
1755 rustc_middle::ty::TyKind::RawPtr(inner, _) | rustc_middle::ty::TyKind::Ref(_, inner, _)
1756 if matches!(inner.kind(), rustc_middle::ty::TyKind::Slice(_)));
1757 let (result_term, result_prov) = if is_slice_ptr {
1758 match self.field_value(dest_local, &[0]).cloned() {
1759 Some(data) => (data.term.clone(), data.provenance.clone()),
1760 None => (term.clone(), None),
1761 }
1762 } else {
1763 (term.clone(), None)
1764 };
1765 VmValue {
1766 term: result_term,
1767 ty: dest_ty,
1768 provenance: result_prov,
1769 invariants: ValueInvariants::default(),
1770 }
1771 }
1772 Rvalue::Discriminant(place) => {
1773 let term = self.discriminant_terms.get(&place.local)
1778 .cloned()
1779 .unwrap_or_else(|| self.fresh_int("discriminant"));
1780 if self.discriminant_terms.contains_key(&place.local) {
1781 self.contract_flags.saw_next_discriminant = true;
1782 }
1783 let place_val = self.value_of_place(place)
1788 .or_else(|| self.local_value(place.local).cloned());
1789 if let Some(ref pv) = place_val {
1790 if let rustc_middle::ty::TyKind::Adt(adt_def, _) = pv.ty.kind() {
1791 let def_path = self.tcx.def_path_str(adt_def.did());
1792 if api_classify::is_std_ordering(&def_path) && adt_def.is_enum() {
1793 let one = Int::from_u64(self.ctx, 1);
1794 let discr_minus_one = Int::sub(self.ctx, &[&term, &one]);
1795 self.path_conditions.push(pv.term._eq(&discr_minus_one));
1796 let zero = Int::from_u64(self.ctx, 0);
1798 let two = Int::from_u64(self.ctx, 2);
1799 self.path_conditions.push(term.ge(&zero));
1800 self.path_conditions.push(term.le(&two));
1801 }
1802 }
1803 }
1804 VmValue {
1805 term,
1806 ty: dest_ty,
1807 provenance: None,
1808 invariants: ValueInvariants::default(),
1809 }
1810 }
1811 #[cfg(not(rapx_ge_99))]
1812 Rvalue::ShallowInitBox(operand, _ty) => {
1813 let val = self.value_of_operand(operand);
1814 VmValue {
1815 term: val.term,
1816 ty: dest_ty,
1817 provenance: val.provenance,
1818 invariants: val.invariants,
1819 }
1820 }
1821 Rvalue::CopyForDeref(place) => {
1822 if let Some(val) = self.value_of_place(place) {
1823 val
1824 } else {
1825 let term = self.fresh_int("copy_for_deref");
1826 VmValue {
1827 term,
1828 ty: dest_ty,
1829 provenance: None,
1830 invariants: ValueInvariants::default(),
1831 }
1832 }
1833 }
1834 Rvalue::Repeat(operand, _count) => {
1835 let _val = self.value_of_operand(operand);
1836 let term = self.fresh_int("repeat");
1837 VmValue {
1838 term,
1839 ty: dest_ty,
1840 provenance: None,
1841 invariants: ValueInvariants::default(),
1842 }
1843 }
1844 Rvalue::ThreadLocalRef(_) => {
1845 let term = self.fresh_int("thread_local");
1846 VmValue {
1847 term,
1848 ty: dest_ty,
1849 provenance: None,
1850 invariants: ValueInvariants::default(),
1851 }
1852 }
1853 #[cfg(not(rapx_ge_99))]
1854 Rvalue::NullaryOp(_op) => {
1855 let term = self.fresh_int("nullary");
1856 let op_debug = format!("{:?}", _op);
1857 let is_align_of = op_debug.contains("AlignOf") || op_debug.contains("min_align_of");
1858 let is_size_of = op_debug.contains("SizeOf");
1859 if is_align_of || is_size_of {
1860 let one = Int::from_u64(self.ctx, 1);
1861 self.path_conditions.push(term.ge(&one));
1862 }
1863 VmValue {
1864 term,
1865 ty: dest_ty,
1866 provenance: None,
1867 invariants: ValueInvariants::default(),
1868 }
1869 }
1870 Rvalue::WrapUnsafeBinder(_operand, _ty) => {
1871 let term = self.fresh_int("wrap_unsafe_binder");
1872 VmValue {
1873 term,
1874 ty: dest_ty,
1875 provenance: None,
1876 invariants: ValueInvariants::default(),
1877 }
1878 }
1879 #[cfg(rapx_rvalue_has_reborrow)]
1880 Rvalue::Reborrow(_ty, _mutability, _place) => {
1881 let term = self.fresh_int("reborrow");
1882 VmValue {
1883 term,
1884 ty: dest_ty,
1885 provenance: None,
1886 invariants: ValueInvariants { non_null: true, ..Default::default() },
1887 }
1888 }
1889 }
1890 }
1891
1892 fn eval_binary_op(&mut self, op: BinOp, lhs: &Int<'ctx>, rhs: &Int<'ctx>) -> Int<'ctx> {
1895 match op {
1896 BinOp::Add | BinOp::AddWithOverflow | BinOp::AddUnchecked => {
1897 Int::add(self.ctx, &[lhs, rhs])
1898 }
1899 BinOp::Sub | BinOp::SubWithOverflow | BinOp::SubUnchecked => {
1900 Int::sub(self.ctx, &[lhs, rhs])
1901 }
1902 BinOp::Mul | BinOp::MulWithOverflow | BinOp::MulUnchecked => {
1903 Int::mul(self.ctx, &[lhs, rhs])
1904 }
1905 BinOp::Div => lhs.div(rhs),
1906 BinOp::Rem => lhs.rem(rhs),
1907 BinOp::Eq => {
1908 let cond = lhs._eq(rhs);
1909 cond.ite(&Int::from_u64(self.ctx, 1), &Int::from_u64(self.ctx, 0))
1910 }
1911 BinOp::Ne => {
1912 let cond = lhs._eq(rhs).not();
1913 cond.ite(&Int::from_u64(self.ctx, 1), &Int::from_u64(self.ctx, 0))
1914 }
1915 BinOp::Lt => {
1916 let cond = lhs.lt(rhs);
1917 cond.ite(&Int::from_u64(self.ctx, 1), &Int::from_u64(self.ctx, 0))
1918 }
1919 BinOp::Le => {
1920 let cond = lhs.le(rhs);
1921 cond.ite(&Int::from_u64(self.ctx, 1), &Int::from_u64(self.ctx, 0))
1922 }
1923 BinOp::Gt => {
1924 let cond = lhs.gt(rhs);
1925 cond.ite(&Int::from_u64(self.ctx, 1), &Int::from_u64(self.ctx, 0))
1926 }
1927 BinOp::Ge => {
1928 let cond = lhs.ge(rhs);
1929 cond.ite(&Int::from_u64(self.ctx, 1), &Int::from_u64(self.ctx, 0))
1930 }
1931 BinOp::Offset => Int::add(self.ctx, &[lhs, rhs]),
1932 BinOp::BitAnd => {
1933 let result = self.fresh_int("binop");
1934 self.path_conditions.push(result.le(lhs));
1937 if rhs.simplify().as_u64().is_some() {
1943 self.path_conditions.push(result.le(rhs));
1944 }
1945 if self.not_mask_terms.contains(rhs) {
1946 let zero = Int::from_u64(self.ctx, 0);
1951 let align = Int::sub(self.ctx, &[&zero, rhs]);
1952 self.path_conditions.push(result.rem(&align)._eq(&zero));
1953 let one = Int::from_u64(self.ctx, 1);
1954 let addr = Int::add(self.ctx, &[lhs, rhs, &one]);
1955 self.path_conditions.push(result.ge(&addr));
1956 }
1957 result
1958 }
1959 BinOp::BitOr => {
1960 let result = self.fresh_int("binop");
1961 let zero = Int::from_u64(self.ctx, 0);
1962 self.path_conditions
1968 .push(lhs._eq(&zero).not().implies(&result._eq(&zero).not()));
1969 self.path_conditions
1970 .push(rhs._eq(&zero).not().implies(&result._eq(&zero).not()));
1971 result
1972 }
1973 _ => self.fresh_int("binop"),
1974 }
1975 }
1976
1977 fn eval_unary_op(&mut self, op: UnOp, val: &Int<'ctx>, is_bool: bool) -> Int<'ctx> {
1978 match op {
1979 UnOp::Not => {
1980 if is_bool {
1981 let zero = Int::from_u64(self.ctx, 0);
1982 let one = Int::from_u64(self.ctx, 1);
1983 val._eq(&zero).ite(&one, &zero)
1984 } else {
1985 let zero = Int::from_u64(self.ctx, 0);
1987 let one = Int::from_u64(self.ctx, 1);
1988 let neg = Int::sub(self.ctx, &[&zero, val]);
1989 let result = Int::sub(self.ctx, &[&neg, &one]);
1990 self.not_mask_terms.insert(result.clone());
1991 result
1992 }
1993 }
1994 UnOp::Neg => {
1995 let zero = Int::from_u64(self.ctx, 0);
1996 Int::sub(self.ctx, &[&zero, val])
1997 }
1998 UnOp::PtrMetadata => self.fresh_int("ptr_metadata"),
1999 }
2000 }
2001
2002 fn slice_len_from_value(&self, val: &VmValue<'ctx, 'tcx>) -> Option<Int<'ctx>> {
2006 let alloc_id = val.provenance_alloc_id()?;
2007 let alloc = self.alloc(alloc_id);
2008 let elem_ty = alloc.element_ty?;
2009 let elem_size = self.size_of_ty(elem_ty).max(1) as u64;
2010 if elem_size == 1 {
2011 return Some(alloc.size.clone());
2012 }
2013 let elem_term = Int::from_u64(self.ctx, elem_size);
2014 Some(alloc.size.div(&elem_term))
2015 }
2016
2017 fn provenance_for_binary_op(
2021 &self,
2022 op: BinOp,
2023 lhs: &VmValue<'ctx, 'tcx>,
2024 rhs: &VmValue<'ctx, 'tcx>,
2025 ) -> Option<Provenance<'ctx>> {
2026 match op {
2027 BinOp::Add | BinOp::AddWithOverflow | BinOp::AddUnchecked
2028 | BinOp::Offset => {
2029 if rhs.provenance.is_some() {
2031 return None;
2032 }
2033 lhs.provenance.as_ref().map(|prov| Provenance {
2034 alloc_id: prov.alloc_id,
2035 offset: Int::add(self.ctx, &[&prov.offset, &rhs.term]),
2036 is_field_offset: false,
2037 })
2038 }
2039 BinOp::Sub | BinOp::SubWithOverflow | BinOp::SubUnchecked => {
2040 if rhs.provenance.is_some() {
2041 return None;
2043 }
2044 lhs.provenance.as_ref().map(|prov| Provenance {
2045 alloc_id: prov.alloc_id,
2046 offset: Int::sub(self.ctx, &[&prov.offset, &rhs.term]),
2047 is_field_offset: false,
2048 })
2049 }
2050 BinOp::BitAnd => {
2051 if rhs.provenance.is_some() {
2052 return None;
2053 }
2054 lhs.provenance.as_ref().map(|prov| Provenance {
2055 alloc_id: prov.alloc_id,
2056 offset: self.fresh_int("align_offset"),
2060 is_field_offset: false,
2061 })
2062 }
2063 BinOp::BitXor | BinOp::Shr | BinOp::ShrUnchecked => {
2064 if rhs.provenance.is_some() {
2065 return None;
2066 }
2067 lhs.provenance.clone()
2068 }
2069 BinOp::BitOr | BinOp::Shl | BinOp::ShlUnchecked => {
2070 if rhs.provenance.is_some() {
2071 return None;
2072 }
2073 lhs.provenance.as_ref().map(|prov| Provenance {
2074 alloc_id: prov.alloc_id,
2075 offset: Int::add(self.ctx, &[&prov.offset, &rhs.term]),
2076 is_field_offset: false,
2077 })
2078 }
2079 BinOp::Mul | BinOp::MulWithOverflow | BinOp::MulUnchecked => {
2080 if rhs.provenance.is_some() {
2081 return None;
2082 }
2083 lhs.provenance.as_ref().map(|prov| Provenance {
2084 alloc_id: prov.alloc_id,
2085 offset: Int::mul(self.ctx, &[&prov.offset, &rhs.term]),
2086 is_field_offset: false,
2087 })
2088 }
2089 BinOp::Div | BinOp::Rem => lhs.provenance.clone(),
2090 _ => None,
2091 }
2092 }
2093
2094 fn invariants_for_binary_op(
2097 &self,
2098 op: BinOp,
2099 lhs: &VmValue<'ctx, 'tcx>,
2100 rhs: &VmValue<'ctx, 'tcx>,
2101 provenance: &Option<Provenance<'ctx>>,
2102 ) -> ValueInvariants {
2103 let non_null = provenance.is_some() && lhs.invariants.non_null;
2104
2105 let align_n = match op {
2106 BinOp::Add | BinOp::AddWithOverflow | BinOp::AddUnchecked
2107 | BinOp::Sub | BinOp::SubWithOverflow | BinOp::SubUnchecked
2108 | BinOp::Offset => {
2109 match (lhs.invariants.align_n, rhs.invariants.align_n) {
2111 (Some(a), Some(b)) if a == b => Some(a),
2112 (Some(a), None) => {
2114 let c = rhs.term.as_u64().unwrap_or(1);
2115 if c % a == 0 { Some(a) } else { None }
2116 }
2117 (Some(a), _) if self.rhs_is_aligned_multiple(rhs, a) => Some(a),
2119 _ => None,
2120 }
2121 }
2122 BinOp::Mul | BinOp::MulWithOverflow | BinOp::MulUnchecked => {
2123 if let Some(c) = rhs.term.as_u64() {
2124 if c > 0 && c.is_power_of_two() {
2125 Some(c)
2126 } else if c > 0 {
2127 let factor = 1u64 << c.trailing_zeros();
2128 if factor > 1 { Some(factor) } else { None }
2129 } else {
2130 None
2131 }
2132 } else if let Some(c) = lhs.term.as_u64() {
2133 if c > 0 && c.is_power_of_two() {
2134 Some(c)
2135 } else if c > 0 {
2136 let factor = 1u64 << c.trailing_zeros();
2137 if factor > 1 { Some(factor) } else { None }
2138 } else {
2139 None
2140 }
2141 } else {
2142 None
2143 }
2144 }
2145 _ => lhs.invariants.align_n,
2146 };
2147
2148 ValueInvariants { non_null, align_n, ..Default::default() }
2149 }
2150
2151 fn rhs_is_aligned_multiple(&self, val: &VmValue<'ctx, 'tcx>, align: u64) -> bool {
2154 if let Some(a) = val.invariants.align_n {
2156 if a >= align && a % align == 0 { return true; }
2157 }
2158 if let Some(c) = val.term.as_u64() {
2160 if c % align == 0 { return true; }
2161 }
2162 false
2163 }
2164
2165 fn exec_storage_live(&mut self, local: Local) {
2168 self.local_address(local);
2169 if let Some(alloc_id) = self.local_alloc_ids.get(&local).copied() {
2170 self.alloc_mut(alloc_id).dead = false;
2171 }
2172 }
2173
2174 fn exec_storage_dead(&mut self, local: Local) {
2175 if let Some(alloc_id) = self.local_alloc_ids.get(&local).copied() {
2176 self.alloc_mut(alloc_id).dead = true;
2177 }
2178 }
2179
2180 pub(crate) fn exec_drop(&mut self, place: &Place<'tcx>) {
2181 if let Some(alloc_id) = self.local_alloc_ids.get(&place.local).copied() {
2182 self.alloc_mut(alloc_id).dead = true;
2183 let mut worklist: Vec<AllocId> = vec![alloc_id];
2185 while let Some(id) = worklist.pop() {
2186 if let Some(data_id) = self.alloc(id).slice_data {
2187 self.alloc_mut(data_id).dead = true;
2188 worklist.push(data_id);
2189 }
2190 }
2191 }
2192 self.notes.push(format!("drop: {:?}", place));
2193 }
2194
2195 fn exec_terminator(
2198 &mut self,
2199 block: BasicBlock,
2200 terminator: &Terminator<'tcx>,
2201 occurrence: usize,
2202 ) {
2203 match &terminator.kind {
2204 TerminatorKind::Call {
2205 func,
2206 args,
2207 destination,
2208 target,
2209 ..
2210 } => {
2211 let caller_id = self.caller_def_id;
2212 self.exec_call(
2213 func,
2214 args,
2215 destination.local,
2216 *target,
2217 None,
2218 caller_id,
2219 );
2220 }
2221 TerminatorKind::SwitchInt { discr, targets } => {
2222 self.exec_switchint(block, discr, targets, occurrence);
2223 }
2224 TerminatorKind::Assert { cond, expected, .. } => {
2225 self.exec_assert(cond, *expected, block, occurrence);
2226 }
2227 TerminatorKind::Goto { .. }
2228 | TerminatorKind::Return
2229 | TerminatorKind::Unreachable
2230 | TerminatorKind::UnwindResume
2231 | TerminatorKind::UnwindTerminate(_)
2232 | TerminatorKind::Yield { .. }
2233 | TerminatorKind::CoroutineDrop
2234 | TerminatorKind::FalseEdge { .. }
2235 | TerminatorKind::FalseUnwind { .. }
2236 | TerminatorKind::InlineAsm { .. }
2237 | TerminatorKind::TailCall { .. } => {}
2238 TerminatorKind::Drop { place, .. } => {
2239 self.exec_drop(place);
2240 }
2241 }
2242 }
2243
2244 fn exec_switchint(
2249 &mut self,
2250 block: BasicBlock,
2251 discr: &Operand<'tcx>,
2252 targets: &rustc_middle::mir::SwitchTargets,
2253 occurrence: usize,
2254 ) {
2255 let discr_val = self.value_of_operand(discr);
2256
2257 let cmp_cond = discr.place().and_then(|p| {
2261 let pk = PlaceKey::from_mir_place(&p);
2262 self.comparison_conds.get(&pk).cloned()
2263 });
2264
2265 if let Some(ref path) = self.path {
2267 if let Some(chosen) = chosen_successor(path, block, occurrence) {
2268 for (value, target) in targets.iter() {
2269 if target == chosen {
2270 let val_term = Int::from_u64(self.ctx, value as u64);
2271 self.path_conditions.push(discr_val.term._eq(&val_term));
2272 if let Some(ref cond) = cmp_cond {
2273 if value != 0 {
2274 self.path_conditions.push(cond.clone());
2275 } else {
2276 self.path_conditions.push(cond.not());
2277 }
2278 }
2279 if value != 0 {
2280 self.infer_switch_guard(discr);
2281 } else {
2282 self.inject_is_empty_len(discr);
2285 }
2286 return;
2287 }
2288 }
2289 if targets.otherwise() == chosen {
2291 for (value, _) in targets.iter() {
2293 let val_term = Int::from_u64(self.ctx, value as u64);
2294 self.path_conditions.push(discr_val.term._eq(&val_term).not());
2295 }
2296 if let Some(ref cond) = cmp_cond {
2297 if targets.iter().any(|(v, _)| v == 0) {
2300 self.path_conditions.push(cond.clone());
2301 }
2302 }
2303 return;
2304 }
2305 }
2306 }
2307
2308 self.notes.push(format!(
2310 "SwitchInt at bb{} occ{}: discr is symbolic, branch unknown",
2311 block.as_usize(),
2312 occurrence
2313 ));
2314 }
2315
2316 fn exec_assert(
2318 &mut self,
2319 cond: &Operand<'tcx>,
2320 expected: bool,
2321 _block: BasicBlock,
2322 _occurrence: usize,
2323 ) {
2324 let cond_val = self.value_of_operand(cond);
2325 if expected {
2326 let zero = Int::from_u64(self.ctx, 0);
2327 self.path_conditions
2328 .push(cond_val.term._eq(&zero).not());
2329 } else {
2330 let zero = Int::from_u64(self.ctx, 0);
2331 self.path_conditions.push(cond_val.term._eq(&zero));
2332 }
2333
2334 self.infer_guard_non_null(cond, expected);
2336 self.infer_guard_align(cond, expected);
2338 }
2339
2340 pub(crate) fn infer_guard_align(&mut self, cond: &Operand<'tcx>, expected: bool) {
2342 if !expected { return; }
2343 let place = match cond {
2344 Operand::Copy(p) | Operand::Move(p) => p,
2345 _ => return,
2346 };
2347 let cond_pk = PlaceKey::from_mir_place(place);
2348
2349 if let Some((lhs_pk, rhs_pk)) = self.binary_op_sources.get(&cond_pk).cloned() {
2351 let rem_pk = match (&lhs_pk, &rhs_pk) {
2353 (Some(pk), None) => pk.clone(),
2354 (None, Some(pk)) => pk.clone(),
2355 _ => return,
2356 };
2357 if let Some((div_lhs, div_rhs)) = self.binary_op_sources.get(&rem_pk).cloned() {
2359 if let Some(divisor) = resolve_u64_from_place_key(&div_rhs, self) {
2361 if divisor > 0 {
2362 if let Some(src_pk) = &div_lhs {
2364 if let Some(local) = src_pk.local() {
2365 if let Some(mut val) = self.locals.get(&local).cloned() {
2366 val.invariants.align_n = Some(divisor);
2367 self.set_local(local, val);
2368 }
2369 }
2370 }
2371 }
2372 }
2373 }
2374 }
2375 }
2376
2377 pub(crate) fn infer_guard_non_null(&mut self, cond: &Operand<'tcx>, expected: bool) {
2379 if !expected { return; }
2380 let place = match cond {
2381 Operand::Copy(p) | Operand::Move(p) => p,
2382 _ => return,
2383 };
2384 let cond_pk = PlaceKey::from_mir_place(place);
2385
2386 if let Some((lhs_pk, rhs_pk)) = self.binary_op_sources.get(&cond_pk).cloned() {
2388 if rhs_pk.is_none() {
2390 if let Some(ptr_pk) = &lhs_pk {
2391 if let Some(local) = ptr_pk.local() {
2392 if let Some(mut val) = self.locals.get(&local).cloned() {
2393 val.invariants.non_null = true;
2394 self.set_local(local, val);
2395 }
2396 }
2397 }
2398 }
2399 if lhs_pk.is_none() {
2401 if let Some(ptr_pk) = &rhs_pk {
2402 if let Some(local) = ptr_pk.local() {
2403 if let Some(mut val) = self.locals.get(&local).cloned() {
2404 val.invariants.non_null = true;
2405 self.set_local(local, val);
2406 }
2407 }
2408 }
2409 }
2410 }
2411 }
2412
2413 fn infer_switch_guard(&mut self, discr: &Operand<'tcx>) {
2415 let place = match discr {
2416 Operand::Copy(p) | Operand::Move(p) => p,
2417 _ => return,
2418 };
2419 let pk = PlaceKey::from_mir_place(place);
2420 if let Some((lhs_pk, rhs_pk)) = self.binary_op_sources.get(&pk).cloned() {
2421 self.mark_guard_pointer(&lhs_pk, &rhs_pk);
2422 }
2423 }
2424
2425 fn mark_guard_pointer(&mut self, lhs: &Option<PlaceKey>, rhs: &Option<PlaceKey>) {
2426 for ptr_pk in [lhs, rhs] {
2427 if let Some(pk) = ptr_pk {
2428 if let Some(local) = pk.local() {
2429 if let Some(mut val) = self.locals.get(&local).cloned() {
2430 val.invariants.non_null = true;
2431 self.set_local(local, val);
2432 }
2433 }
2434 }
2435 }
2436 }
2437
2438 fn check_place_alignment(&self, place: &Place<'tcx>) -> bool {
2440 let ty = place.ty(self.body, self.tcx).ty;
2441 self.align_of_ty(ty) > 0
2442 }
2443
2444 fn assert_contract_fact(&mut self, property: &Property<'tcx>) {
2446 if let Property::Or(or) = property {
2452 for group in &or.groups {
2453 if group.iter().any(|p| p.contract_kind() == ContractKind::Hazard) {
2454 self.contract_flags.alias_hazard_accepted = true;
2455 }
2456 }
2457 }
2458 let Property::Leaf(leaf) = property else {
2459 return;
2460 };
2461 if leaf.contract_kind == ContractKind::Hazard {
2462 self.contract_flags.alias_hazard_accepted = true;
2463 return;
2464 }
2465 let kind = leaf.kind;
2466 match kind {
2467 PropertyKind::NonNull => {
2468 if let Some(val) = self.contract_target_value(property) {
2469 self.set_non_null_for_value(property, val);
2470 }
2471 }
2472 PropertyKind::Align => {
2473 if let Some(val) = self.contract_target_value(property) {
2474 self.set_align_for_value(property, val);
2475 }
2476 }
2477 PropertyKind::Init => {
2478 if let Some(val) = self.contract_target_value(property) {
2479 self.set_init_for_value(property, val);
2480 }
2481 }
2482 PropertyKind::Owning => {
2483 if let Some(val) = self.contract_target_value(property) {
2484 self.set_owning_for_value(val);
2485 }
2486 }
2487 PropertyKind::Alive => {
2488 if let Some(id) = self.contract_alloc_id_field_aware(property) {
2489 self.alloc_mut(id).alive_assumed = true;
2490 }
2491 }
2492 PropertyKind::InBound => {
2493 if let Some(val) = self.contract_target_value(property) {
2494 self.set_in_bounds_for_value(property, val);
2495 }
2496 if let Some(fe_place) = property.for_each() {
2497 self.assert_in_bound_for_each(property, fe_place);
2498 self.contract_flags.has_checked_bounds = true;
2499 }
2500 }
2501 PropertyKind::Allocated => {
2502 self.assert_allocated_fact(property);
2503 }
2504 PropertyKind::Typed => {
2505 if let Some(val) = self.contract_target_value(property) {
2506 if let Some(alloc_id) = val.provenance_alloc_id() {
2507 if let Some(expected_ty) = property.args().get(1)
2508 .and_then(|a| if let PropertyArg::Ty(ty) = a { Some(*ty) } else { None })
2509 {
2510 self.alloc_mut(alloc_id).element_ty = Some(expected_ty);
2511 }
2512 }
2513 }
2514 }
2515 PropertyKind::SplitTransmute => {
2516 self.contract_flags.split_transmute_asserted = true;
2517 }
2518 PropertyKind::ValidCStr => {
2519 let id = self
2530 .contract_alloc_id_field_aware(property)
2531 .or_else(|| {
2532 let local = self.contract_target_local(property)?;
2533 self.locals.get(&local)?.provenance_alloc_id()
2534 });
2535 if let Some(id) = id {
2536 self.alloc_mut(id).dead = false;
2537 self.alloc_mut(id).alive_assumed = true;
2538 self.alloc_mut(id).initialized = true;
2539 self.alloc_mut(id).nul_terminated = true;
2540 if let Some(n) = property
2546 .args()
2547 .get(1)
2548 .and_then(|a| self.resolve_contract_count(a))
2549 {
2550 self.path_conditions.push(self.alloc(id).size.ge(&n));
2551 }
2552 }
2553 }
2554 PropertyKind::ValidNum => {
2555 if let Some(PropertyArg::Predicates(predicates)) = property.args().first() {
2556 for pred in predicates {
2557 if let Some(condition) = self.eval_predicate_as_bool(pred) {
2558 self.path_conditions.push(condition);
2559 if let Some(len_term) = self.try_simple_iter_len_from_pred(pred) {
2563 let one = Int::from_u64(self.ctx, 1);
2564 self.path_conditions.push(len_term.ge(&one));
2565 }
2566 }
2567 }
2568 }
2569 }
2570 _ => {
2571 self.notes.push(format!(
2572 "contract fact {:?} not directly asserted",
2573 kind
2574 ));
2575 }
2576 }
2577 }
2578
2579 fn contract_target_local(&self, property: &Property<'tcx>) -> Option<Local> {
2581 let cp = match property.args().first()? {
2582 PropertyArg::Expr(ContractExpr::Place(cp)) => cp,
2583 PropertyArg::Expr(ContractExpr::IndexAccess { slice, .. }) => {
2584 match slice.as_ref() {
2585 ContractExpr::Place(cp) => cp,
2586 _ => return None,
2587 }
2588 }
2589 _ => return None,
2590 };
2591 match cp.base {
2592 PlaceBase::Local(n) => Some(Local::from_usize(n)),
2593 PlaceBase::Arg(n) => {
2594 Some(Local::from_usize(n + 1))
2595 }
2596 PlaceBase::Return => Some(Local::from_usize(0)),
2597 }
2598 }
2599
2600 fn materialize_external_alloc(
2603 &mut self,
2604 elem_ty: Ty<'tcx>,
2605 count_term: Option<Int<'ctx>>,
2606 val_ty: Ty<'tcx>,
2607 huge: bool,
2608 ) -> VmValue<'ctx, 'tcx> {
2609 let elem_sz_raw = self.size_of_ty(elem_ty);
2610 let heap_align = self.align_of_ty(elem_ty).max(1);
2611 let (heap_id, heap_base) = if huge || elem_sz_raw == 0 {
2612 let max_size = Int::from_u64(self.ctx, i64::MAX as u64);
2616 self.allocate_external(max_size, heap_align, Some(elem_ty))
2617 } else {
2618 let elem_sz = Int::from_u64(self.ctx, elem_sz_raw as u64);
2619 let count = count_term.unwrap_or_else(|| Int::from_u64(self.ctx, 1));
2620 let total = Int::mul(self.ctx, &[&count, &elem_sz]);
2621 self.allocate_external(total, heap_align, Some(elem_ty))
2622 };
2623 self.alloc_mut(heap_id).initialized = true;
2624 VmValue {
2625 term: heap_base,
2626 ty: val_ty,
2627 provenance: Some(Provenance {
2628 alloc_id: heap_id,
2629 offset: Int::from_u64(self.ctx, 0),
2630 is_field_offset: false,
2631 }),
2632 invariants: ValueInvariants {
2633 non_null: true,
2634 init: true,
2635 in_bounds: true,
2636 aligned: true,
2637 align_n: if heap_align > 1 { Some(heap_align) } else { None },
2638 is_field_offset: false,
2639 },
2640 }
2641 }
2642
2643 fn assert_allocated_fact(&mut self, property: &Property<'tcx>) {
2655 let elem_ty = property.args().get(1)
2656 .and_then(|a| if let PropertyArg::Ty(ty) = a { Some(*ty) } else { None });
2657 let count_term = property.args().get(2).and_then(|a| self.resolve_contract_count(a));
2658 let Some(elem_ty) = elem_ty else { return };
2659
2660 let has_nonfield = property.args().first()
2661 .and_then(|a| match a {
2662 PropertyArg::Expr(ContractExpr::Place(cp)) => Some(cp),
2663 PropertyArg::Expr(ContractExpr::IndexAccess { slice, .. }) => {
2664 match slice.as_ref() {
2665 ContractExpr::Place(cp) => Some(cp),
2666 _ => None,
2667 }
2668 }
2669 _ => None,
2670 })
2671 .map(|cp| {
2672 cp.projections.iter().any(|p| {
2673 !matches!(p, crate::verify::contract::ContractProjection::Field { .. })
2674 })
2675 })
2676 .unwrap_or(false);
2677
2678 if has_nonfield {
2679 let Some(local) = self.contract_target_local(property) else { return };
2681 let Some(val) = self.locals.get(&local).cloned() else { return };
2682 if let Some(alloc_id) = val.provenance_alloc_id() {
2683 self.alloc_mut(alloc_id).dead = false;
2684 }
2685 let v = self.materialize_external_alloc(elem_ty, count_term, val.ty, false);
2686 self.set_local(local, v);
2687 } else {
2688 let Some((local, field_path)) = self.contract_field_path(property) else { return };
2689 if field_path.is_empty() {
2690 let Some(val) = self.locals.get(&local).cloned() else { return };
2692 if let Some(alloc_id) = val.provenance_alloc_id() {
2693 self.alloc_mut(alloc_id).dead = false;
2694 }
2695 let v = self.materialize_external_alloc(elem_ty, count_term, val.ty, false);
2696 self.set_local(local, v);
2697 } else {
2698 let is_direct_simple_ptr = self.field_value(local, &field_path)
2706 .map(|val| {
2707 (matches!(val.ty.kind(), rustc_middle::ty::TyKind::RawPtr(..))
2708 || matches!(val.ty.kind(), rustc_middle::ty::TyKind::Adt(adt, _)
2709 if api_classify::is_std_nonnull(&self.tcx.def_path_str(adt.did()))))
2710 && (elem_ty.is_primitive()
2711 || matches!(elem_ty.kind(), rustc_middle::ty::TyKind::Param(_)))
2712 })
2713 .unwrap_or(false);
2714 if is_direct_simple_ptr {
2715 let Some(val) = self.field_value(local, &field_path).cloned() else { return };
2716 if let Some(alloc_id) = val.provenance_alloc_id() {
2717 self.alloc_mut(alloc_id).dead = false;
2718 }
2719 let v = self.materialize_external_alloc(elem_ty, count_term, val.ty, true);
2720 self.set_field_value(local, field_path, v);
2721 } else {
2722 let Some(val) = self.locals.get(&local).cloned() else { return };
2723 if let Some(alloc_id) = val.provenance_alloc_id() {
2724 self.alloc_mut(alloc_id).dead = false;
2725 }
2726 let v = self.materialize_external_alloc(elem_ty, count_term, val.ty, false);
2727 self.set_local(local, v);
2728 }
2729 }
2730 }
2731 }
2732
2733 fn contract_field_path(&self, property: &Property<'tcx>) -> Option<(Local, Vec<usize>)> {
2737 let cp = match property.args().first()? {
2738 PropertyArg::Expr(ContractExpr::Place(cp)) => cp,
2739 PropertyArg::Expr(ContractExpr::IndexAccess { slice, .. }) => {
2740 match slice.as_ref() {
2741 ContractExpr::Place(cp) => cp,
2742 _ => return None,
2743 }
2744 }
2745 _ => return None,
2746 };
2747 let local = match cp.base {
2748 PlaceBase::Local(n) => Local::from_usize(n),
2749 PlaceBase::Arg(n) => Local::from_usize(n + 1),
2750 PlaceBase::Return => Local::from_usize(0),
2751 };
2752 let mut path = Vec::new();
2753 for proj in &cp.projections {
2754 match proj {
2755 crate::verify::contract::ContractProjection::Field { index, .. } => {
2756 path.push(*index);
2757 }
2758 _ => break,
2759 }
2760 }
2761 Some((local, path))
2762 }
2763
2764 fn contract_target_value(&mut self, property: &Property<'tcx>) -> Option<VmValue<'ctx, 'tcx>> {
2768 let (local, path) = self.contract_field_path(property)?;
2769 if path.is_empty() {
2770 self.locals.get(&local).cloned()
2771 } else {
2772 self.field_value(local, &path).cloned()
2773 }
2774 }
2775
2776 fn set_contract_target_value(&mut self, property: &Property<'tcx>, val: VmValue<'ctx, 'tcx>) {
2778 if let Some((local, path)) = self.contract_field_path(property) {
2779 if path.is_empty() {
2780 self.set_local(local, val);
2781 } else {
2782 self.set_field_value(local, path, val);
2783 }
2784 }
2785 }
2786
2787 fn contract_alloc_id_field_aware(&mut self, property: &Property<'tcx>) -> Option<AllocId> {
2790 let cp = match property.args().first()? {
2791 PropertyArg::Expr(ContractExpr::Place(cp)) => cp.clone(),
2792 _ => return None,
2793 };
2794 let local = match cp.base {
2795 PlaceBase::Local(n) => Local::from_usize(n),
2796 PlaceBase::Arg(n) => Local::from_usize(n + 1),
2797 PlaceBase::Return => Local::from_usize(0),
2798 };
2799 let mut field_path: Vec<usize> = Vec::new();
2800 for proj in &cp.projections {
2801 match proj {
2802 crate::verify::contract::ContractProjection::Field { index, .. } => {
2803 field_path.push(*index);
2804 }
2805 _ => return None,
2806 }
2807 }
2808 if field_path.is_empty() {
2809 self.locals.get(&local)?.provenance_alloc_id()
2810 } else {
2811 self.field_value(local, &field_path)?.provenance_alloc_id()
2812 }
2813 }
2814
2815 fn resolve_contract_count(&self, arg: &PropertyArg<'tcx>) -> Option<Int<'ctx>> {
2818 match arg {
2819 PropertyArg::Expr(ContractExpr::Const(n)) => {
2820 Some(Int::from_u64(self.ctx, *n as u64))
2821 }
2822 PropertyArg::Expr(expr) => self.eval_contract_expr_simple(expr),
2825 _ => None,
2826 }
2827 }
2828
2829 fn eval_predicate_as_bool(&self, pred: &crate::verify::contract::NumericPredicate<'tcx>) -> Option<Bool<'ctx>> {
2831 use crate::verify::contract::{ContractExpr, RelOp};
2832 let lhs = self.eval_contract_expr_simple(&pred.lhs)?;
2833 let rhs = match &pred.rhs {
2834 ContractExpr::Const(v) => Int::from_u64(self.ctx, *v as u64),
2835 _ => self.eval_contract_expr_simple(&pred.rhs)?,
2836 };
2837 Some(match pred.op {
2838 RelOp::Eq => lhs._eq(&rhs),
2839 RelOp::Ne => lhs._eq(&rhs).not(),
2840 RelOp::Le => lhs.le(&rhs),
2841 RelOp::Lt => lhs.lt(&rhs),
2842 RelOp::Ge => lhs.ge(&rhs),
2843 RelOp::Gt => lhs.gt(&rhs),
2844 })
2845 }
2846
2847 fn eval_contract_expr_simple(&self, expr: &crate::verify::contract::ContractExpr<'tcx>) -> Option<Int<'ctx>> {
2848 use crate::verify::contract::{ContractExpr, NumericOp, PlaceBase};
2849 match expr {
2850 ContractExpr::SizeOf(ty) => {
2851 let size = self.size_of_ty(*ty).max(1);
2852 Some(Int::from_u64(self.ctx, size as u64))
2853 }
2854 ContractExpr::Place(cp) => {
2855 let local = match cp.base {
2856 PlaceBase::Local(n) => Local::from_usize(n),
2857 PlaceBase::Arg(n) => Local::from_usize(n + 1),
2858 PlaceBase::Return => Local::from_usize(0),
2859 };
2860 let mut path: Vec<usize> = Vec::new();
2861 for proj in &cp.projections {
2862 match proj {
2863 crate::verify::contract::ContractProjection::Field { index, .. } => {
2864 path.push(*index);
2865 }
2866 _ => return None,
2868 }
2869 }
2870 if path.is_empty() {
2871 self.local_value(local).map(|v| v.term.clone())
2872 } else {
2873 self.field_value(local, &path).map(|v| v.term.clone())
2874 }
2875 }
2876 ContractExpr::Len(inner) => {
2877 if let Some(val) = self.eval_contract_expr_simple_value(inner) {
2879 if let Some(term) = self.try_simple_iter_len(&val) {
2880 return Some(term);
2881 }
2882 }
2883 let val = self.eval_contract_expr_simple_value(inner)?;
2884 let alloc_id = val.provenance_alloc_id()?;
2885 let alloc = self.alloc(alloc_id);
2886 let elem_ty = alloc.element_ty?;
2887 let elem_size = self.size_of_ty(elem_ty).max(1) as u64;
2888 if elem_size == 1 {
2889 return Some(alloc.size.clone());
2890 }
2891 let elem_term = Int::from_u64(self.ctx, elem_size);
2892 Some(alloc.size.div(&elem_term))
2893 }
2894 ContractExpr::Binary { op: NumericOp::Mul, lhs, rhs } => {
2895 let l = self.eval_contract_expr_simple(lhs)?;
2896 let r = self.eval_contract_expr_simple(rhs)?;
2897 Some(Int::mul(self.ctx, &[&l, &r]))
2898 }
2899 ContractExpr::Binary { op: NumericOp::Add, lhs, rhs } => {
2900 let l = self.eval_contract_expr_simple(lhs)?;
2901 let r = self.eval_contract_expr_simple(rhs)?;
2902 Some(Int::add(self.ctx, &[&l, &r]))
2903 }
2904 ContractExpr::Binary { op: NumericOp::Sub, lhs, rhs } => {
2905 let l = self.eval_contract_expr_simple(lhs)?;
2906 let r = self.eval_contract_expr_simple(rhs)?;
2907 Some(Int::sub(self.ctx, &[&l, &r]))
2908 }
2909 ContractExpr::Binary { op: NumericOp::Div, lhs, rhs } => {
2910 let l = self.eval_contract_expr_simple(lhs)?;
2911 let r = self.eval_contract_expr_simple(rhs)?;
2912 Some(l.div(&r))
2913 }
2914 ContractExpr::Const(n) => Some(Int::from_u64(self.ctx, *n as u64)),
2915 _ => None,
2916 }
2917 }
2918
2919 fn eval_contract_expr_simple_value(&self, expr: &crate::verify::contract::ContractExpr<'tcx>) -> Option<VmValue<'ctx, 'tcx>> {
2920 match expr {
2921 ContractExpr::Place(cp) => {
2922 match cp.base {
2923 PlaceBase::Local(n) => {
2924 self.local_value(Local::from_usize(n)).cloned()
2925 }
2926 _ => None,
2927 }
2928 }
2929 _ => None,
2930 }
2931 }
2932
2933 fn try_simple_iter_len(&self, arg_val: &VmValue<'ctx, 'tcx>) -> Option<Int<'ctx>> {
2938 use rustc_middle::ty::TyKind;
2939 let is_iter = match arg_val.ty.kind() {
2940 TyKind::Ref(_, pointee, _) => match pointee.kind() {
2941 TyKind::Adt(adt_def, _) => {
2942 let name = self.tcx.def_path_str(adt_def.did());
2943 api_classify::is_std_iter_or_itermut(&name)
2944 }
2945 _ => false,
2946 },
2947 _ => false,
2948 };
2949 if !is_iter { return None; }
2950 let local = Local::from_usize(1);
2951 let ptr = self.field_value(local, &[0])?;
2952 let end = self.field_value(local, &[1])?;
2953 let pp = ptr.provenance.as_ref()?;
2954 let ep = end.provenance.as_ref()?;
2955 if pp.alloc_id != ep.alloc_id { return None; }
2956 let diff = Int::sub(self.ctx, &[&ep.offset, &pp.offset]);
2957 let sz = Int::from_u64(self.ctx, self.iter_elem_size(ptr));
2958 Some(diff.div(&sz))
2959 }
2960
2961 fn try_simple_iter_len_from_pred(
2965 &self,
2966 pred: &crate::verify::contract::NumericPredicate<'tcx>,
2967 ) -> Option<Int<'ctx>> {
2968 use crate::verify::contract::{ContractExpr, RelOp};
2969 if !matches!(pred.op, RelOp::Ne) { return None; }
2970 if !matches!(&pred.rhs, ContractExpr::Const(0)) { return None; }
2971 let ContractExpr::Len(inner) = &pred.lhs else { return None; };
2972 let val = self.eval_contract_expr_simple_value(inner)?;
2973 self.try_simple_iter_len(&val)
2974 }
2975
2976 fn inject_is_empty_len(&mut self, discr: &Operand<'tcx>) {
2979 let place = match discr {
2980 Operand::Copy(p) | Operand::Move(p) => p,
2981 _ => return,
2982 };
2983 if let Some(len_expr) = self.is_empty_len.get(&place.local) {
2984 let one = Int::from_u64(self.ctx, 1);
2985 self.path_conditions.push(len_expr.ge(&one));
2986 }
2987 }
2988
2989 fn track_iter_ptr_update(
2994 &mut self,
2995 local: Local,
2996 ) {
2997 let local_val = match self.locals.get(&local) {
2998 Some(v) => v,
2999 None => return,
3000 };
3001 let is_iter = match local_val.ty.kind() {
3002 rustc_middle::ty::TyKind::Ref(_, pointee, _) => match pointee.kind() {
3003 rustc_middle::ty::TyKind::Adt(adt_def, _) => {
3004 let name = self.tcx.def_path_str(adt_def.did());
3005 api_classify::is_std_iter_or_itermut(&name)
3006 }
3007 _ => false,
3008 },
3009 _ => false,
3010 };
3011 if !is_iter { return; }
3012 let one = Int::from_u64(self.ctx, 1);
3013 let new_offset = match self.iter_ptr_offset.get(&local) {
3014 Some(prev) => Int::add(self.ctx, &[prev, &one]),
3015 None => one,
3016 };
3017 self.iter_ptr_offset.insert(local, new_offset);
3018 }
3019
3020 fn track_iter_ptr_after_inline(&mut self) {
3024 let mut to_update: Vec<Local> = Vec::new();
3025 for (&local, val) in self.locals.iter() {
3026 let is_iter = match val.ty.kind() {
3027 rustc_middle::ty::TyKind::Ref(_, pointee, _) => match pointee.kind() {
3028 rustc_middle::ty::TyKind::Adt(adt_def, _) => {
3029 let name = self.tcx.def_path_str(adt_def.did());
3030 name.ends_with("::Iter") || name == "Iter"
3031 || name.ends_with("::IterMut") || name == "IterMut"
3032 }
3033 _ => false,
3034 },
3035 _ => false,
3036 };
3037 if is_iter {
3038 to_update.push(local);
3039 }
3040 }
3041 let one = Int::from_u64(self.ctx, 1);
3042 for local in to_update {
3043 let new_offset = match self.iter_ptr_offset.get(&local) {
3044 Some(prev) => Int::add(self.ctx, &[prev, &one]),
3045 None => one.clone(),
3046 };
3047 self.iter_ptr_offset.insert(local, new_offset);
3048 }
3049 }
3050
3051 fn set_non_null_for_value(&mut self, property: &Property<'tcx>, mut val: VmValue<'ctx, 'tcx>) {
3053 val.invariants.non_null = true;
3054 self.set_contract_target_value(property, val);
3055 }
3056
3057 fn set_in_bounds_for_value(&mut self, property: &Property<'tcx>, mut val: VmValue<'ctx, 'tcx>) {
3058 val.invariants.in_bounds = true;
3059 self.set_contract_target_value(property, val);
3060 }
3061
3062 fn assert_in_bound_for_each(&mut self, property: &Property<'tcx>, fe_place: &crate::verify::contract::ContractPlace<'tcx>) {
3063 let fe_local = match fe_place.base {
3064 PlaceBase::Arg(n) => Local::from_usize(n + 1),
3065 PlaceBase::Local(n) => Local::from_usize(n),
3066 _ => return,
3067 };
3068 let fe_val = match self.locals.get(&fe_local).cloned() {
3069 Some(v) => v,
3070 None => return,
3071 };
3072 let fe_alloc_id = match fe_val.provenance_alloc_id() {
3073 Some(id) => id,
3074 None => return,
3075 };
3076 let byte_vals: Vec<(usize, Int<'ctx>)> = self
3077 .alloc_byte_values(fe_alloc_id)
3078 .into_iter()
3079 .map(|(off, term)| (off, term.clone()))
3080 .collect();
3081 if byte_vals.is_empty() {
3082 return;
3083 }
3084 let slice_local = match property.args().first() {
3085 Some(PropertyArg::Expr(ContractExpr::IndexAccess { slice, .. })) => {
3086 match slice.as_ref() {
3087 ContractExpr::Place(cp) => match cp.base {
3088 PlaceBase::Arg(n) => Some(Local::from_usize(n + 1)),
3089 PlaceBase::Local(n) => Some(Local::from_usize(n)),
3090 _ => None,
3091 },
3092 _ => None,
3093 }
3094 }
3095 Some(PropertyArg::Expr(ContractExpr::Place(cp))) => match cp.base {
3096 PlaceBase::Arg(n) => Some(Local::from_usize(n + 1)),
3097 PlaceBase::Local(n) => Some(Local::from_usize(n)),
3098 _ => None,
3099 },
3100 _ => None,
3101 };
3102 let data_size = slice_local
3103 .and_then(|loc| self.locals.get(&loc))
3104 .and_then(|sl_val| sl_val.provenance_alloc_id())
3105 .map(|da_id| self.alloc(da_id).size.clone());
3106 let elem_sz = slice_local
3107 .and_then(|loc| self.locals.get(&loc))
3108 .and_then(|sl_val| sl_val.provenance_alloc_id())
3109 .and_then(|da_id| self.alloc(da_id).element_ty)
3110 .map(|ty| self.size_of_ty(ty) as u64)
3111 .unwrap_or(1)
3112 .max(1);
3113 let Some(data_size) = data_size else { return };
3114 let elem_sz_term = Int::from_u64(self.ctx, elem_sz);
3115 let len = data_size.div(&elem_sz_term);
3116 let zero = Int::from_u64(self.ctx, 0);
3117 for (_, term) in &byte_vals {
3118 self.path_conditions.push(term.ge(&zero));
3119 self.path_conditions.push(term.lt(&len));
3120 }
3121 }
3122
3123 fn set_align_for_value(&mut self, property: &Property<'tcx>, mut val: VmValue<'ctx, 'tcx>) {
3125 val.invariants.aligned = true;
3126 if let Some(PropertyArg::Ty(ty)) = property.args().get(1) {
3127 let align = self.align_of_ty(*ty);
3128 if align > 1 {
3129 val.invariants.align_n = Some(align);
3130 }
3131 }
3132 self.set_contract_target_value(property, val);
3133 }
3134
3135 fn set_init_for_value(&mut self, property: &Property<'tcx>, val: VmValue<'ctx, 'tcx>) {
3137 if let Some(prov) = &val.provenance {
3138 self.alloc_mut(prov.alloc_id).initialized = true;
3139 }
3140 if let Some((local, path)) = self.contract_field_path(property) {
3141 let existing = if path.is_empty() {
3142 self.locals.get(&local).cloned()
3143 } else {
3144 self.field_value(local, &path).cloned()
3145 };
3146 if let Some(mut existing) = existing {
3147 existing.invariants.init = true;
3148 if let Some(prov) = &existing.provenance {
3149 self.alloc_mut(prov.alloc_id).initialized = true;
3150 }
3151 if path.is_empty() {
3152 self.set_local(local, existing);
3153 } else {
3154 self.set_field_value(local, path, existing);
3155 }
3156 }
3157 }
3158 }
3159
3160 fn set_owning_for_value(&mut self, val: VmValue<'ctx, 'tcx>) {
3162 if let Some(prov) = &val.provenance {
3163 self.alloc_mut(prov.alloc_id).initialized = true;
3164 }
3165 }
3166
3167 fn find_nn_pointee(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
3170 use rustc_middle::ty::TyKind;
3171 match ty.kind() {
3172 TyKind::Adt(adt_def, substs) => {
3173 let def_path = self.tcx.def_path_str(adt_def.did());
3174 let is_nn = api_classify::is_std_nonnull(&def_path);
3175 if is_nn {
3176 substs.first().and_then(|s| s.as_type())
3177 } else if api_classify::is_std_option(&def_path)
3178 {
3179 if let Some(inner) = substs.first().and_then(|s| s.as_type()) {
3180 match inner.kind() {
3181 TyKind::Adt(ia, is_) => {
3182 let ip = self.tcx.def_path_str(ia.did());
3183 let is_nn_inner = api_classify::is_std_nonnull(&ip);
3184 if is_nn_inner {
3185 is_.first().and_then(|s| s.as_type())
3186 } else {
3187 None
3188 }
3189 }
3190 _ => None,
3191 }
3192 } else {
3193 None
3194 }
3195 } else {
3196 None
3197 }
3198 }
3199 _ => None,
3200 }
3201 }
3202
3203 fn try_as_ptr_fallback(
3208 &mut self,
3209 dest: Local,
3210 func: &Operand<'tcx>,
3211 first_arg_val: VmValue<'ctx, 'tcx>,
3212 first_arg_op: &Operand<'tcx>,
3213 ) -> bool {
3214 let name = crate::helpers::mir_utils::call_name(self.tcx, func);
3215 if !api_classify::is_as_ptr(&name) {
3216 return false;
3217 }
3218 let dest_ty = self.body.local_decls[dest].ty;
3219 let prov = first_arg_val.provenance.clone().or_else(|| {
3220 if let Operand::Move(place) | Operand::Copy(place) = first_arg_op {
3221 self.local_alloc_ids.get(&place.local).map(|&id| {
3222 Provenance { alloc_id: id, offset: Int::from_u64(self.ctx, 0), is_field_offset: false }
3223 })
3224 } else {
3225 None
3226 }
3227 });
3228 if let Some(ref prov) = prov {
3229 self.alloc_mut(prov.alloc_id).initialized = true;
3230 self.set_local(dest, VmValue {
3231 term: first_arg_val.term.clone(),
3232 ty: dest_ty,
3233 provenance: Some(prov.clone()),
3234 invariants: ValueInvariants {
3235 non_null: true, aligned: true, init: true,
3236 in_bounds: first_arg_val.invariants.in_bounds,
3237 align_n: first_arg_val.invariants.align_n,
3238 is_field_offset: false,
3239 },
3240 });
3241 return true;
3242 }
3243 false
3244 }
3245
3246 pub(crate) fn try_materialize_const_bytes(
3250 &mut self,
3251 val: &mut VmValue<'ctx, 'tcx>,
3252 operand: &Operand<'tcx>,
3253 ) {
3254 let operand_val = self.value_of_operand(operand);
3256 let op_ty = operand_val.ty;
3257 let (pointee_ty, _is_ref) = match op_ty.kind() {
3258 rustc_middle::ty::TyKind::Ref(_, inner_ty, _) => (*inner_ty, true),
3259 rustc_middle::ty::TyKind::RawPtr(inner_ty, _) => (*inner_ty, false),
3260 _ => {
3261 let val_ty = val.ty;
3263 match val_ty.kind() {
3264 rustc_middle::ty::TyKind::Ref(_, inner_ty, _) => (*inner_ty, true),
3265 rustc_middle::ty::TyKind::RawPtr(inner_ty, _) => (*inner_ty, false),
3266 _ => return,
3267 }
3268 }
3269 };
3270 match pointee_ty.kind() {
3271 rustc_middle::ty::TyKind::Array(elem_ty, _)
3272 | rustc_middle::ty::TyKind::Slice(elem_ty) => {
3273 let is_byte = match elem_ty.kind() {
3274 rustc_middle::ty::TyKind::Uint(rustc_middle::ty::UintTy::U8) => true,
3275 rustc_middle::ty::TyKind::Int(rustc_middle::ty::IntTy::I8) => true,
3276 _ => false,
3277 };
3278 if is_byte {
3279 let bytes_opt = crate::helpers::mir_utils::extract_const_bytes_from_operand(self.tcx, operand)
3280 .or_else(|| self.trace_to_const_bytes(operand));
3281 if let Some(bytes) = bytes_opt {
3282 let size = z3::ast::Int::from_u64(self.ctx, bytes.len() as u64);
3283 let (alloc_id, base) = self.allocate(
3284 size,
3285 self.align_of_ty(pointee_ty),
3286 Some(pointee_ty),
3287 );
3288 self.alloc_mut(alloc_id).initialized = true;
3289 for (i, &b) in bytes.iter().enumerate() {
3290 self.record_byte_value(alloc_id, i,
3291 z3::ast::Int::from_u64(self.ctx, b as u64));
3292 if b == 0 {
3293 self.mark_byte_nul(alloc_id, i);
3294 } else {
3295 self.mark_byte_non_nul(alloc_id, i);
3296 }
3297 }
3298 val.term = base;
3299 val.provenance = Some(super::state::Provenance {
3300 alloc_id,
3301 offset: z3::ast::Int::from_u64(self.ctx, 0),
3302 is_field_offset: false,
3303 });
3304 val.invariants = ValueInvariants {
3305 non_null: true, init: true, aligned: true, in_bounds: false,
3306 align_n: None,
3307 is_field_offset: false,
3308 };
3309 }
3310 }
3311 }
3312 _ => {}
3313 }
3314 }
3315
3316 pub(crate) fn trace_to_const_bytes(&self, operand: &Operand<'tcx>) -> Option<Vec<u8>> {
3317 let place = match operand {
3318 Operand::Copy(p) | Operand::Move(p) => p,
3319 _ => return None,
3320 };
3321 let base_local = if place.projection.len() == 1
3322 && matches!(place.projection.first().map(|p| p.kind()),
3323 Some(rustc_middle::mir::ProjectionElem::Deref))
3324 {
3325 place.local
3326 } else if place.projection.is_empty() {
3327 place.local
3328 } else {
3329 return None;
3330 };
3331 for block in self.body.basic_blocks.iter() {
3332 for stmt in &block.statements {
3333 if let StatementKind::Assign(assign) = &stmt.kind {
3334 let (dest, rvalue) = &**assign;
3335 if dest.local != base_local || !dest.projection.is_empty() {
3336 continue;
3337 }
3338 match rvalue {
3339 #[cfg(rapx_rvalue_use_with_retag)]
3340 Rvalue::Use(op, _) => {
3341 return crate::helpers::mir_utils::extract_const_bytes_from_operand(self.tcx, op)
3342 .or_else(|| self.trace_to_const_bytes(op));
3343 }
3344 #[cfg(not(rapx_rvalue_use_with_retag))]
3345 Rvalue::Use(op) => {
3346 return crate::helpers::mir_utils::extract_const_bytes_from_operand(self.tcx, op)
3347 .or_else(|| self.trace_to_const_bytes(op));
3348 }
3349 Rvalue::Ref(_, _, p) => {
3350 let op = Operand::Copy(*p);
3351 return self.trace_to_const_bytes(&op);
3352 }
3353 _ => return None,
3354 }
3355 }
3356 }
3357 }
3358 None
3359 }
3360
3361 fn propagate_field_values_to_ref(&mut self, source_place: &Place<'tcx>, dest: Local) {
3372 let only_field_deref = source_place.projection.iter().all(|p| {
3377 matches!(p.kind(), rustc_middle::mir::ProjectionElem::Field(..)
3378 | rustc_middle::mir::ProjectionElem::Deref)
3379 });
3380 if !only_field_deref || source_place.projection.is_empty() {
3381 return;
3382 }
3383 let field_prefix: Vec<usize> = source_place
3384 .projection
3385 .iter()
3386 .filter_map(|p| match p.kind() {
3387 rustc_middle::mir::ProjectionElem::Field(fi, _) => Some(fi.as_usize()),
3388 _ => None,
3389 })
3390 .collect();
3391 let keys: Vec<Vec<usize>> = self
3392 .field_values
3393 .keys()
3394 .filter(|(l, _)| *l == source_place.local)
3395 .map(|(_, p)| p.clone())
3396 .collect();
3397 for path in keys {
3398 let matches_prefix = field_prefix.is_empty()
3399 || (path.len() > field_prefix.len() && path[..field_prefix.len()] == field_prefix[..]);
3400 if matches_prefix {
3401 let rest = if field_prefix.is_empty() {
3402 path.clone()
3403 } else {
3404 path[field_prefix.len()..].to_vec()
3405 };
3406 if let Some(v) = self.field_values.get(&(source_place.local, path.clone())).cloned() {
3407 self.set_field_value(dest, rest, v);
3408 }
3409 }
3410 }
3411 }
3412
3413 fn propagate_byte_values_to_ref(
3414 &mut self,
3415 source_place: &Place<'tcx>,
3416 ref_val: &VmValue<'ctx, 'tcx>,
3417 ) {
3418 let Some(src_alloc_id) = self.local_alloc_ids.get(&source_place.local).copied() else {
3419 return;
3420 };
3421 let Some(ref_alloc_id) = ref_val.provenance_alloc_id() else {
3422 return;
3423 };
3424 if src_alloc_id == ref_alloc_id {
3425 return; }
3427 self.copy_byte_tracking(src_alloc_id, ref_alloc_id);
3429 }
3430
3431 fn aggregate_field_tys(&self, ty: Ty<'tcx>) -> Vec<Ty<'tcx>> {
3433 match ty.kind() {
3434 rustc_middle::ty::TyKind::Array(elem_ty, _len) => {
3435 vec![*elem_ty]
3437 }
3438 rustc_middle::ty::TyKind::Tuple(elems) => {
3439 elems.iter().collect()
3440 }
3441 rustc_middle::ty::TyKind::Adt(adt_def, substs) => {
3442 if adt_def.is_enum() { return vec![]; }
3443 let variant = adt_def.non_enum_variant();
3444 variant.fields.iter()
3445 .map(|f| {
3446 let unnorm = f.ty(self.tcx, substs);
3447 unnorm.skip_norm_wip()
3448 })
3449 .collect()
3450 }
3451 _ => vec![],
3452 }
3453 }
3454}
3455
3456fn chosen_successor(path: &Path, block: BasicBlock, occurrence: usize) -> Option<BasicBlock> {
3458 let mut count = 0;
3459 let mut previous = None;
3460 for step in path.steps.iter() {
3461 match step {
3462 PathStep::Block(current) => {
3463 if previous == Some(block) {
3464 count += 1;
3465 if count == occurrence {
3466 return Some(*current);
3467 }
3468 }
3469 previous = Some(*current);
3470 }
3471 PathStep::Checkpoint(_) => return None,
3472 }
3473 }
3474 None
3475}
3476
3477fn resolve_u64_from_place_key<'ctx, 'tcx>(
3479 pk: &Option<PlaceKey>,
3480 state: &VmState<'ctx, 'tcx>,
3481) -> Option<u64> {
3482 let pk = pk.as_ref()?;
3483 let local = pk.local()?;
3484 let val = state.local_value(local)?;
3485 val.term.as_u64()
3486}