+++ /dev/null
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Helper routines used for fragmenting structural paths due to moves for
-//! tracking drop obligations. Please see the extensive comments in the
-//! section "Structural fragments" in `README.md`.
-
-use self::Fragment::*;
-
-use borrowck::InteriorKind::{InteriorField, InteriorElement};
-use borrowck::{self, LoanPath};
-use borrowck::LoanPathKind::{LpVar, LpUpvar, LpDowncast, LpExtend};
-use borrowck::LoanPathElem::{LpDeref, LpInterior};
-use borrowck::move_data::InvalidMovePathIndex;
-use borrowck::move_data::{MoveData, MovePathIndex};
-use rustc::hir::def_id::{DefId};
-use rustc::ty::{self, AdtKind, TyCtxt};
-use rustc::middle::mem_categorization as mc;
-
-use std::mem;
-use std::rc::Rc;
-use syntax::ast;
-use syntax_pos::DUMMY_SP;
-
-#[derive(PartialEq, Eq, PartialOrd, Ord)]
-enum Fragment {
- // This represents the path described by the move path index
- Just(MovePathIndex),
-
- // This represents the collection of all but one of the elements
- // from an array at the path described by the move path index.
- // Note that attached MovePathIndex should have mem_categorization
- // of InteriorElement (i.e. array dereference `&foo[..]`).
- AllButOneFrom(MovePathIndex),
-}
-
-impl Fragment {
- fn loan_path_repr(&self, move_data: &MoveData) -> String {
- let lp = |mpi| move_data.path_loan_path(mpi);
- match *self {
- Just(mpi) => format!("{:?}", lp(mpi)),
- AllButOneFrom(mpi) => format!("$(allbutone {:?})", lp(mpi)),
- }
- }
-
- fn loan_path_user_string(&self, move_data: &MoveData) -> String {
- let lp = |mpi| move_data.path_loan_path(mpi);
- match *self {
- Just(mpi) => lp(mpi).to_string(),
- AllButOneFrom(mpi) => format!("$(allbutone {})", lp(mpi)),
- }
- }
-}
-
-pub fn build_unfragmented_map(this: &mut borrowck::BorrowckCtxt,
- move_data: &MoveData,
- id: ast::NodeId) {
- let fr = &move_data.fragments.borrow();
-
- // For now, don't care about other kinds of fragments; the precise
- // classfication of all paths for non-zeroing *drop* needs them,
- // but the loose approximation used by non-zeroing moves does not.
- let moved_leaf_paths = fr.moved_leaf_paths();
- let assigned_leaf_paths = fr.assigned_leaf_paths();
-
- let mut fragment_infos = Vec::with_capacity(moved_leaf_paths.len());
-
- let find_var_id = |move_path_index: MovePathIndex| -> Option<ast::NodeId> {
- let lp = move_data.path_loan_path(move_path_index);
- match lp.kind {
- LpVar(var_id) => Some(var_id),
- LpUpvar(ty::UpvarId { var_id, closure_expr_id }) => {
- // The `var_id` is unique *relative to* the current function.
- // (Check that we are indeed talking about the same function.)
- assert_eq!(id, closure_expr_id);
- Some(var_id)
- }
- LpDowncast(..) | LpExtend(..) => {
- // This simple implementation of non-zeroing move does
- // not attempt to deal with tracking substructure
- // accurately in the general case.
- None
- }
- }
- };
-
- let moves = move_data.moves.borrow();
- for &move_path_index in moved_leaf_paths {
- let var_id = match find_var_id(move_path_index) {
- None => continue,
- Some(var_id) => var_id,
- };
-
- move_data.each_applicable_move(move_path_index, |move_index| {
- let info = ty::FragmentInfo::Moved {
- var: var_id,
- move_expr: moves[move_index.get()].id,
- };
- debug!("fragment_infos push({:?} \
- due to move_path_index: {} move_index: {}",
- info, move_path_index.get(), move_index.get());
- fragment_infos.push(info);
- true
- });
- }
-
- for &move_path_index in assigned_leaf_paths {
- let var_id = match find_var_id(move_path_index) {
- None => continue,
- Some(var_id) => var_id,
- };
-
- let var_assigns = move_data.var_assignments.borrow();
- for var_assign in var_assigns.iter()
- .filter(|&assign| assign.path == move_path_index)
- {
- let info = ty::FragmentInfo::Assigned {
- var: var_id,
- assign_expr: var_assign.id,
- assignee_id: var_assign.assignee_id,
- };
- debug!("fragment_infos push({:?} due to var_assignment", info);
- fragment_infos.push(info);
- }
- }
-
- let mut fraginfo_map = this.tcx.fragment_infos.borrow_mut();
- let fn_did = this.tcx.hir.local_def_id(id);
- let prev = fraginfo_map.insert(fn_did, fragment_infos);
- assert!(prev.is_none());
-}
-
-pub struct FragmentSets {
- /// During move_data construction, `moved_leaf_paths` tracks paths
- /// that have been used directly by being moved out of. When
- /// move_data construction has been completed, `moved_leaf_paths`
- /// tracks such paths that are *leaf fragments* (e.g. `a.j` if we
- /// never move out any child like `a.j.x`); any parent paths
- /// (e.g. `a` for the `a.j` example) are moved over to
- /// `parents_of_fragments`.
- moved_leaf_paths: Vec<MovePathIndex>,
-
- /// `assigned_leaf_paths` tracks paths that have been used
- /// directly by being overwritten, but is otherwise much like
- /// `moved_leaf_paths`.
- assigned_leaf_paths: Vec<MovePathIndex>,
-
- /// `parents_of_fragments` tracks paths that are definitely
- /// parents of paths that have been moved.
- ///
- /// FIXME(pnkfelix) probably do not want/need
- /// `parents_of_fragments` at all, if we can avoid it.
- ///
- /// Update: I do not see a way to avoid it. Maybe just remove
- /// above fixme, or at least document why doing this may be hard.
- parents_of_fragments: Vec<MovePathIndex>,
-
- /// During move_data construction (specifically the
- /// fixup_fragment_sets call), `unmoved_fragments` tracks paths
- /// that have been "left behind" after a sibling has been moved or
- /// assigned. When move_data construction has been completed,
- /// `unmoved_fragments` tracks paths that were *only* results of
- /// being left-behind, and never directly moved themselves.
- unmoved_fragments: Vec<Fragment>,
-}
-
-impl FragmentSets {
- pub fn new() -> FragmentSets {
- FragmentSets {
- unmoved_fragments: Vec::new(),
- moved_leaf_paths: Vec::new(),
- assigned_leaf_paths: Vec::new(),
- parents_of_fragments: Vec::new(),
- }
- }
-
- pub fn moved_leaf_paths(&self) -> &[MovePathIndex] {
- &self.moved_leaf_paths
- }
-
- pub fn assigned_leaf_paths(&self) -> &[MovePathIndex] {
- &self.assigned_leaf_paths
- }
-
- pub fn add_move(&mut self, path_index: MovePathIndex) {
- self.moved_leaf_paths.push(path_index);
- }
-
- pub fn add_assignment(&mut self, path_index: MovePathIndex) {
- self.assigned_leaf_paths.push(path_index);
- }
-}
-
-pub fn instrument_move_fragments<'a, 'tcx>(this: &MoveData<'tcx>,
- tcx: TyCtxt<'a, 'tcx, 'tcx>,
- id: ast::NodeId) {
- let span_err = tcx.hir.attrs(id).iter()
- .any(|a| a.check_name("rustc_move_fragments"));
- let print = tcx.sess.opts.debugging_opts.print_move_fragments;
-
- if !span_err && !print { return; }
-
- let sp = tcx.hir.span(id);
-
- let instrument_all_paths = |kind, vec_rc: &Vec<MovePathIndex>| {
- for (i, mpi) in vec_rc.iter().enumerate() {
- let lp = || this.path_loan_path(*mpi);
- if span_err {
- tcx.sess.span_err(sp, &format!("{}: `{}`", kind, lp()));
- }
- if print {
- println!("id:{} {}[{}] `{}`", id, kind, i, lp());
- }
- }
- };
-
- let instrument_all_fragments = |kind, vec_rc: &Vec<Fragment>| {
- for (i, f) in vec_rc.iter().enumerate() {
- let render = || f.loan_path_user_string(this);
- if span_err {
- tcx.sess.span_err(sp, &format!("{}: `{}`", kind, render()));
- }
- if print {
- println!("id:{} {}[{}] `{}`", id, kind, i, render());
- }
- }
- };
-
- let fragments = this.fragments.borrow();
- instrument_all_paths("moved_leaf_path", &fragments.moved_leaf_paths);
- instrument_all_fragments("unmoved_fragment", &fragments.unmoved_fragments);
- instrument_all_paths("parent_of_fragments", &fragments.parents_of_fragments);
- instrument_all_paths("assigned_leaf_path", &fragments.assigned_leaf_paths);
-}
-
-/// Normalizes the fragment sets in `this`; i.e., removes duplicate entries, constructs the set of
-/// parents, and constructs the left-over fragments.
-///
-/// Note: "left-over fragments" means paths that were not directly referenced in moves nor
-/// assignments, but must nonetheless be tracked as potential drop obligations.
-pub fn fixup_fragment_sets<'a, 'tcx>(this: &MoveData<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) {
-
- let mut fragments = this.fragments.borrow_mut();
-
- // Swap out contents of fragments so that we can modify the fields
- // without borrowing the common fragments.
- let mut unmoved = mem::replace(&mut fragments.unmoved_fragments, vec![]);
- let mut parents = mem::replace(&mut fragments.parents_of_fragments, vec![]);
- let mut moved = mem::replace(&mut fragments.moved_leaf_paths, vec![]);
- let mut assigned = mem::replace(&mut fragments.assigned_leaf_paths, vec![]);
-
- let path_lps = |mpis: &[MovePathIndex]| -> Vec<String> {
- mpis.iter().map(|mpi| format!("{:?}", this.path_loan_path(*mpi))).collect()
- };
-
- let frag_lps = |fs: &[Fragment]| -> Vec<String> {
- fs.iter().map(|f| f.loan_path_repr(this)).collect()
- };
-
- // First, filter out duplicates
- moved.sort();
- moved.dedup();
- debug!("fragments 1 moved: {:?}", path_lps(&moved));
-
- assigned.sort();
- assigned.dedup();
- debug!("fragments 1 assigned: {:?}", path_lps(&assigned));
-
- // Second, build parents from the moved and assigned.
- for m in &moved {
- let mut p = this.path_parent(*m);
- while p != InvalidMovePathIndex {
- parents.push(p);
- p = this.path_parent(p);
- }
- }
- for a in &assigned {
- let mut p = this.path_parent(*a);
- while p != InvalidMovePathIndex {
- parents.push(p);
- p = this.path_parent(p);
- }
- }
-
- parents.sort();
- parents.dedup();
- debug!("fragments 2 parents: {:?}", path_lps(&parents));
-
- // Third, filter the moved and assigned fragments down to just the non-parents
- moved.retain(|f| non_member(*f, &parents));
- debug!("fragments 3 moved: {:?}", path_lps(&moved));
-
- assigned.retain(|f| non_member(*f, &parents));
- debug!("fragments 3 assigned: {:?}", path_lps(&assigned));
-
- // Fourth, build the leftover from the moved, assigned, and parents.
- for m in &moved {
- let lp = this.path_loan_path(*m);
- add_fragment_siblings(this, tcx, &mut unmoved, lp, None);
- }
- for a in &assigned {
- let lp = this.path_loan_path(*a);
- add_fragment_siblings(this, tcx, &mut unmoved, lp, None);
- }
- for p in &parents {
- let lp = this.path_loan_path(*p);
- add_fragment_siblings(this, tcx, &mut unmoved, lp, None);
- }
-
- unmoved.sort();
- unmoved.dedup();
- debug!("fragments 4 unmoved: {:?}", frag_lps(&unmoved));
-
- // Fifth, filter the leftover fragments down to its core.
- unmoved.retain(|f| match *f {
- AllButOneFrom(_) => true,
- Just(mpi) => non_member(mpi, &parents) &&
- non_member(mpi, &moved) &&
- non_member(mpi, &assigned)
- });
- debug!("fragments 5 unmoved: {:?}", frag_lps(&unmoved));
-
- // Swap contents back in.
- fragments.unmoved_fragments = unmoved;
- fragments.parents_of_fragments = parents;
- fragments.moved_leaf_paths = moved;
- fragments.assigned_leaf_paths = assigned;
-
- return;
-
- fn non_member(elem: MovePathIndex, set: &[MovePathIndex]) -> bool {
- match set.binary_search(&elem) {
- Ok(_) => false,
- Err(_) => true,
- }
- }
-}
-
-/// Adds all of the precisely-tracked siblings of `lp` as potential move paths of interest. For
-/// example, if `lp` represents `s.x.j`, then adds moves paths for `s.x.i` and `s.x.k`, the
-/// siblings of `s.x.j`.
-fn add_fragment_siblings<'a, 'tcx>(this: &MoveData<'tcx>,
- tcx: TyCtxt<'a, 'tcx, 'tcx>,
- gathered_fragments: &mut Vec<Fragment>,
- lp: Rc<LoanPath<'tcx>>,
- origin_id: Option<ast::NodeId>) {
- match lp.kind {
- LpVar(_) | LpUpvar(..) => {} // Local variables have no siblings.
-
- // Consuming a downcast is like consuming the original value, so propage inward.
- LpDowncast(ref loan_parent, _) => {
- add_fragment_siblings(this, tcx, gathered_fragments, loan_parent.clone(), origin_id);
- }
-
- // *LV for Unique consumes the contents of the box (at
- // least when it is non-copy...), so propagate inward.
- LpExtend(ref loan_parent, _, LpDeref(mc::Unique)) => {
- add_fragment_siblings(this, tcx, gathered_fragments, loan_parent.clone(), origin_id);
- }
-
- // *LV for unsafe and borrowed pointers do not consume their loan path, so stop here.
- LpExtend(.., LpDeref(mc::UnsafePtr(..))) |
- LpExtend(.., LpDeref(mc::Implicit(..))) |
- LpExtend(.., LpDeref(mc::BorrowedPtr(..))) => {}
-
- // FIXME (pnkfelix): LV[j] should be tracked, at least in the
- // sense of we will track the remaining drop obligation of the
- // rest of the array.
- //
- // Well, either that or LV[j] should be made illegal.
- // But even then, we will need to deal with destructuring
- // bind.
- //
- // Anyway, for now: LV[j] is not tracked precisely
- LpExtend(.., LpInterior(_, InteriorElement(..))) => {
- let mp = this.move_path(tcx, lp.clone());
- gathered_fragments.push(AllButOneFrom(mp));
- }
-
- // field access LV.x and tuple access LV#k are the cases
- // we are interested in
- LpExtend(ref loan_parent, mc,
- LpInterior(_, InteriorField(ref field_name))) => {
- let enum_variant_info = match loan_parent.kind {
- LpDowncast(ref loan_parent_2, variant_def_id) =>
- Some((variant_def_id, loan_parent_2.clone())),
- LpExtend(..) | LpVar(..) | LpUpvar(..) =>
- None,
- };
- add_fragment_siblings_for_extension(
- this,
- tcx,
- gathered_fragments,
- loan_parent, mc, field_name, &lp, origin_id, enum_variant_info);
- }
- }
-}
-
-/// We have determined that `origin_lp` destructures to LpExtend(parent, original_field_name).
-/// Based on this, add move paths for all of the siblings of `origin_lp`.
-fn add_fragment_siblings_for_extension<'a, 'tcx>(this: &MoveData<'tcx>,
- tcx: TyCtxt<'a, 'tcx, 'tcx>,
- gathered_fragments: &mut Vec<Fragment>,
- parent_lp: &Rc<LoanPath<'tcx>>,
- mc: mc::MutabilityCategory,
- origin_field_name: &mc::FieldName,
- origin_lp: &Rc<LoanPath<'tcx>>,
- origin_id: Option<ast::NodeId>,
- enum_variant_info: Option<(DefId,
- Rc<LoanPath<'tcx>>)>) {
- let parent_ty = parent_lp.to_type();
-
- let mut add_fragment_sibling_local = |field_name, variant_did| {
- add_fragment_sibling_core(
- this, tcx, gathered_fragments, parent_lp.clone(), mc, field_name, origin_lp,
- variant_did);
- };
-
- match parent_ty.sty {
- ty::TyTuple(ref v, _) => {
- let tuple_idx = match *origin_field_name {
- mc::PositionalField(tuple_idx) => tuple_idx,
- mc::NamedField(_) =>
- bug!("tuple type {:?} should not have named fields.",
- parent_ty),
- };
- let tuple_len = v.len();
- for i in 0..tuple_len {
- if i == tuple_idx { continue }
- let field_name = mc::PositionalField(i);
- add_fragment_sibling_local(field_name, None);
- }
- }
-
- ty::TyAdt(def, ..) => match def.adt_kind() {
- AdtKind::Struct => {
- match *origin_field_name {
- mc::NamedField(ast_name) => {
- for f in &def.struct_variant().fields {
- if f.name == ast_name {
- continue;
- }
- let field_name = mc::NamedField(f.name);
- add_fragment_sibling_local(field_name, None);
- }
- }
- mc::PositionalField(tuple_idx) => {
- for (i, _f) in def.struct_variant().fields.iter().enumerate() {
- if i == tuple_idx {
- continue
- }
- let field_name = mc::PositionalField(i);
- add_fragment_sibling_local(field_name, None);
- }
- }
- }
- }
- AdtKind::Union => {
- // Do nothing, all union fields are moved/assigned together.
- }
- AdtKind::Enum => {
- let variant = match enum_variant_info {
- Some((vid, ref _lp2)) => def.variant_with_id(vid),
- None => {
- assert!(def.is_univariant());
- &def.variants[0]
- }
- };
- match *origin_field_name {
- mc::NamedField(ast_name) => {
- for field in &variant.fields {
- if field.name == ast_name {
- continue;
- }
- let field_name = mc::NamedField(field.name);
- add_fragment_sibling_local(field_name, Some(variant.did));
- }
- }
- mc::PositionalField(tuple_idx) => {
- for (i, _f) in variant.fields.iter().enumerate() {
- if tuple_idx == i {
- continue;
- }
- let field_name = mc::PositionalField(i);
- add_fragment_sibling_local(field_name, None);
- }
- }
- }
- }
- },
-
- ref ty => {
- let span = origin_id.map_or(DUMMY_SP, |id| tcx.hir.span(id));
- span_bug!(span,
- "type {:?} ({:?}) is not fragmentable",
- parent_ty, ty);
- }
- }
-}
-
-/// Adds the single sibling `LpExtend(parent, new_field_name)` of `origin_lp` (the original
-/// loan-path).
-fn add_fragment_sibling_core<'a, 'tcx>(this: &MoveData<'tcx>,
- tcx: TyCtxt<'a, 'tcx, 'tcx>,
- gathered_fragments: &mut Vec<Fragment>,
- parent: Rc<LoanPath<'tcx>>,
- mc: mc::MutabilityCategory,
- new_field_name: mc::FieldName,
- origin_lp: &Rc<LoanPath<'tcx>>,
- enum_variant_did: Option<DefId>)
- -> MovePathIndex {
- let opt_variant_did = match parent.kind {
- LpDowncast(_, variant_did) => Some(variant_did),
- LpVar(..) | LpUpvar(..) | LpExtend(..) => enum_variant_did,
- };
-
- let loan_path_elem = LpInterior(opt_variant_did, InteriorField(new_field_name));
- let new_lp_type = match new_field_name {
- mc::NamedField(ast_name) =>
- tcx.named_element_ty(parent.to_type(), ast_name, opt_variant_did),
- mc::PositionalField(idx) =>
- tcx.positional_element_ty(parent.to_type(), idx, opt_variant_did),
- };
- let new_lp_variant = LpExtend(parent, mc, loan_path_elem);
- let new_lp = LoanPath::new(new_lp_variant, new_lp_type.unwrap());
- debug!("add_fragment_sibling_core(new_lp={:?}, origin_lp={:?})",
- new_lp, origin_lp);
- let mp = this.move_path(tcx, Rc::new(new_lp));
-
- // Do not worry about checking for duplicates here; we will sort
- // and dedup after all are added.
- gathered_fragments.push(Just(mp));
-
- mp
-}