1use rustc_hir::def_id::DefId;
13use rustc_middle::mir::{BasicBlock, Local, Operand, TerminatorKind};
14use rustc_middle::ty::{Ty, TyKind};
15use z3::ast::{Ast, Bool, Int};
16
17use crate::compat::{FxHashMap, FxHashSet, Spanned};
18use crate::limit::MAX_INLINE_DEPTH;
19use crate::verify::api_classify;
20use crate::verify::call_summary::{self, CallEffect};
21use super::state::{AllocId, ElementTy, OffsetKind, Provenance, ValueFacts, ValueSource, VmState, VmValue};
22
23impl<'z3, 'tcx> VmState<'z3, 'tcx> {
24 pub(crate) fn exec_call(
32 &mut self,
33 func: &Operand<'tcx>,
34 args: &[Spanned<Operand<'tcx>>],
35 destination: Local,
36 caller_def_id: DefId,
37 ) {
38 let arg_values: Vec<VmValue<'z3, 'tcx>> = args
39 .iter()
40 .map(|arg| self.value_of_operand(&arg.node))
41 .collect();
42
43 let name = crate::helpers::mir_utils::call_name(self.tcx, func);
44 let callee =
45 crate::helpers::mir_utils::dep_callee_resolved_def_id(self.tcx, caller_def_id, func);
46 let caller_arg_locals: Vec<Option<Local>> = args
47 .iter()
48 .map(|a| a.node.place().map(|p| p.local))
49 .collect();
50
51 if crate::helpers::mir_utils::is_eq_call(self.tcx, func) {
56 self.propagate_const_bytes_to_tracked(args);
57 }
58
59 if self.try_slice_index(callee, &arg_values, args, destination) {
62 return;
63 }
64
65 if self.try_slice_get(callee, &arg_values, args, destination) {
68 return;
69 }
70
71 if self.try_iter_len_is_empty(&name, &arg_values, args, destination) {
73 return;
74 }
75
76 if self.try_iter_next(&name, &arg_values, destination) {
78 return;
79 }
80
81 if self.try_nonnull_new(callee, &arg_values, destination) {
85 return;
86 }
87
88 if let Some(c) = callee {
93 if self.tcx.is_mir_available(c) {
94 if crate::helpers::mir_utils::is_iter_ptr_adj(self.tcx, c) && arg_values.len() >= 2
95 {
96 self.apply_iter_ptr_update(c, &arg_values);
97 }
99 }
100 }
101
102 if let Some(c) = callee {
107 if self.tcx.is_mir_available(c) {
108 if let Some(effect) =
111 crate::verify::call_summary::interprocedural::try_field_load_effect(self.tcx, c)
112 {
113 self.apply_call_effect(&effect, &arg_values, &caller_arg_locals, destination, callee);
114 self.materialize_const_bytes_after_call(args, destination);
115 return;
116 }
117 if let Some(effect) =
118 crate::verify::call_summary::interprocedural::try_ptr_field_return_effect(
119 self.tcx, c,
120 )
121 {
122 self.apply_call_effect(&effect, &arg_values, &caller_arg_locals, destination, callee);
123 self.materialize_const_bytes_after_call(args, destination);
124 return;
125 }
126 if let Some(effect) =
127 crate::verify::call_summary::interprocedural::try_branch_effect(self.tcx, c)
128 {
129 self.apply_call_effect(&effect, &arg_values, &caller_arg_locals, destination, callee);
130 self.materialize_const_bytes_after_call(args, destination);
131 return;
132 }
133 if let Some(effect) =
134 crate::verify::call_summary::interprocedural::try_slice_bounded_return_effect(
135 self.tcx, c,
136 )
137 {
138 self.apply_call_effect(&effect, &arg_values, &caller_arg_locals, destination, callee);
139 self.materialize_const_bytes_after_call(args, destination);
140 return;
141 }
142 if let Some(effect) =
143 crate::verify::call_summary::interprocedural::try_decode_length_return_effect(
144 self.tcx, c,
145 )
146 {
147 self.apply_call_effect(&effect, &arg_values, &caller_arg_locals, destination, callee);
148 self.materialize_const_bytes_after_call(args, destination);
149 return;
150 }
151 let has_fn_sim = crate::verify::call_summary::builtin_models::lookup_effect(
152 self.tcx,
153 caller_def_id,
154 callee,
155 func,
156 destination,
157 )
158 .is_some();
159 if !has_fn_sim {
160 if self.exec_inline_call(c, &arg_values, &caller_arg_locals, destination) {
161 self.materialize_const_bytes_after_call(args, destination);
162 return;
163 }
164 }
165 }
166 }
167
168 let mut concrete = FxHashMap::default();
169 for (i, arg) in arg_values.iter().enumerate() {
170 if let Some(v) = arg.z3_term.simplify().as_u64() {
171 concrete.insert(i, v as i128);
172 }
173 }
174 let context = call_summary::CallContext { concrete };
175
176 let summary = call_summary::effect_summary(
177 self.tcx,
178 caller_def_id,
179 func,
180 destination,
181 &context,
182 );
183
184 if self.try_size_align_effect(func, destination) {
190 self.materialize_const_bytes_after_call(args, destination);
191 return;
192 }
193
194 if !summary.unsupported {
195 for effect in &summary.effects {
196 self.apply_call_effect(effect, &arg_values, &caller_arg_locals, destination, callee);
197 }
198 } else {
199 let dest_ty = self.body().local_decls[destination].ty;
200 let term = self.fresh_int(&format!("callret_{}", destination.as_usize()));
201 if let TyKind::Adt(adt_def, _) = dest_ty.kind() {
202 if api_classify::is_std_ordering(adt_def.did()) {
203 let minus_one = Int::from_i64(self.z3_ctx, -1);
204 let one = Int::from_i64(self.z3_ctx, 1);
205 self.constraints.assertions.push(term.ge(&minus_one));
206 self.constraints.assertions.push(term.le(&one));
207 }
208 }
209 if dest_ty.is_bool() {
211 let zero = Int::from_u64(self.z3_ctx, 0);
212 let one = Int::from_u64(self.z3_ctx, 1);
213 self.constraints.assertions.push(term.ge(&zero));
214 self.constraints.assertions.push(term.le(&one));
215 }
216 self.set_local(
217 destination,
218 VmValue::new(term, dest_ty),
219 );
220 }
221
222 self.materialize_const_bytes_after_call(args, destination);
223 }
224
225 fn try_slice_index(
232 &mut self,
233 callee: Option<DefId>,
234 arg_values: &[VmValue<'z3, 'tcx>],
235 args: &[Spanned<Operand<'tcx>>],
236 destination: Local,
237 ) -> bool {
238 let is_index =
239 callee.is_some_and(|c| crate::helpers::mir_utils::is_index_method(self.tcx, c));
240 if !is_index || arg_values.len() < 2 {
241 return false;
242 }
243 let dest_ty = self.body().local_decls[destination].ty;
244 let is_slice = matches!(dest_ty.kind(), TyKind::Ref(_, inner, _)
245 if matches!(inner.kind(), TyKind::Slice(_)));
246 let range_kind = arg_values.get(1).and_then(|v| match v.ty.kind() {
252 TyKind::Adt(adt_def, _) => Some(crate::helpers::mir_utils::range_kind(
253 self.tcx,
254 adt_def.did(),
255 )),
256 _ => None,
257 });
258 if !is_slice && range_kind.is_none() {
259 return false;
260 }
261 let Some(prov) = arg_values[0].provenance.clone() else {
262 return false;
263 };
264 let array_term = arg_values[0].z3_term.clone();
265 let (elem_ty, elem_size) = match arg_values[0].ty.kind() {
266 TyKind::Ref(_, inner, _) => match inner.kind() {
267 TyKind::Array(e, _) | TyKind::Slice(e) => (*e, self.size_of_ty(*e).max(1)),
268 _ => (arg_values[0].ty, 1),
269 },
270 _ => (arg_values[0].ty, 1),
271 };
272 let elem_align = self.align_sym(elem_ty);
273 let range_local = args.get(1).and_then(|a| match &a.node {
281 Operand::Copy(p) | Operand::Move(p) => Some(p.local),
282 _ => None,
283 });
284 let range_field = |idx: usize| -> Option<Int<'z3>> {
285 range_local.and_then(|l| self.field_value(l, &[idx]).map(|v| v.z3_term.clone()))
286 };
287 let zero = Int::from_u64(self.z3_ctx, 0);
288 let one = Int::from_u64(self.z3_ctx, 1);
289 let total_len = self
290 .alloc(prov.alloc_id)
291 .size
292 .clone()
293 .div(&Int::from_u64(self.z3_ctx, elem_size));
294 let (start, len) = match range_kind {
295 Some(crate::helpers::mir_utils::RangeKind::RangeTo) => (
296 zero.clone(),
297 range_field(0).unwrap_or_else(|| total_len.clone()),
298 ),
299 Some(crate::helpers::mir_utils::RangeKind::RangeFrom) => {
300 let s = range_field(0).unwrap_or_else(|| zero.clone());
301 (s.clone(), Int::sub(self.z3_ctx, &[&total_len, &s]))
302 }
303 Some(crate::helpers::mir_utils::RangeKind::Range) => {
304 let s = range_field(0).unwrap_or_else(|| zero.clone());
305 let e = range_field(1).unwrap_or_else(|| total_len.clone());
306 (s.clone(), Int::sub(self.z3_ctx, &[&e, &s]))
307 }
308 Some(crate::helpers::mir_utils::RangeKind::RangeInclusive) => {
309 let s = range_field(0).unwrap_or_else(|| zero.clone());
310 let e = range_field(1).unwrap_or_else(|| total_len.clone());
311 let l = Int::sub(self.z3_ctx, &[&e, &s]);
312 (s.clone(), Int::add(self.z3_ctx, &[&l, &one]))
313 }
314 _ => (zero.clone(), total_len.clone()),
315 };
316 let elem_size_term = Int::from_u64(self.z3_ctx, elem_size);
317 let start_bytes = if elem_size == 1 {
318 start.clone()
319 } else {
320 Int::mul(self.z3_ctx, &[&start, &elem_size_term])
321 };
322 let size_bytes = if elem_size == 1 {
323 len.clone()
324 } else {
325 Int::mul(self.z3_ctx, &[&len, &elem_size_term])
326 };
327 let dest_term = Int::add(self.z3_ctx, &[&array_term, &start_bytes]);
328 let (alloc_id, _) = self.allocate(size_bytes, elem_align, Some(elem_ty));
329 self.alloc_mut(alloc_id).parent = Some(prov.alloc_id);
330 self.set_local(
331 destination,
332 VmValue {
333 z3_term: dest_term,
334 ty: dest_ty,
335 provenance: Some(Provenance {
336 alloc_id,
337 offset: Int::from_u64(self.z3_ctx, 0),
338 offset_kind: None,
339 }),
340 facts: ValueFacts {
341 non_null: true,
342 init: true,
343 in_bounds: true,
344 ..Default::default()
345 },
346 source: ValueSource::None,
347 },
348 );
349 true
350 }
351
352 fn try_slice_get(
359 &mut self,
360 callee: Option<DefId>,
361 arg_values: &[VmValue<'z3, 'tcx>],
362 args: &[Spanned<Operand<'tcx>>],
363 destination: Local,
364 ) -> bool {
365 let Some(c) = callee else {
366 return false;
367 };
368 let Some(assoc) = self.tcx.opt_associated_item(c) else {
369 return false;
370 };
371 if !matches!(assoc.name().as_str(), "get" | "get_mut") || arg_values.len() < 2 {
372 return false;
373 }
374 let dest_ty = self.body().local_decls[destination].ty;
375 let TyKind::Adt(adt, substs) = dest_ty.kind() else {
376 return false;
377 };
378 if !self
379 .tcx
380 .is_diagnostic_item(rustc_span::sym::Option, adt.did())
381 {
382 return false;
383 }
384 let payload_ty = substs.type_at(0);
385 let TyKind::Ref(_, slice_ty, _) = payload_ty.kind() else {
386 return false;
387 };
388 if !matches!(slice_ty.kind(), TyKind::Slice(_)) {
389 return false;
390 }
391 let Some(prov) = arg_values[0].provenance.clone() else {
392 return false;
393 };
394 let array_term = arg_values[0].z3_term.clone();
395 let (elem_ty, elem_size) = match arg_values[0].ty.kind() {
396 TyKind::Ref(_, inner, _) => match inner.kind() {
397 TyKind::Array(e, _) | TyKind::Slice(e) => (*e, self.size_of_ty(*e).max(1)),
398 _ => (arg_values[0].ty, 1),
399 },
400 _ => (arg_values[0].ty, 1),
401 };
402 let elem_align = self.align_sym(elem_ty);
403 let range_local = args.get(1).and_then(|a| match &a.node {
404 Operand::Copy(p) | Operand::Move(p) => Some(p.local),
405 _ => None,
406 });
407 let range_field = |idx: usize| -> Option<Int<'z3>> {
408 range_local.and_then(|l| self.field_value(l, &[idx]).map(|v| v.z3_term.clone()))
409 };
410 let zero = Int::from_u64(self.z3_ctx, 0);
411 let one = Int::from_u64(self.z3_ctx, 1);
412 let total_len = self
413 .alloc(prov.alloc_id)
414 .size
415 .clone()
416 .div(&Int::from_u64(self.z3_ctx, elem_size));
417 let range_kind = arg_values.get(1).and_then(|v| match v.ty.kind() {
418 TyKind::Adt(adt_def, _) => Some(crate::helpers::mir_utils::range_kind(
419 self.tcx,
420 adt_def.did(),
421 )),
422 _ => None,
423 });
424 let (start, len) = match range_kind {
425 Some(crate::helpers::mir_utils::RangeKind::RangeTo) => (
426 zero.clone(),
427 range_field(0).unwrap_or_else(|| total_len.clone()),
428 ),
429 Some(crate::helpers::mir_utils::RangeKind::RangeFrom) => {
430 let s = range_field(0).unwrap_or_else(|| zero.clone());
431 (s.clone(), Int::sub(self.z3_ctx, &[&total_len, &s]))
432 }
433 Some(crate::helpers::mir_utils::RangeKind::Range) => {
434 let s = range_field(0).unwrap_or_else(|| zero.clone());
435 let e = range_field(1).unwrap_or_else(|| total_len.clone());
436 (s.clone(), Int::sub(self.z3_ctx, &[&e, &s]))
437 }
438 Some(crate::helpers::mir_utils::RangeKind::RangeInclusive) => {
439 let s = range_field(0).unwrap_or_else(|| zero.clone());
440 let e = range_field(1).unwrap_or_else(|| total_len.clone());
441 let l = Int::sub(self.z3_ctx, &[&e, &s]);
442 (s.clone(), Int::add(self.z3_ctx, &[&l, &one]))
443 }
444 _ => (zero.clone(), total_len.clone()),
445 };
446 let elem_size_term = Int::from_u64(self.z3_ctx, elem_size);
447 let start_bytes = if elem_size == 1 {
448 start.clone()
449 } else {
450 Int::mul(self.z3_ctx, &[&start, &elem_size_term])
451 };
452 let size_bytes = if elem_size == 1 {
453 len.clone()
454 } else {
455 Int::mul(self.z3_ctx, &[&len, &elem_size_term])
456 };
457 let dest_term = Int::add(self.z3_ctx, &[&array_term, &start_bytes]);
458 let (alloc_id, _) = self.allocate(size_bytes, elem_align, Some(elem_ty));
459 self.alloc_mut(alloc_id).parent = Some(prov.alloc_id);
460 self.set_field_value(
461 destination,
462 vec![0],
463 VmValue {
464 z3_term: dest_term,
465 ty: payload_ty,
466 provenance: Some(Provenance {
467 alloc_id,
468 offset: Int::from_u64(self.z3_ctx, 0),
469 offset_kind: None,
470 }),
471 facts: ValueFacts {
472 non_null: true,
473 init: true,
474 in_bounds: true,
475 ..Default::default()
476 },
477 source: ValueSource::None,
478 },
479 );
480 true
481 }
482
483 fn try_iter_len_is_empty(
488 &mut self,
489 name: &str,
490 arg_values: &[VmValue<'z3, 'tcx>],
491 args: &[Spanned<Operand<'tcx>>],
492 destination: Local,
493 ) -> bool {
494 if !((name.contains("::Iter<")
495 || name.contains("::IterMut<")
496 || name.ends_with("::Iter::len")
497 || name.ends_with("::IterMut::len")
498 || name.ends_with("::Iter::is_empty")
499 || name.ends_with("::IterMut::is_empty"))
500 && (name.ends_with("::len") || name.ends_with("::is_empty"))
501 && arg_values.len() >= 1)
502 {
503 return false;
504 }
505 let receiver_local = args.first().and_then(|a| a.node.place()).map(|p| p.local);
506 let Some(local) = receiver_local else {
507 return false;
508 };
509 let (Some(ptr), Some(end)) = (self.field_value(local, &[0]), self.field_value(local, &[1]))
512 else {
513 return false;
514 };
515 let (Some(pp), Some(ep)) = (&ptr.provenance, &end.provenance) else {
516 return false;
517 };
518 if pp.alloc_id != ep.alloc_id {
519 return false;
520 }
521 let dest_ty = self.body().local_decls[destination].ty;
522 if name.ends_with("::len") {
523 let diff = Int::sub(self.z3_ctx, &[&ep.offset, &pp.offset]);
524 let sz = self.iter_elem_size(ptr);
525 let val = VmValue::new(diff.div(&sz), dest_ty);
526 self.set_local(destination, val);
527 } else {
528 let eq = pp.offset._eq(&ep.offset);
530 let zero = Int::from_u64(self.z3_ctx, 0);
531 let one = Int::from_u64(self.z3_ctx, 1);
532 let val = VmValue {
533 z3_term: eq.ite(&one, &zero),
534 ty: dest_ty,
535 provenance: None,
536 facts: ValueFacts::default(),
537 source: ValueSource::None,
538 };
539 self.set_local(destination, val);
540 }
541 true
542 }
543
544 fn try_nonnull_new(
552 &mut self,
553 callee: Option<DefId>,
554 arg_values: &[VmValue<'z3, 'tcx>],
555 destination: Local,
556 ) -> bool {
557 if !api_classify::is_nonnull_checked_new(callee) {
558 return false;
559 }
560 let Some(ptr) = arg_values.first() else {
561 return false;
562 };
563 let dest_ty = self.body().local_decls[destination].ty;
564 let definitely_non_null = ptr.facts.non_null
565 || ptr.facts.in_bounds
566 || ptr
567 .provenance
568 .as_ref()
569 .is_some_and(|p| !self.alloc(p.alloc_id).is_external());
570 if definitely_non_null {
571 let mut val = ptr.clone();
573 val.ty = dest_ty;
574 val.facts.non_null = true;
575 let zero = Int::from_u64(self.z3_ctx, 0);
576 self.constraints.assertions.push(ptr.z3_term._eq(&zero).not());
577 self.set_local(destination, val);
578 } else {
579 let term = self.fresh_int(&format!("nn_new_{}", destination.as_usize()));
581 self.set_local(
582 destination,
583 VmValue::new(term, dest_ty),
584 );
585 }
586 true
587 }
588
589 fn try_iter_next(
594 &mut self,
595 name: &str,
596 arg_values: &[VmValue<'z3, 'tcx>],
597 destination: Local,
598 ) -> bool {
599 let is_next = name.ends_with("::next")
600 && (name.starts_with("Iter::")
601 || name.starts_with("IterMut::")
602 || name.contains("::Iter::")
603 || name.contains("::IterMut::")
604 || name.contains("::Iter<")
605 || name.contains("::IterMut<")
606 || name.contains("::Iterator::next"));
607 if !is_next || arg_values.len() < 1 {
608 return false;
609 }
610 let self_val = &arg_values[0];
611 let Some(local) = self.find_iter_self_local(self_val) else {
612 return false;
613 };
614 let (Some(ptr), Some(end)) = (self.field_value(local, &[0]), self.field_value(local, &[1]))
615 else {
616 return false;
617 };
618 let (Some(pp), Some(ep)) = (&ptr.provenance, &end.provenance) else {
619 return false;
620 };
621 if pp.alloc_id != ep.alloc_id {
622 return false;
623 }
624 let buffer = ep.alloc_id;
625 let ep_elem = match &ep.offset_kind {
626 Some(OffsetKind::Element(e)) => Some(e.clone()),
627 _ => None,
628 };
629 let dest_ty = self.body().local_decls[destination].ty;
630 let sz = self.iter_elem_size(ptr);
632 let ep_offset = ep.offset.clone();
633 let remaining = if let Some((off, _)) = self.constraints.term_caches.iter_ptr_offset.get(&buffer) {
634 let base_len = ep_offset.div(&sz);
635 let zero = Int::from_u64(self.z3_ctx, 0);
636 off.gt(&base_len)
637 .ite(&zero, &Int::sub(self.z3_ctx, &[&base_len, off]))
638 } else {
639 let diff = Int::sub(self.z3_ctx, &[&ep_offset, &pp.offset]);
640 diff.div(&sz)
641 };
642 let is_empty = remaining._eq(&Int::from_u64(self.z3_ctx, 0));
643 let zero = Int::from_u64(self.z3_ctx, 0);
647 let cur_off = match self.constraints.term_caches.iter_ptr_offset.get(&buffer) {
648 Some((prev, _)) => Int::mul(self.z3_ctx, &[prev, &sz]),
649 None => pp.offset.clone(),
650 };
651 let old_ptr_val = VmValue {
652 z3_term: cur_off.clone(),
653 ty: ptr.ty,
654 provenance: Some(Provenance {
655 alloc_id: pp.alloc_id,
656 offset: cur_off,
657 offset_kind: None,
658 }),
659 facts: ValueFacts {
660 non_null: true,
661 init: true,
662 ..Default::default()
663 },
664 source: ValueSource::None,
665 };
666 let one_term = Int::from_u64(self.z3_ctx, 1);
668 let (new_offset, base_len_elem) = match self.constraints.term_caches.iter_ptr_offset.get(&buffer) {
669 Some((prev, base)) => (Int::add(self.z3_ctx, &[prev, &one_term]), base.clone()),
670 None => (one_term.clone(), ep_elem),
671 };
672 self.constraints.assertions.push(remaining.gt(&zero));
674 let base_len = ep_offset.div(&sz);
676 self.constraints.assertions.push(new_offset.le(&base_len));
677 self.constraints
678 .term_caches
679 .iter_ptr_offset
680 .insert(buffer, (new_offset, base_len_elem));
681 let result_val = VmValue {
683 z3_term: is_empty.ite(&zero, &old_ptr_val.z3_term),
684 ty: dest_ty,
685 provenance: if is_empty.as_bool().unwrap_or(false) {
686 None
687 } else {
688 old_ptr_val.provenance.clone()
689 },
690 facts: ValueFacts::default(),
691 source: ValueSource::Discriminant(is_empty.ite(&zero, &one_term)),
695 };
696 self.set_local(destination, result_val);
697 true
698 }
699
700 fn materialize_const_bytes_after_call(
701 &mut self,
702 args: &[Spanned<Operand<'tcx>>],
703 destination: Local,
704 ) {
705 if let Some(mut dv) = self.local_value(destination).cloned() {
706 let dest_ty = dv.ty;
707 let pointee_is_byte_like = match dest_ty.kind() {
708 rustc_middle::ty::TyKind::RawPtr(inner, _)
709 | rustc_middle::ty::TyKind::Ref(_, inner, _) => match inner.kind() {
710 rustc_middle::ty::TyKind::Uint(rustc_middle::ty::UintTy::U8)
711 | rustc_middle::ty::TyKind::Int(rustc_middle::ty::IntTy::I8) => true,
712 rustc_middle::ty::TyKind::Array(elem_ty, _)
713 | rustc_middle::ty::TyKind::Slice(elem_ty) => {
714 matches!(
715 elem_ty.kind(),
716 rustc_middle::ty::TyKind::Uint(rustc_middle::ty::UintTy::U8)
717 )
718 }
719 _ => false,
720 },
721 _ => false,
722 };
723 if pointee_is_byte_like {
724 for arg in args {
725 self.try_materialize_const_bytes(&mut dv, &arg.node);
726 if dv.is_pointer() {
727 self.set_local(destination, dv);
728 break;
729 }
730 }
731 }
732 }
733 }
734
735 fn exec_inline_call(
743 &mut self,
744 callee_def_id: DefId,
745 arg_values: &[VmValue<'z3, 'tcx>],
746 caller_arg_locals: &[Option<Local>],
747 dest: Local,
748 ) -> bool {
749 if self.inline.inline_depth >= MAX_INLINE_DEPTH {
750 return false;
751 }
752 self.inline.inline_depth += 1;
753
754 let callee_body = self.tcx.optimized_mir(callee_def_id);
765 let n_return = callee_body
766 .basic_blocks
767 .iter()
768 .filter(|bb| {
769 matches!(
770 bb.terminator().kind,
771 rustc_middle::mir::TerminatorKind::Return
772 )
773 })
774 .count();
775 let has_switch = callee_body.basic_blocks.iter_enumerated().any(|(idx, bb)| {
784 !bb.is_cleanup
785 && matches!(
786 bb.terminator().kind,
787 rustc_middle::mir::TerminatorKind::SwitchInt { .. }
788 )
789 && !crate::helpers::mir_utils::switch_is_debug_assert(self.tcx, callee_body, idx)
790 });
791 if arg_values.len() > 4 || n_return > 1 || has_switch
792 {
793 self.inline.inline_depth -= 1;
794 return false;
795 }
796
797 let inline_arg_referents: Vec<Option<Local>> = arg_values
803 .iter()
804 .map(|v| self.find_local_by_address(&v.z3_term))
805 .collect();
806 let reborrow_referents: Vec<Option<Local>> = caller_arg_locals
810 .iter()
811 .map(|arg_opt| arg_opt.and_then(|a| self.find_whole_reborrow_referent(a)))
812 .collect();
813 let frame = self.save_frame();
814 let saved_inline_arg_referents =
815 std::mem::replace(&mut self.inline.arg_referents, inline_arg_referents);
816 let saved_deferred_field_writes = std::mem::take(&mut self.inline.deferred_field_writes);
817
818 self.current_frame.current_def_id = callee_def_id;
820
821 for (i, arg_val) in arg_values.iter().enumerate() {
823 let callee_local = Local::from_usize(i + 1);
824 self.ensure_local_allocation(callee_local);
825 self.set_local(callee_local, arg_val.clone());
826 }
827
828 for (i, caller_arg_opt) in caller_arg_locals.iter().enumerate() {
832 let callee_param = Local::from_usize(i + 1);
833 let Some(caller_arg) = caller_arg_opt else {
834 continue;
835 };
836 let mut source_locals = vec![*caller_arg];
841 if let Some(r) = reborrow_referents.get(i).copied().flatten() {
842 if r != *caller_arg {
843 source_locals.push(r);
844 }
845 }
846 for src in source_locals {
847 let caller_field_keys: Vec<Vec<usize>> = self.frame_field_paths(&frame, src);
848 for fields in caller_field_keys {
849 if let Some(fv) = self.frame_field_value(&frame, src, &fields).cloned() {
850 self.set_field_value(callee_param, fields, fv);
851 }
852 }
853 }
854 }
855
856 self.inline_execute_body();
858
859 let return_val = self.local_value(Local::from_usize(0)).cloned();
861 crate::rap_debug!(
862 "exec_inline_call: callee={:?} return_val={:?}",
863 callee_def_id,
864 return_val
865 .as_ref()
866 .map(|v| (v.z3_term.to_string(), v.facts.non_null))
867 );
868 let return_fields: Vec<(Vec<usize>, VmValue<'z3, 'tcx>)> = self
869 .field_paths(Local::from_usize(0))
870 .into_iter()
871 .filter_map(|path| {
872 self.field_value(Local::from_usize(0), &path)
873 .cloned()
874 .map(|val| (path, val))
875 })
876 .collect();
877
878 self.restore_frame(frame);
880
881 for (local, path, value) in std::mem::take(&mut self.inline.deferred_field_writes) {
885 self.set_field_value(local, path, value);
886 }
887 self.inline.arg_referents = saved_inline_arg_referents;
888 self.inline.deferred_field_writes = saved_deferred_field_writes;
889
890 let dest_ty = self.body().local_decls[dest].ty;
892 match return_val {
893 Some(mut val) => {
894 val.ty = dest_ty;
895 let at_base = val
898 .provenance
899 .as_ref()
900 .is_some_and(|p| p.offset.as_u64() == Some(0));
901 if at_base {
902 val.facts.non_null = true;
903 self.mark_initialized(&mut val);
904 }
905 self.set_local(dest, val);
906 for (path, fv) in return_fields {
910 self.set_field_value(dest, path, fv);
911 }
912 if let Some(dest_alloc_id) = self.current_frame.local_alloc.get(&dest).copied() {
918 self.content_mut(dest_alloc_id).facts.initialized = true;
919 }
920 }
921 None => {
922 self.inline.inline_depth -= 1;
923 return false;
924 }
925 }
926
927 self.inline.inline_depth -= 1;
928 true
929 }
930
931 fn switch_discr_const(
934 body: &rustc_middle::mir::Body<'tcx>,
935 discr: &Operand<'tcx>,
936 ) -> Option<u64> {
937 if let Some(v) = crate::helpers::mir_utils::operand_const_u64(discr) {
938 return Some(v);
939 }
940 let (Operand::Copy(p) | Operand::Move(p)) = discr else {
941 return None;
942 };
943 for bbd in body.basic_blocks.iter() {
944 for stmt in bbd.statements.iter() {
945 let rustc_middle::mir::StatementKind::Assign(assign) = &stmt.kind else {
946 continue;
947 };
948 let (dest, rvalue) = &**assign;
949 if dest != p {
950 continue;
951 }
952 return crate::helpers::mir_utils::rvalue_runtime_checks_value(rvalue);
953 }
954 }
955 None
956 }
957
958 fn inline_execute_body(&mut self) {
960 let mut visited = FxHashSet::default();
961 let mut queue: Vec<BasicBlock> = Vec::new();
962 queue.push(BasicBlock::from_usize(0));
963
964 while let Some(block) = queue.pop() {
965 if !visited.insert(block) {
966 continue;
967 }
968
969 let bb_data = &self.body().basic_blocks[block];
970
971 for stmt in bb_data.statements.iter() {
973 self.exec_statement(stmt);
974 }
975
976 let terminator = bb_data.terminator();
978
979 match &terminator.kind {
980 TerminatorKind::Goto { target } => {
981 queue.push(*target);
982 }
983 TerminatorKind::Return => {
984 }
986 TerminatorKind::Assert {
987 cond,
988 expected,
989 target,
990 ..
991 } => {
992 let cond_val = self.value_of_operand(cond);
993 if *expected {
994 let zero = Int::from_u64(self.z3_ctx, 0);
995 self.constraints.assertions.push(cond_val.z3_term._eq(&zero).not());
996 } else {
997 let zero = Int::from_u64(self.z3_ctx, 0);
998 self.constraints.assertions.push(cond_val.z3_term._eq(&zero));
999 }
1000 self.infer_guard_non_null(cond, *expected);
1002 self.infer_guard_align(cond, *expected);
1003 queue.push(*target);
1004 }
1005 TerminatorKind::SwitchInt { discr, targets } => {
1006 if let Some(v) = Self::switch_discr_const(self.body(), discr) {
1008 let t = targets
1009 .iter()
1010 .find(|(val, _)| *val == v as u128)
1011 .map(|(_, t)| t)
1012 .unwrap_or_else(|| targets.otherwise());
1013 queue.push(t);
1014 continue;
1015 }
1016 let trivial = crate::helpers::mir_utils::switch_targets_unreachable(
1020 self.tcx,
1021 self.body(),
1022 targets,
1023 );
1024 if trivial {
1025 queue.push(targets.otherwise());
1026 continue;
1027 }
1028 for (value, target) in targets.iter() {
1032 let discr_val = self.value_of_operand(discr);
1033 let val_term = Int::from_u64(self.z3_ctx, value as u64);
1034 self.constraints.assertions.push(discr_val.z3_term._eq(&val_term));
1035 queue.push(target);
1036 }
1037 let otherwise = targets.otherwise();
1038 queue.push(otherwise);
1039 }
1040 TerminatorKind::Call {
1041 func,
1042 args,
1043 destination,
1044 target,
1045 ..
1046 } => {
1047 self.exec_call(
1048 func,
1049 args,
1050 destination.local,
1051 self.current_frame.current_def_id,
1052 );
1053 if let Some(t) = target {
1054 queue.push(*t);
1055 }
1056 }
1057 TerminatorKind::Drop { place, target, .. } => {
1058 self.exec_drop(place);
1059 queue.push(*target);
1060 }
1061 TerminatorKind::Unreachable
1062 | TerminatorKind::UnwindResume
1063 | TerminatorKind::UnwindTerminate(_)
1064 | TerminatorKind::Yield { .. }
1065 | TerminatorKind::CoroutineDrop
1066 | TerminatorKind::FalseEdge { .. }
1067 | TerminatorKind::FalseUnwind { .. }
1068 | TerminatorKind::InlineAsm { .. }
1069 | TerminatorKind::TailCall { .. } => {
1070 }
1072 }
1073 }
1074 }
1075
1076 fn set_dest_as_heap_ptr(&mut self, arg_val: &VmValue<'z3, 'tcx>, dest: Local) {
1079 let mut val = arg_val.clone();
1080 val.ty = self.body().local_decls[dest].ty;
1081 val.facts.non_null = true;
1082 val.facts.init = true;
1083 self.set_local(dest, val);
1084 }
1085
1086 fn try_size_align_effect(&mut self, func: &Operand<'tcx>, destination: Local) -> bool {
1091 let Some(ty) = crate::helpers::mir_utils::fn_def_first_type_arg(func) else {
1092 return false;
1093 };
1094 let Some(callee) = crate::helpers::mir_utils::dep_callee_def_id(func) else {
1095 return false;
1096 };
1097 let is_size = crate::def_id::contains(
1098 &[
1099 crate::def_id::mem_size_of(),
1100 crate::def_id::intrinsics_size_of(),
1101 ],
1102 callee,
1103 );
1104 let is_align = crate::def_id::contains(
1105 &[
1106 crate::def_id::mem_align_of(),
1107 crate::def_id::intrinsics_align_of(),
1108 ],
1109 callee,
1110 );
1111 if !is_size && !is_align {
1112 return false;
1113 }
1114 if crate::helpers::mir_utils::type_layout(self.tcx, self.current_frame.current_def_id, ty)
1119 .is_some_and(|(align, _)| align > 0)
1120 {
1121 return false;
1122 }
1123 let term = if is_size {
1124 self.size_sym(ty)
1125 } else {
1126 self.align_sym(ty)
1127 };
1128 let dest_ty = self.body().local_decls[destination].ty;
1129 self.set_local(
1130 destination,
1131 VmValue::new(term, dest_ty),
1132 );
1133 true
1134 }
1135
1136 fn apply_binary_num(
1139 &mut self,
1140 dest: Local,
1141 args: &[VmValue<'z3, 'tcx>],
1142 lhs_arg: usize,
1143 rhs_arg: usize,
1144 f: impl Fn(&Int<'z3>, &Int<'z3>) -> Int<'z3>,
1145 ) {
1146 if let (Some(lhs), Some(rhs)) = (args.get(lhs_arg), args.get(rhs_arg)) {
1147 let dest_ty = self.body().local_decls[dest].ty;
1148 self.set_local(dest, VmValue::new(f(&lhs.z3_term, &rhs.z3_term), dest_ty));
1149 }
1150 }
1151
1152 fn apply_unary_num(
1155 &mut self,
1156 dest: Local,
1157 args: &[VmValue<'z3, 'tcx>],
1158 arg: usize,
1159 f: impl Fn(&Int<'z3>) -> Int<'z3>,
1160 ) {
1161 if let Some(a) = args.get(arg) {
1162 let dest_ty = self.body().local_decls[dest].ty;
1163 self.set_local(dest, VmValue::new(f(&a.z3_term), dest_ty));
1164 }
1165 }
1166
1167 pub(crate) fn apply_call_effect(
1169 &mut self,
1170 effect: &CallEffect,
1171 args: &[VmValue<'z3, 'tcx>],
1172 caller_arg_locals: &[Option<Local>],
1173 dest: Local,
1174 callee: Option<DefId>,
1175 ) {
1176 match effect {
1177 CallEffect::ReturnAliasArg { arg } => {
1178 if let Some(arg_val) = args.get(*arg) {
1179 self.set_dest_as_heap_ptr(arg_val, dest);
1180 }
1181 }
1182 CallEffect::SelectUnpredictable => {
1183 if args.len() >= 3 {
1184 let term = self.fresh_int(&format!("selunpred_{}", dest.as_usize()));
1185 let dest_ty = self.body().local_decls[dest].ty;
1186 let eq1 = term._eq(&args[1].z3_term);
1187 let eq2 = term._eq(&args[2].z3_term);
1188 self.constraints.assertions.push(Bool::or(self.z3_ctx, &[&eq1, &eq2]));
1189 let prov = args[1]
1190 .provenance
1191 .clone()
1192 .or_else(|| args[2].provenance.clone());
1193 self.set_local(
1194 dest,
1195 VmValue {
1196 z3_term: term,
1197 ty: dest_ty,
1198 provenance: prov,
1199 facts: ValueFacts::default(),
1200 source: ValueSource::None,
1201 },
1202 );
1203 }
1204 }
1205 CallEffect::ReturnDerefArg { arg } => {
1206 let dest_ty = self.body().local_decls[dest].ty;
1216 let mut val = args.get(*arg).cloned().unwrap_or_else(|| VmValue::new(self.fresh_int("replaced"), dest_ty));
1217 if let Some(search) = self
1226 .units
1227 .iter()
1228 .flat_map(|u| u.content.values.values())
1229 .find(|v| v.ty == dest_ty && v.is_pointer())
1230 .cloned()
1231 {
1232 val = search;
1233 } else if let Some(elem) = crate::helpers::mir_utils::pointee_ty(dest_ty) {
1234 let is_slice = matches!(elem.kind(), rustc_middle::ty::TyKind::Slice(_));
1235 if is_slice {
1236 let elem_align = self.align_sym(elem);
1237 let (alloc_id, base) = self.allocate_external(
1238 Int::from_u64(self.z3_ctx, i64::MAX as u64),
1239 elem_align,
1240 Some(elem),
1241 );
1242 val = VmValue {
1243 z3_term: base,
1244 ty: dest_ty,
1245 provenance: Some(Provenance {
1246 alloc_id,
1247 offset: Int::from_u64(self.z3_ctx, 0),
1248 offset_kind: None,
1249 }),
1250 facts: ValueFacts::default(),
1251 source: ValueSource::None,
1252 };
1253 }
1254 }
1255 val.ty = dest_ty;
1256 self.set_local(dest, val);
1257 }
1258 CallEffect::ReturnTransparentDeref { arg, peel } => {
1259 if let Some(arg_val) = args.get(*arg) {
1260 self.set_dest_as_heap_ptr(arg_val, dest);
1261 if let Some(arg_local) = caller_arg_locals.get(*arg).copied().flatten() {
1265 let keys: Vec<Vec<usize>> = self.field_paths(arg_local);
1266 for path in keys {
1267 if path.len() > *peel && path[..*peel].iter().all(|&f| f == 0) {
1268 if let Some(v) = self.field_value(arg_local, &path).cloned() {
1269 self.set_field_value(dest, path[*peel..].to_vec(), v);
1270 }
1271 }
1272 }
1273 }
1274 }
1275 }
1276 CallEffect::ReturnTupleFieldLength {
1277 field: _field,
1278 from_arg: _from_arg,
1279 } => {
1280 if args.len() < 2 {
1281 return;
1282 }
1283 let self_val = &args[0]; let mid_val = &args[1]; let dest_ty = self.body().local_decls[dest].ty;
1287 if let TyKind::Tuple(elem_tys) = dest_ty.kind() {
1288 let src_alloc_id = self_val.provenance.as_ref().map(|p| p.alloc_id);
1290
1291 let (elem_ty, elem_sz_term, alloc_size) = src_alloc_id
1292 .map(|id| self.alloc(id))
1293 .map(|a| {
1294 let ty = a.element_ty.as_ty();
1295 let sz_term = self.size_sym_read(ty.unwrap_or(self_val.ty));
1296 (ty, sz_term, a.size.clone())
1297 })
1298 .unwrap_or_else(|| {
1299 let pointee = crate::helpers::mir_utils::pointee_ty(self_val.ty);
1304 let sz = self.size_sym_read(pointee.unwrap_or(self_val.ty));
1305 (pointee, sz, Int::from_u64(self.z3_ctx, 1))
1306 });
1307
1308 let total_len = self
1309 .slice_len_from_value(self_val)
1310 .unwrap_or_else(|| alloc_size.div(&elem_sz_term)); let zero = Int::from_u64(self.z3_ctx, 0);
1313 self.constraints.assertions.push(mid_val.z3_term.ge(&zero));
1314 self.constraints.assertions.push(mid_val.z3_term.le(&total_len));
1315
1316 let mid = mid_val.z3_term.clone();
1318 let rest_len = Int::sub(self.z3_ctx, &[&total_len, &mid]);
1320
1321 let mid_bytes = Int::mul(self.z3_ctx, &[&mid, &elem_sz_term]);
1323 let ptr1 = Int::add(self.z3_ctx, &[&self_val.z3_term, &mid_bytes]);
1324
1325 for f in 0..elem_tys.len() {
1326 let field_ty = elem_tys[f];
1327 let (field_len, field_ptr) = if f == 0 {
1328 (mid.clone(), self_val.z3_term.clone())
1329 } else {
1330 (rest_len.clone(), ptr1.clone())
1331 };
1332 let field_size = Int::mul(self.z3_ctx, &[&field_len, &elem_sz_term]);
1333 let field_alloc_align = self_val
1334 .provenance
1335 .as_ref()
1336 .map(|p| self.alloc(p.alloc_id).align.clone())
1337 .unwrap_or_else(|| Int::from_u64(self.z3_ctx, 1));
1338
1339 let (alloc_id, _base) = self.allocate_slice(
1340 field_len.clone(),
1341 elem_sz_term.clone(),
1342 field_alloc_align.clone(),
1343 elem_ty,
1344 );
1345 let src_bytes = Int::mul(self.z3_ctx, &[&total_len, &elem_sz_term]);
1346 if f == 0 {
1347 self.constraints.assertions.push(field_size._eq(&mid_bytes));
1348 } else {
1349 let remaining = Int::sub(self.z3_ctx, &[&src_bytes, &mid_bytes]);
1350 self.constraints.assertions.push(field_size._eq(&remaining));
1351 }
1352 self.content_mut(alloc_id).facts.initialized = true;
1353 if let Some(ref source_prov) = self_val.provenance {
1354 self.alloc_mut(alloc_id).parent = Some(source_prov.alloc_id);
1355 }
1356
1357 let field_offset = Int::from_u64(self.z3_ctx, 0);
1358
1359 let field_prov = Provenance {
1360 alloc_id,
1361 offset: field_offset,
1362 offset_kind: None,
1363 };
1364
1365 let field_val = VmValue {
1366 z3_term: field_ptr,
1367 ty: field_ty,
1368 provenance: Some(field_prov),
1369 facts: ValueFacts {
1370 init: true,
1371 non_null: true,
1372 in_bounds: true,
1373 align_n: Some(field_alloc_align),
1374 },
1375 source: ValueSource::None,
1376 };
1377 self.set_field_value(dest, vec![f], field_val);
1378 }
1379 }
1380 }
1381 CallEffect::ReturnIter { receiver_arg } => {
1382 let Some(self_val) = args.get(*receiver_arg).cloned() else {
1383 return;
1384 };
1385 let Some(src_prov) = self_val.provenance.clone() else {
1386 return;
1387 };
1388 let root_alloc_id = {
1394 let mut id = src_prov.alloc_id;
1395 while let Some(parent) = self.alloc(id).parent {
1396 id = parent;
1397 }
1398 id
1399 };
1400 let slice_len = self.alloc(src_prov.alloc_id).size.clone();
1401
1402 let field_ty = match self_val.ty.kind() {
1406 TyKind::Ref(_, inner, _) => match inner.kind() {
1407 TyKind::Slice(t) => *t,
1408 _ => self_val.ty,
1409 },
1410 _ => self_val.ty,
1411 };
1412
1413 let start_off = Int::from_u64(self.z3_ctx, 0);
1414 let end_term = Int::add(self.z3_ctx, &[&self_val.z3_term, &slice_len]);
1415
1416 let elem_align_n = {
1422 let a = self.align_sym(field_ty);
1423 (a.simplify().as_u64() != Some(1)).then_some(a)
1424 };
1425
1426 let start_val = VmValue {
1427 z3_term: self_val.z3_term.clone(),
1428 ty: field_ty,
1429 provenance: Some(Provenance {
1430 alloc_id: root_alloc_id,
1431 offset: start_off,
1432 offset_kind: None,
1433 }),
1434 facts: ValueFacts {
1435 init: true,
1436 non_null: true,
1437 align_n: elem_align_n.clone(),
1438 ..Default::default()
1439 },
1440 source: ValueSource::None,
1441 };
1442 let end_val = VmValue {
1443 z3_term: end_term,
1444 ty: field_ty,
1445 provenance: Some(Provenance {
1446 alloc_id: root_alloc_id,
1447 offset: slice_len,
1448 offset_kind: None,
1449 }),
1450 facts: ValueFacts {
1451 init: true,
1452 non_null: true,
1453 align_n: elem_align_n,
1454 ..Default::default()
1455 },
1456 source: ValueSource::None,
1457 };
1458 self.set_field_value(dest, vec![0], start_val);
1459 self.set_field_value(dest, vec![1], end_val);
1460 }
1461 CallEffect::ReturnRange { bounds_arg } => {
1462 self.apply_range_effect(*bounds_arg, args, caller_arg_locals, dest);
1463 }
1464 CallEffect::ReturnAlignTo { receiver_arg } => {
1465 let Some(self_val) = args.get(*receiver_arg).cloned() else {
1466 return;
1467 };
1468 let dest_ty = self.body().local_decls[dest].ty;
1469 let TyKind::Tuple(elem_tys) = dest_ty.kind() else {
1470 return;
1471 };
1472 if elem_tys.len() < 3 {
1473 return;
1474 }
1475
1476 let body_elem_ty = match elem_tys[1].kind() {
1478 TyKind::Ref(_, inner, _) => match inner.kind() {
1479 TyKind::Slice(u) => *u,
1480 _ => return,
1481 },
1482 _ => return,
1483 };
1484 let size_u = self.size_of_ty(body_elem_ty).max(1);
1485 let align_u = self.align_sym(body_elem_ty);
1486
1487 let Some(src_prov) = self_val.provenance.clone() else {
1488 return;
1489 };
1490 let alloc = self.alloc(src_prov.alloc_id);
1491 let (elem_ty, elem_sz, len_bytes) = {
1492 let ty = alloc.element_ty.as_ty();
1493 let sz = self.size_of_ty(ty.unwrap_or(self_val.ty)).max(1);
1494 (ty, sz, alloc.size.clone())
1495 };
1496
1497 let elem_sz_term = Int::from_u64(self.z3_ctx, elem_sz);
1498 let size_u_term = Int::from_u64(self.z3_ctx, size_u);
1499
1500 let offset = self.fresh_int(&format!("align_to_offset_{}", dest.as_usize()));
1503 let zero = Int::from_u64(self.z3_ctx, 0);
1504 let ptr_plus_offset = Int::add(self.z3_ctx, &[&self_val.z3_term, &offset]);
1505 self.constraints.assertions
1506 .push(ptr_plus_offset.rem(&align_u)._eq(&zero));
1507 self.constraints.assertions.push(offset.ge(&zero));
1508 self.constraints.assertions.push(offset.lt(&align_u));
1509
1510 let body_bytes = Int::sub(self.z3_ctx, &[&len_bytes, &offset]);
1515 let body_len = body_bytes.div(&size_u_term);
1516 let suffix_bytes = body_bytes.rem(&size_u_term);
1517 let mul_term = Int::mul(self.z3_ctx, &[&body_len, &size_u_term]);
1518 let sum_term = Int::add(self.z3_ctx, &[&mul_term, &suffix_bytes]);
1519 self.constraints.assertions.push(body_bytes._eq(&sum_term));
1520 self.constraints.assertions.push(suffix_bytes.ge(&zero));
1521 self.constraints.assertions.push(suffix_bytes.lt(&size_u_term));
1522
1523 let prefix_len = offset.div(&elem_sz_term);
1525 let suffix_len = suffix_bytes.div(&elem_sz_term);
1526
1527 let body_byte_len = Int::mul(self.z3_ctx, &[&body_len, &size_u_term]);
1528 let suffix_ptr = Int::add(self.z3_ctx, &[&ptr_plus_offset, &body_byte_len]);
1529
1530 let base_align = self.alloc(src_prov.alloc_id).align.clone();
1531
1532 let fields: Vec<(Int<'z3>, Int<'z3>, Ty<'tcx>, u64, Int<'z3>)> = vec![
1533 (
1534 prefix_len,
1535 self_val.z3_term.clone(),
1536 elem_tys[0],
1537 elem_sz,
1538 base_align.clone(),
1539 ),
1540 (body_len, ptr_plus_offset, elem_tys[1], size_u, align_u),
1541 (suffix_len, suffix_ptr, elem_tys[2], elem_sz, base_align),
1542 ];
1543
1544 for (f, (f_len, f_ptr, f_ty, f_elem_sz, f_align)) in fields.into_iter().enumerate()
1545 {
1546 let f_elem_ty = if f == 1 { Some(body_elem_ty) } else { elem_ty };
1547 let (alloc_id, _) = self.allocate_slice(
1548 f_len.clone(),
1549 Int::from_u64(self.z3_ctx, f_elem_sz),
1550 f_align.clone(),
1551 f_elem_ty,
1552 );
1553 self.content_mut(alloc_id).facts.initialized = true;
1554 self.alloc_mut(alloc_id).parent = Some(src_prov.alloc_id);
1555 let field_val = VmValue {
1556 z3_term: f_ptr,
1557 ty: f_ty,
1558 provenance: Some(Provenance {
1559 alloc_id,
1560 offset: Int::from_u64(self.z3_ctx, 0),
1561 offset_kind: None,
1562 }),
1563 facts: ValueFacts {
1564 init: true,
1565 non_null: true,
1566 in_bounds: true,
1567 align_n: if f_align.simplify().as_u64() != Some(1) {
1568 Some(f_align)
1569 } else {
1570 None
1571 },
1572 },
1573 source: ValueSource::None,
1574 };
1575 self.set_field_value(dest, vec![f], field_val);
1576 }
1577 }
1578 CallEffect::ReturnPointerFromArg { arg } => {
1579 if let Some(arg_val) = args.get(*arg) {
1580 let mut val = arg_val.clone();
1581 let dest_ty = self.body().local_decls[dest].ty;
1582 val.ty = dest_ty;
1583 let src_non_null = arg_val.facts.non_null
1589 || matches!(arg_val.ty.kind(), rustc_middle::ty::TyKind::Ref(..))
1590 || matches!(
1591 arg_val.ty.kind(),
1592 rustc_middle::ty::TyKind::Adt(adt, _)
1593 if api_classify::is_std_nonnull(adt.did())
1594 );
1595 val.facts.non_null = src_non_null;
1596 val.facts.align_n = arg_val.facts.align_n.clone();
1599 if matches!(dest_ty.kind(), rustc_middle::ty::TyKind::RawPtr(..)) {
1602 val.facts.init = true;
1603 }
1604 if let Some(ref prov) = val.provenance {
1608 if let Some(data_alloc) = self.data_alloc_of(prov.alloc_id, arg_val.ty) {
1609 val.z3_term = self.allocation_base(data_alloc).clone();
1610 val.provenance = Some(Provenance {
1611 alloc_id: data_alloc,
1612 offset: Int::from_u64(self.z3_ctx, 0),
1613 offset_kind: None,
1614 });
1615 }
1616 }
1617 if src_non_null {
1618 let zero = Int::from_u64(self.z3_ctx, 0);
1619 self.constraints.assertions.push(val.z3_term._eq(&zero).not());
1620 }
1621 self.set_local(dest, val);
1622 }
1623 }
1624 CallEffect::ReturnPointerAdd {
1625 base_arg,
1626 offset_arg,
1627 stride,
1628 dereferenceable,
1629 } => {
1630 let stride = *stride;
1631 if let (Some(base), Some(offset)) = (args.get(*base_arg), args.get(*offset_arg)) {
1632 let stride_term = self.pointer_stride_term(dest, stride);
1633 let adjusted_offset = if stride == Some(1) {
1634 offset.z3_term.clone()
1635 } else {
1636 Int::mul(self.z3_ctx, &[&offset.z3_term, &stride_term])
1637 };
1638 let new_term = Int::add(self.z3_ctx, &[&base.z3_term, &adjusted_offset]);
1639 let is_field_offset = offset.is_field_offset()
1640 && base
1641 .provenance
1642 .as_ref()
1643 .is_some_and(|p| p.offset.as_u64() == Some(0));
1644 let element_offset = if stride == Some(1) {
1645 None
1646 } else {
1647 match base.provenance.as_ref().and_then(|p| match &p.offset_kind {
1648 Some(OffsetKind::Element(e)) => Some(e.clone()),
1649 _ => None,
1650 }) {
1651 Some(e) => Some(Int::add(self.z3_ctx, &[&e, &offset.z3_term])),
1652 None if base
1653 .provenance
1654 .as_ref()
1655 .is_some_and(|p| p.offset.as_u64() == Some(0)) =>
1656 {
1657 Some(offset.z3_term.clone())
1658 }
1659 None => None,
1660 }
1661 };
1662 let offset_kind = if is_field_offset {
1663 Some(OffsetKind::Field)
1664 } else if let Some(e) = element_offset {
1665 Some(OffsetKind::Element(e))
1666 } else {
1667 Some(OffsetKind::Byte)
1668 };
1669 let adjusted_provenance = base.provenance.as_ref().map(|prov| Provenance {
1670 alloc_id: prov.alloc_id,
1671 offset: Int::add(self.z3_ctx, &[&prov.offset, &adjusted_offset]),
1672 offset_kind,
1673 });
1674 let align_n = match stride {
1675 Some(s) => self.compute_pointer_add_align(base, s),
1676 None => base.facts.align_n.clone(),
1677 };
1678 let val = VmValue {
1679 z3_term: new_term,
1680 ty: self.body().local_decls[dest].ty,
1681 provenance: adjusted_provenance,
1682 facts: ValueFacts {
1683 non_null: base.facts.non_null,
1684 in_bounds: *dereferenceable,
1685 align_n,
1686 init: base.facts.init,
1687 },
1688 source: ValueSource::None,
1689 };
1690 self.set_local(dest, val);
1691 }
1692 }
1693 CallEffect::ReturnPointerSub {
1694 base_arg,
1695 offset_arg,
1696 stride,
1697 } => {
1698 let stride = *stride;
1699 if let (Some(base), Some(offset)) = (args.get(*base_arg), args.get(*offset_arg)) {
1700 let stride_term = self.pointer_stride_term(dest, stride);
1701 let scaled = if stride == Some(1) {
1702 offset.z3_term.clone()
1703 } else {
1704 Int::mul(self.z3_ctx, &[&offset.z3_term, &stride_term])
1705 };
1706 let new_term = Int::sub(self.z3_ctx, &[&base.z3_term, &scaled]);
1707 let element_offset = if stride == Some(1) {
1708 None
1709 } else {
1710 match base.provenance.as_ref().and_then(|p| match &p.offset_kind {
1711 Some(OffsetKind::Element(e)) => Some(e.clone()),
1712 _ => None,
1713 }) {
1714 Some(e) => Some(Int::sub(self.z3_ctx, &[&e, &offset.z3_term])),
1715 None => None,
1716 }
1717 };
1718 let offset_kind = if let Some(e) = element_offset {
1719 Some(OffsetKind::Element(e))
1720 } else {
1721 Some(OffsetKind::Byte)
1722 };
1723 let adjusted_provenance = base.provenance.as_ref().map(|prov| Provenance {
1724 alloc_id: prov.alloc_id,
1725 offset: Int::sub(self.z3_ctx, &[&prov.offset, &scaled]),
1726 offset_kind,
1727 });
1728 let align_n = match stride {
1729 Some(s) => self.compute_pointer_add_align(base, s),
1730 None => base.facts.align_n.clone(),
1731 };
1732 let val = VmValue {
1733 z3_term: new_term,
1734 ty: self.body().local_decls[dest].ty,
1735 provenance: adjusted_provenance,
1736 facts: ValueFacts {
1737 non_null: base.facts.non_null,
1738 in_bounds: base.facts.in_bounds,
1739 align_n,
1740 init: base.facts.init,
1741 },
1742 source: ValueSource::None,
1743 };
1744 self.set_local(dest, val);
1745 }
1746 }
1747 CallEffect::ReturnNonZero => {
1748 let zero = Int::from_u64(self.z3_ctx, 0);
1749 if let Some(mut existing) = self.local_value(dest).cloned() {
1750 existing.facts.non_null = true;
1751 self.constraints.assertions.push(existing.z3_term._eq(&zero).not());
1756 self.set_local(dest, existing);
1757 } else {
1758 let dest_ty = self.body().local_decls[dest].ty;
1759 let term = self.fresh_int(&format!("ret_nz_{}", dest.as_usize()));
1760 self.constraints.assertions.push(term._eq(&zero).not());
1761 self.set_local(
1762 dest,
1763 VmValue {
1764 z3_term: term,
1765 ty: dest_ty,
1766 provenance: None,
1767 facts: ValueFacts {
1768 non_null: true,
1769 ..Default::default()
1770 },
1771 source: ValueSource::None,
1772 },
1773 );
1774 }
1775 }
1776 CallEffect::ReturnTupleFieldNonZero { field } => {
1777 let dest_ty = self.body().local_decls[dest].ty;
1778 if let TyKind::Tuple(elem_tys) = dest_ty.kind() {
1779 if let Some(field_ty) = elem_tys.get(*field) {
1780 let zero = Int::from_u64(self.z3_ctx, 0);
1781 let term =
1782 self.fresh_int(&format!("ret_tup_nz_{}_{}", dest.as_usize(), field));
1783 self.constraints.assertions.push(term._eq(&zero).not());
1784 self.set_field_value(
1785 dest,
1786 vec![*field],
1787 VmValue {
1788 z3_term: term,
1789 ty: *field_ty,
1790 provenance: None,
1791 facts: ValueFacts {
1792 non_null: true,
1793 init: true,
1794 ..Default::default()
1795 },
1796 source: ValueSource::None,
1797 },
1798 );
1799 }
1800 }
1801 }
1802 CallEffect::ReturnAligned => {
1803 if let Some(mut existing) = self.local_value(dest).cloned() {
1804 if existing.facts.align_n.is_none() {
1808 let dest_ty = self.body().local_decls[dest].ty;
1809 if let Some(pointee) = crate::helpers::mir_utils::pointee_ty(dest_ty) {
1810 let a = self.align_sym(pointee);
1811 if a.simplify().as_u64() != Some(1) {
1812 existing.facts.align_n = Some(a);
1813 }
1814 }
1815 }
1816 self.set_local(dest, existing);
1817 } else {
1818 let dest_ty = self.body().local_decls[dest].ty;
1819 let term = self.fresh_int(&format!("ret_align_{}", dest.as_usize()));
1820 self.set_local(
1821 dest,
1822 VmValue {
1823 z3_term: term,
1824 ty: dest_ty,
1825 provenance: None,
1826 facts: ValueFacts {
1827 ..Default::default()
1828 },
1829 source: ValueSource::None,
1830 },
1831 );
1832 }
1833 }
1834 CallEffect::ReturnLengthOfArg { arg } => {
1835 if let Some(arg_val) = args.get(*arg) {
1836 if self.interpreter_iter_len(arg_val, dest) {
1840 return;
1841 }
1842 }
1843 if let Some(arg_val) = args.get(*arg) {
1847 if self.set_len_from_alloc(arg_val, dest) {
1848 return;
1849 }
1850 }
1851 let dest_ty = self.body().local_decls[dest].ty;
1852 let term = self.fresh_int(&format!("len_{}", dest.as_usize()));
1853 let val = VmValue::new(term, dest_ty);
1854 self.set_local(dest, val);
1855 }
1856 CallEffect::ReturnFieldOfArg { arg, field } => {
1857 self.apply_field_of_arg_effect(*arg, *field, None, args, caller_arg_locals, dest);
1858 }
1859 CallEffect::ReturnFieldOfArgSub { arg, field, offset } => {
1860 self.apply_field_of_arg_effect(
1861 *arg,
1862 *field,
1863 Some(*offset),
1864 args,
1865 caller_arg_locals,
1866 dest,
1867 );
1868 }
1869 CallEffect::ReturnConst { value } => {
1870 let dest_ty = self.body().local_decls[dest].ty;
1871 let term = Int::from_u64(self.z3_ctx, *value);
1872 let val = VmValue::new(term, dest_ty);
1873 self.set_local(dest, val);
1874 }
1875 CallEffect::ReturnAlignOffset { ptr_arg, align_arg } => {
1876 let dest_ty = self.body().local_decls[dest].ty;
1877 let offset = self.fresh_int(&format!("align_offset_{}", dest.as_usize()));
1878 if let (Some(ptr_val), Some(align_val)) = (args.get(*ptr_arg), args.get(*align_arg))
1879 {
1880 let elem = crate::helpers::mir_utils::pointee_ty(ptr_val.ty)
1887 .map(|pointee| self.size_sym(pointee))
1888 .unwrap_or_else(|| Int::from_u64(self.z3_ctx, 1));
1889 let byte_off = Int::mul(self.z3_ctx, &[&offset, &elem]);
1890 let ptr_plus_off = Int::add(self.z3_ctx, &[&ptr_val.z3_term, &byte_off]);
1891 let zero = Int::from_u64(self.z3_ctx, 0);
1892 self.constraints.assertions
1893 .push(ptr_plus_off.rem(&align_val.z3_term)._eq(&zero));
1894 self.constraints.assertions.push(offset.ge(&zero));
1895 self.constraints.assertions.push(offset.lt(&align_val.z3_term));
1896 }
1897 let val = VmValue::new(offset, dest_ty);
1898 self.set_local(dest, val);
1899 }
1900 CallEffect::ReturnMin { lhs_arg, rhs_arg } => {
1901 self.apply_binary_num(dest, args, *lhs_arg, *rhs_arg, |lhs, rhs| {
1912 lhs.le(rhs).ite(lhs, rhs)
1913 });
1914 }
1915 CallEffect::ReturnMax { lhs_arg, rhs_arg } => {
1916 self.apply_binary_num(dest, args, *lhs_arg, *rhs_arg, |lhs, rhs| {
1917 lhs.ge(rhs).ite(lhs, rhs)
1918 });
1919 }
1920 CallEffect::ReturnClamp {
1921 value_arg,
1922 min_arg,
1923 max_arg,
1924 } => {
1925 if let (Some(v), Some(mn), Some(mx)) =
1926 (args.get(*value_arg), args.get(*min_arg), args.get(*max_arg))
1927 {
1928 let dest_ty = self.body().local_decls[dest].ty;
1929 let upper = v.z3_term.gt(&mx.z3_term).ite(&mx.z3_term, &v.z3_term);
1931 let term = v.z3_term.lt(&mn.z3_term).ite(&mn.z3_term, &upper);
1932 let val = VmValue::new(term, dest_ty);
1933 self.set_local(dest, val);
1934 }
1935 }
1936 CallEffect::ReturnAbs { arg } => {
1937 self.apply_unary_num(dest, args, *arg, |a| {
1938 let zero = Int::from_u64(self.z3_ctx, 0);
1939 let neg = Int::sub(self.z3_ctx, &[&zero, a]);
1940 a.ge(&zero).ite(a, &neg)
1941 });
1942 }
1943 CallEffect::ReturnNeg { arg } => {
1944 self.apply_unary_num(dest, args, *arg, |a| {
1945 let zero = Int::from_u64(self.z3_ctx, 0);
1946 Int::sub(self.z3_ctx, &[&zero, a])
1947 });
1948 }
1949 CallEffect::ReturnAdd { lhs_arg, rhs_arg } => {
1950 self.apply_binary_num(dest, args, *lhs_arg, *rhs_arg, |lhs, rhs| {
1951 Int::add(self.z3_ctx, &[lhs, rhs])
1952 });
1953 }
1954 CallEffect::ReturnMul { lhs_arg, rhs_arg } => {
1955 self.apply_binary_num(dest, args, *lhs_arg, *rhs_arg, |lhs, rhs| {
1956 Int::mul(self.z3_ctx, &[lhs, rhs])
1957 });
1958 }
1959 CallEffect::ReturnOptionSomeAdd { lhs_arg, rhs_arg } => {
1960 if let (Some(lhs), Some(rhs)) = (args.get(*lhs_arg), args.get(*rhs_arg)) {
1961 let term = Int::add(self.z3_ctx, &[&lhs.z3_term, &rhs.z3_term]);
1967 self.set_field_value(
1968 dest,
1969 vec![0],
1970 VmValue::new(term, lhs.ty),
1971 );
1972 }
1973 }
1974 CallEffect::ReturnOptionSomeMul { lhs_arg, rhs_arg } => {
1975 if let (Some(lhs), Some(rhs)) = (args.get(*lhs_arg), args.get(*rhs_arg)) {
1976 let term = Int::mul(self.z3_ctx, &[&lhs.z3_term, &rhs.z3_term]);
1977 self.set_field_value(
1978 dest,
1979 vec![0],
1980 VmValue::new(term, lhs.ty),
1981 );
1982 }
1983 }
1984 CallEffect::ReturnOptionSomeScanIndex { self_arg } => {
1985 if let Some(iter_ref) = caller_arg_locals.get(*self_arg).copied().flatten() {
1993 let iter_local = self
1994 .local_value(iter_ref)
1995 .and_then(|v| v.provenance_alloc_id())
1996 .and_then(|alloc| {
1997 self.current_frame.local_alloc
1998 .iter()
1999 .find(|(_, a)| **a == alloc)
2000 .map(|(l, _)| *l)
2001 });
2002 let ptr_term =
2003 iter_local.and_then(|l| self.field_value(l, &[0]).map(|v| v.z3_term.clone()));
2004 let end_term =
2005 iter_local.and_then(|l| self.field_value(l, &[1]).map(|v| v.z3_term.clone()));
2006 if let (Some(ptr), Some(end)) = (ptr_term, end_term) {
2007 let len = Int::sub(self.z3_ctx, &[&end, &ptr]);
2008 let payload = self.fresh_int(&format!("scan_idx_{}", dest.as_usize()));
2009 self.constraints.assertions.push(payload.lt(&len));
2010 let dest_ty = self.body().local_decls[dest].ty;
2011 let payload_ty = match dest_ty.kind() {
2012 TyKind::Adt(adt, substs) if adt.is_enum() => substs.type_at(0),
2013 _ => dest_ty,
2014 };
2015 self.set_field_value(
2016 dest,
2017 vec![0],
2018 VmValue::new(payload, payload_ty),
2019 );
2020 }
2021 }
2022 }
2023 CallEffect::ReturnBranchPayload { arg } => {
2024 let arg_local = caller_arg_locals.get(*arg).copied().flatten();
2028 if let Some(l) = arg_local {
2029 if let Some(payload) = self.field_value(l, &[0]).cloned() {
2030 self.set_field_value(dest, vec![0], payload);
2031 }
2032 }
2033 }
2034 CallEffect::ReturnOptionSomeIndexLtArgLen { arg } => {
2035 if let Some(slice) = args.get(*arg) {
2043 if let Some(len) = self.slice_len_from_value(slice) {
2044 let payload = self.fresh_int(&format!("scan_idx_{}", dest.as_usize()));
2045 self.constraints.assertions.push(payload.lt(&len));
2046 let zero = Int::from_u64(self.z3_ctx, 0);
2047 self.constraints.assertions.push(payload.ge(&zero));
2048 let dest_ty = self.body().local_decls[dest].ty;
2049 let payload_ty = match dest_ty.kind() {
2050 TyKind::Adt(adt, substs) if adt.is_enum() => substs.type_at(0),
2051 _ => dest_ty,
2052 };
2053 self.set_field_value(
2054 dest,
2055 vec![0],
2056 VmValue::new(payload, payload_ty),
2057 );
2058 }
2059 }
2060 }
2061 CallEffect::ReturnOptionSomeTupleFieldLeArgLen { field, arg } => {
2062 if let Some(slice) = args.get(*arg) {
2068 if let Some(arg_len) = self.slice_len_from_value(slice) {
2069 let len = self.fresh_int(&format!("decode_len_{}", dest.as_usize()));
2070 self.constraints.assertions.push(len.le(&arg_len));
2071 let dest_ty = self.body().local_decls[dest].ty;
2072 let payload_ty = match dest_ty.kind() {
2073 TyKind::Adt(adt, substs) if adt.is_enum() => substs.type_at(0),
2074 _ => dest_ty,
2075 };
2076 let field_ty = match payload_ty.kind() {
2077 TyKind::Tuple(tys) => tys.get(*field).copied().unwrap_or(payload_ty),
2078 _ => payload_ty,
2079 };
2080 self.set_field_value(
2081 dest,
2082 vec![0, *field],
2083 VmValue::new(len, field_ty),
2084 );
2085 }
2086 }
2087 }
2088 CallEffect::ReturnScanLength => {
2089 let len = self.fresh_int(&format!("strlen_{}", dest.as_usize()));
2096 let max = Int::from_i64(self.z3_ctx, i64::MAX);
2097 self.constraints.assertions.push(len.lt(&max));
2098 let dest_ty = self.body().local_decls[dest].ty;
2099 self.set_local(
2100 dest,
2101 VmValue::new(len, dest_ty),
2102 );
2103 }
2104 CallEffect::ReturnNonZeroIff { arg } => {
2105 if let Some(a) = args.get(*arg) {
2106 let dest_ty = self.body().local_decls[dest].ty;
2107 let zero = Int::from_u64(self.z3_ctx, 0);
2108 let term = self.fresh_int(&format!("ret_nz_iff_{}", dest.as_usize()));
2109 self.constraints.assertions
2112 .push(term._eq(&zero)._eq(&a.z3_term._eq(&zero)));
2113 self.set_local(
2114 dest,
2115 VmValue::new(term, dest_ty),
2116 );
2117 }
2118 }
2119 CallEffect::ReturnOptionSomeNonZeroIff { arg } => {
2120 if let Some(a) = args.get(*arg) {
2121 let zero = Int::from_u64(self.z3_ctx, 0);
2122 let term = self.fresh_int(&format!("ret_opt_nz_iff_{}", dest.as_usize()));
2123 self.constraints.assertions
2124 .push(term._eq(&zero)._eq(&a.z3_term._eq(&zero)));
2125 self.set_field_value(
2126 dest,
2127 vec![0],
2128 VmValue::new(term, a.ty),
2129 );
2130 }
2131 }
2132 CallEffect::ReturnOptionSomeNonZero => {
2133 let zero = Int::from_u64(self.z3_ctx, 0);
2136 let term = self.fresh_int(&format!("ret_opt_nz_{}", dest.as_usize()));
2137 self.constraints.assertions.push(term._eq(&zero).not());
2138 let payload_ty = args
2139 .first()
2140 .map(|a| a.ty)
2141 .unwrap_or(self.body().local_decls[dest].ty);
2142 self.set_field_value(
2143 dest,
2144 vec![0],
2145 VmValue::new(term, payload_ty),
2146 );
2147 }
2148 CallEffect::WriteMemory { pointer_arg } => {
2149 if let Some(arg_val) = args.get(*pointer_arg) {
2150 if let Some(prov) = &arg_val.provenance {
2151 if let rustc_middle::ty::TyKind::RawPtr(inner, _)
2156 | rustc_middle::ty::TyKind::Ref(_, inner, _) = arg_val.ty.kind()
2157 {
2158 let cur = self.alloc(prov.alloc_id).element_ty.as_ty();
2159 let is_u8 = |t: rustc_middle::ty::Ty<'_>| {
2160 matches!(
2161 t.kind(),
2162 rustc_middle::ty::TyKind::Uint(rustc_middle::ty::UintTy::U8)
2163 )
2164 };
2165 if let Some(c) = cur {
2166 if is_u8(c) && !is_u8(*inner) {
2167 self.alloc_mut(prov.alloc_id).element_ty = ElementTy::Typed(*inner);
2168 }
2169 }
2170 }
2171 let is_vec = crate::verify::api_classify::is_vec_push_or_reserve(callee);
2175 let is_external = self.alloc(prov.alloc_id).is_external();
2176 if is_vec && !is_external {
2177 let elem_ty = match arg_val.ty.kind() {
2178 TyKind::Ref(_, inner, _) | TyKind::RawPtr(inner, _) => {
2179 crate::verify::call_summary::vec_elem_ty(self.tcx, *inner)
2180 }
2181 _ => crate::verify::call_summary::vec_elem_ty(self.tcx, arg_val.ty),
2182 };
2183 let heap_align = elem_ty
2184 .map(|ty| self.align_sym(ty))
2185 .unwrap_or_else(|| Int::from_u64(self.z3_ctx, 1));
2186 if let Some(old_data) =
2187 self.container_data_alloc(prov.alloc_id, arg_val.ty)
2188 {
2189 self.alloc_mut(old_data).facts.dead = true;
2191 }
2192 let max_size = Int::from_u64(self.z3_ctx, i64::MAX as u64);
2193 let (data_alloc, data_base) =
2194 self.allocate_external(max_size, heap_align, elem_ty);
2195 let container_ty = match arg_val.ty.kind() {
2196 TyKind::Ref(_, inner, _) | TyKind::RawPtr(inner, _) => *inner,
2197 _ => arg_val.ty,
2198 };
2199 self.set_container_data_field(
2200 prov.alloc_id,
2201 arg_val.ty,
2202 data_alloc,
2203 data_base,
2204 elem_ty.unwrap_or(container_ty),
2205 );
2206 }
2207 let off_u64 = prov
2210 .offset
2211 .as_u64()
2212 .or_else(|| prov.offset.simplify().as_u64());
2213 if let Some(off) = off_u64 {
2214 if off == 0 {
2215 self.content_mut(prov.alloc_id).facts.initialized = true;
2216 }
2217 let elem_size = match arg_val.ty.kind() {
2218 rustc_middle::ty::TyKind::Ref(_, inner, _) => {
2219 self.size_of_ty(*inner) as usize
2220 }
2221 _ => 0,
2222 };
2223 let write_size = if elem_size > 0 {
2224 elem_size
2225 } else {
2226 self.allocation_size(prov.alloc_id).as_u64().unwrap_or(0) as usize
2227 };
2228 let end = (off as usize + write_size).min(4096);
2229 for byte_off in (off as usize)..end {
2230 self.mark_byte_init(prov.alloc_id, byte_off);
2231 }
2232 } else {
2233 let size_val = self.allocation_size(prov.alloc_id).as_u64();
2242 match size_val {
2243 Some(sz) if sz > 0 => {
2244 for off in 0..(sz as usize).min(1024) {
2245 self.mark_byte_init(prov.alloc_id, off);
2246 }
2247 }
2248 _ => {
2249 self.content_mut(prov.alloc_id).facts.initialized = true;
2250 }
2251 }
2252 }
2253 }
2254 }
2255 }
2256 CallEffect::ReturnFreshAllocation {
2257 pointer_arg,
2258 size_arg,
2259 elem_size,
2260 } => {
2261 if let (Some(ptr_val), Some(size_val)) =
2262 (args.get(*pointer_arg), args.get(*size_arg))
2263 {
2264 let dest_ty = self.body().local_decls[dest].ty;
2265 let elem_ty = crate::verify::call_summary::from_raw_parts_elem_ty(
2266 self.tcx,
2267 self.current_frame.current_def_id,
2268 Some(dest),
2269 );
2270 let elem_sz_term = if *elem_size == 0 {
2274 self.size_sym(elem_ty.unwrap_or(dest_ty))
2275 } else {
2276 Int::from_u64(self.z3_ctx, *elem_size)
2277 };
2278 let heap_align = elem_ty
2279 .map(|ty| self.align_sym(ty))
2280 .unwrap_or_else(|| Int::from_u64(self.z3_ctx, 1));
2281 let (alloc_id, base) = self.allocate_slice(
2282 size_val.z3_term.clone(),
2283 elem_sz_term.clone(),
2284 heap_align,
2285 elem_ty,
2286 );
2287 let prov = Provenance {
2288 alloc_id,
2289 offset: Int::from_u64(self.z3_ctx, 0),
2290 offset_kind: None,
2291 };
2292 if let Some(ref source_prov) = ptr_val.provenance {
2294 if !self.alloc(source_prov.alloc_id).facts.dead {
2295 self.content_mut(alloc_id).facts.initialized = true;
2296 self.alloc_mut(alloc_id).parent = Some(source_prov.alloc_id);
2297 }
2298 let src_offset = source_prov
2303 .offset
2304 .simplify()
2305 .as_u64()
2306 .map(|v| v as usize)
2307 .unwrap_or(0);
2308 self.copy_byte_tracking(source_prov.alloc_id, src_offset, alloc_id);
2309 }
2310 let result_align_n = ptr_val.facts.align_n.clone().or_else(|| {
2311 ptr_val
2312 .provenance
2313 .as_ref()
2314 .map(|p| self.alloc(p.alloc_id).align.clone())
2315 });
2316 let vec_base = base.clone();
2317 let vec_prov = prov.clone();
2318 let vec_len = size_val.z3_term.clone();
2319 self.set_local(
2320 dest,
2321 VmValue {
2322 z3_term: base,
2323 ty: dest_ty,
2324 provenance: Some(prov),
2325 facts: ValueFacts {
2326 non_null: true,
2327 init: true,
2328 in_bounds: true,
2329 align_n: result_align_n.clone(),
2330 ..ValueFacts::default()
2331 },
2332 source: ValueSource::None,
2333 },
2334 );
2335 if let rustc_middle::ty::TyKind::Adt(adt_def, _) = dest_ty.kind() {
2338 if api_classify::is_std_vec(adt_def.did()) {
2339 let ptr_field = VmValue {
2340 z3_term: vec_base,
2341 ty: ptr_val.ty,
2342 provenance: Some(vec_prov),
2343 facts: ValueFacts {
2344 non_null: true,
2345 init: true,
2346 in_bounds: true,
2347 align_n: result_align_n,
2348 ..ValueFacts::default()
2349 },
2350 source: ValueSource::None,
2351 };
2352 self.materialize_vec_fields(dest, ptr_field, vec_len.clone(), vec_len);
2353 }
2354 }
2355 }
2356 }
2357 CallEffect::ReturnBoxAllocation => {
2358 let dest_ty = self.body().local_decls[dest].ty;
2359 let pointee = crate::helpers::mir_utils::pointee_ty(dest_ty).or_else(|| {
2364 if let rustc_middle::ty::TyKind::Adt(adt, substs) = dest_ty.kind() {
2365 if api_classify::is_std_box(adt.did()) {
2366 substs.first().and_then(|s| s.as_type())
2367 } else {
2368 None
2369 }
2370 } else {
2371 None
2372 }
2373 });
2374 let size = pointee
2375 .map(|ty| self.size_sym(ty))
2376 .unwrap_or_else(|| Int::from_u64(self.z3_ctx, 1));
2377 let align = pointee
2378 .map(|ty| self.align_sym(ty))
2379 .unwrap_or_else(|| Int::from_u64(self.z3_ctx, 1));
2380 let (alloc_id, base) = self.allocate(size, align, pointee);
2381 self.content_mut(alloc_id).facts.initialized = true;
2382 let align_n = pointee.map(|ty| self.align_sym(ty));
2383 let heap_prov = Provenance {
2384 alloc_id,
2385 offset: Int::from_u64(self.z3_ctx, 0),
2386 offset_kind: None,
2387 };
2388 let nn_field = VmValue {
2397 z3_term: base.clone(),
2398 ty: dest_ty,
2399 provenance: Some(heap_prov.clone()),
2400 facts: ValueFacts {
2401 non_null: true,
2402 init: true,
2403 ..Default::default()
2404 },
2405 source: ValueSource::None,
2406 };
2407 let nn_path = self
2408 .container_ptr_field(dest_ty)
2409 .map(|(p, _)| p)
2410 .expect("Box has no owning pointer field");
2411 self.set_field_value(dest, nn_path.clone(), nn_field.clone());
2412 self.units[alloc_id.0]
2413 .content
2414 .values
2415 .insert((dest_ty, nn_path), nn_field);
2416 self.set_local(
2417 dest,
2418 VmValue {
2419 z3_term: base.clone(),
2420 ty: dest_ty,
2421 provenance: Some(Provenance {
2422 alloc_id,
2423 offset: Int::from_u64(self.z3_ctx, 0),
2424 offset_kind: None,
2425 }),
2426 facts: ValueFacts {
2427 non_null: true,
2428 init: true,
2429 in_bounds: true,
2430 align_n,
2431 },
2432 source: ValueSource::None,
2433 },
2434 );
2435 }
2436 CallEffect::ReturnExchangeMalloc { size_arg } => {
2437 if let Some(size_val) = args.get(*size_arg) {
2438 let dest_ty = self.body().local_decls[dest].ty;
2439 let u8_ty = self.tcx.types.u8;
2440 let (alloc_id, base) = self.allocate_external(
2441 size_val.z3_term.clone(),
2442 Int::from_u64(self.z3_ctx, 1),
2443 Some(u8_ty),
2444 );
2445 self.alloc_mut(alloc_id).set_slice_len(size_val.z3_term.clone());
2446 self.content_mut(alloc_id).facts.initialized = true;
2447 self.set_local(
2448 dest,
2449 VmValue {
2450 z3_term: base,
2451 ty: dest_ty,
2452 provenance: Some(Provenance {
2453 alloc_id,
2454 offset: Int::from_u64(self.z3_ctx, 0),
2455 offset_kind: None,
2456 }),
2457 facts: ValueFacts {
2458 non_null: true,
2459 init: true,
2460 in_bounds: true,
2461 ..ValueFacts::default()
2462 },
2463 source: ValueSource::None,
2464 },
2465 );
2466 }
2467 }
2468 CallEffect::ReturnNewAllocation {
2469 size_arg,
2470 elem_size,
2471 } => {
2472 if let Some(size_val) = args.get(*size_arg) {
2473 let dest_ty = self.body().local_decls[dest].ty;
2474 let elem_ty = crate::verify::call_summary::vec_elem_ty(self.tcx, dest_ty);
2475 let elem_sz = if *elem_size == 0 {
2479 self.size_sym(elem_ty.unwrap_or(dest_ty))
2480 } else {
2481 Int::from_u64(self.z3_ctx, *elem_size)
2482 };
2483 let total = Int::mul(self.z3_ctx, &[&size_val.z3_term, &elem_sz]);
2484 let heap_align = elem_ty
2485 .map(|ty| self.align_sym(ty))
2486 .unwrap_or_else(|| Int::from_u64(self.z3_ctx, 1));
2487 let (alloc_id, base) = self.allocate_external(total, heap_align, elem_ty);
2488 self.alloc_mut(alloc_id).set_slice_len(size_val.z3_term.clone());
2489 let dest_alloc_id = self.current_frame.local_alloc.get(&dest).copied();
2490 self.content_mut(alloc_id).facts.initialized = true;
2491 let vec_base = base.clone();
2492 let vec_len = size_val.z3_term.clone();
2493 self.set_local(
2494 dest,
2495 VmValue {
2496 z3_term: base,
2497 ty: dest_ty,
2498 provenance: dest_alloc_id.map(|stack_id| Provenance {
2499 alloc_id: stack_id,
2500 offset: Int::from_u64(self.z3_ctx, 0),
2501 offset_kind: None,
2502 }),
2503 facts: ValueFacts {
2504 non_null: true,
2505 init: true,
2506 in_bounds: true,
2507 ..ValueFacts::default()
2508 },
2509 source: ValueSource::None,
2510 },
2511 );
2512 if let rustc_middle::ty::TyKind::Adt(adt_def, _) = dest_ty.kind() {
2515 if api_classify::is_std_vec(adt_def.did()) {
2516 let ptr_field = VmValue {
2517 z3_term: vec_base,
2518 ty: elem_ty.unwrap_or(dest_ty),
2519 provenance: Some(Provenance {
2520 alloc_id,
2521 offset: Int::from_u64(self.z3_ctx, 0),
2522 offset_kind: None,
2523 }),
2524 facts: ValueFacts {
2525 non_null: true,
2526 init: true,
2527 in_bounds: true,
2528 ..ValueFacts::default()
2529 },
2530 source: ValueSource::None,
2531 };
2532 self.materialize_vec_fields(dest, ptr_field, vec_len.clone(), vec_len);
2533 }
2534 }
2535 }
2536 }
2537 CallEffect::ReturnNewAllocationFromCap { cap_arg, elem_size } => {
2538 if let Some(cap_val) = args.get(*cap_arg) {
2539 let dest_ty = self.body().local_decls[dest].ty;
2540 let elem_ty = crate::verify::call_summary::vec_elem_ty(self.tcx, dest_ty);
2541 let elem_sz = if *elem_size == 0 {
2545 self.size_sym(elem_ty.unwrap_or(dest_ty))
2546 } else {
2547 Int::from_u64(self.z3_ctx, *elem_size)
2548 };
2549 let total = Int::mul(self.z3_ctx, &[&cap_val.z3_term, &elem_sz]);
2550 let heap_align = elem_ty
2551 .map(|ty| self.align_sym(ty))
2552 .unwrap_or_else(|| Int::from_u64(self.z3_ctx, 1));
2553 let (alloc_id, base) = self.allocate_external(total, heap_align, elem_ty);
2554 let dest_alloc_id = self.current_frame.local_alloc.get(&dest).copied();
2555 self.content_mut(alloc_id).facts.initialized = true;
2556 let vec_base = base.clone();
2557 let vec_cap = cap_val.z3_term.clone();
2558 self.set_local(
2559 dest,
2560 VmValue {
2561 z3_term: base,
2562 ty: dest_ty,
2563 provenance: dest_alloc_id.map(|stack_id| Provenance {
2564 alloc_id: stack_id,
2565 offset: Int::from_u64(self.z3_ctx, 0),
2566 offset_kind: None,
2567 }),
2568 facts: ValueFacts {
2569 non_null: true,
2570 init: true,
2571 in_bounds: true,
2572 ..ValueFacts::default()
2573 },
2574 source: ValueSource::None,
2575 },
2576 );
2577 if let rustc_middle::ty::TyKind::Adt(adt_def, _) = dest_ty.kind() {
2579 if api_classify::is_std_vec(adt_def.did()) {
2580 let ptr_field = VmValue {
2581 z3_term: vec_base,
2582 ty: elem_ty.unwrap_or(dest_ty),
2583 provenance: Some(Provenance {
2584 alloc_id,
2585 offset: Int::from_u64(self.z3_ctx, 0),
2586 offset_kind: None,
2587 }),
2588 facts: ValueFacts {
2589 non_null: true,
2590 init: true,
2591 in_bounds: true,
2592 ..ValueFacts::default()
2593 },
2594 source: ValueSource::None,
2595 };
2596 let zero = Int::from_u64(self.z3_ctx, 0);
2597 self.materialize_vec_fields(dest, ptr_field, vec_cap, zero);
2598 }
2599 }
2600 }
2601 }
2602 CallEffect::ReturnNewAllocationFromBox => {
2603 self.ensure_local_allocation(dest);
2606 let dest_ty = self.body().local_decls[dest].ty;
2607 let elem_ty = crate::verify::call_summary::vec_elem_ty(self.tcx, dest_ty);
2608 let heap_align = elem_ty
2609 .map(|ty| self.align_sym(ty))
2610 .unwrap_or_else(|| Int::from_u64(self.z3_ctx, 1));
2611 let known_len = args
2614 .first()
2615 .and_then(|v| v.provenance.as_ref())
2616 .and_then(|p| {
2617 let alloc = self.alloc(p.alloc_id);
2618 if let Some(slice_len) = alloc.slice_len().cloned() {
2619 return Some(slice_len);
2620 }
2621 let elem_size = elem_ty.map(|t| self.size_of_ty(t)).unwrap_or(1);
2624 let n = alloc.size.as_u64()?;
2625 if elem_size > 0 {
2626 Some(Int::from_u64(self.z3_ctx, n / elem_size))
2627 } else {
2628 None
2629 }
2630 });
2631 let size = known_len
2632 .clone()
2633 .unwrap_or_else(|| Int::from_u64(self.z3_ctx, i64::MAX as u64));
2634 let (alloc_id, base) = self.allocate_external(size, heap_align, elem_ty);
2635 if let Some(box_alloc) = args.first().and_then(|v| v.provenance_alloc_id()) {
2639 self.copy_byte_tracking(box_alloc, 0, alloc_id);
2640 }
2641 let dest_alloc_id = self.current_frame.local_alloc.get(&dest).copied();
2642 self.content_mut(alloc_id).facts.initialized = true;
2643 let vec_base = base.clone();
2644 self.set_local(
2645 dest,
2646 VmValue {
2647 z3_term: base,
2648 ty: dest_ty,
2649 provenance: dest_alloc_id.map(|stack_id| Provenance {
2650 alloc_id: stack_id,
2651 offset: Int::from_u64(self.z3_ctx, 0),
2652 offset_kind: None,
2653 }),
2654 facts: ValueFacts {
2655 non_null: true,
2656 init: true,
2657 in_bounds: true,
2658 ..ValueFacts::default()
2659 },
2660 source: ValueSource::None,
2661 },
2662 );
2663 if let rustc_middle::ty::TyKind::Adt(adt_def, _) = dest_ty.kind() {
2667 if api_classify::is_std_vec(adt_def.did()) {
2668 let ptr_field = VmValue {
2669 z3_term: vec_base,
2670 ty: elem_ty.unwrap_or(dest_ty),
2671 provenance: Some(Provenance {
2672 alloc_id,
2673 offset: Int::from_u64(self.z3_ctx, 0),
2674 offset_kind: None,
2675 }),
2676 facts: ValueFacts {
2677 non_null: true,
2678 init: true,
2679 in_bounds: true,
2680 ..ValueFacts::default()
2681 },
2682 source: ValueSource::None,
2683 };
2684 let len_term = known_len
2685 .clone()
2686 .unwrap_or_else(|| self.fresh_int(&format!("vec_len_{}", dest.as_usize())));
2687 self.materialize_vec_fields(dest, ptr_field, len_term.clone(), len_term);
2688 }
2689 }
2690 }
2691 CallEffect::ReturnBoxFromVec { arg } => {
2692 if let Some(vec_val) = args.get(*arg) {
2693 if let Some(ref prov) = vec_val.provenance {
2694 if let Some(heap_alloc_id) =
2695 self.container_data_alloc(prov.alloc_id, vec_val.ty)
2696 {
2697 let heap_base = self.allocation_base(heap_alloc_id).clone();
2698 let dest_ty = self.body().local_decls[dest].ty;
2699 self.set_local(
2700 dest,
2701 VmValue {
2702 z3_term: heap_base,
2703 ty: dest_ty,
2704 provenance: Some(Provenance {
2705 alloc_id: heap_alloc_id,
2706 offset: Int::from_u64(self.z3_ctx, 0),
2707 offset_kind: None,
2708 }),
2709 facts: ValueFacts {
2710 non_null: true,
2711 init: true,
2712 in_bounds: true,
2713 ..ValueFacts::default()
2714 },
2715 source: ValueSource::None,
2716 },
2717 );
2718 }
2719 }
2720 }
2721 }
2722 CallEffect::OwnsInitMemory { arg } => {
2723 if let Some(arg_val) = args.get(*arg) {
2724 if let Some(prov) = &arg_val.provenance {
2725 self.content_mut(prov.alloc_id).facts.initialized = true;
2726 }
2727 let mut val = arg_val.clone();
2728 val.ty = self.body().local_decls[dest].ty;
2729 val.facts.init = true;
2730 val.facts.non_null = true;
2731 if let rustc_middle::ty::TyKind::Adt(adt, _) = val.ty.kind() {
2737 if api_classify::is_std_box(adt.did()) {
2738 let nn_path = self
2739 .container_ptr_field(val.ty)
2740 .map(|(p, _)| p)
2741 .expect("Box has no owning pointer field");
2742 self.set_field_value(dest, nn_path.clone(), val.clone());
2743 if let Some(prov) = &val.provenance {
2744 self.units[prov.alloc_id.0]
2745 .content
2746 .values
2747 .insert((val.ty, nn_path), val.clone());
2748 }
2749 }
2750 }
2751 self.set_local(dest, val);
2752 }
2753 }
2754 CallEffect::DropMemory { pointer_arg } => {
2755 if let Some(arg_val) = args.get(*pointer_arg) {
2763 let alloc_id = if matches!(
2764 arg_val.ty.kind(),
2765 rustc_middle::ty::TyKind::Ref(..)
2766 | rustc_middle::ty::TyKind::RawPtr(..)
2767 ) {
2768 self.find_local_by_address(&arg_val.z3_term)
2769 .and_then(|r| self.owner_ptr_field(r))
2770 .and_then(|v| v.provenance_alloc_id())
2771 } else {
2772 arg_val.provenance_alloc_id()
2773 };
2774 if let Some(alloc_id) = alloc_id {
2775 self.alloc_mut(alloc_id).facts.dead = true;
2776 }
2777 }
2778 }
2779 CallEffect::ReturnPowerOfTwo => {
2780 let dest_ty = self.body().local_decls[dest].ty;
2789 let term = self.fresh_int(&format!("layout_align_{}", dest.as_usize()));
2790 let zero = Int::from_u64(self.z3_ctx, 0);
2791 self.constraints.assertions.push(term.gt(&zero));
2792 self.set_local(
2793 dest,
2794 VmValue::new(term, dest_ty),
2795 );
2796 }
2797 CallEffect::ChecksIndexBoundsDisjoint {
2798 indices_arg,
2799 len_arg,
2800 } => {
2801 let indices = args.get(*indices_arg);
2802 let len_val = args.get(*len_arg);
2803 if let (Some(indices_val), Some(len_val)) = (indices, len_val) {
2804 let arr_ty = match indices_val.ty.kind() {
2805 rustc_middle::ty::TyKind::Ref(_, inner, _) => *inner,
2806 _ => indices_val.ty,
2807 };
2808 if let rustc_middle::ty::TyKind::Array(_elem_ty, _const_len) = arr_ty.kind() {
2809 let alloc_id = indices_val.provenance_alloc_id().or_else(|| {
2810 self.all_local_values().into_iter().find_map(|(_, v)| {
2813 if v.ty == arr_ty {
2814 v.provenance_alloc_id()
2815 } else {
2816 None
2817 }
2818 })
2819 });
2820 if let Some(alloc_id) = alloc_id {
2821 self.path_facts.has_checked_bounds = true;
2822 let zero = Int::from_u64(self.z3_ctx, 0);
2823 let byte_offsets: Vec<(usize, Int)> =
2824 self.alloc_byte_values(alloc_id);
2825 for (_, term) in &byte_offsets {
2826 self.constraints.assertions.push(term.ge(&zero));
2827 self.constraints.assertions.push(term.lt(&len_val.z3_term));
2828 }
2829 for i in 0..byte_offsets.len() {
2830 for j in (i + 1)..byte_offsets.len() {
2831 let ti = &byte_offsets[i].1;
2832 let tj = &byte_offsets[j].1;
2833 self.constraints.assertions.push(ti._eq(tj).not());
2834 }
2835 }
2836 }
2837 }
2838 }
2839 let dest_ty = self.body().local_decls[dest].ty;
2840 let term = self.fresh_int(&format!("ck_ok_{}", dest.as_usize()));
2841 self.set_local(
2842 dest,
2843 VmValue::new(term, dest_ty),
2844 );
2845 }
2846 }
2847 }
2848
2849 fn compute_pointer_add_align(
2855 &self,
2856 base: &VmValue<'z3, 'tcx>,
2857 stride_bytes: u64,
2858 ) -> Option<Int<'z3>> {
2859 let base_align = base.facts.align_n.as_ref()?;
2860 let Some(n) = base_align.simplify().as_u64() else {
2866 return None;
2867 };
2868 if stride_bytes > 0 && stride_bytes.is_multiple_of(n) {
2869 return Some(base_align.clone());
2870 }
2871 None
2872 }
2873
2874 fn pointer_stride_term(&mut self, dest: Local, stride: Option<u64>) -> Int<'z3> {
2877 match stride {
2878 Some(s) => Int::from_u64(self.z3_ctx, s),
2879 None => {
2880 let dest_ty = self.body().local_decls[dest].ty;
2881 let pointee = crate::helpers::mir_utils::pointee_ty(dest_ty).unwrap_or(dest_ty);
2882 self.size_sym(pointee)
2883 }
2884 }
2885 }
2886
2887 pub(crate) fn propagate_const_bytes_to_tracked(&mut self, args: &[Spanned<Operand<'tcx>>]) {
2888 let mut const_bytes: Option<(Vec<u8>, usize)> = None;
2889 let mut tracked_alloc: Option<AllocId> = None;
2890 let mut tracked_offset: usize = 0;
2891
2892 for (i, arg) in args.iter().enumerate() {
2893 let arg_val = self.value_of_operand(&arg.node);
2894 if const_bytes.is_none() {
2895 let bytes_opt = crate::helpers::mir_utils::const_operand_bytes(self.tcx, &arg.node)
2896 .or_else(|| self.trace_to_const_bytes(&arg.node));
2897 if let Some(bytes) = bytes_opt {
2898 const_bytes = Some((bytes, i));
2899 }
2900 }
2901 if tracked_alloc.is_none() {
2902 if let Some(alloc_id) = arg_val.provenance_alloc_id() {
2903 tracked_alloc = Some(alloc_id);
2904 if let Some(ref prov) = arg_val.provenance {
2905 tracked_offset = prov.offset.as_u64().map(|v| v as usize).unwrap_or(0);
2906 }
2907 }
2908 }
2909 }
2910
2911 if let (Some((bytes, _)), Some(alloc_id)) = (const_bytes, tracked_alloc) {
2912 for (j, &b) in bytes.iter().enumerate() {
2913 let off = tracked_offset + j;
2914 self.record_byte_value(alloc_id, off, Int::from_u64(self.z3_ctx, b as u64));
2915 }
2916 self.content_mut(alloc_id).facts.initialized = true;
2917 }
2918 }
2919
2920 pub(crate) fn iter_elem_size(&self, ptr: &VmValue<'z3, 'tcx>) -> Int<'z3> {
2923 let elem_ty = match ptr.ty.kind() {
2924 TyKind::Adt(_, substs) => substs.first().and_then(|s| s.as_type()),
2925 _ => None,
2926 };
2927 match elem_ty {
2928 Some(t) => self.size_sym_read(t),
2929 None => Int::from_u64(self.z3_ctx, 1),
2930 }
2931 }
2932
2933 pub(crate) fn iter_len_from_ptrs(
2939 &self,
2940 ptr: &VmValue<'z3, 'tcx>,
2941 end: &VmValue<'z3, 'tcx>,
2942 ) -> Option<Int<'z3>> {
2943 let pp = ptr.provenance.as_ref()?;
2944 let ep = end.provenance.as_ref()?;
2945 if pp.alloc_id != ep.alloc_id {
2946 return None;
2947 }
2948 let elem_of = |p: &Provenance<'z3>| -> Option<Int<'z3>> {
2949 match &p.offset_kind {
2950 Some(OffsetKind::Element(e)) => Some(e.clone()),
2951 Some(OffsetKind::Field) | None => Some(Int::from_u64(self.z3_ctx, 0)),
2952 _ => None,
2953 }
2954 };
2955 if let (Some(pe), Some(ee)) = (elem_of(pp), elem_of(ep)) {
2956 return Some(Int::sub(self.z3_ctx, &[&ee, &pe]));
2957 }
2958 let sz = self.iter_elem_size(ptr);
2959 let diff = Int::sub(self.z3_ctx, &[&ep.offset, &pp.offset]);
2960 Some(diff.div(&sz))
2961 }
2962
2963 fn iter_remaining_len(&self, local: Local) -> Option<Int<'z3>> {
2969 let ptr = self.field_value(local, &[0])?;
2970 let end = self.field_value(local, &[1])?;
2971 let ep = end.provenance.as_ref()?;
2972 if ptr.provenance.as_ref().map(|p| p.alloc_id) != Some(ep.alloc_id) {
2973 return None;
2974 }
2975 let sz = self.iter_elem_size(ptr);
2976 if let Some((offset, _)) = self.constraints.term_caches.iter_ptr_offset.get(&ep.alloc_id) {
2977 let base_len = ep.offset.div(&sz);
2978 let zero = Int::from_u64(self.z3_ctx, 0);
2979 Some(
2980 offset
2981 .gt(&base_len)
2982 .ite(&zero, &Int::sub(self.z3_ctx, &[&base_len, offset])),
2983 )
2984 } else {
2985 self.iter_len_from_ptrs(ptr, end)
2986 }
2987 }
2988
2989 fn interpreter_iter_len(&mut self, arg_val: &VmValue<'z3, 'tcx>, dest: Local) -> bool {
2993 let Some(l) = self.find_iter_self_local(arg_val) else {
2994 return false;
2995 };
2996 let Some(len_term) = self.iter_remaining_len(l) else {
2997 return false;
2998 };
2999 let dest_ty = self.body().local_decls[dest].ty;
3000 self.set_local(dest, VmValue::new(len_term, dest_ty));
3001 true
3002 }
3003
3004 fn apply_iter_ptr_update(
3010 &mut self,
3011 callee: DefId,
3012 arg_values: &[VmValue<'z3, 'tcx>],
3013 ) {
3014 let is_inc = crate::helpers::mir_utils::is_post_inc_start(self.tcx, callee);
3015 if !is_inc {
3016 return;
3017 } let self_val = &arg_values[0];
3019 let some_local = self.find_iter_self_local(self_val);
3020 let Some(local) = some_local else { return };
3021 let Some(buffer) = self.iter_buffer(local) else { return };
3022 let offset_term = arg_values
3023 .get(1)
3024 .map(|v| v.z3_term.clone())
3025 .unwrap_or_else(|| Int::from_u64(self.z3_ctx, 1));
3026 let (new_offset, base_len) = match self.constraints.term_caches.iter_ptr_offset.get(&buffer) {
3027 Some((prev, base)) => (Int::add(self.z3_ctx, &[prev, &offset_term]), base.clone()),
3028 None => {
3029 let base = self
3030 .field_value(local, &[1])
3031 .and_then(|end| end.provenance.as_ref())
3032 .and_then(|ep| match &ep.offset_kind {
3033 Some(OffsetKind::Element(e)) => Some(e.clone()),
3034 _ => None,
3035 });
3036 (offset_term, base)
3037 }
3038 };
3039 self.constraints.term_caches.iter_ptr_offset.insert(buffer, (new_offset, base_len));
3040 }
3041
3042 pub(crate) fn find_local_by_address(&self, term: &Int<'z3>) -> Option<Local> {
3047 for (local, id) in &self.current_frame.local_alloc {
3048 if self.units[id.0].allocation.base == *term {
3049 return Some(*local);
3050 }
3051 }
3052 None
3053 }
3054
3055 pub(crate) fn find_whole_reborrow_referent(&self, local: Local) -> Option<Local> {
3065 use rustc_middle::mir::{ProjectionElem, Rvalue, StatementKind};
3066 for bb in self.body().basic_blocks.iter() {
3067 for stmt in &bb.statements {
3068 if let StatementKind::Assign(assign) = &stmt.kind {
3069 let (dest, rvalue) = &**assign;
3070 if dest.local == local && dest.projection.is_empty() {
3071 if let Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) = rvalue {
3072 if place.projection.len() == 1
3073 && matches!(place.projection[0].kind(), ProjectionElem::Deref)
3074 {
3075 return Some(place.local);
3076 }
3077 }
3078 }
3079 }
3080 }
3081 }
3082 None
3083 }
3084
3085 pub(crate) fn find_field_reborrow_referent(
3088 &self,
3089 local: Local,
3090 ) -> Option<(Local, Vec<usize>)> {
3091 use rustc_middle::mir::{ProjectionElem, Rvalue, StatementKind};
3092 for bb in self.body().basic_blocks.iter() {
3093 for stmt in &bb.statements {
3094 if let StatementKind::Assign(assign) = &stmt.kind {
3095 let (dest, rvalue) = &**assign;
3096 if dest.local == local && dest.projection.is_empty() {
3097 if let Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) = rvalue {
3098 let mut proj = place.projection.iter();
3099 if !matches!(proj.next().map(|p| p.kind()), Some(ProjectionElem::Deref))
3100 {
3101 continue;
3102 }
3103 let mut fields = Vec::new();
3104 for p in proj {
3105 if let ProjectionElem::Field(f, _) = p.kind() {
3106 fields.push(f.as_usize());
3107 } else {
3108 fields.clear();
3109 break;
3110 }
3111 }
3112 if !fields.is_empty() {
3113 return Some((place.local, fields));
3114 }
3115 }
3116 }
3117 }
3118 }
3119 }
3120 None
3121 }
3122
3123 pub(crate) fn find_copy_root(&self, local: Local) -> Option<Local> {
3131 use rustc_middle::mir::{Rvalue, StatementKind};
3132 for bb in self.body().basic_blocks.iter() {
3133 for stmt in &bb.statements {
3134 if let StatementKind::Assign(assign) = &stmt.kind {
3135 let (dest, rvalue) = &**assign;
3136 if dest.local == local && dest.projection.is_empty() {
3137 #[cfg(rapx_rvalue_use_with_retag)]
3138 let op = match rvalue {
3139 Rvalue::Use(op, _) => Some(op),
3140 _ => None,
3141 };
3142 #[cfg(not(rapx_rvalue_use_with_retag))]
3143 let op = match rvalue {
3144 Rvalue::Use(op) => Some(op),
3145 _ => None,
3146 };
3147 if let Some(Operand::Copy(p) | Operand::Move(p)) = op
3148 && p.projection.is_empty()
3149 {
3150 return Some(p.local);
3151 }
3152 }
3153 }
3154 }
3155 }
3156 None
3157 }
3158
3159 fn find_iter_self_local(&self, arg_val: &VmValue<'z3, 'tcx>) -> Option<Local> {
3163 match arg_val.ty.kind() {
3164 TyKind::Ref(_, pointee, _) => match pointee.kind() {
3165 TyKind::Adt(adt_def, _) => {
3166 if api_classify::is_std_iter_or_itermut(adt_def.did()) {
3167 if let Some(local) = self.find_local_by_address(&arg_val.z3_term) {
3175 return Some(local);
3176 }
3177 return Some(Local::from_usize(1));
3179 }
3180 None
3181 }
3182 _ => None,
3183 },
3184 _ => None,
3185 }
3186 }
3187
3188 fn set_len_from_alloc(&mut self, arg_val: &VmValue<'z3, 'tcx>, dest: Local) -> bool {
3193 let effective_alloc_id = arg_val
3194 .provenance_alloc_id()
3195 .and_then(|pid| self.data_alloc_of(pid, arg_val.ty))
3196 .or_else(|| arg_val.provenance_alloc_id());
3197 let Some(alloc_id) = effective_alloc_id else {
3198 return false;
3199 };
3200 let dest_ty = self.body().local_decls[dest].ty;
3201 if let Some(len) = self.alloc(alloc_id).slice_len().cloned() {
3203 let val = VmValue::new(len, dest_ty);
3204 self.set_local(dest, val);
3205 return true;
3206 }
3207 if let Some(elem_ty) = self.alloc(alloc_id).element_ty.as_ty() {
3208 let elem_term = self.size_sym_read(elem_ty);
3209 let size = self.allocation_size(alloc_id);
3210 if elem_term.simplify().as_u64() == Some(1) {
3211 let val = VmValue::new(size.clone(), dest_ty);
3212 self.set_local(dest, val);
3213 return true;
3214 }
3215 let val = VmValue::new(size.div(&elem_term), dest_ty);
3216 self.set_local(dest, val);
3217 return true;
3218 }
3219 let size = self.allocation_size(alloc_id);
3220 let val = VmValue::new(size.clone(), dest_ty);
3221 self.set_local(dest, val);
3222 true
3223 }
3224
3225 fn apply_field_of_arg_effect(
3237 &mut self,
3238 arg: usize,
3239 field: usize,
3240 sub_offset: Option<u64>,
3241 args: &[VmValue<'z3, 'tcx>],
3242 caller_arg_locals: &[Option<Local>],
3243 dest: Local,
3244 ) {
3245 let mut candidates: Vec<Local> = Vec::new();
3250 if let Some(l) = args
3251 .get(arg)
3252 .and_then(|v| self.find_local_by_address(&v.z3_term))
3253 {
3254 candidates.push(l);
3255 }
3256 if let Some(l) = caller_arg_locals.get(arg).copied().flatten() {
3257 candidates.push(l);
3258 }
3259 for l in self.current_frame.local_alloc.keys() {
3262 if !self.field_paths(*l).is_empty() {
3263 candidates.push(*l);
3264 }
3265 }
3266 let mut found: Option<VmValue<'z3, 'tcx>> = None;
3267 for l in candidates {
3268 if let Some(fv) = self.field_value(l, &[field]) {
3269 found = Some(fv.clone());
3270 break;
3271 }
3272 }
3273 if let Some(mut v) = found {
3274 if let Some(offset) = sub_offset {
3275 let stride = self.pointee_elem_size(v.ty).max(1);
3278 let scaled = Int::from_u64(self.z3_ctx, offset * stride);
3279 v.z3_term = Int::sub(self.z3_ctx, &[&v.z3_term, &scaled]);
3280 if let Some(prov) = &v.provenance {
3281 v.provenance = Some(Provenance {
3282 alloc_id: prov.alloc_id,
3283 offset: Int::sub(self.z3_ctx, &[&prov.offset, &scaled]),
3284 offset_kind: None,
3285 });
3286 }
3287 }
3288 v.ty = self.body().local_decls[dest].ty;
3289 self.set_local(dest, v);
3290 return;
3291 }
3292 let dest_ty = self.body().local_decls[dest].ty;
3297 if matches!(dest_ty.kind(), TyKind::Uint(_) | TyKind::Int(_)) {
3298 if let Some(arg_val) = args.get(arg) {
3299 if self.set_len_from_alloc(arg_val, dest) {
3300 return;
3301 }
3302 }
3303 }
3304 let term = self.fresh_int(&format!("field_{}", dest.as_usize()));
3305 let val = VmValue::new(term, dest_ty);
3306 self.set_local(dest, val);
3307 }
3308
3309 fn apply_range_effect(
3315 &mut self,
3316 bounds_arg: usize,
3317 args: &[VmValue<'z3, 'tcx>],
3318 caller_arg_locals: &[Option<Local>],
3319 dest: Local,
3320 ) {
3321 let dest_ty = self.body().local_decls[dest].ty;
3322 let TyKind::Adt(adt, substs) = dest_ty.kind() else {
3323 return;
3324 };
3325 let variant = adt.non_enum_variant();
3326 let field_ty = |idx: usize| -> Ty<'tcx> {
3327 variant
3328 .fields
3329 .iter()
3330 .nth(idx)
3331 .map(|f| crate::helpers::mir_utils::field_ty(self.tcx, f, substs))
3332 .unwrap_or(dest_ty)
3333 };
3334
3335 let mut len_term = None;
3339 if let Some(l) = caller_arg_locals.get(bounds_arg).copied().flatten() {
3340 if let Some(fv) = self.field_value(l, &[0]) {
3341 len_term = Some(fv.z3_term.clone());
3342 }
3343 }
3344 let len_term = len_term.or_else(|| args.get(bounds_arg).map(|v| v.z3_term.clone()));
3345 let Some(len_term) = len_term else {
3346 return;
3347 };
3348
3349 let start = self.fresh_int(&format!("range_start_{}", dest.as_usize()));
3350 let end = self.fresh_int(&format!("range_end_{}", dest.as_usize()));
3351 let zero = Int::from_u64(self.z3_ctx, 0);
3352 self.constraints.assertions.push(start.ge(&zero));
3353 self.constraints.assertions.push(start.le(&end));
3354 self.constraints.assertions.push(end.le(&len_term));
3355
3356 let start_val = VmValue::new(start, field_ty(0));
3357 let end_val = VmValue::new(end, field_ty(1));
3358 self.set_field_value(dest, vec![0], start_val);
3359 self.set_field_value(dest, vec![1], end_val);
3360 }
3361
3362 pub(crate) fn materialize_vec_fields(
3376 &mut self,
3377 local: Local,
3378 ptr: VmValue<'z3, 'tcx>,
3379 cap: Int<'z3>,
3380 len: Int<'z3>,
3381 ) {
3382 let elem_size = self.size_of_ty(ptr.ty).max(1);
3383 let ty = self.body().local_decls[local].ty;
3384 let (ptr_path, _) = self
3385 .container_ptr_field(ty)
3386 .expect("materialize_vec_fields: container has no owning pointer field");
3387 self.set_field_value(local, ptr_path, ptr);
3388 self.materialize_vec_len_cap(cap, len, elem_size);
3389 }
3390
3391 pub(crate) fn materialize_vec_len_cap(
3394 &mut self,
3395 cap: Int<'z3>,
3396 len: Int<'z3>,
3397 elem_size: u64,
3398 ) {
3399 let zero = Int::from_u64(self.z3_ctx, 0);
3400 self.constraints.assertions.push(len.ge(&zero));
3401 self.constraints.assertions.push(len.le(&cap));
3402 self.constraints.assertions.push(cap.ge(&zero));
3403 let isize_max = Int::from_u64(self.z3_ctx, isize::MAX as u64);
3408 let elem_term = Int::from_u64(self.z3_ctx, elem_size.max(1));
3409 self.constraints.assertions
3410 .push(Int::mul(self.z3_ctx, &[&cap, &elem_term]).le(&isize_max));
3411 }
3412}