]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/rustc_peek.rs
Rollup merge of #59166 - seanmonstar:trait-alias-import, r=alexreg
[rust.git] / src / librustc_mir / transform / rustc_peek.rs
1 use rustc_target::spec::abi::{Abi};
2 use syntax::ast;
3 use syntax_pos::Span;
4
5 use rustc::ty::{self, TyCtxt};
6 use rustc::hir::def_id::DefId;
7 use rustc::mir::{self, Mir, Location};
8 use rustc_data_structures::bit_set::BitSet;
9 use crate::transform::{MirPass, MirSource};
10
11 use crate::dataflow::{do_dataflow, DebugFormatted};
12 use crate::dataflow::MoveDataParamEnv;
13 use crate::dataflow::BitDenotation;
14 use crate::dataflow::DataflowResults;
15 use crate::dataflow::{
16     DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces
17 };
18 use crate::dataflow::move_paths::{MovePathIndex, LookupResult};
19 use crate::dataflow::move_paths::{HasMoveData, MoveData};
20 use crate::dataflow;
21
22 use crate::dataflow::has_rustc_mir_with;
23
24 pub struct SanityCheck;
25
26 impl MirPass for SanityCheck {
27     fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
28                           src: MirSource<'tcx>, mir: &mut Mir<'tcx>) {
29         let def_id = src.def_id();
30         if !tcx.has_attr(def_id, "rustc_mir") {
31             debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
32             return;
33         } else {
34             debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
35         }
36
37         let attributes = tcx.get_attrs(def_id);
38         let param_env = tcx.param_env(def_id);
39         let move_data = MoveData::gather_moves(mir, tcx).unwrap();
40         let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
41         let dead_unwinds = BitSet::new_empty(mir.basic_blocks().len());
42         let flow_inits =
43             do_dataflow(tcx, mir, def_id, &attributes, &dead_unwinds,
44                         MaybeInitializedPlaces::new(tcx, mir, &mdpe),
45                         |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
46         let flow_uninits =
47             do_dataflow(tcx, mir, def_id, &attributes, &dead_unwinds,
48                         MaybeUninitializedPlaces::new(tcx, mir, &mdpe),
49                         |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
50         let flow_def_inits =
51             do_dataflow(tcx, mir, def_id, &attributes, &dead_unwinds,
52                         DefinitelyInitializedPlaces::new(tcx, mir, &mdpe),
53                         |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
54
55         if has_rustc_mir_with(&attributes, "rustc_peek_maybe_init").is_some() {
56             sanity_check_via_rustc_peek(tcx, mir, def_id, &attributes, &flow_inits);
57         }
58         if has_rustc_mir_with(&attributes, "rustc_peek_maybe_uninit").is_some() {
59             sanity_check_via_rustc_peek(tcx, mir, def_id, &attributes, &flow_uninits);
60         }
61         if has_rustc_mir_with(&attributes, "rustc_peek_definite_init").is_some() {
62             sanity_check_via_rustc_peek(tcx, mir, def_id, &attributes, &flow_def_inits);
63         }
64         if has_rustc_mir_with(&attributes, "stop_after_dataflow").is_some() {
65             tcx.sess.fatal("stop_after_dataflow ended compilation");
66         }
67     }
68 }
69
70 /// This function scans `mir` for all calls to the intrinsic
71 /// `rustc_peek` that have the expression form `rustc_peek(&expr)`.
72 ///
73 /// For each such call, determines what the dataflow bit-state is for
74 /// the L-value corresponding to `expr`; if the bit-state is a 1, then
75 /// that call to `rustc_peek` is ignored by the sanity check. If the
76 /// bit-state is a 0, then this pass emits a error message saying
77 /// "rustc_peek: bit not set".
78 ///
79 /// The intention is that one can write unit tests for dataflow by
80 /// putting code into a compile-fail test and using `rustc_peek` to
81 /// make observations about the results of dataflow static analyses.
82 ///
83 /// (If there are any calls to `rustc_peek` that do not match the
84 /// expression form above, then that emits an error as well, but those
85 /// errors are not intended to be used for unit tests.)
86 pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
87                                                 mir: &Mir<'tcx>,
88                                                 def_id: DefId,
89                                                 _attributes: &[ast::Attribute],
90                                                 results: &DataflowResults<'tcx, O>)
91     where O: BitDenotation<'tcx, Idx=MovePathIndex> + HasMoveData<'tcx>
92 {
93     debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id);
94     // FIXME: this is not DRY. Figure out way to abstract this and
95     // `dataflow::build_sets`. (But note it is doing non-standard
96     // stuff, so such generalization may not be realistic.)
97
98     for bb in mir.basic_blocks().indices() {
99         each_block(tcx, mir, results, bb);
100     }
101 }
102
103 fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
104                            mir: &Mir<'tcx>,
105                            results: &DataflowResults<'tcx, O>,
106                            bb: mir::BasicBlock) where
107     O: BitDenotation<'tcx, Idx=MovePathIndex> + HasMoveData<'tcx>
108 {
109     let move_data = results.0.operator.move_data();
110     let mir::BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = mir[bb];
111
112     let (args, span) = match is_rustc_peek(tcx, terminator) {
113         Some(args_and_span) => args_and_span,
114         None => return,
115     };
116     assert!(args.len() == 1);
117     let peek_arg_place = match args[0] {
118         mir::Operand::Copy(ref place @ mir::Place::Base(mir::PlaceBase::Local(_))) |
119         mir::Operand::Move(ref place @ mir::Place::Base(mir::PlaceBase::Local(_))) => Some(place),
120         _ => None,
121     };
122
123     let peek_arg_place = match peek_arg_place {
124         Some(arg) => arg,
125         None => {
126             tcx.sess.diagnostic().span_err(
127                 span, "dataflow::sanity_check cannot feed a non-temp to rustc_peek.");
128             return;
129         }
130     };
131
132     let mut on_entry = results.0.sets.on_entry_set_for(bb.index()).to_owned();
133     let mut gen_set = results.0.sets.gen_set_for(bb.index()).clone();
134     let mut kill_set = results.0.sets.kill_set_for(bb.index()).clone();
135
136     // Emulate effect of all statements in the block up to (but not
137     // including) the borrow within `peek_arg_place`. Do *not* include
138     // call to `peek_arg_place` itself (since we are peeking the state
139     // of the argument at time immediate preceding Call to
140     // `rustc_peek`).
141
142     let mut sets = dataflow::BlockSets { on_entry: &mut on_entry,
143                                          gen_set: &mut gen_set,
144                                          kill_set: &mut kill_set };
145
146     for (j, stmt) in statements.iter().enumerate() {
147         debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt);
148         let (place, rvalue) = match stmt.kind {
149             mir::StatementKind::Assign(ref place, ref rvalue) => {
150                 (place, rvalue)
151             }
152             mir::StatementKind::FakeRead(..) |
153             mir::StatementKind::StorageLive(_) |
154             mir::StatementKind::StorageDead(_) |
155             mir::StatementKind::InlineAsm { .. } |
156             mir::StatementKind::Retag { .. } |
157             mir::StatementKind::AscribeUserType(..) |
158             mir::StatementKind::Nop => continue,
159             mir::StatementKind::SetDiscriminant{ .. } =>
160                 span_bug!(stmt.source_info.span,
161                           "sanity_check should run before Deaggregator inserts SetDiscriminant"),
162         };
163
164         if place == peek_arg_place {
165             if let mir::Rvalue::Ref(_, mir::BorrowKind::Shared, ref peeking_at_place) = **rvalue {
166                 // Okay, our search is over.
167                 match move_data.rev_lookup.find(peeking_at_place) {
168                     LookupResult::Exact(peek_mpi) => {
169                         let bit_state = sets.on_entry.contains(peek_mpi);
170                         debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
171                                place, peeking_at_place, bit_state);
172                         if !bit_state {
173                             tcx.sess.span_err(span, "rustc_peek: bit not set");
174                         }
175                     }
176                     LookupResult::Parent(..) => {
177                         tcx.sess.span_err(span, "rustc_peek: argument untracked");
178                     }
179                 }
180                 return;
181             } else {
182                 // Our search should have been over, but the input
183                 // does not match expectations of `rustc_peek` for
184                 // this sanity_check.
185                 let msg = "rustc_peek: argument expression \
186                            must be immediate borrow of form `&expr`";
187                 tcx.sess.span_err(span, msg);
188             }
189         }
190
191         let lhs_mpi = move_data.rev_lookup.find(place);
192
193         debug!("rustc_peek: computing effect on place: {:?} ({:?}) in stmt: {:?}",
194                place, lhs_mpi, stmt);
195         // reset GEN and KILL sets before emulating their effect.
196         sets.gen_set.clear();
197         sets.kill_set.clear();
198         results.0.operator.before_statement_effect(
199             &mut sets, Location { block: bb, statement_index: j });
200         results.0.operator.statement_effect(
201             &mut sets, Location { block: bb, statement_index: j });
202         sets.on_entry.union(sets.gen_set);
203         sets.on_entry.subtract(sets.kill_set);
204     }
205
206     results.0.operator.before_terminator_effect(
207         &mut sets,
208         Location { block: bb, statement_index: statements.len() });
209
210     tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \
211                                       anticipated pattern; note that \
212                                       rustc_peek expects input of \
213                                       form `&expr`"));
214 }
215
216 fn is_rustc_peek<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
217                            terminator: &'a Option<mir::Terminator<'tcx>>)
218                            -> Option<(&'a [mir::Operand<'tcx>], Span)> {
219     if let Some(mir::Terminator { ref kind, source_info, .. }) = *terminator {
220         if let mir::TerminatorKind::Call { func: ref oper, ref args, .. } = *kind {
221             if let mir::Operand::Constant(ref func) = *oper {
222                 if let ty::FnDef(def_id, _) = func.ty.sty {
223                     let abi = tcx.fn_sig(def_id).abi();
224                     let name = tcx.item_name(def_id);
225                     if abi == Abi::RustIntrinsic &&  name == "rustc_peek" {
226                         return Some((args, source_info.span));
227                     }
228                 }
229             }
230         }
231     }
232     return None;
233 }