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