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