]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/upvar.rs
Auto merge of #21158 - alkor:issue-21131, r=nick29581
[rust.git] / src / librustc_typeck / check / upvar.rs
1 // Copyright 2012-2014 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 //! ### Inferring borrow kinds for upvars
12 //!
13 //! Whenever there is a closure expression, we need to determine how each
14 //! upvar is used. We do this by initially assigning each upvar an
15 //! immutable "borrow kind" (see `ty::BorrowKind` for details) and then
16 //! "escalating" the kind as needed. The borrow kind proceeds according to
17 //! the following lattice:
18 //!
19 //!     ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow
20 //!
21 //! So, for example, if we see an assignment `x = 5` to an upvar `x`, we
22 //! will promote its borrow kind to mutable borrow. If we see an `&mut x`
23 //! we'll do the same. Naturally, this applies not just to the upvar, but
24 //! to everything owned by `x`, so the result is the same for something
25 //! like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a
26 //! struct). These adjustments are performed in
27 //! `adjust_upvar_borrow_kind()` (you can trace backwards through the code
28 //! from there).
29 //!
30 //! The fact that we are inferring borrow kinds as we go results in a
31 //! semi-hacky interaction with mem-categorization. In particular,
32 //! mem-categorization will query the current borrow kind as it
33 //! categorizes, and we'll return the *current* value, but this may get
34 //! adjusted later. Therefore, in this module, we generally ignore the
35 //! borrow kind (and derived mutabilities) that are returned from
36 //! mem-categorization, since they may be inaccurate. (Another option
37 //! would be to use a unification scheme, where instead of returning a
38 //! concrete borrow kind like `ty::ImmBorrow`, we return a
39 //! `ty::InferBorrow(upvar_id)` or something like that, but this would
40 //! then mean that all later passes would have to check for these figments
41 //! and report an error, and it just seems like more mess in the end.)
42
43 use super::FnCtxt;
44
45 use middle::expr_use_visitor as euv;
46 use middle::mem_categorization as mc;
47 use middle::ty::{self};
48 use middle::infer::{InferCtxt, UpvarRegion};
49 use syntax::ast;
50 use syntax::codemap::Span;
51 use syntax::visit::{self, Visitor};
52 use util::ppaux::Repr;
53
54 ///////////////////////////////////////////////////////////////////////////
55 // PUBLIC ENTRY POINTS
56
57 pub fn closure_analyze_fn(fcx: &FnCtxt,
58                           _id: ast::NodeId,
59                           decl: &ast::FnDecl,
60                           body: &ast::Block) {
61     let mut seed = SeedBorrowKind::new(fcx);
62     seed.visit_block(body);
63
64     let mut adjust = AdjustBorrowKind::new(fcx);
65     adjust.analyze_fn(decl, body);
66 }
67
68 ///////////////////////////////////////////////////////////////////////////
69 // SEED BORROW KIND
70
71 struct SeedBorrowKind<'a,'tcx:'a> {
72     fcx: &'a FnCtxt<'a,'tcx>,
73 }
74
75 impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> {
76     fn visit_expr(&mut self, expr: &ast::Expr) {
77         match expr.node {
78             ast::ExprClosure(cc, _, _, ref body) => {
79                 self.check_closure(expr, cc, &**body);
80             }
81
82             _ => { }
83         }
84
85         visit::walk_expr(self, expr);
86     }
87
88     fn visit_fn(&mut self,
89                 fn_kind: visit::FnKind<'v>,
90                 decl: &'v ast::FnDecl,
91                 block: &'v ast::Block,
92                 span: Span,
93                 _id: ast::NodeId)
94     {
95         match fn_kind {
96             visit::FkItemFn(..) | visit::FkMethod(..) => {
97                 // ignore nested fn items
98             }
99             visit::FkFnBlock => {
100                 visit::walk_fn(self, fn_kind, decl, block, span);
101             }
102         }
103     }
104 }
105
106 impl<'a,'tcx> SeedBorrowKind<'a,'tcx> {
107     fn new(fcx: &'a FnCtxt<'a,'tcx>) -> SeedBorrowKind<'a,'tcx> {
108         SeedBorrowKind { fcx: fcx }
109     }
110
111     fn tcx(&self) -> &'a ty::ctxt<'tcx> {
112         self.fcx.tcx()
113     }
114
115     fn infcx(&self) -> &'a InferCtxt<'a,'tcx> {
116         self.fcx.infcx()
117     }
118
119     fn check_closure(&mut self,
120                      expr: &ast::Expr,
121                      capture_clause: ast::CaptureClause,
122                      _body: &ast::Block)
123     {
124         match capture_clause {
125             ast::CaptureByValue => {}
126             _ => {
127                 ty::with_freevars(self.tcx(), expr.id, |freevars| {
128                     for freevar in freevars.iter() {
129                         let var_node_id = freevar.def.local_node_id();
130                         let upvar_id = ty::UpvarId { var_id: var_node_id,
131                                                      closure_expr_id: expr.id };
132                         debug!("seed upvar_id {:?}", upvar_id);
133                         let origin = UpvarRegion(upvar_id, expr.span);
134                         let freevar_region = self.infcx().next_region_var(origin);
135                         let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow,
136                                                              region: freevar_region };
137                         self.fcx.inh.upvar_borrow_map.borrow_mut().insert(upvar_id,
138                                                                           upvar_borrow);
139                     }
140                 });
141             }
142         }
143     }
144 }
145
146 ///////////////////////////////////////////////////////////////////////////
147 // ADJUST BORROW KIND
148
149 struct AdjustBorrowKind<'a,'tcx:'a> {
150     fcx: &'a FnCtxt<'a,'tcx>
151 }
152
153 impl<'a,'tcx> AdjustBorrowKind<'a,'tcx>{
154     fn new(fcx: &'a FnCtxt<'a,'tcx>) -> AdjustBorrowKind<'a,'tcx> {
155         AdjustBorrowKind { fcx: fcx }
156     }
157
158     fn tcx(&self) -> &'a ty::ctxt<'tcx> {
159         self.fcx.tcx()
160     }
161
162     fn analyze_fn(&mut self, decl: &ast::FnDecl, body: &ast::Block) {
163         /*!
164          * Analysis starting point.
165          */
166
167         self.visit_block(body);
168
169         debug!("analyzing fn body with id {}", body.id);
170
171         let mut euv = euv::ExprUseVisitor::new(self, self.fcx);
172         euv.walk_fn(decl, body);
173     }
174
175     /// Indicates that `cmt` is being directly mutated (e.g., assigned
176     /// to). If cmt contains any by-ref upvars, this implies that
177     /// those upvars must be borrowed using an `&mut` borow.
178     fn adjust_upvar_borrow_kind_for_mut(&mut self, cmt: mc::cmt<'tcx>) {
179         debug!("adjust_upvar_borrow_kind_for_mut(cmt={})",
180                cmt.repr(self.tcx()));
181
182         match cmt.cat.clone() {
183             mc::cat_deref(base, _, mc::Unique) |
184             mc::cat_interior(base, _) |
185             mc::cat_downcast(base, _) => {
186                 // Interior or owned data is mutable if base is
187                 // mutable, so iterate to the base.
188                 self.adjust_upvar_borrow_kind_for_mut(base);
189             }
190
191             mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
192             mc::cat_deref(base, _, mc::Implicit(..)) => {
193                 if let mc::NoteUpvarRef(upvar_id) = cmt.note {
194                     // if this is an implicit deref of an
195                     // upvar, then we need to modify the
196                     // borrow_kind of the upvar to make sure it
197                     // is inferred to mutable if necessary
198                     let mut upvar_borrow_map = self.fcx.inh.upvar_borrow_map.borrow_mut();
199                     let ub = &mut upvar_borrow_map[upvar_id];
200                     self.adjust_upvar_borrow_kind(upvar_id, ub, ty::MutBorrow);
201                 } else {
202                     // assignment to deref of an `&mut`
203                     // borrowed pointer implies that the
204                     // pointer itself must be unique, but not
205                     // necessarily *mutable*
206                     self.adjust_upvar_borrow_kind_for_unique(base);
207                 }
208             }
209
210             mc::cat_deref(_, _, mc::UnsafePtr(..)) |
211             mc::cat_static_item |
212             mc::cat_rvalue(_) |
213             mc::cat_local(_) |
214             mc::cat_upvar(..) => {
215                 return;
216             }
217         }
218     }
219
220     fn adjust_upvar_borrow_kind_for_unique(&self, cmt: mc::cmt<'tcx>) {
221         debug!("adjust_upvar_borrow_kind_for_unique(cmt={})",
222                cmt.repr(self.tcx()));
223
224         match cmt.cat.clone() {
225             mc::cat_deref(base, _, mc::Unique) |
226             mc::cat_interior(base, _) |
227             mc::cat_downcast(base, _) => {
228                 // Interior or owned data is unique if base is
229                 // unique.
230                 self.adjust_upvar_borrow_kind_for_unique(base);
231             }
232
233             mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
234             mc::cat_deref(base, _, mc::Implicit(..)) => {
235                 if let mc::NoteUpvarRef(upvar_id) = cmt.note {
236                     // if this is an implicit deref of an
237                     // upvar, then we need to modify the
238                     // borrow_kind of the upvar to make sure it
239                     // is inferred to unique if necessary
240                     let mut ub = self.fcx.inh.upvar_borrow_map.borrow_mut();
241                     let ub = &mut ub[upvar_id];
242                     self.adjust_upvar_borrow_kind(upvar_id, ub, ty::UniqueImmBorrow);
243                 } else {
244                     // for a borrowed pointer to be unique, its
245                     // base must be unique
246                     self.adjust_upvar_borrow_kind_for_unique(base);
247                 }
248             }
249
250             mc::cat_deref(_, _, mc::UnsafePtr(..)) |
251             mc::cat_static_item |
252             mc::cat_rvalue(_) |
253             mc::cat_local(_) |
254             mc::cat_upvar(..) => {
255             }
256         }
257     }
258
259     /// We infer the borrow_kind with which to borrow upvars in a stack closure. The borrow_kind
260     /// basically follows a lattice of `imm < unique-imm < mut`, moving from left to right as needed
261     /// (but never right to left). Here the argument `mutbl` is the borrow_kind that is required by
262     /// some particular use.
263     fn adjust_upvar_borrow_kind(&self,
264                                 upvar_id: ty::UpvarId,
265                                 upvar_borrow: &mut ty::UpvarBorrow,
266                                 kind: ty::BorrowKind) {
267         debug!("adjust_upvar_borrow_kind: id={:?} kind=({:?} -> {:?})",
268                upvar_id, upvar_borrow.kind, kind);
269
270         match (upvar_borrow.kind, kind) {
271             // Take RHS:
272             (ty::ImmBorrow, ty::UniqueImmBorrow) |
273             (ty::ImmBorrow, ty::MutBorrow) |
274             (ty::UniqueImmBorrow, ty::MutBorrow) => {
275                 upvar_borrow.kind = kind;
276             }
277             // Take LHS:
278             (ty::ImmBorrow, ty::ImmBorrow) |
279             (ty::UniqueImmBorrow, ty::ImmBorrow) |
280             (ty::UniqueImmBorrow, ty::UniqueImmBorrow) |
281             (ty::MutBorrow, _) => {
282             }
283         }
284     }
285 }
286
287 impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> {
288     fn visit_fn(&mut self,
289                 fn_kind: visit::FnKind<'v>,
290                 decl: &'v ast::FnDecl,
291                 body: &'v ast::Block,
292                 span: Span,
293                 _id: ast::NodeId)
294     {
295         match fn_kind {
296             visit::FkItemFn(..) | visit::FkMethod(..) => {
297                 // ignore nested fn items
298             }
299             visit::FkFnBlock => {
300                 self.analyze_fn(decl, body);
301                 visit::walk_fn(self, fn_kind, decl, body, span);
302             }
303         }
304     }
305 }
306
307 impl<'a,'tcx> euv::Delegate<'tcx> for AdjustBorrowKind<'a,'tcx> {
308     fn consume(&mut self,
309                _consume_id: ast::NodeId,
310                _consume_span: Span,
311                _cmt: mc::cmt<'tcx>,
312                _mode: euv::ConsumeMode)
313     {}
314
315     fn matched_pat(&mut self,
316                    _matched_pat: &ast::Pat,
317                    _cmt: mc::cmt<'tcx>,
318                    _mode: euv::MatchMode)
319     {}
320
321     fn consume_pat(&mut self,
322                    _consume_pat: &ast::Pat,
323                    _cmt: mc::cmt<'tcx>,
324                    _mode: euv::ConsumeMode)
325     {}
326
327     fn borrow(&mut self,
328               borrow_id: ast::NodeId,
329               _borrow_span: Span,
330               cmt: mc::cmt<'tcx>,
331               _loan_region: ty::Region,
332               bk: ty::BorrowKind,
333               _loan_cause: euv::LoanCause)
334     {
335         debug!("borrow(borrow_id={}, cmt={}, bk={:?})",
336                borrow_id, cmt.repr(self.tcx()), bk);
337
338         match bk {
339             ty::ImmBorrow => { }
340             ty::UniqueImmBorrow => {
341                 self.adjust_upvar_borrow_kind_for_unique(cmt);
342             }
343             ty::MutBorrow => {
344                 self.adjust_upvar_borrow_kind_for_mut(cmt);
345             }
346         }
347     }
348
349     fn decl_without_init(&mut self,
350                          _id: ast::NodeId,
351                          _span: Span)
352     {}
353
354     fn mutate(&mut self,
355               _assignment_id: ast::NodeId,
356               _assignment_span: Span,
357               assignee_cmt: mc::cmt<'tcx>,
358               _mode: euv::MutateMode)
359     {
360         debug!("mutate(assignee_cmt={})",
361                assignee_cmt.repr(self.tcx()));
362
363         self.adjust_upvar_borrow_kind_for_mut(assignee_cmt);
364     }
365 }
366
367