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