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