blob: 09b33c6654a9db084a0bc431d1ed99bc81e54ec8 [file] [log] [blame]
//! 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::{PanicInfo::BoundsCheck};
use rustc::mir::*;
use rustc::ty::{CanonicalUserTypeAnnotation, Variance};
use rustc_data_structures::indexed_vec::Idx;
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Compile `expr`, yielding a place that we can move from etc.
pub fn as_place<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
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<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
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<Place<'tcx>> {
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, |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));
// 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,
expr.temp_lifetime,
index,
Mutability::Not,
));
// 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
&lt,
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(slice.index(idx))
}
ExprKind::SelfRef => block.and(Place::from(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::from(index).deref()
} else {
let index = this.var_local_id(id, OutsideGuard);
Place::from(index)
};
block.and(place)
}
ExprKind::StaticRef { id } => block.and(Place {
base: PlaceBase::Static(Box::new(Static {
ty: expr.ty,
kind: StaticKind::Static,
def_id: id,
})),
projection: box [],
}),
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(
box(
place.clone(),
UserTypeProjection { base: annotation_index, projs: vec![], }
),
Variance::Invariant,
),
},
);
}
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(
box(
Place::from(temp.clone()),
UserTypeProjection { base: annotation_index, projs: vec![], },
),
Variance::Invariant,
),
},
);
}
block.and(Place::from(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::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::from(temp))
}
}
}
}