]>
Commit | Line | Data |
---|---|---|
6a06907d XL |
1 | use crate::hir::place::{ |
2 | Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind, | |
3 | }; | |
17df50a5 | 4 | use crate::{mir, ty}; |
6a06907d | 5 | |
94222f64 XL |
6 | use std::fmt::Write; |
7 | ||
6a06907d XL |
8 | use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; |
9 | use rustc_hir as hir; | |
10 | use rustc_hir::def_id::{DefId, LocalDefId}; | |
94222f64 | 11 | use rustc_span::{Span, Symbol}; |
6a06907d XL |
12 | |
13 | use super::{Ty, TyCtxt}; | |
14 | ||
15 | use self::BorrowKind::*; | |
16 | ||
17df50a5 XL |
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 | ||
064997fb FG |
21 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] |
22 | #[derive(TypeFoldable, TypeVisitable)] | |
6a06907d XL |
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. | |
064997fb FG |
30 | #[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] |
31 | #[derive(TypeFoldable, TypeVisitable)] | |
6a06907d XL |
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`. | |
064997fb FG |
45 | #[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] |
46 | #[derive(TypeFoldable, TypeVisitable)] | |
5099ac24 | 47 | pub enum UpvarCapture { |
6a06907d XL |
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. | |
5099ac24 | 51 | ByValue, |
6a06907d XL |
52 | |
53 | /// Upvar is captured by reference. | |
5099ac24 | 54 | ByRef(BorrowKind), |
6a06907d XL |
55 | } |
56 | ||
57 | pub type UpvarListMap = FxHashMap<DefId, FxIndexMap<hir::HirId, UpvarId>>; | |
5099ac24 | 58 | pub type UpvarCaptureMap = FxHashMap<UpvarId, UpvarCapture>; |
6a06907d XL |
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. | |
064997fb | 62 | pub type MinCaptureInformationMap<'tcx> = FxHashMap<LocalDefId, RootVariableMinCaptureList<'tcx>>; |
6a06907d XL |
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 | ||
6a06907d XL |
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. | |
5099ac24 | 111 | /// See `Ty::to_opt_closure_kind` for more details. |
6a06907d XL |
112 | pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { |
113 | match self { | |
5e7ed085 FG |
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 | |
6a06907d XL |
129 | } |
130 | } | |
064997fb FG |
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 | } | |
6a06907d XL |
139 | } |
140 | ||
141 | /// A composite describing a `Place` that is captured by a closure. | |
064997fb FG |
142 | #[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)] |
143 | #[derive(TypeFoldable, TypeVisitable)] | |
6a06907d XL |
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`. | |
5099ac24 | 149 | pub info: CaptureInfo, |
6a06907d XL |
150 | |
151 | /// Represents if `place` can be mutated or not. | |
152 | pub mutability: hir::Mutability, | |
5099ac24 FG |
153 | |
154 | /// Region of the resulting reference if the upvar is captured by ref. | |
155 | pub region: Option<ty::Region<'tcx>>, | |
6a06907d XL |
156 | } |
157 | ||
a2a8927a | 158 | impl<'tcx> CapturedPlace<'tcx> { |
17df50a5 XL |
159 | pub fn to_string(&self, tcx: TyCtxt<'tcx>) -> String { |
160 | place_to_string_for_capture(tcx, &self.place) | |
161 | } | |
162 | ||
94222f64 XL |
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 | "__{}", | |
5e7ed085 | 180 | def.variant(variant).fields[idx as usize].name.as_str(), |
94222f64 XL |
181 | ) |
182 | .unwrap(); | |
183 | } | |
184 | ty => { | |
064997fb FG |
185 | span_bug!( |
186 | self.get_capture_kind_span(tcx), | |
187 | "Unexpected type {:?} for `Field` projection", | |
188 | ty | |
189 | ) | |
94222f64 XL |
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 | ||
6a06907d XL |
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 | ||
cdc7bbd5 | 213 | /// Returns the `LocalDefId` of the closure that captured this Place |
6a06907d XL |
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 | ||
17df50a5 XL |
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 | ||
6a06907d XL |
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 | ||
94222f64 XL |
254 | fn symbols_for_closure_captures<'tcx>( |
255 | tcx: TyCtxt<'tcx>, | |
064997fb | 256 | def_id: (LocalDefId, LocalDefId), |
94222f64 XL |
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 | ||
6a06907d XL |
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. | |
064997fb FG |
292 | #[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] |
293 | #[derive(TypeFoldable, TypeVisitable)] | |
5099ac24 | 294 | pub struct CaptureInfo { |
6a06907d XL |
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 = || { | |
04454e1e | 302 | /// println!("{t:?}"); // L1 |
6a06907d XL |
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 = || { | |
04454e1e | 318 | /// let _ = x; |
6a06907d XL |
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 | |
5099ac24 | 332 | pub capture_kind: UpvarCapture, |
6a06907d XL |
333 | } |
334 | ||
a2a8927a | 335 | pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String { |
136023e0 | 336 | let mut curr_string: String = match place.base { |
6a06907d XL |
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 | }; | |
6a06907d XL |
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, | |
5e7ed085 | 351 | def.variant(variant).fields[idx as usize].name.as_str() |
6a06907d XL |
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 | ||
136023e0 | 368 | curr_string |
6a06907d XL |
369 | } |
370 | ||
064997fb FG |
371 | #[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable)] |
372 | #[derive(TypeFoldable, TypeVisitable)] | |
6a06907d XL |
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 | /// ``` | |
04454e1e FG |
383 | /// let mut z = 3; |
384 | /// let x: &mut isize = &mut z; | |
6a06907d XL |
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 | /// | |
04454e1e FG |
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 | |
6a06907d XL |
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 | /// | |
04454e1e FG |
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 | |
6a06907d XL |
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 | |
94222f64 | 445 | // use `&mut`. It gives all the capabilities of a `&uniq` |
6a06907d XL |
446 | // and hence is a safe "over approximation". |
447 | UniqueImmBorrow => hir::Mutability::Mut, | |
448 | } | |
449 | } | |
6a06907d | 450 | } |
94222f64 XL |
451 | |
452 | pub fn provide(providers: &mut ty::query::Providers) { | |
453 | *providers = ty::query::Providers { symbols_for_closure_captures, ..*providers } | |
454 | } |