]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
New upstream version 1.53.0+dfsg1
[rustc.git] / compiler / rustc_mir / src / borrow_check / diagnostics / mod.rs
CommitLineData
60c5eb7d
XL
1//! Borrow checker diagnostics.
2
dc9dc135 3use rustc_errors::DiagnosticBuilder;
dfeec247
XL
4use rustc_hir as hir;
5use rustc_hir::def::Namespace;
6use rustc_hir::def_id::DefId;
f035d41b 7use rustc_hir::lang_items::LangItemGroup;
dfeec247 8use rustc_hir::GeneratorKind;
ba9703b0 9use rustc_middle::mir::{
cdc7bbd5
XL
10 AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand,
11 Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
ba9703b0
XL
12};
13use rustc_middle::ty::print::Print;
1b1a35ee 14use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt};
f035d41b
XL
15use rustc_span::{
16 hygiene::{DesugaringKind, ForLoopLoc},
17 symbol::sym,
18 Span,
19};
ba9703b0 20use rustc_target::abi::VariantIdx;
cdc7bbd5 21use std::iter;
ff7c6d11 22
94b46f34 23use super::borrow_set::BorrowData;
e74abb32 24use super::MirBorrowckCtxt;
416331ca 25use crate::dataflow::move_paths::{InitLocation, LookupResult};
ff7c6d11 26
60c5eb7d 27mod find_use;
60c5eb7d 28mod outlives_suggestion;
dfeec247
XL
29mod region_name;
30mod var_name;
60c5eb7d
XL
31
32mod conflict_errors;
dfeec247 33mod explain_borrow;
60c5eb7d
XL
34mod move_errors;
35mod mutability_errors;
36mod region_errors;
60c5eb7d
XL
37
38crate use mutability_errors::AccessKind;
60c5eb7d 39crate use outlives_suggestion::OutlivesSuggestionBuilder;
dfeec247
XL
40crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
41crate use region_name::{RegionName, RegionNameSource};
f035d41b 42use rustc_span::symbol::Ident;
60c5eb7d 43
dc9dc135 44pub(super) struct IncludingDowncast(pub(super) bool);
0bf4aa26 45
dc9dc135 46impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
0731742a
XL
47 /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
48 /// is moved after being invoked.
0bf4aa26
XL
49 ///
50 /// ```text
51 /// note: closure cannot be invoked more than once because it moves the variable `dict` out of
52 /// its environment
53 /// --> $DIR/issue-42065.rs:16:29
54 /// |
55 /// LL | for (key, value) in dict {
56 /// | ^^^^
57 /// ```
0731742a 58 pub(super) fn add_moved_or_invoked_closure_note(
0bf4aa26
XL
59 &self,
60 location: Location,
74b04a01 61 place: PlaceRef<'tcx>,
0bf4aa26
XL
62 diag: &mut DiagnosticBuilder<'_>,
63 ) {
0731742a 64 debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
dc9dc135
XL
65 let mut target = place.local_or_deref_local();
66 for stmt in &self.body[location.block].statements[location.statement_index..] {
0731742a 67 debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
dfeec247 68 if let StatementKind::Assign(box (into, Rvalue::Use(from))) = &stmt.kind {
0731742a 69 debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
0bf4aa26 70 match from {
dfeec247
XL
71 Operand::Copy(ref place) | Operand::Move(ref place)
72 if target == place.local_or_deref_local() =>
73 {
74 target = into.local_or_deref_local()
75 }
76 _ => {}
0bf4aa26
XL
77 }
78 }
79 }
80
0731742a 81 // Check if we are attempting to call a closure after it has been invoked.
dc9dc135 82 let terminator = self.body[location.block].terminator();
0731742a 83 debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
0bf4aa26 84 if let TerminatorKind::Call {
6a06907d 85 func: Operand::Constant(box Constant { literal, .. }),
0bf4aa26
XL
86 args,
87 ..
dfeec247
XL
88 } = &terminator.kind
89 {
6a06907d 90 if let ty::FnDef(id, _) = *literal.ty().kind() {
1b1a35ee
XL
91 debug!("add_moved_or_invoked_closure_note: id={:?}", id);
92 if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() {
93 let closure = match args.first() {
94 Some(Operand::Copy(ref place)) | Some(Operand::Move(ref place))
95 if target == place.local_or_deref_local() =>
96 {
97 place.local_or_deref_local().unwrap()
98 }
99 _ => return,
100 };
0bf4aa26 101
1b1a35ee
XL
102 debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
103 if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() {
104 let did = did.expect_local();
105 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
0bf4aa26 106
5869c6ff 107 if let Some((span, hir_place)) =
1b1a35ee
XL
108 self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id)
109 {
110 diag.span_note(
111 *span,
112 &format!(
113 "closure cannot be invoked more than once because it moves the \
114 variable `{}` out of its environment",
5869c6ff 115 ty::place_to_string_for_capture(self.infcx.tcx, hir_place)
1b1a35ee
XL
116 ),
117 );
118 return;
119 }
0bf4aa26
XL
120 }
121 }
122 }
123 }
0731742a
XL
124
125 // Check if we are just moving a closure after it has been invoked.
126 if let Some(target) = target {
1b1a35ee 127 if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() {
f9f354fc 128 let did = did.expect_local();
3dfed10e 129 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
0731742a 130
5869c6ff 131 if let Some((span, hir_place)) =
3dfed10e 132 self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id)
0731742a
XL
133 {
134 diag.span_note(
135 *span,
136 &format!(
137 "closure cannot be moved more than once as it is not `Copy` due to \
138 moving the variable `{}` out of its environment",
5869c6ff 139 ty::place_to_string_for_capture(self.infcx.tcx, hir_place)
0731742a
XL
140 ),
141 );
142 }
143 }
144 }
0bf4aa26
XL
145 }
146
ba9703b0
XL
147 /// End-user visible description of `place` if one can be found.
148 /// If the place is a temporary for instance, `"value"` will be returned.
149 pub(super) fn describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String {
150 match self.describe_place(place_ref) {
151 Some(mut descr) => {
152 // Surround descr with `backticks`.
153 descr.reserve(2);
1b1a35ee
XL
154 descr.insert(0, '`');
155 descr.push('`');
ba9703b0
XL
156 descr
157 }
158 None => "value".to_string(),
159 }
160 }
161
162 /// End-user visible description of `place` if one can be found.
163 /// If the place is a temporary for instance, None will be returned.
74b04a01 164 pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String> {
416331ca 165 self.describe_place_with_options(place_ref, IncludingDowncast(false))
8faf50e0
XL
166 }
167
0bf4aa26
XL
168 /// End-user visible description of `place` if one can be found. If the
169 /// place is a temporary for instance, None will be returned.
170 /// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
171 /// `Downcast` and `IncludingDowncast` is true
8faf50e0
XL
172 pub(super) fn describe_place_with_options(
173 &self,
74b04a01 174 place: PlaceRef<'tcx>,
8faf50e0
XL
175 including_downcast: IncludingDowncast,
176 ) -> Option<String> {
ff7c6d11 177 let mut buf = String::new();
8faf50e0 178 match self.append_place_to_string(place, &mut buf, false, &including_downcast) {
ff7c6d11
XL
179 Ok(()) => Some(buf),
180 Err(()) => None,
181 }
182 }
183
0bf4aa26 184 /// Appends end-user visible description of `place` to `buf`.
ff7c6d11
XL
185 fn append_place_to_string(
186 &self,
74b04a01 187 place: PlaceRef<'tcx>,
ff7c6d11
XL
188 buf: &mut String,
189 mut autoderef: bool,
8faf50e0 190 including_downcast: &IncludingDowncast,
ff7c6d11 191 ) -> Result<(), ()> {
416331ca 192 match place {
dfeec247 193 PlaceRef { local, projection: [] } => {
74b04a01 194 self.append_local_to_string(local, buf)?;
ff7c6d11 195 }
dfeec247 196 PlaceRef { local, projection: [ProjectionElem::Deref] }
74b04a01 197 if self.body.local_decls[local].is_ref_for_guard() =>
dfeec247 198 {
60c5eb7d 199 self.append_place_to_string(
74b04a01 200 PlaceRef { local, projection: &[] },
60c5eb7d
XL
201 buf,
202 autoderef,
203 &including_downcast,
204 )?;
dfeec247
XL
205 }
206 PlaceRef { local, projection: [ProjectionElem::Deref] }
74b04a01 207 if self.body.local_decls[local].is_ref_to_static() =>
dfeec247 208 {
74b04a01 209 let local_info = &self.body.local_decls[local].local_info;
f9f354fc 210 if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info {
60c5eb7d
XL
211 buf.push_str(&self.infcx.tcx.item_name(def_id).as_str());
212 } else {
213 unreachable!();
214 }
dfeec247
XL
215 }
216 PlaceRef { local, projection: [proj_base @ .., elem] } => {
e1599b0c 217 match elem {
ff7c6d11 218 ProjectionElem::Deref => {
5869c6ff 219 // FIXME(project-rfc_2229#36): print capture precisely here.
dfeec247 220 let upvar_field_projection = self.is_upvar_field_projection(place);
8faf50e0 221 if let Some(field) = upvar_field_projection {
ff7c6d11 222 let var_index = field.index();
48663c56
XL
223 let name = self.upvars[var_index].name.to_string();
224 if self.upvars[var_index].by_ref {
ff7c6d11
XL
225 buf.push_str(&name);
226 } else {
1b1a35ee
XL
227 buf.push('*');
228 buf.push_str(&name);
ff7c6d11
XL
229 }
230 } else {
231 if autoderef {
416331ca 232 // FIXME turn this recursion into iteration
8faf50e0 233 self.append_place_to_string(
dfeec247 234 PlaceRef { local, projection: proj_base },
8faf50e0
XL
235 buf,
236 autoderef,
237 &including_downcast,
238 )?;
ff7c6d11 239 } else {
1b1a35ee 240 buf.push('*');
dfeec247
XL
241 self.append_place_to_string(
242 PlaceRef { local, projection: proj_base },
243 buf,
244 autoderef,
245 &including_downcast,
246 )?;
ff7c6d11
XL
247 }
248 }
249 }
250 ProjectionElem::Downcast(..) => {
8faf50e0 251 self.append_place_to_string(
dfeec247 252 PlaceRef { local, projection: proj_base },
8faf50e0
XL
253 buf,
254 autoderef,
255 &including_downcast,
256 )?;
257 if including_downcast.0 {
258 return Err(());
259 }
ff7c6d11
XL
260 }
261 ProjectionElem::Field(field, _ty) => {
262 autoderef = true;
263
5869c6ff 264 // FIXME(project-rfc_2229#36): print capture precisely here.
dfeec247 265 let upvar_field_projection = self.is_upvar_field_projection(place);
8faf50e0 266 if let Some(field) = upvar_field_projection {
ff7c6d11 267 let var_index = field.index();
48663c56 268 let name = self.upvars[var_index].name.to_string();
ff7c6d11
XL
269 buf.push_str(&name);
270 } else {
dfeec247
XL
271 let field_name = self
272 .describe_field(PlaceRef { local, projection: proj_base }, *field);
8faf50e0 273 self.append_place_to_string(
dfeec247 274 PlaceRef { local, projection: proj_base },
8faf50e0
XL
275 buf,
276 autoderef,
277 &including_downcast,
278 )?;
1b1a35ee
XL
279 buf.push('.');
280 buf.push_str(&field_name);
ff7c6d11
XL
281 }
282 }
283 ProjectionElem::Index(index) => {
284 autoderef = true;
285
8faf50e0 286 self.append_place_to_string(
dfeec247 287 PlaceRef { local, projection: proj_base },
8faf50e0
XL
288 buf,
289 autoderef,
290 &including_downcast,
291 )?;
1b1a35ee 292 buf.push('[');
e1599b0c 293 if self.append_local_to_string(*index, buf).is_err() {
1b1a35ee 294 buf.push('_');
ff7c6d11 295 }
1b1a35ee 296 buf.push(']');
ff7c6d11
XL
297 }
298 ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
299 autoderef = true;
300 // Since it isn't possible to borrow an element on a particular index and
301 // then use another while the borrow is held, don't output indices details
302 // to avoid confusing the end-user
8faf50e0 303 self.append_place_to_string(
dfeec247 304 PlaceRef { local, projection: proj_base },
8faf50e0
XL
305 buf,
306 autoderef,
307 &including_downcast,
308 )?;
1b1a35ee 309 buf.push_str("[..]");
ff7c6d11
XL
310 }
311 };
312 }
313 }
314
315 Ok(())
316 }
317
0bf4aa26 318 /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
dc9dc135 319 /// a name, or its name was generated by the compiler, then `Err` is returned
60c5eb7d
XL
320 fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> {
321 let decl = &self.body.local_decls[local];
322 match self.local_names[local] {
323 Some(name) if !decl.from_compiler_desugaring() => {
e1599b0c 324 buf.push_str(&name.as_str());
ff7c6d11
XL
325 Ok(())
326 }
dc9dc135 327 _ => Err(()),
ff7c6d11
XL
328 }
329 }
330
0bf4aa26 331 /// End-user visible description of the `field`nth field of `base`
74b04a01 332 fn describe_field(&self, place: PlaceRef<'tcx>, field: Field) -> String {
416331ca
XL
333 // FIXME Place2 Make this work iteratively
334 match place {
dfeec247 335 PlaceRef { local, projection: [] } => {
74b04a01 336 let local = &self.body.local_decls[local];
532ac7d7 337 self.describe_field_from_ty(&local.ty, field, None)
ff7c6d11 338 }
dfeec247 339 PlaceRef { local, projection: [proj_base @ .., elem] } => match elem {
e1599b0c 340 ProjectionElem::Deref => {
dfeec247 341 self.describe_field(PlaceRef { local, projection: proj_base }, field)
e1599b0c 342 }
532ac7d7 343 ProjectionElem::Downcast(_, variant_index) => {
5869c6ff 344 let base_ty = place.ty(self.body, self.infcx.tcx).ty;
e1599b0c 345 self.describe_field_from_ty(&base_ty, field, Some(*variant_index))
532ac7d7 346 }
ff7c6d11 347 ProjectionElem::Field(_, field_type) => {
532ac7d7 348 self.describe_field_from_ty(&field_type, field, None)
ff7c6d11 349 }
0531ce1d
XL
350 ProjectionElem::Index(..)
351 | ProjectionElem::ConstantIndex { .. }
352 | ProjectionElem::Subslice { .. } => {
dfeec247 353 self.describe_field(PlaceRef { local, projection: proj_base }, field)
ff7c6d11
XL
354 }
355 },
356 }
357 }
358
0bf4aa26 359 /// End-user visible description of the `field_index`nth field of `ty`
532ac7d7
XL
360 fn describe_field_from_ty(
361 &self,
48663c56 362 ty: Ty<'_>,
532ac7d7 363 field: Field,
dfeec247 364 variant_index: Option<VariantIdx>,
532ac7d7 365 ) -> String {
ff7c6d11
XL
366 if ty.is_box() {
367 // If the type is a box, the field is described from the boxed type
532ac7d7 368 self.describe_field_from_ty(&ty.boxed_ty(), field, variant_index)
ff7c6d11 369 } else {
1b1a35ee 370 match *ty.kind() {
532ac7d7
XL
371 ty::Adt(def, _) => {
372 let variant = if let Some(idx) = variant_index {
373 assert!(def.is_enum());
374 &def.variants[idx]
375 } else {
376 def.non_enum_variant()
377 };
dfeec247
XL
378 variant.fields[field.index()].ident.to_string()
379 }
b7449926
XL
380 ty::Tuple(_) => field.index().to_string(),
381 ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
532ac7d7 382 self.describe_field_from_ty(&ty, field, variant_index)
ff7c6d11 383 }
dfeec247
XL
384 ty::Array(ty, _) | ty::Slice(ty) => {
385 self.describe_field_from_ty(&ty, field, variant_index)
386 }
b7449926 387 ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
fc512014
XL
388 // We won't be borrowck'ing here if the closure came from another crate,
389 // so it's safe to call `expect_local`.
390 //
391 // We know the field exists so it's safe to call operator[] and `unwrap` here.
6a06907d
XL
392 let var_id = self
393 .infcx
394 .tcx
395 .typeck(def_id.expect_local())
396 .closure_min_captures_flattened(def_id)
397 .nth(field.index())
398 .unwrap()
399 .get_root_variable();
ff7c6d11 400
dc9dc135 401 self.infcx.tcx.hir().name(var_id).to_string()
ff7c6d11
XL
402 }
403 _ => {
404 // Might need a revision when the fields in trait RFC is implemented
405 // (https://github.com/rust-lang/rfcs/pull/1546)
dfeec247 406 bug!("End-user description not implemented for field access on `{:?}`", ty);
ff7c6d11
XL
407 }
408 }
409 }
410 }
411
dc9dc135
XL
412 /// Add a note that a type does not implement `Copy`
413 pub(super) fn note_type_does_not_implement_copy(
0bf4aa26 414 &self,
dc9dc135
XL
415 err: &mut DiagnosticBuilder<'a>,
416 place_desc: &str,
417 ty: Ty<'tcx>,
418 span: Option<Span>,
1b1a35ee 419 move_prefix: &str,
dc9dc135
XL
420 ) {
421 let message = format!(
1b1a35ee
XL
422 "{}move occurs because {} has type `{}`, which does not implement the `Copy` trait",
423 move_prefix, place_desc, ty,
0bf4aa26 424 );
dc9dc135
XL
425 if let Some(span) = span {
426 err.span_label(span, message);
427 } else {
428 err.note(&message);
0bf4aa26
XL
429 }
430 }
416331ca
XL
431
432 pub(super) fn borrowed_content_source(
433 &self,
74b04a01 434 deref_base: PlaceRef<'tcx>,
416331ca
XL
435 ) -> BorrowedContentSource<'tcx> {
436 let tcx = self.infcx.tcx;
437
438 // Look up the provided place and work out the move path index for it,
439 // we'll use this to check whether it was originally from an overloaded
440 // operator.
441 match self.move_data.rev_lookup.find(deref_base) {
442 LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => {
443 debug!("borrowed_content_source: mpi={:?}", mpi);
444
445 for i in &self.move_data.init_path_map[mpi] {
446 let init = &self.move_data.inits[*i];
447 debug!("borrowed_content_source: init={:?}", init);
448 // We're only interested in statements that initialized a value, not the
449 // initializations from arguments.
450 let loc = match init.location {
451 InitLocation::Statement(stmt) => stmt,
452 _ => continue,
453 };
454
455 let bbd = &self.body[loc.block];
456 let is_terminator = bbd.statements.len() == loc.statement_index;
457 debug!(
458 "borrowed_content_source: loc={:?} is_terminator={:?}",
dfeec247 459 loc, is_terminator,
416331ca
XL
460 );
461 if !is_terminator {
462 continue;
463 } else if let Some(Terminator {
dfeec247 464 kind: TerminatorKind::Call { ref func, from_hir_call: false, .. },
416331ca 465 ..
dfeec247
XL
466 }) = bbd.terminator
467 {
468 if let Some(source) =
f9f354fc 469 BorrowedContentSource::from_call(func.ty(self.body, tcx), tcx)
dfeec247 470 {
416331ca
XL
471 return source;
472 }
473 }
474 }
475 }
476 // Base is a `static` so won't be from an overloaded operator
477 _ => (),
478 };
479
480 // If we didn't find an overloaded deref or index, then assume it's a
481 // built in deref and check the type of the base.
5869c6ff 482 let base_ty = deref_base.ty(self.body, tcx).ty;
416331ca
XL
483 if base_ty.is_unsafe_ptr() {
484 BorrowedContentSource::DerefRawPointer
485 } else if base_ty.is_mutable_ptr() {
486 BorrowedContentSource::DerefMutableRef
487 } else {
488 BorrowedContentSource::DerefSharedRef
489 }
490 }
532ac7d7 491}
0bf4aa26 492
dc9dc135 493impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
0bf4aa26
XL
494 /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
495 /// name where required.
dc9dc135 496 pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
532ac7d7
XL
497 let mut s = String::new();
498 let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
499
0bf4aa26
XL
500 // We need to add synthesized lifetimes where appropriate. We do
501 // this by hooking into the pretty printer and telling it to label the
502 // lifetimes without names with the value `'0`.
1b1a35ee 503 match ty.kind() {
ba9703b0 504 ty::Ref(
cdc7bbd5 505 ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br, .. })
ba9703b0 506 | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }),
0bf4aa26
XL
507 _,
508 _,
532ac7d7
XL
509 ) => printer.region_highlight_mode.highlighting_bound_region(*br, counter),
510 _ => {}
0bf4aa26 511 }
532ac7d7
XL
512
513 let _ = ty.print(printer);
514 s
0bf4aa26
XL
515 }
516
9fa01778 517 /// Returns the name of the provided `Ty` (that must be a reference)'s region with a
0bf4aa26 518 /// synthesized lifetime name where required.
dc9dc135 519 pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
532ac7d7
XL
520 let mut s = String::new();
521 let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
522
1b1a35ee 523 let region = match ty.kind() {
532ac7d7
XL
524 ty::Ref(region, _, _) => {
525 match region {
cdc7bbd5 526 ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br, .. })
532ac7d7
XL
527 | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => {
528 printer.region_highlight_mode.highlighting_bound_region(*br, counter)
529 }
530 _ => {}
0bf4aa26 531 }
532ac7d7
XL
532
533 region
534 }
0bf4aa26 535 _ => bug!("ty for annotation of borrow region is not a reference"),
532ac7d7
XL
536 };
537
538 let _ = region.print(printer);
539 s
0bf4aa26 540 }
b7449926
XL
541}
542
f035d41b 543/// The span(s) associated to a use of a place.
b7449926 544#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1b1a35ee 545pub(super) enum UseSpans<'tcx> {
f035d41b 546 /// The access is caused by capturing a variable for a closure.
b7449926 547 ClosureUse {
f035d41b 548 /// This is true if the captured variable was from a generator.
e74abb32 549 generator_kind: Option<GeneratorKind>,
f035d41b
XL
550 /// The span of the args of the closure, including the `move` keyword if
551 /// it's present.
b7449926 552 args_span: Span,
f035d41b
XL
553 /// The span of the first use of the captured variable inside the closure.
554 var_span: Span,
555 },
556 /// The access is caused by using a variable as the receiver of a method
557 /// that takes 'self'
558 FnSelfUse {
559 /// The span of the variable being moved
b7449926 560 var_span: Span,
f035d41b
XL
561 /// The span of the method call on the variable
562 fn_call_span: Span,
563 /// The definition span of the method being called
564 fn_span: Span,
1b1a35ee 565 kind: FnSelfUseKind<'tcx>,
b7449926 566 },
f035d41b
XL
567 /// This access is caused by a `match` or `if let` pattern.
568 PatUse(Span),
569 /// This access has a single span associated to it: common case.
b7449926
XL
570 OtherUse(Span),
571}
572
f035d41b 573#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1b1a35ee 574pub(super) enum FnSelfUseKind<'tcx> {
f035d41b 575 /// A normal method call of the form `receiver.foo(a, b, c)`
cdc7bbd5
XL
576 Normal {
577 self_arg: Ident,
578 implicit_into_iter: bool,
579 /// Whether the self type of the method call has an `.as_ref()` method.
580 /// Used for better diagnostics.
581 is_option_or_result: bool,
582 },
f035d41b
XL
583 /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)`
584 FnOnceCall,
585 /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`)
586 Operator { self_arg: Ident },
1b1a35ee
XL
587 DerefCoercion {
588 /// The `Span` of the `Target` associated type
589 /// in the `Deref` impl we are using.
590 deref_target: Span,
591 /// The type `T::Deref` we are dereferencing to
592 deref_target_ty: Ty<'tcx>,
593 },
f035d41b
XL
594}
595
1b1a35ee 596impl UseSpans<'_> {
b7449926
XL
597 pub(super) fn args_or_use(self) -> Span {
598 match self {
f035d41b
XL
599 UseSpans::ClosureUse { args_span: span, .. }
600 | UseSpans::PatUse(span)
f035d41b 601 | UseSpans::OtherUse(span) => span,
1b1a35ee
XL
602 UseSpans::FnSelfUse {
603 fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, ..
604 } => fn_call_span,
605 UseSpans::FnSelfUse { var_span, .. } => var_span,
b7449926
XL
606 }
607 }
608
609 pub(super) fn var_or_use(self) -> Span {
610 match self {
f035d41b
XL
611 UseSpans::ClosureUse { var_span: span, .. }
612 | UseSpans::PatUse(span)
f035d41b 613 | UseSpans::OtherUse(span) => span,
1b1a35ee
XL
614 UseSpans::FnSelfUse {
615 fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, ..
616 } => fn_call_span,
617 UseSpans::FnSelfUse { var_span, .. } => var_span,
b7449926
XL
618 }
619 }
620
e74abb32
XL
621 pub(super) fn generator_kind(self) -> Option<GeneratorKind> {
622 match self {
623 UseSpans::ClosureUse { generator_kind, .. } => generator_kind,
624 _ => None,
625 }
626 }
627
b7449926 628 // Add a span label to the arguments of the closure, if it exists.
9fa01778
XL
629 pub(super) fn args_span_label(
630 self,
631 err: &mut DiagnosticBuilder<'_>,
632 message: impl Into<String>,
633 ) {
b7449926
XL
634 if let UseSpans::ClosureUse { args_span, .. } = self {
635 err.span_label(args_span, message);
636 }
637 }
638
639 // Add a span label to the use of the captured variable, if it exists.
9fa01778
XL
640 pub(super) fn var_span_label(
641 self,
642 err: &mut DiagnosticBuilder<'_>,
643 message: impl Into<String>,
644 ) {
b7449926
XL
645 if let UseSpans::ClosureUse { var_span, .. } = self {
646 err.span_label(var_span, message);
647 }
648 }
649
9fa01778 650 /// Returns `false` if this place is not used in a closure.
dc9dc135 651 pub(super) fn for_closure(&self) -> bool {
0bf4aa26 652 match *self {
e74abb32 653 UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_none(),
0bf4aa26
XL
654 _ => false,
655 }
656 }
657
9fa01778 658 /// Returns `false` if this place is not used in a generator.
dc9dc135 659 pub(super) fn for_generator(&self) -> bool {
0bf4aa26 660 match *self {
e74abb32 661 UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_some(),
0bf4aa26
XL
662 _ => false,
663 }
664 }
665
666 /// Describe the span associated with a use of a place.
dc9dc135 667 pub(super) fn describe(&self) -> String {
0bf4aa26 668 match *self {
dfeec247
XL
669 UseSpans::ClosureUse { generator_kind, .. } => {
670 if generator_kind.is_some() {
671 " in generator".to_string()
672 } else {
673 " in closure".to_string()
674 }
675 }
1b1a35ee 676 _ => String::new(),
b7449926
XL
677 }
678 }
679
680 pub(super) fn or_else<F>(self, if_other: F) -> Self
681 where
682 F: FnOnce() -> Self,
683 {
684 match self {
685 closure @ UseSpans::ClosureUse { .. } => closure,
f035d41b
XL
686 UseSpans::PatUse(_) | UseSpans::OtherUse(_) => if_other(),
687 fn_self @ UseSpans::FnSelfUse { .. } => fn_self,
b7449926
XL
688 }
689 }
690}
691
416331ca
XL
692pub(super) enum BorrowedContentSource<'tcx> {
693 DerefRawPointer,
694 DerefMutableRef,
695 DerefSharedRef,
696 OverloadedDeref(Ty<'tcx>),
697 OverloadedIndex(Ty<'tcx>),
698}
699
700impl BorrowedContentSource<'tcx> {
ba9703b0 701 pub(super) fn describe_for_unnamed_place(&self, tcx: TyCtxt<'_>) -> String {
416331ca 702 match *self {
74b04a01
XL
703 BorrowedContentSource::DerefRawPointer => "a raw pointer".to_string(),
704 BorrowedContentSource::DerefSharedRef => "a shared reference".to_string(),
705 BorrowedContentSource::DerefMutableRef => "a mutable reference".to_string(),
1b1a35ee 706 BorrowedContentSource::OverloadedDeref(ty) => match ty.kind() {
ba9703b0 707 ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => {
74b04a01 708 "an `Rc`".to_string()
ba9703b0
XL
709 }
710 ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Arc, def.did) => {
74b04a01 711 "an `Arc`".to_string()
416331ca 712 }
ba9703b0
XL
713 _ => format!("dereference of `{}`", ty),
714 },
416331ca
XL
715 BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{}`", ty),
716 }
717 }
718
719 pub(super) fn describe_for_named_place(&self) -> Option<&'static str> {
720 match *self {
721 BorrowedContentSource::DerefRawPointer => Some("raw pointer"),
722 BorrowedContentSource::DerefSharedRef => Some("shared reference"),
723 BorrowedContentSource::DerefMutableRef => Some("mutable reference"),
724 // Overloaded deref and index operators should be evaluated into a
725 // temporary. So we don't need a description here.
726 BorrowedContentSource::OverloadedDeref(_)
dfeec247 727 | BorrowedContentSource::OverloadedIndex(_) => None,
416331ca
XL
728 }
729 }
730
ba9703b0 731 pub(super) fn describe_for_immutable_place(&self, tcx: TyCtxt<'_>) -> String {
416331ca 732 match *self {
74b04a01
XL
733 BorrowedContentSource::DerefRawPointer => "a `*const` pointer".to_string(),
734 BorrowedContentSource::DerefSharedRef => "a `&` reference".to_string(),
416331ca 735 BorrowedContentSource::DerefMutableRef => {
dfeec247
XL
736 bug!("describe_for_immutable_place: DerefMutableRef isn't immutable")
737 }
1b1a35ee 738 BorrowedContentSource::OverloadedDeref(ty) => match ty.kind() {
ba9703b0 739 ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => {
74b04a01 740 "an `Rc`".to_string()
ba9703b0
XL
741 }
742 ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Arc, def.did) => {
74b04a01 743 "an `Arc`".to_string()
416331ca 744 }
ba9703b0
XL
745 _ => format!("a dereference of `{}`", ty),
746 },
416331ca
XL
747 BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{}`", ty),
748 }
749 }
750
751 fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> {
1b1a35ee 752 match *func.kind() {
416331ca
XL
753 ty::FnDef(def_id, substs) => {
754 let trait_id = tcx.trait_of_item(def_id)?;
755
756 let lang_items = tcx.lang_items();
757 if Some(trait_id) == lang_items.deref_trait()
758 || Some(trait_id) == lang_items.deref_mut_trait()
759 {
760 Some(BorrowedContentSource::OverloadedDeref(substs.type_at(0)))
761 } else if Some(trait_id) == lang_items.index_trait()
762 || Some(trait_id) == lang_items.index_mut_trait()
763 {
764 Some(BorrowedContentSource::OverloadedIndex(substs.type_at(0)))
765 } else {
766 None
767 }
768 }
769 _ => None,
770 }
771 }
772}
773
dc9dc135 774impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
b7449926
XL
775 /// Finds the spans associated to a move or copy of move_place at location.
776 pub(super) fn move_spans(
777 &self,
74b04a01 778 moved_place: PlaceRef<'tcx>, // Could also be an upvar.
b7449926 779 location: Location,
1b1a35ee 780 ) -> UseSpans<'tcx> {
b7449926 781 use self::UseSpans::*;
b7449926 782
dc9dc135 783 let stmt = match self.body[location.block].statements.get(location.statement_index) {
b7449926 784 Some(stmt) => stmt,
dc9dc135 785 None => return OtherUse(self.body.source_info(location).span),
b7449926
XL
786 };
787
0bf4aa26 788 debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
dfeec247 789 if let StatementKind::Assign(box (_, Rvalue::Aggregate(ref kind, ref places))) = stmt.kind {
f035d41b 790 match kind {
e74abb32 791 box AggregateKind::Closure(def_id, _)
f035d41b
XL
792 | box AggregateKind::Generator(def_id, _, _) => {
793 debug!("move_spans: def_id={:?} places={:?}", def_id, places);
794 if let Some((args_span, generator_kind, var_span)) =
795 self.closure_span(*def_id, moved_place, places)
796 {
797 return ClosureUse { generator_kind, args_span, var_span };
798 }
799 }
800 _ => {}
801 }
802 }
803
cdc7bbd5
XL
804 // StatementKind::FakeRead only contains a def_id if they are introduced as a result
805 // of pattern matching within a closure.
806 if let StatementKind::FakeRead(box (cause, ref place)) = stmt.kind {
807 match cause {
808 FakeReadCause::ForMatchedPlace(Some(closure_def_id))
809 | FakeReadCause::ForLet(Some(closure_def_id)) => {
810 debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place);
811 let places = &[Operand::Move(*place)];
812 if let Some((args_span, generator_kind, var_span)) =
813 self.closure_span(closure_def_id, moved_place, places)
814 {
815 return ClosureUse { generator_kind, args_span, var_span };
816 }
817 }
818 _ => {}
819 }
820 }
821
f035d41b
XL
822 let normal_ret =
823 if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) {
824 PatUse(stmt.source_info.span)
825 } else {
826 OtherUse(stmt.source_info.span)
0bf4aa26
XL
827 };
828
f035d41b
XL
829 // We are trying to find MIR of the form:
830 // ```
831 // _temp = _moved_val;
832 // ...
833 // FnSelfCall(_temp, ...)
834 // ```
835 //
836 // where `_moved_val` is the place we generated the move error for,
837 // `_temp` is some other local, and `FnSelfCall` is a function
838 // that has a `self` parameter.
839
840 let target_temp = match stmt.kind {
841 StatementKind::Assign(box (temp, _)) if temp.as_local().is_some() => {
842 temp.as_local().unwrap()
b7449926 843 }
f035d41b
XL
844 _ => return normal_ret,
845 };
b7449926 846
f035d41b
XL
847 debug!("move_spans: target_temp = {:?}", target_temp);
848
849 if let Some(Terminator {
1b1a35ee 850 kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, ..
f035d41b
XL
851 }) = &self.body[location.block].terminator
852 {
1b1a35ee
XL
853 let (method_did, method_substs) = if let Some(info) =
854 crate::util::find_self_call(self.infcx.tcx, &self.body, target_temp, location.block)
855 {
856 info
857 } else {
858 return normal_ret;
859 };
f035d41b
XL
860
861 let tcx = self.infcx.tcx;
1b1a35ee
XL
862 let parent = tcx.parent(method_did);
863 let is_fn_once = parent == tcx.lang_items().fn_once_trait();
864 let is_operator = !from_hir_call
865 && parent.map_or(false, |p| tcx.lang_items().group(LangItemGroup::Op).contains(&p));
866 let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did);
867 let fn_call_span = *fn_span;
868
869 let self_arg = tcx.fn_arg_names(method_did)[0];
870
871 debug!(
872 "terminator = {:?} from_hir_call={:?}",
873 self.body[location.block].terminator, from_hir_call
874 );
875
876 // Check for a 'special' use of 'self' -
877 // an FnOnce call, an operator (e.g. `<<`), or a
878 // deref coercion.
879 let kind = if is_fn_once {
880 Some(FnSelfUseKind::FnOnceCall)
881 } else if is_operator {
882 Some(FnSelfUseKind::Operator { self_arg })
883 } else if is_deref {
884 let deref_target =
885 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
886 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
887 .transpose()
888 });
889 if let Some(Ok(instance)) = deref_target {
890 let deref_target_ty = instance.ty(tcx, self.param_env);
891 Some(FnSelfUseKind::DerefCoercion {
892 deref_target: tcx.def_span(instance.def_id()),
893 deref_target_ty,
894 })
895 } else {
896 None
f035d41b 897 }
1b1a35ee
XL
898 } else {
899 None
900 };
901
902 let kind = kind.unwrap_or_else(|| {
903 // This isn't a 'special' use of `self`
904 debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span);
905 let implicit_into_iter = matches!(
906 fn_call_span.desugaring_kind(),
907 Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
908 );
cdc7bbd5
XL
909 let parent_self_ty = parent
910 .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl)
911 .and_then(|did| match tcx.type_of(did).kind() {
912 ty::Adt(def, ..) => Some(def.did),
913 _ => None,
914 });
915 let is_option_or_result = parent_self_ty.map_or(false, |def_id| {
916 tcx.is_diagnostic_item(sym::option_type, def_id)
917 || tcx.is_diagnostic_item(sym::result_type, def_id)
918 });
919 FnSelfUseKind::Normal { self_arg, implicit_into_iter, is_option_or_result }
1b1a35ee
XL
920 });
921
922 return FnSelfUse {
923 var_span: stmt.source_info.span,
924 fn_call_span,
925 fn_span: self
926 .infcx
927 .tcx
928 .sess
929 .source_map()
930 .guess_head_span(self.infcx.tcx.def_span(method_did)),
931 kind,
932 };
f035d41b 933 }
3dfed10e 934 normal_ret
b7449926
XL
935 }
936
937 /// Finds the span of arguments of a closure (within `maybe_closure_span`)
938 /// and its usage of the local assigned at `location`.
939 /// This is done by searching in statements succeeding `location`
940 /// and originating from `maybe_closure_span`.
1b1a35ee 941 pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx> {
b7449926 942 use self::UseSpans::*;
0bf4aa26 943 debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
b7449926 944
dfeec247
XL
945 let target = match self.body[location.block].statements.get(location.statement_index) {
946 Some(&Statement { kind: StatementKind::Assign(box (ref place, _)), .. }) => {
e74abb32
XL
947 if let Some(local) = place.as_local() {
948 local
949 } else {
950 return OtherUse(use_span);
951 }
952 }
b7449926
XL
953 _ => return OtherUse(use_span),
954 };
955
dc9dc135 956 if self.body.local_kind(target) != LocalKind::Temp {
b7449926
XL
957 // operands are always temporaries.
958 return OtherUse(use_span);
959 }
960
dc9dc135 961 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
dfeec247
XL
962 if let StatementKind::Assign(box (_, Rvalue::Aggregate(ref kind, ref places))) =
963 stmt.kind
964 {
0bf4aa26
XL
965 let (def_id, is_generator) = match kind {
966 box AggregateKind::Closure(def_id, _) => (def_id, false),
967 box AggregateKind::Generator(def_id, _, _) => (def_id, true),
968 _ => continue,
969 };
b7449926 970
0bf4aa26
XL
971 debug!(
972 "borrow_spans: def_id={:?} is_generator={:?} places={:?}",
973 def_id, is_generator, places
974 );
dfeec247
XL
975 if let Some((args_span, generator_kind, var_span)) =
976 self.closure_span(*def_id, Place::from(target).as_ref(), places)
977 {
978 return ClosureUse { generator_kind, args_span, var_span };
0bf4aa26
XL
979 } else {
980 return OtherUse(use_span);
b7449926
XL
981 }
982 }
983
984 if use_span != stmt.source_info.span {
985 break;
986 }
987 }
988
989 OtherUse(use_span)
990 }
991
0bf4aa26
XL
992 /// Finds the span of a captured variable within a closure or generator.
993 fn closure_span(
994 &self,
995 def_id: DefId,
74b04a01 996 target_place: PlaceRef<'tcx>,
5869c6ff 997 places: &[Operand<'tcx>],
e74abb32 998 ) -> Option<(Span, Option<GeneratorKind>, Span)> {
0bf4aa26
XL
999 debug!(
1000 "closure_span: def_id={:?} target_place={:?} places={:?}",
1001 def_id, target_place, places
1002 );
1b1a35ee
XL
1003 let local_did = def_id.as_local()?;
1004 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(local_did);
e74abb32 1005 let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
532ac7d7 1006 debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
dfeec247 1007 if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr {
cdc7bbd5
XL
1008 for (captured_place, place) in iter::zip(
1009 self.infcx.tcx.typeck(def_id.expect_local()).closure_min_captures_flattened(def_id),
1010 places,
1011 ) {
6a06907d
XL
1012 let upvar_hir_id = captured_place.get_root_variable();
1013 //FIXME(project-rfc-2229#8): Use better span from captured_place
1014 let span = self.infcx.tcx.upvars_mentioned(local_did)?[&upvar_hir_id].span;
48663c56 1015 match place {
dfeec247
XL
1016 Operand::Copy(place) | Operand::Move(place)
1017 if target_place == place.as_ref() =>
1018 {
48663c56 1019 debug!("closure_span: found captured local {:?}", place);
e74abb32
XL
1020 let body = self.infcx.tcx.hir().body(*body_id);
1021 let generator_kind = body.generator_kind();
1b1a35ee
XL
1022
1023 // If we have a more specific span available, point to that.
1024 // We do this even though this span might be part of a borrow error
1025 // message rather than a move error message. Our goal is to point
1026 // to a span that shows why the upvar is used in the closure,
1027 // so a move-related span is as good as any (and potentially better,
1028 // if the overall error is due to a move of the upvar).
6a06907d
XL
1029
1030 let usage_span = match captured_place.info.capture_kind {
1031 ty::UpvarCapture::ByValue(Some(span)) => span,
1032 _ => span,
1033 };
1b1a35ee 1034 return Some((*args_span, generator_kind, usage_span));
dfeec247 1035 }
48663c56
XL
1036 _ => {}
1037 }
1038 }
0bf4aa26 1039 }
48663c56 1040 None
0bf4aa26
XL
1041 }
1042
b7449926
XL
1043 /// Helper to retrieve span(s) of given borrow from the current MIR
1044 /// representation
1b1a35ee 1045 pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx> {
dc9dc135 1046 let span = self.body.source_info(borrow.reserve_location).span;
b7449926
XL
1047 self.borrow_spans(span, borrow.reserve_location)
1048 }
ff7c6d11 1049}