rustc_borrowck/polonius/legacy/
facts.rs

1use std::error::Error;
2use std::fmt::Debug;
3use std::fs::{self, File};
4use std::io::Write;
5use std::path::Path;
6
7use polonius_engine::{AllFacts, Atom, Output};
8use rustc_macros::extension;
9use rustc_middle::mir::Local;
10use rustc_middle::ty::{RegionVid, TyCtxt};
11use rustc_mir_dataflow::move_paths::MovePathIndex;
12
13use super::{LocationIndex, PoloniusLocationTable};
14use crate::BorrowIndex;
15
16#[derive(Copy, Clone, Debug)]
17pub struct RustcFacts;
18
19pub type PoloniusOutput = Output<RustcFacts>;
20
21rustc_index::newtype_index! {
22    /// A (kinda) newtype of `RegionVid` so we can implement `Atom` on it.
23    #[orderable]
24    #[debug_format = "'?{}"]
25    pub struct PoloniusRegionVid {}
26}
27
28impl polonius_engine::Atom for PoloniusRegionVid {
29    fn index(self) -> usize {
30        self.as_usize()
31    }
32}
33impl From<RegionVid> for PoloniusRegionVid {
34    fn from(value: RegionVid) -> Self {
35        Self::from_usize(value.as_usize())
36    }
37}
38impl From<PoloniusRegionVid> for RegionVid {
39    fn from(value: PoloniusRegionVid) -> Self {
40        Self::from_usize(value.as_usize())
41    }
42}
43
44impl polonius_engine::FactTypes for RustcFacts {
45    type Origin = PoloniusRegionVid;
46    type Loan = BorrowIndex;
47    type Point = LocationIndex;
48    type Variable = Local;
49    type Path = MovePathIndex;
50}
51
52pub type PoloniusFacts = AllFacts<RustcFacts>;
53
54#[extension(pub(crate) trait PoloniusFactsExt)]
55impl PoloniusFacts {
56    /// Returns `true` if there is a need to gather `PoloniusFacts` given the
57    /// current `-Z` flags.
58    fn enabled(tcx: TyCtxt<'_>) -> bool {
59        tcx.sess.opts.unstable_opts.nll_facts
60            || tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled()
61    }
62
63    fn write_to_dir(
64        &self,
65        dir: impl AsRef<Path>,
66        location_table: &PoloniusLocationTable,
67    ) -> Result<(), Box<dyn Error>> {
68        let dir: &Path = dir.as_ref();
69        fs::create_dir_all(dir)?;
70        let wr = FactWriter { location_table, dir };
71        macro_rules! write_facts_to_path {
72            ($wr:ident . write_facts_to_path($this:ident . [
73                $($field:ident,)*
74            ])) => {
75                $(
76                    $wr.write_facts_to_path(
77                        &$this.$field,
78                        &format!("{}.facts", stringify!($field))
79                    )?;
80                )*
81            }
82        }
83        write_facts_to_path! {
84            wr.write_facts_to_path(self.[
85                loan_issued_at,
86                universal_region,
87                cfg_edge,
88                loan_killed_at,
89                subset_base,
90                loan_invalidated_at,
91                var_used_at,
92                var_defined_at,
93                var_dropped_at,
94                use_of_var_derefs_origin,
95                drop_of_var_derefs_origin,
96                child_path,
97                path_is_var,
98                path_assigned_at_base,
99                path_moved_at_base,
100                path_accessed_at_base,
101                known_placeholder_subset,
102                placeholder,
103            ])
104        }
105        Ok(())
106    }
107}
108
109impl Atom for BorrowIndex {
110    fn index(self) -> usize {
111        self.as_usize()
112    }
113}
114
115impl Atom for LocationIndex {
116    fn index(self) -> usize {
117        self.as_usize()
118    }
119}
120
121struct FactWriter<'w> {
122    location_table: &'w PoloniusLocationTable,
123    dir: &'w Path,
124}
125
126impl<'w> FactWriter<'w> {
127    fn write_facts_to_path<T>(&self, rows: &[T], file_name: &str) -> Result<(), Box<dyn Error>>
128    where
129        T: FactRow,
130    {
131        let file = &self.dir.join(file_name);
132        let mut file = File::create_buffered(file)?;
133        for row in rows {
134            row.write(&mut file, self.location_table)?;
135        }
136        Ok(())
137    }
138}
139
140trait FactRow {
141    fn write(
142        &self,
143        out: &mut dyn Write,
144        location_table: &PoloniusLocationTable,
145    ) -> Result<(), Box<dyn Error>>;
146}
147
148impl FactRow for PoloniusRegionVid {
149    fn write(
150        &self,
151        out: &mut dyn Write,
152        location_table: &PoloniusLocationTable,
153    ) -> Result<(), Box<dyn Error>> {
154        write_row(out, location_table, &[self])
155    }
156}
157
158impl<A, B> FactRow for (A, B)
159where
160    A: FactCell,
161    B: FactCell,
162{
163    fn write(
164        &self,
165        out: &mut dyn Write,
166        location_table: &PoloniusLocationTable,
167    ) -> Result<(), Box<dyn Error>> {
168        write_row(out, location_table, &[&self.0, &self.1])
169    }
170}
171
172impl<A, B, C> FactRow for (A, B, C)
173where
174    A: FactCell,
175    B: FactCell,
176    C: FactCell,
177{
178    fn write(
179        &self,
180        out: &mut dyn Write,
181        location_table: &PoloniusLocationTable,
182    ) -> Result<(), Box<dyn Error>> {
183        write_row(out, location_table, &[&self.0, &self.1, &self.2])
184    }
185}
186
187impl<A, B, C, D> FactRow for (A, B, C, D)
188where
189    A: FactCell,
190    B: FactCell,
191    C: FactCell,
192    D: FactCell,
193{
194    fn write(
195        &self,
196        out: &mut dyn Write,
197        location_table: &PoloniusLocationTable,
198    ) -> Result<(), Box<dyn Error>> {
199        write_row(out, location_table, &[&self.0, &self.1, &self.2, &self.3])
200    }
201}
202
203fn write_row(
204    out: &mut dyn Write,
205    location_table: &PoloniusLocationTable,
206    columns: &[&dyn FactCell],
207) -> Result<(), Box<dyn Error>> {
208    for (index, c) in columns.iter().enumerate() {
209        let tail = if index == columns.len() - 1 { "\n" } else { "\t" };
210        write!(out, "{:?}{tail}", c.to_string(location_table))?;
211    }
212    Ok(())
213}
214
215trait FactCell {
216    fn to_string(&self, location_table: &PoloniusLocationTable) -> String;
217}
218
219impl FactCell for BorrowIndex {
220    fn to_string(&self, _location_table: &PoloniusLocationTable) -> String {
221        format!("{self:?}")
222    }
223}
224
225impl FactCell for Local {
226    fn to_string(&self, _location_table: &PoloniusLocationTable) -> String {
227        format!("{self:?}")
228    }
229}
230
231impl FactCell for MovePathIndex {
232    fn to_string(&self, _location_table: &PoloniusLocationTable) -> String {
233        format!("{self:?}")
234    }
235}
236
237impl FactCell for PoloniusRegionVid {
238    fn to_string(&self, _location_table: &PoloniusLocationTable) -> String {
239        format!("{self:?}")
240    }
241}
242
243impl FactCell for RegionVid {
244    fn to_string(&self, _location_table: &PoloniusLocationTable) -> String {
245        format!("{self:?}")
246    }
247}
248
249impl FactCell for LocationIndex {
250    fn to_string(&self, location_table: &PoloniusLocationTable) -> String {
251        format!("{:?}", location_table.to_rich_location(*self))
252    }
253}