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