]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/upvar.rs
Remove the capture mode map and just store the capture mode for individual variables.
[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         ty::with_freevars(self.tcx(), expr.id, |freevars| {
125             for freevar in freevars.iter() {
126                 let var_node_id = freevar.def.local_node_id();
127                 let upvar_id = ty::UpvarId { var_id: var_node_id,
128                                              closure_expr_id: expr.id };
129                 debug!("seed upvar_id {:?}", upvar_id);
130
131                 let capture_kind = match capture_clause {
132                     ast::CaptureByValue => {
133                         ty::UpvarCapture::ByValue
134                     }
135                     ast::CaptureByRef => {
136                         let origin = UpvarRegion(upvar_id, expr.span);
137                         let freevar_region = self.infcx().next_region_var(origin);
138                         let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow,
139                                                              region: freevar_region };
140                         ty::UpvarCapture::ByRef(upvar_borrow)
141                     }
142                 };
143
144                 self.fcx.inh.upvar_capture_map.borrow_mut().insert(upvar_id, capture_kind);
145             }
146         });
147     }
148 }
149
150 ///////////////////////////////////////////////////////////////////////////
151 // ADJUST BORROW KIND
152
153 struct AdjustBorrowKind<'a,'tcx:'a> {
154     fcx: &'a FnCtxt<'a,'tcx>
155 }
156
157 impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
158     fn new(fcx: &'a FnCtxt<'a,'tcx>) -> AdjustBorrowKind<'a,'tcx> {
159         AdjustBorrowKind { fcx: fcx }
160     }
161
162     fn tcx(&self) -> &'a ty::ctxt<'tcx> {
163         self.fcx.tcx()
164     }
165
166     fn analyze_fn(&mut self, decl: &ast::FnDecl, body: &ast::Block) {
167         /*!
168          * Analysis starting point.
169          */
170
171         self.visit_block(body);
172
173         debug!("analyzing fn body with id {}", body.id);
174
175         let mut euv = euv::ExprUseVisitor::new(self, self.fcx);
176         euv.walk_fn(decl, body);
177     }
178
179     /// Indicates that `cmt` is being directly mutated (e.g., assigned
180     /// to). If cmt contains any by-ref upvars, this implies that
181     /// those upvars must be borrowed using an `&mut` borow.
182     fn adjust_upvar_borrow_kind_for_mut(&mut self, cmt: mc::cmt<'tcx>) {
183         debug!("adjust_upvar_borrow_kind_for_mut(cmt={})",
184                cmt.repr(self.tcx()));
185
186         match cmt.cat.clone() {
187             mc::cat_deref(base, _, mc::Unique) |
188             mc::cat_interior(base, _) |
189             mc::cat_downcast(base, _) => {
190                 // Interior or owned data is mutable if base is
191                 // mutable, so iterate to the base.
192                 self.adjust_upvar_borrow_kind_for_mut(base);
193             }
194
195             mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
196             mc::cat_deref(base, _, mc::Implicit(..)) => {
197                 if let mc::NoteUpvarRef(upvar_id) = cmt.note {
198                     // if this is an implicit deref of an
199                     // upvar, then we need to modify the
200                     // borrow_kind of the upvar to make sure it
201                     // is inferred to mutable if necessary
202                     let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut();
203                     let ub = &mut upvar_capture_map[upvar_id];
204                     self.adjust_upvar_borrow_kind(upvar_id, ub, ty::MutBorrow);
205                 } else {
206                     // assignment to deref of an `&mut`
207                     // borrowed pointer implies that the
208                     // pointer itself must be unique, but not
209                     // necessarily *mutable*
210                     self.adjust_upvar_borrow_kind_for_unique(base);
211                 }
212             }
213
214             mc::cat_deref(_, _, mc::UnsafePtr(..)) |
215             mc::cat_static_item |
216             mc::cat_rvalue(_) |
217             mc::cat_local(_) |
218             mc::cat_upvar(..) => {
219                 return;
220             }
221         }
222     }
223
224     fn adjust_upvar_borrow_kind_for_unique(&self, cmt: mc::cmt<'tcx>) {
225         debug!("adjust_upvar_borrow_kind_for_unique(cmt={})",
226                cmt.repr(self.tcx()));
227
228         match cmt.cat.clone() {
229             mc::cat_deref(base, _, mc::Unique) |
230             mc::cat_interior(base, _) |
231             mc::cat_downcast(base, _) => {
232                 // Interior or owned data is unique if base is
233                 // unique.
234                 self.adjust_upvar_borrow_kind_for_unique(base);
235             }
236
237             mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
238             mc::cat_deref(base, _, mc::Implicit(..)) => {
239                 if let mc::NoteUpvarRef(upvar_id) = cmt.note {
240                     // if this is an implicit deref of an
241                     // upvar, then we need to modify the
242                     // borrow_kind of the upvar to make sure it
243                     // is inferred to unique if necessary
244                     let mut ub = self.fcx.inh.upvar_capture_map.borrow_mut();
245                     let ub = &mut ub[upvar_id];
246                     self.adjust_upvar_borrow_kind(upvar_id, ub, ty::UniqueImmBorrow);
247                 } else {
248                     // for a borrowed pointer to be unique, its
249                     // base must be unique
250                     self.adjust_upvar_borrow_kind_for_unique(base);
251                 }
252             }
253
254             mc::cat_deref(_, _, mc::UnsafePtr(..)) |
255             mc::cat_static_item |
256             mc::cat_rvalue(_) |
257             mc::cat_local(_) |
258             mc::cat_upvar(..) => {
259             }
260         }
261     }
262
263     /// We infer the borrow_kind with which to borrow upvars in a stack closure. The borrow_kind
264     /// basically follows a lattice of `imm < unique-imm < mut`, moving from left to right as needed
265     /// (but never right to left). Here the argument `mutbl` is the borrow_kind that is required by
266     /// some particular use.
267     fn adjust_upvar_borrow_kind(&self,
268                                 upvar_id: ty::UpvarId,
269                                 upvar_capture: &mut ty::UpvarCapture,
270                                 kind: ty::BorrowKind) {
271         debug!("adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})",
272                upvar_id, upvar_capture, kind);
273
274         match *upvar_capture {
275             ty::UpvarCapture::ByValue => {
276                 // Upvar is already by-value, the strongest criteria.
277             }
278             ty::UpvarCapture::ByRef(ref mut upvar_borrow) => {
279                 match (upvar_borrow.kind, kind) {
280                     // Take RHS:
281                     (ty::ImmBorrow, ty::UniqueImmBorrow) |
282                     (ty::ImmBorrow, ty::MutBorrow) |
283                     (ty::UniqueImmBorrow, ty::MutBorrow) => {
284                         upvar_borrow.kind = kind;
285                     }
286                     // Take LHS:
287                     (ty::ImmBorrow, ty::ImmBorrow) |
288                     (ty::UniqueImmBorrow, ty::ImmBorrow) |
289                     (ty::UniqueImmBorrow, ty::UniqueImmBorrow) |
290                     (ty::MutBorrow, _) => {
291                     }
292                 }
293             }
294         }
295     }
296 }
297
298 impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> {
299     fn visit_fn(&mut self,
300                 fn_kind: visit::FnKind<'v>,
301                 decl: &'v ast::FnDecl,
302                 body: &'v ast::Block,
303                 span: Span,
304                 _id: ast::NodeId)
305     {
306         match fn_kind {
307             visit::FkItemFn(..) | visit::FkMethod(..) => {
308                 // ignore nested fn items
309             }
310             visit::FkFnBlock => {
311                 self.analyze_fn(decl, body);
312                 visit::walk_fn(self, fn_kind, decl, body, span);
313             }
314         }
315     }
316 }
317
318 impl<'a,'tcx> euv::Delegate<'tcx> for AdjustBorrowKind<'a,'tcx> {
319     fn consume(&mut self,
320                _consume_id: ast::NodeId,
321                _consume_span: Span,
322                _cmt: mc::cmt<'tcx>,
323                _mode: euv::ConsumeMode)
324     {}
325
326     fn matched_pat(&mut self,
327                    _matched_pat: &ast::Pat,
328                    _cmt: mc::cmt<'tcx>,
329                    _mode: euv::MatchMode)
330     {}
331
332     fn consume_pat(&mut self,
333                    _consume_pat: &ast::Pat,
334                    _cmt: mc::cmt<'tcx>,
335                    _mode: euv::ConsumeMode)
336     {}
337
338     fn borrow(&mut self,
339               borrow_id: ast::NodeId,
340               _borrow_span: Span,
341               cmt: mc::cmt<'tcx>,
342               _loan_region: ty::Region,
343               bk: ty::BorrowKind,
344               _loan_cause: euv::LoanCause)
345     {
346         debug!("borrow(borrow_id={}, cmt={}, bk={:?})",
347                borrow_id, cmt.repr(self.tcx()), bk);
348
349         match bk {
350             ty::ImmBorrow => { }
351             ty::UniqueImmBorrow => {
352                 self.adjust_upvar_borrow_kind_for_unique(cmt);
353             }
354             ty::MutBorrow => {
355                 self.adjust_upvar_borrow_kind_for_mut(cmt);
356             }
357         }
358     }
359
360     fn decl_without_init(&mut self,
361                          _id: ast::NodeId,
362                          _span: Span)
363     {}
364
365     fn mutate(&mut self,
366               _assignment_id: ast::NodeId,
367               _assignment_span: Span,
368               assignee_cmt: mc::cmt<'tcx>,
369               _mode: euv::MutateMode)
370     {
371         debug!("mutate(assignee_cmt={})",
372                assignee_cmt.repr(self.tcx()));
373
374         self.adjust_upvar_borrow_kind_for_mut(assignee_cmt);
375     }
376 }
377
378