From 357bc2c20bdb6f9bbfc59f9a1e7b50cb93c6fa53 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Wed, 8 Aug 2018 09:29:15 +0800 Subject: [PATCH 01/33] Introduce new Place in librustc/mir --- src/librustc/ich/impls_mir.rs | 24 +++-- src/librustc/mir/mod.rs | 165 ++++++++++++++++++++-------------- src/librustc/mir/tcx.rs | 72 +++++++-------- src/librustc/mir/visit.rs | 51 ++++------- 4 files changed, 171 insertions(+), 141 deletions(-) diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 38ea536b4ee72..e30cb0cef5f70 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -290,28 +290,38 @@ 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, 'tcx> HashStable> +for mir::Place<'tcx> +{ + fn hash_stable( + &self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher, + ) { + self.base.hash_stable(hcx, hasher); + self.elems.hash_stable(hcx, hasher); + } +} + impl<'a, 'gcx, B, V, T> HashStable> for mir::Projection<'gcx, B, V, T> where B: HashStable>, diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index dae5709ba114a..41987bd67b317 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 @@ -1731,7 +1737,7 @@ pub struct Projection<'tcx, B, V, T> { 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), @@ -1779,31 +1785,52 @@ 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 deref(self) -> Place<'tcx> { - self.elem(ProjectionElem::Deref) + pub fn field( + self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + f: Field, + ty: Ty<'tcx>, + ) -> Self { + self.elem(tcx, ProjectionElem::Field(f, ty)) } - pub fn downcast(self, adt_def: &'tcx AdtDef, variant_index: usize) -> Place<'tcx> { - self.elem(ProjectionElem::Downcast(adt_def, variant_index)) + pub fn deref(self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self { + self.elem(tcx, ProjectionElem::Deref) } - pub fn index(self, index: Local) -> Place<'tcx> { - self.elem(ProjectionElem::Index(index)) + pub fn downcast( + self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + adt_def: &'tcx AdtDef, variant_index: usize, + ) -> Self { + self.elem(tcx, ProjectionElem::Downcast(adt_def, variant_index)) + } + + pub fn index(self, tcx: TyCtxt<'a, 'tcx, 'tcx>, index: Local) -> Self { + self.elem(tcx, ProjectionElem::Index(index)) } - pub fn elem(self, elem: PlaceElem<'tcx>) -> Place<'tcx> { - Place::Projection(Box::new(PlaceProjection { base: self, elem })) + pub fn elem( + self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + elem: PlaceElem<'tcx>, + ) -> Self { + Place { + base: self.base, + elems: tcx.intern_place_elems(&[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 +1841,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 +2758,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 +2874,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, @@ -2889,6 +2900,26 @@ where } } +impl<'tcx, B, V, T> TypeFoldable<'tcx> for Projection<'tcx, B, V, T> +where + B: TypeFoldable<'tcx>, + V: TypeFoldable<'tcx>, + T: TypeFoldable<'tcx>, +{ + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { + + let base = self.base.fold_with(folder); + let elem = self.elem.fold_with(folder); + Projection { base, elem } + } + + fn super_visit_with>(&self, visitor: &mut Vs) -> bool { + + self.base.visit_with(visitor) || + self.elem.visit_with(visitor) + } +} + impl<'tcx> TypeFoldable<'tcx> for Field { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self { *self diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index b55843ac527df..af0297d0076ca 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -44,10 +44,11 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> { } } - pub fn projection_ty(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, - elem: &PlaceElem<'tcx>) - -> PlaceTy<'tcx> - { + 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) @@ -110,14 +111,25 @@ 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), + // 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 + if let Some(projection) = self.elems.last() { + self.ty(local_decls, tcx).projection_ty(tcx, projection) + } else { + match self.base { + PlaceBase::Local(index) => + PlaceTy::Ty { ty: local_decls.local_decls()[index].ty }, + PlaceBase::Promoted(ref data) => PlaceTy::Ty { ty: data.1 }, + PlaceBase::Static(ref data) => PlaceTy::Ty { ty: data.ty }, + } } } @@ -125,30 +137,20 @@ impl<'tcx> Place<'tcx> { /// 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 - } else { - self - } - } else { - self - }; + pub fn is_upvar_field_projection<'cx, 'gcx>( + &self, + mir: &'cx Mir<'tcx>, + tcx: &TyCtxt<'cx, 'gcx, 'tcx>, + ) -> Option { + match self.projection() { + Some(ProjectionElem::Field(field, _ty)) => { + let base_ty = self.ty(mir, *tcx).to_ty(*tcx); - match place { - Place::Projection(ref proj) => match proj.elem { - ProjectionElem::Field(field, _ty) => { - let base_ty = proj.base.ty(mir, *tcx).to_ty(*tcx); - - if base_ty.is_closure() || base_ty.is_generator() { - Some(field) - } else { - None - } - }, - _ => None, + if base_ty.is_closure() || base_ty.is_generator() { + Some(*field) + } else { + None + } }, _ => None, } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index cab6ed0c122cd..16a7b1b22bd96 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,20 +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); - } + } + + for elem in elems.iter() { + self.visit_projection_elem( + &$($mutability)* (*elem).clone(), + context, + location + ) } } @@ -663,23 +667,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>, From 34329c0a7e52f0939abb3a78012e580abd4cf632 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Wed, 8 Aug 2018 09:31:34 +0800 Subject: [PATCH 02/33] Intern Place elems --- src/librustc/ty/context.rs | 49 +++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) 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 { From bd9f156a84eb53bb3df17c260d6fab9a7720220c Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Wed, 8 Aug 2018 17:47:14 +0800 Subject: [PATCH 03/33] Add split_projection/base_place method --- src/librustc/mir/tcx.rs | 72 ++++++++++++++--- src/librustc_mir/transform/add_validation.rs | 85 ++++++++++---------- 2 files changed, 103 insertions(+), 54 deletions(-) diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index af0297d0076ca..07d7eedf8096f 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -32,7 +32,7 @@ pub enum PlaceTy<'tcx> { impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> { pub fn from_ty(ty: Ty<'tcx>) -> PlaceTy<'tcx> { - PlaceTy::Ty { ty: ty } + PlaceTy::Ty { ty } } pub fn to_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { @@ -142,19 +142,71 @@ impl<'tcx> Place<'tcx> { mir: &'cx Mir<'tcx>, tcx: &TyCtxt<'cx, 'gcx, 'tcx>, ) -> Option { - match self.projection() { - Some(ProjectionElem::Field(field, _ty)) => { - let base_ty = self.ty(mir, *tcx).to_ty(*tcx); + let base_place; + let mut place = self; + let mut by_ref = false; - if base_ty.is_closure() || base_ty.is_generator() { - Some(*field) - } else { - None - } - }, + base_place = place.base_place(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.base_place(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 { + None + } + } else { + None + } + } + + // for Place: + // Base.[a, b, c] + // ^-- projection + // => (Base.[a, b], c]) + // ^^^^^^^^^^ ^-- projection + // |-- base_place + pub fn split_projection<'cx, 'gcx>( + &self, + tcx: &TyCtxt<'cx, 'gcx, 'tcx>, + ) -> Option<(Place<'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)) => Some(( + Place { + base: self.clone().base, + elems: tcx.intern_place_elems(place_elems), + }, + projection, + )), _ => None, } } + + // 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 base_place<'cx, 'gcx>( + &self, + tcx: &TyCtxt<'cx, 'gcx, 'tcx>, + ) -> Place<'tcx> { + match self.split_projection(tcx) { + Some((place, _)) => place, + _ => self.clone(), + } + } } pub enum RvalueInitializationState { diff --git a/src/librustc_mir/transform/add_validation.rs b/src/librustc_mir/transform/add_validation.rs index 4f7f45f173f73..d7e4351884948 100644 --- a/src/librustc_mir/transform/add_validation.rs +++ b/src/librustc_mir/transform/add_validation.rs @@ -30,54 +30,51 @@ fn place_context<'a, 'tcx, D>( ) -> (Option, hir::Mutability) where D: HasLocalDecls<'tcx> { - use rustc::mir::Place::*; - - 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); + use rustc::mir::PlaceBase::*; + match place.elems.last() { + Some(proj) => match proj { + 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(&place.base_place(&tcx), 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); + (re, mutbl) } - _ => place_context(&proj.base, local_decls, tcx), } - } + _ => place_context(&place.base_place(&tcx), local_decls, tcx), + }, + _ => match place.base { + Local { .. } => (None, hir::MutMutable), + Promoted(_) | Static(_) => (None, hir::MutImmutable), + }, } } From 85a87a1d9a1e1ca51048c876c1966776b6429b38 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Wed, 8 Aug 2018 19:55:05 +0800 Subject: [PATCH 04/33] Try read Place --- src/librustc_mir/interpret/place.rs | 118 +++++++++++++++------------- 1 file changed, 62 insertions(+), 56 deletions(-) diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 91c2519230695..d3dbb19ad4de9 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -101,17 +101,22 @@ 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), + use rustc::mir::PlaceBase::*; + + if let Some((base_place, projection)) = place.split_projection(self.tcx()) { + self.try_read_place(&base_place); + self.tr_read_place_projection(&base_place, projection); + } else { + match place.base { + // 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), + } } } @@ -157,16 +162,17 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { fn try_read_place_projection( &self, + base_place: &mir::Place<'tcx>, proj: &mir::PlaceProjection<'tcx>, ) -> EvalResult<'tcx, Option> { use rustc::mir::ProjectionElem::*; - let base = match self.try_read_place(&proj.base)? { + let base = match self.try_read_place(base_place)? { Some(base) => base, None => return Ok(None), }; - let base_ty = self.place_ty(&proj.base); + let base_ty = self.place_ty(base_place); let base_layout = self.layout_of(base_ty)?; - match proj.elem { + 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(..) | @@ -204,53 +210,53 @@ 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 { - Local(mir::RETURN_PLACE) => self.frame().return_place, - Local(local) => Place::Local { - frame: self.cur_frame(), - local, - }, + use rustc::mir::PlaceBase::*; - Promoted(ref promoted) => { - let instance = self.frame().instance; - let val = self.read_global_as_value(GlobalId { - instance, - promoted: Some(promoted.0), - })?; - if let Value::ByRef(ptr, align) = val { - Place::Ptr { - ptr: ptr.into(), - align, - extra: PlaceExtra::None, + let place = if let Some((base_place, projection)) = mir_place.split_projection(self.tcx()) { + let ty = self.place_ty(&base_place); + let place = self.eval_place(&base_place)?; + self.eval_place_projection(place, ty, projection) + } else { + match mir_place.base { + Local(mir::RETURN_PLACE) => self.frame().return_place, + Local(local) => Place::Local { + frame: self.cur_frame(), + local, + }, + + Promoted(ref promoted) => { + let instance = self.frame().instance; + let val = self.read_global_as_value(GlobalId { + instance, + promoted: Some(promoted.0), + })?; + if let Value::ByRef(ptr, align) = val { + Place::Ptr { + ptr: ptr.into(), + align, + extra: PlaceExtra::None, + } + } else { + bug!("evaluated promoted and got {:#?}", val); } - } else { - bug!("evaluated promoted and got {:#?}", val); } - } - Static(ref static_) => { - let layout = self.layout_of(self.place_ty(mir_place))?; - let instance = ty::Instance::mono(*self.tcx, static_.def_id); - let cid = GlobalId { - instance, - promoted: None - }; - let alloc = Machine::init_static(self, cid)?; - Place::Ptr { - ptr: ScalarMaybeUndef::Scalar(Scalar::Ptr(alloc.into())), - align: layout.align, - extra: PlaceExtra::None, + Static(ref static_) => { + let layout = self.layout_of(self.place_ty(mir_place))?; + let instance = ty::Instance::mono(*self.tcx, static_.def_id); + let cid = GlobalId { + instance, + promoted: None + }; + let alloc = Machine::init_static(self, cid)?; + Place::Ptr { + ptr: ScalarMaybeUndef::Scalar(Scalar::Ptr(alloc.into())), + align: layout.align, + 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); - } }; - self.dump_local(place); Ok(place) @@ -383,10 +389,10 @@ 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) From a5c428bab7ddb84bf4efd2676a7f677b7d3429bf Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Thu, 9 Aug 2018 19:11:49 +0800 Subject: [PATCH 05/33] Replace with new Place in mir/borrowck --- src/librustc/mir/mod.rs | 7 + src/librustc/mir/tcx.rs | 8 +- src/librustc_mir/borrow_check/borrow_set.rs | 12 +- .../borrow_check/error_reporting.rs | 264 +++++---- src/librustc_mir/borrow_check/mod.rs | 547 +++++++++--------- src/librustc_mir/borrow_check/move_errors.rs | 70 ++- .../borrow_check/mutability_errors.rs | 528 ++++++++--------- .../borrow_check/nll/constraint_generation.rs | 6 +- .../borrow_check/nll/explain_borrow/mod.rs | 8 +- .../borrow_check/nll/invalidation.rs | 14 +- .../nll/region_infer/error_reporting/mod.rs | 4 +- .../borrow_check/nll/type_check/mod.rs | 92 +-- src/librustc_mir/borrow_check/path_utils.rs | 39 +- src/librustc_mir/borrow_check/place_ext.rs | 47 +- .../borrow_check/places_conflict.rs | 270 +++++---- src/librustc_mir/borrow_check/prefixes.rs | 162 +++--- src/librustc_mir/borrow_check/used_muts.rs | 4 +- src/librustc_mir/interpret/place.rs | 4 +- src/librustc_mir/transform/add_validation.rs | 6 +- 19 files changed, 1066 insertions(+), 1026 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 41987bd67b317..f7fc694188962 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1826,6 +1826,13 @@ impl<'a, 'tcx> Place<'tcx> { elems: tcx.intern_place_elems(&[elem]), } } + + pub fn local(local: Local) -> Self { + Place { + base: PlaceBase::Local(local), + elems: Slice::empty(), + } + } } impl<'tcx> Debug for PlaceBase<'tcx> { diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index 07d7eedf8096f..353d48930c81c 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -146,14 +146,14 @@ impl<'tcx> Place<'tcx> { let mut place = self; let mut by_ref = false; - base_place = place.base_place(tcx); + base_place = place.base_place(*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.base_place(tcx).ty(mir, *tcx).to_ty(*tcx); + let base_ty = place.base_place(*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)) { @@ -174,7 +174,7 @@ impl<'tcx> Place<'tcx> { // |-- base_place pub fn split_projection<'cx, 'gcx>( &self, - tcx: &TyCtxt<'cx, 'gcx, 'tcx>, + tcx: TyCtxt<'cx, 'gcx, 'tcx>, ) -> Option<(Place<'tcx>, &PlaceElem<'tcx>)> { // split place_elems // Base.[a, b, c] @@ -200,7 +200,7 @@ impl<'tcx> Place<'tcx> { // ^^-- no projection pub fn base_place<'cx, 'gcx>( &self, - tcx: &TyCtxt<'cx, 'gcx, 'tcx>, + tcx: TyCtxt<'cx, 'gcx, 'tcx>, ) -> Place<'tcx> { match self.split_projection(tcx) { Some((place, _)) => place, diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/src/librustc_mir/borrow_check/borrow_set.rs index fe33dc0a58a47..d97b5781f4120 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; @@ -177,7 +177,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { self.insert_as_pending_if_two_phase(location, &assigned_place, region, kind, idx); insert(&mut self.region_map, ®ion, idx); - if let Some(local) = borrowed_place.root_local() { + if let Some(local) = borrowed_place.root_local(self.tcx) { insert(&mut self.local_map, &local, idx); } } @@ -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..383ac011b8669 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -12,7 +12,7 @@ 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::{LocalDecl, LocalKind, Location, Operand, Place, PlaceBase}; use rustc::mir::{ProjectionElem, Rvalue, Statement, StatementKind}; use rustc::ty; use rustc_data_structures::indexed_vec::Idx; @@ -392,8 +392,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { 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) => self.mir.local_decls[local].source_info.span, _ => drop_span, }; @@ -531,7 +531,7 @@ 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 { + let (from_arg, local_decl) = if let PlaceBase::Local(local) = err_place.base { if let LocalKind::Arg = self.mir.local_kind(local) { (true, Some(&self.mir.local_decls[local])) } else { @@ -633,130 +633,130 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { mut autoderef: bool, including_downcast: &IncludingDowncast, ) -> Result<(), ()> { - match *place { - Place::Promoted(_) => { - buf.push_str("promoted"); - } - Place::Local(local) => { - self.append_local_to_string(local, buf)?; - } - Place::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)); - } + if let Some((base_place, projection)) = place.split_projection(self.tcx) { + 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.to_string(); + if self.mir.upvar_decls[var_index].by_ref { + buf.push_str(&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 + buf.push_str(&format!("*{}", &name)); + } + } else { + if autoderef { + self.append_place_to_string( + &base_place, + buf, + autoderef, + &including_downcast, + )?; + } else if let PlaceBase::Local(local) = base_place.base { + if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) = + self.mir.local_decls[local].is_user_variable { self.append_place_to_string( - &proj.base, + &base_place, 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, + &base_place, 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); + buf.push_str(&"*"); self.append_place_to_string( - &proj.base, + &base_place, 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::Downcast(..) => { + self.append_place_to_string( + &base_place, + buf, + autoderef, + &including_downcast, + )?; + if including_downcast.0 { + return Err(()); } - 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 + } + 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(&base_place, *field); self.append_place_to_string( - &proj.base, + &base_place, buf, autoderef, &including_downcast, )?; - buf.push_str(&"[..]"); + buf.push_str(&format!(".{}", field_name)); } - }; + } + ProjectionElem::Index(index) => { + autoderef = true; + + self.append_place_to_string( + &base_place, + 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( + &base_place, + buf, + autoderef, + &including_downcast, + )?; + buf.push_str(&"[..]"); + } + } + } else { + match place.base { + PlaceBase::Promoted(_) => { + buf.push_str("promoted"); + } + PlaceBase::Local(local) => { + self.append_local_to_string(local, buf)?; + } + PlaceBase::Static(ref static_) => { + buf.push_str(&self.tcx.item_name(static_.def_id).to_string()); + } } } - Ok(()) } @@ -774,19 +774,14 @@ 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 { + if let Some((base_place, projection)) = place.split_projection(self.tcx) { + match projection { + + ProjectionElem::Deref => self.describe_field(&base_place, field), ProjectionElem::Downcast(def, variant_index) => format!( "{}", - def.variants[variant_index].fields[field.index()].ident + def.variants[*variant_index].fields[field.index()].ident ), ProjectionElem::Field(_, field_type) => { self.describe_field_from_ty(&field_type, field) @@ -794,9 +789,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { - self.describe_field(&proj.base, field).to_string() + self.describe_field(&base_place, field).to_string() } - }, + } + } else { + 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), + } } } @@ -843,17 +847,21 @@ 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]; - 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), + let place = place.clone(); + if let Some(projection) = place.projection() { + match projection { + ProjectionElem::Field(_, ty) => Some(*ty), _ => None, - }, + } + } else { + match place.base { + PlaceBase::Local(local) => { + let local = &self.mir.local_decls[local]; + Some(local.ty) + } + PlaceBase::Promoted(ref prom) => Some(prom.1), + PlaceBase::Static(ref st) => Some(st.ty), + } } } } @@ -990,7 +998,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 +1032,15 @@ 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 => { 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..21ba1194aa07a 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -18,8 +18,8 @@ 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::ty::query::Providers; use rustc::ty::{self, ParamEnv, TyCtxt, Ty}; @@ -547,7 +547,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 +924,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 +957,33 @@ 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() { + 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() { + 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 +1005,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 +1398,19 @@ 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(place) | Operand::Copy(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 { + self.used_mut.insert(local); + } + }, + _ => {} } } } @@ -1485,26 +1491,27 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // 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 let Some(projection) = root_place.projection() { + bug!("root of {:?} is a projection ({:?})?", place, root_place) + } else { + match root_place.base { + PlaceBase::Promoted(_) => (true, false), + PlaceBase::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) + } + 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) + } } }; @@ -1539,7 +1546,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { fn check_for_local_borrow(&mut self, borrow: &BorrowData<'tcx>, yield_span: Span) { debug!("check_for_local_borrow({:?})", borrow); - if borrow_of_local_data(&borrow.borrowed_place) { + if borrow_of_local_data(self.tcx, &borrow.borrowed_place) { let err = self.tcx .cannot_borrow_across_generator_yield( self.retrieve_borrow_spans(borrow).var_or_use(), @@ -1769,11 +1776,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } 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), + if let Some(_) = last_prefix.projection() { + panic!("PrefixSet::All meant don't stop for Projection") + } else { + match last_prefix.base { + PlaceBase::Local(_) => panic!("should have move path for every Local"), + PlaceBase::Promoted(_) | + PlaceBase::Static(_) => return Err(NoMovePathFound::ReachedStatic), + } } } @@ -1798,66 +1808,67 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // 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? + if let Some((base_place, projection)) = place.split_projection(self.tcx) { + match projection { + 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; - } + // 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::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; - } - _ => {} + 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_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; } + _ => {} } } + } - place = base; - continue; + place = &base_place; + continue; + } else { + match place.base { + PlaceBase::Promoted(_) + | PlaceBase::Local(_) + | PlaceBase::Static(_) => { + // assigning to `x` does not require `x` be initialized. + break; + }, } } } @@ -1990,7 +2001,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ) { match root_place { RootPlace { - place: Place::Local(local), + place: Place { + base: PlaceBase::Local(local), + elems: _, + }, is_local_mutation_allowed, } => { if is_local_mutation_allowed != LocalMutationIsAllowed::Yes { @@ -2012,7 +2026,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { is_local_mutation_allowed: LocalMutationIsAllowed::Yes, } => {} RootPlace { - place: place @ Place::Projection(_), + place, is_local_mutation_allowed: _, } => { if let Some(field) = place.is_upvar_field_projection(self.mir, &self.tcx) { @@ -2020,11 +2034,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } } RootPlace { - place: Place::Promoted(..), + place: Place { + base: PlaceBase::Promoted(..), + elems: _, + }, is_local_mutation_allowed: _, } => {} RootPlace { - place: Place::Static(..), + place: Place { + base: PlaceBase::Static(..), + elems: _, + }, is_local_mutation_allowed: _, } => {} } @@ -2037,154 +2057,154 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { place: &'d Place<'tcx>, 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 { - 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, - }), - } - } - // 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, - }) - } - } - 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) - { - Some(field) - if { - self.mir.upvar_decls[field.index()].by_ref - } => + if let Some((base_place, projection)) = place.split_projection(self.tcx) { + match projection { + ProjectionElem::Deref => { + let base_ty = base_place.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, + ) { + Some(field) + if { + self.mir.upvar_decls[field.index()].by_ref + } => { is_local_mutation_allowed } - _ => LocalMutationIsAllowed::Yes, - }; + _ => LocalMutationIsAllowed::Yes, + }; - self.is_mutable(&proj.base, mode) - } + self.is_mutable(&base_place, 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, - }); - } - } - } - // `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() => self.is_mutable(&base_place, 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(&base_place, is_local_mutation_allowed)?; + Ok(RootPlace { + place, + is_local_mutation_allowed, + }) + } + } + } else { + self.is_mutable(&base_place, is_local_mutation_allowed) + } + } + } + } else { + match place.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, + }), + } + } + // 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(ref static_) => { + if self.tcx.is_static(static_.def_id) != Some(hir::Mutability::MutMutable) { + Err(place) + } else { + Ok(RootPlace { + place, + is_local_mutation_allowed, + }) } } } @@ -2229,17 +2249,20 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { 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; + if let Some((base_place, projection)) = cursor.split_projection(self.tcx) { + if *projection == ProjectionElem::Deref + && place.ty(self.mir, self.tcx).to_ty(self.tcx).is_box() + { + deepest = &base_place; + } + cursor = &base_place; + } else { + match cursor.base { + PlaceBase::Promoted(_) + | PlaceBase::Local(..) + | PlaceBase::Static(..) => return deepest, + } } - cursor = &proj.base; } } } diff --git a/src/librustc_mir/borrow_check/move_errors.rs b/src/librustc_mir/borrow_check/move_errors.rs index 4d988fef450b8..9d410aecfbcfe 100644 --- a/src/librustc_mir/borrow_check/move_errors.rs +++ b/src/librustc_mir/borrow_check/move_errors.rs @@ -92,7 +92,10 @@ 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 @@ -238,12 +241,10 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { 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!(), + if let Some((base_place, _)) = place.split_projection(self.tcx) { + PlaceBase::Local(Local::new(1)) == base_place.base + } else { + false } } => { @@ -300,23 +301,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 Some((base_place, 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 +375,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.projection() { + 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..bebb2ea9b76d8 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,102 @@ 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)) + if let Some((base_place, 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() { - "`&` 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) + 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) + } } + } 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 @@ -199,15 +183,139 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { } }; - 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 Some((base_place, projection)) = the_place_err.split_projection(self.tcx) { + match projection { + ProjectionElem::Deref => { + if let PlaceBase::Local(local) = base_place.base { + 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 +324,47 @@ 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 Some((base_place, 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 Some((base_place, projection)) = base_place.split_projection(self.tcx) { + if let ProjectionElem::Field(upvar_index, _) = projection { + 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 { + unreachable!{} + } + } else { + unreachable!{} + } + } + _ => unreachable!{}, } - _ => false, + } else { + false } } } diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 457499ded5657..34b9e58f7a2be 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,8 +138,8 @@ 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) { + if let PlaceBase::Local(temp) = place.base { + 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..f98ae0a778c95 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,10 +90,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ); if let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place { - if let Place::Local(borrowed_local) = place { + if let PlaceBase::Local(borrowed_local) = place.base { let dropped_local_scope = mir.local_decls[local].visibility_scope; let borrowed_local_scope = - mir.local_decls[*borrowed_local].visibility_scope; + mir.local_decls[borrowed_local].visibility_scope; if mir.is_sub_scope(borrowed_local_scope, dropped_local_scope) { err.note( diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs index 301999cc4a51e..9227582f30090 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, ); @@ -251,7 +251,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tc let borrow_set = self.borrow_set.clone(); let resume = self.location_table.start_index(resume.start_location()); for i in borrow_set.borrows.indices() { - if borrow_of_local_data(&borrow_set.borrows[i].borrowed_place) { + if borrow_of_local_data(self.infcx.tcx, &borrow_set.borrows[i].borrowed_place) { self.all_facts.invalidates.push((resume, i)); } } @@ -261,7 +261,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tc let borrow_set = self.borrow_set.clone(); let start = self.location_table.start_index(location); for i in borrow_set.borrows.indices() { - if borrow_of_local_data(&borrow_set.borrows[i].borrowed_place) { + if borrow_of_local_data(self.infcx.tcx, &borrow_set.borrows[i].borrowed_place) { self.all_facts.invalidates.push((start, i)); } } @@ -300,7 +300,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> { (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(self.infcx.tcx, Field::new(index), field_ty); ig.visit_terminator_drop(loc, term, &place, field_ty); }; @@ -326,13 +326,13 @@ 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)) && !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)) && !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..0035d03b6b65d 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,7 @@ 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) { 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..f0ea52f4ff17d 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -368,48 +368,14 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { 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, - }, - Place::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) - // that they ought not to do. We have to figure out - // how best to handle that -- probably we want treat - // promoted MIR much like closures, renumbering all - // their free regions and propagating constraints - // upwards. We have the same acyclic guarantees, so - // that should be possible. But for now, ignore them. - // - // let promoted_mir = &self.mir.promoted[index]; - // promoted_mir.return_ty() - PlaceTy::Ty { ty: sty } - } - Place::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); - if let Err(terr) = self.cx.eq_types(ty, sty, location.boring()) { - span_mirbug!( - self, - place, - "bad static type ({:?}: {:?}): {:?}", - ty, - sty, - terr - ); - } - PlaceTy::Ty { ty: sty } - } - Place::Projection(ref proj) => { + + let place_ty = if let Some((base_place, projection)) = place.split_projection(self.tcx()) { 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); + let base_ty = self.sanitize_place(&base_place, location, base_context); if let PlaceTy::Ty { ty } = base_ty { if ty.references_error() { assert!(self.errors_reported); @@ -418,7 +384,43 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { }; } } - self.sanitize_projection(base_ty, &proj.elem, place, location) + self.sanitize_projection(base_ty, &projection, place, location) + } else { + match place.base { + PlaceBase::Local(index) => PlaceTy::Ty { + ty: self.mir.local_decls[index].ty, + }, + 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) + // that they ought not to do. We have to figure out + // how best to handle that -- probably we want treat + // promoted MIR much like closures, renumbering all + // their free regions and propagating constraints + // upwards. We have the same acyclic guarantees, so + // that should be possible. But for now, ignore them. + // + // let promoted_mir = &self.mir.promoted[index]; + // promoted_mir.return_ty() + PlaceTy::Ty { 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); + if let Err(terr) = self.cx.eq_types(ty, sty, location.boring()) { + span_mirbug!( + self, + place, + "bad static type ({:?}: {:?}): {:?}", + ty, + sty, + terr + ); + } + PlaceTy::Ty { ty: sty } + } } }; if let PlaceContext::Copy = context { @@ -460,7 +462,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,8 +872,8 @@ 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() + let is_temp = if let PlaceBase::Local(l) = place.base { + !mir.local_decls[l].is_user_variable.is_some() } else { false }; @@ -1597,13 +1599,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { "add_reborrow_constraint({:?}, {:?}, {:?})", location, borrow_region, borrowed_place ); - while let Place::Projection(box PlaceProjection { base, elem }) = borrowed_place { + while let Some((base_place, projection)) = borrowed_place.split_projection(self.infcx.tcx) { debug!("add_reborrow_constraint - iteration {:?}", borrowed_place); - match *elem { + match projection { ProjectionElem::Deref => { let tcx = self.infcx.tcx; - let base_ty = base.ty(self.mir, tcx).to_ty(tcx); + let base_ty = base_place.ty(self.mir, tcx).to_ty(tcx); debug!("add_reborrow_constraint - base_ty = {:?}", base_ty); match base_ty.sty { @@ -1675,7 +1677,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // The "propagate" case. We need to check that our base is valid // for the borrow's lifetime. - borrowed_place = base; + borrowed_place = &base_place; } } diff --git a/src/librustc_mir/borrow_check/path_utils.rs b/src/librustc_mir/borrow_check/path_utils.rs index 70b4e0ea2f03c..3b053b2aae00b 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,27 @@ 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<'a, 'tcx, 'gcx: 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + place: &Place<'tcx> +) -> bool { + if let Some((base_place, projection)) = place.split_projection(tcx) { + match projection {// Reborrow of already borrowed data is ignored + // Any errors will be caught on the initial borrow + ProjectionElem::Deref => false, - // 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 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(tcx, &base_place), + } + } else { + match place.base { + PlaceBase::Promoted(_) + | PlaceBase::Static(..) => false, + PlaceBase::Local(..) => true, } } } diff --git a/src/librustc_mir/borrow_check/place_ext.rs b/src/librustc_mir/borrow_check/place_ext.rs index b0517c5e61f2b..fa6bd09bc4678 100644 --- a/src/librustc_mir/borrow_check/place_ext.rs +++ b/src/librustc_mir/borrow_check/place_ext.rs @@ -10,7 +10,7 @@ use rustc::hir; use rustc::mir::ProjectionElem; -use rustc::mir::{Local, Mir, Place}; +use rustc::mir::{Local, Mir, Place, PlaceBase}; use rustc::ty::{self, TyCtxt}; /// Extension methods for the `Place` type. @@ -20,42 +20,51 @@ crate trait PlaceExt<'tcx> { /// If this is a place like `x.f.g`, returns the local /// `x`. Returns `None` if this is based in a static. - fn root_local(&self) -> Option; + fn root_local(&self, tcx: TyCtxt<'_, '_, 'tcx>) -> Option; } 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_) => { - tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable) - } - Place::Projection(proj) => match proj.elem { + if let Some((base_place, projectioin)) = self.split_projection(tcx) { + match projectioin { ProjectionElem::Field(..) | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Index(_) => proj.base.is_unsafe_place(tcx, mir), + | ProjectionElem::Index(_) => base_place.is_unsafe_place(tcx, mir), ProjectionElem::Deref => { - let ty = proj.base.ty(mir, tcx).to_ty(tcx); + let ty = base_place.ty(mir, tcx).to_ty(tcx); match ty.sty { ty::TyRawPtr(..) => true, - _ => proj.base.is_unsafe_place(tcx, mir), + _ => base_place.is_unsafe_place(tcx, mir), } } - }, + } + } else { + match self.base { + PlaceBase::Promoted(_) | + PlaceBase::Local(_) => false, + PlaceBase::Static(static_) => { + tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable) + } + } } } - fn root_local(&self) -> Option { + fn root_local( + &self, + tcx: TyCtxt<'_, '_, 'tcx>, + ) -> 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), + if let Some((base_place, projection)) = p.split_projection(tcx) { + p = &base_place + } else { + match p.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..a63d42e3404ea 100644 --- a/src/librustc_mir/borrow_check/places_conflict.rs +++ b/src/librustc_mir/borrow_check/places_conflict.rs @@ -12,11 +12,13 @@ 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::mir::{Mir, Place, PlaceBase}; +use rustc::mir::ProjectionElem; use rustc::ty::{self, TyCtxt}; use std::cmp::max; +// FIXME(csmoe): rewrite place_conflict with slice + pub(super) fn places_conflict<'gcx, 'tcx>( tcx: TyCtxt<'_, 'gcx, 'tcx>, mir: &Mir<'tcx>, @@ -29,8 +31,8 @@ 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| { + unroll_place(tcx, borrow_place, None, |borrow_components| { + unroll_place(tcx, access_place, None, |access_components| { place_components_conflict(tcx, mir, borrow_components, access_components, access) }) }) @@ -141,56 +143,56 @@ 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); + if let Some((base_place, projection)) = borrow_c.split_projection(tcx) { + let base_ty = base_place.ty(mir, tcx).to_ty(tcx); - match (elem, &base_ty.sty, access) { - (_, _, Shallow(Some(ArtificialField::Discriminant))) - | (_, _, Shallow(Some(ArtificialField::ArrayLength))) => { - // The discriminant and array length are like - // additional fields on the type; they do not - // overlap any existing data there. Furthermore, - // they cannot actually be a prefix of any - // borrowed place (at least in MIR as it is - // currently.) - // - // e.g. a (mutable) borrow of `a[5]` while we read the - // array length of `a`. - debug!("places_conflict: implicit field"); - return false; - } + match (projection, &base_ty.sty, access) { + (_, _, Shallow(Some(ArtificialField::Discriminant))) + | (_, _, Shallow(Some(ArtificialField::ArrayLength))) => { + // The discriminant and array length are like + // additional fields on the type; they do not + // overlap any existing data there. Furthermore, + // they cannot actually be a prefix of any + // borrowed place (at least in MIR as it is + // currently.) + // + // e.g. a (mutable) borrow of `a[5]` while we read the + // array length of `a`. + debug!("places_conflict: implicit field"); + return false; + } - (ProjectionElem::Deref, _, Shallow(None)) => { - // e.g. a borrow of `*x.y` while we shallowly access `x.y` or some - // prefix thereof - the shallow access can't touch anything behind - // the pointer. - debug!("places_conflict: shallow access behind ptr"); - return false; - } - (ProjectionElem::Deref, ty::TyRef(_, _, hir::MutImmutable), _) => { - // the borrow goes through a dereference of a shared reference. - // - // I'm not sure why we are tracking these borrows - shared - // references can *always* be aliased, which means the - // permission check already account for this borrow. - debug!("places_conflict: behind a shared ref"); - return false; - } + (ProjectionElem::Deref, _, Shallow(None)) => { + // e.g. a borrow of `*x.y` while we shallowly access `x.y` or some + // prefix thereof - the shallow access can't touch anything behind + // the pointer. + debug!("places_conflict: shallow access behind ptr"); + return false; + } + (ProjectionElem::Deref, ty::TyRef(_, _, hir::MutImmutable), _) => { + // the borrow goes through a dereference of a shared reference. + // + // I'm not sure why we are tracking these borrows - shared + // references can *always* be aliased, which means the + // permission check already account for this borrow. + debug!("places_conflict: behind a shared ref"); + return false; + } - (ProjectionElem::Deref, _, Deep) - | (ProjectionElem::Field { .. }, _, _) - | (ProjectionElem::Index { .. }, _, _) - | (ProjectionElem::ConstantIndex { .. }, _, _) - | (ProjectionElem::Subslice { .. }, _, _) - | (ProjectionElem::Downcast { .. }, _, _) => { - // Recursive case. This can still be disjoint on a - // further iteration if this a shallow access and - // there's a deref later on, e.g. a borrow - // of `*x.y` while accessing `x`. + (ProjectionElem::Deref, _, Deep) + | (ProjectionElem::Field { .. }, _, _) + | (ProjectionElem::Index { .. }, _, _) + | (ProjectionElem::ConstantIndex { .. }, _, _) + | (ProjectionElem::Subslice { .. }, _, _) + | (ProjectionElem::Downcast { .. }, _, _) => { + // Recursive case. This can still be disjoint on a + // further iteration if this a shallow access and + // there's a deref later on, e.g. a borrow + // of `*x.y` while accessing `x`. + } } + } else { + bug!("place has no base?"); } } } else { @@ -267,28 +269,33 @@ impl<'p, 'tcx> PlaceComponentsIter<'p, 'tcx> { /// Recursively "unroll" a place into a `PlaceComponents` list, /// invoking `op` with a `PlaceComponentsIter`. -fn unroll_place<'tcx, R>( +fn unroll_place<'a, 'gcx: 'tcx, 'tcx, R>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, place: &Place<'tcx>, next: Option<&PlaceComponents<'_, 'tcx>>, op: impl FnOnce(PlaceComponentsIter<'_, 'tcx>) -> R, ) -> R { - match place { - Place::Projection(interior) => unroll_place( - &interior.base, + if let Some((base_place, projection)) = place.split_projection(tcx) { + unroll_place( + tcx, + &base_place, Some(&PlaceComponents { component: place, next, }), op, - ), - - Place::Promoted(_) | - Place::Local(_) | Place::Static(_) => { - let list = PlaceComponents { - component: place, - next, - }; - op(list.iter()) + ) + } else { + match place.base { + PlaceBase::Promoted(_) + | PlaceBase::Local(_) + | PlaceBase::Static(_) => { + let list = PlaceComponents { + component: place, + next, + }; + op(list.iter()) + } } } } @@ -302,57 +309,9 @@ fn place_element_conflict<'a, 'gcx: 'tcx, '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) { + match (elem1.split_projection(tcx), elem2.projection()) { + (Some((base_place1, proj1)), Some(proj2)) => { + match (proj1, proj2) { (ProjectionElem::Deref, ProjectionElem::Deref) => { // derefs (e.g. `*x` vs. `*x`) - recur. debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF"); @@ -364,7 +323,7 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>( debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD"); Overlap::EqualOrDisjoint } else { - let ty = pi1.base.ty(mir, tcx).to_ty(tcx); + let ty = base_place1.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. @@ -427,7 +386,8 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>( 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 }) => { + offset: o2, min_length: _, from_end: true + }) => { if o1 == o2 { debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX"); Overlap::EqualOrDisjoint @@ -437,13 +397,17 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>( } } (ProjectionElem::ConstantIndex { - offset: offset_from_begin, min_length: min_length1, from_end: false }, + offset: offset_from_begin, min_length: min_length1, from_end: false + }, ProjectionElem::ConstantIndex { - offset: offset_from_end, min_length: min_length2, from_end: true }) + 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 }) => { + 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 @@ -459,8 +423,8 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>( } } (ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false }, - ProjectionElem::Subslice {from, .. }) - | (ProjectionElem::Subslice {from, .. }, + ProjectionElem::Subslice { from, .. }) + | (ProjectionElem::Subslice { from, .. }, ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false }) => { if offset >= from { debug!( @@ -472,8 +436,8 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>( } } (ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true }, - ProjectionElem::Subslice {from: _, to }) - | (ProjectionElem::Subslice {from: _, to }, + ProjectionElem::Subslice { from: _, to }) + | (ProjectionElem::Subslice { from: _, to }, ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true }) => { if offset > to { debug!("place_element_conflict: \ @@ -486,7 +450,7 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>( } (ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => { debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-SUBSLICES"); - Overlap::EqualOrDisjoint + Overlap::EqualOrDisjoint } (ProjectionElem::Deref, _) | (ProjectionElem::Field(..), _) @@ -500,10 +464,62 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>( ), } } - (Place::Projection(_), _) | (_, Place::Projection(_)) => bug!( + (Some(_), _) | (_, Some(_)) => bug!( "unexpected elements in place_element_conflict: {:?} and {:?}", elem1, elem2 ), + (None, None) => { + match (elem1.base, elem2.base) { + (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 + } + } + } } } diff --git a/src/librustc_mir/borrow_check/prefixes.rs b/src/librustc_mir/borrow_check/prefixes.rs index 9b16130d25ec4..1fba00f41ce2d 100644 --- a/src/librustc_mir/borrow_check/prefixes.rs +++ b/src/librustc_mir/borrow_check/prefixes.rs @@ -21,25 +21,31 @@ use super::MirBorrowckCtxt; use rustc::hir; use rustc::ty::{self, TyCtxt}; -use rustc::mir::{Mir, Place, ProjectionElem}; +use rustc::mir::{Mir, Place, PlaceBase, ProjectionElem}; pub trait IsPrefixOf<'tcx> { - fn is_prefix_of(&self, other: &Place<'tcx>) -> bool; + fn is_prefix_of(&self, tcx: TyCtxt<'_, '_, 'tcx>, other: &Place<'tcx>) -> bool; } impl<'tcx> IsPrefixOf<'tcx> for Place<'tcx> { - fn is_prefix_of(&self, other: &Place<'tcx>) -> bool { + fn is_prefix_of( + &self, + tcx: TyCtxt<'_, '_, 'tcx>, + 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; + if let Some((base_place, _projection)) = cursor.split_projection(tcx) { + cursor = &base_place; + } else { + match cursor.base { + PlaceBase::Promoted(_) + | PlaceBase::Local(_) + | PlaceBase::Static(_) => return false, } } } @@ -98,89 +104,79 @@ impl<'cx, 'gcx, 'tcx> Iterator for Prefixes<'cx, 'gcx, 'tcx> { // 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*/) => { + if let Some((base_place, projection)) = cursor.split_projection(self.tcx) { + match projection { + 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; + self.next = Some(&base_place); + return Some(cursor); + } + ProjectionElem::Downcast(..) | + ProjectionElem::Subslice { .. } | + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Index(_) => { + cursor = &base_place; + continue 'cursor; + } + ProjectionElem::Deref => { + // (handled below) + } } - ProjectionElem::Deref => { - // (handled below) + assert_eq!(*projection, 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(&base_place); + return Some(cursor); + } + PrefixSet::Supporting => { + // fall through! + } } - } - - 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 + 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 = base_place.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, + // 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); - } + self.next = Some(&base_place); + return Some(cursor); + } - ty::TyAdt(..) if ty.is_box() => { - self.next = Some(&proj.base); - return Some(cursor); - } + ty::TyAdt(..) if ty.is_box() => { + self.next = Some(&base_place); + return Some(cursor); + } - _ => panic!("unknown type fed to Projection Deref."), + _ => 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..e9eeee6b28325 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,7 +55,7 @@ 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 { + if let PlaceBase::Local(user_local) = path.place.base { self.mbcx.used_mut.insert(user_local); } } diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index d3dbb19ad4de9..51cb09df5dbd4 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -105,7 +105,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { if let Some((base_place, projection)) = place.split_projection(self.tcx()) { self.try_read_place(&base_place); - self.tr_read_place_projection(&base_place, projection); + self.tr_read_place_projection(&base_place, projection) } else { match place.base { // Might allow this in the future, right now there's no way to do this from Rust code anyway @@ -163,7 +163,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { fn try_read_place_projection( &self, base_place: &mir::Place<'tcx>, - proj: &mir::PlaceProjection<'tcx>, + proj: &mir::PlaceElem<'tcx>, ) -> EvalResult<'tcx, Option> { use rustc::mir::ProjectionElem::*; let base = match self.try_read_place(base_place)? { diff --git a/src/librustc_mir/transform/add_validation.rs b/src/librustc_mir/transform/add_validation.rs index d7e4351884948..78c418b83141b 100644 --- a/src/librustc_mir/transform/add_validation.rs +++ b/src/librustc_mir/transform/add_validation.rs @@ -62,14 +62,14 @@ fn place_context<'a, 'tcx, D>( // This is already as restricted as it gets, no need to even recurse context } else { - let base_context = place_context(&place.base_place(&tcx), local_decls, tcx); + let base_context = place_context(&place.base_place(tcx), 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); (re, mutbl) } } - _ => place_context(&place.base_place(&tcx), local_decls, tcx), + _ => place_context(&place.base_place(tcx), local_decls, tcx), }, _ => match place.base { Local { .. } => (None, hir::MutMutable), @@ -236,7 +236,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); } From 6002649bf1c7a296a1de91e67fba08ffd719ebcc Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Fri, 10 Aug 2018 21:10:17 +0800 Subject: [PATCH 06/33] Clean up old Place in rustc_mir --- src/librustc/mir/mod.rs | 43 ++- src/librustc_mir/borrow_check/mod.rs | 2 +- src/librustc_mir/borrow_check/move_errors.rs | 4 +- src/librustc_mir/build/expr/as_operand.rs | 2 +- src/librustc_mir/build/expr/as_place.rs | 20 +- src/librustc_mir/build/expr/as_rvalue.rs | 134 +++++--- src/librustc_mir/build/expr/as_temp.rs | 4 +- src/librustc_mir/build/expr/into.rs | 6 +- src/librustc_mir/build/expr/stmt.rs | 4 +- src/librustc_mir/build/matches/mod.rs | 4 +- src/librustc_mir/build/matches/simplify.rs | 5 +- src/librustc_mir/build/matches/test.rs | 9 +- src/librustc_mir/build/matches/util.rs | 22 +- src/librustc_mir/build/misc.rs | 2 +- src/librustc_mir/build/mod.rs | 6 +- src/librustc_mir/build/scope.rs | 14 +- .../dataflow/drop_flag_effects.rs | 22 +- .../dataflow/impls/borrowed_locals.rs | 76 +++-- src/librustc_mir/dataflow/impls/borrows.rs | 30 +- src/librustc_mir/dataflow/impls/mod.rs | 10 +- .../dataflow/move_paths/builder.rs | 55 +-- src/librustc_mir/dataflow/move_paths/mod.rs | 31 +- src/librustc_mir/interpret/place.rs | 30 +- src/librustc_mir/interpret/terminator/mod.rs | 13 +- src/librustc_mir/shim.rs | 62 ++-- .../transform/add_moves_for_packed_drops.rs | 4 +- src/librustc_mir/transform/add_validation.rs | 21 +- src/librustc_mir/transform/check_unsafety.rs | 103 +++--- src/librustc_mir/transform/const_prop.rs | 79 ++--- src/librustc_mir/transform/copy_prop.rs | 63 +++- src/librustc_mir/transform/deaggregator.rs | 13 +- src/librustc_mir/transform/elaborate_drops.rs | 42 +-- src/librustc_mir/transform/generator.rs | 92 ++--- src/librustc_mir/transform/inline.rs | 63 ++-- src/librustc_mir/transform/instcombine.rs | 22 +- src/librustc_mir/transform/lower_128bit.rs | 4 +- src/librustc_mir/transform/promote_consts.rs | 46 ++- src/librustc_mir/transform/qualify_consts.rs | 320 ++++++++++-------- .../transform/remove_noop_landing_pads.rs | 8 +- src/librustc_mir/transform/rustc_peek.rs | 22 +- .../transform/uniform_array_move_out.rs | 190 +++++++---- src/librustc_mir/util/alignment.rs | 10 +- src/librustc_mir/util/elaborate_drops.rs | 64 ++-- src/librustc_mir/util/graphviz.rs | 6 +- src/librustc_mir/util/pretty.rs | 2 +- 45 files changed, 1029 insertions(+), 755 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index f7fc694188962..9392c1085d459 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1807,7 +1807,8 @@ impl<'a, 'tcx> Place<'tcx> { pub fn downcast( self, tcx: TyCtxt<'a, 'tcx, 'tcx>, - adt_def: &'tcx AdtDef, variant_index: usize, + adt_def: &'tcx AdtDef, + variant_index: usize, ) -> Self { self.elem(tcx, ProjectionElem::Downcast(adt_def, variant_index)) } @@ -1816,6 +1817,29 @@ impl<'a, 'tcx> Place<'tcx> { self.elem(tcx, ProjectionElem::Index(index)) } + pub fn constant_index( + self, + tcx: TyCtxt<'a, 'tcx, '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<'a, 'tcx, 'tcx>, + from: u32, + to: u32, + ) -> Self { + self.elem(tcx, ProjectionElem::Subslice { + from, to, + }) + } + pub fn elem( self, tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -1833,6 +1857,23 @@ impl<'a, 'tcx> Place<'tcx> { elems: Slice::empty(), } } + + pub fn static_(static_: Static<'tcx>) -> Self { + Place { + base: PlaceBase::Static(box static_), + elems: Slice::empty(), + } + } + + pub fn promoted( + promoted: Promoted, + ty: Ty<'tcx>, + ) -> Self { + Place { + base: PlaceBase::Promoted(box (promoted, ty)), + elems: Slice::empty(), + } + } } impl<'tcx> Debug for PlaceBase<'tcx> { diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 21ba1194aa07a..7c88e4600ecbd 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -1792,7 +1792,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // to a direct owner of `place` (which means there is nothing // that borrowck tracks for its analysis). - match self.move_data.rev_lookup.find(place) { + match self.move_data.rev_lookup.find(self.tcx, place) { LookupResult::Parent(_) => None, LookupResult::Exact(mpi) => Some(mpi), } diff --git a/src/librustc_mir/borrow_check/move_errors.rs b/src/librustc_mir/borrow_check/move_errors.rs index 9d410aecfbcfe..2310c98b238ed 100644 --- a/src/librustc_mir/borrow_check/move_errors.rs +++ b/src/librustc_mir/borrow_check/move_errors.rs @@ -154,7 +154,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { let from_simple_let = match_place.is_none(); let match_place = match_place.as_ref().unwrap_or(move_from); - match self.move_data.rev_lookup.find(match_place) { + match self.move_data.rev_lookup.find(self.tcx, match_place) { // Error with the match place LookupResult::Parent(_) => { for ge in &mut *grouped_errors { @@ -185,7 +185,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { } // Error with the pattern LookupResult::Exact(_) => { - let mpi = match self.move_data.rev_lookup.find(move_from) { + let mpi = match self.move_data.rev_lookup.find(self.tcx, move_from) { LookupResult::Parent(Some(mpi)) => mpi, // move_from should be a projection from match_place. _ => unreachable!("Probably not unreachable..."), 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..62c6522a521af 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -103,17 +103,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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)))) + unpack!(block = this.into(&Place::local(result).deref(this.hir.tcx()), block, value)); + block.and(Rvalue::Use(Operand::Move(Place::local(result)))) } ExprKind::Cast { source } => { let source = this.hir.mirror(source); @@ -277,7 +277,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { .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)) + None => this.consume_by_copy_or_move(base.clone().field( + this.hir.tcx(), + n, + ty, + )) }) .collect() } else { @@ -350,8 +354,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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); @@ -423,6 +427,7 @@ 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)); @@ -433,56 +438,73 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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 + let mutability = if let Some((base_place, projection)) = arg_place.split_projection(tcx) { + match projection { + ProjectionElem::Deref => { + 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 if let Some((base_place, projection)) = base_place.split_projection(tcx) { + if let ProjectionElem::Field(upvar_index, _) = projection { + // Not projected from the implicit `self` in a closure. + debug_assert!( + if let Some((base_place, projection)) = base_place.split_projection(tcx) { + PlaceBase::Local(Local::new(1)) == base_place.base + } else { + match base_place.base { + PlaceBase::Local(local) => 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 + } } else { - false - }, - "Unexpected capture place", - ); - this.local_decls[local].mutability + bug!("Unexpected capture place"); + } + }, + ProjectionElem::Field(upvar_index, _) => { + // Not projected from the implicit `self` in a closure. + debug_assert!( + if let Some((base_place, projection)) = base_place.split_projection(tcx) { + PlaceBase::Local(Local::new(1)) == base_place.base + } else { + match base_place.base { + PlaceBase::Local(local) => 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 + } } - 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 + } else { + if let PlaceBase::Local(local) = arg_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 }, @@ -491,7 +513,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.cfg.push_assign( block, source_info, - &Place::Local(temp), + &Place::local(temp), Rvalue::Ref(region, borrow_kind, arg_place), ); @@ -500,11 +522,11 @@ 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..58a43a37125e6 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -251,9 +251,9 @@ 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) + this.into(&ptr_temp.deref(this.hir.tcx()), block, val) } else { let args: Vec<_> = args.into_iter() @@ -307,7 +307,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..80a7a21d7924c 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -669,13 +669,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // 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().elem(self.hir.tcx(), elem); // `(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..8b1143cecf240 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() @@ -47,16 +50,19 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { min_length, from_end: false, }; - let place = place.clone().elem(elem); + let place = place.clone().elem(self.hir.tcx(), elem); 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 - }); + let subslice = place.clone().elem( + self.hir.tcx(), + ProjectionElem::Subslice { + from: prefix.len() as u32, + to: suffix.len() as u32 + } + ); match_pairs.push(MatchPair::new(subslice, subslice_pat)); } @@ -70,7 +76,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { min_length, from_end: true, }; - let place = place.clone().elem(elem); + let place = place.clone().elem(self.hir.tcx(), elem); 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..87410825cbf34 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -660,16 +660,18 @@ 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.base { + PlaceBase::Local(index) => if index.index() <= self.arg_count { 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 ), } } @@ -936,8 +938,8 @@ 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 => { + match drop_data.location.base { + PlaceBase::Local(index) if index.index() > arg_count => { 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..67b6dd27ad785 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; @@ -165,7 +165,7 @@ pub(crate) fn on_all_drop_children_bits<'a, 'gcx, 'tcx, F>( } pub(crate) fn drop_flag_effects_for_function_entry<'a, 'gcx, 'tcx, F>( - tcx: TyCtxt<'a, 'gcx, 'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, ctxt: &MoveDataParamEnv<'gcx, 'tcx>, mut callback: F) @@ -173,8 +173,8 @@ 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 lookup_result = move_data.rev_lookup.find(&place); + let place = mir::Place::local(arg); + let lookup_result = move_data.rev_lookup.find(tcx, &place); on_lookup_result_bits(tcx, mir, move_data, lookup_result, |mpi| callback(mpi, DropFlagState::Present)); @@ -214,7 +214,7 @@ pub(crate) fn drop_flag_effects_for_location<'a, 'gcx, 'tcx, F>( } pub(crate) fn for_location_inits<'a, 'gcx, 'tcx, F>( - tcx: TyCtxt<'a, 'gcx, 'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, move_data: &MoveData<'tcx>, loc: Location, diff --git a/src/librustc_mir/dataflow/impls/borrowed_locals.rs b/src/librustc_mir/dataflow/impls/borrowed_locals.rs index c8c41c13b0fbb..a838bcc131f8a 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 @@ -22,23 +22,31 @@ use dataflow::BitDenotation; /// immovable generators. #[derive(Copy, Clone)] pub struct HaveBeenBorrowedLocals<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>, } impl<'a, 'tcx: 'a> HaveBeenBorrowedLocals<'a, 'tcx> { - pub fn new(mir: &'a Mir<'tcx>) - -> Self { - HaveBeenBorrowedLocals { mir: mir } + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { + HaveBeenBorrowedLocals { tcx, mir } } pub fn mir(&self) -> &Mir<'tcx> { self.mir } + + pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { + self.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,12 +55,11 @@ 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 { + tcx: self.tcx(), sets, }.visit_statement(loc.block, stmt, loc); @@ -63,19 +70,20 @@ 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 { + tcx: self.tcx(), 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 } } @@ -94,30 +102,32 @@ impl<'a, 'tcx> InitialFlow for HaveBeenBorrowedLocals<'a, 'tcx> { } } -struct BorrowedLocalsVisitor<'b, 'c: 'b> { - sets: &'b mut BlockSets<'c, Local>, +struct BorrowedLocalsVisitor<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + sets: &'a mut BlockSets<'tcx, 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<'a, 'tcx: 'a> BorrowedLocalsVisitor<'a, 'tcx> { + fn find_local(&self, place: &Place<'tcx>) -> Option { + if let Some((base_place, projection)) = place.split_projection(self.tcx) { + if let ProjectionElem::Deref = projection { + None + } else { + self.find_local(&base_place) + } + } else { + match place.base { + PlaceBase::Local(l) => Some(l), + PlaceBase::Promoted(_) | PlaceBase::Static(..) => None, } } } } impl<'tcx, 'b, 'c> Visitor<'tcx> for BorrowedLocalsVisitor<'b, 'c> { - fn visit_rvalue(&mut self, - rvalue: &Rvalue<'tcx>, - location: Location) { - if let Rvalue::Ref(_, _, ref place) = *rvalue { - if let Some(local) = find_local(place) { + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + if let Rvalue::Ref(_, _, place) = *rvalue { + 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..3b8098e8638ce 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,10 @@ 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 { + 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); + self.kill_borrows_on_local(sets, &local); } // NOTE: if/when the Assign case is revised to inspect @@ -258,15 +258,17 @@ 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 let Some(_projection) = lhs.projection() { + // ... can assign into projections, + // e.g. `box (&mut _)`. Current + // conservative solution: force + // immediate activation here. + sets.gen(&index); + } else { + match lhs.base { + PlaceBase::Promoted(_) + | PlaceBase::Local(..) + | PlaceBase::Static(..) => {} // okay } } } @@ -283,10 +285,10 @@ 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); + 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..0bd71734d1409 100644 --- a/src/librustc_mir/dataflow/impls/mod.rs +++ b/src/librustc_mir/dataflow/impls/mod.rs @@ -374,7 +374,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MaybeInitializedPlaces<'a, 'gcx, 'tcx> { // when a call returns successfully, that means we need to set // the bits for that dest_place to 1 (initialized). on_lookup_result_bits(self.tcx, self.mir, self.move_data(), - self.move_data().rev_lookup.find(dest_place), + self.move_data().rev_lookup.find(self.tcx, dest_place), |mpi| { in_out.add(&mpi); }); } } @@ -429,7 +429,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> // when a call returns successfully, that means we need to set // the bits for that dest_place to 0 (initialized). on_lookup_result_bits(self.tcx, self.mir, self.move_data(), - self.move_data().rev_lookup.find(dest_place), + self.move_data().rev_lookup.find(self.tcx, dest_place), |mpi| { in_out.remove(&mpi); }); } } @@ -483,7 +483,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for DefinitelyInitializedPlaces<'a, 'gcx, 'tc // when a call returns successfully, that means we need to set // the bits for that dest_place to 1 (initialized). on_lookup_result_bits(self.tcx, self.mir, self.move_data(), - self.move_data().rev_lookup.find(dest_place), + self.move_data().rev_lookup.find(self.tcx, dest_place), |mpi| { in_out.add(&mpi); }); } } @@ -557,7 +557,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MovingOutStatements<'a, 'gcx, 'tcx> { on_lookup_result_bits(self.tcx, self.mir, move_data, - move_data.rev_lookup.find(dest_place), + move_data.rev_lookup.find(self.tcx, dest_place), |mpi| for moi in &path_map[mpi] { assert!(moi.index() < bits_per_block); in_out.remove(&moi); @@ -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(self.tcx, &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..a5c4eef7b2d97 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -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,14 +106,15 @@ 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) + if let Some((base_place, projection)) = place.split_projection(self.builder.tcx) { + self.move_path_for_projection(place, &base_place, projection) + } else { + match place.base { + PlaceBase::Local(local) => Ok(self.builder.data.rev_lookup.locals[local]), + PlaceBase::Promoted(..) | + PlaceBase::Static(..) => { + Err(MoveError::cannot_move_out_of(self.loc, Static)) + } } } } @@ -126,13 +127,13 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { fn move_path_for_projection(&mut self, place: &Place<'tcx>, - proj: &PlaceProjection<'tcx>) - -> Result> - { - let base = try!(self.move_path_for(&proj.base)); + base_place: &Place<'tcx>, + projection: &PlaceElem<'tcx> + ) -> Result> { + let base = try!(self.move_path_for(base_place)); let mir = self.builder.mir; let tcx = self.builder.tcx; - let place_ty = proj.base.ty(mir, tcx).to_ty(tcx); + let place_ty = base_place.ty(mir, tcx).to_ty(tcx); match place_ty.sty { ty::TyRef(..) | ty::TyRawPtr(..) => return Err(MoveError::cannot_move_out_of( @@ -150,12 +151,12 @@ 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: place_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, @@ -168,7 +169,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,10 +273,10 @@ 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()); - self.gather_init(place, InitKind::Shallow); + self.create_move_path(&place.clone().deref(self.builder.tcx)); + self.gather_init(self.builder.tcx, place, InitKind::Shallow); } else { - self.gather_init(place, InitKind::Deep); + self.gather_init(self.builder.tcx, place, InitKind::Deep); } self.gather_rvalue(rval); } @@ -285,7 +286,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { StatementKind::InlineAsm { ref outputs, ref inputs, ref asm } => { for (output, kind) in outputs.iter().zip(&asm.outputs) { if !kind.is_indirect { - self.gather_init(output, InitKind::Deep); + self.gather_init(self.builder.tcx, output, InitKind::Deep); } } for input in inputs { @@ -294,7 +295,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 +356,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, .. } => { @@ -376,7 +377,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { TerminatorKind::DropAndReplace { ref location, ref value, .. } => { self.create_move_path(location); self.gather_operand(value); - self.gather_init(location, InitKind::Deep); + self.gather_init(self.builder.tcx, location, InitKind::Deep); } TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => { self.gather_operand(func); @@ -385,7 +386,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { } if let Some((ref destination, _bb)) = *destination { self.create_move_path(destination); - self.gather_init(destination, InitKind::NonPanicPathOnly); + self.gather_init(self.builder.tcx, destination, InitKind::NonPanicPathOnly); } } } @@ -420,10 +421,10 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { self.builder.data.loc_map[self.loc].push(move_out); } - fn gather_init(&mut self, place: &Place<'tcx>, kind: InitKind) { + fn gather_init(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, place: &Place<'tcx>, kind: InitKind) { debug!("gather_init({:?}, {:?})", self.loc, place); - if let LookupResult::Exact(path) = self.builder.data.rev_lookup.find(place) { + if let LookupResult::Exact(path) = self.builder.data.rev_lookup.find(tcx, place) { let init = self.builder.data.inits.push(Init { span: self.builder.mir.source_info(self.loc).span, path, diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 64bfd36b7eeb6..78da5f1dc32e0 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -246,21 +246,26 @@ 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, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + place: &Place<'tcx> + ) -> LookupResult { + if let Some((base_place, projection)) = place.split_projection(tcx) { + match self.find(tcx, &base_place) { + LookupResult::Exact(base_path) => { + match self.projections.get(&(base_path, projection.lift())) { + Some(subpath) => LookupResult::Exact(*subpath), + None => LookupResult::Parent(Some(base_path)), } - inexact => inexact } + inexact => inexact, + } + } else { + match place.base { + PlaceBase::Local(local) => LookupResult::Exact(self.locals[local]), + PlaceBase::Promoted(_) | + PlaceBase::Static(..) => LookupResult::Parent(None), } } } diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 51cb09df5dbd4..944efe1aa7214 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -103,9 +103,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { ) -> EvalResult<'tcx, Option> { use rustc::mir::PlaceBase::*; - if let Some((base_place, projection)) = place.split_projection(self.tcx()) { + if let Some((base_place, projection)) = place.split_projection(self.tcx.tcx) { self.try_read_place(&base_place); - self.tr_read_place_projection(&base_place, projection) + self.try_read_place_projection(&base_place, projection) } else { match place.base { // Might allow this in the future, right now there's no way to do this from Rust code anyway @@ -173,7 +173,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { let base_ty = self.place_ty(base_place); let base_layout = self.layout_of(base_ty)?; match proj { - Field(field, _) => Ok(Some(self.read_field(base, None, field, base_layout)?.0)), + 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 { .. } | @@ -212,10 +212,10 @@ 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::PlaceBase::*; - let place = if let Some((base_place, projection)) = mir_place.split_projection(self.tcx()) { + let place = if let Some((base_place, projection)) = mir_place.split_projection(self.tcx.tcx) { let ty = self.place_ty(&base_place); let place = self.eval_place(&base_place)?; - self.eval_place_projection(place, ty, projection) + self.eval_place_projection(place, ty, projection)? } else { match mir_place.base { Local(mir::RETURN_PLACE) => self.frame().return_place, @@ -395,11 +395,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { 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 => { @@ -418,7 +418,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 })? @@ -437,12 +437,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)?; @@ -456,13 +456,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..a6e9ce0d87a09 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)?; } } @@ -302,7 +302,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { trace!("args: {:#?}", args); let local = arg_locals.nth(1).unwrap(); 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( + *self.tcx, mir::Field::new(i), valty.ty, ))?; @@ -337,7 +338,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 +353,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 +364,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 +378,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..389dfd2fea7d5 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,13 @@ 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])`; self.block(vec![], TerminatorKind::Drop { - location: dest.index(beg), + location: dest.index(self.tcx, beg), target: BasicBlock::new(8), unwind: None, }, true); @@ -600,10 +600,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 +620,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 +688,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 +710,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 +733,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 +755,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 +767,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 +777,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 +845,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 78c418b83141b..27f9421ffc413 100644 --- a/src/librustc_mir/transform/add_validation.rs +++ b/src/librustc_mir/transform/add_validation.rs @@ -30,13 +30,12 @@ fn place_context<'a, 'tcx, D>( ) -> (Option, hir::Mutability) where D: HasLocalDecls<'tcx> { - use rustc::mir::PlaceBase::*; - match place.elems.last() { - Some(proj) => match proj { + if let Some((base_place, projection)) = place.split_projection(tcx){ + match projection { 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); + let ty = base_place.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 { @@ -68,13 +67,15 @@ fn place_context<'a, 'tcx, D>( let mutbl = context.1.and(base_context.1); (re, mutbl) } - } - _ => place_context(&place.base_place(tcx), local_decls, tcx), - }, - _ => match place.base { + }, + _ => place_context(&base_place, local_decls, tcx), + } + } else { + use rustc::mir::PlaceBase::*; + match place.base { Local { .. } => (None, hir::MutMutable), Promoted(_) | Static(_) => (None, hir::MutImmutable), - }, + } } } @@ -333,7 +334,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..8f6b3f5b10dd2 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -164,41 +164,38 @@ 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 let Some((base_place, projection)) = place.split_projection(self.tcx) { + 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); - match base_ty.sty { - ty::TyRawPtr(..) => { - self.require_unsafe("dereference of raw pointer", - "raw pointers may be NULL, dangling or unaligned; they can violate \ + } + let base_ty = base_place.ty(self.mir, self.tcx).to_ty(self.tcx); + 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") - } - ty::TyAdt(adt, _) => { - if adt.is_union() { - if context == PlaceContext::Store || - context == PlaceContext::AsmOutput || - context == PlaceContext::Drop + } + 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, + let projection_ty = match projection { + 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, + if projection_ty.moves_by_default(self.tcx, self.param_env, self.source_info.span) { self.require_unsafe( "assignment to non-`Copy` union field", @@ -209,44 +206,46 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { // write to non-move union, safe } } else { - self.require_unsafe("access to union field", - "the field may not be properly initialized: using \ + self.require_unsafe("access to union field", + "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 - } - &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 \ + 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: + } 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..6d648a313f0e5 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,52 @@ 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 { + if let Some((base_place, projection)) = place.split_projection(self.tcx) { + match projection { ProjectionElem::Field(field, _) => { - trace!("field proj on {:?}", proj.base); - let (base, layout, span) = self.eval_place(&proj.base, source_info)?; + trace!("field proj on {:?}", base_place); + let (base, layout, span) = self.eval_place(&base_place, source_info)?; let valty = self.use_ecx(source_info, |this| { - this.ecx.read_field(base, None, field, layout) + 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) - }, - _ => None, + } + } else { + match place.base { + PlaceBase::Local(local) => self.places[local].clone(), + PlaceBase::Promoted(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, + } } } 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::Move(ref place) - | Operand::Copy(ref place) => self.eval_place(place, source_info), + Operand::Constant(c) => self.eval_constant(&c, source_info), + | Operand::Move(place) + | Operand::Copy(place) => self.eval_place(&place, source_info), } } @@ -551,7 +554,7 @@ 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 { + 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); @@ -580,12 +583,12 @@ 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) => { + Operand::Move(place) | Operand::Copy(place) => { let mut place = place; - while let Place::Projection(ref proj) = *place { - place = &proj.base; + while let Some((base_place, _)) = place.split_projection(self.tcx) { + place = &base_place; } - if let Place::Local(local) = *place { + if let PlaceBase::Local(local) = place.base { self.places[local] = None; } }, diff --git a/src/librustc_mir/transform/copy_prop.rs b/src/librustc_mir/transform/copy_prop.rs index fba60c7e8dc27..1d51445b47836 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,13 @@ 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 => { let maybe_action = match *operand { Operand::Copy(ref src_place) | Operand::Move(ref src_place) => { @@ -154,12 +160,32 @@ 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: _, + }, + Rvalue::Use( + Operand::Copy( + Place { + base: PlaceBase::Local(src_local), + elems: _, + } + ) + ), ) | StatementKind::Assign( - Place::Local(local), - Rvalue::Use(Operand::Move(Place::Local(src_local))), + Place { + base: PlaceBase::Local(local), + elems: _, + }, + Rvalue::Use( + Operand::Move( + Place { + base: PlaceBase::Local(src_local), + elems: _, + } + ) + ), ) if local == dest_local && dest_local == src_local => {} _ => { continue; @@ -183,10 +209,13 @@ 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 { + let src_local = if let PlaceBase::Local(local) = src_place.base { local } else { debug!(" Can't copy-propagate local: source is not a local"); @@ -340,8 +369,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..d25a0e21436a1 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -121,7 +121,7 @@ fn find_dead_unwinds<'a, 'tcx>( init_data.apply_location(tcx, mir, env, loc); } - let path = match env.move_data.rev_lookup.find(location) { + let path = match env.move_data.rev_lookup.find(tcx, location) { LookupResult::Exact(e) => e, LookupResult::Parent(..) => { debug!("find_dead_unwinds: has parent; skipping"); @@ -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 @@ -375,7 +375,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { statement_index: data.statements.len() }); - let path = self.move_data().rev_lookup.find(location); + let path = self.move_data().rev_lookup.find(self.tcx, location); debug!("collect_drop_flags: {:?}, place {:?} ({:?})", bb, location, path); @@ -414,7 +414,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { match terminator.kind { TerminatorKind::Drop { ref location, target, unwind } => { let init_data = self.initialization_data_at(loc); - match self.move_data().rev_lookup.find(location) { + match self.move_data().rev_lookup.find(self.tcx, location) { LookupResult::Exact(path) => { elaborate_drop( &mut Elaborator { @@ -503,7 +503,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { is_cleanup: false, }); - match self.move_data().rev_lookup.find(location) { + match self.move_data().rev_lookup.find(self.tcx, location) { LookupResult::Exact(path) => { debug!("elaborate_drop_and_replace({:?}) - tracked {:?}", terminator, path); let init_data = self.initialization_data_at(loc); @@ -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()); } } @@ -572,7 +572,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { assert!(!self.patch.is_patched(bb)); let loc = Location { block: tgt, statement_index: 0 }; - let path = self.move_data().rev_lookup.find(place); + let path = self.move_data().rev_lookup.find(self.tcx, place); on_lookup_result_bits( self.tcx, self.mir, self.move_data(), path, |child| self.set_drop_flag(loc, child, DropFlagState::Present) @@ -646,7 +646,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { assert!(!self.patch.is_patched(bb)); let loc = Location { block: bb, statement_index: data.statements.len() }; - let path = self.move_data().rev_lookup.find(place); + let path = self.move_data().rev_lookup.find(self.tcx, place); on_lookup_result_bits( self.tcx, self.mir, self.move_data(), path, |child| self.set_drop_flag(loc, child, DropFlagState::Present) diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index a3647edd155d3..6ec6d0be632c6 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> { + 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,8 @@ 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 = place.clone().deref(self.tcx); } else { self.super_place(place, context, location); } @@ -163,17 +162,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,7 +197,7 @@ 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); @@ -228,7 +223,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 +237,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 +263,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 +285,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>( @@ -333,30 +329,42 @@ impl<'tcx> Visitor<'tcx> for StorageIgnored { } } -struct BorrowedLocals(liveness::LiveVarSet); +struct BorrowedLocals<'a, 'tcx>{ + tcx: TyCtxt<'a, 'tcx, 'tcx>, + local_live: 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 { +impl<'a, 'tcx> BorrowedLocals<'a, 'tcx> { + fn mark_as_borrowed( + &mut self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + place: &Place<'tcx>, + ) { + if let Some((base_place, projection)) = place.split_projection(tcx) { + if let ProjectionElem::Deref = projection { // 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) + () + } else { + self.mark_as_borrowed(self.tcx, &base_place) + } + } else { + match place.base { + PlaceBase::Local(l) => { self.local_live.add(&l); } + _ => (), } } } } -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); +impl<'a, 'tcx> Visitor<'tcx> for BorrowedLocals<'a, 'tcx> { + fn visit_rvalue( + &mut self, + rvalue: &Rvalue<'tcx>, + location: Location, + ) { + if let Rvalue::Ref(_, _, place) = rvalue { + self.mark_as_borrowed(self.tcx, place); } self.super_rvalue(rvalue, location) @@ -388,7 +396,7 @@ fn locals_live_across_suspend_points<'a, 'tcx,>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // borrowed (even if they are still active). // This is only used for immovable generators. let borrowed_locals = if !movable { - let analysis = HaveBeenBorrowedLocals::new(mir); + let analysis = HaveBeenBorrowedLocals::new(tcx, mir); let result = do_dataflow(tcx, mir, node_id, &[], &dead_unwinds, analysis, |bd, p| DebugFormatted::new(&bd.mir().local_decls[p])); @@ -588,11 +596,11 @@ fn elaborate_generator_drops<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, &Terminator { source_info, kind: TerminatorKind::Drop { - location: Place::Local(local), + location: place, target, unwind } - } if local == gen => (target, unwind, source_info), + } if PlaceBase::Local(gen) == place.base => (target, unwind, source_info), _ => continue, }; let unwind = if let Some(unwind) = unwind { @@ -610,7 +618,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 +793,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, }; diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index 85115427edae9..6bb7973b5dc99 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -415,23 +415,28 @@ 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<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + place: &Place<'tcx>, + ) -> bool { + if let Some((base_place, projection)) = place.split_projection(tcx) { + match projection { + ProjectionElem::Deref | + ProjectionElem::Index(_) => true, + _ => dest_needs_borrow(tcx, &base_place) + } + } else { + if let PlaceBase::Static(_) = place.base { + // Static variables need a borrow because the callee + // might modify the same static. + true + } else { + false } - // Static variables need a borrow because the callee - // might modify the same static. - Place::Static(_) => true, - _ => false } } - let dest = if dest_needs_borrow(&destination.0) { + let dest = if dest_needs_borrow(self.tcx, &destination.0) { debug!("Creating temp for return destination"); let dest = Rvalue::Ref( self.tcx.types.re_erased, @@ -443,7 +448,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 +456,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 +542,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 +556,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 +581,12 @@ 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(place) = arg { + if let PlaceBase::Local(local) = place.base { + if caller_mir.local_kind(local) == LocalKind::Temp { + // Reuse the operand if it's a temporary already + return local; + } } } @@ -594,7 +601,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,12 +648,12 @@ 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) => { *local = l; return; }, - ref place => bug!("Return place is {:?}, not local", place) + place => bug!("Return place is {:?}, not local", place) } } let idx = local.index() - 1; @@ -662,12 +669,12 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { _ctxt: PlaceContext<'tcx>, _location: Location) { - match place { - Place::Local(RETURN_PLACE) => { + match place.base { + PlaceBase::Local(RETURN_PLACE) => { // Return pointer; update the place itself *place = self.destination.clone(); }, - Place::Promoted(ref mut promoted) => { + PlaceBase::Promoted(ref mut promoted) => { if let Some(p) = self.promoted_map.get(promoted.0).cloned() { promoted.0 = p; } diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs index 6e06beb30419d..b84f7c233d02a 100644 --- a/src/librustc_mir/transform/instcombine.rs +++ b/src/librustc_mir/transform/instcombine.rs @@ -40,22 +40,24 @@ impl MirPass for InstCombine { }; // Then carry out those optimizations. - MutVisitor::visit_mir(&mut InstCombineVisitor { optimizations }, mir); + MutVisitor::visit_mir(&mut InstCombineVisitor { tcx, optimizations }, mir); } } -pub struct InstCombineVisitor<'tcx> { +pub struct InstCombineVisitor<'a, 'tcx> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, optimizations: OptimizationList<'tcx>, } -impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> { +impl<'a, 'tcx> MutVisitor<'tcx> for InstCombineVisitor<'a, 'tcx> { fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { if self.optimizations.and_stars.remove(&location) { debug!("Replacing `&*`: {:?}", rvalue); let new_place = match *rvalue { - Rvalue::Ref(_, _, Place::Projection(ref mut projection)) => { + Rvalue::Ref(_, _, place) => { // Replace with dummy - mem::replace(&mut projection.base, Place::Local(Local::new(0))) + let base_place = place.base_place(self.tcx); + mem::replace(&mut base_place, Place::local(Local::new(0))) } _ => bug!("Detected `&*` but didn't find `&*`!"), }; @@ -90,10 +92,12 @@ 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(_, _, place) = *rvalue { + if let Some((base_place, projection)) = place.split_projection(self.tcx) { + if let ProjectionElem::Deref = projection { + if base_place.ty(self.mir, self.tcx).to_ty(self.tcx).is_region_ptr() { + self.optimizations.and_stars.insert(location); + } } } } 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..9d3c4c42fcd52 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 }; @@ -294,7 +294,7 @@ 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 { @@ -304,15 +304,17 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { StatementKind::Assign(_, Rvalue::Ref(_, _, ref mut 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); + fn interior_base<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + place: &'a mut Place<'tcx> + ) -> &'a mut Place<'tcx> { + if let Some((base_place, projection)) = place.split_projection(tcx) { + assert_ne!(*projection, ProjectionElem::Deref); + return interior_base(tcx, &mut base_place); } place } - let place = interior_base(place); + let place = interior_base(self.tcx, place); let ty = place.ty(local_decls, self.tcx).to_ty(self.tcx); let span = statement.source_info.span; @@ -372,10 +374,12 @@ 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(place, _) => { + if let PlaceBase::Local(local) = place.base { + if temps[local] == TempState::PromotedOut { + // Already promoted. + continue; + } } } _ => {} @@ -417,7 +421,12 @@ 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: _, + }, + _, ) | StatementKind::StorageLive(index) | StatementKind::StorageDead(index) => { !promoted(index) @@ -427,7 +436,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 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..3dc2d01112721 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -234,7 +234,7 @@ 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() { debug!("store to promotable temp {:?} ({:?})", index, qualif); @@ -244,36 +244,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((base_place, projection)) = dest.split_projection(self.tcx){ + 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() + && 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 +353,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 +395,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: _, + } + ) + ) => { promoted_temps.add(&index); } _ => {} @@ -447,105 +463,106 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { 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, - ), - ); - } + if let Some((base_place, 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 +574,12 @@ 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(place) = *operand { + if let PlaceBase::Local(local) = place.base { + self.local_qualif[local] = self.local_qualif[local].map(|q| + q - Qualif::NEEDS_DROP + ); + } } } Operand::Constant(ref constant) => { @@ -586,11 +605,11 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { // Recurse through operands and places. - if let Rvalue::Ref(region, kind, ref place) = *rvalue { + if let Rvalue::Ref(region, kind, 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 Some((base_place, 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 +617,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); @@ -623,7 +642,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { Rvalue::Discriminant(..) | Rvalue::Len(_) => {} - Rvalue::Ref(_, kind, ref place) => { + Rvalue::Ref(_, kind, place) => { let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx); // Default to forbidding the borrow and/or its promotion, @@ -697,13 +716,13 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { 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 { + while let Some((base_place, projection)) = place.split_projection(self.tcx) { + if *projection == ProjectionElem::Deref { break; } - place = &proj.base; + place = base_place; } - if let Place::Local(local) = *place { + if let PlaceBase::Local(local) = place.base { if self.mir.local_kind(local) == LocalKind::Temp { if let Some(qualif) = self.local_qualif[local] { // `forbidden_mut` is false, so we can safely ignore @@ -961,7 +980,7 @@ 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 { + let needs_drop = 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 { @@ -997,15 +1016,29 @@ 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 let (Mode::ConstFn, PlaceBase::Local(index)) = (self.mode, dest.base) { if self.mir.local_kind(index) == LocalKind::Var && self.const_fn_arg_vars.insert(index) && !self.tcx.sess.features_untracked().const_let { // 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 self.mir.local_kind(local) == LocalKind::Arg { return; } @@ -1176,7 +1209,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 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..8f2bfb26b5a63 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(_) + ) => { // 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..d7e80228e03de 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( + place @ Place { + base: PlaceBase::Local(_), + elems: _, + } + ) | + mir::Operand::Move( + place @ Place { + base: PlaceBase::Local(_), + elems: _, + } + ) => Some(place), _ => None, }; @@ -171,10 +181,10 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, "sanity_check should run before Deaggregator inserts SetDiscriminant"), }; - if place == peek_arg_place { + if *place == peek_arg_place { if let mir::Rvalue::Ref(_, mir::BorrowKind::Shared, ref peeking_at_place) = *rvalue { // Okay, our search is over. - match move_data.rev_lookup.find(peeking_at_place) { + match move_data.rev_lookup.find(tcx, peeking_at_place) { LookupResult::Exact(peek_mpi) => { let bit_state = sets.on_entry.contains(&peek_mpi); debug!("rustc_peek({:?} = &{:?}) bit_state: {}", @@ -198,7 +208,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } - let lhs_mpi = move_data.rev_lookup.find(place); + let lhs_mpi = move_data.rev_lookup.find(tcx, place); debug!("rustc_peek: computing effect on place: {:?} ({:?}) in stmt: {:?}", place, lhs_mpi, stmt); diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs index 7a8c35e7b96f5..3bb79c2ea7ac7 100644 --- a/src/librustc_mir/transform/uniform_array_move_out.rs +++ b/src/librustc_mir/transform/uniform_array_move_out.rs @@ -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 Some((base_place, 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,14 +208,16 @@ 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 { + 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 @@ -208,7 +232,7 @@ 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,11 +245,14 @@ 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>) { + fn check_and_patch<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, '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); if opt_size.is_some() && items.iter().all( @@ -249,29 +276,46 @@ 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<'a, 'tcx, 'tcx>, + local_use: &LocalUse, + mir: &'a Mir<'tcx> + ) -> Option<(u32, &'a 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)) + place1, + Rvalue::Use(Operand::Move(place2)), + ) = statement.kind { + if let PlaceBase::Local(_) = place1.base { + if let Some((base_place, projection)) = place2.split_projection(tcx) { + if let ProjectionElem::ConstantIndex { + offset, + min_length: _, + from_end: false, + } = projection { + return Some((*offset, &base_place)); + } + } + } } } } diff --git a/src/librustc_mir/util/alignment.rs b/src/librustc_mir/util/alignment.rs index d1410210bda96..e745f5ca9ae97 100644 --- a/src/librustc_mir/util/alignment.rs +++ b/src/librustc_mir/util/alignment.rs @@ -50,14 +50,12 @@ fn is_within_packed<'a, 'tcx, L>(tcx: TyCtxt<'a, 'tcx, 'tcx>, where L: HasLocalDecls<'tcx> { let mut place = place; - while let &Place::Projection(box Projection { - ref base, ref elem - }) = place { - match *elem { + while let Some((base_place, projection)) = place.split_projection(tcx) { + match projection { // encountered a Deref, which is ABI-aligned ProjectionElem::Deref => break, ProjectionElem::Field(..) => { - let ty = base.ty(local_decls, tcx).to_ty(tcx); + let ty = base_place.ty(local_decls, tcx).to_ty(tcx); match ty.sty { ty::TyAdt(def, _) if def.repr.packed() => { return true @@ -67,7 +65,7 @@ fn is_within_packed<'a, 'tcx, L>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } _ => {} } - place = base; + place = &base_place; } false diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index bbffeec631abd..dcd1ac5a62a3f 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) @@ -421,8 +421,9 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> self.path, variant_index); if let Some(variant_path) = subpath { let base_place = self.place.clone().elem( + self.tcx(), ProjectionElem::Downcast(adt, variant_index) - ); + ); let fields = self.move_paths_for_fields( &base_place, variant_path, @@ -495,7 +496,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 +530,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 +543,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 +587,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 +619,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(), @@ -630,7 +631,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let loop_block = self.elaborator.patch().new_block(loop_block); self.elaborator.patch().patch_terminator(drop_block, TerminatorKind::Drop { - location: ptr.clone().deref(), + location: ptr.clone().deref(self.tcx()), target: loop_block, unwind: unwind.into_option() }); @@ -652,11 +653,14 @@ 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.place.clone().elem( + self.tcx(), + ProjectionElem::ConstantIndex{ + offset: i, + min_length: size, + from_end: false + } + ), self.elaborator.array_subpath(self.path, i, size)) }).collect(); @@ -668,8 +672,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 +708,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 +733,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 +888,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())?; From b1dbe6f72f5a37661e425f8a1817c2f148640613 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Fri, 17 Aug 2018 18:24:06 +0800 Subject: [PATCH 07/33] Intern PlaceElem with iterator --- src/librustc/mir/mod.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 9392c1085d459..087a8ae9e287b 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1840,17 +1840,6 @@ impl<'a, 'tcx> Place<'tcx> { }) } - pub fn elem( - self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - elem: PlaceElem<'tcx>, - ) -> Self { - Place { - base: self.base, - elems: tcx.intern_place_elems(&[elem]), - } - } - pub fn local(local: Local) -> Self { Place { base: PlaceBase::Local(local), @@ -1874,6 +1863,19 @@ impl<'a, 'tcx> Place<'tcx> { elems: Slice::empty(), } } + + 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 PlaceBase<'tcx> { From c5a7e57f8e1c016b5b5f02f2139de623e47cbe00 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Fri, 17 Aug 2018 18:36:43 +0800 Subject: [PATCH 08/33] Remove Projection and impl stablehash for Place with macro --- src/librustc/ich/impls_mir.rs | 32 +------------------------------- src/librustc/mir/mod.rs | 34 ---------------------------------- 2 files changed, 1 insertion(+), 65 deletions(-) diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index e30cb0cef5f70..eb825012e97f0 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -309,37 +309,7 @@ impl<'a, 'gcx> HashStable> for mir::PlaceBase<'gcx> { } } -impl<'a, 'tcx> HashStable> -for mir::Place<'tcx> -{ - fn hash_stable( - &self, - hcx: &mut StableHashingContext<'a>, - hasher: &mut StableHasher, - ) { - self.base.hash_stable(hcx, hasher); - self.elems.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 087a8ae9e287b..897f22707f028 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1727,16 +1727,6 @@ 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, Copy, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub enum ProjectionElem<'tcx, V, T> { Deref, @@ -1775,10 +1765,6 @@ 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>>; @@ -2950,26 +2936,6 @@ impl<'tcx, V, T> TypeFoldable<'tcx> for ProjectionElem<'tcx, V, T> } } -impl<'tcx, B, V, T> TypeFoldable<'tcx> for Projection<'tcx, B, V, T> -where - B: TypeFoldable<'tcx>, - V: TypeFoldable<'tcx>, - T: TypeFoldable<'tcx>, -{ - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - - let base = self.base.fold_with(folder); - let elem = self.elem.fold_with(folder); - Projection { base, elem } - } - - fn super_visit_with>(&self, visitor: &mut Vs) -> bool { - - self.base.visit_with(visitor) || - self.elem.visit_with(visitor) - } -} - impl<'tcx> TypeFoldable<'tcx> for Field { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self { *self From e684ea3907b76ae83c203e525c1b755972457777 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Sun, 19 Aug 2018 18:09:19 +0800 Subject: [PATCH 09/33] Rewrite places_conflict --- .../borrow_check/places_conflict.rs | 767 ++++++++---------- 1 file changed, 350 insertions(+), 417 deletions(-) diff --git a/src/librustc_mir/borrow_check/places_conflict.rs b/src/librustc_mir/borrow_check/places_conflict.rs index a63d42e3404ea..961ab7751f341 100644 --- a/src/librustc_mir/borrow_check/places_conflict.rs +++ b/src/librustc_mir/borrow_check/places_conflict.rs @@ -12,9 +12,8 @@ use borrow_check::ArtificialField; use borrow_check::Overlap; use borrow_check::{Deep, Shallow, ShallowOrDeep}; use rustc::hir; -use rustc::mir::{Mir, Place, PlaceBase}; -use rustc::mir::ProjectionElem; -use rustc::ty::{self, TyCtxt}; +use rustc::mir::{Mir, Place, PlaceBase, PlaceElem, ProjectionElem}; +use rustc::ty::{self, Slice, Ty, TyCtxt}; use std::cmp::max; // FIXME(csmoe): rewrite place_conflict with slice @@ -31,122 +30,80 @@ pub(super) fn places_conflict<'gcx, 'tcx>( borrow_place, access_place, access ); - unroll_place(tcx, borrow_place, None, |borrow_components| { - unroll_place(tcx, access_place, None, |access_components| { - place_components_conflict(tcx, mir, borrow_components, access_components, access) - }) - }) -} + let place_elements_conflict = |tcx: TyCtxt<'_, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + borrow_place: &Place<'tcx>, + access_place: &Place<'tcx>| { + // Enumerate for later base_place generation + let mut borrow_elems = borrow_place.elems.iter().cloned().enumerate(); -fn place_components_conflict<'gcx, 'tcx>( - tcx: TyCtxt<'_, 'gcx, 'tcx>, - mir: &Mir<'tcx>, - mut borrow_components: PlaceComponentsIter<'_, 'tcx>, - mut access_components: PlaceComponentsIter<'_, '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); + let mut access_elems = access_place.elems.iter().cloned(); - if let Some(access_c) = access_components.next() { - debug!("places_conflict: access_c = {:?}", access_c); + loop { + if let Some((i, borrow_elem)) = borrow_elems.next() { + let base_place = Place { + base: borrow_place.base, + elems: if i > 0 { + tcx.mk_place_elems(borrow_place.elems.iter().cloned().take(i)) + } else { + Slice::empty() + }, + }; + let base_ty = base_place.ty(mir, tcx).to_ty(tcx); - // Borrow and access path both have more components. - // - // Examples: - // - // - borrow of `a.(...)`, access to `a.(...)` - // - borrow of `a.(...)`, access to `b.(...)` - // - // Here we only see the components we have checked so - // far (in our examples, just the first component). We - // 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) { - Overlap::Arbitrary => { - // We have encountered different fields of potentially - // the same union - the borrow now partially overlaps. - // - // There is no *easy* way of comparing the fields - // further on, because they might have different types - // (e.g. borrows of `u.a.0` and `u.b.y` where `.0` and - // `.y` come from different structs). - // - // We could try to do some things here - e.g. count - // dereferences - but that's probably not a good - // idea, at least for now, so just give up and - // report a conflict. This is unsafe code anyway so - // the user could always use raw pointers. - debug!("places_conflict: arbitrary -> conflict"); - return true; - } - Overlap::EqualOrDisjoint => { - // This is the recursive case - proceed to the next element. - } - Overlap::Disjoint => { - // We have proven the borrow disjoint - further - // projections will remain disjoint. - debug!("places_conflict: disjoint"); - return false; - } - } - } else { - // Borrow path is longer than the access path. Examples: - // - // - borrow of `a.b.c`, access to `a.b` - // - // Here, we know that the borrow can access a part of - // our place. This is a conflict if that is a part our - // access cares about. + if let Some(access_elem) = access_elems.next() { + debug!("places_conflict: access_elem = {:?}", access_elem); - if let Some((base_place, projection)) = borrow_c.split_projection(tcx) { - let base_ty = base_place.ty(mir, tcx).to_ty(tcx); + // Borrow and access path both have more components. + // + // Examples: + // + // - borrow of `a.(...)`, access to `a.(...)` + // - borrow of `a.(...)`, access to `b.(...)` + // + // Here we only see the components we have checked so + // far (in our examples, just the first component). We + // check whether the components being borrowed vs + // accessed are disjoint (as in the second example, + // but not the first). + match place_element_conflict(base_ty, (&borrow_elem, &access_elem)) { + Overlap::Arbitrary => { + // We have encountered different fields of potentially + // the same union - the borrow now partially overlaps. + // + // There is no *easy* way of comparing the fields + // further on, because they might have different types + // (e.g. borrows of `u.a.0` and `u.b.y` where `.0` and + // `.y` come from different structs). + // + // We could try to do some things here - e.g. count + // dereferences - but that's probably not a good + // idea, at least for now, so just give up and + // report a conflict. This is unsafe code anyway so + // the user could always use raw pointers. + debug!("places_conflict: arbitrary -> conflict"); + return true; + } + Overlap::EqualOrDisjoint => { + // This is the recursive case - proceed to the next element. + } + Overlap::Disjoint => { + // We have proven the borrow disjoint - further + // projections will remain disjoint. + debug!("places_conflict: disjoint"); + return false; + } + } + } else { + // Borrow path is longer than the access path. Examples: + // + // - borrow of `a.b.c`, access to `a.b` + // + // Here, we know that the borrow can access a part of + // our place. This is a conflict if that is a part our + // access cares about. - match (projection, &base_ty.sty, access) { + match (borrow_elem, &base_ty.sty, access) { (_, _, Shallow(Some(ArtificialField::Discriminant))) | (_, _, Shallow(Some(ArtificialField::ArrayLength))) => { // The discriminant and array length are like @@ -191,335 +148,311 @@ fn place_components_conflict<'gcx, 'tcx>( // of `*x.y` while accessing `x`. } } - } else { - bug!("place has no base?"); } + } else { + // Borrow path ran out but access path may not + // have. Examples: + // + // - borrow of `a.b`, access to `a.b.c` + // - borrow of `a.b`, access to `a.b` + // + // In the first example, where we didn't run out of + // access, the borrow can access all of our place, so we + // have a conflict. + // + // If the second example, where we did, then we still know + // that the borrow can access a *part* of our place that + // our access cares about, so we still have a conflict. + // + // FIXME: Differs from AST-borrowck; includes drive-by fix + // to #38899. Will probably need back-compat mode flag. + debug!("places_conflict: full borrow, CONFLICT"); + return true; } - } else { - // Borrow path ran out but access path may not - // have. Examples: - // - // - borrow of `a.b`, access to `a.b.c` - // - borrow of `a.b`, access to `a.b` - // - // In the first example, where we didn't run out of - // access, the borrow can access all of our place, so we - // have a conflict. - // - // If the second example, where we did, then we still know - // that the borrow can access a *part* of our place that - // our access cares about, so we still have a conflict. - // - // FIXME: Differs from AST-borrowck; includes drive-by fix - // to #38899. Will probably need back-compat mode flag. - debug!("places_conflict: full borrow, CONFLICT"); - return true; } - } -} + }; -/// 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 - } + 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), + // place.base overlap is obvious, no Abitrary. + _ => unreachable!(), } } -/// Recursively "unroll" a place into a `PlaceComponents` list, -/// invoking `op` with a `PlaceComponentsIter`. -fn unroll_place<'a, 'gcx: 'tcx, 'tcx, R>( +fn place_base_conflict<'a, 'gcx: 'tcx, 'tcx>( tcx: TyCtxt<'a, 'gcx, 'tcx>, - place: &Place<'tcx>, - next: Option<&PlaceComponents<'_, 'tcx>>, - op: impl FnOnce(PlaceComponentsIter<'_, 'tcx>) -> R, -) -> R { - if let Some((base_place, projection)) = place.split_projection(tcx) { - unroll_place( - tcx, - &base_place, - Some(&PlaceComponents { - component: place, - next, - }), - op, - ) - } else { - match place.base { - PlaceBase::Promoted(_) - | PlaceBase::Local(_) - | PlaceBase::Static(_) => { - let list = PlaceComponents { - component: place, - next, - }; - op(list.iter()) + 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<'a, 'gcx: 'tcx, 'tcx>( - tcx: TyCtxt<'a, 'gcx, 'tcx>, - mir: &Mir<'tcx>, - elem1: &Place<'tcx>, - elem2: &Place<'tcx>, +fn place_element_conflict<'tcx>( + base_ty: Ty<'tcx>, + (elem1, elem2): (&PlaceElem<'tcx>, &PlaceElem<'tcx>), ) -> Overlap { - match (elem1.split_projection(tcx), elem2.projection()) { - (Some((base_place1, proj1)), Some(proj2)) => { - match (proj1, proj2) { - (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 = base_place1.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 - } - } + 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 } - } - (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 { + _ => { + // Different fields of a struct (`a.x` vs. `a.y`). Disjoint! 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 - ), } } - (Some(_), _) | (_, Some(_)) => bug!( - "unexpected elements in place_element_conflict: {:?} and {:?}", + (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 ), - (None, None) => { - match (elem1.base, elem2.base) { - (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 - } - } - } } } From 091802de77ba13ae3ca9d11c0d0585d21746a716 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Sun, 19 Aug 2018 19:28:37 +0800 Subject: [PATCH 10/33] Rewrite recurseive place traversal to iteration --- src/librustc/mir/mod.rs | 12 +- src/librustc/mir/tcx.rs | 259 +++++---- src/librustc/mir/visit.rs | 15 +- src/librustc_mir/borrow_check/borrow_set.rs | 2 +- .../borrow_check/error_reporting.rs | 273 ++++------ src/librustc_mir/borrow_check/mod.rs | 494 ++++++++---------- src/librustc_mir/borrow_check/move_errors.rs | 18 +- .../borrow_check/mutability_errors.rs | 77 +-- .../borrow_check/nll/invalidation.rs | 13 +- .../borrow_check/nll/type_check/mod.rs | 114 ++-- src/librustc_mir/borrow_check/path_utils.rs | 30 +- src/librustc_mir/borrow_check/place_ext.rs | 66 +-- .../borrow_check/places_conflict.rs | 4 +- src/librustc_mir/borrow_check/prefixes.rs | 183 ------- src/librustc_mir/build/expr/as_rvalue.rs | 33 +- src/librustc_mir/build/expr/into.rs | 3 +- src/librustc_mir/build/matches/test.rs | 7 +- src/librustc_mir/build/matches/util.rs | 30 +- .../dataflow/drop_flag_effects.rs | 6 +- .../dataflow/impls/borrowed_locals.rs | 41 +- src/librustc_mir/dataflow/impls/mod.rs | 10 +- .../dataflow/move_paths/builder.rs | 37 +- src/librustc_mir/dataflow/move_paths/mod.rs | 36 +- src/librustc_mir/interpret/place.rs | 112 ++-- src/librustc_mir/interpret/terminator/mod.rs | 3 +- src/librustc_mir/shim.rs | 3 +- src/librustc_mir/transform/add_validation.rs | 15 +- src/librustc_mir/transform/check_unsafety.rs | 13 +- src/librustc_mir/transform/const_prop.rs | 82 +-- src/librustc_mir/transform/elaborate_drops.rs | 12 +- src/librustc_mir/transform/generator.rs | 35 +- src/librustc_mir/transform/inline.rs | 39 +- src/librustc_mir/transform/instcombine.rs | 27 +- src/librustc_mir/transform/promote_consts.rs | 33 +- src/librustc_mir/transform/qualify_consts.rs | 51 +- src/librustc_mir/transform/rustc_peek.rs | 10 +- .../transform/uniform_array_move_out.rs | 60 ++- src/librustc_mir/util/alignment.rs | 56 +- src/librustc_mir/util/elaborate_drops.rs | 19 +- 39 files changed, 1066 insertions(+), 1267 deletions(-) delete mode 100644 src/librustc_mir/borrow_check/prefixes.rs diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 897f22707f028..c5c573c262fc1 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1779,33 +1779,33 @@ impl<'a, 'tcx> Place<'tcx> { pub fn field( self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, + tcx: TyCtxt<'_, '_, 'tcx>, f: Field, ty: Ty<'tcx>, ) -> Self { self.elem(tcx, ProjectionElem::Field(f, ty)) } - pub fn deref(self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self { + pub fn deref(self, tcx: TyCtxt<'_, '_, 'tcx>) -> Self { self.elem(tcx, ProjectionElem::Deref) } pub fn downcast( self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, + 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<'a, 'tcx, 'tcx>, index: Local) -> Self { + pub fn index(self, tcx: TyCtxt<'_, '_, 'tcx>, index: Local) -> Self { self.elem(tcx, ProjectionElem::Index(index)) } pub fn constant_index( self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, + tcx: TyCtxt<'_, '_, 'tcx>, offset: u32, min_length: u32, from_end: bool, @@ -1817,7 +1817,7 @@ impl<'a, 'tcx> Place<'tcx> { pub fn subslice( self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, + tcx: TyCtxt<'_, '_, 'tcx>, from: u32, to: u32, ) -> Self { diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index 353d48930c81c..70777006a5c74 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,9 +25,11 @@ 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> { @@ -37,65 +39,73 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> { 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), + } + } + + fn base_ty(local_decls: &T, base: &PlaceBase<'tcx>) -> PlaceTy<'tcx> + where + T: HasLocalDecls<'tcx>, + { + match base { + PlaceBase::Local(index) => PlaceTy::Ty { + ty: local_decls.local_decls()[*index].ty, + }, + PlaceBase::Promoted(data) => PlaceTy::Ty { ty: data.1 }, + PlaceBase::Static(data) => PlaceTy::Ty { ty: data.ty }, } } pub fn projection_ty( self, tcx: TyCtxt<'a, 'gcx, 'tcx>, - elem: &PlaceElem<'tcx> + elem: &PlaceElem<'tcx>, ) -> PlaceTy<'tcx> { - match *elem { + 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 }, } } } @@ -109,7 +119,8 @@ EnumTypeFoldableImpl! { 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> + where + D: HasLocalDecls<'tcx>, { // the PlaceTy is the *final* type with all projection applied // if there is no projection, that just refers to `base`: @@ -121,16 +132,17 @@ impl<'tcx> Place<'tcx> { // Place: base.[] // ^^-- no projection // ^^^^-- PlaceTy - if let Some(projection) = self.elems.last() { - self.ty(local_decls, tcx).projection_ty(tcx, projection) - } else { - match self.base { - PlaceBase::Local(index) => - PlaceTy::Ty { ty: local_decls.local_decls()[index].ty }, - PlaceBase::Promoted(ref data) => PlaceTy::Ty { ty: data.1 }, - PlaceBase::Static(ref data) => PlaceTy::Ty { ty: data.ty }, + + let mut place_ty = PlaceTy::base_ty(local_decls, &self.base); + + // apply .projection_ty() to all elems but only returns the final one. + if !self.elems.is_empty() { + 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, @@ -146,17 +158,18 @@ impl<'tcx> Place<'tcx> { let mut place = self; let mut by_ref = false; - base_place = place.base_place(*tcx); + 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.base_place(*tcx).ty(mir, *tcx).to_ty(*tcx); + 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)) { + if base_ty.is_closure() + || base_ty.is_generator() && (!(by_ref && !mir.upvar_decls[field.index()].by_ref)) + { Some(*field) } else { None @@ -167,28 +180,30 @@ impl<'tcx> Place<'tcx> { } // for Place: - // Base.[a, b, c] - // ^-- projection - // => (Base.[a, b], c]) - // ^^^^^^^^^^ ^-- projection + // (Base.[a, b, c]) + // ^^^^^^^^^^ ^-- projection + // |-- base_place + // + // Base.[] + // ^^^^ ^^-- no projection(empty) // |-- base_place - pub fn split_projection<'cx, 'gcx>( + pub fn final_projection<'cx, 'gcx>( &self, tcx: TyCtxt<'cx, 'gcx, 'tcx>, - ) -> Option<(Place<'tcx>, &PlaceElem<'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)) => Some(( + Some((projection, place_elems)) => ( Place { base: self.clone().base, elems: tcx.intern_place_elems(place_elems), }, - projection, - )), - _ => None, + Some(projection), + ), + _ => (self.clone(), None) } } @@ -198,38 +213,64 @@ impl<'tcx> Place<'tcx> { // if no projection returns the place itself, // Base.[] => Base.[] // ^^-- no projection - pub fn base_place<'cx, 'gcx>( + pub fn projection_base<'cx, 'gcx>(&self, tcx: TyCtxt<'cx, 'gcx, 'tcx>) -> Place<'tcx> { + match self.final_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> { - match self.split_projection(tcx) { - Some((place, _)) => place, - _ => self.clone(), + // only works for place with projections + assert!(!self.elems.is_empty()); + + 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, @@ -245,8 +286,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) => { @@ -260,25 +300,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) } - } + }, } } @@ -288,32 +318,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 @@ -321,8 +359,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 } } @@ -362,7 +399,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 16a7b1b22bd96..9a8dcf1fc40e6 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -645,13 +645,14 @@ macro_rules! make_mir_visitor { self.visit_ty(& $($mutability)* promoted.1, TyContext::Location(location)); }, } - - for elem in elems.iter() { - self.visit_projection_elem( - &$($mutability)* (*elem).clone(), - context, - location - ) + if !elems.is_empty() { + for elem in elems.iter().cloned().rev() { + self.visit_projection_elem( + &$($mutability)* elem.clone(), + context, + location + ); + } } } diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/src/librustc_mir/borrow_check/borrow_set.rs index d97b5781f4120..1a795b846a367 100644 --- a/src/librustc_mir/borrow_check/borrow_set.rs +++ b/src/librustc_mir/borrow_check/borrow_set.rs @@ -177,7 +177,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { self.insert_as_pending_if_two_phase(location, &assigned_place, region, kind, idx); insert(&mut self.region_map, ®ion, idx); - if let Some(local) = borrowed_place.root_local(self.tcx) { + if let Some(local) = borrowed_place.root_local() { insert(&mut self.local_map, &local, idx); } } diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index 383ac011b8669..db166a4ca7fbe 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -13,7 +13,7 @@ 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, PlaceBase}; -use rustc::mir::{ProjectionElem, Rvalue, Statement, StatementKind}; +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,10 +384,7 @@ 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(); @@ -619,7 +616,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,131 +627,85 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { &self, place: &Place<'tcx>, buf: &mut String, - mut autoderef: bool, including_downcast: &IncludingDowncast, ) -> Result<(), ()> { - if let Some((base_place, projection)) = place.split_projection(self.tcx) { - 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.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( - &base_place, - buf, - autoderef, - &including_downcast, - )?; - } else if let PlaceBase::Local(local) = base_place.base { - if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) = - self.mir.local_decls[local].is_user_variable - { - self.append_place_to_string( - &base_place, - buf, - autoderef, - &including_downcast, - )?; - } else { - buf.push_str(&"*"); - self.append_place_to_string( - &base_place, - buf, - autoderef, - &including_downcast, - )?; - } - } else { - buf.push_str(&"*"); - self.append_place_to_string( - &base_place, - buf, - autoderef, - &including_downcast, - )?; - } - } - } - ProjectionElem::Downcast(..) => { - self.append_place_to_string( - &base_place, - 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(); + self.append_place_base_to_string(&place.base, buf)?; + if !place.elems.is_empty() { + 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"); + } + PlaceBase::Local(local) => { + self.append_local_to_string(*local, buf)?; + } + PlaceBase::Static(ref static_) => { + buf.push_str(&self.tcx.item_name(static_.def_id).to_string()); + } + } + 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 { - let field_name = self.describe_field(&base_place, *field); - self.append_place_to_string( - &base_place, - buf, - autoderef, - &including_downcast, - )?; - buf.push_str(&format!(".{}", field_name)); + buf.push_str(&format!("*{}", name)); } - } - ProjectionElem::Index(index) => { - autoderef = true; - - self.append_place_to_string( - &base_place, - 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( - &base_place, - buf, - autoderef, - &including_downcast, - )?; - buf.push_str(&"[..]"); + } else { + buf.push_str(&"*"); } } - } else { - match place.base { - PlaceBase::Promoted(_) => { - buf.push_str("promoted"); + ProjectionElem::Downcast(..) => { + if including_downcast.0 { + return Err(()); } - PlaceBase::Local(local) => { - self.append_local_to_string(local, buf)?; + } + 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)); } - PlaceBase::Static(ref static_) => { - buf.push_str(&self.tcx.item_name(static_.def_id).to_string()); + } + 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(()) @@ -775,33 +726,33 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // End-user visible description of the `field`nth field of `base` fn describe_field(&self, place: &Place<'tcx>, field: Field) -> String { - if let Some((base_place, projection)) = place.split_projection(self.tcx) { - match projection { - - ProjectionElem::Deref => self.describe_field(&base_place, field), - ProjectionElem::Downcast(def, variant_index) => format!( - "{}", - def.variants[*variant_index].fields[field.index()].ident - ), - ProjectionElem::Field(_, field_type) => { - self.describe_field_from_ty(&field_type, field) - } - ProjectionElem::Index(..) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => { - self.describe_field(&base_place, field).to_string() - } - } - } else { - 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), + 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), + }; + if !place.elems.is_empty() { + 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 + ).to_string(), + ProjectionElem::Field(_, field_type) => { + self.describe_field_from_ty(&field_type, field).to_string() + } + ProjectionElem::Index(..) + | ProjectionElem::Deref + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } => continue, + }; + string.push_str(proj_str.as_str()); } } + string } // End-user visible description of the `field_index`nth field of `ty` @@ -848,21 +799,21 @@ 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 { let place = place.clone(); - if let Some(projection) = place.projection() { - match projection { - ProjectionElem::Field(_, ty) => Some(*ty), - _ => None, - } - } else { - match place.base { - PlaceBase::Local(local) => { - let local = &self.mir.local_decls[local]; - Some(local.ty) - } - PlaceBase::Promoted(ref prom) => Some(prom.1), - PlaceBase::Static(ref st) => Some(st.ty), + let mut ty = match place.base { + PlaceBase::Local(local) => { + let local = &self.mir.local_decls[local]; + Some(local.ty) } + 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 } } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 7c88e4600ecbd..94a696ac7f3b7 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -49,7 +49,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 +63,6 @@ mod mutability_errors; mod path_utils; crate mod place_ext; mod places_conflict; -mod prefixes; mod used_muts; pub(crate) mod nll; @@ -1398,7 +1396,7 @@ 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) | Operand::Copy(place) => { + Operand::Move(ref place) | Operand::Copy(ref place) => { if let Some(field) = place.is_upvar_field_projection( self.mir, &self.tcx @@ -1484,19 +1482,18 @@ 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) = if let Some(projection) = root_place.projection() { - bug!("root of {:?} is a projection ({:?})?", place, root_place) + let (might_be_alive, will_be_dropped) = if !place.elems.is_empty() { + bug!("root of {:?} is a projection ({:?})?", place, place) } else { - match root_place.base { + match place.base { PlaceBase::Promoted(_) => (true, false), - PlaceBase::Static(statik) => { + PlaceBase::Static(ref statik) => { // Thread-locals might be dropped after the function exits, but // "true" statics will never be. let is_thread_local = self @@ -1527,7 +1524,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. @@ -1546,7 +1545,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { fn check_for_local_borrow(&mut self, borrow: &BorrowData<'tcx>, yield_span: Span) { debug!("check_for_local_borrow({:?})", borrow); - if borrow_of_local_data(self.tcx, &borrow.borrowed_place) { + if borrow_of_local_data(&borrow.borrowed_place) { let err = self.tcx .cannot_borrow_across_generator_yield( self.retrieve_borrow_spans(borrow).var_or_use(), @@ -1676,7 +1675,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( @@ -1741,7 +1740,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, @@ -1769,21 +1768,19 @@ 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) { - return Ok(mpi); + let mut prefix = place.clone(); + if !prefix.elems.is_empty() { + 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; } - if let Some(_) = last_prefix.projection() { - panic!("PrefixSet::All meant don't stop for Projection") - } else { - match last_prefix.base { - PlaceBase::Local(_) => panic!("should have move path for every Local"), - PlaceBase::Promoted(_) | - PlaceBase::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), } } @@ -1792,7 +1789,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // to a direct owner of `place` (which means there is nothing // that borrowck tracks for its analysis). - match self.move_data.rev_lookup.find(self.tcx, place) { + match self.move_data.rev_lookup.find(place) { LookupResult::Parent(_) => None, LookupResult::Exact(mpi) => Some(mpi), } @@ -1805,70 +1802,57 @@ 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 { - if let Some((base_place, projection)) = place.split_projection(self.tcx) { - match projection { - 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 - let tcx = self.tcx; - 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; - } - _ => {} - } - } - } - - place = &base_place; - continue; - } else { - match place.base { - PlaceBase::Promoted(_) - | PlaceBase::Local(_) - | PlaceBase::Static(_) => { - // assigning to `x` does not require `x` be initialized. - break; - }, + if !place.elems.is_empty() { + 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; + } + _ => {} + } + } } } } @@ -1999,54 +1983,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 { - base: PlaceBase::Local(local), - elems: _, - }, - is_local_mutation_allowed, - } => { - if is_local_mutation_allowed != LocalMutationIsAllowed::Yes { + let RootPlace { + ref place, + is_local_mutation_allowed, + } = root_place; + + if !place.elems.is_empty() { + 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, - 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 { - base: PlaceBase::Promoted(..), - elems: _, - }, - is_local_mutation_allowed: _, - } => {} - RootPlace { - place: Place { - base: PlaceBase::Static(..), - elems: _, - }, - is_local_mutation_allowed: _, - } => {} } } @@ -2055,124 +2016,15 @@ 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>> { - if let Some((base_place, projection)) = place.split_projection(self.tcx) { - match projection { - ProjectionElem::Deref => { - let base_ty = base_place.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, - ) { - Some(field) - if { - self.mir.upvar_decls[field.index()].by_ref - } => - { - is_local_mutation_allowed - } - _ => LocalMutationIsAllowed::Yes, - }; - - self.is_mutable(&base_place, 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, - }); - } - } - } - // `Box` owns its content, so mutable if its location is mutable - _ if base_ty.is_box() => self.is_mutable(&base_place, 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(&base_place, is_local_mutation_allowed)?; - Ok(RootPlace { - place, - is_local_mutation_allowed, - }) - } - } - } else { - self.is_mutable(&base_place, is_local_mutation_allowed) - } - } - } - } else { - match place.base { + 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]; + let local = &self.mir.local_decls[*local]; match local.mutability { Mutability::Not => match is_local_mutation_allowed { LocalMutationIsAllowed::Yes => Ok(RootPlace { @@ -2191,13 +2043,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { }), } } - // 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 + // 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(ref static_) => { + PlaceBase::Static(static_) => { if self.tcx.is_static(static_.def_id) != Some(hir::Mutability::MutMutable) { Err(place) } else { @@ -2208,7 +2061,127 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } } } + }; + + let mut result = base_is_mutable( + &place.base, + is_local_mutation_allowed + ); + + if !place.elems.is_empty() { + let mut base_place = place.clone(); + for (i, elem) in place.elems.iter().cloned().enumerate().rev() { + 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_place = base_place.elem_base(self.tcx, i); + let base_ty = base_place.ty(self.mir, self.tcx).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 + } => + { + is_local_mutation_allowed + } + _ => LocalMutationIsAllowed::Yes, + }; + + continue; + } + } + } + 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, + }); + } + } + } + // `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 } } @@ -2241,29 +2214,26 @@ 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 { - if let Some((base_place, projection)) = cursor.split_projection(self.tcx) { - if *projection == ProjectionElem::Deref - && place.ty(self.mir, self.tcx).to_ty(self.tcx).is_box() - { - deepest = &base_place; - } - cursor = &base_place; - } else { - match cursor.base { - PlaceBase::Promoted(_) - | PlaceBase::Local(..) - | PlaceBase::Static(..) => return deepest, + let mut deepest = place.clone(); + + if !place.elems.is_empty() { + for (i, elem) in place.elems.iter().cloned().enumerate().rev() { + deepest = match elem { + ProjectionElem::Deref + if place.ty(self.mir, self.tcx).to_ty(self.tcx).is_box() => { + place.elem_base(self.tcx, i) + }, + _ => continue, } } } + + deepest } } diff --git a/src/librustc_mir/borrow_check/move_errors.rs b/src/librustc_mir/borrow_check/move_errors.rs index 2310c98b238ed..6068bc5349f0e 100644 --- a/src/librustc_mir/borrow_check/move_errors.rs +++ b/src/librustc_mir/borrow_check/move_errors.rs @@ -154,7 +154,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { let from_simple_let = match_place.is_none(); let match_place = match_place.as_ref().unwrap_or(move_from); - match self.move_data.rev_lookup.find(self.tcx, match_place) { + match self.move_data.rev_lookup.find(match_place) { // Error with the match place LookupResult::Parent(_) => { for ge in &mut *grouped_errors { @@ -185,7 +185,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { } // Error with the pattern LookupResult::Exact(_) => { - let mpi = match self.move_data.rev_lookup.find(self.tcx, move_from) { + let mpi = match self.move_data.rev_lookup.find(move_from) { LookupResult::Parent(Some(mpi)) => mpi, // move_from should be a projection from match_place. _ => unreachable!("Probably not unreachable..."), @@ -240,14 +240,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() - && { - if let Some((base_place, _)) = place.split_projection(self.tcx) { - PlaceBase::Local(Local::new(1)) == base_place.base - } else { - false - } - } => - { + && 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(); @@ -301,7 +295,7 @@ 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) { - if let Some((base_place, proj)) = move_from.split_projection(self.tcx) { + if let (base_place, Some(proj)) = move_from.final_projection(self.tcx) { if self.suitable_to_remove_deref(&base_place, proj, &snippet) { err.span_suggestion( span, @@ -387,7 +381,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { }; *proj == ProjectionElem::Deref && snippet.starts_with('*') - && if let Some(projection) = place.projection() { + && if let Some(projection) = place.elems.last() { if let ProjectionElem::Field(_, ty) = projection { is_shared_ref(ty) } else { diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs index bebb2ea9b76d8..ce715283189d6 100644 --- a/src/librustc_mir/borrow_check/mutability_errors.rs +++ b/src/librustc_mir/borrow_check/mutability_errors.rs @@ -41,10 +41,11 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { let reason; let access_place_desc = self.describe_place(access_place); - if let Some((base_place, projection)) = the_place_err.split_projection(self.tcx) { + if let (base_place, Some(projection)) = the_place_err.final_projection(self.tcx) { match projection { ProjectionElem::Deref => { - if base_place.base == PlaceBase::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty() { + 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( @@ -81,7 +82,8 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { item_msg = format!("`{}`", desc); reason = match error_access { AccessKind::Move | - AccessKind::Mutate => format!(" which is behind a {}", pointer_type), + AccessKind::Mutate => + format!(" which is behind a {}", pointer_type), AccessKind::MutableBorrow => { format!(", as it is behind a {}", pointer_type) } @@ -127,7 +129,10 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { } PlaceBase::Static(box Static { def_id, ty: _ }) => { if let PlaceBase::Static(_) = access_place.base { - item_msg = format!("immutable static item `{}`", access_place_desc.unwrap()); + item_msg = format!( + "immutable static item `{}`", + access_place_desc.unwrap() + ); reason = "".to_string(); } else { item_msg = format!("`{}`", access_place_desc.unwrap()); @@ -183,7 +188,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { } }; - if let Some((base_place, projection)) = the_place_err.split_projection(self.tcx) { + if let (base_place, Some(projection)) = the_place_err.final_projection(self.tcx) { match projection { ProjectionElem::Deref => { if let PlaceBase::Local(local) = base_place.base { @@ -191,7 +196,8 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { 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", + "variables bound in patterns are \ + immutable until the end of the pattern guard", ); }, Some(_) => { @@ -201,16 +207,19 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { // 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() { + 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( + ClearCrossCrate::Set( + mir::BindingForm::Var( + mir::VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(_), + opt_ty_info, + .. + })) => Some(suggest_ampmut( self.tcx, self.mir, local, @@ -218,18 +227,23 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { *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::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::Set( + mir::BindingForm::RefForGuard + ) => unreachable!(), ClearCrossCrate::Clear => bug!("saw cleared local state"), }; - let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() { + let (pointer_sigil, pointer_desc) = + if local_decl.ty.is_region_ptr() { ("&", "reference") } else { ("*const", "pointer") @@ -238,7 +252,10 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { 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), + &format!( + "consider changing this to be a mutable {}", + pointer_desc + ), suggested_code, ); } @@ -343,25 +360,25 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { // Does this place refer to what the user sees as an upvar fn is_upvar(&self, place: &Place<'tcx>) -> bool { - if let Some((base_place, projection)) = place.split_projection(self.tcx) { + if let (base_place, Some(projection)) = place.final_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 Some((base_place, projection)) = base_place.split_projection(self.tcx) { - if let ProjectionElem::Field(upvar_index, _) = projection { - 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 { - unreachable!{} - } + if let ( + ref base_place, + Some(ProjectionElem::Field(upvar_index, _)), + ) = base_place.final_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 { - unreachable!{} + false } } - _ => unreachable!{}, + _ => false, } } else { false diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs index 9227582f30090..7976fea861847 100644 --- a/src/librustc_mir/borrow_check/nll/invalidation.rs +++ b/src/librustc_mir/borrow_check/nll/invalidation.rs @@ -251,7 +251,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tc let borrow_set = self.borrow_set.clone(); let resume = self.location_table.start_index(resume.start_location()); for i in borrow_set.borrows.indices() { - if borrow_of_local_data(self.infcx.tcx, &borrow_set.borrows[i].borrowed_place) { + if borrow_of_local_data(&borrow_set.borrows[i].borrowed_place) { self.all_facts.invalidates.push((resume, i)); } } @@ -261,7 +261,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tc let borrow_set = self.borrow_set.clone(); let start = self.location_table.start_index(location); for i in borrow_set.borrows.indices() { - if borrow_of_local_data(self.infcx.tcx, &borrow_set.borrows[i].borrowed_place) { + if borrow_of_local_data(&borrow_set.borrows[i].borrowed_place) { self.all_facts.invalidates.push((start, i)); } } @@ -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(self.infcx.tcx, 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,15 @@ 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.base == PlaceBase::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty() + if drop_place.base == PlaceBase::Local(Local::new(1)) + && !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.base == PlaceBase::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty() + if drop_place.base == PlaceBase::Local(Local::new(1)) + && !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/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index f0ea52f4ff17d..23ac369e00bd1 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -365,52 +365,34 @@ 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 = if let Some((base_place, projection)) = place.split_projection(self.tcx()) { - let base_context = if context.is_mutating_use() { - PlaceContext::Projection(Mutability::Mut) - } else { - PlaceContext::Projection(Mutability::Not) - }; - let base_ty = self.sanitize_place(&base_place, 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, - }; - } - } - self.sanitize_projection(base_ty, &projection, place, location) - } else { - match place.base { - PlaceBase::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 }, - 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) - // that they ought not to do. We have to figure out - // how best to handle that -- probably we want treat - // promoted MIR much like closures, renumbering all - // their free regions and propagating constraints - // upwards. We have the same acyclic guarantees, so - // that should be possible. But for now, ignore them. - // - // let promoted_mir = &self.mir.promoted[index]; - // promoted_mir.return_ty() - PlaceTy::Ty { 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); - if let Err(terr) = self.cx.eq_types(ty, sty, location.boring()) { - span_mirbug!( + 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) + // that they ought not to do. We have to figure out + // how best to handle that -- probably we want treat + // promoted MIR much like closures, renumbering all + // their free regions and propagating constraints + // upwards. We have the same acyclic guarantees, so + // that should be possible. But for now, ignore them. + // + // let promoted_mir = &self.mir.promoted[index]; + // promoted_mir.return_ty() + PlaceTy::Ty { 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); + if let Err(terr) = self.cx.eq_types(ty, sty, location.boring()) { + span_mirbug!( self, place, "bad static type ({:?}: {:?}): {:?}", @@ -418,9 +400,9 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { sty, terr ); - } - PlaceTy::Ty { ty: sty } } + + PlaceTy::Ty { ty: sty } } }; if let PlaceContext::Copy = context { @@ -439,20 +421,43 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { // Copy, then it must. self.cx.prove_trait_ref(trait_ref, location.interesting()); } + + if !place.elems.is_empty() { + + for elem in place.elems.iter(){ + context = if context.is_mutating_use() { + PlaceContext::Projection(Mutability::Mut) + } else { + PlaceContext::Projection(Mutability::Not) + }; + let base_ty = place_ty; + if let PlaceTy::Ty { ty } = base_ty { + if ty.references_error() { + assert!(self.errors_reported); + place_ty = PlaceTy::Ty { + ty: self.tcx().types.err, + }; + break; + } + } + place_ty = self.sanitize_projection(base_ty, elem, place, location); + } + }; + 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 { @@ -1593,19 +1598,18 @@ 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 Some((base_place, projection)) = borrowed_place.split_projection(self.infcx.tcx) { + + for elem in borrowed_place.elems.iter() { debug!("add_reborrow_constraint - iteration {:?}", borrowed_place); - match projection { + match elem { ProjectionElem::Deref => { let tcx = self.infcx.tcx; - let base_ty = base_place.ty(self.mir, tcx).to_ty(tcx); + let base_ty = borrowed_place.ty(self.mir, tcx).to_ty(tcx); debug!("add_reborrow_constraint - base_ty = {:?}", base_ty); match base_ty.sty { @@ -1674,10 +1678,6 @@ 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_place; } } diff --git a/src/librustc_mir/borrow_check/path_utils.rs b/src/librustc_mir/borrow_check/path_utils.rs index 3b053b2aae00b..32879884ea2c6 100644 --- a/src/librustc_mir/borrow_check/path_utils.rs +++ b/src/librustc_mir/borrow_check/path_utils.rs @@ -138,27 +138,21 @@ 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<'a, 'tcx, 'gcx: 'tcx>( - tcx: TyCtxt<'a, 'gcx, 'tcx>, +pub(super) fn borrow_of_local_data<'tcx>( place: &Place<'tcx> ) -> bool { - if let Some((base_place, projection)) = place.split_projection(tcx) { - match projection {// Reborrow of already borrowed data is ignored - // Any errors will be caught on the initial borrow - ProjectionElem::Deref => false, + 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(tcx, &base_place), - } - } else { - match place.base { - PlaceBase::Promoted(_) - | PlaceBase::Static(..) => false, - PlaceBase::Local(..) => true, + if !place.elems.is_empty() { + 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 fa6bd09bc4678..e974e918056de 100644 --- a/src/librustc_mir/borrow_check/place_ext.rs +++ b/src/librustc_mir/borrow_check/place_ext.rs @@ -20,52 +20,44 @@ crate trait PlaceExt<'tcx> { /// If this is a place like `x.f.g`, returns the local /// `x`. Returns `None` if this is based in a static. - fn root_local(&self, tcx: TyCtxt<'_, '_, 'tcx>) -> Option; + fn root_local(&self) -> Option; } impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { fn is_unsafe_place(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool { - if let Some((base_place, projectioin)) = self.split_projection(tcx) { - match projectioin { - ProjectionElem::Field(..) - | ProjectionElem::Downcast(..) - | ProjectionElem::Subslice { .. } - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Index(_) => base_place.is_unsafe_place(tcx, mir), - ProjectionElem::Deref => { - let ty = base_place.ty(mir, tcx).to_ty(tcx); - match ty.sty { - ty::TyRawPtr(..) => true, - _ => base_place.is_unsafe_place(tcx, mir), - } - } + 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) } - } else { - match self.base { - PlaceBase::Promoted(_) | - PlaceBase::Local(_) => false, - PlaceBase::Static(static_) => { - tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable) - } + }; + if !self.elems.is_empty() { + for elem in self.elems.iter().cloned().rev() { + is_unsafe_place = match elem { + ProjectionElem::Field(..) + | ProjectionElem::Downcast(..) + | ProjectionElem::Subslice { .. } + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Index(_) => continue, + ProjectionElem::Deref => { + let ty = self.ty(mir, tcx).to_ty(tcx); + match ty.sty { + ty::TyRawPtr(..) => true, + _ => continue, + } + }}; } } + + is_unsafe_place } - fn root_local( - &self, - tcx: TyCtxt<'_, '_, 'tcx>, - ) -> Option { - let mut p = self; - loop { - if let Some((base_place, projection)) = p.split_projection(tcx) { - p = &base_place - } else { - match p.base { - PlaceBase::Promoted(_) | - PlaceBase::Static(_) => return None, - PlaceBase::Local(local) => return Some(local), - } - } + fn root_local(&self) -> Option { + 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 961ab7751f341..9a05e96cdb985 100644 --- a/src/librustc_mir/borrow_check/places_conflict.rs +++ b/src/librustc_mir/borrow_check/places_conflict.rs @@ -42,7 +42,7 @@ pub(super) fn places_conflict<'gcx, 'tcx>( loop { if let Some((i, borrow_elem)) = borrow_elems.next() { let base_place = Place { - base: borrow_place.base, + base: borrow_place.clone().base, elems: if i > 0 { tcx.mk_place_elems(borrow_place.elems.iter().cloned().take(i)) } else { @@ -85,7 +85,7 @@ pub(super) fn places_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 diff --git a/src/librustc_mir/borrow_check/prefixes.rs b/src/librustc_mir/borrow_check/prefixes.rs deleted file mode 100644 index 1fba00f41ce2d..0000000000000 --- a/src/librustc_mir/borrow_check/prefixes.rs +++ /dev/null @@ -1,183 +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, PlaceBase, ProjectionElem}; - -pub trait IsPrefixOf<'tcx> { - fn is_prefix_of(&self, tcx: TyCtxt<'_, '_, 'tcx>, other: &Place<'tcx>) -> bool; -} - -impl<'tcx> IsPrefixOf<'tcx> for Place<'tcx> { - fn is_prefix_of( - &self, - tcx: TyCtxt<'_, '_, 'tcx>, - other: &Place<'tcx> - ) -> bool { - let mut cursor = other; - loop { - if self == cursor { - return true; - } - - if let Some((base_place, _projection)) = cursor.split_projection(tcx) { - cursor = &base_place; - } else { - match cursor.base { - PlaceBase::Promoted(_) - | PlaceBase::Local(_) - | PlaceBase::Static(_) => return false, - } - } - } - } -} - - -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 { - if let Some((base_place, projection)) = cursor.split_projection(self.tcx) { - match projection { - ProjectionElem::Field(_ /*field*/, _ /*ty*/) => { - // FIXME: add union handling - self.next = Some(&base_place); - return Some(cursor); - } - ProjectionElem::Downcast(..) | - ProjectionElem::Subslice { .. } | - ProjectionElem::ConstantIndex { .. } | - ProjectionElem::Index(_) => { - cursor = &base_place; - continue 'cursor; - } - ProjectionElem::Deref => { - // (handled below) - } - } - assert_eq!(*projection, 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(&base_place); - 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 = base_place.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(&base_place); - return Some(cursor); - } - - ty::TyAdt(..) if ty.is_box() => { - self.next = Some(&base_place); - return Some(cursor); - } - - _ => panic!("unknown type fed to Projection Deref."), - } - } - } - } -} diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 62c6522a521af..fc24ad48f7fa7 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -112,7 +112,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.cfg.push_assign(block, source_info, &Place::local(result), box_); // initialize the box contents: - unpack!(block = this.into(&Place::local(result).deref(this.hir.tcx()), block, value)); + 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 } => { @@ -438,7 +439,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let arg_place = unpack!(block = this.as_place(block, arg)); - let mutability = if let Some((base_place, projection)) = arg_place.split_projection(tcx) { + let mutability = if let (base_place, Some(projection)) = arg_place.final_projection(tcx) { match projection { ProjectionElem::Deref => { if let PlaceBase::Local(local) = base_place.base { @@ -452,17 +453,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { "Unexpected capture place", ); this.local_decls[local].mutability - } else if let Some((base_place, projection)) = base_place.split_projection(tcx) { + } else if let (base_place, Some(projection)) = base_place.final_projection(tcx) + { if let ProjectionElem::Field(upvar_index, _) = projection { // Not projected from the implicit `self` in a closure. debug_assert!( - if let Some((base_place, projection)) = base_place.split_projection(tcx) { - PlaceBase::Local(Local::new(1)) == base_place.base - } else { - match base_place.base { - PlaceBase::Local(local) => local == Local::new(1), - _ => false, - } + match base_place.base { + PlaceBase::Local(local) => local == Local::new(1), + _ => false, }, "Unexpected capture place" ); @@ -472,6 +470,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { "Unexpected capture place" ); this.upvar_decls[upvar_index.index()].mutability + } else { + bug!("Unexpected capture place"); } } else { bug!("Unexpected capture place"); @@ -480,13 +480,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ProjectionElem::Field(upvar_index, _) => { // Not projected from the implicit `self` in a closure. debug_assert!( - if let Some((base_place, projection)) = base_place.split_projection(tcx) { - PlaceBase::Local(Local::new(1)) == base_place.base - } else { - match base_place.base { - PlaceBase::Local(local) => local == Local::new(1), - _ => false, - } + match base_place.base { + PlaceBase::Local(local) => local == Local::new(1), + _ => false, }, "Unexpected capture place" ); @@ -497,6 +493,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ); this.upvar_decls[upvar_index.index()].mutability } + _ => { + bug!("Unexpected capture place"); + } } } else { if let PlaceBase::Local(local) = arg_place.base { diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 58a43a37125e6..617e36d762ce2 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -253,7 +253,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }); let ptr_temp = Place::local(ptr_temp); let block = unpack!(this.into(&ptr_temp, block, ptr)); - this.into(&ptr_temp.deref(this.hir.tcx()), block, val) + let tcx = this.hir.tcx(); + this.into(&ptr_temp.deref(tcx), block, val) } else { let args: Vec<_> = args.into_iter() diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 80a7a21d7924c..c09bad4e061bc 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -668,8 +668,11 @@ 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(self.hir.tcx(), 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| { diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs index 8b1143cecf240..29609f422770c 100644 --- a/src/librustc_mir/build/matches/util.rs +++ b/src/librustc_mir/build/matches/util.rs @@ -45,25 +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(self.hir.tcx(), elem); + false, + ); MatchPair::new(place, subpattern) }) ); if let Some(subslice_pat) = opt_slice { - let subslice = place.clone().elem( + let place = place.clone().subslice( self.hir.tcx(), - ProjectionElem::Subslice { - from: prefix.len() as u32, - to: suffix.len() as u32 - } + prefix.len() as u32, + suffix.len() as u32, ); - match_pairs.push(MatchPair::new(subslice, subslice_pat)); + match_pairs.push(MatchPair::new(place, subslice_pat)); } match_pairs.extend( @@ -71,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(self.hir.tcx(), elem); + true, + ); MatchPair::new(place, subpattern) }) ); diff --git a/src/librustc_mir/dataflow/drop_flag_effects.rs b/src/librustc_mir/dataflow/drop_flag_effects.rs index 67b6dd27ad785..43693bf04f438 100644 --- a/src/librustc_mir/dataflow/drop_flag_effects.rs +++ b/src/librustc_mir/dataflow/drop_flag_effects.rs @@ -165,7 +165,7 @@ pub(crate) fn on_all_drop_children_bits<'a, 'gcx, 'tcx, F>( } pub(crate) fn drop_flag_effects_for_function_entry<'a, 'gcx, 'tcx, F>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, ctxt: &MoveDataParamEnv<'gcx, 'tcx>, mut callback: F) @@ -174,7 +174,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 lookup_result = move_data.rev_lookup.find(tcx, &place); + let lookup_result = move_data.rev_lookup.find(&place); on_lookup_result_bits(tcx, mir, move_data, lookup_result, |mpi| callback(mpi, DropFlagState::Present)); @@ -214,7 +214,7 @@ pub(crate) fn drop_flag_effects_for_location<'a, 'gcx, 'tcx, F>( } pub(crate) fn for_location_inits<'a, 'gcx, 'tcx, F>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, move_data: &MoveData<'tcx>, loc: Location, diff --git a/src/librustc_mir/dataflow/impls/borrowed_locals.rs b/src/librustc_mir/dataflow/impls/borrowed_locals.rs index a838bcc131f8a..0472083b0dbd8 100644 --- a/src/librustc_mir/dataflow/impls/borrowed_locals.rs +++ b/src/librustc_mir/dataflow/impls/borrowed_locals.rs @@ -22,22 +22,17 @@ use rustc::mir::*; /// immovable generators. #[derive(Copy, Clone)] pub struct HaveBeenBorrowedLocals<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>, } impl<'a, 'tcx: 'a> HaveBeenBorrowedLocals<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { - HaveBeenBorrowedLocals { tcx, mir } + pub fn new(mir: &'a Mir<'tcx>) -> Self { + HaveBeenBorrowedLocals { mir } } pub fn mir(&self) -> &Mir<'tcx> { self.mir } - - pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { - self.tcx - } } impl<'a, 'tcx> BitDenotation for HaveBeenBorrowedLocals<'a, 'tcx> { @@ -59,7 +54,6 @@ impl<'a, 'tcx> BitDenotation for HaveBeenBorrowedLocals<'a, 'tcx> { let stmt = &self.mir[loc.block].statements[loc.statement_index]; BorrowedLocalsVisitor { - tcx: self.tcx(), sets, }.visit_statement(loc.block, stmt, loc); @@ -72,7 +66,6 @@ impl<'a, 'tcx> BitDenotation for HaveBeenBorrowedLocals<'a, 'tcx> { fn terminator_effect(&self, sets: &mut BlockSets, loc: Location) { BorrowedLocalsVisitor { - tcx: self.tcx(), sets, }.visit_terminator(loc.block, self.mir[loc.block].terminator(), loc); } @@ -102,31 +95,31 @@ impl<'a, 'tcx> InitialFlow for HaveBeenBorrowedLocals<'a, 'tcx> { } } -struct BorrowedLocalsVisitor<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - sets: &'a mut BlockSets<'tcx, Local>, +struct BorrowedLocalsVisitor<'b, 'c: 'b> { + sets: &'b mut BlockSets<'c, Local>, } -impl<'a, 'tcx: 'a> BorrowedLocalsVisitor<'a, 'tcx> { +impl<'b, 'c, 'tcx> BorrowedLocalsVisitor<'b, 'c> { fn find_local(&self, place: &Place<'tcx>) -> Option { - if let Some((base_place, projection)) = place.split_projection(self.tcx) { - if let ProjectionElem::Deref = projection { - None - } else { - self.find_local(&base_place) - } - } else { - match place.base { - PlaceBase::Local(l) => Some(l), - PlaceBase::Promoted(_) | PlaceBase::Static(..) => None, + let mut local = match place.base { + PlaceBase::Local(local) => Some(local), + PlaceBase::Promoted(_) | PlaceBase::Static(..) => None, + }; + if !place.elems.is_empty() { + for elem in place.elems.iter() { + match elem { + ProjectionElem::Deref => local = None, + _ => continue, + } } } + local } } impl<'tcx, 'b, 'c> Visitor<'tcx> for BorrowedLocalsVisitor<'b, 'c> { fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { - if let Rvalue::Ref(_, _, place) = *rvalue { + if let Rvalue::Ref(_, _, ref place) = *rvalue { if let Some(local) = self.find_local(&place) { self.sets.gen(&local); } diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs index 0bd71734d1409..b479b1499c616 100644 --- a/src/librustc_mir/dataflow/impls/mod.rs +++ b/src/librustc_mir/dataflow/impls/mod.rs @@ -374,7 +374,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MaybeInitializedPlaces<'a, 'gcx, 'tcx> { // when a call returns successfully, that means we need to set // the bits for that dest_place to 1 (initialized). on_lookup_result_bits(self.tcx, self.mir, self.move_data(), - self.move_data().rev_lookup.find(self.tcx, dest_place), + self.move_data().rev_lookup.find(dest_place), |mpi| { in_out.add(&mpi); }); } } @@ -429,7 +429,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> // when a call returns successfully, that means we need to set // the bits for that dest_place to 0 (initialized). on_lookup_result_bits(self.tcx, self.mir, self.move_data(), - self.move_data().rev_lookup.find(self.tcx, dest_place), + self.move_data().rev_lookup.find(dest_place), |mpi| { in_out.remove(&mpi); }); } } @@ -483,7 +483,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for DefinitelyInitializedPlaces<'a, 'gcx, 'tc // when a call returns successfully, that means we need to set // the bits for that dest_place to 1 (initialized). on_lookup_result_bits(self.tcx, self.mir, self.move_data(), - self.move_data().rev_lookup.find(self.tcx, dest_place), + self.move_data().rev_lookup.find(dest_place), |mpi| { in_out.add(&mpi); }); } } @@ -557,7 +557,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MovingOutStatements<'a, 'gcx, 'tcx> { on_lookup_result_bits(self.tcx, self.mir, move_data, - move_data.rev_lookup.find(self.tcx, dest_place), + move_data.rev_lookup.find(dest_place), |mpi| for moi in &path_map[mpi] { assert!(moi.index() < bits_per_block); in_out.remove(&moi); @@ -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(self.tcx, &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 a5c4eef7b2d97..612bc149e2d5a 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -106,17 +106,19 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { -> Result> { debug!("lookup({:?})", place); - if let Some((base_place, projection)) = place.split_projection(self.builder.tcx) { - self.move_path_for_projection(place, &base_place, projection) - } else { - match place.base { - PlaceBase::Local(local) => Ok(self.builder.data.rev_lookup.locals[local]), - PlaceBase::Promoted(..) | - PlaceBase::Static(..) => { - Err(MoveError::cannot_move_out_of(self.loc, Static)) - } + let mut result = match place.base { + PlaceBase::Local(local) => Ok(self.builder.data.rev_lookup.locals[local]), + PlaceBase::Promoted(..) | + PlaceBase::Static(..) => { + Err(MoveError::cannot_move_out_of(self.loc, Static)) + } + }; + if !place.elems.is_empty() { + for elem in place.elems.iter() { + result = self.move_path_for_projection(place, place, elem); } } + result } fn create_move_path(&mut self, place: &Place<'tcx>) { @@ -273,10 +275,11 @@ 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(self.builder.tcx)); - self.gather_init(self.builder.tcx, place, InitKind::Shallow); + let tcx = self.builder.tcx; + self.create_move_path(&place.clone().deref(tcx)); + self.gather_init(place, InitKind::Shallow); } else { - self.gather_init(self.builder.tcx, place, InitKind::Deep); + self.gather_init(place, InitKind::Deep); } self.gather_rvalue(rval); } @@ -286,7 +289,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { StatementKind::InlineAsm { ref outputs, ref inputs, ref asm } => { for (output, kind) in outputs.iter().zip(&asm.outputs) { if !kind.is_indirect { - self.gather_init(self.builder.tcx, output, InitKind::Deep); + self.gather_init(output, InitKind::Deep); } } for input in inputs { @@ -377,7 +380,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { TerminatorKind::DropAndReplace { ref location, ref value, .. } => { self.create_move_path(location); self.gather_operand(value); - self.gather_init(self.builder.tcx, location, InitKind::Deep); + self.gather_init(location, InitKind::Deep); } TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => { self.gather_operand(func); @@ -386,7 +389,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { } if let Some((ref destination, _bb)) = *destination { self.create_move_path(destination); - self.gather_init(self.builder.tcx, destination, InitKind::NonPanicPathOnly); + self.gather_init(destination, InitKind::NonPanicPathOnly); } } } @@ -421,10 +424,10 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { self.builder.data.loc_map[self.loc].push(move_out); } - fn gather_init(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, place: &Place<'tcx>, kind: InitKind) { + fn gather_init(&mut self, place: &Place<'tcx>, kind: InitKind) { debug!("gather_init({:?}, {:?})", self.loc, place); - if let LookupResult::Exact(path) = self.builder.data.rev_lookup.find(tcx, place) { + if let LookupResult::Exact(path) = self.builder.data.rev_lookup.find(place) { let init = self.builder.data.inits.push(Init { span: self.builder.mir.source_info(self.loc).span, path, diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 78da5f1dc32e0..e2bc43a478250 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}; @@ -248,26 +248,28 @@ impl<'tcx> MovePathLookup<'tcx> { // parent. pub fn find<'a>( &self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, place: &Place<'tcx> ) -> LookupResult { - if let Some((base_place, projection)) = place.split_projection(tcx) { - match self.find(tcx, &base_place) { - LookupResult::Exact(base_path) => { - match self.projections.get(&(base_path, projection.lift())) { - Some(subpath) => LookupResult::Exact(*subpath), - None => LookupResult::Parent(Some(base_path)), + let mut result = match place.base { + PlaceBase::Local(local) => LookupResult::Exact(self.locals[local]), + PlaceBase::Promoted(_) | + PlaceBase::Static(..) => LookupResult::Parent(None), + }; + if !place.elems.is_empty() { + 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, - } - } else { - match place.base { - PlaceBase::Local(local) => LookupResult::Exact(self.locals[local]), - PlaceBase::Promoted(_) | - PlaceBase::Static(..) => LookupResult::Parent(None), + 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 944efe1aa7214..e23af50280ab2 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -102,22 +102,27 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { place: &mir::Place<'tcx>, ) -> EvalResult<'tcx, Option> { use rustc::mir::PlaceBase::*; - - if let Some((base_place, projection)) = place.split_projection(self.tcx.tcx) { - self.try_read_place(&base_place); - self.try_read_place_projection(&base_place, projection) - } else { - match place.base { - // 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), + let mut result = 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), + }; + if !place.elems.is_empty() { + for elem in place.elems.iter() { + result = self.try_read_place_projection(place, elem); } } + + result } pub fn read_field( @@ -212,51 +217,54 @@ 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::PlaceBase::*; - let place = if let Some((base_place, projection)) = mir_place.split_projection(self.tcx.tcx) { - let ty = self.place_ty(&base_place); - let place = self.eval_place(&base_place)?; - self.eval_place_projection(place, ty, projection)? - } else { - match mir_place.base { - Local(mir::RETURN_PLACE) => self.frame().return_place, - Local(local) => Place::Local { - frame: self.cur_frame(), - local, - }, - - Promoted(ref promoted) => { - let instance = self.frame().instance; - let val = self.read_global_as_value(GlobalId { - instance, - promoted: Some(promoted.0), - })?; - if let Value::ByRef(ptr, align) = val { - Place::Ptr { - ptr: ptr.into(), - align, - extra: PlaceExtra::None, - } - } else { - bug!("evaluated promoted and got {:#?}", val); - } - } + let mut place = match mir_place.base { + Local(mir::RETURN_PLACE) => self.frame().return_place, + Local(local) => Place::Local { + frame: self.cur_frame(), + local, + }, - Static(ref static_) => { - let layout = self.layout_of(self.place_ty(mir_place))?; - let instance = ty::Instance::mono(*self.tcx, static_.def_id); - let cid = GlobalId { - instance, - promoted: None - }; - let alloc = Machine::init_static(self, cid)?; + Promoted(ref promoted) => { + let instance = self.frame().instance; + let val = self.read_global_as_value(GlobalId { + instance, + promoted: Some(promoted.0), + })?; + if let Value::ByRef(ptr, align) = val { Place::Ptr { - ptr: ScalarMaybeUndef::Scalar(Scalar::Ptr(alloc.into())), - align: layout.align, + ptr: ptr.into(), + align, extra: PlaceExtra::None, } + } else { + bug!("evaluated promoted and got {:#?}", val); + } + } + + Static(ref static_) => { + let layout = self.layout_of(self.place_ty(mir_place))?; + let instance = ty::Instance::mono(*self.tcx, static_.def_id); + let cid = GlobalId { + instance, + promoted: None + }; + let alloc = Machine::init_static(self, cid)?; + Place::Ptr { + ptr: ScalarMaybeUndef::Scalar(Scalar::Ptr(alloc.into())), + align: layout.align, + extra: PlaceExtra::None, } } }; + + if !mir_place.elems.is_empty() { + let ty = self.place_ty(mir_place); + let place1 = self.eval_place(mir_place)?; + for elem in mir_place.elems.iter() { + place = self.eval_place_projection(place1, ty, elem)?; + } + } + self.dump_local(place); Ok(place) diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index a6e9ce0d87a09..a09ba9907dec8 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -301,9 +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( - *self.tcx, + tcx, mir::Field::new(i), valty.ty, ))?; diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 389dfd2fea7d5..cafb5b7f68b60 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -589,8 +589,9 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { // BB #7 (cleanup) // `drop(dest[beg])`; + let tcx = self.tcx; self.block(vec![], TerminatorKind::Drop { - location: dest.index(self.tcx, beg), + location: dest.index(tcx, beg), target: BasicBlock::new(8), unwind: None, }, true); diff --git a/src/librustc_mir/transform/add_validation.rs b/src/librustc_mir/transform/add_validation.rs index 27f9421ffc413..8c9358b73c050 100644 --- a/src/librustc_mir/transform/add_validation.rs +++ b/src/librustc_mir/transform/add_validation.rs @@ -30,7 +30,7 @@ fn place_context<'a, 'tcx, D>( ) -> (Option, hir::Mutability) where D: HasLocalDecls<'tcx> { - if let Some((base_place, projection)) = place.split_projection(tcx){ + if let (base_place, Some(projection)) = place.final_projection(tcx){ match projection { ProjectionElem::Deref => { // Computing the inside the recursion makes this quadratic. @@ -61,7 +61,7 @@ fn place_context<'a, 'tcx, D>( // This is already as restricted as it gets, no need to even recurse context } else { - let base_context = place_context(&place.base_place(tcx), local_decls, tcx); + let base_context = place_context(&place.projection_base(tcx), 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); @@ -71,12 +71,17 @@ fn place_context<'a, 'tcx, D>( _ => place_context(&base_place, local_decls, tcx), } } else { - use rustc::mir::PlaceBase::*; match place.base { - Local { .. } => (None, hir::MutMutable), - Promoted(_) | Static(_) => (None, hir::MutImmutable), + PlaceBase::Local { .. } => (None, hir::MutMutable), + PlaceBase::Promoted(_) + | PlaceBase::Static(_) => (None, hir::MutImmutable), } } + // let mut place_context = match place.base { + // PlaceBase::Local { .. } => (None, hir::MutMutable), + // PlaceBase::Promoted(_) + // | PlaceBase::Static(_) => (None, hir::MutImmutable), + // }; } /// Check if this function contains an unsafe block or is an unsafe function. diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 8f6b3f5b10dd2..bbd815ff1e673 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -164,7 +164,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { } } - if let Some((base_place, projection)) = place.split_projection(self.tcx) { + if let (base_place, Some(projection)) = place.final_projection(self.tcx) { let old_source_info = self.source_info; if let PlaceBase::Local(local) = place.base { if self.mir.local_decls[local].internal { @@ -178,9 +178,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { 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() { @@ -226,8 +226,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { 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"); + "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 = diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 6d648a313f0e5..ebe26881f6e98 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -279,52 +279,56 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { } fn eval_place(&mut self, place: &Place<'tcx>, source_info: SourceInfo) -> Option> { - if let Some((base_place, projection)) = place.split_projection(self.tcx) { - match projection { - ProjectionElem::Field(field, _) => { - trace!("field proj on {:?}", base_place); - let (base, layout, span) = self.eval_place(&base_place, source_info)?; + 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, + }; + + if !place.elems.is_empty() { + 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) })?; - Some((valty.0, valty.1, span)) - }, - _ => None, - } - } else { - match place.base { - PlaceBase::Local(local) => self.places[local].clone(), - PlaceBase::Promoted(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, + 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(c) => self.eval_constant(&c, source_info), - | Operand::Move(place) - | Operand::Copy(place) => self.eval_place(&place, 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), } } @@ -584,10 +588,6 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { // doesn't use the invalid value match cond { Operand::Move(place) | Operand::Copy(place) => { - let mut place = place; - while let Some((base_place, _)) = place.split_projection(self.tcx) { - place = &base_place; - } if let PlaceBase::Local(local) = place.base { self.places[local] = None; } diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index d25a0e21436a1..a66152699ef98 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -121,7 +121,7 @@ fn find_dead_unwinds<'a, 'tcx>( init_data.apply_location(tcx, mir, env, loc); } - let path = match env.move_data.rev_lookup.find(tcx, location) { + let path = match env.move_data.rev_lookup.find(location) { LookupResult::Exact(e) => e, LookupResult::Parent(..) => { debug!("find_dead_unwinds: has parent; skipping"); @@ -375,7 +375,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { statement_index: data.statements.len() }); - let path = self.move_data().rev_lookup.find(self.tcx, location); + let path = self.move_data().rev_lookup.find(location); debug!("collect_drop_flags: {:?}, place {:?} ({:?})", bb, location, path); @@ -414,7 +414,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { match terminator.kind { TerminatorKind::Drop { ref location, target, unwind } => { let init_data = self.initialization_data_at(loc); - match self.move_data().rev_lookup.find(self.tcx, location) { + match self.move_data().rev_lookup.find(location) { LookupResult::Exact(path) => { elaborate_drop( &mut Elaborator { @@ -503,7 +503,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { is_cleanup: false, }); - match self.move_data().rev_lookup.find(self.tcx, location) { + match self.move_data().rev_lookup.find(location) { LookupResult::Exact(path) => { debug!("elaborate_drop_and_replace({:?}) - tracked {:?}", terminator, path); let init_data = self.initialization_data_at(loc); @@ -572,7 +572,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { assert!(!self.patch.is_patched(bb)); let loc = Location { block: tgt, statement_index: 0 }; - let path = self.move_data().rev_lookup.find(self.tcx, place); + let path = self.move_data().rev_lookup.find(place); on_lookup_result_bits( self.tcx, self.mir, self.move_data(), path, |child| self.set_drop_flag(loc, child, DropFlagState::Present) @@ -646,7 +646,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { assert!(!self.patch.is_patched(bb)); let loc = Location { block: bb, statement_index: data.statements.len() }; - let path = self.move_data().rev_lookup.find(self.tcx, place); + let path = self.move_data().rev_lookup.find(place); on_lookup_result_bits( self.tcx, self.mir, self.move_data(), path, |child| self.set_drop_flag(loc, child, DropFlagState::Present) diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 6ec6d0be632c6..d80a630210ca5 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -97,7 +97,7 @@ impl<'tcx> MutVisitor<'tcx> for RenameLocalVisitor { } } -struct DerefArgVisitor<'a, 'tcx> { +struct DerefArgVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> } @@ -329,42 +329,28 @@ impl<'tcx> Visitor<'tcx> for StorageIgnored { } } -struct BorrowedLocals<'a, 'tcx>{ - tcx: TyCtxt<'a, 'tcx, 'tcx>, - local_live: liveness::LiveVarSet, -} +struct BorrowedLocals(liveness::LiveVarSet); -impl<'a, 'tcx> BorrowedLocals<'a, 'tcx> { +impl<'tcx> BorrowedLocals { fn mark_as_borrowed( &mut self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, place: &Place<'tcx>, ) { - if let Some((base_place, projection)) = place.split_projection(tcx) { - if let ProjectionElem::Deref = projection { - // For derefs we don't look any further. - // If it pointed to a Local, it would already be borrowed elsewhere - () - } else { - self.mark_as_borrowed(self.tcx, &base_place) - } - } else { - match place.base { - PlaceBase::Local(l) => { self.local_live.add(&l); } - _ => (), - } + match place.base { + PlaceBase::Local(l) => { self.0.add(&l); } + _ => (), } } } -impl<'a, 'tcx> Visitor<'tcx> for BorrowedLocals<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for BorrowedLocals { fn visit_rvalue( &mut self, rvalue: &Rvalue<'tcx>, location: Location, ) { if let Rvalue::Ref(_, _, place) = rvalue { - self.mark_as_borrowed(self.tcx, place); + self.mark_as_borrowed(place); } self.super_rvalue(rvalue, location) @@ -396,7 +382,7 @@ fn locals_live_across_suspend_points<'a, 'tcx,>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // borrowed (even if they are still active). // This is only used for immovable generators. let borrowed_locals = if !movable { - let analysis = HaveBeenBorrowedLocals::new(tcx, mir); + let analysis = HaveBeenBorrowedLocals::new(mir); let result = do_dataflow(tcx, mir, node_id, &[], &dead_unwinds, analysis, |bd, p| DebugFormatted::new(&bd.mir().local_decls[p])); @@ -596,7 +582,7 @@ fn elaborate_generator_drops<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, &Terminator { source_info, kind: TerminatorKind::Drop { - location: place, + location: ref place, target, unwind } @@ -956,3 +942,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 6bb7973b5dc99..65f7881eab882 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -415,28 +415,31 @@ 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<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, + fn dest_needs_borrow<'tcx>( place: &Place<'tcx>, ) -> bool { - if let Some((base_place, projection)) = place.split_projection(tcx) { - match projection { - ProjectionElem::Deref | - ProjectionElem::Index(_) => true, - _ => dest_needs_borrow(tcx, &base_place) - } + let mut dest_needs_borrow = if let PlaceBase::Static(_) = place.base { + // Static variables need a borrow because the callee + // might modify the same static. + true } else { - if let PlaceBase::Static(_) = place.base { - // Static variables need a borrow because the callee - // might modify the same static. - true - } else { - false - } + false + }; + if !place.elems.is_empty() { + for elem in place.elems.iter() { + match elem { + ProjectionElem::Deref + | ProjectionElem::Index(_) => { + dest_needs_borrow = true; + } + _ => continue, + } + } } + dest_needs_borrow } - let dest = if dest_needs_borrow(self.tcx, &destination.0) { + let dest = if dest_needs_borrow(&destination.0) { debug!("Creating temp for return destination"); let dest = Rvalue::Ref( self.tcx.types.re_erased, @@ -581,7 +584,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { // FIXME: Analysis of the usage of the arguments to avoid // unnecessary temporaries. - if let Operand::Move(place) = arg { + if let Operand::Move(ref place) = arg { if let PlaceBase::Local(local) = place.base { if caller_mir.local_kind(local) == LocalKind::Temp { // Reuse the operand if it's a temporary already @@ -653,7 +656,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { *local = l; return; }, - place => bug!("Return place is {:?}, not local", place) + ref place => bug!("Return place is {:?}, not local", place) } } let idx = local.index() - 1; diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs index b84f7c233d02a..00bfa11ed5607 100644 --- a/src/librustc_mir/transform/instcombine.rs +++ b/src/librustc_mir/transform/instcombine.rs @@ -40,28 +40,27 @@ impl MirPass for InstCombine { }; // Then carry out those optimizations. - MutVisitor::visit_mir(&mut InstCombineVisitor { tcx, optimizations }, mir); + MutVisitor::visit_mir(&mut InstCombineVisitor { optimizations }, mir); } } -pub struct InstCombineVisitor<'a, 'tcx> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub struct InstCombineVisitor<'tcx> { optimizations: OptimizationList<'tcx>, } -impl<'a, 'tcx> MutVisitor<'tcx> for InstCombineVisitor<'a, '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 { + let new_place = match rvalue { Rvalue::Ref(_, _, place) => { // Replace with dummy - let base_place = place.base_place(self.tcx); - mem::replace(&mut base_place, 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) { @@ -92,11 +91,13 @@ 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) = *rvalue { - if let Some((base_place, projection)) = place.split_projection(self.tcx) { - if let ProjectionElem::Deref = projection { - if base_place.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 { + if !place.elems.is_empty() { + for elem in place.elems.iter() { + if let ProjectionElem::Deref = elem { + if place.ty(self.mir, self.tcx).to_ty(self.tcx).is_region_ptr() { + self.optimizations.and_stars.insert(location); + } } } } diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 9d3c4c42fcd52..746d8dc1e03e6 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -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()); @@ -299,27 +300,31 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { 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>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - place: &'a mut Place<'tcx> - ) -> &'a mut Place<'tcx> { - if let Some((base_place, projection)) = place.split_projection(tcx) { - assert_ne!(*projection, ProjectionElem::Deref); - return interior_base(tcx, &mut base_place); + let mut place = place.clone(); + if !place.elems.is_empty() { + // 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(self.tcx, 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!() } @@ -374,7 +379,7 @@ 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, _) => { + StatementKind::Assign(ref place, _) => { if let PlaceBase::Local(local) = place.base { if temps[local] == TempState::PromotedOut { // Already promoted. diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 3dc2d01112721..8594a43967daa 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -244,7 +244,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { return; } - if let Some((base_place, projection)) = dest.split_projection(self.tcx){ + 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 @@ -274,7 +274,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { 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]) + store(&mut self.local_qualif[index]); } _ => { // Catch more errors in the destination. @@ -459,11 +459,13 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } } - fn visit_place(&mut self, - place: &Place<'tcx>, - context: PlaceContext<'tcx>, - location: Location) { - if let Some((base_place, projection)) = place.split_projection(self.tcx) { + fn visit_place( + &mut self, + place: &Place<'tcx>, + context: PlaceContext<'tcx>, + location: Location, + ) { + if let (base_place, Some(projection)) = place.final_projection(self.tcx) { self.nest(|this| { this.super_place(place, context, location); match projection { @@ -553,8 +555,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { 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." + "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." @@ -574,7 +576,7 @@ 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) = *operand { + if let Operand::Move(ref place) = *operand { if let PlaceBase::Local(local) = place.base { self.local_qualif[local] = self.local_qualif[local].map(|q| q - Qualif::NEEDS_DROP @@ -605,9 +607,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { // Recurse through operands and places. - if let Rvalue::Ref(region, kind, place) = *rvalue { + if let Rvalue::Ref(region, kind, ref place) = *rvalue { let mut is_reborrow = false; - if let Some((base_place, projection)) = place.split_projection(self.tcx) { + if let (base_place, Some(projection)) = place.final_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 { @@ -642,7 +644,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { Rvalue::Discriminant(..) | Rvalue::Len(_) => {} - Rvalue::Ref(_, kind, place) => { + Rvalue::Ref(_, kind, ref place) => { let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx); // Default to forbidding the borrow and/or its promotion, @@ -715,12 +717,17 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { // 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 Some((base_place, projection)) = place.split_projection(self.tcx) { - if *projection == ProjectionElem::Deref { - break; + let mut place = place.clone(); + if !place.elems.is_empty() { + for (i, elem) in place.elems.iter().cloned().enumerate().rev() { + match elem { + ProjectionElem::Deref => break, + _ => { + place = place.elem_base(self.tcx, i); + continue; + } + } } - place = base_place; } if let PlaceBase::Local(local) = place.base { if self.mir.local_kind(local) == LocalKind::Temp { @@ -1016,9 +1023,9 @@ 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, PlaceBase::Local(index)) = (self.mode, dest.base) { - if self.mir.local_kind(index) == LocalKind::Var && - self.const_fn_arg_vars.insert(index) && + 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 { // Direct use of an argument is permitted. @@ -1048,7 +1055,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", diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs index d7e80228e03de..4e22fd8e9f8b3 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/src/librustc_mir/transform/rustc_peek.rs @@ -125,13 +125,13 @@ 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( - place @ Place { + ref place @ Place { base: PlaceBase::Local(_), elems: _, } ) | mir::Operand::Move( - place @ Place { + ref place @ Place { base: PlaceBase::Local(_), elems: _, } @@ -181,10 +181,10 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, "sanity_check should run before Deaggregator inserts SetDiscriminant"), }; - if *place == peek_arg_place { + if place == peek_arg_place { if let mir::Rvalue::Ref(_, mir::BorrowKind::Shared, ref peeking_at_place) = *rvalue { // Okay, our search is over. - match move_data.rev_lookup.find(tcx, peeking_at_place) { + match move_data.rev_lookup.find(peeking_at_place) { LookupResult::Exact(peek_mpi) => { let bit_state = sets.on_entry.contains(&peek_mpi); debug!("rustc_peek({:?} = &{:?}) bit_state: {}", @@ -208,7 +208,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } - let lhs_mpi = move_data.rev_lookup.find(tcx, place); + let lhs_mpi = move_data.rev_lookup.find(place); debug!("rustc_peek: computing effect on place: {:?} ({:?}) in stmt: {:?}", place, lhs_mpi, stmt); diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs index 3bb79c2ea7ac7..7cd8311492c25 100644 --- a/src/librustc_mir/transform/uniform_array_move_out.rs +++ b/src/librustc_mir/transform/uniform_array_move_out.rs @@ -1,5 +1,5 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at +// Copyright 2015 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 Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> { location: Location ) { if let Rvalue::Use(Operand::Move(src_place)) = rvalue { - if let Some((base_place, projection)) = src_place.split_projection(self.tcx) { + if let (base_place, Some(projection)) = src_place.final_projection(self.tcx) { if let ProjectionElem::ConstantIndex { offset: _, min_length: _, @@ -93,7 +93,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> { location, dst_place, &base_place, - projection, + &projection, item_ty, size as u32, ); @@ -211,7 +211,11 @@ impl MirPass for RestoreSubsliceArrayMoveOut { if let Operand::Move(place) = item { if let PlaceBase::Local(local) = place.base { let local_use = &visitor.locals_use[local]; - let opt_index_and_place = Self::try_get_item_source(tcx, local_use, mir); + 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() { @@ -223,7 +227,7 @@ impl MirPass for RestoreSubsliceArrayMoveOut { 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 { @@ -232,7 +236,9 @@ impl MirPass for RestoreSubsliceArrayMoveOut { None } }); - Self::check_and_patch(tcx, *candidate, &items, opt_size, &mut patch, dst_place); + Self::check_and_patch(tcx, *candidate, &items, + opt_size, &mut patch, dst_place + ); } } } @@ -245,20 +251,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<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, + fn check_and_patch<'tcx>( + tcx: TyCtxt<'_, '_, 'tcx>, candidate: Location, - items: &[Option<(&LocalUse, u32, &Place<'tcx>)>], + 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); + 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; @@ -268,7 +274,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()); @@ -293,26 +299,28 @@ impl RestoreSubsliceArrayMoveOut { } fn try_get_item_source<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, + tcx: TyCtxt<'_, '_, 'tcx>, local_use: &LocalUse, mir: &'a Mir<'tcx> - ) -> Option<(u32, &'a Place<'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( - place1, - Rvalue::Use(Operand::Move(place2)), + ref place1, + Rvalue::Use(Operand::Move(ref place2)), ) = statement.kind { if let PlaceBase::Local(_) = place1.base { - if let Some((base_place, projection)) = place2.split_projection(tcx) { - if let ProjectionElem::ConstantIndex { - offset, - min_length: _, - from_end: false, - } = projection { - return Some((*offset, &base_place)); + if place1.elems.is_empty() { + 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 e745f5ca9ae97..9a77f18f078e3 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,29 +44,30 @@ 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 Some((base_place, projection)) = place.split_projection(tcx) { - match projection { - // encountered a Deref, which is ABI-aligned - ProjectionElem::Deref => break, - ProjectionElem::Field(..) => { - let ty = base_place.ty(local_decls, tcx).to_ty(tcx); - match ty.sty { - ty::TyAdt(def, _) if def.repr.packed() => { - return true + if !place.elems.is_empty() { + for (i, elem) in place.elems.iter().cloned().enumerate().rev() { + match elem { + // encountered a Deref, which is ABI-aligned + ProjectionElem::Deref => break, + ProjectionElem::Field(..) => { + let base_place = place.elem_base(tcx, i); + let ty = base_place.ty(local_decls, tcx).to_ty(tcx); + match ty.sty { + ty::TyAdt(def, _) if def.repr.packed() => return true, + _ => continue, } - _ => {} } + _ => continue, } - _ => {} } - place = &base_place; } false diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index dcd1ac5a62a3f..159da39832861 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -420,9 +420,10 @@ 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( + let base_place = self.place.clone().downcast( self.tcx(), - ProjectionElem::Downcast(adt, variant_index) + adt, + variant_index, ); let fields = self.move_paths_for_fields( &base_place, @@ -629,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(self.tcx()), + location: ptr.clone().deref(tcx), target: loop_block, unwind: unwind.into_option() }); @@ -653,15 +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( - self.tcx(), - 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()) { From c1fff4819e1f24b7d43d268bebc2cfff5249e992 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Wed, 22 Aug 2018 11:05:03 +0800 Subject: [PATCH 11/33] Port new Place into llvm codegen --- src/librustc_codegen_llvm/mir/analyze.rs | 83 ++++++++------ src/librustc_codegen_llvm/mir/block.rs | 123 ++++++++++++--------- src/librustc_codegen_llvm/mir/operand.rs | 66 +++++------ src/librustc_codegen_llvm/mir/place.rs | 70 ++++++------ src/librustc_codegen_llvm/mir/rvalue.rs | 12 +- src/librustc_codegen_llvm/mir/statement.rs | 48 ++++---- src/librustc_passes/mir_stats.rs | 40 +++---- 7 files changed, 240 insertions(+), 202 deletions(-) diff --git a/src/librustc_codegen_llvm/mir/analyze.rs b/src/librustc_codegen_llvm/mir/analyze.rs index 993138aee1cec..a443e08ed2511 100644 --- a/src/librustc_codegen_llvm/mir/analyze.rs +++ b/src/librustc_codegen_llvm/mir/analyze.rs @@ -110,13 +110,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,43 +158,48 @@ 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; - 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); - - // 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() { - return; - } + if !place.elems.is_empty() { + for (i, elem) in place.elems.iter().cloned().enumerate().rev() { + debug!("visit_place(place={:?}, context={:?})", place, context); + + // Allow uses of projections that are ZSTs or from scalar fields. + let is_consume = match context { + PlaceContext::Copy | PlaceContext::Move => true, + _ => false + }; - if let mir::ProjectionElem::Field(..) = proj.elem { - let layout = cx.layout_of(base_ty.to_ty(cx.tcx)); - 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); + let base = place.elem_base(cx.tcx, i); + if is_consume { + let base_ty = base.ty(self.fx.mir, cx.tcx); + let base_ty = self.fx.monomorphize(&base_ty); + + // ZSTs don't require any actual memory access. + let elem_ty = base_ty.projection_ty(cx.tcx, &elem).to_ty(cx.tcx); + let elem_ty = self.fx.monomorphize(&elem_ty); + if cx.layout_of(elem_ty).is_zst() { return; } + + if let mir::ProjectionElem::Field(..) = elem { + let layout = cx.layout_of(base_ty.to_ty(cx.tcx)); + 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. + 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); + // A deref projection only reads the pointer, never needs the place. + if let mir::ProjectionElem::Deref = elem { + context = PlaceContext::Copy; + continue; + } } } @@ -234,7 +245,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..ae80aeabacd96 100644 --- a/src/librustc_codegen_llvm/mir/operand.rs +++ b/src/librustc_codegen_llvm/mir/operand.rs @@ -310,45 +310,47 @@ 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 { - match self.locals[index] { - LocalRef::Operand(Some(o)) => { - return Some(o); - } - LocalRef::Operand(None) => { - bug!("use of {:?} before def", place); - } - 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 { - mir::ProjectionElem::Field(ref f, _) => { - return Some(o.extract_field(bx, f.index())); + if place.elems.is_empty() { + if let mir::PlaceBase::Local(index) = place.base { + match self.locals[index] { + LocalRef::Operand(Some(o)) => { + result = Some(o); } - mir::ProjectionElem::Index(_) | - mir::ProjectionElem::ConstantIndex { .. } => { - // ZSTs don't require any actual memory access. - // FIXME(eddyb) deduplicate this with the identical - // 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)); - } + LocalRef::Operand(None) => { + bug!("use of {:?} before def", place); } - _ => {} + LocalRef::Place(..) => { + // use path below + } + }; + } + } else { + // Moves out of scalar and scalar pair fields are trivial. + for e in place.elems.iter() { + if let Some(o) = result { + match e { + mir::ProjectionElem::Field(ref f, _) => { + result = Some(o.extract_field(bx, f.index())); + } + mir::ProjectionElem::Index(_) | + mir::ProjectionElem::ConstantIndex { .. } => { + // ZSTs don't require any actual memory access. + // FIXME(eddyb) deduplicate this with the identical + // checks in `codegen_consume` and `extract_field`. + let elem = o.layout.field(bx.cx, 0); + if elem.is_zst() { + 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..1d065c016eddb 100644 --- a/src/librustc_codegen_llvm/mir/place.rs +++ b/src/librustc_codegen_llvm/mir/place.rs @@ -419,20 +419,18 @@ impl FunctionCx<'a, 'll, 'tcx> { 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); + let mut result = match place.base { + mir::PlaceBase::Local(index) => { + 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)) => { + mir::PlaceBase::Promoted(box (index, ty)) => { let param_env = ty::ParamEnv::reveal_all(); let cid = mir::interpret::GlobalId { instance: self.instance, @@ -458,54 +456,52 @@ 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!(), + if !place.elems.is_empty() { + 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, _) => { - cg_base.project_field(bx, field.index()) + result.project_field(bx, field.index()) } mir::ProjectionElem::Index(index) => { - let index = &mir::Operand::Copy(mir::Place::Local(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) + result.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) + 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 = cg_base.len(bx.cx); + let lllen = result.len(bx.cx); let llindex = bx.sub(lllen, lloffset); - cg_base.project_index(bx, llindex) + result.project_index(bx, llindex) } mir::ProjectionElem::Subslice { from, to } => { - let mut subslice = cg_base.project_index(bx, + let mut subslice = result.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()); + 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(cg_base.llextra.unwrap(), + subslice.llextra = Some(bx.sub(result.llextra.unwrap(), C_usize(bx.cx, (from as u64) + (to as u64)))); } @@ -517,11 +513,11 @@ impl FunctionCx<'a, 'll, 'tcx> { subslice } mir::ProjectionElem::Downcast(_, v) => { - cg_base.project_downcast(bx, 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..e3f601e7c36d7 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.elems.is_empty() { + 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_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index 3206fa6e17265..886d75236dcf5 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,39 @@ 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); + if !place.elems.is_empty() { + 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, From 56773a99d21f7a540d1616632f26eff692fc9c63 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Aug 2018 14:29:48 -0400 Subject: [PATCH 12/33] refactor `PlaceTy::base_ty` into a method on `PlaceBase` --- src/librustc/mir/tcx.rs | 44 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index 70777006a5c74..d5a061db9de33 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -33,10 +33,6 @@ pub enum PlaceTy<'tcx> { } impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> { - pub fn from_ty(ty: Ty<'tcx>) -> PlaceTy<'tcx> { - PlaceTy::Ty { ty } - } - pub fn to_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { match *self { PlaceTy::Ty { ty } => ty, @@ -48,19 +44,6 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> { } } - fn base_ty(local_decls: &T, base: &PlaceBase<'tcx>) -> PlaceTy<'tcx> - where - T: HasLocalDecls<'tcx>, - { - match base { - PlaceBase::Local(index) => PlaceTy::Ty { - ty: local_decls.local_decls()[*index].ty, - }, - PlaceBase::Promoted(data) => PlaceTy::Ty { ty: data.1 }, - PlaceBase::Static(data) => PlaceTy::Ty { ty: data.ty }, - } - } - pub fn projection_ty( self, tcx: TyCtxt<'a, 'gcx, 'tcx>, @@ -110,6 +93,12 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> { } } +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 }, @@ -117,11 +106,22 @@ 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>, - { + 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`: // @@ -133,7 +133,7 @@ impl<'tcx> Place<'tcx> { // ^^-- no projection // ^^^^-- PlaceTy - let mut place_ty = PlaceTy::base_ty(local_decls, &self.base); + let mut place_ty = PlaceTy::from(self.base.ty(local_decls)); // apply .projection_ty() to all elems but only returns the final one. if !self.elems.is_empty() { From 36b5cac68e2e94f54eb2afd6167f64016a475123 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Aug 2018 14:30:04 -0400 Subject: [PATCH 13/33] fix the whitespace after `elems}` --- src/librustc/ich/impls_mir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index eb825012e97f0..e5d77d3edc74e 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -309,7 +309,7 @@ impl<'a, 'gcx> HashStable> for mir::PlaceBase<'gcx> { } } -impl_stable_hash_for!(struct mir::Place<'tcx> { base, elems} ); +impl_stable_hash_for!(struct mir::Place<'tcx> { base, elems }); impl<'a, 'gcx, V, T> HashStable> for mir::ProjectionElem<'gcx, V, T> From 2cd7680db19bc70fbcdbe59cbda28a1f3dd4cb41 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 24 Aug 2018 14:30:13 -0400 Subject: [PATCH 14/33] rewrite `builder` so that it terminates The idea is to update the `result` (and `result_ty` as we go) --- .../dataflow/move_paths/builder.rs | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 612bc149e2d5a..aef40683ddb5f 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}; @@ -107,18 +107,23 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { { debug!("lookup({:?})", place); let mut result = match place.base { - PlaceBase::Local(local) => Ok(self.builder.data.rev_lookup.locals[local]), + PlaceBase::Local(local) => self.builder.data.rev_lookup.locals[local], PlaceBase::Promoted(..) | - PlaceBase::Static(..) => { - Err(MoveError::cannot_move_out_of(self.loc, Static)) - } + PlaceBase::Static(..) => + return Err(MoveError::cannot_move_out_of(self.loc, Static)), }; + if !place.elems.is_empty() { + 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, place, elem); + result = self.move_path_for_projection(place, result, result_ty, elem)?; + result_ty = PlaceTy::from(result_ty).projection_ty(tcx, elem).to_ty(tcx); } } - result + + Ok(result) } fn create_move_path(&mut self, place: &Place<'tcx>) { @@ -127,16 +132,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>, - base_place: &Place<'tcx>, - projection: &PlaceElem<'tcx> + fn move_path_for_projection( + &mut self, + place: &Place<'tcx>, + base: MovePathIndex, + base_ty: Ty<'tcx>, + projection: &PlaceElem<'tcx> ) -> Result> { - let base = try!(self.move_path_for(base_place)); - let mir = self.builder.mir; let tcx = self.builder.tcx; - let place_ty = base_place.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, @@ -144,7 +148,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() => @@ -153,7 +157,7 @@ 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 projection { + ty: base_ty, is_index: match projection { ProjectionElem::Index(..) => true, _ => false }, @@ -163,7 +167,7 @@ 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: true + ty: base_ty, is_index: true })), _ => { // FIXME: still badly broken From ec4025503d82f75c37617b0638b95c80ea973532 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Sat, 25 Aug 2018 16:17:37 +0800 Subject: [PATCH 15/33] Rewrite place workflow in rustc_mir/interpret --- src/librustc_mir/interpret/place.rs | 91 +++++++++++++++++------------ 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index e23af50280ab2..2207764ca7cbc 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,28 +102,20 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { &self, place: &mir::Place<'tcx>, ) -> EvalResult<'tcx, Option> { - use rustc::mir::PlaceBase::*; - let mut result = 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), - }; - if !place.elems.is_empty() { + 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)?; + if !place.has_no_projection() { for elem in place.elems.iter() { - result = self.try_read_place_projection(place, elem); + 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)?; } } - result + Ok(result) } pub fn read_field( @@ -165,27 +158,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, - base_place: &mir::Place<'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(base_place)? { - Some(base) => base, - None => return Ok(None), - }; - let base_ty = self.place_ty(base_place); - let base_layout = self.layout_of(base_ty)?; - 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), + 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) } } @@ -257,11 +270,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } }; - if !mir_place.elems.is_empty() { - let ty = self.place_ty(mir_place); - let place1 = self.eval_place(mir_place)?; + if !mir_place.has_no_projection() { + let mut ty = mir_place.base.ty(self.mir()); for elem in mir_place.elems.iter() { - place = self.eval_place_projection(place1, ty, elem)?; + 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); } } From 7b6556643c710f814e3b44d78387bbb518548513 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Tue, 28 Aug 2018 22:21:05 +0800 Subject: [PATCH 16/33] Rewrite place workflow in rustc_mir/borrow_check --- .../borrow_check/error_reporting.rs | 29 ++-- src/librustc_mir/borrow_check/mod.rs | 50 +++--- src/librustc_mir/borrow_check/move_errors.rs | 52 +++--- .../borrow_check/nll/explain_borrow/mod.rs | 20 ++- .../borrow_check/nll/invalidation.rs | 2 + .../nll/region_infer/error_reporting/mod.rs | 3 +- .../borrow_check/nll/type_check/mod.rs | 158 +++++++++--------- src/librustc_mir/borrow_check/path_utils.rs | 2 +- src/librustc_mir/borrow_check/place_ext.rs | 13 +- .../borrow_check/places_conflict.rs | 26 ++- src/librustc_mir/borrow_check/used_muts.rs | 4 +- 11 files changed, 189 insertions(+), 170 deletions(-) diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index db166a4ca7fbe..588d502fde607 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -390,7 +390,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let borrow_span = borrow_spans.var_or_use(); let proper_span = match root_place.base { - PlaceBase::Local(local) => self.mir.local_decls[local].source_info.span, + PlaceBase::Local(local) if root_place.has_no_projection() + => self.mir.local_decls[local].source_info.span, _ => drop_span, }; @@ -528,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 PlaceBase::Local(local) = err_place.base { - 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 @@ -630,7 +634,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { including_downcast: &IncludingDowncast, ) -> Result<(), ()> { self.append_place_base_to_string(&place.base, buf)?; - if !place.elems.is_empty() { + if !place.has_no_projection() { for elem in place.elems.iter() { self.append_place_projection_to_string(place, elem, buf, including_downcast)?; } @@ -734,7 +738,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { PlaceBase::Promoted(ref prom) => self.describe_field_from_ty(&prom.1, field), PlaceBase::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field), }; - if !place.elems.is_empty() { + if !place.has_no_projection() { for elem in place.elems.iter() { let proj_str = match elem { ProjectionElem::Downcast(def, variant_index) => format!( @@ -986,7 +990,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { match place { Operand::Copy(place) | Operand::Move(place) - if PlaceBase::Local(local) == place.base => + if PlaceBase::Local(local) == place.base && + place.has_no_projection() => { debug!( "find_closure_borrow_span: found captured local \ diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 94a696ac7f3b7..8cdd44726e513 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -21,6 +21,7 @@ use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}; 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}; @@ -958,9 +959,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ty::TyClosure(def, substs) => { if let Place { base: PlaceBase::Local(local), - elems: _, + elems, } = drop_place { - if *local == Local::new(1) && !self.mir.upvar_decls.is_empty() { + if *local == Local::new(1) + && !self.mir.upvar_decls.is_empty() + && elems.is_empty() { substs .upvar_tys(def, self.tcx) .enumerate() @@ -973,9 +976,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ty::TyGenerator(def, substs, _) => { if let Place { base: PlaceBase::Local(local), - elems: _, + elems, } = drop_place { - if *local == Local::new(1) && !self.mir.upvar_decls.is_empty() { + if *local == Local::new(1) + && !self.mir.upvar_decls.is_empty() + && elems.is_empty() { substs .upvar_tys(def, self.tcx) .enumerate() @@ -1405,7 +1410,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } if let PlaceBase::Local(local) = place.base { - self.used_mut.insert(local); + if place.has_no_projection() { + self.used_mut.insert(local); + } } }, _ => {} @@ -1488,7 +1495,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // 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) = if !place.elems.is_empty() { + let (might_be_alive, will_be_dropped) = if !place.has_no_projection() { bug!("root of {:?} is a projection ({:?})?", place, place) } else { match place.base { @@ -1769,7 +1776,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { place: &Place<'tcx>, ) -> Result { let mut prefix = place.clone(); - if !prefix.elems.is_empty() { + if !prefix.has_no_projection() { 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) { @@ -1802,7 +1809,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { flow_state: &Flows<'cx, 'gcx, 'tcx>, ) { debug!("check_if_assigned_path_is_moved place: {:?}", place); - if !place.elems.is_empty() { + if !place.has_no_projection() { let tcx = self.tcx; for (i, elem) in place.elems.iter().cloned().enumerate().rev() { let base_place = place.elem_base(tcx, i); @@ -1988,7 +1995,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { is_local_mutation_allowed, } = root_place; - if !place.elems.is_empty() { + if !place.has_no_projection() { if let Some(field) = place.is_upvar_field_projection(self.mir, &self.tcx) { self.used_mut_upvars.push(field); } @@ -2068,17 +2075,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { is_local_mutation_allowed ); - if !place.elems.is_empty() { - let mut base_place = place.clone(); - for (i, elem) in place.elems.iter().cloned().enumerate().rev() { + if !place.has_no_projection() { + 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_place = base_place.elem_base(self.tcx, i); - let base_ty = base_place.ty(self.mir, self.tcx).to_ty(self.tcx); + base_ty = PlaceTy::from(base_ty) + .projection_ty(self.tcx, elem).to_ty(self.tcx); match base_ty.sty { ty::TyRef(_, _, mutbl) => { match mutbl { @@ -2101,8 +2108,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } _ => LocalMutationIsAllowed::Yes, }; - - continue; + continue } } } @@ -2121,9 +2127,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } } // `Box` owns its content, so mutable if its location is mutable - _ if base_ty.is_box() => { - continue; - } + _ if base_ty.is_box() => { continue; } // Deref should only be for reference, pointers or boxes _ => bug!("Deref of unexpected type: {:?}", base_ty), } @@ -2221,14 +2225,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let mut deepest = place.clone(); - if !place.elems.is_empty() { + if !place.has_no_projection() { for (i, elem) in place.elems.iter().cloned().enumerate().rev() { - deepest = match elem { + match elem { ProjectionElem::Deref if place.ty(self.mir, self.tcx).to_ty(self.tcx).is_box() => { - place.elem_base(self.tcx, i) + deepest = place.elem_base(self.tcx, i); }, - _ => continue, + _ => {}, } } } diff --git a/src/librustc_mir/borrow_check/move_errors.rs b/src/librustc_mir/borrow_check/move_errors.rs index 6068bc5349f0e..ca0598500b23c 100644 --- a/src/librustc_mir/borrow_check/move_errors.rs +++ b/src/librustc_mir/borrow_check/move_errors.rs @@ -94,7 +94,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { if let Some(StatementKind::Assign( Place { base: PlaceBase::Local(local), - elems: _, + elems, }, Rvalue::Use(Operand::Move(move_from)), )) = self.mir.basic_blocks()[location.block] @@ -102,30 +102,32 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { .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 { 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 f98ae0a778c95..de6c3c81cdb8d 100644 --- a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs +++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs @@ -91,15 +91,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place { if let PlaceBase::Local(borrowed_local) = place.base { - 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 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 7976fea861847..a76fe20aa48be 100644 --- a/src/librustc_mir/borrow_check/nll/invalidation.rs +++ b/src/librustc_mir/borrow_check/nll/invalidation.rs @@ -328,6 +328,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> { // directly accessed in the body of the closure/generator. ty::TyGenerator(def, substs, ..) 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() @@ -335,6 +336,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> { } ty::TyClosure(def, substs) 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() 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 0035d03b6b65d..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 @@ -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.base == PlaceBase::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 23ac369e00bd1..f8f23731fb8dc 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -422,16 +422,14 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { self.cx.prove_trait_ref(trait_ref, location.interesting()); } - if !place.elems.is_empty() { - + if !place.has_no_projection() { for elem in place.elems.iter(){ context = if context.is_mutating_use() { PlaceContext::Projection(Mutability::Mut) } else { PlaceContext::Projection(Mutability::Not) }; - let base_ty = place_ty; - if let PlaceTy::Ty { ty } = base_ty { + if let PlaceTy::Ty { ty } = place_ty { if ty.references_error() { assert!(self.errors_reported); place_ty = PlaceTy::Ty { @@ -440,7 +438,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { break; } } - place_ty = self.sanitize_projection(base_ty, elem, place, location); + place_ty = self.sanitize_projection(place_ty, elem, place, location); } }; @@ -877,11 +875,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 PlaceBase::Local(l) = place.base { - !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() @@ -1603,80 +1602,83 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { location, borrow_region, borrowed_place ); - 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 = borrowed_place.ty(self.mir, tcx).to_ty(tcx); - - debug!("add_reborrow_constraint - base_ty = {:?}", base_ty); - match base_ty.sty { - ty::TyRef(ref_region, _, mutbl) => { - constraints.outlives_constraints.push(OutlivesConstraint { - sup: ref_region.to_region_vid(), - sub: borrow_region.to_region_vid(), - locations: location.boring(), - }); - - if let Some(all_facts) = all_facts { - all_facts.outlives.push(( - ref_region.to_region_vid(), - borrow_region.to_region_vid(), - location_table.mid_index(location), - )); - } - - match mutbl { - hir::Mutability::MutImmutable => { - // Immutable reference. We don't need the base - // to be valid for the entire lifetime of - // the borrow. - break; + if borrowed_place.has_no_projection() { + 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 => { + + debug!("add_reborrow_constraint - base_ty = {:?}", base_ty); + match base_ty.sty { + ty::TyRef(ref_region, _, mutbl) => { + constraints.outlives_constraints.push(OutlivesConstraint { + sup: ref_region.to_region_vid(), + sub: borrow_region.to_region_vid(), + locations: location.boring(), + }); + + if let Some(all_facts) = all_facts { + all_facts.outlives.push(( + ref_region.to_region_vid(), + borrow_region.to_region_vid(), + location_table.mid_index(location), + )); } - hir::Mutability::MutMutable => { - // Mutable reference. We *do* need the base - // to be valid, because after the base becomes - // invalid, someone else can use our mutable deref. - - // This is in order to make the following function - // illegal: - // ``` - // fn unsafe_deref<'a, 'b>(x: &'a &'b mut T) -> &'b mut T { - // &mut *x - // } - // ``` - // - // As otherwise you could clone `&mut T` using the - // following function: - // ``` - // fn bad(x: &mut T) -> (&mut T, &mut T) { - // let my_clone = unsafe_deref(&'a x); - // ENDREGION 'a; - // (my_clone, x) - // } - // ``` + + match mutbl { + hir::Mutability::MutImmutable => { + // Immutable reference. We don't need the base + // to be valid for the entire lifetime of + // the borrow. + break; + } + hir::Mutability::MutMutable => { + // Mutable reference. We *do* need the base + // to be valid, because after the base becomes + // invalid, someone else can use our mutable deref. + + // This is in order to make the following function + // illegal: + // ``` + // fn unsafe_deref<'a, 'b>(x: &'a &'b mut T) -> &'b mut T { + // &mut *x + // } + // ``` + // + // As otherwise you could clone `&mut T` using the + // following function: + // ``` + // fn bad(x: &mut T) -> (&mut T, &mut T) { + // let my_clone = unsafe_deref(&'a x); + // ENDREGION 'a; + // (my_clone, x) + // } + // ``` + } } } + ty::TyRawPtr(..) => { + // deref of raw pointer, guaranteed to be valid + break; + } + ty::TyAdt(def, _) if def.is_box() => { + // deref of `Box`, need the base to be valid - propagate + } + _ => bug!("unexpected deref ty {:?} in {:?}", base_ty, borrowed_place), } - ty::TyRawPtr(..) => { - // deref of raw pointer, guaranteed to be valid - break; - } - ty::TyAdt(def, _) if def.is_box() => { - // deref of `Box`, need the base to be valid - propagate - } - _ => bug!("unexpected deref ty {:?} in {:?}", base_ty, borrowed_place), + } + ProjectionElem::Field(..) + | ProjectionElem::Downcast(..) + | ProjectionElem::Index(..) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } => { + // other field access } } - ProjectionElem::Field(..) - | ProjectionElem::Downcast(..) - | ProjectionElem::Index(..) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => { - // other field access - } + 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 32879884ea2c6..048cdc6042ca2 100644 --- a/src/librustc_mir/borrow_check/path_utils.rs +++ b/src/librustc_mir/borrow_check/path_utils.rs @@ -147,7 +147,7 @@ pub(super) fn borrow_of_local_data<'tcx>( PlaceBase::Local(..) => true, }; - if !place.elems.is_empty() { + if !place.has_no_projection() { for elem in place.elems.iter().cloned().rev() { if elem == ProjectionElem::Deref { borrow_of_data = false; diff --git a/src/librustc_mir/borrow_check/place_ext.rs b/src/librustc_mir/borrow_check/place_ext.rs index e974e918056de..d0b73cc10a930 100644 --- a/src/librustc_mir/borrow_check/place_ext.rs +++ b/src/librustc_mir/borrow_check/place_ext.rs @@ -11,6 +11,7 @@ use rustc::hir; use rustc::mir::ProjectionElem; use rustc::mir::{Local, Mir, Place, PlaceBase}; +use rustc::mir::tcx::PlaceTy; use rustc::ty::{self, TyCtxt}; /// Extension methods for the `Place` type. @@ -32,8 +33,9 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable) } }; - if !self.elems.is_empty() { - for elem in self.elems.iter().cloned().rev() { + if !self.has_no_projection() { + let mut base_ty = self.base.ty(mir); + for elem in self.elems.iter() { is_unsafe_place = match elem { ProjectionElem::Field(..) | ProjectionElem::Downcast(..) @@ -41,12 +43,13 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => continue, ProjectionElem::Deref => { - let ty = self.ty(mir, tcx).to_ty(tcx); - match ty.sty { + match base_ty.sty { ty::TyRawPtr(..) => true, _ => continue, + } } - }}; + }; + base_ty = PlaceTy::from(base_ty).projection_ty(tcx, elem).to_ty(tcx); } } diff --git a/src/librustc_mir/borrow_check/places_conflict.rs b/src/librustc_mir/borrow_check/places_conflict.rs index 9a05e96cdb985..918f67206fbe5 100644 --- a/src/librustc_mir/borrow_check/places_conflict.rs +++ b/src/librustc_mir/borrow_check/places_conflict.rs @@ -13,7 +13,8 @@ use borrow_check::Overlap; use borrow_check::{Deep, Shallow, ShallowOrDeep}; use rustc::hir; use rustc::mir::{Mir, Place, PlaceBase, PlaceElem, ProjectionElem}; -use rustc::ty::{self, Slice, Ty, TyCtxt}; +use rustc::mir::tcx::PlaceTy; +use rustc::ty::{self, Ty, TyCtxt}; use std::cmp::max; // FIXME(csmoe): rewrite place_conflict with slice @@ -35,22 +36,14 @@ pub(super) fn places_conflict<'gcx, 'tcx>( borrow_place: &Place<'tcx>, access_place: &Place<'tcx>| { // Enumerate for later base_place generation - let mut borrow_elems = borrow_place.elems.iter().cloned().enumerate(); + let mut borrow_elems = borrow_place.elems.iter().cloned(); let mut access_elems = access_place.elems.iter().cloned(); - loop { - if let Some((i, borrow_elem)) = borrow_elems.next() { - let base_place = Place { - base: borrow_place.clone().base, - elems: if i > 0 { - tcx.mk_place_elems(borrow_place.elems.iter().cloned().take(i)) - } else { - Slice::empty() - }, - }; - let base_ty = base_place.ty(mir, tcx).to_ty(tcx); + 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); @@ -66,7 +59,7 @@ pub(super) fn places_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(base_ty, (&borrow_elem, &access_elem)) { + 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. @@ -103,7 +96,7 @@ pub(super) fn places_conflict<'gcx, 'tcx>( // our place. This is a conflict if that is a part our // access cares about. - match (borrow_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 @@ -149,6 +142,9 @@ pub(super) fn places_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: diff --git a/src/librustc_mir/borrow_check/used_muts.rs b/src/librustc_mir/borrow_check/used_muts.rs index e9eeee6b28325..d3be38eac0479 100644 --- a/src/librustc_mir/borrow_check/used_muts.rs +++ b/src/librustc_mir/borrow_check/used_muts.rs @@ -56,7 +56,9 @@ impl<'visit, 'cx, 'gcx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'c path.place, local, path.place ); if let PlaceBase::Local(user_local) = path.place.base { - self.mbcx.used_mut.insert(user_local); + if path.place.has_no_projection() { + self.mbcx.used_mut.insert(user_local); + } } } } From a6520ec76eed28b823215d01fd799dde5ad7cde3 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Tue, 28 Aug 2018 22:21:24 +0800 Subject: [PATCH 17/33] Rewrite place workflow in rustc_mir/dataflow --- .../dataflow/impls/borrowed_locals.rs | 4 ++-- src/librustc_mir/dataflow/impls/borrows.rs | 22 +++++++++---------- .../dataflow/move_paths/builder.rs | 2 +- src/librustc_mir/dataflow/move_paths/mod.rs | 2 +- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/librustc_mir/dataflow/impls/borrowed_locals.rs b/src/librustc_mir/dataflow/impls/borrowed_locals.rs index 0472083b0dbd8..934e5f3d342db 100644 --- a/src/librustc_mir/dataflow/impls/borrowed_locals.rs +++ b/src/librustc_mir/dataflow/impls/borrowed_locals.rs @@ -105,11 +105,11 @@ impl<'b, 'c, 'tcx> BorrowedLocalsVisitor<'b, 'c> { PlaceBase::Local(local) => Some(local), PlaceBase::Promoted(_) | PlaceBase::Static(..) => None, }; - if !place.elems.is_empty() { + if !place.has_no_projection() { for elem in place.elems.iter() { match elem { ProjectionElem::Deref => local = None, - _ => continue, + _ => {}, } } } diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 3b8098e8638ce..383ad8fa09cda 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -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 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); + 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,18 +260,12 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { // Issue #46746: Two-phase borrows handles // stmts of form `Tmp = &mut Borrow` ... - if let Some(_projection) = lhs.projection() { + if !lhs.has_no_projection() { // ... can assign into projections, // e.g. `box (&mut _)`. Current // conservative solution: force // immediate activation here. sets.gen(&index); - } else { - match lhs.base { - PlaceBase::Promoted(_) - | PlaceBase::Local(..) - | PlaceBase::Static(..) => {} // okay - } } } } @@ -288,7 +284,9 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { 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/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index aef40683ddb5f..562878c13a525 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -113,7 +113,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { return Err(MoveError::cannot_move_out_of(self.loc, Static)), }; - if !place.elems.is_empty() { + if !place.has_no_projection() { let mir = self.builder.mir; let tcx = self.builder.tcx; let mut result_ty = place.base.ty(mir); diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index e2bc43a478250..6151ac75098fe 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -255,7 +255,7 @@ impl<'tcx> MovePathLookup<'tcx> { PlaceBase::Promoted(_) | PlaceBase::Static(..) => LookupResult::Parent(None), }; - if !place.elems.is_empty() { + if !place.has_no_projection() { for elem in place.elems.iter() { result = match result { LookupResult::Exact(base_path) => { From fea2e88240ed36a236b544dbbda57ca94f6bed57 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Tue, 28 Aug 2018 22:21:36 +0800 Subject: [PATCH 18/33] Rewrite place workflow in rustc_mir/transform --- src/librustc_mir/transform/add_validation.rs | 97 +++++++++---------- src/librustc_mir/transform/check_unsafety.rs | 77 ++++++++------- src/librustc_mir/transform/const_prop.rs | 20 ++-- src/librustc_mir/transform/copy_prop.rs | 25 +++-- src/librustc_mir/transform/generator.rs | 21 +++- src/librustc_mir/transform/inline.rs | 43 ++++---- src/librustc_mir/transform/instcombine.rs | 9 +- src/librustc_mir/transform/promote_consts.rs | 21 ++-- src/librustc_mir/transform/qualify_consts.rs | 75 +++++++------- .../transform/remove_noop_landing_pads.rs | 4 +- src/librustc_mir/transform/rustc_peek.rs | 2 +- .../transform/uniform_array_move_out.rs | 6 +- 12 files changed, 228 insertions(+), 172 deletions(-) diff --git a/src/librustc_mir/transform/add_validation.rs b/src/librustc_mir/transform/add_validation.rs index 8c9358b73c050..9834b3465845f 100644 --- a/src/librustc_mir/transform/add_validation.rs +++ b/src/librustc_mir/transform/add_validation.rs @@ -30,58 +30,57 @@ fn place_context<'a, 'tcx, D>( ) -> (Option, hir::Mutability) where D: HasLocalDecls<'tcx> { - if let (base_place, Some(projection)) = place.final_projection(tcx){ - match projection { - ProjectionElem::Deref => { - // Computing the inside the recursion makes this quadratic. - // We don't expect deep paths though. - let ty = base_place.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) + let mut place_context = match place.base { + PlaceBase::Local { .. } => (None, hir::MutMutable), + PlaceBase::Promoted(_) + | PlaceBase::Static(_) => (None, hir::MutImmutable), + }; + + if !place.has_no_projection() { + 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); } - 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(&place.projection_base(tcx), 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); - (re, mutbl) - } - }, - _ => place_context(&base_place, local_decls, tcx), - } - } else { - match place.base { - PlaceBase::Local { .. } => (None, hir::MutMutable), - PlaceBase::Promoted(_) - | PlaceBase::Static(_) => (None, hir::MutImmutable), + + }, + _ => {}, + } + base_ty = tcx::PlaceTy::from(base_ty) + .projection_ty(tcx, elem) + .to_ty(tcx); + base_context = place_context; } } - // let mut place_context = match place.base { - // PlaceBase::Local { .. } => (None, hir::MutMutable), - // PlaceBase::Promoted(_) - // | PlaceBase::Static(_) => (None, hir::MutImmutable), - // }; + place_context } /// Check if this function contains an unsafe block or is an unsafe function. diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index bbd815ff1e673..4e196352e4158 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -164,7 +164,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { } } - if let (base_place, Some(projection)) = place.final_projection(self.tcx) { + 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 { @@ -174,45 +174,50 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { self.source_info = self.mir.local_decls[local].source_info; } } - let base_ty = base_place.ty(self.mir, self.tcx).to_ty(self.tcx); - 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") - } - ty::TyAdt(adt, _) => { - if adt.is_union() { - if context == PlaceContext::Store || - context == PlaceContext::AsmOutput || - context == PlaceContext::Drop - { - let projection_ty = match projection { - 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") + 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") + } + ty::TyAdt(adt, _) => { + if adt.is_union() { + if context == PlaceContext::Store || + context == PlaceContext::AsmOutput || + context == PlaceContext::Drop + { + 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") + self.require_unsafe("access to union field", + "the field may not be properly initialized: \ + using uninitialized data will cause \ + undefined behavior") + } } } + _ => {} } - _ => {} + base_ty = tcx::PlaceTy::from(base_ty) + .projection_ty(self.tcx, elem).to_ty(self.tcx); } self.source_info = old_source_info; } else { diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index ebe26881f6e98..74ce603464e34 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -305,7 +305,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { _ => None, }; - if !place.elems.is_empty() { + if !place.has_no_projection() { for elem in place.elems.iter() { if let ProjectionElem::Field(field, _) = elem { trace!("field projection on {:?}", place); @@ -558,12 +558,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 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); + 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); + } } } } @@ -589,7 +591,9 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { match cond { Operand::Move(place) | Operand::Copy(place) => { if let PlaceBase::Local(local) = place.base { - self.places[local] = None; + 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 1d51445b47836..e08939f788550 100644 --- a/src/librustc_mir/transform/copy_prop.rs +++ b/src/librustc_mir/transform/copy_prop.rs @@ -108,10 +108,11 @@ impl MirPass for CopyPropagation { StatementKind::Assign( Place { base: PlaceBase::Local(local), - elems: _, + elems, }, Rvalue::Use(ref operand) - ) if local == dest_local => { + ) if local == dest_local + && elems.is_empty() => { let maybe_action = match *operand { Operand::Copy(ref src_place) | Operand::Move(ref src_place) => { @@ -162,13 +163,13 @@ fn eliminate_self_assignments<'tcx>( StatementKind::Assign( Place { base: PlaceBase::Local(local), - elems: _, + elems: elems1, }, Rvalue::Use( Operand::Copy( Place { base: PlaceBase::Local(src_local), - elems: _, + elems: elems2, } ) ), @@ -176,17 +177,20 @@ fn eliminate_self_assignments<'tcx>( StatementKind::Assign( Place { base: PlaceBase::Local(local), - elems: _, + elems: elems1, }, Rvalue::Use( Operand::Move( Place { base: PlaceBase::Local(src_local), - elems: _, + elems: elems2, } ) ), - ) if local == dest_local && dest_local == src_local => {} + ) if local == dest_local + && dest_local == src_local + && elems1.is_empty() + && elems2.is_empty() => {} _ => { continue; } @@ -216,7 +220,12 @@ impl<'tcx> Action<'tcx> { ) -> Option> { // The source must be a local. let src_local = if let PlaceBase::Local(local) = src_place.base { - local + 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; diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index d80a630210ca5..d8e4ee36e9661 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -113,7 +113,8 @@ impl<'a, 'tcx> MutVisitor<'tcx> for DerefArgVisitor<'a, 'tcx> { place: &mut Place<'tcx>, context: PlaceContext<'tcx>, location: Location) { - if place.base == PlaceBase::Local(self_arg()) { + if place.base == PlaceBase::Local(self_arg()) + && place.has_no_projection() { *place = place.clone().deref(self.tcx); } else { self.super_place(place, context, location); @@ -199,8 +200,10 @@ impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> { location: Location) { 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); @@ -340,6 +343,15 @@ impl<'tcx> BorrowedLocals { PlaceBase::Local(l) => { self.0.add(&l); } _ => (), } + if !place.has_no_projection() { + for elem in place.elems.iter() { + if let ProjectionElem::Deref = elem { + (); + } else { + continue; + } + } + } } } @@ -586,7 +598,8 @@ fn elaborate_generator_drops<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, target, unwind } - } if PlaceBase::Local(gen) == place.base => (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 { diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index 65f7881eab882..4d983bc3ccd87 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -425,14 +425,14 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { } else { false }; - if !place.elems.is_empty() { + if !place.has_no_projection() { for elem in place.elems.iter() { match elem { ProjectionElem::Deref | ProjectionElem::Index(_) => { dest_needs_borrow = true; } - _ => continue, + _ => {}, } } } @@ -586,9 +586,11 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { if let Operand::Move(ref place) = arg { if let PlaceBase::Local(local) = place.base { - if caller_mir.local_kind(local) == LocalKind::Temp { - // Reuse the operand if it's a temporary already - return local; + if place.has_no_projection() { + if caller_mir.local_kind(local) == LocalKind::Temp { + // Reuse the operand if it's a temporary already + return local; + } } } } @@ -652,7 +654,8 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { _location: Location) { if *local == RETURN_PLACE { match self.destination.base { - PlaceBase::Local(l) => { + PlaceBase::Local(l) + if self.destination.has_no_projection() => { *local = l; return; }, @@ -671,19 +674,23 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { place: &mut Place<'tcx>, _ctxt: PlaceContext<'tcx>, _location: Location) { - - 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), + 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 00bfa11ed5607..f000c23e9b1db 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; @@ -92,13 +93,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(_, _, ref place) = *rvalue { - if !place.elems.is_empty() { + if !place.has_no_projection() { + let mut base_ty = place.base.ty(self.mir); for elem in place.elems.iter() { if let ProjectionElem::Deref = elem { - if place.ty(self.mir, self.tcx).to_ty(self.tcx).is_region_ptr() { + 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/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 746d8dc1e03e6..04677e8b6ffc9 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -305,7 +305,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { StatementKind::Assign(_, Rvalue::Ref(_, _, ref place)) => { // Find the underlying local for this (necessarilly interior) borrow. let mut place = place.clone(); - if !place.elems.is_empty() { + if !place.has_no_projection() { // for Place: // Base.[a, b, Deref, c] // ^^^^^^^^^^ ^^^^^ ^ @@ -381,9 +381,11 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, match mir[block].statements[statement_index].kind { StatementKind::Assign(ref place, _) => { if let PlaceBase::Local(local) = place.base { - if temps[local] == TempState::PromotedOut { - // Already promoted. - continue; + if place.has_no_projection() { + if temps[local] == TempState::PromotedOut { + // Already promoted. + continue; + } } } } @@ -429,9 +431,12 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, StatementKind::Assign( Place { base: PlaceBase::Local(index), - elems: _, + elems, }, - _, ) | + _, + ) if elems.is_empty() => { + !promoted(index) + }, StatementKind::StorageLive(index) | StatementKind::StorageDead(index) => { !promoted(index) @@ -444,11 +449,11 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, TerminatorKind::Drop { location: Place { base: PlaceBase::Local(index), - elems: _, + 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 8594a43967daa..2607d7a478aa0 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -255,13 +255,12 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { // 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 { + // 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 { @@ -401,10 +400,10 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { _, _, Place { base: PlaceBase::Local(index), - elems: _, + elems, } ) - ) => { + ) if elems.is_empty() => { promoted_temps.add(&index); } _ => {} @@ -578,9 +577,11 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { // Mark the consumed locals to indicate later drops are noops. if let Operand::Move(ref place) = *operand { if let PlaceBase::Local(local) = place.base { - self.local_qualif[local] = self.local_qualif[local].map(|q| - q - Qualif::NEEDS_DROP - ); + if place.has_no_projection() { + self.local_qualif[local] = self.local_qualif[local].map(|q| + q - Qualif::NEEDS_DROP + ); + } } } } @@ -718,7 +719,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { let candidate = Candidate::Ref(location); // We can only promote interior borrows of promotable temps. let mut place = place.clone(); - if !place.elems.is_empty() { + if !place.has_no_projection() { for (i, elem) in place.elems.iter().cloned().enumerate().rev() { match elem { ProjectionElem::Deref => break, @@ -728,17 +729,18 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } } } - } - if let PlaceBase::Local(local) = place.base { - if self.mir.local_kind(local) == LocalKind::Temp { - if let Some(qualif) = self.local_qualif[local] { - // `forbidden_mut` is false, so we can safely ignore - // `MUTABLE_INTERIOR` from the local's qualifications. - // This allows borrowing fields which don't have - // `MUTABLE_INTERIOR`, from a type that does, e.g.: - // `let _: &'static _ = &(Cell::new(1), 2).1;` - if (qualif - Qualif::MUTABLE_INTERIOR).is_empty() { - self.promotion_candidates.push(candidate); + } else { + if let PlaceBase::Local(local) = place.base { + if self.mir.local_kind(local) == LocalKind::Temp { + if let Some(qualif) = self.local_qualif[local] { + // `forbidden_mut` is false, so we can safely ignore + // `MUTABLE_INTERIOR` from the local's qualifications. + // This allows borrowing fields which don't have + // `MUTABLE_INTERIOR`, from a type that does, e.g.: + // `let _: &'static _ = &(Cell::new(1), 2).1;` + if (qualif - Qualif::MUTABLE_INTERIOR).is_empty() { + self.promotion_candidates.push(candidate); + } } } } @@ -987,11 +989,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 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) + 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) @@ -1026,7 +1034,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { 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 { + !self.tcx.sess.features_untracked().const_let && + dest.has_no_projection() { // Direct use of an argument is permitted. match *rvalue { @@ -1034,7 +1043,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { Operand::Copy( Place { base: PlaceBase::Local(local), - elems: _, + elems, } ) ) | @@ -1042,10 +1051,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { Operand::Move( Place { base: PlaceBase::Local(local), - elems: _, + elems, } ) - ) => { + ) => if elems.is_empty() { if self.mir.local_kind(local) == LocalKind::Arg { return; } @@ -1220,10 +1229,10 @@ impl MirPass for QualifyAndPromoteConstants { location: Place { base: PlaceBase::Local(index), - elems: _, + 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 8f2bfb26b5a63..ed1309372d7e4 100644 --- a/src/librustc_mir/transform/remove_noop_landing_pads.rs +++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs @@ -63,10 +63,10 @@ impl RemoveNoopLandingPads { StatementKind::Assign( Place { base: PlaceBase::Local(_), - elems: _, + 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 4e22fd8e9f8b3..0b9f91cd5e70b 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/src/librustc_mir/transform/rustc_peek.rs @@ -135,7 +135,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, base: PlaceBase::Local(_), elems: _, } - ) => Some(place), + ) 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 7cd8311492c25..4de020c202df2 100644 --- a/src/librustc_mir/transform/uniform_array_move_out.rs +++ b/src/librustc_mir/transform/uniform_array_move_out.rs @@ -1,5 +1,5 @@ -// Copyright 2015 The Rust Project Developers. See -// the COPYRIGHT file at the top-level directory of this distribution and at +// 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. // // Licensed under the Apache License, Version 2.0 Date: Tue, 28 Aug 2018 22:22:12 +0800 Subject: [PATCH 19/33] Rewrite place workflow in rustc_codegen_llvm --- src/librustc_codegen_llvm/mir/analyze.rs | 18 ++++++++---------- src/librustc_codegen_llvm/mir/operand.rs | 2 +- src/librustc_codegen_llvm/mir/place.rs | 12 +++++++----- src/librustc_codegen_llvm/mir/rvalue.rs | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/librustc_codegen_llvm/mir/analyze.rs b/src/librustc_codegen_llvm/mir/analyze.rs index a443e08ed2511..c479c2edeaf45 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; @@ -162,8 +163,9 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> { location: Location) { let cx = self.fx.cx; - if !place.elems.is_empty() { - for (i, elem) in place.elems.iter().cloned().enumerate().rev() { + if !place.has_no_projection() { + let mut base_ty = place.base.ty(self.fx.mir); + for elem in place.elems.iter().cloned() { debug!("visit_place(place={:?}, context={:?})", place, context); // Allow uses of projections that are ZSTs or from scalar fields. @@ -172,20 +174,15 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> { _ => false }; - let base = place.elem_base(cx.tcx, i); if is_consume { - let base_ty = 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, &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(..) = elem { - let layout = cx.layout_of(base_ty.to_ty(cx.tcx)); + 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, @@ -200,6 +197,7 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> { context = PlaceContext::Copy; continue; } + base_ty = PlaceTy::from(base_ty).projection_ty(cx.tcx, &elem).to_ty(cx.tcx); } } diff --git a/src/librustc_codegen_llvm/mir/operand.rs b/src/librustc_codegen_llvm/mir/operand.rs index ae80aeabacd96..b573e41b3ae32 100644 --- a/src/librustc_codegen_llvm/mir/operand.rs +++ b/src/librustc_codegen_llvm/mir/operand.rs @@ -313,7 +313,7 @@ impl FunctionCx<'a, 'll, 'tcx> { let mut result = None; // watch out for locals that do not have an // alloca; they are handled somewhat differently - if place.elems.is_empty() { + if place.has_no_projection() { if let mir::PlaceBase::Local(index) = place.base { match self.locals[index] { LocalRef::Operand(Some(o)) => { diff --git a/src/librustc_codegen_llvm/mir/place.rs b/src/librustc_codegen_llvm/mir/place.rs index 1d065c016eddb..ede7687e3f5eb 100644 --- a/src/librustc_codegen_llvm/mir/place.rs +++ b/src/librustc_codegen_llvm/mir/place.rs @@ -417,10 +417,8 @@ impl FunctionCx<'a, 'll, 'tcx> { debug!("codegen_place(place={:?})", place); let cx = bx.cx; - let tcx = cx.tcx; - - let mut result = match place.base { - mir::PlaceBase::Local(index) => { + if let mir::PlaceBase::Local(index) = place.base { + if place.has_no_projection() { match self.locals[index] { LocalRef::Place(place) => { return place; @@ -430,6 +428,9 @@ impl FunctionCx<'a, 'll, 'tcx> { } } } + }; + 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 { @@ -462,7 +463,8 @@ impl FunctionCx<'a, 'll, 'tcx> { } }; - if !place.elems.is_empty() { + if !place.has_no_projection() { + let tcx = cx.tcx; for (i, elem) in place.elems.iter().cloned().enumerate().rev() { let base = place.elem_base(tcx, i); result = match elem { diff --git a/src/librustc_codegen_llvm/mir/rvalue.rs b/src/librustc_codegen_llvm/mir/rvalue.rs index e3f601e7c36d7..2190cd9ae4f33 100644 --- a/src/librustc_codegen_llvm/mir/rvalue.rs +++ b/src/librustc_codegen_llvm/mir/rvalue.rs @@ -518,7 +518,7 @@ 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 place.elems.is_empty() { + 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 { From 73e86a211c8479a354d936595702391860644a56 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Tue, 28 Aug 2018 22:22:33 +0800 Subject: [PATCH 20/33] Rewrite place workflow in rustc_mir/build --- src/librustc_mir/build/expr/as_rvalue.rs | 92 ++++++++++++------------ src/librustc_mir/build/scope.rs | 16 +++-- 2 files changed, 57 insertions(+), 51 deletions(-) diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index fc24ad48f7fa7..9fd606f838daa 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -428,7 +428,6 @@ 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)); @@ -439,26 +438,32 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let arg_place = unpack!(block = this.as_place(block, arg)); - let mutability = if let (base_place, Some(projection)) = arg_place.final_projection(tcx) { - match projection { - ProjectionElem::Deref => { - 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 if let (base_place, Some(projection)) = base_place.final_projection(tcx) - { - if let ProjectionElem::Field(upvar_index, _) = projection { + let mut mutability = if let PlaceBase::Local(local) = arg_place.base { + this.local_decls[local].mutability + } else { + bug!("Unexpected capture place"); + }; + if !arg_place.has_no_projection() { + let mut proj_iter = arg_place.elems.iter().cloned().rev(); + if let Some(projection) = proj_iter.next() { + match projection { + ProjectionElem::Deref => { + if let PlaceBase::Local(local) = arg_place.base { + debug_assert!( + if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) + = this.local_decls[local].is_user_variable { + true + } else { + false + }, + "Unexpected capture place", + ); + } + + if let Some(ProjectionElem::Field(upvar_index, _)) = proj_iter.next() { // Not projected from the implicit `self` in a closure. debug_assert!( - match base_place.base { + match arg_place.base { PlaceBase::Local(local) => local == Local::new(1), _ => false, }, @@ -469,41 +474,34 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.upvar_decls.len() > upvar_index.index(), "Unexpected capture place" ); - this.upvar_decls[upvar_index.index()].mutability + mutability = this.upvar_decls[upvar_index.index()].mutability; } else { bug!("Unexpected capture place"); } - } else { + }, + ProjectionElem::Field(upvar_index, _) => { + // Not projected from the implicit `self` in a closure. + debug_assert!( + match arg_place.base { + PlaceBase::Local(local) => local == Local::new(1), + _ => false, + }, + "Unexpected capture place" + ); + // Not in a closure + debug_assert!( + this.upvar_decls.len() > upvar_index.index(), + "Unexpected capture place" + ); + mutability = this.upvar_decls[upvar_index.index()].mutability; + }, + _ => { bug!("Unexpected capture place"); } - }, - ProjectionElem::Field(upvar_index, _) => { - // Not projected from the implicit `self` in a closure. - debug_assert!( - match base_place.base { - PlaceBase::Local(local) => 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 - } - _ => { - bug!("Unexpected capture place"); } } - } else { - if let PlaceBase::Local(local) = arg_place.base { - this.local_decls[local].mutability - } else { - bug!("Unexpected capture place"); - } - }; + } + let borrow_kind = match mutability { Mutability::Not => BorrowKind::Unique, Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false }, diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 87410825cbf34..0b9266c85fb81 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -660,8 +660,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { match drop_kind { DropKind::Value { .. } => if !needs_drop { return }, DropKind::Storage => { - match place.base { - PlaceBase::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 {}", @@ -938,8 +942,12 @@ 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.base { - PlaceBase::Local(index) if index.index() > arg_count => { + match drop_data.location { + Place { + base: PlaceBase::Local(index), + elems, + } if index.index() > arg_count + && elems.is_empty() => { cfg.push(block, Statement { source_info, kind: StatementKind::StorageDead(index) From 5aae53e3aff9d619ad28a9a837cdd09de995f109 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Tue, 28 Aug 2018 22:22:55 +0800 Subject: [PATCH 21/33] Introduce has_no_projection --- src/librustc/mir/tcx.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index d5a061db9de33..1e27199345d0a 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -136,7 +136,7 @@ impl<'tcx> Place<'tcx> { let mut place_ty = PlaceTy::from(self.base.ty(local_decls)); // apply .projection_ty() to all elems but only returns the final one. - if !self.elems.is_empty() { + if !self.has_no_projection() { for elem in self.elems.iter() { place_ty = place_ty.projection_ty(tcx, elem); } @@ -207,6 +207,10 @@ impl<'tcx> Place<'tcx> { } } + 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 @@ -230,7 +234,7 @@ impl<'tcx> Place<'tcx> { elem_index: usize, ) -> Place<'tcx> { // only works for place with projections - assert!(!self.elems.is_empty()); + assert!(!self.has_no_projection()); if elem_index < 1 { // Base.[a] From 9ec03b37b0434b1052319af3e3073d3c2fef00e4 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Tue, 28 Aug 2018 22:23:34 +0800 Subject: [PATCH 22/33] Rewrite place workflow in rustc_passes --- src/librustc_passes/mir_stats.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index 886d75236dcf5..0892918914c0c 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -208,7 +208,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { PlaceBase::Promoted(..) => "PlaceBase::Promoted", }, place); - if !place.elems.is_empty() { + if !place.has_no_projection() { for elem in place.elems.iter() { self.visit_projection_elem( elem, From 2af0dd06254de0b0fcb57c20c4f56b119711904e Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Tue, 28 Aug 2018 22:24:01 +0800 Subject: [PATCH 23/33] Rewrite place workflow in rustc_mir/util --- src/librustc_mir/util/alignment.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/librustc_mir/util/alignment.rs b/src/librustc_mir/util/alignment.rs index 9a77f18f078e3..9fd42b6865a62 100644 --- a/src/librustc_mir/util/alignment.rs +++ b/src/librustc_mir/util/alignment.rs @@ -52,21 +52,21 @@ fn is_within_packed<'a, 'tcx, L>( where L: HasLocalDecls<'tcx>, { - if !place.elems.is_empty() { - for (i, elem) in place.elems.iter().cloned().enumerate().rev() { + if !place.has_no_projection() { + 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 base_place = place.elem_base(tcx, i); - let ty = base_place.ty(local_decls, tcx).to_ty(tcx); - match ty.sty { + match base_ty.sty { ty::TyAdt(def, _) if def.repr.packed() => return true, - _ => continue, + _ => {}, } } - _ => continue, + _ => {}, } + base_ty = tcx::PlaceTy::from(base_ty).projection_ty(tcx, elem).to_ty(tcx); } } From 6045a8a7e25b597e12aaa26eec8b70fee797c114 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 30 Aug 2018 09:32:49 -0400 Subject: [PATCH 24/33] move `PlaceContext::Copy` check later, once type is fully computed --- .../borrow_check/nll/type_check/mod.rs | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) 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 f8f23731fb8dc..6a6af736abd62 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -405,22 +405,6 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { PlaceTy::Ty { ty: sty } } }; - if let PlaceContext::Copy = context { - let tcx = self.tcx(); - let trait_ref = ty::TraitRef { - def_id: tcx.lang_items().copy_trait().unwrap(), - substs: tcx.mk_substs_trait(place_ty.to_ty(tcx), &[]), - }; - - // In order to have a Copy operand, the type T of the value must be Copy. Note that we - // prove that T: Copy, rather than using the type_moves_by_default test. This is - // important because type_moves_by_default ignores the resulting region obligations and - // assumes they pass. This can result in bounds from Copy impls being unsoundly ignored - // (e.g., #29149). Note that we decide to use Copy before knowing whether the bounds - // fully apply: in effect, the rule is that if a value of some type could implement - // Copy, then it must. - self.cx.prove_trait_ref(trait_ref, location.interesting()); - } if !place.has_no_projection() { for elem in place.elems.iter(){ @@ -442,6 +426,23 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { } }; + if let PlaceContext::Copy = context { + let tcx = self.tcx(); + let trait_ref = ty::TraitRef { + def_id: tcx.lang_items().copy_trait().unwrap(), + substs: tcx.mk_substs_trait(place_ty.to_ty(tcx), &[]), + }; + + // In order to have a Copy operand, the type T of the value must be Copy. Note that we + // prove that T: Copy, rather than using the type_moves_by_default test. This is + // important because type_moves_by_default ignores the resulting region obligations and + // assumes they pass. This can result in bounds from Copy impls being unsoundly ignored + // (e.g., #29149). Note that we decide to use Copy before knowing whether the bounds + // fully apply: in effect, the rule is that if a value of some type could implement + // Copy, then it must. + self.cx.prove_trait_ref(trait_ref, location.interesting()); + } + place_ty } From e912a265d2ca24f61fe0b83d95432369180f05ae Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Fri, 31 Aug 2018 09:10:23 +0800 Subject: [PATCH 25/33] Trip unneeded has_no_projection checking --- src/librustc/mir/tcx.rs | 6 +- src/librustc_codegen_llvm/mir/analyze.rs | 54 ++- src/librustc_codegen_llvm/mir/operand.rs | 63 ++-- src/librustc_codegen_llvm/mir/place.rs | 104 +++--- .../borrow_check/error_reporting.rs | 47 +-- src/librustc_mir/borrow_check/mod.rs | 316 +++++++++--------- .../borrow_check/nll/type_check/mod.rs | 172 +++++----- src/librustc_mir/borrow_check/path_utils.rs | 9 +- src/librustc_mir/borrow_check/place_ext.rs | 33 +- src/librustc_mir/build/expr/as_rvalue.rs | 75 ++--- .../dataflow/impls/borrowed_locals.rs | 12 +- .../dataflow/move_paths/builder.rs | 14 +- src/librustc_mir/dataflow/move_paths/mod.rs | 21 +- src/librustc_mir/interpret/place.rs | 29 +- src/librustc_mir/transform/add_validation.rs | 79 +++-- src/librustc_mir/transform/const_prop.rs | 24 +- src/librustc_mir/transform/generator.rs | 13 +- src/librustc_mir/transform/inline.rs | 18 +- src/librustc_mir/transform/instcombine.rs | 18 +- src/librustc_mir/transform/promote_consts.rs | 25 +- src/librustc_mir/transform/qualify_consts.rs | 43 ++- src/librustc_mir/util/alignment.rs | 24 +- src/librustc_passes/mir_stats.rs | 15 +- 23 files changed, 589 insertions(+), 625 deletions(-) diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index 1e27199345d0a..061f0f702dafc 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -136,10 +136,8 @@ impl<'tcx> Place<'tcx> { let mut place_ty = PlaceTy::from(self.base.ty(local_decls)); // apply .projection_ty() to all elems but only returns the final one. - if !self.has_no_projection() { - for elem in self.elems.iter() { - place_ty = place_ty.projection_ty(tcx, elem); - } + for elem in self.elems.iter() { + place_ty = place_ty.projection_ty(tcx, elem); } place_ty diff --git a/src/librustc_codegen_llvm/mir/analyze.rs b/src/librustc_codegen_llvm/mir/analyze.rs index c479c2edeaf45..a40c8381fee20 100644 --- a/src/librustc_codegen_llvm/mir/analyze.rs +++ b/src/librustc_codegen_llvm/mir/analyze.rs @@ -162,43 +162,41 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> { mut context: PlaceContext<'tcx>, location: Location) { let cx = self.fx.cx; + let mut base_ty = place.base.ty(self.fx.mir); - if !place.has_no_projection() { - let mut base_ty = place.base.ty(self.fx.mir); - for elem in place.elems.iter().cloned() { - debug!("visit_place(place={:?}, context={:?})", place, context); + for elem in place.elems.iter().cloned() { + debug!("visit_place(place={:?}, context={:?})", place, context); - // Allow uses of projections that are ZSTs or from scalar fields. - let is_consume = match context { - PlaceContext::Copy | PlaceContext::Move => true, - _ => false - }; + // 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 { - base_ty = self.fx.monomorphize(&base_ty); + if is_consume { + base_ty = self.fx.monomorphize(&base_ty); - if cx.layout_of(base_ty).is_zst() { - return; - } + if cx.layout_of(base_ty).is_zst() { + return; + } - 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. - continue; - } + 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. + continue; } } + } - // A deref projection only reads the pointer, never needs the place. - 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); + // A deref projection only reads the pointer, never needs the place. + 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); diff --git a/src/librustc_codegen_llvm/mir/operand.rs b/src/librustc_codegen_llvm/mir/operand.rs index b573e41b3ae32..e4fcdc6999211 100644 --- a/src/librustc_codegen_llvm/mir/operand.rs +++ b/src/librustc_codegen_llvm/mir/operand.rs @@ -313,43 +313,42 @@ impl FunctionCx<'a, 'll, 'tcx> { let mut result = None; // watch out for locals that do not have an // alloca; they are handled somewhat differently - if place.has_no_projection() { - if let mir::PlaceBase::Local(index) = place.base { - match self.locals[index] { - LocalRef::Operand(Some(o)) => { - result = Some(o); - } - LocalRef::Operand(None) => { - bug!("use of {:?} before def", place); + if let mir::PlaceBase::Local(index) = place.base { + match self.locals[index] { + LocalRef::Operand(Some(o)) => { + result = Some(o); + } + LocalRef::Operand(None) => { + bug!("use of {:?} before def", place); + } + LocalRef::Place(..) => { + // use path below + } + }; + } + + // Moves out of scalar and scalar pair fields are trivial. + for e in place.elems.iter() { + if let Some(o) = result { + match e { + mir::ProjectionElem::Field(ref f, _) => { + result = Some(o.extract_field(bx, f.index())); } - LocalRef::Place(..) => { - // use path below + mir::ProjectionElem::Index(_) | + mir::ProjectionElem::ConstantIndex { .. } => { + // ZSTs don't require any actual memory access. + // FIXME(eddyb) deduplicate this with the identical + // checks in `codegen_consume` and `extract_field`. + let elem = o.layout.field(bx.cx, 0); + if elem.is_zst() { + result = Some(OperandRef::new_zst(bx.cx, elem)); + } } + _ => {} }; } - } else { - // Moves out of scalar and scalar pair fields are trivial. - for e in place.elems.iter() { - if let Some(o) = result { - match e { - mir::ProjectionElem::Field(ref f, _) => { - result = Some(o.extract_field(bx, f.index())); - } - mir::ProjectionElem::Index(_) | - mir::ProjectionElem::ConstantIndex { .. } => { - // ZSTs don't require any actual memory access. - // FIXME(eddyb) deduplicate this with the identical - // checks in `codegen_consume` and `extract_field`. - let elem = o.layout.field(bx.cx, 0); - if elem.is_zst() { - result = Some(OperandRef::new_zst(bx.cx, elem)); - } - } - _ => {} - }; - } - } } + result } diff --git a/src/librustc_codegen_llvm/mir/place.rs b/src/librustc_codegen_llvm/mir/place.rs index ede7687e3f5eb..3bd8cd3b9768f 100644 --- a/src/librustc_codegen_llvm/mir/place.rs +++ b/src/librustc_codegen_llvm/mir/place.rs @@ -463,62 +463,60 @@ impl FunctionCx<'a, 'll, 'tcx> { } }; - if !place.has_no_projection() { - 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) + 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 = 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)))); - } - // 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) => { - result.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_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index 588d502fde607..0424bf0f5202a 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -634,11 +634,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { including_downcast: &IncludingDowncast, ) -> Result<(), ()> { self.append_place_base_to_string(&place.base, buf)?; - if !place.has_no_projection() { - for elem in place.elems.iter() { - self.append_place_projection_to_string(place, elem, buf, including_downcast)?; - } + + for elem in place.elems.iter() { + self.append_place_projection_to_string( + place, + elem, + buf, + including_downcast + )?; } + Ok(()) } @@ -738,24 +743,24 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { PlaceBase::Promoted(ref prom) => self.describe_field_from_ty(&prom.1, field), PlaceBase::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field), }; - if !place.has_no_projection() { - 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 - ).to_string(), - ProjectionElem::Field(_, field_type) => { - self.describe_field_from_ty(&field_type, field).to_string() - } - ProjectionElem::Index(..) - | ProjectionElem::Deref - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => continue, - }; - string.push_str(proj_str.as_str()); - } + + 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 + ).to_string(), + ProjectionElem::Field(_, field_type) => { + self.describe_field_from_ty(&field_type, field).to_string() + } + ProjectionElem::Index(..) + | ProjectionElem::Deref + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } => continue, + }; + string.push_str(proj_str.as_str()); } + string } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 8cdd44726e513..d69f7cfe6b1f7 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -1776,12 +1776,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { place: &Place<'tcx>, ) -> Result { let mut prefix = place.clone(); - if !prefix.has_no_projection() { - 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); - } + 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); } } match place.base { @@ -1809,58 +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); - if !place.has_no_projection() { - 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; - } - _ => {} + 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; } + _ => {} } - } + } } } } @@ -2075,111 +2071,109 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { is_local_mutation_allowed ); - if !place.has_no_projection() { - 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 - } => - { - is_local_mutation_allowed - } - _ => LocalMutationIsAllowed::Yes, - }; - continue - } - } - } - 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, - }); - } + 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 + } => + { + is_local_mutation_allowed + } + _ => LocalMutationIsAllowed::Yes, + }; + continue } } - // `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 { + 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 { - base_is_mutable(&place.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) } } } @@ -2225,15 +2219,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let mut deepest = place.clone(); - if !place.has_no_projection() { - 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); - }, - _ => {}, - } + 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); + }, + _ => {}, } } 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 6a6af736abd62..5481e98dc352c 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -406,25 +406,23 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { } }; - if !place.has_no_projection() { - 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); - place_ty = PlaceTy::Ty { - ty: self.tcx().types.err, - }; - break; - } + 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); + place_ty = PlaceTy::Ty { + ty: self.tcx().types.err, + }; + break; } - place_ty = self.sanitize_projection(place_ty, elem, place, location); } - }; + place_ty = self.sanitize_projection(place_ty, elem, place, location); + } if let PlaceContext::Copy = context { let tcx = self.tcx(); @@ -1603,84 +1601,82 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { location, borrow_region, borrowed_place ); - if borrowed_place.has_no_projection() { - 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 => { - - debug!("add_reborrow_constraint - base_ty = {:?}", base_ty); - match base_ty.sty { - ty::TyRef(ref_region, _, mutbl) => { - constraints.outlives_constraints.push(OutlivesConstraint { - sup: ref_region.to_region_vid(), - sub: borrow_region.to_region_vid(), - locations: location.boring(), - }); - - if let Some(all_facts) = all_facts { - all_facts.outlives.push(( - ref_region.to_region_vid(), - borrow_region.to_region_vid(), - location_table.mid_index(location), - )); - } + 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 => { + + debug!("add_reborrow_constraint - base_ty = {:?}", base_ty); + match base_ty.sty { + ty::TyRef(ref_region, _, mutbl) => { + constraints.outlives_constraints.push(OutlivesConstraint { + sup: ref_region.to_region_vid(), + sub: borrow_region.to_region_vid(), + locations: location.boring(), + }); + + if let Some(all_facts) = all_facts { + all_facts.outlives.push(( + ref_region.to_region_vid(), + borrow_region.to_region_vid(), + location_table.mid_index(location), + )); + } - match mutbl { - hir::Mutability::MutImmutable => { - // Immutable reference. We don't need the base - // to be valid for the entire lifetime of - // the borrow. - break; - } - hir::Mutability::MutMutable => { - // Mutable reference. We *do* need the base - // to be valid, because after the base becomes - // invalid, someone else can use our mutable deref. - - // This is in order to make the following function - // illegal: - // ``` - // fn unsafe_deref<'a, 'b>(x: &'a &'b mut T) -> &'b mut T { - // &mut *x - // } - // ``` - // - // As otherwise you could clone `&mut T` using the - // following function: - // ``` - // fn bad(x: &mut T) -> (&mut T, &mut T) { - // let my_clone = unsafe_deref(&'a x); - // ENDREGION 'a; - // (my_clone, x) - // } - // ``` - } + match mutbl { + hir::Mutability::MutImmutable => { + // Immutable reference. We don't need the base + // to be valid for the entire lifetime of + // the borrow. + break; + } + hir::Mutability::MutMutable => { + // Mutable reference. We *do* need the base + // to be valid, because after the base becomes + // invalid, someone else can use our mutable deref. + + // This is in order to make the following function + // illegal: + // ``` + // fn unsafe_deref<'a, 'b>(x: &'a &'b mut T) -> &'b mut T { + // &mut *x + // } + // ``` + // + // As otherwise you could clone `&mut T` using the + // following function: + // ``` + // fn bad(x: &mut T) -> (&mut T, &mut T) { + // let my_clone = unsafe_deref(&'a x); + // ENDREGION 'a; + // (my_clone, x) + // } + // ``` } } - ty::TyRawPtr(..) => { - // deref of raw pointer, guaranteed to be valid - break; - } - ty::TyAdt(def, _) if def.is_box() => { - // deref of `Box`, need the base to be valid - propagate - } - _ => bug!("unexpected deref ty {:?} in {:?}", base_ty, borrowed_place), } + ty::TyRawPtr(..) => { + // deref of raw pointer, guaranteed to be valid + break; + } + ty::TyAdt(def, _) if def.is_box() => { + // deref of `Box`, need the base to be valid - propagate + } + _ => bug!("unexpected deref ty {:?} in {:?}", base_ty, borrowed_place), } - ProjectionElem::Field(..) - | ProjectionElem::Downcast(..) - | ProjectionElem::Index(..) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => { - // other field access - } } - base_ty = PlaceTy::from(base_ty) - .projection_ty(tcx, elem).to_ty(tcx); + ProjectionElem::Field(..) + | ProjectionElem::Downcast(..) + | ProjectionElem::Index(..) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } => { + // other field access + } } + 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 048cdc6042ca2..3f01f390232d7 100644 --- a/src/librustc_mir/borrow_check/path_utils.rs +++ b/src/librustc_mir/borrow_check/path_utils.rs @@ -147,12 +147,11 @@ pub(super) fn borrow_of_local_data<'tcx>( PlaceBase::Local(..) => true, }; - if !place.has_no_projection() { - for elem in place.elems.iter().cloned().rev() { - if elem == ProjectionElem::Deref { - borrow_of_data = false; - } + 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 d0b73cc10a930..3b6dc307114d6 100644 --- a/src/librustc_mir/borrow_check/place_ext.rs +++ b/src/librustc_mir/borrow_check/place_ext.rs @@ -33,24 +33,23 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable) } }; - if !self.has_no_projection() { - 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(_) => continue, - ProjectionElem::Deref => { - match base_ty.sty { - ty::TyRawPtr(..) => true, - _ => continue, - } + + 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(_) => continue, + ProjectionElem::Deref => { + match base_ty.sty { + ty::TyRawPtr(..) => true, + _ => continue, } - }; - base_ty = PlaceTy::from(base_ty).projection_ty(tcx, elem).to_ty(tcx); - } + } + }; + base_ty = PlaceTy::from(base_ty).projection_ty(tcx, elem).to_ty(tcx); } is_unsafe_place diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 9fd606f838daa..0492ba1d1f667 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -443,43 +443,24 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } else { bug!("Unexpected capture place"); }; - if !arg_place.has_no_projection() { - let mut proj_iter = arg_place.elems.iter().cloned().rev(); - if let Some(projection) = proj_iter.next() { - match projection { - ProjectionElem::Deref => { - if let PlaceBase::Local(local) = arg_place.base { - debug_assert!( - if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) - = this.local_decls[local].is_user_variable { - true - } else { - false - }, - "Unexpected capture place", - ); - } - if let Some(ProjectionElem::Field(upvar_index, _)) = proj_iter.next() { - // Not projected from the implicit `self` in a closure. - debug_assert!( - match arg_place.base { - PlaceBase::Local(local) => local == Local::new(1), - _ => false, - }, - "Unexpected capture place" - ); - // Not in a closure - debug_assert!( - this.upvar_decls.len() > upvar_index.index(), - "Unexpected capture place" - ); - mutability = this.upvar_decls[upvar_index.index()].mutability; - } else { - bug!("Unexpected capture place"); - } - }, - ProjectionElem::Field(upvar_index, _) => { + let mut proj_iter = arg_place.elems.iter().cloned().rev(); + if let Some(projection) = proj_iter.next() { + match projection { + ProjectionElem::Deref => { + if let PlaceBase::Local(local) = arg_place.base { + debug_assert!( + if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) + = this.local_decls[local].is_user_variable { + true + } else { + false + }, + "Unexpected capture place", + ); + } + + if let Some(ProjectionElem::Field(upvar_index, _)) = proj_iter.next() { // Not projected from the implicit `self` in a closure. debug_assert!( match arg_place.base { @@ -494,10 +475,28 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { "Unexpected capture place" ); mutability = this.upvar_decls[upvar_index.index()].mutability; - }, - _ => { + } else { bug!("Unexpected capture place"); } + }, + ProjectionElem::Field(upvar_index, _) => { + // Not projected from the implicit `self` in a closure. + debug_assert!( + match arg_place.base { + PlaceBase::Local(local) => local == Local::new(1), + _ => false, + }, + "Unexpected capture place" + ); + // Not in a closure + debug_assert!( + this.upvar_decls.len() > upvar_index.index(), + "Unexpected capture place" + ); + mutability = this.upvar_decls[upvar_index.index()].mutability; + }, + _ => { + bug!("Unexpected capture place"); } } } diff --git a/src/librustc_mir/dataflow/impls/borrowed_locals.rs b/src/librustc_mir/dataflow/impls/borrowed_locals.rs index 934e5f3d342db..845848cd06f69 100644 --- a/src/librustc_mir/dataflow/impls/borrowed_locals.rs +++ b/src/librustc_mir/dataflow/impls/borrowed_locals.rs @@ -105,14 +105,14 @@ impl<'b, 'c, 'tcx> BorrowedLocalsVisitor<'b, 'c> { PlaceBase::Local(local) => Some(local), PlaceBase::Promoted(_) | PlaceBase::Static(..) => None, }; - if !place.has_no_projection() { - for elem in place.elems.iter() { - match elem { - ProjectionElem::Deref => local = None, - _ => {}, - } + + for elem in place.elems.iter() { + match elem { + ProjectionElem::Deref => local = None, + _ => {}, } } + local } } diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 562878c13a525..9c16539aff6ab 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -113,14 +113,12 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { return Err(MoveError::cannot_move_out_of(self.loc, Static)), }; - if !place.has_no_projection() { - 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); - } + 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) diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 6151ac75098fe..ce340fcf2bbe7 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -255,18 +255,17 @@ impl<'tcx> MovePathLookup<'tcx> { PlaceBase::Promoted(_) | PlaceBase::Static(..) => LookupResult::Parent(None), }; - if !place.has_no_projection() { - 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)), - } + + 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 diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 2207764ca7cbc..928952e7126fc 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -105,14 +105,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { 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)?; - if !place.has_no_projection() { - 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)?; - } + 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) @@ -270,15 +269,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } }; - if !mir_place.has_no_projection() { - 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); - } - } + 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); diff --git a/src/librustc_mir/transform/add_validation.rs b/src/librustc_mir/transform/add_validation.rs index 9834b3465845f..4b9ae18c5f463 100644 --- a/src/librustc_mir/transform/add_validation.rs +++ b/src/librustc_mir/transform/add_validation.rs @@ -36,50 +36,49 @@ fn place_context<'a, 'tcx, D>( | PlaceBase::Static(_) => (None, hir::MutImmutable), }; - if !place.has_no_projection() { - 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); + 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); + } - }, - _ => {}, - } - base_ty = tcx::PlaceTy::from(base_ty) - .projection_ty(tcx, elem) - .to_ty(tcx); - base_context = place_context; + }, + _ => {}, } + base_ty = tcx::PlaceTy::from(base_ty) + .projection_ty(tcx, elem) + .to_ty(tcx); + base_context = place_context; } + place_context } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 74ce603464e34..08c3797305e13 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -305,19 +305,17 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { _ => None, }; - if !place.has_no_projection() { - 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; - } + 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; } } diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index d8e4ee36e9661..893d76336df74 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -343,13 +343,12 @@ impl<'tcx> BorrowedLocals { PlaceBase::Local(l) => { self.0.add(&l); } _ => (), } - if !place.has_no_projection() { - for elem in place.elems.iter() { - if let ProjectionElem::Deref = elem { - (); - } else { - continue; - } + + for elem in place.elems.iter() { + if let ProjectionElem::Deref = elem { + (); + } else { + continue; } } } diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index 4d983bc3ccd87..df0a7963ff251 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -425,16 +425,14 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { } else { false }; - if !place.has_no_projection() { - for elem in place.elems.iter() { - match elem { - ProjectionElem::Deref - | ProjectionElem::Index(_) => { - dest_needs_borrow = true; - } - _ => {}, - } - } + for elem in place.elems.iter() { + match elem { + ProjectionElem::Deref + | ProjectionElem::Index(_) => { + dest_needs_borrow = true; + } + _ => {}, + } } dest_needs_borrow } diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs index f000c23e9b1db..0e2dc72ceb23b 100644 --- a/src/librustc_mir/transform/instcombine.rs +++ b/src/librustc_mir/transform/instcombine.rs @@ -93,18 +93,16 @@ 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(_, _, ref place) = *rvalue { - if !place.has_no_projection() { - 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); - } + 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); } + base_ty = PlaceTy::from(base_ty) + .projection_ty(self.tcx, elem) + .to_ty(self.tcx); } } diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 04677e8b6ffc9..e311cc9c9a406 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -305,19 +305,18 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { StatementKind::Assign(_, Rvalue::Ref(_, _, ref place)) => { // Find the underlying local for this (necessarilly interior) borrow. let mut place = place.clone(); - if !place.has_no_projection() { - // 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; - } + + // 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; } } diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 2607d7a478aa0..636a0369c182f 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -717,31 +717,30 @@ 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.clone(); - if !place.has_no_projection() { - for (i, elem) in place.elems.iter().cloned().enumerate().rev() { - match elem { - ProjectionElem::Deref => break, - _ => { - place = place.elem_base(self.tcx, i); - continue; + + if let PlaceBase::Local(local) = place.base { + if self.mir.local_kind(local) == LocalKind::Temp { + if let Some(qualif) = self.local_qualif[local] { + // `forbidden_mut` is false, so we can safely ignore + // `MUTABLE_INTERIOR` from the local's qualifications. + // This allows borrowing fields which don't have + // `MUTABLE_INTERIOR`, from a type that does, e.g.: + // `let _: &'static _ = &(Cell::new(1), 2).1;` + if (qualif - Qualif::MUTABLE_INTERIOR).is_empty() { + self.promotion_candidates.push(candidate); } } } - } else { - if let PlaceBase::Local(local) = place.base { - if self.mir.local_kind(local) == LocalKind::Temp { - if let Some(qualif) = self.local_qualif[local] { - // `forbidden_mut` is false, so we can safely ignore - // `MUTABLE_INTERIOR` from the local's qualifications. - // This allows borrowing fields which don't have - // `MUTABLE_INTERIOR`, from a type that does, e.g.: - // `let _: &'static _ = &(Cell::new(1), 2).1;` - if (qualif - Qualif::MUTABLE_INTERIOR).is_empty() { - self.promotion_candidates.push(candidate); - } - } + } + + // We can only promote interior borrows of promotable temps. + let mut place = place.clone(); + for (i, elem) in place.elems.iter().cloned().enumerate().rev() { + match elem { + ProjectionElem::Deref => break, + _ => { + place = place.elem_base(self.tcx, i); + continue; } } } diff --git a/src/librustc_mir/util/alignment.rs b/src/librustc_mir/util/alignment.rs index 9fd42b6865a62..2c3b562fb361b 100644 --- a/src/librustc_mir/util/alignment.rs +++ b/src/librustc_mir/util/alignment.rs @@ -52,22 +52,20 @@ fn is_within_packed<'a, 'tcx, L>( where L: HasLocalDecls<'tcx>, { - if !place.has_no_projection() { - 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(..) => { - match base_ty.sty { - ty::TyAdt(def, _) if def.repr.packed() => return true, - _ => {}, - } + 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(..) => { + match base_ty.sty { + ty::TyAdt(def, _) if def.repr.packed() => return true, + _ => {}, } - _ => {}, } - base_ty = tcx::PlaceTy::from(base_ty).projection_ty(tcx, elem).to_ty(tcx); + _ => {}, } + base_ty = tcx::PlaceTy::from(base_ty).projection_ty(tcx, elem).to_ty(tcx); } false diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index 0892918914c0c..eb58533c4697b 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -208,15 +208,14 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { PlaceBase::Promoted(..) => "PlaceBase::Promoted", }, place); - if !place.has_no_projection() { - for elem in place.elems.iter() { - self.visit_projection_elem( - elem, - context, - location, - ); - } + for elem in place.elems.iter() { + self.visit_projection_elem( + elem, + context, + location, + ); } + self.super_place(place, context, location); } From 13a9c20d8961c93baf1f143f4714d5caeaf79719 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Fri, 31 Aug 2018 20:01:42 +0800 Subject: [PATCH 26/33] Separate inner closure to fn --- .../borrow_check/places_conflict.rs | 284 +++++++++--------- 1 file changed, 143 insertions(+), 141 deletions(-) diff --git a/src/librustc_mir/borrow_check/places_conflict.rs b/src/librustc_mir/borrow_check/places_conflict.rs index 918f67206fbe5..72a8f7b02528f 100644 --- a/src/librustc_mir/borrow_check/places_conflict.rs +++ b/src/librustc_mir/borrow_check/places_conflict.rs @@ -12,13 +12,11 @@ use borrow_check::ArtificialField; use borrow_check::Overlap; use borrow_check::{Deep, Shallow, ShallowOrDeep}; use rustc::hir; -use rustc::mir::{Mir, Place, PlaceBase, PlaceElem, ProjectionElem}; use rustc::mir::tcx::PlaceTy; +use rustc::mir::{Mir, Place, PlaceBase, PlaceElem, ProjectionElem}; use rustc::ty::{self, Ty, TyCtxt}; use std::cmp::max; -// FIXME(csmoe): rewrite place_conflict with slice - pub(super) fn places_conflict<'gcx, 'tcx>( tcx: TyCtxt<'_, 'gcx, 'tcx>, mir: &Mir<'tcx>, @@ -31,148 +29,12 @@ pub(super) fn places_conflict<'gcx, 'tcx>( borrow_place, access_place, access ); - let place_elements_conflict = |tcx: TyCtxt<'_, 'gcx, 'tcx>, - mir: &Mir<'tcx>, - borrow_place: &Place<'tcx>, - access_place: &Place<'tcx>| { - // Enumerate for later base_place generation - let mut borrow_elems = borrow_place.elems.iter().cloned(); - - 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. - // - // Examples: - // - // - borrow of `a.(...)`, access to `a.(...)` - // - borrow of `a.(...)`, access to `b.(...)` - // - // Here we only see the components we have checked so - // far (in our examples, just the first component). We - // check whether the components being borrowed vs - // accessed are disjoint (as in the second example, - // but not the first). - 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. - // - // There is no *easy* way of comparing the fields - // further on, because they might have different types - // (e.g. borrows of `u.a.0` and `u.b.y` where `.0` and - // `.y` come from different structs). - // - // We could try to do some things here - e.g. count - // dereferences - but that's probably not a good - // idea, at least for now, so just give up and - // report a conflict. This is unsafe code anyway so - // the user could always use raw pointers. - debug!("places_conflict: arbitrary -> conflict"); - return true; - } - Overlap::EqualOrDisjoint => { - // proceed to the next element. - } - Overlap::Disjoint => { - // We have proven the borrow disjoint - further - // projections will remain disjoint. - debug!("places_conflict: disjoint"); - return false; - } - } - } else { - // Borrow path is longer than the access path. Examples: - // - // - borrow of `a.b.c`, access to `a.b` - // - // Here, we know that the borrow can access a part of - // our place. This is a conflict if that is a part our - // access cares about. - - match (borrow_elem, &borrow_base_ty.sty, access) { - (_, _, Shallow(Some(ArtificialField::Discriminant))) - | (_, _, Shallow(Some(ArtificialField::ArrayLength))) => { - // The discriminant and array length are like - // additional fields on the type; they do not - // overlap any existing data there. Furthermore, - // they cannot actually be a prefix of any - // borrowed place (at least in MIR as it is - // currently.) - // - // e.g. a (mutable) borrow of `a[5]` while we read the - // array length of `a`. - debug!("places_conflict: implicit field"); - return false; - } - - (ProjectionElem::Deref, _, Shallow(None)) => { - // e.g. a borrow of `*x.y` while we shallowly access `x.y` or some - // prefix thereof - the shallow access can't touch anything behind - // the pointer. - debug!("places_conflict: shallow access behind ptr"); - return false; - } - (ProjectionElem::Deref, ty::TyRef(_, _, hir::MutImmutable), _) => { - // the borrow goes through a dereference of a shared reference. - // - // I'm not sure why we are tracking these borrows - shared - // references can *always* be aliased, which means the - // permission check already account for this borrow. - debug!("places_conflict: behind a shared ref"); - return false; - } - - (ProjectionElem::Deref, _, Deep) - | (ProjectionElem::Field { .. }, _, _) - | (ProjectionElem::Index { .. }, _, _) - | (ProjectionElem::ConstantIndex { .. }, _, _) - | (ProjectionElem::Subslice { .. }, _, _) - | (ProjectionElem::Downcast { .. }, _, _) => { - // Recursive case. This can still be disjoint on a - // further iteration if this a shallow access and - // there's a deref later on, e.g. a borrow - // of `*x.y` while accessing `x`. - } - } - } - 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: - // - // - borrow of `a.b`, access to `a.b.c` - // - borrow of `a.b`, access to `a.b` - // - // In the first example, where we didn't run out of - // access, the borrow can access all of our place, so we - // have a conflict. - // - // If the second example, where we did, then we still know - // that the borrow can access a *part* of our place that - // our access cares about, so we still have a conflict. - // - // FIXME: Differs from AST-borrowck; includes drive-by fix - // to #38899. Will probably need back-compat mode flag. - debug!("places_conflict: full borrow, CONFLICT"); - return true; - } - } - }; - 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), + Overlap::EqualOrDisjoint => + place_elements_conflict(tcx, mir, borrow_place, access_place, access), // place.base overlap is obvious, no Abitrary. _ => unreachable!(), } @@ -452,3 +314,143 @@ fn place_element_conflict<'tcx>( ), } } + +fn place_elements_conflict<'gcx, 'tcx>( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + borrow_place: &Place<'tcx>, + access_place: &Place<'tcx>, + access: ShallowOrDeep, +) -> bool { + // Enumerate for later base_place generation + let mut borrow_elems = borrow_place.elems.iter().cloned(); + + 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. + // + // Examples: + // + // - borrow of `a.(...)`, access to `a.(...)` + // - borrow of `a.(...)`, access to `b.(...)` + // + // Here we only see the components we have checked so + // far (in our examples, just the first component). We + // check whether the components being borrowed vs + // accessed are disjoint (as in the second example, + // but not the first). + 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. + // + // There is no *easy* way of comparing the fields + // further on, because they might have different types + // (e.g. borrows of `u.a.0` and `u.b.y` where `.0` and + // `.y` come from different structs). + // + // We could try to do some things here - e.g. count + // dereferences - but that's probably not a good + // idea, at least for now, so just give up and + // report a conflict. This is unsafe code anyway so + // the user could always use raw pointers. + debug!("places_conflict: arbitrary -> conflict"); + return true; + } + Overlap::EqualOrDisjoint => { + // proceed to the next element. + } + Overlap::Disjoint => { + // We have proven the borrow disjoint - further + // projections will remain disjoint. + debug!("places_conflict: disjoint"); + return false; + } + } + } else { + // Borrow path is longer than the access path. Examples: + // + // - borrow of `a.b.c`, access to `a.b` + // + // Here, we know that the borrow can access a part of + // our place. This is a conflict if that is a part our + // access cares about. + + match (borrow_elem, &borrow_base_ty.sty, access) { + (_, _, Shallow(Some(ArtificialField::Discriminant))) + | (_, _, Shallow(Some(ArtificialField::ArrayLength))) => { + // The discriminant and array length are like + // additional fields on the type; they do not + // overlap any existing data there. Furthermore, + // they cannot actually be a prefix of any + // borrowed place (at least in MIR as it is + // currently.) + // + // e.g. a (mutable) borrow of `a[5]` while we read the + // array length of `a`. + debug!("places_conflict: implicit field"); + return false; + } + + (ProjectionElem::Deref, _, Shallow(None)) => { + // e.g. a borrow of `*x.y` while we shallowly access `x.y` or some + // prefix thereof - the shallow access can't touch anything behind + // the pointer. + debug!("places_conflict: shallow access behind ptr"); + return false; + } + (ProjectionElem::Deref, ty::TyRef(_, _, hir::MutImmutable), _) => { + // the borrow goes through a dereference of a shared reference. + // + // I'm not sure why we are tracking these borrows - shared + // references can *always* be aliased, which means the + // permission check already account for this borrow. + debug!("places_conflict: behind a shared ref"); + return false; + } + + (ProjectionElem::Deref, _, Deep) + | (ProjectionElem::Field { .. }, _, _) + | (ProjectionElem::Index { .. }, _, _) + | (ProjectionElem::ConstantIndex { .. }, _, _) + | (ProjectionElem::Subslice { .. }, _, _) + | (ProjectionElem::Downcast { .. }, _, _) => { + // Recursive case. This can still be disjoint on a + // further iteration if this a shallow access and + // there's a deref later on, e.g. a borrow + // of `*x.y` while accessing `x`. + } + } + } + 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: + // + // - borrow of `a.b`, access to `a.b.c` + // - borrow of `a.b`, access to `a.b` + // + // In the first example, where we didn't run out of + // access, the borrow can access all of our place, so we + // have a conflict. + // + // If the second example, where we did, then we still know + // that the borrow can access a *part* of our place that + // our access cares about, so we still have a conflict. + // + // FIXME: Differs from AST-borrowck; includes drive-by fix + // to #38899. Will probably need back-compat mode flag. + debug!("places_conflict: full borrow, CONFLICT"); + return true; + } + } +} From d5095f619a288423b22e6158c8f7305c86357404 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Sat, 1 Sep 2018 16:41:54 +0800 Subject: [PATCH 27/33] Fix mutability capture from place --- src/librustc/mir/tcx.rs | 4 +- src/librustc_mir/borrow_check/move_errors.rs | 2 +- .../borrow_check/mutability_errors.rs | 8 +- src/librustc_mir/build/expr/as_rvalue.rs | 475 +++++++++++------- src/librustc_mir/transform/qualify_consts.rs | 4 +- .../transform/uniform_array_move_out.rs | 2 +- 6 files changed, 307 insertions(+), 188 deletions(-) diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index 061f0f702dafc..8b15cdb5136f8 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -185,7 +185,7 @@ impl<'tcx> Place<'tcx> { // Base.[] // ^^^^ ^^-- no projection(empty) // |-- base_place - pub fn final_projection<'cx, 'gcx>( + pub fn split_projection<'cx, 'gcx>( &self, tcx: TyCtxt<'cx, 'gcx, 'tcx>, ) -> (Place<'tcx>, Option<&'tcx PlaceElem<'tcx>>) { @@ -216,7 +216,7 @@ impl<'tcx> Place<'tcx> { // Base.[] => Base.[] // ^^-- no projection pub fn projection_base<'cx, 'gcx>(&self, tcx: TyCtxt<'cx, 'gcx, 'tcx>) -> Place<'tcx> { - match self.final_projection(tcx) { + match self.split_projection(tcx) { (place, Some(_)) => place, (_, None) => self.clone(), } diff --git a/src/librustc_mir/borrow_check/move_errors.rs b/src/librustc_mir/borrow_check/move_errors.rs index ca0598500b23c..8e31e73d1530b 100644 --- a/src/librustc_mir/borrow_check/move_errors.rs +++ b/src/librustc_mir/borrow_check/move_errors.rs @@ -297,7 +297,7 @@ 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) { - if let (base_place, Some(proj)) = move_from.final_projection(self.tcx) { + 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, diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs index ce715283189d6..734fe04c87e43 100644 --- a/src/librustc_mir/borrow_check/mutability_errors.rs +++ b/src/librustc_mir/borrow_check/mutability_errors.rs @@ -41,7 +41,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { let reason; let access_place_desc = self.describe_place(access_place); - if let (base_place, Some(projection)) = the_place_err.final_projection(self.tcx) { + 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)) @@ -188,7 +188,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { } }; - if let (base_place, Some(projection)) = the_place_err.final_projection(self.tcx) { + 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 { @@ -360,7 +360,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { // Does this place refer to what the user sees as an upvar fn is_upvar(&self, place: &Place<'tcx>) -> bool { - if let (base_place, Some(projection)) = place.final_projection(self.tcx) { + 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); @@ -370,7 +370,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { if let ( ref base_place, Some(ProjectionElem::Field(upvar_index, _)), - ) = base_place.final_projection(self.tcx) { + ) = 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 diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 0492ba1d1f667..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,22 +120,30 @@ 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: let tcx = this.hir.tcx(); @@ -171,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() @@ -213,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(); @@ -248,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); @@ -274,7 +323,8 @@ 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(), @@ -282,19 +332,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.hir.tcx(), n, ty, - )) - }) - .collect() + )), + }).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()) } @@ -302,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) { @@ -339,19 +394,27 @@ 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); @@ -360,8 +423,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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 { @@ -370,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. @@ -393,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); } } @@ -428,45 +502,98 @@ 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 mut mutability = if let PlaceBase::Local(local) = arg_place.base { - this.local_decls[local].mutability - } else { - bug!("Unexpected capture place"); - }; - - let mut proj_iter = arg_place.elems.iter().cloned().rev(); - if let Some(projection) = proj_iter.next() { - match projection { - ProjectionElem::Deref => { - if let PlaceBase::Local(local) = arg_place.base { - debug_assert!( - if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) - = this.local_decls[local].is_user_variable { - true - } else { - false - }, - "Unexpected capture place", - ); + 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"); + } + } + } + } } - if let Some(ProjectionElem::Field(upvar_index, _)) = proj_iter.next() { - // Not projected from the implicit `self` in a closure. + ProjectionElem::Field(upvar_index, _) => { debug_assert!( - match arg_place.base { - PlaceBase::Local(local) => local == Local::new(1), - _ => false, - }, + 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 @@ -474,36 +601,25 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.upvar_decls.len() > upvar_index.index(), "Unexpected capture place" ); - mutability = this.upvar_decls[upvar_index.index()].mutability; - } else { - bug!("Unexpected capture place"); + this.upvar_decls[upvar_index.index()].mutability } - }, - ProjectionElem::Field(upvar_index, _) => { - // Not projected from the implicit `self` in a closure. - debug_assert!( - match arg_place.base { - PlaceBase::Local(local) => local == Local::new(1), - _ => false, - }, - "Unexpected capture place" - ); - // Not in a closure - debug_assert!( - this.upvar_decls.len() > upvar_index.index(), - "Unexpected capture place" - ); - mutability = this.upvar_decls[upvar_index.index()].mutability; - }, - _ => { - bug!("Unexpected capture place"); + _ => bug!("Unexpected capture place"), } } - } + (base_place, None) => { + if let PlaceBase::Local(local) = base_place.base { + this.local_decls[local].mutability + } else { + 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( @@ -518,7 +634,10 @@ 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, ); } diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 636a0369c182f..181aeb8356cab 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -464,7 +464,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { context: PlaceContext<'tcx>, location: Location, ) { - if let (base_place, Some(projection)) = place.final_projection(self.tcx) { + if let (base_place, Some(projection)) = place.split_projection(self.tcx) { self.nest(|this| { this.super_place(place, context, location); match projection { @@ -610,7 +610,7 @@ 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 (base_place, Some(projection)) = place.final_projection(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 { diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs index 4de020c202df2..ceff369d821f0 100644 --- a/src/librustc_mir/transform/uniform_array_move_out.rs +++ b/src/librustc_mir/transform/uniform_array_move_out.rs @@ -75,7 +75,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> { location: Location ) { if let Rvalue::Use(Operand::Move(src_place)) = rvalue { - if let (base_place, Some(projection)) = src_place.final_projection(self.tcx) { + if let (base_place, Some(projection)) = src_place.split_projection(self.tcx) { if let ProjectionElem::ConstantIndex { offset: _, min_length: _, From 6f8aa3dbb9730a89d79d95768818ddeffcfa6651 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Thu, 6 Sep 2018 10:03:12 +0800 Subject: [PATCH 28/33] mutability_errors: rustfmt --- .../borrow_check/mutability_errors.rs | 162 +++++++++--------- 1 file changed, 84 insertions(+), 78 deletions(-) diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs index 734fe04c87e43..1ab2b0bd8ff2f 100644 --- a/src/librustc_mir/borrow_check/mutability_errors.rs +++ b/src/librustc_mir/borrow_check/mutability_errors.rs @@ -45,7 +45,8 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { match projection { ProjectionElem::Deref => { if base_place.base == PlaceBase::Local(Local::new(1)) - && !self.mir.upvar_decls.is_empty() { + && !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( @@ -59,8 +60,9 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { } } 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 { + if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) = + self.mir.local_decls[local].is_user_variable + { true } else { false @@ -72,18 +74,21 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { item_msg = format!("`{}`", access_place_desc.unwrap()); reason = ", as it is immutable for the pattern guard".to_string(); } else { - let pointer_type = - if base_place.ty(self.mir, self.tcx).to_ty(self.tcx).is_region_ptr() { - "`&` reference" - } else { - "`*const` pointer" - }; + 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::Move | AccessKind::Mutate => { + format!(" which is behind a {}", pointer_type) + } AccessKind::MutableBorrow => { format!(", as it is behind a {}", pointer_type) } @@ -93,7 +98,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { reason = "".to_string(); } } - }, + } ProjectionElem::Field(upvar_index, _) => { debug_assert!(is_closure_or_generator( base_place.ty(self.mir, self.tcx).to_ty(self.tcx) @@ -110,9 +115,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(..) | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Index(_) => { - bug!("Unexpected immutable place.") - } + | ProjectionElem::Index(_) => bug!("Unexpected immutable place."), } } else { match the_place_err.base { @@ -129,10 +132,8 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { } PlaceBase::Static(box Static { def_id, ty: _ }) => { if let PlaceBase::Static(_) = access_place.base { - item_msg = format!( - "immutable static item `{}`", - access_place_desc.unwrap() - ); + item_msg = + format!("immutable static item `{}`", access_place_desc.unwrap()); reason = "".to_string(); } else { item_msg = format!("`{}`", access_place_desc.unwrap()); @@ -151,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"; @@ -182,7 +185,7 @@ 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 } @@ -197,9 +200,9 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { err.span_label(span, format!("cannot {ACT}", ACT = act)); err.note( "variables bound in patterns are \ - immutable until the end of the pattern guard", + immutable until the end of the pattern guard", ); - }, + } Some(_) => { // We want to point out when a `&` can be readily replaced // with an `&mut`. @@ -207,19 +210,19 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { // 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() { + 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( + ClearCrossCrate::Set(mir::BindingForm::Var( + mir::VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(_), + opt_ty_info, + .. + }, + )) => Some(suggest_ampmut( self.tcx, self.mir, local, @@ -227,23 +230,22 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { *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::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::Set(mir::BindingForm::RefForGuard) => { + unreachable!() + } ClearCrossCrate::Clear => bug!("saw cleared local state"), }; - let (pointer_sigil, pointer_desc) = - if local_decl.ty.is_region_ptr() { + let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() + { ("&", "reference") } else { ("*const", "pointer") @@ -265,7 +267,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { span, format!( "`{NAME}` is a `{SIGIL}` {DESC}, \ - so the data it refers to cannot be {ACTED_ON}", + so the data it refers to cannot be {ACTED_ON}", NAME = name, SIGIL = pointer_sigil, DESC = pointer_desc, @@ -284,17 +286,17 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { ); } } - _ => {}, + _ => {} } 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`" + "consider changing this to accept closures that implement `FnMut`", ); } } - }, + } ProjectionElem::Field(upvar_index, _) => { // Suggest adding mut for upvars debug_assert!(is_closure_or_generator( @@ -314,16 +316,16 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { upvar_ident, _, ) = pat.node - { - err.span_suggestion( - upvar_ident.span, - "consider changing this to be mutable", - format!("mut {}", upvar_ident.name), - ); - } + { + 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() { @@ -367,10 +369,9 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, '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) { + 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 @@ -391,17 +392,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 @@ -467,12 +471,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 { From 7da72056cf7140102419edafc4b272fc333b01b6 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Thu, 6 Sep 2018 10:15:58 +0800 Subject: [PATCH 29/33] returns immediately on references error --- src/librustc_mir/borrow_check/nll/type_check/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 5481e98dc352c..89ae391830ea2 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -415,10 +415,9 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { if let PlaceTy::Ty { ty } = place_ty { if ty.references_error() { assert!(self.errors_reported); - place_ty = PlaceTy::Ty { + return PlaceTy::Ty { ty: self.tcx().types.err, }; - break; } } place_ty = self.sanitize_projection(place_ty, elem, place, location); From c47b2a916f34fbd9d207c6e8dafeb9322f4b238f Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Thu, 6 Sep 2018 10:18:48 +0800 Subject: [PATCH 30/33] guarantee working no projection place when reporting mutability error --- .../borrow_check/mutability_errors.rs | 168 +++++++++--------- 1 file changed, 85 insertions(+), 83 deletions(-) diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs index 1ab2b0bd8ff2f..97bd8c982cb92 100644 --- a/src/librustc_mir/borrow_check/mutability_errors.rs +++ b/src/librustc_mir/borrow_check/mutability_errors.rs @@ -195,98 +195,100 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { match projection { ProjectionElem::Deref => { if let PlaceBase::Local(local) = base_place.base { - 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)) - } + 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::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::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::Set(mir::BindingForm::RefForGuard) => { + unreachable!() + } - ClearCrossCrate::Clear => bug!("saw cleared local state"), - }; + ClearCrossCrate::Clear => bug!("saw cleared local state"), + }; - let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() - { - ("&", "reference") - } else { - ("*const", "pointer") - }; + 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((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 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)); From 86166727acd951245fad4c4be0d067bf80c5aa3d Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Thu, 6 Sep 2018 10:23:11 +0800 Subject: [PATCH 31/33] working on on projection place when visit_assign --- .../borrow_check/nll/constraint_generation.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 34b9e58f7a2be..299ab7e9ffa3f 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -139,10 +139,12 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx // `(*X).foo` and so forth. if let Some(all_facts) = self.all_facts { if let PlaceBase::Local(temp) = place.base { - 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 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)); + } } } } From ba7feb15c688ffecdbea3c9faaa203d90a745154 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Thu, 6 Sep 2018 15:48:41 +0800 Subject: [PATCH 32/33] guarantee has_no_projection when visit_rvalue --- src/librustc_mir/transform/qualify_consts.rs | 32 +++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 181aeb8356cab..0de3e9033e632 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -236,7 +236,8 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { if self.mode == Mode::Fn { 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]); } @@ -249,6 +250,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { 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) }) { @@ -717,9 +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); + 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); + } + } + } - if let PlaceBase::Local(local) = place.base { - 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. @@ -732,18 +746,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } } } - - // We can only promote interior borrows of promotable temps. - let mut place = place.clone(); - for (i, elem) in place.elems.iter().cloned().enumerate().rev() { - match elem { - ProjectionElem::Deref => break, - _ => { - place = place.elem_base(self.tcx, i); - continue; - } - } - } } } From 552a935f26fc6106ad944b4ed54af4741a80b34d Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Thu, 6 Sep 2018 15:50:55 +0800 Subject: [PATCH 33/33] guarantee has_no_projection in uniform_array_moveout --- .../transform/uniform_array_move_out.rs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs index ceff369d821f0..2d1bcb39d6aae 100644 --- a/src/librustc_mir/transform/uniform_array_move_out.rs +++ b/src/librustc_mir/transform/uniform_array_move_out.rs @@ -210,17 +210,19 @@ impl MirPass for RestoreSubsliceArrayMoveOut { let items : Vec<_> = items.iter().map(|item| { if let Operand::Move(place) = item { if let PlaceBase::Local(local) = place.base { - 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)); + 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)); + } } } }