]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_typeck/src/check/upvar.rs
New upstream version 1.54.0+dfsg1
[rustc.git] / compiler / rustc_typeck / src / check / upvar.rs
CommitLineData
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
33use super::FnCtxt;
34
60c5eb7d 35use crate::expr_use_visitor as euv;
dc9dc135 36use rustc_data_structures::fx::FxIndexMap;
cdc7bbd5 37use rustc_errors::Applicability;
dfeec247
XL
38use rustc_hir as hir;
39use rustc_hir::def_id::DefId;
40use rustc_hir::def_id::LocalDefId;
41use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
74b04a01 42use rustc_infer::infer::UpvarRegion;
6a06907d
XL
43use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, ProjectionKind};
44use rustc_middle::mir::FakeReadCause;
17df50a5 45use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, TypeckResults, UpvarSubsts};
5869c6ff 46use rustc_session::lint;
fc512014 47use rustc_span::sym;
5869c6ff 48use rustc_span::{MultiSpan, Span, Symbol};
17df50a5 49use rustc_trait_selection::traits::{Obligation, ObligationCause};
fc512014 50
17df50a5 51use rustc_data_structures::stable_set::FxHashSet;
6a06907d
XL
52use rustc_index::vec::Idx;
53use rustc_target::abi::VariantIdx;
54
cdc7bbd5
XL
55use 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
62enum 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.
71type InferredCaptureInformation<'tcx> = FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>;
72
dc9dc135 73impl<'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
82struct InferBorrowKindVisitor<'a, 'tcx> {
83 fcx: &'a FnCtxt<'a, 'tcx>,
1a4d82fc
JJ
84}
85
dc9dc135 86impl<'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 104impl<'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)`.
1234fn 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
1278struct 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 1330impl<'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 1588impl<'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 1661fn 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
1700fn 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 1710fn 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
1733fn 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
1748fn 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
1754fn 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 1768fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
dc9dc135 1769 tcx.hir().name(var_hir_id)
3b2f2976 1770}
fc512014 1771
17df50a5
XL
1772fn 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.
1781fn 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.
1839fn 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.
1897fn 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}