]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/upvar.rs
Rollup merge of #67102 - Aaron1011:patch-3, r=Mark-Simulacrum
[rust.git] / src / librustc_typeck / check / upvar.rs
1 //! ### Inferring borrow kinds for upvars
2 //!
3 //! Whenever there is a closure expression, we need to determine how each
4 //! upvar is used. We do this by initially assigning each upvar an
5 //! immutable "borrow kind" (see `ty::BorrowKind` for details) and then
6 //! "escalating" the kind as needed. The borrow kind proceeds according to
7 //! the following lattice:
8 //!
9 //!     ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow
10 //!
11 //! So, for example, if we see an assignment `x = 5` to an upvar `x`, we
12 //! will promote its borrow kind to mutable borrow. If we see an `&mut x`
13 //! we'll do the same. Naturally, this applies not just to the upvar, but
14 //! to everything owned by `x`, so the result is the same for something
15 //! like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a
16 //! struct). These adjustments are performed in
17 //! `adjust_upvar_borrow_kind()` (you can trace backwards through the code
18 //! from there).
19 //!
20 //! The fact that we are inferring borrow kinds as we go results in a
21 //! semi-hacky interaction with mem-categorization. In particular,
22 //! mem-categorization will query the current borrow kind as it
23 //! categorizes, and we'll return the *current* value, but this may get
24 //! adjusted later. Therefore, in this module, we generally ignore the
25 //! borrow kind (and derived mutabilities) that are returned from
26 //! mem-categorization, since they may be inaccurate. (Another option
27 //! would be to use a unification scheme, where instead of returning a
28 //! concrete borrow kind like `ty::ImmBorrow`, we return a
29 //! `ty::InferBorrow(upvar_id)` or something like that, but this would
30 //! then mean that all later passes would have to check for these figments
31 //! and report an error, and it just seems like more mess in the end.)
32
33 use super::FnCtxt;
34
35 use crate::expr_use_visitor as euv;
36 use crate::mem_categorization as mc;
37 use crate::mem_categorization::PlaceBase;
38 use rustc::hir;
39 use rustc::hir::def_id::DefId;
40 use rustc::hir::def_id::LocalDefId;
41 use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
42 use rustc::infer::UpvarRegion;
43 use rustc::ty::{self, Ty, TyCtxt, UpvarSubsts};
44 use rustc_data_structures::fx::FxIndexMap;
45 use syntax::ast;
46 use syntax_pos::Span;
47
48 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
49     pub fn closure_analyze(&self, body: &'tcx hir::Body) {
50         InferBorrowKindVisitor { fcx: self }.visit_body(body);
51
52         // it's our job to process these.
53         assert!(self.deferred_call_resolutions.borrow().is_empty());
54     }
55 }
56
57 struct InferBorrowKindVisitor<'a, 'tcx> {
58     fcx: &'a FnCtxt<'a, 'tcx>,
59 }
60
61 impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> {
62     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
63         NestedVisitorMap::None
64     }
65
66     fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
67         if let hir::ExprKind::Closure(cc, _, body_id, _, _) = expr.kind {
68             let body = self.fcx.tcx.hir().body(body_id);
69             self.visit_body(body);
70             self.fcx
71                 .analyze_closure(expr.hir_id, expr.span, body, cc);
72         }
73
74         intravisit::walk_expr(self, expr);
75     }
76 }
77
78 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
79     /// Analysis starting point.
80     fn analyze_closure(
81         &self,
82         closure_hir_id: hir::HirId,
83         span: Span,
84         body: &hir::Body,
85         capture_clause: hir::CaptureBy,
86     ) {
87
88         debug!(
89             "analyze_closure(id={:?}, body.id={:?})",
90             closure_hir_id,
91             body.id()
92         );
93
94         // Extract the type of the closure.
95         let ty = self.node_ty(closure_hir_id);
96         let (closure_def_id, substs) = match ty.kind {
97             ty::Closure(def_id, substs) => (
98                 def_id,
99                 UpvarSubsts::Closure(substs)
100             ),
101             ty::Generator(def_id, substs, _) => (def_id, UpvarSubsts::Generator(substs)),
102             ty::Error => {
103                 // #51714: skip analysis when we have already encountered type errors
104                 return;
105             }
106             _ => {
107                 span_bug!(
108                     span,
109                     "type of closure expr {:?} is not a closure {:?}",
110                     closure_hir_id,
111                     ty
112                 );
113             }
114         };
115
116         let infer_kind = if let UpvarSubsts::Closure(closure_substs) = substs {
117             self.closure_kind(closure_def_id, closure_substs).is_none().then_some(closure_substs)
118         } else {
119             None
120         };
121
122         if let Some(upvars) = self.tcx.upvars(closure_def_id) {
123             let mut upvar_list: FxIndexMap<hir::HirId, ty::UpvarId> =
124                 FxIndexMap::with_capacity_and_hasher(upvars.len(), Default::default());
125             for (&var_hir_id, _) in upvars.iter() {
126                 let upvar_id = ty::UpvarId {
127                     var_path: ty::UpvarPath {
128                         hir_id: var_hir_id,
129                     },
130                     closure_expr_id: LocalDefId::from_def_id(closure_def_id),
131                 };
132                 debug!("seed upvar_id {:?}", upvar_id);
133                 // Adding the upvar Id to the list of Upvars, which will be added
134                 // to the map for the closure at the end of the for loop.
135                 upvar_list.insert(var_hir_id, upvar_id);
136
137                 let capture_kind = match capture_clause {
138                     hir::CaptureBy::Value => ty::UpvarCapture::ByValue,
139                     hir::CaptureBy::Ref => {
140                         let origin = UpvarRegion(upvar_id, span);
141                         let upvar_region = self.next_region_var(origin);
142                         let upvar_borrow = ty::UpvarBorrow {
143                             kind: ty::ImmBorrow,
144                             region: upvar_region,
145                         };
146                         ty::UpvarCapture::ByRef(upvar_borrow)
147                     }
148                 };
149
150                 self.tables
151                     .borrow_mut()
152                     .upvar_capture_map
153                     .insert(upvar_id, capture_kind);
154             }
155             // Add the vector of upvars to the map keyed with the closure id.
156             // This gives us an easier access to them without having to call
157             // tcx.upvars again..
158             if !upvar_list.is_empty() {
159                 self.tables
160                     .borrow_mut()
161                     .upvar_list
162                     .insert(closure_def_id, upvar_list);
163             }
164         }
165
166         let body_owner_def_id = self.tcx.hir().body_owner_def_id(body.id());
167         assert_eq!(body_owner_def_id, closure_def_id);
168         let mut delegate = InferBorrowKind {
169             fcx: self,
170             closure_def_id,
171             current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM,
172             current_origin: None,
173             adjust_upvar_captures: ty::UpvarCaptureMap::default(),
174         };
175         euv::ExprUseVisitor::new(
176             &mut delegate,
177             &self.infcx,
178             body_owner_def_id,
179             self.param_env,
180             &self.tables.borrow(),
181         )
182         .consume_body(body);
183
184         if let Some(closure_substs) = infer_kind {
185             // Unify the (as yet unbound) type variable in the closure
186             // substs with the kind we inferred.
187             let inferred_kind = delegate.current_closure_kind;
188             let closure_kind_ty = closure_substs
189                 .as_closure().kind_ty(closure_def_id, self.tcx);
190             self.demand_eqtype(span, inferred_kind.to_ty(self.tcx), closure_kind_ty);
191
192             // If we have an origin, store it.
193             if let Some(origin) = delegate.current_origin {
194                 self.tables
195                     .borrow_mut()
196                     .closure_kind_origins_mut()
197                     .insert(closure_hir_id, origin);
198             }
199         }
200
201         self.tables
202             .borrow_mut()
203             .upvar_capture_map
204             .extend(delegate.adjust_upvar_captures);
205
206         // Now that we've analyzed the closure, we know how each
207         // variable is borrowed, and we know what traits the closure
208         // implements (Fn vs FnMut etc). We now have some updates to do
209         // with that information.
210         //
211         // Note that no closure type C may have an upvar of type C
212         // (though it may reference itself via a trait object). This
213         // results from the desugaring of closures to a struct like
214         // `Foo<..., UV0...UVn>`. If one of those upvars referenced
215         // C, then the type would have infinite size (and the
216         // inference algorithm will reject it).
217
218         // Equate the type variables for the upvars with the actual types.
219         let final_upvar_tys = self.final_upvar_tys(closure_hir_id);
220         debug!(
221             "analyze_closure: id={:?} substs={:?} final_upvar_tys={:?}",
222             closure_hir_id, substs, final_upvar_tys
223         );
224         for (upvar_ty, final_upvar_ty) in substs
225             .upvar_tys(closure_def_id, self.tcx)
226             .zip(final_upvar_tys)
227         {
228             self.demand_suptype(span, upvar_ty, final_upvar_ty);
229         }
230
231         // If we are also inferred the closure kind here,
232         // process any deferred resolutions.
233         let deferred_call_resolutions = self.remove_deferred_call_resolutions(closure_def_id);
234         for deferred_call_resolution in deferred_call_resolutions {
235             deferred_call_resolution.resolve(self);
236         }
237     }
238
239     // Returns a list of `ClosureUpvar`s for each upvar.
240     fn final_upvar_tys(&self, closure_id: hir::HirId) -> Vec<Ty<'tcx>> {
241         // Presently an unboxed closure type cannot "escape" out of a
242         // function, so we will only encounter ones that originated in the
243         // local crate or were inlined into it along with some function.
244         // This may change if abstract return types of some sort are
245         // implemented.
246         let tcx = self.tcx;
247         let closure_def_id = tcx.hir().local_def_id(closure_id);
248
249         tcx.upvars(closure_def_id).iter().flat_map(|upvars| {
250             upvars
251                 .iter()
252                 .map(|(&var_hir_id, _)| {
253                     let upvar_ty = self.node_ty(var_hir_id);
254                     let upvar_id = ty::UpvarId {
255                         var_path: ty::UpvarPath { hir_id: var_hir_id },
256                         closure_expr_id: LocalDefId::from_def_id(closure_def_id),
257                     };
258                     let capture = self.tables.borrow().upvar_capture(upvar_id);
259
260                     debug!(
261                         "var_id={:?} upvar_ty={:?} capture={:?}",
262                         var_hir_id, upvar_ty, capture
263                     );
264
265                     match capture {
266                         ty::UpvarCapture::ByValue => upvar_ty,
267                         ty::UpvarCapture::ByRef(borrow) => tcx.mk_ref(
268                             borrow.region,
269                             ty::TypeAndMut {
270                                 ty: upvar_ty,
271                                 mutbl: borrow.kind.to_mutbl_lossy(),
272                             },
273                         ),
274                     }
275                 })
276         })
277             .collect()
278     }
279 }
280
281 struct InferBorrowKind<'a, 'tcx> {
282     fcx: &'a FnCtxt<'a, 'tcx>,
283
284     // The def-id of the closure whose kind and upvar accesses are being inferred.
285     closure_def_id: DefId,
286
287     // The kind that we have inferred that the current closure
288     // requires. Note that we *always* infer a minimal kind, even if
289     // we don't always *use* that in the final result (i.e., sometimes
290     // we've taken the closure kind from the expectations instead, and
291     // for generators we don't even implement the closure traits
292     // really).
293     current_closure_kind: ty::ClosureKind,
294
295     // If we modified `current_closure_kind`, this field contains a `Some()` with the
296     // variable access that caused us to do so.
297     current_origin: Option<(Span, ast::Name)>,
298
299     // For each upvar that we access, we track the minimal kind of
300     // access we need (ref, ref mut, move, etc).
301     adjust_upvar_captures: ty::UpvarCaptureMap<'tcx>,
302 }
303
304 impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
305     fn adjust_upvar_borrow_kind_for_consume(
306         &mut self,
307         place: &mc::Place<'tcx>,
308         mode: euv::ConsumeMode,
309     ) {
310         debug!("adjust_upvar_borrow_kind_for_consume(place={:?}, mode={:?})", place, mode);
311
312         // we only care about moves
313         match mode {
314             euv::Copy => {
315                 return;
316             }
317             euv::Move => {}
318         }
319
320         let tcx = self.fcx.tcx;
321         let upvar_id = if let PlaceBase::Upvar(upvar_id) = place.base {
322             upvar_id
323         } else {
324             return;
325         };
326
327         debug!("adjust_upvar_borrow_kind_for_consume: upvar={:?}", upvar_id);
328
329         // To move out of an upvar, this must be a FnOnce closure
330         self.adjust_closure_kind(
331             upvar_id.closure_expr_id,
332             ty::ClosureKind::FnOnce,
333             place.span,
334             var_name(tcx, upvar_id.var_path.hir_id),
335         );
336
337         self.adjust_upvar_captures.insert(upvar_id, ty::UpvarCapture::ByValue);
338     }
339
340     /// Indicates that `place` is being directly mutated (e.g., assigned
341     /// to). If the place is based on a by-ref upvar, this implies that
342     /// the upvar must be borrowed using an `&mut` borrow.
343     fn adjust_upvar_borrow_kind_for_mut(&mut self, place: &mc::Place<'tcx>) {
344         debug!("adjust_upvar_borrow_kind_for_mut(place={:?})", place);
345
346         if let PlaceBase::Upvar(upvar_id) = place.base {
347             let mut borrow_kind = ty::MutBorrow;
348             for pointer_ty in place.deref_tys() {
349                 match pointer_ty.kind {
350                     // Raw pointers don't inherit mutability.
351                     ty::RawPtr(_) => return,
352                     // assignment to deref of an `&mut`
353                     // borrowed pointer implies that the
354                     // pointer itself must be unique, but not
355                     // necessarily *mutable*
356                     ty::Ref(.., hir::Mutability::Mutable) => borrow_kind = ty::UniqueImmBorrow,
357                     _ => (),
358                 }
359             }
360             self.adjust_upvar_deref(upvar_id, place.span, borrow_kind);
361         }
362     }
363
364     fn adjust_upvar_borrow_kind_for_unique(&mut self, place: &mc::Place<'tcx>) {
365         debug!("adjust_upvar_borrow_kind_for_unique(place={:?})", place);
366
367         if let PlaceBase::Upvar(upvar_id) = place.base {
368             if place.deref_tys().any(ty::TyS::is_unsafe_ptr) {
369                 // Raw pointers don't inherit mutability.
370                 return;
371             }
372             // for a borrowed pointer to be unique, its base must be unique
373             self.adjust_upvar_deref(upvar_id, place.span, ty::UniqueImmBorrow);
374         }
375     }
376
377     fn adjust_upvar_deref(
378         &mut self,
379         upvar_id: ty::UpvarId,
380         place_span: Span,
381         borrow_kind: ty::BorrowKind,
382     ) {
383         assert!(match borrow_kind {
384             ty::MutBorrow => true,
385             ty::UniqueImmBorrow => true,
386
387             // imm borrows never require adjusting any kinds, so we don't wind up here
388             ty::ImmBorrow => false,
389         });
390
391         let tcx = self.fcx.tcx;
392
393         // if this is an implicit deref of an
394         // upvar, then we need to modify the
395         // borrow_kind of the upvar to make sure it
396         // is inferred to mutable if necessary
397         self.adjust_upvar_borrow_kind(upvar_id, borrow_kind);
398
399         // also need to be in an FnMut closure since this is not an ImmBorrow
400         self.adjust_closure_kind(
401             upvar_id.closure_expr_id,
402             ty::ClosureKind::FnMut,
403             place_span,
404             var_name(tcx, upvar_id.var_path.hir_id),
405         );
406     }
407
408     /// We infer the borrow_kind with which to borrow upvars in a stack closure.
409     /// The borrow_kind basically follows a lattice of `imm < unique-imm < mut`,
410     /// moving from left to right as needed (but never right to left).
411     /// Here the argument `mutbl` is the borrow_kind that is required by
412     /// some particular use.
413     fn adjust_upvar_borrow_kind(&mut self, upvar_id: ty::UpvarId, kind: ty::BorrowKind) {
414         let upvar_capture = self
415             .adjust_upvar_captures
416             .get(&upvar_id)
417             .copied()
418             .unwrap_or_else(|| self.fcx.tables.borrow().upvar_capture(upvar_id));
419         debug!(
420             "adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})",
421             upvar_id, upvar_capture, kind
422         );
423
424         match upvar_capture {
425             ty::UpvarCapture::ByValue => {
426                 // Upvar is already by-value, the strongest criteria.
427             }
428             ty::UpvarCapture::ByRef(mut upvar_borrow) => {
429                 match (upvar_borrow.kind, kind) {
430                     // Take RHS:
431                     (ty::ImmBorrow, ty::UniqueImmBorrow)
432                     | (ty::ImmBorrow, ty::MutBorrow)
433                     | (ty::UniqueImmBorrow, ty::MutBorrow) => {
434                         upvar_borrow.kind = kind;
435                         self.adjust_upvar_captures
436                             .insert(upvar_id, ty::UpvarCapture::ByRef(upvar_borrow));
437                     }
438                     // Take LHS:
439                     (ty::ImmBorrow, ty::ImmBorrow)
440                     | (ty::UniqueImmBorrow, ty::ImmBorrow)
441                     | (ty::UniqueImmBorrow, ty::UniqueImmBorrow)
442                     | (ty::MutBorrow, _) => {}
443                 }
444             }
445         }
446     }
447
448     fn adjust_closure_kind(
449         &mut self,
450         closure_id: LocalDefId,
451         new_kind: ty::ClosureKind,
452         upvar_span: Span,
453         var_name: ast::Name,
454     ) {
455         debug!(
456             "adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, var_name={})",
457             closure_id, new_kind, upvar_span, var_name
458         );
459
460         // Is this the closure whose kind is currently being inferred?
461         if closure_id.to_def_id() != self.closure_def_id {
462             debug!("adjust_closure_kind: not current closure");
463             return;
464         }
465
466         // closures start out as `Fn`.
467         let existing_kind = self.current_closure_kind;
468
469         debug!(
470             "adjust_closure_kind: closure_id={:?}, existing_kind={:?}, new_kind={:?}",
471             closure_id, existing_kind, new_kind
472         );
473
474         match (existing_kind, new_kind) {
475             (ty::ClosureKind::Fn, ty::ClosureKind::Fn)
476             | (ty::ClosureKind::FnMut, ty::ClosureKind::Fn)
477             | (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut)
478             | (ty::ClosureKind::FnOnce, _) => {
479                 // no change needed
480             }
481
482             (ty::ClosureKind::Fn, ty::ClosureKind::FnMut)
483             | (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce)
484             | (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
485                 // new kind is stronger than the old kind
486                 self.current_closure_kind = new_kind;
487                 self.current_origin = Some((upvar_span, var_name));
488             }
489         }
490     }
491 }
492
493 impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
494     fn consume(&mut self, place: &mc::Place<'tcx>,mode: euv::ConsumeMode) {
495         debug!("consume(place={:?},mode={:?})", place, mode);
496         self.adjust_upvar_borrow_kind_for_consume(place, mode);
497     }
498
499     fn borrow(&mut self, place: &mc::Place<'tcx>, bk: ty::BorrowKind) {
500         debug!("borrow(place={:?}, bk={:?})", place, bk);
501
502         match bk {
503             ty::ImmBorrow => {}
504             ty::UniqueImmBorrow => {
505                 self.adjust_upvar_borrow_kind_for_unique(place);
506             }
507             ty::MutBorrow => {
508                 self.adjust_upvar_borrow_kind_for_mut(place);
509             }
510         }
511     }
512
513     fn mutate(&mut self, assignee_place: &mc::Place<'tcx>) {
514         debug!("mutate(assignee_place={:?})", assignee_place);
515
516         self.adjust_upvar_borrow_kind_for_mut(assignee_place);
517     }
518 }
519
520 fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> ast::Name {
521     tcx.hir().name(var_hir_id)
522 }