diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 38ea536b4ee72..e5d77d3edc74e 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -290,46 +290,26 @@ impl<'a, 'gcx, T> HashStable> impl_stable_hash_for!(enum mir::ValidationOp { Acquire, Release, Suspend(region_scope) }); -impl<'a, 'gcx> HashStable> for mir::Place<'gcx> { +impl<'a, 'gcx> HashStable> for mir::PlaceBase<'gcx> { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { - mir::Place::Local(ref local) => { + mir::PlaceBase::Local(ref local) => { local.hash_stable(hcx, hasher); } - mir::Place::Static(ref statik) => { + mir::PlaceBase::Static(ref statik) => { statik.hash_stable(hcx, hasher); } - mir::Place::Promoted(ref promoted) => { + mir::PlaceBase::Promoted(ref promoted) => { promoted.hash_stable(hcx, hasher); } - mir::Place::Projection(ref place_projection) => { - place_projection.hash_stable(hcx, hasher); - } } } } -impl<'a, 'gcx, B, V, T> HashStable> -for mir::Projection<'gcx, B, V, T> - where B: HashStable>, - V: HashStable>, - T: HashStable> -{ - fn hash_stable(&self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher) { - let mir::Projection { - ref base, - ref elem, - } = *self; - - base.hash_stable(hcx, hasher); - elem.hash_stable(hcx, hasher); - } -} +impl_stable_hash_for!(struct mir::Place<'tcx> { base, elems }); impl<'a, 'gcx, V, T> HashStable> for mir::ProjectionElem<'gcx, V, T> diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index dae5709ba114a..c5c573c262fc1 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -21,6 +21,7 @@ use mir::interpret::{EvalErrorKind, Scalar, Value, ScalarMaybeUndef}; use mir::visit::MirVisitable; use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::Float; +use rustc_data_structures::accumulate_vec::AccumulateVec; use rustc_data_structures::graph::dominators::{dominators, Dominators}; use rustc_data_structures::graph::{self, GraphPredecessors, GraphSuccessors}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; @@ -39,7 +40,7 @@ use syntax::symbol::InternedString; use syntax_pos::{Span, DUMMY_SP}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use ty::subst::{Subst, Substs}; -use ty::{self, AdtDef, CanonicalTy, ClosureSubsts, GeneratorSubsts, Region, Ty, TyCtxt}; +use ty::{self, AdtDef, CanonicalTy, ClosureSubsts, GeneratorSubsts, Region, Slice, Ty, TyCtxt}; use util::ppaux; pub use mir::interpret::AssertMessage; @@ -1693,8 +1694,16 @@ impl<'tcx> Debug for Statement<'tcx> { /// A path to a value; something that can be evaluated without /// changing or disturbing program state. +#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +pub struct Place<'tcx> { + pub base: PlaceBase<'tcx>, + pub elems: &'tcx Slice>, +} + +impl<'tcx> serialize::UseSpecializedDecodable for &'tcx Slice> {} + #[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] -pub enum Place<'tcx> { +pub enum PlaceBase<'tcx> { /// local variable Local(Local), @@ -1703,9 +1712,6 @@ pub enum Place<'tcx> { /// Constant code promoted to an injected static Promoted(Box<(Promoted, Ty<'tcx>)>), - - /// projection out of a place (access a field, deref a pointer, etc) - Projection(Box>), } /// The def-id of a static, along with its normalized type (which is @@ -1721,17 +1727,7 @@ impl_stable_hash_for!(struct Static<'tcx> { ty }); -/// The `Projection` data structure defines things of the form `B.x` -/// or `*B` or `B[index]`. Note that it is parameterized because it is -/// shared between `Constant` and `Place`. See the aliases -/// `PlaceProjection` etc below. -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] -pub struct Projection<'tcx, B, V, T> { - pub base: B, - pub elem: ProjectionElem<'tcx, V, T>, -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub enum ProjectionElem<'tcx, V, T> { Deref, Field(Field, T), @@ -1769,41 +1765,108 @@ pub enum ProjectionElem<'tcx, V, T> { Downcast(&'tcx AdtDef, usize), } -/// Alias for projections as they appear in places, where the base is a place -/// and the index is a local. -pub type PlaceProjection<'tcx> = Projection<'tcx, Place<'tcx>, Local, Ty<'tcx>>; - /// Alias for projections as they appear in places, where the base is a place /// and the index is a local. pub type PlaceElem<'tcx> = ProjectionElem<'tcx, Local, Ty<'tcx>>; newtype_index!(Field { DEBUG_FORMAT = "field[{}]" }); -impl<'tcx> Place<'tcx> { - pub fn field(self, f: Field, ty: Ty<'tcx>) -> Place<'tcx> { - self.elem(ProjectionElem::Field(f, ty)) +impl<'a, 'tcx> Place<'tcx> { + // projection lives in the last elem. + pub fn projection(&self) -> Option<&PlaceElem> { + self.elems.last() + } + + pub fn field( + self, + tcx: TyCtxt<'_, '_, 'tcx>, + f: Field, + ty: Ty<'tcx>, + ) -> Self { + self.elem(tcx, ProjectionElem::Field(f, ty)) + } + + pub fn deref(self, tcx: TyCtxt<'_, '_, 'tcx>) -> Self { + self.elem(tcx, ProjectionElem::Deref) + } + + pub fn downcast( + self, + tcx: TyCtxt<'_, '_, 'tcx>, + adt_def: &'tcx AdtDef, + variant_index: usize, + ) -> Self { + self.elem(tcx, ProjectionElem::Downcast(adt_def, variant_index)) + } + + pub fn index(self, tcx: TyCtxt<'_, '_, 'tcx>, index: Local) -> Self { + self.elem(tcx, ProjectionElem::Index(index)) + } + + pub fn constant_index( + self, + tcx: TyCtxt<'_, '_, 'tcx>, + offset: u32, + min_length: u32, + from_end: bool, + ) -> Self { + self.elem(tcx, ProjectionElem::ConstantIndex { + offset, min_length, from_end, + }) + } + + pub fn subslice( + self, + tcx: TyCtxt<'_, '_, 'tcx>, + from: u32, + to: u32, + ) -> Self { + self.elem(tcx, ProjectionElem::Subslice { + from, to, + }) } - pub fn deref(self) -> Place<'tcx> { - self.elem(ProjectionElem::Deref) + pub fn local(local: Local) -> Self { + Place { + base: PlaceBase::Local(local), + elems: Slice::empty(), + } } - pub fn downcast(self, adt_def: &'tcx AdtDef, variant_index: usize) -> Place<'tcx> { - self.elem(ProjectionElem::Downcast(adt_def, variant_index)) + pub fn static_(static_: Static<'tcx>) -> Self { + Place { + base: PlaceBase::Static(box static_), + elems: Slice::empty(), + } } - pub fn index(self, index: Local) -> Place<'tcx> { - self.elem(ProjectionElem::Index(index)) + pub fn promoted( + promoted: Promoted, + ty: Ty<'tcx>, + ) -> Self { + Place { + base: PlaceBase::Promoted(box (promoted, ty)), + elems: Slice::empty(), + } } - pub fn elem(self, elem: PlaceElem<'tcx>) -> Place<'tcx> { - Place::Projection(Box::new(PlaceProjection { base: self, elem })) + fn elem( + self, + tcx: TyCtxt<'_, '_, 'tcx>, + elem: PlaceElem<'tcx>, + ) -> Self { + Place { + base: self.base, + elems: tcx.mk_place_elems( + self.elems.iter().cloned().chain(iter::once(elem)) + ), + } } } -impl<'tcx> Debug for Place<'tcx> { +impl<'tcx> Debug for PlaceBase<'tcx> { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { - use self::Place::*; + use self::PlaceBase::*; match *self { Local(id) => write!(fmt, "{:?}", id), @@ -1814,35 +1877,6 @@ impl<'tcx> Debug for Place<'tcx> { ty ), Promoted(ref promoted) => write!(fmt, "({:?}: {:?})", promoted.0, promoted.1), - Projection(ref data) => match data.elem { - ProjectionElem::Downcast(ref adt_def, index) => { - write!(fmt, "({:?} as {})", data.base, adt_def.variants[index].name) - } - ProjectionElem::Deref => write!(fmt, "(*{:?})", data.base), - ProjectionElem::Field(field, ty) => { - write!(fmt, "({:?}.{:?}: {:?})", data.base, field.index(), ty) - } - ProjectionElem::Index(ref index) => write!(fmt, "{:?}[{:?}]", data.base, index), - ProjectionElem::ConstantIndex { - offset, - min_length, - from_end: false, - } => write!(fmt, "{:?}[{:?} of {:?}]", data.base, offset, min_length), - ProjectionElem::ConstantIndex { - offset, - min_length, - from_end: true, - } => write!(fmt, "{:?}[-{:?} of {:?}]", data.base, offset, min_length), - ProjectionElem::Subslice { from, to } if to == 0 => { - write!(fmt, "{:?}[{:?}:]", data.base, from) - } - ProjectionElem::Subslice { from, to } if from == 0 => { - write!(fmt, "{:?}[:-{:?}]", data.base, to) - } - ProjectionElem::Subslice { from, to } => { - write!(fmt, "{:?}[{:?}:-{:?}]", data.base, from, to) - } - }, } } } @@ -2760,18 +2794,36 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { impl<'tcx> TypeFoldable<'tcx> for Place<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - match self { - &Place::Projection(ref p) => Place::Projection(p.fold_with(folder)), - _ => self.clone(), + Place { + base: self.base.fold_with(folder), + elems: self.elems.fold_with(folder), } } fn super_visit_with>(&self, visitor: &mut V) -> bool { - if let &Place::Projection(ref p) = self { - p.visit_with(visitor) - } else { - false - } + self.base.visit_with(visitor) || + self.elems.visit_with(visitor) + } +} + +impl<'tcx> TypeFoldable<'tcx> for PlaceBase<'tcx> { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _folder: &mut F) -> Self { + self.clone() + } + + fn super_visit_with>(&self, _visitor: &mut V) -> bool { + false + } +} + +impl<'tcx> TypeFoldable<'tcx> for &'tcx Slice> { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { + let v = self.iter().map(|p| p.fold_with(folder)).collect::>(); + folder.tcx().intern_place_elems(&v) + } + + fn super_visit_with>(&self, visitor: &mut Vs) -> bool { + self.iter().any(|p| p.visit_with(visitor)) } } @@ -2858,30 +2910,25 @@ impl<'tcx> TypeFoldable<'tcx> for Operand<'tcx> { } } -impl<'tcx, B, V, T> TypeFoldable<'tcx> for Projection<'tcx, B, V, T> -where - B: TypeFoldable<'tcx>, - V: TypeFoldable<'tcx>, - T: TypeFoldable<'tcx>, +impl<'tcx, V, T> TypeFoldable<'tcx> for ProjectionElem<'tcx, V, T> + where V: TypeFoldable<'tcx>, + T: TypeFoldable<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { use mir::ProjectionElem::*; - let base = self.base.fold_with(folder); - let elem = match self.elem { + match *self { Deref => Deref, Field(f, ref ty) => Field(f, ty.fold_with(folder)), Index(ref v) => Index(v.fold_with(folder)), ref elem => elem.clone(), - }; - - Projection { base, elem } + } } fn super_visit_with>(&self, visitor: &mut Vs) -> bool { use mir::ProjectionElem::*; - self.base.visit_with(visitor) || match self.elem { + match *self { Field(_, ref ty) => ty.visit_with(visitor), Index(ref v) => v.visit_with(visitor), _ => false, diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index b55843ac527df..8b15cdb5136f8 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -13,11 +13,11 @@ * building is complete. */ +use hir; use mir::*; use ty::subst::{Subst, Substs}; -use ty::{self, AdtDef, Ty, TyCtxt}; -use hir; use ty::util::IntTypeExt; +use ty::{self, AdtDef, Ty, TyCtxt}; #[derive(Copy, Clone, Debug)] pub enum PlaceTy<'tcx> { @@ -25,80 +25,80 @@ pub enum PlaceTy<'tcx> { Ty { ty: Ty<'tcx> }, /// Downcast to a particular variant of an enum. - Downcast { adt_def: &'tcx AdtDef, - substs: &'tcx Substs<'tcx>, - variant_index: usize }, + Downcast { + adt_def: &'tcx AdtDef, + substs: &'tcx Substs<'tcx>, + variant_index: usize, + }, } impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> { - pub fn from_ty(ty: Ty<'tcx>) -> PlaceTy<'tcx> { - PlaceTy::Ty { ty: ty } - } - pub fn to_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { match *self { - PlaceTy::Ty { ty } => - ty, - PlaceTy::Downcast { adt_def, substs, variant_index: _ } => - tcx.mk_adt(adt_def, substs), + PlaceTy::Ty { ty } => ty, + PlaceTy::Downcast { + adt_def, + substs, + variant_index: _, + } => tcx.mk_adt(adt_def, substs), } } - pub fn projection_ty(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, - elem: &PlaceElem<'tcx>) - -> PlaceTy<'tcx> - { - match *elem { + pub fn projection_ty( + self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + elem: &PlaceElem<'tcx>, + ) -> PlaceTy<'tcx> { + match elem { ProjectionElem::Deref => { - let ty = self.to_ty(tcx) - .builtin_deref(true) - .unwrap_or_else(|| { - bug!("deref projection of non-dereferencable ty {:?}", self) - }) - .ty; - PlaceTy::Ty { - ty, - } + let ty = self + .to_ty(tcx) + .builtin_deref(true) + .unwrap_or_else(|| bug!("deref projection of non-dereferencable ty {:?}", self)) + .ty; + PlaceTy::Ty { ty } } - ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => - PlaceTy::Ty { - ty: self.to_ty(tcx).builtin_index().unwrap() - }, + ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => PlaceTy::Ty { + ty: self.to_ty(tcx).builtin_index().unwrap(), + }, ProjectionElem::Subslice { from, to } => { let ty = self.to_ty(tcx); PlaceTy::Ty { ty: match ty.sty { ty::TyArray(inner, size) => { let size = size.unwrap_usize(tcx); - let len = size - (from as u64) - (to as u64); + let len = size - (*from as u64) - (*to as u64); tcx.mk_array(inner, len) } ty::TySlice(..) => ty, - _ => { - bug!("cannot subslice non-array type: `{:?}`", self) - } - } + _ => bug!("cannot subslice non-array type: `{:?}`", self), + }, } } - ProjectionElem::Downcast(adt_def1, index) => - match self.to_ty(tcx).sty { - ty::TyAdt(adt_def, substs) => { - assert!(adt_def.is_enum()); - assert!(index < adt_def.variants.len()); - assert_eq!(adt_def, adt_def1); - PlaceTy::Downcast { adt_def, - substs, - variant_index: index } + ProjectionElem::Downcast(adt_def1, index) => match self.to_ty(tcx).sty { + ty::TyAdt(adt_def, substs) => { + assert!(adt_def.is_enum()); + assert!(*index < adt_def.variants.len()); + assert_eq!(adt_def, *adt_def1); + PlaceTy::Downcast { + adt_def, + substs, + variant_index: *index, } - _ => { - bug!("cannot downcast non-ADT type: `{:?}`", self) - } - }, - ProjectionElem::Field(_, fty) => PlaceTy::Ty { ty: fty } + } + _ => bug!("cannot downcast non-ADT type: `{:?}`", self), + }, + ProjectionElem::Field(_, fty) => PlaceTy::Ty { ty: fty }, } } } +impl From> for PlaceTy<'tcx> { + fn from(ty: Ty<'tcx>) -> Self { + PlaceTy::Ty { ty } + } +} + EnumTypeFoldableImpl! { impl<'tcx> TypeFoldable<'tcx> for PlaceTy<'tcx> { (PlaceTy::Ty) { ty }, @@ -106,76 +106,173 @@ EnumTypeFoldableImpl! { } } +impl<'tcx> PlaceBase<'tcx> { + pub fn ty(&self, local_decls: &impl HasLocalDecls<'tcx>) -> Ty<'tcx> { + match self { + PlaceBase::Local(index) => local_decls.local_decls()[*index].ty, + PlaceBase::Promoted(data) => data.1, + PlaceBase::Static(data) => data.ty, + } + } +} + impl<'tcx> Place<'tcx> { - pub fn ty<'a, 'gcx, D>(&self, local_decls: &D, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> PlaceTy<'tcx> - where D: HasLocalDecls<'tcx> - { - match *self { - Place::Local(index) => - PlaceTy::Ty { ty: local_decls.local_decls()[index].ty }, - Place::Promoted(ref data) => PlaceTy::Ty { ty: data.1 }, - Place::Static(ref data) => - PlaceTy::Ty { ty: data.ty }, - Place::Projection(ref proj) => - proj.base.ty(local_decls, tcx).projection_ty(tcx, &proj.elem), + pub fn ty<'a, 'gcx>( + &self, + local_decls: &impl HasLocalDecls<'tcx>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + ) -> PlaceTy<'tcx> { + // the PlaceTy is the *final* type with all projection applied + // if there is no projection, that just refers to `base`: + // + // Place: base.[a, b, c] + // ^-- projection + // ^-- PlaceTy + // + // Place: base.[] + // ^^-- no projection + // ^^^^-- PlaceTy + + let mut place_ty = PlaceTy::from(self.base.ty(local_decls)); + + // apply .projection_ty() to all elems but only returns the final one. + for elem in self.elems.iter() { + place_ty = place_ty.projection_ty(tcx, elem); } + + place_ty } /// If this is a field projection, and the field is being projected from a closure type, /// then returns the index of the field being projected. Note that this closure will always /// be `self` in the current MIR, because that is the only time we directly access the fields /// of a closure type. - pub fn is_upvar_field_projection<'cx, 'gcx>(&self, mir: &'cx Mir<'tcx>, - tcx: &TyCtxt<'cx, 'gcx, 'tcx>) -> Option { - let place = if let Place::Projection(ref proj) = self { - if let ProjectionElem::Deref = proj.elem { - &proj.base + pub fn is_upvar_field_projection<'cx, 'gcx>( + &self, + mir: &'cx Mir<'tcx>, + tcx: &TyCtxt<'cx, 'gcx, 'tcx>, + ) -> Option { + let base_place; + let mut place = self; + let mut by_ref = false; + + base_place = place.projection_base(*tcx); + + if let Some(ProjectionElem::Deref) = place.projection() { + place = &base_place; + by_ref = true; + } + if let Some(ProjectionElem::Field(field, _ty)) = place.projection() { + let base_ty = place.projection_base(*tcx).ty(mir, *tcx).to_ty(*tcx); + + if base_ty.is_closure() + || base_ty.is_generator() && (!(by_ref && !mir.upvar_decls[field.index()].by_ref)) + { + Some(*field) } else { - self + None } } else { - self - }; - - match place { - Place::Projection(ref proj) => match proj.elem { - ProjectionElem::Field(field, _ty) => { - let base_ty = proj.base.ty(mir, *tcx).to_ty(*tcx); + None + } + } - if base_ty.is_closure() || base_ty.is_generator() { - Some(field) - } else { - None - } + // for Place: + // (Base.[a, b, c]) + // ^^^^^^^^^^ ^-- projection + // |-- base_place + // + // Base.[] + // ^^^^ ^^-- no projection(empty) + // |-- base_place + pub fn split_projection<'cx, 'gcx>( + &self, + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + ) -> (Place<'tcx>, Option<&'tcx PlaceElem<'tcx>>) { + // split place_elems + // Base.[a, b, c] + // ^^^^ ^-- projection(projection lives in the last elem) + // |-- place_elems + match self.elems.split_last() { + Some((projection, place_elems)) => ( + Place { + base: self.clone().base, + elems: tcx.intern_place_elems(place_elems), }, - _ => None, - }, - _ => None, + Some(projection), + ), + _ => (self.clone(), None) + } + } + + pub fn has_no_projection(&self) -> bool { + self.elems.is_empty() + } + + // for projection returns the base place; + // Base.[a, b, c] => Base.[a, b] + // ^-- projection + // if no projection returns the place itself, + // Base.[] => Base.[] + // ^^-- no projection + pub fn projection_base<'cx, 'gcx>(&self, tcx: TyCtxt<'cx, 'gcx, 'tcx>) -> Place<'tcx> { + match self.split_projection(tcx) { + (place, Some(_)) => place, + (_, None) => self.clone(), + } + } + + // for a place_elem returns it's base projection + // Base.[a, b, c] + // ^-- place_elem + // ^^^^^^^-- base + pub fn elem_base<'cx, 'gcx>( + &self, + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + elem_index: usize, + ) -> Place<'tcx> { + // only works for place with projections + assert!(!self.has_no_projection()); + + if elem_index < 1 { + // Base.[a] + // ^-- elems[0] + Place { + base: self.clone().base, + elems: Slice::empty(), + } + } else { + Place { + base: self.clone().base, + elems: tcx.mk_place_elems( + self.elems.iter().cloned().take(elem_index) + ) + } } } } pub enum RvalueInitializationState { Shallow, - Deep + Deep, } impl<'tcx> Rvalue<'tcx> { pub fn ty<'a, 'gcx, D>(&self, local_decls: &D, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> - where D: HasLocalDecls<'tcx> + where + D: HasLocalDecls<'tcx>, { match *self { Rvalue::Use(ref operand) => operand.ty(local_decls, tcx), - Rvalue::Repeat(ref operand, count) => { - tcx.mk_array(operand.ty(local_decls, tcx), count) - } + Rvalue::Repeat(ref operand, count) => tcx.mk_array(operand.ty(local_decls, tcx), count), Rvalue::Ref(reg, bk, ref place) => { let place_ty = place.ty(local_decls, tcx).to_ty(tcx); - tcx.mk_ref(reg, + tcx.mk_ref( + reg, ty::TypeAndMut { ty: place_ty, - mutbl: bk.to_mutbl_lossy() - } + mutbl: bk.to_mutbl_lossy(), + }, ) } Rvalue::Len(..) => tcx.types.usize, @@ -191,8 +288,7 @@ impl<'tcx> Rvalue<'tcx> { let ty = op.ty(tcx, lhs_ty, rhs_ty); tcx.intern_tup(&[ty, tcx.types.bool]) } - Rvalue::UnaryOp(UnOp::Not, ref operand) | - Rvalue::UnaryOp(UnOp::Neg, ref operand) => { + Rvalue::UnaryOp(UnOp::Not, ref operand) | Rvalue::UnaryOp(UnOp::Neg, ref operand) => { operand.ty(local_decls, tcx) } Rvalue::Discriminant(ref place) => { @@ -206,25 +302,15 @@ impl<'tcx> Rvalue<'tcx> { } Rvalue::NullaryOp(NullOp::Box, t) => tcx.mk_box(t), Rvalue::NullaryOp(NullOp::SizeOf, _) => tcx.types.usize, - Rvalue::Aggregate(ref ak, ref ops) => { - match **ak { - AggregateKind::Array(ty) => { - tcx.mk_array(ty, ops.len() as u64) - } - AggregateKind::Tuple => { - tcx.mk_tup(ops.iter().map(|op| op.ty(local_decls, tcx))) - } - AggregateKind::Adt(def, _, substs, _) => { - tcx.type_of(def.did).subst(tcx, substs) - } - AggregateKind::Closure(did, substs) => { - tcx.mk_closure(did, substs) - } - AggregateKind::Generator(did, substs, movability) => { - tcx.mk_generator(did, substs, movability) - } + Rvalue::Aggregate(ref ak, ref ops) => match **ak { + AggregateKind::Array(ty) => tcx.mk_array(ty, ops.len() as u64), + AggregateKind::Tuple => tcx.mk_tup(ops.iter().map(|op| op.ty(local_decls, tcx))), + AggregateKind::Adt(def, _, substs, _) => tcx.type_of(def.did).subst(tcx, substs), + AggregateKind::Closure(did, substs) => tcx.mk_closure(did, substs), + AggregateKind::Generator(did, substs, movability) => { + tcx.mk_generator(did, substs, movability) } - } + }, } } @@ -234,32 +320,40 @@ impl<'tcx> Rvalue<'tcx> { pub fn initialization_state(&self) -> RvalueInitializationState { match *self { Rvalue::NullaryOp(NullOp::Box, _) => RvalueInitializationState::Shallow, - _ => RvalueInitializationState::Deep + _ => RvalueInitializationState::Deep, } } } impl<'tcx> Operand<'tcx> { pub fn ty<'a, 'gcx, D>(&self, local_decls: &D, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> - where D: HasLocalDecls<'tcx> + where + D: HasLocalDecls<'tcx>, { match self { - &Operand::Copy(ref l) | - &Operand::Move(ref l) => l.ty(local_decls, tcx).to_ty(tcx), + &Operand::Copy(ref l) | &Operand::Move(ref l) => l.ty(local_decls, tcx).to_ty(tcx), &Operand::Constant(ref c) => c.ty, } } } impl<'tcx> BinOp { - pub fn ty<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, - lhs_ty: Ty<'tcx>, - rhs_ty: Ty<'tcx>) - -> Ty<'tcx> { + pub fn ty<'a, 'gcx>( + &self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + lhs_ty: Ty<'tcx>, + rhs_ty: Ty<'tcx>, + ) -> Ty<'tcx> { // FIXME: handle SIMD correctly match self { - &BinOp::Add | &BinOp::Sub | &BinOp::Mul | &BinOp::Div | &BinOp::Rem | - &BinOp::BitXor | &BinOp::BitAnd | &BinOp::BitOr => { + &BinOp::Add + | &BinOp::Sub + | &BinOp::Mul + | &BinOp::Div + | &BinOp::Rem + | &BinOp::BitXor + | &BinOp::BitAnd + | &BinOp::BitOr => { // these should be integers or floats of the same size. assert_eq!(lhs_ty, rhs_ty); lhs_ty @@ -267,8 +361,7 @@ impl<'tcx> BinOp { &BinOp::Shl | &BinOp::Shr | &BinOp::Offset => { lhs_ty // lhs_ty can be != rhs_ty } - &BinOp::Eq | &BinOp::Lt | &BinOp::Le | - &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => { + &BinOp::Eq | &BinOp::Lt | &BinOp::Le | &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => { tcx.types.bool } } @@ -308,7 +401,7 @@ impl BinOp { BinOp::Gt => hir::BinOpKind::Gt, BinOp::Le => hir::BinOpKind::Le, BinOp::Ge => hir::BinOpKind::Ge, - BinOp::Offset => unreachable!() + BinOp::Offset => unreachable!(), } } } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index cab6ed0c122cd..9a8dcf1fc40e6 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -165,13 +165,6 @@ macro_rules! make_mir_visitor { self.super_static(static_, context, location); } - fn visit_projection(&mut self, - place: & $($mutability)* PlaceProjection<'tcx>, - context: PlaceContext<'tcx>, - location: Location) { - self.super_projection(place, context, location); - } - fn visit_projection_elem(&mut self, place: & $($mutability)* PlaceElem<'tcx>, context: PlaceContext<'tcx>, @@ -634,19 +627,31 @@ macro_rules! make_mir_visitor { fn super_place(&mut self, place: & $($mutability)* Place<'tcx>, context: PlaceContext<'tcx>, - location: Location) { - match *place { - Place::Local(ref $($mutability)* local) => { + location: Location + ) { + let Place { + ref $($mutability)* base, + ref $($mutability)* elems, + } = *place; + + match base { + PlaceBase::Local(ref $($mutability)* local) => { self.visit_local(local, context, location); } - Place::Static(ref $($mutability)* static_) => { + PlaceBase::Static(ref $($mutability)* static_) => { self.visit_static(static_, context, location); } - Place::Promoted(ref $($mutability)* promoted) => { + PlaceBase::Promoted(ref $($mutability)* promoted) => { self.visit_ty(& $($mutability)* promoted.1, TyContext::Location(location)); }, - Place::Projection(ref $($mutability)* proj) => { - self.visit_projection(proj, context, location); + } + if !elems.is_empty() { + for elem in elems.iter().cloned().rev() { + self.visit_projection_elem( + &$($mutability)* elem.clone(), + context, + location + ); } } } @@ -663,23 +668,6 @@ macro_rules! make_mir_visitor { self.visit_ty(ty, TyContext::Location(location)); } - fn super_projection(&mut self, - proj: & $($mutability)* PlaceProjection<'tcx>, - context: PlaceContext<'tcx>, - location: Location) { - let Projection { - ref $($mutability)* base, - ref $($mutability)* elem, - } = *proj; - let context = if context.is_mutating_use() { - PlaceContext::Projection(Mutability::Mut) - } else { - PlaceContext::Projection(Mutability::Not) - }; - self.visit_place(base, context, location); - self.visit_projection_elem(elem, context, location); - } - fn super_projection_elem(&mut self, proj: & $($mutability)* PlaceElem<'tcx>, _context: PlaceContext<'tcx>, diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index bb14af29a7afe..db96d1da11d95 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -31,7 +31,7 @@ use middle::cstore::EncodedMetadata; use middle::lang_items; use middle::resolve_lifetime::{self, ObjectLifetimeDefault}; use middle::stability; -use mir::{self, Mir, interpret}; +use mir::{self, Mir, interpret, PlaceElem}; use mir::interpret::Allocation; use ty::subst::{Kind, Substs, Subst}; use ty::ReprOptions; @@ -140,6 +140,7 @@ pub struct CtxtInterners<'tcx> { canonical_var_infos: InternedSet<'tcx, Slice>, region: InternedSet<'tcx, RegionKind>, existential_predicates: InternedSet<'tcx, Slice>>, + place_elems: InternedSet<'tcx, Slice>>, predicates: InternedSet<'tcx, Slice>>, const_: InternedSet<'tcx, Const<'tcx>>, clauses: InternedSet<'tcx, Slice>>, @@ -156,6 +157,7 @@ impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> { region: Default::default(), existential_predicates: Default::default(), canonical_var_infos: Default::default(), + place_elems: Default::default(), predicates: Default::default(), const_: Default::default(), clauses: Default::default(), @@ -1720,6 +1722,27 @@ impl<'a, 'tcx> Lift<'tcx> for &'a Slice> { } } +impl<'a, 'tcx> Lift<'tcx> for &'a Slice> { + type Lifted = &'tcx Slice>; + fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) + -> Option<&'tcx Slice>> { + if self.is_empty() { + return Some(Slice::empty()); + } + if tcx.interners.arena.in_arena(*self as *const _) { + return Some(unsafe { + mem::transmute(*self) + }); + } + + if !tcx.is_global() { + self.lift_to_tcx(tcx.global_tcx()) + } else { + None + } + } +} + impl<'a, 'tcx> Lift<'tcx> for &'a Slice { type Lifted = &'tcx Slice; fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { @@ -2160,6 +2183,13 @@ impl<'tcx: 'lcx, 'lcx> Borrow<[ExistentialPredicate<'lcx>]> } } +impl<'tcx: 'lcx, 'lcx> Borrow<[PlaceElem<'lcx>]> + for Interned<'tcx, Slice>> { + fn borrow<'a>(&'a self) -> &'a [PlaceElem<'lcx>] { + &self.0[..] + } +} + impl<'tcx: 'lcx, 'lcx> Borrow<[Predicate<'lcx>]> for Interned<'tcx, Slice>> { fn borrow<'a>(&'a self) -> &'a [Predicate<'lcx>] { @@ -2284,6 +2314,7 @@ macro_rules! slice_interners { slice_interners!( existential_predicates: _intern_existential_predicates(ExistentialPredicate), predicates: _intern_predicates(Predicate), + place_elems: _intern_place_elems(PlaceElem), type_list: _intern_type_list(Ty), substs: _intern_substs(Kind), clauses: _intern_clauses(Clause), @@ -2573,6 +2604,15 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + pub fn intern_place_elems(self, place_elems: &[PlaceElem<'tcx>]) + -> &'tcx Slice> { + if place_elems.is_empty() { + Slice::empty() + } else { + self._intern_place_elems(place_elems) + } + } + pub fn intern_type_list(self, ts: &[Ty<'tcx>]) -> &'tcx Slice> { if ts.len() == 0 { Slice::empty() @@ -2635,6 +2675,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { iter.intern_with(|xs| self.intern_existential_predicates(xs)) } + pub fn mk_place_elems], &'tcx Slice>>>( + self, + iter: I, + ) -> I::Output { + iter.intern_with(|xs| self.intern_place_elems(xs)) + } + pub fn mk_predicates], &'tcx Slice>>>(self, iter: I) -> I::Output { diff --git a/src/librustc_codegen_llvm/mir/analyze.rs b/src/librustc_codegen_llvm/mir/analyze.rs index 993138aee1cec..a40c8381fee20 100644 --- a/src/librustc_codegen_llvm/mir/analyze.rs +++ b/src/librustc_codegen_llvm/mir/analyze.rs @@ -15,6 +15,7 @@ use rustc_data_structures::bitvec::BitArray; use rustc_data_structures::graph::dominators::Dominators; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc::mir::{self, Location, TerminatorKind}; +use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{Visitor, PlaceContext}; use rustc::mir::traversal; use rustc::ty; @@ -110,13 +111,19 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> { location: Location) { debug!("visit_assign(block={:?}, place={:?}, rvalue={:?})", block, place, rvalue); - if let mir::Place::Local(index) = *place { - self.assign(index, location); - if !self.fx.rvalue_creates_operand(rvalue) { - self.not_ssa(index); + match place { + mir::Place { + base: mir::PlaceBase::Local(index), + elems, + } if elems.is_empty() => { + self.assign(*index, location); + if !self.fx.rvalue_creates_operand(rvalue) { + self.not_ssa(*index); + } + }, + _ => { + self.visit_place(place, PlaceContext::Store, location); } - } else { - self.visit_place(place, PlaceContext::Store, location); } self.visit_rvalue(rvalue, location); @@ -152,44 +159,44 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> { fn visit_place(&mut self, place: &mir::Place<'tcx>, - context: PlaceContext<'tcx>, + mut context: PlaceContext<'tcx>, location: Location) { - debug!("visit_place(place={:?}, context={:?})", place, context); let cx = self.fx.cx; + let mut base_ty = place.base.ty(self.fx.mir); + + for elem in place.elems.iter().cloned() { + debug!("visit_place(place={:?}, context={:?})", place, context); - if let mir::Place::Projection(ref proj) = *place { // Allow uses of projections that are ZSTs or from scalar fields. let is_consume = match context { PlaceContext::Copy | PlaceContext::Move => true, _ => false }; + if is_consume { - let base_ty = proj.base.ty(self.fx.mir, cx.tcx); - let base_ty = self.fx.monomorphize(&base_ty); + base_ty = self.fx.monomorphize(&base_ty); - // ZSTs don't require any actual memory access. - let elem_ty = base_ty.projection_ty(cx.tcx, &proj.elem).to_ty(cx.tcx); - let elem_ty = self.fx.monomorphize(&elem_ty); - if cx.layout_of(elem_ty).is_zst() { + if cx.layout_of(base_ty).is_zst() { return; } - if let mir::ProjectionElem::Field(..) = proj.elem { - let layout = cx.layout_of(base_ty.to_ty(cx.tcx)); + if let mir::ProjectionElem::Field(..) = elem { + let layout = cx.layout_of(base_ty); if layout.is_llvm_immediate() || layout.is_llvm_scalar_pair() { // Recurse with the same context, instead of `Projection`, // potentially stopping at non-operand projections, // which would trigger `not_ssa` on locals. - self.visit_place(&proj.base, context, location); - return; + continue; } } } // A deref projection only reads the pointer, never needs the place. - if let mir::ProjectionElem::Deref = proj.elem { - return self.visit_place(&proj.base, PlaceContext::Copy, location); + if let mir::ProjectionElem::Deref = elem { + context = PlaceContext::Copy; + continue; } + base_ty = PlaceTy::from(base_ty).projection_ty(cx.tcx, &elem).to_ty(cx.tcx); } self.super_place(place, context, location); @@ -234,7 +241,7 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> { } PlaceContext::Drop => { - let ty = mir::Place::Local(local).ty(self.fx.mir, self.fx.cx.tcx); + let ty = mir::Place::local(local).ty(self.fx.mir, self.fx.cx.tcx); let ty = self.fx.monomorphize(&ty.to_ty(self.fx.cx.tcx)); // Only need the place if we're actually dropping it. diff --git a/src/librustc_codegen_llvm/mir/block.rs b/src/librustc_codegen_llvm/mir/block.rs index 4e389c3b915f0..8331453859298 100644 --- a/src/librustc_codegen_llvm/mir/block.rs +++ b/src/librustc_codegen_llvm/mir/block.rs @@ -231,7 +231,7 @@ impl FunctionCx<'a, 'll, 'tcx> { } PassMode::Direct(_) | PassMode::Pair(..) => { - let op = self.codegen_consume(&bx, &mir::Place::Local(mir::RETURN_PLACE)); + let op = self.codegen_consume(&bx, &mir::Place::local(mir::RETURN_PLACE)); if let Ref(llval, align) = op.val { bx.load(llval, align) } else { @@ -514,16 +514,24 @@ impl FunctionCx<'a, 'll, 'tcx> { // checked by const-qualification, which also // promotes any complex rvalues to constants. if i == 2 && intrinsic.unwrap().starts_with("simd_shuffle") { - match *arg { + match arg { // The shuffle array argument is usually not an explicit constant, // but specified directly in the code. This means it gets promoted // and we can then extract the value by evaluating the promoted. - mir::Operand::Copy(mir::Place::Promoted(box(index, ty))) | - mir::Operand::Move(mir::Place::Promoted(box(index, ty))) => { + mir::Operand::Copy(mir::Place { + base: mir::PlaceBase::Promoted(box (index, ty)), + elems, + }) | + mir::Operand::Move(mir::Place { + base: mir::PlaceBase::Promoted(box (index, ty)), + elems, + }) + if elems.is_empty() + => { let param_env = ty::ParamEnv::reveal_all(); let cid = mir::interpret::GlobalId { instance: self.instance, - promoted: Some(index), + promoted: Some(*index), }; let c = bx.tcx().const_eval(param_env.and(cid)); let (llval, ty) = self.simd_shuffle_indices( @@ -817,37 +825,42 @@ impl FunctionCx<'a, 'll, 'tcx> { if fn_ret.is_ignore() { return ReturnDest::Nothing; } - let dest = if let mir::Place::Local(index) = *dest { - match self.locals[index] { - LocalRef::Place(dest) => dest, - LocalRef::Operand(None) => { - // Handle temporary places, specifically Operand ones, as - // they don't have allocas - return if fn_ret.is_indirect() { - // Odd, but possible, case, we have an operand temporary, - // but the calling convention has an indirect return. - let tmp = PlaceRef::alloca(bx, fn_ret.layout, "tmp_ret"); - tmp.storage_live(bx); - llargs.push(tmp.llval); - ReturnDest::IndirectOperand(tmp, index) - } else if is_intrinsic { - // Currently, intrinsics always need a location to store - // the result. so we create a temporary alloca for the - // result - let tmp = PlaceRef::alloca(bx, fn_ret.layout, "tmp_ret"); - tmp.storage_live(bx); - ReturnDest::IndirectOperand(tmp, index) - } else { - ReturnDest::DirectOperand(index) - }; - } - LocalRef::Operand(Some(_)) => { - bug!("place local already assigned to"); + let dest = match dest { + mir::Place { + base: mir::PlaceBase::Local(index), + elems, + } if elems.is_empty() => { + match self.locals[*index] { + LocalRef::Place(dest) => dest, + LocalRef::Operand(None) => { + // Handle temporary places, specifically Operand ones, as + // they don't have allocas + return if fn_ret.is_indirect() { + // Odd, but possible, case, we have an operand temporary, + // but the calling convention has an indirect return. + let tmp = PlaceRef::alloca(bx, fn_ret.layout, "tmp_ret"); + tmp.storage_live(bx); + llargs.push(tmp.llval); + ReturnDest::IndirectOperand(tmp, *index) + } else if is_intrinsic { + // Currently, intrinsics always need a location to store + // the result. so we create a temporary alloca for the + // result + let tmp = PlaceRef::alloca(bx, fn_ret.layout, "tmp_ret"); + tmp.storage_live(bx); + ReturnDest::IndirectOperand(tmp, *index) + } else { + ReturnDest::DirectOperand(*index) + }; + } + LocalRef::Operand(Some(_)) => { + bug!("place local already assigned to"); + } } } - } else { - self.codegen_place(bx, dest) + _ => self.codegen_place(bx, dest), }; + if fn_ret.is_indirect() { if dest.align.abi() < dest.layout.align.abi() { // Currently, MIR code generation does not create calls @@ -868,27 +881,33 @@ impl FunctionCx<'a, 'll, 'tcx> { fn codegen_transmute(&mut self, bx: &Builder<'a, 'll, 'tcx>, src: &mir::Operand<'tcx>, dst: &mir::Place<'tcx>) { - if let mir::Place::Local(index) = *dst { - match self.locals[index] { - LocalRef::Place(place) => self.codegen_transmute_into(bx, src, place), - LocalRef::Operand(None) => { - let dst_layout = bx.cx.layout_of(self.monomorphized_place_ty(dst)); - assert!(!dst_layout.ty.has_erasable_regions()); - let place = PlaceRef::alloca(bx, dst_layout, "transmute_temp"); - place.storage_live(bx); - self.codegen_transmute_into(bx, src, place); - let op = place.load(bx); - place.storage_dead(bx); - self.locals[index] = LocalRef::Operand(Some(op)); - } - LocalRef::Operand(Some(op)) => { - assert!(op.layout.is_zst(), - "assigning to initialized SSAtemp"); + match dst { + mir::Place { + base: mir::PlaceBase::Local(index), + elems, + } if elems.is_empty() => { + match self.locals[*index] { + LocalRef::Place(place) => self.codegen_transmute_into(bx, src, place), + LocalRef::Operand(None) => { + let dst_layout = bx.cx.layout_of(self.monomorphized_place_ty(dst)); + assert!(!dst_layout.ty.has_erasable_regions()); + let place = PlaceRef::alloca(bx, dst_layout, "transmute_temp"); + place.storage_live(bx); + self.codegen_transmute_into(bx, src, place); + let op = place.load(bx); + place.storage_dead(bx); + self.locals[*index] = LocalRef::Operand(Some(op)); + } + LocalRef::Operand(Some(op)) => { + assert!(op.layout.is_zst(), + "assigning to initialized SSAtemp"); + } } } - } else { - let dst = self.codegen_place(bx, dst); - self.codegen_transmute_into(bx, src, dst); + _ => { + let dst = self.codegen_place(bx, dst); + self.codegen_transmute_into(bx, src, dst); + } } } diff --git a/src/librustc_codegen_llvm/mir/operand.rs b/src/librustc_codegen_llvm/mir/operand.rs index f8166ee649147..e4fcdc6999211 100644 --- a/src/librustc_codegen_llvm/mir/operand.rs +++ b/src/librustc_codegen_llvm/mir/operand.rs @@ -310,12 +310,13 @@ impl FunctionCx<'a, 'll, 'tcx> { { debug!("maybe_codegen_consume_direct(place={:?})", place); + let mut result = None; // watch out for locals that do not have an // alloca; they are handled somewhat differently - if let mir::Place::Local(index) = *place { + if let mir::PlaceBase::Local(index) = place.base { match self.locals[index] { LocalRef::Operand(Some(o)) => { - return Some(o); + result = Some(o); } LocalRef::Operand(None) => { bug!("use of {:?} before def", place); @@ -323,15 +324,15 @@ impl FunctionCx<'a, 'll, 'tcx> { LocalRef::Place(..) => { // use path below } - } + }; } // Moves out of scalar and scalar pair fields are trivial. - if let &mir::Place::Projection(ref proj) = place { - if let Some(o) = self.maybe_codegen_consume_direct(bx, &proj.base) { - match proj.elem { + for e in place.elems.iter() { + if let Some(o) = result { + match e { mir::ProjectionElem::Field(ref f, _) => { - return Some(o.extract_field(bx, f.index())); + result = Some(o.extract_field(bx, f.index())); } mir::ProjectionElem::Index(_) | mir::ProjectionElem::ConstantIndex { .. } => { @@ -340,15 +341,15 @@ impl FunctionCx<'a, 'll, 'tcx> { // checks in `codegen_consume` and `extract_field`. let elem = o.layout.field(bx.cx, 0); if elem.is_zst() { - return Some(OperandRef::new_zst(bx.cx, elem)); + result = Some(OperandRef::new_zst(bx.cx, elem)); } } _ => {} - } + }; } } - None + result } pub fn codegen_consume(&mut self, diff --git a/src/librustc_codegen_llvm/mir/place.rs b/src/librustc_codegen_llvm/mir/place.rs index 6fa0845dd0ceb..3bd8cd3b9768f 100644 --- a/src/librustc_codegen_llvm/mir/place.rs +++ b/src/librustc_codegen_llvm/mir/place.rs @@ -417,22 +417,21 @@ impl FunctionCx<'a, 'll, 'tcx> { debug!("codegen_place(place={:?})", place); let cx = bx.cx; - let tcx = cx.tcx; - - if let mir::Place::Local(index) = *place { - match self.locals[index] { - LocalRef::Place(place) => { - return place; - } - LocalRef::Operand(..) => { - bug!("using operand local {:?} as place", place); + if let mir::PlaceBase::Local(index) = place.base { + if place.has_no_projection() { + match self.locals[index] { + LocalRef::Place(place) => { + return place; + } + LocalRef::Operand(..) => { + bug!("using operand local {:?} as place", place); + } } } - } - - let result = match *place { - mir::Place::Local(_) => bug!(), // handled above - mir::Place::Promoted(box (index, ty)) => { + }; + let mut result = match place.base { + mir::PlaceBase::Local(_) => bug!(), + mir::PlaceBase::Promoted(box (index, ty)) => { let param_env = ty::ParamEnv::reveal_all(); let cid = mir::interpret::GlobalId { instance: self.instance, @@ -458,70 +457,67 @@ impl FunctionCx<'a, 'll, 'tcx> { } } } - mir::Place::Static(box mir::Static { def_id, ty }) => { + mir::PlaceBase::Static(box mir::Static { def_id, ty }) => { let layout = cx.layout_of(self.monomorphize(&ty)); PlaceRef::new_sized(consts::get_static(cx, def_id), layout, layout.align) - }, - mir::Place::Projection(box mir::Projection { - ref base, - elem: mir::ProjectionElem::Deref - }) => { - // Load the pointer from its location. - self.codegen_consume(bx, base).deref(bx.cx) } - mir::Place::Projection(ref projection) => { - let cg_base = self.codegen_place(bx, &projection.base); + }; - match projection.elem { - mir::ProjectionElem::Deref => bug!(), - mir::ProjectionElem::Field(ref field, _) => { - cg_base.project_field(bx, field.index()) - } - mir::ProjectionElem::Index(index) => { - let index = &mir::Operand::Copy(mir::Place::Local(index)); - let index = self.codegen_operand(bx, index); - let llindex = index.immediate(); - cg_base.project_index(bx, llindex) - } - mir::ProjectionElem::ConstantIndex { offset, - from_end: false, - min_length: _ } => { - let lloffset = C_usize(bx.cx, offset as u64); - cg_base.project_index(bx, lloffset) - } - mir::ProjectionElem::ConstantIndex { offset, - from_end: true, - min_length: _ } => { - let lloffset = C_usize(bx.cx, offset as u64); - let lllen = cg_base.len(bx.cx); - let llindex = bx.sub(lllen, lloffset); - cg_base.project_index(bx, llindex) + let tcx = cx.tcx; + for (i, elem) in place.elems.iter().cloned().enumerate().rev() { + let base = place.elem_base(tcx, i); + result = match elem { + mir::ProjectionElem::Deref => { + // Load the pointer from its location. + self.codegen_consume(bx, &base).deref(bx.cx) + } + mir::ProjectionElem::Field(ref field, _) => { + result.project_field(bx, field.index()) + } + mir::ProjectionElem::Index(index) => { + let index = &mir::Operand::Copy(mir::Place::local(index)); + let index = self.codegen_operand(bx, index); + let llindex = index.immediate(); + result.project_index(bx, llindex) + } + mir::ProjectionElem::ConstantIndex { offset, + from_end: false, + min_length: _ } => { + let lloffset = C_usize(bx.cx, offset as u64); + result.project_index(bx, lloffset) + } + mir::ProjectionElem::ConstantIndex { offset, + from_end: true, + min_length: _ } => { + let lloffset = C_usize(bx.cx, offset as u64); + let lllen = result.len(bx.cx); + let llindex = bx.sub(lllen, lloffset); + result.project_index(bx, llindex) + } + mir::ProjectionElem::Subslice { from, to } => { + let mut subslice = result.project_index(bx, + C_usize(bx.cx, from as u64)); + let projected_ty = PlaceTy::Ty { ty: result.layout.ty } + .projection_ty(tcx, &elem).to_ty(bx.tcx()); + subslice.layout = bx.cx.layout_of(self.monomorphize(&projected_ty)); + + if subslice.layout.is_unsized() { + subslice.llextra = Some(bx.sub(result.llextra.unwrap(), + C_usize(bx.cx, (from as u64) + (to as u64)))); } - mir::ProjectionElem::Subslice { from, to } => { - let mut subslice = cg_base.project_index(bx, - C_usize(bx.cx, from as u64)); - let projected_ty = PlaceTy::Ty { ty: cg_base.layout.ty } - .projection_ty(tcx, &projection.elem).to_ty(bx.tcx()); - subslice.layout = bx.cx.layout_of(self.monomorphize(&projected_ty)); - - if subslice.layout.is_unsized() { - subslice.llextra = Some(bx.sub(cg_base.llextra.unwrap(), - C_usize(bx.cx, (from as u64) + (to as u64)))); - } - // Cast the place pointer type to the new - // array or slice type (*[%_; new_len]). - subslice.llval = bx.pointercast(subslice.llval, - subslice.layout.llvm_type(bx.cx).ptr_to()); + // Cast the place pointer type to the new + // array or slice type (*[%_; new_len]). + subslice.llval = bx.pointercast(subslice.llval, + subslice.layout.llvm_type(bx.cx).ptr_to()); - subslice - } - mir::ProjectionElem::Downcast(_, v) => { - cg_base.project_downcast(bx, v) - } + subslice } - } - }; + mir::ProjectionElem::Downcast(_, v) => { + result.project_downcast(bx, v) + } + }; + } debug!("codegen_place(place={:?}) => {:?}", place, result); result } diff --git a/src/librustc_codegen_llvm/mir/rvalue.rs b/src/librustc_codegen_llvm/mir/rvalue.rs index dda33ae3fecdf..2190cd9ae4f33 100644 --- a/src/librustc_codegen_llvm/mir/rvalue.rs +++ b/src/librustc_codegen_llvm/mir/rvalue.rs @@ -518,11 +518,13 @@ impl FunctionCx<'a, 'll, 'tcx> { ) -> &'ll Value { // ZST are passed as operands and require special handling // because codegen_place() panics if Local is operand. - if let mir::Place::Local(index) = *place { - if let LocalRef::Operand(Some(op)) = self.locals[index] { - if let ty::TyArray(_, n) = op.layout.ty.sty { - let n = n.unwrap_usize(bx.cx.tcx); - return common::C_usize(bx.cx, n); + if place.has_no_projection() { + if let mir::PlaceBase::Local(index) = place.base { + if let LocalRef::Operand(Some(op)) = self.locals[index] { + if let ty::TyArray(_, n) = op.layout.ty.sty { + let n = n.unwrap_usize(bx.cx.tcx); + return common::C_usize(bx.cx, n); + } } } } diff --git a/src/librustc_codegen_llvm/mir/statement.rs b/src/librustc_codegen_llvm/mir/statement.rs index 06340a3e5d8b4..482b404e68694 100644 --- a/src/librustc_codegen_llvm/mir/statement.rs +++ b/src/librustc_codegen_llvm/mir/statement.rs @@ -26,31 +26,37 @@ impl FunctionCx<'a, 'll, 'tcx> { self.set_debug_loc(&bx, statement.source_info); match statement.kind { mir::StatementKind::Assign(ref place, ref rvalue) => { - if let mir::Place::Local(index) = *place { - match self.locals[index] { - LocalRef::Place(cg_dest) => { - self.codegen_rvalue(bx, cg_dest, rvalue) - } - LocalRef::Operand(None) => { - let (bx, operand) = self.codegen_rvalue_operand(bx, rvalue); - self.locals[index] = LocalRef::Operand(Some(operand)); - bx - } - LocalRef::Operand(Some(op)) => { - if !op.layout.is_zst() { - span_bug!(statement.source_info.span, - "operand {:?} already assigned", - rvalue); + match place { + mir::Place { + base: mir::PlaceBase::Local(index), + elems, + } if elems.is_empty() => { + match self.locals[*index] { + LocalRef::Place(cg_dest) => { + self.codegen_rvalue(bx, cg_dest, rvalue) + } + LocalRef::Operand(None) => { + let (bx, operand) = self.codegen_rvalue_operand(bx, rvalue); + self.locals[*index] = LocalRef::Operand(Some(operand)); + bx } + LocalRef::Operand(Some(op)) => { + if !op.layout.is_zst() { + span_bug!(statement.source_info.span, + "operand {:?} already assigned", + rvalue); + } - // If the type is zero-sized, it's already been set here, - // but we still need to make sure we codegen the operand - self.codegen_rvalue_operand(bx, rvalue).0 + // If the type is zero-sized, it's already been set here, + // but we still need to make sure we codegen the operand + self.codegen_rvalue_operand(bx, rvalue).0 + } } } - } else { - let cg_dest = self.codegen_place(&bx, place); - self.codegen_rvalue(bx, cg_dest, rvalue) + _ => { + let cg_dest = self.codegen_place(&bx, place); + self.codegen_rvalue(bx, cg_dest, rvalue) + } } } mir::StatementKind::SetDiscriminant{ref place, variant_index} => { diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/src/librustc_mir/borrow_check/borrow_set.rs index fe33dc0a58a47..1a795b846a367 100644 --- a/src/librustc_mir/borrow_check/borrow_set.rs +++ b/src/librustc_mir/borrow_check/borrow_set.rs @@ -12,7 +12,7 @@ use borrow_check::place_ext::PlaceExt; use dataflow::indexes::BorrowIndex; use rustc::mir::traversal; use rustc::mir::visit::{PlaceContext, Visitor}; -use rustc::mir::{self, Location, Mir, Place}; +use rustc::mir::{self, Location, Mir, Place, PlaceBase}; use rustc::ty::{Region, TyCtxt}; use rustc::util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::IndexVec; @@ -202,7 +202,10 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { self.super_place(place, context, location); // We found a use of some temporary TEMP... - if let Place::Local(temp) = place { + if let Place { + base: PlaceBase::Local(temp), + elems: _, + } = place { // ... check whether we (earlier) saw a 2-phase borrow like // // TMP = &mut place @@ -254,7 +257,6 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { } }; } - None => {} } } @@ -321,7 +323,7 @@ impl<'a, 'gcx, 'tcx> GatherBorrows<'a, 'gcx, 'tcx> { // TEMP = &foo // // so extract `temp`. - let temp = if let &mir::Place::Local(temp) = assigned_place { + let temp = if let mir::PlaceBase::Local(temp) = assigned_place.base { temp } else { span_bug!( diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index aabed6686858f..0424bf0f5202a 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -12,8 +12,8 @@ use borrow_check::WriteKind; use rustc::middle::region::ScopeTree; use rustc::mir::VarBindingForm; use rustc::mir::{BindingForm, BorrowKind, ClearCrossCrate, Field, Local}; -use rustc::mir::{LocalDecl, LocalKind, Location, Operand, Place}; -use rustc::mir::{ProjectionElem, Rvalue, Statement, StatementKind}; +use rustc::mir::{LocalDecl, LocalKind, Location, Operand, Place, PlaceBase}; +use rustc::mir::{PlaceElem, ProjectionElem, Rvalue, Statement, StatementKind}; use rustc::ty; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::sync::Lrc; @@ -22,7 +22,7 @@ use syntax_pos::Span; use super::borrow_set::BorrowData; use super::{Context, MirBorrowckCtxt}; -use super::{InitializationRequiringAction, PrefixSet}; +use super::InitializationRequiringAction; use dataflow::move_paths::MovePathIndex; use dataflow::{FlowAtLocation, MovingOutStatements}; @@ -48,7 +48,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { .collect::>(); if mois.is_empty() { - let root_place = self.prefixes(&place, PrefixSet::All).last().unwrap(); + let root_place = place.elem_base(self.tcx, 0); if self.moved_error_reported.contains(&root_place.clone()) { debug!( @@ -384,16 +384,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ) { let drop_span = place_span.1; let scope_tree = self.tcx.region_scope_tree(self.mir_def_id); - let root_place = self - .prefixes(&borrow.borrowed_place, PrefixSet::All) - .last() - .unwrap(); + let root_place = borrow.borrowed_place.elem_base(self.tcx, 0); let borrow_spans = self.retrieve_borrow_spans(borrow); let borrow_span = borrow_spans.var_or_use(); - let proper_span = match *root_place { - Place::Local(local) => self.mir.local_decls[local].source_info.span, + let proper_span = match root_place.base { + PlaceBase::Local(local) if root_place.has_no_projection() + => self.mir.local_decls[local].source_info.span, _ => drop_span, }; @@ -531,15 +529,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { assigned_span: Span, err_place: &Place<'tcx>, ) { - let (from_arg, local_decl) = if let Place::Local(local) = *err_place { - if let LocalKind::Arg = self.mir.local_kind(local) { - (true, Some(&self.mir.local_decls[local])) - } else { - (false, Some(&self.mir.local_decls[local])) + let (mut from_arg, mut local_decl) = (false, None); + + if let PlaceBase::Local(local) = err_place.base { + if err_place.has_no_projection() { + if let LocalKind::Arg = self.mir.local_kind(local) { + from_arg = true; + local_decl = Some(&self.mir.local_decls[local]); + } else { + local_decl = Some(&self.mir.local_decls[local]); + } } - } else { - (false, None) - }; + } // If root local is initialized immediately (everything apart from let // PATTERN;) then make the error refer to that local, rather than the @@ -619,7 +620,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { including_downcast: IncludingDowncast, ) -> Option { let mut buf = String::new(); - match self.append_place_to_string(place, &mut buf, false, &including_downcast) { + match self.append_place_to_string(place, &mut buf, &including_downcast) { Ok(()) => Some(buf), Err(()) => None, } @@ -630,133 +631,92 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { &self, place: &Place<'tcx>, buf: &mut String, - mut autoderef: bool, including_downcast: &IncludingDowncast, ) -> Result<(), ()> { - match *place { - Place::Promoted(_) => { + self.append_place_base_to_string(&place.base, buf)?; + + for elem in place.elems.iter() { + self.append_place_projection_to_string( + place, + elem, + buf, + including_downcast + )?; + } + + Ok(()) + } + + fn append_place_base_to_string( + &self, + base: &PlaceBase<'tcx>, + buf: &mut String, + ) -> Result<(), ()> { + match base { + PlaceBase::Promoted(_) => { buf.push_str("promoted"); } - Place::Local(local) => { - self.append_local_to_string(local, buf)?; + PlaceBase::Local(local) => { + self.append_local_to_string(*local, buf)?; } - Place::Static(ref static_) => { + PlaceBase::Static(ref static_) => { buf.push_str(&self.tcx.item_name(static_.def_id).to_string()); } - Place::Projection(ref proj) => { - match proj.elem { - ProjectionElem::Deref => { - let upvar_field_projection = - place.is_upvar_field_projection(self.mir, &self.tcx); - if let Some(field) = upvar_field_projection { - let var_index = field.index(); - let name = self.mir.upvar_decls[var_index].debug_name.to_string(); - if self.mir.upvar_decls[var_index].by_ref { - buf.push_str(&name); - } else { - buf.push_str(&format!("*{}", &name)); - } - } else { - if autoderef { - self.append_place_to_string( - &proj.base, - buf, - autoderef, - &including_downcast, - )?; - } else if let Place::Local(local) = proj.base { - if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) = - self.mir.local_decls[local].is_user_variable - { - self.append_place_to_string( - &proj.base, - buf, - autoderef, - &including_downcast, - )?; - } else { - buf.push_str(&"*"); - self.append_place_to_string( - &proj.base, - buf, - autoderef, - &including_downcast, - )?; - } - } else { - buf.push_str(&"*"); - self.append_place_to_string( - &proj.base, - buf, - autoderef, - &including_downcast, - )?; - } - } - } - ProjectionElem::Downcast(..) => { - self.append_place_to_string( - &proj.base, - buf, - autoderef, - &including_downcast, - )?; - if including_downcast.0 { - return Err(()); - } - } - ProjectionElem::Field(field, _ty) => { - autoderef = true; - - let upvar_field_projection = - place.is_upvar_field_projection(self.mir, &self.tcx); - if let Some(field) = upvar_field_projection { - let var_index = field.index(); - let name = self.mir.upvar_decls[var_index].debug_name.to_string(); - buf.push_str(&name); - } else { - let field_name = self.describe_field(&proj.base, field); - self.append_place_to_string( - &proj.base, - buf, - autoderef, - &including_downcast, - )?; - buf.push_str(&format!(".{}", field_name)); - } - } - ProjectionElem::Index(index) => { - autoderef = true; - - self.append_place_to_string( - &proj.base, - buf, - autoderef, - &including_downcast, - )?; - buf.push_str("["); - if let Err(_) = self.append_local_to_string(index, buf) { - buf.push_str(".."); - } - buf.push_str("]"); - } - ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { - autoderef = true; - // Since it isn't possible to borrow an element on a particular index and - // then use another while the borrow is held, don't output indices details - // to avoid confusing the end-user - self.append_place_to_string( - &proj.base, - buf, - autoderef, - &including_downcast, - )?; - buf.push_str(&"[..]"); + } + Ok(()) + } + + fn append_place_projection_to_string( + &self, + place: &Place<'tcx>, + projection: &PlaceElem<'tcx>, + buf: &mut String, + including_downcast: &IncludingDowncast, + ) -> Result<(), ()> { + match projection { + ProjectionElem::Deref => { + let upvar_field_projection = + place.is_upvar_field_projection(self.mir, &self.tcx); + if let Some(field) = upvar_field_projection { + let var_index = field.index(); + let name = self.mir.upvar_decls[var_index].debug_name.as_str(); + if self.mir.upvar_decls[var_index].by_ref { + buf.push_str(&name); + } else { + buf.push_str(&format!("*{}", name)); } - }; + } else { + buf.push_str(&"*"); + } + } + ProjectionElem::Downcast(..) => { + if including_downcast.0 { + return Err(()); + } + } + ProjectionElem::Field(field, _ty) => { + let upvar_field_projection = + place.is_upvar_field_projection(self.mir, &self.tcx); + if let Some(field) = upvar_field_projection { + let var_index = field.index(); + let name = self.mir.upvar_decls[var_index].debug_name.as_str(); + buf.push_str(&name); + } else { + let field_name = self.describe_field(&place, *field); + buf.push_str(&format!(".{}", field_name)); + } + } + ProjectionElem::Index(index) => { + buf.push_str("["); + if let Err(_) = self.append_local_to_string(*index, buf) { + buf.push_str(".."); + } + buf.push_str("]"); + } + ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { + buf.push_str(&"[..]"); } } - Ok(()) } @@ -774,30 +734,34 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } // End-user visible description of the `field`nth field of `base` - fn describe_field(&self, base: &Place, field: Field) -> String { - match *base { - Place::Local(local) => { - let local = &self.mir.local_decls[local]; - self.describe_field_from_ty(&local.ty, field) - } - Place::Promoted(ref prom) => self.describe_field_from_ty(&prom.1, field), - Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field), - Place::Projection(ref proj) => match proj.elem { - ProjectionElem::Deref => self.describe_field(&proj.base, field), + fn describe_field(&self, place: &Place<'tcx>, field: Field) -> String { + let mut string: String = match place.base { + PlaceBase::Local(local) => { + let local = &self.mir.local_decls[local]; + self.describe_field_from_ty(&local.ty, field) + } + PlaceBase::Promoted(ref prom) => self.describe_field_from_ty(&prom.1, field), + PlaceBase::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field), + }; + + for elem in place.elems.iter() { + let proj_str = match elem { ProjectionElem::Downcast(def, variant_index) => format!( "{}", - def.variants[variant_index].fields[field.index()].ident - ), + def.variants[*variant_index].fields[field.index()].ident + ).to_string(), ProjectionElem::Field(_, field_type) => { - self.describe_field_from_ty(&field_type, field) + self.describe_field_from_ty(&field_type, field).to_string() } ProjectionElem::Index(..) + | ProjectionElem::Deref | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => { - self.describe_field(&proj.base, field).to_string() - } - }, + | ProjectionElem::Subslice { .. } => continue, + }; + string.push_str(proj_str.as_str()); } + + string } // End-user visible description of the `field_index`nth field of `ty` @@ -843,18 +807,22 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // Retrieve type of a place for the current MIR representation fn retrieve_type_for_place(&self, place: &Place<'tcx>) -> Option { - match place { - Place::Local(local) => { - let local = &self.mir.local_decls[*local]; + let place = place.clone(); + let mut ty = match place.base { + PlaceBase::Local(local) => { + let local = &self.mir.local_decls[local]; Some(local.ty) } - Place::Promoted(ref prom) => Some(prom.1), - Place::Static(ref st) => Some(st.ty), - Place::Projection(ref proj) => match proj.elem { - ProjectionElem::Field(_, ty) => Some(ty), - _ => None, - }, + PlaceBase::Promoted(prom) => Some(prom.1), + PlaceBase::Static(st) => Some(st.ty), + }; + if let Some(projection) = place.elems.last() { + ty = match projection { + ProjectionElem::Field(_, ty) => Some(ty), + _ => None, + }; } + ty } } @@ -990,7 +958,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { .get(location.statement_index) { Some(&Statement { - kind: StatementKind::Assign(Place::Local(local), _), + kind: StatementKind::Assign( + Place { + base: PlaceBase::Local(local), + elems: _, + }, + _, + ), .. }) => local, _ => return OtherUse(use_span), @@ -1018,15 +992,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { self.tcx .with_freevars(node_id, |freevars| { for (v, place) in freevars.iter().zip(places) { - match *place { - Operand::Copy(Place::Local(l)) - | Operand::Move(Place::Local(l)) - if local == l => + match place { + Operand::Copy(place) + | Operand::Move(place) + if PlaceBase::Local(local) == place.base && + place.has_no_projection() => { debug!( "find_closure_borrow_span: found captured local \ {:?}", - l + local ); return Some(v.span); } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 320d3a4720321..d69f7cfe6b1f7 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -18,9 +18,10 @@ use rustc::infer::InferCtxt; use rustc::lint::builtin::UNUSED_MUT; use rustc::middle::borrowck::SignalledError; use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}; -use rustc::mir::{ClearCrossCrate, Local, Location, Mir, Mutability, Operand, Place}; -use rustc::mir::{Field, Projection, ProjectionElem, Rvalue, Statement, StatementKind}; +use rustc::mir::{ClearCrossCrate, Local, Location, Mir, Mutability, Operand, Place, PlaceBase}; +use rustc::mir::{Field, ProjectionElem, Rvalue, Statement, StatementKind}; use rustc::mir::{Terminator, TerminatorKind}; +use rustc::mir::tcx::PlaceTy; use rustc::ty::query::Providers; use rustc::ty::{self, ParamEnv, TyCtxt, Ty}; @@ -49,7 +50,6 @@ use util::borrowck_errors::{BorrowckErrors, Origin}; use self::borrow_set::{BorrowData, BorrowSet}; use self::flows::Flows; use self::location::LocationTable; -use self::prefixes::PrefixSet; use self::MutateMode::{JustWrite, WriteAndRead}; use self::mutability_errors::AccessKind; @@ -64,7 +64,6 @@ mod mutability_errors; mod path_utils; crate mod place_ext; mod places_conflict; -mod prefixes; mod used_muts; pub(crate) mod nll; @@ -547,7 +546,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx StatementKind::StorageDead(local) => { self.access_place( ContextKind::StorageDead.new(location), - (&Place::Local(local), span), + (&Place::local(local), span), (Shallow(None), Write(WriteKind::StorageDeadOrDrop)), LocalMutationIsAllowed::Yes, flow_state, @@ -924,10 +923,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } let gcx = self.tcx.global_tcx(); + let tcx = self.tcx; let drop_field = |mir: &mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>, (index, field): (usize, ty::Ty<'gcx>)| { let field_ty = gcx.normalize_erasing_regions(mir.param_env, field); - let place = drop_place.clone().field(Field::new(index), field_ty); + let place = drop_place.clone().field(tcx, Field::new(index), field_ty); debug!("visit_terminator_drop drop_field place: {:?} field_ty: {:?}", place, field_ty); let seen = prev_seen.cons(erased_drop_place_ty); @@ -956,25 +956,37 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } // Closures also have disjoint fields, but they are only // directly accessed in the body of the closure. - ty::TyClosure(def, substs) - if *drop_place == Place::Local(Local::new(1)) - && !self.mir.upvar_decls.is_empty() => - { - substs - .upvar_tys(def, self.tcx) - .enumerate() - .for_each(|field| drop_field(self, field)); + ty::TyClosure(def, substs) => { + if let Place { + base: PlaceBase::Local(local), + elems, + } = drop_place { + if *local == Local::new(1) + && !self.mir.upvar_decls.is_empty() + && elems.is_empty() { + substs + .upvar_tys(def, self.tcx) + .enumerate() + .for_each(|field| drop_field(self, field)); + } + } } // Generators also have disjoint fields, but they are only // directly accessed in the body of the generator. - ty::TyGenerator(def, substs, _) - if *drop_place == Place::Local(Local::new(1)) - && !self.mir.upvar_decls.is_empty() => - { - substs - .upvar_tys(def, self.tcx) - .enumerate() - .for_each(|field| drop_field(self, field)); + ty::TyGenerator(def, substs, _) => { + if let Place { + base: PlaceBase::Local(local), + elems, + } = drop_place { + if *local == Local::new(1) + && !self.mir.upvar_decls.is_empty() + && elems.is_empty() { + substs + .upvar_tys(def, self.tcx) + .enumerate() + .for_each( | field | drop_field( self, field)); + } + } } // #45696: special-case Box by treating its dtor as @@ -996,7 +1008,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // `Box>>`, while // still restricting Write to *owned* content. let ty = erased_drop_place_ty.boxed_ty(); - let deref_place = drop_place.clone().deref(); + let deref_place = drop_place.clone().deref(tcx); debug!("visit_terminator_drop drop-box-content deref_place: {:?} ty: {:?}", deref_place, ty); let seen = prev_seen.cons(erased_drop_place_ty); @@ -1389,22 +1401,21 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // captures of a closure are copied/moved directly // when generating MIR. match operands[field.index()] { - Operand::Move(Place::Local(local)) - | Operand::Copy(Place::Local(local)) => { - self.used_mut.insert(local); - } - Operand::Move(ref place @ Place::Projection(_)) - | Operand::Copy(ref place @ Place::Projection(_)) => { + Operand::Move(ref place) | Operand::Copy(ref place) => { if let Some(field) = place.is_upvar_field_projection( - self.mir, &self.tcx) { + self.mir, + &self.tcx + ) { self.used_mut_upvars.push(field); } - } - Operand::Move(Place::Static(..)) - | Operand::Copy(Place::Static(..)) - | Operand::Move(Place::Promoted(..)) - | Operand::Copy(Place::Promoted(..)) - | Operand::Constant(..) => {} + + if let PlaceBase::Local(local) = place.base { + if place.has_no_projection() { + self.used_mut.insert(local); + } + } + }, + _ => {} } } } @@ -1478,33 +1489,33 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ) { debug!("check_for_invalidation_at_exit({:?})", borrow); let place = &borrow.borrowed_place; - let root_place = self.prefixes(place, PrefixSet::All).last().unwrap(); // FIXME(nll-rfc#40): do more precise destructor tracking here. For now // we just know that all locals are dropped at function exit (otherwise // we'll have a memory leak) and assume that all statics have a destructor. // // FIXME: allow thread-locals to borrow other thread locals? - let (might_be_alive, will_be_dropped) = match root_place { - Place::Promoted(_) => (true, false), - Place::Static(statik) => { - // Thread-locals might be dropped after the function exits, but - // "true" statics will never be. - let is_thread_local = self - .tcx - .get_attrs(statik.def_id) - .iter() - .any(|attr| attr.check_name("thread_local")); - - (true, is_thread_local) - } - Place::Local(_) => { - // Locals are always dropped at function exit, and if they - // have a destructor it would've been called already. - (false, self.locals_are_invalidated_at_exit) - } - Place::Projection(..) => { - bug!("root of {:?} is a projection ({:?})?", place, root_place) + let (might_be_alive, will_be_dropped) = if !place.has_no_projection() { + bug!("root of {:?} is a projection ({:?})?", place, place) + } else { + match place.base { + PlaceBase::Promoted(_) => (true, false), + PlaceBase::Static(ref statik) => { + // Thread-locals might be dropped after the function exits, but + // "true" statics will never be. + let is_thread_local = self + .tcx + .get_attrs(statik.def_id) + .iter() + .any(|attr| attr.check_name("thread_local")); + + (true, is_thread_local) + } + PlaceBase::Local(_) => { + // Locals are always dropped at function exit, and if they + // have a destructor it would've been called already. + (false, self.locals_are_invalidated_at_exit) + } } }; @@ -1520,7 +1531,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // that is merged. let sd = if might_be_alive { Deep } else { Shallow(None) }; - if places_conflict::places_conflict(self.tcx, self.mir, place, root_place, sd) { + let root_place = place.elem_base(self.tcx, 0); + + if places_conflict::places_conflict(self.tcx, self.mir, place, &root_place, sd) { debug!("check_for_invalidation_at_exit({:?}): INVALID", place); // FIXME: should be talking about the region lifetime instead // of just a span here. @@ -1669,7 +1682,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // This code covers scenarios 1, 2, and 3. debug!("check_if_full_path_is_moved place: {:?}", place); - match self.move_path_closest_to(place) { + match self.move_path_closest_to(&place) { Ok(mpi) => { if maybe_uninits.contains(&mpi) { self.report_use_of_moved_or_uninitialized( @@ -1734,7 +1747,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // This code covers scenario 1. debug!("check_if_path_or_subpath_is_moved place: {:?}", place); - if let Some(mpi) = self.move_path_for_place(place) { + if let Some(mpi) = self.move_path_for_place(&place) { if let Some(child_mpi) = maybe_uninits.has_any_child_of(mpi) { self.report_use_of_moved_or_uninitialized( context, @@ -1762,18 +1775,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { &mut self, place: &Place<'tcx>, ) -> Result { - let mut last_prefix = place; - for prefix in self.prefixes(place, PrefixSet::All) { - if let Some(mpi) = self.move_path_for_place(prefix) { + let mut prefix = place.clone(); + for (i, _) in prefix.elems.iter().cloned().enumerate().rev() { + prefix = place.elem_base(self.tcx, i); + if let Some(mpi) = self.move_path_for_place(&prefix) { return Ok(mpi); } - last_prefix = prefix; } - match *last_prefix { - Place::Local(_) => panic!("should have move path for every Local"), - Place::Projection(_) => panic!("PrefixSet::All meant don't stop for Projection"), - Place::Promoted(_) | - Place::Static(_) => return Err(NoMovePathFound::ReachedStatic), + match place.base { + PlaceBase::Local(_) => panic!("should have move path for every Local"), + PlaceBase::Promoted(_) | + PlaceBase::Static(_) => return Err(NoMovePathFound::ReachedStatic), } } @@ -1795,70 +1807,56 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { flow_state: &Flows<'cx, 'gcx, 'tcx>, ) { debug!("check_if_assigned_path_is_moved place: {:?}", place); - // recur down place; dispatch to external checks when necessary - let mut place = place; - loop { - match *place { - Place::Promoted(_) | - Place::Local(_) | Place::Static(_) => { - // assigning to `x` does not require `x` be initialized. - break; - } - Place::Projection(ref proj) => { - let Projection { ref base, ref elem } = **proj; - match *elem { - ProjectionElem::Index(_/*operand*/) | - ProjectionElem::ConstantIndex { .. } | - // assigning to P[i] requires P to be valid. - ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) => - // assigning to (P->variant) is okay if assigning to `P` is okay - // - // FIXME: is this true even if P is a adt with a dtor? - { } - - // assigning to (*P) requires P to be initialized - ProjectionElem::Deref => { - self.check_if_full_path_is_moved( - context, InitializationRequiringAction::Use, - (base, span), flow_state); - // (base initialized; no need to - // recur further) - break; - } - - ProjectionElem::Subslice { .. } => { - panic!("we don't allow assignments to subslices, context: {:?}", - context); - } - - ProjectionElem::Field(..) => { - // if type of `P` has a dtor, then - // assigning to `P.f` requires `P` itself - // be already initialized - let tcx = self.tcx; - match base.ty(self.mir, tcx).to_ty(tcx).sty { - ty::TyAdt(def, _) if def.has_dtor(tcx) => { - - // FIXME: analogous code in - // check_loans.rs first maps - // `base` to its base_path. - - self.check_if_path_or_subpath_is_moved( - context, InitializationRequiringAction::Assignment, - (base, span), flow_state); - - // (base initialized; no need to - // recur further) - break; - } - _ => {} - } - } - } - - place = base; - continue; - } + let tcx = self.tcx; + for (i, elem) in place.elems.iter().cloned().enumerate().rev() { + let base_place = place.elem_base(tcx, i); + match elem { + ProjectionElem::Index(_/*operand*/) | + ProjectionElem::ConstantIndex { .. } | + // assigning to P[i] requires P to be valid. + ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) => + // assigning to (P->variant) is okay if assigning to `P` is okay + // + // FIXME: is this true even if P is a adt with a dtor? + { } + + // assigning to (*P) requires P to be initialized + ProjectionElem::Deref => { + self.check_if_full_path_is_moved( + context, InitializationRequiringAction::Use, + (&base_place, span), flow_state); + // (base initialized; no need to + // recur further) + break; + } + + ProjectionElem::Subslice { .. } => { + panic!("we don't allow assignments to subslices, context: {:?}", + context); + } + + ProjectionElem::Field(..) => { + // if type of `P` has a dtor, then + // assigning to `P.f` requires `P` itself + // be already initialized + match base_place.ty(self.mir, tcx).to_ty(tcx).sty { + ty::TyAdt(def, _) if def.has_dtor(tcx) => { + + // FIXME: analogous code in + // check_loans.rs first maps + // `base` to its base_path. + + self.check_if_path_or_subpath_is_moved( + context, InitializationRequiringAction::Assignment, + (&base_place, span), flow_state); + + // (base initialized; no need to + // recur further) + break; + } + _ => {} + } + } } } } @@ -1988,45 +1986,31 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { root_place: RootPlace<'d, 'tcx>, flow_state: &Flows<'cx, 'gcx, 'tcx>, ) { - match root_place { - RootPlace { - place: Place::Local(local), - is_local_mutation_allowed, - } => { - if is_local_mutation_allowed != LocalMutationIsAllowed::Yes { + let RootPlace { + ref place, + is_local_mutation_allowed, + } = root_place; + + if !place.has_no_projection() { + if let Some(field) = place.is_upvar_field_projection(self.mir, &self.tcx) { + self.used_mut_upvars.push(field); + } + } else { + if is_local_mutation_allowed != LocalMutationIsAllowed::Yes { + if let PlaceBase::Local(local) = place.base { // If the local may be initialized, and it is now currently being // mutated, then it is justified to be annotated with the `mut` // keyword, since the mutation may be a possible reassignment. - let mpi = self.move_data.rev_lookup.find_local(*local); + let mpi = self.move_data.rev_lookup.find_local(local); let ii = &self.move_data.init_path_map[mpi]; for index in ii { if flow_state.ever_inits.contains(index) { - self.used_mut.insert(*local); + self.used_mut.insert(local); break; } } } } - RootPlace { - place: _, - is_local_mutation_allowed: LocalMutationIsAllowed::Yes, - } => {} - RootPlace { - place: place @ Place::Projection(_), - is_local_mutation_allowed: _, - } => { - if let Some(field) = place.is_upvar_field_projection(self.mir, &self.tcx) { - self.used_mut_upvars.push(field); - } - } - RootPlace { - place: Place::Promoted(..), - is_local_mutation_allowed: _, - } => {} - RootPlace { - place: Place::Static(..), - is_local_mutation_allowed: _, - } => {} } } @@ -2035,160 +2019,167 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { fn is_mutable<'d>( &self, place: &'d Place<'tcx>, - is_local_mutation_allowed: LocalMutationIsAllowed, + mut is_local_mutation_allowed: LocalMutationIsAllowed, ) -> Result, &'d Place<'tcx>> { - match *place { - Place::Local(local) => { - let local = &self.mir.local_decls[local]; - match local.mutability { - Mutability::Not => match is_local_mutation_allowed { - LocalMutationIsAllowed::Yes => Ok(RootPlace { + let base_is_mutable = | + base: &'d PlaceBase<'tcx>, + is_local_mutation_allowed: LocalMutationIsAllowed, + | { + match base { + PlaceBase::Local(local) => { + let local = &self.mir.local_decls[*local]; + match local.mutability { + Mutability::Not => match is_local_mutation_allowed { + LocalMutationIsAllowed::Yes => Ok(RootPlace { + place, + is_local_mutation_allowed: LocalMutationIsAllowed::Yes, + }), + LocalMutationIsAllowed::ExceptUpvars => Ok(RootPlace { + place, + is_local_mutation_allowed: LocalMutationIsAllowed::ExceptUpvars, + }), + LocalMutationIsAllowed::No => Err(place), + }, + Mutability::Mut => Ok(RootPlace { place, - is_local_mutation_allowed: LocalMutationIsAllowed::Yes, + is_local_mutation_allowed, }), - LocalMutationIsAllowed::ExceptUpvars => Ok(RootPlace { - place, - is_local_mutation_allowed: LocalMutationIsAllowed::ExceptUpvars, - }), - LocalMutationIsAllowed::No => Err(place), - }, - Mutability::Mut => Ok(RootPlace { - place, - is_local_mutation_allowed, - }), + } } - } - // The rules for promotion are made by `qualify_consts`, there wouldn't even be a - // `Place::Promoted` if the promotion weren't 100% legal. So we just forward this - Place::Promoted(_) => Ok(RootPlace { - place, - is_local_mutation_allowed, - }), - Place::Static(ref static_) => { - if self.tcx.is_static(static_.def_id) != Some(hir::Mutability::MutMutable) { - Err(place) - } else { - Ok(RootPlace { - place, - is_local_mutation_allowed, - }) + // The rules for promotion are made by `qualify_consts`, + // there wouldn't even be a `PlaceBase::Promoted` + // if the promotion weren't 100% legal. So we just forward this + PlaceBase::Promoted(_) => Ok(RootPlace { + place, + is_local_mutation_allowed, + }), + PlaceBase::Static(static_) => { + if self.tcx.is_static(static_.def_id) != Some(hir::Mutability::MutMutable) { + Err(place) + } else { + Ok(RootPlace { + place, + is_local_mutation_allowed, + }) + } } } - Place::Projection(ref proj) => { - match proj.elem { - ProjectionElem::Deref => { - let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); - - // Check the kind of deref to decide - match base_ty.sty { - ty::TyRef(_, _, mutbl) => { - match mutbl { - // Shared borrowed data is never mutable - hir::MutImmutable => Err(place), - // Mutably borrowed data is mutable, but only if we have a - // unique path to the `&mut` - hir::MutMutable => { - let mode = match place.is_upvar_field_projection( - self.mir, &self.tcx) + }; + + let mut result = base_is_mutable( + &place.base, + is_local_mutation_allowed + ); + + let mut base_ty = place.base.ty(self.mir); + for elem in place.elems.iter() { + result = match elem { + // NOTE(review): deref is really special. + // + // All other projections are owned by their base path, so mutable if + // base path is mutable + ProjectionElem::Deref => { + base_ty = PlaceTy::from(base_ty) + .projection_ty(self.tcx, elem).to_ty(self.tcx); + match base_ty.sty { + ty::TyRef(_, _, mutbl) => { + match mutbl { + // Shared borrowed data is never mutable + hir::MutImmutable => Err(place), + // Mutably borrowed data is mutable, but only if we have a + // unique path to the `&mut` + hir::MutMutable => { + is_local_mutation_allowed = + match place.is_upvar_field_projection( + self.mir, + &self.tcx + ) { + Some(field) + if { + self.mir.upvar_decls[field.index()].by_ref + } => { - Some(field) - if { - self.mir.upvar_decls[field.index()].by_ref - } => - { - is_local_mutation_allowed - } - _ => LocalMutationIsAllowed::Yes, - }; - - self.is_mutable(&proj.base, mode) - } - } - } - ty::TyRawPtr(tnm) => { - match tnm.mutbl { - // `*const` raw pointers are not mutable - hir::MutImmutable => return Err(place), - // `*mut` raw pointers are always mutable, regardless of - // context. The users have to check by themselves. - hir::MutMutable => { - return Ok(RootPlace { - place, - is_local_mutation_allowed, - }); - } + is_local_mutation_allowed + } + _ => LocalMutationIsAllowed::Yes, + }; + continue } } - // `Box` owns its content, so mutable if its location is mutable - _ if base_ty.is_box() => { - self.is_mutable(&proj.base, is_local_mutation_allowed) - } - // Deref should only be for reference, pointers or boxes - _ => bug!("Deref of unexpected type: {:?}", base_ty), } - } - // All other projections are owned by their base path, so mutable if - // base path is mutable - ProjectionElem::Field(..) - | ProjectionElem::Index(..) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::Downcast(..) => { - let upvar_field_projection = place.is_upvar_field_projection( - self.mir, &self.tcx); - if let Some(field) = upvar_field_projection { - let decl = &self.mir.upvar_decls[field.index()]; - debug!( - "decl.mutability={:?} local_mutation_is_allowed={:?} place={:?}", - decl, is_local_mutation_allowed, place - ); - match (decl.mutability, is_local_mutation_allowed) { - (Mutability::Not, LocalMutationIsAllowed::No) - | (Mutability::Not, LocalMutationIsAllowed::ExceptUpvars) => { - Err(place) - } - (Mutability::Not, LocalMutationIsAllowed::Yes) - | (Mutability::Mut, _) => { - // Subtle: this is an upvar - // reference, so it looks like - // `self.foo` -- we want to double - // check that the context `*self` - // is mutable (i.e., this is not a - // `Fn` closure). But if that - // check succeeds, we want to - // *blame* the mutability on - // `place` (that is, - // `self.foo`). This is used to - // propagate the info about - // whether mutability declarations - // are used outwards, so that we register - // the outer variable as mutable. Otherwise a - // test like this fails to record the `mut` - // as needed: - // - // ``` - // fn foo(_f: F) { } - // fn main() { - // let var = Vec::new(); - // foo(move || { - // var.push(1); - // }); - // } - // ``` - let _ = self.is_mutable(&proj.base, is_local_mutation_allowed)?; - Ok(RootPlace { + ty::TyRawPtr(tnm) => { + match tnm.mutbl { + // `*const` raw pointers are not mutable + hir::MutImmutable => return Err(place), + // `*mut` raw pointers are always mutable, regardless of + // context. The users have to check by themselves. + hir::MutMutable => { + return Ok(RootPlace { place, is_local_mutation_allowed, - }) + }); } } - } else { - self.is_mutable(&proj.base, is_local_mutation_allowed) } + // `Box` owns its content, so mutable if its location is mutable + _ if base_ty.is_box() => { continue; } + // Deref should only be for reference, pointers or boxes + _ => bug!("Deref of unexpected type: {:?}", base_ty), + } + } + ProjectionElem::Field(..) + | ProjectionElem::Index(..) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::Downcast(..) => { + let upvar_field_projection = place.is_upvar_field_projection( + self.mir, &self.tcx); + if let Some(field) = upvar_field_projection { + let decl = &self.mir.upvar_decls[field.index()]; + debug!( + "decl.mutability={:?} local_mutation_is_allowed={:?} place={:?}", + decl, is_local_mutation_allowed, place + ); + match (decl.mutability, is_local_mutation_allowed) { + (Mutability::Not, LocalMutationIsAllowed::No) + | (Mutability::Not, LocalMutationIsAllowed::ExceptUpvars) => { + Err(place) + } + (Mutability::Not, LocalMutationIsAllowed::Yes) + | (Mutability::Mut, _) => { + // Subtle: this is an upvar reference, so it looks like + // `self.foo` -- we want to double check that the context + // `*self` is mutable (i.e., this is not a `Fn` closure). + // But if that check succeeds, we want to *blame* the mutability + // on `place` (that is, `self.foo`). This is used to propagate + // the info about whether mutability declarations are used + // outwards, so that we register the outer variable as mutable. + // Otherwise a test like this fails to + // record the `mut` as needed: + // ``` + // fn foo(_f: F) { } + // fn main() { + // let var = Vec::new(); + // foo(move || { + // var.push(1); + // }); + // } + // ``` + let _ = base_is_mutable(&place.base, is_local_mutation_allowed); + Ok(RootPlace { + place, + is_local_mutation_allowed, + }) + } + } + } else { + base_is_mutable(&place.base, is_local_mutation_allowed) } } } } + + result } } @@ -2221,26 +2212,24 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // moves out of a Box. They should be removed when/if we stop // treating Box specially (e.g. when/if DerefMove is added...) - fn base_path<'d>(&self, place: &'d Place<'tcx>) -> &'d Place<'tcx> { + fn base_path(&self, place: &Place<'tcx>) -> Place<'tcx> { //! Returns the base of the leftmost (deepest) dereference of an //! Box in `place`. If there is no dereference of an Box //! in `place`, then it just returns `place` itself. - let mut cursor = place; - let mut deepest = place; - loop { - let proj = match *cursor { - Place::Promoted(_) | - Place::Local(..) | Place::Static(..) => return deepest, - Place::Projection(ref proj) => proj, - }; - if proj.elem == ProjectionElem::Deref - && place.ty(self.mir, self.tcx).to_ty(self.tcx).is_box() - { - deepest = &proj.base; + let mut deepest = place.clone(); + + for (i, elem) in place.elems.iter().cloned().enumerate().rev() { + match elem { + ProjectionElem::Deref + if place.ty(self.mir, self.tcx).to_ty(self.tcx).is_box() => { + deepest = place.elem_base(self.tcx, i); + }, + _ => {}, } - cursor = &proj.base; } + + deepest } } diff --git a/src/librustc_mir/borrow_check/move_errors.rs b/src/librustc_mir/borrow_check/move_errors.rs index 4d988fef450b8..8e31e73d1530b 100644 --- a/src/librustc_mir/borrow_check/move_errors.rs +++ b/src/librustc_mir/borrow_check/move_errors.rs @@ -92,37 +92,42 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { // If that ever stops being the case, then the ever initialized // flow could be used. if let Some(StatementKind::Assign( - Place::Local(local), + Place { + base: PlaceBase::Local(local), + elems, + }, Rvalue::Use(Operand::Move(move_from)), )) = self.mir.basic_blocks()[location.block] .statements .get(location.statement_index) .map(|stmt| &stmt.kind) { - let local_decl = &self.mir.local_decls[*local]; - // opt_match_place is the - // match_span is the span of the expression being matched on - // match *x.y { ... } match_place is Some(*x.y) - // ^^^^ match_span is the span of *x.y - // - // opt_match_place is None for let [mut] x = ... statements, - // whether or not the right-hand side is a place expression - if let Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { - opt_match_place: Some((ref opt_match_place, match_span)), - binding_mode: _, - opt_ty_info: _, - }))) = local_decl.is_user_variable - { - self.append_binding_error( - grouped_errors, - kind, - move_from, - *local, - opt_match_place, - match_span, - stmt_source_info.span, - ); - return; + if elems.is_empty() { + let local_decl = &self.mir.local_decls[*local]; + // opt_match_place is the + // match_span is the span of the expression being matched on + // match *x.y { ... } match_place is Some(*x.y) + // ^^^^ match_span is the span of *x.y + // + // opt_match_place is None for let [mut] x = ... statements, + // whether or not the right-hand side is a place expression + if let Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { + opt_match_place: Some((ref opt_match_place, match_span)), + binding_mode: _, + opt_ty_info: _, + }))) = local_decl.is_user_variable + { + self.append_binding_error( + grouped_errors, + kind, + move_from, + *local, + opt_match_place, + match_span, + stmt_source_info.span, + ); + return; + } } } grouped_errors.push(GroupedMoveError::OtherIllegalMove { @@ -237,16 +242,8 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { .cannot_move_out_of_interior_noncopy(span, ty, None, origin), ty::TyClosure(def_id, closure_substs) if !self.mir.upvar_decls.is_empty() - && { - match place { - Place::Projection(ref proj) => { - proj.base == Place::Local(Local::new(1)) - } - Place::Promoted(_) | - Place::Local(_) | Place::Static(_) => unreachable!(), - } - } => - { + && PlaceBase::Local(Local::new(1)) == place.base + => { let closure_kind_ty = closure_substs.closure_kind_ty(def_id, self.tcx); let closure_kind = closure_kind_ty.to_opt_closure_kind(); @@ -300,23 +297,20 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { // Ok to suggest a borrow, since the target can't be moved from // anyway. if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) { - match move_from { - Place::Projection(ref proj) - if self.suitable_to_remove_deref(proj, &snippet) => - { + if let (base_place, Some(proj)) = move_from.split_projection(self.tcx) { + if self.suitable_to_remove_deref(&base_place, proj, &snippet) { err.span_suggestion( span, "consider removing this dereference operator", (&snippet[1..]).to_owned(), ); } - _ => { - err.span_suggestion( - span, - "consider using a reference instead", - format!("&{}", snippet), - ); - } + } else { + err.span_suggestion( + span, + "consider using a reference instead", + format!("&{}", snippet), + ); } binds_to.sort(); @@ -377,25 +371,35 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { } } - fn suitable_to_remove_deref(&self, proj: &PlaceProjection<'tcx>, snippet: &str) -> bool { + fn suitable_to_remove_deref( + &self, + place: &Place<'tcx>, + proj: &PlaceElem<'tcx>, + snippet: &str, + ) -> bool { let is_shared_ref = |ty: ty::Ty| match ty.sty { ty::TypeVariants::TyRef(.., hir::Mutability::MutImmutable) => true, _ => false, }; - proj.elem == ProjectionElem::Deref && snippet.starts_with('*') && match proj.base { - Place::Local(local) => { - let local_decl = &self.mir.local_decls[local]; - // If this is a temporary, then this could be from an - // overloaded * operator. - local_decl.is_user_variable.is_some() && is_shared_ref(local_decl.ty) + *proj == ProjectionElem::Deref && snippet.starts_with('*') + && if let Some(projection) = place.elems.last() { + if let ProjectionElem::Field(_, ty) = projection { + is_shared_ref(ty) + } else { + false + } + } else { + match place.base { + PlaceBase::Local(local) => { + let local_decl = &self.mir.local_decls[local]; + // If this is a temporary, then this could be from an + // overloaded * operator. + local_decl.is_user_variable.is_some() && is_shared_ref(local_decl.ty) + } + PlaceBase::Promoted(_) => true, + PlaceBase::Static(ref st) => is_shared_ref(st.ty), } - Place::Promoted(_) => true, - Place::Static(ref st) => is_shared_ref(st.ty), - Place::Projection(ref proj) => match proj.elem { - ProjectionElem::Field(_, ty) => is_shared_ref(ty), - _ => false, - }, } } } diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs index e8862320ddf3f..97bd8c982cb92 100644 --- a/src/librustc_mir/borrow_check/mutability_errors.rs +++ b/src/librustc_mir/borrow_check/mutability_errors.rs @@ -10,7 +10,7 @@ use rustc::hir; use rustc::mir::{self, BindingForm, ClearCrossCrate, Local, Location, Mir}; -use rustc::mir::{Mutability, Place, Projection, ProjectionElem, Static}; +use rustc::mir::{Mutability, Place, PlaceBase, ProjectionElem, Static}; use rustc::ty::{self, TyCtxt}; use rustc_data_structures::indexed_vec::Idx; use syntax_pos::Span; @@ -41,118 +41,108 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { let reason; let access_place_desc = self.describe_place(access_place); - match the_place_err { - Place::Local(local) => { - item_msg = format!("`{}`", access_place_desc.unwrap()); - if let Place::Local(_) = access_place { - reason = ", as it is not declared as mutable".to_string(); - } else { - let name = self.mir.local_decls[*local] - .name - .expect("immutable unnamed local"); - reason = format!(", as `{}` is not declared as mutable", name); - } - } - - Place::Projection(box Projection { - base, - elem: ProjectionElem::Field(upvar_index, _), - }) => { - debug_assert!(is_closure_or_generator( - base.ty(self.mir, self.tcx).to_ty(self.tcx) - )); - - item_msg = format!("`{}`", access_place_desc.unwrap()); - if self.is_upvar(access_place) { - reason = ", as it is not declared as mutable".to_string(); - } else { - let name = self.mir.upvar_decls[upvar_index.index()].debug_name; - reason = format!(", as `{}` is not declared as mutable", name); - } - } - - Place::Projection(box Projection { - base, - elem: ProjectionElem::Deref, - }) => { - if *base == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty() { - item_msg = format!("`{}`", access_place_desc.unwrap()); - debug_assert!(self.mir.local_decls[Local::new(1)].ty.is_region_ptr()); - debug_assert!(is_closure_or_generator( - the_place_err.ty(self.mir, self.tcx).to_ty(self.tcx) - )); - - reason = if self.is_upvar(access_place) { - ", as it is a captured variable in a `Fn` closure".to_string() - } else { - ", as `Fn` closures cannot mutate their captured variables".to_string() - } - } else if { - if let Place::Local(local) = *base { - if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) - = self.mir.local_decls[local].is_user_variable { + if let (base_place, Some(projection)) = the_place_err.split_projection(self.tcx) { + match projection { + ProjectionElem::Deref => { + if base_place.base == PlaceBase::Local(Local::new(1)) + && !self.mir.upvar_decls.is_empty() + { + item_msg = format!("`{}`", access_place_desc.unwrap()); + debug_assert!(self.mir.local_decls[Local::new(1)].ty.is_region_ptr()); + debug_assert!(is_closure_or_generator( + the_place_err.ty(self.mir, self.tcx).to_ty(self.tcx) + )); + + reason = if self.is_upvar(access_place) { + ", as it is a captured variable in a `Fn` closure".to_string() + } else { + ", as `Fn` closures cannot mutate their captured variables".to_string() + } + } else if { + if let PlaceBase::Local(local) = base_place.base { + if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) = + self.mir.local_decls[local].is_user_variable + { true + } else { + false + } } else { false } + } { + item_msg = format!("`{}`", access_place_desc.unwrap()); + reason = ", as it is immutable for the pattern guard".to_string(); } else { - false - } - } { - item_msg = format!("`{}`", access_place_desc.unwrap()); - reason = ", as it is immutable for the pattern guard".to_string(); - } else { - let pointer_type = - if base.ty(self.mir, self.tcx).to_ty(self.tcx).is_region_ptr() { + let pointer_type = if base_place + .ty(self.mir, self.tcx) + .to_ty(self.tcx) + .is_region_ptr() + { "`&` reference" } else { "`*const` pointer" }; - if let Some(desc) = access_place_desc { - item_msg = format!("`{}`", desc); - reason = match error_access { - AccessKind::Move | - AccessKind::Mutate => format!(" which is behind a {}", pointer_type), - AccessKind::MutableBorrow => { - format!(", as it is behind a {}", pointer_type) + if let Some(desc) = access_place_desc { + item_msg = format!("`{}`", desc); + reason = match error_access { + AccessKind::Move | AccessKind::Mutate => { + format!(" which is behind a {}", pointer_type) + } + AccessKind::MutableBorrow => { + format!(", as it is behind a {}", pointer_type) + } } + } else { + item_msg = format!("data in a {}", pointer_type); + reason = "".to_string(); } + } + } + ProjectionElem::Field(upvar_index, _) => { + debug_assert!(is_closure_or_generator( + base_place.ty(self.mir, self.tcx).to_ty(self.tcx) + )); + + item_msg = format!("`{}`", access_place_desc.unwrap()); + if self.is_upvar(access_place) { + reason = ", as it is not declared as mutable".to_string(); } else { - item_msg = format!("data in a {}", pointer_type); - reason = "".to_string(); + let name = self.mir.upvar_decls[upvar_index.index()].debug_name; + reason = format!(", as `{}` is not declared as mutable", name); } } + ProjectionElem::Subslice { .. } + | ProjectionElem::Downcast(..) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Index(_) => bug!("Unexpected immutable place."), } - - Place::Promoted(_) => unreachable!(), - - Place::Static(box Static { def_id, ty: _ }) => { - if let Place::Static(_) = access_place { - item_msg = format!("immutable static item `{}`", access_place_desc.unwrap()); - reason = "".to_string(); - } else { + } else { + match the_place_err.base { + PlaceBase::Local(local) => { item_msg = format!("`{}`", access_place_desc.unwrap()); - let static_name = &self.tcx.item_name(*def_id); - reason = format!(", as `{}` is an immutable static item", static_name); + if let PlaceBase::Local(_) = access_place.base { + reason = ", as it is not declared as mutable".to_string(); + } else { + let name = self.mir.local_decls[local] + .name + .expect("immutable unnamed local"); + reason = format!(", as `{}` is not declared as mutable", name); + } + } + PlaceBase::Static(box Static { def_id, ty: _ }) => { + if let PlaceBase::Static(_) = access_place.base { + item_msg = + format!("immutable static item `{}`", access_place_desc.unwrap()); + reason = "".to_string(); + } else { + item_msg = format!("`{}`", access_place_desc.unwrap()); + let static_name = &self.tcx.item_name(def_id); + reason = format!(", as `{}` is an immutable static item", static_name); + } } + PlaceBase::Promoted(_) => unreachable!(), } - - Place::Projection(box Projection { - base: _, - elem: ProjectionElem::Index(_), - }) - | Place::Projection(box Projection { - base: _, - elem: ProjectionElem::ConstantIndex { .. }, - }) - | Place::Projection(box Projection { - base: _, - elem: ProjectionElem::Subslice { .. }, - }) - | Place::Projection(box Projection { - base: _, - elem: ProjectionElem::Downcast(..), - }) => bug!("Unexpected immutable place."), } // `act` and `acted_on` are strings that let us abstract over @@ -162,14 +152,16 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { let span = match error_access { AccessKind::Move => { - err = self.tcx + err = self + .tcx .cannot_move_out_of(span, &(item_msg + &reason), Origin::Mir); act = "move"; acted_on = "moved"; span } AccessKind::Mutate => { - err = self.tcx + err = self + .tcx .cannot_assign(span, &(item_msg + &reason), Origin::Mir); act = "assign"; acted_on = "written"; @@ -193,21 +185,158 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { "mutable borrow occurs due to use of `{}` in closure", // always Some() if the message is printed. self.describe_place(access_place).unwrap_or(String::new()), - ) + ), ); borrow_span } }; - match the_place_err { - // We want to suggest users use `let mut` for local (user - // variable) mutations... - Place::Local(local) if self.mir.local_decls[*local].can_be_made_mutable() => { - // ... but it doesn't make sense to suggest it on - // variables that are `ref x`, `ref mut x`, `&self`, + if let (base_place, Some(projection)) = the_place_err.split_projection(self.tcx) { + match projection { + ProjectionElem::Deref => { + if let PlaceBase::Local(local) = base_place.base { + if base_place.has_no_projection() { + match self.mir.local_decls[local].is_user_variable { + Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) => { + err.span_label(span, format!("cannot {ACT}", ACT = act)); + err.note( + "variables bound in patterns are \ + immutable until the end of the pattern guard", + ); + } + Some(_) => { + // We want to point out when a `&` can be readily replaced + // with an `&mut`. + // + // FIXME: can this case be generalized to work for an + // arbitrary base for the projection? + let local_decl = &self.mir.local_decls[local]; + let suggestion = match local_decl.is_user_variable.as_ref().unwrap() + { + ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf) => { + Some(suggest_ampmut_self(self.tcx, local_decl)) + } + + ClearCrossCrate::Set(mir::BindingForm::Var( + mir::VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(_), + opt_ty_info, + .. + }, + )) => Some(suggest_ampmut( + self.tcx, + self.mir, + local, + local_decl, + *opt_ty_info, + )), + + ClearCrossCrate::Set(mir::BindingForm::Var( + mir::VarBindingForm { + binding_mode: ty::BindingMode::BindByReference(_), + .. + }, + )) => suggest_ref_mut(self.tcx, local_decl.source_info.span), + + ClearCrossCrate::Set(mir::BindingForm::RefForGuard) => { + unreachable!() + } + + ClearCrossCrate::Clear => bug!("saw cleared local state"), + }; + + let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() + { + ("&", "reference") + } else { + ("*const", "pointer") + }; + + if let Some((err_help_span, suggested_code)) = suggestion { + err.span_suggestion( + err_help_span, + &format!( + "consider changing this to be a mutable {}", + pointer_desc + ), + suggested_code, + ); + } + + if let Some(name) = local_decl.name { + err.span_label( + span, + format!( + "`{NAME}` is a `{SIGIL}` {DESC}, \ + so the data it refers to cannot be {ACTED_ON}", + NAME = name, + SIGIL = pointer_sigil, + DESC = pointer_desc, + ACTED_ON = acted_on + ), + ); + } else { + err.span_label( + span, + format!( + "cannot {ACT} through `{SIGIL}` {DESC}", + ACT = act, + SIGIL = pointer_sigil, + DESC = pointer_desc + ), + ); + } + } + _ => {} + } + } + if local == Local::new(1) && !self.mir.upvar_decls.is_empty() { + err.span_label(span, format!("cannot {ACT}", ACT = act)); + err.span_help( + self.mir.span, + "consider changing this to accept closures that implement `FnMut`", + ); + } + } + } + ProjectionElem::Field(upvar_index, _) => { + // Suggest adding mut for upvars + debug_assert!(is_closure_or_generator( + base_place.ty(self.mir, self.tcx).to_ty(self.tcx) + )); + + err.span_label(span, format!("cannot {ACT}", ACT = act)); + + let upvar_hir_id = self.mir.upvar_decls[upvar_index.index()] + .var_hir_id + .assert_crate_local(); + let upvar_node_id = self.tcx.hir.hir_to_node_id(upvar_hir_id); + if let Some(hir::map::NodeBinding(pat)) = self.tcx.hir.find(upvar_node_id) { + if let hir::PatKind::Binding( + hir::BindingAnnotation::Unannotated, + _, + upvar_ident, + _, + ) = pat.node + { + err.span_suggestion( + upvar_ident.span, + "consider changing this to be mutable", + format!("mut {}", upvar_ident.name), + ); + } + } + } + _ => {} + } + } else if let PlaceBase::Local(local) = the_place_err.base { + if self.mir.local_decls[local].can_be_made_mutable() { + // We want to suggest users use `let mut` for local (user + // variable) mutations, but it doesn't make sense to suggest + // it on variables that are `ref x`, `ref mut x`, `&self`, // or `&mut self` (such variables are simply not // mutable). - let local_decl = &self.mir.local_decls[*local]; + let local_decl = &self.mir.local_decls[local]; assert_eq!(local_decl.mutability, Mutability::Not); err.span_label(span, format!("cannot {ACT}", ACT = act)); @@ -216,195 +345,46 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { "consider changing this to be mutable", format!("mut {}", local_decl.name.unwrap()), ); - } - - // Also suggest adding mut for upvars - Place::Projection(box Projection { - base, - elem: ProjectionElem::Field(upvar_index, _), - }) => { - debug_assert!(is_closure_or_generator( - base.ty(self.mir, self.tcx).to_ty(self.tcx) - )); - - err.span_label(span, format!("cannot {ACT}", ACT = act)); - - let upvar_hir_id = self.mir.upvar_decls[upvar_index.index()] - .var_hir_id - .assert_crate_local(); - let upvar_node_id = self.tcx.hir.hir_to_node_id(upvar_hir_id); - if let Some(hir::map::NodeBinding(pat)) = self.tcx.hir.find(upvar_node_id) { - if let hir::PatKind::Binding( - hir::BindingAnnotation::Unannotated, - _, - upvar_ident, - _, - ) = pat.node - { - err.span_suggestion( - upvar_ident.span, - "consider changing this to be mutable", - format!("mut {}", upvar_ident.name), - ); - } - } - } - - // complete hack to approximate old AST-borrowck - // diagnostic: if the span starts with a mutable borrow of - // a local variable, then just suggest the user remove it. - Place::Local(_) - if { - if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) { - snippet.starts_with("&mut ") - } else { - false - } - } => - { - err.span_label(span, format!("cannot {ACT}", ACT = act)); - err.span_label(span, "try removing `&mut` here"); - } - - Place::Projection(box Projection { - base: Place::Local(local), - elem: ProjectionElem::Deref, - }) if { - if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) = - self.mir.local_decls[*local].is_user_variable - { - true - } else { - false - } - } => - { - err.span_label(span, format!("cannot {ACT}", ACT = act)); - err.note( - "variables bound in patterns are immutable until the end of the pattern guard", - ); - } - - // We want to point out when a `&` can be readily replaced - // with an `&mut`. - // - // FIXME: can this case be generalized to work for an - // arbitrary base for the projection? - Place::Projection(box Projection { - base: Place::Local(local), - elem: ProjectionElem::Deref, - }) if self.mir.local_decls[*local].is_user_variable.is_some() => - { - let local_decl = &self.mir.local_decls[*local]; - let suggestion = match local_decl.is_user_variable.as_ref().unwrap() { - ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf) => { - Some(suggest_ampmut_self(self.tcx, local_decl)) + } else { + // complete hack to approximate old AST-borrowck + // diagnostic: if the span starts with a mutable borrow of + // a local variable, then just suggest the user remove it. + if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) { + if snippet.starts_with("&mut ") { + err.span_label(span, format!("cannot {ACT}", ACT = act)); + err.span_label(span, "try removing `&mut` here"); } - - ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), - opt_ty_info, - .. - })) => Some(suggest_ampmut( - self.tcx, - self.mir, - *local, - local_decl, - *opt_ty_info, - )), - - ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByReference(_), - .. - })) => suggest_ref_mut(self.tcx, local_decl.source_info.span), - - // - ClearCrossCrate::Set(mir::BindingForm::RefForGuard) => unreachable!(), - - ClearCrossCrate::Clear => bug!("saw cleared local state"), - }; - - let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() { - ("&", "reference") - } else { - ("*const", "pointer") - }; - - if let Some((err_help_span, suggested_code)) = suggestion { - err.span_suggestion( - err_help_span, - &format!("consider changing this to be a mutable {}", pointer_desc), - suggested_code, - ); - } - - if let Some(name) = local_decl.name { - err.span_label( - span, - format!( - "`{NAME}` is a `{SIGIL}` {DESC}, \ - so the data it refers to cannot be {ACTED_ON}", - NAME = name, - SIGIL = pointer_sigil, - DESC = pointer_desc, - ACTED_ON = acted_on - ), - ); - } else { - err.span_label( - span, - format!( - "cannot {ACT} through `{SIGIL}` {DESC}", - ACT = act, - SIGIL = pointer_sigil, - DESC = pointer_desc - ), - ); } } - - Place::Projection(box Projection { - base, - elem: ProjectionElem::Deref, - }) if *base == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty() => - { - err.span_label(span, format!("cannot {ACT}", ACT = act)); - err.span_help( - self.mir.span, - "consider changing this to accept closures that implement `FnMut`" - ); - } - - _ => { - err.span_label(span, format!("cannot {ACT}", ACT = act)); - } + } else { + err.span_label(span, format!("cannot {ACT}", ACT = act)); } - err.buffer(&mut self.errors_buffer); } // Does this place refer to what the user sees as an upvar fn is_upvar(&self, place: &Place<'tcx>) -> bool { - match *place { - Place::Projection(box Projection { - ref base, - elem: ProjectionElem::Field(_, _), - }) => { - let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx); - is_closure_or_generator(base_ty) - } - Place::Projection(box Projection { - base: - Place::Projection(box Projection { - ref base, - elem: ProjectionElem::Field(upvar_index, _), - }), - elem: ProjectionElem::Deref, - }) => { - let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx); - is_closure_or_generator(base_ty) && self.mir.upvar_decls[upvar_index.index()].by_ref + if let (base_place, Some(projection)) = place.split_projection(self.tcx) { + match projection { + ProjectionElem::Field(_, _) => { + let base_ty = base_place.ty(self.mir, self.tcx).to_ty(self.tcx); + is_closure_or_generator(base_ty) + } + ProjectionElem::Deref => { + if let (ref base_place, Some(ProjectionElem::Field(upvar_index, _))) = + base_place.split_projection(self.tcx) + { + let base_ty = base_place.ty(self.mir, self.tcx).to_ty(self.tcx); + is_closure_or_generator(base_ty) + && self.mir.upvar_decls[upvar_index.index()].by_ref + } else { + false + } + } + _ => false, } - _ => false, + } else { + false } } } @@ -414,17 +394,20 @@ fn suggest_ampmut_self<'cx, 'gcx, 'tcx>( local_decl: &mir::LocalDecl<'tcx>, ) -> (Span, String) { let sp = local_decl.source_info.span; - (sp, match tcx.sess.codemap().span_to_snippet(sp) { - Ok(snippet) => { - let lt_pos = snippet.find('\''); - if let Some(lt_pos) = lt_pos { - format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4]) - } else { - "&mut self".to_string() + ( + sp, + match tcx.sess.codemap().span_to_snippet(sp) { + Ok(snippet) => { + let lt_pos = snippet.find('\''); + if let Some(lt_pos) = lt_pos { + format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4]) + } else { + "&mut self".to_string() + } } - } - _ => "&mut self".to_string() - }) + _ => "&mut self".to_string(), + }, + ) } // When we want to suggest a user change a local variable to be a `&mut`, there @@ -490,12 +473,14 @@ fn suggest_ampmut<'cx, 'gcx, 'tcx>( let ty_mut = local_decl.ty.builtin_deref(true).unwrap(); assert_eq!(ty_mut.mutbl, hir::MutImmutable); - (highlight_span, - if local_decl.ty.is_region_ptr() { - format!("&mut {}", ty_mut.ty) - } else { - format!("*mut {}", ty_mut.ty) - }) + ( + highlight_span, + if local_decl.ty.is_region_ptr() { + format!("&mut {}", ty_mut.ty) + } else { + format!("*mut {}", ty_mut.ty) + }, + ) } fn is_closure_or_generator(ty: ty::Ty) -> bool { diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 457499ded5657..299ab7e9ffa3f 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -16,7 +16,7 @@ use borrow_check::nll::region_infer::values::LivenessValues; use rustc::infer::InferCtxt; use rustc::mir::visit::TyContext; use rustc::mir::visit::Visitor; -use rustc::mir::{BasicBlock, BasicBlockData, Location, Mir, Place, Rvalue}; +use rustc::mir::{BasicBlock, BasicBlockData, Location, Mir, Place, PlaceBase, Rvalue}; use rustc::mir::{Local, Statement, Terminator}; use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::Substs; @@ -138,11 +138,13 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx // When we see `X = ...`, then kill borrows of // `(*X).foo` and so forth. if let Some(all_facts) = self.all_facts { - if let Place::Local(temp) = place { - if let Some(borrow_indices) = self.borrow_set.local_map.get(temp) { - for &borrow_index in borrow_indices { - let location_index = self.location_table.mid_index(location); - all_facts.killed.push((borrow_index, location_index)); + if let PlaceBase::Local(temp) = place.base { + if place.has_no_projection() { + if let Some(borrow_indices) = self.borrow_set.local_map.get(&temp) { + for &borrow_index in borrow_indices { + let location_index = self.location_table.mid_index(location); + all_facts.killed.push((borrow_index, location_index)); + } } } } diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs index 5098b24adc367..de6c3c81cdb8d 100644 --- a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs +++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs @@ -11,7 +11,7 @@ use borrow_check::borrow_set::BorrowData; use borrow_check::nll::region_infer::Cause; use borrow_check::{Context, MirBorrowckCtxt, WriteKind}; -use rustc::mir::{Location, Place, TerminatorKind}; +use rustc::mir::{Location, Place, PlaceBase, TerminatorKind}; use rustc_errors::DiagnosticBuilder; mod find_use; @@ -64,7 +64,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { match find_use::find(mir, regioncx, tcx, region_sub, context.loc) { Some(Cause::LiveVar(local, location)) => { let span = mir.source_info(location).span; - let spans = self.move_spans(&Place::Local(local), location) + let spans = self.move_spans(&Place::local(local), location) .or_else(|| self.borrow_spans(span, location)); let message = if self.is_borrow_location_in_loop(context.loc) { if spans.for_closure() { @@ -90,16 +90,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ); if let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place { - if let Place::Local(borrowed_local) = place { - let dropped_local_scope = mir.local_decls[local].visibility_scope; - let borrowed_local_scope = - mir.local_decls[*borrowed_local].visibility_scope; - - if mir.is_sub_scope(borrowed_local_scope, dropped_local_scope) { - err.note( - "values in a scope are dropped \ - in the opposite order they are defined", - ); + if let PlaceBase::Local(borrowed_local) = place.base { + if place.has_no_projection() { + let dropped_local_scope = mir.local_decls[local].visibility_scope; + let borrowed_local_scope = + mir.local_decls[borrowed_local].visibility_scope; + + if mir.is_sub_scope(borrowed_local_scope, dropped_local_scope) { + err.note( + "values in a scope are dropped \ + in the opposite order they are defined", + ); + } } } } diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs index 301999cc4a51e..a76fe20aa48be 100644 --- a/src/librustc_mir/borrow_check/nll/invalidation.rs +++ b/src/librustc_mir/borrow_check/nll/invalidation.rs @@ -23,7 +23,7 @@ use dataflow::move_paths::indexes::BorrowIndex; use rustc::hir::def_id::DefId; use rustc::infer::InferCtxt; use rustc::mir::visit::Visitor; -use rustc::mir::{BasicBlock, Location, Mir, Place, Rvalue, Local}; +use rustc::mir::{BasicBlock, Location, Mir, Place, PlaceBase, Rvalue, Local}; use rustc::mir::{Statement, StatementKind}; use rustc::mir::{Terminator, TerminatorKind}; use rustc::mir::{Field, Operand, BorrowKind}; @@ -153,7 +153,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tc StatementKind::StorageDead(local) => { self.access_place( ContextKind::StorageDead.new(location), - &Place::Local(local), + &Place::local(local), (Shallow(None), Write(WriteKind::StorageDeadOrDrop)), LocalMutationIsAllowed::Yes, ); @@ -295,12 +295,13 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> { erased_drop_place_ty: ty::Ty<'gcx>, ) { let gcx = self.infcx.tcx.global_tcx(); + let tcx = self.infcx.tcx; let drop_field = | ig: &mut InvalidationGenerator<'cg, 'cx, 'gcx, 'tcx>, (index, field): (usize, ty::Ty<'gcx>), | { let field_ty = gcx.normalize_erasing_regions(ig.param_env, field); - let place = drop_place.clone().field(Field::new(index), field_ty); + let place = drop_place.clone().field(tcx, Field::new(index), field_ty); ig.visit_terminator_drop(loc, term, &place, field_ty); }; @@ -326,13 +327,17 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> { // Closures and generators also have disjoint fields, but they are only // directly accessed in the body of the closure/generator. ty::TyGenerator(def, substs, ..) - if *drop_place == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty() + if drop_place.base == PlaceBase::Local(Local::new(1)) + && drop_place.has_no_projection() + && !self.mir.upvar_decls.is_empty() => { substs.upvar_tys(def, self.infcx.tcx).enumerate() .for_each(|field| drop_field(self, field)); } ty::TyClosure(def, substs) - if *drop_place == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty() + if drop_place.base == PlaceBase::Local(Local::new(1)) + && drop_place.has_no_projection() + && !self.mir.upvar_decls.is_empty() => { substs.upvar_tys(def, self.infcx.tcx).enumerate() .for_each(|field| drop_field(self, field)); diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index c2670389e2d69..f723d05e83d8b 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -13,7 +13,7 @@ use borrow_check::nll::type_check::Locations; use rustc::hir::def_id::DefId; use rustc::infer::error_reporting::nice_region_error::NiceRegionError; use rustc::infer::InferCtxt; -use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind}; +use rustc::mir::{self, Location, Mir, PlaceBase, Rvalue, StatementKind, TerminatorKind}; use rustc::ty::RegionVid; use rustc_data_structures::indexed_vec::IndexVec; use rustc_errors::Diagnostic; @@ -266,7 +266,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { match statement.kind { StatementKind::Assign(ref place, ref rvalue) => { debug!("classify_constraint: place={:?} rvalue={:?}", place, rvalue); - if *place == Place::Local(mir::RETURN_PLACE) { + if place.base == PlaceBase::Local(mir::RETURN_PLACE) && + place.has_no_projection() { ConstraintCategory::Return } else { match rvalue { diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index a18e2368bf724..89ae391830ea2 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -365,14 +365,14 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { &mut self, place: &Place<'tcx>, location: Location, - context: PlaceContext, + mut context: PlaceContext, ) -> PlaceTy<'tcx> { debug!("sanitize_place: {:?}", place); - let place_ty = match *place { - Place::Local(index) => PlaceTy::Ty { - ty: self.mir.local_decls[index].ty, + let mut place_ty = match place.base { + PlaceBase::Local(index) => PlaceTy::Ty { + ty: self.mir.local_decls[index].ty }, - Place::Promoted(box (_index, sty)) => { + PlaceBase::Promoted(box (_index, sty)) => { let sty = self.sanitize_type(place, sty); // FIXME -- promoted MIR return types reference // various "free regions" (e.g., scopes and things) @@ -387,7 +387,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { // promoted_mir.return_ty() PlaceTy::Ty { ty: sty } } - Place::Static(box Static { def_id, ty: sty }) => { + PlaceBase::Static(box Static { def_id, ty: sty }) => { let sty = self.sanitize_type(place, sty); let ty = self.tcx().type_of(def_id); let ty = self.cx.normalize(ty, location); @@ -401,26 +401,28 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { terr ); } + PlaceTy::Ty { ty: sty } } - Place::Projection(ref proj) => { - let base_context = if context.is_mutating_use() { - PlaceContext::Projection(Mutability::Mut) - } else { - PlaceContext::Projection(Mutability::Not) - }; - let base_ty = self.sanitize_place(&proj.base, location, base_context); - if let PlaceTy::Ty { ty } = base_ty { - if ty.references_error() { - assert!(self.errors_reported); - return PlaceTy::Ty { - ty: self.tcx().types.err, - }; - } + }; + + for elem in place.elems.iter(){ + context = if context.is_mutating_use() { + PlaceContext::Projection(Mutability::Mut) + } else { + PlaceContext::Projection(Mutability::Not) + }; + if let PlaceTy::Ty { ty } = place_ty { + if ty.references_error() { + assert!(self.errors_reported); + return PlaceTy::Ty { + ty: self.tcx().types.err, + }; } - self.sanitize_projection(base_ty, &proj.elem, place, location) } - }; + place_ty = self.sanitize_projection(place_ty, elem, place, location); + } + if let PlaceContext::Copy = context { let tcx = self.tcx(); let trait_ref = ty::TraitRef { @@ -437,20 +439,21 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { // Copy, then it must. self.cx.prove_trait_ref(trait_ref, location.interesting()); } + place_ty } fn sanitize_projection( &mut self, base: PlaceTy<'tcx>, - pi: &PlaceElem<'tcx>, + proj: &PlaceElem<'tcx>, place: &Place<'tcx>, location: Location, ) -> PlaceTy<'tcx> { - debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place); + debug!("sanitize_projection: {:?} {:?} {:?}", base, proj, place); let tcx = self.tcx(); let base_ty = base.to_ty(tcx); - match *pi { + match *proj { ProjectionElem::Deref => { let deref_ty = base_ty.builtin_deref(true); PlaceTy::Ty { @@ -460,7 +463,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { } } ProjectionElem::Index(i) => { - let index_ty = Place::Local(i).ty(self.mir, tcx).to_ty(tcx); + let index_ty = Place::local(i).ty(self.mir, tcx).to_ty(tcx); if index_ty != tcx.types.usize { PlaceTy::Ty { ty: span_mirbug_and_err!(self, i, "index by non-usize {:?}", i), @@ -870,11 +873,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // they are not caused by the user, but rather artifacts // of lowering. Assignments to other sorts of places *are* interesting // though. - let is_temp = if let Place::Local(l) = place { - !mir.local_decls[*l].is_user_variable.is_some() - } else { - false - }; + let mut is_temp = false; + if let PlaceBase::Local(l) = place.base { + if place.has_no_projection() { + is_temp = !mir.local_decls[l].is_user_variable.is_some(); + } + } let locations = if is_temp { location.boring() @@ -1591,19 +1595,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // *p`, where the `p` has type `&'b mut Foo`, for example, we // need to ensure that `'b: 'a`. - let mut borrowed_place = borrowed_place; - debug!( "add_reborrow_constraint({:?}, {:?}, {:?})", location, borrow_region, borrowed_place ); - while let Place::Projection(box PlaceProjection { base, elem }) = borrowed_place { - debug!("add_reborrow_constraint - iteration {:?}", borrowed_place); - match *elem { + let tcx = self.infcx.tcx; + let mut base_ty = borrowed_place.base.ty(self.mir); + for elem in borrowed_place.elems.iter() { + debug!("add_reborrow_constraint - iteration {:?}", borrowed_place); + match elem { ProjectionElem::Deref => { - let tcx = self.infcx.tcx; - let base_ty = base.ty(self.mir, tcx).to_ty(tcx); debug!("add_reborrow_constraint - base_ty = {:?}", base_ty); match base_ty.sty { @@ -1672,10 +1674,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // other field access } } - - // The "propagate" case. We need to check that our base is valid - // for the borrow's lifetime. - borrowed_place = base; + base_ty = PlaceTy::from(base_ty) + .projection_ty(tcx, elem).to_ty(tcx); } } diff --git a/src/librustc_mir/borrow_check/path_utils.rs b/src/librustc_mir/borrow_check/path_utils.rs index 70b4e0ea2f03c..3f01f390232d7 100644 --- a/src/librustc_mir/borrow_check/path_utils.rs +++ b/src/librustc_mir/borrow_check/path_utils.rs @@ -13,7 +13,7 @@ use borrow_check::places_conflict; use borrow_check::Context; use borrow_check::ShallowOrDeep; use dataflow::indexes::BorrowIndex; -use rustc::mir::{BasicBlock, Location, Mir, Place}; +use rustc::mir::{BasicBlock, Location, Mir, Place, PlaceBase}; use rustc::mir::{ProjectionElem, BorrowKind}; use rustc::ty::TyCtxt; use rustc_data_structures::graph::dominators::Dominators; @@ -138,24 +138,20 @@ pub(super) fn is_active<'tcx>( /// Determines if a given borrow is borrowing local data /// This is called for all Yield statements on movable generators -pub(super) fn borrow_of_local_data<'tcx>(place: &Place<'tcx>) -> bool { - match place { - Place::Promoted(_) | - Place::Static(..) => false, - Place::Local(..) => true, - Place::Projection(box proj) => { - match proj.elem { - // Reborrow of already borrowed data is ignored - // Any errors will be caught on the initial borrow - ProjectionElem::Deref => false, +pub(super) fn borrow_of_local_data<'tcx>( + place: &Place<'tcx> +) -> bool { + let mut borrow_of_data = match place.base { + PlaceBase::Promoted(_) + | PlaceBase::Static(..) => false, + PlaceBase::Local(..) => true, + }; - // For interior references and downcasts, find out if the base is local - ProjectionElem::Field(..) - | ProjectionElem::Index(..) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::Downcast(..) => borrow_of_local_data(&proj.base), - } + for elem in place.elems.iter().cloned().rev() { + if elem == ProjectionElem::Deref { + borrow_of_data = false; } } + + borrow_of_data } diff --git a/src/librustc_mir/borrow_check/place_ext.rs b/src/librustc_mir/borrow_check/place_ext.rs index b0517c5e61f2b..3b6dc307114d6 100644 --- a/src/librustc_mir/borrow_check/place_ext.rs +++ b/src/librustc_mir/borrow_check/place_ext.rs @@ -10,7 +10,8 @@ use rustc::hir; use rustc::mir::ProjectionElem; -use rustc::mir::{Local, Mir, Place}; +use rustc::mir::{Local, Mir, Place, PlaceBase}; +use rustc::mir::tcx::PlaceTy; use rustc::ty::{self, TyCtxt}; /// Extension methods for the `Place` type. @@ -25,38 +26,40 @@ crate trait PlaceExt<'tcx> { impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { fn is_unsafe_place(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool { - match self { - Place::Promoted(_) | - Place::Local(_) => false, - Place::Static(static_) => { + let mut is_unsafe_place = match self.base { + PlaceBase::Promoted(_) | + PlaceBase::Local(_) => false, + PlaceBase::Static(ref static_) => { tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable) } - Place::Projection(proj) => match proj.elem { + }; + + let mut base_ty = self.base.ty(mir); + for elem in self.elems.iter() { + is_unsafe_place = match elem { ProjectionElem::Field(..) | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Index(_) => proj.base.is_unsafe_place(tcx, mir), + | ProjectionElem::Index(_) => continue, ProjectionElem::Deref => { - let ty = proj.base.ty(mir, tcx).to_ty(tcx); - match ty.sty { + match base_ty.sty { ty::TyRawPtr(..) => true, - _ => proj.base.is_unsafe_place(tcx, mir), + _ => continue, } } - }, + }; + base_ty = PlaceTy::from(base_ty).projection_ty(tcx, elem).to_ty(tcx); } + + is_unsafe_place } fn root_local(&self) -> Option { - let mut p = self; - loop { - match p { - Place::Projection(pi) => p = &pi.base, - Place::Promoted(_) | - Place::Static(_) => return None, - Place::Local(l) => return Some(*l), - } + match self.base { + PlaceBase::Promoted(_) | + PlaceBase::Static(_) => return None, + PlaceBase::Local(local) => return Some(local), } } } diff --git a/src/librustc_mir/borrow_check/places_conflict.rs b/src/librustc_mir/borrow_check/places_conflict.rs index 640ae31d45b34..72a8f7b02528f 100644 --- a/src/librustc_mir/borrow_check/places_conflict.rs +++ b/src/librustc_mir/borrow_check/places_conflict.rs @@ -12,9 +12,9 @@ use borrow_check::ArtificialField; use borrow_check::Overlap; use borrow_check::{Deep, Shallow, ShallowOrDeep}; use rustc::hir; -use rustc::mir::{Mir, Place}; -use rustc::mir::{Projection, ProjectionElem}; -use rustc::ty::{self, TyCtxt}; +use rustc::mir::tcx::PlaceTy; +use rustc::mir::{Mir, Place, PlaceBase, PlaceElem, ProjectionElem}; +use rustc::ty::{self, Ty, TyCtxt}; use std::cmp::max; pub(super) fn places_conflict<'gcx, 'tcx>( @@ -29,68 +29,310 @@ pub(super) fn places_conflict<'gcx, 'tcx>( borrow_place, access_place, access ); - unroll_place(borrow_place, None, |borrow_components| { - unroll_place(access_place, None, |access_components| { - place_components_conflict(tcx, mir, borrow_components, access_components, access) - }) - }) + match place_base_conflict(tcx, &borrow_place.base, &access_place.base) { + // if the place.base disjoint, further projections will remain disjoint. + Overlap::Disjoint => false, + // process to projections to check further conflict. + Overlap::EqualOrDisjoint => + place_elements_conflict(tcx, mir, borrow_place, access_place, access), + // place.base overlap is obvious, no Abitrary. + _ => unreachable!(), + } } -fn place_components_conflict<'gcx, 'tcx>( +fn place_base_conflict<'a, 'gcx: 'tcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + base1: &PlaceBase<'tcx>, + base2: &PlaceBase<'tcx>, +) -> Overlap { + match (base1, base2) { + (PlaceBase::Local(l1), PlaceBase::Local(l2)) => { + if l1 == l2 { + // the same local - base case, equal + debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL"); + Overlap::EqualOrDisjoint + } else { + // different locals - base case, disjoint + debug!("place_element_conflict: DISJOINT-LOCAL"); + Overlap::Disjoint + } + } + (PlaceBase::Static(static1), PlaceBase::Static(static2)) => { + if static1.def_id != static2.def_id { + debug!("place_element_conflict: DISJOINT-STATIC"); + Overlap::Disjoint + } else if tcx.is_static(static1.def_id) == Some(hir::Mutability::MutMutable) { + // We ignore mutable statics - they can only be unsafe code. + debug!("place_element_conflict: IGNORE-STATIC-MUT"); + Overlap::Disjoint + } else { + debug!("place_element_conflict: DISJOINT-OR-EQ-STATIC"); + Overlap::EqualOrDisjoint + } + } + (PlaceBase::Promoted(p1), PlaceBase::Promoted(p2)) => { + if p1.0 == p2.0 { + if let ty::TyArray(_, size) = p1.1.sty { + if size.unwrap_usize(tcx) == 0 { + // Ignore conflicts with promoted [T; 0]. + debug!("place_element_conflict: IGNORE-LEN-0-PROMOTED"); + return Overlap::Disjoint; + } + } + // the same promoted - base case, equal + debug!("place_element_conflict: DISJOINT-OR-EQ-PROMOTED"); + Overlap::EqualOrDisjoint + } else { + // different promoteds - base case, disjoint + debug!("place_element_conflict: DISJOINT-PROMOTED"); + Overlap::Disjoint + } + } + (PlaceBase::Local(_), PlaceBase::Promoted(_)) + | (PlaceBase::Promoted(_), PlaceBase::Local(_)) + | (PlaceBase::Promoted(_), PlaceBase::Static(_)) + | (PlaceBase::Static(_), PlaceBase::Promoted(_)) + | (PlaceBase::Local(_), PlaceBase::Static(_)) + | (PlaceBase::Static(_), PlaceBase::Local(_)) => { + debug!("place_element_conflict: DISJOINT-STATIC-LOCAL-PROMOTED"); + Overlap::Disjoint + } + } +} + +// Given that the bases of `elem1` and `elem2` are always either equal +// or disjoint (and have the same type!), return the overlap situation +// between `elem1` and `elem2`. +fn place_element_conflict<'tcx>( + base_ty: Ty<'tcx>, + (elem1, elem2): (&PlaceElem<'tcx>, &PlaceElem<'tcx>), +) -> Overlap { + match (elem1, elem2) { + (ProjectionElem::Deref, ProjectionElem::Deref) => { + // derefs (e.g. `*x` vs. `*x`) - recur. + debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF"); + Overlap::EqualOrDisjoint + } + (ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => { + if f1 == f2 { + // same field (e.g. `a.y` vs. `a.y`) - recur. + debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD"); + Overlap::EqualOrDisjoint + } else { + match base_ty.sty { + ty::TyAdt(def, _) if def.is_union() => { + // Different fields of a union, we are basically stuck. + debug!("place_element_conflict: STUCK-UNION"); + Overlap::Arbitrary + } + _ => { + // Different fields of a struct (`a.x` vs. `a.y`). Disjoint! + debug!("place_element_conflict: DISJOINT-FIELD"); + Overlap::Disjoint + } + } + } + } + (ProjectionElem::Downcast(_, v1), ProjectionElem::Downcast(_, v2)) => { + // different variants are treated as having disjoint fields, + // even if they occupy the same "space", because it's + // impossible for 2 variants of the same enum to exist + // (and therefore, to be borrowed) at the same time. + // + // Note that this is different from unions - we *do* allow + // this code to compile: + // + // ``` + // fn foo(x: &mut Result) { + // let mut v = None; + // if let Ok(ref mut a) = *x { + // v = Some(a); + // } + // // here, you would *think* that the + // // *entirety* of `x` would be borrowed, + // // but in fact only the `Ok` variant is, + // // so the `Err` variant is *entirely free*: + // if let Err(ref mut a) = *x { + // v = Some(a); + // } + // drop(v); + // } + // ``` + if v1 == v2 { + debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD"); + Overlap::EqualOrDisjoint + } else { + debug!("place_element_conflict: DISJOINT-FIELD"); + Overlap::Disjoint + } + } + (ProjectionElem::Index(..), ProjectionElem::Index(..)) + | (ProjectionElem::Index(..), ProjectionElem::ConstantIndex { .. }) + | (ProjectionElem::Index(..), ProjectionElem::Subslice { .. }) + | (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..)) + | (ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) => { + // Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint + // (if the indexes differ) or equal (if they are the same), so this + // is the recursive case that gives "equal *or* disjoint" its meaning. + debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-INDEX"); + Overlap::EqualOrDisjoint + } + ( + ProjectionElem::ConstantIndex { + offset: o1, + min_length: _, + from_end: false, + }, + ProjectionElem::ConstantIndex { + offset: o2, + min_length: _, + from_end: false, + }, + ) + | ( + ProjectionElem::ConstantIndex { + offset: o1, + min_length: _, + from_end: true, + }, + ProjectionElem::ConstantIndex { + offset: o2, + min_length: _, + from_end: true, + }, + ) => { + if o1 == o2 { + debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX"); + Overlap::EqualOrDisjoint + } else { + debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX"); + Overlap::Disjoint + } + } + ( + ProjectionElem::ConstantIndex { + offset: offset_from_begin, + min_length: min_length1, + from_end: false, + }, + ProjectionElem::ConstantIndex { + offset: offset_from_end, + min_length: min_length2, + from_end: true, + }, + ) + | ( + ProjectionElem::ConstantIndex { + offset: offset_from_end, + min_length: min_length1, + from_end: true, + }, + ProjectionElem::ConstantIndex { + offset: offset_from_begin, + min_length: min_length2, + from_end: false, + }, + ) => { + // both patterns matched so it must be at least the greater of the two + let min_length = max(min_length1, min_length2); + // `offset_from_end` can be in range `[1..min_length]`, 1 indicates the last + // element (like -1 in Python) and `min_length` the first. + // Therefore, `min_length - offset_from_end` gives the minimal possible + // offset from the beginning + if *offset_from_begin >= min_length - offset_from_end { + debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-FE"); + Overlap::EqualOrDisjoint + } else { + debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-FE"); + Overlap::Disjoint + } + } + ( + ProjectionElem::ConstantIndex { + offset, + min_length: _, + from_end: false, + }, + ProjectionElem::Subslice { from, .. }, + ) + | ( + ProjectionElem::Subslice { from, .. }, + ProjectionElem::ConstantIndex { + offset, + min_length: _, + from_end: false, + }, + ) => { + if offset >= from { + debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE"); + Overlap::EqualOrDisjoint + } else { + debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE"); + Overlap::Disjoint + } + } + ( + ProjectionElem::ConstantIndex { + offset, + min_length: _, + from_end: true, + }, + ProjectionElem::Subslice { from: _, to }, + ) + | ( + ProjectionElem::Subslice { from: _, to }, + ProjectionElem::ConstantIndex { + offset, + min_length: _, + from_end: true, + }, + ) => { + if offset > to { + debug!( + "place_element_conflict: \ + DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE-FE" + ); + Overlap::EqualOrDisjoint + } else { + debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE-FE"); + Overlap::Disjoint + } + } + (ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => { + debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-SUBSLICES"); + Overlap::EqualOrDisjoint + } + (ProjectionElem::Deref, _) + | (ProjectionElem::Field(..), _) + | (ProjectionElem::Index(..), _) + | (ProjectionElem::ConstantIndex { .. }, _) + | (ProjectionElem::Subslice { .. }, _) + | (ProjectionElem::Downcast(..), _) => bug!( + "mismatched projections in place_element_conflict: {:?} and {:?}", + elem1, + elem2 + ), + } +} + +fn place_elements_conflict<'gcx, 'tcx>( tcx: TyCtxt<'_, 'gcx, 'tcx>, mir: &Mir<'tcx>, - mut borrow_components: PlaceComponentsIter<'_, 'tcx>, - mut access_components: PlaceComponentsIter<'_, 'tcx>, + borrow_place: &Place<'tcx>, + access_place: &Place<'tcx>, access: ShallowOrDeep, ) -> bool { - // The borrowck rules for proving disjointness are applied from the "root" of the - // borrow forwards, iterating over "similar" projections in lockstep until - // we can prove overlap one way or another. Essentially, we treat `Overlap` as - // a monoid and report a conflict if the product ends up not being `Disjoint`. - // - // At each step, if we didn't run out of borrow or place, we know that our elements - // have the same type, and that they only overlap if they are the identical. - // - // For example, if we are comparing these: - // BORROW: (*x1[2].y).z.a - // ACCESS: (*x1[i].y).w.b - // - // Then our steps are: - // x1 | x1 -- places are the same - // x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ) - // x1[2].y | x1[i].y -- equal or disjoint - // *x1[2].y | *x1[i].y -- equal or disjoint - // (*x1[2].y).z | (*x1[i].y).w -- we are disjoint and don't need to check more! - // - // Because `zip` does potentially bad things to the iterator inside, this loop - // also handles the case where the access might be a *prefix* of the borrow, e.g. - // - // BORROW: (*x1[2].y).z.a - // ACCESS: x1[i].y - // - // Then our steps are: - // x1 | x1 -- places are the same - // x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ) - // x1[2].y | x1[i].y -- equal or disjoint - // - // -- here we run out of access - the borrow can access a part of it. If this - // is a full deep access, then we *know* the borrow conflicts with it. However, - // if the access is shallow, then we can proceed: - // - // x1[2].y | (*x1[i].y) -- a deref! the access can't get past this, so we - // are disjoint - // - // Our invariant is, that at each step of the iteration: - // - If we didn't run out of access to match, our borrow and access are comparable - // and either equal or disjoint. - // - If we did run out of accesss, the borrow can access a part of it. - loop { - // loop invariant: borrow_c is always either equal to access_c or disjoint from it. - if let Some(borrow_c) = borrow_components.next() { - debug!("places_conflict: borrow_c = {:?}", borrow_c); + // Enumerate for later base_place generation + let mut borrow_elems = borrow_place.elems.iter().cloned(); - if let Some(access_c) = access_components.next() { - debug!("places_conflict: access_c = {:?}", access_c); + let mut access_elems = access_place.elems.iter().cloned(); + + let mut borrow_base_ty = borrow_place.base.ty(mir); + + loop { + if let Some(borrow_elem) = borrow_elems.next() { + if let Some(access_elem) = access_elems.next() { + debug!("places_conflict: access_elem = {:?}", access_elem); // Borrow and access path both have more components. // @@ -104,7 +346,7 @@ fn place_components_conflict<'gcx, 'tcx>( // check whether the components being borrowed vs // accessed are disjoint (as in the second example, // but not the first). - match place_element_conflict(tcx, mir, borrow_c, access_c) { + match place_element_conflict(borrow_base_ty, (&borrow_elem, &access_elem)) { Overlap::Arbitrary => { // We have encountered different fields of potentially // the same union - the borrow now partially overlaps. @@ -123,7 +365,7 @@ fn place_components_conflict<'gcx, 'tcx>( return true; } Overlap::EqualOrDisjoint => { - // This is the recursive case - proceed to the next element. + // proceed to the next element. } Overlap::Disjoint => { // We have proven the borrow disjoint - further @@ -141,13 +383,7 @@ fn place_components_conflict<'gcx, 'tcx>( // our place. This is a conflict if that is a part our // access cares about. - let (base, elem) = match borrow_c { - Place::Projection(box Projection { base, elem }) => (base, elem), - _ => bug!("place has no base?"), - }; - let base_ty = base.ty(mir, tcx).to_ty(tcx); - - match (elem, &base_ty.sty, access) { + match (borrow_elem, &borrow_base_ty.sty, access) { (_, _, Shallow(Some(ArtificialField::Discriminant))) | (_, _, Shallow(Some(ArtificialField::ArrayLength))) => { // The discriminant and array length are like @@ -193,6 +429,9 @@ fn place_components_conflict<'gcx, 'tcx>( } } } + borrow_base_ty = PlaceTy::from(borrow_base_ty) + .projection_ty(tcx, &borrow_elem) + .to_ty(tcx); } else { // Borrow path ran out but access path may not // have. Examples: @@ -215,295 +454,3 @@ fn place_components_conflict<'gcx, 'tcx>( } } } - -/// A linked list of places running up the stack; begins with the -/// innermost place and extends to projections (e.g., `a.b` would have -/// the place `a` with a "next" pointer to `a.b`). Created by -/// `unroll_place`. -/// -/// NB: This particular impl strategy is not the most obvious. It was -/// chosen because it makes a measurable difference to NLL -/// performance, as this code (`places_conflict`) is somewhat hot. -struct PlaceComponents<'p, 'tcx: 'p> { - component: &'p Place<'tcx>, - next: Option<&'p PlaceComponents<'p, 'tcx>>, -} - -impl<'p, 'tcx> PlaceComponents<'p, 'tcx> { - /// Converts a list of `Place` components into an iterator; this - /// iterator yields up a never-ending stream of `Option<&Place>`. - /// These begin with the "innermst" place and then with each - /// projection therefrom. So given a place like `a.b.c` it would - /// yield up: - /// - /// ```notrust - /// Some(`a`), Some(`a.b`), Some(`a.b.c`), None, None, ... - /// ``` - fn iter(&self) -> PlaceComponentsIter<'_, 'tcx> { - PlaceComponentsIter { value: Some(self) } - } -} - -/// Iterator over components; see `PlaceComponents::iter` for more -/// information. -/// -/// NB: This is not a *true* Rust iterator -- the code above just -/// manually invokes `next`. This is because we (sometimes) want to -/// keep executing even after `None` has been returned. -struct PlaceComponentsIter<'p, 'tcx: 'p> { - value: Option<&'p PlaceComponents<'p, 'tcx>>, -} - -impl<'p, 'tcx> PlaceComponentsIter<'p, 'tcx> { - fn next(&mut self) -> Option<&'p Place<'tcx>> { - if let Some(&PlaceComponents { component, next }) = self.value { - self.value = next; - Some(component) - } else { - None - } - } -} - -/// Recursively "unroll" a place into a `PlaceComponents` list, -/// invoking `op` with a `PlaceComponentsIter`. -fn unroll_place<'tcx, R>( - place: &Place<'tcx>, - next: Option<&PlaceComponents<'_, 'tcx>>, - op: impl FnOnce(PlaceComponentsIter<'_, 'tcx>) -> R, -) -> R { - match place { - Place::Projection(interior) => unroll_place( - &interior.base, - Some(&PlaceComponents { - component: place, - next, - }), - op, - ), - - Place::Promoted(_) | - Place::Local(_) | Place::Static(_) => { - let list = PlaceComponents { - component: place, - next, - }; - op(list.iter()) - } - } -} - -// Given that the bases of `elem1` and `elem2` are always either equal -// or disjoint (and have the same type!), return the overlap situation -// between `elem1` and `elem2`. -fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>( - tcx: TyCtxt<'a, 'gcx, 'tcx>, - mir: &Mir<'tcx>, - elem1: &Place<'tcx>, - elem2: &Place<'tcx>, -) -> Overlap { - match (elem1, elem2) { - (Place::Local(l1), Place::Local(l2)) => { - if l1 == l2 { - // the same local - base case, equal - debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL"); - Overlap::EqualOrDisjoint - } else { - // different locals - base case, disjoint - debug!("place_element_conflict: DISJOINT-LOCAL"); - Overlap::Disjoint - } - } - (Place::Static(static1), Place::Static(static2)) => { - if static1.def_id != static2.def_id { - debug!("place_element_conflict: DISJOINT-STATIC"); - Overlap::Disjoint - } else if tcx.is_static(static1.def_id) == Some(hir::Mutability::MutMutable) { - // We ignore mutable statics - they can only be unsafe code. - debug!("place_element_conflict: IGNORE-STATIC-MUT"); - Overlap::Disjoint - } else { - debug!("place_element_conflict: DISJOINT-OR-EQ-STATIC"); - Overlap::EqualOrDisjoint - } - } - (Place::Promoted(p1), Place::Promoted(p2)) => { - if p1.0 == p2.0 { - if let ty::TyArray(_, size) = p1.1.sty { - if size.unwrap_usize(tcx) == 0 { - // Ignore conflicts with promoted [T; 0]. - debug!("place_element_conflict: IGNORE-LEN-0-PROMOTED"); - return Overlap::Disjoint; - } - } - // the same promoted - base case, equal - debug!("place_element_conflict: DISJOINT-OR-EQ-PROMOTED"); - Overlap::EqualOrDisjoint - } else { - // different promoteds - base case, disjoint - debug!("place_element_conflict: DISJOINT-PROMOTED"); - Overlap::Disjoint - } - } - (Place::Local(_), Place::Promoted(_)) | (Place::Promoted(_), Place::Local(_)) | - (Place::Promoted(_), Place::Static(_)) | (Place::Static(_), Place::Promoted(_)) | - (Place::Local(_), Place::Static(_)) | (Place::Static(_), Place::Local(_)) => { - debug!("place_element_conflict: DISJOINT-STATIC-LOCAL-PROMOTED"); - Overlap::Disjoint - } - (Place::Projection(pi1), Place::Projection(pi2)) => { - match (&pi1.elem, &pi2.elem) { - (ProjectionElem::Deref, ProjectionElem::Deref) => { - // derefs (e.g. `*x` vs. `*x`) - recur. - debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF"); - Overlap::EqualOrDisjoint - } - (ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => { - if f1 == f2 { - // same field (e.g. `a.y` vs. `a.y`) - recur. - debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD"); - Overlap::EqualOrDisjoint - } else { - let ty = pi1.base.ty(mir, tcx).to_ty(tcx); - match ty.sty { - ty::TyAdt(def, _) if def.is_union() => { - // Different fields of a union, we are basically stuck. - debug!("place_element_conflict: STUCK-UNION"); - Overlap::Arbitrary - } - _ => { - // Different fields of a struct (`a.x` vs. `a.y`). Disjoint! - debug!("place_element_conflict: DISJOINT-FIELD"); - Overlap::Disjoint - } - } - } - } - (ProjectionElem::Downcast(_, v1), ProjectionElem::Downcast(_, v2)) => { - // different variants are treated as having disjoint fields, - // even if they occupy the same "space", because it's - // impossible for 2 variants of the same enum to exist - // (and therefore, to be borrowed) at the same time. - // - // Note that this is different from unions - we *do* allow - // this code to compile: - // - // ``` - // fn foo(x: &mut Result) { - // let mut v = None; - // if let Ok(ref mut a) = *x { - // v = Some(a); - // } - // // here, you would *think* that the - // // *entirety* of `x` would be borrowed, - // // but in fact only the `Ok` variant is, - // // so the `Err` variant is *entirely free*: - // if let Err(ref mut a) = *x { - // v = Some(a); - // } - // drop(v); - // } - // ``` - if v1 == v2 { - debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD"); - Overlap::EqualOrDisjoint - } else { - debug!("place_element_conflict: DISJOINT-FIELD"); - Overlap::Disjoint - } - } - (ProjectionElem::Index(..), ProjectionElem::Index(..)) - | (ProjectionElem::Index(..), ProjectionElem::ConstantIndex { .. }) - | (ProjectionElem::Index(..), ProjectionElem::Subslice { .. }) - | (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..)) - | (ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) => { - // Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint - // (if the indexes differ) or equal (if they are the same), so this - // is the recursive case that gives "equal *or* disjoint" its meaning. - debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-INDEX"); - Overlap::EqualOrDisjoint - } - (ProjectionElem::ConstantIndex { offset: o1, min_length: _, from_end: false }, - ProjectionElem::ConstantIndex { offset: o2, min_length: _, from_end: false }) - | (ProjectionElem::ConstantIndex { offset: o1, min_length: _, from_end: true }, - ProjectionElem::ConstantIndex { - offset: o2, min_length: _, from_end: true }) => { - if o1 == o2 { - debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX"); - Overlap::EqualOrDisjoint - } else { - debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX"); - Overlap::Disjoint - } - } - (ProjectionElem::ConstantIndex { - offset: offset_from_begin, min_length: min_length1, from_end: false }, - ProjectionElem::ConstantIndex { - offset: offset_from_end, min_length: min_length2, from_end: true }) - | (ProjectionElem::ConstantIndex { - offset: offset_from_end, min_length: min_length1, from_end: true }, - ProjectionElem::ConstantIndex { - offset: offset_from_begin, min_length: min_length2, from_end: false }) => { - // both patterns matched so it must be at least the greater of the two - let min_length = max(min_length1, min_length2); - // `offset_from_end` can be in range `[1..min_length]`, 1 indicates the last - // element (like -1 in Python) and `min_length` the first. - // Therefore, `min_length - offset_from_end` gives the minimal possible - // offset from the beginning - if *offset_from_begin >= min_length - offset_from_end { - debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-FE"); - Overlap::EqualOrDisjoint - } else { - debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-FE"); - Overlap::Disjoint - } - } - (ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false }, - ProjectionElem::Subslice {from, .. }) - | (ProjectionElem::Subslice {from, .. }, - ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false }) => { - if offset >= from { - debug!( - "place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE"); - Overlap::EqualOrDisjoint - } else { - debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE"); - Overlap::Disjoint - } - } - (ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true }, - ProjectionElem::Subslice {from: _, to }) - | (ProjectionElem::Subslice {from: _, to }, - ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true }) => { - if offset > to { - debug!("place_element_conflict: \ - DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE-FE"); - Overlap::EqualOrDisjoint - } else { - debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE-FE"); - Overlap::Disjoint - } - } - (ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => { - debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-SUBSLICES"); - Overlap::EqualOrDisjoint - } - (ProjectionElem::Deref, _) - | (ProjectionElem::Field(..), _) - | (ProjectionElem::Index(..), _) - | (ProjectionElem::ConstantIndex { .. }, _) - | (ProjectionElem::Subslice { .. }, _) - | (ProjectionElem::Downcast(..), _) => bug!( - "mismatched projections in place_element_conflict: {:?} and {:?}", - elem1, - elem2 - ), - } - } - (Place::Projection(_), _) | (_, Place::Projection(_)) => bug!( - "unexpected elements in place_element_conflict: {:?} and {:?}", - elem1, - elem2 - ), - } -} diff --git a/src/librustc_mir/borrow_check/prefixes.rs b/src/librustc_mir/borrow_check/prefixes.rs deleted file mode 100644 index 9b16130d25ec4..0000000000000 --- a/src/librustc_mir/borrow_check/prefixes.rs +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2017 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! From the NLL RFC: "The deep [aka 'supporting'] prefixes for an -//! place are formed by stripping away fields and derefs, except that -//! we stop when we reach the deref of a shared reference. [...] " -//! -//! "Shallow prefixes are found by stripping away fields, but stop at -//! any dereference. So: writing a path like `a` is illegal if `a.b` -//! is borrowed. But: writing `a` is legal if `*a` is borrowed, -//! whether or not `a` is a shared or mutable reference. [...] " - -use super::MirBorrowckCtxt; - -use rustc::hir; -use rustc::ty::{self, TyCtxt}; -use rustc::mir::{Mir, Place, ProjectionElem}; - -pub trait IsPrefixOf<'tcx> { - fn is_prefix_of(&self, other: &Place<'tcx>) -> bool; -} - -impl<'tcx> IsPrefixOf<'tcx> for Place<'tcx> { - fn is_prefix_of(&self, other: &Place<'tcx>) -> bool { - let mut cursor = other; - loop { - if self == cursor { - return true; - } - - match *cursor { - Place::Promoted(_) | - Place::Local(_) | Place::Static(_) => return false, - Place::Projection(ref proj) => { - cursor = &proj.base; - } - } - } - } -} - - -pub(super) struct Prefixes<'cx, 'gcx: 'tcx, 'tcx: 'cx> { - mir: &'cx Mir<'tcx>, - tcx: TyCtxt<'cx, 'gcx, 'tcx>, - kind: PrefixSet, - next: Option<&'cx Place<'tcx>>, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -#[allow(dead_code)] -pub(super) enum PrefixSet { - /// Doesn't stop until it returns the base case (a Local or - /// Static prefix). - All, - /// Stops at any dereference. - Shallow, - /// Stops at the deref of a shared reference. - Supporting, -} - -impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { - /// Returns an iterator over the prefixes of `place` - /// (inclusive) from longest to smallest, potentially - /// terminating the iteration early based on `kind`. - pub(super) fn prefixes( - &self, - place: &'cx Place<'tcx>, - kind: PrefixSet, - ) -> Prefixes<'cx, 'gcx, 'tcx> { - Prefixes { - next: Some(place), - kind, - mir: self.mir, - tcx: self.tcx, - } - } -} - -impl<'cx, 'gcx, 'tcx> Iterator for Prefixes<'cx, 'gcx, 'tcx> { - type Item = &'cx Place<'tcx>; - fn next(&mut self) -> Option { - let mut cursor = match self.next { - None => return None, - Some(place) => place, - }; - - // Post-processing `place`: Enqueue any remaining - // work. Also, `place` may not be a prefix itself, but - // may hold one further down (e.g. we never return - // downcasts here, but may return a base of a downcast). - - 'cursor: loop { - let proj = match *cursor { - Place::Promoted(_) | - Place::Local(_) | // search yielded this leaf - Place::Static(_) => { - self.next = None; - return Some(cursor); - } - - Place::Projection(ref proj) => proj, - }; - - match proj.elem { - ProjectionElem::Field(_ /*field*/, _ /*ty*/) => { - // FIXME: add union handling - self.next = Some(&proj.base); - return Some(cursor); - } - ProjectionElem::Downcast(..) | - ProjectionElem::Subslice { .. } | - ProjectionElem::ConstantIndex { .. } | - ProjectionElem::Index(_) => { - cursor = &proj.base; - continue 'cursor; - } - ProjectionElem::Deref => { - // (handled below) - } - } - - assert_eq!(proj.elem, ProjectionElem::Deref); - - match self.kind { - PrefixSet::Shallow => { - // shallow prefixes are found by stripping away - // fields, but stop at *any* dereference. - // So we can just stop the traversal now. - self.next = None; - return Some(cursor); - } - PrefixSet::All => { - // all prefixes: just blindly enqueue the base - // of the projection - self.next = Some(&proj.base); - return Some(cursor); - } - PrefixSet::Supporting => { - // fall through! - } - } - - assert_eq!(self.kind, PrefixSet::Supporting); - // supporting prefixes: strip away fields and - // derefs, except we stop at the deref of a shared - // reference. - - let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); - match ty.sty { - ty::TyRawPtr(_) | - ty::TyRef( - _, /*rgn*/ - _, /*ty*/ - hir::MutImmutable - ) => { - // don't continue traversing over derefs of raw pointers or shared borrows. - self.next = None; - return Some(cursor); - } - - ty::TyRef( - _, /*rgn*/ - _, /*ty*/ - hir::MutMutable, - ) => { - self.next = Some(&proj.base); - return Some(cursor); - } - - ty::TyAdt(..) if ty.is_box() => { - self.next = Some(&proj.base); - return Some(cursor); - } - - _ => panic!("unknown type fed to Projection Deref."), - } - } - } -} diff --git a/src/librustc_mir/borrow_check/used_muts.rs b/src/librustc_mir/borrow_check/used_muts.rs index dbe19bc47859f..d3be38eac0479 100644 --- a/src/librustc_mir/borrow_check/used_muts.rs +++ b/src/librustc_mir/borrow_check/used_muts.rs @@ -9,7 +9,7 @@ // except according to those terms. use rustc::mir::visit::{PlaceContext, Visitor}; -use rustc::mir::{Local, Location, Place}; +use rustc::mir::{Local, Location, PlaceBase}; use rustc_data_structures::fx::FxHashSet; @@ -55,8 +55,10 @@ impl<'visit, 'cx, 'gcx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'c "assignment of {:?} to {:?}, adding {:?} to used mutable set", path.place, local, path.place ); - if let Place::Local(user_local) = path.place { - self.mbcx.used_mut.insert(user_local); + if let PlaceBase::Local(user_local) = path.place.base { + if path.place.has_no_projection() { + self.mbcx.used_mut.insert(user_local); + } } } } diff --git a/src/librustc_mir/build/expr/as_operand.rs b/src/librustc_mir/build/expr/as_operand.rs index 7eae414a39137..c3ba35f0e237f 100644 --- a/src/librustc_mir/build/expr/as_operand.rs +++ b/src/librustc_mir/build/expr/as_operand.rs @@ -74,7 +74,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Category::Rvalue(..) => { let operand = unpack!(block = this.as_temp(block, scope, expr)); - block.and(Operand::Move(Place::Local(operand))) + block.and(Operand::Move(Place::local(operand))) } } } diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs index a38ca7ae5bf64..7fa55d10c4f38 100644 --- a/src/librustc_mir/build/expr/as_place.rs +++ b/src/librustc_mir/build/expr/as_place.rs @@ -48,12 +48,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } ExprKind::Field { lhs, name } => { let place = unpack!(block = this.as_place(block, lhs)); - let place = place.field(name, expr.ty); + let place = place.field(this.hir.tcx(), name, expr.ty); block.and(place) } ExprKind::Deref { arg } => { let place = unpack!(block = this.as_place(block, arg)); - let place = place.deref(); + let place = place.deref(this.hir.tcx()); block.and(place) } ExprKind::Index { lhs, index } => { @@ -72,34 +72,34 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { &len, Rvalue::Len(slice.clone())); this.cfg.push_assign(block, source_info, // lt = idx < len <, Rvalue::BinaryOp(BinOp::Lt, - Operand::Copy(Place::Local(idx)), + Operand::Copy(Place::local(idx)), Operand::Copy(len.clone()))); let msg = BoundsCheck { len: Operand::Move(len), - index: Operand::Copy(Place::Local(idx)) + index: Operand::Copy(Place::local(idx)) }; let success = this.assert(block, Operand::Move(lt), true, msg, expr_span); - success.and(slice.index(idx)) + success.and(slice.index(this.hir.tcx(), idx)) } ExprKind::SelfRef => { - block.and(Place::Local(Local::new(1))) + block.and(Place::local(Local::new(1))) } ExprKind::VarRef { id } => { let place = if this.is_bound_var_in_guard(id) && this.hir.tcx().all_pat_vars_are_implicit_refs_within_guards() { let index = this.var_local_id(id, RefWithinGuard); - Place::Local(index).deref() + Place::local(index).deref(this.hir.tcx()) } else { let index = this.var_local_id(id, OutsideGuard); - Place::Local(index) + Place::local(index) }; block.and(place) } ExprKind::StaticRef { id } => { - block.and(Place::Static(Box::new(Static { def_id: id, ty: expr.ty }))) + block.and(Place::static_(Static { def_id: id, ty: expr.ty })) } ExprKind::Array { .. } | @@ -138,7 +138,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { _ => true, }); let temp = unpack!(block = this.as_temp(block, expr.temp_lifetime, expr)); - block.and(Place::Local(temp)) + block.and(Place::local(temp)) } } } diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 68009e962a302..4aad84399f666 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -13,64 +13,81 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; -use build::{BlockAnd, BlockAndExtension, Builder}; use build::expr::category::{Category, RvalueFunc}; +use build::{BlockAnd, BlockAndExtension, Builder}; use hair::*; use rustc::middle::region; -use rustc::ty::{self, Ty, UpvarSubsts}; -use rustc::mir::*; use rustc::mir::interpret::EvalErrorKind; +use rustc::mir::*; +use rustc::ty::{self, Ty, UpvarSubsts}; use syntax_pos::Span; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// See comment on `as_local_operand` - pub fn as_local_rvalue(&mut self, block: BasicBlock, expr: M) - -> BlockAnd> - where M: Mirror<'tcx, Output = Expr<'tcx>> + pub fn as_local_rvalue(&mut self, block: BasicBlock, expr: M) -> BlockAnd> + where + M: Mirror<'tcx, Output = Expr<'tcx>>, { let local_scope = self.local_scope(); self.as_rvalue(block, local_scope, expr) } /// Compile `expr`, yielding an rvalue. - pub fn as_rvalue(&mut self, block: BasicBlock, scope: Option, expr: M) - -> BlockAnd> - where M: Mirror<'tcx, Output = Expr<'tcx>> + pub fn as_rvalue( + &mut self, + block: BasicBlock, + scope: Option, + expr: M, + ) -> BlockAnd> + where + M: Mirror<'tcx, Output = Expr<'tcx>>, { let expr = self.hir.mirror(expr); self.expr_as_rvalue(block, scope, expr) } - fn expr_as_rvalue(&mut self, - mut block: BasicBlock, - scope: Option, - expr: Expr<'tcx>) - -> BlockAnd> { - debug!("expr_as_rvalue(block={:?}, scope={:?}, expr={:?})", block, scope, expr); + fn expr_as_rvalue( + &mut self, + mut block: BasicBlock, + scope: Option, + expr: Expr<'tcx>, + ) -> BlockAnd> { + debug!( + "expr_as_rvalue(block={:?}, scope={:?}, expr={:?})", + block, scope, expr + ); let this = self; let expr_span = expr.span; let source_info = this.source_info(expr_span); match expr.kind { - ExprKind::Scope { region_scope, lint_level, value } => { + ExprKind::Scope { + region_scope, + lint_level, + value, + } => { let region_scope = (region_scope, source_info); - this.in_scope(region_scope, lint_level, block, - |this| this.as_rvalue(block, scope, value)) + this.in_scope(region_scope, lint_level, block, |this| { + this.as_rvalue(block, scope, value) + }) } ExprKind::Repeat { value, count } => { let value_operand = unpack!(block = this.as_operand(block, scope, value)); block.and(Rvalue::Repeat(value_operand, count)) } - ExprKind::Borrow { region, borrow_kind, arg } => { + ExprKind::Borrow { + region, + borrow_kind, + arg, + } => { let arg_place = unpack!(block = this.as_place(block, arg)); block.and(Rvalue::Ref(region, borrow_kind, arg_place)) } ExprKind::Binary { op, lhs, rhs } => { let lhs = unpack!(block = this.as_operand(block, scope, lhs)); let rhs = unpack!(block = this.as_operand(block, scope, rhs)); - this.build_binary_op(block, op, expr_span, expr.ty, - lhs, rhs) + this.build_binary_op(block, op, expr_span, expr.ty, lhs, rhs) } ExprKind::Unary { op, arg } => { let arg = unpack!(block = this.as_operand(block, scope, arg)); @@ -81,11 +98,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let minval = this.minval_literal(expr_span, expr.ty); let is_min = this.temp(bool_ty, expr_span); - this.cfg.push_assign(block, source_info, &is_min, - Rvalue::BinaryOp(BinOp::Eq, arg.to_copy(), minval)); + this.cfg.push_assign( + block, + source_info, + &is_min, + Rvalue::BinaryOp(BinOp::Eq, arg.to_copy(), minval), + ); - block = this.assert(block, Operand::Move(is_min), false, - EvalErrorKind::OverflowNeg, expr_span); + block = this.assert( + block, + Operand::Move(is_min), + false, + EvalErrorKind::OverflowNeg, + expr_span, + ); } block.and(Rvalue::UnaryOp(op, arg)) } @@ -94,26 +120,35 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // The `Box` temporary created here is not a part of the HIR, // and therefore is not considered during generator OIBIT // determination. See the comment about `box` at `yield_in_scope`. - let result = this.local_decls.push( - LocalDecl::new_internal(expr.ty, expr_span)); - this.cfg.push(block, Statement { - source_info, - kind: StatementKind::StorageLive(result) - }); + let result = this + .local_decls + .push(LocalDecl::new_internal(expr.ty, expr_span)); + this.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::StorageLive(result), + }, + ); if let Some(scope) = scope { // schedule a shallow free of that memory, lest we unwind: this.schedule_drop_storage_and_value( - expr_span, scope, &Place::Local(result), value.ty, + expr_span, + scope, + &Place::local(result), + value.ty, ); } // malloc some memory of suitable type (thus far, uninitialized): let box_ = Rvalue::NullaryOp(NullOp::Box, value.ty); - this.cfg.push_assign(block, source_info, &Place::Local(result), box_); + this.cfg + .push_assign(block, source_info, &Place::local(result), box_); // initialize the box contents: - unpack!(block = this.into(&Place::Local(result).deref(), block, value)); - block.and(Rvalue::Use(Operand::Move(Place::Local(result)))) + let tcx = this.hir.tcx(); + unpack!(block = this.into(&Place::local(result).deref(tcx), block, value)); + block.and(Rvalue::Use(Operand::Move(Place::local(result)))) } ExprKind::Cast { source } => { let source = this.hir.mirror(source); @@ -170,23 +205,29 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // first process the set of fields let el_ty = expr.ty.sequence_element_type(this.hir.tcx()); - let fields: Vec<_> = - fields.into_iter() - .map(|f| unpack!(block = this.as_operand(block, scope, f))) - .collect(); + let fields: Vec<_> = fields + .into_iter() + .map(|f| unpack!(block = this.as_operand(block, scope, f))) + .collect(); block.and(Rvalue::Aggregate(box AggregateKind::Array(el_ty), fields)) } - ExprKind::Tuple { fields } => { // see (*) above + ExprKind::Tuple { fields } => { + // see (*) above // first process the set of fields - let fields: Vec<_> = - fields.into_iter() - .map(|f| unpack!(block = this.as_operand(block, scope, f))) - .collect(); + let fields: Vec<_> = fields + .into_iter() + .map(|f| unpack!(block = this.as_operand(block, scope, f))) + .collect(); block.and(Rvalue::Aggregate(box AggregateKind::Tuple, fields)) } - ExprKind::Closure { closure_id, substs, upvars, movability } => { + ExprKind::Closure { + closure_id, + substs, + upvars, + movability, + } => { // see (*) above let mut operands: Vec<_> = upvars .into_iter() @@ -212,25 +253,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // that caused the capture will cause an error. match upvar.kind { ExprKind::Borrow { - borrow_kind: BorrowKind::Mut { - allow_two_phase_borrow: false - }, + borrow_kind: + BorrowKind::Mut { + allow_two_phase_borrow: false, + }, region, arg, - } => unpack!(block = this.limit_capture_mutability( - upvar.span, - upvar.ty, - scope, - block, - arg, - region, - )), + } => unpack!( + block = this.limit_capture_mutability( + upvar.span, upvar.ty, scope, block, arg, region, + ) + ), _ => unpack!(block = this.as_operand(block, scope, upvar)), } } } - }) - .collect(); + }).collect(); let result = match substs { UpvarSubsts::Generator(substs) => { let movability = movability.unwrap(); @@ -247,23 +285,35 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { })); box AggregateKind::Generator(closure_id, substs, movability) } - UpvarSubsts::Closure(substs) => { - box AggregateKind::Closure(closure_id, substs) - } + UpvarSubsts::Closure(substs) => box AggregateKind::Closure(closure_id, substs), }; block.and(Rvalue::Aggregate(result, operands)) } ExprKind::Adt { - adt_def, variant_index, substs, fields, base - } => { // see (*) above + adt_def, + variant_index, + substs, + fields, + base, + } => { + // see (*) above let is_union = adt_def.is_union(); - let active_field_index = if is_union { Some(fields[0].name.index()) } else { None }; + let active_field_index = if is_union { + Some(fields[0].name.index()) + } else { + None + }; // first process the set of fields that were provided // (evaluating them in order given by user) - let fields_map: FxHashMap<_, _> = fields.into_iter() - .map(|f| (f.name, unpack!(block = this.as_operand(block, scope, f.expr)))) - .collect(); + let fields_map: FxHashMap<_, _> = fields + .into_iter() + .map(|f| { + ( + f.name, + unpack!(block = this.as_operand(block, scope, f.expr)), + ) + }).collect(); let field_names = this.hir.all_fields(adt_def, variant_index); @@ -273,23 +323,29 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // MIR does not natively support FRU, so for each // base-supplied field, generate an operand that // reads it from the base. - field_names.into_iter() + field_names + .into_iter() .zip(field_types.into_iter()) .map(|(n, ty)| match fields_map.get(&n) { Some(v) => v.clone(), - None => this.consume_by_copy_or_move(base.clone().field(n, ty)) - }) - .collect() + None => this.consume_by_copy_or_move(base.clone().field( + this.hir.tcx(), + n, + ty, + )), + }).collect() } else { - field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect() + field_names + .iter() + .filter_map(|n| fields_map.get(n).cloned()) + .collect() }; let adt = box AggregateKind::Adt(adt_def, variant_index, substs, active_field_index); block.and(Rvalue::Aggregate(adt, fields)) } - ExprKind::Assign { .. } | - ExprKind::AssignOp { .. } => { + ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { block = unpack!(this.stmt_expr(block, expr)); block.and(this.unit_rvalue()) } @@ -297,31 +353,35 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let value = unpack!(block = this.as_operand(block, scope, value)); let resume = this.cfg.start_new_block(); let cleanup = this.generator_drop_cleanup(); - this.cfg.terminate(block, source_info, TerminatorKind::Yield { - value: value, - resume: resume, - drop: cleanup, - }); + this.cfg.terminate( + block, + source_info, + TerminatorKind::Yield { + value: value, + resume: resume, + drop: cleanup, + }, + ); resume.and(this.unit_rvalue()) } - ExprKind::Literal { .. } | - ExprKind::Block { .. } | - ExprKind::Match { .. } | - ExprKind::If { .. } | - ExprKind::NeverToAny { .. } | - ExprKind::Loop { .. } | - ExprKind::LogicalOp { .. } | - ExprKind::Call { .. } | - ExprKind::Field { .. } | - ExprKind::Deref { .. } | - ExprKind::Index { .. } | - ExprKind::VarRef { .. } | - ExprKind::SelfRef | - ExprKind::Break { .. } | - ExprKind::Continue { .. } | - ExprKind::Return { .. } | - ExprKind::InlineAsm { .. } | - ExprKind::StaticRef { .. } => { + ExprKind::Literal { .. } + | ExprKind::Block { .. } + | ExprKind::Match { .. } + | ExprKind::If { .. } + | ExprKind::NeverToAny { .. } + | ExprKind::Loop { .. } + | ExprKind::LogicalOp { .. } + | ExprKind::Call { .. } + | ExprKind::Field { .. } + | ExprKind::Deref { .. } + | ExprKind::Index { .. } + | ExprKind::VarRef { .. } + | ExprKind::SelfRef + | ExprKind::Break { .. } + | ExprKind::Continue { .. } + | ExprKind::Return { .. } + | ExprKind::InlineAsm { .. } + | ExprKind::StaticRef { .. } => { // these do not have corresponding `Rvalue` variants, // so make an operand and then return that debug_assert!(match Category::of(&expr.kind) { @@ -334,29 +394,36 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } - pub fn build_binary_op(&mut self, mut block: BasicBlock, - op: BinOp, span: Span, ty: Ty<'tcx>, - lhs: Operand<'tcx>, rhs: Operand<'tcx>) -> BlockAnd> { + pub fn build_binary_op( + &mut self, + mut block: BasicBlock, + op: BinOp, + span: Span, + ty: Ty<'tcx>, + lhs: Operand<'tcx>, + rhs: Operand<'tcx>, + ) -> BlockAnd> { let source_info = self.source_info(span); let bool_ty = self.hir.bool_ty(); if self.hir.check_overflow() && op.is_checkable() && ty.is_integral() { let result_tup = self.hir.tcx().intern_tup(&[ty, bool_ty]); let result_value = self.temp(result_tup, span); - self.cfg.push_assign(block, source_info, - &result_value, Rvalue::CheckedBinaryOp(op, - lhs, - rhs)); + self.cfg.push_assign( + block, + source_info, + &result_value, + Rvalue::CheckedBinaryOp(op, lhs, rhs), + ); let val_fld = Field::new(0); let of_fld = Field::new(1); - let val = result_value.clone().field(val_fld, ty); - let of = result_value.field(of_fld, bool_ty); + let val = result_value.clone().field(self.hir.tcx(), val_fld, ty); + let of = result_value.field(self.hir.tcx(), of_fld, bool_ty); let err = EvalErrorKind::Overflow(op); - block = self.assert(block, Operand::Move(of), false, - err, span); + block = self.assert(block, Operand::Move(of), false, err, span); block.and(Rvalue::Use(Operand::Move(val))) } else { @@ -365,21 +432,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // and 2. there are two possible failure cases, divide-by-zero and overflow. let (zero_err, overflow_err) = if op == BinOp::Div { - (EvalErrorKind::DivisionByZero, - EvalErrorKind::Overflow(op)) + (EvalErrorKind::DivisionByZero, EvalErrorKind::Overflow(op)) } else { - (EvalErrorKind::RemainderByZero, - EvalErrorKind::Overflow(op)) + (EvalErrorKind::RemainderByZero, EvalErrorKind::Overflow(op)) }; // Check for / 0 let is_zero = self.temp(bool_ty, span); let zero = self.zero_literal(span, ty); - self.cfg.push_assign(block, source_info, &is_zero, - Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), zero)); + self.cfg.push_assign( + block, + source_info, + &is_zero, + Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), zero), + ); - block = self.assert(block, Operand::Move(is_zero), false, - zero_err, span); + block = self.assert(block, Operand::Move(is_zero), false, zero_err, span); // We only need to check for the overflow in one case: // MIN / -1, and only for signed values. @@ -388,23 +456,34 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let min = self.minval_literal(span, ty); let is_neg_1 = self.temp(bool_ty, span); - let is_min = self.temp(bool_ty, span); - let of = self.temp(bool_ty, span); + let is_min = self.temp(bool_ty, span); + let of = self.temp(bool_ty, span); // this does (rhs == -1) & (lhs == MIN). It could short-circuit instead - self.cfg.push_assign(block, source_info, &is_neg_1, - Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), neg_1)); - self.cfg.push_assign(block, source_info, &is_min, - Rvalue::BinaryOp(BinOp::Eq, lhs.to_copy(), min)); + self.cfg.push_assign( + block, + source_info, + &is_neg_1, + Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), neg_1), + ); + self.cfg.push_assign( + block, + source_info, + &is_min, + Rvalue::BinaryOp(BinOp::Eq, lhs.to_copy(), min), + ); let is_neg_1 = Operand::Move(is_neg_1); let is_min = Operand::Move(is_min); - self.cfg.push_assign(block, source_info, &of, - Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min)); + self.cfg.push_assign( + block, + source_info, + &of, + Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min), + ); - block = self.assert(block, Operand::Move(of), false, - overflow_err, span); + block = self.assert(block, Operand::Move(of), false, overflow_err, span); } } @@ -423,75 +502,130 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ) -> BlockAnd> { let this = self; + let tcx = this.hir.tcx(); let source_info = this.source_info(upvar_span); - let temp = this.local_decls.push(LocalDecl::new_temp(upvar_ty, upvar_span)); + let temp = this + .local_decls + .push(LocalDecl::new_temp(upvar_ty, upvar_span)); - this.cfg.push(block, Statement { - source_info, - kind: StatementKind::StorageLive(temp) - }); + this.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::StorageLive(temp), + }, + ); let arg_place = unpack!(block = this.as_place(block, arg)); - let mutability = match arg_place { - Place::Local(local) => this.local_decls[local].mutability, - Place::Projection(box Projection { - base: Place::Local(local), - elem: ProjectionElem::Deref, - }) => { - debug_assert!( - if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) - = this.local_decls[local].is_user_variable { - true - } else { - false - }, - "Unexpected capture place", - ); - this.local_decls[local].mutability + let mutability = match arg_place.split_projection(tcx) { + (base_place, Some(projection)) => { + match projection { + ProjectionElem::Deref => { + match base_place.split_projection(tcx) { + (base_place, None) => { + if let PlaceBase::Local(local) = base_place.base { + debug_assert!( + if let Some(ClearCrossCrate::Set( + BindingForm::RefForGuard, + )) = this.local_decls[local].is_user_variable + { + true + } else { + false + }, + "Unexpected capture place", + ); + this.local_decls[local].mutability + } else { + bug!("Unexpected capture place") + } + } + (base_place, Some(projection)) => { + match projection { + ProjectionElem::Field(upvar_index, _) => { + // Not projected from the implicit `self` in a closure. + debug_assert!( + if base_place.has_no_projection() { + if let PlaceBase::Local(local) = base_place.base { + local == Local::new(1) + } else { + false + } + } else { + if base_place.elems.len() == 1 && + base_place.elems[0] == ProjectionElem::Deref { + base_place.base == + PlaceBase::Local(Local::new(1)) + } else { + false + } + } + "Unexpected capture place" + ); + // Not in a closure + debug_assert!( + this.upvar_decls.len() > upvar_index.index(), + "Unexpected capture place" + ); + this.upvar_decls[upvar_index.index()].mutability + } + _ => { + bug!("Unexpected capture place"); + } + } + } + } + } + + ProjectionElem::Field(upvar_index, _) => { + debug_assert!( + if base_place.has_no_projection() { + if let PlaceBase::Local(local) = base_place.base { + local == Local::new(1) + } else { + false + } + } else { + if base_place.elems.len() == 1 && + base_place.elems[0] == ProjectionElem::Deref { + base_place.base == PlaceBase::Local(Local::new(1)) + } else { + false + } + } + "Unexpected capture place" + ); + // Not in a closure + debug_assert!( + this.upvar_decls.len() > upvar_index.index(), + "Unexpected capture place" + ); + this.upvar_decls[upvar_index.index()].mutability + } + _ => bug!("Unexpected capture place"), + } } - Place::Projection(box Projection { - ref base, - elem: ProjectionElem::Field(upvar_index, _), - }) - | Place::Projection(box Projection { - base: Place::Projection(box Projection { - ref base, - elem: ProjectionElem::Field(upvar_index, _), - }), - elem: ProjectionElem::Deref, - }) => { - // Not projected from the implicit `self` in a closure. - debug_assert!( - match *base { - Place::Local(local) => local == Local::new(1), - Place::Projection(box Projection { - ref base, - elem: ProjectionElem::Deref, - }) => *base == Place::Local(Local::new(1)), - _ => false, - }, - "Unexpected capture place" - ); - // Not in a closure - debug_assert!( - this.upvar_decls.len() > upvar_index.index(), - "Unexpected capture place" - ); - this.upvar_decls[upvar_index.index()].mutability + (base_place, None) => { + if let PlaceBase::Local(local) = base_place.base { + this.local_decls[local].mutability + } else { + bug!("Unexpected capture place") + } } - _ => bug!("Unexpected capture place"), }; let borrow_kind = match mutability { Mutability::Not => BorrowKind::Unique, - Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false }, + Mutability::Mut => BorrowKind::Mut { + allow_two_phase_borrow: false, + }, }; this.cfg.push_assign( block, source_info, - &Place::Local(temp), + &Place::local(temp), Rvalue::Ref(region, borrow_kind, arg_place), ); @@ -500,11 +634,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // a constant at this time, even if the type may need dropping. if let Some(temp_lifetime) = temp_lifetime { this.schedule_drop_storage_and_value( - upvar_span, temp_lifetime, &Place::Local(temp), upvar_ty, + upvar_span, + temp_lifetime, + &Place::local(temp), + upvar_ty, ); } - block.and(Operand::Move(Place::Local(temp))) + block.and(Operand::Move(Place::local(temp))) } // Helper to get a `-1` value of the appropriate type diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs index f66fe763b759d..0c1b60600c867 100644 --- a/src/librustc_mir/build/expr/as_temp.rs +++ b/src/librustc_mir/build/expr/as_temp.rs @@ -56,14 +56,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }); } - unpack!(block = this.into(&Place::Local(temp), block, expr)); + unpack!(block = this.into(&Place::local(temp), block, expr)); // In constants, temp_lifetime is None. We should not need to drop // anything because no values with a destructor can be created in // a constant at this time, even if the type may need dropping. if let Some(temp_lifetime) = temp_lifetime { this.schedule_drop_storage_and_value( - expr_span, temp_lifetime, &Place::Local(temp), expr_ty, + expr_span, temp_lifetime, &Place::local(temp), expr_ty, ); } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 59a7f49af8074..617e36d762ce2 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -251,9 +251,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { internal: true, is_user_variable: None, }); - let ptr_temp = Place::Local(ptr_temp); + let ptr_temp = Place::local(ptr_temp); let block = unpack!(this.into(&ptr_temp, block, ptr)); - this.into(&ptr_temp.deref(), block, val) + let tcx = this.hir.tcx(); + this.into(&ptr_temp.deref(tcx), block, val) } else { let args: Vec<_> = args.into_iter() @@ -307,7 +308,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Create a "fake" temporary variable so that we check that the // value is Sized. Usually, this is caught in type checking, but // in the case of box expr there is no such check. - if let Place::Projection(..) = destination { + if let Some(_) = destination.projection() { this.local_decls.push(LocalDecl::new_temp(expr.ty, expr.span)); } diff --git a/src/librustc_mir/build/expr/stmt.rs b/src/librustc_mir/build/expr/stmt.rs index 6f1fe8335780d..0ed0aaa942e4a 100644 --- a/src/librustc_mir/build/expr/stmt.rs +++ b/src/librustc_mir/build/expr/stmt.rs @@ -107,12 +107,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ExprKind::Return { value } => { block = match value { Some(value) => { - unpack!(this.into(&Place::Local(RETURN_PLACE), block, value)) + unpack!(this.into(&Place::local(RETURN_PLACE), block, value)) } None => { this.cfg.push_assign_unit(block, source_info, - &Place::Local(RETURN_PLACE)); + &Place::local(RETURN_PLACE)); block } }; diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 6b6ec749bcbe6..ee8b28fedb896 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -374,7 +374,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { source_info, kind: StatementKind::StorageLive(local_id) }); - let place = Place::Local(local_id); + let place = Place::local(local_id); let var_ty = self.local_decls[local_id].ty; let hir_id = self.hir.tcx().hir.node_to_hir_id(var); let region_scope = self.hir.region_scope_tree.var_scope(hir_id.local_id); @@ -394,7 +394,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let hir_id = self.hir.tcx().hir.node_to_hir_id(var); let region_scope = self.hir.region_scope_tree.var_scope(hir_id.local_id); self.schedule_drop( - span, region_scope, &Place::Local(local_id), var_ty, + span, region_scope, &Place::local(local_id), var_ty, DropKind::Value { cached_block: CachedBlock::default(), }, diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index 147b8cc2175af..992cdf5859f57 100644 --- a/src/librustc_mir/build/matches/simplify.rs +++ b/src/librustc_mir/build/matches/simplify.rs @@ -119,7 +119,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } }); if irrefutable { - let place = match_pair.place.downcast(adt_def, variant_index); + let place = match_pair.place.downcast(self.hir.tcx(), adt_def, variant_index); candidate.match_pairs.extend(self.field_match_pairs(place, subpatterns)); Ok(()) } else { @@ -144,7 +144,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } PatternKind::Deref { ref subpattern } => { - let place = match_pair.place.deref(); + let tcx = self.hir.tcx(); + let place = match_pair.place.deref(tcx); candidate.match_pairs.push(MatchPair::new(place, subpattern)); Ok(()) } diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 7106e02284da3..c09bad4e061bc 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -668,14 +668,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`, // we want to create a set of derived match-patterns like // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`. - let elem = ProjectionElem::Downcast(adt_def, variant_index); - let downcast_place = match_pair.place.clone().elem(elem); // `(x as Variant)` + let downcast_place = match_pair.place.clone().downcast( + self.hir.tcx(), + adt_def, + variant_index + ); // `(x as Variant)` let consequent_match_pairs = subpatterns.iter() .map(|subpattern| { // e.g., `(x as Variant).0` - let place = downcast_place.clone().field(subpattern.field, - subpattern.pattern.ty); + let place = downcast_place.clone().field( + self.hir.tcx(), + subpattern.field, + subpattern.pattern.ty + ); // e.g., `(x as Variant).0 @ P1` MatchPair::new(place, &subpattern.pattern) }); diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs index cfd9100fc6ae7..29609f422770c 100644 --- a/src/librustc_mir/build/matches/util.rs +++ b/src/librustc_mir/build/matches/util.rs @@ -21,8 +21,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { -> Vec> { subpatterns.iter() .map(|fieldpat| { - let place = place.clone().field(fieldpat.field, - fieldpat.pattern.ty); + let place = place.clone().field( + self.hir.tcx(), + fieldpat.field, + fieldpat.pattern.ty + ); MatchPair::new(place, &fieldpat.pattern) }) .collect() @@ -42,22 +45,23 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { prefix.iter() .enumerate() .map(|(idx, subpattern)| { - let elem = ProjectionElem::ConstantIndex { - offset: idx as u32, + let place = place.clone().constant_index( + self.hir.tcx(), + idx as u32, min_length, - from_end: false, - }; - let place = place.clone().elem(elem); + false, + ); MatchPair::new(place, subpattern) }) ); if let Some(subslice_pat) = opt_slice { - let subslice = place.clone().elem(ProjectionElem::Subslice { - from: prefix.len() as u32, - to: suffix.len() as u32 - }); - match_pairs.push(MatchPair::new(subslice, subslice_pat)); + let place = place.clone().subslice( + self.hir.tcx(), + prefix.len() as u32, + suffix.len() as u32, + ); + match_pairs.push(MatchPair::new(place, subslice_pat)); } match_pairs.extend( @@ -65,12 +69,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { .rev() .enumerate() .map(|(idx, subpattern)| { - let elem = ProjectionElem::ConstantIndex { - offset: (idx+1) as u32, + let place = place.clone().constant_index( + self.hir.tcx(), + (idx + 1) as u32, min_length, - from_end: true, - }; - let place = place.clone().elem(elem); + true, + ); MatchPair::new(place, subpattern) }) ); diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs index ae8070698c299..350673515a78d 100644 --- a/src/librustc_mir/build/misc.rs +++ b/src/librustc_mir/build/misc.rs @@ -26,7 +26,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// call `schedule_drop` once the temporary is initialized. pub fn temp(&mut self, ty: Ty<'tcx>, span: Span) -> Place<'tcx> { let temp = self.local_decls.push(LocalDecl::new_temp(ty, span)); - let place = Place::Local(temp); + let place = Place::local(temp); debug!("temp: created temp {:?} with type {:?}", place, self.local_decls[temp].ty); place diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 054bd69c361b9..216410bca704e 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -607,7 +607,7 @@ fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>, let mut block = START_BLOCK; let expr = builder.hir.mirror(ast_expr); - unpack!(block = builder.into_expr(&Place::Local(RETURN_PLACE), block, expr)); + unpack!(block = builder.into_expr(&Place::local(RETURN_PLACE), block, expr)); let source_info = builder.source_info(span); builder.cfg.terminate(block, source_info, TerminatorKind::Return); @@ -743,7 +743,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { for (index, arg_info) in arguments.iter().enumerate() { // Function arguments always get the first Local indices after the return place let local = Local::new(index + 1); - let place = Place::Local(local); + let place = Place::local(local); let &ArgInfo(ty, opt_ty_info, pattern, ref self_binding) = arg_info; if let Some(pattern) = pattern { @@ -791,7 +791,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } let body = self.hir.mirror(ast_body); - self.into(&Place::Local(RETURN_PLACE), block, body) + self.into(&Place::local(RETURN_PLACE), block, body) } fn get_unit_temp(&mut self) -> Place<'tcx> { diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 2dc5138c6f082..0b9266c85fb81 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -660,16 +660,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { match drop_kind { DropKind::Value { .. } => if !needs_drop { return }, DropKind::Storage => { - match *place { - Place::Local(index) => if index.index() <= self.arg_count { + match place { + Place { + base: PlaceBase::Local(index), + elems, + } => if index.index() <= self.arg_count + && elems.is_empty() { span_bug!( - span, "`schedule_drop` called with index {} and arg_count {}", + span, + "`schedule_drop` called with index {} and arg_count {}", index.index(), self.arg_count, ) }, _ => span_bug!( - span, "`schedule_drop` called with non-`Local` place {:?}", place + span, + "`schedule_drop` called with non-`Local` place {:?}", place ), } } @@ -937,7 +943,11 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, // Drop the storage for both value and storage drops. // Only temps and vars need their storage dead. match drop_data.location { - Place::Local(index) if index.index() > arg_count => { + Place { + base: PlaceBase::Local(index), + elems, + } if index.index() > arg_count + && elems.is_empty() => { cfg.push(block, Statement { source_info, kind: StatementKind::StorageDead(index) diff --git a/src/librustc_mir/dataflow/drop_flag_effects.rs b/src/librustc_mir/dataflow/drop_flag_effects.rs index 1cbe0dcc017f9..43693bf04f438 100644 --- a/src/librustc_mir/dataflow/drop_flag_effects.rs +++ b/src/librustc_mir/dataflow/drop_flag_effects.rs @@ -16,20 +16,20 @@ use super::{MoveDataParamEnv}; use super::indexes::MovePathIndex; use super::move_paths::{MoveData, LookupResult, InitKind}; -pub fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>, +pub fn move_path_children_matching<'tcx, F>(move_data: &'tcx MoveData<'tcx>, path: MovePathIndex, mut cond: F) -> Option - where F: FnMut(&mir::PlaceProjection<'tcx>) -> bool + where F: FnMut(&mir::PlaceElem<'tcx>) -> bool { let mut next_child = move_data.move_paths[path].first_child; while let Some(child_index) = next_child { - match move_data.move_paths[child_index].place { - mir::Place::Projection(ref proj) => { - if cond(proj) { - return Some(child_index) + match move_data.move_paths[child_index].place.projection() { + Some(projection) => { + if cond(&projection) { + return Some(child_index); } - } + }, _ => {} } next_child = move_data.move_paths[child_index].next_sibling; @@ -173,7 +173,7 @@ pub(crate) fn drop_flag_effects_for_function_entry<'a, 'gcx, 'tcx, F>( { let move_data = &ctxt.move_data; for arg in mir.args_iter() { - let place = mir::Place::Local(arg); + let place = mir::Place::local(arg); let lookup_result = move_data.rev_lookup.find(&place); on_lookup_result_bits(tcx, mir, move_data, lookup_result, diff --git a/src/librustc_mir/dataflow/impls/borrowed_locals.rs b/src/librustc_mir/dataflow/impls/borrowed_locals.rs index c8c41c13b0fbb..845848cd06f69 100644 --- a/src/librustc_mir/dataflow/impls/borrowed_locals.rs +++ b/src/librustc_mir/dataflow/impls/borrowed_locals.rs @@ -10,9 +10,9 @@ pub use super::*; -use rustc::mir::*; -use rustc::mir::visit::Visitor; use dataflow::BitDenotation; +use rustc::mir::visit::Visitor; +use rustc::mir::*; /// This calculates if any part of a MIR local could have previously been borrowed. /// This means that once a local has been borrowed, its bit will be set @@ -26,9 +26,8 @@ pub struct HaveBeenBorrowedLocals<'a, 'tcx: 'a> { } impl<'a, 'tcx: 'a> HaveBeenBorrowedLocals<'a, 'tcx> { - pub fn new(mir: &'a Mir<'tcx>) - -> Self { - HaveBeenBorrowedLocals { mir: mir } + pub fn new(mir: &'a Mir<'tcx>) -> Self { + HaveBeenBorrowedLocals { mir } } pub fn mir(&self) -> &Mir<'tcx> { @@ -38,7 +37,11 @@ impl<'a, 'tcx: 'a> HaveBeenBorrowedLocals<'a, 'tcx> { impl<'a, 'tcx> BitDenotation for HaveBeenBorrowedLocals<'a, 'tcx> { type Idx = Local; - fn name() -> &'static str { "has_been_borrowed_locals" } + + fn name() -> &'static str { + "has_been_borrowed_locals" + } + fn bits_per_block(&self) -> usize { self.mir.local_decls.len() } @@ -47,9 +50,7 @@ impl<'a, 'tcx> BitDenotation for HaveBeenBorrowedLocals<'a, 'tcx> { // Nothing is borrowed on function entry } - fn statement_effect(&self, - sets: &mut BlockSets, - loc: Location) { + fn statement_effect(&self, sets: &mut BlockSets, loc: Location) { let stmt = &self.mir[loc.block].statements[loc.statement_index]; BorrowedLocalsVisitor { @@ -63,19 +64,19 @@ impl<'a, 'tcx> BitDenotation for HaveBeenBorrowedLocals<'a, 'tcx> { } } - fn terminator_effect(&self, - sets: &mut BlockSets, - loc: Location) { + fn terminator_effect(&self, sets: &mut BlockSets, loc: Location) { BorrowedLocalsVisitor { sets, }.visit_terminator(loc.block, self.mir[loc.block].terminator(), loc); } - fn propagate_call_return(&self, - _in_out: &mut IdxSet, - _call_bb: mir::BasicBlock, - _dest_bb: mir::BasicBlock, - _dest_place: &mir::Place) { + fn propagate_call_return( + &self, + _in_out: &mut IdxSet, + _call_bb: mir::BasicBlock, + _dest_bb: mir::BasicBlock, + _dest_place: &mir::Place, + ) { // Nothing to do when a call returns successfully } } @@ -98,26 +99,28 @@ struct BorrowedLocalsVisitor<'b, 'c: 'b> { sets: &'b mut BlockSets<'c, Local>, } -fn find_local<'tcx>(place: &Place<'tcx>) -> Option { - match *place { - Place::Local(l) => Some(l), - Place::Promoted(_) | - Place::Static(..) => None, - Place::Projection(ref proj) => { - match proj.elem { - ProjectionElem::Deref => None, - _ => find_local(&proj.base) +impl<'b, 'c, 'tcx> BorrowedLocalsVisitor<'b, 'c> { + fn find_local(&self, place: &Place<'tcx>) -> Option { + let mut local = match place.base { + PlaceBase::Local(local) => Some(local), + PlaceBase::Promoted(_) | PlaceBase::Static(..) => None, + }; + + for elem in place.elems.iter() { + match elem { + ProjectionElem::Deref => local = None, + _ => {}, } } + + local } } impl<'tcx, 'b, 'c> Visitor<'tcx> for BorrowedLocalsVisitor<'b, 'c> { - fn visit_rvalue(&mut self, - rvalue: &Rvalue<'tcx>, - location: Location) { + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { if let Rvalue::Ref(_, _, ref place) = *rvalue { - if let Some(local) = find_local(place) { + if let Some(local) = self.find_local(&place) { self.sets.gen(&local); } } diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index ea59e42fd479b..383ad8fa09cda 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -15,7 +15,7 @@ use rustc; use rustc::hir; use rustc::hir::def_id::DefId; use rustc::middle::region; -use rustc::mir::{self, Location, Place, Mir}; +use rustc::mir::{self, Location, PlaceBase, Mir}; use rustc::ty::TyCtxt; use rustc::ty::{RegionKind, RegionVid}; use rustc::ty::RegionKind::ReScope; @@ -221,10 +221,12 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { mir::StatementKind::Assign(ref lhs, ref rhs) => { // Make sure there are no remaining borrows for variables // that are assigned over. - if let Place::Local(ref local) = *lhs { - // FIXME: Handle the case in which we're assigning over - // a projection (`foo.bar`). - self.kill_borrows_on_local(sets, local); + if lhs.has_no_projection() { + if let PlaceBase::Local(local) = lhs.base { + // FIXME: Handle the case in which we're assigning over + // a projection (`foo.bar`). + self.kill_borrows_on_local(sets, &local); + } } // NOTE: if/when the Assign case is revised to inspect @@ -258,16 +260,12 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { // Issue #46746: Two-phase borrows handles // stmts of form `Tmp = &mut Borrow` ... - match lhs { - Place::Promoted(_) | - Place::Local(..) | Place::Static(..) => {} // okay - Place::Projection(..) => { - // ... can assign into projections, - // e.g. `box (&mut _)`. Current - // conservative solution: force - // immediate activation here. - sets.gen(&index); - } + if !lhs.has_no_projection() { + // ... can assign into projections, + // e.g. `box (&mut _)`. Current + // conservative solution: force + // immediate activation here. + sets.gen(&index); } } } @@ -283,10 +281,12 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { if !kind.is_indirect && !kind.is_rw { // Make sure there are no remaining borrows for direct // output variables. - if let Place::Local(ref local) = *output { + if let PlaceBase::Local(local) = output.base { // FIXME: Handle the case in which we're assigning over // a projection (`foo.bar`). - self.kill_borrows_on_local(sets, local); + if output.has_no_projection() { + self.kill_borrows_on_local(sets, &local); + } } } } diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs index ee3bba840c67b..b479b1499c616 100644 --- a/src/librustc_mir/dataflow/impls/mod.rs +++ b/src/librustc_mir/dataflow/impls/mod.rs @@ -615,7 +615,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedPlaces<'a, 'gcx, 'tcx> { // storagedeads after everything ends, so if we don't regard the // storagelive as killing storage, we would have a multiple assignment // to immutable data error. - if let LookupResult::Exact(mpi) = rev_lookup.find(&mir::Place::Local(local)) { + if let LookupResult::Exact(mpi) = rev_lookup.find(&mir::Place::local(local)) { debug!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}", stmt, location, &init_path_map[mpi]); sets.kill_all(&init_path_map[mpi]); diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 44e46e90549a7..9c16539aff6ab 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::ty::{self, TyCtxt}; +use rustc::ty::{self, Ty, TyCtxt}; use rustc::mir::*; -use rustc::mir::tcx::RvalueInitializationState; +use rustc::mir::tcx::{PlaceTy, RvalueInitializationState}; use rustc::util::nodemap::FxHashMap; use rustc_data_structures::indexed_vec::{IndexVec}; @@ -44,7 +44,7 @@ impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> { moves: IndexVec::new(), loc_map: LocationMap::new(mir), rev_lookup: MovePathLookup { - locals: mir.local_decls.indices().map(Place::Local).map(|v| { + locals: mir.local_decls.indices().map(Place::local).map(|v| { Self::new_move_path( &mut move_paths, &mut path_map, @@ -106,16 +106,22 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { -> Result> { debug!("lookup({:?})", place); - match *place { - Place::Local(local) => Ok(self.builder.data.rev_lookup.locals[local]), - Place::Promoted(..) | - Place::Static(..) => { - Err(MoveError::cannot_move_out_of(self.loc, Static)) - } - Place::Projection(ref proj) => { - self.move_path_for_projection(place, proj) - } + let mut result = match place.base { + PlaceBase::Local(local) => self.builder.data.rev_lookup.locals[local], + PlaceBase::Promoted(..) | + PlaceBase::Static(..) => + return Err(MoveError::cannot_move_out_of(self.loc, Static)), + }; + + let mir = self.builder.mir; + let tcx = self.builder.tcx; + let mut result_ty = place.base.ty(mir); + for elem in place.elems.iter() { + result = self.move_path_for_projection(place, result, result_ty, elem)?; + result_ty = PlaceTy::from(result_ty).projection_ty(tcx, elem).to_ty(tcx); } + + Ok(result) } fn create_move_path(&mut self, place: &Place<'tcx>) { @@ -124,16 +130,15 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { let _ = self.move_path_for(place); } - fn move_path_for_projection(&mut self, - place: &Place<'tcx>, - proj: &PlaceProjection<'tcx>) - -> Result> - { - let base = try!(self.move_path_for(&proj.base)); - let mir = self.builder.mir; + fn move_path_for_projection( + &mut self, + place: &Place<'tcx>, + base: MovePathIndex, + base_ty: Ty<'tcx>, + projection: &PlaceElem<'tcx> + ) -> Result> { let tcx = self.builder.tcx; - let place_ty = proj.base.ty(mir, tcx).to_ty(tcx); - match place_ty.sty { + match base_ty.sty { ty::TyRef(..) | ty::TyRawPtr(..) => return Err(MoveError::cannot_move_out_of( self.loc, @@ -141,7 +146,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { ty::TyAdt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() => return Err(MoveError::cannot_move_out_of(self.loc, InteriorOfTypeWithDestructor { - container_ty: place_ty + container_ty: base_ty })), // move out of union - always move the entire union ty::TyAdt(adt, _) if adt.is_union() => @@ -150,17 +155,17 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { return Err(MoveError::cannot_move_out_of( self.loc, InteriorOfSliceOrArray { - ty: place_ty, is_index: match proj.elem { + ty: base_ty, is_index: match projection { ProjectionElem::Index(..) => true, _ => false }, })), - ty::TyArray(..) => match proj.elem { + ty::TyArray(..) => match projection { ProjectionElem::Index(..) => return Err(MoveError::cannot_move_out_of( self.loc, InteriorOfSliceOrArray { - ty: place_ty, is_index: true + ty: base_ty, is_index: true })), _ => { // FIXME: still badly broken @@ -168,7 +173,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { }, _ => {} }; - match self.builder.data.rev_lookup.projections.entry((base, proj.elem.lift())) { + match self.builder.data.rev_lookup.projections.entry((base, projection.lift())) { Entry::Occupied(ent) => Ok(*ent.get()), Entry::Vacant(ent) => { let path = MoveDataBuilder::new_move_path( @@ -272,7 +277,8 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { // Box starts out uninitialized - need to create a separate // move-path for the interior so it will be separate from // the exterior. - self.create_move_path(&place.clone().deref()); + let tcx = self.builder.tcx; + self.create_move_path(&place.clone().deref(tcx)); self.gather_init(place, InitKind::Shallow); } else { self.gather_init(place, InitKind::Deep); @@ -294,7 +300,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { } StatementKind::StorageLive(_) => {} StatementKind::StorageDead(local) => { - self.gather_move(&Place::Local(local)); + self.gather_move(&Place::local(local)); } StatementKind::SetDiscriminant{ .. } => { span_bug!(stmt.source_info.span, @@ -355,7 +361,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { TerminatorKind::Unreachable => { } TerminatorKind::Return => { - self.gather_move(&Place::Local(RETURN_PLACE)); + self.gather_move(&Place::local(RETURN_PLACE)); } TerminatorKind::Assert { ref cond, .. } => { diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 64bfd36b7eeb6..ce340fcf2bbe7 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -12,8 +12,8 @@ use rustc::ty::{self, TyCtxt}; use rustc::mir::*; use rustc::util::nodemap::FxHashMap; -use rustc_data_structures::indexed_vec::{IndexVec}; -use syntax_pos::{Span}; +use rustc_data_structures::indexed_vec::IndexVec; +use syntax_pos::Span; use std::fmt; use std::ops::{Index, IndexMut}; @@ -246,23 +246,29 @@ impl<'tcx> MovePathLookup<'tcx> { // alternative will *not* create a MovePath on the fly for an // unknown place, but will rather return the nearest available // parent. - pub fn find(&self, place: &Place<'tcx>) -> LookupResult { - match *place { - Place::Local(local) => LookupResult::Exact(self.locals[local]), - Place::Promoted(_) | - Place::Static(..) => LookupResult::Parent(None), - Place::Projection(ref proj) => { - match self.find(&proj.base) { - LookupResult::Exact(base_path) => { - match self.projections.get(&(base_path, proj.elem.lift())) { - Some(&subpath) => LookupResult::Exact(subpath), - None => LookupResult::Parent(Some(base_path)) - } + pub fn find<'a>( + &self, + place: &Place<'tcx> + ) -> LookupResult { + let mut result = match place.base { + PlaceBase::Local(local) => LookupResult::Exact(self.locals[local]), + PlaceBase::Promoted(_) | + PlaceBase::Static(..) => LookupResult::Parent(None), + }; + + for elem in place.elems.iter() { + result = match result { + LookupResult::Exact(base_path) => { + match self.projections.get(&(base_path, elem.lift())) { + Some(subpath) => LookupResult::Exact(*subpath), + None => LookupResult::Parent(Some(base_path)), } - inexact => inexact } - } + inexact => inexact, + }; } + + result } pub fn find_local(&self, local: Local) -> MovePathIndex { diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 91c2519230695..928952e7126fc 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -1,4 +1,5 @@ use rustc::mir; +use rustc::mir::tcx::PlaceTy; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; use rustc_data_structures::indexed_vec::Idx; @@ -101,18 +102,19 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { &self, place: &mir::Place<'tcx>, ) -> EvalResult<'tcx, Option> { - use rustc::mir::Place::*; - match *place { - // Might allow this in the future, right now there's no way to do this from Rust code anyway - Local(mir::RETURN_PLACE) => err!(ReadFromReturnPointer), - // Directly reading a local will always succeed - Local(local) => self.frame().locals[local].access().map(Some), - // No fast path for statics. Reading from statics is rare and would require another - // Machine function to handle differently in miri. - Promoted(_) | - Static(_) => Ok(None), - Projection(ref proj) => self.try_read_place_projection(proj), + let mut result = self.try_read_place_base(&place.base)?; + let mut base_ty = place.base.ty(self.mir()); + let mut base_layout = self.layout_of(base_ty)?; + + for elem in place.elems.iter() { + result = self.try_read_place_projection(result, elem, base_layout)?; + + base_ty = PlaceTy::from(base_ty) + .projection_ty(*self.tcx, elem).to_ty(*self.tcx); + base_layout = self.layout_of(base_ty)?; } + + Ok(result) } pub fn read_field( @@ -155,26 +157,47 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { Ok((value, field)) } + fn try_read_place_base( + &self, + place_base: &mir::PlaceBase<'tcx>, + ) -> EvalResult<'tcx, Option> { + use rustc::mir::PlaceBase::*; + match place_base { + Local(local) => { + if *local == mir::RETURN_PLACE { + // Might allow this in the future, right now there's no way to do this from Rust code anyway + err!(ReadFromReturnPointer) + } else { + // Directly reading a local will always succeed + self.frame().locals[*local].access().map(Some) + } + }, + // No fast path for statics. Reading from statics is rare and would require another + // Machine function to handle differently in miri. + Promoted(_) | Static(_) => Ok(None), + } + } + fn try_read_place_projection( &self, - proj: &mir::PlaceProjection<'tcx>, + base: Option, + proj: &mir::PlaceElem<'tcx>, + base_layout: TyLayout<'tcx>, ) -> EvalResult<'tcx, Option> { use rustc::mir::ProjectionElem::*; - let base = match self.try_read_place(&proj.base)? { - Some(base) => base, - None => return Ok(None), - }; - let base_ty = self.place_ty(&proj.base); - let base_layout = self.layout_of(base_ty)?; - match proj.elem { - Field(field, _) => Ok(Some(self.read_field(base, None, field, base_layout)?.0)), - // The NullablePointer cases should work fine, need to take care for normal enums - Downcast(..) | - Subslice { .. } | - // reading index 0 or index 1 from a ByVal or ByVal pair could be optimized - ConstantIndex { .. } | Index(_) | - // No way to optimize this projection any better than the normal place path - Deref => Ok(None), + if let Some(base) = base { + match proj { + Field(field, _) => Ok(Some(self.read_field(base, None, *field, base_layout)?.0)), + // The NullablePointer cases should work fine, need to take care for normal enums + Downcast(..) | + Subslice { .. } | + // reading index 0 or index 1 from a ByVal or ByVal pair could be optimized + ConstantIndex { .. } | Index(_) | + // No way to optimize this projection any better than the normal place path + Deref => Ok(None), + } + } else { + return Ok(None) } } @@ -204,8 +227,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } pub fn eval_place(&mut self, mir_place: &mir::Place<'tcx>) -> EvalResult<'tcx, Place> { - use rustc::mir::Place::*; - let place = match *mir_place { + use rustc::mir::PlaceBase::*; + + let mut place = match mir_place.base { Local(mir::RETURN_PLACE) => self.frame().return_place, Local(local) => Place::Local { frame: self.cur_frame(), @@ -243,14 +267,16 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { extra: PlaceExtra::None, } } - - Projection(ref proj) => { - let ty = self.place_ty(&proj.base); - let place = self.eval_place(&proj.base)?; - return self.eval_place_projection(place, ty, &proj.elem); - } }; + let mut ty = mir_place.base.ty(self.mir()); + for elem in mir_place.elems.iter() { + ty = self.monomorphize(ty, self.substs()); + place = self.eval_place_projection(place, ty, elem)?; + ty = PlaceTy::from(ty) + .projection_ty(*self.tcx, elem).to_ty(*self.tcx); + } + self.dump_local(place); Ok(place) @@ -383,17 +409,17 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { &mut self, base: Place, base_ty: Ty<'tcx>, - proj_elem: &mir::ProjectionElem<'tcx, mir::Local, Ty<'tcx>>, + place_elem: &mir::PlaceElem<'tcx>, ) -> EvalResult<'tcx, Place> { use rustc::mir::ProjectionElem::*; - match *proj_elem { + match place_elem { Field(field, _) => { let layout = self.layout_of(base_ty)?; - Ok(self.place_field(base, field, layout)?.0) + Ok(self.place_field(base, *field, layout)?.0) } Downcast(_, variant) => { - self.place_downcast(base, variant) + self.place_downcast(base, *variant) } Deref => { @@ -412,7 +438,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } Index(local) => { - let value = self.frame().locals[local].access()?; + let value = self.frame().locals[*local].access()?; let ty = self.tcx.types.usize; let n = self .value_to_scalar(ValTy { value, ty })? @@ -431,12 +457,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { let (elem_ty, n) = base.elem_ty_and_len(base_ty, self.tcx.tcx); let elem_size = self.layout_of(elem_ty)?.size; - assert!(n >= min_length as u64); + assert!(n >= *min_length as u64); - let index = if from_end { - n - u64::from(offset) + let index = if *from_end { + n - u64::from(*offset) } else { - u64::from(offset) + u64::from(*offset) }; let ptr = base_ptr.ptr_offset(elem_size * index, &self)?; @@ -450,13 +476,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { let (elem_ty, n) = base.elem_ty_and_len(base_ty, self.tcx.tcx); let elem_size = self.layout_of(elem_ty)?.size; - assert!(u64::from(from) <= n - u64::from(to)); - let ptr = base_ptr.ptr_offset(elem_size * u64::from(from), &self)?; + assert!(u64::from(*from) <= n - u64::from(*to)); + let ptr = base_ptr.ptr_offset(elem_size * u64::from(*from), &self)?; // sublicing arrays produces arrays let extra = if self.type_is_sized(base_ty) { PlaceExtra::None } else { - PlaceExtra::Length(n - u64::from(to) - u64::from(from)) + PlaceExtra::Length(n - u64::from(*to) - u64::from(*from)) }; Ok(Place::Ptr { ptr, align, extra }) } diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index 682e384da3936..a09ba9907dec8 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -287,7 +287,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { // closure as closure once Abi::RustCall => { for (arg_local, &valty) in arg_locals.zip(args) { - let dest = self.eval_place(&mir::Place::Local(arg_local))?; + let dest = self.eval_place(&mir::Place::local(arg_local))?; self.write_value(valty, dest)?; } } @@ -301,8 +301,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { ); trace!("args: {:#?}", args); let local = arg_locals.nth(1).unwrap(); + let tcx = *self.tcx; for (i, &valty) in args.into_iter().enumerate() { - let dest = self.eval_place(&mir::Place::Local(local).field( + let dest = self.eval_place(&mir::Place::local(local).field( + tcx, mir::Field::new(i), valty.ty, ))?; @@ -337,7 +339,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { { // write first argument let first_local = arg_locals.next().unwrap(); - let dest = self.eval_place(&mir::Place::Local(first_local))?; + let dest = self.eval_place(&mir::Place::local(first_local))?; self.write_value(args[0], dest)?; } @@ -352,7 +354,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { for (i, arg_local) in arg_locals.enumerate() { let field = mir::Field::new(i); let (value, layout) = self.read_field(args[1].value, None, field, layout)?; - let dest = self.eval_place(&mir::Place::Local(arg_local))?; + let dest = self.eval_place(&mir::Place::local(arg_local))?; let valty = ValTy { value, ty: layout.ty, @@ -363,7 +365,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { trace!("manual impl of rust-call ABI"); // called a manual impl of a rust-call function let dest = self.eval_place( - &mir::Place::Local(arg_locals.next().unwrap()), + &mir::Place::local(arg_locals.next().unwrap()), )?; self.write_value(args[1], dest)?; } @@ -377,7 +379,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } _ => { for (arg_local, &valty) in arg_locals.zip(args) { - let dest = self.eval_place(&mir::Place::Local(arg_local))?; + let dest = self.eval_place(&mir::Place::local(arg_local))?; self.write_value(valty, dest)?; } } diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 7bfbda8b786e3..cafb5b7f68b60 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -216,7 +216,7 @@ fn build_drop_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx, param_env }; - let dropee = Place::Local(Local::new(1+0)).deref(); + let dropee = Place::local(Local::new(1+0)).deref(tcx); let resume_block = elaborator.patch.resume_block(); elaborate_drops::elaborate_drop( &mut elaborator, @@ -296,8 +296,8 @@ fn build_clone_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut builder = CloneShimBuilder::new(tcx, def_id, self_ty); let is_copy = !self_ty.moves_by_default(tcx, tcx.param_env(def_id), builder.span); - let dest = Place::Local(RETURN_PLACE); - let src = Place::Local(Local::new(1+0)).deref(); + let dest = Place::local(RETURN_PLACE); + let src = Place::local(Local::new(1)).deref(tcx); match self_ty.sty { _ if is_copy => builder.copy_shim(), @@ -401,10 +401,10 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { } fn copy_shim(&mut self) { - let rcvr = Place::Local(Local::new(1+0)).deref(); + let rcvr = Place::local(Local::new(1+0)).deref(self.tcx); let ret_statement = self.make_statement( StatementKind::Assign( - Place::Local(RETURN_PLACE), + Place::local(RETURN_PLACE), Rvalue::Use(Operand::Copy(rcvr)) ) ); @@ -413,7 +413,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { fn make_place(&mut self, mutability: Mutability, ty: Ty<'tcx>) -> Place<'tcx> { let span = self.span; - Place::Local( + Place::local( self.local_decls.push(temp_decl(mutability, ty, span)) ) } @@ -516,7 +516,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { let inits = vec![ self.make_statement( StatementKind::Assign( - Place::Local(beg), + Place::local(beg), Rvalue::Use(Operand::Constant(self.make_usize(0))) ) ), @@ -534,13 +534,13 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { // BB #3; // } // BB #4; - self.loop_header(Place::Local(beg), end, BasicBlock::new(2), BasicBlock::new(4), false); + self.loop_header(Place::local(beg), end, BasicBlock::new(2), BasicBlock::new(4), false); // BB #2 // `dest[i] = Clone::clone(src[beg])`; // Goto #3 if ok, #5 if unwinding happens. - let dest_field = dest.clone().index(beg); - let src_field = src.clone().index(beg); + let dest_field = dest.clone().index(self.tcx, beg); + let src_field = src.clone().index(self.tcx, beg); self.make_clone_call(dest_field, src_field, ty, BasicBlock::new(3), BasicBlock::new(5)); @@ -550,10 +550,10 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { let statements = vec![ self.make_statement( StatementKind::Assign( - Place::Local(beg), + Place::local(beg), Rvalue::BinaryOp( BinOp::Add, - Operand::Copy(Place::Local(beg)), + Operand::Copy(Place::local(beg)), Operand::Constant(self.make_usize(1)) ) ) @@ -573,7 +573,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { let beg = self.local_decls.push(temp_decl(Mutability::Mut, tcx.types.usize, span)); let init = self.make_statement( StatementKind::Assign( - Place::Local(beg), + Place::local(beg), Rvalue::Use(Operand::Constant(self.make_usize(0))) ) ); @@ -584,13 +584,14 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { // BB #8; // } // BB #9; - self.loop_header(Place::Local(beg), Place::Local(end), + self.loop_header(Place::local(beg), Place::local(end), BasicBlock::new(7), BasicBlock::new(9), true); // BB #7 (cleanup) // `drop(dest[beg])`; + let tcx = self.tcx; self.block(vec![], TerminatorKind::Drop { - location: dest.index(beg), + location: dest.index(tcx, beg), target: BasicBlock::new(8), unwind: None, }, true); @@ -600,10 +601,10 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { // `goto #6;` let statement = self.make_statement( StatementKind::Assign( - Place::Local(beg), + Place::local(beg), Rvalue::BinaryOp( BinOp::Add, - Operand::Copy(Place::Local(beg)), + Operand::Copy(Place::local(beg)), Operand::Constant(self.make_usize(1)) ) ) @@ -620,9 +621,9 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { let mut previous_field = None; for (i, ity) in tys.enumerate() { let field = Field::new(i); - let src_field = src.clone().field(field, ity); + let src_field = src.clone().field(self.tcx, field, ity); - let dest_field = dest.clone().field(field, ity); + let dest_field = dest.clone().field(self.tcx, field, ity); // #(2i + 1) is the cleanup block for the previous clone operation let cleanup_block = self.block_index_offset(1); @@ -688,12 +689,12 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let source_info = SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE }; let rcvr_arg = Local::new(1+0); - let rcvr_l = Place::Local(rcvr_arg); + let rcvr_l = Place::local(rcvr_arg); let mut statements = vec![]; let rcvr = match rcvr_adjustment { Adjustment::Identity => Operand::Move(rcvr_l), - Adjustment::Deref => Operand::Copy(rcvr_l.deref()), + Adjustment::Deref => Operand::Copy(rcvr_l.deref(tcx)), Adjustment::RefMut => { // let rcvr = &mut rcvr; let ref_rcvr = local_decls.push(temp_decl( @@ -710,11 +711,11 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, statements.push(Statement { source_info, kind: StatementKind::Assign( - Place::Local(ref_rcvr), + Place::local(ref_rcvr), Rvalue::Ref(tcx.types.re_erased, borrow_kind, rcvr_l) ) }); - Operand::Move(Place::Local(ref_rcvr)) + Operand::Move(Place::local(ref_rcvr)) } }; @@ -733,12 +734,12 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if let Some(untuple_args) = untuple_args { args.extend(untuple_args.iter().enumerate().map(|(i, ity)| { - let arg_place = Place::Local(Local::new(1+1)); - Operand::Move(arg_place.field(Field::new(i), *ity)) + let arg_place = Place::local(Local::new(1+1)); + Operand::Move(arg_place.field(tcx, Field::new(i), *ity)) })); } else { args.extend((1..sig.inputs().len()).map(|i| { - Operand::Move(Place::Local(Local::new(1+i))) + Operand::Move(Place::local(Local::new(1+i))) })); } @@ -755,7 +756,7 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, block(&mut blocks, statements, TerminatorKind::Call { func: callee, args, - destination: Some((Place::Local(RETURN_PLACE), + destination: Some((Place::local(RETURN_PLACE), BasicBlock::new(1))), cleanup: if let Adjustment::RefMut = rcvr_adjustment { Some(BasicBlock::new(3)) @@ -767,7 +768,7 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if let Adjustment::RefMut = rcvr_adjustment { // BB #1 - drop for Self block(&mut blocks, vec![], TerminatorKind::Drop { - location: Place::Local(rcvr_arg), + location: Place::local(rcvr_arg), target: BasicBlock::new(2), unwind: None }, false); @@ -777,7 +778,7 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if let Adjustment::RefMut = rcvr_adjustment { // BB #3 - drop if closure panics block(&mut blocks, vec![], TerminatorKind::Drop { - location: Place::Local(rcvr_arg), + location: Place::local(rcvr_arg), target: BasicBlock::new(4), unwind: None }, true); @@ -845,11 +846,11 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, statements: vec![Statement { source_info, kind: StatementKind::Assign( - Place::Local(RETURN_PLACE), + Place::local(RETURN_PLACE), Rvalue::Aggregate( box AggregateKind::Adt(adt_def, variant_no, substs, None), (1..sig.inputs().len()+1).map(|i| { - Operand::Move(Place::Local(Local::new(i))) + Operand::Move(Place::local(Local::new(i))) }).collect() ) ) diff --git a/src/librustc_mir/transform/add_moves_for_packed_drops.rs b/src/librustc_mir/transform/add_moves_for_packed_drops.rs index 203669c61badd..c2d4a40968224 100644 --- a/src/librustc_mir/transform/add_moves_for_packed_drops.rs +++ b/src/librustc_mir/transform/add_moves_for_packed_drops.rs @@ -131,10 +131,10 @@ fn add_move_for_packed_drop<'a, 'tcx>( patch.add_statement( loc, StatementKind::StorageLive(temp)); - patch.add_assign(loc, Place::Local(temp), + patch.add_assign(loc, Place::local(temp), Rvalue::Use(Operand::Move(location.clone()))); patch.patch_terminator(loc.block, TerminatorKind::Drop { - location: Place::Local(temp), + location: Place::local(temp), target: storage_dead_block, unwind }); diff --git a/src/librustc_mir/transform/add_validation.rs b/src/librustc_mir/transform/add_validation.rs index 4f7f45f173f73..4b9ae18c5f463 100644 --- a/src/librustc_mir/transform/add_validation.rs +++ b/src/librustc_mir/transform/add_validation.rs @@ -30,55 +30,56 @@ fn place_context<'a, 'tcx, D>( ) -> (Option, hir::Mutability) where D: HasLocalDecls<'tcx> { - use rustc::mir::Place::*; + let mut place_context = match place.base { + PlaceBase::Local { .. } => (None, hir::MutMutable), + PlaceBase::Promoted(_) + | PlaceBase::Static(_) => (None, hir::MutImmutable), + }; - match *place { - Local { .. } => (None, hir::MutMutable), - Promoted(_) | - Static(_) => (None, hir::MutImmutable), - Projection(ref proj) => { - match proj.elem { - ProjectionElem::Deref => { - // Computing the inside the recursion makes this quadratic. - // We don't expect deep paths though. - let ty = proj.base.ty(local_decls, tcx).to_ty(tcx); - // A Deref projection may restrict the context, this depends on the type - // being deref'd. - let context = match ty.sty { - ty::TyRef(re, _, mutbl) => { - let re = match re { - &RegionKind::ReScope(ce) => Some(ce), - &RegionKind::ReErased => - bug!("AddValidation pass must be run before erasing lifetimes"), - _ => None - }; - (re, mutbl) - } - ty::TyRawPtr(_) => - // There is no guarantee behind even a mutable raw pointer, - // no write locks are acquired there, so we also don't want to - // release any. - (None, hir::MutImmutable), - ty::TyAdt(adt, _) if adt.is_box() => (None, hir::MutMutable), - _ => bug!("Deref on a non-pointer type {:?}", ty), - }; - // "Intersect" this restriction with proj.base. - if let (Some(_), hir::MutImmutable) = context { - // This is already as restricted as it gets, no need to even recurse - context - } else { - let base_context = place_context(&proj.base, local_decls, tcx); - // The region of the outermost Deref is always most restrictive. - let re = context.0.or(base_context.0); - let mutbl = context.1.and(base_context.1); + let mut base_ty = place.base.ty(local_decls); + let mut base_context = place_context; + for elem in place.elems.iter() { + match elem { + ProjectionElem::Deref => { + // A Deref projection may restrict the context, this depends on the type + // being deref'd. + place_context = match base_ty.sty { + ty::TyRef(re, _, mutbl) => { + let re = match re { + &RegionKind::ReScope(ce) => Some(ce), + &RegionKind::ReErased => + bug!("AddValidation pass must be run before erasing lifetimes"), + _ => None + }; (re, mutbl) } - + ty::TyRawPtr(_) => + // There is no guarantee behind even a mutable raw pointer, + // no write locks are acquired there, so we also don't want to + // release any. + (None, hir::MutImmutable), + ty::TyAdt(adt, _) if adt.is_box() => (None, hir::MutMutable), + _ => bug!("Deref on a non-pointer type {:?}", base_ty), + }; + // "Intersect" this restriction with proj.base. + if let (Some(_), hir::MutImmutable) = place_context { + // This is already as restricted as it gets, no need to even recurse + } else { + let re = place_context.0.or(base_context.0); + let mutbl = place_context.1.and(base_context.1); + place_context = (re, mutbl); } - _ => place_context(&proj.base, local_decls, tcx), - } + + }, + _ => {}, } + base_ty = tcx::PlaceTy::from(base_ty) + .projection_ty(tcx, elem) + .to_ty(tcx); + base_context = place_context; } + + place_context } /// Check if this function contains an unsafe block or is an unsafe function. @@ -239,7 +240,7 @@ impl MirPass for AddValidation { }; // Gather all arguments, skip return value. let operands = local_decls.iter_enumerated().skip(1).take(arg_count) - .map(|(local, _)| place_to_operand(Place::Local(local))).collect(); + .map(|(local, _)| place_to_operand(Place::local(local))).collect(); emit_acquire(&mut basic_blocks[START_BLOCK], source_info, operands); } @@ -336,7 +337,7 @@ impl MirPass for AddValidation { let acquire_stmt = Statement { source_info: block_data.statements[i].source_info, kind: StatementKind::Validate(ValidationOp::Acquire, - vec![place_to_operand(dest_place.deref())]), + vec![place_to_operand(dest_place.deref(tcx))]), }; block_data.statements.insert(i+1, acquire_stmt); diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index aba39966110ad..4e196352e4158 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -164,89 +164,94 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { } } - match place { - &Place::Projection(box Projection { - ref base, ref elem - }) => { - let old_source_info = self.source_info; - if let &Place::Local(local) = base { - if self.mir.local_decls[local].internal { - // Internal locals are used in the `move_val_init` desugaring. - // We want to check unsafety against the source info of the - // desugaring, rather than the source info of the RHS. - self.source_info = self.mir.local_decls[local].source_info; - } + if !place.has_no_projection() { + let old_source_info = self.source_info; + if let PlaceBase::Local(local) = place.base { + if self.mir.local_decls[local].internal { + // Internal locals are used in the `move_val_init` desugaring. + // We want to check unsafety against the source info of the + // desugaring, rather than the source info of the RHS. + self.source_info = self.mir.local_decls[local].source_info; } - let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx); + } + let mut base_ty = place.base.ty(self.mir); + for elem in place.elems.iter() { match base_ty.sty { ty::TyRawPtr(..) => { self.require_unsafe("dereference of raw pointer", - "raw pointers may be NULL, dangling or unaligned; they can violate \ - aliasing rules and cause data races: all of these are undefined \ - behavior") + "raw pointers may be NULL, dangling or unaligned; \ + they can violate aliasing rules and cause data races: \ + all of these are undefined behavior") } ty::TyAdt(adt, _) => { if adt.is_union() { if context == PlaceContext::Store || context == PlaceContext::AsmOutput || context == PlaceContext::Drop - { - let elem_ty = match elem { - &ProjectionElem::Field(_, ty) => ty, - _ => span_bug!( - self.source_info.span, - "non-field projection {:?} from union?", - place) - }; - if elem_ty.moves_by_default(self.tcx, self.param_env, - self.source_info.span) { - self.require_unsafe( - "assignment to non-`Copy` union field", - "the previous content of the field will be dropped, which \ - causes undefined behavior if the field was not properly \ - initialized") + { + let projection_ty = match elem { + ProjectionElem::Field(_, ty) => ty, + _ => span_bug!( + self.source_info.span, + "non-field projection {:?} from union?", + place) + }; + if projection_ty.moves_by_default(self.tcx, self.param_env, + self.source_info.span) { + self.require_unsafe( + "assignment to non-`Copy` union field", + "the previous content of the field will be dropped, \ + which causes undefined behavior if the field was not \ + properly initialized") + } else { + // write to non-move union, safe + } } else { - // write to non-move union, safe - } - } else { self.require_unsafe("access to union field", - "the field may not be properly initialized: using \ - uninitialized data will cause undefined behavior") + "the field may not be properly initialized: \ + using uninitialized data will cause \ + undefined behavior") } } } _ => {} } - self.source_info = old_source_info; - } - &Place::Local(..) => { - // locals are safe + base_ty = tcx::PlaceTy::from(base_ty) + .projection_ty(self.tcx, elem).to_ty(self.tcx); } - &Place::Promoted(_) => { - bug!("unsafety checking should happen before promotion") - } - &Place::Static(box Static { def_id, ty: _ }) => { - if self.tcx.is_static(def_id) == Some(hir::Mutability::MutMutable) { - self.require_unsafe("use of mutable static", - "mutable statics can be mutated by multiple threads: aliasing violations \ - or data races will cause undefined behavior"); - } else if self.tcx.is_foreign_item(def_id) { - let source_info = self.source_info; - let lint_root = - self.source_scope_local_data[source_info.scope].lint_root; - self.register_violations(&[UnsafetyViolation { - source_info, - description: Symbol::intern("use of extern static").as_interned_str(), - details: + self.source_info = old_source_info; + } else { + match place.base { + PlaceBase::Local(..) => { + // locals are safe + } + PlaceBase::Promoted(_) => { + bug!("unsafety checking should happen before promotion") + } + PlaceBase::Static(box Static { def_id, ty: _ }) => { + if self.tcx.is_static(def_id) == Some(hir::Mutability::MutMutable) { + self.require_unsafe("use of mutable static", + "mutable statics can be mutated by \ + multiple threads: aliasing violations \ + or data races will cause undefined behavior"); + } else if self.tcx.is_foreign_item(def_id) { + let source_info = self.source_info; + let lint_root = + self.source_scope_local_data[source_info.scope].lint_root; + self.register_violations(&[UnsafetyViolation { + source_info, + description: Symbol::intern("use of extern static").as_interned_str(), + details: Symbol::intern("extern statics are not controlled by the Rust type \ system: invalid data, aliasing violations or data \ races will cause undefined behavior") .as_interned_str(), - kind: UnsafetyViolationKind::ExternStatic(lint_root) - }], &[]); + kind: UnsafetyViolationKind::ExternStatic(lint_root) + }], &[]); + } } } - }; + } self.super_place(place, context, location); } } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 05e51c5430d7f..08c3797305e13 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -13,7 +13,7 @@ use rustc::hir::def::Def; -use rustc::mir::{Constant, Location, Place, Mir, Operand, Rvalue, Local}; +use rustc::mir::{Constant, Location, Place, PlaceBase, Mir, Operand, Rvalue, Local}; use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind}; use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem}; use rustc::mir::visit::{Visitor, PlaceContext}; @@ -279,49 +279,54 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { } fn eval_place(&mut self, place: &Place<'tcx>, source_info: SourceInfo) -> Option> { - match *place { - Place::Local(loc) => self.places[loc].clone(), - Place::Projection(ref proj) => match proj.elem { - ProjectionElem::Field(field, _) => { - trace!("field proj on {:?}", proj.base); - let (base, layout, span) = self.eval_place(&proj.base, source_info)?; - let valty = self.use_ecx(source_info, |this| { - this.ecx.read_field(base, None, field, layout) - })?; - Some((valty.0, valty.1, span)) - }, - _ => None, - }, - Place::Promoted(ref promoted) => { - let generics = self.tcx.generics_of(self.source.def_id); - if generics.requires_monomorphization(self.tcx) { - // FIXME: can't handle code with generics - return None; - } - let substs = Substs::identity_for_item(self.tcx, self.source.def_id); - let instance = Instance::new(self.source.def_id, substs); - let cid = GlobalId { - instance, - promoted: Some(promoted.0), - }; - // cannot use `const_eval` here, because that would require having the MIR - // for the current function available, but we're producing said MIR right now - let (value, _, ty) = self.use_ecx(source_info, |this| { - eval_promoted(&mut this.ecx, cid, this.mir, this.param_env) - })?; - let val = (value, ty, source_info.span); - trace!("evaluated promoted {:?} to {:?}", promoted, val); - Some(val) + let mut result = match place.base { + PlaceBase::Local(local) => self.places[local].clone(), + PlaceBase::Promoted(ref promoted) => { + let generics = self.tcx.generics_of(self.source.def_id); + if generics.requires_monomorphization(self.tcx) { + // FIXME: can't handle code with generics + return None; + } + let substs = Substs::identity_for_item(self.tcx, self.source.def_id); + let instance = Instance::new(self.source.def_id, substs); + let cid = GlobalId { + instance, + promoted: Some(promoted.0), + }; + // cannot use `const_eval` here, because that would require having the MIR + // for the current function available, but we're producing said MIR right now + let (value, _, ty) = self.use_ecx(source_info, |this| { + eval_promoted(&mut this.ecx, cid, this.mir, this.param_env) + })?; + let val = (value, ty, source_info.span); + trace!("evaluated promoted {:?} to {:?}", promoted, val); + Some(val) }, _ => None, + }; + + for elem in place.elems.iter() { + if let ProjectionElem::Field(field, _) = elem { + trace!("field projection on {:?}", place); + let (base, layout, span) = result?; + let valty = self.use_ecx(source_info, |this| { + this.ecx.read_field(base, None, *field, layout) + })?; + result = Some((valty.0, valty.1, span)) + } else { + result = None; + continue; + } } + + result } fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option> { match *op { - Operand::Constant(ref c) => self.eval_constant(c, source_info), + Operand::Constant(ref c) => self.eval_constant(&c, source_info), | Operand::Move(ref place) - | Operand::Copy(ref place) => self.eval_place(place, source_info), + | Operand::Copy(ref place) => self.eval_place(&place, source_info), } } @@ -551,12 +556,14 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { .to_ty(self.tcx); if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) { if let Some(value) = self.const_prop(rval, place_layout, statement.source_info) { - if let Place::Local(local) = *place { - trace!("checking whether {:?} can be stored to {:?}", value, local); - if self.can_const_prop[local] { - trace!("storing {:?} to {:?}", value, local); - assert!(self.places[local].is_none()); - self.places[local] = Some(value); + if !place.has_no_projection() { + if let PlaceBase::Local(local) = place.base { + trace!("checking whether {:?} can be stored to {:?}", value, local); + if self.can_const_prop[local] { + trace!("storing {:?} to {:?}", value, local); + assert!(self.places[local].is_none()); + self.places[local] = Some(value); + } } } } @@ -580,13 +587,11 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { // poison all places this operand references so that further code // doesn't use the invalid value match cond { - Operand::Move(ref place) | Operand::Copy(ref place) => { - let mut place = place; - while let Place::Projection(ref proj) = *place { - place = &proj.base; - } - if let Place::Local(local) = *place { - self.places[local] = None; + Operand::Move(place) | Operand::Copy(place) => { + if let PlaceBase::Local(local) = place.base { + if place.has_no_projection() { + self.places[local] = None; + } } }, Operand::Constant(_) => {} diff --git a/src/librustc_mir/transform/copy_prop.rs b/src/librustc_mir/transform/copy_prop.rs index fba60c7e8dc27..e08939f788550 100644 --- a/src/librustc_mir/transform/copy_prop.rs +++ b/src/librustc_mir/transform/copy_prop.rs @@ -29,7 +29,8 @@ //! (non-mutating) use of `SRC`. These restrictions are conservative and may be relaxed in the //! future. -use rustc::mir::{Constant, Local, LocalKind, Location, Place, Mir, Operand, Rvalue, StatementKind}; +use rustc::mir::{Constant, Local, LocalKind, Location, Place, PlaceBase, + Mir, Operand, Rvalue, StatementKind}; use rustc::mir::visit::MutVisitor; use rustc::ty::TyCtxt; use transform::{MirPass, MirSource}; @@ -104,8 +105,14 @@ impl MirPass for CopyPropagation { // That use of the source must be an assignment. match statement.kind { - StatementKind::Assign(Place::Local(local), Rvalue::Use(ref operand)) if - local == dest_local => { + StatementKind::Assign( + Place { + base: PlaceBase::Local(local), + elems, + }, + Rvalue::Use(ref operand) + ) if local == dest_local + && elems.is_empty() => { let maybe_action = match *operand { Operand::Copy(ref src_place) | Operand::Move(ref src_place) => { @@ -154,13 +161,36 @@ fn eliminate_self_assignments<'tcx>( if let Some(stmt) = mir[location.block].statements.get(location.statement_index) { match stmt.kind { StatementKind::Assign( - Place::Local(local), - Rvalue::Use(Operand::Copy(Place::Local(src_local))), + Place { + base: PlaceBase::Local(local), + elems: elems1, + }, + Rvalue::Use( + Operand::Copy( + Place { + base: PlaceBase::Local(src_local), + elems: elems2, + } + ) + ), ) | StatementKind::Assign( - Place::Local(local), - Rvalue::Use(Operand::Move(Place::Local(src_local))), - ) if local == dest_local && dest_local == src_local => {} + Place { + base: PlaceBase::Local(local), + elems: elems1, + }, + Rvalue::Use( + Operand::Move( + Place { + base: PlaceBase::Local(src_local), + elems: elems2, + } + ) + ), + ) if local == dest_local + && dest_local == src_local + && elems1.is_empty() + && elems2.is_empty() => {} _ => { continue; } @@ -183,11 +213,19 @@ enum Action<'tcx> { } impl<'tcx> Action<'tcx> { - fn local_copy(mir: &Mir<'tcx>, def_use_analysis: &DefUseAnalysis, src_place: &Place<'tcx>) - -> Option> { + fn local_copy( + mir: &Mir<'tcx>, + def_use_analysis: &DefUseAnalysis, + src_place: &Place<'tcx> + ) -> Option> { // The source must be a local. - let src_local = if let Place::Local(local) = *src_place { - local + let src_local = if let PlaceBase::Local(local) = src_place.base { + if !src_place.has_no_projection() { + local + } else { + debug!(" Can't copy-propagate local: source is not a local"); + return None; + } } else { debug!(" Can't copy-propagate local: source is not a local"); return None; @@ -340,8 +378,18 @@ impl<'tcx> MutVisitor<'tcx> for ConstantPropagationVisitor<'tcx> { self.super_operand(operand, location); match *operand { - Operand::Copy(Place::Local(local)) | - Operand::Move(Place::Local(local)) if local == self.dest_local => {} + Operand::Copy( + Place { + base: PlaceBase::Local(local), + elems: _, + } + ) | + Operand::Move( + Place { + base: PlaceBase::Local(local), + elems: _, + } + ) if local == self.dest_local => {} _ => return, } diff --git a/src/librustc_mir/transform/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs index 8b2b9ef7e814d..0d65a35a1f763 100644 --- a/src/librustc_mir/transform/deaggregator.rs +++ b/src/librustc_mir/transform/deaggregator.rs @@ -57,7 +57,7 @@ impl MirPass for Deaggregator { }, source_info, }); - lhs = lhs.downcast(adt_def, variant_index); + lhs = lhs.downcast(tcx, adt_def, variant_index); } active_field_index } @@ -69,16 +69,17 @@ impl MirPass for Deaggregator { // FIXME(eddyb) `offset` should be u64. let offset = i as u32; assert_eq!(offset as usize, i); - lhs.clone().elem(ProjectionElem::ConstantIndex { + lhs.clone().constant_index( + tcx, offset, // FIXME(eddyb) `min_length` doesn't appear to be used. - min_length: offset + 1, - from_end: false - }) + offset + 1, + false + ) } else { let ty = op.ty(local_decls, tcx); let field = Field::new(active_field_index.unwrap_or(i)); - lhs.clone().field(field, ty) + lhs.clone().field(tcx, field, ty) }; Statement { source_info, diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index 937d01a0c5e88..a66152699ef98 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -253,9 +253,7 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { fn field_subpath(&self, path: Self::Path, field: Field) -> Option { dataflow::move_path_children_matching(self.ctxt.move_data(), path, |p| { match p { - &Projection { - elem: ProjectionElem::Field(idx, _), .. - } => idx == field, + ProjectionElem::Field(idx, _) => *idx == field, _ => false } }) @@ -264,11 +262,15 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option { dataflow::move_path_children_matching(self.ctxt.move_data(), path, |p| { match p { - &Projection { - elem: ProjectionElem::ConstantIndex{offset, min_length: _, from_end: false}, .. - } => offset == index, - &Projection { - elem: ProjectionElem::ConstantIndex{offset, min_length: _, from_end: true}, .. + ProjectionElem::ConstantIndex{ + offset, + min_length: _, + from_end: false + } => *offset == index, + ProjectionElem::ConstantIndex{ + offset, + min_length: _, + from_end: true } => size - offset == index, _ => false } @@ -278,7 +280,7 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { fn deref_subpath(&self, path: Self::Path) -> Option { dataflow::move_path_children_matching(self.ctxt.move_data(), path, |p| { match p { - &Projection { elem: ProjectionElem::Deref, .. } => true, + ProjectionElem::Deref => true, _ => false } }) @@ -287,9 +289,7 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { fn downcast_subpath(&self, path: Self::Path, variant: usize) -> Option { dataflow::move_path_children_matching(self.ctxt.move_data(), path, |p| { match p { - &Projection { - elem: ProjectionElem::Downcast(_, idx), .. - } => idx == variant, + ProjectionElem::Downcast(_, idx) => *idx == variant, _ => false } }) @@ -341,7 +341,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { } fn drop_flag(&mut self, index: MovePathIndex) -> Option> { - self.drop_flags.get(&index).map(|t| Place::Local(*t)) + self.drop_flags.get(&index).map(|t| Place::local(*t)) } /// create a patch that elaborates all drops in the input @@ -551,7 +551,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { if let Some(&flag) = self.drop_flags.get(&path) { let span = self.patch.source_info_for_location(self.mir, loc).span; let val = self.constant_bool(span, val.value()); - self.patch.add_assign(loc, Place::Local(flag), val); + self.patch.add_assign(loc, Place::local(flag), val); } } @@ -560,7 +560,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let span = self.patch.source_info_for_location(self.mir, loc).span; let false_ = self.constant_bool(span, false); for flag in self.drop_flags.values() { - self.patch.add_assign(loc, Place::Local(*flag), false_.clone()); + self.patch.add_assign(loc, Place::local(*flag), false_.clone()); } } diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index a3647edd155d3..893d76336df74 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -97,9 +97,11 @@ impl<'tcx> MutVisitor<'tcx> for RenameLocalVisitor { } } -struct DerefArgVisitor; +struct DerefArgVisitor<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx> +} -impl<'tcx> MutVisitor<'tcx> for DerefArgVisitor { +impl<'a, 'tcx> MutVisitor<'tcx> for DerefArgVisitor<'a, 'tcx> { fn visit_local(&mut self, local: &mut Local, _: PlaceContext<'tcx>, @@ -111,11 +113,9 @@ impl<'tcx> MutVisitor<'tcx> for DerefArgVisitor { place: &mut Place<'tcx>, context: PlaceContext<'tcx>, location: Location) { - if *place == Place::Local(self_arg()) { - *place = Place::Projection(Box::new(Projection { - base: place.clone(), - elem: ProjectionElem::Deref, - })); + if place.base == PlaceBase::Local(self_arg()) + && place.has_no_projection() { + *place = place.clone().deref(self.tcx); } else { self.super_place(place, context, location); } @@ -163,17 +163,13 @@ impl<'a, 'tcx> TransformVisitor<'a, 'tcx> { // Create a Place referencing a generator struct field fn make_field(&self, idx: usize, ty: Ty<'tcx>) -> Place<'tcx> { - let base = Place::Local(self_arg()); - let field = Projection { - base: base, - elem: ProjectionElem::Field(Field::new(idx), ty), - }; - Place::Projection(Box::new(field)) + let base = Place::local(self_arg()); + base.field(self.tcx, Field::new(idx), ty) } // Create a statement which changes the generator state fn set_state(&self, state_disc: u32, source_info: SourceInfo) -> Statement<'tcx> { - let state = self.make_field(self.state_field, self.tcx.types.u32); + let state = self.make_field( self.state_field, self.tcx.types.u32); let val = Operand::Constant(box Constant { span: source_info.span, ty: self.tcx.types.u32, @@ -202,10 +198,12 @@ impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> { place: &mut Place<'tcx>, context: PlaceContext<'tcx>, location: Location) { - if let Place::Local(l) = *place { + if let PlaceBase::Local(l) = place.base { // Replace an Local in the remap with a generator struct access - if let Some(&(ty, idx)) = self.remap.get(&l) { - *place = self.make_field(idx, ty); + if place.has_no_projection() { + if let Some(&(ty, idx)) = self.remap.get(&l) { + *place = self.make_field(idx, ty); + } } } else { self.super_place(place, context, location); @@ -228,7 +226,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> { let ret_val = match data.terminator().kind { TerminatorKind::Return => Some((1, None, - Operand::Move(Place::Local(self.new_ret_local)), + Operand::Move(Place::local(self.new_ret_local)), None)), TerminatorKind::Yield { ref value, resume, drop } => Some((0, Some(resume), @@ -242,7 +240,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> { // We must assign the value first in case it gets declared dead below data.statements.push(Statement { source_info, - kind: StatementKind::Assign(Place::Local(RETURN_PLACE), + kind: StatementKind::Assign(Place::local(RETURN_PLACE), self.make_state(state_idx, v)), }); let state = if let Some(resume) = resume { // Yield @@ -268,9 +266,10 @@ impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> { } fn make_generator_state_argument_indirect<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, - mir: &mut Mir<'tcx>) { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + mir: &mut Mir<'tcx> +) { let gen_ty = mir.local_decls.raw[1].ty; let region = ty::ReFree(ty::FreeRegion { @@ -289,7 +288,7 @@ fn make_generator_state_argument_indirect<'a, 'tcx>( mir.local_decls.raw[1].ty = ref_gen_ty; // Add a deref to accesses of the generator state - DerefArgVisitor.visit_mir(mir); + DerefArgVisitor { tcx }.visit_mir(mir); } fn replace_result_variable<'tcx>( @@ -335,28 +334,34 @@ impl<'tcx> Visitor<'tcx> for StorageIgnored { struct BorrowedLocals(liveness::LiveVarSet); -fn mark_as_borrowed<'tcx>(place: &Place<'tcx>, locals: &mut BorrowedLocals) { - match *place { - Place::Local(l) => { locals.0.add(&l); }, - Place::Promoted(_) | - Place::Static(..) => (), - Place::Projection(ref proj) => { - match proj.elem { - // For derefs we don't look any further. - // If it pointed to a Local, it would already be borrowed elsewhere - ProjectionElem::Deref => (), - _ => mark_as_borrowed(&proj.base, locals) +impl<'tcx> BorrowedLocals { + fn mark_as_borrowed( + &mut self, + place: &Place<'tcx>, + ) { + match place.base { + PlaceBase::Local(l) => { self.0.add(&l); } + _ => (), + } + + for elem in place.elems.iter() { + if let ProjectionElem::Deref = elem { + (); + } else { + continue; } } } } impl<'tcx> Visitor<'tcx> for BorrowedLocals { - fn visit_rvalue(&mut self, - rvalue: &Rvalue<'tcx>, - location: Location) { - if let Rvalue::Ref(_, _, ref place) = *rvalue { - mark_as_borrowed(place, self); + fn visit_rvalue( + &mut self, + rvalue: &Rvalue<'tcx>, + location: Location, + ) { + if let Rvalue::Ref(_, _, place) = rvalue { + self.mark_as_borrowed(place); } self.super_rvalue(rvalue, location) @@ -588,11 +593,12 @@ fn elaborate_generator_drops<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, &Terminator { source_info, kind: TerminatorKind::Drop { - location: Place::Local(local), + location: ref place, target, unwind } - } if local == gen => (target, unwind, source_info), + } if PlaceBase::Local(gen) == place.base + && place.has_no_projection() => (target, unwind, source_info), _ => continue, }; let unwind = if let Some(unwind) = unwind { @@ -610,7 +616,7 @@ fn elaborate_generator_drops<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, elaborate_drop( &mut elaborator, source_info, - &Place::Local(gen), + &Place::local(gen), (), target, unwind, @@ -785,7 +791,7 @@ fn insert_clean_drop<'a, 'tcx>(mir: &mut Mir<'tcx>) -> BasicBlock { // Create a block to destroy an unresumed generators. This can only destroy upvars. let drop_clean = BasicBlock::new(mir.basic_blocks().len()); let term = TerminatorKind::Drop { - location: Place::Local(self_arg()), + location: Place::local(self_arg()), target: return_block, unwind: None, }; @@ -948,3 +954,4 @@ impl MirPass for StateTransform { create_generator_resume_function(tcx, transform, def_id, source, mir); } } + diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index 85115427edae9..df0a7963ff251 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -415,20 +415,26 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { // Place could result in two different locations if `f` // writes to `i`. To prevent this we need to create a temporary // borrow of the place and pass the destination as `*temp` instead. - fn dest_needs_borrow(place: &Place) -> bool { - match *place { - Place::Projection(ref p) => { - match p.elem { - ProjectionElem::Deref | - ProjectionElem::Index(_) => true, - _ => dest_needs_borrow(&p.base) - } - } + fn dest_needs_borrow<'tcx>( + place: &Place<'tcx>, + ) -> bool { + let mut dest_needs_borrow = if let PlaceBase::Static(_) = place.base { // Static variables need a borrow because the callee // might modify the same static. - Place::Static(_) => true, - _ => false + true + } else { + false + }; + for elem in place.elems.iter() { + match elem { + ProjectionElem::Deref + | ProjectionElem::Index(_) => { + dest_needs_borrow = true; + } + _ => {}, + } } + dest_needs_borrow } let dest = if dest_needs_borrow(&destination.0) { @@ -443,7 +449,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { let temp = LocalDecl::new_temp(ty, callsite.location.span); let tmp = caller_mir.local_decls.push(temp); - let tmp = Place::Local(tmp); + let tmp = Place::local(tmp); let stmt = Statement { source_info: callsite.location, @@ -451,7 +457,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { }; caller_mir[callsite.bb] .statements.push(stmt); - tmp.deref() + tmp.deref(self.tcx) } else { destination.0 }; @@ -537,7 +543,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { let tuple = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_mir); assert!(args.next().is_none()); - let tuple = Place::Local(tuple); + let tuple = Place::local(tuple); let tuple_tys = if let ty::TyTuple(s) = tuple.ty(caller_mir, tcx).to_ty(tcx).sty { s } else { @@ -551,7 +557,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { let tuple_tmp_args = tuple_tys.iter().enumerate().map(|(i, ty)| { // This is e.g. `tuple_tmp.0` in our example above. - let tuple_field = Operand::Move(tuple.clone().field(Field::new(i), ty)); + let tuple_field = Operand::Move(tuple.clone().field(tcx, Field::new(i), ty)); // Spill to a local to make e.g. `tmp0`. self.create_temp_if_necessary(tuple_field, callsite, caller_mir) @@ -576,10 +582,14 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { // FIXME: Analysis of the usage of the arguments to avoid // unnecessary temporaries. - if let Operand::Move(Place::Local(local)) = arg { - if caller_mir.local_kind(local) == LocalKind::Temp { - // Reuse the operand if it's a temporary already - return local; + if let Operand::Move(ref place) = arg { + if let PlaceBase::Local(local) = place.base { + if place.has_no_projection() { + if caller_mir.local_kind(local) == LocalKind::Temp { + // Reuse the operand if it's a temporary already + return local; + } + } } } @@ -594,7 +604,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { let stmt = Statement { source_info: callsite.location, - kind: StatementKind::Assign(Place::Local(arg_tmp), arg), + kind: StatementKind::Assign(Place::local(arg_tmp), arg), }; caller_mir[callsite.bb].statements.push(stmt); arg_tmp @@ -641,8 +651,9 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { _ctxt: PlaceContext<'tcx>, _location: Location) { if *local == RETURN_PLACE { - match self.destination { - Place::Local(l) => { + match self.destination.base { + PlaceBase::Local(l) + if self.destination.has_no_projection() => { *local = l; return; }, @@ -661,19 +672,23 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { place: &mut Place<'tcx>, _ctxt: PlaceContext<'tcx>, _location: Location) { - - match place { - Place::Local(RETURN_PLACE) => { - // Return pointer; update the place itself - *place = self.destination.clone(); - }, - Place::Promoted(ref mut promoted) => { - if let Some(p) = self.promoted_map.get(promoted.0).cloned() { - promoted.0 = p; - } - }, - _ => self.super_place(place, _ctxt, _location), + if place.has_no_projection() { + match place.base { + PlaceBase::Local(RETURN_PLACE) => { + // Return pointer; update the place itself + *place = self.destination.clone(); + }, + PlaceBase::Promoted(ref mut promoted) => { + if let Some(p) = self.promoted_map.get(promoted.0).cloned() { + promoted.0 = p; + } + }, + _ => self.super_place(place, _ctxt, _location), + } + } else { + self.super_place(place, _ctxt, _location); } + } fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs index 6e06beb30419d..0e2dc72ceb23b 100644 --- a/src/librustc_mir/transform/instcombine.rs +++ b/src/librustc_mir/transform/instcombine.rs @@ -12,6 +12,7 @@ use rustc::mir::{Constant, Location, Place, Mir, Operand, ProjectionElem, Rvalue, Local}; use rustc::mir::visit::{MutVisitor, Visitor}; +use rustc::mir::tcx::PlaceTy; use rustc::ty::{TyCtxt, TypeVariants}; use rustc::util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::Idx; @@ -51,15 +52,16 @@ pub struct InstCombineVisitor<'tcx> { impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> { fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { if self.optimizations.and_stars.remove(&location) { + let local = Place::local(Local::new(0)); debug!("Replacing `&*`: {:?}", rvalue); - let new_place = match *rvalue { - Rvalue::Ref(_, _, Place::Projection(ref mut projection)) => { + let new_place = match rvalue { + Rvalue::Ref(_, _, place) => { // Replace with dummy - mem::replace(&mut projection.base, Place::Local(Local::new(0))) + mem::replace(&mut place.clone(), local) } _ => bug!("Detected `&*` but didn't find `&*`!"), }; - *rvalue = Rvalue::Use(Operand::Copy(new_place)) + *rvalue = Rvalue::Use(Operand::Copy(new_place.clone())) } if let Some(constant) = self.optimizations.arrays_lengths.remove(&location) { @@ -90,11 +92,17 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { - if let Rvalue::Ref(_, _, Place::Projection(ref projection)) = *rvalue { - if let ProjectionElem::Deref = projection.elem { - if projection.base.ty(self.mir, self.tcx).to_ty(self.tcx).is_region_ptr() { - self.optimizations.and_stars.insert(location); + if let Rvalue::Ref(_, _, ref place) = *rvalue { + let mut base_ty = place.base.ty(self.mir); + for elem in place.elems.iter() { + if let ProjectionElem::Deref = elem { + if base_ty.is_region_ptr() { + self.optimizations.and_stars.insert(location); + } } + base_ty = PlaceTy::from(base_ty) + .projection_ty(self.tcx, elem) + .to_ty(self.tcx); } } diff --git a/src/librustc_mir/transform/lower_128bit.rs b/src/librustc_mir/transform/lower_128bit.rs index 83cd7bf549d55..99c73814f51e6 100644 --- a/src/librustc_mir/transform/lower_128bit.rs +++ b/src/librustc_mir/transform/lower_128bit.rs @@ -94,13 +94,13 @@ impl Lower128Bit { block.statements.push(Statement { source_info: source_info, kind: StatementKind::Assign( - Place::Local(local), + Place::local(local), Rvalue::Cast( CastKind::Misc, rhs, rhs_override_ty.unwrap())), }); - rhs = Operand::Move(Place::Local(local)); + rhs = Operand::Move(Place::local(local)); } let call_did = check_lang_item_type( diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index b3ae65f532592..e311cc9c9a406 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -182,7 +182,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { span, scope: OUTERMOST_SOURCE_SCOPE }, - kind: StatementKind::Assign(Place::Local(dest), rvalue) + kind: StatementKind::Assign(Place::local(dest), rvalue) }); } @@ -271,7 +271,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { func, args, cleanup: None, - destination: Some((Place::Local(new_temp), new_target)) + destination: Some((Place::local(new_temp), new_target)) }, ..terminator }; @@ -287,6 +287,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { } fn promote_candidate(mut self, candidate: Candidate) { + let tcx = self.tcx; let mut operand = { let promoted = &mut self.promoted; let promoted_id = Promoted::new(self.source.promoted.len()); @@ -294,30 +295,35 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { promoted.span = span; promoted.local_decls[RETURN_PLACE] = LocalDecl::new_return_place(ty, span); - Place::Promoted(box (promoted_id, ty)) + Place::promoted(promoted_id, ty) }; let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut(); match candidate { Candidate::Ref(loc) => { - let ref mut statement = blocks[loc.block].statements[loc.statement_index]; + let statement = &blocks[loc.block].statements[loc.statement_index]; match statement.kind { - StatementKind::Assign(_, Rvalue::Ref(_, _, ref mut place)) => { + StatementKind::Assign(_, Rvalue::Ref(_, _, ref place)) => { // Find the underlying local for this (necessarilly interior) borrow. - // HACK(eddyb) using a recursive function because of mutable borrows. - fn interior_base<'a, 'tcx>(place: &'a mut Place<'tcx>) - -> &'a mut Place<'tcx> { - if let Place::Projection(ref mut proj) = *place { - assert_ne!(proj.elem, ProjectionElem::Deref); - return interior_base(&mut proj.base); + let mut place = place.clone(); + + // for Place: + // Base.[a, b, Deref, c] + // ^^^^^^^^^^ ^^^^^ ^ + // |-- new_place |-- skip + // + for (i, elem) in place.elems.iter().cloned().enumerate().rev() { + if elem == ProjectionElem::Deref { + place = place.elem_base(tcx, i); + break; + } else { + continue; } - place } - let place = interior_base(place); let ty = place.ty(local_decls, self.tcx).to_ty(self.tcx); let span = statement.source_info.span; - Operand::Move(mem::replace(place, promoted_place(ty, span))) + Operand::Move(mem::replace(&mut place, promoted_place(ty, span))) } _ => bug!() } @@ -372,10 +378,14 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, match candidate { Candidate::Ref(Location { block, statement_index }) => { match mir[block].statements[statement_index].kind { - StatementKind::Assign(Place::Local(local), _) => { - if temps[local] == TempState::PromotedOut { - // Already promoted. - continue; + StatementKind::Assign(ref place, _) => { + if let PlaceBase::Local(local) = place.base { + if place.has_no_projection() { + if temps[local] == TempState::PromotedOut { + // Already promoted. + continue; + } + } } } _ => {} @@ -417,7 +427,15 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, for block in mir.basic_blocks_mut() { block.statements.retain(|statement| { match statement.kind { - StatementKind::Assign(Place::Local(index), _) | + StatementKind::Assign( + Place { + base: PlaceBase::Local(index), + elems, + }, + _, + ) if elems.is_empty() => { + !promoted(index) + }, StatementKind::StorageLive(index) | StatementKind::StorageDead(index) => { !promoted(index) @@ -427,7 +445,14 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, }); let terminator = block.terminator_mut(); match terminator.kind { - TerminatorKind::Drop { location: Place::Local(index), target, .. } => { + TerminatorKind::Drop { + location: Place { + base: PlaceBase::Local(index), + elems, + }, + target, + .. + } if elems.is_empty() => { if promoted(index) { terminator.kind = TerminatorKind::Goto { target, diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index d876ee77e76cf..0de3e9033e632 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -234,9 +234,10 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { // Only handle promotable temps in non-const functions. if self.mode == Mode::Fn { - if let Place::Local(index) = *dest { + if let PlaceBase::Local(index) = dest.base { if self.mir.local_kind(index) == LocalKind::Temp - && self.temp_promotion_state[index].is_promotable() { + && self.temp_promotion_state[index].is_promotable() + && dest.has_no_projection() { debug!("store to promotable temp {:?} ({:?})", index, qualif); store(&mut self.local_qualif[index]); } @@ -244,36 +245,43 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { return; } - match *dest { - Place::Local(index) if (self.mir.local_kind(index) == LocalKind::Var || - self.mir.local_kind(index) == LocalKind::Arg) && - self.tcx.sess.features_untracked().const_let => { - debug!("store to var {:?}", index); - self.local_qualif[index] = Some(self.qualif); - } - Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp || - self.mir.local_kind(index) == LocalKind::ReturnPointer => { - debug!("store to {:?} (temp or return pointer)", index); - store(&mut self.local_qualif[index]) - } - - Place::Projection(box Projection { - base: Place::Local(index), - elem: ProjectionElem::Deref - }) if self.mir.local_kind(index) == LocalKind::Temp - && self.mir.local_decls[index].ty.is_box() - && self.local_qualif[index].map_or(false, |qualif| { - qualif.contains(Qualif::NOT_CONST) - }) => { - // Part of `box expr`, we should've errored - // already for the Box allocation Rvalue. - } - - // This must be an explicit assignment. - _ => { - // Catch more errors in the destination. - self.visit_place(dest, PlaceContext::Store, location); - self.statement_like(); + if let Some(projection) = dest.elems.last() { + if let ProjectionElem::Deref = projection { + if let PlaceBase::Local(index) = dest.base { + if self.mir.local_kind(index) == LocalKind::Temp + && self.mir.local_decls[index].ty.is_box() + && dest.elems.len() == 1 + && self.local_qualif[index].map_or(false, |qualif| { + qualif.contains(Qualif::NOT_CONST) + }) { + // Part of `box expr`, we should've errored + // already for the Box allocation Rvalue. + } + } + } else { + // This must be an explicit assignment. + // Catch more errors in the destination. + self.visit_place(dest, PlaceContext::Store, location); + self.statement_like(); + } + } else { + match dest.base { + PlaceBase::Local(index) if (self.mir.local_kind(index) == LocalKind::Var || + self.mir.local_kind(index) == LocalKind::Arg) && + self.tcx.sess.features_untracked().const_let => { + debug!("store to var {:?}", index); + self.local_qualif[index] = Some(self.qualif); + } + PlaceBase::Local(index) if self.mir.local_kind(index) == LocalKind::Temp || + self.mir.local_kind(index) == LocalKind::ReturnPointer => { + debug!("store to {:?} (temp or return pointer)", index); + store(&mut self.local_qualif[index]); + } + _ => { + // Catch more errors in the destination. + self.visit_place(dest, PlaceContext::Store, location); + self.statement_like(); + }, } } } @@ -346,7 +354,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { for index in mir.vars_iter() { if !self.const_fn_arg_vars.contains(index) { debug!("unassigned variable {:?}", index); - self.assign(&Place::Local(index), Location { + self.assign(&Place::local(index), Location { block: bb, statement_index: usize::MAX, }); @@ -388,7 +396,16 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { match *candidate { Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => { match self.mir[bb].statements[stmt_idx].kind { - StatementKind::Assign(_, Rvalue::Ref(_, _, Place::Local(index))) => { + StatementKind::Assign( + _, + Rvalue::Ref( + _, _, + Place { + base: PlaceBase::Local(index), + elems, + } + ) + ) if elems.is_empty() => { promoted_temps.add(&index); } _ => {} @@ -443,109 +460,112 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } } - fn visit_place(&mut self, - place: &Place<'tcx>, - context: PlaceContext<'tcx>, - location: Location) { - match *place { - Place::Local(ref local) => self.visit_local(local, context, location), - Place::Promoted(_) => bug!("promoting already promoted MIR"), - Place::Static(ref global) => { - if self.tcx - .get_attrs(global.def_id) - .iter() - .any(|attr| attr.check_name("thread_local")) { - if self.mode != Mode::Fn { - span_err!(self.tcx.sess, self.span, E0625, - "thread-local statics cannot be \ - accessed at compile-time"); - } - self.add(Qualif::NOT_CONST); - return; - } - - // Only allow statics (not consts) to refer to other statics. - if self.mode == Mode::Static || self.mode == Mode::StaticMut { - return; - } - self.add(Qualif::NOT_CONST); - - if self.mode != Mode::Fn { - let mut err = struct_span_err!(self.tcx.sess, self.span, E0013, - "{}s cannot refer to statics, use \ - a constant instead", self.mode); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "Static and const variables can refer to other const variables. But a \ - const variable cannot refer to a static variable." - ); - err.help( - "To fix this, the value can be extracted as a const and then used." - ); - } - err.emit() - } - } - Place::Projection(ref proj) => { - self.nest(|this| { - this.super_place(place, context, location); - match proj.elem { - ProjectionElem::Deref => { - if let Mode::Fn = this.mode { - this.add(Qualif::NOT_CONST); - } else { - let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx); - if let ty::TyRawPtr(_) = base_ty.sty { - if !this.tcx.sess.features_untracked().const_raw_ptr_deref { - emit_feature_err( - &this.tcx.sess.parse_sess, "const_raw_ptr_deref", - this.span, GateIssue::Language, - &format!( - "dereferencing raw pointers in {}s is unstable", - this.mode, - ), - ); - } + fn visit_place( + &mut self, + place: &Place<'tcx>, + context: PlaceContext<'tcx>, + location: Location, + ) { + if let (base_place, Some(projection)) = place.split_projection(self.tcx) { + self.nest(|this| { + this.super_place(place, context, location); + match projection { + ProjectionElem::Deref => { + if let Mode::Fn = this.mode { + this.add(Qualif::NOT_CONST); + } else { + let base_ty = base_place.ty(this.mir, this.tcx).to_ty(this.tcx); + if let ty::TyRawPtr(_) = base_ty.sty { + if !this.tcx.sess.features_untracked().const_raw_ptr_deref { + emit_feature_err( + &this.tcx.sess.parse_sess, "const_raw_ptr_deref", + this.span, GateIssue::Language, + &format!( + "dereferencing raw pointers in {}s is unstable", + this.mode, + ), + ); } } } + } - ProjectionElem::Field(..) | - ProjectionElem::Index(_) => { - let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx); - if let Some(def) = base_ty.ty_adt_def() { - if def.is_union() { - match this.mode { - Mode::Fn => this.not_const(), - Mode::ConstFn => { - if !this.tcx.sess.features_untracked().const_fn_union { - emit_feature_err( - &this.tcx.sess.parse_sess, "const_fn_union", - this.span, GateIssue::Language, - "unions in const fn are unstable", - ); - } - }, - - | Mode::Static - | Mode::StaticMut - | Mode::Const - => {}, - } + ProjectionElem::Field(..) | + ProjectionElem::Index(_) => { + let base_ty =base_place.ty(this.mir, this.tcx).to_ty(this.tcx); + if let Some(def) = base_ty.ty_adt_def() { + if def.is_union() { + match this.mode { + Mode::Fn => this.not_const(), + Mode::ConstFn => { + if !this.tcx.sess.features_untracked().const_fn_union { + emit_feature_err( + &this.tcx.sess.parse_sess, "const_fn_union", + this.span, GateIssue::Language, + "unions in const fn are unstable", + ); + } + }, + + | Mode::Static + | Mode::StaticMut + | Mode::Const + => {}, } } + } + + let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx); + this.qualif.restrict(ty, this.tcx, this.param_env); + } - let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx); - this.qualif.restrict(ty, this.tcx, this.param_env); + ProjectionElem::ConstantIndex {..} | + ProjectionElem::Subslice {..} | + ProjectionElem::Downcast(..) => { + this.not_const() + } + } + }); + } else { + match place.base { + PlaceBase::Local(ref local) => self.visit_local(local, context, location), + PlaceBase::Promoted(_) => bug!("promoting already promoted MIR"), + PlaceBase::Static(ref global) => { + if self.tcx + .get_attrs(global.def_id) + .iter() + .any(|attr| attr.check_name("thread_local")) { + if self.mode != Mode::Fn { + span_err!(self.tcx.sess, self.span, E0625, + "thread-local statics cannot be \ + accessed at compile-time"); } + self.add(Qualif::NOT_CONST); + return; + } - ProjectionElem::ConstantIndex {..} | - ProjectionElem::Subslice {..} | - ProjectionElem::Downcast(..) => { - this.not_const() + // Only allow statics (not consts) to refer to other statics. + if self.mode == Mode::Static || self.mode == Mode::StaticMut { + return; + } + self.add(Qualif::NOT_CONST); + + if self.mode != Mode::Fn { + let mut err = struct_span_err!(self.tcx.sess, self.span, E0013, + "{}s cannot refer to statics, use \ + a constant instead", self.mode); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note( + "Static and const variables can refer to other const variables. \ + But a const variable cannot refer to a static variable." + ); + err.help( + "To fix this, the value can be extracted as a const and then used." + ); } + err.emit() } - }); + } } } } @@ -557,10 +577,14 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { Operand::Copy(_) | Operand::Move(_) => { // Mark the consumed locals to indicate later drops are noops. - if let Operand::Move(Place::Local(local)) = *operand { - self.local_qualif[local] = self.local_qualif[local].map(|q| - q - Qualif::NEEDS_DROP - ); + if let Operand::Move(ref place) = *operand { + if let PlaceBase::Local(local) = place.base { + if place.has_no_projection() { + self.local_qualif[local] = self.local_qualif[local].map(|q| + q - Qualif::NEEDS_DROP + ); + } + } } } Operand::Constant(ref constant) => { @@ -588,9 +612,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { // Recurse through operands and places. if let Rvalue::Ref(region, kind, ref place) = *rvalue { let mut is_reborrow = false; - if let Place::Projection(ref proj) = *place { - if let ProjectionElem::Deref = proj.elem { - let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); + if let (base_place, Some(projection)) = place.split_projection(self.tcx) { + if let ProjectionElem::Deref = projection { + let base_ty = base_place.ty(self.mir, self.tcx).to_ty(self.tcx); if let ty::TyRef(..) = base_ty.sty { is_reborrow = true; } @@ -598,7 +622,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } if is_reborrow { - self.super_place(place, PlaceContext::Borrow { + self.super_place(&place, PlaceContext::Borrow { region, kind }, location); @@ -695,16 +719,21 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } else { // We might have a candidate for promotion. let candidate = Candidate::Ref(location); - // We can only promote interior borrows of promotable temps. - let mut place = place; - while let Place::Projection(ref proj) = *place { - if proj.elem == ProjectionElem::Deref { - break; + let mut tmp_place = place.clone(); + for (i, elem) in place.elems.iter().cloned().enumerate() { + match elem { + ProjectionElem::Deref => { + break; + } + _ => { + tmp_place = place.elem_base(self.tcx, i); + } } - place = &proj.base; } - if let Place::Local(local) = *place { - if self.mir.local_kind(local) == LocalKind::Temp { + + if let PlaceBase::Local(local) = tmp_place.base { + if self.mir.local_kind(local) == LocalKind::Temp + && tmp_place.has_no_projection() { if let Some(qualif) = self.local_qualif[local] { // `forbidden_mut` is false, so we can safely ignore // `MUTABLE_INTERIOR` from the local's qualifications. @@ -961,11 +990,17 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { if self.mode != Mode::Fn { // HACK(eddyb) Emulate a bit of dataflow analysis, // conservatively, that drop elaboration will do. - let needs_drop = if let Place::Local(local) = *place { - if self.local_qualif[local].map_or(true, |q| q.contains(Qualif::NEEDS_DROP)) { - Some(self.mir.local_decls[local].source_info.span) + let needs_drop = if place.has_no_projection() { + if let PlaceBase::Local(local) = place.base { + if self.local_qualif[local].map_or( + true, |q| q.contains(Qualif::NEEDS_DROP) + ) { + Some(self.mir.local_decls[local].source_info.span) + } else { + None + } } else { - None + Some(self.span) } } else { Some(self.span) @@ -997,15 +1032,30 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { self.visit_rvalue(rvalue, location); // Check the allowed const fn argument forms. - if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) { - if self.mir.local_kind(index) == LocalKind::Var && - self.const_fn_arg_vars.insert(index) && - !self.tcx.sess.features_untracked().const_let { + if let (Mode::ConstFn, PlaceBase::Local(ref index)) = (self.mode, dest.clone().base) { + if self.mir.local_kind(*index) == LocalKind::Var && + self.const_fn_arg_vars.insert(*index) && + !self.tcx.sess.features_untracked().const_let && + dest.has_no_projection() { // Direct use of an argument is permitted. match *rvalue { - Rvalue::Use(Operand::Copy(Place::Local(local))) | - Rvalue::Use(Operand::Move(Place::Local(local))) => { + Rvalue::Use( + Operand::Copy( + Place { + base: PlaceBase::Local(local), + elems, + } + ) + ) | + Rvalue::Use( + Operand::Move( + Place { + base: PlaceBase::Local(local), + elems, + } + ) + ) => if elems.is_empty() { if self.mir.local_kind(local) == LocalKind::Arg { return; } @@ -1015,7 +1065,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { // Avoid a generic error for other uses of arguments. if self.qualif.contains(Qualif::FN_ARGUMENT) { - let decl = &self.mir.local_decls[index]; + let decl = &self.mir.local_decls[*index]; let mut err = feature_err( &self.tcx.sess.parse_sess, "const_let", @@ -1176,7 +1226,14 @@ impl MirPass for QualifyAndPromoteConstants { }); let terminator = block.terminator_mut(); match terminator.kind { - TerminatorKind::Drop { location: Place::Local(index), target, .. } => { + TerminatorKind::Drop { + location: + Place { + base: PlaceBase::Local(index), + elems, + }, + target, .. + } if elems.is_empty() => { if promoted_temps.contains(&index) { terminator.kind = TerminatorKind::Goto { target, diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs index 04a7a81eb126f..ed1309372d7e4 100644 --- a/src/librustc_mir/transform/remove_noop_landing_pads.rs +++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs @@ -60,7 +60,13 @@ impl RemoveNoopLandingPads { // instructions, but this should all run after borrowck). } - StatementKind::Assign(Place::Local(_), Rvalue::Use(_)) => { + StatementKind::Assign( + Place { + base: PlaceBase::Local(_), + elems, + }, + Rvalue::Use(_) + ) => if elems.is_empty() { // Writing to a local (e.g. a drop flag) does not // turn a landing pad to a non-nop } diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs index da149f420644c..0b9f91cd5e70b 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/src/librustc_mir/transform/rustc_peek.rs @@ -13,7 +13,7 @@ use syntax::ast; use syntax_pos::Span; use rustc::ty::{self, TyCtxt}; -use rustc::mir::{self, Mir, Location}; +use rustc::mir::{self, Mir, Location, Place, PlaceBase}; use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_data_structures::indexed_vec::Idx; use transform::{MirPass, MirSource}; @@ -124,8 +124,18 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }; assert!(args.len() == 1); let peek_arg_place = match args[0] { - mir::Operand::Copy(ref place @ mir::Place::Local(_)) | - mir::Operand::Move(ref place @ mir::Place::Local(_)) => Some(place), + mir::Operand::Copy( + ref place @ Place { + base: PlaceBase::Local(_), + elems: _, + } + ) | + mir::Operand::Move( + ref place @ Place { + base: PlaceBase::Local(_), + elems: _, + } + ) if place.has_no_projection() => Some(place), _ => None, }; diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs index 7a8c35e7b96f5..2d1bcb39d6aae 100644 --- a/src/librustc_mir/transform/uniform_array_move_out.rs +++ b/src/librustc_mir/transform/uniform_array_move_out.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -67,28 +67,38 @@ struct UniformArrayMoveOutVisitor<'a, 'tcx: 'a> { } impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> { - fn visit_assign(&mut self, - block: BasicBlock, - dst_place: &Place<'tcx>, - rvalue: &Rvalue<'tcx>, - location: Location) { - if let Rvalue::Use(Operand::Move(ref src_place)) = rvalue { - if let Place::Projection(ref proj) = *src_place { - if let ProjectionElem::ConstantIndex{offset: _, - min_length: _, - from_end: false} = proj.elem { + fn visit_assign( + &mut self, + block: BasicBlock, + dst_place: &Place<'tcx>, + rvalue: &Rvalue<'tcx>, + location: Location + ) { + if let Rvalue::Use(Operand::Move(src_place)) = rvalue { + if let (base_place, Some(projection)) = src_place.split_projection(self.tcx) { + if let ProjectionElem::ConstantIndex { + offset: _, + min_length: _, + from_end: false, + } = projection { // no need to transformation } else { - let place_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); + let place_ty = base_place.ty(self.mir, self.tcx).to_ty(self.tcx); if let ty::TyArray(item_ty, const_size) = place_ty.sty { if let Some(size) = const_size.assert_usize(self.tcx) { assert!(size <= u32::max_value() as u64, "uniform array move out doesn't supported for array bigger then u32"); - self.uniform(location, dst_place, proj, item_ty, size as u32); + self.uniform( + location, + dst_place, + &base_place, + &projection, + item_ty, + size as u32, + ); } } - } } } @@ -100,35 +110,43 @@ impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> { fn uniform(&mut self, location: Location, dst_place: &Place<'tcx>, - proj: &PlaceProjection<'tcx>, + base_place: &Place<'tcx>, + proj: &PlaceElem<'tcx>, item_ty: &'tcx ty::TyS<'tcx>, size: u32) { - match proj.elem { + match proj { // uniforms statements like_10 = move _2[:-1]; - ProjectionElem::Subslice{from, to} => { + ProjectionElem::Subslice{ from, to } => { self.patch.make_nop(location); - let temps : Vec<_> = (from..(size-to)).map(|i| { + let temps : Vec<_> = (*from..(size-to)).map(|i| { let temp = self.patch.new_temp(item_ty, self.mir.source_info(location).span); self.patch.add_statement(location, StatementKind::StorageLive(temp)); - self.patch.add_assign(location, - Place::Local(temp), - Rvalue::Use( - Operand::Move( - Place::Projection(box PlaceProjection{ - base: proj.base.clone(), - elem: ProjectionElem::ConstantIndex{ - offset: i, - min_length: size, - from_end: false} - })))); + self.patch.add_assign( + location, + Place::local(temp), + Rvalue::Use( + Operand::Move( + base_place.clone().constant_index( + self.tcx, + i, + size, + false, + ) + ) + ) + ); temp }).collect(); - self.patch.add_assign(location, - dst_place.clone(), - Rvalue::Aggregate(box AggregateKind::Array(item_ty), - temps.iter().map( - |x| Operand::Move(Place::Local(*x))).collect() - )); + self.patch.add_assign( + location, + dst_place.clone(), + Rvalue::Aggregate( + box AggregateKind::Array(item_ty), + temps.iter().map( + |x| Operand::Move(Place::local(*x)) + ).collect() + ) + ); for temp in temps { self.patch.add_statement(location, StatementKind::StorageDead(temp)); } @@ -136,16 +154,20 @@ impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> { // uniforms statements like _11 = move _2[-1 of 1]; ProjectionElem::ConstantIndex{offset, min_length: _, from_end: true} => { self.patch.make_nop(location); - self.patch.add_assign(location, - dst_place.clone(), - Rvalue::Use( - Operand::Move( - Place::Projection(box PlaceProjection{ - base: proj.base.clone(), - elem: ProjectionElem::ConstantIndex{ - offset: size - offset, - min_length: size, - from_end: false }})))); + self.patch.add_assign( + location, + dst_place.clone(), + Rvalue::Use( + Operand::Move( + base_place.clone().constant_index( + self.tcx, + size - offset, + size, + false, + ) + ) + ) + ); } _ => {} } @@ -186,20 +208,28 @@ impl MirPass for RestoreSubsliceArrayMoveOut { if let StatementKind::Assign(ref dst_place, ref rval) = statement.kind { if let Rvalue::Aggregate(box AggregateKind::Array(_), ref items) = *rval { let items : Vec<_> = items.iter().map(|item| { - if let Operand::Move(Place::Local(local)) = item { - let local_use = &visitor.locals_use[*local]; - let opt_index_and_place = Self::try_get_item_source(local_use, mir); - // each local should be used twice: - // in assign and in aggregate statments - if local_use.use_count == 2 && opt_index_and_place.is_some() { - let (index, src_place) = opt_index_and_place.unwrap(); - return Some((local_use, index, src_place)); + if let Operand::Move(place) = item { + if let PlaceBase::Local(local) = place.base { + if place.has_no_projection() { + let local_use = &visitor.locals_use[local]; + let opt_index_and_place = Self::try_get_item_source( + tcx, + local_use, + mir + ); + // each local should be used twice: + // in assign and in aggregate statments + if local_use.use_count == 2 && opt_index_and_place.is_some() { + let (index, src_place) = opt_index_and_place.unwrap(); + return Some((local_use, index, src_place)); + } + } } } None }).collect(); - let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2); + let opt_src_place = items.first().and_then(|x| x.clone()).map(|x| x.2); let opt_size = opt_src_place.and_then(|src_place| { let src_ty = src_place.ty(mir, tcx).to_ty(tcx); if let ty::TyArray(_, ref size_o) = src_ty.sty { @@ -208,7 +238,9 @@ impl MirPass for RestoreSubsliceArrayMoveOut { None } }); - Self::check_and_patch(*candidate, &items, opt_size, &mut patch, dst_place); + Self::check_and_patch(tcx, *candidate, &items, + opt_size, &mut patch, dst_place + ); } } } @@ -221,17 +253,20 @@ impl RestoreSubsliceArrayMoveOut { // Checks that source has size, all locals are inited from same source place and // indices is an integer interval. If all checks pass do the replacent. // items are Vec> - fn check_and_patch<'tcx>(candidate: Location, - items: &[Option<(&LocalUse, u32, &Place<'tcx>)>], - opt_size: Option, - patch: &mut MirPatch<'tcx>, - dst_place: &Place<'tcx>) { - let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2); + fn check_and_patch<'tcx>( + tcx: TyCtxt<'_, '_, 'tcx>, + candidate: Location, + items: &[Option<(&LocalUse, u32, Place<'tcx>)>], + opt_size: Option, + patch: &mut MirPatch<'tcx>, + dst_place: &Place<'tcx> + ) { + let opt_src_place = items.first().and_then(|x| x.clone()).map(|x| x.2); - if opt_size.is_some() && items.iter().all( - |l| l.is_some() && l.unwrap().2 == opt_src_place.unwrap()) { + if opt_size.is_some() && items.iter().cloned().all( + |l| l.is_some() && l.unwrap().2 == opt_src_place.clone().unwrap()) { - let indicies: Vec<_> = items.iter().map(|x| x.unwrap().1).collect(); + let indicies: Vec<_> = items.iter().map(|x| x.clone().unwrap().1).collect(); for i in 1..indicies.len() { if indicies[i - 1] + 1 != indicies[i] { return; @@ -241,7 +276,7 @@ impl RestoreSubsliceArrayMoveOut { let min = *indicies.first().unwrap(); let max = *indicies.last().unwrap(); - for item in items { + for item in items.iter().cloned() { let locals_use = item.unwrap().0; patch.make_nop(locals_use.alive.unwrap()); patch.make_nop(locals_use.dead.unwrap()); @@ -249,29 +284,48 @@ impl RestoreSubsliceArrayMoveOut { } patch.make_nop(candidate); let size = opt_size.unwrap() as u32; - patch.add_assign(candidate, - dst_place.clone(), - Rvalue::Use( - Operand::Move( - Place::Projection(box PlaceProjection{ - base: opt_src_place.unwrap().clone(), - elem: ProjectionElem::Subslice{ - from: min, to: size - max - 1}})))); + patch.add_assign( + candidate, + dst_place.clone(), + Rvalue::Use( + Operand::Move( + opt_src_place.unwrap().clone().subslice( + tcx, + min, + size - max - 1, + ) + ) + ) + ); } } - fn try_get_item_source<'a, 'tcx>(local_use: &LocalUse, - mir: &'a Mir<'tcx>) -> Option<(u32, &'a Place<'tcx>)> { + fn try_get_item_source<'a, 'tcx>( + tcx: TyCtxt<'_, '_, 'tcx>, + local_use: &LocalUse, + mir: &'a Mir<'tcx> + ) -> Option<(u32, Place<'tcx>)> { if let Some(location) = local_use.first_use { let block = &mir[location.block]; if block.statements.len() > location.statement_index { let statement = &block.statements[location.statement_index]; if let StatementKind::Assign( - Place::Local(_), - Rvalue::Use(Operand::Move(Place::Projection(box PlaceProjection{ - ref base, elem: ProjectionElem::ConstantIndex{ - offset, min_length: _, from_end: false}})))) = statement.kind { - return Some((offset, base)) + ref place1, + Rvalue::Use(Operand::Move(ref place2)), + ) = statement.kind { + if let PlaceBase::Local(_) = place1.base { + if place1.has_no_projection() { + if let Some(projection) = place2.elems.last() { + if let ProjectionElem::ConstantIndex { + offset, + min_length: _, + from_end: false, + } = projection { + return Some((*offset, place2.projection_base(tcx))); + } + } + } + } } } } diff --git a/src/librustc_mir/util/alignment.rs b/src/librustc_mir/util/alignment.rs index d1410210bda96..2c3b562fb361b 100644 --- a/src/librustc_mir/util/alignment.rs +++ b/src/librustc_mir/util/alignment.rs @@ -8,24 +8,25 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - -use rustc::ty::{self, TyCtxt}; use rustc::mir::*; +use rustc::ty::{self, TyCtxt}; /// Return `true` if this place is allowed to be less aligned /// than its containing struct (because it is within a packed /// struct). -pub fn is_disaligned<'a, 'tcx, L>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - local_decls: &L, - param_env: ty::ParamEnv<'tcx>, - place: &Place<'tcx>) - -> bool - where L: HasLocalDecls<'tcx> +pub fn is_disaligned<'a, 'tcx, L>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + local_decls: &L, + param_env: ty::ParamEnv<'tcx>, + place: &Place<'tcx>, +) -> bool +where + L: HasLocalDecls<'tcx>, { debug!("is_disaligned({:?})", place); if !is_within_packed(tcx, local_decls, place) { debug!("is_disaligned({:?}) - not within packed", place); - return false + return false; } let ty = place.ty(local_decls, tcx).to_ty(tcx); @@ -43,31 +44,28 @@ pub fn is_disaligned<'a, 'tcx, L>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -fn is_within_packed<'a, 'tcx, L>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - local_decls: &L, - place: &Place<'tcx>) - -> bool - where L: HasLocalDecls<'tcx> +fn is_within_packed<'a, 'tcx, L>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + local_decls: &L, + place: &Place<'tcx>, +) -> bool +where + L: HasLocalDecls<'tcx>, { - let mut place = place; - while let &Place::Projection(box Projection { - ref base, ref elem - }) = place { - match *elem { + let mut base_ty = place.base.ty(local_decls); + for elem in place.elems.iter() { + match elem { // encountered a Deref, which is ABI-aligned ProjectionElem::Deref => break, ProjectionElem::Field(..) => { - let ty = base.ty(local_decls, tcx).to_ty(tcx); - match ty.sty { - ty::TyAdt(def, _) if def.repr.packed() => { - return true - } - _ => {} + match base_ty.sty { + ty::TyAdt(def, _) if def.repr.packed() => return true, + _ => {}, } } - _ => {} + _ => {}, } - place = base; + base_ty = tcx::PlaceTy::from(base_ty).projection_ty(tcx, elem).to_ty(tcx); } false diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index bbffeec631abd..159da39832861 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -210,7 +210,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> self.elaborator.param_env(), f.ty(self.tcx(), substs), ); - (base_place.clone().field(field, field_ty), subpath) + (base_place.clone().field(self.tcx(), field, field_ty), subpath) }).collect() } @@ -329,7 +329,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> debug!("open_drop_for_tuple({:?}, {:?})", self, tys); let fields = tys.iter().enumerate().map(|(i, &ty)| { - (self.place.clone().field(Field::new(i), ty), + (self.place.clone().field(self.tcx(), Field::new(i), ty), self.elaborator.field_subpath(self.path, Field::new(i))) }).collect(); @@ -342,7 +342,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> { debug!("open_drop_for_box({:?}, {:?}, {:?})", self, adt, substs); - let interior = self.place.clone().deref(); + let interior = self.place.clone().deref(self.tcx()); let interior_path = self.elaborator.deref_subpath(self.path); let succ = self.succ; // FIXME(#43234) @@ -420,9 +420,11 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let subpath = self.elaborator.downcast_subpath( self.path, variant_index); if let Some(variant_path) = subpath { - let base_place = self.place.clone().elem( - ProjectionElem::Downcast(adt, variant_index) - ); + let base_place = self.place.clone().downcast( + self.tcx(), + adt, + variant_index, + ); let fields = self.move_paths_for_fields( &base_place, variant_path, @@ -495,7 +497,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> // discriminant after it is free-ed, because that // way lies only trouble. let discr_ty = adt.repr.discr_type().to_ty(self.tcx()); - let discr = Place::Local(self.new_temp(discr_ty)); + let discr = Place::local(self.new_temp(discr_ty)); let discr_rv = Rvalue::Discriminant(self.place.clone()); let switch_block = BasicBlockData { statements: vec![self.assign(&discr, discr_rv)], @@ -529,11 +531,11 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> mutbl: hir::Mutability::MutMutable }); let ref_place = self.new_temp(ref_ty); - let unit_temp = Place::Local(self.new_temp(tcx.mk_nil())); + let unit_temp = Place::local(self.new_temp(tcx.mk_nil())); let result = BasicBlockData { statements: vec![self.assign( - &Place::Local(ref_place), + &Place::local(ref_place), Rvalue::Ref(tcx.types.re_erased, BorrowKind::Mut { allow_two_phase_borrow: false }, self.place.clone()) @@ -542,7 +544,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> kind: TerminatorKind::Call { func: Operand::function_handle(tcx, drop_fn.def_id, substs, self.source_info.span), - args: vec![Operand::Move(Place::Local(ref_place))], + args: vec![Operand::Move(Place::local(ref_place))], destination: Some((unit_temp, succ)), cleanup: unwind.into_option(), }, @@ -586,25 +588,25 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> ty: ety, mutbl: hir::Mutability::MutMutable }); - let ptr = &Place::Local(self.new_temp(ref_ty)); - let can_go = &Place::Local(self.new_temp(tcx.types.bool)); + let ptr = &Place::local(self.new_temp(ref_ty)); + let can_go = &Place::local(self.new_temp(tcx.types.bool)); let one = self.constant_usize(1); let (ptr_next, cur_next) = if ptr_based { - (Rvalue::Use(copy(&Place::Local(cur))), - Rvalue::BinaryOp(BinOp::Offset, copy(&Place::Local(cur)), one)) + (Rvalue::Use(copy(&Place::local(cur))), + Rvalue::BinaryOp(BinOp::Offset, copy(&Place::local(cur)), one)) } else { (Rvalue::Ref( tcx.types.re_erased, BorrowKind::Mut { allow_two_phase_borrow: false }, - self.place.clone().index(cur)), - Rvalue::BinaryOp(BinOp::Add, copy(&Place::Local(cur)), one)) + self.place.clone().index(self.tcx(), cur)), + Rvalue::BinaryOp(BinOp::Add, copy(&Place::local(cur)), one)) }; let drop_block = BasicBlockData { statements: vec![ self.assign(ptr, ptr_next), - self.assign(&Place::Local(cur), cur_next) + self.assign(&Place::local(cur), cur_next) ], is_cleanup: unwind.is_cleanup(), terminator: Some(Terminator { @@ -618,7 +620,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let loop_block = BasicBlockData { statements: vec![ self.assign(can_go, Rvalue::BinaryOp(BinOp::Eq, - copy(&Place::Local(cur)), + copy(&Place::local(cur)), copy(length_or_end))) ], is_cleanup: unwind.is_cleanup(), @@ -628,9 +630,10 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> }) }; let loop_block = self.elaborator.patch().new_block(loop_block); + let tcx = self.tcx(); self.elaborator.patch().patch_terminator(drop_block, TerminatorKind::Drop { - location: ptr.clone().deref(), + location: ptr.clone().deref(tcx), target: loop_block, unwind: unwind.into_option() }); @@ -652,12 +655,8 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> "move out check doesn't implemented for array bigger then u32"); let size = size as u32; let fields: Vec<(Place<'tcx>, Option)> = (0..size).map(|i| { - (self.place.clone().elem(ProjectionElem::ConstantIndex{ - offset: i, - min_length: size, - from_end: false - }), - self.elaborator.array_subpath(self.path, i, size)) + (self.place.clone().constant_index(self.tcx(), i, size, false), + self.elaborator.array_subpath(self.path, i, size)) }).collect(); if fields.iter().any(|(_,path)| path.is_some()) { @@ -668,8 +667,8 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let move_ = |place: &Place<'tcx>| Operand::Move(place.clone()); let tcx = self.tcx(); - let size = &Place::Local(self.new_temp(tcx.types.usize)); - let size_is_zero = &Place::Local(self.new_temp(tcx.types.bool)); + let size = &Place::local(self.new_temp(tcx.types.usize)); + let size_is_zero = &Place::local(self.new_temp(tcx.types.bool)); let base_block = BasicBlockData { statements: vec![ self.assign(size, Rvalue::NullaryOp(NullOp::SizeOf, ety)), @@ -704,9 +703,9 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> }; let cur = self.new_temp(iter_ty); - let length = Place::Local(self.new_temp(tcx.types.usize)); + let length = Place::local(self.new_temp(tcx.types.usize)); let length_or_end = if ptr_based { - Place::Local(self.new_temp(iter_ty)) + Place::local(self.new_temp(iter_ty)) } else { length.clone() }; @@ -729,13 +728,13 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> unwind, ptr_based); - let cur = Place::Local(cur); + let cur = Place::local(cur); let zero = self.constant_usize(0); let mut drop_block_stmts = vec![]; drop_block_stmts.push(self.assign(&length, Rvalue::Len(self.place.clone()))); if ptr_based { let tmp_ty = tcx.mk_mut_ptr(self.place_ty(self.place)); - let tmp = Place::Local(self.new_temp(tmp_ty)); + let tmp = Place::local(self.new_temp(tmp_ty)); // tmp = &P; // cur = tmp as *mut T; // end = Offset(cur, len); @@ -884,12 +883,12 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> unwind: Unwind ) -> BasicBlock { let tcx = self.tcx(); - let unit_temp = Place::Local(self.new_temp(tcx.mk_nil())); + let unit_temp = Place::local(self.new_temp(tcx.mk_nil())); let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem); let args = adt.variants[0].fields.iter().enumerate().map(|(i, f)| { let field = Field::new(i); let field_ty = f.ty(self.tcx(), substs); - Operand::Move(self.place.clone().field(field, field_ty)) + Operand::Move(self.place.clone().field(self.tcx(), field, field_ty)) }).collect(); let call = TerminatorKind::Call { diff --git a/src/librustc_mir/util/graphviz.rs b/src/librustc_mir/util/graphviz.rs index 22e2b1b0b09c9..32f5d4ef1dccd 100644 --- a/src/librustc_mir/util/graphviz.rs +++ b/src/librustc_mir/util/graphviz.rs @@ -147,7 +147,7 @@ fn write_graph_label<'a, 'gcx, 'tcx, W: Write>(tcx: TyCtxt<'a, 'gcx, 'tcx>, if i > 0 { write!(w, ", ")?; } - write!(w, "{:?}: {}", Place::Local(arg), escape(&mir.local_decls[arg].ty))?; + write!(w, "{:?}: {}", Place::local(arg), escape(&mir.local_decls[arg].ty))?; } write!(w, ") -> {}", escape(mir.return_ty()))?; @@ -163,10 +163,10 @@ fn write_graph_label<'a, 'gcx, 'tcx, W: Write>(tcx: TyCtxt<'a, 'gcx, 'tcx>, if let Some(name) = decl.name { write!(w, r#"{:?}: {}; // {}
"#, - Place::Local(local), escape(&decl.ty), name)?; + Place::local(local), escape(&decl.ty), name)?; } else { write!(w, r#"let mut {:?}: {};
"#, - Place::Local(local), escape(&decl.ty))?; + Place::local(local), escape(&decl.ty))?; } } diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 4bb74c6097450..57ee951e21f2b 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -580,7 +580,7 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut dyn Write) -> i if i != 0 { write!(w, ", ")?; } - write!(w, "{:?}: {}", Place::Local(arg), mir.local_decls[arg].ty)?; + write!(w, "{:?}: {}", Place::local(arg), mir.local_decls[arg].ty)?; } write!(w, ") -> {}", mir.return_ty())?; diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index 3206fa6e17265..eb58533c4697b 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -14,7 +14,7 @@ use rustc::mir::{AggregateKind, AssertMessage, BasicBlock, BasicBlockData}; use rustc::mir::{Constant, Location, Local, LocalDecl}; -use rustc::mir::{Place, PlaceElem, PlaceProjection}; +use rustc::mir::{Place, PlaceBase, PlaceElem}; use rustc::mir::{Mir, Operand, ProjectionElem}; use rustc::mir::{Rvalue, SourceInfo, Statement, StatementKind}; use rustc::mir::{Terminator, TerminatorKind, SourceScope, SourceScopeData}; @@ -201,37 +201,38 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { context: mir_visit::PlaceContext<'tcx>, location: Location) { self.record("Place", place); - self.record(match *place { - Place::Local(..) => "Place::Local", - Place::Static(..) => "Place::Static", - Place::Promoted(..) => "Place::Promoted", - Place::Projection(..) => "Place::Projection", + self.record("PlaceBase", &place.base); + self.record(match place.base { + PlaceBase::Local(..) => "PlaceBase::Local", + PlaceBase::Static(..) => "PlaceBase::Static", + PlaceBase::Promoted(..) => "PlaceBase::Promoted", }, place); - self.super_place(place, context, location); - } - fn visit_projection(&mut self, - place: &PlaceProjection<'tcx>, - context: mir_visit::PlaceContext<'tcx>, - location: Location) { - self.record("PlaceProjection", place); - self.super_projection(place, context, location); + for elem in place.elems.iter() { + self.visit_projection_elem( + elem, + context, + location, + ); + } + + self.super_place(place, context, location); } fn visit_projection_elem(&mut self, - place: &PlaceElem<'tcx>, + place_elem: &PlaceElem<'tcx>, context: mir_visit::PlaceContext<'tcx>, location: Location) { - self.record("PlaceElem", place); - self.record(match *place { + self.record("PlaceElem", place_elem); + self.record(match place_elem { ProjectionElem::Deref => "PlaceElem::Deref", ProjectionElem::Subslice { .. } => "PlaceElem::Subslice", ProjectionElem::Field(..) => "PlaceElem::Field", ProjectionElem::Index(..) => "PlaceElem::Index", ProjectionElem::ConstantIndex { .. } => "PlaceElem::ConstantIndex", ProjectionElem::Downcast(..) => "PlaceElem::Downcast", - }, place); - self.super_projection_elem(place, context, location); + }, place_elem); + self.super_projection_elem(place_elem, context, location); } fn visit_constant(&mut self,