blob: 658e4307348db02fd4530156c6ae2530aed3127f [file] [log] [blame]
//! Computes moves.
use crate::borrowck::*;
use crate::borrowck::gather_loans::move_error::MovePlace;
use crate::borrowck::gather_loans::move_error::{MoveError, MoveErrorCollector};
use crate::borrowck::move_data::*;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::mem_categorization as mc;
use rustc::middle::mem_categorization::Categorization;
use rustc::middle::mem_categorization::InteriorOffsetKind as Kind;
use rustc::ty::{self, Ty};
use std::rc::Rc;
use syntax_pos::Span;
use rustc::hir::*;
use rustc::hir::Node;
use log::debug;
struct GatherMoveInfo<'c, 'tcx> {
id: hir::ItemLocalId,
kind: MoveKind,
cmt: &'c mc::cmt_<'tcx>,
span_path_opt: Option<MovePlace<'tcx>>,
}
/// Represents the kind of pattern
#[derive(Debug, Clone, Copy)]
pub enum PatternSource<'tcx> {
MatchExpr(&'tcx Expr),
LetDecl(&'tcx Local),
Other,
}
/// Analyzes the context where the pattern appears to determine the
/// kind of hint we want to give. In particular, if the pattern is in a `match`
/// or nested within other patterns, we want to suggest a `ref` binding:
///
/// let (a, b) = v[0]; // like the `a` and `b` patterns here
/// match v[0] { a => ... } // or the `a` pattern here
///
/// But if the pattern is the outermost pattern in a `let`, we would rather
/// suggest that the author add a `&` to the initializer:
///
/// let x = v[0]; // suggest `&v[0]` here
///
/// In this latter case, this function will return `PatternSource::LetDecl`
/// with a reference to the let
fn get_pattern_source<'tcx>(tcx: TyCtxt<'tcx>, pat: &Pat) -> PatternSource<'tcx> {
let parent = tcx.hir().get_parent_node(pat.hir_id);
match tcx.hir().get(parent) {
Node::Expr(ref e) => {
// the enclosing expression must be a `match` or something else
assert!(match e.node {
ExprKind::Match(..) => true,
_ => return PatternSource::Other,
});
PatternSource::MatchExpr(e)
}
Node::Local(local) => PatternSource::LetDecl(local),
_ => return PatternSource::Other,
}
}
pub fn gather_decl<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
move_data: &MoveData<'tcx>,
var_id: hir::HirId,
var_ty: Ty<'tcx>) {
let loan_path = Rc::new(LoanPath::new(LpVar(var_id), var_ty));
move_data.add_move(bccx.tcx, loan_path, var_id.local_id, Declared);
}
pub fn gather_move_from_expr<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
move_data: &MoveData<'tcx>,
move_error_collector: &mut MoveErrorCollector<'tcx>,
move_expr_id: hir::ItemLocalId,
cmt: &mc::cmt_<'tcx>,
move_reason: euv::MoveReason) {
let kind = match move_reason {
euv::DirectRefMove | euv::PatBindingMove => MoveExpr,
euv::CaptureMove => Captured
};
let move_info = GatherMoveInfo {
id: move_expr_id,
kind,
cmt,
span_path_opt: None,
};
gather_move(bccx, move_data, move_error_collector, move_info);
}
pub fn gather_move_from_pat<'a, 'c, 'tcx>(
bccx: &BorrowckCtxt<'a, 'tcx>,
move_data: &MoveData<'tcx>,
move_error_collector: &mut MoveErrorCollector<'tcx>,
move_pat: &hir::Pat,
cmt: &'c mc::cmt_<'tcx>,
) {
let source = get_pattern_source(bccx.tcx,move_pat);
let pat_span_path_opt = match move_pat.node {
PatKind::Binding(_, _, ident, _) => {
Some(MovePlace {
span: move_pat.span,
name: ident.name,
pat_source: source,
})
}
_ => None,
};
let move_info = GatherMoveInfo {
id: move_pat.hir_id.local_id,
kind: MovePat,
cmt,
span_path_opt: pat_span_path_opt,
};
debug!("gather_move_from_pat: move_pat={:?} source={:?}",
move_pat,
source);
gather_move(bccx, move_data, move_error_collector, move_info);
}
fn gather_move<'a, 'c, 'tcx>(
bccx: &BorrowckCtxt<'a, 'tcx>,
move_data: &MoveData<'tcx>,
move_error_collector: &mut MoveErrorCollector<'tcx>,
move_info: GatherMoveInfo<'c, 'tcx>,
) {
debug!("gather_move(move_id={:?}, cmt={:?})",
move_info.id, move_info.cmt);
let potentially_illegal_move = check_and_get_illegal_move_origin(bccx, move_info.cmt);
if let Some(illegal_move_origin) = potentially_illegal_move {
debug!("illegal_move_origin={:?}", illegal_move_origin);
let error = MoveError::with_move_info(Rc::new(illegal_move_origin),
move_info.span_path_opt);
move_error_collector.add_error(error);
return;
}
match opt_loan_path(&move_info.cmt) {
Some(loan_path) => {
move_data.add_move(bccx.tcx, loan_path,
move_info.id, move_info.kind);
}
None => {
// move from rvalue or raw pointer, hence ok
}
}
}
pub fn gather_assignment<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
move_data: &MoveData<'tcx>,
assignment_id: hir::ItemLocalId,
assignment_span: Span,
assignee_loan_path: Rc<LoanPath<'tcx>>) {
move_data.add_assignment(bccx.tcx,
assignee_loan_path,
assignment_id,
assignment_span);
}
// (keep in sync with move_error::report_cannot_move_out_of )
fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
cmt: &mc::cmt_<'tcx>)
-> Option<mc::cmt_<'tcx>> {
match cmt.cat {
Categorization::Deref(_, mc::BorrowedPtr(..)) |
Categorization::Deref(_, mc::UnsafePtr(..)) |
Categorization::ThreadLocal(..) |
Categorization::StaticItem => {
Some(cmt.clone())
}
Categorization::Rvalue(..) |
Categorization::Local(..) |
Categorization::Upvar(..) => {
None
}
Categorization::Downcast(ref b, _) |
Categorization::Interior(ref b, mc::InteriorField(_)) |
Categorization::Interior(ref b, mc::InteriorElement(Kind::Pattern)) => {
match b.ty.sty {
ty::Adt(def, _) => {
if def.has_dtor(bccx.tcx) {
Some(cmt.clone())
} else {
check_and_get_illegal_move_origin(bccx, b)
}
}
ty::Slice(..) => Some(cmt.clone()),
_ => {
check_and_get_illegal_move_origin(bccx, b)
}
}
}
Categorization::Interior(_, mc::InteriorElement(Kind::Index)) => {
// Forbid move of arr[i] for arr: [T; 3]; see RFC 533.
Some(cmt.clone())
}
Categorization::Deref(ref b, mc::Unique) => {
check_and_get_illegal_move_origin(bccx, b)
}
}
}