1use std::fmt::Write;
4
5use rustc_middle::ty::TyCtxt;
6
7use crate::verify::{helpers::CheckpointLocation, path_extractor::PathStep};
8
9use super::types::{BackwardItem, ForgetReason, KeepReason, RelevantMirItems};
10
11impl<'tcx> RelevantMirItems<'tcx> {
12 pub fn describe(
14 &self,
15 tcx: TyCtxt<'tcx>,
16 checkpoint: &crate::verify::helpers::Checkpoint<'tcx>,
17 path_index: usize,
18 ) -> String {
19 let header = format!(
20 " checkpoint: {} at bb{}",
21 checkpoint.callee_name(tcx),
22 checkpoint.block.as_usize()
23 );
24 self.describe_common(tcx, path_index, &header)
25 }
26
27 pub fn describe_for_checkpoint(
29 &self,
30 tcx: TyCtxt<'tcx>,
31 checkpoint: CheckpointLocation,
32 path_index: usize,
33 ) -> String {
34 let header = format!(" checkpoint: bb{}", checkpoint.block.as_usize());
35 self.describe_common(tcx, path_index, &header)
36 }
37
38 fn describe_common(&self, tcx: TyCtxt<'tcx>, path_index: usize, header: &str) -> String {
39 let mut out = String::new();
40 let caller = self.checkpoint.caller;
41 let body = tcx.optimized_mir(caller);
42
43 let _ = writeln!(out, "{header}");
44 let _ = writeln!(
45 out,
46 " property: kind={:?}, args={:?}",
47 self.property.kind, self.property.args
48 );
49 let _ = writeln!(out, " path {path_index}:");
50 let _ = writeln!(out, " |_ kind: entry",);
51 let _ = writeln!(out, " |_ steps: {}", self.path.describe_body());
52 let _ = writeln!(
53 out,
54 " |_ roots: {} place(s), {} local(s)",
55 self.roots.place_count(),
56 self.roots.local_count()
57 );
58 let _ = writeln!(out, " relevant MIR items:");
59
60 let mut has_relevant_item = false;
61 for step in self.path.steps.iter() {
62 let step_items: Vec<_> = self
63 .items
64 .iter()
65 .filter(|item| item_belongs_to_step(item, step))
66 .collect();
67 if step_items.is_empty() {
68 continue;
69 }
70 has_relevant_item = true;
71
72 let _ = writeln!(out, " |_ {}", describe_path_step(step));
73 for item in step_items {
74 let _ = writeln!(out, " | |_ {}", describe_backward_item(item, body));
75 }
76 }
77 if !has_relevant_item {
78 let _ = writeln!(out, " |_ <none>");
79 }
80
81 let forgets: Vec<_> = self
82 .items
83 .iter()
84 .filter_map(|item| match item {
85 BackwardItem::Forget { reason } => Some(reason),
86 _ => None,
87 })
88 .collect();
89 if !forgets.is_empty() {
90 let _ = writeln!(out, " precision loss:");
91 for reason in forgets {
92 let _ = writeln!(out, " |_ {}", describe_forget_reason(reason));
93 }
94 }
95
96 out
97 }
98}
99
100fn item_belongs_to_step(item: &BackwardItem<'_>, step: &PathStep) -> bool {
103 match (item, step) {
104 (BackwardItem::Statement { block, .. }, PathStep::Block(step_block)) => block == step_block,
105 (BackwardItem::Terminator { block, kind }, PathStep::Block(step_block)) => {
106 block == step_block && *kind != KeepReason::Checkpoint
107 }
108 (BackwardItem::Terminator { block, kind }, PathStep::Checkpoint(location)) => {
109 *kind == KeepReason::Checkpoint && *block == location.block
110 }
111
112 (
113 BackwardItem::PathStep {
114 step: item_step, ..
115 },
116 step,
117 ) => same_path_step(item_step, step),
118 _ => false,
119 }
120}
121
122fn same_path_step(lhs: &PathStep, rhs: &PathStep) -> bool {
123 match (lhs, rhs) {
124 (PathStep::Block(lhs), PathStep::Block(rhs)) => lhs == rhs,
125 (PathStep::Checkpoint(lhs), PathStep::Checkpoint(rhs)) => lhs == rhs,
126 _ => false,
127 }
128}
129
130fn describe_path_step(step: &PathStep) -> String {
131 match step {
132 PathStep::Block(block) => format!("bb{}", block.as_usize()),
133 PathStep::Checkpoint(location) => format!("checkpoint(bb{})", location.block.as_usize()),
134 }
135}
136
137fn describe_backward_item(item: &BackwardItem<'_>, body: &rustc_middle::mir::Body<'_>) -> String {
138 match item {
139 BackwardItem::Statement {
140 block,
141 statement_index,
142 kind,
143 ..
144 } => {
145 let statement = &body.basic_blocks[*block].statements[*statement_index];
146 format!("stmt#{} [{:?}] {:?}", statement_index, kind, statement.kind)
147 }
148 BackwardItem::Terminator { block, kind } => {
149 let terminator = body.basic_blocks[*block].terminator();
150 format!("terminator [{:?}] {:?}", kind, terminator.kind)
151 }
152 BackwardItem::PathStep { kind, .. } => format!("path-step {:?}", kind),
153 BackwardItem::ContractFact { property } => {
154 format!(
155 "contract kind={:?}, args={:?}",
156 property.kind, property.args
157 )
158 }
159 BackwardItem::Forget { reason } => format!("forget {:?}", reason),
160 }
161}
162
163fn describe_forget_reason(reason: &ForgetReason) -> &'static str {
164 match reason {
165 ForgetReason::UnknownCall => {
166 "UnknownCall: a retained call may affect relevant state and has no summary yet"
167 }
168 ForgetReason::SccWithoutSummary => {
169 "SccWithoutSummary: a relevant SCC exit has no verified SCC summary yet"
170 }
171 ForgetReason::MayAliasWrite => {
172 "MayAliasWrite: a write may alias relevant state and is not modeled precisely yet"
173 }
174 ForgetReason::UnsupportedEffect => {
175 "UnsupportedEffect: a relevant MIR effect is not modeled precisely yet"
176 }
177 }
178}