1 use rustc_target::spec::abi::{Abi};
3 use syntax::symbol::sym;
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};
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
19 use crate::dataflow::move_paths::{MovePathIndex, LookupResult};
20 use crate::dataflow::move_paths::{HasMoveData, MoveData};
23 use crate::dataflow::has_rustc_mir_with;
25 pub struct SanityCheck;
27 impl MirPass for SanityCheck {
28 fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
29 src: MirSource<'tcx>, mir: &mut Body<'tcx>) {
30 let def_id = src.def_id();
31 if !tcx.has_attr(def_id, sym::rustc_mir) {
32 debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
35 debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
38 let attributes = tcx.get_attrs(def_id);
39 let param_env = tcx.param_env(def_id);
40 let move_data = MoveData::gather_moves(mir, tcx).unwrap();
41 let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
42 let dead_unwinds = BitSet::new_empty(mir.basic_blocks().len());
44 do_dataflow(tcx, mir, def_id, &attributes, &dead_unwinds,
45 MaybeInitializedPlaces::new(tcx, mir, &mdpe),
46 |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
48 do_dataflow(tcx, mir, def_id, &attributes, &dead_unwinds,
49 MaybeUninitializedPlaces::new(tcx, mir, &mdpe),
50 |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
52 do_dataflow(tcx, mir, def_id, &attributes, &dead_unwinds,
53 DefinitelyInitializedPlaces::new(tcx, mir, &mdpe),
54 |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
56 if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_init).is_some() {
57 sanity_check_via_rustc_peek(tcx, mir, def_id, &attributes, &flow_inits);
59 if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_uninit).is_some() {
60 sanity_check_via_rustc_peek(tcx, mir, def_id, &attributes, &flow_uninits);
62 if has_rustc_mir_with(&attributes, sym::rustc_peek_definite_init).is_some() {
63 sanity_check_via_rustc_peek(tcx, mir, def_id, &attributes, &flow_def_inits);
65 if has_rustc_mir_with(&attributes, sym::stop_after_dataflow).is_some() {
66 tcx.sess.fatal("stop_after_dataflow ended compilation");
71 /// This function scans `mir` for all calls to the intrinsic
72 /// `rustc_peek` that have the expression form `rustc_peek(&expr)`.
74 /// For each such call, determines what the dataflow bit-state is for
75 /// the L-value corresponding to `expr`; if the bit-state is a 1, then
76 /// that call to `rustc_peek` is ignored by the sanity check. If the
77 /// bit-state is a 0, then this pass emits a error message saying
78 /// "rustc_peek: bit not set".
80 /// The intention is that one can write unit tests for dataflow by
81 /// putting code into a compile-fail test and using `rustc_peek` to
82 /// make observations about the results of dataflow static analyses.
84 /// (If there are any calls to `rustc_peek` that do not match the
85 /// expression form above, then that emits an error as well, but those
86 /// errors are not intended to be used for unit tests.)
87 pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
90 _attributes: &[ast::Attribute],
91 results: &DataflowResults<'tcx, O>)
92 where O: BitDenotation<'tcx, Idx=MovePathIndex> + HasMoveData<'tcx>
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.)
99 for bb in mir.basic_blocks().indices() {
100 each_block(tcx, mir, results, bb);
104 fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
106 results: &DataflowResults<'tcx, O>,
107 bb: mir::BasicBlock) where
108 O: BitDenotation<'tcx, Idx=MovePathIndex> + HasMoveData<'tcx>
110 let move_data = results.0.operator.move_data();
111 let mir::BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = mir[bb];
113 let (args, span) = match is_rustc_peek(tcx, terminator) {
114 Some(args_and_span) => args_and_span,
117 assert!(args.len() == 1);
118 let peek_arg_place = match args[0] {
119 mir::Operand::Copy(ref place @ mir::Place::Base(mir::PlaceBase::Local(_))) |
120 mir::Operand::Move(ref place @ mir::Place::Base(mir::PlaceBase::Local(_))) => Some(place),
124 let peek_arg_place = match peek_arg_place {
127 tcx.sess.diagnostic().span_err(
128 span, "dataflow::sanity_check cannot feed a non-temp to rustc_peek.");
133 let mut on_entry = results.0.sets.on_entry_set_for(bb.index()).to_owned();
134 let mut gen_set = results.0.sets.gen_set_for(bb.index()).clone();
135 let mut kill_set = results.0.sets.kill_set_for(bb.index()).clone();
137 // Emulate effect of all statements in the block up to (but not
138 // including) the borrow within `peek_arg_place`. Do *not* include
139 // call to `peek_arg_place` itself (since we are peeking the state
140 // of the argument at time immediate preceding Call to
143 let mut sets = dataflow::BlockSets { on_entry: &mut on_entry,
144 gen_set: &mut gen_set,
145 kill_set: &mut kill_set };
147 for (j, stmt) in statements.iter().enumerate() {
148 debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt);
149 let (place, rvalue) = match stmt.kind {
150 mir::StatementKind::Assign(ref place, ref rvalue) => {
153 mir::StatementKind::FakeRead(..) |
154 mir::StatementKind::StorageLive(_) |
155 mir::StatementKind::StorageDead(_) |
156 mir::StatementKind::InlineAsm { .. } |
157 mir::StatementKind::Retag { .. } |
158 mir::StatementKind::AscribeUserType(..) |
159 mir::StatementKind::Nop => continue,
160 mir::StatementKind::SetDiscriminant{ .. } =>
161 span_bug!(stmt.source_info.span,
162 "sanity_check should run before Deaggregator inserts SetDiscriminant"),
165 if place == peek_arg_place {
166 if let mir::Rvalue::Ref(_, mir::BorrowKind::Shared, ref peeking_at_place) = **rvalue {
167 // Okay, our search is over.
168 match move_data.rev_lookup.find(peeking_at_place) {
169 LookupResult::Exact(peek_mpi) => {
170 let bit_state = sets.on_entry.contains(peek_mpi);
171 debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
172 place, peeking_at_place, bit_state);
174 tcx.sess.span_err(span, "rustc_peek: bit not set");
177 LookupResult::Parent(..) => {
178 tcx.sess.span_err(span, "rustc_peek: argument untracked");
183 // Our search should have been over, but the input
184 // does not match expectations of `rustc_peek` for
185 // this sanity_check.
186 let msg = "rustc_peek: argument expression \
187 must be immediate borrow of form `&expr`";
188 tcx.sess.span_err(span, msg);
192 let lhs_mpi = move_data.rev_lookup.find(place);
194 debug!("rustc_peek: computing effect on place: {:?} ({:?}) in stmt: {:?}",
195 place, lhs_mpi, stmt);
196 // reset GEN and KILL sets before emulating their effect.
197 sets.gen_set.clear();
198 sets.kill_set.clear();
199 results.0.operator.before_statement_effect(
200 &mut sets, Location { block: bb, statement_index: j });
201 results.0.operator.statement_effect(
202 &mut sets, Location { block: bb, statement_index: j });
203 sets.on_entry.union(sets.gen_set);
204 sets.on_entry.subtract(sets.kill_set);
207 results.0.operator.before_terminator_effect(
209 Location { block: bb, statement_index: statements.len() });
211 tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \
212 anticipated pattern; note that \
213 rustc_peek expects input of \
217 fn is_rustc_peek<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
218 terminator: &'a Option<mir::Terminator<'tcx>>)
219 -> Option<(&'a [mir::Operand<'tcx>], Span)> {
220 if let Some(mir::Terminator { ref kind, source_info, .. }) = *terminator {
221 if let mir::TerminatorKind::Call { func: ref oper, ref args, .. } = *kind {
222 if let mir::Operand::Constant(ref func) = *oper {
223 if let ty::FnDef(def_id, _) = func.ty.sty {
224 let abi = tcx.fn_sig(def_id).abi();
225 let name = tcx.item_name(def_id);
226 if abi == Abi::RustIntrinsic && name == sym::rustc_peek {
227 return Some((args, source_info.span));