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::{FxHashSet, Spanned};
18use crate::verify::call_summary::{self, CallEffect};
19use crate::verify::def_use::{PlaceBaseKey, PlaceKey};
20use crate::helpers::mir_utils::operand_place;
21use crate::helpers::api_classify;
22
23use super::state::{AllocId, Provenance, VmState, VmValue, ValueInvariants};
24
25const MAX_INLINE_DEPTH: usize = 5;
27
28impl<'ctx, 'tcx> VmState<'ctx, 'tcx> {
29 pub fn exec_call(
37 &mut self,
38 func: &Operand<'tcx>,
39 args: &[Spanned<Operand<'tcx>>],
40 destination: Local,
41 _target: Option<BasicBlock>,
42 _cleanup: Option<BasicBlock>,
43 caller_def_id: DefId,
44 ) {
45 let arg_values: Vec<VmValue<'ctx, 'tcx>> = args
46 .iter()
47 .map(|arg| self.value_of_operand(&arg.node))
48 .collect();
49
50 let name = crate::helpers::mir_utils::call_name(self.tcx, func);
51 let callee = crate::helpers::mir_utils::dep_callee_def_id(func);
52 let caller_arg_locals: Vec<Option<Local>> = args.iter()
53 .map(|a| a.node.place().map(|p| p.local))
54 .collect();
55
56 if self.try_select_unpredictable(&name, &arg_values, args, destination) {
58 return;
59 }
60
61 if self.try_slice_index(&name, &arg_values, args, destination) {
64 return;
65 }
66
67 if self.try_iter_len_is_empty(&name, &arg_values, args, destination) {
69 return;
70 }
71
72 if self.try_iter_next(&name, &arg_values, args, destination) {
74 return;
75 }
76
77 if let Some(c) = callee {
82 if self.tcx.is_mir_available(c) {
83 let cname = self.tcx.def_path_str(c);
84 if (api_classify::is_iter_ptr_adj(&cname))
85 && arg_values.len() >= 2
86 {
87 self.apply_iter_ptr_update(c, &cname, &arg_values, &caller_arg_locals);
88 }
90 }
91 }
92
93 if let Some(c) = callee {
98 if self.tcx.is_mir_available(c) {
99 let has_fn_sim = crate::verify::call_summary::fn_simulator::lookup_effect(
100 self.tcx, caller_def_id, Some(c), &name, func, destination,
101 ).is_some();
102 if !has_fn_sim {
103 if self.exec_inline_call(c, &arg_values, &caller_arg_locals, destination) {
104 self.materialize_const_bytes_after_call(args, destination);
105 return;
106 }
107 }
108 }
109 }
110
111 let summary = call_summary::effect_summary(
112 self.tcx,
113 caller_def_id,
114 func,
115 destination,
116 );
117
118 self.last_call_name = summary.name.clone();
119
120 if !summary.unsupported {
121 for effect in &summary.effects {
122 self.apply_call_effect(effect, &arg_values, &caller_arg_locals, destination);
123 }
124 } else {
125 self.notes.push(format!("unsupported call: {}", summary.name));
126 let dest_ty = self.body.local_decls[destination].ty;
127 let term = self.fresh_int(&format!("callret_{}", destination.as_usize()));
128 if let TyKind::Adt(adt_def, _) = dest_ty.kind() {
129 let path = self.tcx.def_path_str(adt_def.did());
130 if api_classify::is_std_ordering(&path) {
131 let minus_one = Int::from_i64(self.ctx, -1);
132 let one = Int::from_i64(self.ctx, 1);
133 self.path_conditions.push(term.ge(&minus_one));
134 self.path_conditions.push(term.le(&one));
135 }
136 }
137 if dest_ty.is_bool() {
139 let zero = Int::from_u64(self.ctx, 0);
140 let one = Int::from_u64(self.ctx, 1);
141 self.path_conditions.push(term.ge(&zero));
142 self.path_conditions.push(term.le(&one));
143 }
144 self.set_local(
145 destination,
146 VmValue {
147 term,
148 ty: dest_ty,
149 provenance: None,
150 invariants: ValueInvariants::default(),
151 },
152 );
153 return;
154 }
155
156 self.materialize_const_bytes_after_call(args, destination);
157 }
158
159 fn try_select_unpredictable(
161 &mut self,
162 name: &str,
163 arg_values: &[VmValue<'ctx, 'tcx>],
164 args: &[Spanned<Operand<'tcx>>],
165 destination: Local,
166 ) -> bool {
167 if !api_classify::is_select_unpredictable(name) || arg_values.len() < 3 {
168 return false;
169 }
170 let term = self.fresh_int(&format!("selunpred_{}", destination.as_usize()));
171 let dest_ty = self.body.local_decls[destination].ty;
172 let eq1 = term._eq(&arg_values[1].term);
173 let eq2 = term._eq(&arg_values[2].term);
174 self.path_conditions.push(Bool::or(self.ctx, &[&eq1, &eq2]));
175 let prov = arg_values[1].provenance.clone()
176 .or_else(|| arg_values[2].provenance.clone());
177 let dest_pk = PlaceKey { base: PlaceBaseKey::Local(destination.as_usize()), fields: vec![] };
181 let lhs_pk = args.get(1).and_then(|a| operand_place(&a.node));
182 let rhs_pk = args.get(2).and_then(|a| operand_place(&a.node));
183 self.other_op_sources.insert(dest_pk, (lhs_pk, rhs_pk));
184 self.set_local(destination, VmValue {
185 term,
186 ty: dest_ty,
187 provenance: prov,
188 invariants: ValueInvariants::default(),
189 });
190 true
191 }
192
193 fn try_slice_index(
200 &mut self,
201 name: &str,
202 arg_values: &[VmValue<'ctx, 'tcx>],
203 args: &[Spanned<Operand<'tcx>>],
204 destination: Local,
205 ) -> bool {
206 let is_index = name.ends_with("::Index::index") || name.ends_with("::IndexMut::index_mut");
207 if !is_index || arg_values.len() < 2 {
208 return false;
209 }
210 let dest_ty = self.body.local_decls[destination].ty;
211 let is_slice = matches!(dest_ty.kind(), TyKind::Ref(_, inner, _)
212 if matches!(inner.kind(), TyKind::Slice(_)));
213 if !is_slice {
214 return false;
215 }
216 let Some(prov) = arg_values[0].provenance.clone() else {
217 return false;
218 };
219 let array_term = arg_values[0].term.clone();
220 let (elem_ty, elem_size) = match arg_values[0].ty.kind() {
221 TyKind::Ref(_, inner, _) => match inner.kind() {
222 TyKind::Array(e, _) | TyKind::Slice(e) => {
223 (*e, self.size_of_ty(*e).max(1) as u64)
224 }
225 _ => (arg_values[0].ty, 1),
226 },
227 _ => (arg_values[0].ty, 1),
228 };
229 let elem_align = self.align_of_ty(elem_ty).max(1);
230 let range_local = args.get(1).and_then(|a| match &a.node {
238 Operand::Copy(p) | Operand::Move(p) => Some(p.local),
239 _ => None,
240 });
241 let range_field = |idx: usize| -> Option<Int<'ctx>> {
242 range_local.and_then(|l| self.field_value(l, &[idx]).map(|v| v.term.clone()))
243 };
244 let zero = Int::from_u64(self.ctx, 0);
245 let one = Int::from_u64(self.ctx, 1);
246 let total_len = self.alloc(prov.alloc_id).size.clone()
247 .div(&Int::from_u64(self.ctx, elem_size));
248 let range_ty_path = arg_values.get(1).and_then(|v| match v.ty.kind() {
249 TyKind::Adt(adt_def, _) => Some(self.tcx.def_path_str(adt_def.did())),
250 _ => None,
251 });
252 let (start, len) = match range_ty_path.as_deref() {
253 Some(p) if p.ends_with("::RangeTo") => {
254 (zero.clone(), range_field(0).unwrap_or_else(|| total_len.clone()))
255 }
256 Some(p) if p.ends_with("::RangeFrom") => {
257 let s = range_field(0).unwrap_or_else(|| zero.clone());
258 (s.clone(), Int::sub(self.ctx, &[&total_len, &s]))
259 }
260 Some(p) if p.ends_with("::Range") || p.ends_with("::RangeInclusive") => {
261 let inclusive = p.ends_with("::RangeInclusive");
262 let s = range_field(0).unwrap_or_else(|| zero.clone());
263 let e = range_field(1).unwrap_or_else(|| total_len.clone());
264 let l = Int::sub(self.ctx, &[&e, &s]);
265 (s.clone(), if inclusive { Int::add(self.ctx, &[&l, &one]) } else { l })
266 }
267 _ => (zero.clone(), total_len.clone()),
268 };
269 let elem_size_term = Int::from_u64(self.ctx, elem_size);
270 let start_bytes = Int::mul(self.ctx, &[&start, &elem_size_term]);
271 let size_bytes = Int::mul(self.ctx, &[&len, &elem_size_term]);
272 let dest_term = Int::add(self.ctx, &[&array_term, &start_bytes]);
273 let (alloc_id, _) = self.allocate(size_bytes, elem_align, Some(elem_ty));
274 self.alloc_mut(alloc_id).parent = Some(prov.alloc_id);
275 self.set_local(destination, VmValue {
276 term: dest_term,
277 ty: dest_ty,
278 provenance: Some(Provenance {
279 alloc_id,
280 offset: Int::from_u64(self.ctx, 0),
281 is_field_offset: false,
282 }),
283 invariants: ValueInvariants {
284 non_null: true, aligned: true, init: true, in_bounds: true,
285 ..Default::default()
286 },
287 });
288 true
289 }
290
291 fn try_iter_len_is_empty(
296 &mut self,
297 name: &str,
298 arg_values: &[VmValue<'ctx, 'tcx>],
299 args: &[Spanned<Operand<'tcx>>],
300 destination: Local,
301 ) -> bool {
302 if !((name.contains("::Iter<") || name.contains("::IterMut<")
303 || name.ends_with("::Iter::len") || name.ends_with("::IterMut::len")
304 || name.ends_with("::Iter::is_empty") || name.ends_with("::IterMut::is_empty"))
305 && (name.ends_with("::len") || name.ends_with("::is_empty"))
306 && arg_values.len() >= 1)
307 {
308 return false;
309 }
310 let receiver_local = args.first().and_then(|a| a.node.place()).map(|p| p.local);
311 let Some(local) = receiver_local else { return false; };
312 let (Some(ptr), Some(end)) = (self.field_value(local, &[0]), self.field_value(local, &[1])) else {
315 return false;
316 };
317 let (Some(pp), Some(ep)) = (&ptr.provenance, &end.provenance) else {
318 return false;
319 };
320 if pp.alloc_id != ep.alloc_id {
321 return false;
322 }
323 let dest_ty = self.body.local_decls[destination].ty;
324 if name.ends_with("::len") {
325 let diff = Int::sub(self.ctx, &[&ep.offset, &pp.offset]);
326 let sz = Int::from_u64(self.ctx, self.iter_elem_size(ptr));
327 let val = VmValue::new(diff.div(&sz), dest_ty);
328 self.set_local(destination, val);
329 } else {
330 let eq = pp.offset._eq(&ep.offset);
332 let zero = Int::from_u64(self.ctx, 0);
333 let one = Int::from_u64(self.ctx, 1);
334 let val = VmValue {
335 term: eq.ite(&one, &zero),
336 ty: dest_ty,
337 provenance: None,
338 invariants: ValueInvariants::default(),
339 };
340 self.set_local(destination, val);
341 }
342 true
343 }
344
345 fn try_iter_next(
350 &mut self,
351 name: &str,
352 arg_values: &[VmValue<'ctx, 'tcx>],
353 _args: &[Spanned<Operand<'tcx>>],
354 destination: Local,
355 ) -> bool {
356 let is_next = name.contains("::next")
357 && (name.starts_with("Iter::") || name.starts_with("IterMut::")
358 || name.contains("::Iter::") || name.contains("::IterMut::")
359 || name.contains("::Iter<") || name.contains("::IterMut<")
360 || name.contains("::Iterator::next"));
361 if !is_next || arg_values.len() < 1 {
362 return false;
363 }
364 let self_val = &arg_values[0];
365 let Some(local) = self.find_iter_self_local(self_val) else {
366 return false;
367 };
368 let (Some(ptr), Some(end)) = (self.field_value(local, &[0]), self.field_value(local, &[1])) else {
369 return false;
370 };
371 let (Some(pp), Some(ep)) = (&ptr.provenance, &end.provenance) else {
372 return false;
373 };
374 if pp.alloc_id != ep.alloc_id {
375 return false;
376 }
377 let dest_ty = self.body.local_decls[destination].ty;
378 let sz = Int::from_u64(self.ctx, self.iter_elem_size(ptr));
380 let ep_offset = ep.offset.clone();
381 let remaining = if let Some(off) = self.iter_ptr_offset.get(&local) {
382 let base_len = ep_offset.div(&sz);
383 let zero = Int::from_u64(self.ctx, 0);
384 off.gt(&base_len).ite(&zero, &Int::sub(self.ctx, &[&base_len, off]))
385 } else {
386 let diff = Int::sub(self.ctx, &[&ep_offset, &pp.offset]);
387 diff.div(&sz)
388 };
389 let is_empty = remaining._eq(&Int::from_u64(self.ctx, 0));
390 let zero = Int::from_u64(self.ctx, 0);
394 let cur_off = match self.iter_ptr_offset.get(&local) {
395 Some(prev) => Int::mul(self.ctx, &[prev, &sz]),
396 None => pp.offset.clone(),
397 };
398 let old_ptr_val = VmValue {
399 term: cur_off.clone(),
400 ty: ptr.ty,
401 provenance: Some(Provenance {
402 alloc_id: pp.alloc_id,
403 offset: cur_off,
404 is_field_offset: false,
405 }),
406 invariants: ValueInvariants { non_null: true, init: true, ..Default::default() },
407 };
408 let one_term = Int::from_u64(self.ctx, 1);
410 let new_offset = match self.iter_ptr_offset.get(&local) {
411 Some(prev) => Int::add(self.ctx, &[prev, &one_term]),
412 None => one_term.clone(),
413 };
414 self.path_conditions.push(remaining.gt(&zero));
416 let base_len = ep_offset.div(&sz);
418 self.path_conditions.push(new_offset.le(&base_len));
419 self.iter_ptr_offset.insert(local, new_offset);
420 let result_val = VmValue {
422 term: is_empty.ite(&zero, &old_ptr_val.term),
423 ty: dest_ty,
424 provenance: if is_empty.as_bool().unwrap_or(false) { None } else { old_ptr_val.provenance.clone() },
425 invariants: ValueInvariants::default(),
426 };
427 self.set_local(destination, result_val);
428 let discr_term = is_empty.ite(&zero, &one_term);
432 self.discriminant_terms.insert(destination, discr_term);
433 true
434 }
435
436 fn materialize_const_bytes_after_call(
437 &mut self,
438 args: &[Spanned<Operand<'tcx>>],
439 destination: Local,
440 ) {
441 if let Some(mut dv) = self.locals.get(&destination).cloned() {
442 let dest_ty = dv.ty;
443 let pointee_is_byte_like = match dest_ty.kind() {
444 rustc_middle::ty::TyKind::RawPtr(inner, _)
445 | rustc_middle::ty::TyKind::Ref(_, inner, _) => {
446 match inner.kind() {
447 rustc_middle::ty::TyKind::Uint(rustc_middle::ty::UintTy::U8)
448 | rustc_middle::ty::TyKind::Int(rustc_middle::ty::IntTy::I8) => true,
449 rustc_middle::ty::TyKind::Array(elem_ty, _)
450 | rustc_middle::ty::TyKind::Slice(elem_ty) => {
451 matches!(elem_ty.kind(), rustc_middle::ty::TyKind::Uint(rustc_middle::ty::UintTy::U8))
452 }
453 _ => false,
454 }
455 }
456 _ => false,
457 };
458 if pointee_is_byte_like {
459 for arg in args {
460 self.try_materialize_const_bytes(&mut dv, &arg.node);
461 if dv.provenance.is_some() {
462 self.set_local(destination, dv);
463 break;
464 }
465 }
466 }
467 }
468 }
469
470 fn exec_inline_call(
478 &mut self,
479 callee_def_id: DefId,
480 arg_values: &[VmValue<'ctx, 'tcx>],
481 caller_arg_locals: &[Option<Local>],
482 dest: Local,
483 ) -> bool {
484 if self.inline_depth >= MAX_INLINE_DEPTH {
485 return false;
486 }
487 self.inline_depth += 1;
488
489 let callee_body = self.tcx.optimized_mir(callee_def_id);
499 let n_return = callee_body
500 .basic_blocks
501 .iter()
502 .filter(|bb| matches!(bb.terminator().kind, rustc_middle::mir::TerminatorKind::Return))
503 .count();
504 let has_switch = callee_body.basic_blocks.iter_enumerated().any(|(idx, bb)| {
513 !bb.is_cleanup
514 && matches!(bb.terminator().kind, rustc_middle::mir::TerminatorKind::SwitchInt { .. })
515 && !Self::switch_is_debug_assert(self.tcx, &callee_body, idx)
516 });
517 if arg_values.len() > 4 || callee_body.basic_blocks.len() > 16 || n_return > 1 || has_switch {
518 self.inline_depth -= 1;
519 return false;
520 }
521
522 let saved_body = self.body;
524 let saved_caller = self.caller_def_id;
525 let saved_locals = std::mem::take(&mut self.locals);
526 let saved_field_values = std::mem::take(&mut self.field_values);
527 let saved_local_addresses = std::mem::take(&mut self.local_addresses);
528 let saved_local_alloc_ids = std::mem::take(&mut self.local_alloc_ids);
529 let saved_binary_op_sources = std::mem::take(&mut self.binary_op_sources);
530 let saved_other_op_sources = std::mem::take(&mut self.other_op_sources);
531 let saved_iter_ptr_offset = std::mem::take(&mut self.iter_ptr_offset);
532 let saved_discriminant_terms = std::mem::take(&mut self.discriminant_terms);
533
534 self.body = callee_body;
536 self.caller_def_id = callee_def_id;
537
538 for (i, arg_val) in arg_values.iter().enumerate() {
540 let callee_local = Local::from_usize(i + 1);
541 self.ensure_local_allocation(callee_local);
542 self.set_local(callee_local, arg_val.clone());
543 }
544
545 for (i, caller_arg_opt) in caller_arg_locals.iter().enumerate() {
549 let callee_param = Local::from_usize(i + 1);
550 let Some(caller_arg) = caller_arg_opt else { continue; };
551 if *caller_arg == callee_param {
552 continue; }
554 let caller_field_keys: Vec<Vec<usize>> = saved_field_values.keys()
555 .filter(|(l, _)| *l == *caller_arg)
556 .map(|(_, f)| f.clone())
557 .collect();
558 for fields in caller_field_keys {
559 if let Some(fv) = saved_field_values.get(&(*caller_arg, fields.clone())).cloned() {
560 self.set_field_value(callee_param, fields, fv);
561 }
562 }
563 }
564
565 self.inline_execute_body();
567
568 let return_val = self.locals.get(&Local::from_usize(0)).cloned();
570 let return_fields: Vec<(Vec<usize>, VmValue<'ctx, 'tcx>)> = self
571 .field_values
572 .iter()
573 .filter(|((l, _), _)| *l == Local::from_usize(0))
574 .map(|((_, path), val)| (path.clone(), val.clone()))
575 .collect();
576
577 self.body = saved_body;
579 self.caller_def_id = saved_caller;
580 self.locals = saved_locals;
581 self.field_values = saved_field_values;
582 self.local_addresses = saved_local_addresses;
583 self.local_alloc_ids = saved_local_alloc_ids;
584 self.binary_op_sources = saved_binary_op_sources;
585 self.other_op_sources = saved_other_op_sources;
586 self.iter_ptr_offset = saved_iter_ptr_offset;
587 self.discriminant_terms = saved_discriminant_terms;
588
589 let dest_ty = self.body.local_decls[dest].ty;
591 match return_val {
592 Some(mut val) => {
593 val.ty = dest_ty;
594 if let Some(ref prov) = val.provenance {
597 if prov.offset.as_u64() == Some(0) {
598 val.invariants.non_null = true;
599 val.invariants.init = true;
600 val.invariants.aligned = true;
601 self.alloc_mut(prov.alloc_id).initialized = true;
602 }
603 }
604 self.set_local(dest, val);
605 for (path, fv) in return_fields {
609 self.set_field_value(dest, path, fv);
610 }
611 if let Some(dest_alloc_id) = self.local_alloc_ids.get(&dest).copied() {
617 self.alloc_mut(dest_alloc_id).initialized = true;
618 }
619 }
620 None => {
621 self.inline_depth -= 1;
622 return false;
623 }
624 }
625
626 self.inline_depth -= 1;
627 true
628 }
629
630 fn switch_targets_unreachable(
635 tcx: rustc_middle::ty::TyCtxt<'tcx>,
636 body: &rustc_middle::mir::Body<'tcx>,
637 targets: &rustc_middle::mir::SwitchTargets,
638 ) -> bool {
639 targets.iter().all(|(_, target)| {
640 let mut cur = target;
641 let mut seen = FxHashSet::default();
642 loop {
643 if !seen.insert(cur) {
644 return false;
645 }
646 let bb = &body.basic_blocks[cur];
647 let term = bb.terminator();
648 match &term.kind {
649 rustc_middle::mir::TerminatorKind::Unreachable => return true,
650 rustc_middle::mir::TerminatorKind::Call { func, .. } => {
651 let name = crate::helpers::mir_utils::call_name(tcx, func);
652 return name.contains("panic") || name.contains("unreachable") || name.contains("abort");
653 }
654 rustc_middle::mir::TerminatorKind::Goto { target: next } => {
655 cur = *next;
656 }
657 rustc_middle::mir::TerminatorKind::Return => return bb.statements.is_empty(),
662 _ => return false,
663 }
664 }
665 })
666 }
667
668 fn switch_is_debug_assert(
671 tcx: rustc_middle::ty::TyCtxt<'tcx>,
672 body: &rustc_middle::mir::Body<'tcx>,
673 bb: BasicBlock,
674 ) -> bool {
675 let rustc_middle::mir::TerminatorKind::SwitchInt { discr, targets } =
676 &body.basic_blocks[bb].terminator().kind
677 else {
678 return false;
679 };
680 let discr_is_const = match discr {
685 rustc_middle::mir::Operand::Constant(_) => true,
686 rustc_middle::mir::Operand::Copy(p) | rustc_middle::mir::Operand::Move(p) => {
687 body.basic_blocks.iter().any(|bbd| {
688 bbd.statements.iter().any(|stmt| {
689 let rustc_middle::mir::StatementKind::Assign(assign) = &stmt.kind else {
690 return false;
691 };
692 let (dest, rvalue) = &**assign;
693 let is_const = match rvalue {
694 #[cfg(rapx_rvalue_use_with_retag)]
695 rustc_middle::mir::Rvalue::Use(rustc_middle::mir::Operand::Constant(_), _) => true,
696 #[cfg(not(rapx_rvalue_use_with_retag))]
697 rustc_middle::mir::Rvalue::Use(rustc_middle::mir::Operand::Constant(_)) => true,
698 _ => false,
699 };
700 dest == p && is_const
701 })
702 })
703 }
704 #[allow(unreachable_patterns)]
705 _ => false,
706 };
707 if discr_is_const {
708 return true;
709 }
710 Self::switch_targets_unreachable(tcx, body, targets)
711 }
712
713 fn inline_execute_body(&mut self) {
715 let mut visited = FxHashSet::default();
716 let mut queue: Vec<BasicBlock> = Vec::new();
717 queue.push(BasicBlock::from_usize(0));
718
719 while let Some(block) = queue.pop() {
720 if !visited.insert(block) {
721 continue;
722 }
723
724 let bb_data = &self.body.basic_blocks[block];
725
726 for (si, stmt) in bb_data.statements.iter().enumerate() {
728 self.exec_statement(block, si, stmt);
729 }
730
731 let terminator = bb_data.terminator();
733
734 match &terminator.kind {
735 TerminatorKind::Goto { target } => {
736 queue.push(*target);
737 }
738 TerminatorKind::Return => {
739 }
741 TerminatorKind::Assert { cond, expected, target, .. } => {
742 let cond_val = self.value_of_operand(cond);
743 if *expected {
744 let zero = Int::from_u64(self.ctx, 0);
745 self.path_conditions.push(cond_val.term._eq(&zero).not());
746 } else {
747 let zero = Int::from_u64(self.ctx, 0);
748 self.path_conditions.push(cond_val.term._eq(&zero));
749 }
750 self.infer_guard_non_null(cond, *expected);
752 self.infer_guard_align(cond, *expected);
753 queue.push(*target);
754 }
755 TerminatorKind::SwitchInt { discr, targets } => {
756 if let rustc_middle::mir::Operand::Constant(c) = discr {
758 let text = format!("{:?}", c.const_);
759 if let Some(v) = crate::helpers::mir_utils::const_int_from_debug(&text) {
760 let t = targets.iter().find(|(val, _)| *val == v as u128)
761 .map(|(_, t)| t)
762 .unwrap_or_else(|| targets.otherwise());
763 queue.push(t);
764 continue;
765 }
766 }
767 let trivial = Self::switch_targets_unreachable(self.tcx, &self.body, targets);
771 if trivial {
772 queue.push(targets.otherwise());
773 continue;
774 }
775 for (value, target) in targets.iter() {
779 let discr_val = self.value_of_operand(discr);
780 let val_term = Int::from_u64(self.ctx, value as u64);
781 self.path_conditions.push(discr_val.term._eq(&val_term));
782 queue.push(target);
783 }
784 let otherwise = targets.otherwise();
785 queue.push(otherwise);
786 }
787 TerminatorKind::Call {
788 func,
789 args,
790 destination,
791 target,
792 ..
793 } => {
794 self.exec_call(
795 func,
796 args,
797 destination.local,
798 *target,
799 None,
800 self.caller_def_id,
801 );
802 if let Some(t) = target {
803 queue.push(*t);
804 }
805 }
806 TerminatorKind::Drop { place, target, .. } => {
807 self.exec_drop(place);
808 queue.push(*target);
809 }
810 TerminatorKind::Unreachable
811 | TerminatorKind::UnwindResume
812 | TerminatorKind::UnwindTerminate(_)
813 | TerminatorKind::Yield { .. }
814 | TerminatorKind::CoroutineDrop
815 | TerminatorKind::FalseEdge { .. }
816 | TerminatorKind::FalseUnwind { .. }
817 | TerminatorKind::InlineAsm { .. }
818 | TerminatorKind::TailCall { .. } => {
819 }
821 }
822 }
823 }
824
825 fn apply_call_effect(
827 &mut self,
828 effect: &CallEffect,
829 args: &[VmValue<'ctx, 'tcx>],
830 caller_arg_locals: &[Option<Local>],
831 dest: Local,
832 ) {
833 match effect {
834 CallEffect::ReturnAliasArg { arg } => {
835 if let Some(arg_val) = args.get(*arg) {
836 let mut val = arg_val.clone();
837 val.ty = self.body.local_decls[dest].ty;
838 val.invariants.non_null = true;
839 val.invariants.aligned = true;
840 val.invariants.init = true;
841 self.set_local(dest, val);
842 }
843 }
844 CallEffect::ReturnTransparentDeref { arg, peel } => {
845 if let Some(arg_val) = args.get(*arg) {
846 let mut val = arg_val.clone();
847 val.ty = self.body.local_decls[dest].ty;
848 val.invariants.non_null = true;
849 val.invariants.aligned = true;
850 val.invariants.init = true;
851 self.set_local(dest, val);
852 if let Some(arg_local) = caller_arg_locals.get(*arg).copied().flatten() {
856 let keys: Vec<Vec<usize>> = self
857 .field_values
858 .keys()
859 .filter(|(l, _)| *l == arg_local)
860 .map(|(_, p)| p.clone())
861 .collect();
862 for path in keys {
863 if path.len() > *peel
864 && path[..*peel].iter().all(|&f| f == 0)
865 {
866 if let Some(v) =
867 self.field_values.get(&(arg_local, path.clone())).cloned()
868 {
869 self.set_field_value(dest, path[*peel..].to_vec(), v);
870 }
871 }
872 }
873 }
874 }
875 }
876 CallEffect::ReturnTupleFieldLength { field: _field, from_arg: _from_arg } => {
877 if args.len() < 2 {
878 return;
879 }
880 let self_val = &args[0]; let mid_val = &args[1]; let dest_ty = self.body.local_decls[dest].ty;
884 if let TyKind::Tuple(elem_tys) = dest_ty.kind() {
885 let src_alloc_id = self_val.provenance.as_ref().map(|p| p.alloc_id);
887 let _src_offset = self_val.provenance.as_ref()
888 .map(|p| p.offset.clone())
889 .unwrap_or_else(|| Int::from_u64(self.ctx, 0));
890
891 let (elem_ty, elem_sz, alloc_size) = src_alloc_id
892 .map(|id| self.alloc(id))
893 .map(|a| {
894 let ty = a.element_ty;
895 let sz = self.size_of_ty(ty.unwrap_or(self_val.ty)).max(1) as u64;
896 (ty, sz, a.size.clone())
897 })
898 .unwrap_or((None, 1, Int::from_u64(self.ctx, 1)));
899
900 let elem_sz_term = Int::from_u64(self.ctx, elem_sz);
901 let total_len = alloc_size.div(&elem_sz_term); let zero = Int::from_u64(self.ctx, 0);
904 self.path_conditions.push(mid_val.term.ge(&zero));
905 self.path_conditions.push(mid_val.term.le(&total_len));
906
907 let mid = mid_val.term.clone();
909 let rest_len = Int::sub(self.ctx, &[&total_len, &mid]);
911
912 let mid_bytes = Int::mul(self.ctx, &[&mid, &elem_sz_term]);
914 let ptr1 = Int::add(self.ctx, &[&self_val.term, &mid_bytes]);
915
916 for f in 0..elem_tys.len() {
917 let field_ty = elem_tys[f];
918 let (field_len, field_ptr) = if f == 0 {
919 (mid.clone(), self_val.term.clone())
920 } else {
921 (rest_len.clone(), ptr1.clone())
922 };
923 let field_size = Int::mul(self.ctx, &[&field_len, &elem_sz_term]);
924 let field_alloc_align = self_val.provenance.as_ref()
925 .map(|p| self.alloc(p.alloc_id).align)
926 .unwrap_or(1);
927
928 let (alloc_id, _base) = self.allocate(
929 field_size.clone(), field_alloc_align, elem_ty,
930 );
931 let src_bytes = Int::mul(self.ctx, &[&total_len, &elem_sz_term]);
932 if f == 0 {
933 self.path_conditions.push(field_size._eq(&mid_bytes));
934 } else {
935 let remaining = Int::sub(self.ctx, &[&src_bytes, &mid_bytes]);
936 self.path_conditions.push(field_size._eq(&remaining));
937 }
938 self.alloc_mut(alloc_id).initialized = true;
939 if let Some(ref source_prov) = self_val.provenance {
940 self.alloc_mut(alloc_id).parent = Some(source_prov.alloc_id);
941 }
942 if let Some(ref_dest_alloc_id) = self.local_alloc_ids.get(&dest).copied() {
943 self.alloc_mut(ref_dest_alloc_id).slice_data = Some(alloc_id);
944 }
945
946 let field_offset = Int::from_u64(self.ctx, 0);
947
948 let field_prov = Provenance {
949 alloc_id,
950 offset: field_offset,
951 is_field_offset: false,
952 };
953
954 let field_val = VmValue {
955 term: field_ptr,
956 ty: field_ty,
957 provenance: Some(field_prov),
958 invariants: ValueInvariants {
959 init: true,
960 non_null: true,
961 aligned: true,
962 in_bounds: true,
963 align_n: Some(field_alloc_align),
964 is_field_offset: false,
965 },
966 };
967 self.set_field_value(dest, vec![f], field_val);
968 }
969 }
970 }
971 CallEffect::ReturnIter { receiver_arg } => {
972 let Some(self_val) = args.get(*receiver_arg).cloned() else { return };
973 let Some(src_prov) = self_val.provenance.clone() else { return };
974 let root_alloc_id = {
980 let mut id = src_prov.alloc_id;
981 while let Some(parent) = self.alloc(id).parent {
982 id = parent;
983 }
984 id
985 };
986 let slice_len = self.alloc(src_prov.alloc_id).size.clone();
987
988 let field_ty = match self_val.ty.kind() {
992 TyKind::Ref(_, inner, _) => match inner.kind() {
993 TyKind::Slice(t) => *t,
994 _ => self_val.ty,
995 },
996 _ => self_val.ty,
997 };
998
999 let start_off = Int::from_u64(self.ctx, 0);
1000 let end_term = Int::add(self.ctx, &[&self_val.term, &slice_len]);
1001
1002 let start_val = VmValue {
1003 term: self_val.term.clone(),
1004 ty: field_ty,
1005 provenance: Some(Provenance {
1006 alloc_id: root_alloc_id,
1007 offset: start_off,
1008 is_field_offset: false,
1009 }),
1010 invariants: ValueInvariants { init: true, non_null: true, ..Default::default() },
1011 };
1012 let end_val = VmValue {
1013 term: end_term,
1014 ty: field_ty,
1015 provenance: Some(Provenance {
1016 alloc_id: root_alloc_id,
1017 offset: slice_len,
1018 is_field_offset: false,
1019 }),
1020 invariants: ValueInvariants { init: true, non_null: true, ..Default::default() },
1021 };
1022 self.set_field_value(dest, vec![0], start_val);
1023 self.set_field_value(dest, vec![1], end_val);
1024 }
1025 CallEffect::ReturnAlignTo { receiver_arg } => {
1026 let Some(self_val) = args.get(*receiver_arg).cloned() else { return };
1027 let dest_ty = self.body.local_decls[dest].ty;
1028 let TyKind::Tuple(elem_tys) = dest_ty.kind() else { return };
1029 if elem_tys.len() < 3 { return; }
1030
1031 let body_elem_ty = match elem_tys[1].kind() {
1033 TyKind::Ref(_, inner, _) => match inner.kind() {
1034 TyKind::Slice(u) => *u,
1035 _ => return,
1036 },
1037 _ => return,
1038 };
1039 let size_u = self.size_of_ty(body_elem_ty).max(1) as u64;
1040 let align_u = self.align_of_ty(body_elem_ty).max(1);
1041
1042 let Some(src_prov) = self_val.provenance.clone() else { return };
1043 let alloc = self.alloc(src_prov.alloc_id);
1044 let (elem_ty, elem_sz, len_bytes) = {
1045 let ty = alloc.element_ty;
1046 let sz = self.size_of_ty(ty.unwrap_or(self_val.ty)).max(1) as u64;
1047 (ty, sz, alloc.size.clone())
1048 };
1049
1050 let elem_sz_term = Int::from_u64(self.ctx, elem_sz);
1051 let size_u_term = Int::from_u64(self.ctx, size_u);
1052 let align_u_term = Int::from_u64(self.ctx, align_u);
1053
1054 let offset = self.fresh_int(&format!("align_to_offset_{}", dest.as_usize()));
1057 let zero = Int::from_u64(self.ctx, 0);
1058 let ptr_plus_offset = Int::add(self.ctx, &[&self_val.term, &offset]);
1059 self.path_conditions.push(ptr_plus_offset.rem(&align_u_term)._eq(&zero));
1060 self.path_conditions.push(offset.ge(&zero));
1061 self.path_conditions.push(offset.lt(&align_u_term));
1062
1063 let body_bytes = Int::sub(self.ctx, &[&len_bytes, &offset]);
1068 let body_len = body_bytes.div(&size_u_term);
1069 let suffix_bytes = body_bytes.rem(&size_u_term);
1070 let mul_term = Int::mul(self.ctx, &[&body_len, &size_u_term]);
1071 let sum_term = Int::add(self.ctx, &[&mul_term, &suffix_bytes]);
1072 self.path_conditions.push(body_bytes._eq(&sum_term));
1073 self.path_conditions.push(suffix_bytes.ge(&zero));
1074 self.path_conditions.push(suffix_bytes.lt(&size_u_term));
1075
1076 let prefix_len = offset.div(&elem_sz_term);
1078 let suffix_len = suffix_bytes.div(&elem_sz_term);
1079
1080 let body_byte_len = Int::mul(self.ctx, &[&body_len, &size_u_term]);
1081 let suffix_ptr = Int::add(self.ctx, &[&ptr_plus_offset, &body_byte_len]);
1082
1083 let base_align = self.alloc(src_prov.alloc_id).align;
1084
1085 let fields: Vec<(Int<'ctx>, Int<'ctx>, Ty<'tcx>, u64, u64)> = vec![
1086 (prefix_len, self_val.term.clone(), elem_tys[0], elem_sz, base_align),
1087 (body_len, ptr_plus_offset, elem_tys[1], size_u, align_u),
1088 (suffix_len, suffix_ptr, elem_tys[2], elem_sz, base_align),
1089 ];
1090
1091 for (f, (f_len, f_ptr, f_ty, f_elem_sz, f_align)) in fields.into_iter().enumerate() {
1092 let f_size = Int::mul(self.ctx, &[&f_len, &Int::from_u64(self.ctx, f_elem_sz)]);
1093 let f_elem_ty = if f == 1 { Some(body_elem_ty) } else { elem_ty };
1094 let (alloc_id, _) = self.allocate(f_size.clone(), f_align, f_elem_ty);
1095 self.alloc_mut(alloc_id).initialized = true;
1096 self.alloc_mut(alloc_id).parent = Some(src_prov.alloc_id);
1097 if let Some(ref_dest_alloc_id) = self.local_alloc_ids.get(&dest).copied() {
1098 self.alloc_mut(ref_dest_alloc_id).slice_data = Some(alloc_id);
1099 }
1100 let field_val = VmValue {
1101 term: f_ptr,
1102 ty: f_ty,
1103 provenance: Some(Provenance {
1104 alloc_id,
1105 offset: Int::from_u64(self.ctx, 0),
1106 is_field_offset: false,
1107 }),
1108 invariants: ValueInvariants {
1109 init: true, non_null: true, aligned: true, in_bounds: true,
1110 align_n: if f_align > 1 { Some(f_align) } else { None },
1111 is_field_offset: false,
1112 },
1113 };
1114 self.set_field_value(dest, vec![f], field_val);
1115 }
1116 }
1117 CallEffect::ReturnPointerFromArg { arg } => {
1118 if let Some(arg_val) = args.get(*arg) {
1119 let mut val = arg_val.clone();
1120 let dest_ty = self.body.local_decls[dest].ty;
1121 val.ty = dest_ty;
1122 val.invariants.non_null = true;
1123 val.invariants.aligned = true;
1124 if matches!(dest_ty.kind(), rustc_middle::ty::TyKind::RawPtr(..)) {
1127 val.invariants.init = true;
1128 }
1129 let is_vec = api_classify::is_vec_or_cstring_call(&self.last_call_name);
1132 if is_vec {
1133 if let Some(ref prov) = val.provenance {
1134 if let Some(data_alloc) = self.alloc(prov.alloc_id).slice_data {
1135 if let Some(data_base) = self.allocation_base(data_alloc).cloned() {
1136 val.term = data_base;
1137 val.provenance = Some(Provenance {
1138 alloc_id: data_alloc,
1139 offset: Int::from_u64(self.ctx, 0),
1140 is_field_offset: false,
1141 });
1142 }
1143 }
1144 }
1145 }
1146 self.set_local(dest, val);
1147 }
1148 }
1149 CallEffect::ReturnPointerAdd { base_arg, offset_arg, stride } => {
1150 if let (Some(base), Some(offset)) = (args.get(*base_arg), args.get(*offset_arg)) {
1151 let stride_bytes = stride.unwrap_or(1);
1152 let adjusted_offset = if stride_bytes == 1 {
1153 Int::add(self.ctx, &[&offset.term])
1154 } else {
1155 let stride_term = Int::from_u64(self.ctx, stride_bytes);
1156 Int::mul(self.ctx, &[&offset.term, &stride_term])
1157 };
1158 let new_term = Int::add(self.ctx, &[&base.term, &adjusted_offset]);
1159 let is_field_offset = offset.invariants.is_field_offset
1162 && base
1163 .provenance
1164 .as_ref()
1165 .is_some_and(|p| p.offset.as_u64() == Some(0));
1166 let adjusted_provenance = base.provenance.as_ref().map(|prov| {
1167 Provenance {
1168 alloc_id: prov.alloc_id,
1169 offset: Int::add(self.ctx, &[&prov.offset, &adjusted_offset]),
1170 is_field_offset,
1171 }
1172 });
1173 let align_n = self.compute_pointer_add_align(base, offset, stride_bytes);
1175 let val = VmValue {
1176 term: new_term,
1177 ty: self.body.local_decls[dest].ty,
1178 provenance: adjusted_provenance,
1179 invariants: ValueInvariants {
1180 non_null: base.invariants.non_null,
1181 aligned: align_n.is_some() && base.invariants.aligned,
1182 in_bounds: base.invariants.in_bounds,
1183 align_n,
1184 init: base.invariants.init,
1185 is_field_offset: false,
1186 },
1187 };
1188 self.set_local(dest, val);
1189 }
1190 }
1191 CallEffect::ReturnPointerSub { base_arg, offset_arg, stride } => {
1192 if let (Some(base), Some(offset)) = (args.get(*base_arg), args.get(*offset_arg)) {
1193 let stride_bytes = stride.unwrap_or(1);
1194 let stride_term = Int::from_u64(self.ctx, stride_bytes);
1195 let scaled = Int::mul(self.ctx, &[&offset.term, &stride_term]);
1196 let new_term = Int::sub(self.ctx, &[&base.term, &scaled]);
1197 let adjusted_provenance = base.provenance.as_ref().map(|prov| {
1198 Provenance {
1199 alloc_id: prov.alloc_id,
1200 offset: Int::sub(self.ctx, &[&prov.offset, &scaled]),
1201 is_field_offset: false,
1202 }
1203 });
1204 let align_n = self.compute_pointer_add_align(base, offset, stride_bytes);
1205 let val = VmValue {
1206 term: new_term,
1207 ty: self.body.local_decls[dest].ty,
1208 provenance: adjusted_provenance,
1209 invariants: ValueInvariants {
1210 non_null: base.invariants.non_null,
1211 aligned: align_n.is_some() && base.invariants.aligned,
1212 in_bounds: base.invariants.in_bounds,
1213 align_n,
1214 init: base.invariants.init,
1215 is_field_offset: false,
1216 },
1217 };
1218 self.set_local(dest, val);
1219 }
1220 }
1221 CallEffect::CleanSliceDataLinks { arg } => {
1222 if let Some(arg_val) = args.get(*arg) {
1223 if let Some(ref prov) = arg_val.provenance {
1224 self.alloc_mut(prov.alloc_id).slice_data = None;
1225 }
1226 }
1227 }
1228 CallEffect::ReturnNonZero => {
1229 let zero = Int::from_u64(self.ctx, 0);
1230 if let Some(mut existing) = self.locals.get(&dest).cloned() {
1231 existing.invariants.non_null = true;
1232 self.path_conditions.push(existing.term._eq(&zero).not());
1237 self.set_local(dest, existing);
1238 } else {
1239 let dest_ty = self.body.local_decls[dest].ty;
1240 let term = self.fresh_int(&format!("ret_nz_{}", dest.as_usize()));
1241 self.path_conditions.push(term._eq(&zero).not());
1242 self.set_local(dest, VmValue {
1243 term, ty: dest_ty, provenance: None,
1244 invariants: ValueInvariants { non_null: true, ..Default::default() },
1245 });
1246 }
1247 }
1248 CallEffect::ReturnTupleFieldNonZero { field } => {
1249 let dest_ty = self.body.local_decls[dest].ty;
1250 if let TyKind::Tuple(elem_tys) = dest_ty.kind() {
1251 if let Some(field_ty) = elem_tys.get(*field) {
1252 let zero = Int::from_u64(self.ctx, 0);
1253 let term = self
1254 .fresh_int(&format!("ret_tup_nz_{}_{}", dest.as_usize(), field));
1255 self.path_conditions.push(term._eq(&zero).not());
1256 self.set_field_value(dest, vec![*field], VmValue {
1257 term,
1258 ty: *field_ty,
1259 provenance: None,
1260 invariants: ValueInvariants { non_null: true, init: true, ..Default::default() },
1261 });
1262 }
1263 }
1264 }
1265 CallEffect::ReturnAligned { align: _, ty_name: _ } => {
1266 if let Some(mut existing) = self.locals.get(&dest).cloned() {
1267 existing.invariants.aligned = true;
1268 existing.invariants.non_null = true;
1269 self.set_local(dest, existing);
1270 } else {
1271 let dest_ty = self.body.local_decls[dest].ty;
1272 let term = self.fresh_int(&format!("ret_align_{}", dest.as_usize()));
1273 self.set_local(dest, VmValue {
1274 term, ty: dest_ty, provenance: None,
1275 invariants: ValueInvariants { aligned: true, non_null: true, ..Default::default() },
1276 });
1277 }
1278 }
1279 CallEffect::ReturnLengthOfArg { arg } => {
1280 if let Some(arg_val) = args.get(*arg) {
1281 if self.interpreter_iter_len(arg_val, dest) {
1285 return;
1286 }
1287 let effective_alloc_id = arg_val.provenance_alloc_id()
1288 .and_then(|pid| self.alloc(pid).slice_data)
1289 .or_else(|| arg_val.provenance_alloc_id());
1290
1291 if let Some(alloc_id) = effective_alloc_id {
1292 let dest_ty = self.body.local_decls[dest].ty;
1293 if let Some(elem_ty) = self.alloc(alloc_id).element_ty {
1297 let elem_size = self.size_of_ty(elem_ty) as u64;
1298 if elem_size > 1 {
1299 if let Some(size) = self.allocation_size(alloc_id) {
1300 let div = Int::from_u64(self.ctx, elem_size);
1301 let val = VmValue::new(size.div(&div), dest_ty);
1302 self.set_local(dest, val);
1303 return;
1304 }
1305 } else if let Some(size) = self.allocation_size(alloc_id) {
1306 let val = VmValue::new(size.clone(), dest_ty);
1307 self.set_local(dest, val);
1308 return;
1309 }
1310 } else if let Some(size) = self.allocation_size(alloc_id) {
1311 let val = VmValue::new(size.clone(), dest_ty);
1312 self.set_local(dest, val);
1313 return;
1314 }
1315 }
1316 }
1317 let dest_ty = self.body.local_decls[dest].ty;
1318 let term = self.fresh_int(&format!("len_{}", dest.as_usize()));
1319 let val = VmValue {
1320 term,
1321 ty: dest_ty,
1322 provenance: None,
1323 invariants: ValueInvariants::default(),
1324 };
1325 self.set_local(dest, val);
1326 }
1327 CallEffect::ReturnIsEmptyOfArg { arg } => {
1328 if let Some(arg_val) = args.get(*arg) {
1329 if self.interpreter_iter_is_empty(arg_val, dest) {
1330 return;
1331 }
1332 let effective_alloc_id = arg_val.provenance_alloc_id()
1333 .and_then(|pid| self.alloc(pid).slice_data)
1334 .or_else(|| arg_val.provenance_alloc_id());
1335 if let Some(alloc_id) = effective_alloc_id {
1336 if let Some(len_term) = self.allocation_size(alloc_id).cloned() {
1337 let zero = Int::from_u64(self.ctx, 0);
1338 let one = Int::from_u64(self.ctx, 1);
1339 let dest_ty = self.body.local_decls[dest].ty;
1340 let cond = len_term._eq(&zero);
1341 let val = VmValue {
1342 term: cond.ite(&one, &zero),
1343 ty: dest_ty,
1344 provenance: None,
1345 invariants: ValueInvariants::default(),
1346 };
1347 self.set_local(dest, val);
1348 return;
1349 }
1350 }
1351 }
1352 let dest_ty = self.body.local_decls[dest].ty;
1353 let one = Int::from_u64(self.ctx, 1);
1354 let zero = Int::from_u64(self.ctx, 0);
1355 let fresh = self.fresh_int(&format!("empty_{}", dest.as_usize()));
1356 let val = VmValue {
1357 term: fresh.le(&zero).ite(&one, &zero),
1358 ty: dest_ty,
1359 provenance: None,
1360 invariants: ValueInvariants::default(),
1361 };
1362 self.set_local(dest, val);
1363 }
1364 CallEffect::ReturnOffsetFromUnsigned { self_arg, origin_arg } => {
1365 if let (Some(self_val), Some(origin_val)) = (args.get(*self_arg), args.get(*origin_arg)) {
1366 let dest_ty = self.body.local_decls[dest].ty;
1367 if let (Some(self_prov), Some(origin_prov)) = (&self_val.provenance, &origin_val.provenance) {
1368 let elem_ty = match self_val.ty.kind() {
1371 TyKind::Adt(_, substs) => substs.first().and_then(|s| s.as_type()),
1372 _ => None,
1373 };
1374 let elem_size = elem_ty.map(|t| self.size_of_ty(t).max(1)).unwrap_or(1) as u64;
1375 let diff = Int::sub(self.ctx, &[&self_prov.offset, &origin_prov.offset]);
1376 let sz = Int::from_u64(self.ctx, elem_size);
1377 let val = VmValue::new(diff.div(&sz), dest_ty);
1378 self.set_local(dest, val);
1379 return;
1380 }
1381 let term = self.fresh_int(&format!("offset_{}", dest.as_usize()));
1383 let val = VmValue {
1384 term,
1385 ty: dest_ty,
1386 provenance: None,
1387 invariants: ValueInvariants::default(),
1388 };
1389 self.set_local(dest, val);
1390 return;
1391 }
1392 let dest_ty = self.body.local_decls[dest].ty;
1393 let term = self.fresh_int(&format!("offset_{}", dest.as_usize()));
1394 let val = VmValue {
1395 term,
1396 ty: dest_ty,
1397 provenance: None,
1398 invariants: ValueInvariants::default(),
1399 };
1400 self.set_local(dest, val);
1401 }
1402 CallEffect::ReturnConst { value, label: _ } => {
1403 let dest_ty = self.body.local_decls[dest].ty;
1404 let term = Int::from_u64(self.ctx, *value);
1405 let val = VmValue {
1406 term,
1407 ty: dest_ty,
1408 provenance: None,
1409 invariants: ValueInvariants::default(),
1410 };
1411 self.set_local(dest, val);
1412 }
1413 CallEffect::ReturnAlignOffset { ptr_arg, align_arg } => {
1414 let dest_ty = self.body.local_decls[dest].ty;
1415 let offset = self.fresh_int(&format!("align_offset_{}", dest.as_usize()));
1416 if let (Some(ptr_val), Some(align_val)) = (args.get(*ptr_arg), args.get(*align_arg)) {
1417 let zero = Int::from_u64(self.ctx, 0);
1421 let ptr_plus_off = Int::add(self.ctx, &[&ptr_val.term, &offset]);
1422 self.path_conditions
1423 .push(ptr_plus_off.rem(&align_val.term)._eq(&zero));
1424 self.path_conditions.push(offset.ge(&zero));
1425 self.path_conditions.push(offset.lt(&align_val.term));
1426 }
1427 let val = VmValue {
1428 term: offset,
1429 ty: dest_ty,
1430 provenance: None,
1431 invariants: ValueInvariants::default(),
1432 };
1433 self.set_local(dest, val);
1434 }
1435 CallEffect::ReturnMin { lhs_arg, rhs_arg } => {
1436 if let (Some(lhs), Some(rhs)) = (args.get(*lhs_arg), args.get(*rhs_arg)) {
1437 let dest_ty = self.body.local_decls[dest].ty;
1438 let term = lhs.term.le(&rhs.term).ite(&lhs.term, &rhs.term);
1449 let val = VmValue {
1450 term,
1451 ty: dest_ty,
1452 provenance: None,
1453 invariants: ValueInvariants::default(),
1454 };
1455 self.set_local(dest, val);
1456 }
1457 }
1458 CallEffect::ReturnMax { lhs_arg, rhs_arg } => {
1459 if let (Some(lhs), Some(rhs)) = (args.get(*lhs_arg), args.get(*rhs_arg)) {
1460 let dest_ty = self.body.local_decls[dest].ty;
1461 let term = lhs.term.ge(&rhs.term).ite(&lhs.term, &rhs.term);
1462 let val = VmValue {
1463 term,
1464 ty: dest_ty,
1465 provenance: None,
1466 invariants: ValueInvariants::default(),
1467 };
1468 self.set_local(dest, val);
1469 }
1470 }
1471 CallEffect::ReturnClamp { value_arg, min_arg, max_arg } => {
1472 if let (Some(v), Some(mn), Some(mx)) =
1473 (args.get(*value_arg), args.get(*min_arg), args.get(*max_arg))
1474 {
1475 let dest_ty = self.body.local_decls[dest].ty;
1476 let upper = v.term.gt(&mx.term).ite(&mx.term, &v.term);
1478 let term = v.term.lt(&mn.term).ite(&mn.term, &upper);
1479 let val = VmValue {
1480 term,
1481 ty: dest_ty,
1482 provenance: None,
1483 invariants: ValueInvariants::default(),
1484 };
1485 self.set_local(dest, val);
1486 }
1487 }
1488 CallEffect::ReturnAbs { arg } => {
1489 if let Some(a) = args.get(*arg) {
1490 let dest_ty = self.body.local_decls[dest].ty;
1491 let zero = Int::from_u64(self.ctx, 0);
1492 let neg = Int::sub(self.ctx, &[&zero, &a.term]);
1493 let term = a.term.ge(&zero).ite(&a.term, &neg);
1494 let val = VmValue {
1495 term,
1496 ty: dest_ty,
1497 provenance: None,
1498 invariants: ValueInvariants::default(),
1499 };
1500 self.set_local(dest, val);
1501 }
1502 }
1503 CallEffect::ReturnNeg { arg } => {
1504 if let Some(a) = args.get(*arg) {
1505 let dest_ty = self.body.local_decls[dest].ty;
1506 let zero = Int::from_u64(self.ctx, 0);
1507 let term = Int::sub(self.ctx, &[&zero, &a.term]);
1508 let val = VmValue {
1509 term,
1510 ty: dest_ty,
1511 provenance: None,
1512 invariants: ValueInvariants::default(),
1513 };
1514 self.set_local(dest, val);
1515 }
1516 }
1517 CallEffect::ReturnAdd { lhs_arg, rhs_arg } => {
1518 if let (Some(lhs), Some(rhs)) = (args.get(*lhs_arg), args.get(*rhs_arg)) {
1519 let dest_ty = self.body.local_decls[dest].ty;
1520 let term = Int::add(self.ctx, &[&lhs.term, &rhs.term]);
1521 let val = VmValue {
1522 term,
1523 ty: dest_ty,
1524 provenance: None,
1525 invariants: ValueInvariants::default(),
1526 };
1527 self.set_local(dest, val);
1528 }
1529 }
1530 CallEffect::ReturnSub { lhs_arg, rhs_arg } => {
1531 if let (Some(lhs), Some(rhs)) = (args.get(*lhs_arg), args.get(*rhs_arg)) {
1532 let dest_ty = self.body.local_decls[dest].ty;
1533 let term = Int::sub(self.ctx, &[&lhs.term, &rhs.term]);
1534 let val = VmValue {
1535 term,
1536 ty: dest_ty,
1537 provenance: None,
1538 invariants: ValueInvariants::default(),
1539 };
1540 self.set_local(dest, val);
1541 }
1542 }
1543 CallEffect::ReturnMul { lhs_arg, rhs_arg } => {
1544 if let (Some(lhs), Some(rhs)) = (args.get(*lhs_arg), args.get(*rhs_arg)) {
1545 let dest_ty = self.body.local_decls[dest].ty;
1546 let term = Int::mul(self.ctx, &[&lhs.term, &rhs.term]);
1547 let val = VmValue {
1548 term,
1549 ty: dest_ty,
1550 provenance: None,
1551 invariants: ValueInvariants::default(),
1552 };
1553 self.set_local(dest, val);
1554 }
1555 }
1556 CallEffect::ReturnOptionSomeAdd { lhs_arg, rhs_arg } => {
1557 if let (Some(lhs), Some(rhs)) = (args.get(*lhs_arg), args.get(*rhs_arg)) {
1558 let term = Int::add(self.ctx, &[&lhs.term, &rhs.term]);
1564 self.set_field_value(dest, vec![0], VmValue {
1565 term,
1566 ty: lhs.ty,
1567 provenance: None,
1568 invariants: ValueInvariants::default(),
1569 });
1570 }
1571 }
1572 CallEffect::ReturnOptionSomeMul { lhs_arg, rhs_arg } => {
1573 if let (Some(lhs), Some(rhs)) = (args.get(*lhs_arg), args.get(*rhs_arg)) {
1574 let term = Int::mul(self.ctx, &[&lhs.term, &rhs.term]);
1575 self.set_field_value(dest, vec![0], VmValue {
1576 term,
1577 ty: lhs.ty,
1578 provenance: None,
1579 invariants: ValueInvariants::default(),
1580 });
1581 }
1582 }
1583 CallEffect::ReturnOptionSomeScanIndex { self_arg } => {
1584 if let Some(iter_ref) = caller_arg_locals.get(*self_arg).copied().flatten() {
1592 let iter_local = self
1593 .locals
1594 .get(&iter_ref)
1595 .and_then(|v| v.provenance_alloc_id())
1596 .and_then(|alloc| {
1597 self.local_alloc_ids
1598 .iter()
1599 .find(|(_, a)| **a == alloc)
1600 .map(|(l, _)| *l)
1601 });
1602 let ptr_term = iter_local.and_then(|l| {
1603 self.field_value(l, &[0]).map(|v| v.term.clone())
1604 });
1605 let end_term = iter_local.and_then(|l| {
1606 self.field_value(l, &[1]).map(|v| v.term.clone())
1607 });
1608 if let (Some(ptr), Some(end)) = (ptr_term, end_term) {
1609 let len = Int::sub(self.ctx, &[&end, &ptr]);
1610 let payload = self.fresh_int(&format!("scan_idx_{}", dest.as_usize()));
1611 self.path_conditions.push(payload.lt(&len));
1612 let dest_ty = self.body.local_decls[dest].ty;
1613 let payload_ty = match dest_ty.kind() {
1614 TyKind::Adt(adt, substs) if adt.is_enum() => substs.type_at(0),
1615 _ => dest_ty,
1616 };
1617 self.set_field_value(dest, vec![0], VmValue {
1618 term: payload,
1619 ty: payload_ty,
1620 provenance: None,
1621 invariants: ValueInvariants::default(),
1622 });
1623 }
1624 }
1625 }
1626 CallEffect::ReturnScanLength { ptr_arg: _ } => {
1627 let len = self.fresh_int(&format!("strlen_{}", dest.as_usize()));
1634 let max = Int::from_i64(self.ctx, i64::MAX);
1635 self.path_conditions.push(len.lt(&max));
1636 let dest_ty = self.body.local_decls[dest].ty;
1637 self.set_local(dest, VmValue {
1638 term: len,
1639 ty: dest_ty,
1640 provenance: None,
1641 invariants: ValueInvariants::default(),
1642 });
1643 }
1644 CallEffect::ReturnNonZeroIff { arg } => {
1645 if let Some(a) = args.get(*arg) {
1646 let dest_ty = self.body.local_decls[dest].ty;
1647 let zero = Int::from_u64(self.ctx, 0);
1648 let term = self.fresh_int(&format!("ret_nz_iff_{}", dest.as_usize()));
1649 self.path_conditions
1652 .push(term._eq(&zero)._eq(&a.term._eq(&zero)));
1653 self.set_local(dest, VmValue {
1654 term,
1655 ty: dest_ty,
1656 provenance: None,
1657 invariants: ValueInvariants::default(),
1658 });
1659 }
1660 }
1661 CallEffect::ReturnOptionSomeNonZeroIff { arg } => {
1662 if let Some(a) = args.get(*arg) {
1663 let zero = Int::from_u64(self.ctx, 0);
1664 let term = self.fresh_int(&format!("ret_opt_nz_iff_{}", dest.as_usize()));
1665 self.path_conditions
1666 .push(term._eq(&zero)._eq(&a.term._eq(&zero)));
1667 self.set_field_value(dest, vec![0], VmValue {
1668 term,
1669 ty: a.ty,
1670 provenance: None,
1671 invariants: ValueInvariants::default(),
1672 });
1673 }
1674 }
1675 CallEffect::WriteMemory { pointer_arg } => {
1676 if let Some(arg_val) = args.get(*pointer_arg) {
1677 if let Some(prov) = &arg_val.provenance {
1678 let is_vec = crate::helpers::api_classify::is_vec_push(&self.last_call_name);
1682 let is_external = self.alloc(prov.alloc_id).is_external;
1683 if is_vec && !is_external {
1684 let elem_ty = match arg_val.ty.kind() {
1685 TyKind::Ref(_, inner, _) | TyKind::RawPtr(inner, _) => crate::helpers::mir_utils::vec_elem_ty(self.tcx, *inner),
1686 _ => crate::helpers::mir_utils::vec_elem_ty(self.tcx, arg_val.ty),
1687 };
1688 let heap_align = elem_ty.map(|ty| self.align_of_ty(ty)).unwrap_or(1).max(1);
1689 if let Some(old_data) = self.alloc(prov.alloc_id).slice_data {
1690 self.alloc_mut(old_data).dead = true;
1692 let max_size = Int::from_u64(self.ctx, i64::MAX as u64);
1693 let (data_alloc, _) = self.allocate_external(max_size, heap_align, elem_ty);
1694 self.alloc_mut(prov.alloc_id).slice_data = Some(data_alloc);
1695 } else {
1696 let max_size = Int::from_u64(self.ctx, i64::MAX as u64);
1698 let (data_alloc, _) = self.allocate_external(max_size, heap_align, elem_ty);
1699 self.alloc_mut(prov.alloc_id).slice_data = Some(data_alloc);
1700 }
1701 }
1702 let off_u64 = prov.offset.as_u64()
1705 .or_else(|| prov.offset.simplify().as_u64());
1706 if let Some(off) = off_u64 {
1707 if off == 0 {
1708 self.alloc_mut(prov.alloc_id).initialized = true;
1709 }
1710 let elem_size = match arg_val.ty.kind() {
1711 rustc_middle::ty::TyKind::Ref(_, inner, _) => self.size_of_ty(*inner) as usize,
1712 _ => 0,
1713 };
1714 let write_size = if elem_size > 0 { elem_size } else {
1715 self.allocation_size(prov.alloc_id).and_then(|s| s.as_u64()).unwrap_or(0) as usize
1716 };
1717 let end = (off as usize + write_size).min(4096);
1718 for byte_off in (off as usize)..end {
1719 self.mark_byte_init(prov.alloc_id, byte_off);
1720 }
1721 } else {
1722 let size_val = self.allocation_size(prov.alloc_id)
1731 .and_then(|s| s.as_u64());
1732 match size_val {
1733 Some(sz) if sz > 0 => {
1734 for off in 0..(sz as usize).min(1024) {
1735 self.mark_byte_init(prov.alloc_id, off);
1736 }
1737 }
1738 _ => {
1739 self.alloc_mut(prov.alloc_id).initialized = true;
1740 }
1741 }
1742 }
1743 }
1744 }
1745 }
1746 CallEffect::ReadMemory { arg: _ } => {
1747 let dest_ty = self.body.local_decls[dest].ty;
1748 let term = self.fresh_int(&format!("read_{}", dest.as_usize()));
1749 let val = VmValue {
1750 term,
1751 ty: dest_ty,
1752 provenance: None,
1753 invariants: ValueInvariants::default(),
1754 };
1755 self.set_local(dest, val);
1756 }
1757 CallEffect::ReturnFreshAllocation { pointer_arg, size_arg, elem_size } => {
1758 if let (Some(ptr_val), Some(size_val)) = (args.get(*pointer_arg), args.get(*size_arg)) {
1759 let elem_sz = Int::from_u64(self.ctx, *elem_size);
1760 let total = Int::mul(self.ctx, &[&size_val.term, &elem_sz]);
1761 let dest_ty = self.body.local_decls[dest].ty;
1762 let (alloc_id, base) = if *elem_size == 0 {
1765 let max = Int::from_u64(self.ctx, i64::MAX as u64);
1766 self.allocate_external(max, 1, None)
1767 } else {
1768 self.allocate(total, *elem_size, None)
1769 };
1770 let prov = Provenance {
1771 alloc_id,
1772 offset: Int::from_u64(self.ctx, 0),
1773 is_field_offset: false,
1774 };
1775 if let Some(ref dest_alloc_id) = self.local_alloc_ids.get(&dest).copied() {
1777 self.alloc_mut(*dest_alloc_id).slice_data = Some(alloc_id);
1778 }
1779 let is_external = self.alloc(alloc_id).is_external;
1782 if is_external {
1783 self.alloc_mut(alloc_id).initialized = true;
1784 }
1785 if let Some(ref source_prov) = ptr_val.provenance {
1786 if !self.alloc(source_prov.alloc_id).dead {
1787 self.alloc_mut(alloc_id).initialized = true;
1788 self.alloc_mut(alloc_id).parent = Some(source_prov.alloc_id);
1789 }
1790 self.copy_byte_tracking(source_prov.alloc_id, alloc_id);
1792 }
1793 let result_align_n = ptr_val.invariants.align_n.or_else(|| {
1794 ptr_val.provenance.as_ref()
1795 .map(|p| self.alloc(p.alloc_id).align)
1796 });
1797 self.set_local(dest, VmValue {
1798 term: base,
1799 ty: dest_ty,
1800 provenance: Some(prov),
1801 invariants: ValueInvariants {
1802 non_null: true, init: true, in_bounds: true, aligned: true,
1803 align_n: result_align_n,
1804 ..ValueInvariants::default()
1805 },
1806 });
1807 }
1808 }
1809 CallEffect::ReturnNewAllocation { size_arg, elem_size } => {
1810 if let Some(size_val) = args.get(*size_arg) {
1811 let elem_sz = Int::from_u64(self.ctx, *elem_size);
1812 let total = Int::mul(self.ctx, &[&size_val.term, &elem_sz]);
1813 let dest_ty = self.body.local_decls[dest].ty;
1814 let elem_ty = crate::helpers::mir_utils::vec_elem_ty(self.tcx, dest_ty);
1815 let heap_align = elem_ty.map(|ty| self.align_of_ty(ty)).unwrap_or(1).max(1);
1816 let (alloc_id, base) = self.allocate_external(total, heap_align, elem_ty);
1817 let dest_alloc_id = self.local_alloc_ids.get(&dest).copied();
1818 if let Some(dest_alloc_id) = dest_alloc_id {
1819 self.alloc_mut(dest_alloc_id).slice_data = Some(alloc_id);
1820 }
1821 self.alloc_mut(alloc_id).initialized = true;
1822 self.set_local(dest, VmValue {
1823 term: base,
1824 ty: dest_ty,
1825 provenance: dest_alloc_id.map(|stack_id| Provenance {
1826 alloc_id: stack_id,
1827 offset: Int::from_u64(self.ctx, 0),
1828 is_field_offset: false,
1829 }),
1830 invariants: ValueInvariants {
1831 non_null: true,
1832 init: true,
1833 in_bounds: true,
1834 aligned: true,
1835 ..ValueInvariants::default()
1836 },
1837 });
1838 }
1839 }
1840 CallEffect::ReturnNewAllocationFromBox { box_arg: _ } => {
1841 self.ensure_local_allocation(dest);
1843 let dest_ty = self.body.local_decls[dest].ty;
1844 let elem_ty = crate::helpers::mir_utils::vec_elem_ty(self.tcx, dest_ty);
1845 let heap_align = elem_ty.map(|ty| self.align_of_ty(ty)).unwrap_or(1).max(1);
1846 let max = Int::from_u64(self.ctx, i64::MAX as u64);
1847 let (alloc_id, base) = self.allocate_external(max, heap_align, elem_ty);
1848 let dest_alloc_id = self.local_alloc_ids.get(&dest).copied();
1849 if let Some(ref dest_alloc_id) = dest_alloc_id {
1850 self.alloc_mut(*dest_alloc_id).slice_data = Some(alloc_id);
1851 }
1852 self.alloc_mut(alloc_id).initialized = true;
1853 self.set_local(dest, VmValue {
1854 term: base,
1855 ty: dest_ty,
1856 provenance: dest_alloc_id.map(|stack_id| Provenance {
1857 alloc_id: stack_id,
1858 offset: Int::from_u64(self.ctx, 0),
1859 is_field_offset: false,
1860 }),
1861 invariants: ValueInvariants {
1862 non_null: true,
1863 init: true,
1864 in_bounds: true,
1865 aligned: true,
1866 ..ValueInvariants::default()
1867 },
1868 });
1869 }
1870 CallEffect::ReturnBoxFromVec { arg } => {
1871 if let Some(vec_val) = args.get(*arg) {
1872 if let Some(ref prov) = vec_val.provenance {
1873 if let Some(heap_alloc_id) = self.alloc(prov.alloc_id).slice_data {
1874 if let Some(heap_base) = self.allocation_base(heap_alloc_id).cloned() {
1875 let dest_ty = self.body.local_decls[dest].ty;
1876 self.set_local(dest, VmValue {
1877 term: heap_base,
1878 ty: dest_ty,
1879 provenance: Some(Provenance {
1880 alloc_id: heap_alloc_id,
1881 offset: Int::from_u64(self.ctx, 0),
1882 is_field_offset: false,
1883 }),
1884 invariants: ValueInvariants {
1885 non_null: true,
1886 init: true,
1887 in_bounds: true,
1888 aligned: true,
1889 ..ValueInvariants::default()
1890 },
1891 });
1892 }
1893 }
1894 }
1895 }
1896 }
1897 CallEffect::OwnsInitMemory { arg } => {
1898 if let Some(arg_val) = args.get(*arg) {
1899 if let Some(prov) = &arg_val.provenance {
1900 self.alloc_mut(prov.alloc_id).initialized = true;
1901 }
1902 let mut val = arg_val.clone();
1903 val.ty = self.body.local_decls[dest].ty;
1904 val.invariants.init = true;
1905 val.invariants.non_null = true;
1906 self.set_local(dest, val);
1907 }
1908 }
1909 CallEffect::ReturnAllocBuffer => {
1910 let dest_ty = self.body.local_decls[dest].ty;
1917 let max = Int::from_u64(self.ctx, i64::MAX as u64);
1918 let (alloc_id, base) = self.allocate_external(max, 1, None);
1919 self.alloc_mut(alloc_id).initialized = true;
1920 self.set_local(dest, VmValue {
1921 term: base,
1922 ty: dest_ty,
1923 provenance: Some(Provenance {
1924 alloc_id,
1925 offset: Int::from_u64(self.ctx, 0),
1926 is_field_offset: false,
1927 }),
1928 invariants: ValueInvariants {
1929 non_null: true,
1930 init: true,
1931 in_bounds: true,
1932 aligned: true,
1933 ..ValueInvariants::default()
1934 },
1935 });
1936 }
1937 CallEffect::ReturnPowerOfTwo => {
1938 let dest_ty = self.body.local_decls[dest].ty;
1947 let term = self.fresh_int(&format!("layout_align_{}", dest.as_usize()));
1948 let zero = Int::from_u64(self.ctx, 0);
1949 self.path_conditions.push(term.gt(&zero));
1950 self.set_local(dest, VmValue {
1951 term,
1952 ty: dest_ty,
1953 provenance: None,
1954 invariants: ValueInvariants::default(),
1955 });
1956 }
1957 CallEffect::ChecksIndexBoundsDisjoint { indices_arg, len_arg } => {
1958 let indices = args.get(*indices_arg);
1959 let len_val = args.get(*len_arg);
1960 if let (Some(indices_val), Some(len_val)) = (indices, len_val) {
1961 let arr_ty = match indices_val.ty.kind() {
1962 rustc_middle::ty::TyKind::Ref(_, inner, _) => *inner,
1963 _ => indices_val.ty,
1964 };
1965 if let rustc_middle::ty::TyKind::Array(_elem_ty, _const_len) = arr_ty.kind() {
1966 let alloc_id = indices_val.provenance_alloc_id()
1967 .or_else(|| {
1968 let fallback = self.locals.values().find_map(|v| {
1971 if v.ty == arr_ty { v.provenance_alloc_id() }
1972 else { None }
1973 });
1974 fallback
1975 });
1976 if let Some(alloc_id) = alloc_id {
1977 self.contract_flags.has_checked_bounds = true;
1978 let zero = Int::from_u64(self.ctx, 0);
1979 let mut byte_offsets: Vec<(usize, Int)> = self
1980 .alloc_byte_values(alloc_id)
1981 .into_iter()
1982 .map(|(off, term)| (off, term.clone()))
1983 .collect();
1984 byte_offsets.sort_by_key(|(off, _)| *off);
1985 for (_, term) in &byte_offsets {
1986 self.path_conditions.push(term.ge(&zero));
1987 self.path_conditions.push(term.lt(&len_val.term));
1988 }
1989 for i in 0..byte_offsets.len() {
1990 for j in (i + 1)..byte_offsets.len() {
1991 let ti = &byte_offsets[i].1;
1992 let tj = &byte_offsets[j].1;
1993 self.path_conditions.push(ti._eq(tj).not());
1994 }
1995 }
1996 }
1997 }
1998 }
1999 let dest_ty = self.body.local_decls[dest].ty;
2000 let term = self.fresh_int(&format!("ck_ok_{}", dest.as_usize()));
2001 self.set_local( dest, VmValue { term, ty: dest_ty, provenance: None, invariants: ValueInvariants::default() });
2002 }
2003 }
2004 }
2005
2006 fn compute_pointer_add_align(
2012 &self,
2013 base: &VmValue<'ctx, 'tcx>,
2014 _offset: &VmValue<'ctx, 'tcx>,
2015 stride_bytes: u64,
2016 ) -> Option<u64> {
2017 let base_align = base.invariants.align_n;
2018 let Some(n) = base_align else { return None };
2019 if stride_bytes > 0 && stride_bytes % n == 0 {
2020 return Some(n);
2021 }
2022 None
2023 }
2024
2025 pub(crate) fn propagate_const_bytes_to_tracked(
2026 &mut self,
2027 args: &[Spanned<Operand<'tcx>>],
2028 ) {
2029 let mut const_bytes: Option<(Vec<u8>, usize)> = None;
2030 let mut tracked_alloc: Option<AllocId> = None;
2031 let mut tracked_offset: usize = 0;
2032
2033 for (i, arg) in args.iter().enumerate() {
2034 let arg_val = self.value_of_operand(&arg.node);
2035 if const_bytes.is_none() {
2036 let bytes_opt = crate::helpers::mir_utils::extract_const_bytes_from_operand(
2037 self.tcx,
2038 &arg.node,
2039 ).or_else(|| self.trace_to_const_bytes(&arg.node));
2040 if let Some(bytes) = bytes_opt {
2041 const_bytes = Some((bytes, i));
2042 }
2043 }
2044 if tracked_alloc.is_none() {
2045 if let Some(alloc_id) = arg_val.provenance_alloc_id() {
2046 tracked_alloc = Some(alloc_id);
2047 if let Some(ref prov) = arg_val.provenance {
2048 tracked_offset = prov.offset.as_u64().map(|v| v as usize).unwrap_or(0);
2049 }
2050 }
2051 }
2052 }
2053
2054 if let (Some((bytes, _)), Some(alloc_id)) = (const_bytes, tracked_alloc) {
2055 for (j, &b) in bytes.iter().enumerate() {
2056 let off = tracked_offset + j;
2057 self.record_byte_value(
2058 alloc_id,
2059 off,
2060 Int::from_u64(self.ctx, b as u64),
2061 );
2062 if b == 0 {
2063 self.mark_byte_nul(alloc_id, off);
2064 } else {
2065 self.mark_byte_non_nul(alloc_id, off);
2066 }
2067 }
2068 self.alloc_mut(alloc_id).initialized = true;
2069 }
2070 }
2071
2072 pub(crate) fn iter_elem_size(&self, ptr: &VmValue<'ctx, 'tcx>) -> u64 {
2074 let elem_ty = match ptr.ty.kind() {
2075 TyKind::Adt(_, substs) => substs.first().and_then(|s| s.as_type()),
2076 _ => None,
2077 };
2078 elem_ty.map(|t| self.size_of_ty(t).max(1)).unwrap_or(1) as u64
2079 }
2080
2081 fn iter_remaining_len(&self, local: Local) -> Option<Int<'ctx>> {
2087 let ptr = self.field_value(local, &[0])?;
2088 let end = self.field_value(local, &[1])?;
2089 let pp = ptr.provenance.as_ref()?;
2090 let ep = end.provenance.as_ref()?;
2091 if pp.alloc_id != ep.alloc_id {
2092 return None;
2093 }
2094 let sz = Int::from_u64(self.ctx, self.iter_elem_size(&ptr));
2095 if let Some(offset) = self.iter_ptr_offset.get(&local) {
2096 let base_len = ep.offset.div(&sz);
2097 let zero = Int::from_u64(self.ctx, 0);
2098 Some(offset.gt(&base_len).ite(&zero, &Int::sub(self.ctx, &[&base_len, offset])))
2099 } else {
2100 Some(Int::sub(self.ctx, &[&ep.offset, &pp.offset]).div(&sz))
2101 }
2102 }
2103
2104 fn interpreter_iter_len(&mut self, arg_val: &VmValue<'ctx, 'tcx>, dest: Local) -> bool {
2108 let Some(l) = self.find_iter_self_local(arg_val) else {
2109 return false;
2110 };
2111 let Some(len_term) = self.iter_remaining_len(l) else {
2112 return false;
2113 };
2114 let dest_ty = self.body.local_decls[dest].ty;
2115 self.set_local(dest, VmValue::new(len_term, dest_ty));
2116 true
2117 }
2118
2119 fn interpreter_iter_is_empty(&mut self, arg_val: &VmValue<'ctx, 'tcx>, dest: Local) -> bool {
2122 let Some(l) = self.find_iter_self_local(arg_val) else {
2123 return false;
2124 };
2125 let Some(remaining) = self.iter_remaining_len(l) else {
2126 return false;
2127 };
2128 let dest_ty = self.body.local_decls[dest].ty;
2129 let zero = Int::from_u64(self.ctx, 0);
2130 let one = Int::from_u64(self.ctx, 1);
2131 let val = VmValue {
2132 term: remaining._eq(&zero).ite(&one, &zero),
2133 ty: dest_ty,
2134 provenance: None,
2135 invariants: ValueInvariants::default(),
2136 };
2137 self.is_empty_len.insert(dest, remaining);
2138 self.set_local(dest, val);
2139 true
2140 }
2141
2142 fn apply_iter_ptr_update(
2148 &mut self,
2149 _callee: DefId,
2150 cname: &str,
2151 arg_values: &[VmValue<'ctx, 'tcx>],
2152 _caller_arg_locals: &[Option<Local>],
2153 ) {
2154 let is_inc = api_classify::is_post_inc_start(&cname);
2155 if !is_inc { return; } let self_val = &arg_values[0];
2157 let some_local = self.find_iter_self_local(self_val);
2158 let Some(local) = some_local else { return };
2159 let offset_term = arg_values.get(1).map(|v| v.term.clone())
2160 .unwrap_or_else(|| Int::from_u64(self.ctx, 1));
2161 let new_offset = match self.iter_ptr_offset.get(&local) {
2162 Some(prev) => Int::add(self.ctx, &[prev, &offset_term]),
2163 None => offset_term,
2164 };
2165 self.iter_ptr_offset.insert(local, new_offset);
2166 }
2167
2168 fn find_iter_self_local(&self, arg_val: &VmValue<'ctx, 'tcx>) -> Option<Local> {
2172 match arg_val.ty.kind() {
2173 TyKind::Ref(_, pointee, _) => match pointee.kind() {
2174 TyKind::Adt(adt_def, _) => {
2175 let name = self.tcx.def_path_str(adt_def.did());
2176 if api_classify::is_std_iter_or_itermut(&name) {
2177 for (local, addr) in &self.local_addresses {
2185 if addr == &arg_val.term {
2186 return Some(*local);
2187 }
2188 }
2189 return Some(Local::from_usize(1));
2191 }
2192 None
2193 }
2194 _ => None,
2195 },
2196 _ => None,
2197 }
2198 }
2199}