]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/ty/closure.rs
Merge commit '984330a6ee3c4d15626685d6dc8b7b759ff630bd' into clippyup
[rust.git] / compiler / rustc_middle / src / ty / closure.rs
1 use crate::hir::place::{
2     Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
3 };
4 use crate::{mir, ty};
5
6 use std::fmt::Write;
7
8 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
9 use rustc_hir as hir;
10 use rustc_hir::def_id::{DefId, LocalDefId};
11 use rustc_span::{Span, Symbol};
12
13 use super::{Ty, TyCtxt};
14
15 use self::BorrowKind::*;
16
17 // Captures are represented using fields inside a structure.
18 // This represents accessing self in the closure structure
19 pub const CAPTURE_STRUCT_LOCAL: mir::Local = mir::Local::from_u32(1);
20
21 #[derive(
22     Clone,
23     Copy,
24     Debug,
25     PartialEq,
26     Eq,
27     Hash,
28     TyEncodable,
29     TyDecodable,
30     TypeFoldable,
31     HashStable
32 )]
33 pub struct UpvarPath {
34     pub hir_id: hir::HirId,
35 }
36
37 /// Upvars do not get their own `NodeId`. Instead, we use the pair of
38 /// the original var ID (that is, the root variable that is referenced
39 /// by the upvar) and the ID of the closure expression.
40 #[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
41 pub struct UpvarId {
42     pub var_path: UpvarPath,
43     pub closure_expr_id: LocalDefId,
44 }
45
46 impl UpvarId {
47     pub fn new(var_hir_id: hir::HirId, closure_def_id: LocalDefId) -> UpvarId {
48         UpvarId { var_path: UpvarPath { hir_id: var_hir_id }, closure_expr_id: closure_def_id }
49     }
50 }
51
52 /// Information describing the capture of an upvar. This is computed
53 /// during `typeck`, specifically by `regionck`.
54 #[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
55 pub enum UpvarCapture {
56     /// Upvar is captured by value. This is always true when the
57     /// closure is labeled `move`, but can also be true in other cases
58     /// depending on inference.
59     ByValue,
60
61     /// Upvar is captured by reference.
62     ByRef(BorrowKind),
63 }
64
65 pub type UpvarListMap = FxHashMap<DefId, FxIndexMap<hir::HirId, UpvarId>>;
66 pub type UpvarCaptureMap = FxHashMap<UpvarId, UpvarCapture>;
67
68 /// Given the closure DefId this map provides a map of root variables to minimum
69 /// set of `CapturedPlace`s that need to be tracked to support all captures of that closure.
70 pub type MinCaptureInformationMap<'tcx> = FxHashMap<DefId, RootVariableMinCaptureList<'tcx>>;
71
72 /// Part of `MinCaptureInformationMap`; Maps a root variable to the list of `CapturedPlace`.
73 /// Used to track the minimum set of `Place`s that need to be captured to support all
74 /// Places captured by the closure starting at a given root variable.
75 ///
76 /// This provides a convenient and quick way of checking if a variable being used within
77 /// a closure is a capture of a local variable.
78 pub type RootVariableMinCaptureList<'tcx> = FxIndexMap<hir::HirId, MinCaptureList<'tcx>>;
79
80 /// Part of `MinCaptureInformationMap`; List of `CapturePlace`s.
81 pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>;
82
83 /// Represents the various closure traits in the language. This
84 /// will determine the type of the environment (`self`, in the
85 /// desugaring) argument that the closure expects.
86 ///
87 /// You can get the environment type of a closure using
88 /// `tcx.closure_env_ty()`.
89 #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)]
90 #[derive(HashStable)]
91 pub enum ClosureKind {
92     // Warning: Ordering is significant here! The ordering is chosen
93     // because the trait Fn is a subtrait of FnMut and so in turn, and
94     // hence we order it so that Fn < FnMut < FnOnce.
95     Fn,
96     FnMut,
97     FnOnce,
98 }
99
100 impl<'tcx> ClosureKind {
101     // This is the initial value used when doing upvar inference.
102     pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn;
103
104     /// Returns `true` if a type that impls this closure kind
105     /// must also implement `other`.
106     pub fn extends(self, other: ty::ClosureKind) -> bool {
107         matches!(
108             (self, other),
109             (ClosureKind::Fn, ClosureKind::Fn)
110                 | (ClosureKind::Fn, ClosureKind::FnMut)
111                 | (ClosureKind::Fn, ClosureKind::FnOnce)
112                 | (ClosureKind::FnMut, ClosureKind::FnMut)
113                 | (ClosureKind::FnMut, ClosureKind::FnOnce)
114                 | (ClosureKind::FnOnce, ClosureKind::FnOnce)
115         )
116     }
117
118     /// Returns the representative scalar type for this closure kind.
119     /// See `Ty::to_opt_closure_kind` for more details.
120     pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
121         match self {
122             ClosureKind::Fn => tcx.types.i8,
123             ClosureKind::FnMut => tcx.types.i16,
124             ClosureKind::FnOnce => tcx.types.i32,
125         }
126     }
127
128     pub fn from_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ClosureKind> {
129         if Some(def_id) == tcx.lang_items().fn_once_trait() {
130             Some(ClosureKind::FnOnce)
131         } else if Some(def_id) == tcx.lang_items().fn_mut_trait() {
132             Some(ClosureKind::FnMut)
133         } else if Some(def_id) == tcx.lang_items().fn_trait() {
134             Some(ClosureKind::Fn)
135         } else {
136             None
137         }
138     }
139 }
140
141 /// A composite describing a `Place` that is captured by a closure.
142 #[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
143 pub struct CapturedPlace<'tcx> {
144     /// The `Place` that is captured.
145     pub place: HirPlace<'tcx>,
146
147     /// `CaptureKind` and expression(s) that resulted in such capture of `place`.
148     pub info: CaptureInfo,
149
150     /// Represents if `place` can be mutated or not.
151     pub mutability: hir::Mutability,
152
153     /// Region of the resulting reference if the upvar is captured by ref.
154     pub region: Option<ty::Region<'tcx>>,
155 }
156
157 impl<'tcx> CapturedPlace<'tcx> {
158     pub fn to_string(&self, tcx: TyCtxt<'tcx>) -> String {
159         place_to_string_for_capture(tcx, &self.place)
160     }
161
162     /// Returns a symbol of the captured upvar, which looks like `name__field1__field2`.
163     fn to_symbol(&self, tcx: TyCtxt<'tcx>) -> Symbol {
164         let hir_id = match self.place.base {
165             HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
166             base => bug!("Expected an upvar, found {:?}", base),
167         };
168         let mut symbol = tcx.hir().name(hir_id).as_str().to_string();
169
170         let mut ty = self.place.base_ty;
171         for proj in self.place.projections.iter() {
172             match proj.kind {
173                 HirProjectionKind::Field(idx, variant) => match ty.kind() {
174                     ty::Tuple(_) => write!(&mut symbol, "__{}", idx).unwrap(),
175                     ty::Adt(def, ..) => {
176                         write!(
177                             &mut symbol,
178                             "__{}",
179                             def.variant(variant).fields[idx as usize].name.as_str(),
180                         )
181                         .unwrap();
182                     }
183                     ty => {
184                         bug!("Unexpected type {:?} for `Field` projection", ty)
185                     }
186                 },
187
188                 // Ignore derefs for now, as they are likely caused by
189                 // autoderefs that don't appear in the original code.
190                 HirProjectionKind::Deref => {}
191                 proj => bug!("Unexpected projection {:?} in captured place", proj),
192             }
193             ty = proj.ty;
194         }
195
196         Symbol::intern(&symbol)
197     }
198
199     /// Returns the hir-id of the root variable for the captured place.
200     /// e.g., if `a.b.c` was captured, would return the hir-id for `a`.
201     pub fn get_root_variable(&self) -> hir::HirId {
202         match self.place.base {
203             HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
204             base => bug!("Expected upvar, found={:?}", base),
205         }
206     }
207
208     /// Returns the `LocalDefId` of the closure that captured this Place
209     pub fn get_closure_local_def_id(&self) -> LocalDefId {
210         match self.place.base {
211             HirPlaceBase::Upvar(upvar_id) => upvar_id.closure_expr_id,
212             base => bug!("expected upvar, found={:?}", base),
213         }
214     }
215
216     /// Return span pointing to use that resulted in selecting the captured path
217     pub fn get_path_span(&self, tcx: TyCtxt<'tcx>) -> Span {
218         if let Some(path_expr_id) = self.info.path_expr_id {
219             tcx.hir().span(path_expr_id)
220         } else if let Some(capture_kind_expr_id) = self.info.capture_kind_expr_id {
221             tcx.hir().span(capture_kind_expr_id)
222         } else {
223             // Fallback on upvars mentioned if neither path or capture expr id is captured
224
225             // Safe to unwrap since we know this place is captured by the closure, therefore the closure must have upvars.
226             tcx.upvars_mentioned(self.get_closure_local_def_id()).unwrap()
227                 [&self.get_root_variable()]
228                 .span
229         }
230     }
231
232     /// Return span pointing to use that resulted in selecting the current capture kind
233     pub fn get_capture_kind_span(&self, tcx: TyCtxt<'tcx>) -> Span {
234         if let Some(capture_kind_expr_id) = self.info.capture_kind_expr_id {
235             tcx.hir().span(capture_kind_expr_id)
236         } else if let Some(path_expr_id) = self.info.path_expr_id {
237             tcx.hir().span(path_expr_id)
238         } else {
239             // Fallback on upvars mentioned if neither path or capture expr id is captured
240
241             // Safe to unwrap since we know this place is captured by the closure, therefore the closure must have upvars.
242             tcx.upvars_mentioned(self.get_closure_local_def_id()).unwrap()
243                 [&self.get_root_variable()]
244                 .span
245         }
246     }
247 }
248
249 fn symbols_for_closure_captures<'tcx>(
250     tcx: TyCtxt<'tcx>,
251     def_id: (LocalDefId, DefId),
252 ) -> Vec<Symbol> {
253     let typeck_results = tcx.typeck(def_id.0);
254     let captures = typeck_results.closure_min_captures_flattened(def_id.1);
255     captures.into_iter().map(|captured_place| captured_place.to_symbol(tcx)).collect()
256 }
257
258 /// Return true if the `proj_possible_ancestor` represents an ancestor path
259 /// to `proj_capture` or `proj_possible_ancestor` is same as `proj_capture`,
260 /// assuming they both start off of the same root variable.
261 ///
262 /// **Note:** It's the caller's responsibility to ensure that both lists of projections
263 ///           start off of the same root variable.
264 ///
265 /// Eg: 1. `foo.x` which is represented using `projections=[Field(x)]` is an ancestor of
266 ///        `foo.x.y` which is represented using `projections=[Field(x), Field(y)]`.
267 ///        Note both `foo.x` and `foo.x.y` start off of the same root variable `foo`.
268 ///     2. Since we only look at the projections here function will return `bar.x` as an a valid
269 ///        ancestor of `foo.x.y`. It's the caller's responsibility to ensure that both projections
270 ///        list are being applied to the same root variable.
271 pub fn is_ancestor_or_same_capture(
272     proj_possible_ancestor: &[HirProjectionKind],
273     proj_capture: &[HirProjectionKind],
274 ) -> bool {
275     // We want to make sure `is_ancestor_or_same_capture("x.0.0", "x.0")` to return false.
276     // Therefore we can't just check if all projections are same in the zipped iterator below.
277     if proj_possible_ancestor.len() > proj_capture.len() {
278         return false;
279     }
280
281     proj_possible_ancestor.iter().zip(proj_capture).all(|(a, b)| a == b)
282 }
283
284 /// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move)
285 /// for a particular capture as well as identifying the part of the source code
286 /// that triggered this capture to occur.
287 #[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
288 pub struct CaptureInfo {
289     /// Expr Id pointing to use that resulted in selecting the current capture kind
290     ///
291     /// Eg:
292     /// ```rust,no_run
293     /// let mut t = (0,1);
294     ///
295     /// let c = || {
296     ///     println!("{t}"); // L1
297     ///     t.1 = 4; // L2
298     /// };
299     /// ```
300     /// `capture_kind_expr_id` will point to the use on L2 and `path_expr_id` will point to the
301     /// use on L1.
302     ///
303     /// If the user doesn't enable feature `capture_disjoint_fields` (RFC 2229) then, it is
304     /// possible that we don't see the use of a particular place resulting in capture_kind_expr_id being
305     /// None. In such case we fallback on uvpars_mentioned for span.
306     ///
307     /// Eg:
308     /// ```rust,no_run
309     /// let x = 5;
310     ///
311     /// let c = || {
312     ///     let _ = x
313     /// };
314     /// ```
315     ///
316     /// In this example, if `capture_disjoint_fields` is **not** set, then x will be captured,
317     /// but we won't see it being used during capture analysis, since it's essentially a discard.
318     pub capture_kind_expr_id: Option<hir::HirId>,
319     /// Expr Id pointing to use that resulted the corresponding place being captured
320     ///
321     /// See `capture_kind_expr_id` for example.
322     ///
323     pub path_expr_id: Option<hir::HirId>,
324
325     /// Capture mode that was selected
326     pub capture_kind: UpvarCapture,
327 }
328
329 pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String {
330     let mut curr_string: String = match place.base {
331         HirPlaceBase::Upvar(upvar_id) => tcx.hir().name(upvar_id.var_path.hir_id).to_string(),
332         _ => bug!("Capture_information should only contain upvars"),
333     };
334
335     for (i, proj) in place.projections.iter().enumerate() {
336         match proj.kind {
337             HirProjectionKind::Deref => {
338                 curr_string = format!("*{}", curr_string);
339             }
340             HirProjectionKind::Field(idx, variant) => match place.ty_before_projection(i).kind() {
341                 ty::Adt(def, ..) => {
342                     curr_string = format!(
343                         "{}.{}",
344                         curr_string,
345                         def.variant(variant).fields[idx as usize].name.as_str()
346                     );
347                 }
348                 ty::Tuple(_) => {
349                     curr_string = format!("{}.{}", curr_string, idx);
350                 }
351                 _ => {
352                     bug!(
353                         "Field projection applied to a type other than Adt or Tuple: {:?}.",
354                         place.ty_before_projection(i).kind()
355                     )
356                 }
357             },
358             proj => bug!("{:?} unexpected because it isn't captured", proj),
359         }
360     }
361
362     curr_string
363 }
364
365 #[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, TypeFoldable, Copy, HashStable)]
366 pub enum BorrowKind {
367     /// Data must be immutable and is aliasable.
368     ImmBorrow,
369
370     /// Data must be immutable but not aliasable. This kind of borrow
371     /// cannot currently be expressed by the user and is used only in
372     /// implicit closure bindings. It is needed when the closure
373     /// is borrowing or mutating a mutable referent, e.g.:
374     ///
375     /// ```
376     /// let x: &mut isize = ...;
377     /// let y = || *x += 5;
378     /// ```
379     ///
380     /// If we were to try to translate this closure into a more explicit
381     /// form, we'd encounter an error with the code as written:
382     ///
383     /// ```
384     /// struct Env { x: & &mut isize }
385     /// let x: &mut isize = ...;
386     /// let y = (&mut Env { &x }, fn_ptr);  // Closure is pair of env and fn
387     /// fn fn_ptr(env: &mut Env) { **env.x += 5; }
388     /// ```
389     ///
390     /// This is then illegal because you cannot mutate a `&mut` found
391     /// in an aliasable location. To solve, you'd have to translate with
392     /// an `&mut` borrow:
393     ///
394     /// ```
395     /// struct Env { x: &mut &mut isize }
396     /// let x: &mut isize = ...;
397     /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x
398     /// fn fn_ptr(env: &mut Env) { **env.x += 5; }
399     /// ```
400     ///
401     /// Now the assignment to `**env.x` is legal, but creating a
402     /// mutable pointer to `x` is not because `x` is not mutable. We
403     /// could fix this by declaring `x` as `let mut x`. This is ok in
404     /// user code, if awkward, but extra weird for closures, since the
405     /// borrow is hidden.
406     ///
407     /// So we introduce a "unique imm" borrow -- the referent is
408     /// immutable, but not aliasable. This solves the problem. For
409     /// simplicity, we don't give users the way to express this
410     /// borrow, it's just used when translating closures.
411     UniqueImmBorrow,
412
413     /// Data is mutable and not aliasable.
414     MutBorrow,
415 }
416
417 impl BorrowKind {
418     pub fn from_mutbl(m: hir::Mutability) -> BorrowKind {
419         match m {
420             hir::Mutability::Mut => MutBorrow,
421             hir::Mutability::Not => ImmBorrow,
422         }
423     }
424
425     /// Returns a mutability `m` such that an `&m T` pointer could be used to obtain this borrow
426     /// kind. Because borrow kinds are richer than mutabilities, we sometimes have to pick a
427     /// mutability that is stronger than necessary so that it at least *would permit* the borrow in
428     /// question.
429     pub fn to_mutbl_lossy(self) -> hir::Mutability {
430         match self {
431             MutBorrow => hir::Mutability::Mut,
432             ImmBorrow => hir::Mutability::Not,
433
434             // We have no type corresponding to a unique imm borrow, so
435             // use `&mut`. It gives all the capabilities of a `&uniq`
436             // and hence is a safe "over approximation".
437             UniqueImmBorrow => hir::Mutability::Mut,
438         }
439     }
440 }
441
442 pub fn provide(providers: &mut ty::query::Providers) {
443     *providers = ty::query::Providers { symbols_for_closure_captures, ..*providers }
444 }