]>
Commit | Line | Data |
---|---|---|
60c5eb7d XL |
1 | //! Borrow checker diagnostics. |
2 | ||
04454e1e | 3 | use itertools::Itertools; |
5e7ed085 FG |
4 | use rustc_const_eval::util::{call_kind, CallDesugaringKind}; |
5 | use rustc_errors::{Applicability, Diagnostic}; | |
dfeec247 | 6 | use rustc_hir as hir; |
064997fb | 7 | use rustc_hir::def::{CtorKind, Namespace}; |
dfeec247 | 8 | use rustc_hir::GeneratorKind; |
353b0b11 | 9 | use rustc_index::vec::IndexSlice; |
9c376795 | 10 | use rustc_infer::infer::{LateBoundRegionConversionTime, TyCtxtInferExt}; |
064997fb | 11 | use rustc_middle::mir::tcx::PlaceTy; |
ba9703b0 | 12 | use rustc_middle::mir::{ |
353b0b11 FG |
13 | AggregateKind, Constant, FakeReadCause, Local, LocalInfo, LocalKind, Location, Operand, Place, |
14 | PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, | |
ba9703b0 XL |
15 | }; |
16 | use rustc_middle::ty::print::Print; | |
353b0b11 | 17 | use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; |
c295e0f8 | 18 | use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult}; |
064997fb FG |
19 | use rustc_span::def_id::LocalDefId; |
20 | use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP}; | |
353b0b11 | 21 | use rustc_target::abi::{FieldIdx, VariantIdx}; |
9c376795 FG |
22 | use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; |
23 | use rustc_trait_selection::traits::{ | |
24 | type_known_to_meet_bound_modulo_regions, Obligation, ObligationCause, | |
25 | }; | |
ff7c6d11 | 26 | |
94b46f34 | 27 | use super::borrow_set::BorrowData; |
e74abb32 | 28 | use super::MirBorrowckCtxt; |
ff7c6d11 | 29 | |
a2a8927a | 30 | mod find_all_local_uses; |
60c5eb7d | 31 | mod find_use; |
60c5eb7d | 32 | mod outlives_suggestion; |
dfeec247 XL |
33 | mod region_name; |
34 | mod var_name; | |
60c5eb7d | 35 | |
94222f64 | 36 | mod bound_region_errors; |
60c5eb7d | 37 | mod conflict_errors; |
dfeec247 | 38 | mod explain_borrow; |
60c5eb7d XL |
39 | mod move_errors; |
40 | mod mutability_errors; | |
41 | mod region_errors; | |
60c5eb7d | 42 | |
923072b8 FG |
43 | pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo}; |
44 | pub(crate) use mutability_errors::AccessKind; | |
45 | pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder; | |
46 | pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors}; | |
47 | pub(crate) use region_name::{RegionName, RegionNameSource}; | |
48 | pub(crate) use rustc_const_eval::util::CallKind; | |
60c5eb7d | 49 | |
064997fb FG |
50 | pub(super) struct DescribePlaceOpt { |
51 | pub including_downcast: bool, | |
52 | ||
53 | /// Enable/Disable tuple fields. | |
54 | /// For example `x` tuple. if it's `true` `x.0`. Otherwise `x` | |
55 | pub including_tuple_field: bool, | |
56 | } | |
57 | ||
58 | pub(super) struct IncludingTupleField(pub(super) bool); | |
0bf4aa26 | 59 | |
dc9dc135 | 60 | impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { |
0731742a XL |
61 | /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure |
62 | /// is moved after being invoked. | |
0bf4aa26 XL |
63 | /// |
64 | /// ```text | |
65 | /// note: closure cannot be invoked more than once because it moves the variable `dict` out of | |
66 | /// its environment | |
67 | /// --> $DIR/issue-42065.rs:16:29 | |
68 | /// | | |
69 | /// LL | for (key, value) in dict { | |
70 | /// | ^^^^ | |
71 | /// ``` | |
0731742a | 72 | pub(super) fn add_moved_or_invoked_closure_note( |
0bf4aa26 XL |
73 | &self, |
74 | location: Location, | |
74b04a01 | 75 | place: PlaceRef<'tcx>, |
5e7ed085 | 76 | diag: &mut Diagnostic, |
487cf647 | 77 | ) -> bool { |
0731742a | 78 | debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place); |
dc9dc135 XL |
79 | let mut target = place.local_or_deref_local(); |
80 | for stmt in &self.body[location.block].statements[location.statement_index..] { | |
0731742a | 81 | debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target); |
dfeec247 | 82 | if let StatementKind::Assign(box (into, Rvalue::Use(from))) = &stmt.kind { |
0731742a | 83 | debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from); |
0bf4aa26 | 84 | match from { |
487cf647 | 85 | Operand::Copy(place) | Operand::Move(place) |
dfeec247 XL |
86 | if target == place.local_or_deref_local() => |
87 | { | |
88 | target = into.local_or_deref_local() | |
89 | } | |
90 | _ => {} | |
0bf4aa26 XL |
91 | } |
92 | } | |
93 | } | |
94 | ||
0731742a | 95 | // Check if we are attempting to call a closure after it has been invoked. |
dc9dc135 | 96 | let terminator = self.body[location.block].terminator(); |
0731742a | 97 | debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator); |
0bf4aa26 | 98 | if let TerminatorKind::Call { |
6a06907d | 99 | func: Operand::Constant(box Constant { literal, .. }), |
0bf4aa26 XL |
100 | args, |
101 | .. | |
dfeec247 XL |
102 | } = &terminator.kind |
103 | { | |
6a06907d | 104 | if let ty::FnDef(id, _) = *literal.ty().kind() { |
1b1a35ee | 105 | debug!("add_moved_or_invoked_closure_note: id={:?}", id); |
04454e1e | 106 | if Some(self.infcx.tcx.parent(id)) == self.infcx.tcx.lang_items().fn_once_trait() { |
1b1a35ee | 107 | let closure = match args.first() { |
487cf647 | 108 | Some(Operand::Copy(place) | Operand::Move(place)) |
1b1a35ee XL |
109 | if target == place.local_or_deref_local() => |
110 | { | |
111 | place.local_or_deref_local().unwrap() | |
112 | } | |
487cf647 | 113 | _ => return false, |
1b1a35ee | 114 | }; |
0bf4aa26 | 115 | |
1b1a35ee XL |
116 | debug!("add_moved_or_invoked_closure_note: closure={:?}", closure); |
117 | if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() { | |
118 | let did = did.expect_local(); | |
9ffffee4 | 119 | if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { |
1b1a35ee XL |
120 | diag.span_note( |
121 | *span, | |
122 | &format!( | |
123 | "closure cannot be invoked more than once because it moves the \ | |
124 | variable `{}` out of its environment", | |
5869c6ff | 125 | ty::place_to_string_for_capture(self.infcx.tcx, hir_place) |
1b1a35ee XL |
126 | ), |
127 | ); | |
487cf647 | 128 | return true; |
1b1a35ee | 129 | } |
0bf4aa26 XL |
130 | } |
131 | } | |
132 | } | |
133 | } | |
0731742a XL |
134 | |
135 | // Check if we are just moving a closure after it has been invoked. | |
136 | if let Some(target) = target { | |
1b1a35ee | 137 | if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() { |
f9f354fc | 138 | let did = did.expect_local(); |
9ffffee4 | 139 | if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { |
0731742a XL |
140 | diag.span_note( |
141 | *span, | |
142 | &format!( | |
143 | "closure cannot be moved more than once as it is not `Copy` due to \ | |
144 | moving the variable `{}` out of its environment", | |
5869c6ff | 145 | ty::place_to_string_for_capture(self.infcx.tcx, hir_place) |
0731742a XL |
146 | ), |
147 | ); | |
487cf647 | 148 | return true; |
0731742a XL |
149 | } |
150 | } | |
151 | } | |
487cf647 | 152 | false |
0bf4aa26 XL |
153 | } |
154 | ||
ba9703b0 XL |
155 | /// End-user visible description of `place` if one can be found. |
156 | /// If the place is a temporary for instance, `"value"` will be returned. | |
157 | pub(super) fn describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String { | |
158 | match self.describe_place(place_ref) { | |
159 | Some(mut descr) => { | |
160 | // Surround descr with `backticks`. | |
161 | descr.reserve(2); | |
1b1a35ee XL |
162 | descr.insert(0, '`'); |
163 | descr.push('`'); | |
ba9703b0 XL |
164 | descr |
165 | } | |
166 | None => "value".to_string(), | |
167 | } | |
168 | } | |
169 | ||
170 | /// End-user visible description of `place` if one can be found. | |
04454e1e | 171 | /// If the place is a temporary for instance, `None` will be returned. |
74b04a01 | 172 | pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String> { |
064997fb FG |
173 | self.describe_place_with_options( |
174 | place_ref, | |
175 | DescribePlaceOpt { including_downcast: false, including_tuple_field: true }, | |
176 | ) | |
8faf50e0 XL |
177 | } |
178 | ||
04454e1e FG |
179 | /// End-user visible description of `place` if one can be found. If the place is a temporary |
180 | /// for instance, `None` will be returned. | |
181 | /// `IncludingDowncast` parameter makes the function return `None` if `ProjectionElem` is | |
0bf4aa26 | 182 | /// `Downcast` and `IncludingDowncast` is true |
8faf50e0 XL |
183 | pub(super) fn describe_place_with_options( |
184 | &self, | |
74b04a01 | 185 | place: PlaceRef<'tcx>, |
064997fb | 186 | opt: DescribePlaceOpt, |
8faf50e0 | 187 | ) -> Option<String> { |
04454e1e FG |
188 | let local = place.local; |
189 | let mut autoderef_index = None; | |
ff7c6d11 | 190 | let mut buf = String::new(); |
04454e1e | 191 | let mut ok = self.append_local_to_string(local, &mut buf); |
ff7c6d11 | 192 | |
04454e1e FG |
193 | for (index, elem) in place.projection.into_iter().enumerate() { |
194 | match elem { | |
195 | ProjectionElem::Deref => { | |
196 | if index == 0 { | |
197 | if self.body.local_decls[local].is_ref_for_guard() { | |
198 | continue; | |
ff7c6d11 | 199 | } |
353b0b11 FG |
200 | if let LocalInfo::StaticRef { def_id, .. } = |
201 | *self.body.local_decls[local].local_info() | |
04454e1e | 202 | { |
353b0b11 | 203 | buf.push_str(self.infcx.tcx.item_name(def_id).as_str()); |
04454e1e FG |
204 | ok = Ok(()); |
205 | continue; | |
8faf50e0 | 206 | } |
ff7c6d11 | 207 | } |
04454e1e FG |
208 | if let Some(field) = self.is_upvar_field_projection(PlaceRef { |
209 | local, | |
210 | projection: place.projection.split_at(index + 1).0, | |
211 | }) { | |
212 | let var_index = field.index(); | |
213 | buf = self.upvars[var_index].place.to_string(self.infcx.tcx); | |
214 | ok = Ok(()); | |
215 | if !self.upvars[var_index].by_ref { | |
216 | buf.insert(0, '*'); | |
ff7c6d11 | 217 | } |
04454e1e FG |
218 | } else { |
219 | if autoderef_index.is_none() { | |
220 | autoderef_index = | |
221 | match place.projection.into_iter().rev().find_position(|elem| { | |
222 | !matches!( | |
223 | elem, | |
224 | ProjectionElem::Deref | ProjectionElem::Downcast(..) | |
225 | ) | |
226 | }) { | |
227 | Some((index, _)) => Some(place.projection.len() - index), | |
228 | None => Some(0), | |
229 | }; | |
230 | } | |
231 | if index >= autoderef_index.unwrap() { | |
232 | buf.insert(0, '*'); | |
ff7c6d11 | 233 | } |
ff7c6d11 | 234 | } |
04454e1e | 235 | } |
064997fb | 236 | ProjectionElem::Downcast(..) if opt.including_downcast => return None, |
04454e1e | 237 | ProjectionElem::Downcast(..) => (), |
2b03887a | 238 | ProjectionElem::OpaqueCast(..) => (), |
04454e1e FG |
239 | ProjectionElem::Field(field, _ty) => { |
240 | // FIXME(project-rfc_2229#36): print capture precisely here. | |
241 | if let Some(field) = self.is_upvar_field_projection(PlaceRef { | |
242 | local, | |
243 | projection: place.projection.split_at(index + 1).0, | |
244 | }) { | |
245 | buf = self.upvars[field.index()].place.to_string(self.infcx.tcx); | |
246 | ok = Ok(()); | |
247 | } else { | |
248 | let field_name = self.describe_field( | |
249 | PlaceRef { local, projection: place.projection.split_at(index).0 }, | |
250 | *field, | |
064997fb | 251 | IncludingTupleField(opt.including_tuple_field), |
04454e1e | 252 | ); |
064997fb FG |
253 | if let Some(field_name_str) = field_name { |
254 | buf.push('.'); | |
255 | buf.push_str(&field_name_str); | |
256 | } | |
ff7c6d11 | 257 | } |
04454e1e FG |
258 | } |
259 | ProjectionElem::Index(index) => { | |
260 | buf.push('['); | |
261 | if self.append_local_to_string(*index, &mut buf).is_err() { | |
262 | buf.push('_'); | |
263 | } | |
264 | buf.push(']'); | |
265 | } | |
266 | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { | |
267 | // Since it isn't possible to borrow an element on a particular index and | |
268 | // then use another while the borrow is held, don't output indices details | |
269 | // to avoid confusing the end-user | |
270 | buf.push_str("[..]"); | |
271 | } | |
ff7c6d11 XL |
272 | } |
273 | } | |
04454e1e | 274 | ok.ok().map(|_| buf) |
ff7c6d11 XL |
275 | } |
276 | ||
064997fb FG |
277 | fn describe_name(&self, place: PlaceRef<'tcx>) -> Option<Symbol> { |
278 | for elem in place.projection.into_iter() { | |
279 | match elem { | |
280 | ProjectionElem::Downcast(Some(name), _) => { | |
281 | return Some(*name); | |
282 | } | |
283 | _ => {} | |
284 | } | |
285 | } | |
286 | None | |
287 | } | |
288 | ||
0bf4aa26 | 289 | /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have |
dc9dc135 | 290 | /// a name, or its name was generated by the compiler, then `Err` is returned |
60c5eb7d XL |
291 | fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> { |
292 | let decl = &self.body.local_decls[local]; | |
293 | match self.local_names[local] { | |
294 | Some(name) if !decl.from_compiler_desugaring() => { | |
a2a8927a | 295 | buf.push_str(name.as_str()); |
ff7c6d11 XL |
296 | Ok(()) |
297 | } | |
dc9dc135 | 298 | _ => Err(()), |
ff7c6d11 XL |
299 | } |
300 | } | |
301 | ||
0bf4aa26 | 302 | /// End-user visible description of the `field`nth field of `base` |
064997fb FG |
303 | fn describe_field( |
304 | &self, | |
305 | place: PlaceRef<'tcx>, | |
353b0b11 | 306 | field: FieldIdx, |
064997fb FG |
307 | including_tuple_field: IncludingTupleField, |
308 | ) -> Option<String> { | |
04454e1e FG |
309 | let place_ty = match place { |
310 | PlaceRef { local, projection: [] } => PlaceTy::from_ty(self.body.local_decls[local].ty), | |
dfeec247 | 311 | PlaceRef { local, projection: [proj_base @ .., elem] } => match elem { |
04454e1e FG |
312 | ProjectionElem::Deref |
313 | | ProjectionElem::Index(..) | |
0531ce1d XL |
314 | | ProjectionElem::ConstantIndex { .. } |
315 | | ProjectionElem::Subslice { .. } => { | |
04454e1e | 316 | PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx) |
ff7c6d11 | 317 | } |
04454e1e | 318 | ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx), |
2b03887a | 319 | ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(*ty), |
04454e1e | 320 | ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type), |
ff7c6d11 | 321 | }, |
04454e1e | 322 | }; |
064997fb FG |
323 | self.describe_field_from_ty( |
324 | place_ty.ty, | |
325 | field, | |
326 | place_ty.variant_index, | |
327 | including_tuple_field, | |
328 | ) | |
ff7c6d11 XL |
329 | } |
330 | ||
0bf4aa26 | 331 | /// End-user visible description of the `field_index`nth field of `ty` |
532ac7d7 XL |
332 | fn describe_field_from_ty( |
333 | &self, | |
48663c56 | 334 | ty: Ty<'_>, |
353b0b11 | 335 | field: FieldIdx, |
dfeec247 | 336 | variant_index: Option<VariantIdx>, |
064997fb FG |
337 | including_tuple_field: IncludingTupleField, |
338 | ) -> Option<String> { | |
ff7c6d11 XL |
339 | if ty.is_box() { |
340 | // If the type is a box, the field is described from the boxed type | |
064997fb | 341 | self.describe_field_from_ty(ty.boxed_ty(), field, variant_index, including_tuple_field) |
ff7c6d11 | 342 | } else { |
1b1a35ee | 343 | match *ty.kind() { |
532ac7d7 XL |
344 | ty::Adt(def, _) => { |
345 | let variant = if let Some(idx) = variant_index { | |
346 | assert!(def.is_enum()); | |
5e7ed085 | 347 | &def.variant(idx) |
532ac7d7 XL |
348 | } else { |
349 | def.non_enum_variant() | |
350 | }; | |
487cf647 | 351 | if !including_tuple_field.0 && variant.ctor_kind() == Some(CtorKind::Fn) { |
064997fb FG |
352 | return None; |
353 | } | |
353b0b11 | 354 | Some(variant.fields[field].name.to_string()) |
dfeec247 | 355 | } |
064997fb | 356 | ty::Tuple(_) => Some(field.index().to_string()), |
b7449926 | 357 | ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { |
064997fb | 358 | self.describe_field_from_ty(ty, field, variant_index, including_tuple_field) |
ff7c6d11 | 359 | } |
dfeec247 | 360 | ty::Array(ty, _) | ty::Slice(ty) => { |
064997fb | 361 | self.describe_field_from_ty(ty, field, variant_index, including_tuple_field) |
dfeec247 | 362 | } |
b7449926 | 363 | ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { |
fc512014 XL |
364 | // We won't be borrowck'ing here if the closure came from another crate, |
365 | // so it's safe to call `expect_local`. | |
366 | // | |
367 | // We know the field exists so it's safe to call operator[] and `unwrap` here. | |
064997fb | 368 | let def_id = def_id.expect_local(); |
9ffffee4 FG |
369 | let var_id = |
370 | self.infcx.tcx.closure_captures(def_id)[field.index()].get_root_variable(); | |
ff7c6d11 | 371 | |
064997fb | 372 | Some(self.infcx.tcx.hir().name(var_id).to_string()) |
ff7c6d11 XL |
373 | } |
374 | _ => { | |
375 | // Might need a revision when the fields in trait RFC is implemented | |
376 | // (https://github.com/rust-lang/rfcs/pull/1546) | |
dfeec247 | 377 | bug!("End-user description not implemented for field access on `{:?}`", ty); |
ff7c6d11 XL |
378 | } |
379 | } | |
380 | } | |
381 | } | |
382 | ||
dc9dc135 XL |
383 | /// Add a note that a type does not implement `Copy` |
384 | pub(super) fn note_type_does_not_implement_copy( | |
0bf4aa26 | 385 | &self, |
5e7ed085 | 386 | err: &mut Diagnostic, |
dc9dc135 XL |
387 | place_desc: &str, |
388 | ty: Ty<'tcx>, | |
389 | span: Option<Span>, | |
1b1a35ee | 390 | move_prefix: &str, |
dc9dc135 XL |
391 | ) { |
392 | let message = format!( | |
9c376795 | 393 | "{move_prefix}move occurs because {place_desc} has type `{ty}`, which does not implement the `Copy` trait", |
0bf4aa26 | 394 | ); |
dc9dc135 XL |
395 | if let Some(span) = span { |
396 | err.span_label(span, message); | |
397 | } else { | |
398 | err.note(&message); | |
0bf4aa26 XL |
399 | } |
400 | } | |
416331ca XL |
401 | |
402 | pub(super) fn borrowed_content_source( | |
403 | &self, | |
74b04a01 | 404 | deref_base: PlaceRef<'tcx>, |
416331ca XL |
405 | ) -> BorrowedContentSource<'tcx> { |
406 | let tcx = self.infcx.tcx; | |
407 | ||
408 | // Look up the provided place and work out the move path index for it, | |
409 | // we'll use this to check whether it was originally from an overloaded | |
410 | // operator. | |
411 | match self.move_data.rev_lookup.find(deref_base) { | |
412 | LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => { | |
413 | debug!("borrowed_content_source: mpi={:?}", mpi); | |
414 | ||
415 | for i in &self.move_data.init_path_map[mpi] { | |
416 | let init = &self.move_data.inits[*i]; | |
417 | debug!("borrowed_content_source: init={:?}", init); | |
418 | // We're only interested in statements that initialized a value, not the | |
419 | // initializations from arguments. | |
5e7ed085 | 420 | let InitLocation::Statement(loc) = init.location else { continue }; |
416331ca XL |
421 | |
422 | let bbd = &self.body[loc.block]; | |
423 | let is_terminator = bbd.statements.len() == loc.statement_index; | |
424 | debug!( | |
425 | "borrowed_content_source: loc={:?} is_terminator={:?}", | |
dfeec247 | 426 | loc, is_terminator, |
416331ca XL |
427 | ); |
428 | if !is_terminator { | |
429 | continue; | |
430 | } else if let Some(Terminator { | |
487cf647 | 431 | kind: TerminatorKind::Call { func, from_hir_call: false, .. }, |
416331ca | 432 | .. |
487cf647 | 433 | }) = &bbd.terminator |
dfeec247 XL |
434 | { |
435 | if let Some(source) = | |
f9f354fc | 436 | BorrowedContentSource::from_call(func.ty(self.body, tcx), tcx) |
dfeec247 | 437 | { |
416331ca XL |
438 | return source; |
439 | } | |
440 | } | |
441 | } | |
442 | } | |
443 | // Base is a `static` so won't be from an overloaded operator | |
444 | _ => (), | |
445 | }; | |
446 | ||
447 | // If we didn't find an overloaded deref or index, then assume it's a | |
448 | // built in deref and check the type of the base. | |
5869c6ff | 449 | let base_ty = deref_base.ty(self.body, tcx).ty; |
416331ca XL |
450 | if base_ty.is_unsafe_ptr() { |
451 | BorrowedContentSource::DerefRawPointer | |
452 | } else if base_ty.is_mutable_ptr() { | |
453 | BorrowedContentSource::DerefMutableRef | |
454 | } else { | |
455 | BorrowedContentSource::DerefSharedRef | |
456 | } | |
457 | } | |
0bf4aa26 XL |
458 | |
459 | /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime | |
460 | /// name where required. | |
dc9dc135 | 461 | pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String { |
5e7ed085 | 462 | let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS); |
532ac7d7 | 463 | |
0bf4aa26 XL |
464 | // We need to add synthesized lifetimes where appropriate. We do |
465 | // this by hooking into the pretty printer and telling it to label the | |
466 | // lifetimes without names with the value `'0`. | |
5099ac24 FG |
467 | if let ty::Ref(region, ..) = ty.kind() { |
468 | match **region { | |
469 | ty::ReLateBound(_, ty::BoundRegion { kind: br, .. }) | |
353b0b11 FG |
470 | | ty::RePlaceholder(ty::PlaceholderRegion { |
471 | bound: ty::BoundRegion { kind: br, .. }, | |
472 | .. | |
473 | }) => printer.region_highlight_mode.highlighting_bound_region(br, counter), | |
5099ac24 FG |
474 | _ => {} |
475 | } | |
0bf4aa26 | 476 | } |
532ac7d7 | 477 | |
5e7ed085 | 478 | ty.print(printer).unwrap().into_buffer() |
0bf4aa26 XL |
479 | } |
480 | ||
9fa01778 | 481 | /// Returns the name of the provided `Ty` (that must be a reference)'s region with a |
0bf4aa26 | 482 | /// synthesized lifetime name where required. |
dc9dc135 | 483 | pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String { |
5e7ed085 | 484 | let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS); |
532ac7d7 | 485 | |
5099ac24 FG |
486 | let region = if let ty::Ref(region, ..) = ty.kind() { |
487 | match **region { | |
488 | ty::ReLateBound(_, ty::BoundRegion { kind: br, .. }) | |
353b0b11 FG |
489 | | ty::RePlaceholder(ty::PlaceholderRegion { |
490 | bound: ty::BoundRegion { kind: br, .. }, | |
491 | .. | |
492 | }) => printer.region_highlight_mode.highlighting_bound_region(br, counter), | |
5099ac24 | 493 | _ => {} |
532ac7d7 | 494 | } |
5099ac24 FG |
495 | region |
496 | } else { | |
497 | bug!("ty for annotation of borrow region is not a reference"); | |
532ac7d7 XL |
498 | }; |
499 | ||
5e7ed085 | 500 | region.print(printer).unwrap().into_buffer() |
0bf4aa26 | 501 | } |
b7449926 XL |
502 | } |
503 | ||
f035d41b | 504 | /// The span(s) associated to a use of a place. |
b7449926 | 505 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
1b1a35ee | 506 | pub(super) enum UseSpans<'tcx> { |
f035d41b | 507 | /// The access is caused by capturing a variable for a closure. |
b7449926 | 508 | ClosureUse { |
f035d41b | 509 | /// This is true if the captured variable was from a generator. |
e74abb32 | 510 | generator_kind: Option<GeneratorKind>, |
f035d41b XL |
511 | /// The span of the args of the closure, including the `move` keyword if |
512 | /// it's present. | |
b7449926 | 513 | args_span: Span, |
17df50a5 XL |
514 | /// The span of the use resulting in capture kind |
515 | /// Check `ty::CaptureInfo` for more details | |
516 | capture_kind_span: Span, | |
517 | /// The span of the use resulting in the captured path | |
518 | /// Check `ty::CaptureInfo` for more details | |
519 | path_span: Span, | |
f035d41b XL |
520 | }, |
521 | /// The access is caused by using a variable as the receiver of a method | |
522 | /// that takes 'self' | |
523 | FnSelfUse { | |
524 | /// The span of the variable being moved | |
b7449926 | 525 | var_span: Span, |
f035d41b XL |
526 | /// The span of the method call on the variable |
527 | fn_call_span: Span, | |
528 | /// The definition span of the method being called | |
529 | fn_span: Span, | |
5099ac24 | 530 | kind: CallKind<'tcx>, |
b7449926 | 531 | }, |
f035d41b XL |
532 | /// This access is caused by a `match` or `if let` pattern. |
533 | PatUse(Span), | |
534 | /// This access has a single span associated to it: common case. | |
b7449926 XL |
535 | OtherUse(Span), |
536 | } | |
537 | ||
1b1a35ee | 538 | impl UseSpans<'_> { |
b7449926 XL |
539 | pub(super) fn args_or_use(self) -> Span { |
540 | match self { | |
f035d41b XL |
541 | UseSpans::ClosureUse { args_span: span, .. } |
542 | | UseSpans::PatUse(span) | |
f035d41b | 543 | | UseSpans::OtherUse(span) => span, |
5099ac24 FG |
544 | UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { |
545 | fn_call_span | |
546 | } | |
1b1a35ee | 547 | UseSpans::FnSelfUse { var_span, .. } => var_span, |
b7449926 XL |
548 | } |
549 | } | |
550 | ||
17df50a5 XL |
551 | /// Returns the span of `self`, in the case of a `ClosureUse` returns the `path_span` |
552 | pub(super) fn var_or_use_path_span(self) -> Span { | |
553 | match self { | |
554 | UseSpans::ClosureUse { path_span: span, .. } | |
555 | | UseSpans::PatUse(span) | |
556 | | UseSpans::OtherUse(span) => span, | |
5099ac24 FG |
557 | UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { |
558 | fn_call_span | |
559 | } | |
17df50a5 XL |
560 | UseSpans::FnSelfUse { var_span, .. } => var_span, |
561 | } | |
562 | } | |
563 | ||
564 | /// Returns the span of `self`, in the case of a `ClosureUse` returns the `capture_kind_span` | |
b7449926 XL |
565 | pub(super) fn var_or_use(self) -> Span { |
566 | match self { | |
17df50a5 | 567 | UseSpans::ClosureUse { capture_kind_span: span, .. } |
f035d41b | 568 | | UseSpans::PatUse(span) |
f035d41b | 569 | | UseSpans::OtherUse(span) => span, |
5099ac24 FG |
570 | UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { |
571 | fn_call_span | |
572 | } | |
1b1a35ee | 573 | UseSpans::FnSelfUse { var_span, .. } => var_span, |
b7449926 XL |
574 | } |
575 | } | |
576 | ||
e74abb32 XL |
577 | pub(super) fn generator_kind(self) -> Option<GeneratorKind> { |
578 | match self { | |
579 | UseSpans::ClosureUse { generator_kind, .. } => generator_kind, | |
580 | _ => None, | |
581 | } | |
582 | } | |
583 | ||
487cf647 | 584 | /// Add a span label to the arguments of the closure, if it exists. |
5e7ed085 | 585 | pub(super) fn args_span_label(self, err: &mut Diagnostic, message: impl Into<String>) { |
b7449926 XL |
586 | if let UseSpans::ClosureUse { args_span, .. } = self { |
587 | err.span_label(args_span, message); | |
588 | } | |
589 | } | |
590 | ||
487cf647 FG |
591 | /// Add a span label to the use of the captured variable, if it exists. |
592 | /// only adds label to the `path_span` | |
593 | pub(super) fn var_path_only_subdiag( | |
594 | self, | |
595 | err: &mut Diagnostic, | |
596 | action: crate::InitializationRequiringAction, | |
597 | ) { | |
598 | use crate::session_diagnostics::CaptureVarPathUseCause::*; | |
599 | use crate::InitializationRequiringAction::*; | |
600 | if let UseSpans::ClosureUse { generator_kind, path_span, .. } = self { | |
601 | match generator_kind { | |
602 | Some(_) => { | |
603 | err.subdiagnostic(match action { | |
604 | Borrow => BorrowInGenerator { path_span }, | |
605 | MatchOn | Use => UseInGenerator { path_span }, | |
606 | Assignment => AssignInGenerator { path_span }, | |
607 | PartialAssignment => AssignPartInGenerator { path_span }, | |
608 | }); | |
609 | } | |
610 | None => { | |
611 | err.subdiagnostic(match action { | |
612 | Borrow => BorrowInClosure { path_span }, | |
613 | MatchOn | Use => UseInClosure { path_span }, | |
614 | Assignment => AssignInClosure { path_span }, | |
615 | PartialAssignment => AssignPartInClosure { path_span }, | |
616 | }); | |
617 | } | |
618 | } | |
17df50a5 XL |
619 | } |
620 | } | |
621 | ||
487cf647 | 622 | /// Add a span label to the use of the captured variable, if it exists. |
9fa01778 XL |
623 | pub(super) fn var_span_label( |
624 | self, | |
5e7ed085 | 625 | err: &mut Diagnostic, |
9fa01778 | 626 | message: impl Into<String>, |
17df50a5 | 627 | kind_desc: impl Into<String>, |
9fa01778 | 628 | ) { |
17df50a5 XL |
629 | if let UseSpans::ClosureUse { capture_kind_span, path_span, .. } = self { |
630 | if capture_kind_span == path_span { | |
631 | err.span_label(capture_kind_span, message); | |
632 | } else { | |
633 | let capture_kind_label = | |
634 | format!("capture is {} because of use here", kind_desc.into()); | |
635 | let path_label = message; | |
636 | err.span_label(capture_kind_span, capture_kind_label); | |
637 | err.span_label(path_span, path_label); | |
638 | } | |
b7449926 XL |
639 | } |
640 | } | |
641 | ||
487cf647 FG |
642 | /// Add a subdiagnostic to the use of the captured variable, if it exists. |
643 | pub(super) fn var_subdiag( | |
644 | self, | |
645 | err: &mut Diagnostic, | |
646 | kind: Option<rustc_middle::mir::BorrowKind>, | |
647 | f: impl Fn(Option<GeneratorKind>, Span) -> crate::session_diagnostics::CaptureVarCause, | |
648 | ) { | |
649 | use crate::session_diagnostics::CaptureVarKind::*; | |
650 | if let UseSpans::ClosureUse { generator_kind, capture_kind_span, path_span, .. } = self { | |
651 | if capture_kind_span != path_span { | |
652 | err.subdiagnostic(match kind { | |
653 | Some(kd) => match kd { | |
654 | rustc_middle::mir::BorrowKind::Shared | |
655 | | rustc_middle::mir::BorrowKind::Shallow | |
656 | | rustc_middle::mir::BorrowKind::Unique => { | |
657 | Immute { kind_span: capture_kind_span } | |
658 | } | |
659 | ||
660 | rustc_middle::mir::BorrowKind::Mut { .. } => { | |
661 | Mut { kind_span: capture_kind_span } | |
662 | } | |
663 | }, | |
664 | None => Move { kind_span: capture_kind_span }, | |
665 | }); | |
666 | }; | |
667 | err.subdiagnostic(f(generator_kind, path_span)); | |
668 | } | |
669 | } | |
670 | ||
9fa01778 | 671 | /// Returns `false` if this place is not used in a closure. |
dc9dc135 | 672 | pub(super) fn for_closure(&self) -> bool { |
0bf4aa26 | 673 | match *self { |
e74abb32 | 674 | UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_none(), |
0bf4aa26 XL |
675 | _ => false, |
676 | } | |
677 | } | |
678 | ||
9fa01778 | 679 | /// Returns `false` if this place is not used in a generator. |
dc9dc135 | 680 | pub(super) fn for_generator(&self) -> bool { |
0bf4aa26 | 681 | match *self { |
e74abb32 | 682 | UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_some(), |
0bf4aa26 XL |
683 | _ => false, |
684 | } | |
685 | } | |
686 | ||
687 | /// Describe the span associated with a use of a place. | |
064997fb | 688 | pub(super) fn describe(&self) -> &str { |
0bf4aa26 | 689 | match *self { |
dfeec247 XL |
690 | UseSpans::ClosureUse { generator_kind, .. } => { |
691 | if generator_kind.is_some() { | |
064997fb | 692 | " in generator" |
dfeec247 | 693 | } else { |
064997fb | 694 | " in closure" |
dfeec247 XL |
695 | } |
696 | } | |
064997fb | 697 | _ => "", |
b7449926 XL |
698 | } |
699 | } | |
700 | ||
701 | pub(super) fn or_else<F>(self, if_other: F) -> Self | |
702 | where | |
703 | F: FnOnce() -> Self, | |
704 | { | |
705 | match self { | |
706 | closure @ UseSpans::ClosureUse { .. } => closure, | |
f035d41b XL |
707 | UseSpans::PatUse(_) | UseSpans::OtherUse(_) => if_other(), |
708 | fn_self @ UseSpans::FnSelfUse { .. } => fn_self, | |
b7449926 XL |
709 | } |
710 | } | |
711 | } | |
712 | ||
416331ca XL |
713 | pub(super) enum BorrowedContentSource<'tcx> { |
714 | DerefRawPointer, | |
715 | DerefMutableRef, | |
716 | DerefSharedRef, | |
717 | OverloadedDeref(Ty<'tcx>), | |
718 | OverloadedIndex(Ty<'tcx>), | |
719 | } | |
720 | ||
a2a8927a | 721 | impl<'tcx> BorrowedContentSource<'tcx> { |
ba9703b0 | 722 | pub(super) fn describe_for_unnamed_place(&self, tcx: TyCtxt<'_>) -> String { |
416331ca | 723 | match *self { |
74b04a01 XL |
724 | BorrowedContentSource::DerefRawPointer => "a raw pointer".to_string(), |
725 | BorrowedContentSource::DerefSharedRef => "a shared reference".to_string(), | |
726 | BorrowedContentSource::DerefMutableRef => "a mutable reference".to_string(), | |
a2a8927a XL |
727 | BorrowedContentSource::OverloadedDeref(ty) => ty |
728 | .ty_adt_def() | |
5e7ed085 | 729 | .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? { |
9c376795 | 730 | name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")), |
a2a8927a XL |
731 | _ => None, |
732 | }) | |
9c376795 FG |
733 | .unwrap_or_else(|| format!("dereference of `{ty}`")), |
734 | BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{ty}`"), | |
416331ca XL |
735 | } |
736 | } | |
737 | ||
738 | pub(super) fn describe_for_named_place(&self) -> Option<&'static str> { | |
739 | match *self { | |
740 | BorrowedContentSource::DerefRawPointer => Some("raw pointer"), | |
741 | BorrowedContentSource::DerefSharedRef => Some("shared reference"), | |
742 | BorrowedContentSource::DerefMutableRef => Some("mutable reference"), | |
743 | // Overloaded deref and index operators should be evaluated into a | |
744 | // temporary. So we don't need a description here. | |
745 | BorrowedContentSource::OverloadedDeref(_) | |
dfeec247 | 746 | | BorrowedContentSource::OverloadedIndex(_) => None, |
416331ca XL |
747 | } |
748 | } | |
749 | ||
ba9703b0 | 750 | pub(super) fn describe_for_immutable_place(&self, tcx: TyCtxt<'_>) -> String { |
416331ca | 751 | match *self { |
74b04a01 XL |
752 | BorrowedContentSource::DerefRawPointer => "a `*const` pointer".to_string(), |
753 | BorrowedContentSource::DerefSharedRef => "a `&` reference".to_string(), | |
416331ca | 754 | BorrowedContentSource::DerefMutableRef => { |
dfeec247 XL |
755 | bug!("describe_for_immutable_place: DerefMutableRef isn't immutable") |
756 | } | |
a2a8927a XL |
757 | BorrowedContentSource::OverloadedDeref(ty) => ty |
758 | .ty_adt_def() | |
5e7ed085 | 759 | .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? { |
9c376795 | 760 | name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")), |
a2a8927a XL |
761 | _ => None, |
762 | }) | |
9c376795 FG |
763 | .unwrap_or_else(|| format!("dereference of `{ty}`")), |
764 | BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{ty}`"), | |
416331ca XL |
765 | } |
766 | } | |
767 | ||
768 | fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> { | |
1b1a35ee | 769 | match *func.kind() { |
416331ca XL |
770 | ty::FnDef(def_id, substs) => { |
771 | let trait_id = tcx.trait_of_item(def_id)?; | |
772 | ||
773 | let lang_items = tcx.lang_items(); | |
774 | if Some(trait_id) == lang_items.deref_trait() | |
775 | || Some(trait_id) == lang_items.deref_mut_trait() | |
776 | { | |
777 | Some(BorrowedContentSource::OverloadedDeref(substs.type_at(0))) | |
778 | } else if Some(trait_id) == lang_items.index_trait() | |
779 | || Some(trait_id) == lang_items.index_mut_trait() | |
780 | { | |
781 | Some(BorrowedContentSource::OverloadedIndex(substs.type_at(0))) | |
782 | } else { | |
783 | None | |
784 | } | |
785 | } | |
786 | _ => None, | |
787 | } | |
788 | } | |
789 | } | |
790 | ||
dc9dc135 | 791 | impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { |
b7449926 XL |
792 | /// Finds the spans associated to a move or copy of move_place at location. |
793 | pub(super) fn move_spans( | |
794 | &self, | |
74b04a01 | 795 | moved_place: PlaceRef<'tcx>, // Could also be an upvar. |
b7449926 | 796 | location: Location, |
1b1a35ee | 797 | ) -> UseSpans<'tcx> { |
b7449926 | 798 | use self::UseSpans::*; |
b7449926 | 799 | |
5e7ed085 FG |
800 | let Some(stmt) = self.body[location.block].statements.get(location.statement_index) else { |
801 | return OtherUse(self.body.source_info(location).span); | |
b7449926 XL |
802 | }; |
803 | ||
0bf4aa26 | 804 | debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt); |
487cf647 FG |
805 | if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind |
806 | && let AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) = **kind | |
807 | { | |
808 | debug!("move_spans: def_id={:?} places={:?}", def_id, places); | |
9ffffee4 | 809 | let def_id = def_id.expect_local(); |
487cf647 FG |
810 | if let Some((args_span, generator_kind, capture_kind_span, path_span)) = |
811 | self.closure_span(def_id, moved_place, places) | |
812 | { | |
813 | return ClosureUse { | |
814 | generator_kind, | |
815 | args_span, | |
816 | capture_kind_span, | |
817 | path_span, | |
818 | }; | |
f035d41b XL |
819 | } |
820 | } | |
821 | ||
cdc7bbd5 XL |
822 | // StatementKind::FakeRead only contains a def_id if they are introduced as a result |
823 | // of pattern matching within a closure. | |
487cf647 | 824 | if let StatementKind::FakeRead(box (cause, place)) = stmt.kind { |
cdc7bbd5 XL |
825 | match cause { |
826 | FakeReadCause::ForMatchedPlace(Some(closure_def_id)) | |
827 | | FakeReadCause::ForLet(Some(closure_def_id)) => { | |
828 | debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place); | |
487cf647 | 829 | let places = &[Operand::Move(place)]; |
17df50a5 | 830 | if let Some((args_span, generator_kind, capture_kind_span, path_span)) = |
353b0b11 | 831 | self.closure_span(closure_def_id, moved_place, IndexSlice::from_raw(places)) |
cdc7bbd5 | 832 | { |
17df50a5 XL |
833 | return ClosureUse { |
834 | generator_kind, | |
835 | args_span, | |
836 | capture_kind_span, | |
837 | path_span, | |
838 | }; | |
cdc7bbd5 XL |
839 | } |
840 | } | |
841 | _ => {} | |
842 | } | |
843 | } | |
844 | ||
f035d41b XL |
845 | let normal_ret = |
846 | if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) { | |
847 | PatUse(stmt.source_info.span) | |
848 | } else { | |
849 | OtherUse(stmt.source_info.span) | |
0bf4aa26 XL |
850 | }; |
851 | ||
f035d41b XL |
852 | // We are trying to find MIR of the form: |
853 | // ``` | |
854 | // _temp = _moved_val; | |
855 | // ... | |
856 | // FnSelfCall(_temp, ...) | |
857 | // ``` | |
858 | // | |
859 | // where `_moved_val` is the place we generated the move error for, | |
860 | // `_temp` is some other local, and `FnSelfCall` is a function | |
861 | // that has a `self` parameter. | |
862 | ||
863 | let target_temp = match stmt.kind { | |
864 | StatementKind::Assign(box (temp, _)) if temp.as_local().is_some() => { | |
865 | temp.as_local().unwrap() | |
b7449926 | 866 | } |
f035d41b XL |
867 | _ => return normal_ret, |
868 | }; | |
b7449926 | 869 | |
f035d41b XL |
870 | debug!("move_spans: target_temp = {:?}", target_temp); |
871 | ||
872 | if let Some(Terminator { | |
1b1a35ee | 873 | kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, .. |
f035d41b XL |
874 | }) = &self.body[location.block].terminator |
875 | { | |
5099ac24 | 876 | let Some((method_did, method_substs)) = |
c295e0f8 XL |
877 | rustc_const_eval::util::find_self_call( |
878 | self.infcx.tcx, | |
879 | &self.body, | |
880 | target_temp, | |
881 | location.block, | |
5099ac24 FG |
882 | ) |
883 | else { | |
1b1a35ee XL |
884 | return normal_ret; |
885 | }; | |
f035d41b | 886 | |
5099ac24 FG |
887 | let kind = call_kind( |
888 | self.infcx.tcx, | |
889 | self.param_env, | |
890 | method_did, | |
891 | method_substs, | |
892 | *fn_span, | |
893 | *from_hir_call, | |
894 | Some(self.infcx.tcx.fn_arg_names(method_did)[0]), | |
1b1a35ee XL |
895 | ); |
896 | ||
1b1a35ee XL |
897 | return FnSelfUse { |
898 | var_span: stmt.source_info.span, | |
5099ac24 | 899 | fn_call_span: *fn_span, |
064997fb | 900 | fn_span: self.infcx.tcx.def_span(method_did), |
1b1a35ee XL |
901 | kind, |
902 | }; | |
f035d41b | 903 | } |
3dfed10e | 904 | normal_ret |
b7449926 XL |
905 | } |
906 | ||
907 | /// Finds the span of arguments of a closure (within `maybe_closure_span`) | |
908 | /// and its usage of the local assigned at `location`. | |
909 | /// This is done by searching in statements succeeding `location` | |
910 | /// and originating from `maybe_closure_span`. | |
1b1a35ee | 911 | pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx> { |
b7449926 | 912 | use self::UseSpans::*; |
0bf4aa26 | 913 | debug!("borrow_spans: use_span={:?} location={:?}", use_span, location); |
b7449926 | 914 | |
dfeec247 | 915 | let target = match self.body[location.block].statements.get(location.statement_index) { |
487cf647 | 916 | Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) => { |
e74abb32 XL |
917 | if let Some(local) = place.as_local() { |
918 | local | |
919 | } else { | |
920 | return OtherUse(use_span); | |
921 | } | |
922 | } | |
b7449926 XL |
923 | _ => return OtherUse(use_span), |
924 | }; | |
925 | ||
dc9dc135 | 926 | if self.body.local_kind(target) != LocalKind::Temp { |
b7449926 XL |
927 | // operands are always temporaries. |
928 | return OtherUse(use_span); | |
929 | } | |
930 | ||
353b0b11 FG |
931 | // drop and replace might have moved the assignment to the next block |
932 | let maybe_additional_statement = | |
933 | if let TerminatorKind::Drop { target: drop_target, .. } = | |
934 | self.body[location.block].terminator().kind | |
935 | { | |
936 | self.body[drop_target].statements.first() | |
937 | } else { | |
938 | None | |
939 | }; | |
940 | ||
941 | let statements = | |
942 | self.body[location.block].statements[location.statement_index + 1..].iter(); | |
943 | ||
944 | for stmt in statements.chain(maybe_additional_statement) { | |
487cf647 | 945 | if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind { |
064997fb | 946 | let (&def_id, is_generator) = match kind { |
0bf4aa26 XL |
947 | box AggregateKind::Closure(def_id, _) => (def_id, false), |
948 | box AggregateKind::Generator(def_id, _, _) => (def_id, true), | |
949 | _ => continue, | |
950 | }; | |
9ffffee4 | 951 | let def_id = def_id.expect_local(); |
b7449926 | 952 | |
0bf4aa26 XL |
953 | debug!( |
954 | "borrow_spans: def_id={:?} is_generator={:?} places={:?}", | |
955 | def_id, is_generator, places | |
956 | ); | |
17df50a5 | 957 | if let Some((args_span, generator_kind, capture_kind_span, path_span)) = |
064997fb | 958 | self.closure_span(def_id, Place::from(target).as_ref(), places) |
dfeec247 | 959 | { |
17df50a5 | 960 | return ClosureUse { generator_kind, args_span, capture_kind_span, path_span }; |
0bf4aa26 XL |
961 | } else { |
962 | return OtherUse(use_span); | |
b7449926 XL |
963 | } |
964 | } | |
965 | ||
966 | if use_span != stmt.source_info.span { | |
967 | break; | |
968 | } | |
969 | } | |
970 | ||
971 | OtherUse(use_span) | |
972 | } | |
973 | ||
17df50a5 XL |
974 | /// Finds the spans of a captured place within a closure or generator. |
975 | /// The first span is the location of the use resulting in the capture kind of the capture | |
976 | /// The second span is the location the use resulting in the captured path of the capture | |
0bf4aa26 XL |
977 | fn closure_span( |
978 | &self, | |
064997fb | 979 | def_id: LocalDefId, |
74b04a01 | 980 | target_place: PlaceRef<'tcx>, |
353b0b11 | 981 | places: &IndexSlice<FieldIdx, Operand<'tcx>>, |
17df50a5 | 982 | ) -> Option<(Span, Option<GeneratorKind>, Span, Span)> { |
0bf4aa26 XL |
983 | debug!( |
984 | "closure_span: def_id={:?} target_place={:?} places={:?}", | |
985 | def_id, target_place, places | |
986 | ); | |
064997fb | 987 | let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(def_id); |
e74abb32 | 988 | let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind; |
532ac7d7 | 989 | debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr); |
064997fb FG |
990 | if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = expr { |
991 | for (captured_place, place) in | |
9ffffee4 | 992 | self.infcx.tcx.closure_captures(def_id).iter().zip(places) |
17df50a5 | 993 | { |
48663c56 | 994 | match place { |
dfeec247 XL |
995 | Operand::Copy(place) | Operand::Move(place) |
996 | if target_place == place.as_ref() => | |
997 | { | |
48663c56 | 998 | debug!("closure_span: found captured local {:?}", place); |
064997fb | 999 | let body = self.infcx.tcx.hir().body(body); |
e74abb32 | 1000 | let generator_kind = body.generator_kind(); |
1b1a35ee | 1001 | |
17df50a5 | 1002 | return Some(( |
064997fb | 1003 | fn_decl_span, |
17df50a5 XL |
1004 | generator_kind, |
1005 | captured_place.get_capture_kind_span(self.infcx.tcx), | |
1006 | captured_place.get_path_span(self.infcx.tcx), | |
1007 | )); | |
dfeec247 | 1008 | } |
48663c56 XL |
1009 | _ => {} |
1010 | } | |
1011 | } | |
0bf4aa26 | 1012 | } |
48663c56 | 1013 | None |
0bf4aa26 XL |
1014 | } |
1015 | ||
b7449926 XL |
1016 | /// Helper to retrieve span(s) of given borrow from the current MIR |
1017 | /// representation | |
1b1a35ee | 1018 | pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx> { |
dc9dc135 | 1019 | let span = self.body.source_info(borrow.reserve_location).span; |
b7449926 XL |
1020 | self.borrow_spans(span, borrow.reserve_location) |
1021 | } | |
5e7ed085 FG |
1022 | |
1023 | fn explain_captures( | |
1024 | &mut self, | |
1025 | err: &mut Diagnostic, | |
1026 | span: Span, | |
1027 | move_span: Span, | |
1028 | move_spans: UseSpans<'tcx>, | |
1029 | moved_place: Place<'tcx>, | |
5e7ed085 FG |
1030 | partially_str: &str, |
1031 | loop_message: &str, | |
1032 | move_msg: &str, | |
1033 | is_loop_move: bool, | |
1034 | maybe_reinitialized_locations_is_empty: bool, | |
1035 | ) { | |
1036 | if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans { | |
1037 | let place_name = self | |
1038 | .describe_place(moved_place.as_ref()) | |
9c376795 | 1039 | .map(|n| format!("`{n}`")) |
5e7ed085 FG |
1040 | .unwrap_or_else(|| "value".to_owned()); |
1041 | match kind { | |
1042 | CallKind::FnCall { fn_trait_id, .. } | |
1043 | if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() => | |
1044 | { | |
1045 | err.span_label( | |
1046 | fn_call_span, | |
1047 | &format!( | |
9c376795 | 1048 | "{place_name} {partially_str}moved due to this call{loop_message}", |
5e7ed085 FG |
1049 | ), |
1050 | ); | |
1051 | err.span_note( | |
1052 | var_span, | |
1053 | "this value implements `FnOnce`, which causes it to be moved when called", | |
1054 | ); | |
1055 | } | |
1056 | CallKind::Operator { self_arg, .. } => { | |
1057 | let self_arg = self_arg.unwrap(); | |
1058 | err.span_label( | |
1059 | fn_call_span, | |
1060 | &format!( | |
9c376795 | 1061 | "{place_name} {partially_str}moved due to usage in operator{loop_message}", |
5e7ed085 FG |
1062 | ), |
1063 | ); | |
1064 | if self.fn_self_span_reported.insert(fn_span) { | |
1065 | err.span_note( | |
9c376795 | 1066 | self_arg.span, |
5e7ed085 FG |
1067 | "calling this operator moves the left-hand side", |
1068 | ); | |
1069 | } | |
1070 | } | |
9ffffee4 | 1071 | CallKind::Normal { self_arg, desugaring, method_did, method_substs } => { |
5e7ed085 | 1072 | let self_arg = self_arg.unwrap(); |
9c376795 | 1073 | let tcx = self.infcx.tcx; |
5e7ed085 | 1074 | if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring { |
9c376795 FG |
1075 | let ty = moved_place.ty(self.body, tcx).ty; |
1076 | let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) { | |
2b03887a FG |
1077 | Some(def_id) => { |
1078 | let infcx = self.infcx.tcx.infer_ctxt().build(); | |
5e7ed085 FG |
1079 | type_known_to_meet_bound_modulo_regions( |
1080 | &infcx, | |
1081 | self.param_env, | |
9c376795 | 1082 | tcx.mk_imm_ref(tcx.lifetimes.re_erased, tcx.erase_regions(ty)), |
5e7ed085 | 1083 | def_id, |
5e7ed085 | 1084 | ) |
2b03887a | 1085 | } |
5e7ed085 FG |
1086 | _ => false, |
1087 | }; | |
1088 | if suggest { | |
1089 | err.span_suggestion_verbose( | |
1090 | move_span.shrink_to_lo(), | |
1091 | &format!( | |
9c376795 | 1092 | "consider iterating over a slice of the `{ty}`'s content to \ |
5e7ed085 | 1093 | avoid moving into the `for` loop", |
5e7ed085 | 1094 | ), |
923072b8 | 1095 | "&", |
5e7ed085 FG |
1096 | Applicability::MaybeIncorrect, |
1097 | ); | |
1098 | } | |
1099 | ||
1100 | err.span_label( | |
1101 | fn_call_span, | |
1102 | &format!( | |
9c376795 | 1103 | "{place_name} {partially_str}moved due to this implicit call to `.into_iter()`{loop_message}", |
5e7ed085 FG |
1104 | ), |
1105 | ); | |
2b03887a FG |
1106 | // If the moved place was a `&mut` ref, then we can |
1107 | // suggest to reborrow it where it was moved, so it | |
1108 | // will still be valid by the time we get to the usage. | |
1109 | if let ty::Ref(_, _, hir::Mutability::Mut) = | |
1110 | moved_place.ty(self.body, self.infcx.tcx).ty.kind() | |
5e7ed085 FG |
1111 | { |
1112 | // If we are in a loop this will be suggested later. | |
1113 | if !is_loop_move { | |
1114 | err.span_suggestion_verbose( | |
1115 | move_span.shrink_to_lo(), | |
1116 | &format!( | |
1117 | "consider creating a fresh reborrow of {} here", | |
1118 | self.describe_place(moved_place.as_ref()) | |
9c376795 | 1119 | .map(|n| format!("`{n}`")) |
5e7ed085 FG |
1120 | .unwrap_or_else(|| "the mutable reference".to_string()), |
1121 | ), | |
923072b8 | 1122 | "&mut *", |
5e7ed085 FG |
1123 | Applicability::MachineApplicable, |
1124 | ); | |
1125 | } | |
1126 | } | |
1127 | } else { | |
1128 | err.span_label( | |
1129 | fn_call_span, | |
1130 | &format!( | |
9c376795 | 1131 | "{place_name} {partially_str}moved due to this method call{loop_message}", |
5e7ed085 FG |
1132 | ), |
1133 | ); | |
9ffffee4 | 1134 | |
9c376795 | 1135 | let infcx = tcx.infer_ctxt().build(); |
9ffffee4 | 1136 | // Erase and shadow everything that could be passed to the new infcx. |
9c376795 | 1137 | let ty = tcx.erase_regions(moved_place.ty(self.body, tcx).ty); |
9ffffee4 FG |
1138 | let method_substs = tcx.erase_regions(method_substs); |
1139 | ||
9c376795 FG |
1140 | if let ty::Adt(def, substs) = ty.kind() |
1141 | && Some(def.did()) == tcx.lang_items().pin_type() | |
1142 | && let ty::Ref(_, _, hir::Mutability::Mut) = substs.type_at(0).kind() | |
9ffffee4 | 1143 | && let self_ty = infcx.instantiate_binder_with_fresh_vars( |
9c376795 FG |
1144 | fn_call_span, |
1145 | LateBoundRegionConversionTime::FnCall, | |
9ffffee4 | 1146 | tcx.fn_sig(method_did).subst(tcx, method_substs).input(0), |
9c376795 | 1147 | ) |
9ffffee4 | 1148 | && infcx.can_eq(self.param_env, ty, self_ty) |
9c376795 FG |
1149 | { |
1150 | err.span_suggestion_verbose( | |
1151 | fn_call_span.shrink_to_lo(), | |
1152 | "consider reborrowing the `Pin` instead of moving it", | |
1153 | "as_mut().".to_string(), | |
1154 | Applicability::MaybeIncorrect, | |
1155 | ); | |
1156 | } | |
1157 | if let Some(clone_trait) = tcx.lang_items().clone_trait() | |
1158 | && let trait_ref = tcx.mk_trait_ref(clone_trait, [ty]) | |
1159 | && let o = Obligation::new( | |
1160 | tcx, | |
1161 | ObligationCause::dummy(), | |
1162 | self.param_env, | |
1163 | ty::Binder::dummy(trait_ref), | |
1164 | ) | |
1165 | && infcx.predicate_must_hold_modulo_regions(&o) | |
1166 | { | |
1167 | err.span_suggestion_verbose( | |
1168 | fn_call_span.shrink_to_lo(), | |
1169 | "you can `clone` the value and consume it, but this might not be \ | |
1170 | your desired behavior", | |
1171 | "clone().".to_string(), | |
1172 | Applicability::MaybeIncorrect, | |
1173 | ); | |
1174 | } | |
5e7ed085 | 1175 | } |
5e7ed085 FG |
1176 | // Avoid pointing to the same function in multiple different |
1177 | // error messages. | |
1178 | if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) { | |
9c376795 | 1179 | let func = tcx.def_path_str(method_did); |
5e7ed085 FG |
1180 | err.span_note( |
1181 | self_arg.span, | |
9c376795 | 1182 | &format!("`{func}` takes ownership of the receiver `self`, which moves {place_name}") |
5e7ed085 FG |
1183 | ); |
1184 | } | |
9c376795 | 1185 | let parent_did = tcx.parent(method_did); |
9ffffee4 FG |
1186 | let parent_self_ty = |
1187 | matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. }) | |
1188 | .then_some(parent_did) | |
1189 | .and_then(|did| match tcx.type_of(did).subst_identity().kind() { | |
1190 | ty::Adt(def, ..) => Some(def.did()), | |
1191 | _ => None, | |
1192 | }); | |
9c376795 FG |
1193 | let is_option_or_result = parent_self_ty.map_or(false, |def_id| { |
1194 | matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result)) | |
1195 | }); | |
f2b60f7d FG |
1196 | if is_option_or_result && maybe_reinitialized_locations_is_empty { |
1197 | err.span_label( | |
1198 | var_span, | |
1199 | "help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents", | |
1200 | ); | |
1201 | } | |
5e7ed085 FG |
1202 | } |
1203 | // Other desugarings takes &self, which cannot cause a move | |
1204 | _ => {} | |
1205 | } | |
1206 | } else { | |
1207 | if move_span != span || !loop_message.is_empty() { | |
1208 | err.span_label( | |
1209 | move_span, | |
9c376795 | 1210 | format!("value {partially_str}moved{move_msg} here{loop_message}"), |
5e7ed085 FG |
1211 | ); |
1212 | } | |
1213 | // If the move error occurs due to a loop, don't show | |
1214 | // another message for the same span | |
1215 | if loop_message.is_empty() { | |
1216 | move_spans.var_span_label( | |
1217 | err, | |
9c376795 | 1218 | format!("variable {partially_str}moved due to use{}", move_spans.describe()), |
5e7ed085 FG |
1219 | "moved", |
1220 | ); | |
1221 | } | |
1222 | } | |
1223 | } | |
ff7c6d11 | 1224 | } |