1use std::collections::HashSet;
2
3use super::{
4 contracts::abstract_state::AlignState,
5 matcher::{UnsafeApi, get_arg_place},
6 visitor::{BodyVisitor, CheckResult, PlaceTy},
7};
8use crate::{
9 analysis::{
10 core::{
11 alias_analysis::AAResult,
12 dataflow::{DataFlowAnalysis, default::DataFlowAnalyzer},
13 },
14 senryx::{
15 contracts::property::{CisRange, CisRangeItem, PropertyContract},
16 symbolic_analysis::{AnaOperand, SymbolicDef, verify_with_z3},
17 },
18 utils::fn_info::{
19 display_hashmap, generate_contract_from_annotation_without_field_types,
20 generate_contract_from_std_annotation_json, get_cleaned_def_path_name, get_pointee,
21 is_ptr, is_ref, is_strict_ty_convert, reflect_generic,
22 },
23 },
24 rap_debug, rap_error, rap_info, rap_warn,
25};
26use rustc_data_structures::fx::FxHashMap;
27use rustc_hir::def_id::DefId;
28use rustc_middle::mir::BinOp;
29use rustc_middle::mir::Operand;
30use rustc_middle::mir::Place;
31use rustc_middle::ty::Ty;
32use rustc_span::Span;
33use rustc_span::source_map::Spanned;
34use z3::ast::Ast;
35use z3::ast::BV;
36
37impl<'tcx> BodyVisitor<'tcx> {
38 pub fn handle_std_unsafe_call(
40 &mut self,
41 _dst_place: &Place<'_>,
42 def_id: &DefId,
43 args: &[Spanned<Operand>],
44 _path_index: usize,
45 _fn_map: &FxHashMap<DefId, AAResult>,
46 fn_span: Span,
47 fn_result: UnsafeApi,
48 generic_mapping: FxHashMap<String, Ty<'tcx>>,
49 ) {
50 let func_name = get_cleaned_def_path_name(self.tcx, *def_id);
51
52 let args_with_contracts = generate_contract_from_std_annotation_json(self.tcx, *def_id);
55
56 for (idx, (base, fields, contract)) in args_with_contracts.iter().enumerate() {
57 rap_debug!("Find contract for {:?}, {base}: {:?}", def_id, contract);
58 let arg_tuple = get_arg_place(&args[*base].node);
59 if arg_tuple.0 {
61 continue; } else {
63 let arg_place = self.chains.find_var_id_with_fields_seq(arg_tuple.1, fields);
64 self.check_contract(
65 arg_place,
66 args,
67 contract.clone(),
68 &generic_mapping,
69 func_name.clone(),
70 fn_span,
71 idx,
72 );
73 }
74 }
75 }
76
77 pub fn check_contract(
79 &mut self,
80 arg: usize,
81 args: &[Spanned<Operand>],
82 contract: PropertyContract<'tcx>,
83 generic_mapping: &FxHashMap<String, Ty<'tcx>>,
84 func_name: String,
85 fn_span: Span,
86 idx: usize,
87 ) -> bool {
88 rap_debug!("Check contract {:?} for {:?}.", contract, func_name);
89 let (sp_name, check_result) = match contract {
90 PropertyContract::Align(ty) => {
91 let contract_required_ty = reflect_generic(generic_mapping, &func_name, ty);
92 let check_result = self.check_align(arg, contract_required_ty);
93 ("Align", check_result)
94 }
95 PropertyContract::InBound(ty, contract_len) => {
96 let contract_required_ty = reflect_generic(generic_mapping, &func_name, ty);
97 let check_result = self.check_inbound(arg, contract_len, contract_required_ty);
98 ("Inbound", check_result)
99 }
100 PropertyContract::NonNull => {
101 let check_result = self.check_non_null(arg);
102 ("NonNull", check_result)
103 }
104 PropertyContract::Typed(ty) => {
105 let check_result = self.check_typed(arg);
106 ("Typed", check_result)
107 }
108 PropertyContract::ValidPtr(ty, contract_len) => {
109 let contract_required_ty = reflect_generic(generic_mapping, &func_name, ty);
110 let check_result = self.check_valid_ptr(arg, contract_len, contract_required_ty);
111 ("ValidPtr", check_result)
112 }
113 _ => ("Unknown", false),
114 };
115
116 self.insert_checking_result(sp_name, check_result, func_name, fn_span, idx);
117 true
118 }
119
120 fn is_base_determined(&self, base_local: usize) -> bool {
128 if let Some(domain) = self.value_domains.get(&base_local) {
129 if let Some(def) = &domain.def {
130 match def {
131 SymbolicDef::Ref(_) => return true, SymbolicDef::Use(src) => return self.is_base_determined(*src), SymbolicDef::Cast(src, _) => return self.is_base_determined(*src), SymbolicDef::Call(func_name, _) => {
135 if func_name.contains("as_ptr")
137 || func_name.contains("as_mut_ptr")
138 || func_name.contains("alloc")
139 {
140 return true;
141 }
142 }
143 _ => {}
144 }
145 }
146 }
147 let points_to = self.chains.get_point_to_id(base_local);
149
150 if points_to != base_local {
152 if let Some(target_node) = self.chains.get_var_node(points_to) {
153 if self.chains.is_local(target_node.id) {
155 return true;
156 }
157 }
158 }
159
160 false
161 }
162
163 pub fn check_non_zst(&self, arg: usize) -> bool {
166 let obj_ty = self.chains.get_obj_ty_through_chain(arg);
167 if obj_ty.is_none() {
168 self.show_error_info(arg);
169 }
170 let ori_ty = self.visit_ty_and_get_layout(obj_ty.unwrap());
171 match ori_ty {
172 PlaceTy::Ty(_align, size) => size == 0,
173 PlaceTy::GenericTy(_, _, tys) => {
174 if tys.is_empty() {
175 return false;
176 }
177 for (_, size) in tys {
178 if size != 0 {
179 return false;
180 }
181 }
182 true
183 }
184 _ => false,
185 }
186 }
187
188 pub fn check_typed(&self, arg: usize) -> bool {
190 let obj_ty = self.chains.get_obj_ty_through_chain(arg).unwrap();
191 let var = self.chains.get_var_node(arg);
192 let var_ty = var.unwrap().ty.unwrap();
193 if obj_ty != var_ty && is_strict_ty_convert(self.tcx, obj_ty, var_ty) {
194 return false;
195 }
196 self.check_init(arg)
197 }
198
199 pub fn check_non_null(&self, arg: usize) -> bool {
200 let point_to_id = self.chains.get_point_to_id(arg);
201 let var_ty = self.chains.get_var_node(point_to_id);
202 if var_ty.is_none() {
203 self.show_error_info(arg);
204 }
205 var_ty.unwrap().ots.nonnull
206 }
207
208 pub fn check_init(&self, arg: usize) -> bool {
211 let point_to_id = self.chains.get_point_to_id(arg);
212 let var = self.chains.get_var_node(point_to_id);
213 if var.unwrap().field.is_empty() {
214 let mut init_flag = true;
215 for field in &var.unwrap().field {
216 init_flag &= self.check_init(*field.1);
217 }
218 init_flag
219 } else {
220 var.unwrap().ots.init
221 }
222 }
223
224 pub fn check_allocator_consistency(&self, _func_name: String, _arg: usize) -> bool {
225 true
226 }
227
228 pub fn check_allocated(&self, _arg: usize) -> bool {
229 true
230 }
231
232 pub fn check_inbound(
233 &self,
234 arg: usize,
235 length_arg: CisRangeItem,
236 contract_ty: Ty<'tcx>,
237 ) -> bool {
238 false
239 }
240
241 pub fn check_valid_string(&self, _arg: usize) -> bool {
242 true
243 }
244
245 pub fn check_valid_cstr(&self, _arg: usize) -> bool {
246 true
247 }
248
249 pub fn check_valid_num(&self, _arg: usize) -> bool {
250 true
251 }
252
253 pub fn check_alias(&self, _arg: usize) -> bool {
254 true
255 }
256
257 pub fn check_valid_ptr(
260 &self,
261 arg: usize,
262 length_arg: CisRangeItem,
263 contract_ty: Ty<'tcx>,
264 ) -> bool {
265 !self.check_non_zst(arg)
266 || (self.check_non_zst(arg) && self.check_deref(arg, length_arg, contract_ty))
267 }
268
269 pub fn check_deref(&self, arg: usize, length_arg: CisRangeItem, contract_ty: Ty<'tcx>) -> bool {
270 self.check_allocated(arg) && self.check_inbound(arg, length_arg, contract_ty)
271 }
272
273 pub fn check_ref_to_ptr(
274 &self,
275 arg: usize,
276 length_arg: CisRangeItem,
277 contract_ty: Ty<'tcx>,
278 ) -> bool {
279 self.check_deref(arg, length_arg, contract_ty)
280 && self.check_init(arg)
281 && self.check_align(arg, contract_ty)
282 && self.check_alias(arg)
283 }
284
285 pub fn insert_checking_result(
289 &mut self,
290 sp: &str,
291 is_passed: bool,
292 func_name: String,
293 fn_span: Span,
294 idx: usize,
295 ) {
296 if sp == "Unknown" {
297 return;
298 }
299 if is_passed {
300 self.insert_successful_check_result(func_name.clone(), fn_span, idx + 1, sp);
301 } else {
302 self.insert_failed_check_result(func_name.clone(), fn_span, idx + 1, sp);
303 }
304 }
305
306 pub fn insert_failed_check_result(
308 &mut self,
309 func_name: String,
310 fn_span: Span,
311 idx: usize,
312 sp: &str,
313 ) {
314 if let Some(existing) = self
315 .check_results
316 .iter_mut()
317 .find(|result| result.func_name == func_name && result.func_span == fn_span)
318 {
319 if let Some(passed_set) = existing.passed_contracts.get_mut(&idx) {
320 passed_set.remove(sp);
321 if passed_set.is_empty() {
322 existing.passed_contracts.remove(&idx);
323 }
324 }
325 existing
326 .failed_contracts
327 .entry(idx)
328 .and_modify(|set| {
329 set.insert(sp.to_string());
330 })
331 .or_insert_with(|| {
332 let mut new_set = HashSet::new();
333 new_set.insert(sp.to_string());
334 new_set
335 });
336 } else {
337 let mut new_result = CheckResult::new(&func_name, fn_span);
338 new_result
339 .failed_contracts
340 .insert(idx, HashSet::from([sp.to_string()]));
341 self.check_results.push(new_result);
342 }
343 }
344
345 pub fn insert_successful_check_result(
347 &mut self,
348 func_name: String,
349 fn_span: Span,
350 idx: usize,
351 sp: &str,
352 ) {
353 if let Some(existing) = self
354 .check_results
355 .iter_mut()
356 .find(|result| result.func_name == func_name && result.func_span == fn_span)
357 {
358 if let Some(failed_set) = existing.failed_contracts.get_mut(&idx) {
359 if failed_set.contains(sp) {
360 return;
361 }
362 }
363
364 existing
365 .passed_contracts
366 .entry(idx)
367 .and_modify(|set| {
368 set.insert(sp.to_string());
369 })
370 .or_insert_with(|| HashSet::from([sp.to_string()]));
371 } else {
372 let mut new_result = CheckResult::new(&func_name, fn_span);
373 new_result
374 .passed_contracts
375 .insert(idx, HashSet::from([sp.to_string()]));
376 self.check_results.push(new_result);
377 }
378 }
379
380 pub fn show_error_info(&self, arg: usize) {
381 rap_warn!(
382 "In func {:?}, visitor checker error! Can't get {arg} in chain!",
383 get_cleaned_def_path_name(self.tcx, self.def_id)
384 );
385 display_hashmap(&self.chains.variables, 1);
386 }
387}
388
389impl<'tcx> BodyVisitor<'tcx> {
392 pub fn check_align(&self, arg: usize, contract_required_ty: Ty<'tcx>) -> bool {
394 let required_ty_layout = self.visit_ty_and_get_layout(contract_required_ty);
395 if self.check_align_from_cis(arg, &required_ty_layout) {
396 return true;
397 }
398 if let Some((op, base_local, offset_op, stride_layout)) = self.get_ptr_offset_info(arg) {
400 return self.check_offset_align_with_z3(
401 op,
402 base_local,
403 offset_op,
404 stride_layout,
405 contract_required_ty,
406 );
407 }
408
409 self.check_align_directly(arg, required_ty_layout)
411 }
412
413 fn check_align_from_cis(&self, arg: usize, required_layout: &PlaceTy<'tcx>) -> bool {
419 if let Some(var) = self.chains.get_var_node(arg) {
420 for cis in &var.cis.contracts {
421 if let PropertyContract::Align(cis_ty) = cis {
422 let cis_layout = self.visit_ty_and_get_layout(*cis_ty);
423 if Self::two_types_cast_check(cis_layout, required_layout.clone()) {
424 return true;
425 }
426 }
427 }
428 }
429 false
430 }
431
432 fn check_align_directly(&self, pointer_id: usize, required_ty: PlaceTy<'tcx>) -> bool {
435 if let Some(pointee) = self.chains.get_obj_ty_through_chain(pointer_id) {
436 let pointee_ty = self.visit_ty_and_get_layout(pointee);
437 let pointer = self.chains.get_var_node(pointer_id).unwrap();
438
439 if let AlignState::Aligned(_) = pointer.ots.align {
441 return Self::two_types_cast_check(pointee_ty, required_ty);
442 }
443 }
444 false
445 }
446
447 fn check_offset_align_with_z3(
451 &self,
452 op: BinOp,
453 base_local: usize,
454 offset_op: AnaOperand,
455 stride_layout: PlaceTy<'tcx>,
456 contract_required_ty: Ty<'tcx>,
457 ) -> bool {
458 let req_layout = self.visit_ty_and_get_layout(contract_required_ty);
460 let mut req_aligns = req_layout.possible_aligns();
461
462 if let PlaceTy::GenericTy(..) = req_layout {
464 if req_aligns.is_empty() {
465 req_aligns.extend([1, 2, 4, 8, 16, 32, 64]);
466 }
467 }
468
469 if req_aligns.len() == 1 && req_aligns.contains(&1) {
471 return true;
472 }
473
474 let base_node = if let Some(node) = self.chains.get_var_node(base_local) {
476 node
477 } else {
478 return false;
479 };
480
481 let base_pointee_ty = if let Some(ty) = base_node.ty {
483 crate::analysis::utils::fn_info::get_pointee(ty)
485 } else {
486 return false;
487 };
488
489 let base_layout = self.visit_ty_and_get_layout(base_pointee_ty);
490 let mut base_aligns = base_layout.possible_aligns();
491
492 if let PlaceTy::GenericTy(..) = base_layout {
494 if base_aligns.is_empty() {
495 base_aligns.extend([1, 2, 4, 8, 16, 32, 64]);
496 }
497 }
498
499 rap_debug!(
500 "Z3 Align Check: base_{} {:?} (aligns {:?}) {:?} offset (stride {:?}) => req_aligns {:?}",
501 base_local,
502 op,
503 base_aligns,
504 op,
505 stride_layout,
506 req_aligns
507 );
508
509 verify_with_z3(
510 self.value_domains.clone(),
511 self.path_constraints.clone(),
512 |ctx, vars| {
513 let bv_zero = BV::from_u64(ctx, 0, 64);
514
515 let bv_base = if let Some(b) = vars.get(&base_local) {
517 b.clone()
518 } else {
519 return z3::ast::Bool::from_bool(ctx, false);
521 };
522
523 let bv_index = match &offset_op {
525 AnaOperand::Local(idx) => {
526 if let Some(v) = vars.get(idx) {
527 v.clone()
528 } else {
529 BV::from_u64(ctx, 0, 64)
530 }
531 }
532 AnaOperand::Const(val) => BV::from_u64(ctx, *val as u64, 64),
533 };
534
535 let possible_strides: Vec<u64> = match &stride_layout {
537 PlaceTy::Ty(_, size) => vec![*size as u64],
538 PlaceTy::GenericTy(_, _, layout_set) => {
539 if layout_set.is_empty() {
540 vec![1, 2, 4, 8, 16, 32, 64]
542 } else {
543 layout_set.iter().map(|(_, size)| *size as u64).collect()
544 }
545 }
546 PlaceTy::Unknown => vec![1],
547 };
548
549 let mut constraints = Vec::new();
550
551 let is_same_generic = match (&req_layout, &base_layout) {
553 (PlaceTy::GenericTy(n1, _, _), PlaceTy::GenericTy(n2, _, _)) => n1 == n2,
554 _ => false,
555 };
556
557 for stride in possible_strides {
559 let bv_stride = BV::from_u64(ctx, stride, 64);
560 let bv_byte_offset = bv_index.bvmul(&bv_stride);
561
562 let result_ptr = match op {
564 BinOp::Add => bv_base.bvadd(&bv_byte_offset),
565 BinOp::Sub => bv_base.bvsub(&bv_byte_offset),
566 _ => bv_base.bvadd(&bv_byte_offset), };
568
569 if is_same_generic {
570 for align in &req_aligns {
572 let bv_align = BV::from_u64(ctx, *align as u64, 64);
573
574 let base_is_aligned = bv_base.bvurem(&bv_align)._eq(&bv_zero);
576 let result_aligned = result_ptr.bvurem(&bv_align)._eq(&bv_zero);
578
579 constraints.push(base_is_aligned.implies(&result_aligned));
580 }
581 } else {
582 for b_align in &base_aligns {
584 for r_align in &req_aligns {
585 let bv_base_align = BV::from_u64(ctx, *b_align as u64, 64);
586 let bv_req_align = BV::from_u64(ctx, *r_align as u64, 64);
587
588 let base_is_aligned = bv_base.bvurem(&bv_base_align)._eq(&bv_zero);
589 let result_aligned = result_ptr.bvurem(&bv_req_align)._eq(&bv_zero);
590
591 constraints.push(base_is_aligned.implies(&result_aligned));
592 }
593 }
594 }
595 }
596
597 if constraints.is_empty() {
598 return z3::ast::Bool::from_bool(ctx, false);
600 }
601
602 let constraints_refs: Vec<&z3::ast::Bool> = constraints.iter().collect();
604 z3::ast::Bool::and(ctx, &constraints_refs)
605 },
606 )
607 }
608
609 fn get_ptr_offset_info(&self, arg: usize) -> Option<(BinOp, usize, AnaOperand, PlaceTy<'tcx>)> {
611 if let Some(domain) = self.chains.get_var_node(arg) {
612 if let Some(def) = &domain.offset_from {
613 match def {
614 SymbolicDef::PtrOffset(op, base, off, place_ty) => {
615 return Some((*op, *base, off.clone(), place_ty.clone()));
616 }
617 _ => {}
618 }
619 }
620 }
621 None
622 }
623
624 pub fn update_align_state(&mut self, ptr_local: usize, is_aligned: bool) {
628 let ptr_ty_opt = self.chains.get_var_node(ptr_local).and_then(|n| n.ty);
630
631 if let Some(ptr_ty) = ptr_ty_opt {
632 if is_ptr(ptr_ty) || is_ref(ptr_ty) {
633 let pointee_ty = get_pointee(ptr_ty);
634
635 if let Some(ptr_node) = self.chains.get_var_node_mut(ptr_local) {
636 if is_aligned {
637 ptr_node.ots.align = AlignState::Aligned(pointee_ty);
638 rap_debug!(
639 "Refine State: _{} (source) marked as Aligned({:?}) via condition (True).",
640 ptr_local,
641 pointee_ty
642 );
643 } else {
644 ptr_node.ots.align = AlignState::Unaligned(pointee_ty);
645
646 rap_debug!(
647 "Refine State: _{} (source) marked as Unaligned({:?}) via condition (False).",
648 ptr_local,
649 pointee_ty
650 );
651 }
652 }
653 }
654 }
655 }
656
657 pub fn check_align_by_pre_computed_state(
660 &self,
661 arg: usize,
662 contract_required_ty: Ty<'tcx>,
663 ) -> bool {
664 if let Some(var) = self.chains.get_var_node(arg) {
666 if let AlignState::Aligned(state_ty) = var.ots.align {
668 let state_layout = self.visit_ty_and_get_layout(state_ty);
672 let req_layout = self.visit_ty_and_get_layout(contract_required_ty);
673
674 rap_debug!(
675 "Check Align: arg_{} StateTy: {:?} vs ReqTy: {:?}",
676 arg,
677 state_layout,
678 req_layout
679 );
680
681 return Self::two_types_cast_check(state_layout, req_layout);
683 } else {
684 rap_debug!("Check Align: arg_{} is Unaligned or Unknown", arg);
685 }
686 } else {
687 rap_debug!("Check Align: arg_{} node not found", arg);
688 }
689 false
690 }
691
692 fn two_types_cast_check(src: PlaceTy<'tcx>, dest: PlaceTy<'tcx>) -> bool {
694 let src_aligns = src.possible_aligns();
695 let dest_aligns = dest.possible_aligns();
696 if dest_aligns.len() == 0 && src != dest {
697 return false;
699 }
700
701 for &d_align in &dest_aligns {
702 if d_align != 1 && src_aligns.len() == 0 {
703 return false;
705 }
706 for &s_align in &src_aligns {
707 if s_align < d_align {
708 return false;
709 }
710 }
711 }
712 true
713 }
714
715 fn try_refine_alignment(&self, base_local: usize, current_align: u64) -> u64 {
717 for candidate in [64, 32, 16, 8, 4] {
719 if candidate <= current_align {
720 break;
721 }
722
723 let is_proven = verify_with_z3(
724 self.value_domains.clone(),
725 self.path_constraints.clone(),
726 |ctx, vars| {
727 if let Some(bv_base) = vars.get(&base_local) {
728 let bv_cand = BV::from_u64(ctx, candidate, 64);
729 let bv_zero = BV::from_u64(ctx, 0, 64);
730 bv_base.bvurem(&bv_cand)._eq(&bv_zero)
732 } else {
733 z3::ast::Bool::from_bool(ctx, false)
734 }
735 },
736 );
737
738 if is_proven {
739 rap_debug!(
740 "Refine Align: Successfully refined base_{} to align {}",
741 base_local,
742 candidate
743 );
744 return candidate;
745 }
746 }
747 current_align
748 }
749
750 fn check_offset_is_aligned(&self, _base_local: usize, offset: &AnaOperand, align: u64) -> bool {
752 verify_with_z3(
753 self.value_domains.clone(),
754 self.path_constraints.clone(),
755 |ctx, vars| {
756 let bv_align = BV::from_u64(ctx, align, 64);
757 let bv_zero = BV::from_u64(ctx, 0, 64);
758
759 let bv_offset = match offset {
760 AnaOperand::Local(idx) => {
761 if let Some(v) = vars.get(idx) {
762 v.clone()
763 } else {
764 BV::from_u64(ctx, 0, 64)
765 }
766 }
767 AnaOperand::Const(val) => BV::from_u64(ctx, *val as u64, 64),
768 };
769
770 bv_offset.bvurem(&bv_align)._eq(&bv_zero)
772 },
773 )
774 }
775
776 fn check_cumulative_offset_aligned(
778 &self,
779 _base_local: usize,
780 acc_def: &SymbolicDef,
781 curr_op: &AnaOperand,
782 align: u64,
783 ) -> bool {
784 verify_with_z3(
785 self.value_domains.clone(),
786 self.path_constraints.clone(),
787 |ctx, vars| {
788 let bv_align = BV::from_u64(ctx, align, 64);
789 let bv_zero = BV::from_u64(ctx, 0, 64);
790 let bv_acc = self.symbolic_def_to_bv(ctx, vars, acc_def);
791 let bv_curr = match curr_op {
792 AnaOperand::Local(idx) => {
793 if let Some(v) = vars.get(idx) {
794 v.clone()
795 } else {
796 BV::from_u64(ctx, 0, 64)
797 }
798 }
799 AnaOperand::Const(val) => BV::from_u64(ctx, *val as u64, 64),
800 };
801 let total = bv_acc.bvadd(&bv_curr);
802 total.bvurem(&bv_align)._eq(&bv_zero)
804 },
805 )
806 }
807
808 fn operand_to_symbolic_def(&self, op: AnaOperand) -> SymbolicDef<'tcx> {
810 match op {
811 AnaOperand::Local(l) => SymbolicDef::Use(l),
812 AnaOperand::Const(c) => SymbolicDef::Constant(c),
813 }
814 }
815
816 fn combine_offsets(&self, def: SymbolicDef<'tcx>, op: AnaOperand) -> SymbolicDef<'tcx> {
818 match (def, op) {
819 (SymbolicDef::Constant(c1), AnaOperand::Const(c2)) => SymbolicDef::Constant(c1 + c2),
820 (SymbolicDef::Use(l), o) => SymbolicDef::Binary(BinOp::Add, l, o),
821 (d, _) => d,
822 }
823 }
824
825 fn symbolic_def_to_bv<'a>(
827 &self,
828 ctx: &'a z3::Context,
829 vars: &std::collections::HashMap<usize, BV<'a>>,
830 def: &SymbolicDef,
831 ) -> BV<'a> {
832 match def {
833 SymbolicDef::Constant(c) => BV::from_u64(ctx, *c as u64, 64),
834 SymbolicDef::Use(l) => vars.get(l).cloned().unwrap_or(BV::from_u64(ctx, 0, 64)),
835 SymbolicDef::Binary(op, lhs, rhs) => {
836 let lhs_bv = vars.get(lhs).cloned().unwrap_or(BV::from_u64(ctx, 0, 64));
837 let rhs_bv = match rhs {
838 AnaOperand::Local(l) => {
839 vars.get(l).cloned().unwrap_or(BV::from_u64(ctx, 0, 64))
840 }
841 AnaOperand::Const(c) => BV::from_u64(ctx, *c as u64, 64),
842 };
843 match op {
844 BinOp::Add => lhs_bv.bvadd(&rhs_bv),
845 _ => BV::from_u64(ctx, 0, 64),
846 }
847 }
848 _ => BV::from_u64(ctx, 0, 64),
849 }
850 }
851}