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