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 borrowck::BorrowckCtxt;
13 use syntax::ast::{self, MetaItem};
14 use syntax_pos::DUMMY_SP;
16 use rustc::mir::{self, BasicBlock, BasicBlockData, Mir, Statement, Terminator, Location};
17 use rustc::session::Session;
18 use rustc::ty::{self, TyCtxt};
19 use rustc_mir::util::elaborate_drops::DropFlagState;
20 use rustc_data_structures::indexed_set::{IdxSet, IdxSetBuf};
23 pub mod elaborate_drops;
28 use self::dataflow::{BitDenotation};
29 use self::dataflow::{DataflowOperator};
30 use self::dataflow::{Dataflow, DataflowAnalysis, DataflowResults};
31 use self::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
32 use self::dataflow::{DefinitelyInitializedLvals};
33 use self::gather_moves::{HasMoveData, MoveData, MovePathIndex, LookupResult};
37 fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option<MetaItem> {
39 if attr.check_name("rustc_mir") {
40 let items = attr.meta_item_list();
41 for item in items.iter().flat_map(|l| l.iter()) {
42 match item.meta_item() {
43 Some(mi) if mi.check_name(name) => return Some(mi.clone()),
52 pub struct MoveDataParamEnv<'tcx> {
53 move_data: MoveData<'tcx>,
54 param_env: ty::ParameterEnvironment<'tcx>,
57 pub fn borrowck_mir(bcx: &mut BorrowckCtxt,
59 attributes: &[ast::Attribute]) {
61 let def_id = tcx.hir.local_def_id(id);
62 debug!("borrowck_mir({}) UNIMPLEMENTED", tcx.item_path_str(def_id));
64 let mir = &tcx.item_mir(def_id);
65 let param_env = ty::ParameterEnvironment::for_item(tcx, id);
66 let move_data = MoveData::gather_moves(mir, tcx, ¶m_env);
67 let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
68 let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
70 do_dataflow(tcx, mir, id, attributes, &dead_unwinds,
71 MaybeInitializedLvals::new(tcx, mir, &mdpe),
72 |bd, i| &bd.move_data().move_paths[i]);
74 do_dataflow(tcx, mir, id, attributes, &dead_unwinds,
75 MaybeUninitializedLvals::new(tcx, mir, &mdpe),
76 |bd, i| &bd.move_data().move_paths[i]);
78 do_dataflow(tcx, mir, id, attributes, &dead_unwinds,
79 DefinitelyInitializedLvals::new(tcx, mir, &mdpe),
80 |bd, i| &bd.move_data().move_paths[i]);
82 if has_rustc_mir_with(attributes, "rustc_peek_maybe_init").is_some() {
83 dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &flow_inits);
85 if has_rustc_mir_with(attributes, "rustc_peek_maybe_uninit").is_some() {
86 dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &flow_uninits);
88 if has_rustc_mir_with(attributes, "rustc_peek_definite_init").is_some() {
89 dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &flow_def_inits);
92 if has_rustc_mir_with(attributes, "stop_after_dataflow").is_some() {
93 bcx.tcx.sess.fatal("stop_after_dataflow ended compilation");
96 let mut mbcx = MirBorrowckCtxt {
100 move_data: &mdpe.move_data,
101 flow_inits: flow_inits,
102 flow_uninits: flow_uninits,
105 for bb in mir.basic_blocks().indices() {
106 mbcx.process_basic_block(bb);
109 debug!("borrowck_mir done");
112 fn do_dataflow<'a, 'tcx, BD, P>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
114 node_id: ast::NodeId,
115 attributes: &[ast::Attribute],
116 dead_unwinds: &IdxSet<BasicBlock>,
119 -> DataflowResults<BD>
120 where BD: BitDenotation<Idx=MovePathIndex> + DataflowOperator,
121 P: Fn(&BD, BD::Idx) -> &fmt::Debug
123 let name_found = |sess: &Session, attrs: &[ast::Attribute], name| -> Option<String> {
124 if let Some(item) = has_rustc_mir_with(attrs, name) {
125 if let Some(s) = item.value_str() {
126 return Some(s.to_string())
130 &format!("{} attribute requires a path", item.name()));
137 let print_preflow_to =
138 name_found(tcx.sess, attributes, "borrowck_graphviz_preflow");
139 let print_postflow_to =
140 name_found(tcx.sess, attributes, "borrowck_graphviz_postflow");
142 let mut mbcx = MirBorrowckCtxtPreDataflow {
144 print_preflow_to: print_preflow_to,
145 print_postflow_to: print_postflow_to,
146 flow_state: DataflowAnalysis::new(tcx, mir, dead_unwinds, bd),
150 mbcx.flow_state.results()
154 pub struct MirBorrowckCtxtPreDataflow<'a, 'tcx: 'a, BD> where BD: BitDenotation
156 node_id: ast::NodeId,
157 flow_state: DataflowAnalysis<'a, 'tcx, BD>,
158 print_preflow_to: Option<String>,
159 print_postflow_to: Option<String>,
163 pub struct MirBorrowckCtxt<'b, 'a: 'b, 'tcx: 'a> {
164 bcx: &'b mut BorrowckCtxt<'a, 'tcx>,
166 node_id: ast::NodeId,
167 move_data: &'b MoveData<'tcx>,
168 flow_inits: DataflowResults<MaybeInitializedLvals<'b, 'tcx>>,
169 flow_uninits: DataflowResults<MaybeUninitializedLvals<'b, 'tcx>>
172 impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
173 fn process_basic_block(&mut self, bb: BasicBlock) {
174 let BasicBlockData { ref statements, ref terminator, is_cleanup: _ } =
176 for stmt in statements {
177 self.process_statement(bb, stmt);
180 self.process_terminator(bb, terminator);
183 fn process_statement(&mut self, bb: BasicBlock, stmt: &Statement<'tcx>) {
184 debug!("MirBorrowckCtxt::process_statement({:?}, {:?}", bb, stmt);
187 fn process_terminator(&mut self, bb: BasicBlock, term: &Option<Terminator<'tcx>>) {
188 debug!("MirBorrowckCtxt::process_terminator({:?}, {:?})", bb, term);
192 fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>,
195 -> Option<MovePathIndex>
196 where F: FnMut(&mir::LvalueProjection<'tcx>) -> bool
198 let mut next_child = move_data.move_paths[path].first_child;
199 while let Some(child_index) = next_child {
200 match move_data.move_paths[child_index].lvalue {
201 mir::Lvalue::Projection(ref proj) => {
203 return Some(child_index)
208 next_child = move_data.move_paths[child_index].next_sibling;
214 /// When enumerating the child fragments of a path, don't recurse into
215 /// paths (1.) past arrays, slices, and pointers, nor (2.) into a type
216 /// that implements `Drop`.
218 /// Lvalues behind references or arrays are not tracked by elaboration
219 /// and are always assumed to be initialized when accessible. As
220 /// references and indexes can be reseated, trying to track them can
221 /// only lead to trouble.
223 /// Lvalues behind ADT's with a Drop impl are not tracked by
224 /// elaboration since they can never have a drop-flag state that
225 /// differs from that of the parent with the Drop impl.
227 /// In both cases, the contents can only be accessed if and only if
228 /// their parents are initialized. This implies for example that there
229 /// is no need to maintain separate drop flags to track such state.
231 /// FIXME: we have to do something for moving slice patterns.
232 fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
234 lv: &mir::Lvalue<'tcx>) -> bool {
235 let ty = lv.ty(mir, tcx).to_ty(tcx);
237 ty::TyArray(..) | ty::TySlice(..) | ty::TyRef(..) | ty::TyRawPtr(..) => {
238 debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} refd => true",
242 ty::TyAdt(def, _) if (def.has_dtor(tcx) && !def.is_box()) || def.is_union() => {
243 debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} Drop => true",
253 fn on_lookup_result_bits<'a, 'tcx, F>(
254 tcx: TyCtxt<'a, 'tcx, 'tcx>,
256 move_data: &MoveData<'tcx>,
257 lookup_result: LookupResult,
259 where F: FnMut(MovePathIndex)
261 match lookup_result {
262 LookupResult::Parent(..) => {
263 // access to untracked value - do not touch children
265 LookupResult::Exact(e) => {
266 on_all_children_bits(tcx, mir, move_data, e, each_child)
271 fn on_all_children_bits<'a, 'tcx, F>(
272 tcx: TyCtxt<'a, 'tcx, 'tcx>,
274 move_data: &MoveData<'tcx>,
275 move_path_index: MovePathIndex,
277 where F: FnMut(MovePathIndex)
279 fn is_terminal_path<'a, 'tcx>(
280 tcx: TyCtxt<'a, 'tcx, 'tcx>,
282 move_data: &MoveData<'tcx>,
283 path: MovePathIndex) -> bool
285 lvalue_contents_drop_state_cannot_differ(
286 tcx, mir, &move_data.move_paths[path].lvalue)
289 fn on_all_children_bits<'a, 'tcx, F>(
290 tcx: TyCtxt<'a, 'tcx, 'tcx>,
292 move_data: &MoveData<'tcx>,
293 move_path_index: MovePathIndex,
295 where F: FnMut(MovePathIndex)
297 each_child(move_path_index);
299 if is_terminal_path(tcx, mir, move_data, move_path_index) {
303 let mut next_child_index = move_data.move_paths[move_path_index].first_child;
304 while let Some(child_index) = next_child_index {
305 on_all_children_bits(tcx, mir, move_data, child_index, each_child);
306 next_child_index = move_data.move_paths[child_index].next_sibling;
309 on_all_children_bits(tcx, mir, move_data, move_path_index, &mut each_child);
312 fn on_all_drop_children_bits<'a, 'tcx, F>(
313 tcx: TyCtxt<'a, 'tcx, 'tcx>,
315 ctxt: &MoveDataParamEnv<'tcx>,
318 where F: FnMut(MovePathIndex)
320 on_all_children_bits(tcx, mir, &ctxt.move_data, path, |child| {
321 let lvalue = &ctxt.move_data.move_paths[path].lvalue;
322 let ty = lvalue.ty(mir, tcx).to_ty(tcx);
323 debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, lvalue, ty);
325 if ty.needs_drop(tcx, &ctxt.param_env) {
328 debug!("on_all_drop_children_bits - skipping")
333 fn drop_flag_effects_for_function_entry<'a, 'tcx, F>(
334 tcx: TyCtxt<'a, 'tcx, 'tcx>,
336 ctxt: &MoveDataParamEnv<'tcx>,
338 where F: FnMut(MovePathIndex, DropFlagState)
340 let move_data = &ctxt.move_data;
341 for arg in mir.args_iter() {
342 let lvalue = mir::Lvalue::Local(arg);
343 let lookup_result = move_data.rev_lookup.find(&lvalue);
344 on_lookup_result_bits(tcx, mir, move_data,
346 |moi| callback(moi, DropFlagState::Present));
350 fn drop_flag_effects_for_location<'a, 'tcx, F>(
351 tcx: TyCtxt<'a, 'tcx, 'tcx>,
353 ctxt: &MoveDataParamEnv<'tcx>,
356 where F: FnMut(MovePathIndex, DropFlagState)
358 let move_data = &ctxt.move_data;
359 let param_env = &ctxt.param_env;
360 debug!("drop_flag_effects_for_location({:?})", loc);
362 // first, move out of the RHS
363 for mi in &move_data.loc_map[loc] {
364 let path = mi.move_path_index(move_data);
365 debug!("moving out of path {:?}", move_data.move_paths[path]);
367 // don't move out of non-Copy things
368 let lvalue = &move_data.move_paths[path].lvalue;
369 let ty = lvalue.ty(mir, tcx).to_ty(tcx);
370 if !ty.moves_by_default(tcx, param_env, DUMMY_SP) {
374 on_all_children_bits(tcx, mir, move_data,
376 |moi| callback(moi, DropFlagState::Absent))
379 let block = &mir[loc.block];
380 match block.statements.get(loc.statement_index) {
381 Some(stmt) => match stmt.kind {
382 mir::StatementKind::SetDiscriminant{ .. } => {
383 span_bug!(stmt.source_info.span, "SetDiscrimant should not exist during borrowck");
385 mir::StatementKind::Assign(ref lvalue, _) => {
386 debug!("drop_flag_effects: assignment {:?}", stmt);
387 on_lookup_result_bits(tcx, mir, move_data,
388 move_data.rev_lookup.find(lvalue),
389 |moi| callback(moi, DropFlagState::Present))
391 mir::StatementKind::StorageLive(_) |
392 mir::StatementKind::StorageDead(_) |
393 mir::StatementKind::InlineAsm { .. } |
394 mir::StatementKind::Nop => {}
397 debug!("drop_flag_effects: replace {:?}", block.terminator());
398 match block.terminator().kind {
399 mir::TerminatorKind::DropAndReplace { ref location, .. } => {
400 on_lookup_result_bits(tcx, mir, move_data,
401 move_data.rev_lookup.find(location),
402 |moi| callback(moi, DropFlagState::Present))
405 // other terminators do not contain move-ins