//! See docs in build/expr/mod.rs use crate::build::expr::category::Category; use crate::build::ForGuard::{OutsideGuard, RefWithinGuard}; use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::hair::*; use rustc::mir::interpret::InterpError::BoundsCheck; use rustc::mir::*; use rustc::ty::{CanonicalUserTypeAnnotation, Variance}; use rustc_data_structures::indexed_vec::Idx; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Compile `expr`, yielding a place that we can move from etc. pub fn as_place(&mut self, block: BasicBlock, expr: M) -> BlockAnd> where M: Mirror<'tcx, Output = Expr<'tcx>>, { let expr = self.hir.mirror(expr); self.expr_as_place(block, expr, Mutability::Mut) } /// Compile `expr`, yielding a place that we can move from etc. /// Mutability note: The caller of this method promises only to read from the resulting /// place. The place itself may or may not be mutable: /// * If this expr is a place expr like a.b, then we will return that place. /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary. pub fn as_read_only_place(&mut self, block: BasicBlock, expr: M) -> BlockAnd> where M: Mirror<'tcx, Output = Expr<'tcx>>, { let expr = self.hir.mirror(expr); self.expr_as_place(block, expr, Mutability::Not) } fn expr_as_place( &mut self, mut block: BasicBlock, expr: Expr<'tcx>, mutability: Mutability, ) -> BlockAnd> { debug!( "expr_as_place(block={:?}, expr={:?}, mutability={:?})", block, expr, mutability ); 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, } => this.in_scope((region_scope, source_info), lint_level, block, |this| { if mutability == Mutability::Not { this.as_read_only_place(block, value) } else { this.as_place(block, value) } }), ExprKind::Field { lhs, name } => { let place = unpack!(block = this.as_place(block, lhs)); let place = place.field(name, expr.ty); block.and(place) } ExprKind::Deref { arg } => { let place = unpack!(block = this.as_place(block, arg)); let place = place.deref(); block.and(place) } ExprKind::Index { lhs, index } => { let (usize_ty, bool_ty) = (this.hir.usize_ty(), this.hir.bool_ty()); let slice = unpack!(block = this.as_place(block, lhs)); // region_scope=None so place indexes live forever. They are scalars so they // do not need storage annotations, and they are often copied between // places. // 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(block, None, index, Mutability::Mut)); // 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.clone()), ); this.cfg.push_assign( block, source_info, // lt = idx < len <, Rvalue::BinaryOp( BinOp::Lt, Operand::Copy(Place::Base(PlaceBase::Local(idx))), Operand::Copy(len.clone()), ), ); let msg = BoundsCheck { len: Operand::Move(len), index: Operand::Copy(Place::Base(PlaceBase::Local(idx))), }; let success = this.assert(block, Operand::Move(lt), true, msg, expr_span); success.and(slice.index(idx)) } ExprKind::SelfRef => block.and(Place::Base(PlaceBase::Local(Local::new(1)))), ExprKind::VarRef { id } => { let place = if this.is_bound_var_in_guard(id) { let index = this.var_local_id(id, RefWithinGuard); Place::Base(PlaceBase::Local(index)).deref() } else { let index = this.var_local_id(id, OutsideGuard); Place::Base(PlaceBase::Local(index)) }; block.and(place) } ExprKind::StaticRef { id } => block.and(Place::Base(PlaceBase::Static(Box::new(Static { ty: expr.ty, kind: StaticKind::Static(id), })))), ExprKind::PlaceTypeAscription { source, user_ty } => { let place = unpack!(block = this.as_place(block, source)); if let Some(user_ty) = user_ty { let annotation_index = this.canonical_user_type_annotations.push( CanonicalUserTypeAnnotation { span: source_info.span, user_ty, inferred_ty: expr.ty, } ); this.cfg.push( block, Statement { source_info, kind: StatementKind::AscribeUserType( place.clone(), Variance::Invariant, box UserTypeProjection { base: annotation_index, projs: vec![], }, ), }, ); } block.and(place) } ExprKind::ValueTypeAscription { source, user_ty } => { let source = this.hir.mirror(source); let temp = unpack!( block = this.as_temp(block, source.temp_lifetime, source, mutability) ); if let Some(user_ty) = user_ty { let annotation_index = this.canonical_user_type_annotations.push( CanonicalUserTypeAnnotation { span: source_info.span, user_ty, inferred_ty: expr.ty, } ); this.cfg.push( block, Statement { source_info, kind: StatementKind::AscribeUserType( Place::Base(PlaceBase::Local(temp.clone())), Variance::Invariant, box UserTypeProjection { base: annotation_index, projs: vec![], }, ), }, ); } block.and(Place::Base(PlaceBase::Local(temp))) } ExprKind::Array { .. } | ExprKind::Tuple { .. } | ExprKind::Adt { .. } | ExprKind::Closure { .. } | ExprKind::Unary { .. } | ExprKind::Binary { .. } | ExprKind::LogicalOp { .. } | ExprKind::Box { .. } | ExprKind::Cast { .. } | ExprKind::Use { .. } | ExprKind::NeverToAny { .. } | ExprKind::Pointer { .. } | ExprKind::Repeat { .. } | ExprKind::Borrow { .. } | ExprKind::If { .. } | ExprKind::Match { .. } | ExprKind::Loop { .. } | ExprKind::Block { .. } | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } | ExprKind::Break { .. } | ExprKind::Continue { .. } | ExprKind::Return { .. } | ExprKind::Literal { .. } | ExprKind::InlineAsm { .. } | ExprKind::Yield { .. } | ExprKind::Call { .. } => { // these are not places, so we need to make a temporary. debug_assert!(match Category::of(&expr.kind) { Some(Category::Place) => false, _ => true, }); let temp = unpack!(block = this.as_temp(block, expr.temp_lifetime, expr, mutability)); block.and(Place::Base(PlaceBase::Local(temp))) } } } }