1use crate::analysis::Analysis;
9use crate::analysis::safety_flow::root::{
10 function_has_struct_invariant, function_has_trait_ensurance, hir_contains_unsafe,
11};
12use crate::cli::VerifyMode;
13use crate::compat::FxHashMap;
14use crate::helpers::mir_scan::{collect_raw_ptr_deref_info, collect_static_mut_access_info};
15use crate::helpers::name::short_fn_name;
16#[cfg(rapx_has_attr_ir)]
17use rustc_attr_ir::LangItem;
18#[cfg(all(not(rapx_has_attr_ir), not(rapx_ge_100)))]
19use rustc_hir::LangItem;
20#[cfg(all(not(rapx_has_attr_ir), rapx_ge_100))]
21use rustc_hir::attrs::lang_items::LangItem;
22use rustc_hir::{
23 BodyId, FnDecl, ItemKind,
24 def_id::{DefId, LocalDefId},
25 intravisit::{FnKind, Visitor},
26};
27#[cfg(rapx_has_attr_ir)]
28use rustc_attr_ir::Attribute;
29#[cfg(not(rapx_has_attr_ir))]
30use rustc_hir::Attribute;
31use rustc_middle::{hir::nested_filter, ty::TyCtxt};
32use rustc_span::Span;
33use std::collections::{HashMap, HashSet, VecDeque};
34
35use super::{
36 contract::{
37 ContractExpr, ContractPlace, PlaceBase, Property, PropertyArg, PropertyKind,
38 attr::parse_rapx_attr,
39 },
40 path_extractor::PathExtractor,
41 type_invariants::build_type_invariants_from_params,
42};
43use crate::helpers::fn_info::get_adt_def_id_by_adt_method;
44use crate::helpers::mir_scan::{Checkpoint, collect_unsafe_callsites};
45use crate::helpers::mir_utils::{
46 collect_return_block_indices, has_rapx_verify_attr, is_std_crate_def_id, is_trait_unsafe,
47 resolve_impl_self_ty_def_id,
48};
49
50pub(crate) type FnContracts<'tcx> = Vec<Property<'tcx>>;
52
53pub(crate) type StructInvariants<'tcx> = Vec<Property<'tcx>>;
55
56#[derive(Clone, Debug)]
84pub(crate) struct FunctionTarget<'tcx> {
85 pub def_id: DefId,
87
88 pub owner_struct_def_id: Option<DefId>,
94
95 pub checkpoints: Vec<Checkpoint<'tcx>>,
100
101 pub callee_requires: HashMap<DefId, FnContracts<'tcx>>,
109
110 pub caller_requires: FnContracts<'tcx>,
117
118 pub struct_invariants: Vec<Property<'tcx>>,
124
125 pub type_invariants: Vec<Property<'tcx>>,
133
134 pub raw_ptr_deref_checks: Vec<(Checkpoint<'tcx>, Vec<Property<'tcx>>)>,
143
144 pub static_mut_checks: Vec<(Checkpoint<'tcx>, Vec<Property<'tcx>>)>,
153}
154
155impl<'tcx> FunctionTarget<'tcx> {
156 pub(crate) fn all_checkpoints(&self) -> Vec<&Checkpoint<'tcx>> {
157 self.checkpoints
158 .iter()
159 .chain(
160 self.raw_ptr_deref_checks
161 .iter()
162 .map(|(checkpoint, _)| checkpoint),
163 )
164 .chain(
165 self.static_mut_checks
166 .iter()
167 .map(|(checkpoint, _)| checkpoint),
168 )
169 .collect()
170 }
171
172 pub(crate) fn properties_for_callsite(
173 &self,
174 checkpoint: &Checkpoint<'tcx>,
175 ) -> &[Property<'tcx>] {
176 let loc = checkpoint.location();
177 match checkpoint.kind {
178 crate::helpers::mir_scan::CheckpointKind::RawPtrDeref => self
179 .raw_ptr_deref_checks
180 .iter()
181 .find(|(candidate, _)| candidate.location() == loc)
182 .map(|(_, properties)| properties.as_slice())
183 .unwrap_or(&[]),
184 crate::helpers::mir_scan::CheckpointKind::StaticMutAccess => self
185 .static_mut_checks
186 .iter()
187 .find(|(candidate, _)| candidate.location() == loc)
188 .map(|(_, properties)| properties.as_slice())
189 .unwrap_or(&[]),
190 crate::helpers::mir_scan::CheckpointKind::UnsafeCall => checkpoint
191 .callee
192 .and_then(|callee| self.callee_requires.get(&callee))
193 .map(Vec::as_slice)
194 .unwrap_or(&[]),
195 }
196 }
197}
198
199pub(crate) struct StructTarget<'tcx> {
201 pub def_id: DefId,
203 pub invariants: StructInvariants<'tcx>,
205 pub function_targets: Vec<FunctionTarget<'tcx>>,
207}
208
209pub(crate) struct TraitEnsurance<'tcx> {
216 pub def_id: DefId,
218 pub impl_def_id: DefId,
221 pub self_ty_def_id: Option<DefId>,
223 pub kind: TraitEnsuranceKind<'tcx>,
225}
226
227pub(crate) enum TraitEnsuranceKind<'tcx> {
229 Marker(MarkerTraitKind, Vec<Property<'tcx>>),
232 Unsafe(Vec<(String, FnContracts<'tcx>)>),
234}
235
236#[derive(Clone, Copy, PartialEq, Eq, Debug)]
238pub(crate) enum MarkerTraitKind {
239 Send,
240 Sync,
241}
242
243fn call_arg_to_outer_param(
249 op: &rustc_middle::mir::Operand<'_>,
250 body: &rustc_middle::mir::Body<'_>,
251) -> Option<usize> {
252 let local = match op {
253 rustc_middle::mir::Operand::Copy(p) | rustc_middle::mir::Operand::Move(p) => {
254 if p.projection.is_empty() {
255 p.local
256 } else {
257 return None;
258 }
259 }
260 _ => return None,
261 };
262 let mut queue = VecDeque::from([local]);
263 let mut seen = HashSet::from([local]);
264 while let Some(current) = queue.pop_front() {
265 let cidx = current.as_usize();
266 if cidx >= 1 && cidx <= body.arg_count {
267 return Some(cidx - 1);
268 }
269 for bb in body.basic_blocks.iter() {
270 for stmt in &bb.statements {
271 let rustc_middle::mir::StatementKind::Assign(assign) = &stmt.kind else {
272 continue;
273 };
274 let (dest, rvalue) = &**assign;
275 if dest.local != current || !dest.projection.is_empty() {
276 continue;
277 }
278 let source = match rvalue {
279 rustc_middle::mir::Rvalue::Use(
280 rustc_middle::mir::Operand::Copy(p)
281 | rustc_middle::mir::Operand::Move(p),
282 ..,
283 ) => p.local,
284 _ => continue,
285 };
286 if !seen.contains(&source) {
287 seen.insert(source);
288 queue.push_back(source);
289 }
290 }
291 }
292 }
293 None
294}
295
296fn rebind_property_to_args<'tcx>(
306 prop: &mut Property<'tcx>,
307 args: &[rustc_middle::mir::Operand<'tcx>],
308 callee_args: &rustc_middle::ty::GenericArgs<'tcx>,
309 body: &rustc_middle::mir::Body<'tcx>,
310) -> bool {
311 match prop {
312 Property::Atom(atom) => {
313 let mut ok = true;
314 for arg in &mut atom.args {
315 if !rebind_property_arg(arg, args, callee_args, body) {
316 ok = false;
317 }
318 }
319 if let Some(place) = &mut atom.for_each {
320 if !rebind_place(place, args, body) {
321 ok = false;
322 }
323 }
324 ok
325 }
326 Property::And(and) => {
327 let mut ok = true;
328 for conjunct in &mut and.conjuncts {
329 if !rebind_property_to_args(conjunct, args, callee_args, body) {
330 ok = false;
331 }
332 }
333 ok
334 }
335 Property::Or(or) => {
336 let mut ok = true;
343 for disjunct in &mut or.disjuncts {
344 if !rebind_property_to_args(disjunct, args, callee_args, body) {
345 ok = false;
346 }
347 }
348 ok
349 }
350 }
351}
352
353fn rebind_property_arg<'tcx>(
354 arg: &mut PropertyArg<'tcx>,
355 args: &[rustc_middle::mir::Operand<'tcx>],
356 callee_args: &rustc_middle::ty::GenericArgs<'tcx>,
357 body: &rustc_middle::mir::Body<'tcx>,
358) -> bool {
359 match arg {
360 PropertyArg::Expr(expr) => rebind_expr(expr, args, callee_args, body),
361 PropertyArg::Predicates(preds) => {
362 let mut ok = true;
363 for p in preds {
364 if !rebind_expr(&mut p.lhs, args, callee_args, body) {
365 ok = false;
366 }
367 if !rebind_expr(&mut p.rhs, args, callee_args, body) {
368 ok = false;
369 }
370 }
371 ok
372 }
373 PropertyArg::Ty(ty) => {
377 rebind_ty(ty, callee_args);
378 true
379 }
380 _ => true,
381 }
382}
383
384fn rebind_expr<'tcx>(
385 expr: &mut ContractExpr<'tcx>,
386 args: &[rustc_middle::mir::Operand<'tcx>],
387 callee_args: &rustc_middle::ty::GenericArgs<'tcx>,
388 body: &rustc_middle::mir::Body<'tcx>,
389) -> bool {
390 match expr {
391 ContractExpr::Place(place) => rebind_place(place, args, body),
392 ContractExpr::Len(inner) => rebind_expr(inner, args, callee_args, body),
393 ContractExpr::SizeOf(ty) => {
394 rebind_ty(ty, callee_args);
395 true
396 }
397 ContractExpr::AlignOf(ty) => {
398 rebind_ty(ty, callee_args);
399 true
400 }
401 ContractExpr::IndexAccess { slice, index } => {
402 let a = rebind_expr(slice, args, callee_args, body);
403 let b = rebind_expr(index, args, callee_args, body);
404 a && b
405 }
406 ContractExpr::Binary { lhs, rhs, .. } => {
407 let a = rebind_expr(lhs, args, callee_args, body);
408 let b = rebind_expr(rhs, args, callee_args, body);
409 a && b
410 }
411 ContractExpr::Unary { expr, .. } => rebind_expr(expr, args, callee_args, body),
412 ContractExpr::If {
413 cond,
414 then_expr,
415 else_expr,
416 } => {
417 let a = rebind_expr(&mut cond.lhs, args, callee_args, body);
418 let b = rebind_expr(&mut cond.rhs, args, callee_args, body);
419 let c = rebind_expr(then_expr, args, callee_args, body);
420 let d = rebind_expr(else_expr, args, callee_args, body);
421 a && b && c && d
422 }
423 _ => true,
424 }
425}
426
427fn rebind_ty<'tcx>(
430 ty: &mut rustc_middle::ty::Ty<'tcx>,
431 callee_args: &rustc_middle::ty::GenericArgs<'tcx>,
432) {
433 if let rustc_middle::ty::TyKind::Param(param) = ty.kind() {
434 if let Some(actual) = callee_args.get(param.index as usize).and_then(|a| a.as_type()) {
435 *ty = actual;
436 }
437 }
438}
439
440fn rebind_place<'tcx>(
441 place: &mut ContractPlace<'tcx>,
442 args: &[rustc_middle::mir::Operand<'tcx>],
443 body: &rustc_middle::mir::Body<'tcx>,
444) -> bool {
445 let arg_idx = match place.base {
452 PlaceBase::Arg(i) => Some(i),
453 PlaceBase::Local(k) if k >= 1 => Some(k - 1),
454 _ => return false,
455 };
456 match arg_idx
457 .and_then(|i| args.get(i))
458 .and_then(|op| call_arg_to_outer_param(op, body))
459 {
460 Some(outer) => {
461 place.base = PlaceBase::Arg(outer);
462 true
463 }
464 None => false,
465 }
466}
467
468fn resolve_chain_contracts<'tcx>(
477 tcx: TyCtxt<'tcx>,
478 callee_def_id: DefId,
479 visited: &mut HashSet<DefId>,
480) -> FnContracts<'tcx> {
481 if !visited.insert(callee_def_id) {
482 return Vec::new();
483 }
484
485 if !tcx.is_mir_available(callee_def_id) {
486 return Vec::new();
487 }
488
489 let body = tcx.optimized_mir(callee_def_id);
490 let mut contracts = Vec::new();
491
492 for bb in body.basic_blocks.iter() {
493 let Some(terminator) = &bb.terminator else {
494 continue;
495 };
496 if let rustc_middle::mir::TerminatorKind::Call { func, args, .. } = &terminator.kind {
497 if let rustc_middle::mir::Operand::Constant(c) = func {
498 let rustc_middle::ty::TyKind::FnDef(sub_def_id, callee_args) =
499 c.const_.ty().kind()
500 else {
501 continue;
502 };
503 let sub_def_id = *sub_def_id;
504
505 let fn_sig = tcx.fn_sig(sub_def_id).skip_binder();
506 if fn_sig.safety() != rustc_hir::Safety::Unsafe {
507 continue;
508 }
509
510 let mut reqs = get_contract_from_annotation(tcx, sub_def_id);
512
513 if reqs.is_empty() {
515 reqs = get_trait_method_requires(tcx, sub_def_id);
516 }
517
518 if reqs.is_empty() && is_std_crate_def_id(tcx, sub_def_id) {
520 reqs = super::contract::json::query_json_contracts(tcx, sub_def_id);
521 }
522
523 if reqs.is_empty() {
525 reqs = resolve_chain_contracts(tcx, sub_def_id, visited);
526 }
527
528 let arg_operands: Vec<_> = args.iter().map(|a| a.node.clone()).collect();
535 #[cfg(rapx_ge_99)]
536 let callee_args = callee_args.skip_binder();
537 reqs.retain_mut(|req| {
538 rebind_property_to_args(req, &arg_operands, callee_args, body)
539 });
540
541 contracts.extend(reqs);
542 }
543 }
544 }
545
546 contracts
547}
548
549pub(crate) struct VerifyTargetCollector<'tcx> {
551 tcx: TyCtxt<'tcx>,
552 mode: VerifyMode,
553 skip_invariant: bool,
554 crate_filter: Option<String>,
555 crate_filter_matched: bool,
556 module_filter: Option<String>,
557 module_filter_matched: bool,
558 pub function_targets: Vec<FunctionTarget<'tcx>>,
560 pub struct_targets: HashMap<DefId, StructTarget<'tcx>>,
562 pub trait_targets: Vec<TraitEnsurance<'tcx>>,
565 fn_contract_cache: HashMap<(DefId, bool), FnContracts<'tcx>>,
567}
568
569impl<'tcx> VerifyTargetCollector<'tcx> {
570 pub(crate) fn collect_all(
573 tcx: TyCtxt<'tcx>,
574 mode: VerifyMode,
575 skip_invariant: bool,
576 crate_filter: Option<String>,
577 module_filter: Option<String>,
578 ) -> Self {
579 let mut collector = Self::new(
580 tcx,
581 mode,
582 skip_invariant,
583 crate_filter.clone(),
584 module_filter,
585 );
586 tcx.hir_visit_all_item_likes_in_crate(&mut collector);
587 if crate_filter.is_some() {
588 collector.collect_extern_crate_targets();
589 }
590 collector.check_module_filter_result();
591 collector
592 }
593
594 pub(crate) fn new(
596 tcx: TyCtxt<'tcx>,
597 mode: VerifyMode,
598 skip_invariant: bool,
599 crate_filter: Option<String>,
600 module_filter: Option<String>,
601 ) -> Self {
602 VerifyTargetCollector {
603 tcx,
604 mode,
605 skip_invariant,
606 crate_filter,
607 crate_filter_matched: false,
608 module_filter,
609 module_filter_matched: false,
610 function_targets: Vec::new(),
611 struct_targets: HashMap::new(),
612 trait_targets: Vec::new(),
613 fn_contract_cache: HashMap::new(),
614 }
615 }
616
617 fn get_fn_contracts(&mut self, callee_def_id: DefId, follow_chain: bool) -> FnContracts<'tcx> {
635 let is_std = is_std_crate_def_id(self.tcx, callee_def_id);
636
637 let trait_requires = get_trait_method_requires(self.tcx, callee_def_id);
638
639 self.fn_contract_cache
640 .entry((callee_def_id, follow_chain))
641 .or_insert_with(|| {
642 let mut requires = get_contract_from_annotation(self.tcx, callee_def_id);
643
644 if requires.is_empty() && !trait_requires.is_empty() {
645 requires = trait_requires.clone();
646 }
647
648 if requires.is_empty() && is_std {
649 requires = super::contract::json::query_json_contracts(
650 self.tcx,
651 callee_def_id,
652 );
653 }
654
655 if requires.is_empty() && follow_chain {
656 let mut visited = HashSet::new();
660 requires = resolve_chain_contracts(
661 self.tcx,
662 callee_def_id,
663 &mut visited,
664 );
665 if requires.is_empty() {
666 let is_intrinsic = self.tcx.intrinsic(callee_def_id).is_some();
675 let has_json_entry = is_std
676 && super::contract::json::std_contracts_has_entry(
677 self.tcx,
678 callee_def_id,
679 );
680 let is_verify_target = callee_def_id
681 .as_local()
682 .is_some_and(|id| has_rapx_verify_attr(self.tcx, id));
683 if self.tcx.fn_sig(callee_def_id).skip_binder().safety()
684 == rustc_hir::Safety::Unsafe
685 && !is_intrinsic
686 && !has_json_entry
687 && !is_verify_target
688 {
689 let path = crate::helpers::name::get_cleaned_def_path_name(
690 self.tcx,
691 callee_def_id,
692 );
693 rap_warn!(
694 "no safety contracts found for callee \"{path}\""
695 );
696 }
697 } else {
698 let path = crate::helpers::name::get_cleaned_def_path_name(
699 self.tcx,
700 callee_def_id,
701 );
702 rap_debug!(
703 "resolved {} safety contract(s) for callee \"{path}\" via call chain",
704 requires.len()
705 );
706 }
707 }
708
709 if requires.is_empty() {
710 let has_json_entry = is_std
716 && super::contract::json::std_contracts_has_entry(
717 self.tcx,
718 callee_def_id,
719 );
720 let is_verify_target = callee_def_id
721 .as_local()
722 .is_some_and(|id| has_rapx_verify_attr(self.tcx, id));
723 if !has_json_entry && !is_verify_target {
724 requires.push(Property::new(
725 self.tcx,
726 callee_def_id,
727 "Unknown",
728 &[],
729 ));
730 }
731 }
732
733 requires
734 })
735 .clone()
736 }
737
738 fn build_function_target(&mut self, def_id: DefId) -> FunctionTarget<'tcx> {
740 let checkpoints = collect_unsafe_callsites(self.tcx, def_id);
741 let unsafe_callees: HashSet<_> = checkpoints
742 .iter()
743 .filter_map(|checkpoint| checkpoint.callee)
744 .collect();
745 let callee_requires = unsafe_callees
746 .iter()
747 .map(|callee_def_id| {
748 let contracts = self.get_fn_contracts(*callee_def_id, true);
749 (*callee_def_id, contracts)
750 })
751 .collect();
752
753 let mut caller_requires = self.get_fn_contracts(def_id, false);
754 for contract in &mut caller_requires {
758 bind_alive_regions(self.tcx, def_id, contract);
759 }
760 let raw_ptr_deref_checks = build_raw_ptr_deref_checks(self.tcx, def_id);
766 let static_mut_checks = build_static_mut_checks(self.tcx, def_id);
767
768 let owner_struct_def_id = get_adt_def_id_by_adt_method(self.tcx, def_id);
769 let mut struct_invariants = owner_struct_def_id
770 .map(|struct_def_id| {
771 get_struct_invariants_from_annotation(self.tcx, struct_def_id, def_id)
772 })
773 .unwrap_or_default();
774
775 caller_requires.extend(struct_invariants.clone());
779
780 if is_drop_impl(self.tcx, def_id) {
783 struct_invariants.clear();
784 }
785
786 let type_invariants = build_type_invariants_from_params(self.tcx, def_id);
791 caller_requires.extend(type_invariants.clone());
792
793 FunctionTarget {
794 def_id,
795 owner_struct_def_id,
796 checkpoints,
797 callee_requires,
798 caller_requires,
799 struct_invariants,
800 type_invariants,
801 raw_ptr_deref_checks,
802 static_mut_checks,
803 }
804 }
805
806 fn push_function_target(&mut self, function_target: FunctionTarget<'tcx>) {
808 self.function_targets.push(function_target.clone());
809
810 if let Some(struct_def_id) = function_target.owner_struct_def_id {
811 self.struct_targets
812 .entry(struct_def_id)
813 .or_insert_with(|| StructTarget {
814 def_id: struct_def_id,
815 invariants: get_struct_invariants_from_annotation(
816 self.tcx,
817 struct_def_id,
818 function_target.def_id,
819 ),
820 function_targets: Vec::new(),
821 })
822 .function_targets
823 .push(function_target);
824 }
825 }
826
827 fn collect_extern_crate_targets(&mut self) {
834 let local_crate = rustc_hir::def_id::LOCAL_CRATE;
835
836 for def_id in self.tcx.mir_keys(()) {
837 let def_id = def_id.to_def_id();
838 if def_id.krate == local_crate {
839 continue; }
841 if !self.crate_name_matches(def_id) {
842 continue;
843 }
844 let def_kind = self.tcx.def_kind(def_id);
845 if !matches!(
846 def_kind,
847 rustc_hir::def::DefKind::Fn | rustc_hir::def::DefKind::AssocFn
848 ) {
849 continue;
850 }
851
852 if matches!(self.mode, VerifyMode::Targeted) {
855 continue;
856 }
857
858 self.crate_filter_matched = true;
859
860 if !self.module_path_matches(def_id) {
861 continue;
862 }
863 self.module_filter_matched = true;
864
865 let function_target = self.build_function_target(def_id);
866 self.push_function_target(function_target);
867 }
868 }
869
870 fn crate_name_matches(&self, def_id: DefId) -> bool {
871 match self.crate_filter {
872 None => true,
873 Some(ref filter) => {
874 let crate_name = self.tcx.crate_name(def_id.krate);
875 if crate_name.as_str() == *filter {
876 return true;
877 }
878 if let Ok(pkg_name) = std::env::var("CARGO_PKG_NAME") {
879 if pkg_name == *filter {
880 return true;
881 }
882 }
883 false
884 }
885 }
886 }
887
888 fn module_path_matches(&self, def_id: DefId) -> bool {
889 let Some(ref filter) = self.module_filter else {
890 return true;
891 };
892 let def_path = self.tcx.def_path_str(def_id);
893
894 if def_path == *filter || def_path.starts_with(&format!("{}::", filter)) {
895 return true;
896 }
897 let crate_name = self.tcx.crate_name(def_id.krate);
898 let crate_prefix = format!("{}::", crate_name.as_str());
899
900 if let Some(inner) = filter.strip_prefix(&crate_prefix) {
904 if def_path == inner || def_path.starts_with(&format!("{}::", inner)) {
905 return true;
906 }
907 }
908
909 if let Some(inner) = def_path.strip_prefix(&crate_prefix) {
913 if inner == *filter || inner.starts_with(&format!("{}::", filter)) {
914 return true;
915 }
916 }
917
918 false
919 }
920
921 pub(crate) fn check_module_filter_result(&self) {
922 if let Some(ref filter) = self.crate_filter {
923 if !self.crate_filter_matched {
924 rap_warn!("[rapx::verify] --crate \"{filter}\" matched no targets");
925 }
926 }
927 if let Some(ref filter) = self.module_filter {
928 if !self.module_filter_matched {
929 rap_warn!("[rapx::verify] --module \"{filter}\" matched no functions in the crate");
930 }
931 }
932 }
933}
934
935fn get_trait_method_requires<'tcx>(tcx: TyCtxt<'tcx>, callee_def_id: DefId) -> FnContracts<'tcx> {
936 let Some(assoc_item) = tcx.opt_associated_item(callee_def_id) else {
937 return Vec::new();
938 };
939 let Some(trait_item_def_id) = assoc_item.trait_item_def_id() else {
940 return Vec::new();
941 };
942 get_contract_from_annotation(tcx, trait_item_def_id)
943}
944
945impl<'tcx> Visitor<'tcx> for VerifyTargetCollector<'tcx> {
946 type NestedFilter = nested_filter::OnlyBodies;
947
948 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
949 self.tcx
950 }
951
952 fn visit_item(&mut self, item: &'tcx rustc_hir::Item<'tcx>) {
959 if let ItemKind::Impl(rustc_hir::Impl { of_trait, .. }) = &item.kind
960 && of_trait.is_some()
961 {
962 if matches!(self.mode, VerifyMode::Targeted)
963 && !has_rapx_verify_attr(self.tcx, item.owner_id.def_id)
964 {
965 rustc_hir::intravisit::walk_item(self, item);
966 return;
967 }
968
969 let impl_def_id = item.owner_id.to_def_id();
970
971 if !self.crate_name_matches(impl_def_id) {
972 rustc_hir::intravisit::walk_item(self, item);
973 return;
974 }
975 self.crate_filter_matched = true;
976
977 if !self.module_path_matches(impl_def_id) {
978 rustc_hir::intravisit::walk_item(self, item);
979 return;
980 }
981 self.module_filter_matched = true;
982
983 let trait_ref = { self.tcx.impl_opt_trait_ref(impl_def_id) };
984
985 if let Some(trait_ref) = trait_ref {
986 let trait_def_id = trait_ref.skip_binder().def_id;
987
988 let self_ty_def_id = resolve_impl_self_ty_def_id(item);
989
990 if let Some(kind) = marker_trait_kind(self.tcx, trait_def_id) {
993 let obligations =
994 build_marker_trait_obligations(self.tcx, self_ty_def_id, kind);
995 self.trait_targets.push(TraitEnsurance {
996 def_id: trait_def_id,
997 impl_def_id,
998 self_ty_def_id,
999 kind: TraitEnsuranceKind::Marker(kind, obligations),
1000 });
1001 } else if is_trait_unsafe(self.tcx, trait_def_id) {
1002 let ensures = get_trait_contracts_from_annotation(self.tcx, trait_def_id);
1003
1004 self.trait_targets.push(TraitEnsurance {
1005 def_id: trait_def_id,
1006 impl_def_id,
1007 self_ty_def_id,
1008 kind: TraitEnsuranceKind::Unsafe(ensures),
1009 });
1010 }
1011 }
1012 }
1013
1014 rustc_hir::intravisit::walk_item(self, item);
1015 }
1016
1017 fn visit_fn(
1024 &mut self,
1025 _: FnKind<'tcx>,
1026 _: &'tcx FnDecl<'tcx>,
1027 body_id: BodyId,
1028 _: Span,
1029 id: LocalDefId,
1030 ) -> Self::Result {
1031 if matches!(self.mode, VerifyMode::Targeted) && !has_rapx_verify_attr(self.tcx, id) {
1032 if !is_drop_impl(self.tcx, id.to_def_id()) {
1034 return;
1035 }
1036 }
1037
1038 let def_id = id.to_def_id();
1043
1044 if let rustc_hir::def::DefKind::Fn = self.tcx.def_kind(def_id) {
1047 let fn_sig = self.tcx.fn_sig(def_id).skip_binder();
1048 if matches!(
1049 fn_sig.output().skip_binder().kind(),
1050 rustc_type_ir::TyKind::Never
1051 ) {
1052 return;
1053 }
1054 }
1055
1056 if !matches!(self.mode, VerifyMode::Targeted) {
1057 if !hir_contains_unsafe(self.tcx, body_id)
1058 && !function_has_struct_invariant(self.tcx, def_id)
1059 && !function_has_trait_ensurance(self.tcx, def_id)
1060 {
1061 return;
1062 }
1063 }
1064
1065 if !self.crate_name_matches(def_id) {
1066 return;
1067 }
1068 self.crate_filter_matched = true;
1069
1070 if !self.module_path_matches(def_id) {
1071 return;
1072 }
1073 self.module_filter_matched = true;
1074
1075 let function_target = self.build_function_target(def_id);
1076
1077 match self.mode {
1078 VerifyMode::Targeted => {}
1079 VerifyMode::Scan => {
1080 if function_target.checkpoints.is_empty()
1081 && function_target.raw_ptr_deref_checks.is_empty()
1082 && function_target.static_mut_checks.is_empty()
1083 {
1084 if !function_target.struct_invariants.is_empty() {
1085 if self.skip_invariant {
1086 return;
1087 }
1088 } else {
1089 let root = crate::analysis::safety_flow::root::scan_mir(self.tcx, def_id);
1090 if root.is_none() {
1091 return;
1092 }
1093 }
1094 }
1095 }
1096 }
1097
1098 self.push_function_target(function_target);
1099 }
1100}
1101
1102pub(crate) struct PrepareTargets<'tcx> {
1107 tcx: TyCtxt<'tcx>,
1108 mode: VerifyMode,
1109 skip_invariant: bool,
1110 crate_filter: Option<String>,
1111 module_filter: Option<String>,
1112}
1113
1114impl<'tcx> Analysis for PrepareTargets<'tcx> {
1115 fn run(&mut self) {
1116 let collector = VerifyTargetCollector::collect_all(
1117 self.tcx,
1118 self.mode,
1119 self.skip_invariant,
1120 self.crate_filter.clone(),
1121 self.module_filter.clone(),
1122 );
1123
1124 let free_targets: Vec<_> = collector
1126 .function_targets
1127 .iter()
1128 .filter(|target| target.owner_struct_def_id.is_none())
1129 .collect();
1130 for target in &free_targets {
1131 let target_path = self.tcx.def_path_str(target.def_id);
1132 rap_info!("============================================================");
1133 rap_info!(
1134 "[rapx::verify] prepare targets for free function: {}",
1135 target_path
1136 );
1137 rap_info!("============================================================");
1138 self.log_free_function_unsafe_callees(target);
1139 rap_info!("");
1140 }
1141
1142 let mut struct_ids: Vec<_> = collector.struct_targets.keys().copied().collect();
1144 struct_ids.sort_by_key(|def_id| self.tcx.def_path_str(*def_id));
1145
1146 for struct_def_id in struct_ids {
1147 let Some(struct_target) = collector.struct_targets.get(&struct_def_id) else {
1148 continue;
1149 };
1150 let struct_path = self.tcx.def_path_str(struct_target.def_id);
1151
1152 rap_info!("============================================================");
1153 rap_info!("[rapx::verify] prepare targets for struct: {}", struct_path);
1154 rap_info!("============================================================");
1155
1156 self.log_struct_invariants(struct_target);
1157
1158 for target in &struct_target.function_targets {
1159 self.log_method_target(target);
1160 }
1161 }
1162
1163 let mut trait_targets: Vec<_> = collector.trait_targets.iter().collect();
1165 trait_targets.sort_by_key(|t| self.tcx.def_path_str(t.def_id));
1166
1167 for trait_target in trait_targets {
1168 let trait_path = self.tcx.def_path_str(trait_target.def_id);
1169
1170 match &trait_target.kind {
1171 TraitEnsuranceKind::Unsafe(_) => {
1172 rap_info!("============================================================");
1173 rap_info!(
1174 "[rapx::verify] prepare targets for unsafe trait: {}",
1175 trait_path
1176 );
1177 rap_info!("============================================================");
1178
1179 self.log_trait_ensurance(trait_target);
1180 }
1181 TraitEnsuranceKind::Marker(kind, obligations) => {
1182 let name = match kind {
1183 MarkerTraitKind::Send => "Send",
1184 MarkerTraitKind::Sync => "Sync",
1185 };
1186 rap_info!("============================================================");
1187 rap_info!("[rapx::verify] prepare targets for marker trait: {}", name);
1188 rap_info!("============================================================");
1189
1190 self.log_marker_trait(trait_target, obligations);
1191 }
1192 }
1193
1194 rap_info!("");
1195 }
1196
1197 let total_free = free_targets.len();
1198 let total_method = collector
1199 .function_targets
1200 .iter()
1201 .filter(|target| target.owner_struct_def_id.is_some())
1202 .count();
1203 let total_struct = collector.struct_targets.len();
1204 let total_trait = collector.trait_targets.len();
1205
1206 rap_info!("============================================================");
1207 rap_info!(
1208 "[rapx::verify] total: {} free function(s), {} method(s), {} struct(s), {} trait(s)",
1209 total_free,
1210 total_method,
1211 total_struct,
1212 total_trait
1213 );
1214 rap_info!("============================================================");
1215 }
1216}
1217
1218impl<'tcx> PrepareTargets<'tcx> {
1219 pub(crate) fn new(
1220 tcx: TyCtxt<'tcx>,
1221 mode: VerifyMode,
1222 skip_invariant: bool,
1223 crate_filter: Option<String>,
1224 module_filter: Option<String>,
1225 ) -> Self {
1226 PrepareTargets {
1227 tcx,
1228 mode,
1229 skip_invariant,
1230 crate_filter,
1231 module_filter,
1232 }
1233 }
1234
1235 fn log_struct_invariants(&self, struct_target: &StructTarget<'tcx>) {
1236 if struct_target.invariants.is_empty() {
1237 rap_info!(" struct invariants: <none>");
1238 } else {
1239 rap_info!(" struct invariants:");
1240 for property in
1241 crate::verify::display::dedup_compound_props(struct_target.invariants.iter())
1242 {
1243 rap_info!(
1244 " - {}",
1245 property.display_for_report(self.tcx, Some(struct_target.def_id), None,)
1246 );
1247 }
1248 }
1249 }
1250
1251 fn log_trait_ensurance(&self, trait_target: &TraitEnsurance<'tcx>) {
1252 if let Some(self_ty) = trait_target.self_ty_def_id {
1253 rap_info!(" impl for: {}", self.tcx.def_path_str(self_ty));
1254 }
1255 let TraitEnsuranceKind::Unsafe(ensures) = &trait_target.kind else {
1256 return;
1257 };
1258 if ensures.is_empty() {
1259 rap_info!(" ensures: <none>");
1260 } else {
1261 rap_info!(" ensures (implementor must satisfy):");
1262 for (method_name, contracts) in ensures {
1263 rap_info!(" fn {}:", method_name);
1264 for property in crate::verify::display::dedup_compound_props(contracts.iter()) {
1265 let (call, _meaning) = crate::verify::display::fmt_contract_expanded(
1266 self.tcx,
1267 property,
1268 trait_target.self_ty_def_id,
1269 None,
1270 );
1271 rap_info!(" - {call}");
1272 }
1273 }
1274 }
1275 }
1276
1277 fn log_marker_trait(
1278 &self,
1279 trait_target: &TraitEnsurance<'tcx>,
1280 obligations: &[Property<'tcx>],
1281 ) {
1282 if let Some(self_ty) = trait_target.self_ty_def_id {
1283 rap_info!(" impl for: {}", self.tcx.def_path_str(self_ty));
1284 }
1285 if obligations.is_empty() {
1286 rap_info!(" obligations: <none>");
1287 } else {
1288 rap_info!(" obligations:");
1289 for property in obligations {
1290 let (call, _meaning) = crate::verify::display::fmt_contract_expanded(
1291 self.tcx,
1292 property,
1293 trait_target.self_ty_def_id,
1294 None,
1295 );
1296 rap_info!(" - {call}");
1297 }
1298 }
1299 }
1300
1301 fn log_method_target(&self, target: &FunctionTarget<'tcx>) {
1302 let name = short_fn_name(self.tcx, target.def_id);
1303 let dashes = 62usize.saturating_sub(10 + name.len());
1304 rap_info!(" --- method: {name} {}", "-".repeat(dashes));
1305
1306 let return_blocks = collect_return_block_indices(self.tcx, target.def_id);
1307 rap_info!(
1308 " return checkpoints: {} block(s) {:?}",
1309 return_blocks.len(),
1310 return_blocks
1311 .iter()
1312 .map(|bb| bb.as_usize())
1313 .collect::<Vec<_>>()
1314 );
1315
1316 let path_map = self.build_checkpoint_path_map(target);
1317 self.log_unsafe_callees_and_contracts(target, &path_map);
1318 }
1319
1320 fn log_free_function_unsafe_callees(&self, target: &FunctionTarget<'tcx>) {
1321 let path_map = self.build_checkpoint_path_map(target);
1322 self.log_unsafe_callees_and_contracts(target, &path_map);
1323 }
1324
1325 fn log_unsafe_callees_and_contracts(
1326 &self,
1327 target: &FunctionTarget<'tcx>,
1328 path_map: &FxHashMap<DefId, Vec<(usize, Vec<String>)>>,
1329 ) {
1330 if target.callee_requires.is_empty() {
1331 rap_info!(" unsafe checkpoints: <none>");
1332 return;
1333 }
1334
1335 let mut unsafe_callee_ids: Vec<_> = target.callee_requires.keys().copied().collect();
1336 unsafe_callee_ids.sort_by_key(|def_id| self.tcx.def_path_str(*def_id));
1337
1338 for unsafe_callee_def_id in unsafe_callee_ids {
1339 let fn_sig = self.tcx.fn_sig(unsafe_callee_def_id).skip_binder();
1340 let unsafe_callee_path = self.tcx.def_path_str(unsafe_callee_def_id);
1341 let inputs: Vec<String> = fn_sig
1342 .inputs()
1343 .skip_binder()
1344 .iter()
1345 .map(|ty| format!("{}", ty))
1346 .collect();
1347 let output = format!("{}", fn_sig.output().skip_binder());
1348 rap_info!(
1349 " unsafe callee: {}({}) -> {}",
1350 unsafe_callee_path,
1351 inputs.join(", "),
1352 output,
1353 );
1354
1355 if let Some(requires) = target.callee_requires.get(&unsafe_callee_def_id) {
1356 if requires.is_empty() {
1357 rap_info!(" safety contracts: <none>");
1358 } else {
1359 rap_info!(" safety contracts:");
1360 for property in crate::verify::display::dedup_compound_props(requires.iter()) {
1361 rap_info!(
1362 " - {}",
1363 property.display_for_report(
1364 self.tcx,
1365 target.owner_struct_def_id,
1366 Some(unsafe_callee_def_id),
1367 )
1368 );
1369 }
1370 }
1371 }
1372
1373 if let Some(path_entries) = path_map.get(&unsafe_callee_def_id) {
1374 for (_block_idx, path_strings) in path_entries {
1375 if path_strings.is_empty() {
1376 rap_info!(" path: <none>");
1377 } else {
1378 for desc in path_strings {
1379 rap_info!(" path: shortest path: {desc}");
1380 }
1381 }
1382 }
1383 }
1384 }
1385 }
1386
1387 fn build_checkpoint_path_map(
1388 &self,
1389 target: &FunctionTarget<'tcx>,
1390 ) -> FxHashMap<DefId, Vec<(usize, Vec<String>)>> {
1391 let mut path_map: FxHashMap<DefId, Vec<(usize, Vec<String>)>> = FxHashMap::default();
1392
1393 if target.checkpoints.is_empty() {
1394 return path_map;
1395 }
1396
1397 let groups =
1398 PathExtractor::new(self.tcx, target.def_id, target.checkpoints.clone(), 0).run();
1399
1400 for group in &groups {
1401 for checkpoint in &group.checkpoints {
1402 if let Some(callee_def_id) = checkpoint.callee {
1403 let block_idx = checkpoint.block.as_usize();
1404 let mut path_strings: Vec<String> = Vec::new();
1405 let _ = group.tree.walk_prefixes(
1406 checkpoint.block.as_usize(),
1407 &mut |prefix: &[usize]| -> bool {
1408 let desc = prefix
1409 .iter()
1410 .map(usize::to_string)
1411 .collect::<Vec<_>>()
1412 .join(" -> ");
1413 path_strings.push(desc);
1414 true
1415 },
1416 );
1417
1418 path_map
1419 .entry(callee_def_id)
1420 .or_insert_with(Vec::new)
1421 .push((block_idx, path_strings));
1422 }
1423 }
1424 }
1425
1426 path_map
1427 }
1428}
1429
1430fn is_rapx_named_attr(attr: &Attribute, name: &str) -> bool {
1431 let path = attr.path();
1432 if path.len() >= 2
1433 && path[path.len() - 2].as_str() == "rapx"
1434 && path[path.len() - 1].as_str() == name
1435 {
1436 return true;
1437 }
1438 path.len() == 1 && path[0].as_str() == name
1441}
1442
1443fn marker_trait_kind(tcx: TyCtxt<'_>, trait_def_id: DefId) -> Option<MarkerTraitKind> {
1445 if tcx.get_diagnostic_item(rustc_span::sym::Send) == Some(trait_def_id) {
1446 Some(MarkerTraitKind::Send)
1447 } else if tcx.get_diagnostic_item(rustc_span::sym::Sync) == Some(trait_def_id) {
1448 Some(MarkerTraitKind::Sync)
1449 } else {
1450 None
1451 }
1452}
1453
1454fn build_marker_trait_obligations<'tcx>(
1458 tcx: TyCtxt<'tcx>,
1459 self_ty_def_id: Option<DefId>,
1460 trait_kind: MarkerTraitKind,
1461) -> Vec<Property<'tcx>> {
1462 let Some(self_ty_def_id) = self_ty_def_id else {
1463 return Vec::new();
1464 };
1465 let self_ty = tcx.type_of(self_ty_def_id).skip_binder();
1466
1467 let trait_def_id = match trait_kind {
1468 MarkerTraitKind::Send => tcx.get_diagnostic_item(rustc_span::sym::Send),
1469 MarkerTraitKind::Sync => tcx.get_diagnostic_item(rustc_span::sym::Sync),
1470 };
1471 let Some(trait_def_id) = trait_def_id else {
1472 return Vec::new();
1473 };
1474
1475 let templates = super::contract::json::query_trait_ensures(tcx, trait_def_id);
1476 let mut obligations = Vec::new();
1477 for entry in templates {
1478 if let Some(prop) = build_type_atom(tcx, self_ty_def_id, &entry, self_ty) {
1479 obligations.push(prop);
1480 }
1481 }
1482 obligations
1483}
1484
1485fn build_type_atom<'tcx>(
1490 tcx: TyCtxt<'tcx>,
1491 def_id: DefId,
1492 entry: &super::contract::json::JsonProperty,
1493 self_ty: rustc_middle::ty::Ty<'tcx>,
1494) -> Option<Property<'tcx>> {
1495 if let Some(items) = &entry.any {
1496 let disjuncts: Vec<Property<'tcx>> = items
1497 .iter()
1498 .filter_map(|item| match item {
1499 super::contract::json::AnyItem::Single(e) => {
1500 build_type_atom(tcx, def_id, e, self_ty)
1501 }
1502 super::contract::json::AnyItem::And(es) => {
1503 let conjuncts: Vec<Property<'tcx>> = es
1504 .iter()
1505 .filter_map(|e| build_type_atom(tcx, def_id, e, self_ty))
1506 .collect();
1507 if conjuncts.is_empty() {
1508 None
1509 } else {
1510 Some(Property::new_and(conjuncts))
1511 }
1512 }
1513 })
1514 .collect();
1515 return if disjuncts.is_empty() {
1516 None
1517 } else {
1518 Some(Property::new_or(disjuncts))
1519 };
1520 }
1521
1522 match entry.tag.as_str() {
1523 "ContainNoType" => {
1524 let mut args = vec![PropertyArg::Ty(self_ty)];
1525 args.extend(entry.args[1..].iter().map(|s| {
1526 PropertyArg::Ident(s.strip_prefix("ty:").unwrap_or(s).to_string())
1527 }));
1528 Some(Property::new_atom(PropertyKind::ContainNoType, args))
1529 }
1530 "NoRawPtr" => Some(Property::new_atom(
1531 PropertyKind::NoRawPtr,
1532 vec![PropertyArg::Ty(self_ty)],
1533 )),
1534 "NoInternalMut" => Some(Property::new_atom(
1535 PropertyKind::NoInternalMut,
1536 vec![PropertyArg::Ty(self_ty)],
1537 )),
1538 "UniInternalMut" => Some(Property::new_atom(
1539 PropertyKind::UniInternalMut,
1540 vec![PropertyArg::Ty(self_ty)],
1541 )),
1542 "AtomicUpdate" => Some(Property::new_atom(
1543 PropertyKind::AtomicUpdate,
1544 vec![PropertyArg::Ty(self_ty)],
1545 )),
1546 "RefSend" => Some(Property::new_atom(
1547 PropertyKind::RefSend,
1548 vec![PropertyArg::Ty(self_ty)],
1549 )),
1550 _ => {
1556 let mut exprs: Vec<syn::Expr> = entry
1557 .args
1558 .iter()
1559 .filter_map(|s| {
1560 let normalized = super::contract::json::normalize_json_contract_arg(s);
1561 syn::parse_str::<syn::Expr>(&normalized).ok()
1562 })
1563 .collect();
1564 if exprs.len() != entry.args.len() {
1565 return None;
1566 }
1567
1568 if let Some(spec) = super::contract::compound::find_compound(def_id.krate, &entry.tag) {
1569 for i in exprs.len()..spec.param_tys.len() {
1570 if spec.param_tys.get(i).map(|s| s.as_str()) != Some("Ptr") {
1571 return None;
1572 }
1573 let Some(field) = extract_tamed_field(tcx, def_id) else {
1574 return None;
1575 };
1576 let Ok(e) = syn::parse_str::<syn::Expr>(&field) else {
1577 return None;
1578 };
1579 exprs.push(e);
1580 }
1581 }
1582
1583 match super::contract::compound::expand_compound(tcx, def_id, &entry.tag, &exprs) {
1584 Some(mut props) if !props.is_empty() => {
1585 let origin = props.first().and_then(|p| p.origin()).cloned();
1590 for p in &mut props {
1591 p.clear_origin();
1592 }
1593 let mut combined = Property::conjunction(props);
1594 if let Some(o) = origin {
1595 combined.set_origin(o.name, o.args, o.meaning);
1596 }
1597 Some(combined)
1598 }
1599 _ => None,
1600 }
1601 }
1602 }
1603}
1604
1605fn extract_tamed_field<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<String> {
1609 let invariants = get_struct_invariants_from_annotation(tcx, def_id, def_id);
1610 invariants
1611 .iter()
1612 .find(|p| {
1613 matches!(
1614 p.kind(),
1615 Some(PropertyKind::Allocated) | Some(PropertyKind::Owning)
1616 )
1617 })
1618 .and_then(|p| p.args().first())
1619 .and_then(|a| super::contract::place::field_name_from_arg(tcx, def_id, a))
1620}
1621
1622fn collect_properties_from_named_attrs<'tcx>(
1623 tcx: TyCtxt<'tcx>,
1624 attrs: impl IntoIterator<Item = &'tcx Attribute>,
1625 property_def_id: DefId,
1626 parse_error_label: &str,
1627 attr_name: &str,
1628) -> Vec<Property<'tcx>> {
1629 let mut results = Vec::new();
1630
1631 for attr in attrs {
1632 if !is_rapx_named_attr(attr, attr_name) {
1633 continue;
1634 }
1635
1636 let attr_str = crate::compat::attribute_to_string(tcx, attr);
1637 let parsed = match parse_rapx_attr(attr_str.as_str(), attr_name) {
1638 Ok(parsed) => parsed,
1639 Err(err) => {
1640 rap_error!(
1641 "Failed to parse RAPx {} attr '{}': {}",
1642 parse_error_label,
1643 attr_str,
1644 err
1645 );
1646 continue;
1647 }
1648 };
1649
1650 let Some(property) = parsed else { continue };
1651 results.extend(
1652 Property::parse_list(tcx, property_def_id, property.tag.as_str(), &property.args)
1653 .into_iter()
1654 .map(move |mut p| {
1655 p.apply_kind(property.kind.as_deref());
1656 p
1657 }),
1658 );
1659 }
1660
1661 results
1662}
1663
1664pub(crate) fn get_contract_from_annotation<'tcx>(
1666 tcx: TyCtxt<'tcx>,
1667 def_id: DefId,
1668) -> FnContracts<'tcx> {
1669 if let Some(local_def_id) = def_id.as_local() {
1672 let hir_id = tcx.local_def_id_to_hir_id(local_def_id);
1673 let hir_attrs = tcx.hir_attrs(hir_id);
1674 return collect_properties_from_named_attrs(tcx, hir_attrs, def_id, "requires", "requires");
1676 }
1677
1678 let attrs = crate::compat::get_all_attrs(tcx, def_id);
1679 collect_properties_from_named_attrs(tcx, attrs, def_id, "requires", "requires")
1680}
1681
1682pub(crate) fn get_struct_invariants_from_annotation<'tcx>(
1684 tcx: TyCtxt<'tcx>,
1685 struct_def_id: DefId,
1686 context_def_id: DefId,
1687) -> StructInvariants<'tcx> {
1688 let Some(local_def_id) = struct_def_id.as_local() else {
1689 return Vec::new();
1690 };
1691
1692 let item = tcx.hir_expect_item(local_def_id);
1693 if !matches!(item.kind, ItemKind::Struct(..)) {
1694 return Vec::new();
1695 }
1696
1697 let mut invariants = collect_properties_from_named_attrs(
1698 tcx,
1699 crate::compat::get_all_attrs(tcx, struct_def_id),
1700 context_def_id,
1701 "invariant",
1702 "requires",
1703 );
1704 invariants.extend(collect_properties_from_named_attrs(
1705 tcx,
1706 crate::compat::get_all_attrs(tcx, struct_def_id),
1707 context_def_id,
1708 "invariant",
1709 "invariant",
1710 ));
1711 for inv in &mut invariants {
1715 bind_alive_regions(tcx, struct_def_id, inv);
1716 }
1717 invariants
1718}
1719
1720fn bind_alive_regions<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, property: &mut Property<'tcx>) {
1724 match property {
1725 Property::Atom(atom) => {
1726 if atom.kind == PropertyKind::Alive
1727 && let Some(PropertyArg::Ident(name)) = atom.args.get(1).cloned()
1728 && let Some(region) =
1729 crate::verify::vm::region::resolve_region_name(tcx, def_id, &name)
1730 {
1731 atom.args[1] = PropertyArg::Region(region);
1732 }
1733 }
1734 Property::And(and) => {
1735 for conjunct in &mut and.conjuncts {
1736 bind_alive_regions(tcx, def_id, conjunct);
1737 }
1738 }
1739 Property::Or(or) => {
1740 for disjunct in &mut or.disjuncts {
1741 bind_alive_regions(tcx, def_id, disjunct);
1742 }
1743 }
1744 }
1745}
1746
1747fn get_trait_contracts_from_annotation<'tcx>(
1750 tcx: TyCtxt<'tcx>,
1751 trait_def_id: DefId,
1752) -> Vec<(String, FnContracts<'tcx>)> {
1753 let Some(local_id) = trait_def_id.as_local() else {
1754 return Vec::new();
1755 };
1756
1757 let item = tcx.hir_expect_item(local_id);
1758
1759 let trait_items = {
1760 #[cfg(not(rapx_ge_99))]
1761 if let ItemKind::Trait(.., items) = &item.kind {
1762 items
1763 } else {
1764 return Vec::new();
1765 }
1766 #[cfg(rapx_ge_99)]
1767 if let ItemKind::Trait { items, .. } = &item.kind {
1768 items
1769 } else {
1770 return Vec::new();
1771 }
1772 };
1773
1774 let mut ensures: Vec<(String, FnContracts<'tcx>)> = Vec::new();
1775
1776 for trait_item_id in trait_items.iter() {
1777 let trait_item_def_id = trait_item_id.owner_id.to_def_id();
1778 let method_name = tcx.def_path_str(trait_item_def_id);
1779 let attrs = crate::compat::get_all_attrs(tcx, trait_item_def_id);
1780
1781 let method_ensures = collect_properties_from_named_attrs(
1782 tcx,
1783 attrs,
1784 trait_item_def_id,
1785 "trait ensures",
1786 "ensures",
1787 );
1788
1789 if !method_ensures.is_empty() {
1790 ensures.push((method_name, method_ensures));
1791 }
1792 }
1793
1794 ensures
1795}
1796
1797fn build_raw_ptr_deref_checks<'tcx>(
1800 tcx: TyCtxt<'tcx>,
1801 def_id: DefId,
1802) -> Vec<(Checkpoint<'tcx>, Vec<Property<'tcx>>)> {
1803 let infos = collect_raw_ptr_deref_info(tcx, def_id);
1804 if infos.is_empty() {
1805 return Vec::new();
1806 }
1807
1808 infos
1809 .into_iter()
1810 .map(|info| {
1811 let target = PropertyArg::Expr(ContractExpr::Place(ContractPlace {
1812 base: PlaceBase::Arg(0),
1813 projections: vec![],
1814 }));
1815 let ty = PropertyArg::Ty(info.pointee_ty);
1816 let count = PropertyArg::Expr(ContractExpr::Const(1));
1817
1818 let mut properties = if info.is_ptr2ref {
1819 vec![
1820 Property::new_atom(
1821 PropertyKind::Init,
1822 vec![target.clone(), ty.clone(), count.clone()],
1823 ),
1824 Property::new_atom(PropertyKind::NonNull, vec![target.clone()]),
1825 Property::new_atom(
1826 PropertyKind::Allocated,
1827 vec![target.clone(), ty.clone(), count.clone()],
1828 ),
1829 Property::new_atom(
1830 PropertyKind::InBound,
1831 vec![target.clone(), ty.clone(), count.clone()],
1832 ),
1833 Property::new_atom(PropertyKind::Align, vec![target.clone(), ty.clone()]),
1834 {
1835 let mut p = Property::new_atom(PropertyKind::Alias, vec![target.clone()]);
1836 p.set_contract_kind(crate::verify::contract::ContractKind::Hazard);
1837 p
1838 },
1839 ]
1840 } else {
1841 vec![
1842 Property::new_atom(
1843 PropertyKind::Allocated,
1844 vec![target.clone(), ty.clone(), count.clone()],
1845 ),
1846 Property::new_atom(
1847 PropertyKind::InBound,
1848 vec![target.clone(), ty.clone(), count.clone()],
1849 ),
1850 Property::new_atom(PropertyKind::Align, vec![target.clone(), ty.clone()]),
1851 ]
1852 };
1853
1854 if info.is_read && !info.is_ptr2ref {
1855 properties.push(Property::new_atom(PropertyKind::Typed, vec![target, ty]));
1856 }
1857
1858 (
1859 Checkpoint {
1860 caller: def_id,
1861 callee: None,
1862 block: info.block,
1863 args: vec![info.ptr_operand],
1864 kind: crate::helpers::mir_scan::CheckpointKind::RawPtrDeref,
1865 destination: Some(info.destination),
1866 is_mut_ref: info.is_mut_ref,
1867 statement_index: info.statement_index,
1868 },
1869 properties,
1870 )
1871 })
1872 .collect()
1873}
1874
1875fn build_static_mut_checks<'tcx>(
1878 tcx: TyCtxt<'tcx>,
1879 def_id: DefId,
1880) -> Vec<(Checkpoint<'tcx>, Vec<Property<'tcx>>)> {
1881 let infos = collect_static_mut_access_info(tcx, def_id);
1882 if infos.is_empty() {
1883 return Vec::new();
1884 }
1885
1886 infos
1887 .into_iter()
1888 .map(|info| {
1889 let target = PropertyArg::Expr(ContractExpr::Place(ContractPlace {
1890 base: PlaceBase::Arg(0),
1891 projections: vec![],
1892 }));
1893 let ty = PropertyArg::Ty(info.ty);
1894 let count = PropertyArg::Expr(ContractExpr::Const(1));
1895
1896 let properties = vec![
1897 Property::new_atom(
1898 PropertyKind::Allocated,
1899 vec![target.clone(), ty.clone(), count.clone()],
1900 ),
1901 Property::new_atom(
1902 PropertyKind::InBound,
1903 vec![target.clone(), ty.clone(), count.clone()],
1904 ),
1905 Property::new_atom(PropertyKind::Align, vec![target.clone(), ty.clone()]),
1906 Property::new_atom(PropertyKind::Init, vec![target, ty, count]),
1907 ];
1908
1909 (
1910 Checkpoint {
1911 caller: def_id,
1912 callee: None,
1913 block: info.block,
1914 args: vec![info.ptr_operand],
1915 kind: crate::helpers::mir_scan::CheckpointKind::StaticMutAccess,
1916 destination: None,
1917 is_mut_ref: false,
1918 statement_index: 0,
1919 },
1920 properties,
1921 )
1922 })
1923 .collect()
1924}
1925
1926fn is_drop_impl(tcx: TyCtxt<'_>, fn_did: DefId) -> bool {
1927 let Some(impl_id) = tcx.trait_impl_of_assoc(fn_did) else {
1928 return false;
1929 };
1930 let trait_did = tcx.impl_trait_id(impl_id);
1931 tcx.is_lang_item(trait_did, LangItem::Drop)
1932}