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