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