]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | //! ### Inferring borrow kinds for upvars |
2 | //! | |
3 | //! Whenever there is a closure expression, we need to determine how each | |
4 | //! upvar is used. We do this by initially assigning each upvar an | |
5 | //! immutable "borrow kind" (see `ty::BorrowKind` for details) and then | |
6 | //! "escalating" the kind as needed. The borrow kind proceeds according to | |
7 | //! the following lattice: | |
8 | //! | |
9 | //! ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow | |
10 | //! | |
11 | //! So, for example, if we see an assignment `x = 5` to an upvar `x`, we | |
12 | //! will promote its borrow kind to mutable borrow. If we see an `&mut x` | |
13 | //! we'll do the same. Naturally, this applies not just to the upvar, but | |
14 | //! to everything owned by `x`, so the result is the same for something | |
15 | //! like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a | |
16 | //! struct). These adjustments are performed in | |
17 | //! `adjust_upvar_borrow_kind()` (you can trace backwards through the code | |
18 | //! from there). | |
19 | //! | |
20 | //! The fact that we are inferring borrow kinds as we go results in a | |
21 | //! semi-hacky interaction with mem-categorization. In particular, | |
22 | //! mem-categorization will query the current borrow kind as it | |
23 | //! categorizes, and we'll return the *current* value, but this may get | |
24 | //! adjusted later. Therefore, in this module, we generally ignore the | |
25 | //! borrow kind (and derived mutabilities) that are returned from | |
26 | //! mem-categorization, since they may be inaccurate. (Another option | |
27 | //! would be to use a unification scheme, where instead of returning a | |
28 | //! concrete borrow kind like `ty::ImmBorrow`, we return a | |
29 | //! `ty::InferBorrow(upvar_id)` or something like that, but this would | |
30 | //! then mean that all later passes would have to check for these figments | |
31 | //! and report an error, and it just seems like more mess in the end.) | |
32 | ||
33 | use super::FnCtxt; | |
34 | ||
60c5eb7d | 35 | use crate::expr_use_visitor as euv; |
dc9dc135 | 36 | use rustc_data_structures::fx::FxIndexMap; |
cdc7bbd5 | 37 | use rustc_errors::Applicability; |
dfeec247 XL |
38 | use rustc_hir as hir; |
39 | use rustc_hir::def_id::DefId; | |
40 | use rustc_hir::def_id::LocalDefId; | |
41 | use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; | |
74b04a01 | 42 | use rustc_infer::infer::UpvarRegion; |
6a06907d XL |
43 | use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, ProjectionKind}; |
44 | use rustc_middle::mir::FakeReadCause; | |
17df50a5 | 45 | use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, TypeckResults, UpvarSubsts}; |
5869c6ff | 46 | use rustc_session::lint; |
fc512014 | 47 | use rustc_span::sym; |
5869c6ff | 48 | use rustc_span::{MultiSpan, Span, Symbol}; |
17df50a5 | 49 | use rustc_trait_selection::traits::{Obligation, ObligationCause}; |
fc512014 | 50 | |
17df50a5 | 51 | use rustc_data_structures::stable_set::FxHashSet; |
6a06907d XL |
52 | use rustc_index::vec::Idx; |
53 | use rustc_target::abi::VariantIdx; | |
54 | ||
cdc7bbd5 XL |
55 | use std::iter; |
56 | ||
fc512014 XL |
57 | /// Describe the relationship between the paths of two places |
58 | /// eg: | |
59 | /// - `foo` is ancestor of `foo.bar.baz` | |
60 | /// - `foo.bar.baz` is an descendant of `foo.bar` | |
61 | /// - `foo.bar` and `foo.baz` are divergent | |
62 | enum PlaceAncestryRelation { | |
63 | Ancestor, | |
64 | Descendant, | |
65 | Divergent, | |
66 | } | |
1a4d82fc | 67 | |
5869c6ff XL |
68 | /// Intermediate format to store a captured `Place` and associated `ty::CaptureInfo` |
69 | /// during capture analysis. Information in this map feeds into the minimum capture | |
70 | /// analysis pass. | |
71 | type InferredCaptureInformation<'tcx> = FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>; | |
72 | ||
dc9dc135 | 73 | impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
dfeec247 | 74 | pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { |
041b39d2 | 75 | InferBorrowKindVisitor { fcx: self }.visit_body(body); |
e9174d1e | 76 | |
a7813a04 XL |
77 | // it's our job to process these. |
78 | assert!(self.deferred_call_resolutions.borrow().is_empty()); | |
79 | } | |
e9174d1e SL |
80 | } |
81 | ||
dc9dc135 XL |
82 | struct InferBorrowKindVisitor<'a, 'tcx> { |
83 | fcx: &'a FnCtxt<'a, 'tcx>, | |
1a4d82fc JJ |
84 | } |
85 | ||
dc9dc135 | 86 | impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> { |
ba9703b0 | 87 | type Map = intravisit::ErasedMap<'tcx>; |
dfeec247 | 88 | |
ba9703b0 | 89 | fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { |
32a655c1 | 90 | NestedVisitorMap::None |
476ff2be SL |
91 | } |
92 | ||
dfeec247 | 93 | fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { |
e74abb32 | 94 | if let hir::ExprKind::Closure(cc, _, body_id, _, _) = expr.kind { |
0731742a | 95 | let body = self.fcx.tcx.hir().body(body_id); |
0bf4aa26 | 96 | self.visit_body(body); |
cdc7bbd5 | 97 | self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, cc); |
1a4d82fc JJ |
98 | } |
99 | ||
92a42be0 | 100 | intravisit::walk_expr(self, expr); |
1a4d82fc | 101 | } |
1a4d82fc JJ |
102 | } |
103 | ||
dc9dc135 | 104 | impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
60c5eb7d | 105 | /// Analysis starting point. |
ff7c6d11 XL |
106 | fn analyze_closure( |
107 | &self, | |
ff7c6d11 XL |
108 | closure_hir_id: hir::HirId, |
109 | span: Span, | |
cdc7bbd5 | 110 | body_id: hir::BodyId, |
5869c6ff | 111 | body: &'tcx hir::Body<'tcx>, |
60c5eb7d | 112 | capture_clause: hir::CaptureBy, |
ff7c6d11 | 113 | ) { |
dfeec247 | 114 | debug!("analyze_closure(id={:?}, body.id={:?})", closure_hir_id, body.id()); |
1a4d82fc | 115 | |
ff7c6d11 | 116 | // Extract the type of the closure. |
532ac7d7 | 117 | let ty = self.node_ty(closure_hir_id); |
1b1a35ee | 118 | let (closure_def_id, substs) = match *ty.kind() { |
dfeec247 | 119 | ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs)), |
b7449926 | 120 | ty::Generator(def_id, substs, _) => (def_id, UpvarSubsts::Generator(substs)), |
f035d41b | 121 | ty::Error(_) => { |
8faf50e0 XL |
122 | // #51714: skip analysis when we have already encountered type errors |
123 | return; | |
124 | } | |
532ac7d7 | 125 | _ => { |
ff7c6d11 XL |
126 | span_bug!( |
127 | span, | |
128 | "type of closure expr {:?} is not a closure {:?}", | |
532ac7d7 XL |
129 | closure_hir_id, |
130 | ty | |
ff7c6d11 | 131 | ); |
041b39d2 XL |
132 | } |
133 | }; | |
134 | ||
a1dfa0c6 | 135 | let infer_kind = if let UpvarSubsts::Closure(closure_substs) = substs { |
ba9703b0 | 136 | self.closure_kind(closure_substs).is_none().then_some(closure_substs) |
ff7c6d11 | 137 | } else { |
94b46f34 | 138 | None |
ff7c6d11 | 139 | }; |
3b2f2976 | 140 | |
fc512014 | 141 | let local_def_id = closure_def_id.expect_local(); |
85aaf69f | 142 | |
f9f354fc XL |
143 | let body_owner_def_id = self.tcx.hir().body_owner_def_id(body.id()); |
144 | assert_eq!(body_owner_def_id.to_def_id(), closure_def_id); | |
ff7c6d11 XL |
145 | let mut delegate = InferBorrowKind { |
146 | fcx: self, | |
60c5eb7d | 147 | closure_def_id, |
fc512014 XL |
148 | closure_span: span, |
149 | capture_clause, | |
ff7c6d11 XL |
150 | current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM, |
151 | current_origin: None, | |
5869c6ff | 152 | capture_information: Default::default(), |
6a06907d | 153 | fake_reads: Default::default(), |
ff7c6d11 | 154 | }; |
60c5eb7d | 155 | euv::ExprUseVisitor::new( |
ff7c6d11 XL |
156 | &mut delegate, |
157 | &self.infcx, | |
dc9dc135 | 158 | body_owner_def_id, |
ff7c6d11 | 159 | self.param_env, |
3dfed10e | 160 | &self.typeck_results.borrow(), |
0731742a XL |
161 | ) |
162 | .consume_body(body); | |
ff7c6d11 | 163 | |
fc512014 XL |
164 | debug!( |
165 | "For closure={:?}, capture_information={:#?}", | |
166 | closure_def_id, delegate.capture_information | |
167 | ); | |
168 | self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span); | |
169 | ||
5869c6ff XL |
170 | self.compute_min_captures(closure_def_id, delegate.capture_information); |
171 | ||
172 | let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); | |
17df50a5 XL |
173 | |
174 | if should_do_disjoint_capture_migration_analysis(self.tcx, closure_hir_id) { | |
cdc7bbd5 | 175 | self.perform_2229_migration_anaysis(closure_def_id, body_id, capture_clause, span); |
5869c6ff XL |
176 | } |
177 | ||
178 | // We now fake capture information for all variables that are mentioned within the closure | |
179 | // We do this after handling migrations so that min_captures computes before | |
180 | if !self.tcx.features().capture_disjoint_fields { | |
181 | let mut capture_information: InferredCaptureInformation<'tcx> = Default::default(); | |
182 | ||
183 | if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { | |
184 | for var_hir_id in upvars.keys() { | |
185 | let place = self.place_for_root_variable(local_def_id, *var_hir_id); | |
186 | ||
187 | debug!("seed place {:?}", place); | |
188 | ||
189 | let upvar_id = ty::UpvarId::new(*var_hir_id, local_def_id); | |
6a06907d XL |
190 | let capture_kind = |
191 | self.init_capture_kind_for_place(&place, capture_clause, upvar_id, span); | |
5869c6ff XL |
192 | let fake_info = ty::CaptureInfo { |
193 | capture_kind_expr_id: None, | |
194 | path_expr_id: None, | |
195 | capture_kind, | |
196 | }; | |
197 | ||
198 | capture_information.insert(place, fake_info); | |
199 | } | |
200 | } | |
201 | ||
202 | // This will update the min captures based on this new fake information. | |
203 | self.compute_min_captures(closure_def_id, capture_information); | |
204 | } | |
205 | ||
94b46f34 | 206 | if let Some(closure_substs) = infer_kind { |
ff7c6d11 XL |
207 | // Unify the (as yet unbound) type variable in the closure |
208 | // substs with the kind we inferred. | |
209 | let inferred_kind = delegate.current_closure_kind; | |
ba9703b0 | 210 | let closure_kind_ty = closure_substs.as_closure().kind_ty(); |
ff7c6d11 XL |
211 | self.demand_eqtype(span, inferred_kind.to_ty(self.tcx), closure_kind_ty); |
212 | ||
213 | // If we have an origin, store it. | |
5869c6ff XL |
214 | if let Some(origin) = delegate.current_origin.clone() { |
215 | let origin = if self.tcx.features().capture_disjoint_fields { | |
6a06907d | 216 | (origin.0, restrict_capture_precision(origin.1)) |
5869c6ff | 217 | } else { |
5869c6ff XL |
218 | (origin.0, Place { projections: vec![], ..origin.1 }) |
219 | }; | |
220 | ||
3dfed10e XL |
221 | self.typeck_results |
222 | .borrow_mut() | |
223 | .closure_kind_origins_mut() | |
224 | .insert(closure_hir_id, origin); | |
041b39d2 | 225 | } |
c1a9b12d | 226 | } |
85aaf69f | 227 | |
fc512014 XL |
228 | self.log_closure_min_capture_info(closure_def_id, span); |
229 | ||
c1a9b12d SL |
230 | // Now that we've analyzed the closure, we know how each |
231 | // variable is borrowed, and we know what traits the closure | |
232 | // implements (Fn vs FnMut etc). We now have some updates to do | |
233 | // with that information. | |
85aaf69f | 234 | // |
c1a9b12d SL |
235 | // Note that no closure type C may have an upvar of type C |
236 | // (though it may reference itself via a trait object). This | |
237 | // results from the desugaring of closures to a struct like | |
238 | // `Foo<..., UV0...UVn>`. If one of those upvars referenced | |
239 | // C, then the type would have infinite size (and the | |
240 | // inference algorithm will reject it). | |
241 | ||
ff7c6d11 | 242 | // Equate the type variables for the upvars with the actual types. |
fc512014 | 243 | let final_upvar_tys = self.final_upvar_tys(closure_def_id); |
ff7c6d11 | 244 | debug!( |
94b46f34 | 245 | "analyze_closure: id={:?} substs={:?} final_upvar_tys={:?}", |
532ac7d7 | 246 | closure_hir_id, substs, final_upvar_tys |
ff7c6d11 | 247 | ); |
29967ef6 XL |
248 | |
249 | // Build a tuple (U0..Un) of the final upvar types U0..Un | |
250 | // and unify the upvar tupe type in the closure with it: | |
251 | let final_tupled_upvars_type = self.tcx.mk_tup(final_upvar_tys.iter()); | |
252 | self.demand_suptype(span, substs.tupled_upvars_ty(), final_tupled_upvars_type); | |
c1a9b12d | 253 | |
6a06907d XL |
254 | let fake_reads = delegate |
255 | .fake_reads | |
256 | .into_iter() | |
257 | .map(|(place, cause, hir_id)| (place, cause, hir_id)) | |
258 | .collect(); | |
259 | self.typeck_results.borrow_mut().closure_fake_reads.insert(closure_def_id, fake_reads); | |
260 | ||
041b39d2 XL |
261 | // If we are also inferred the closure kind here, |
262 | // process any deferred resolutions. | |
ff7c6d11 XL |
263 | let deferred_call_resolutions = self.remove_deferred_call_resolutions(closure_def_id); |
264 | for deferred_call_resolution in deferred_call_resolutions { | |
265 | deferred_call_resolution.resolve(self); | |
85aaf69f SL |
266 | } |
267 | } | |
268 | ||
dfeec247 | 269 | // Returns a list of `Ty`s for each upvar. |
fc512014 | 270 | fn final_upvar_tys(&self, closure_id: DefId) -> Vec<Ty<'tcx>> { |
c1a9b12d SL |
271 | // Presently an unboxed closure type cannot "escape" out of a |
272 | // function, so we will only encounter ones that originated in the | |
273 | // local crate or were inlined into it along with some function. | |
274 | // This may change if abstract return types of some sort are | |
275 | // implemented. | |
fc512014 XL |
276 | self.typeck_results |
277 | .borrow() | |
278 | .closure_min_captures_flattened(closure_id) | |
279 | .map(|captured_place| { | |
280 | let upvar_ty = captured_place.place.ty(); | |
281 | let capture = captured_place.info.capture_kind; | |
ff7c6d11 | 282 | |
fc512014 | 283 | debug!( |
5869c6ff XL |
284 | "final_upvar_tys: place={:?} upvar_ty={:?} capture={:?}, mutability={:?}", |
285 | captured_place.place, upvar_ty, capture, captured_place.mutability, | |
fc512014 XL |
286 | ); |
287 | ||
288 | match capture { | |
289 | ty::UpvarCapture::ByValue(_) => upvar_ty, | |
6a06907d | 290 | ty::UpvarCapture::ByRef(borrow) => self.tcx.mk_ref( |
fc512014 XL |
291 | borrow.region, |
292 | ty::TypeAndMut { ty: upvar_ty, mutbl: borrow.kind.to_mutbl_lossy() }, | |
293 | ), | |
294 | } | |
dfeec247 | 295 | }) |
48663c56 | 296 | .collect() |
c1a9b12d | 297 | } |
fc512014 | 298 | |
fc512014 XL |
299 | /// Analyzes the information collected by `InferBorrowKind` to compute the min number of |
300 | /// Places (and corresponding capture kind) that we need to keep track of to support all | |
301 | /// the required captured paths. | |
302 | /// | |
5869c6ff XL |
303 | /// |
304 | /// Note: If this function is called multiple times for the same closure, it will update | |
305 | /// the existing min_capture map that is stored in TypeckResults. | |
306 | /// | |
fc512014 XL |
307 | /// Eg: |
308 | /// ```rust,no_run | |
309 | /// struct Point { x: i32, y: i32 } | |
310 | /// | |
311 | /// let s: String; // hir_id_s | |
312 | /// let mut p: Point; // his_id_p | |
313 | /// let c = || { | |
314 | /// println!("{}", s); // L1 | |
315 | /// p.x += 10; // L2 | |
316 | /// println!("{}" , p.y) // L3 | |
317 | /// println!("{}", p) // L4 | |
318 | /// drop(s); // L5 | |
319 | /// }; | |
320 | /// ``` | |
321 | /// and let hir_id_L1..5 be the expressions pointing to use of a captured variable on | |
322 | /// the lines L1..5 respectively. | |
323 | /// | |
324 | /// InferBorrowKind results in a structure like this: | |
325 | /// | |
17df50a5 | 326 | /// ```text |
fc512014 | 327 | /// { |
5869c6ff XL |
328 | /// Place(base: hir_id_s, projections: [], ....) -> { |
329 | /// capture_kind_expr: hir_id_L5, | |
330 | /// path_expr_id: hir_id_L5, | |
331 | /// capture_kind: ByValue | |
332 | /// }, | |
333 | /// Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> { | |
334 | /// capture_kind_expr: hir_id_L2, | |
335 | /// path_expr_id: hir_id_L2, | |
336 | /// capture_kind: ByValue | |
337 | /// }, | |
338 | /// Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> { | |
339 | /// capture_kind_expr: hir_id_L3, | |
340 | /// path_expr_id: hir_id_L3, | |
341 | /// capture_kind: ByValue | |
342 | /// }, | |
343 | /// Place(base: hir_id_p, projections: [], ...) -> { | |
344 | /// capture_kind_expr: hir_id_L4, | |
345 | /// path_expr_id: hir_id_L4, | |
346 | /// capture_kind: ByValue | |
347 | /// }, | |
fc512014 XL |
348 | /// ``` |
349 | /// | |
350 | /// After the min capture analysis, we get: | |
17df50a5 | 351 | /// ```text |
fc512014 XL |
352 | /// { |
353 | /// hir_id_s -> [ | |
5869c6ff XL |
354 | /// Place(base: hir_id_s, projections: [], ....) -> { |
355 | /// capture_kind_expr: hir_id_L5, | |
356 | /// path_expr_id: hir_id_L5, | |
357 | /// capture_kind: ByValue | |
358 | /// }, | |
fc512014 XL |
359 | /// ], |
360 | /// hir_id_p -> [ | |
5869c6ff XL |
361 | /// Place(base: hir_id_p, projections: [], ...) -> { |
362 | /// capture_kind_expr: hir_id_L2, | |
363 | /// path_expr_id: hir_id_L4, | |
364 | /// capture_kind: ByValue | |
365 | /// }, | |
fc512014 XL |
366 | /// ], |
367 | /// ``` | |
368 | fn compute_min_captures( | |
369 | &self, | |
370 | closure_def_id: DefId, | |
5869c6ff | 371 | capture_information: InferredCaptureInformation<'tcx>, |
fc512014 | 372 | ) { |
5869c6ff XL |
373 | if capture_information.is_empty() { |
374 | return; | |
375 | } | |
376 | ||
377 | let mut typeck_results = self.typeck_results.borrow_mut(); | |
fc512014 | 378 | |
5869c6ff XL |
379 | let mut root_var_min_capture_list = |
380 | typeck_results.closure_min_captures.remove(&closure_def_id).unwrap_or_default(); | |
381 | ||
382 | for (place, capture_info) in capture_information.into_iter() { | |
fc512014 XL |
383 | let var_hir_id = match place.base { |
384 | PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, | |
385 | base => bug!("Expected upvar, found={:?}", base), | |
386 | }; | |
387 | ||
6a06907d | 388 | let place = restrict_capture_precision(place); |
fc512014 XL |
389 | |
390 | let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) { | |
391 | None => { | |
5869c6ff XL |
392 | let mutability = self.determine_capture_mutability(&typeck_results, &place); |
393 | let min_cap_list = | |
394 | vec![ty::CapturedPlace { place, info: capture_info, mutability }]; | |
fc512014 XL |
395 | root_var_min_capture_list.insert(var_hir_id, min_cap_list); |
396 | continue; | |
397 | } | |
398 | Some(min_cap_list) => min_cap_list, | |
399 | }; | |
400 | ||
401 | // Go through each entry in the current list of min_captures | |
402 | // - if ancestor is found, update it's capture kind to account for current place's | |
403 | // capture information. | |
404 | // | |
405 | // - if descendant is found, remove it from the list, and update the current place's | |
406 | // capture information to account for the descendants's capture kind. | |
407 | // | |
408 | // We can never be in a case where the list contains both an ancestor and a descendant | |
409 | // Also there can only be ancestor but in case of descendants there might be | |
410 | // multiple. | |
411 | ||
412 | let mut descendant_found = false; | |
413 | let mut updated_capture_info = capture_info; | |
414 | min_cap_list.retain(|possible_descendant| { | |
415 | match determine_place_ancestry_relation(&place, &possible_descendant.place) { | |
416 | // current place is ancestor of possible_descendant | |
417 | PlaceAncestryRelation::Ancestor => { | |
418 | descendant_found = true; | |
5869c6ff XL |
419 | let backup_path_expr_id = updated_capture_info.path_expr_id; |
420 | ||
fc512014 XL |
421 | updated_capture_info = |
422 | determine_capture_info(updated_capture_info, possible_descendant.info); | |
5869c6ff XL |
423 | |
424 | // we need to keep the ancestor's `path_expr_id` | |
425 | updated_capture_info.path_expr_id = backup_path_expr_id; | |
fc512014 XL |
426 | false |
427 | } | |
428 | ||
429 | _ => true, | |
430 | } | |
431 | }); | |
432 | ||
433 | let mut ancestor_found = false; | |
434 | if !descendant_found { | |
435 | for possible_ancestor in min_cap_list.iter_mut() { | |
436 | match determine_place_ancestry_relation(&place, &possible_ancestor.place) { | |
437 | // current place is descendant of possible_ancestor | |
438 | PlaceAncestryRelation::Descendant => { | |
439 | ancestor_found = true; | |
5869c6ff | 440 | let backup_path_expr_id = possible_ancestor.info.path_expr_id; |
fc512014 XL |
441 | possible_ancestor.info = |
442 | determine_capture_info(possible_ancestor.info, capture_info); | |
443 | ||
5869c6ff XL |
444 | // we need to keep the ancestor's `path_expr_id` |
445 | possible_ancestor.info.path_expr_id = backup_path_expr_id; | |
446 | ||
fc512014 XL |
447 | // Only one ancestor of the current place will be in the list. |
448 | break; | |
449 | } | |
450 | _ => {} | |
451 | } | |
452 | } | |
453 | } | |
454 | ||
455 | // Only need to insert when we don't have an ancestor in the existing min capture list | |
456 | if !ancestor_found { | |
5869c6ff | 457 | let mutability = self.determine_capture_mutability(&typeck_results, &place); |
fc512014 | 458 | let captured_place = |
5869c6ff | 459 | ty::CapturedPlace { place, info: updated_capture_info, mutability }; |
fc512014 XL |
460 | min_cap_list.push(captured_place); |
461 | } | |
462 | } | |
463 | ||
464 | debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list); | |
5869c6ff XL |
465 | typeck_results.closure_min_captures.insert(closure_def_id, root_var_min_capture_list); |
466 | } | |
fc512014 | 467 | |
5869c6ff XL |
468 | /// Perform the migration analysis for RFC 2229, and emit lint |
469 | /// `disjoint_capture_drop_reorder` if needed. | |
470 | fn perform_2229_migration_anaysis( | |
471 | &self, | |
472 | closure_def_id: DefId, | |
cdc7bbd5 | 473 | body_id: hir::BodyId, |
5869c6ff XL |
474 | capture_clause: hir::CaptureBy, |
475 | span: Span, | |
5869c6ff | 476 | ) { |
17df50a5 | 477 | let (need_migrations, reasons) = self.compute_2229_migrations( |
5869c6ff XL |
478 | closure_def_id, |
479 | span, | |
480 | capture_clause, | |
5869c6ff XL |
481 | self.typeck_results.borrow().closure_min_captures.get(&closure_def_id), |
482 | ); | |
483 | ||
484 | if !need_migrations.is_empty() { | |
cdc7bbd5 XL |
485 | let (migration_string, migrated_variables_concat) = |
486 | migration_suggestion_for_2229(self.tcx, &need_migrations); | |
5869c6ff XL |
487 | |
488 | let local_def_id = closure_def_id.expect_local(); | |
489 | let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); | |
490 | self.tcx.struct_span_lint_hir( | |
17df50a5 | 491 | lint::builtin::DISJOINT_CAPTURE_MIGRATION, |
5869c6ff XL |
492 | closure_hir_id, |
493 | span, | |
494 | |lint| { | |
495 | let mut diagnostics_builder = lint.build( | |
17df50a5 XL |
496 | format!( |
497 | "{} affected for closure because of `capture_disjoint_fields`", | |
498 | reasons | |
499 | ) | |
500 | .as_str(), | |
5869c6ff | 501 | ); |
cdc7bbd5 XL |
502 | let closure_body_span = self.tcx.hir().span(body_id.hir_id); |
503 | let (sugg, app) = | |
504 | match self.tcx.sess.source_map().span_to_snippet(closure_body_span) { | |
505 | Ok(s) => { | |
506 | let trimmed = s.trim_start(); | |
507 | ||
508 | // If the closure contains a block then replace the opening brace | |
509 | // with "{ let _ = (..); " | |
510 | let sugg = if let Some('{') = trimmed.chars().next() { | |
511 | format!("{{ {}; {}", migration_string, &trimmed[1..]) | |
512 | } else { | |
513 | format!("{{ {}; {} }}", migration_string, s) | |
514 | }; | |
515 | (sugg, Applicability::MachineApplicable) | |
516 | } | |
517 | Err(_) => (migration_string.clone(), Applicability::HasPlaceholders), | |
518 | }; | |
519 | ||
520 | let diagnostic_msg = format!( | |
521 | "add a dummy let to cause {} to be fully captured", | |
522 | migrated_variables_concat | |
523 | ); | |
524 | ||
525 | diagnostics_builder.span_suggestion( | |
526 | closure_body_span, | |
527 | &diagnostic_msg, | |
528 | sugg, | |
529 | app, | |
530 | ); | |
5869c6ff XL |
531 | diagnostics_builder.emit(); |
532 | }, | |
533 | ); | |
534 | } | |
535 | } | |
536 | ||
17df50a5 XL |
537 | /// Combines all the reasons for 2229 migrations |
538 | fn compute_2229_migrations_reasons( | |
539 | &self, | |
540 | auto_trait_reasons: FxHashSet<&str>, | |
541 | drop_reason: bool, | |
542 | ) -> String { | |
543 | let mut reasons = String::new(); | |
544 | ||
545 | if auto_trait_reasons.len() > 0 { | |
546 | reasons = format!( | |
547 | "{} trait implementation", | |
548 | auto_trait_reasons.clone().into_iter().collect::<Vec<&str>>().join(", ") | |
549 | ); | |
550 | } | |
551 | ||
552 | if auto_trait_reasons.len() > 0 && drop_reason { | |
553 | reasons = format!("{}, and ", reasons); | |
554 | } | |
555 | ||
556 | if drop_reason { | |
557 | reasons = format!("{}drop order", reasons); | |
558 | } | |
559 | ||
560 | reasons | |
561 | } | |
562 | ||
563 | /// Returns true if `ty` may implement `trait_def_id` | |
564 | fn ty_impls_trait( | |
565 | &self, | |
566 | ty: Ty<'tcx>, | |
567 | cause: &ObligationCause<'tcx>, | |
568 | trait_def_id: DefId, | |
569 | ) -> bool { | |
570 | use crate::rustc_middle::ty::ToPredicate; | |
571 | use crate::rustc_middle::ty::WithConstness; | |
572 | use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; | |
573 | let tcx = self.infcx.tcx; | |
574 | ||
575 | let trait_ref = TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, &[]) }; | |
576 | ||
577 | let obligation = Obligation::new( | |
578 | cause.clone(), | |
579 | self.param_env, | |
580 | trait_ref.without_const().to_predicate(tcx), | |
581 | ); | |
582 | ||
583 | self.infcx.predicate_may_hold(&obligation) | |
584 | } | |
585 | ||
586 | /// Returns true if migration is needed for trait for the provided var_hir_id | |
587 | fn need_2229_migrations_for_trait( | |
588 | &self, | |
589 | min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, | |
590 | var_hir_id: hir::HirId, | |
591 | check_trait: Option<DefId>, | |
592 | ) -> bool { | |
593 | let root_var_min_capture_list = if let Some(root_var_min_capture_list) = | |
594 | min_captures.and_then(|m| m.get(&var_hir_id)) | |
595 | { | |
596 | root_var_min_capture_list | |
597 | } else { | |
598 | return false; | |
599 | }; | |
600 | ||
601 | let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id)); | |
602 | ||
603 | let cause = ObligationCause::misc(self.tcx.hir().span(var_hir_id), self.body_id); | |
604 | ||
605 | let obligation_should_hold = check_trait | |
606 | .map(|check_trait| self.ty_impls_trait(ty, &cause, check_trait)) | |
607 | .unwrap_or(false); | |
608 | ||
609 | // Check whether catpured fields also implement the trait | |
610 | ||
611 | for capture in root_var_min_capture_list.iter() { | |
612 | let ty = capture.place.ty(); | |
613 | ||
614 | let obligation_holds_for_capture = check_trait | |
615 | .map(|check_trait| self.ty_impls_trait(ty, &cause, check_trait)) | |
616 | .unwrap_or(false); | |
617 | ||
618 | if !obligation_holds_for_capture && obligation_should_hold { | |
619 | return true; | |
620 | } | |
621 | } | |
622 | false | |
623 | } | |
624 | ||
625 | /// Figures out the list of root variables (and their types) that aren't completely | |
626 | /// captured by the closure when `capture_disjoint_fields` is enabled and auto-traits | |
627 | /// differ between the root variable and the captured paths. | |
628 | /// | |
629 | /// The output list would include a root variable if: | |
630 | /// - It would have been captured into the closure when `capture_disjoint_fields` wasn't | |
631 | /// enabled, **and** | |
632 | /// - It wasn't completely captured by the closure, **and** | |
633 | /// - One of the paths captured does not implement all the auto-traits its root variable | |
634 | /// implements. | |
635 | fn compute_2229_migrations_for_trait( | |
636 | &self, | |
637 | min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, | |
638 | var_hir_id: hir::HirId, | |
639 | ) -> Option<FxHashSet<&str>> { | |
640 | let tcx = self.infcx.tcx; | |
641 | ||
642 | // Check whether catpured fields also implement the trait | |
643 | let mut auto_trait_reasons = FxHashSet::default(); | |
644 | ||
645 | if self.need_2229_migrations_for_trait( | |
646 | min_captures, | |
647 | var_hir_id, | |
648 | tcx.lang_items().clone_trait(), | |
649 | ) { | |
650 | auto_trait_reasons.insert("`Clone`"); | |
651 | } | |
652 | ||
653 | if self.need_2229_migrations_for_trait( | |
654 | min_captures, | |
655 | var_hir_id, | |
656 | tcx.lang_items().sync_trait(), | |
657 | ) { | |
658 | auto_trait_reasons.insert("`Sync`"); | |
659 | } | |
660 | ||
661 | if self.need_2229_migrations_for_trait( | |
662 | min_captures, | |
663 | var_hir_id, | |
664 | tcx.lang_items().send_trait(), | |
665 | ) { | |
666 | auto_trait_reasons.insert("`Send`"); | |
667 | } | |
668 | ||
669 | if self.need_2229_migrations_for_trait( | |
670 | min_captures, | |
671 | var_hir_id, | |
672 | tcx.lang_items().unpin_trait(), | |
673 | ) { | |
674 | auto_trait_reasons.insert("`Unpin`"); | |
675 | } | |
676 | ||
677 | if self.need_2229_migrations_for_trait( | |
678 | min_captures, | |
679 | var_hir_id, | |
680 | tcx.lang_items().unwind_safe_trait(), | |
681 | ) { | |
682 | auto_trait_reasons.insert("`UnwindSafe`"); | |
683 | } | |
684 | ||
685 | if self.need_2229_migrations_for_trait( | |
686 | min_captures, | |
687 | var_hir_id, | |
688 | tcx.lang_items().ref_unwind_safe_trait(), | |
689 | ) { | |
690 | auto_trait_reasons.insert("`RefUnwindSafe`"); | |
691 | } | |
692 | ||
693 | if auto_trait_reasons.len() > 0 { | |
694 | return Some(auto_trait_reasons); | |
695 | } | |
696 | ||
697 | return None; | |
698 | } | |
699 | ||
5869c6ff XL |
700 | /// Figures out the list of root variables (and their types) that aren't completely |
701 | /// captured by the closure when `capture_disjoint_fields` is enabled and drop order of | |
702 | /// some path starting at that root variable **might** be affected. | |
703 | /// | |
704 | /// The output list would include a root variable if: | |
705 | /// - It would have been moved into the closure when `capture_disjoint_fields` wasn't | |
706 | /// enabled, **and** | |
707 | /// - It wasn't completely captured by the closure, **and** | |
6a06907d | 708 | /// - One of the paths starting at this root variable, that is not captured needs Drop. |
17df50a5 XL |
709 | /// |
710 | /// This function only returns true for significant drops. A type is considerent to have a | |
711 | /// significant drop if it's Drop implementation is not annotated by `rustc_insignificant_dtor`. | |
712 | fn compute_2229_migrations_for_drop( | |
5869c6ff XL |
713 | &self, |
714 | closure_def_id: DefId, | |
715 | closure_span: Span, | |
5869c6ff | 716 | min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, |
17df50a5 XL |
717 | closure_clause: hir::CaptureBy, |
718 | var_hir_id: hir::HirId, | |
719 | ) -> bool { | |
720 | let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id)); | |
5869c6ff | 721 | |
17df50a5 XL |
722 | if !ty.has_significant_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) { |
723 | return false; | |
724 | } | |
5869c6ff | 725 | |
17df50a5 XL |
726 | let root_var_min_capture_list = if let Some(root_var_min_capture_list) = |
727 | min_captures.and_then(|m| m.get(&var_hir_id)) | |
728 | { | |
729 | root_var_min_capture_list | |
730 | } else { | |
731 | // The upvar is mentioned within the closure but no path starting from it is | |
732 | // used. | |
5869c6ff | 733 | |
17df50a5 XL |
734 | match closure_clause { |
735 | // Only migrate if closure is a move closure | |
736 | hir::CaptureBy::Value => return true, | |
737 | hir::CaptureBy::Ref => {} | |
5869c6ff XL |
738 | } |
739 | ||
17df50a5 XL |
740 | return false; |
741 | }; | |
5869c6ff | 742 | |
17df50a5 XL |
743 | let projections_list = root_var_min_capture_list |
744 | .iter() | |
745 | .filter_map(|captured_place| match captured_place.info.capture_kind { | |
746 | // Only care about captures that are moved into the closure | |
747 | ty::UpvarCapture::ByValue(..) => Some(captured_place.place.projections.as_slice()), | |
748 | ty::UpvarCapture::ByRef(..) => None, | |
749 | }) | |
750 | .collect::<Vec<_>>(); | |
5869c6ff | 751 | |
17df50a5 | 752 | let is_moved = !projections_list.is_empty(); |
5869c6ff | 753 | |
17df50a5 XL |
754 | let is_not_completely_captured = |
755 | root_var_min_capture_list.iter().any(|capture| capture.place.projections.len() > 0); | |
5869c6ff | 756 | |
17df50a5 XL |
757 | if is_moved |
758 | && is_not_completely_captured | |
759 | && self.has_significant_drop_outside_of_captures( | |
760 | closure_def_id, | |
761 | closure_span, | |
762 | ty, | |
763 | projections_list, | |
764 | ) | |
765 | { | |
766 | return true; | |
767 | } | |
6a06907d | 768 | |
17df50a5 XL |
769 | return false; |
770 | } | |
5869c6ff | 771 | |
17df50a5 XL |
772 | /// Figures out the list of root variables (and their types) that aren't completely |
773 | /// captured by the closure when `capture_disjoint_fields` is enabled and either drop | |
774 | /// order of some path starting at that root variable **might** be affected or auto-traits | |
775 | /// differ between the root variable and the captured paths. | |
776 | /// | |
777 | /// The output list would include a root variable if: | |
778 | /// - It would have been moved into the closure when `capture_disjoint_fields` wasn't | |
779 | /// enabled, **and** | |
780 | /// - It wasn't completely captured by the closure, **and** | |
781 | /// - One of the paths starting at this root variable, that is not captured needs Drop **or** | |
782 | /// - One of the paths captured does not implement all the auto-traits its root variable | |
783 | /// implements. | |
784 | /// | |
785 | /// Returns a tuple containing a vector of HirIds as well as a String containing the reason | |
786 | /// why root variables whose HirId is contained in the vector should be fully captured. | |
787 | fn compute_2229_migrations( | |
788 | &self, | |
789 | closure_def_id: DefId, | |
790 | closure_span: Span, | |
791 | closure_clause: hir::CaptureBy, | |
792 | min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, | |
793 | ) -> (Vec<hir::HirId>, String) { | |
794 | let upvars = if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { | |
795 | upvars | |
796 | } else { | |
797 | return (Vec::new(), format!("")); | |
798 | }; | |
5869c6ff | 799 | |
17df50a5 XL |
800 | let mut need_migrations = Vec::new(); |
801 | let mut auto_trait_reasons = FxHashSet::default(); | |
802 | let mut drop_reorder_reason = false; | |
803 | ||
804 | // Perform auto-trait analysis | |
805 | for (&var_hir_id, _) in upvars.iter() { | |
806 | let mut need_migration = false; | |
807 | if let Some(trait_migration_cause) = | |
808 | self.compute_2229_migrations_for_trait(min_captures, var_hir_id) | |
6a06907d | 809 | { |
17df50a5 XL |
810 | need_migration = true; |
811 | auto_trait_reasons.extend(trait_migration_cause); | |
812 | } | |
813 | ||
814 | if self.compute_2229_migrations_for_drop( | |
815 | closure_def_id, | |
816 | closure_span, | |
817 | min_captures, | |
818 | closure_clause, | |
819 | var_hir_id, | |
820 | ) { | |
821 | need_migration = true; | |
822 | drop_reorder_reason = true; | |
823 | } | |
824 | ||
825 | if need_migration { | |
6a06907d | 826 | need_migrations.push(var_hir_id); |
5869c6ff | 827 | } |
fc512014 | 828 | } |
5869c6ff | 829 | |
17df50a5 XL |
830 | ( |
831 | need_migrations, | |
832 | self.compute_2229_migrations_reasons(auto_trait_reasons, drop_reorder_reason), | |
833 | ) | |
fc512014 XL |
834 | } |
835 | ||
6a06907d XL |
836 | /// This is a helper function to `compute_2229_migrations_precise_pass`. Provided the type |
837 | /// of a root variable and a list of captured paths starting at this root variable (expressed | |
838 | /// using list of `Projection` slices), it returns true if there is a path that is not | |
839 | /// captured starting at this root variable that implements Drop. | |
840 | /// | |
6a06907d XL |
841 | /// The way this function works is at a given call it looks at type `base_path_ty` of some base |
842 | /// path say P and then list of projection slices which represent the different captures moved | |
843 | /// into the closure starting off of P. | |
844 | /// | |
845 | /// This will make more sense with an example: | |
846 | /// | |
847 | /// ```rust | |
848 | /// #![feature(capture_disjoint_fields)] | |
849 | /// | |
850 | /// struct FancyInteger(i32); // This implements Drop | |
851 | /// | |
852 | /// struct Point { x: FancyInteger, y: FancyInteger } | |
853 | /// struct Color; | |
854 | /// | |
855 | /// struct Wrapper { p: Point, c: Color } | |
856 | /// | |
857 | /// fn f(w: Wrapper) { | |
858 | /// let c = || { | |
859 | /// // Closure captures w.p.x and w.c by move. | |
860 | /// }; | |
861 | /// | |
862 | /// c(); | |
863 | /// } | |
864 | /// ``` | |
865 | /// | |
866 | /// If `capture_disjoint_fields` wasn't enabled the closure would've moved `w` instead of the | |
867 | /// precise paths. If we look closely `w.p.y` isn't captured which implements Drop and | |
868 | /// therefore Drop ordering would change and we want this function to return true. | |
869 | /// | |
870 | /// Call stack to figure out if we need to migrate for `w` would look as follows: | |
871 | /// | |
872 | /// Our initial base path is just `w`, and the paths captured from it are `w[p, x]` and | |
873 | /// `w[c]`. | |
874 | /// Notation: | |
875 | /// - Ty(place): Type of place | |
cdc7bbd5 | 876 | /// - `(a, b)`: Represents the function parameters `base_path_ty` and `captured_by_move_projs` |
6a06907d XL |
877 | /// respectively. |
878 | /// ``` | |
879 | /// (Ty(w), [ &[p, x], &[c] ]) | |
880 | /// | | |
881 | /// ---------------------------- | |
882 | /// | | | |
883 | /// v v | |
884 | /// (Ty(w.p), [ &[x] ]) (Ty(w.c), [ &[] ]) // I(1) | |
885 | /// | | | |
886 | /// v v | |
887 | /// (Ty(w.p), [ &[x] ]) false | |
888 | /// | | |
889 | /// | | |
890 | /// ------------------------------- | |
891 | /// | | | |
892 | /// v v | |
893 | /// (Ty((w.p).x), [ &[] ]) (Ty((w.p).y), []) // IMP 2 | |
894 | /// | | | |
895 | /// v v | |
17df50a5 | 896 | /// false NeedsSignificantDrop(Ty(w.p.y)) |
6a06907d XL |
897 | /// | |
898 | /// v | |
899 | /// true | |
900 | /// ``` | |
901 | /// | |
902 | /// IMP 1 `(Ty(w.c), [ &[] ])`: Notice the single empty slice inside `captured_projs`. | |
903 | /// This implies that the `w.c` is completely captured by the closure. | |
904 | /// Since drop for this path will be called when the closure is | |
905 | /// dropped we don't need to migrate for it. | |
906 | /// | |
907 | /// IMP 2 `(Ty((w.p).y), [])`: Notice that `captured_projs` is empty. This implies that this | |
908 | /// path wasn't captured by the closure. Also note that even | |
909 | /// though we didn't capture this path, the function visits it, | |
910 | /// which is kind of the point of this function. We then return | |
911 | /// if the type of `w.p.y` implements Drop, which in this case is | |
912 | /// true. | |
913 | /// | |
914 | /// Consider another example: | |
915 | /// | |
916 | /// ```rust | |
917 | /// struct X; | |
918 | /// impl Drop for X {} | |
919 | /// | |
920 | /// struct Y(X); | |
921 | /// impl Drop for Y {} | |
922 | /// | |
923 | /// fn foo() { | |
924 | /// let y = Y(X); | |
925 | /// let c = || move(y.0); | |
926 | /// } | |
927 | /// ``` | |
928 | /// | |
929 | /// Note that `y.0` is captured by the closure. When this function is called for `y`, it will | |
930 | /// return true, because even though all paths starting at `y` are captured, `y` itself | |
931 | /// implements Drop which will be affected since `y` isn't completely captured. | |
932 | fn has_significant_drop_outside_of_captures( | |
933 | &self, | |
934 | closure_def_id: DefId, | |
935 | closure_span: Span, | |
936 | base_path_ty: Ty<'tcx>, | |
cdc7bbd5 | 937 | captured_by_move_projs: Vec<&[Projection<'tcx>]>, |
6a06907d XL |
938 | ) -> bool { |
939 | let needs_drop = |ty: Ty<'tcx>| { | |
17df50a5 | 940 | ty.has_significant_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) |
6a06907d XL |
941 | }; |
942 | ||
943 | let is_drop_defined_for_ty = |ty: Ty<'tcx>| { | |
944 | let drop_trait = self.tcx.require_lang_item(hir::LangItem::Drop, Some(closure_span)); | |
945 | let ty_params = self.tcx.mk_substs_trait(base_path_ty, &[]); | |
946 | self.tcx.type_implements_trait(( | |
947 | drop_trait, | |
948 | ty, | |
949 | ty_params, | |
950 | self.tcx.param_env(closure_def_id.expect_local()), | |
951 | )) | |
952 | }; | |
953 | ||
954 | let is_drop_defined_for_ty = is_drop_defined_for_ty(base_path_ty); | |
955 | ||
956 | // If there is a case where no projection is applied on top of current place | |
957 | // then there must be exactly one capture corresponding to such a case. Note that this | |
958 | // represents the case of the path being completely captured by the variable. | |
959 | // | |
960 | // eg. If `a.b` is captured and we are processing `a.b`, then we can't have the closure also | |
961 | // capture `a.b.c`, because that voilates min capture. | |
cdc7bbd5 | 962 | let is_completely_captured = captured_by_move_projs.iter().any(|projs| projs.is_empty()); |
6a06907d | 963 | |
cdc7bbd5 | 964 | assert!(!is_completely_captured || (captured_by_move_projs.len() == 1)); |
6a06907d XL |
965 | |
966 | if is_completely_captured { | |
967 | // The place is captured entirely, so doesn't matter if needs dtor, it will be drop | |
968 | // when the closure is dropped. | |
969 | return false; | |
970 | } | |
971 | ||
cdc7bbd5 XL |
972 | if captured_by_move_projs.is_empty() { |
973 | return needs_drop(base_path_ty); | |
974 | } | |
975 | ||
6a06907d XL |
976 | if is_drop_defined_for_ty { |
977 | // If drop is implemented for this type then we need it to be fully captured, | |
cdc7bbd5 | 978 | // and we know it is not completely captured because of the previous checks. |
6a06907d | 979 | |
cdc7bbd5 XL |
980 | // Note that this is a bug in the user code that will be reported by the |
981 | // borrow checker, since we can't move out of drop types. | |
982 | ||
983 | // The bug exists in the user's code pre-migration, and we don't migrate here. | |
984 | return false; | |
6a06907d XL |
985 | } |
986 | ||
987 | match base_path_ty.kind() { | |
988 | // Observations: | |
cdc7bbd5 XL |
989 | // - `captured_by_move_projs` is not empty. Therefore we can call |
990 | // `captured_by_move_projs.first().unwrap()` safely. | |
991 | // - All entries in `captured_by_move_projs` have atleast one projection. | |
992 | // Therefore we can call `captured_by_move_projs.first().unwrap().first().unwrap()` safely. | |
6a06907d XL |
993 | |
994 | // We don't capture derefs in case of move captures, which would have be applied to | |
995 | // access any further paths. | |
996 | ty::Adt(def, _) if def.is_box() => unreachable!(), | |
997 | ty::Ref(..) => unreachable!(), | |
998 | ty::RawPtr(..) => unreachable!(), | |
999 | ||
1000 | ty::Adt(def, substs) => { | |
1001 | // Multi-varaint enums are captured in entirety, | |
cdc7bbd5 | 1002 | // which would've been handled in the case of single empty slice in `captured_by_move_projs`. |
6a06907d XL |
1003 | assert_eq!(def.variants.len(), 1); |
1004 | ||
1005 | // Only Field projections can be applied to a non-box Adt. | |
1006 | assert!( | |
cdc7bbd5 | 1007 | captured_by_move_projs.iter().all(|projs| matches!( |
6a06907d XL |
1008 | projs.first().unwrap().kind, |
1009 | ProjectionKind::Field(..) | |
1010 | )) | |
1011 | ); | |
1012 | def.variants.get(VariantIdx::new(0)).unwrap().fields.iter().enumerate().any( | |
1013 | |(i, field)| { | |
cdc7bbd5 | 1014 | let paths_using_field = captured_by_move_projs |
6a06907d XL |
1015 | .iter() |
1016 | .filter_map(|projs| { | |
1017 | if let ProjectionKind::Field(field_idx, _) = | |
1018 | projs.first().unwrap().kind | |
1019 | { | |
1020 | if (field_idx as usize) == i { Some(&projs[1..]) } else { None } | |
1021 | } else { | |
1022 | unreachable!(); | |
1023 | } | |
1024 | }) | |
1025 | .collect(); | |
1026 | ||
1027 | let after_field_ty = field.ty(self.tcx, substs); | |
1028 | self.has_significant_drop_outside_of_captures( | |
1029 | closure_def_id, | |
1030 | closure_span, | |
1031 | after_field_ty, | |
1032 | paths_using_field, | |
1033 | ) | |
1034 | }, | |
1035 | ) | |
1036 | } | |
1037 | ||
1038 | ty::Tuple(..) => { | |
1039 | // Only Field projections can be applied to a tuple. | |
1040 | assert!( | |
cdc7bbd5 | 1041 | captured_by_move_projs.iter().all(|projs| matches!( |
6a06907d XL |
1042 | projs.first().unwrap().kind, |
1043 | ProjectionKind::Field(..) | |
1044 | )) | |
1045 | ); | |
1046 | ||
1047 | base_path_ty.tuple_fields().enumerate().any(|(i, element_ty)| { | |
cdc7bbd5 | 1048 | let paths_using_field = captured_by_move_projs |
6a06907d XL |
1049 | .iter() |
1050 | .filter_map(|projs| { | |
1051 | if let ProjectionKind::Field(field_idx, _) = projs.first().unwrap().kind | |
1052 | { | |
1053 | if (field_idx as usize) == i { Some(&projs[1..]) } else { None } | |
1054 | } else { | |
1055 | unreachable!(); | |
1056 | } | |
1057 | }) | |
1058 | .collect(); | |
1059 | ||
1060 | self.has_significant_drop_outside_of_captures( | |
1061 | closure_def_id, | |
1062 | closure_span, | |
1063 | element_ty, | |
1064 | paths_using_field, | |
1065 | ) | |
1066 | }) | |
1067 | } | |
1068 | ||
1069 | // Anything else would be completely captured and therefore handled already. | |
1070 | _ => unreachable!(), | |
1071 | } | |
1072 | } | |
1073 | ||
1074 | fn init_capture_kind_for_place( | |
fc512014 | 1075 | &self, |
6a06907d | 1076 | place: &Place<'tcx>, |
fc512014 XL |
1077 | capture_clause: hir::CaptureBy, |
1078 | upvar_id: ty::UpvarId, | |
1079 | closure_span: Span, | |
1080 | ) -> ty::UpvarCapture<'tcx> { | |
1081 | match capture_clause { | |
6a06907d XL |
1082 | // In case of a move closure if the data is accessed through a reference we |
1083 | // want to capture by ref to allow precise capture using reborrows. | |
1084 | // | |
1085 | // If the data will be moved out of this place, then the place will be truncated | |
1086 | // at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into | |
1087 | // the closure. | |
1088 | hir::CaptureBy::Value if !place.deref_tys().any(ty::TyS::is_ref) => { | |
1089 | ty::UpvarCapture::ByValue(None) | |
1090 | } | |
1091 | hir::CaptureBy::Value | hir::CaptureBy::Ref => { | |
fc512014 XL |
1092 | let origin = UpvarRegion(upvar_id, closure_span); |
1093 | let upvar_region = self.next_region_var(origin); | |
1094 | let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region }; | |
1095 | ty::UpvarCapture::ByRef(upvar_borrow) | |
1096 | } | |
1097 | } | |
1098 | } | |
1099 | ||
1100 | fn place_for_root_variable( | |
1101 | &self, | |
1102 | closure_def_id: LocalDefId, | |
1103 | var_hir_id: hir::HirId, | |
1104 | ) -> Place<'tcx> { | |
1105 | let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id); | |
1106 | ||
1107 | Place { | |
1108 | base_ty: self.node_ty(var_hir_id), | |
1109 | base: PlaceBase::Upvar(upvar_id), | |
1110 | projections: Default::default(), | |
1111 | } | |
1112 | } | |
1113 | ||
1114 | fn should_log_capture_analysis(&self, closure_def_id: DefId) -> bool { | |
1115 | self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) | |
1116 | } | |
1117 | ||
1118 | fn log_capture_analysis_first_pass( | |
1119 | &self, | |
1120 | closure_def_id: rustc_hir::def_id::DefId, | |
1121 | capture_information: &FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>, | |
1122 | closure_span: Span, | |
1123 | ) { | |
1124 | if self.should_log_capture_analysis(closure_def_id) { | |
1125 | let mut diag = | |
1126 | self.tcx.sess.struct_span_err(closure_span, "First Pass analysis includes:"); | |
1127 | for (place, capture_info) in capture_information { | |
1128 | let capture_str = construct_capture_info_string(self.tcx, place, capture_info); | |
1129 | let output_str = format!("Capturing {}", capture_str); | |
1130 | ||
5869c6ff XL |
1131 | let span = |
1132 | capture_info.path_expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); | |
fc512014 XL |
1133 | diag.span_note(span, &output_str); |
1134 | } | |
1135 | diag.emit(); | |
1136 | } | |
1137 | } | |
1138 | ||
1139 | fn log_closure_min_capture_info(&self, closure_def_id: DefId, closure_span: Span) { | |
1140 | if self.should_log_capture_analysis(closure_def_id) { | |
1141 | if let Some(min_captures) = | |
1142 | self.typeck_results.borrow().closure_min_captures.get(&closure_def_id) | |
1143 | { | |
1144 | let mut diag = | |
1145 | self.tcx.sess.struct_span_err(closure_span, "Min Capture analysis includes:"); | |
1146 | ||
1147 | for (_, min_captures_for_var) in min_captures { | |
1148 | for capture in min_captures_for_var { | |
1149 | let place = &capture.place; | |
1150 | let capture_info = &capture.info; | |
1151 | ||
1152 | let capture_str = | |
1153 | construct_capture_info_string(self.tcx, place, capture_info); | |
1154 | let output_str = format!("Min Capture {}", capture_str); | |
1155 | ||
5869c6ff XL |
1156 | if capture.info.path_expr_id != capture.info.capture_kind_expr_id { |
1157 | let path_span = capture_info | |
1158 | .path_expr_id | |
1159 | .map_or(closure_span, |e| self.tcx.hir().span(e)); | |
1160 | let capture_kind_span = capture_info | |
1161 | .capture_kind_expr_id | |
1162 | .map_or(closure_span, |e| self.tcx.hir().span(e)); | |
1163 | ||
1164 | let mut multi_span: MultiSpan = | |
1165 | MultiSpan::from_spans(vec![path_span, capture_kind_span]); | |
1166 | ||
1167 | let capture_kind_label = | |
1168 | construct_capture_kind_reason_string(self.tcx, place, capture_info); | |
1169 | let path_label = construct_path_string(self.tcx, place); | |
1170 | ||
1171 | multi_span.push_span_label(path_span, path_label); | |
1172 | multi_span.push_span_label(capture_kind_span, capture_kind_label); | |
1173 | ||
1174 | diag.span_note(multi_span, &output_str); | |
1175 | } else { | |
1176 | let span = capture_info | |
1177 | .path_expr_id | |
1178 | .map_or(closure_span, |e| self.tcx.hir().span(e)); | |
1179 | ||
1180 | diag.span_note(span, &output_str); | |
1181 | }; | |
fc512014 XL |
1182 | } |
1183 | } | |
1184 | diag.emit(); | |
1185 | } | |
1186 | } | |
1187 | } | |
5869c6ff XL |
1188 | |
1189 | /// A captured place is mutable if | |
1190 | /// 1. Projections don't include a Deref of an immut-borrow, **and** | |
1191 | /// 2. PlaceBase is mut or projections include a Deref of a mut-borrow. | |
1192 | fn determine_capture_mutability( | |
1193 | &self, | |
1194 | typeck_results: &'a TypeckResults<'tcx>, | |
1195 | place: &Place<'tcx>, | |
1196 | ) -> hir::Mutability { | |
1197 | let var_hir_id = match place.base { | |
1198 | PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, | |
1199 | _ => unreachable!(), | |
1200 | }; | |
1201 | ||
1202 | let bm = *typeck_results.pat_binding_modes().get(var_hir_id).expect("missing binding mode"); | |
1203 | ||
1204 | let mut is_mutbl = match bm { | |
1205 | ty::BindByValue(mutability) => mutability, | |
1206 | ty::BindByReference(_) => hir::Mutability::Not, | |
1207 | }; | |
1208 | ||
1209 | for pointer_ty in place.deref_tys() { | |
1210 | match pointer_ty.kind() { | |
1211 | // We don't capture derefs of raw ptrs | |
1212 | ty::RawPtr(_) => unreachable!(), | |
1213 | ||
1214 | // Derefencing a mut-ref allows us to mut the Place if we don't deref | |
1215 | // an immut-ref after on top of this. | |
1216 | ty::Ref(.., hir::Mutability::Mut) => is_mutbl = hir::Mutability::Mut, | |
1217 | ||
1218 | // The place isn't mutable once we dereference a immutable reference. | |
1219 | ty::Ref(.., hir::Mutability::Not) => return hir::Mutability::Not, | |
1220 | ||
1221 | // Dereferencing a box doesn't change mutability | |
1222 | ty::Adt(def, ..) if def.is_box() => {} | |
1223 | ||
1224 | unexpected_ty => bug!("deref of unexpected pointer type {:?}", unexpected_ty), | |
1225 | } | |
1226 | } | |
1227 | ||
1228 | is_mutbl | |
1229 | } | |
041b39d2 | 1230 | } |
c1a9b12d | 1231 | |
6a06907d XL |
1232 | /// Truncate the capture so that the place being borrowed is in accordance with RFC 1240, |
1233 | /// which states that it's unsafe to take a reference into a struct marked `repr(packed)`. | |
1234 | fn restrict_repr_packed_field_ref_capture<'tcx>( | |
1235 | tcx: TyCtxt<'tcx>, | |
1236 | param_env: ty::ParamEnv<'tcx>, | |
1237 | place: &Place<'tcx>, | |
1238 | ) -> Place<'tcx> { | |
1239 | let pos = place.projections.iter().enumerate().position(|(i, p)| { | |
1240 | let ty = place.ty_before_projection(i); | |
1241 | ||
1242 | // Return true for fields of packed structs, unless those fields have alignment 1. | |
1243 | match p.kind { | |
1244 | ProjectionKind::Field(..) => match ty.kind() { | |
1245 | ty::Adt(def, _) if def.repr.packed() => { | |
1246 | match tcx.layout_raw(param_env.and(p.ty)) { | |
1247 | Ok(layout) if layout.align.abi.bytes() == 1 => { | |
1248 | // if the alignment is 1, the type can't be further | |
1249 | // disaligned. | |
1250 | debug!( | |
1251 | "restrict_repr_packed_field_ref_capture: ({:?}) - align = 1", | |
1252 | place | |
1253 | ); | |
1254 | false | |
1255 | } | |
1256 | _ => { | |
1257 | debug!("restrict_repr_packed_field_ref_capture: ({:?}) - true", place); | |
1258 | true | |
1259 | } | |
1260 | } | |
1261 | } | |
1262 | ||
1263 | _ => false, | |
1264 | }, | |
1265 | _ => false, | |
1266 | } | |
1267 | }); | |
1268 | ||
1269 | let mut place = place.clone(); | |
1270 | ||
1271 | if let Some(pos) = pos { | |
1272 | place.projections.truncate(pos); | |
1273 | } | |
1274 | ||
1275 | place | |
1276 | } | |
1277 | ||
dc9dc135 XL |
1278 | struct InferBorrowKind<'a, 'tcx> { |
1279 | fcx: &'a FnCtxt<'a, 'tcx>, | |
ff7c6d11 XL |
1280 | |
1281 | // The def-id of the closure whose kind and upvar accesses are being inferred. | |
1282 | closure_def_id: DefId, | |
1283 | ||
fc512014 XL |
1284 | closure_span: Span, |
1285 | ||
1286 | capture_clause: hir::CaptureBy, | |
1287 | ||
ff7c6d11 XL |
1288 | // The kind that we have inferred that the current closure |
1289 | // requires. Note that we *always* infer a minimal kind, even if | |
1290 | // we don't always *use* that in the final result (i.e., sometimes | |
1291 | // we've taken the closure kind from the expectations instead, and | |
1292 | // for generators we don't even implement the closure traits | |
1293 | // really). | |
1294 | current_closure_kind: ty::ClosureKind, | |
1295 | ||
1296 | // If we modified `current_closure_kind`, this field contains a `Some()` with the | |
1297 | // variable access that caused us to do so. | |
5869c6ff | 1298 | current_origin: Option<(Span, Place<'tcx>)>, |
ff7c6d11 | 1299 | |
fc512014 XL |
1300 | /// For each Place that is captured by the closure, we track the minimal kind of |
1301 | /// access we need (ref, ref mut, move, etc) and the expression that resulted in such access. | |
1302 | /// | |
1303 | /// Consider closure where s.str1 is captured via an ImmutableBorrow and | |
1304 | /// s.str2 via a MutableBorrow | |
1305 | /// | |
1306 | /// ```rust,no_run | |
1307 | /// struct SomeStruct { str1: String, str2: String } | |
1308 | /// | |
1309 | /// // Assume that the HirId for the variable definition is `V1` | |
1310 | /// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") } | |
1311 | /// | |
1312 | /// let fix_s = |new_s2| { | |
1313 | /// // Assume that the HirId for the expression `s.str1` is `E1` | |
1314 | /// println!("Updating SomeStruct with str1=", s.str1); | |
1315 | /// // Assume that the HirId for the expression `*s.str2` is `E2` | |
1316 | /// s.str2 = new_s2; | |
1317 | /// }; | |
1318 | /// ``` | |
1319 | /// | |
1320 | /// For closure `fix_s`, (at a high level) the map contains | |
1321 | /// | |
5869c6ff | 1322 | /// ``` |
fc512014 XL |
1323 | /// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow } |
1324 | /// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow } | |
5869c6ff XL |
1325 | /// ``` |
1326 | capture_information: InferredCaptureInformation<'tcx>, | |
6a06907d | 1327 | fake_reads: Vec<(Place<'tcx>, FakeReadCause, hir::HirId)>, |
041b39d2 XL |
1328 | } |
1329 | ||
dc9dc135 | 1330 | impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { |
a1dfa0c6 XL |
1331 | fn adjust_upvar_borrow_kind_for_consume( |
1332 | &mut self, | |
3dfed10e | 1333 | place_with_id: &PlaceWithHirId<'tcx>, |
29967ef6 | 1334 | diag_expr_id: hir::HirId, |
a1dfa0c6 XL |
1335 | mode: euv::ConsumeMode, |
1336 | ) { | |
f035d41b | 1337 | debug!( |
29967ef6 XL |
1338 | "adjust_upvar_borrow_kind_for_consume(place_with_id={:?}, diag_expr_id={:?}, mode={:?})", |
1339 | place_with_id, diag_expr_id, mode | |
f035d41b | 1340 | ); |
85aaf69f | 1341 | |
6a06907d XL |
1342 | match (self.capture_clause, mode) { |
1343 | // In non-move closures, we only care about moves | |
1344 | (hir::CaptureBy::Ref, euv::Copy) => return, | |
1345 | ||
1346 | // We want to capture Copy types that read through a ref via a reborrow | |
1347 | (hir::CaptureBy::Value, euv::Copy) | |
1348 | if place_with_id.place.deref_tys().any(ty::TyS::is_ref) => | |
1349 | { | |
ff7c6d11 XL |
1350 | return; |
1351 | } | |
6a06907d XL |
1352 | |
1353 | (hir::CaptureBy::Ref, euv::Move) | (hir::CaptureBy::Value, euv::Move | euv::Copy) => {} | |
1354 | }; | |
1355 | ||
1356 | let place = truncate_capture_for_move(place_with_id.place.clone()); | |
1357 | let place_with_id = PlaceWithHirId { place: place.clone(), hir_id: place_with_id.hir_id }; | |
1358 | ||
1359 | if !self.capture_information.contains_key(&place) { | |
1360 | self.init_capture_info_for_place(&place_with_id, diag_expr_id); | |
85aaf69f SL |
1361 | } |
1362 | ||
7cac9316 | 1363 | let tcx = self.fcx.tcx; |
f035d41b | 1364 | let upvar_id = if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { |
60c5eb7d XL |
1365 | upvar_id |
1366 | } else { | |
1367 | return; | |
1368 | }; | |
7cac9316 | 1369 | |
60c5eb7d | 1370 | debug!("adjust_upvar_borrow_kind_for_consume: upvar={:?}", upvar_id); |
0bf4aa26 | 1371 | |
29967ef6 | 1372 | let usage_span = tcx.hir().span(diag_expr_id); |
1b1a35ee | 1373 | |
6a06907d XL |
1374 | if matches!(mode, euv::Move) { |
1375 | // To move out of an upvar, this must be a FnOnce closure | |
1376 | self.adjust_closure_kind( | |
1377 | upvar_id.closure_expr_id, | |
1378 | ty::ClosureKind::FnOnce, | |
1379 | usage_span, | |
1380 | place.clone(), | |
1381 | ); | |
1382 | } | |
0bf4aa26 | 1383 | |
fc512014 | 1384 | let capture_info = ty::CaptureInfo { |
5869c6ff XL |
1385 | capture_kind_expr_id: Some(diag_expr_id), |
1386 | path_expr_id: Some(diag_expr_id), | |
fc512014 XL |
1387 | capture_kind: ty::UpvarCapture::ByValue(Some(usage_span)), |
1388 | }; | |
1389 | ||
1390 | let curr_info = self.capture_information[&place_with_id.place]; | |
1391 | let updated_info = determine_capture_info(curr_info, capture_info); | |
1392 | ||
1393 | self.capture_information[&place_with_id.place] = updated_info; | |
1a4d82fc JJ |
1394 | } |
1395 | ||
f035d41b | 1396 | /// Indicates that `place_with_id` is being directly mutated (e.g., assigned |
60c5eb7d XL |
1397 | /// to). If the place is based on a by-ref upvar, this implies that |
1398 | /// the upvar must be borrowed using an `&mut` borrow. | |
29967ef6 XL |
1399 | fn adjust_upvar_borrow_kind_for_mut( |
1400 | &mut self, | |
1401 | place_with_id: &PlaceWithHirId<'tcx>, | |
1402 | diag_expr_id: hir::HirId, | |
1403 | ) { | |
1404 | debug!( | |
1405 | "adjust_upvar_borrow_kind_for_mut(place_with_id={:?}, diag_expr_id={:?})", | |
1406 | place_with_id, diag_expr_id | |
1407 | ); | |
60c5eb7d | 1408 | |
fc512014 | 1409 | if let PlaceBase::Upvar(_) = place_with_id.place.base { |
60c5eb7d | 1410 | let mut borrow_kind = ty::MutBorrow; |
f035d41b | 1411 | for pointer_ty in place_with_id.place.deref_tys() { |
1b1a35ee | 1412 | match pointer_ty.kind() { |
60c5eb7d XL |
1413 | // Raw pointers don't inherit mutability. |
1414 | ty::RawPtr(_) => return, | |
1a4d82fc JJ |
1415 | // assignment to deref of an `&mut` |
1416 | // borrowed pointer implies that the | |
1417 | // pointer itself must be unique, but not | |
1418 | // necessarily *mutable* | |
dfeec247 | 1419 | ty::Ref(.., hir::Mutability::Mut) => borrow_kind = ty::UniqueImmBorrow, |
60c5eb7d | 1420 | _ => (), |
1a4d82fc JJ |
1421 | } |
1422 | } | |
fc512014 | 1423 | self.adjust_upvar_deref(place_with_id, diag_expr_id, borrow_kind); |
1a4d82fc JJ |
1424 | } |
1425 | } | |
1426 | ||
29967ef6 XL |
1427 | fn adjust_upvar_borrow_kind_for_unique( |
1428 | &mut self, | |
1429 | place_with_id: &PlaceWithHirId<'tcx>, | |
1430 | diag_expr_id: hir::HirId, | |
1431 | ) { | |
1432 | debug!( | |
1433 | "adjust_upvar_borrow_kind_for_unique(place_with_id={:?}, diag_expr_id={:?})", | |
1434 | place_with_id, diag_expr_id | |
1435 | ); | |
1a4d82fc | 1436 | |
fc512014 | 1437 | if let PlaceBase::Upvar(_) = place_with_id.place.base { |
f035d41b | 1438 | if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) { |
60c5eb7d XL |
1439 | // Raw pointers don't inherit mutability. |
1440 | return; | |
1a4d82fc | 1441 | } |
60c5eb7d | 1442 | // for a borrowed pointer to be unique, its base must be unique |
fc512014 | 1443 | self.adjust_upvar_deref(place_with_id, diag_expr_id, ty::UniqueImmBorrow); |
1a4d82fc JJ |
1444 | } |
1445 | } | |
1446 | ||
60c5eb7d | 1447 | fn adjust_upvar_deref( |
a1dfa0c6 | 1448 | &mut self, |
fc512014 XL |
1449 | place_with_id: &PlaceWithHirId<'tcx>, |
1450 | diag_expr_id: hir::HirId, | |
a1dfa0c6 | 1451 | borrow_kind: ty::BorrowKind, |
60c5eb7d | 1452 | ) { |
85aaf69f SL |
1453 | assert!(match borrow_kind { |
1454 | ty::MutBorrow => true, | |
1455 | ty::UniqueImmBorrow => true, | |
1456 | ||
1457 | // imm borrows never require adjusting any kinds, so we don't wind up here | |
1458 | ty::ImmBorrow => false, | |
1459 | }); | |
1460 | ||
7cac9316 XL |
1461 | let tcx = self.fcx.tcx; |
1462 | ||
60c5eb7d XL |
1463 | // if this is an implicit deref of an |
1464 | // upvar, then we need to modify the | |
1465 | // borrow_kind of the upvar to make sure it | |
1466 | // is inferred to mutable if necessary | |
fc512014 | 1467 | self.adjust_upvar_borrow_kind(place_with_id, diag_expr_id, borrow_kind); |
60c5eb7d | 1468 | |
fc512014 XL |
1469 | if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { |
1470 | self.adjust_closure_kind( | |
1471 | upvar_id.closure_expr_id, | |
1472 | ty::ClosureKind::FnMut, | |
1473 | tcx.hir().span(diag_expr_id), | |
5869c6ff | 1474 | place_with_id.place.clone(), |
fc512014 XL |
1475 | ); |
1476 | } | |
85aaf69f SL |
1477 | } |
1478 | ||
a7813a04 XL |
1479 | /// We infer the borrow_kind with which to borrow upvars in a stack closure. |
1480 | /// The borrow_kind basically follows a lattice of `imm < unique-imm < mut`, | |
1481 | /// moving from left to right as needed (but never right to left). | |
1482 | /// Here the argument `mutbl` is the borrow_kind that is required by | |
1a4d82fc | 1483 | /// some particular use. |
fc512014 XL |
1484 | fn adjust_upvar_borrow_kind( |
1485 | &mut self, | |
1486 | place_with_id: &PlaceWithHirId<'tcx>, | |
1487 | diag_expr_id: hir::HirId, | |
1488 | kind: ty::BorrowKind, | |
1489 | ) { | |
1490 | let curr_capture_info = self.capture_information[&place_with_id.place]; | |
1491 | ||
ff7c6d11 | 1492 | debug!( |
fc512014 XL |
1493 | "adjust_upvar_borrow_kind(place={:?}, diag_expr_id={:?}, capture_info={:?}, kind={:?})", |
1494 | place_with_id, diag_expr_id, curr_capture_info, kind | |
ff7c6d11 | 1495 | ); |
85aaf69f | 1496 | |
fc512014 XL |
1497 | if let ty::UpvarCapture::ByValue(_) = curr_capture_info.capture_kind { |
1498 | // It's already captured by value, we don't need to do anything here | |
1499 | return; | |
1500 | } else if let ty::UpvarCapture::ByRef(curr_upvar_borrow) = curr_capture_info.capture_kind { | |
1501 | // Use the same region as the current capture information | |
1502 | // Doesn't matter since only one of the UpvarBorrow will be used. | |
1503 | let new_upvar_borrow = ty::UpvarBorrow { kind, region: curr_upvar_borrow.region }; | |
1504 | ||
1505 | let capture_info = ty::CaptureInfo { | |
5869c6ff XL |
1506 | capture_kind_expr_id: Some(diag_expr_id), |
1507 | path_expr_id: Some(diag_expr_id), | |
fc512014 XL |
1508 | capture_kind: ty::UpvarCapture::ByRef(new_upvar_borrow), |
1509 | }; | |
1510 | let updated_info = determine_capture_info(curr_capture_info, capture_info); | |
1511 | self.capture_information[&place_with_id.place] = updated_info; | |
1512 | }; | |
85aaf69f SL |
1513 | } |
1514 | ||
ff7c6d11 XL |
1515 | fn adjust_closure_kind( |
1516 | &mut self, | |
1517 | closure_id: LocalDefId, | |
1518 | new_kind: ty::ClosureKind, | |
1519 | upvar_span: Span, | |
5869c6ff | 1520 | place: Place<'tcx>, |
ff7c6d11 XL |
1521 | ) { |
1522 | debug!( | |
5869c6ff XL |
1523 | "adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, place={:?})", |
1524 | closure_id, new_kind, upvar_span, place | |
ff7c6d11 XL |
1525 | ); |
1526 | ||
1527 | // Is this the closure whose kind is currently being inferred? | |
1528 | if closure_id.to_def_id() != self.closure_def_id { | |
1529 | debug!("adjust_closure_kind: not current closure"); | |
1530 | return; | |
1531 | } | |
85aaf69f | 1532 | |
ff7c6d11 XL |
1533 | // closures start out as `Fn`. |
1534 | let existing_kind = self.current_closure_kind; | |
1535 | ||
1536 | debug!( | |
1537 | "adjust_closure_kind: closure_id={:?}, existing_kind={:?}, new_kind={:?}", | |
a1dfa0c6 | 1538 | closure_id, existing_kind, new_kind |
ff7c6d11 XL |
1539 | ); |
1540 | ||
1541 | match (existing_kind, new_kind) { | |
a1dfa0c6 | 1542 | (ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
ba9703b0 | 1543 | | (ty::ClosureKind::FnMut, ty::ClosureKind::Fn | ty::ClosureKind::FnMut) |
a1dfa0c6 | 1544 | | (ty::ClosureKind::FnOnce, _) => { |
ff7c6d11 XL |
1545 | // no change needed |
1546 | } | |
1547 | ||
ba9703b0 | 1548 | (ty::ClosureKind::Fn, ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce) |
a1dfa0c6 | 1549 | | (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { |
ff7c6d11 XL |
1550 | // new kind is stronger than the old kind |
1551 | self.current_closure_kind = new_kind; | |
5869c6ff | 1552 | self.current_origin = Some((upvar_span, place)); |
1a4d82fc JJ |
1553 | } |
1554 | } | |
1555 | } | |
fc512014 XL |
1556 | |
1557 | fn init_capture_info_for_place( | |
1558 | &mut self, | |
1559 | place_with_id: &PlaceWithHirId<'tcx>, | |
1560 | diag_expr_id: hir::HirId, | |
1561 | ) { | |
1562 | if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { | |
1563 | assert_eq!(self.closure_def_id.expect_local(), upvar_id.closure_expr_id); | |
1564 | ||
6a06907d XL |
1565 | let capture_kind = self.fcx.init_capture_kind_for_place( |
1566 | &place_with_id.place, | |
1567 | self.capture_clause, | |
1568 | upvar_id, | |
1569 | self.closure_span, | |
1570 | ); | |
fc512014 XL |
1571 | |
1572 | let expr_id = Some(diag_expr_id); | |
5869c6ff XL |
1573 | let capture_info = ty::CaptureInfo { |
1574 | capture_kind_expr_id: expr_id, | |
1575 | path_expr_id: expr_id, | |
1576 | capture_kind, | |
1577 | }; | |
fc512014 XL |
1578 | |
1579 | debug!("Capturing new place {:?}, capture_info={:?}", place_with_id, capture_info); | |
1580 | ||
1581 | self.capture_information.insert(place_with_id.place.clone(), capture_info); | |
1582 | } else { | |
1583 | debug!("Not upvar: {:?}", place_with_id); | |
1584 | } | |
1585 | } | |
1a4d82fc JJ |
1586 | } |
1587 | ||
dc9dc135 | 1588 | impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { |
6a06907d XL |
1589 | fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id: hir::HirId) { |
1590 | if let PlaceBase::Upvar(_) = place.base { | |
17df50a5 XL |
1591 | // We need to restrict Fake Read precision to avoid fake reading unsafe code, |
1592 | // such as deref of a raw pointer. | |
1593 | let place = restrict_capture_precision(place); | |
1594 | let place = | |
1595 | restrict_repr_packed_field_ref_capture(self.fcx.tcx, self.fcx.param_env, &place); | |
6a06907d XL |
1596 | self.fake_reads.push((place, cause, diag_expr_id)); |
1597 | } | |
1598 | } | |
1599 | ||
29967ef6 XL |
1600 | fn consume( |
1601 | &mut self, | |
1602 | place_with_id: &PlaceWithHirId<'tcx>, | |
1603 | diag_expr_id: hir::HirId, | |
1604 | mode: euv::ConsumeMode, | |
1605 | ) { | |
1606 | debug!( | |
1607 | "consume(place_with_id={:?}, diag_expr_id={:?}, mode={:?})", | |
1608 | place_with_id, diag_expr_id, mode | |
1609 | ); | |
fc512014 XL |
1610 | if !self.capture_information.contains_key(&place_with_id.place) { |
1611 | self.init_capture_info_for_place(place_with_id, diag_expr_id); | |
1612 | } | |
1613 | ||
1614 | self.adjust_upvar_borrow_kind_for_consume(place_with_id, diag_expr_id, mode); | |
85aaf69f | 1615 | } |
1a4d82fc | 1616 | |
29967ef6 XL |
1617 | fn borrow( |
1618 | &mut self, | |
1619 | place_with_id: &PlaceWithHirId<'tcx>, | |
1620 | diag_expr_id: hir::HirId, | |
1621 | bk: ty::BorrowKind, | |
1622 | ) { | |
1623 | debug!( | |
1624 | "borrow(place_with_id={:?}, diag_expr_id={:?}, bk={:?})", | |
1625 | place_with_id, diag_expr_id, bk | |
1626 | ); | |
1a4d82fc | 1627 | |
6a06907d XL |
1628 | let place = restrict_repr_packed_field_ref_capture( |
1629 | self.fcx.tcx, | |
1630 | self.fcx.param_env, | |
1631 | &place_with_id.place, | |
1632 | ); | |
1633 | let place_with_id = PlaceWithHirId { place, ..*place_with_id }; | |
1634 | ||
fc512014 | 1635 | if !self.capture_information.contains_key(&place_with_id.place) { |
6a06907d | 1636 | self.init_capture_info_for_place(&place_with_id, diag_expr_id); |
fc512014 XL |
1637 | } |
1638 | ||
1a4d82fc | 1639 | match bk { |
ff7c6d11 | 1640 | ty::ImmBorrow => {} |
1a4d82fc | 1641 | ty::UniqueImmBorrow => { |
29967ef6 | 1642 | self.adjust_upvar_borrow_kind_for_unique(&place_with_id, diag_expr_id); |
1a4d82fc JJ |
1643 | } |
1644 | ty::MutBorrow => { | |
29967ef6 | 1645 | self.adjust_upvar_borrow_kind_for_mut(&place_with_id, diag_expr_id); |
1a4d82fc JJ |
1646 | } |
1647 | } | |
1648 | } | |
1649 | ||
29967ef6 XL |
1650 | fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { |
1651 | debug!("mutate(assignee_place={:?}, diag_expr_id={:?})", assignee_place, diag_expr_id); | |
fc512014 | 1652 | |
6a06907d | 1653 | self.borrow(assignee_place, diag_expr_id, ty::BorrowKind::MutBorrow); |
1a4d82fc JJ |
1654 | } |
1655 | } | |
3b2f2976 | 1656 | |
5869c6ff | 1657 | /// Truncate projections so that following rules are obeyed by the captured `place`: |
5869c6ff XL |
1658 | /// - No projections are applied to raw pointers, since these require unsafe blocks. We capture |
1659 | /// them completely. | |
1660 | /// - No Index projections are captured, since arrays are captured completely. | |
6a06907d | 1661 | fn restrict_capture_precision<'tcx>(mut place: Place<'tcx>) -> Place<'tcx> { |
5869c6ff XL |
1662 | if place.projections.is_empty() { |
1663 | // Nothing to do here | |
1664 | return place; | |
1665 | } | |
1666 | ||
1667 | if place.base_ty.is_unsafe_ptr() { | |
1668 | place.projections.truncate(0); | |
1669 | return place; | |
1670 | } | |
1671 | ||
1672 | let mut truncated_length = usize::MAX; | |
5869c6ff XL |
1673 | |
1674 | for (i, proj) in place.projections.iter().enumerate() { | |
1675 | if proj.ty.is_unsafe_ptr() { | |
1676 | // Don't apply any projections on top of an unsafe ptr | |
1677 | truncated_length = truncated_length.min(i + 1); | |
1678 | break; | |
1679 | } | |
1680 | match proj.kind { | |
1681 | ProjectionKind::Index => { | |
1682 | // Arrays are completely captured, so we drop Index projections | |
1683 | truncated_length = truncated_length.min(i); | |
1684 | break; | |
1685 | } | |
6a06907d | 1686 | ProjectionKind::Deref => {} |
5869c6ff XL |
1687 | ProjectionKind::Field(..) => {} // ignore |
1688 | ProjectionKind::Subslice => {} // We never capture this | |
1689 | } | |
1690 | } | |
1691 | ||
6a06907d | 1692 | let length = place.projections.len().min(truncated_length); |
5869c6ff XL |
1693 | |
1694 | place.projections.truncate(length); | |
1695 | ||
1696 | place | |
1697 | } | |
1698 | ||
6a06907d XL |
1699 | /// Truncates a place so that the resultant capture doesn't move data out of a reference |
1700 | fn truncate_capture_for_move(mut place: Place<'tcx>) -> Place<'tcx> { | |
1701 | if let Some(i) = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref) { | |
1702 | // We only drop Derefs in case of move closures | |
1703 | // There might be an index projection or raw ptr ahead, so we don't stop here. | |
1704 | place.projections.truncate(i); | |
1705 | } | |
1706 | ||
1707 | place | |
1708 | } | |
1709 | ||
5869c6ff | 1710 | fn construct_place_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String { |
fc512014 XL |
1711 | let variable_name = match place.base { |
1712 | PlaceBase::Upvar(upvar_id) => var_name(tcx, upvar_id.var_path.hir_id).to_string(), | |
1713 | _ => bug!("Capture_information should only contain upvars"), | |
1714 | }; | |
1715 | ||
1716 | let mut projections_str = String::new(); | |
1717 | for (i, item) in place.projections.iter().enumerate() { | |
1718 | let proj = match item.kind { | |
1719 | ProjectionKind::Field(a, b) => format!("({:?}, {:?})", a, b), | |
1720 | ProjectionKind::Deref => String::from("Deref"), | |
1721 | ProjectionKind::Index => String::from("Index"), | |
1722 | ProjectionKind::Subslice => String::from("Subslice"), | |
1723 | }; | |
1724 | if i != 0 { | |
6a06907d | 1725 | projections_str.push(','); |
fc512014 XL |
1726 | } |
1727 | projections_str.push_str(proj.as_str()); | |
1728 | } | |
1729 | ||
5869c6ff XL |
1730 | format!("{}[{}]", variable_name, projections_str) |
1731 | } | |
1732 | ||
1733 | fn construct_capture_kind_reason_string( | |
1734 | tcx: TyCtxt<'_>, | |
1735 | place: &Place<'tcx>, | |
1736 | capture_info: &ty::CaptureInfo<'tcx>, | |
1737 | ) -> String { | |
1738 | let place_str = construct_place_string(tcx, &place); | |
1739 | ||
fc512014 XL |
1740 | let capture_kind_str = match capture_info.capture_kind { |
1741 | ty::UpvarCapture::ByValue(_) => "ByValue".into(), | |
1742 | ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind), | |
1743 | }; | |
5869c6ff XL |
1744 | |
1745 | format!("{} captured as {} here", place_str, capture_kind_str) | |
1746 | } | |
1747 | ||
1748 | fn construct_path_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String { | |
1749 | let place_str = construct_place_string(tcx, &place); | |
1750 | ||
1751 | format!("{} used here", place_str) | |
1752 | } | |
1753 | ||
1754 | fn construct_capture_info_string( | |
1755 | tcx: TyCtxt<'_>, | |
1756 | place: &Place<'tcx>, | |
1757 | capture_info: &ty::CaptureInfo<'tcx>, | |
1758 | ) -> String { | |
1759 | let place_str = construct_place_string(tcx, &place); | |
1760 | ||
1761 | let capture_kind_str = match capture_info.capture_kind { | |
1762 | ty::UpvarCapture::ByValue(_) => "ByValue".into(), | |
1763 | ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind), | |
1764 | }; | |
1765 | format!("{} -> {}", place_str, capture_kind_str) | |
fc512014 XL |
1766 | } |
1767 | ||
f9f354fc | 1768 | fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { |
dc9dc135 | 1769 | tcx.hir().name(var_hir_id) |
3b2f2976 | 1770 | } |
fc512014 | 1771 | |
17df50a5 XL |
1772 | fn should_do_disjoint_capture_migration_analysis(tcx: TyCtxt<'_>, closure_id: hir::HirId) -> bool { |
1773 | let (level, _) = tcx.lint_level_at_node(lint::builtin::DISJOINT_CAPTURE_MIGRATION, closure_id); | |
5869c6ff XL |
1774 | |
1775 | !matches!(level, lint::Level::Allow) | |
1776 | } | |
1777 | ||
cdc7bbd5 XL |
1778 | /// Return a two string tuple (s1, s2) |
1779 | /// - s1: Line of code that is needed for the migration: eg: `let _ = (&x, ...)`. | |
1780 | /// - s2: Comma separated names of the variables being migrated. | |
1781 | fn migration_suggestion_for_2229( | |
1782 | tcx: TyCtxt<'_>, | |
1783 | need_migrations: &Vec<hir::HirId>, | |
1784 | ) -> (String, String) { | |
1785 | let need_migrations_variables = | |
1786 | need_migrations.iter().map(|v| var_name(tcx, *v)).collect::<Vec<_>>(); | |
1787 | ||
1788 | let migration_ref_concat = | |
1789 | need_migrations_variables.iter().map(|v| format!("&{}", v)).collect::<Vec<_>>().join(", "); | |
1790 | ||
1791 | let migration_string = if 1 == need_migrations.len() { | |
1792 | format!("let _ = {}", migration_ref_concat) | |
1793 | } else { | |
1794 | format!("let _ = ({})", migration_ref_concat) | |
1795 | }; | |
1796 | ||
1797 | let migrated_variables_concat = | |
1798 | need_migrations_variables.iter().map(|v| format!("`{}`", v)).collect::<Vec<_>>().join(", "); | |
5869c6ff | 1799 | |
cdc7bbd5 | 1800 | (migration_string, migrated_variables_concat) |
5869c6ff XL |
1801 | } |
1802 | ||
fc512014 XL |
1803 | /// Helper function to determine if we need to escalate CaptureKind from |
1804 | /// CaptureInfo A to B and returns the escalated CaptureInfo. | |
1805 | /// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way) | |
1806 | /// | |
1807 | /// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based | |
5869c6ff XL |
1808 | /// on the `CaptureInfo` containing an associated `capture_kind_expr_id`. |
1809 | /// | |
1810 | /// It is the caller's duty to figure out which path_expr_id to use. | |
fc512014 XL |
1811 | /// |
1812 | /// If both the CaptureKind and Expression are considered to be equivalent, | |
1813 | /// then `CaptureInfo` A is preferred. This can be useful in cases where we want to priortize | |
1814 | /// expressions reported back to the user as part of diagnostics based on which appears earlier | |
cdc7bbd5 | 1815 | /// in the closure. This can be achieved simply by calling |
fc512014 XL |
1816 | /// `determine_capture_info(existing_info, current_info)`. This works out because the |
1817 | /// expressions that occur earlier in the closure body than the current expression are processed before. | |
1818 | /// Consider the following example | |
1819 | /// ```rust,no_run | |
1820 | /// struct Point { x: i32, y: i32 } | |
1821 | /// let mut p: Point { x: 10, y: 10 }; | |
1822 | /// | |
1823 | /// let c = || { | |
1824 | /// p.x += 10; | |
1825 | /// // ^ E1 ^ | |
1826 | /// // ... | |
1827 | /// // More code | |
1828 | /// // ... | |
1829 | /// p.x += 10; // E2 | |
1830 | /// // ^ E2 ^ | |
1831 | /// }; | |
1832 | /// ``` | |
1833 | /// `CaptureKind` associated with both `E1` and `E2` will be ByRef(MutBorrow), | |
1834 | /// and both have an expression associated, however for diagnostics we prefer reporting | |
1835 | /// `E1` since it appears earlier in the closure body. When `E2` is being processed we | |
1836 | /// would've already handled `E1`, and have an existing capture_information for it. | |
1837 | /// Calling `determine_capture_info(existing_info_e1, current_info_e2)` will return | |
1838 | /// `existing_info_e1` in this case, allowing us to point to `E1` in case of diagnostics. | |
1839 | fn determine_capture_info( | |
1840 | capture_info_a: ty::CaptureInfo<'tcx>, | |
1841 | capture_info_b: ty::CaptureInfo<'tcx>, | |
1842 | ) -> ty::CaptureInfo<'tcx> { | |
1843 | // If the capture kind is equivalent then, we don't need to escalate and can compare the | |
1844 | // expressions. | |
1845 | let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { | |
1846 | (ty::UpvarCapture::ByValue(_), ty::UpvarCapture::ByValue(_)) => { | |
1847 | // We don't need to worry about the spans being ignored here. | |
1848 | // | |
1849 | // The expr_id in capture_info corresponds to the span that is stored within | |
1850 | // ByValue(span) and therefore it gets handled with priortizing based on | |
1851 | // expressions below. | |
1852 | true | |
1853 | } | |
1854 | (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { | |
1855 | ref_a.kind == ref_b.kind | |
1856 | } | |
1857 | (ty::UpvarCapture::ByValue(_), _) | (ty::UpvarCapture::ByRef(_), _) => false, | |
1858 | }; | |
1859 | ||
1860 | if eq_capture_kind { | |
5869c6ff | 1861 | match (capture_info_a.capture_kind_expr_id, capture_info_b.capture_kind_expr_id) { |
fc512014 XL |
1862 | (Some(_), _) | (None, None) => capture_info_a, |
1863 | (None, Some(_)) => capture_info_b, | |
1864 | } | |
1865 | } else { | |
1866 | // We select the CaptureKind which ranks higher based the following priority order: | |
1867 | // ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow | |
1868 | match (capture_info_a.capture_kind, capture_info_b.capture_kind) { | |
1869 | (ty::UpvarCapture::ByValue(_), _) => capture_info_a, | |
1870 | (_, ty::UpvarCapture::ByValue(_)) => capture_info_b, | |
1871 | (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { | |
1872 | match (ref_a.kind, ref_b.kind) { | |
1873 | // Take LHS: | |
1874 | (ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow) | |
1875 | | (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a, | |
1876 | ||
1877 | // Take RHS: | |
1878 | (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) | |
1879 | | (ty::UniqueImmBorrow, ty::MutBorrow) => capture_info_b, | |
1880 | ||
1881 | (ty::ImmBorrow, ty::ImmBorrow) | |
1882 | | (ty::UniqueImmBorrow, ty::UniqueImmBorrow) | |
1883 | | (ty::MutBorrow, ty::MutBorrow) => { | |
1884 | bug!("Expected unequal capture kinds"); | |
1885 | } | |
1886 | } | |
1887 | } | |
1888 | } | |
1889 | } | |
1890 | } | |
1891 | ||
1892 | /// Determines the Ancestry relationship of Place A relative to Place B | |
1893 | /// | |
1894 | /// `PlaceAncestryRelation::Ancestor` implies Place A is ancestor of Place B | |
1895 | /// `PlaceAncestryRelation::Descendant` implies Place A is descendant of Place B | |
1896 | /// `PlaceAncestryRelation::Divergent` implies neither of them is the ancestor of the other. | |
1897 | fn determine_place_ancestry_relation( | |
1898 | place_a: &Place<'tcx>, | |
1899 | place_b: &Place<'tcx>, | |
1900 | ) -> PlaceAncestryRelation { | |
1901 | // If Place A and Place B, don't start off from the same root variable, they are divergent. | |
1902 | if place_a.base != place_b.base { | |
1903 | return PlaceAncestryRelation::Divergent; | |
1904 | } | |
1905 | ||
1906 | // Assume of length of projections_a = n | |
1907 | let projections_a = &place_a.projections; | |
1908 | ||
1909 | // Assume of length of projections_b = m | |
1910 | let projections_b = &place_b.projections; | |
1911 | ||
6a06907d | 1912 | let same_initial_projections = |
cdc7bbd5 | 1913 | iter::zip(projections_a, projections_b).all(|(proj_a, proj_b)| proj_a == proj_b); |
fc512014 XL |
1914 | |
1915 | if same_initial_projections { | |
1916 | // First min(n, m) projections are the same | |
1917 | // Select Ancestor/Descendant | |
1918 | if projections_b.len() >= projections_a.len() { | |
1919 | PlaceAncestryRelation::Ancestor | |
1920 | } else { | |
1921 | PlaceAncestryRelation::Descendant | |
1922 | } | |
1923 | } else { | |
1924 | PlaceAncestryRelation::Divergent | |
1925 | } | |
1926 | } |