]> git.lizzy.rs Git - rust.git/blob - src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs
b76931c301748c44cc995473755bb1547833c017
[rust.git] / src / librustc_borrowck / borrowck / gather_loans / gather_moves.rs
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.
4 //
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.
10
11 //! Computes moves.
12
13 use borrowck::*;
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};
22
23 use std::rc::Rc;
24 use syntax::ast;
25 use syntax_pos::Span;
26 use rustc::hir::*;
27 use rustc::hir::map::Node::*;
28
29 struct GatherMoveInfo<'c, 'tcx: 'c> {
30     id: hir::ItemLocalId,
31     kind: MoveKind,
32     cmt: &'c mc::cmt_<'tcx>,
33     span_path_opt: Option<MovePlace<'tcx>>
34 }
35
36 /// Represents the kind of pattern
37 #[derive(Debug, Clone, Copy)]
38 pub enum PatternSource<'tcx> {
39     MatchExpr(&'tcx Expr),
40     LetDecl(&'tcx Local),
41     Other,
42 }
43
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:
47 ///
48 ///     let (a, b) = v[0]; // like the `a` and `b` patterns here
49 ///     match v[0] { a => ... } // or the `a` pattern here
50 ///
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:
53 ///
54 ///     let x = v[0]; // suggest `&v[0]` here
55 ///
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> {
59
60     let parent = tcx.hir.get_parent_node(pat.id);
61
62     match tcx.hir.get(parent) {
63         NodeExpr(ref e) => {
64             // the enclosing expression must be a `match` or something else
65             assert!(match e.node {
66                         ExprKind::Match(..) => true,
67                         _ => return PatternSource::Other,
68                     });
69             PatternSource::MatchExpr(e)
70         }
71         NodeLocal(local) => PatternSource::LetDecl(local),
72         _ => return PatternSource::Other,
73
74     }
75 }
76
77 pub fn gather_decl<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
78                              move_data: &MoveData<'tcx>,
79                              var_id: ast::NodeId,
80                              var_ty: Ty<'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);
84 }
85
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,
90                                        cmt: &mc::cmt_<'tcx>,
91                                        move_reason: euv::MoveReason) {
92     let kind = match move_reason {
93         euv::DirectRefMove | euv::PatBindingMove => MoveExpr,
94         euv::CaptureMove => Captured
95     };
96     let move_info = GatherMoveInfo {
97         id: move_expr_id,
98         kind,
99         cmt,
100         span_path_opt: None,
101     };
102     gather_move(bccx, move_data, move_error_collector, move_info);
103 }
104
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>,
108                                               move_pat: &hir::Pat,
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, _) => {
113             Some(MovePlace {
114                      span: move_pat.span,
115                      name: ident.name,
116                      pat_source: source,
117                  })
118         }
119         _ => None,
120     };
121     let move_info = GatherMoveInfo {
122         id: move_pat.hir_id.local_id,
123         kind: MovePat,
124         cmt,
125         span_path_opt: pat_span_path_opt,
126     };
127
128     debug!("gather_move_from_pat: move_pat={:?} source={:?}",
129            move_pat,
130            source);
131
132     gather_move(bccx, move_data, move_error_collector, move_info);
133 }
134
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);
141
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);
148         return;
149     }
150
151     match opt_loan_path(&move_info.cmt) {
152         Some(loan_path) => {
153             move_data.add_move(bccx.tcx, loan_path,
154                                move_info.id, move_info.kind);
155         }
156         None => {
157             // move from rvalue or raw pointer, hence ok
158         }
159     }
160 }
161
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,
168                              assignee_loan_path,
169                              assignment_id,
170                              assignment_span);
171 }
172
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>> {
177     match cmt.cat {
178         Categorization::Deref(_, mc::BorrowedPtr(..)) |
179         Categorization::Deref(_, mc::UnsafePtr(..)) |
180         Categorization::StaticItem => {
181             Some(cmt.clone())
182         }
183
184         Categorization::Rvalue(..) |
185         Categorization::Local(..) |
186         Categorization::Upvar(..) => {
187             None
188         }
189
190         Categorization::Downcast(ref b, _) |
191         Categorization::Interior(ref b, mc::InteriorField(_)) |
192         Categorization::Interior(ref b, mc::InteriorElement(Kind::Pattern)) => {
193             match b.ty.sty {
194                 ty::TyAdt(def, _) => {
195                     if def.has_dtor(bccx.tcx) {
196                         Some(cmt.clone())
197                     } else {
198                         check_and_get_illegal_move_origin(bccx, b)
199                     }
200                 }
201                 ty::TySlice(..) => Some(cmt.clone()),
202                 _ => {
203                     check_and_get_illegal_move_origin(bccx, b)
204                 }
205             }
206         }
207
208         Categorization::Interior(_, mc::InteriorElement(Kind::Index)) => {
209             // Forbid move of arr[i] for arr: [T; 3]; see RFC 533.
210             Some(cmt.clone())
211         }
212
213         Categorization::Deref(ref b, mc::Unique) => {
214             check_and_get_illegal_move_origin(bccx, b)
215         }
216     }
217 }