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.
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.
11 use syntax::abi::{Abi};
15 use rustc::ty::{self, TyCtxt};
16 use rustc::mir::{self, Mir, Location};
17 use rustc_data_structures::indexed_set::IdxSetBuf;
18 use rustc_data_structures::indexed_vec::Idx;
19 use transform::{MirPass, MirSource};
21 use dataflow::do_dataflow;
22 use dataflow::MoveDataParamEnv;
23 use dataflow::BitDenotation;
24 use dataflow::DataflowResults;
25 use dataflow::{DefinitelyInitializedLvals, MaybeInitializedLvals, MaybeUninitializedLvals};
26 use dataflow::move_paths::{MovePathIndex, LookupResult};
27 use dataflow::move_paths::{HasMoveData, MoveData};
30 use dataflow::has_rustc_mir_with;
32 pub struct SanityCheck;
34 impl MirPass for SanityCheck {
35 fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
36 src: MirSource, mir: &mut Mir<'tcx>) {
37 let def_id = src.def_id;
38 let id = tcx.hir.as_local_node_id(def_id).unwrap();
39 if !tcx.has_attr(def_id, "rustc_mir_borrowck") {
40 debug!("skipping rustc_peek::SanityCheck on {}", tcx.item_path_str(def_id));
43 debug!("running rustc_peek::SanityCheck on {}", tcx.item_path_str(def_id));
46 let attributes = tcx.get_attrs(def_id);
47 let param_env = tcx.param_env(def_id);
48 let move_data = MoveData::gather_moves(mir, tcx, param_env).unwrap();
49 let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
50 let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
52 do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
53 MaybeInitializedLvals::new(tcx, mir, &mdpe),
54 |bd, i| &bd.move_data().move_paths[i]);
56 do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
57 MaybeUninitializedLvals::new(tcx, mir, &mdpe),
58 |bd, i| &bd.move_data().move_paths[i]);
60 do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
61 DefinitelyInitializedLvals::new(tcx, mir, &mdpe),
62 |bd, i| &bd.move_data().move_paths[i]);
64 if has_rustc_mir_with(&attributes, "rustc_peek_maybe_init").is_some() {
65 sanity_check_via_rustc_peek(tcx, mir, id, &attributes, &flow_inits);
67 if has_rustc_mir_with(&attributes, "rustc_peek_maybe_uninit").is_some() {
68 sanity_check_via_rustc_peek(tcx, mir, id, &attributes, &flow_uninits);
70 if has_rustc_mir_with(&attributes, "rustc_peek_definite_init").is_some() {
71 sanity_check_via_rustc_peek(tcx, mir, id, &attributes, &flow_def_inits);
73 if has_rustc_mir_with(&attributes, "stop_after_dataflow").is_some() {
74 tcx.sess.fatal("stop_after_dataflow ended compilation");
79 /// This function scans `mir` for all calls to the intrinsic
80 /// `rustc_peek` that have the expression form `rustc_peek(&expr)`.
82 /// For each such call, determines what the dataflow bit-state is for
83 /// the L-value corresponding to `expr`; if the bit-state is a 1, then
84 /// that call to `rustc_peek` is ignored by the sanity check. If the
85 /// bit-state is a 0, then this pass emits a error message saying
86 /// "rustc_peek: bit not set".
88 /// The intention is that one can write unit tests for dataflow by
89 /// putting code into a compile-fail test and using `rustc_peek` to
90 /// make observations about the results of dataflow static analyses.
92 /// (If there are any calls to `rustc_peek` that do not match the
93 /// expression form above, then that emits an error as well, but those
94 /// errors are not intended to be used for unit tests.)
95 pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
98 _attributes: &[ast::Attribute],
99 results: &DataflowResults<O>)
100 where O: BitDenotation<Idx=MovePathIndex> + HasMoveData<'tcx>
102 debug!("sanity_check_via_rustc_peek id: {:?}", id);
103 // FIXME: this is not DRY. Figure out way to abstract this and
104 // `dataflow::build_sets`. (But note it is doing non-standard
105 // stuff, so such generalization may not be realistic.)
107 for bb in mir.basic_blocks().indices() {
108 each_block(tcx, mir, results, bb);
112 fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
114 results: &DataflowResults<O>,
115 bb: mir::BasicBlock) where
116 O: BitDenotation<Idx=MovePathIndex> + HasMoveData<'tcx>
118 let move_data = results.0.operator.move_data();
119 let mir::BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = mir[bb];
121 let (args, span) = match is_rustc_peek(tcx, terminator) {
122 Some(args_and_span) => args_and_span,
125 assert!(args.len() == 1);
126 let peek_arg_lval = match args[0] {
127 mir::Operand::Consume(ref lval @ mir::Lvalue::Local(_)) => Some(lval),
131 let peek_arg_lval = match peek_arg_lval {
134 tcx.sess.diagnostic().span_err(
135 span, "dataflow::sanity_check cannot feed a non-temp to rustc_peek.");
140 let mut entry = results.0.sets.on_entry_set_for(bb.index()).to_owned();
141 let mut gen = results.0.sets.gen_set_for(bb.index()).to_owned();
142 let mut kill = results.0.sets.kill_set_for(bb.index()).to_owned();
144 // Emulate effect of all statements in the block up to (but not
145 // including) the borrow within `peek_arg_lval`. Do *not* include
146 // call to `peek_arg_lval` itself (since we are peeking the state
147 // of the argument at time immediate preceding Call to
150 let mut sets = dataflow::BlockSets { on_entry: &mut entry,
152 kill_set: &mut kill };
154 for (j, stmt) in statements.iter().enumerate() {
155 debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt);
156 let (lvalue, rvalue) = match stmt.kind {
157 mir::StatementKind::Assign(ref lvalue, ref rvalue) => {
160 mir::StatementKind::StorageLive(_) |
161 mir::StatementKind::StorageDead(_) |
162 mir::StatementKind::InlineAsm { .. } |
163 mir::StatementKind::EndRegion(_) |
164 mir::StatementKind::Validate(..) |
165 mir::StatementKind::Nop => continue,
166 mir::StatementKind::SetDiscriminant{ .. } =>
167 span_bug!(stmt.source_info.span,
168 "sanity_check should run before Deaggregator inserts SetDiscriminant"),
171 if lvalue == peek_arg_lval {
172 if let mir::Rvalue::Ref(_, mir::BorrowKind::Shared, ref peeking_at_lval) = *rvalue {
173 // Okay, our search is over.
174 match move_data.rev_lookup.find(peeking_at_lval) {
175 LookupResult::Exact(peek_mpi) => {
176 let bit_state = sets.on_entry.contains(&peek_mpi);
177 debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
178 lvalue, peeking_at_lval, bit_state);
180 tcx.sess.span_err(span, "rustc_peek: bit not set");
183 LookupResult::Parent(..) => {
184 tcx.sess.span_err(span, "rustc_peek: argument untracked");
189 // Our search should have been over, but the input
190 // does not match expectations of `rustc_peek` for
191 // this sanity_check.
192 let msg = "rustc_peek: argument expression \
193 must be immediate borrow of form `&expr`";
194 tcx.sess.span_err(span, msg);
198 let lhs_mpi = move_data.rev_lookup.find(lvalue);
200 debug!("rustc_peek: computing effect on lvalue: {:?} ({:?}) in stmt: {:?}",
201 lvalue, lhs_mpi, stmt);
202 // reset GEN and KILL sets before emulating their effect.
203 for e in sets.gen_set.words_mut() { *e = 0; }
204 for e in sets.kill_set.words_mut() { *e = 0; }
205 results.0.operator.statement_effect(&mut sets, Location { block: bb, statement_index: j });
206 sets.on_entry.union(sets.gen_set);
207 sets.on_entry.subtract(sets.kill_set);
210 tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \
211 anticipated pattern; note that \
212 rustc_peek expects input of \
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::TyFnDef(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));