Auto merge of #61151 - Centril:rollup-5rpyhfo, r=Centril

Rollup of 6 pull requests

Successful merges:

 - #61092 (Make sanitize_place iterate instead of recurse)
 - #61093 (Make borrow_of_local_data iterate instead of recurse)
 - #61094 (Make find_local iterate instead of recurse)
 - #61099 (Make ignore_borrow iterate instead of recurse)
 - #61103 (Make find iterate instead of recurse)
 - #61104 (Make eval_place_to_op iterate instead of recurse)

Failed merges:

r? @ghost
This commit is contained in:
bors 2019-05-25 09:30:02 +00:00
commit 02f5786a32
7 changed files with 189 additions and 174 deletions

View File

@ -29,7 +29,7 @@ use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionV
use rustc::infer::type_variable::TypeVariableOrigin; use rustc::infer::type_variable::TypeVariableOrigin;
use rustc::mir::interpret::{InterpError::BoundsCheck, ConstValue}; use rustc::mir::interpret::{InterpError::BoundsCheck, ConstValue};
use rustc::mir::tcx::PlaceTy; use rustc::mir::tcx::PlaceTy;
use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext}; use rustc::mir::visit::{PlaceContext, Visitor, NonMutatingUseContext};
use rustc::mir::*; use rustc::mir::*;
use rustc::traits::query::type_op; use rustc::traits::query::type_op;
use rustc::traits::query::type_op::custom::CustomTypeOp; use rustc::traits::query::type_op::custom::CustomTypeOp;
@ -447,92 +447,95 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
context: PlaceContext, context: PlaceContext,
) -> PlaceTy<'tcx> { ) -> PlaceTy<'tcx> {
debug!("sanitize_place: {:?}", place); debug!("sanitize_place: {:?}", place);
let place_ty = match place {
Place::Base(PlaceBase::Local(index)) =>
PlaceTy::from_ty(self.mir.local_decls[*index].ty),
Place::Base(PlaceBase::Static(box Static { kind, ty: sty })) => {
let sty = self.sanitize_type(place, sty);
let check_err =
|verifier: &mut TypeVerifier<'a, 'b, 'gcx, 'tcx>,
place: &Place<'tcx>,
ty,
sty| {
if let Err(terr) = verifier.cx.eq_types(
sty,
ty,
location.to_locations(),
ConstraintCategory::Boring,
) {
span_mirbug!(
verifier,
place,
"bad promoted type ({:?}: {:?}): {:?}",
ty,
sty,
terr
);
};
};
match kind {
StaticKind::Promoted(promoted) => {
if !self.errors_reported {
let promoted_mir = &self.mir.promoted[*promoted];
self.sanitize_promoted(promoted_mir, location);
let promoted_ty = promoted_mir.return_ty(); place.iterate(|place_base, place_projection| {
check_err(self, place, promoted_ty, sty); let mut place_ty = match place_base {
PlaceBase::Local(index) =>
PlaceTy::from_ty(self.mir.local_decls[*index].ty),
PlaceBase::Static(box Static { kind, ty: sty }) => {
let sty = self.sanitize_type(place, sty);
let check_err =
|verifier: &mut TypeVerifier<'a, 'b, 'gcx, 'tcx>,
place: &Place<'tcx>,
ty,
sty| {
if let Err(terr) = verifier.cx.eq_types(
sty,
ty,
location.to_locations(),
ConstraintCategory::Boring,
) {
span_mirbug!(
verifier,
place,
"bad promoted type ({:?}: {:?}): {:?}",
ty,
sty,
terr
);
};
};
match kind {
StaticKind::Promoted(promoted) => {
if !self.errors_reported {
let promoted_mir = &self.mir.promoted[*promoted];
self.sanitize_promoted(promoted_mir, location);
let promoted_ty = promoted_mir.return_ty();
check_err(self, place, promoted_ty, sty);
}
}
StaticKind::Static(def_id) => {
let ty = self.tcx().type_of(*def_id);
let ty = self.cx.normalize(ty, location);
check_err(self, place, ty, sty);
} }
} }
StaticKind::Static(def_id) => { PlaceTy::from_ty(sty)
let ty = self.tcx().type_of(*def_id); }
let ty = self.cx.normalize(ty, location); };
check_err(self, place, ty, sty); // FIXME use place_projection.is_empty() when is available
} if let Place::Base(_) = place {
if let PlaceContext::NonMutatingUse(NonMutatingUseContext::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.ty, &[]),
};
// 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 `is_copy_modulo_regions`
// test. This is important because
// `is_copy_modulo_regions` 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.to_locations(),
ConstraintCategory::CopyBound,
);
} }
PlaceTy::from_ty(sty)
} }
Place::Projection(ref proj) => {
let base_context = if context.is_mutating_use() { for proj in place_projection {
PlaceContext::MutatingUse(MutatingUseContext::Projection) if place_ty.variant_index.is_none() {
} else { if place_ty.ty.references_error() {
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
};
let base_ty = self.sanitize_place(&proj.base, location, base_context);
if base_ty.variant_index.is_none() {
if base_ty.ty.references_error() {
assert!(self.errors_reported); assert!(self.errors_reported);
return PlaceTy::from_ty(self.tcx().types.err); return PlaceTy::from_ty(self.tcx().types.err);
} }
} }
self.sanitize_projection(base_ty, &proj.elem, place, location) place_ty = self.sanitize_projection(place_ty, &proj.elem, place, location)
} }
};
if let PlaceContext::NonMutatingUse(NonMutatingUseContext::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.ty, &[]),
};
// In order to have a Copy operand, the type T of the place_ty
// value must be Copy. Note that we prove that T: Copy, })
// rather than using the `is_copy_modulo_regions`
// test. This is important because
// `is_copy_modulo_regions` 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.to_locations(),
ConstraintCategory::CopyBound,
);
}
place_ty
} }
fn sanitize_promoted(&mut self, promoted_mir: &'b Mir<'tcx>, location: Location) { fn sanitize_promoted(&mut self, promoted_mir: &'b Mir<'tcx>, location: Location) {

View File

@ -131,22 +131,20 @@ pub(super) fn is_active<'tcx>(
/// Determines if a given borrow is borrowing local data /// Determines if a given borrow is borrowing local data
/// This is called for all Yield statements on movable generators /// This is called for all Yield statements on movable generators
pub(super) fn borrow_of_local_data<'tcx>(place: &Place<'tcx>) -> bool { pub(super) fn borrow_of_local_data<'tcx>(place: &Place<'tcx>) -> bool {
match place { place.iterate(|place_base, place_projection| {
Place::Base(PlaceBase::Static(..)) => false, match place_base {
Place::Base(PlaceBase::Local(..)) => true, PlaceBase::Static(..) => return false,
Place::Projection(box proj) => { PlaceBase::Local(..) => {},
match proj.elem { }
// 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 for proj in place_projection {
ProjectionElem::Field(..) // Reborrow of already borrowed data is ignored
| ProjectionElem::Index(..) // Any errors will be caught on the initial borrow
| ProjectionElem::ConstantIndex { .. } if proj.elem == ProjectionElem::Deref {
| ProjectionElem::Subslice { .. } return false;
| ProjectionElem::Downcast(..) => borrow_of_local_data(&proj.base),
} }
} }
}
true
})
} }

View File

@ -25,40 +25,36 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
mir: &Mir<'tcx>, mir: &Mir<'tcx>,
locals_state_at_exit: &LocalsStateAtExit, locals_state_at_exit: &LocalsStateAtExit,
) -> bool { ) -> bool {
match self { self.iterate(|place_base, place_projection| {
// If a local variable is immutable, then we only need to track borrows to guard let ignore = match place_base {
// against two kinds of errors: // If a local variable is immutable, then we only need to track borrows to guard
// * The variable being dropped while still borrowed (e.g., because the fn returns // against two kinds of errors:
// a reference to a local variable) // * The variable being dropped while still borrowed (e.g., because the fn returns
// * The variable being moved while still borrowed // a reference to a local variable)
// // * The variable being moved while still borrowed
// In particular, the variable cannot be mutated -- the "access checks" will fail -- //
// so we don't have to worry about mutation while borrowed. // In particular, the variable cannot be mutated -- the "access checks" will fail --
Place::Base(PlaceBase::Local(index)) => { // so we don't have to worry about mutation while borrowed.
match locals_state_at_exit { PlaceBase::Local(index) => {
LocalsStateAtExit::AllAreInvalidated => false, match locals_state_at_exit {
LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } => { LocalsStateAtExit::AllAreInvalidated => false,
let ignore = !has_storage_dead_or_moved.contains(*index) && LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } => {
mir.local_decls[*index].mutability == Mutability::Not; let ignore = !has_storage_dead_or_moved.contains(*index) &&
debug!("ignore_borrow: local {:?} => {:?}", index, ignore); mir.local_decls[*index].mutability == Mutability::Not;
ignore debug!("ignore_borrow: local {:?} => {:?}", index, ignore);
ignore
}
} }
} }
} PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. }) =>
Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. })) => false,
false, PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. }) => {
Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. })) => { tcx.is_mutable_static(*def_id)
tcx.is_mutable_static(*def_id) }
} };
Place::Projection(proj) => match proj.elem {
ProjectionElem::Field(..)
| ProjectionElem::Downcast(..)
| ProjectionElem::Subslice { .. }
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Index(_) => proj.base.ignore_borrow(
tcx, mir, locals_state_at_exit),
ProjectionElem::Deref => { for proj in place_projection {
if proj.elem == ProjectionElem::Deref {
let ty = proj.base.ty(mir, tcx).ty; let ty = proj.base.ty(mir, tcx).ty;
match ty.sty { match ty.sty {
// For both derefs of raw pointers and `&T` // For both derefs of raw pointers and `&T`
@ -71,11 +67,13 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
// original path into a new variable and // original path into a new variable and
// borrowed *that* one, leaving the original // borrowed *that* one, leaving the original
// path unborrowed. // path unborrowed.
ty::RawPtr(..) | ty::Ref(_, _, hir::MutImmutable) => true, ty::RawPtr(..) | ty::Ref(_, _, hir::MutImmutable) => return true,
_ => proj.base.ignore_borrow(tcx, mir, locals_state_at_exit), _ => {}
} }
} }
}, }
}
ignore
})
} }
} }

View File

@ -91,16 +91,19 @@ struct BorrowedLocalsVisitor<'b, 'c: 'b> {
} }
fn find_local<'tcx>(place: &Place<'tcx>) -> Option<Local> { fn find_local<'tcx>(place: &Place<'tcx>) -> Option<Local> {
match *place { place.iterate(|place_base, place_projection| {
Place::Base(PlaceBase::Local(l)) => Some(l), for proj in place_projection {
Place::Base(PlaceBase::Static(..)) => None, if proj.elem == ProjectionElem::Deref {
Place::Projection(ref proj) => { return None;
match proj.elem {
ProjectionElem::Deref => None,
_ => find_local(&proj.base)
} }
} }
}
if let PlaceBase::Local(local) = place_base {
Some(*local)
} else {
None
}
})
} }
impl<'tcx, 'b, 'c> Visitor<'tcx> for BorrowedLocalsVisitor<'b, 'c> { impl<'tcx, 'b, 'c> Visitor<'tcx> for BorrowedLocalsVisitor<'b, 'c> {

View File

@ -241,21 +241,22 @@ impl MovePathLookup {
// unknown place, but will rather return the nearest available // unknown place, but will rather return the nearest available
// parent. // parent.
pub fn find(&self, place: &Place<'tcx>) -> LookupResult { pub fn find(&self, place: &Place<'tcx>) -> LookupResult {
match *place { place.iterate(|place_base, place_projection| {
Place::Base(PlaceBase::Local(local)) => LookupResult::Exact(self.locals[local]), let mut result = match place_base {
Place::Base(PlaceBase::Static(..)) => LookupResult::Parent(None), PlaceBase::Local(local) => self.locals[*local],
Place::Projection(ref proj) => { PlaceBase::Static(..) => return LookupResult::Parent(None),
match self.find(&proj.base) { };
LookupResult::Exact(base_path) => {
match self.projections.get(&(base_path, proj.elem.lift())) { for proj in place_projection {
Some(&subpath) => LookupResult::Exact(subpath), if let Some(&subpath) = self.projections.get(&(result, proj.elem.lift())) {
None => LookupResult::Parent(Some(base_path)) result = subpath;
} } else {
} return LookupResult::Parent(Some(result));
inexact => inexact
} }
} }
}
LookupResult::Exact(result)
})
} }
pub fn find_local(&self, local: Local) -> MovePathIndex { pub fn find_local(&self, local: Local) -> MovePathIndex {

View File

@ -467,22 +467,34 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
mir_place: &mir::Place<'tcx>, mir_place: &mir::Place<'tcx>,
layout: Option<TyLayout<'tcx>>, layout: Option<TyLayout<'tcx>>,
) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> { ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> {
use rustc::mir::Place::*; use rustc::mir::Place;
use rustc::mir::PlaceBase; use rustc::mir::PlaceBase;
let op = match *mir_place {
Base(PlaceBase::Local(mir::RETURN_PLACE)) => return err!(ReadFromReturnPointer),
Base(PlaceBase::Local(local)) => self.access_local(self.frame(), local, layout)?,
Projection(ref proj) => { mir_place.iterate(|place_base, place_projection| {
let op = self.eval_place_to_op(&proj.base, None)?; let mut op = match place_base {
self.operand_projection(op, &proj.elem)? PlaceBase::Local(mir::RETURN_PLACE) => return err!(ReadFromReturnPointer),
PlaceBase::Local(local) => {
// FIXME use place_projection.is_empty() when is available
let layout = if let Place::Base(_) = mir_place {
layout
} else {
None
};
self.access_local(self.frame(), *local, layout)?
}
PlaceBase::Static(place_static) => {
self.eval_static_to_mplace(place_static)?.into()
}
};
for proj in place_projection {
op = self.operand_projection(op, &proj.elem)?
} }
_ => self.eval_place_to_mplace(mir_place)?.into(), trace!("eval_place_to_op: got {:?}", *op);
}; Ok(op)
})
trace!("eval_place_to_op: got {:?}", *op);
Ok(op)
} }
/// Evaluate the operand, returning a place where you can then find the data. /// Evaluate the operand, returning a place where you can then find the data.

View File

@ -562,15 +562,14 @@ where
/// Evaluate statics and promoteds to an `MPlace`. Used to share some code between /// Evaluate statics and promoteds to an `MPlace`. Used to share some code between
/// `eval_place` and `eval_place_to_op`. /// `eval_place` and `eval_place_to_op`.
pub(super) fn eval_place_to_mplace( pub(super) fn eval_static_to_mplace(
&self, &self,
mir_place: &mir::Place<'tcx> place_static: &mir::Static<'tcx>
) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
use rustc::mir::Place::*; use rustc::mir::StaticKind;
use rustc::mir::PlaceBase;
use rustc::mir::{Static, StaticKind}; Ok(match place_static.kind {
Ok(match *mir_place { StaticKind::Promoted(promoted) => {
Base(PlaceBase::Static(box Static { kind: StaticKind::Promoted(promoted), .. })) => {
let instance = self.frame().instance; let instance = self.frame().instance;
self.const_eval_raw(GlobalId { self.const_eval_raw(GlobalId {
instance, instance,
@ -578,7 +577,8 @@ where
})? })?
} }
Base(PlaceBase::Static(box Static { kind: StaticKind::Static(def_id), ty })) => { StaticKind::Static(def_id) => {
let ty = place_static.ty;
assert!(!ty.needs_subst()); assert!(!ty.needs_subst());
let layout = self.layout_of(ty)?; let layout = self.layout_of(ty)?;
let instance = ty::Instance::mono(*self.tcx, def_id); let instance = ty::Instance::mono(*self.tcx, def_id);
@ -600,8 +600,6 @@ where
let alloc = self.tcx.alloc_map.lock().intern_static(cid.instance.def_id()); let alloc = self.tcx.alloc_map.lock().intern_static(cid.instance.def_id());
MPlaceTy::from_aligned_ptr(Pointer::from(alloc).with_default_tag(), layout) MPlaceTy::from_aligned_ptr(Pointer::from(alloc).with_default_tag(), layout)
} }
_ => bug!("eval_place_to_mplace called on {:?}", mir_place),
}) })
} }
@ -613,7 +611,7 @@ where
) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { ) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
use rustc::mir::Place::*; use rustc::mir::Place::*;
use rustc::mir::PlaceBase; use rustc::mir::PlaceBase;
let place = match *mir_place { let place = match mir_place {
Base(PlaceBase::Local(mir::RETURN_PLACE)) => match self.frame().return_place { Base(PlaceBase::Local(mir::RETURN_PLACE)) => match self.frame().return_place {
Some(return_place) => Some(return_place) =>
// We use our layout to verify our assumption; caller will validate // We use our layout to verify our assumption; caller will validate
@ -628,17 +626,19 @@ where
// This works even for dead/uninitialized locals; we check further when writing // This works even for dead/uninitialized locals; we check further when writing
place: Place::Local { place: Place::Local {
frame: self.cur_frame(), frame: self.cur_frame(),
local, local: *local,
}, },
layout: self.layout_of_local(self.frame(), local, None)?, layout: self.layout_of_local(self.frame(), *local, None)?,
}, },
Projection(ref proj) => { Projection(proj) => {
let place = self.eval_place(&proj.base)?; let place = self.eval_place(&proj.base)?;
self.place_projection(place, &proj.elem)? self.place_projection(place, &proj.elem)?
} }
_ => self.eval_place_to_mplace(mir_place)?.into(), Base(PlaceBase::Static(place_static)) => {
self.eval_static_to_mplace(place_static)?.into()
}
}; };
self.dump_place(place.place); self.dump_place(place.place);