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