1 // Copyright 2012-2017 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::ast::{self, MetaItem};
12 use syntax_pos::DUMMY_SP;
15 use rustc::mir::{self, Mir, BasicBlock, Location};
16 use rustc::session::Session;
17 use rustc::ty::{self, TyCtxt};
18 use util::elaborate_drops::DropFlagState;
19 use rustc_data_structures::indexed_set::{IdxSet};
23 use super::{Dataflow, DataflowBuilder, DataflowAnalysis};
24 use super::{BitDenotation, DataflowOperator, DataflowResults};
25 use super::indexes::MovePathIndex;
26 use super::move_paths::{MoveData, LookupResult};
28 pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option<MetaItem> {
30 if attr.check_name("rustc_mir") {
31 let items = attr.meta_item_list();
32 for item in items.iter().flat_map(|l| l.iter()) {
33 match item.meta_item() {
34 Some(mi) if mi.check_name(name) => return Some(mi.clone()),
43 pub struct MoveDataParamEnv<'tcx> {
44 pub(crate) move_data: MoveData<'tcx>,
45 pub(crate) param_env: ty::ParamEnv<'tcx>,
48 pub(crate) fn do_dataflow<'a, 'tcx, BD, P>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
51 attributes: &[ast::Attribute],
52 dead_unwinds: &IdxSet<BasicBlock>,
55 -> DataflowResults<BD>
56 where BD: BitDenotation<Idx=MovePathIndex> + DataflowOperator,
57 P: Fn(&BD, BD::Idx) -> &fmt::Debug
59 let name_found = |sess: &Session, attrs: &[ast::Attribute], name| -> Option<String> {
60 if let Some(item) = has_rustc_mir_with(attrs, name) {
61 if let Some(s) = item.value_str() {
62 return Some(s.to_string())
66 &format!("{} attribute requires a path", item.name()));
73 let print_preflow_to =
74 name_found(tcx.sess, attributes, "borrowck_graphviz_preflow");
75 let print_postflow_to =
76 name_found(tcx.sess, attributes, "borrowck_graphviz_postflow");
78 let mut mbcx = DataflowBuilder {
80 print_preflow_to: print_preflow_to,
81 print_postflow_to: print_postflow_to,
82 flow_state: DataflowAnalysis::new(tcx, mir, dead_unwinds, bd),
86 mbcx.flow_state.results()
89 pub fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>,
92 -> Option<MovePathIndex>
93 where F: FnMut(&mir::LvalueProjection<'tcx>) -> bool
95 let mut next_child = move_data.move_paths[path].first_child;
96 while let Some(child_index) = next_child {
97 match move_data.move_paths[child_index].lvalue {
98 mir::Lvalue::Projection(ref proj) => {
100 return Some(child_index)
105 next_child = move_data.move_paths[child_index].next_sibling;
111 /// When enumerating the child fragments of a path, don't recurse into
112 /// paths (1.) past arrays, slices, and pointers, nor (2.) into a type
113 /// that implements `Drop`.
115 /// Lvalues behind references or arrays are not tracked by elaboration
116 /// and are always assumed to be initialized when accessible. As
117 /// references and indexes can be reseated, trying to track them can
118 /// only lead to trouble.
120 /// Lvalues behind ADT's with a Drop impl are not tracked by
121 /// elaboration since they can never have a drop-flag state that
122 /// differs from that of the parent with the Drop impl.
124 /// In both cases, the contents can only be accessed if and only if
125 /// their parents are initialized. This implies for example that there
126 /// is no need to maintain separate drop flags to track such state.
128 /// FIXME: we have to do something for moving slice patterns.
129 fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
131 lv: &mir::Lvalue<'tcx>) -> bool {
132 let ty = lv.ty(mir, tcx).to_ty(tcx);
134 ty::TyArray(..) | ty::TySlice(..) | ty::TyRef(..) | ty::TyRawPtr(..) => {
135 debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} refd => true",
139 ty::TyAdt(def, _) if (def.has_dtor(tcx) && !def.is_box()) || def.is_union() => {
140 debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} Drop => true",
150 pub(crate) fn on_lookup_result_bits<'a, 'tcx, F>(
151 tcx: TyCtxt<'a, 'tcx, 'tcx>,
153 move_data: &MoveData<'tcx>,
154 lookup_result: LookupResult,
156 where F: FnMut(MovePathIndex)
158 match lookup_result {
159 LookupResult::Parent(..) => {
160 // access to untracked value - do not touch children
162 LookupResult::Exact(e) => {
163 on_all_children_bits(tcx, mir, move_data, e, each_child)
168 pub(crate) fn on_all_children_bits<'a, 'tcx, F>(
169 tcx: TyCtxt<'a, 'tcx, 'tcx>,
171 move_data: &MoveData<'tcx>,
172 move_path_index: MovePathIndex,
174 where F: FnMut(MovePathIndex)
176 fn is_terminal_path<'a, 'tcx>(
177 tcx: TyCtxt<'a, 'tcx, 'tcx>,
179 move_data: &MoveData<'tcx>,
180 path: MovePathIndex) -> bool
182 lvalue_contents_drop_state_cannot_differ(
183 tcx, mir, &move_data.move_paths[path].lvalue)
186 fn on_all_children_bits<'a, 'tcx, F>(
187 tcx: TyCtxt<'a, 'tcx, 'tcx>,
189 move_data: &MoveData<'tcx>,
190 move_path_index: MovePathIndex,
192 where F: FnMut(MovePathIndex)
194 each_child(move_path_index);
196 if is_terminal_path(tcx, mir, move_data, move_path_index) {
200 let mut next_child_index = move_data.move_paths[move_path_index].first_child;
201 while let Some(child_index) = next_child_index {
202 on_all_children_bits(tcx, mir, move_data, child_index, each_child);
203 next_child_index = move_data.move_paths[child_index].next_sibling;
206 on_all_children_bits(tcx, mir, move_data, move_path_index, &mut each_child);
209 pub(crate) fn on_all_drop_children_bits<'a, 'tcx, F>(
210 tcx: TyCtxt<'a, 'tcx, 'tcx>,
212 ctxt: &MoveDataParamEnv<'tcx>,
215 where F: FnMut(MovePathIndex)
217 on_all_children_bits(tcx, mir, &ctxt.move_data, path, |child| {
218 let lvalue = &ctxt.move_data.move_paths[path].lvalue;
219 let ty = lvalue.ty(mir, tcx).to_ty(tcx);
220 debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, lvalue, ty);
222 if ty.needs_drop(tcx, ctxt.param_env) {
225 debug!("on_all_drop_children_bits - skipping")
230 pub(crate) fn drop_flag_effects_for_function_entry<'a, 'tcx, F>(
231 tcx: TyCtxt<'a, 'tcx, 'tcx>,
233 ctxt: &MoveDataParamEnv<'tcx>,
235 where F: FnMut(MovePathIndex, DropFlagState)
237 let move_data = &ctxt.move_data;
238 for arg in mir.args_iter() {
239 let lvalue = mir::Lvalue::Local(arg);
240 let lookup_result = move_data.rev_lookup.find(&lvalue);
241 on_lookup_result_bits(tcx, mir, move_data,
243 |moi| callback(moi, DropFlagState::Present));
247 pub(crate) fn drop_flag_effects_for_location<'a, 'tcx, F>(
248 tcx: TyCtxt<'a, 'tcx, 'tcx>,
250 ctxt: &MoveDataParamEnv<'tcx>,
253 where F: FnMut(MovePathIndex, DropFlagState)
255 let move_data = &ctxt.move_data;
256 let param_env = ctxt.param_env;
257 debug!("drop_flag_effects_for_location({:?})", loc);
259 // first, move out of the RHS
260 for mi in &move_data.loc_map[loc] {
261 let path = mi.move_path_index(move_data);
262 debug!("moving out of path {:?}", move_data.move_paths[path]);
264 // don't move out of non-Copy things
265 let lvalue = &move_data.move_paths[path].lvalue;
266 let ty = lvalue.ty(mir, tcx).to_ty(tcx);
267 if !ty.moves_by_default(tcx, param_env, DUMMY_SP) {
271 on_all_children_bits(tcx, mir, move_data,
273 |moi| callback(moi, DropFlagState::Absent))
276 let block = &mir[loc.block];
277 match block.statements.get(loc.statement_index) {
278 Some(stmt) => match stmt.kind {
279 mir::StatementKind::SetDiscriminant{ .. } => {
280 span_bug!(stmt.source_info.span, "SetDiscrimant should not exist during borrowck");
282 mir::StatementKind::Assign(ref lvalue, _) => {
283 debug!("drop_flag_effects: assignment {:?}", stmt);
284 on_lookup_result_bits(tcx, mir, move_data,
285 move_data.rev_lookup.find(lvalue),
286 |moi| callback(moi, DropFlagState::Present))
288 mir::StatementKind::StorageLive(_) |
289 mir::StatementKind::StorageDead(_) |
290 mir::StatementKind::InlineAsm { .. } |
291 mir::StatementKind::EndRegion(_) |
292 mir::StatementKind::Nop => {}
295 debug!("drop_flag_effects: replace {:?}", block.terminator());
296 match block.terminator().kind {
297 mir::TerminatorKind::DropAndReplace { ref location, .. } => {
298 on_lookup_result_bits(tcx, mir, move_data,
299 move_data.rev_lookup.find(location),
300 |moi| callback(moi, DropFlagState::Present))
303 // other terminators do not contain move-ins