]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_dataflow/src/value_analysis.rs
Clarify registration and tracking of references
[rust.git] / compiler / rustc_mir_dataflow / src / value_analysis.rs
1 //! This module provides a framework on top of the normal MIR dataflow framework to simplify the
2 //! implementation of analyses that track the values stored in places of interest.
3 //!
4 //! The default methods of [`ValueAnalysis`] (prefixed with `super_` instead of `handle_`)
5 //! provide some behavior that should be valid for all abstract domains that are based only on the
6 //! value stored in a certain place. On top of these default rules, an implementation should
7 //! override some of the `handle_` methods. For an example, see `ConstAnalysis`.
8 //!
9 //! An implementation must also provide a [`Map`]. Before the anaylsis begins, all places that
10 //! should be tracked during the analysis must be registered. Currently, the projections of these
11 //! places may only contain derefs, fields and downcasts (otherwise registration fails). During the
12 //! analysis, no new places can be registered.
13 //!
14 //! Note that if you want to track values behind references, you have to register the dereferenced
15 //! place. For example: Assume `let x = (0, 0)` and that we want to propagate values from `x.0` and
16 //! `x.1` also through the assignment `let y = &x`. In this case, we should register `x.0`, `x.1`,
17 //! `(*y).0` and `(*y).1`.
18
19 use std::fmt::{Debug, Formatter};
20
21 use rustc_data_structures::fx::FxHashMap;
22 use rustc_index::vec::IndexVec;
23 use rustc_middle::mir::*;
24 use rustc_middle::ty::{self, Ty, TyCtxt};
25 use rustc_target::abi::VariantIdx;
26
27 use crate::{
28     fmt::DebugWithContext, lattice::FlatSet, Analysis, AnalysisDomain, CallReturnPlaces,
29     JoinSemiLattice, SwitchIntEdgeEffects,
30 };
31
32 pub trait ValueAnalysis<'tcx> {
33     /// For each place of interest, the analysis tracks a value of the given type.
34     type Value: Clone + JoinSemiLattice + HasBottom + HasTop;
35
36     const NAME: &'static str;
37
38     fn map(&self) -> &Map;
39
40     fn handle_statement(&self, statement: &Statement<'tcx>, state: &mut State<Self::Value>) {
41         self.super_statement(statement, state)
42     }
43
44     fn super_statement(&self, statement: &Statement<'tcx>, state: &mut State<Self::Value>) {
45         match &statement.kind {
46             StatementKind::Assign(box (place, rvalue)) => {
47                 self.handle_assign(*place, rvalue, state);
48             }
49             StatementKind::SetDiscriminant { .. } => {
50                 // Could treat this as writing a constant to a pseudo-place.
51             }
52             StatementKind::CopyNonOverlapping(..) => {
53                 // FIXME: What to do here?
54             }
55             StatementKind::StorageLive(..)
56             | StatementKind::StorageDead(..)
57             | StatementKind::Deinit(_) => {
58                 // Could perhaps use these.
59             }
60             StatementKind::Nop
61             | StatementKind::Retag(..)
62             | StatementKind::FakeRead(..)
63             | StatementKind::Coverage(..)
64             | StatementKind::AscribeUserType(..) => (),
65         }
66     }
67
68     fn handle_assign(
69         &self,
70         target: Place<'tcx>,
71         rvalue: &Rvalue<'tcx>,
72         state: &mut State<Self::Value>,
73     ) {
74         self.super_assign(target, rvalue, state)
75     }
76
77     fn super_assign(
78         &self,
79         target: Place<'tcx>,
80         rvalue: &Rvalue<'tcx>,
81         state: &mut State<Self::Value>,
82     ) {
83         match rvalue {
84             Rvalue::Ref(_, BorrowKind::Shared, place) => {
85                 let target_deref = self
86                     .map()
87                     .find(target.as_ref())
88                     .and_then(|target| self.map().apply_elem(target, ProjElem::Deref));
89                 let place = self.map().find(place.as_ref());
90                 match (target_deref, place) {
91                     (Some(target_deref), Some(place)) => {
92                         state.assign_idx(target_deref, ValueOrPlace::Place(place), self.map())
93                     }
94                     _ => (),
95                 }
96             }
97             Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
98                 state.flood(place.as_ref(), self.map(), Self::Value::top());
99             }
100             _ => {
101                 let result = self.handle_rvalue(rvalue, state);
102                 state.assign(target.as_ref(), result, self.map());
103             }
104         }
105     }
106
107     fn handle_rvalue(
108         &self,
109         rvalue: &Rvalue<'tcx>,
110         state: &mut State<Self::Value>,
111     ) -> ValueOrPlace<Self::Value> {
112         self.super_rvalue(rvalue, state)
113     }
114
115     fn super_rvalue(
116         &self,
117         rvalue: &Rvalue<'tcx>,
118         state: &mut State<Self::Value>,
119     ) -> ValueOrPlace<Self::Value> {
120         match rvalue {
121             Rvalue::Use(operand) => self.handle_operand(operand, state),
122             Rvalue::CopyForDeref(place) => self.handle_operand(&Operand::Copy(*place), state),
123             Rvalue::Ref(..) | Rvalue::AddressOf(..) => {
124                 bug!("this rvalue must be handled by handle_assign() or super_assign()")
125             }
126             _ => {
127                 // FIXME: Check that other Rvalues really have no side-effect.
128                 ValueOrPlace::Unknown
129             }
130         }
131     }
132
133     fn handle_operand(
134         &self,
135         operand: &Operand<'tcx>,
136         state: &mut State<Self::Value>,
137     ) -> ValueOrPlace<Self::Value> {
138         self.super_operand(operand, state)
139     }
140
141     fn super_operand(
142         &self,
143         operand: &Operand<'tcx>,
144         state: &mut State<Self::Value>,
145     ) -> ValueOrPlace<Self::Value> {
146         match operand {
147             Operand::Constant(box constant) => {
148                 ValueOrPlace::Value(self.handle_constant(constant, state))
149             }
150             Operand::Copy(place) | Operand::Move(place) => {
151                 // Do want want to handle moves different? Could flood place with bottom.
152                 self.map()
153                     .find(place.as_ref())
154                     .map(ValueOrPlace::Place)
155                     .unwrap_or(ValueOrPlace::Unknown)
156             }
157         }
158     }
159
160     fn handle_constant(
161         &self,
162         constant: &Constant<'tcx>,
163         state: &mut State<Self::Value>,
164     ) -> Self::Value {
165         self.super_constant(constant, state)
166     }
167
168     fn super_constant(
169         &self,
170         _constant: &Constant<'tcx>,
171         _state: &mut State<Self::Value>,
172     ) -> Self::Value {
173         Self::Value::top()
174     }
175
176     fn handle_terminator(&self, terminator: &Terminator<'tcx>, state: &mut State<Self::Value>) {
177         self.super_terminator(terminator, state)
178     }
179
180     fn super_terminator(&self, _terminator: &Terminator<'tcx>, _state: &mut State<Self::Value>) {}
181
182     fn handle_call_return(
183         &self,
184         return_places: CallReturnPlaces<'_, 'tcx>,
185         state: &mut State<Self::Value>,
186     ) {
187         self.super_call_return(return_places, state)
188     }
189
190     fn super_call_return(
191         &self,
192         return_places: CallReturnPlaces<'_, 'tcx>,
193         state: &mut State<Self::Value>,
194     ) {
195         return_places.for_each(|place| {
196             state.flood(place.as_ref(), self.map(), Self::Value::top());
197         })
198     }
199
200     fn handle_switch_int(
201         &self,
202         discr: &Operand<'tcx>,
203         apply_edge_effects: &mut impl SwitchIntEdgeEffects<State<Self::Value>>,
204     ) {
205         self.super_switch_int(discr, apply_edge_effects)
206     }
207
208     fn super_switch_int(
209         &self,
210         _discr: &Operand<'tcx>,
211         _apply_edge_effects: &mut impl SwitchIntEdgeEffects<State<Self::Value>>,
212     ) {
213     }
214
215     fn wrap(self) -> ValueAnalysisWrapper<Self>
216     where
217         Self: Sized,
218     {
219         ValueAnalysisWrapper(self)
220     }
221 }
222
223 pub struct ValueAnalysisWrapper<T>(pub T);
224
225 impl<'tcx, T: ValueAnalysis<'tcx>> AnalysisDomain<'tcx> for ValueAnalysisWrapper<T> {
226     type Domain = State<T::Value>;
227
228     type Direction = crate::Forward;
229
230     const NAME: &'static str = T::NAME;
231
232     fn bottom_value(&self, _body: &Body<'tcx>) -> Self::Domain {
233         State(IndexVec::from_elem_n(T::Value::bottom(), self.0.map().value_count))
234     }
235
236     fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
237         for arg in body.args_iter() {
238             state.flood(PlaceRef { local: arg, projection: &[] }, self.0.map(), T::Value::top());
239         }
240     }
241 }
242
243 impl<'tcx, T> Analysis<'tcx> for ValueAnalysisWrapper<T>
244 where
245     T: ValueAnalysis<'tcx>,
246 {
247     fn apply_statement_effect(
248         &self,
249         state: &mut Self::Domain,
250         statement: &Statement<'tcx>,
251         _location: Location,
252     ) {
253         self.0.handle_statement(statement, state);
254     }
255
256     fn apply_terminator_effect(
257         &self,
258         state: &mut Self::Domain,
259         terminator: &Terminator<'tcx>,
260         _location: Location,
261     ) {
262         self.0.handle_terminator(terminator, state);
263     }
264
265     fn apply_call_return_effect(
266         &self,
267         state: &mut Self::Domain,
268         _block: BasicBlock,
269         return_places: crate::CallReturnPlaces<'_, 'tcx>,
270     ) {
271         self.0.handle_call_return(return_places, state)
272     }
273
274     fn apply_switch_int_edge_effects(
275         &self,
276         _block: BasicBlock,
277         discr: &Operand<'tcx>,
278         apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
279     ) {
280         self.0.handle_switch_int(discr, apply_edge_effects)
281     }
282 }
283
284 rustc_index::newtype_index!(
285     pub struct PlaceIndex {}
286 );
287
288 rustc_index::newtype_index!(
289     struct ValueIndex {}
290 );
291
292 #[derive(PartialEq, Eq, Clone, Debug)]
293 pub struct State<V>(IndexVec<ValueIndex, V>);
294
295 impl<V: Clone + HasTop> State<V> {
296     pub fn flood_all(&mut self, value: V) {
297         self.0.raw.fill(value);
298     }
299
300     pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map, value: V) {
301         if let Some(root) = map.find(place) {
302             self.flood_idx(root, map, value);
303         }
304     }
305
306     pub fn flood_idx(&mut self, place: PlaceIndex, map: &Map, value: V) {
307         map.preorder_invoke(place, &mut |place| {
308             if let Some(vi) = map.places[place].value_index {
309                 self.0[vi] = value.clone();
310             }
311         });
312     }
313
314     pub fn assign_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) {
315         if let Some(target_value) = map.places[target].value_index {
316             if let Some(source_value) = map.places[source].value_index {
317                 self.0[target_value] = self.0[source_value].clone();
318             } else {
319                 self.0[target_value] = V::top();
320             }
321         }
322         for target_child in map.children(target) {
323             // Try to find corresponding child in source.
324             let projection = map.places[target_child].proj_elem.unwrap();
325             if let Some(source_child) = map.projections.get(&(source, projection)) {
326                 self.assign_place_idx(target_child, *source_child, map);
327             } else {
328                 self.flood_idx(target_child, map, V::top());
329             }
330         }
331     }
332
333     pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map) {
334         if let Some(target) = map.find(target) {
335             self.assign_idx(target, result, map);
336         }
337     }
338
339     pub fn assign_idx(&mut self, target: PlaceIndex, result: ValueOrPlace<V>, map: &Map) {
340         match result {
341             ValueOrPlace::Value(value) => {
342                 // FIXME: What if not all tracked projections are overwritten? Can this happen?
343                 if let Some(value_index) = map.places[target].value_index {
344                     self.0[value_index] = value;
345                 }
346             }
347             ValueOrPlace::Place(source) => self.assign_place_idx(target, source, map),
348             ValueOrPlace::Unknown => {
349                 self.flood_idx(target, map, V::top());
350             }
351         }
352     }
353
354     pub fn get(&self, place: PlaceRef<'_>, map: &Map) -> V {
355         map.find(place).map(|place| self.get_idx(place, map)).unwrap_or(V::top())
356     }
357
358     pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V {
359         map.places[place].value_index.map(|v| self.0[v].clone()).unwrap_or(V::top())
360     }
361 }
362
363 impl<V: JoinSemiLattice> JoinSemiLattice for State<V> {
364     fn join(&mut self, other: &Self) -> bool {
365         self.0.join(&other.0)
366     }
367 }
368
369 #[derive(Debug)]
370 pub struct Map {
371     locals: IndexVec<Local, Option<PlaceIndex>>,
372     projections: FxHashMap<(PlaceIndex, ProjElem), PlaceIndex>,
373     places: IndexVec<PlaceIndex, PlaceInfo>,
374     value_count: usize,
375 }
376
377 impl Map {
378     pub fn new() -> Self {
379         Self {
380             locals: IndexVec::new(),
381             projections: FxHashMap::default(),
382             places: IndexVec::new(),
383             value_count: 0,
384         }
385     }
386
387     /// Register all places with suitable types up to a certain derefence depth (to prevent cycles).
388     pub fn register_with_filter<'tcx>(
389         &mut self,
390         tcx: TyCtxt<'tcx>,
391         source: &impl HasLocalDecls<'tcx>,
392         max_derefs: u32,
393         mut filter: impl FnMut(Ty<'tcx>) -> bool,
394     ) {
395         let mut projection = Vec::new();
396         for (local, decl) in source.local_decls().iter_enumerated() {
397             self.register_with_filter_rec(
398                 tcx,
399                 max_derefs,
400                 local,
401                 &mut projection,
402                 decl.ty,
403                 &mut filter,
404             );
405         }
406     }
407
408     fn register_with_filter_rec<'tcx>(
409         &mut self,
410         tcx: TyCtxt<'tcx>,
411         max_derefs: u32,
412         local: Local,
413         projection: &mut Vec<PlaceElem<'tcx>>,
414         ty: Ty<'tcx>,
415         filter: &mut impl FnMut(Ty<'tcx>) -> bool,
416     ) {
417         if filter(ty) {
418             self.register(local, projection)
419                 .expect("projection should only contain convertible elements");
420         }
421         if max_derefs > 0 {
422             if let Some(ty::TypeAndMut { ty, .. }) = ty.builtin_deref(false) {
423                 projection.push(PlaceElem::Deref);
424                 self.register_with_filter_rec(tcx, max_derefs - 1, local, projection, ty, filter);
425                 projection.pop();
426             }
427         }
428         iter_fields(ty, tcx, |variant, field, ty| {
429             if let Some(variant) = variant {
430                 projection.push(PlaceElem::Downcast(None, variant));
431             }
432             projection.push(PlaceElem::Field(field, ty));
433             self.register_with_filter_rec(tcx, max_derefs, local, projection, ty, filter);
434             projection.pop();
435             if variant.is_some() {
436                 projection.pop();
437             }
438         });
439     }
440
441     pub fn register<'tcx>(
442         &mut self,
443         local: Local,
444         projection: &[PlaceElem<'tcx>],
445     ) -> Result<(), ()> {
446         // Get the base index of the local.
447         let mut index =
448             *self.locals.get_or_insert_with(local, || self.places.push(PlaceInfo::new(None)));
449
450         // Apply the projection.
451         for &elem in projection {
452             let elem = elem.try_into()?;
453             index = *self.projections.entry((index, elem)).or_insert_with(|| {
454                 // Prepend new child to the linked list.
455                 let next = self.places.push(PlaceInfo::new(Some(elem)));
456                 self.places[next].next_sibling = self.places[index].first_child;
457                 self.places[index].first_child = Some(next);
458                 next
459             });
460         }
461
462         // Allocate a value slot if it doesn't have one.
463         if self.places[index].value_index.is_none() {
464             self.places[index].value_index = Some(self.value_count.into());
465             self.value_count += 1;
466         }
467
468         Ok(())
469     }
470
471     pub fn apply_elem(&self, place: PlaceIndex, elem: ProjElem) -> Option<PlaceIndex> {
472         self.projections.get(&(place, elem)).copied()
473     }
474
475     pub fn find(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
476         let mut index = *self.locals.get(place.local)?.as_ref()?;
477
478         for &elem in place.projection {
479             index = self.apply_elem(index, elem.try_into().ok()?)?;
480         }
481
482         Some(index)
483     }
484
485     pub fn children(&self, parent: PlaceIndex) -> impl Iterator<Item = PlaceIndex> + '_ {
486         Children::new(self, parent)
487     }
488
489     pub fn preorder_invoke(&self, root: PlaceIndex, f: &mut impl FnMut(PlaceIndex)) {
490         f(root);
491         for child in self.children(root) {
492             self.preorder_invoke(child, f);
493         }
494     }
495 }
496
497 #[derive(Debug)]
498 struct PlaceInfo {
499     next_sibling: Option<PlaceIndex>,
500     first_child: Option<PlaceIndex>,
501     /// The projection used to go from parent to this node (only None for root).
502     proj_elem: Option<ProjElem>,
503     value_index: Option<ValueIndex>,
504 }
505
506 impl PlaceInfo {
507     fn new(proj_elem: Option<ProjElem>) -> Self {
508         Self { next_sibling: None, first_child: None, proj_elem, value_index: None }
509     }
510 }
511
512 struct Children<'a> {
513     map: &'a Map,
514     next: Option<PlaceIndex>,
515 }
516
517 impl<'a> Children<'a> {
518     fn new(map: &'a Map, parent: PlaceIndex) -> Self {
519         Self { map, next: map.places[parent].first_child }
520     }
521 }
522
523 impl<'a> Iterator for Children<'a> {
524     type Item = PlaceIndex;
525
526     fn next(&mut self) -> Option<Self::Item> {
527         match self.next {
528             Some(child) => {
529                 self.next = self.map.places[child].next_sibling;
530                 Some(child)
531             }
532             None => None,
533         }
534     }
535 }
536
537 // FIXME: See if we can get rid of `Unknown`.
538 pub enum ValueOrPlace<V> {
539     Value(V),
540     Place(PlaceIndex),
541     Unknown,
542 }
543
544 pub trait HasBottom {
545     fn bottom() -> Self;
546 }
547
548 pub trait HasTop {
549     fn top() -> Self;
550 }
551
552 impl<V> HasBottom for FlatSet<V> {
553     fn bottom() -> Self {
554         Self::Bottom
555     }
556 }
557
558 impl<V> HasTop for FlatSet<V> {
559     fn top() -> Self {
560         Self::Top
561     }
562 }
563
564 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
565 pub enum ProjElem {
566     Deref,
567     Field(Field),
568     Downcast(VariantIdx),
569 }
570
571 impl<V, T> TryFrom<ProjectionElem<V, T>> for ProjElem {
572     type Error = ();
573
574     fn try_from(value: ProjectionElem<V, T>) -> Result<Self, Self::Error> {
575         match value {
576             ProjectionElem::Deref => Ok(ProjElem::Deref),
577             ProjectionElem::Field(field, _) => Ok(ProjElem::Field(field)),
578             ProjectionElem::Downcast(_, variant) => Ok(ProjElem::Downcast(variant)),
579             _ => Err(()),
580         }
581     }
582 }
583
584 fn iter_fields<'tcx>(
585     ty: Ty<'tcx>,
586     tcx: TyCtxt<'tcx>,
587     mut f: impl FnMut(Option<VariantIdx>, Field, Ty<'tcx>),
588 ) {
589     match ty.kind() {
590         ty::Tuple(list) => {
591             for (field, ty) in list.iter().enumerate() {
592                 f(None, field.into(), ty);
593             }
594         }
595         ty::Adt(def, substs) => {
596             for (v_index, v_def) in def.variants().iter_enumerated() {
597                 for (f_index, f_def) in v_def.fields.iter().enumerate() {
598                     let field_ty = f_def.ty(tcx, substs);
599                     let field_ty = tcx
600                         .try_normalize_erasing_regions(ty::ParamEnv::reveal_all(), field_ty)
601                         .unwrap_or(field_ty);
602                     f(Some(v_index), f_index.into(), field_ty);
603                 }
604             }
605         }
606         ty::Closure(_, substs) => {
607             iter_fields(substs.as_closure().tupled_upvars_ty(), tcx, f);
608         }
609         _ => (),
610     }
611 }
612
613 fn debug_with_context_rec<V: Debug + Eq>(
614     place: PlaceIndex,
615     place_str: &str,
616     new: &State<V>,
617     old: Option<&State<V>>,
618     map: &Map,
619     f: &mut Formatter<'_>,
620 ) -> std::fmt::Result {
621     if let Some(value) = map.places[place].value_index {
622         match old {
623             None => writeln!(f, "{}: {:?}", place_str, new.0[value])?,
624             Some(old) => {
625                 if new.0[value] != old.0[value] {
626                     writeln!(f, "\u{001f}-{}: {:?}", place_str, old.0[value])?;
627                     writeln!(f, "\u{001f}+{}: {:?}", place_str, new.0[value])?;
628                 }
629             }
630         }
631     }
632
633     for child in map.children(place) {
634         let info_elem = map.places[child].proj_elem.unwrap();
635         let child_place_str = match info_elem {
636             ProjElem::Deref => format!("*{}", place_str),
637             ProjElem::Field(field) => {
638                 if place_str.starts_with("*") {
639                     format!("({}).{}", place_str, field.index())
640                 } else {
641                     format!("{}.{}", place_str, field.index())
642                 }
643             }
644             ProjElem::Downcast(variant) => format!("({} as #{})", place_str, variant.index()),
645         };
646         debug_with_context_rec(child, &child_place_str, new, old, map, f)?;
647     }
648
649     Ok(())
650 }
651
652 fn debug_with_context<V: Debug + Eq>(
653     new: &State<V>,
654     old: Option<&State<V>>,
655     map: &Map,
656     f: &mut Formatter<'_>,
657 ) -> std::fmt::Result {
658     for (local, place) in map.locals.iter_enumerated() {
659         if let Some(place) = place {
660             debug_with_context_rec(*place, &format!("{:?}", local), new, old, map, f)?;
661         }
662     }
663     Ok(())
664 }
665
666 impl<'tcx, T> DebugWithContext<ValueAnalysisWrapper<T>> for State<T::Value>
667 where
668     T: ValueAnalysis<'tcx>,
669     T::Value: Debug,
670 {
671     fn fmt_with(&self, ctxt: &ValueAnalysisWrapper<T>, f: &mut Formatter<'_>) -> std::fmt::Result {
672         debug_with_context(self, None, ctxt.0.map(), f)
673     }
674
675     fn fmt_diff_with(
676         &self,
677         old: &Self,
678         ctxt: &ValueAnalysisWrapper<T>,
679         f: &mut Formatter<'_>,
680     ) -> std::fmt::Result {
681         debug_with_context(self, Some(old), ctxt.0.map(), f)
682     }
683 }