]>
Commit | Line | Data |
---|---|---|
ff7c6d11 XL |
1 | // Copyright 2017 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
0bf4aa26 XL |
11 | use borrow_check::nll::explain_borrow::BorrowExplanation; |
12 | use borrow_check::nll::region_infer::{RegionName, RegionNameSource}; | |
13 | use borrow_check::prefixes::IsPrefixOf; | |
94b46f34 | 14 | use borrow_check::WriteKind; |
0bf4aa26 XL |
15 | use rustc::hir; |
16 | use rustc::hir::def_id::DefId; | |
ff7c6d11 | 17 | use rustc::middle::region::ScopeTree; |
0bf4aa26 XL |
18 | use rustc::mir::{ |
19 | self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, Constant, | |
20 | ConstraintCategory, Field, Local, LocalDecl, LocalKind, Location, Operand, | |
21 | Place, PlaceProjection, ProjectionElem, Rvalue, Statement, StatementKind, | |
22 | TerminatorKind, VarBindingForm, | |
23 | }; | |
24 | use rustc::ty::{self, DefIdTree}; | |
25 | use rustc::util::ppaux::with_highlight_region_for_bound_region; | |
b7449926 | 26 | use rustc_data_structures::fx::FxHashSet; |
0bf4aa26 | 27 | use rustc_data_structures::indexed_vec::Idx; |
0531ce1d | 28 | use rustc_data_structures::sync::Lrc; |
b7449926 | 29 | use rustc_errors::{Applicability, DiagnosticBuilder}; |
94b46f34 | 30 | use syntax_pos::Span; |
ff7c6d11 | 31 | |
94b46f34 | 32 | use super::borrow_set::BorrowData; |
0531ce1d | 33 | use super::{Context, MirBorrowckCtxt}; |
ff7c6d11 | 34 | use super::{InitializationRequiringAction, PrefixSet}; |
b7449926 XL |
35 | use dataflow::drop_flag_effects; |
36 | use dataflow::move_paths::indexes::MoveOutIndex; | |
ff7c6d11 XL |
37 | use dataflow::move_paths::MovePathIndex; |
38 | use util::borrowck_errors::{BorrowckErrors, Origin}; | |
39 | ||
0bf4aa26 XL |
40 | #[derive(Debug)] |
41 | struct MoveSite { | |
42 | /// Index of the "move out" that we found. The `MoveData` can | |
43 | /// then tell us where the move occurred. | |
44 | moi: MoveOutIndex, | |
45 | ||
46 | /// True if we traversed a back edge while walking from the point | |
47 | /// of error to the move site. | |
48 | traversed_back_edge: bool | |
49 | } | |
50 | ||
ff7c6d11 XL |
51 | impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { |
52 | pub(super) fn report_use_of_moved_or_uninitialized( | |
53 | &mut self, | |
b7449926 | 54 | context: Context, |
ff7c6d11 | 55 | desired_action: InitializationRequiringAction, |
0bf4aa26 | 56 | (moved_place, used_place, span): (&Place<'tcx>, &Place<'tcx>, Span), |
ff7c6d11 | 57 | mpi: MovePathIndex, |
ff7c6d11 | 58 | ) { |
0bf4aa26 XL |
59 | debug!( |
60 | "report_use_of_moved_or_uninitialized: context={:?} desired_action={:?} \ | |
61 | moved_place={:?} used_place={:?} span={:?} mpi={:?}", | |
62 | context, desired_action, moved_place, used_place, span, mpi | |
63 | ); | |
64 | ||
65 | let use_spans = self.move_spans(moved_place, context.loc) | |
b7449926 XL |
66 | .or_else(|| self.borrow_spans(span, context.loc)); |
67 | let span = use_spans.args_or_use(); | |
68 | ||
0bf4aa26 XL |
69 | let move_site_vec = self.get_moved_indexes(context, mpi); |
70 | debug!( | |
71 | "report_use_of_moved_or_uninitialized: move_site_vec={:?}", | |
72 | move_site_vec | |
73 | ); | |
74 | let move_out_indices: Vec<_> = move_site_vec | |
75 | .iter() | |
76 | .map(|move_site| move_site.moi) | |
77 | .collect(); | |
ff7c6d11 | 78 | |
0bf4aa26 XL |
79 | if move_out_indices.is_empty() { |
80 | let root_place = self.prefixes(&used_place, PrefixSet::All).last().unwrap(); | |
83c7162d | 81 | |
0bf4aa26 | 82 | if self.uninitialized_error_reported.contains(root_place) { |
83c7162d XL |
83 | debug!( |
84 | "report_use_of_moved_or_uninitialized place: error about {:?} suppressed", | |
85 | root_place | |
86 | ); | |
87 | return; | |
88 | } | |
89 | ||
0bf4aa26 | 90 | self.uninitialized_error_reported.insert(root_place.clone()); |
83c7162d | 91 | |
0bf4aa26 XL |
92 | let item_msg = match self.describe_place_with_options(used_place, |
93 | IncludingDowncast(true)) { | |
ff7c6d11 XL |
94 | Some(name) => format!("`{}`", name), |
95 | None => "value".to_owned(), | |
96 | }; | |
0bf4aa26 | 97 | let mut err = self.infcx.tcx.cannot_act_on_uninitialized_variable( |
b7449926 XL |
98 | span, |
99 | desired_action.as_noun(), | |
0bf4aa26 XL |
100 | &self.describe_place_with_options(moved_place, IncludingDowncast(true)) |
101 | .unwrap_or_else(|| "_".to_owned()), | |
b7449926 XL |
102 | Origin::Mir, |
103 | ); | |
8faf50e0 | 104 | err.span_label(span, format!("use of possibly uninitialized {}", item_msg)); |
b7449926 XL |
105 | |
106 | use_spans.var_span_label( | |
107 | &mut err, | |
0bf4aa26 | 108 | format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()), |
b7449926 XL |
109 | ); |
110 | ||
8faf50e0 | 111 | err.buffer(&mut self.errors_buffer); |
ff7c6d11 | 112 | } else { |
0bf4aa26 XL |
113 | if let Some((reported_place, _)) = self.move_error_reported.get(&move_out_indices) { |
114 | if self.prefixes(&reported_place, PrefixSet::All) | |
115 | .any(|p| p == used_place) | |
116 | { | |
117 | debug!( | |
118 | "report_use_of_moved_or_uninitialized place: error suppressed \ | |
119 | mois={:?}", | |
120 | move_out_indices | |
121 | ); | |
122 | return; | |
123 | } | |
124 | } | |
125 | ||
ff7c6d11 XL |
126 | let msg = ""; //FIXME: add "partially " or "collaterally " |
127 | ||
0bf4aa26 | 128 | let mut err = self.infcx.tcx.cannot_act_on_moved_value( |
ff7c6d11 XL |
129 | span, |
130 | desired_action.as_noun(), | |
131 | msg, | |
0bf4aa26 | 132 | self.describe_place_with_options(&moved_place, IncludingDowncast(true)), |
ff7c6d11 XL |
133 | Origin::Mir, |
134 | ); | |
135 | ||
0bf4aa26 XL |
136 | self.add_closure_invoked_twice_with_moved_variable_suggestion( |
137 | context.loc, | |
138 | used_place, | |
139 | &mut err, | |
140 | ); | |
141 | ||
2c00a5a8 | 142 | let mut is_loop_move = false; |
0bf4aa26 XL |
143 | for move_site in &move_site_vec { |
144 | let move_out = self.move_data.moves[(*move_site).moi]; | |
b7449926 XL |
145 | let moved_place = &self.move_data.move_paths[move_out.path].place; |
146 | ||
147 | let move_spans = self.move_spans(moved_place, move_out.source); | |
148 | let move_span = move_spans.args_or_use(); | |
149 | ||
150 | let move_msg = if move_spans.for_closure() { | |
151 | " into closure" | |
152 | } else { | |
153 | "" | |
154 | }; | |
155 | ||
ff7c6d11 XL |
156 | if span == move_span { |
157 | err.span_label( | |
158 | span, | |
0bf4aa26 | 159 | format!("value moved{} here, in previous iteration of loop", move_msg), |
ff7c6d11 | 160 | ); |
2c00a5a8 | 161 | is_loop_move = true; |
0bf4aa26 XL |
162 | } else if move_site.traversed_back_edge { |
163 | err.span_label( | |
164 | move_span, | |
165 | format!( | |
166 | "value moved{} here, in previous iteration of loop", | |
167 | move_msg | |
168 | ), | |
169 | ); | |
ff7c6d11 XL |
170 | } else { |
171 | err.span_label(move_span, format!("value moved{} here", move_msg)); | |
0bf4aa26 XL |
172 | move_spans.var_span_label( |
173 | &mut err, | |
174 | format!("variable moved due to use{}", move_spans.describe()), | |
175 | ); | |
ff7c6d11 XL |
176 | }; |
177 | } | |
b7449926 XL |
178 | |
179 | use_spans.var_span_label( | |
180 | &mut err, | |
0bf4aa26 | 181 | format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()), |
b7449926 XL |
182 | ); |
183 | ||
2c00a5a8 XL |
184 | if !is_loop_move { |
185 | err.span_label( | |
186 | span, | |
187 | format!( | |
188 | "value {} here after move", | |
189 | desired_action.as_verb_in_past_tense() | |
190 | ), | |
191 | ); | |
192 | } | |
ff7c6d11 | 193 | |
0bf4aa26 | 194 | if let Some(ty) = self.retrieve_type_for_place(used_place) { |
ff7c6d11 | 195 | let needs_note = match ty.sty { |
b7449926 | 196 | ty::Closure(id, _) => { |
0bf4aa26 XL |
197 | let tables = self.infcx.tcx.typeck_tables_of(id); |
198 | let node_id = self.infcx.tcx.hir.as_local_node_id(id).unwrap(); | |
199 | let hir_id = self.infcx.tcx.hir.node_to_hir_id(node_id); | |
200 | ||
201 | tables.closure_kind_origins().get(hir_id).is_none() | |
0531ce1d | 202 | } |
ff7c6d11 XL |
203 | _ => true, |
204 | }; | |
205 | ||
206 | if needs_note { | |
0bf4aa26 | 207 | let mpi = self.move_data.moves[move_out_indices[0]].path; |
8faf50e0 XL |
208 | let place = &self.move_data.move_paths[mpi].place; |
209 | ||
210 | if let Some(ty) = self.retrieve_type_for_place(place) { | |
0bf4aa26 XL |
211 | let note_msg = match self.describe_place_with_options( |
212 | place, | |
213 | IncludingDowncast(true), | |
214 | ) { | |
8faf50e0 XL |
215 | Some(name) => format!("`{}`", name), |
216 | None => "value".to_owned(), | |
217 | }; | |
ff7c6d11 | 218 | |
8faf50e0 XL |
219 | err.note(&format!( |
220 | "move occurs because {} has type `{}`, \ | |
221 | which does not implement the `Copy` trait", | |
222 | note_msg, ty | |
223 | )); | |
224 | } | |
ff7c6d11 XL |
225 | } |
226 | } | |
227 | ||
0bf4aa26 XL |
228 | if let Some((_, mut old_err)) = self.move_error_reported |
229 | .insert(move_out_indices, (used_place.clone(), err)) | |
230 | { | |
231 | // Cancel the old error so it doesn't ICE. | |
232 | old_err.cancel(); | |
233 | } | |
ff7c6d11 XL |
234 | } |
235 | } | |
236 | ||
237 | pub(super) fn report_move_out_while_borrowed( | |
238 | &mut self, | |
239 | context: Context, | |
0bf4aa26 | 240 | (place, span): (&Place<'tcx>, Span), |
ff7c6d11 XL |
241 | borrow: &BorrowData<'tcx>, |
242 | ) { | |
0bf4aa26 XL |
243 | debug!( |
244 | "report_move_out_while_borrowed: context={:?} place={:?} span={:?} borrow={:?}", | |
245 | context, place, span, borrow | |
246 | ); | |
247 | let tcx = self.infcx.tcx; | |
ff7c6d11 XL |
248 | let value_msg = match self.describe_place(place) { |
249 | Some(name) => format!("`{}`", name), | |
250 | None => "value".to_owned(), | |
251 | }; | |
252 | let borrow_msg = match self.describe_place(&borrow.borrowed_place) { | |
253 | Some(name) => format!("`{}`", name), | |
254 | None => "value".to_owned(), | |
255 | }; | |
b7449926 XL |
256 | |
257 | let borrow_spans = self.retrieve_borrow_spans(borrow); | |
258 | let borrow_span = borrow_spans.args_or_use(); | |
259 | ||
260 | let move_spans = self.move_spans(place, context.loc); | |
261 | let span = move_spans.args_or_use(); | |
262 | ||
0531ce1d | 263 | let mut err = tcx.cannot_move_when_borrowed( |
ff7c6d11 | 264 | span, |
0bf4aa26 | 265 | &self.describe_place(place).unwrap_or_else(|| "_".to_owned()), |
ff7c6d11 XL |
266 | Origin::Mir, |
267 | ); | |
b7449926 | 268 | err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg)); |
ff7c6d11 | 269 | err.span_label(span, format!("move out of {} occurs here", value_msg)); |
b7449926 | 270 | |
0bf4aa26 XL |
271 | borrow_spans.var_span_label( |
272 | &mut err, | |
273 | format!("borrow occurs due to use{}", borrow_spans.describe()) | |
274 | ); | |
b7449926 | 275 | |
0bf4aa26 XL |
276 | move_spans.var_span_label( |
277 | &mut err, | |
278 | format!("move occurs due to use{}", move_spans.describe()) | |
279 | ); | |
b7449926 | 280 | |
0bf4aa26 XL |
281 | self.explain_why_borrow_contains_point(context, borrow, None) |
282 | .add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, ""); | |
8faf50e0 | 283 | err.buffer(&mut self.errors_buffer); |
ff7c6d11 XL |
284 | } |
285 | ||
286 | pub(super) fn report_use_while_mutably_borrowed( | |
287 | &mut self, | |
288 | context: Context, | |
b7449926 | 289 | (place, _span): (&Place<'tcx>, Span), |
ff7c6d11 XL |
290 | borrow: &BorrowData<'tcx>, |
291 | ) { | |
0bf4aa26 | 292 | let tcx = self.infcx.tcx; |
b7449926 XL |
293 | |
294 | let borrow_spans = self.retrieve_borrow_spans(borrow); | |
295 | let borrow_span = borrow_spans.args_or_use(); | |
296 | ||
297 | // Conflicting borrows are reported separately, so only check for move | |
298 | // captures. | |
299 | let use_spans = self.move_spans(place, context.loc); | |
300 | let span = use_spans.var_or_use(); | |
301 | ||
0531ce1d | 302 | let mut err = tcx.cannot_use_when_mutably_borrowed( |
ff7c6d11 | 303 | span, |
0bf4aa26 | 304 | &self.describe_place(place).unwrap_or_else(|| "_".to_owned()), |
b7449926 | 305 | borrow_span, |
0bf4aa26 XL |
306 | &self.describe_place(&borrow.borrowed_place) |
307 | .unwrap_or_else(|| "_".to_owned()), | |
ff7c6d11 XL |
308 | Origin::Mir, |
309 | ); | |
310 | ||
b7449926 XL |
311 | borrow_spans.var_span_label(&mut err, { |
312 | let place = &borrow.borrowed_place; | |
0bf4aa26 | 313 | let desc_place = self.describe_place(place).unwrap_or_else(|| "_".to_owned()); |
ff7c6d11 | 314 | |
0bf4aa26 | 315 | format!("borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe()) |
b7449926 | 316 | }); |
ff7c6d11 | 317 | |
0bf4aa26 XL |
318 | self.explain_why_borrow_contains_point(context, borrow, None) |
319 | .add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, ""); | |
b7449926 | 320 | err.buffer(&mut self.errors_buffer); |
ff7c6d11 XL |
321 | } |
322 | ||
323 | pub(super) fn report_conflicting_borrow( | |
324 | &mut self, | |
325 | context: Context, | |
326 | (place, span): (&Place<'tcx>, Span), | |
327 | gen_borrow_kind: BorrowKind, | |
0531ce1d | 328 | issued_borrow: &BorrowData<'tcx>, |
ff7c6d11 | 329 | ) { |
b7449926 XL |
330 | let issued_spans = self.retrieve_borrow_spans(issued_borrow); |
331 | let issued_span = issued_spans.args_or_use(); | |
ff7c6d11 | 332 | |
b7449926 XL |
333 | let borrow_spans = self.borrow_spans(span, context.loc); |
334 | let span = borrow_spans.args_or_use(); | |
ff7c6d11 | 335 | |
0bf4aa26 XL |
336 | let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() { |
337 | "generator" | |
338 | } else { | |
339 | "closure" | |
340 | }; | |
341 | ||
342 | let desc_place = self.describe_place(place).unwrap_or_else(|| "_".to_owned()); | |
343 | let tcx = self.infcx.tcx; | |
344 | ||
345 | let first_borrow_desc; | |
ff7c6d11 XL |
346 | |
347 | // FIXME: supply non-"" `opt_via` when appropriate | |
348 | let mut err = match ( | |
349 | gen_borrow_kind, | |
350 | "immutable", | |
351 | "mutable", | |
352 | issued_borrow.kind, | |
353 | "immutable", | |
354 | "mutable", | |
355 | ) { | |
0bf4aa26 XL |
356 | (BorrowKind::Shared, lft, _, BorrowKind::Mut { .. }, _, rgt) => { |
357 | first_borrow_desc = "mutable "; | |
358 | tcx.cannot_reborrow_already_borrowed( | |
ff7c6d11 XL |
359 | span, |
360 | &desc_place, | |
361 | "", | |
362 | lft, | |
363 | issued_span, | |
364 | "it", | |
365 | rgt, | |
366 | "", | |
83c7162d | 367 | None, |
ff7c6d11 | 368 | Origin::Mir, |
0bf4aa26 XL |
369 | ) |
370 | } | |
371 | (BorrowKind::Mut { .. }, _, lft, BorrowKind::Shared, rgt, _) => { | |
372 | first_borrow_desc = "immutable "; | |
373 | tcx.cannot_reborrow_already_borrowed( | |
374 | span, | |
375 | &desc_place, | |
376 | "", | |
377 | lft, | |
378 | issued_span, | |
379 | "it", | |
380 | rgt, | |
381 | "", | |
382 | None, | |
383 | Origin::Mir, | |
384 | ) | |
385 | } | |
ff7c6d11 | 386 | |
0bf4aa26 XL |
387 | (BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => { |
388 | first_borrow_desc = "first "; | |
389 | tcx.cannot_mutably_borrow_multiply( | |
ff7c6d11 XL |
390 | span, |
391 | &desc_place, | |
392 | "", | |
393 | issued_span, | |
394 | "", | |
83c7162d | 395 | None, |
ff7c6d11 | 396 | Origin::Mir, |
0bf4aa26 XL |
397 | ) |
398 | } | |
ff7c6d11 | 399 | |
0bf4aa26 XL |
400 | (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => { |
401 | first_borrow_desc = "first "; | |
402 | tcx.cannot_uniquely_borrow_by_two_closures( | |
ff7c6d11 XL |
403 | span, |
404 | &desc_place, | |
405 | issued_span, | |
83c7162d | 406 | None, |
ff7c6d11 | 407 | Origin::Mir, |
0bf4aa26 XL |
408 | ) |
409 | } | |
ff7c6d11 | 410 | |
0bf4aa26 XL |
411 | (BorrowKind::Mut { .. }, _, _, BorrowKind::Shallow, _, _) |
412 | | (BorrowKind::Unique, _, _, BorrowKind::Shallow, _, _) => { | |
413 | let mut err = tcx.cannot_mutate_in_match_guard( | |
414 | span, | |
415 | issued_span, | |
416 | &desc_place, | |
417 | "mutably borrow", | |
418 | Origin::Mir, | |
419 | ); | |
420 | borrow_spans.var_span_label( | |
421 | &mut err, | |
422 | format!( | |
423 | "borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe() | |
424 | ), | |
425 | ); | |
426 | err.buffer(&mut self.errors_buffer); | |
427 | ||
428 | return; | |
429 | } | |
430 | ||
431 | (BorrowKind::Unique, _, _, _, _, _) => { | |
432 | first_borrow_desc = "first "; | |
433 | tcx.cannot_uniquely_borrow_by_one_closure( | |
434 | span, | |
435 | container_name, | |
436 | &desc_place, | |
437 | "", | |
438 | issued_span, | |
439 | "it", | |
440 | "", | |
441 | None, | |
442 | Origin::Mir, | |
443 | ) | |
444 | }, | |
ff7c6d11 | 445 | |
0bf4aa26 XL |
446 | (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => { |
447 | first_borrow_desc = "first "; | |
448 | tcx.cannot_reborrow_already_uniquely_borrowed( | |
ff7c6d11 | 449 | span, |
0bf4aa26 | 450 | container_name, |
ff7c6d11 XL |
451 | &desc_place, |
452 | "", | |
453 | lft, | |
454 | issued_span, | |
455 | "", | |
83c7162d | 456 | None, |
ff7c6d11 | 457 | Origin::Mir, |
0bf4aa26 XL |
458 | ) |
459 | } | |
ff7c6d11 | 460 | |
0bf4aa26 XL |
461 | (BorrowKind::Mut { .. }, _, lft, BorrowKind::Unique, _, _) => { |
462 | first_borrow_desc = "first "; | |
463 | tcx.cannot_reborrow_already_uniquely_borrowed( | |
ff7c6d11 | 464 | span, |
0bf4aa26 | 465 | container_name, |
ff7c6d11 XL |
466 | &desc_place, |
467 | "", | |
468 | lft, | |
469 | issued_span, | |
470 | "", | |
83c7162d | 471 | None, |
ff7c6d11 | 472 | Origin::Mir, |
0bf4aa26 XL |
473 | ) |
474 | } | |
ff7c6d11 | 475 | |
0bf4aa26 XL |
476 | (BorrowKind::Shallow, _, _, BorrowKind::Unique, _, _) |
477 | | (BorrowKind::Shallow, _, _, BorrowKind::Mut { .. }, _, _) => { | |
478 | // Shallow borrows are uses from the user's point of view. | |
479 | self.report_use_while_mutably_borrowed(context, (place, span), issued_borrow); | |
480 | return; | |
481 | } | |
482 | (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) | |
483 | | (BorrowKind::Shared, _, _, BorrowKind::Shallow, _, _) | |
484 | | (BorrowKind::Shallow, _, _, BorrowKind::Shared, _, _) | |
485 | | (BorrowKind::Shallow, _, _, BorrowKind::Shallow, _, _) => unreachable!(), | |
ff7c6d11 XL |
486 | }; |
487 | ||
b7449926 XL |
488 | if issued_spans == borrow_spans { |
489 | borrow_spans.var_span_label( | |
490 | &mut err, | |
0bf4aa26 | 491 | format!("borrows occur due to use of `{}`{}", desc_place, borrow_spans.describe()), |
b7449926 XL |
492 | ); |
493 | } else { | |
494 | let borrow_place = &issued_borrow.borrowed_place; | |
0bf4aa26 XL |
495 | let borrow_place_desc = self.describe_place(borrow_place) |
496 | .unwrap_or_else(|| "_".to_owned()); | |
b7449926 XL |
497 | issued_spans.var_span_label( |
498 | &mut err, | |
ff7c6d11 | 499 | format!( |
0bf4aa26 XL |
500 | "first borrow occurs due to use of `{}`{}", |
501 | borrow_place_desc, | |
502 | issued_spans.describe(), | |
ff7c6d11 XL |
503 | ), |
504 | ); | |
ff7c6d11 | 505 | |
b7449926 XL |
506 | borrow_spans.var_span_label( |
507 | &mut err, | |
508 | format!( | |
0bf4aa26 XL |
509 | "second borrow occurs due to use of `{}`{}", |
510 | desc_place, | |
511 | borrow_spans.describe(), | |
b7449926 | 512 | ), |
ff7c6d11 XL |
513 | ); |
514 | } | |
515 | ||
0bf4aa26 XL |
516 | self.explain_why_borrow_contains_point(context, issued_borrow, None) |
517 | .add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, first_borrow_desc); | |
ff7c6d11 | 518 | |
8faf50e0 | 519 | err.buffer(&mut self.errors_buffer); |
ff7c6d11 XL |
520 | } |
521 | ||
0bf4aa26 XL |
522 | /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`. |
523 | /// | |
524 | /// This means that some data referenced by `borrow` needs to live | |
525 | /// past the point where the StorageDeadOrDrop of `place` occurs. | |
526 | /// This is usually interpreted as meaning that `place` has too | |
527 | /// short a lifetime. (But sometimes it is more useful to report | |
528 | /// it as a more direct conflict between the execution of a | |
529 | /// `Drop::drop` with an aliasing borrow.) | |
ff7c6d11 XL |
530 | pub(super) fn report_borrowed_value_does_not_live_long_enough( |
531 | &mut self, | |
532 | context: Context, | |
533 | borrow: &BorrowData<'tcx>, | |
94b46f34 XL |
534 | place_span: (&Place<'tcx>, Span), |
535 | kind: Option<WriteKind>, | |
ff7c6d11 | 536 | ) { |
0bf4aa26 XL |
537 | debug!( |
538 | "report_borrowed_value_does_not_live_long_enough(\ | |
539 | {:?}, {:?}, {:?}, {:?}\ | |
540 | )", | |
541 | context, borrow, place_span, kind | |
542 | ); | |
543 | ||
94b46f34 | 544 | let drop_span = place_span.1; |
0bf4aa26 XL |
545 | let scope_tree = self.infcx.tcx.region_scope_tree(self.mir_def_id); |
546 | let root_place = self.prefixes(&borrow.borrowed_place, PrefixSet::All) | |
0531ce1d XL |
547 | .last() |
548 | .unwrap(); | |
ff7c6d11 | 549 | |
b7449926 XL |
550 | let borrow_spans = self.retrieve_borrow_spans(borrow); |
551 | let borrow_span = borrow_spans.var_or_use(); | |
552 | ||
ff7c6d11 XL |
553 | let proper_span = match *root_place { |
554 | Place::Local(local) => self.mir.local_decls[local].source_info.span, | |
555 | _ => drop_span, | |
556 | }; | |
557 | ||
0bf4aa26 | 558 | if self.access_place_error_reported |
0531ce1d XL |
559 | .contains(&(root_place.clone(), borrow_span)) |
560 | { | |
561 | debug!( | |
562 | "suppressing access_place error when borrow doesn't live long enough for {:?}", | |
563 | borrow_span | |
564 | ); | |
2c00a5a8 XL |
565 | return; |
566 | } | |
567 | ||
0531ce1d XL |
568 | self.access_place_error_reported |
569 | .insert((root_place.clone(), borrow_span)); | |
2c00a5a8 | 570 | |
0bf4aa26 XL |
571 | if let StorageDeadOrDrop::Destructor(dropped_ty) = |
572 | self.classify_drop_access_kind(&borrow.borrowed_place) | |
573 | { | |
574 | // If a borrow of path `B` conflicts with drop of `D` (and | |
575 | // we're not in the uninteresting case where `B` is a | |
576 | // prefix of `D`), then report this as a more interesting | |
577 | // destructor conflict. | |
578 | if !borrow.borrowed_place.is_prefix_of(place_span.0) { | |
579 | self.report_borrow_conflicts_with_destructor( | |
580 | context, borrow, place_span, kind, dropped_ty, | |
581 | ); | |
582 | return; | |
583 | } | |
584 | } | |
585 | ||
586 | let place_desc = self.describe_place(&borrow.borrowed_place); | |
b7449926 | 587 | |
0bf4aa26 XL |
588 | let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0)); |
589 | let explanation = self.explain_why_borrow_contains_point(context, &borrow, kind_place); | |
590 | ||
591 | let err = match (place_desc, explanation) { | |
592 | (Some(_), _) if self.is_place_thread_local(root_place) => { | |
b7449926 | 593 | self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span) |
0531ce1d | 594 | } |
0bf4aa26 XL |
595 | // If the outlives constraint comes from inside the closure, |
596 | // for example: | |
597 | // | |
598 | // let x = 0; | |
599 | // let y = &x; | |
600 | // Box::new(|| y) as Box<Fn() -> &'static i32> | |
601 | // | |
602 | // then just use the normal error. The closure isn't escaping | |
603 | // and `move` will not help here. | |
604 | ( | |
605 | Some(ref name), | |
606 | BorrowExplanation::MustBeValidFor { | |
607 | category: category @ ConstraintCategory::Return, | |
608 | from_closure: false, | |
609 | ref region_name, | |
610 | span, | |
611 | .. | |
612 | }, | |
613 | ) | |
614 | | ( | |
615 | Some(ref name), | |
616 | BorrowExplanation::MustBeValidFor { | |
617 | category: category @ ConstraintCategory::CallArgument, | |
618 | from_closure: false, | |
619 | ref region_name, | |
620 | span, | |
621 | .. | |
622 | }, | |
623 | ) if borrow_spans.for_closure() => self.report_escaping_closure_capture( | |
624 | borrow_spans.args_or_use(), | |
625 | borrow_span, | |
626 | region_name, | |
627 | category, | |
628 | span, | |
629 | &format!("`{}`", name), | |
630 | ), | |
631 | ( | |
632 | ref name, | |
633 | BorrowExplanation::MustBeValidFor { | |
634 | category: ConstraintCategory::Assignment, | |
635 | from_closure: false, | |
636 | region_name: RegionName { | |
637 | source: RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name), | |
638 | .. | |
639 | }, | |
640 | span, | |
641 | .. | |
642 | }, | |
643 | ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span), | |
644 | (Some(name), explanation) => self.report_local_value_does_not_live_long_enough( | |
b7449926 | 645 | context, |
0bf4aa26 | 646 | &name, |
b7449926 XL |
647 | &scope_tree, |
648 | &borrow, | |
b7449926 | 649 | drop_span, |
0bf4aa26 XL |
650 | borrow_spans, |
651 | explanation, | |
b7449926 | 652 | ), |
0bf4aa26 | 653 | (None, explanation) => self.report_temporary_value_does_not_live_long_enough( |
b7449926 XL |
654 | context, |
655 | &scope_tree, | |
656 | &borrow, | |
b7449926 | 657 | drop_span, |
0bf4aa26 | 658 | borrow_spans, |
b7449926 | 659 | proper_span, |
0bf4aa26 | 660 | explanation, |
b7449926 XL |
661 | ), |
662 | }; | |
663 | ||
b7449926 | 664 | err.buffer(&mut self.errors_buffer); |
ff7c6d11 XL |
665 | } |
666 | ||
8faf50e0 | 667 | fn report_local_value_does_not_live_long_enough( |
ff7c6d11 XL |
668 | &mut self, |
669 | context: Context, | |
0bf4aa26 | 670 | name: &str, |
0531ce1d | 671 | scope_tree: &Lrc<ScopeTree>, |
ff7c6d11 XL |
672 | borrow: &BorrowData<'tcx>, |
673 | drop_span: Span, | |
0bf4aa26 XL |
674 | borrow_spans: UseSpans, |
675 | explanation: BorrowExplanation, | |
b7449926 | 676 | ) -> DiagnosticBuilder<'cx> { |
0531ce1d | 677 | debug!( |
8faf50e0 | 678 | "report_local_value_does_not_live_long_enough(\ |
0bf4aa26 | 679 | {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\ |
0531ce1d | 680 | )", |
0bf4aa26 | 681 | context, name, scope_tree, borrow, drop_span, borrow_spans |
b7449926 XL |
682 | ); |
683 | ||
0bf4aa26 XL |
684 | let borrow_span = borrow_spans.var_or_use(); |
685 | if let BorrowExplanation::MustBeValidFor { | |
686 | category: ConstraintCategory::Return, | |
687 | span, | |
688 | ref opt_place_desc, | |
689 | from_closure: false, | |
690 | .. | |
691 | } = explanation { | |
692 | return self.report_cannot_return_reference_to_local( | |
693 | borrow, | |
694 | borrow_span, | |
695 | span, | |
696 | opt_place_desc.as_ref(), | |
697 | ); | |
698 | } | |
699 | ||
700 | let mut err = self.infcx.tcx.path_does_not_live_long_enough( | |
b7449926 XL |
701 | borrow_span, |
702 | &format!("`{}`", name), | |
703 | Origin::Mir, | |
0531ce1d XL |
704 | ); |
705 | ||
0bf4aa26 XL |
706 | if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) { |
707 | let region_name = annotation.emit(&mut err); | |
708 | ||
709 | err.span_label( | |
710 | borrow_span, | |
711 | format!("`{}` would have to be valid for `{}`...", name, region_name), | |
712 | ); | |
713 | ||
714 | if let Some(fn_node_id) = self.infcx.tcx.hir.as_local_node_id(self.mir_def_id) { | |
715 | err.span_label( | |
716 | drop_span, | |
717 | format!( | |
718 | "...but `{}` will be dropped here, when the function `{}` returns", | |
719 | name, | |
720 | self.infcx.tcx.hir.name(fn_node_id), | |
721 | ), | |
722 | ); | |
723 | ||
724 | err.note( | |
725 | "functions cannot return a borrow to data owned within the function's scope, \ | |
726 | functions can only return borrows to data passed as arguments", | |
727 | ); | |
728 | err.note( | |
729 | "to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch04-02-\ | |
730 | references-and-borrowing.html#dangling-references>", | |
731 | ); | |
732 | } else { | |
733 | err.span_label( | |
734 | drop_span, | |
735 | format!("...but `{}` dropped here while still borrowed", name), | |
736 | ); | |
737 | } | |
738 | ||
739 | if let BorrowExplanation::MustBeValidFor { .. } = explanation { | |
740 | } else { | |
741 | explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, ""); | |
742 | } | |
743 | } else { | |
744 | err.span_label(borrow_span, "borrowed value does not live long enough"); | |
745 | err.span_label( | |
746 | drop_span, | |
747 | format!("`{}` dropped here while still borrowed", name), | |
748 | ); | |
749 | ||
750 | let within = if borrow_spans.for_generator() { | |
751 | " by generator" | |
752 | } else { | |
753 | "" | |
754 | }; | |
755 | ||
756 | borrow_spans.args_span_label( | |
757 | &mut err, | |
758 | format!("value captured here{}", within), | |
759 | ); | |
760 | ||
761 | explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, ""); | |
762 | } | |
0531ce1d | 763 | |
b7449926 XL |
764 | err |
765 | } | |
766 | ||
0bf4aa26 XL |
767 | fn report_borrow_conflicts_with_destructor( |
768 | &mut self, | |
769 | context: Context, | |
770 | borrow: &BorrowData<'tcx>, | |
771 | (place, drop_span): (&Place<'tcx>, Span), | |
772 | kind: Option<WriteKind>, | |
773 | dropped_ty: ty::Ty<'tcx>, | |
774 | ) { | |
775 | debug!( | |
776 | "report_borrow_conflicts_with_destructor(\ | |
777 | {:?}, {:?}, ({:?}, {:?}), {:?}\ | |
778 | )", | |
779 | context, borrow, place, drop_span, kind, | |
780 | ); | |
781 | ||
782 | let borrow_spans = self.retrieve_borrow_spans(borrow); | |
783 | let borrow_span = borrow_spans.var_or_use(); | |
784 | ||
785 | let mut err = self.infcx | |
786 | .tcx | |
787 | .cannot_borrow_across_destructor(borrow_span, Origin::Mir); | |
788 | ||
789 | let what_was_dropped = match self.describe_place(place) { | |
790 | Some(name) => format!("`{}`", name.as_str()), | |
791 | None => format!("temporary value"), | |
792 | }; | |
793 | ||
794 | let label = match self.describe_place(&borrow.borrowed_place) { | |
795 | Some(borrowed) => format!( | |
796 | "here, drop of {D} needs exclusive access to `{B}`, \ | |
797 | because the type `{T}` implements the `Drop` trait", | |
798 | D = what_was_dropped, | |
799 | T = dropped_ty, | |
800 | B = borrowed | |
801 | ), | |
802 | None => format!( | |
803 | "here is drop of {D}; whose type `{T}` implements the `Drop` trait", | |
804 | D = what_was_dropped, | |
805 | T = dropped_ty | |
806 | ), | |
807 | }; | |
808 | err.span_label(drop_span, label); | |
809 | ||
810 | // Only give this note and suggestion if they could be relevant. | |
811 | let explanation = | |
812 | self.explain_why_borrow_contains_point(context, borrow, kind.map(|k| (k, place))); | |
813 | match explanation { | |
814 | BorrowExplanation::UsedLater { .. } | |
815 | | BorrowExplanation::UsedLaterWhenDropped { .. } => { | |
816 | err.note("consider using a `let` binding to create a longer lived value"); | |
817 | } | |
818 | _ => {} | |
819 | } | |
820 | ||
821 | explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, ""); | |
822 | ||
823 | err.buffer(&mut self.errors_buffer); | |
824 | } | |
825 | ||
b7449926 XL |
826 | fn report_thread_local_value_does_not_live_long_enough( |
827 | &mut self, | |
828 | drop_span: Span, | |
829 | borrow_span: Span, | |
830 | ) -> DiagnosticBuilder<'cx> { | |
831 | debug!( | |
832 | "report_thread_local_value_does_not_live_long_enough(\ | |
833 | {:?}, {:?}\ | |
834 | )", | |
835 | drop_span, borrow_span | |
836 | ); | |
837 | ||
0bf4aa26 | 838 | let mut err = self.infcx |
b7449926 XL |
839 | .tcx |
840 | .thread_local_value_does_not_live_long_enough(borrow_span, Origin::Mir); | |
841 | ||
842 | err.span_label( | |
843 | borrow_span, | |
844 | "thread-local variables cannot be borrowed beyond the end of the function", | |
845 | ); | |
846 | err.span_label(drop_span, "end of enclosing function is here"); | |
0bf4aa26 | 847 | |
b7449926 | 848 | err |
ff7c6d11 XL |
849 | } |
850 | ||
8faf50e0 | 851 | fn report_temporary_value_does_not_live_long_enough( |
ff7c6d11 XL |
852 | &mut self, |
853 | context: Context, | |
0531ce1d | 854 | scope_tree: &Lrc<ScopeTree>, |
ff7c6d11 XL |
855 | borrow: &BorrowData<'tcx>, |
856 | drop_span: Span, | |
0bf4aa26 | 857 | borrow_spans: UseSpans, |
ff7c6d11 | 858 | proper_span: Span, |
0bf4aa26 | 859 | explanation: BorrowExplanation, |
b7449926 | 860 | ) -> DiagnosticBuilder<'cx> { |
0531ce1d | 861 | debug!( |
8faf50e0 | 862 | "report_temporary_value_does_not_live_long_enough(\ |
0bf4aa26 | 863 | {:?}, {:?}, {:?}, {:?}, {:?}\ |
0531ce1d | 864 | )", |
0bf4aa26 | 865 | context, scope_tree, borrow, drop_span, proper_span |
0531ce1d XL |
866 | ); |
867 | ||
0bf4aa26 XL |
868 | if let BorrowExplanation::MustBeValidFor { |
869 | category: ConstraintCategory::Return, | |
870 | span, | |
871 | from_closure: false, | |
872 | .. | |
873 | } = explanation { | |
874 | return self.report_cannot_return_reference_to_local( | |
875 | borrow, | |
876 | proper_span, | |
877 | span, | |
878 | None, | |
879 | ); | |
880 | } | |
0531ce1d | 881 | |
0bf4aa26 XL |
882 | let tcx = self.infcx.tcx; |
883 | let mut err = tcx.temporary_value_borrowed_for_too_long(proper_span, Origin::Mir); | |
884 | err.span_label( | |
885 | proper_span, | |
886 | "creates a temporary which is freed while still in use", | |
887 | ); | |
888 | err.span_label( | |
889 | drop_span, | |
890 | "temporary value is freed at the end of this statement", | |
891 | ); | |
892 | ||
893 | match explanation { | |
894 | BorrowExplanation::UsedLater(..) | |
895 | | BorrowExplanation::UsedLaterInLoop(..) | |
896 | | BorrowExplanation::UsedLaterWhenDropped { .. } => { | |
897 | // Only give this note and suggestion if it could be relevant. | |
b7449926 XL |
898 | err.note("consider using a `let` binding to create a longer lived value"); |
899 | } | |
0bf4aa26 | 900 | _ => {} |
b7449926 | 901 | } |
0bf4aa26 XL |
902 | explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, ""); |
903 | ||
904 | let within = if borrow_spans.for_generator() { | |
905 | " by generator" | |
906 | } else { | |
907 | "" | |
908 | }; | |
909 | ||
910 | borrow_spans.args_span_label( | |
911 | &mut err, | |
912 | format!("value captured here{}", within), | |
913 | ); | |
b7449926 | 914 | |
b7449926 XL |
915 | err |
916 | } | |
917 | ||
0bf4aa26 XL |
918 | fn report_cannot_return_reference_to_local( |
919 | &self, | |
920 | borrow: &BorrowData<'tcx>, | |
921 | borrow_span: Span, | |
922 | return_span: Span, | |
923 | opt_place_desc: Option<&String>, | |
924 | ) -> DiagnosticBuilder<'cx> { | |
925 | let tcx = self.infcx.tcx; | |
926 | ||
927 | // FIXME use a better heuristic than Spans | |
928 | let reference_desc = if return_span == self.mir.source_info(borrow.reserve_location).span { | |
929 | "reference to" | |
930 | } else { | |
931 | "value referencing" | |
932 | }; | |
933 | ||
934 | let (place_desc, note) = if let Some(place_desc) = opt_place_desc { | |
935 | let local_kind = match borrow.borrowed_place { | |
936 | Place::Local(local) => { | |
937 | match self.mir.local_kind(local) { | |
938 | LocalKind::ReturnPointer | |
939 | | LocalKind::Temp => bug!("temporary or return pointer with a name"), | |
940 | LocalKind::Var => "local variable ", | |
941 | LocalKind::Arg | |
942 | if !self.mir.upvar_decls.is_empty() | |
943 | && local == Local::new(1) => { | |
944 | "variable captured by `move` " | |
945 | } | |
946 | LocalKind::Arg => { | |
947 | "function parameter " | |
948 | } | |
949 | } | |
950 | } | |
951 | _ => "local data ", | |
952 | }; | |
953 | ( | |
954 | format!("{}`{}`", local_kind, place_desc), | |
955 | format!("`{}` is borrowed here", place_desc), | |
956 | ) | |
957 | } else { | |
958 | let root_place = self.prefixes(&borrow.borrowed_place, PrefixSet::All) | |
959 | .last() | |
960 | .unwrap(); | |
961 | let local = if let Place::Local(local) = *root_place { | |
962 | local | |
963 | } else { | |
964 | bug!("report_cannot_return_reference_to_local: not a local") | |
965 | }; | |
966 | match self.mir.local_kind(local) { | |
967 | LocalKind::ReturnPointer | LocalKind::Temp => { | |
968 | ( | |
969 | "temporary value".to_string(), | |
970 | "temporary value created here".to_string(), | |
971 | ) | |
972 | } | |
973 | LocalKind::Arg => { | |
974 | ( | |
975 | "function parameter".to_string(), | |
976 | "function parameter borrowed here".to_string(), | |
977 | ) | |
978 | }, | |
979 | LocalKind::Var => bug!("local variable without a name"), | |
980 | } | |
981 | }; | |
982 | ||
983 | let mut err = tcx.cannot_return_reference_to_local( | |
984 | return_span, | |
985 | reference_desc, | |
986 | &place_desc, | |
987 | Origin::Mir, | |
988 | ); | |
989 | ||
990 | if return_span != borrow_span { | |
991 | err.span_label(borrow_span, note); | |
992 | } | |
993 | ||
994 | err | |
995 | } | |
996 | ||
997 | fn report_escaping_closure_capture( | |
998 | &mut self, | |
999 | args_span: Span, | |
1000 | var_span: Span, | |
1001 | fr_name: &RegionName, | |
1002 | category: ConstraintCategory, | |
1003 | constraint_span: Span, | |
1004 | captured_var: &str, | |
1005 | ) -> DiagnosticBuilder<'cx> { | |
1006 | let tcx = self.infcx.tcx; | |
1007 | ||
1008 | let mut err = tcx.cannot_capture_in_long_lived_closure( | |
1009 | args_span, | |
1010 | captured_var, | |
1011 | var_span, | |
1012 | Origin::Mir, | |
1013 | ); | |
1014 | ||
1015 | let suggestion = match tcx.sess.source_map().span_to_snippet(args_span) { | |
1016 | Ok(string) => format!("move {}", string), | |
1017 | Err(_) => "move |<args>| <body>".to_string() | |
1018 | }; | |
1019 | ||
1020 | err.span_suggestion_with_applicability( | |
1021 | args_span, | |
1022 | &format!("to force the closure to take ownership of {} (and any \ | |
1023 | other referenced variables), use the `move` keyword", | |
1024 | captured_var), | |
1025 | suggestion, | |
1026 | Applicability::MachineApplicable, | |
1027 | ); | |
1028 | ||
1029 | match category { | |
1030 | ConstraintCategory::Return => { | |
1031 | err.span_note(constraint_span, &format!("closure is returned here")); | |
1032 | } | |
1033 | ConstraintCategory::CallArgument => { | |
1034 | fr_name.highlight_region_name(&mut err); | |
1035 | err.span_note( | |
1036 | constraint_span, | |
1037 | &format!("function requires argument type to outlive `{}`", fr_name), | |
1038 | ); | |
1039 | } | |
1040 | _ => bug!("report_escaping_closure_capture called with unexpected constraint \ | |
1041 | category: `{:?}`", category), | |
1042 | } | |
1043 | err | |
1044 | } | |
1045 | ||
1046 | fn report_escaping_data( | |
1047 | &mut self, | |
1048 | borrow_span: Span, | |
1049 | name: &Option<String>, | |
1050 | upvar_span: Span, | |
1051 | upvar_name: &str, | |
1052 | escape_span: Span, | |
1053 | ) -> DiagnosticBuilder<'cx> { | |
1054 | let tcx = self.infcx.tcx; | |
1055 | ||
1056 | let escapes_from = if tcx.is_closure(self.mir_def_id) { | |
1057 | let tables = tcx.typeck_tables_of(self.mir_def_id); | |
1058 | let mir_hir_id = tcx.hir.def_index_to_hir_id(self.mir_def_id.index); | |
1059 | match tables.node_id_to_type(mir_hir_id).sty { | |
1060 | ty::Closure(..) => "closure", | |
1061 | ty::Generator(..) => "generator", | |
1062 | _ => bug!("Closure body doesn't have a closure or generator type"), | |
1063 | } | |
1064 | } else { | |
1065 | "function" | |
1066 | }; | |
1067 | ||
1068 | let mut err = tcx.borrowed_data_escapes_closure(escape_span, escapes_from, Origin::Mir); | |
1069 | ||
1070 | err.span_label( | |
1071 | upvar_span, | |
1072 | format!( | |
1073 | "`{}` is declared here, outside of the {} body", | |
1074 | upvar_name, escapes_from | |
1075 | ), | |
1076 | ); | |
1077 | ||
1078 | err.span_label( | |
1079 | borrow_span, | |
1080 | format!( | |
1081 | "borrow is only valid in the {} body", | |
1082 | escapes_from | |
1083 | ), | |
1084 | ); | |
1085 | ||
1086 | if let Some(name) = name { | |
1087 | err.span_label( | |
1088 | escape_span, | |
1089 | format!("reference to `{}` escapes the {} body here", name, escapes_from), | |
1090 | ); | |
1091 | } else { | |
1092 | err.span_label( | |
1093 | escape_span, | |
1094 | format!("reference escapes the {} body here", escapes_from), | |
1095 | ); | |
1096 | } | |
1097 | ||
1098 | err | |
1099 | } | |
1100 | ||
1101 | fn get_moved_indexes(&mut self, context: Context, mpi: MovePathIndex) -> Vec<MoveSite> { | |
b7449926 XL |
1102 | let mir = self.mir; |
1103 | ||
1104 | let mut stack = Vec::new(); | |
0bf4aa26 XL |
1105 | stack.extend(mir.predecessor_locations(context.loc).map(|predecessor| { |
1106 | let is_back_edge = context.loc.dominates(predecessor, &self.dominators); | |
1107 | (predecessor, is_back_edge) | |
1108 | })); | |
b7449926 | 1109 | |
0bf4aa26 | 1110 | let mut visited = FxHashSet::default(); |
b7449926 XL |
1111 | let mut result = vec![]; |
1112 | ||
0bf4aa26 | 1113 | 'dfs: while let Some((location, is_back_edge)) = stack.pop() { |
b7449926 | 1114 | debug!( |
0bf4aa26 XL |
1115 | "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})", |
1116 | location, is_back_edge | |
b7449926 XL |
1117 | ); |
1118 | ||
0bf4aa26 | 1119 | if !visited.insert(location) { |
b7449926 XL |
1120 | continue; |
1121 | } | |
1122 | ||
1123 | // check for moves | |
0bf4aa26 | 1124 | let stmt_kind = mir[location.block] |
b7449926 | 1125 | .statements |
0bf4aa26 | 1126 | .get(location.statement_index) |
b7449926 XL |
1127 | .map(|s| &s.kind); |
1128 | if let Some(StatementKind::StorageDead(..)) = stmt_kind { | |
1129 | // this analysis only tries to find moves explicitly | |
1130 | // written by the user, so we ignore the move-outs | |
1131 | // created by `StorageDead` and at the beginning | |
1132 | // of a function. | |
1133 | } else { | |
0bf4aa26 XL |
1134 | // If we are found a use of a.b.c which was in error, then we want to look for |
1135 | // moves not only of a.b.c but also a.b and a. | |
1136 | // | |
1137 | // Note that the moves data already includes "parent" paths, so we don't have to | |
1138 | // worry about the other case: that is, if there is a move of a.b.c, it is already | |
1139 | // marked as a move of a.b and a as well, so we will generate the correct errors | |
1140 | // there. | |
1141 | let mut mpis = vec![mpi]; | |
1142 | let move_paths = &self.move_data.move_paths; | |
1143 | mpis.extend(move_paths[mpi].parents(move_paths)); | |
1144 | ||
1145 | for moi in &self.move_data.loc_map[location] { | |
b7449926 | 1146 | debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi); |
0bf4aa26 | 1147 | if mpis.contains(&self.move_data.moves[*moi].path) { |
b7449926 | 1148 | debug!("report_use_of_moved_or_uninitialized: found"); |
0bf4aa26 XL |
1149 | result.push(MoveSite { |
1150 | moi: *moi, | |
1151 | traversed_back_edge: is_back_edge, | |
1152 | }); | |
b7449926 XL |
1153 | |
1154 | // Strictly speaking, we could continue our DFS here. There may be | |
1155 | // other moves that can reach the point of error. But it is kind of | |
1156 | // confusing to highlight them. | |
1157 | // | |
1158 | // Example: | |
1159 | // | |
1160 | // ``` | |
1161 | // let a = vec![]; | |
1162 | // let b = a; | |
1163 | // let c = a; | |
1164 | // drop(a); // <-- current point of error | |
1165 | // ``` | |
1166 | // | |
1167 | // Because we stop the DFS here, we only highlight `let c = a`, | |
1168 | // and not `let b = a`. We will of course also report an error at | |
1169 | // `let c = a` which highlights `let b = a` as the move. | |
1170 | continue 'dfs; | |
1171 | } | |
1172 | } | |
1173 | } | |
1174 | ||
1175 | // check for inits | |
1176 | let mut any_match = false; | |
0bf4aa26 XL |
1177 | drop_flag_effects::for_location_inits( |
1178 | self.infcx.tcx, | |
1179 | self.mir, | |
1180 | self.move_data, | |
1181 | location, | |
1182 | |m| { | |
1183 | if m == mpi { | |
1184 | any_match = true; | |
1185 | } | |
1186 | }, | |
1187 | ); | |
b7449926 XL |
1188 | if any_match { |
1189 | continue 'dfs; | |
1190 | } | |
1191 | ||
0bf4aa26 XL |
1192 | stack.extend(mir.predecessor_locations(location).map(|predecessor| { |
1193 | let back_edge = location.dominates(predecessor, &self.dominators); | |
1194 | (predecessor, is_back_edge || back_edge) | |
1195 | })); | |
b7449926 XL |
1196 | } |
1197 | ||
1198 | result | |
ff7c6d11 XL |
1199 | } |
1200 | ||
1201 | pub(super) fn report_illegal_mutation_of_borrowed( | |
1202 | &mut self, | |
1203 | context: Context, | |
1204 | (place, span): (&Place<'tcx>, Span), | |
0531ce1d | 1205 | loan: &BorrowData<'tcx>, |
ff7c6d11 | 1206 | ) { |
b7449926 XL |
1207 | let loan_spans = self.retrieve_borrow_spans(loan); |
1208 | let loan_span = loan_spans.args_or_use(); | |
1209 | ||
0bf4aa26 XL |
1210 | let tcx = self.infcx.tcx; |
1211 | let mut err = if loan.kind == BorrowKind::Shallow { | |
1212 | tcx.cannot_mutate_in_match_guard( | |
1213 | span, | |
1214 | loan_span, | |
1215 | &self.describe_place(place).unwrap_or_else(|| "_".to_owned()), | |
1216 | "assign", | |
1217 | Origin::Mir, | |
1218 | ) | |
1219 | } else { | |
1220 | tcx.cannot_assign_to_borrowed( | |
1221 | span, | |
1222 | loan_span, | |
1223 | &self.describe_place(place).unwrap_or_else(|| "_".to_owned()), | |
1224 | Origin::Mir, | |
1225 | ) | |
1226 | }; | |
ff7c6d11 | 1227 | |
0bf4aa26 XL |
1228 | loan_spans.var_span_label( |
1229 | &mut err, | |
1230 | format!("borrow occurs due to use{}", loan_spans.describe()), | |
1231 | ); | |
b7449926 | 1232 | |
0bf4aa26 XL |
1233 | self.explain_why_borrow_contains_point(context, loan, None) |
1234 | .add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, ""); | |
ff7c6d11 | 1235 | |
8faf50e0 | 1236 | err.buffer(&mut self.errors_buffer); |
ff7c6d11 XL |
1237 | } |
1238 | ||
94b46f34 XL |
1239 | /// Reports an illegal reassignment; for example, an assignment to |
1240 | /// (part of) a non-`mut` local that occurs potentially after that | |
1241 | /// local has already been initialized. `place` is the path being | |
1242 | /// assigned; `err_place` is a place providing a reason why | |
1243 | /// `place` is not mutable (e.g. the non-`mut` local `x` in an | |
1244 | /// assignment to `x.f`). | |
ff7c6d11 XL |
1245 | pub(super) fn report_illegal_reassignment( |
1246 | &mut self, | |
1247 | _context: Context, | |
1248 | (place, span): (&Place<'tcx>, Span), | |
1249 | assigned_span: Span, | |
94b46f34 | 1250 | err_place: &Place<'tcx>, |
ff7c6d11 | 1251 | ) { |
8faf50e0 XL |
1252 | let (from_arg, local_decl) = if let Place::Local(local) = *err_place { |
1253 | if let LocalKind::Arg = self.mir.local_kind(local) { | |
1254 | (true, Some(&self.mir.local_decls[local])) | |
2c00a5a8 | 1255 | } else { |
8faf50e0 | 1256 | (false, Some(&self.mir.local_decls[local])) |
2c00a5a8 XL |
1257 | } |
1258 | } else { | |
8faf50e0 XL |
1259 | (false, None) |
1260 | }; | |
1261 | ||
1262 | // If root local is initialized immediately (everything apart from let | |
1263 | // PATTERN;) then make the error refer to that local, rather than the | |
1264 | // place being assigned later. | |
1265 | let (place_description, assigned_span) = match local_decl { | |
b7449926 XL |
1266 | Some(LocalDecl { |
1267 | is_user_variable: Some(ClearCrossCrate::Clear), | |
1268 | .. | |
1269 | }) | |
1270 | | Some(LocalDecl { | |
1271 | is_user_variable: | |
1272 | Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { | |
1273 | opt_match_place: None, | |
1274 | .. | |
1275 | }))), | |
1276 | .. | |
1277 | }) | |
1278 | | Some(LocalDecl { | |
1279 | is_user_variable: None, | |
1280 | .. | |
1281 | }) | |
8faf50e0 XL |
1282 | | None => (self.describe_place(place), assigned_span), |
1283 | Some(decl) => (self.describe_place(err_place), decl.source_info.span), | |
2c00a5a8 XL |
1284 | }; |
1285 | ||
0bf4aa26 | 1286 | let mut err = self.infcx.tcx.cannot_reassign_immutable( |
ff7c6d11 | 1287 | span, |
8faf50e0 XL |
1288 | place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"), |
1289 | from_arg, | |
ff7c6d11 XL |
1290 | Origin::Mir, |
1291 | ); | |
8faf50e0 | 1292 | let msg = if from_arg { |
2c00a5a8 XL |
1293 | "cannot assign to immutable argument" |
1294 | } else { | |
1295 | "cannot assign twice to immutable variable" | |
1296 | }; | |
ff7c6d11 | 1297 | if span != assigned_span { |
8faf50e0 XL |
1298 | if !from_arg { |
1299 | let value_msg = match place_description { | |
2c00a5a8 XL |
1300 | Some(name) => format!("`{}`", name), |
1301 | None => "value".to_owned(), | |
1302 | }; | |
1303 | err.span_label(assigned_span, format!("first assignment to {}", value_msg)); | |
1304 | } | |
ff7c6d11 | 1305 | } |
8faf50e0 XL |
1306 | if let Some(decl) = local_decl { |
1307 | if let Some(name) = decl.name { | |
1308 | if decl.can_be_made_mutable() { | |
b7449926 | 1309 | err.span_suggestion_with_applicability( |
8faf50e0 | 1310 | decl.source_info.span, |
b7449926 XL |
1311 | "make this binding mutable", |
1312 | format!("mut {}", name), | |
1313 | Applicability::MachineApplicable, | |
8faf50e0 | 1314 | ); |
94b46f34 XL |
1315 | } |
1316 | } | |
1317 | } | |
2c00a5a8 | 1318 | err.span_label(span, msg); |
8faf50e0 | 1319 | err.buffer(&mut self.errors_buffer); |
ff7c6d11 XL |
1320 | } |
1321 | } | |
1322 | ||
8faf50e0 XL |
1323 | pub(super) struct IncludingDowncast(bool); |
1324 | ||
0bf4aa26 XL |
1325 | /// Which case a StorageDeadOrDrop is for. |
1326 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
1327 | enum StorageDeadOrDrop<'tcx> { | |
1328 | LocalStorageDead, | |
1329 | BoxedStorageDead, | |
1330 | Destructor(ty::Ty<'tcx>), | |
1331 | } | |
1332 | ||
ff7c6d11 | 1333 | impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { |
0bf4aa26 XL |
1334 | |
1335 | /// Adds a suggestion when a closure is invoked twice with a moved variable. | |
1336 | /// | |
1337 | /// ```text | |
1338 | /// note: closure cannot be invoked more than once because it moves the variable `dict` out of | |
1339 | /// its environment | |
1340 | /// --> $DIR/issue-42065.rs:16:29 | |
1341 | /// | | |
1342 | /// LL | for (key, value) in dict { | |
1343 | /// | ^^^^ | |
1344 | /// ``` | |
1345 | pub(super) fn add_closure_invoked_twice_with_moved_variable_suggestion( | |
1346 | &self, | |
1347 | location: Location, | |
1348 | place: &Place<'tcx>, | |
1349 | diag: &mut DiagnosticBuilder<'_>, | |
1350 | ) { | |
1351 | let mut target = place.local(); | |
1352 | debug!( | |
1353 | "add_closure_invoked_twice_with_moved_variable_suggestion: location={:?} place={:?} \ | |
1354 | target={:?}", | |
1355 | location, place, target, | |
1356 | ); | |
1357 | for stmt in &self.mir[location.block].statements[location.statement_index..] { | |
1358 | debug!( | |
1359 | "add_closure_invoked_twice_with_moved_variable_suggestion: stmt={:?} \ | |
1360 | target={:?}", | |
1361 | stmt, target, | |
1362 | ); | |
1363 | if let StatementKind::Assign(into, box Rvalue::Use(from)) = &stmt.kind { | |
1364 | debug!( | |
1365 | "add_closure_invoked_twice_with_moved_variable_suggestion: into={:?} \ | |
1366 | from={:?}", | |
1367 | into, from, | |
1368 | ); | |
1369 | match from { | |
1370 | Operand::Copy(ref place) | | |
1371 | Operand::Move(ref place) if target == place.local() => | |
1372 | target = into.local(), | |
1373 | _ => {}, | |
1374 | } | |
1375 | } | |
1376 | } | |
1377 | ||
1378 | ||
1379 | let terminator = self.mir[location.block].terminator(); | |
1380 | debug!( | |
1381 | "add_closure_invoked_twice_with_moved_variable_suggestion: terminator={:?}", | |
1382 | terminator, | |
1383 | ); | |
1384 | if let TerminatorKind::Call { | |
1385 | func: Operand::Constant(box Constant { | |
1386 | literal: ty::Const { ty: &ty::TyS { sty: ty::TyKind::FnDef(id, _), .. }, .. }, | |
1387 | .. | |
1388 | }), | |
1389 | args, | |
1390 | .. | |
1391 | } = &terminator.kind { | |
1392 | debug!("add_closure_invoked_twice_with_moved_variable_suggestion: id={:?}", id); | |
1393 | if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() { | |
1394 | let closure = match args.first() { | |
1395 | Some(Operand::Copy(ref place)) | | |
1396 | Some(Operand::Move(ref place)) if target == place.local() => | |
1397 | place.local().unwrap(), | |
1398 | _ => return, | |
1399 | }; | |
1400 | debug!( | |
1401 | "add_closure_invoked_twice_with_moved_variable_suggestion: closure={:?}", | |
1402 | closure, | |
1403 | ); | |
1404 | ||
1405 | if let ty::TyKind::Closure(did, _substs) = self.mir.local_decls[closure].ty.sty { | |
1406 | let node_id = match self.infcx.tcx.hir.as_local_node_id(did) { | |
1407 | Some(node_id) => node_id, | |
1408 | _ => return, | |
1409 | }; | |
1410 | let hir_id = self.infcx.tcx.hir.node_to_hir_id(node_id); | |
1411 | ||
1412 | if let Some(( | |
1413 | span, name | |
1414 | )) = self.infcx.tcx.typeck_tables_of(did).closure_kind_origins().get(hir_id) { | |
1415 | diag.span_note( | |
1416 | *span, | |
1417 | &format!( | |
1418 | "closure cannot be invoked more than once because it \ | |
1419 | moves the variable `{}` out of its environment", | |
1420 | name, | |
1421 | ), | |
1422 | ); | |
1423 | } | |
1424 | } | |
1425 | } | |
1426 | } | |
1427 | } | |
1428 | ||
1429 | /// End-user visible description of `place` if one can be found. If the | |
1430 | /// place is a temporary for instance, None will be returned. | |
ff7c6d11 | 1431 | pub(super) fn describe_place(&self, place: &Place<'tcx>) -> Option<String> { |
8faf50e0 XL |
1432 | self.describe_place_with_options(place, IncludingDowncast(false)) |
1433 | } | |
1434 | ||
0bf4aa26 XL |
1435 | /// End-user visible description of `place` if one can be found. If the |
1436 | /// place is a temporary for instance, None will be returned. | |
1437 | /// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is | |
1438 | /// `Downcast` and `IncludingDowncast` is true | |
8faf50e0 XL |
1439 | pub(super) fn describe_place_with_options( |
1440 | &self, | |
1441 | place: &Place<'tcx>, | |
1442 | including_downcast: IncludingDowncast, | |
1443 | ) -> Option<String> { | |
ff7c6d11 | 1444 | let mut buf = String::new(); |
8faf50e0 | 1445 | match self.append_place_to_string(place, &mut buf, false, &including_downcast) { |
ff7c6d11 XL |
1446 | Ok(()) => Some(buf), |
1447 | Err(()) => None, | |
1448 | } | |
1449 | } | |
1450 | ||
0bf4aa26 | 1451 | /// Appends end-user visible description of `place` to `buf`. |
ff7c6d11 XL |
1452 | fn append_place_to_string( |
1453 | &self, | |
1454 | place: &Place<'tcx>, | |
1455 | buf: &mut String, | |
1456 | mut autoderef: bool, | |
8faf50e0 | 1457 | including_downcast: &IncludingDowncast, |
ff7c6d11 XL |
1458 | ) -> Result<(), ()> { |
1459 | match *place { | |
8faf50e0 XL |
1460 | Place::Promoted(_) => { |
1461 | buf.push_str("promoted"); | |
1462 | } | |
ff7c6d11 XL |
1463 | Place::Local(local) => { |
1464 | self.append_local_to_string(local, buf)?; | |
1465 | } | |
1466 | Place::Static(ref static_) => { | |
0bf4aa26 | 1467 | buf.push_str(&self.infcx.tcx.item_name(static_.def_id).to_string()); |
ff7c6d11 XL |
1468 | } |
1469 | Place::Projection(ref proj) => { | |
1470 | match proj.elem { | |
1471 | ProjectionElem::Deref => { | |
b7449926 | 1472 | let upvar_field_projection = |
0bf4aa26 | 1473 | place.is_upvar_field_projection(self.mir, &self.infcx.tcx); |
8faf50e0 | 1474 | if let Some(field) = upvar_field_projection { |
ff7c6d11 XL |
1475 | let var_index = field.index(); |
1476 | let name = self.mir.upvar_decls[var_index].debug_name.to_string(); | |
1477 | if self.mir.upvar_decls[var_index].by_ref { | |
1478 | buf.push_str(&name); | |
1479 | } else { | |
1480 | buf.push_str(&format!("*{}", &name)); | |
1481 | } | |
1482 | } else { | |
1483 | if autoderef { | |
8faf50e0 XL |
1484 | self.append_place_to_string( |
1485 | &proj.base, | |
1486 | buf, | |
1487 | autoderef, | |
1488 | &including_downcast, | |
1489 | )?; | |
1490 | } else if let Place::Local(local) = proj.base { | |
b7449926 XL |
1491 | if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) = |
1492 | self.mir.local_decls[local].is_user_variable | |
1493 | { | |
8faf50e0 XL |
1494 | self.append_place_to_string( |
1495 | &proj.base, | |
1496 | buf, | |
1497 | autoderef, | |
1498 | &including_downcast, | |
1499 | )?; | |
1500 | } else { | |
1501 | buf.push_str(&"*"); | |
1502 | self.append_place_to_string( | |
1503 | &proj.base, | |
1504 | buf, | |
1505 | autoderef, | |
1506 | &including_downcast, | |
1507 | )?; | |
1508 | } | |
ff7c6d11 XL |
1509 | } else { |
1510 | buf.push_str(&"*"); | |
8faf50e0 XL |
1511 | self.append_place_to_string( |
1512 | &proj.base, | |
1513 | buf, | |
1514 | autoderef, | |
1515 | &including_downcast, | |
1516 | )?; | |
ff7c6d11 XL |
1517 | } |
1518 | } | |
1519 | } | |
1520 | ProjectionElem::Downcast(..) => { | |
8faf50e0 XL |
1521 | self.append_place_to_string( |
1522 | &proj.base, | |
1523 | buf, | |
1524 | autoderef, | |
1525 | &including_downcast, | |
1526 | )?; | |
1527 | if including_downcast.0 { | |
1528 | return Err(()); | |
1529 | } | |
ff7c6d11 XL |
1530 | } |
1531 | ProjectionElem::Field(field, _ty) => { | |
1532 | autoderef = true; | |
1533 | ||
b7449926 | 1534 | let upvar_field_projection = |
0bf4aa26 | 1535 | place.is_upvar_field_projection(self.mir, &self.infcx.tcx); |
8faf50e0 | 1536 | if let Some(field) = upvar_field_projection { |
ff7c6d11 XL |
1537 | let var_index = field.index(); |
1538 | let name = self.mir.upvar_decls[var_index].debug_name.to_string(); | |
1539 | buf.push_str(&name); | |
1540 | } else { | |
1541 | let field_name = self.describe_field(&proj.base, field); | |
8faf50e0 XL |
1542 | self.append_place_to_string( |
1543 | &proj.base, | |
1544 | buf, | |
1545 | autoderef, | |
1546 | &including_downcast, | |
1547 | )?; | |
ff7c6d11 XL |
1548 | buf.push_str(&format!(".{}", field_name)); |
1549 | } | |
1550 | } | |
1551 | ProjectionElem::Index(index) => { | |
1552 | autoderef = true; | |
1553 | ||
8faf50e0 XL |
1554 | self.append_place_to_string( |
1555 | &proj.base, | |
1556 | buf, | |
1557 | autoderef, | |
1558 | &including_downcast, | |
1559 | )?; | |
ff7c6d11 | 1560 | buf.push_str("["); |
b7449926 | 1561 | if self.append_local_to_string(index, buf).is_err() { |
ff7c6d11 XL |
1562 | buf.push_str(".."); |
1563 | } | |
1564 | buf.push_str("]"); | |
1565 | } | |
1566 | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { | |
1567 | autoderef = true; | |
1568 | // Since it isn't possible to borrow an element on a particular index and | |
1569 | // then use another while the borrow is held, don't output indices details | |
1570 | // to avoid confusing the end-user | |
8faf50e0 XL |
1571 | self.append_place_to_string( |
1572 | &proj.base, | |
1573 | buf, | |
1574 | autoderef, | |
1575 | &including_downcast, | |
1576 | )?; | |
ff7c6d11 XL |
1577 | buf.push_str(&"[..]"); |
1578 | } | |
1579 | }; | |
1580 | } | |
1581 | } | |
1582 | ||
1583 | Ok(()) | |
1584 | } | |
1585 | ||
0bf4aa26 XL |
1586 | /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have |
1587 | /// a name, then `Err` is returned | |
ff7c6d11 XL |
1588 | fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> { |
1589 | let local = &self.mir.local_decls[local_index]; | |
1590 | match local.name { | |
1591 | Some(name) => { | |
8faf50e0 | 1592 | buf.push_str(&name.to_string()); |
ff7c6d11 XL |
1593 | Ok(()) |
1594 | } | |
1595 | None => Err(()), | |
1596 | } | |
1597 | } | |
1598 | ||
0bf4aa26 | 1599 | /// End-user visible description of the `field`nth field of `base` |
ff7c6d11 XL |
1600 | fn describe_field(&self, base: &Place, field: Field) -> String { |
1601 | match *base { | |
1602 | Place::Local(local) => { | |
1603 | let local = &self.mir.local_decls[local]; | |
1604 | self.describe_field_from_ty(&local.ty, field) | |
1605 | } | |
8faf50e0 | 1606 | Place::Promoted(ref prom) => self.describe_field_from_ty(&prom.1, field), |
ff7c6d11 XL |
1607 | Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field), |
1608 | Place::Projection(ref proj) => match proj.elem { | |
1609 | ProjectionElem::Deref => self.describe_field(&proj.base, field), | |
0bf4aa26 XL |
1610 | ProjectionElem::Downcast(def, variant_index) => |
1611 | def.variants[variant_index].fields[field.index()].ident.to_string(), | |
ff7c6d11 XL |
1612 | ProjectionElem::Field(_, field_type) => { |
1613 | self.describe_field_from_ty(&field_type, field) | |
1614 | } | |
0531ce1d XL |
1615 | ProjectionElem::Index(..) |
1616 | | ProjectionElem::ConstantIndex { .. } | |
1617 | | ProjectionElem::Subslice { .. } => { | |
0bf4aa26 | 1618 | self.describe_field(&proj.base, field) |
ff7c6d11 XL |
1619 | } |
1620 | }, | |
1621 | } | |
1622 | } | |
1623 | ||
0bf4aa26 | 1624 | /// End-user visible description of the `field_index`nth field of `ty` |
ff7c6d11 XL |
1625 | fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String { |
1626 | if ty.is_box() { | |
1627 | // If the type is a box, the field is described from the boxed type | |
1628 | self.describe_field_from_ty(&ty.boxed_ty(), field) | |
1629 | } else { | |
1630 | match ty.sty { | |
b7449926 | 1631 | ty::Adt(def, _) => if def.is_enum() { |
8faf50e0 | 1632 | field.index().to_string() |
ff7c6d11 | 1633 | } else { |
b7449926 XL |
1634 | def.non_enum_variant().fields[field.index()] |
1635 | .ident | |
1636 | .to_string() | |
ff7c6d11 | 1637 | }, |
b7449926 XL |
1638 | ty::Tuple(_) => field.index().to_string(), |
1639 | ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { | |
94b46f34 | 1640 | self.describe_field_from_ty(&ty, field) |
ff7c6d11 | 1641 | } |
b7449926 XL |
1642 | ty::Array(ty, _) | ty::Slice(ty) => self.describe_field_from_ty(&ty, field), |
1643 | ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { | |
ff7c6d11 XL |
1644 | // Convert the def-id into a node-id. node-ids are only valid for |
1645 | // the local code in the current crate, so this returns an `Option` in case | |
1646 | // the closure comes from another crate. But in that case we wouldn't | |
1647 | // be borrowck'ing it, so we can just unwrap: | |
0bf4aa26 XL |
1648 | let node_id = self.infcx.tcx.hir.as_local_node_id(def_id).unwrap(); |
1649 | let freevar = self.infcx | |
1650 | .tcx | |
1651 | .with_freevars(node_id, |fv| fv[field.index()]); | |
ff7c6d11 | 1652 | |
0bf4aa26 | 1653 | self.infcx.tcx.hir.name(freevar.var_id()).to_string() |
ff7c6d11 XL |
1654 | } |
1655 | _ => { | |
1656 | // Might need a revision when the fields in trait RFC is implemented | |
1657 | // (https://github.com/rust-lang/rfcs/pull/1546) | |
1658 | bug!( | |
1659 | "End-user description not implemented for field access on `{:?}`", | |
1660 | ty.sty | |
1661 | ); | |
1662 | } | |
1663 | } | |
1664 | } | |
1665 | } | |
1666 | ||
0bf4aa26 | 1667 | /// Retrieve type of a place for the current MIR representation |
ff7c6d11 XL |
1668 | fn retrieve_type_for_place(&self, place: &Place<'tcx>) -> Option<ty::Ty> { |
1669 | match place { | |
1670 | Place::Local(local) => { | |
1671 | let local = &self.mir.local_decls[*local]; | |
1672 | Some(local.ty) | |
0531ce1d | 1673 | } |
8faf50e0 | 1674 | Place::Promoted(ref prom) => Some(prom.1), |
ff7c6d11 | 1675 | Place::Static(ref st) => Some(st.ty), |
0531ce1d XL |
1676 | Place::Projection(ref proj) => match proj.elem { |
1677 | ProjectionElem::Field(_, ty) => Some(ty), | |
1678 | _ => None, | |
ff7c6d11 XL |
1679 | }, |
1680 | } | |
1681 | } | |
b7449926 XL |
1682 | |
1683 | /// Check if a place is a thread-local static. | |
1684 | pub fn is_place_thread_local(&self, place: &Place<'tcx>) -> bool { | |
1685 | if let Place::Static(statik) = place { | |
0bf4aa26 | 1686 | let attrs = self.infcx.tcx.get_attrs(statik.def_id); |
b7449926 XL |
1687 | let is_thread_local = attrs.iter().any(|attr| attr.check_name("thread_local")); |
1688 | ||
1689 | debug!( | |
1690 | "is_place_thread_local: attrs={:?} is_thread_local={:?}", | |
1691 | attrs, is_thread_local | |
1692 | ); | |
1693 | is_thread_local | |
1694 | } else { | |
1695 | debug!("is_place_thread_local: no"); | |
1696 | false | |
1697 | } | |
1698 | } | |
0bf4aa26 XL |
1699 | |
1700 | fn classify_drop_access_kind(&self, place: &Place<'tcx>) -> StorageDeadOrDrop<'tcx> { | |
1701 | let tcx = self.infcx.tcx; | |
1702 | match place { | |
1703 | Place::Local(_) | Place::Static(_) | Place::Promoted(_) => { | |
1704 | StorageDeadOrDrop::LocalStorageDead | |
1705 | } | |
1706 | Place::Projection(box PlaceProjection { base, elem }) => { | |
1707 | let base_access = self.classify_drop_access_kind(base); | |
1708 | match elem { | |
1709 | ProjectionElem::Deref => match base_access { | |
1710 | StorageDeadOrDrop::LocalStorageDead | |
1711 | | StorageDeadOrDrop::BoxedStorageDead => { | |
1712 | assert!( | |
1713 | base.ty(self.mir, tcx).to_ty(tcx).is_box(), | |
1714 | "Drop of value behind a reference or raw pointer" | |
1715 | ); | |
1716 | StorageDeadOrDrop::BoxedStorageDead | |
1717 | } | |
1718 | StorageDeadOrDrop::Destructor(_) => base_access, | |
1719 | }, | |
1720 | ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => { | |
1721 | let base_ty = base.ty(self.mir, tcx).to_ty(tcx); | |
1722 | match base_ty.sty { | |
1723 | ty::Adt(def, _) if def.has_dtor(tcx) => { | |
1724 | // Report the outermost adt with a destructor | |
1725 | match base_access { | |
1726 | StorageDeadOrDrop::Destructor(_) => base_access, | |
1727 | StorageDeadOrDrop::LocalStorageDead | |
1728 | | StorageDeadOrDrop::BoxedStorageDead => { | |
1729 | StorageDeadOrDrop::Destructor(base_ty) | |
1730 | } | |
1731 | } | |
1732 | } | |
1733 | _ => base_access, | |
1734 | } | |
1735 | } | |
1736 | ||
1737 | ProjectionElem::ConstantIndex { .. } | |
1738 | | ProjectionElem::Subslice { .. } | |
1739 | | ProjectionElem::Index(_) => base_access, | |
1740 | } | |
1741 | } | |
1742 | } | |
1743 | } | |
1744 | ||
1745 | /// Annotate argument and return type of function and closure with (synthesized) lifetime for | |
1746 | /// borrow of local value that does not live long enough. | |
1747 | fn annotate_argument_and_return_for_borrow( | |
1748 | &self, | |
1749 | borrow: &BorrowData<'tcx>, | |
1750 | ) -> Option<AnnotatedBorrowFnSignature> { | |
1751 | // Define a fallback for when we can't match a closure. | |
1752 | let fallback = || { | |
1753 | let is_closure = self.infcx.tcx.is_closure(self.mir_def_id); | |
1754 | if is_closure { | |
1755 | None | |
1756 | } else { | |
1757 | let ty = self.infcx.tcx.type_of(self.mir_def_id); | |
1758 | match ty.sty { | |
1759 | ty::TyKind::FnDef(_, _) | ty::TyKind::FnPtr(_) => self.annotate_fn_sig( | |
1760 | self.mir_def_id, | |
1761 | self.infcx.tcx.fn_sig(self.mir_def_id), | |
1762 | ), | |
1763 | _ => None, | |
1764 | } | |
1765 | } | |
1766 | }; | |
1767 | ||
1768 | // In order to determine whether we need to annotate, we need to check whether the reserve | |
1769 | // place was an assignment into a temporary. | |
1770 | // | |
1771 | // If it was, we check whether or not that temporary is eventually assigned into the return | |
1772 | // place. If it was, we can add annotations about the function's return type and arguments | |
1773 | // and it'll make sense. | |
1774 | let location = borrow.reserve_location; | |
1775 | debug!( | |
1776 | "annotate_argument_and_return_for_borrow: location={:?}", | |
1777 | location | |
1778 | ); | |
1779 | if let Some(&Statement { kind: StatementKind::Assign(ref reservation, _), ..}) | |
1780 | = &self.mir[location.block].statements.get(location.statement_index) | |
1781 | { | |
1782 | debug!( | |
1783 | "annotate_argument_and_return_for_borrow: reservation={:?}", | |
1784 | reservation | |
1785 | ); | |
1786 | // Check that the initial assignment of the reserve location is into a temporary. | |
1787 | let mut target = *match reservation { | |
1788 | Place::Local(local) if self.mir.local_kind(*local) == LocalKind::Temp => local, | |
1789 | _ => return None, | |
1790 | }; | |
1791 | ||
1792 | // Next, look through the rest of the block, checking if we are assigning the | |
1793 | // `target` (that is, the place that contains our borrow) to anything. | |
1794 | let mut annotated_closure = None; | |
1795 | for stmt in &self.mir[location.block].statements[location.statement_index + 1..] { | |
1796 | debug!( | |
1797 | "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}", | |
1798 | target, stmt | |
1799 | ); | |
1800 | if let StatementKind::Assign(Place::Local(assigned_to), box rvalue) = &stmt.kind | |
1801 | { | |
1802 | debug!( | |
1803 | "annotate_argument_and_return_for_borrow: assigned_to={:?} \ | |
1804 | rvalue={:?}", | |
1805 | assigned_to, rvalue | |
1806 | ); | |
1807 | // Check if our `target` was captured by a closure. | |
1808 | if let Rvalue::Aggregate( | |
1809 | box AggregateKind::Closure(def_id, substs), | |
1810 | operands, | |
1811 | ) = rvalue | |
1812 | { | |
1813 | for operand in operands { | |
1814 | let assigned_from = match operand { | |
1815 | Operand::Copy(assigned_from) | Operand::Move(assigned_from) => { | |
1816 | assigned_from | |
1817 | } | |
1818 | _ => continue, | |
1819 | }; | |
1820 | debug!( | |
1821 | "annotate_argument_and_return_for_borrow: assigned_from={:?}", | |
1822 | assigned_from | |
1823 | ); | |
1824 | ||
1825 | // Find the local from the operand. | |
1826 | let assigned_from_local = match assigned_from.local() { | |
1827 | Some(local) => local, | |
1828 | None => continue, | |
1829 | }; | |
1830 | ||
1831 | if assigned_from_local != target { | |
1832 | continue; | |
1833 | } | |
1834 | ||
1835 | // If a closure captured our `target` and then assigned | |
1836 | // into a place then we should annotate the closure in | |
1837 | // case it ends up being assigned into the return place. | |
1838 | annotated_closure = self.annotate_fn_sig( | |
1839 | *def_id, | |
1840 | self.infcx.closure_sig(*def_id, *substs), | |
1841 | ); | |
1842 | debug!( | |
1843 | "annotate_argument_and_return_for_borrow: \ | |
1844 | annotated_closure={:?} assigned_from_local={:?} \ | |
1845 | assigned_to={:?}", | |
1846 | annotated_closure, assigned_from_local, assigned_to | |
1847 | ); | |
1848 | ||
1849 | if *assigned_to == mir::RETURN_PLACE { | |
1850 | // If it was assigned directly into the return place, then | |
1851 | // return now. | |
1852 | return annotated_closure; | |
1853 | } else { | |
1854 | // Otherwise, update the target. | |
1855 | target = *assigned_to; | |
1856 | } | |
1857 | } | |
1858 | ||
1859 | // If none of our closure's operands matched, then skip to the next | |
1860 | // statement. | |
1861 | continue; | |
1862 | } | |
1863 | ||
1864 | // Otherwise, look at other types of assignment. | |
1865 | let assigned_from = match rvalue { | |
1866 | Rvalue::Ref(_, _, assigned_from) => assigned_from, | |
1867 | Rvalue::Use(operand) => match operand { | |
1868 | Operand::Copy(assigned_from) | Operand::Move(assigned_from) => { | |
1869 | assigned_from | |
1870 | } | |
1871 | _ => continue, | |
1872 | }, | |
1873 | _ => continue, | |
1874 | }; | |
1875 | debug!( | |
1876 | "annotate_argument_and_return_for_borrow: \ | |
1877 | assigned_from={:?}", | |
1878 | assigned_from, | |
1879 | ); | |
1880 | ||
1881 | // Find the local from the rvalue. | |
1882 | let assigned_from_local = match assigned_from.local() { | |
1883 | Some(local) => local, | |
1884 | None => continue, | |
1885 | }; | |
1886 | debug!( | |
1887 | "annotate_argument_and_return_for_borrow: \ | |
1888 | assigned_from_local={:?}", | |
1889 | assigned_from_local, | |
1890 | ); | |
1891 | ||
1892 | // Check if our local matches the target - if so, we've assigned our | |
1893 | // borrow to a new place. | |
1894 | if assigned_from_local != target { | |
1895 | continue; | |
1896 | } | |
1897 | ||
1898 | // If we assigned our `target` into a new place, then we should | |
1899 | // check if it was the return place. | |
1900 | debug!( | |
1901 | "annotate_argument_and_return_for_borrow: \ | |
1902 | assigned_from_local={:?} assigned_to={:?}", | |
1903 | assigned_from_local, assigned_to | |
1904 | ); | |
1905 | if *assigned_to == mir::RETURN_PLACE { | |
1906 | // If it was then return the annotated closure if there was one, | |
1907 | // else, annotate this function. | |
1908 | return annotated_closure.or_else(fallback); | |
1909 | } | |
1910 | ||
1911 | // If we didn't assign into the return place, then we just update | |
1912 | // the target. | |
1913 | target = *assigned_to; | |
1914 | } | |
1915 | } | |
1916 | ||
1917 | // Check the terminator if we didn't find anything in the statements. | |
1918 | let terminator = &self.mir[location.block].terminator(); | |
1919 | debug!( | |
1920 | "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}", | |
1921 | target, terminator | |
1922 | ); | |
1923 | if let TerminatorKind::Call { | |
1924 | destination: Some((Place::Local(assigned_to), _)), | |
1925 | args, | |
1926 | .. | |
1927 | } = &terminator.kind | |
1928 | { | |
1929 | debug!( | |
1930 | "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}", | |
1931 | assigned_to, args | |
1932 | ); | |
1933 | for operand in args { | |
1934 | let assigned_from = match operand { | |
1935 | Operand::Copy(assigned_from) | Operand::Move(assigned_from) => { | |
1936 | assigned_from | |
1937 | } | |
1938 | _ => continue, | |
1939 | }; | |
1940 | debug!( | |
1941 | "annotate_argument_and_return_for_borrow: assigned_from={:?}", | |
1942 | assigned_from, | |
1943 | ); | |
1944 | ||
1945 | if let Some(assigned_from_local) = assigned_from.local() { | |
1946 | debug!( | |
1947 | "annotate_argument_and_return_for_borrow: assigned_from_local={:?}", | |
1948 | assigned_from_local, | |
1949 | ); | |
1950 | ||
1951 | if *assigned_to == mir::RETURN_PLACE && assigned_from_local == target { | |
1952 | return annotated_closure.or_else(fallback); | |
1953 | } | |
1954 | } | |
1955 | } | |
1956 | } | |
1957 | } | |
1958 | ||
1959 | // If we haven't found an assignment into the return place, then we need not add | |
1960 | // any annotations. | |
1961 | debug!("annotate_argument_and_return_for_borrow: none found"); | |
1962 | None | |
1963 | } | |
1964 | ||
1965 | /// Annotate the first argument and return type of a function signature if they are | |
1966 | /// references. | |
1967 | fn annotate_fn_sig( | |
1968 | &self, | |
1969 | did: DefId, | |
1970 | sig: ty::PolyFnSig<'tcx>, | |
1971 | ) -> Option<AnnotatedBorrowFnSignature> { | |
1972 | debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig); | |
1973 | let is_closure = self.infcx.tcx.is_closure(did); | |
1974 | let fn_node_id = self.infcx.tcx.hir.as_local_node_id(did)?; | |
1975 | let fn_decl = self.infcx.tcx.hir.fn_decl(fn_node_id)?; | |
1976 | ||
1977 | // We need to work out which arguments to highlight. We do this by looking | |
1978 | // at the return type, where there are three cases: | |
1979 | // | |
1980 | // 1. If there are named arguments, then we should highlight the return type and | |
1981 | // highlight any of the arguments that are also references with that lifetime. | |
1982 | // If there are no arguments that have the same lifetime as the return type, | |
1983 | // then don't highlight anything. | |
1984 | // 2. The return type is a reference with an anonymous lifetime. If this is | |
1985 | // the case, then we can take advantage of (and teach) the lifetime elision | |
1986 | // rules. | |
1987 | // | |
1988 | // We know that an error is being reported. So the arguments and return type | |
1989 | // must satisfy the elision rules. Therefore, if there is a single argument | |
1990 | // then that means the return type and first (and only) argument have the same | |
1991 | // lifetime and the borrow isn't meeting that, we can highlight the argument | |
1992 | // and return type. | |
1993 | // | |
1994 | // If there are multiple arguments then the first argument must be self (else | |
1995 | // it would not satisfy the elision rules), so we can highlight self and the | |
1996 | // return type. | |
1997 | // 3. The return type is not a reference. In this case, we don't highlight | |
1998 | // anything. | |
1999 | let return_ty = sig.output(); | |
2000 | match return_ty.skip_binder().sty { | |
2001 | ty::TyKind::Ref(return_region, _, _) if return_region.has_name() && !is_closure => { | |
2002 | // This is case 1 from above, return type is a named reference so we need to | |
2003 | // search for relevant arguments. | |
2004 | let mut arguments = Vec::new(); | |
2005 | for (index, argument) in sig.inputs().skip_binder().iter().enumerate() { | |
2006 | if let ty::TyKind::Ref(argument_region, _, _) = argument.sty { | |
2007 | if argument_region == return_region { | |
2008 | // Need to use the `rustc::ty` types to compare against the | |
2009 | // `return_region`. Then use the `rustc::hir` type to get only | |
2010 | // the lifetime span. | |
2011 | if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].node { | |
2012 | // With access to the lifetime, we can get | |
2013 | // the span of it. | |
2014 | arguments.push((*argument, lifetime.span)); | |
2015 | } else { | |
2016 | bug!("ty type is a ref but hir type is not"); | |
2017 | } | |
2018 | } | |
2019 | } | |
2020 | } | |
2021 | ||
2022 | // We need to have arguments. This shouldn't happen, but it's worth checking. | |
2023 | if arguments.is_empty() { | |
2024 | return None; | |
2025 | } | |
2026 | ||
2027 | // We use a mix of the HIR and the Ty types to get information | |
2028 | // as the HIR doesn't have full types for closure arguments. | |
2029 | let return_ty = *sig.output().skip_binder(); | |
2030 | let mut return_span = fn_decl.output.span(); | |
2031 | if let hir::FunctionRetTy::Return(ty) = fn_decl.output { | |
2032 | if let hir::TyKind::Rptr(lifetime, _) = ty.into_inner().node { | |
2033 | return_span = lifetime.span; | |
2034 | } | |
2035 | } | |
2036 | ||
2037 | Some(AnnotatedBorrowFnSignature::NamedFunction { | |
2038 | arguments, | |
2039 | return_ty, | |
2040 | return_span, | |
2041 | }) | |
2042 | } | |
2043 | ty::TyKind::Ref(_, _, _) if is_closure => { | |
2044 | // This is case 2 from above but only for closures, return type is anonymous | |
2045 | // reference so we select | |
2046 | // the first argument. | |
2047 | let argument_span = fn_decl.inputs.first()?.span; | |
2048 | let argument_ty = sig.inputs().skip_binder().first()?; | |
2049 | ||
2050 | // Closure arguments are wrapped in a tuple, so we need to get the first | |
2051 | // from that. | |
2052 | if let ty::TyKind::Tuple(elems) = argument_ty.sty { | |
2053 | let argument_ty = elems.first()?; | |
2054 | if let ty::TyKind::Ref(_, _, _) = argument_ty.sty { | |
2055 | return Some(AnnotatedBorrowFnSignature::Closure { | |
2056 | argument_ty, | |
2057 | argument_span, | |
2058 | }); | |
2059 | } | |
2060 | } | |
2061 | ||
2062 | None | |
2063 | } | |
2064 | ty::TyKind::Ref(_, _, _) => { | |
2065 | // This is also case 2 from above but for functions, return type is still an | |
2066 | // anonymous reference so we select the first argument. | |
2067 | let argument_span = fn_decl.inputs.first()?.span; | |
2068 | let argument_ty = sig.inputs().skip_binder().first()?; | |
2069 | ||
2070 | let return_span = fn_decl.output.span(); | |
2071 | let return_ty = *sig.output().skip_binder(); | |
2072 | ||
2073 | // We expect the first argument to be a reference. | |
2074 | match argument_ty.sty { | |
2075 | ty::TyKind::Ref(_, _, _) => {} | |
2076 | _ => return None, | |
2077 | } | |
2078 | ||
2079 | Some(AnnotatedBorrowFnSignature::AnonymousFunction { | |
2080 | argument_ty, | |
2081 | argument_span, | |
2082 | return_ty, | |
2083 | return_span, | |
2084 | }) | |
2085 | } | |
2086 | _ => { | |
2087 | // This is case 3 from above, return type is not a reference so don't highlight | |
2088 | // anything. | |
2089 | None | |
2090 | } | |
2091 | } | |
2092 | } | |
2093 | } | |
2094 | ||
2095 | #[derive(Debug)] | |
2096 | enum AnnotatedBorrowFnSignature<'tcx> { | |
2097 | NamedFunction { | |
2098 | arguments: Vec<(ty::Ty<'tcx>, Span)>, | |
2099 | return_ty: ty::Ty<'tcx>, | |
2100 | return_span: Span, | |
2101 | }, | |
2102 | AnonymousFunction { | |
2103 | argument_ty: ty::Ty<'tcx>, | |
2104 | argument_span: Span, | |
2105 | return_ty: ty::Ty<'tcx>, | |
2106 | return_span: Span, | |
2107 | }, | |
2108 | Closure { | |
2109 | argument_ty: ty::Ty<'tcx>, | |
2110 | argument_span: Span, | |
2111 | }, | |
2112 | } | |
2113 | ||
2114 | impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { | |
2115 | /// Annotate the provided diagnostic with information about borrow from the fn signature that | |
2116 | /// helps explain. | |
2117 | fn emit(&self, diag: &mut DiagnosticBuilder<'_>) -> String { | |
2118 | match self { | |
2119 | AnnotatedBorrowFnSignature::Closure { | |
2120 | argument_ty, | |
2121 | argument_span, | |
2122 | } => { | |
2123 | diag.span_label( | |
2124 | *argument_span, | |
2125 | format!("has type `{}`", self.get_name_for_ty(argument_ty, 0)), | |
2126 | ); | |
2127 | ||
2128 | self.get_region_name_for_ty(argument_ty, 0) | |
2129 | } | |
2130 | AnnotatedBorrowFnSignature::AnonymousFunction { | |
2131 | argument_ty, | |
2132 | argument_span, | |
2133 | return_ty, | |
2134 | return_span, | |
2135 | } => { | |
2136 | let argument_ty_name = self.get_name_for_ty(argument_ty, 0); | |
2137 | diag.span_label(*argument_span, format!("has type `{}`", argument_ty_name)); | |
2138 | ||
2139 | let return_ty_name = self.get_name_for_ty(return_ty, 0); | |
2140 | let types_equal = return_ty_name == argument_ty_name; | |
2141 | diag.span_label( | |
2142 | *return_span, | |
2143 | format!( | |
2144 | "{}has type `{}`", | |
2145 | if types_equal { "also " } else { "" }, | |
2146 | return_ty_name, | |
2147 | ), | |
2148 | ); | |
2149 | ||
2150 | diag.note( | |
2151 | "argument and return type have the same lifetime due to lifetime elision rules", | |
2152 | ); | |
2153 | diag.note( | |
2154 | "to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch10-03-\ | |
2155 | lifetime-syntax.html#lifetime-elision>", | |
2156 | ); | |
2157 | ||
2158 | self.get_region_name_for_ty(return_ty, 0) | |
2159 | } | |
2160 | AnnotatedBorrowFnSignature::NamedFunction { | |
2161 | arguments, | |
2162 | return_ty, | |
2163 | return_span, | |
2164 | } => { | |
2165 | // Region of return type and arguments checked to be the same earlier. | |
2166 | let region_name = self.get_region_name_for_ty(return_ty, 0); | |
2167 | for (_, argument_span) in arguments { | |
2168 | diag.span_label(*argument_span, format!("has lifetime `{}`", region_name)); | |
2169 | } | |
2170 | ||
2171 | diag.span_label( | |
2172 | *return_span, | |
2173 | format!("also has lifetime `{}`", region_name,), | |
2174 | ); | |
2175 | ||
2176 | diag.help(&format!( | |
2177 | "use data from the highlighted arguments which match the `{}` lifetime of \ | |
2178 | the return type", | |
2179 | region_name, | |
2180 | )); | |
2181 | ||
2182 | region_name | |
2183 | } | |
2184 | } | |
2185 | } | |
2186 | ||
2187 | /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime | |
2188 | /// name where required. | |
2189 | fn get_name_for_ty(&self, ty: ty::Ty<'tcx>, counter: usize) -> String { | |
2190 | // We need to add synthesized lifetimes where appropriate. We do | |
2191 | // this by hooking into the pretty printer and telling it to label the | |
2192 | // lifetimes without names with the value `'0`. | |
2193 | match ty.sty { | |
2194 | ty::TyKind::Ref(ty::RegionKind::ReLateBound(_, br), _, _) | |
2195 | | ty::TyKind::Ref( | |
2196 | ty::RegionKind::RePlaceholder(ty::Placeholder { name: br, .. }), | |
2197 | _, | |
2198 | _, | |
2199 | ) => with_highlight_region_for_bound_region(*br, counter, || ty.to_string()), | |
2200 | _ => ty.to_string(), | |
2201 | } | |
2202 | } | |
2203 | ||
2204 | /// Return the name of the provided `Ty` (that must be a reference)'s region with a | |
2205 | /// synthesized lifetime name where required. | |
2206 | fn get_region_name_for_ty(&self, ty: ty::Ty<'tcx>, counter: usize) -> String { | |
2207 | match ty.sty { | |
2208 | ty::TyKind::Ref(region, _, _) => match region { | |
2209 | ty::RegionKind::ReLateBound(_, br) | |
2210 | | ty::RegionKind::RePlaceholder(ty::Placeholder { name: br, .. }) => { | |
2211 | with_highlight_region_for_bound_region(*br, counter, || region.to_string()) | |
2212 | } | |
2213 | _ => region.to_string(), | |
2214 | }, | |
2215 | _ => bug!("ty for annotation of borrow region is not a reference"), | |
2216 | } | |
2217 | } | |
b7449926 XL |
2218 | } |
2219 | ||
2220 | // The span(s) associated to a use of a place. | |
2221 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
2222 | pub(super) enum UseSpans { | |
2223 | // The access is caused by capturing a variable for a closure. | |
2224 | ClosureUse { | |
0bf4aa26 XL |
2225 | // This is true if the captured variable was from a generator. |
2226 | is_generator: bool, | |
b7449926 XL |
2227 | // The span of the args of the closure, including the `move` keyword if |
2228 | // it's present. | |
2229 | args_span: Span, | |
2230 | // The span of the first use of the captured variable inside the closure. | |
2231 | var_span: Span, | |
2232 | }, | |
2233 | // This access has a single span associated to it: common case. | |
2234 | OtherUse(Span), | |
2235 | } | |
2236 | ||
2237 | impl UseSpans { | |
2238 | pub(super) fn args_or_use(self) -> Span { | |
2239 | match self { | |
2240 | UseSpans::ClosureUse { | |
2241 | args_span: span, .. | |
2242 | } | |
2243 | | UseSpans::OtherUse(span) => span, | |
2244 | } | |
2245 | } | |
2246 | ||
2247 | pub(super) fn var_or_use(self) -> Span { | |
2248 | match self { | |
2249 | UseSpans::ClosureUse { var_span: span, .. } | UseSpans::OtherUse(span) => span, | |
2250 | } | |
2251 | } | |
2252 | ||
2253 | // Add a span label to the arguments of the closure, if it exists. | |
2254 | pub(super) fn args_span_label(self, err: &mut DiagnosticBuilder, message: impl Into<String>) { | |
2255 | if let UseSpans::ClosureUse { args_span, .. } = self { | |
2256 | err.span_label(args_span, message); | |
2257 | } | |
2258 | } | |
2259 | ||
2260 | // Add a span label to the use of the captured variable, if it exists. | |
2261 | pub(super) fn var_span_label(self, err: &mut DiagnosticBuilder, message: impl Into<String>) { | |
2262 | if let UseSpans::ClosureUse { var_span, .. } = self { | |
2263 | err.span_label(var_span, message); | |
2264 | } | |
2265 | } | |
2266 | ||
0bf4aa26 XL |
2267 | /// Return `false` if this place is not used in a closure. |
2268 | fn for_closure(&self) -> bool { | |
2269 | match *self { | |
2270 | UseSpans::ClosureUse { is_generator, .. } => !is_generator, | |
2271 | _ => false, | |
2272 | } | |
2273 | } | |
2274 | ||
2275 | /// Return `false` if this place is not used in a generator. | |
2276 | fn for_generator(&self) -> bool { | |
2277 | match *self { | |
2278 | UseSpans::ClosureUse { is_generator, .. } => is_generator, | |
2279 | _ => false, | |
2280 | } | |
2281 | } | |
2282 | ||
2283 | /// Describe the span associated with a use of a place. | |
2284 | fn describe(&self) -> String { | |
2285 | match *self { | |
2286 | UseSpans::ClosureUse { is_generator, .. } => if is_generator { | |
2287 | " in generator".to_string() | |
2288 | } else { | |
2289 | " in closure".to_string() | |
2290 | }, | |
2291 | _ => "".to_string(), | |
b7449926 XL |
2292 | } |
2293 | } | |
2294 | ||
2295 | pub(super) fn or_else<F>(self, if_other: F) -> Self | |
2296 | where | |
2297 | F: FnOnce() -> Self, | |
2298 | { | |
2299 | match self { | |
2300 | closure @ UseSpans::ClosureUse { .. } => closure, | |
2301 | UseSpans::OtherUse(_) => if_other(), | |
2302 | } | |
2303 | } | |
2304 | } | |
2305 | ||
2306 | impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |
2307 | /// Finds the spans associated to a move or copy of move_place at location. | |
2308 | pub(super) fn move_spans( | |
2309 | &self, | |
2310 | moved_place: &Place<'tcx>, // Could also be an upvar. | |
2311 | location: Location, | |
2312 | ) -> UseSpans { | |
2313 | use self::UseSpans::*; | |
b7449926 | 2314 | |
0bf4aa26 | 2315 | let stmt = match self.mir[location.block].statements.get(location.statement_index) { |
b7449926 XL |
2316 | Some(stmt) => stmt, |
2317 | None => return OtherUse(self.mir.source_info(location).span), | |
2318 | }; | |
2319 | ||
0bf4aa26 XL |
2320 | debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt); |
2321 | if let StatementKind::Assign( | |
2322 | _, | |
2323 | box Rvalue::Aggregate(ref kind, ref places) | |
2324 | ) = stmt.kind { | |
2325 | let (def_id, is_generator) = match kind { | |
2326 | box AggregateKind::Closure(def_id, _) => (def_id, false), | |
2327 | box AggregateKind::Generator(def_id, _, _) => (def_id, true), | |
2328 | _ => return OtherUse(stmt.source_info.span), | |
2329 | }; | |
2330 | ||
2331 | debug!( | |
2332 | "move_spans: def_id={:?} is_generator={:?} places={:?}", | |
2333 | def_id, is_generator, places | |
2334 | ); | |
2335 | if let Some((args_span, var_span)) = self.closure_span(*def_id, moved_place, places) { | |
2336 | return ClosureUse { | |
2337 | is_generator, | |
2338 | args_span, | |
2339 | var_span, | |
2340 | }; | |
b7449926 XL |
2341 | } |
2342 | } | |
2343 | ||
0bf4aa26 | 2344 | OtherUse(stmt.source_info.span) |
b7449926 XL |
2345 | } |
2346 | ||
2347 | /// Finds the span of arguments of a closure (within `maybe_closure_span`) | |
2348 | /// and its usage of the local assigned at `location`. | |
2349 | /// This is done by searching in statements succeeding `location` | |
2350 | /// and originating from `maybe_closure_span`. | |
2351 | pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans { | |
2352 | use self::UseSpans::*; | |
0bf4aa26 | 2353 | debug!("borrow_spans: use_span={:?} location={:?}", use_span, location); |
b7449926 | 2354 | |
0bf4aa26 | 2355 | let target = match self.mir[location.block] |
b7449926 XL |
2356 | .statements |
2357 | .get(location.statement_index) | |
2358 | { | |
2359 | Some(&Statement { | |
2360 | kind: StatementKind::Assign(Place::Local(local), _), | |
2361 | .. | |
2362 | }) => local, | |
2363 | _ => return OtherUse(use_span), | |
2364 | }; | |
2365 | ||
0bf4aa26 | 2366 | if self.mir.local_kind(target) != LocalKind::Temp { |
b7449926 XL |
2367 | // operands are always temporaries. |
2368 | return OtherUse(use_span); | |
2369 | } | |
2370 | ||
2371 | for stmt in &self.mir[location.block].statements[location.statement_index + 1..] { | |
0bf4aa26 XL |
2372 | if let StatementKind::Assign( |
2373 | _, box Rvalue::Aggregate(ref kind, ref places) | |
2374 | ) = stmt.kind { | |
2375 | let (def_id, is_generator) = match kind { | |
2376 | box AggregateKind::Closure(def_id, _) => (def_id, false), | |
2377 | box AggregateKind::Generator(def_id, _, _) => (def_id, true), | |
2378 | _ => continue, | |
2379 | }; | |
b7449926 | 2380 | |
0bf4aa26 XL |
2381 | debug!( |
2382 | "borrow_spans: def_id={:?} is_generator={:?} places={:?}", | |
2383 | def_id, is_generator, places | |
2384 | ); | |
2385 | if let Some((args_span, var_span)) = self.closure_span( | |
2386 | *def_id, &Place::Local(target), places | |
2387 | ) { | |
2388 | return ClosureUse { | |
2389 | is_generator, | |
2390 | args_span, | |
2391 | var_span, | |
b7449926 | 2392 | }; |
0bf4aa26 XL |
2393 | } else { |
2394 | return OtherUse(use_span); | |
b7449926 XL |
2395 | } |
2396 | } | |
2397 | ||
2398 | if use_span != stmt.source_info.span { | |
2399 | break; | |
2400 | } | |
2401 | } | |
2402 | ||
2403 | OtherUse(use_span) | |
2404 | } | |
2405 | ||
0bf4aa26 XL |
2406 | /// Finds the span of a captured variable within a closure or generator. |
2407 | fn closure_span( | |
2408 | &self, | |
2409 | def_id: DefId, | |
2410 | target_place: &Place<'tcx>, | |
2411 | places: &Vec<Operand<'tcx>>, | |
2412 | ) -> Option<(Span, Span)> { | |
2413 | debug!( | |
2414 | "closure_span: def_id={:?} target_place={:?} places={:?}", | |
2415 | def_id, target_place, places | |
2416 | ); | |
2417 | let node_id = self.infcx.tcx.hir.as_local_node_id(def_id)?; | |
2418 | let expr = &self.infcx.tcx.hir.expect_expr(node_id).node; | |
2419 | debug!("closure_span: node_id={:?} expr={:?}", node_id, expr); | |
2420 | if let hir::ExprKind::Closure( | |
2421 | .., args_span, _ | |
2422 | ) = expr { | |
2423 | let var_span = self.infcx.tcx.with_freevars( | |
2424 | node_id, | |
2425 | |freevars| { | |
2426 | for (v, place) in freevars.iter().zip(places) { | |
2427 | match place { | |
2428 | Operand::Copy(place) | | |
2429 | Operand::Move(place) if target_place == place => { | |
2430 | debug!("closure_span: found captured local {:?}", place); | |
2431 | return Some(v.span); | |
2432 | }, | |
2433 | _ => {} | |
2434 | } | |
2435 | } | |
2436 | ||
2437 | None | |
2438 | }, | |
2439 | )?; | |
2440 | ||
2441 | Some((*args_span, var_span)) | |
2442 | } else { | |
2443 | None | |
2444 | } | |
2445 | } | |
2446 | ||
b7449926 XL |
2447 | /// Helper to retrieve span(s) of given borrow from the current MIR |
2448 | /// representation | |
2449 | pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData) -> UseSpans { | |
2450 | let span = self.mir.source_info(borrow.reserve_location).span; | |
2451 | self.borrow_spans(span, borrow.reserve_location) | |
2452 | } | |
ff7c6d11 | 2453 | } |