use crate::build::ForGuard::{OutsideGuard, RefWithinGuard};
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::hair::*;
+use rustc::middle::region;
use rustc::mir::interpret::{PanicInfo::BoundsCheck};
use rustc::mir::*;
-use rustc::ty::{CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
+use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
+use syntax_pos::Span;
use rustc_index::vec::Idx;
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Compile `expr`, yielding a place that we can move from etc.
+ ///
+ /// WARNING: Any user code might:
+ /// * Invalidate any slice bounds checks performed.
+ /// * Change the address that this `Place` refers to.
+ /// * Modify the memory that this place refers to.
+ /// * Invalidate the memory that this place refers to, this will be caught
+ /// by borrow checking.
+ ///
+ /// Extra care is needed if any user code is allowed to run between calling
+ /// this method and using it, as is the case for `match` and index
+ /// expressions.
pub fn as_place<M>(&mut self, mut block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
where
M: Mirror<'tcx, Output = Expr<'tcx>>,
M: Mirror<'tcx, Output = Expr<'tcx>>,
{
let expr = self.hir.mirror(expr);
- self.expr_as_place(block, expr, Mutability::Mut)
+ self.expr_as_place(block, expr, Mutability::Mut, None)
}
/// Compile `expr`, yielding a place that we can move from etc.
M: Mirror<'tcx, Output = Expr<'tcx>>,
{
let expr = self.hir.mirror(expr);
- self.expr_as_place(block, expr, Mutability::Not)
+ self.expr_as_place(block, expr, Mutability::Not, None)
}
fn expr_as_place(
mut block: BasicBlock,
expr: Expr<'tcx>,
mutability: Mutability,
+ fake_borrow_temps: Option<&mut Vec<Local>>,
) -> BlockAnd<PlaceBuilder<'tcx>> {
debug!(
"expr_as_place(block={:?}, expr={:?}, mutability={:?})",
lint_level,
value,
} => this.in_scope((region_scope, source_info), lint_level, |this| {
- if mutability == Mutability::Not {
- this.as_read_only_place_builder(block, value)
- } else {
- this.as_place_builder(block, value)
- }
+ let value = this.hir.mirror(value);
+ this.expr_as_place(block, value, mutability, fake_borrow_temps)
}),
ExprKind::Field { lhs, name } => {
- let place_builder = unpack!(block = this.as_place_builder(block, lhs));
+ let lhs = this.hir.mirror(lhs);
+ let place_builder = unpack!(block = this.expr_as_place(
+ block,
+ lhs,
+ mutability,
+ fake_borrow_temps,
+ ));
block.and(place_builder.field(name, expr.ty))
}
ExprKind::Deref { arg } => {
- let place_builder = unpack!(block = this.as_place_builder(block, arg));
+ let arg = this.hir.mirror(arg);
+ let place_builder = unpack!(block = this.expr_as_place(
+ block,
+ arg,
+ mutability,
+ fake_borrow_temps,
+ ));
block.and(place_builder.deref())
}
ExprKind::Index { lhs, index } => {
- let (usize_ty, bool_ty) = (this.hir.usize_ty(), this.hir.bool_ty());
-
- let place_builder = unpack!(block = this.as_place_builder(block, lhs));
- // Making this a *fresh* temporary also means we do not have to worry about
- // the index changing later: Nothing will ever change this temporary.
- // The "retagging" transformation (for Stacked Borrows) relies on this.
- let idx = unpack!(block = this.as_temp(
+ this.lower_index_expression(
block,
- expr.temp_lifetime,
+ lhs,
index,
- Mutability::Not,
- ));
-
- let slice = place_builder.clone().into_place(this.hir.tcx());
- // bounds check:
- let (len, lt) = (
- this.temp(usize_ty.clone(), expr_span),
- this.temp(bool_ty, expr_span),
- );
- this.cfg.push_assign(
- block,
- source_info, // len = len(slice)
- &len,
- Rvalue::Len(slice),
- );
- this.cfg.push_assign(
- block,
- source_info, // lt = idx < len
- <,
- Rvalue::BinaryOp(
- BinOp::Lt,
- Operand::Copy(Place::from(idx)),
- Operand::Copy(len.clone()),
- ),
- );
-
- let msg = BoundsCheck {
- len: Operand::Move(len),
- index: Operand::Copy(Place::from(idx)),
- };
- let success = this.assert(block, Operand::Move(lt), true, msg, expr_span);
- success.and(place_builder.index(idx))
+ mutability,
+ fake_borrow_temps,
+ expr.temp_lifetime,
+ expr_span,
+ source_info,
+ )
}
ExprKind::SelfRef => block.and(PlaceBuilder::from(Local::new(1))),
ExprKind::VarRef { id } => {
};
block.and(place_builder)
}
- ExprKind::StaticRef { id } => block.and(PlaceBuilder::from(
- PlaceBase::Static(Box::new(Static {
- ty: expr.ty,
- kind: StaticKind::Static,
- def_id: id,
- }))
- )),
ExprKind::PlaceTypeAscription { source, user_ty } => {
- let place_builder = unpack!(block = this.as_place_builder(block, source));
+ let source = this.hir.mirror(source);
+ let place_builder = unpack!(block = this.expr_as_place(
+ block,
+ source,
+ mutability,
+ fake_borrow_temps,
+ ));
if let Some(user_ty) = user_ty {
let annotation_index = this.canonical_user_type_annotations.push(
CanonicalUserTypeAnnotation {
| ExprKind::Continue { .. }
| ExprKind::Return { .. }
| ExprKind::Literal { .. }
+ | ExprKind::StaticRef { .. }
| ExprKind::InlineAsm { .. }
| ExprKind::Yield { .. }
| ExprKind::Call { .. } => {
}
}
}
+
+ /// Lower an index expression
+ ///
+ /// This has two complications;
+ ///
+ /// * We need to do a bounds check.
+ /// * We need to ensure that the bounds check can't be invalidated using an
+ /// expression like `x[1][{x = y; 2}]`. We use fake borrows here to ensure
+ /// that this is the case.
+ fn lower_index_expression(
+ &mut self,
+ mut block: BasicBlock,
+ base: ExprRef<'tcx>,
+ index: ExprRef<'tcx>,
+ mutability: Mutability,
+ fake_borrow_temps: Option<&mut Vec<Local>>,
+ temp_lifetime: Option<region::Scope>,
+ expr_span: Span,
+ source_info: SourceInfo
+ ) -> BlockAnd<PlaceBuilder<'tcx>> {
+ let lhs = self.hir.mirror(base);
+
+ let base_fake_borrow_temps = &mut Vec::new();
+ let is_outermost_index = fake_borrow_temps.is_none();
+ let fake_borrow_temps = fake_borrow_temps.unwrap_or(base_fake_borrow_temps);
+
+ let base_place = unpack!(block = self.expr_as_place(
+ block,
+ lhs,
+ mutability,
+ Some(fake_borrow_temps),
+ ));
+
+ // Making this a *fresh* temporary means we do not have to worry about
+ // the index changing later: Nothing will ever change this temporary.
+ // The "retagging" transformation (for Stacked Borrows) relies on this.
+ let idx = unpack!(block = self.as_temp(
+ block,
+ temp_lifetime,
+ index,
+ Mutability::Not,
+ ));
+
+ block = self.bounds_check(
+ block,
+ base_place.clone().into_place(self.hir.tcx()),
+ idx,
+ expr_span,
+ source_info,
+ );
+
+ if is_outermost_index {
+ self.read_fake_borrows(block, fake_borrow_temps, source_info)
+ } else {
+ self.add_fake_borrows_of_base(
+ &base_place,
+ block,
+ fake_borrow_temps,
+ expr_span,
+ source_info,
+ );
+ }
+
+ block.and(base_place.index(idx))
+ }
+
+ fn bounds_check(
+ &mut self,
+ block: BasicBlock,
+ slice: Place<'tcx>,
+ index: Local,
+ expr_span: Span,
+ source_info: SourceInfo,
+ ) -> BasicBlock {
+ let usize_ty = self.hir.usize_ty();
+ let bool_ty = self.hir.bool_ty();
+ // bounds check:
+ let len = self.temp(usize_ty, expr_span);
+ let lt = self.temp(bool_ty, expr_span);
+
+ // len = len(slice)
+ self.cfg.push_assign(
+ block,
+ source_info,
+ &len,
+ Rvalue::Len(slice),
+ );
+ // lt = idx < len
+ self.cfg.push_assign(
+ block,
+ source_info,
+ <,
+ Rvalue::BinaryOp(
+ BinOp::Lt,
+ Operand::Copy(Place::from(index)),
+ Operand::Copy(len.clone()),
+ ),
+ );
+ let msg = BoundsCheck {
+ len: Operand::Move(len),
+ index: Operand::Copy(Place::from(index)),
+ };
+ // assert!(lt, "...")
+ self.assert(block, Operand::Move(lt), true, msg, expr_span)
+ }
+
+ fn add_fake_borrows_of_base(
+ &mut self,
+ base_place: &PlaceBuilder<'tcx>,
+ block: BasicBlock,
+ fake_borrow_temps: &mut Vec<Local>,
+ expr_span: Span,
+ source_info: SourceInfo,
+ ) {
+ let tcx = self.hir.tcx();
+ let place_ty = Place::ty_from(
+ &base_place.base,
+ &base_place.projection,
+ &self.local_decls,
+ tcx,
+ );
+ if let ty::Slice(_) = place_ty.ty.kind {
+ // We need to create fake borrows to ensure that the bounds
+ // check that we just did stays valid. Since we can't assign to
+ // unsized values, we only need to ensure that none of the
+ // pointers in the base place are modified.
+ for (idx, elem) in base_place.projection.iter().enumerate().rev() {
+ match elem {
+ ProjectionElem::Deref => {
+ let fake_borrow_deref_ty = Place::ty_from(
+ &base_place.base,
+ &base_place.projection[..idx],
+ &self.local_decls,
+ tcx,
+ ).ty;
+ let fake_borrow_ty = tcx.mk_imm_ref(
+ tcx.lifetimes.re_erased,
+ fake_borrow_deref_ty,
+ );
+ let fake_borrow_temp = self.local_decls.push(
+ LocalDecl::new_temp(fake_borrow_ty, expr_span)
+ );
+ let projection = tcx.intern_place_elems(&base_place.projection[..idx]);
+ self.cfg.push_assign(
+ block,
+ source_info,
+ &fake_borrow_temp.into(),
+ Rvalue::Ref(
+ tcx.lifetimes.re_erased,
+ BorrowKind::Shallow,
+ Place {
+ base: base_place.base.clone(),
+ projection,
+ }
+ ),
+ );
+ fake_borrow_temps.push(fake_borrow_temp);
+ }
+ ProjectionElem::Index(_) => {
+ let index_ty = Place::ty_from(
+ &base_place.base,
+ &base_place.projection[..idx],
+ &self.local_decls,
+ tcx,
+ );
+ match index_ty.ty.kind {
+ // The previous index expression has already
+ // done any index expressions needed here.
+ ty::Slice(_) => break,
+ ty::Array(..) => (),
+ _ => bug!("unexpected index base"),
+ }
+ }
+ ProjectionElem::Field(..)
+ | ProjectionElem::Downcast(..)
+ | ProjectionElem::ConstantIndex { .. }
+ | ProjectionElem::Subslice { .. } => (),
+ }
+ }
+ }
+ }
+
+ fn read_fake_borrows(
+ &mut self,
+ bb: BasicBlock,
+ fake_borrow_temps: &mut Vec<Local>,
+ source_info: SourceInfo,
+ ) {
+ // All indexes have been evaluated now, read all of the
+ // fake borrows so that they are live across those index
+ // expressions.
+ for temp in fake_borrow_temps {
+ self.cfg.push_fake_read(bb, source_info, FakeReadCause::ForIndex, Place::from(*temp));
+ }
+ }
}