1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
14 use borrowck::gather_loans::move_error::MovePlace;
15 use borrowck::gather_loans::move_error::{MoveError, MoveErrorCollector};
16 use borrowck::move_data::*;
17 use rustc::middle::expr_use_visitor as euv;
18 use rustc::middle::mem_categorization as mc;
19 use rustc::middle::mem_categorization::Categorization;
20 use rustc::middle::mem_categorization::InteriorOffsetKind as Kind;
21 use rustc::ty::{self, Ty};
27 use rustc::hir::map::Node::*;
29 struct GatherMoveInfo<'c, 'tcx: 'c> {
32 cmt: &'c mc::cmt_<'tcx>,
33 span_path_opt: Option<MovePlace<'tcx>>
36 /// Represents the kind of pattern
37 #[derive(Debug, Clone, Copy)]
38 pub enum PatternSource<'tcx> {
39 MatchExpr(&'tcx Expr),
44 /// Analyzes the context where the pattern appears to determine the
45 /// kind of hint we want to give. In particular, if the pattern is in a `match`
46 /// or nested within other patterns, we want to suggest a `ref` binding:
48 /// let (a, b) = v[0]; // like the `a` and `b` patterns here
49 /// match v[0] { a => ... } // or the `a` pattern here
51 /// But if the pattern is the outermost pattern in a `let`, we would rather
52 /// suggest that the author add a `&` to the initializer:
54 /// let x = v[0]; // suggest `&v[0]` here
56 /// In this latter case, this function will return `PatternSource::LetDecl`
57 /// with a reference to the let
58 fn get_pattern_source<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pat: &Pat) -> PatternSource<'tcx> {
60 let parent = tcx.hir.get_parent_node(pat.id);
62 match tcx.hir.get(parent) {
64 // the enclosing expression must be a `match` or something else
65 assert!(match e.node {
66 ExprKind::Match(..) => true,
67 _ => return PatternSource::Other,
69 PatternSource::MatchExpr(e)
71 NodeLocal(local) => PatternSource::LetDecl(local),
72 _ => return PatternSource::Other,
77 pub fn gather_decl<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
78 move_data: &MoveData<'tcx>,
81 let loan_path = Rc::new(LoanPath::new(LpVar(var_id), var_ty));
82 let hir_id = bccx.tcx.hir.node_to_hir_id(var_id);
83 move_data.add_move(bccx.tcx, loan_path, hir_id.local_id, Declared);
86 pub fn gather_move_from_expr<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
87 move_data: &MoveData<'tcx>,
88 move_error_collector: &mut MoveErrorCollector<'tcx>,
89 move_expr_id: hir::ItemLocalId,
91 move_reason: euv::MoveReason) {
92 let kind = match move_reason {
93 euv::DirectRefMove | euv::PatBindingMove => MoveExpr,
94 euv::CaptureMove => Captured
96 let move_info = GatherMoveInfo {
102 gather_move(bccx, move_data, move_error_collector, move_info);
105 pub fn gather_move_from_pat<'a, 'c, 'tcx: 'c>(bccx: &BorrowckCtxt<'a, 'tcx>,
106 move_data: &MoveData<'tcx>,
107 move_error_collector: &mut MoveErrorCollector<'tcx>,
109 cmt: &'c mc::cmt_<'tcx>) {
110 let source = get_pattern_source(bccx.tcx,move_pat);
111 let pat_span_path_opt = match move_pat.node {
112 PatKind::Binding(_, _, ident, _) => {
121 let move_info = GatherMoveInfo {
122 id: move_pat.hir_id.local_id,
125 span_path_opt: pat_span_path_opt,
128 debug!("gather_move_from_pat: move_pat={:?} source={:?}",
132 gather_move(bccx, move_data, move_error_collector, move_info);
135 fn gather_move<'a, 'c, 'tcx: 'c>(bccx: &BorrowckCtxt<'a, 'tcx>,
136 move_data: &MoveData<'tcx>,
137 move_error_collector: &mut MoveErrorCollector<'tcx>,
138 move_info: GatherMoveInfo<'c, 'tcx>) {
139 debug!("gather_move(move_id={:?}, cmt={:?})",
140 move_info.id, move_info.cmt);
142 let potentially_illegal_move = check_and_get_illegal_move_origin(bccx, move_info.cmt);
143 if let Some(illegal_move_origin) = potentially_illegal_move {
144 debug!("illegal_move_origin={:?}", illegal_move_origin);
145 let error = MoveError::with_move_info(Rc::new(illegal_move_origin),
146 move_info.span_path_opt);
147 move_error_collector.add_error(error);
151 match opt_loan_path(&move_info.cmt) {
153 move_data.add_move(bccx.tcx, loan_path,
154 move_info.id, move_info.kind);
157 // move from rvalue or raw pointer, hence ok
162 pub fn gather_assignment<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
163 move_data: &MoveData<'tcx>,
164 assignment_id: hir::ItemLocalId,
165 assignment_span: Span,
166 assignee_loan_path: Rc<LoanPath<'tcx>>) {
167 move_data.add_assignment(bccx.tcx,
173 // (keep in sync with move_error::report_cannot_move_out_of )
174 fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
175 cmt: &mc::cmt_<'tcx>)
176 -> Option<mc::cmt_<'tcx>> {
178 Categorization::Deref(_, mc::BorrowedPtr(..)) |
179 Categorization::Deref(_, mc::UnsafePtr(..)) |
180 Categorization::StaticItem => {
184 Categorization::Rvalue(..) |
185 Categorization::Local(..) |
186 Categorization::Upvar(..) => {
190 Categorization::Downcast(ref b, _) |
191 Categorization::Interior(ref b, mc::InteriorField(_)) |
192 Categorization::Interior(ref b, mc::InteriorElement(Kind::Pattern)) => {
194 ty::TyAdt(def, _) => {
195 if def.has_dtor(bccx.tcx) {
198 check_and_get_illegal_move_origin(bccx, b)
201 ty::TySlice(..) => Some(cmt.clone()),
203 check_and_get_illegal_move_origin(bccx, b)
208 Categorization::Interior(_, mc::InteriorElement(Kind::Index)) => {
209 // Forbid move of arr[i] for arr: [T; 3]; see RFC 533.
213 Categorization::Deref(ref b, mc::Unique) => {
214 check_and_get_illegal_move_origin(bccx, b)