]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/error_reporting.rs
Auto merge of #61361 - estebank:infer-type, r=varkor
[rust.git] / src / librustc_mir / borrow_check / error_reporting.rs
1 use rustc::hir;
2 use rustc::hir::def::Namespace;
3 use rustc::hir::def_id::DefId;
4 use rustc::mir::{
5     AggregateKind, BindingForm, ClearCrossCrate, Constant, Field, Local,
6     LocalKind, Location, Operand, Place, PlaceBase, ProjectionElem, Rvalue,
7     Statement, StatementKind, Static, StaticKind, TerminatorKind,
8 };
9 use rustc::ty::{self, DefIdTree, Ty};
10 use rustc::ty::layout::VariantIdx;
11 use rustc::ty::print::Print;
12 use rustc_errors::DiagnosticBuilder;
13 use syntax_pos::Span;
14 use syntax::symbol::sym;
15
16 use super::borrow_set::BorrowData;
17 use super::{MirBorrowckCtxt};
18
19 pub(super) struct IncludingDowncast(pub(super) bool);
20
21 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
22     /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
23     /// is moved after being invoked.
24     ///
25     /// ```text
26     /// note: closure cannot be invoked more than once because it moves the variable `dict` out of
27     ///       its environment
28     ///   --> $DIR/issue-42065.rs:16:29
29     ///    |
30     /// LL |         for (key, value) in dict {
31     ///    |                             ^^^^
32     /// ```
33     pub(super) fn add_moved_or_invoked_closure_note(
34         &self,
35         location: Location,
36         place: &Place<'tcx>,
37         diag: &mut DiagnosticBuilder<'_>,
38     ) {
39         debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
40         let mut target = place.local_or_deref_local();
41         for stmt in &self.mir[location.block].statements[location.statement_index..] {
42             debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
43             if let StatementKind::Assign(into, box Rvalue::Use(from)) = &stmt.kind {
44                 debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
45                 match from {
46                     Operand::Copy(ref place) |
47                     Operand::Move(ref place) if target == place.local_or_deref_local() =>
48                         target = into.local_or_deref_local(),
49                     _ => {},
50                 }
51             }
52         }
53
54         // Check if we are attempting to call a closure after it has been invoked.
55         let terminator = self.mir[location.block].terminator();
56         debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
57         if let TerminatorKind::Call {
58             func: Operand::Constant(box Constant {
59                 literal: ty::Const {
60                     ty: &ty::TyS { sty: ty::FnDef(id, _), ..  },
61                     ..
62                 },
63                 ..
64             }),
65             args,
66             ..
67         } = &terminator.kind {
68             debug!("add_moved_or_invoked_closure_note: id={:?}", id);
69             if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() {
70                 let closure = match args.first() {
71                     Some(Operand::Copy(ref place)) |
72                     Some(Operand::Move(ref place)) if target == place.local_or_deref_local() =>
73                         place.local_or_deref_local().unwrap(),
74                     _ => return,
75                 };
76
77                 debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
78                 if let ty::Closure(did, _) = self.mir.local_decls[closure].ty.sty {
79                     let hir_id = self.infcx.tcx.hir().as_local_hir_id(did).unwrap();
80
81                     if let Some((span, name)) = self.infcx.tcx.typeck_tables_of(did)
82                         .closure_kind_origins()
83                         .get(hir_id)
84                     {
85                         diag.span_note(
86                             *span,
87                             &format!(
88                                 "closure cannot be invoked more than once because it moves the \
89                                  variable `{}` out of its environment",
90                                 name,
91                             ),
92                         );
93                         return;
94                     }
95                 }
96             }
97         }
98
99         // Check if we are just moving a closure after it has been invoked.
100         if let Some(target) = target {
101             if let ty::Closure(did, _) = self.mir.local_decls[target].ty.sty {
102                 let hir_id = self.infcx.tcx.hir().as_local_hir_id(did).unwrap();
103
104                 if let Some((span, name)) = self.infcx.tcx.typeck_tables_of(did)
105                     .closure_kind_origins()
106                     .get(hir_id)
107                 {
108                     diag.span_note(
109                         *span,
110                         &format!(
111                             "closure cannot be moved more than once as it is not `Copy` due to \
112                              moving the variable `{}` out of its environment",
113                              name
114                         ),
115                     );
116                 }
117             }
118         }
119     }
120
121     /// End-user visible description of `place` if one can be found. If the
122     /// place is a temporary for instance, None will be returned.
123     pub(super) fn describe_place(&self, place: &Place<'tcx>) -> Option<String> {
124         self.describe_place_with_options(place, IncludingDowncast(false))
125     }
126
127     /// End-user visible description of `place` if one can be found. If the
128     /// place is a temporary for instance, None will be returned.
129     /// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
130     /// `Downcast` and `IncludingDowncast` is true
131     pub(super) fn describe_place_with_options(
132         &self,
133         place: &Place<'tcx>,
134         including_downcast: IncludingDowncast,
135     ) -> Option<String> {
136         let mut buf = String::new();
137         match self.append_place_to_string(place, &mut buf, false, &including_downcast) {
138             Ok(()) => Some(buf),
139             Err(()) => None,
140         }
141     }
142
143     /// Appends end-user visible description of `place` to `buf`.
144     fn append_place_to_string(
145         &self,
146         place: &Place<'tcx>,
147         buf: &mut String,
148         mut autoderef: bool,
149         including_downcast: &IncludingDowncast,
150     ) -> Result<(), ()> {
151         match *place {
152             Place::Base(PlaceBase::Local(local)) => {
153                 self.append_local_to_string(local, buf)?;
154             }
155             Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. })) => {
156                 buf.push_str("promoted");
157             }
158             Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. })) => {
159                 buf.push_str(&self.infcx.tcx.item_name(def_id).to_string());
160             }
161             Place::Projection(ref proj) => {
162                 match proj.elem {
163                     ProjectionElem::Deref => {
164                         let upvar_field_projection =
165                             self.is_upvar_field_projection(place);
166                         if let Some(field) = upvar_field_projection {
167                             let var_index = field.index();
168                             let name = self.upvars[var_index].name.to_string();
169                             if self.upvars[var_index].by_ref {
170                                 buf.push_str(&name);
171                             } else {
172                                 buf.push_str(&format!("*{}", &name));
173                             }
174                         } else {
175                             if autoderef {
176                                 self.append_place_to_string(
177                                     &proj.base,
178                                     buf,
179                                     autoderef,
180                                     &including_downcast,
181                                 )?;
182                             } else if let Place::Base(PlaceBase::Local(local)) = proj.base {
183                                 if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) =
184                                     self.mir.local_decls[local].is_user_variable
185                                 {
186                                     self.append_place_to_string(
187                                         &proj.base,
188                                         buf,
189                                         autoderef,
190                                         &including_downcast,
191                                     )?;
192                                 } else {
193                                     buf.push_str(&"*");
194                                     self.append_place_to_string(
195                                         &proj.base,
196                                         buf,
197                                         autoderef,
198                                         &including_downcast,
199                                     )?;
200                                 }
201                             } else {
202                                 buf.push_str(&"*");
203                                 self.append_place_to_string(
204                                     &proj.base,
205                                     buf,
206                                     autoderef,
207                                     &including_downcast,
208                                 )?;
209                             }
210                         }
211                     }
212                     ProjectionElem::Downcast(..) => {
213                         self.append_place_to_string(
214                             &proj.base,
215                             buf,
216                             autoderef,
217                             &including_downcast,
218                         )?;
219                         if including_downcast.0 {
220                             return Err(());
221                         }
222                     }
223                     ProjectionElem::Field(field, _ty) => {
224                         autoderef = true;
225
226                         let upvar_field_projection =
227                             self.is_upvar_field_projection(place);
228                         if let Some(field) = upvar_field_projection {
229                             let var_index = field.index();
230                             let name = self.upvars[var_index].name.to_string();
231                             buf.push_str(&name);
232                         } else {
233                             let field_name = self.describe_field(&proj.base, field);
234                             self.append_place_to_string(
235                                 &proj.base,
236                                 buf,
237                                 autoderef,
238                                 &including_downcast,
239                             )?;
240                             buf.push_str(&format!(".{}", field_name));
241                         }
242                     }
243                     ProjectionElem::Index(index) => {
244                         autoderef = true;
245
246                         self.append_place_to_string(
247                             &proj.base,
248                             buf,
249                             autoderef,
250                             &including_downcast,
251                         )?;
252                         buf.push_str("[");
253                         if self.append_local_to_string(index, buf).is_err() {
254                             buf.push_str("_");
255                         }
256                         buf.push_str("]");
257                     }
258                     ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
259                         autoderef = true;
260                         // Since it isn't possible to borrow an element on a particular index and
261                         // then use another while the borrow is held, don't output indices details
262                         // to avoid confusing the end-user
263                         self.append_place_to_string(
264                             &proj.base,
265                             buf,
266                             autoderef,
267                             &including_downcast,
268                         )?;
269                         buf.push_str(&"[..]");
270                     }
271                 };
272             }
273         }
274
275         Ok(())
276     }
277
278     /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
279     /// a name, or its name was generated by the compiler, then `Err` is returned
280     fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
281         let local = &self.mir.local_decls[local_index];
282         match local.name {
283             Some(name) if !local.from_compiler_desugaring() => {
284                 buf.push_str(name.as_str().get());
285                 Ok(())
286             }
287             _ => Err(()),
288         }
289     }
290
291     /// End-user visible description of the `field`nth field of `base`
292     fn describe_field(&self, base: &Place<'tcx>, field: Field) -> String {
293         match *base {
294             Place::Base(PlaceBase::Local(local)) => {
295                 let local = &self.mir.local_decls[local];
296                 self.describe_field_from_ty(&local.ty, field, None)
297             }
298             Place::Base(PlaceBase::Static(ref static_)) =>
299                 self.describe_field_from_ty(&static_.ty, field, None),
300             Place::Projection(ref proj) => match proj.elem {
301                 ProjectionElem::Deref => self.describe_field(&proj.base, field),
302                 ProjectionElem::Downcast(_, variant_index) => {
303                     let base_ty = base.ty(self.mir, self.infcx.tcx).ty;
304                     self.describe_field_from_ty(&base_ty, field, Some(variant_index))
305                 }
306                 ProjectionElem::Field(_, field_type) => {
307                     self.describe_field_from_ty(&field_type, field, None)
308                 }
309                 ProjectionElem::Index(..)
310                 | ProjectionElem::ConstantIndex { .. }
311                 | ProjectionElem::Subslice { .. } => {
312                     self.describe_field(&proj.base, field)
313                 }
314             },
315         }
316     }
317
318     /// End-user visible description of the `field_index`nth field of `ty`
319     fn describe_field_from_ty(
320         &self,
321         ty: Ty<'_>,
322         field: Field,
323         variant_index: Option<VariantIdx>
324     ) -> String {
325         if ty.is_box() {
326             // If the type is a box, the field is described from the boxed type
327             self.describe_field_from_ty(&ty.boxed_ty(), field, variant_index)
328         } else {
329             match ty.sty {
330                 ty::Adt(def, _) => {
331                     let variant = if let Some(idx) = variant_index {
332                         assert!(def.is_enum());
333                         &def.variants[idx]
334                     } else {
335                         def.non_enum_variant()
336                     };
337                     variant.fields[field.index()]
338                         .ident
339                         .to_string()
340                 },
341                 ty::Tuple(_) => field.index().to_string(),
342                 ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
343                     self.describe_field_from_ty(&ty, field, variant_index)
344                 }
345                 ty::Array(ty, _) | ty::Slice(ty) =>
346                     self.describe_field_from_ty(&ty, field, variant_index),
347                 ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
348                     // `tcx.upvars(def_id)` returns an `Option`, which is `None` in case
349                     // the closure comes from another crate. But in that case we wouldn't
350                     // be borrowck'ing it, so we can just unwrap:
351                     let (&var_id, _) = self.infcx.tcx.upvars(def_id).unwrap()
352                         .get_index(field.index()).unwrap();
353
354                     self.infcx.tcx.hir().name_by_hir_id(var_id).to_string()
355                 }
356                 _ => {
357                     // Might need a revision when the fields in trait RFC is implemented
358                     // (https://github.com/rust-lang/rfcs/pull/1546)
359                     bug!(
360                         "End-user description not implemented for field access on `{:?}`",
361                         ty
362                     );
363                 }
364             }
365         }
366     }
367
368     /// Checks if a place is a thread-local static.
369     pub fn is_place_thread_local(&self, place: &Place<'tcx>) -> bool {
370         if let Place::Base(
371             PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. })
372         ) = place {
373             let attrs = self.infcx.tcx.get_attrs(*def_id);
374             let is_thread_local = attrs.iter().any(|attr| attr.check_name(sym::thread_local));
375
376             debug!(
377                 "is_place_thread_local: attrs={:?} is_thread_local={:?}",
378                 attrs, is_thread_local
379             );
380             is_thread_local
381         } else {
382             debug!("is_place_thread_local: no");
383             false
384         }
385     }
386 }
387
388 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
389     /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
390     /// name where required.
391     pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
392         let mut s = String::new();
393         let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
394
395         // We need to add synthesized lifetimes where appropriate. We do
396         // this by hooking into the pretty printer and telling it to label the
397         // lifetimes without names with the value `'0`.
398         match ty.sty {
399             ty::Ref(ty::RegionKind::ReLateBound(_, br), _, _)
400             | ty::Ref(
401                 ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }),
402                 _,
403                 _,
404             ) => printer.region_highlight_mode.highlighting_bound_region(*br, counter),
405             _ => {}
406         }
407
408         let _ = ty.print(printer);
409         s
410     }
411
412     /// Returns the name of the provided `Ty` (that must be a reference)'s region with a
413     /// synthesized lifetime name where required.
414     pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
415         let mut s = String::new();
416         let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
417
418         let region = match ty.sty {
419             ty::Ref(region, _, _) => {
420                 match region {
421                     ty::RegionKind::ReLateBound(_, br)
422                     | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => {
423                         printer.region_highlight_mode.highlighting_bound_region(*br, counter)
424                     }
425                     _ => {}
426                 }
427
428                 region
429             }
430             _ => bug!("ty for annotation of borrow region is not a reference"),
431         };
432
433         let _ = region.print(printer);
434         s
435     }
436 }
437
438 // The span(s) associated to a use of a place.
439 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
440 pub(super) enum UseSpans {
441     // The access is caused by capturing a variable for a closure.
442     ClosureUse {
443         // This is true if the captured variable was from a generator.
444         is_generator: bool,
445         // The span of the args of the closure, including the `move` keyword if
446         // it's present.
447         args_span: Span,
448         // The span of the first use of the captured variable inside the closure.
449         var_span: Span,
450     },
451     // This access has a single span associated to it: common case.
452     OtherUse(Span),
453 }
454
455 impl UseSpans {
456     pub(super) fn args_or_use(self) -> Span {
457         match self {
458             UseSpans::ClosureUse {
459                 args_span: span, ..
460             }
461             | UseSpans::OtherUse(span) => span,
462         }
463     }
464
465     pub(super) fn var_or_use(self) -> Span {
466         match self {
467             UseSpans::ClosureUse { var_span: span, .. } | UseSpans::OtherUse(span) => span,
468         }
469     }
470
471     // Add a span label to the arguments of the closure, if it exists.
472     pub(super) fn args_span_label(
473         self,
474         err: &mut DiagnosticBuilder<'_>,
475         message: impl Into<String>,
476     ) {
477         if let UseSpans::ClosureUse { args_span, .. } = self {
478             err.span_label(args_span, message);
479         }
480     }
481
482     // Add a span label to the use of the captured variable, if it exists.
483     pub(super) fn var_span_label(
484         self,
485         err: &mut DiagnosticBuilder<'_>,
486         message: impl Into<String>,
487     ) {
488         if let UseSpans::ClosureUse { var_span, .. } = self {
489             err.span_label(var_span, message);
490         }
491     }
492
493     /// Returns `false` if this place is not used in a closure.
494     pub(super) fn for_closure(&self) -> bool {
495         match *self {
496             UseSpans::ClosureUse { is_generator, .. } => !is_generator,
497             _ => false,
498         }
499     }
500
501     /// Returns `false` if this place is not used in a generator.
502     pub(super) fn for_generator(&self) -> bool {
503         match *self {
504             UseSpans::ClosureUse { is_generator, .. } => is_generator,
505             _ => false,
506         }
507     }
508
509     /// Describe the span associated with a use of a place.
510     pub(super) fn describe(&self) -> String {
511         match *self {
512             UseSpans::ClosureUse { is_generator, .. } => if is_generator {
513                 " in generator".to_string()
514             } else {
515                 " in closure".to_string()
516             },
517             _ => "".to_string(),
518         }
519     }
520
521     pub(super) fn or_else<F>(self, if_other: F) -> Self
522     where
523         F: FnOnce() -> Self,
524     {
525         match self {
526             closure @ UseSpans::ClosureUse { .. } => closure,
527             UseSpans::OtherUse(_) => if_other(),
528         }
529     }
530 }
531
532 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
533     /// Finds the spans associated to a move or copy of move_place at location.
534     pub(super) fn move_spans(
535         &self,
536         moved_place: &Place<'tcx>, // Could also be an upvar.
537         location: Location,
538     ) -> UseSpans {
539         use self::UseSpans::*;
540
541         let stmt = match self.mir[location.block].statements.get(location.statement_index) {
542             Some(stmt) => stmt,
543             None => return OtherUse(self.mir.source_info(location).span),
544         };
545
546         debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
547         if let  StatementKind::Assign(
548             _,
549             box Rvalue::Aggregate(ref kind, ref places)
550         ) = stmt.kind {
551             let (def_id, is_generator) = match kind {
552                 box AggregateKind::Closure(def_id, _) => (def_id, false),
553                 box AggregateKind::Generator(def_id, _, _) => (def_id, true),
554                 _ => return OtherUse(stmt.source_info.span),
555             };
556
557             debug!(
558                 "move_spans: def_id={:?} is_generator={:?} places={:?}",
559                 def_id, is_generator, places
560             );
561             if let Some((args_span, var_span)) = self.closure_span(*def_id, moved_place, places) {
562                 return ClosureUse {
563                     is_generator,
564                     args_span,
565                     var_span,
566                 };
567             }
568         }
569
570         OtherUse(stmt.source_info.span)
571     }
572
573     /// Finds the span of arguments of a closure (within `maybe_closure_span`)
574     /// and its usage of the local assigned at `location`.
575     /// This is done by searching in statements succeeding `location`
576     /// and originating from `maybe_closure_span`.
577     pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans {
578         use self::UseSpans::*;
579         debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
580
581         let target = match self.mir[location.block]
582             .statements
583             .get(location.statement_index)
584         {
585             Some(&Statement {
586                 kind: StatementKind::Assign(Place::Base(PlaceBase::Local(local)), _),
587                 ..
588             }) => local,
589             _ => return OtherUse(use_span),
590         };
591
592         if self.mir.local_kind(target) != LocalKind::Temp {
593             // operands are always temporaries.
594             return OtherUse(use_span);
595         }
596
597         for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
598             if let StatementKind::Assign(
599                 _, box Rvalue::Aggregate(ref kind, ref places)
600             ) = stmt.kind {
601                 let (def_id, is_generator) = match kind {
602                     box AggregateKind::Closure(def_id, _) => (def_id, false),
603                     box AggregateKind::Generator(def_id, _, _) => (def_id, true),
604                     _ => continue,
605                 };
606
607                 debug!(
608                     "borrow_spans: def_id={:?} is_generator={:?} places={:?}",
609                     def_id, is_generator, places
610                 );
611                 if let Some((args_span, var_span)) = self.closure_span(
612                     *def_id, &Place::Base(PlaceBase::Local(target)), places
613                 ) {
614                     return ClosureUse {
615                         is_generator,
616                         args_span,
617                         var_span,
618                     };
619                 } else {
620                     return OtherUse(use_span);
621                 }
622             }
623
624             if use_span != stmt.source_info.span {
625                 break;
626             }
627         }
628
629         OtherUse(use_span)
630     }
631
632     /// Finds the span of a captured variable within a closure or generator.
633     fn closure_span(
634         &self,
635         def_id: DefId,
636         target_place: &Place<'tcx>,
637         places: &Vec<Operand<'tcx>>,
638     ) -> Option<(Span, Span)> {
639         debug!(
640             "closure_span: def_id={:?} target_place={:?} places={:?}",
641             def_id, target_place, places
642         );
643         let hir_id = self.infcx.tcx.hir().as_local_hir_id(def_id)?;
644         let expr = &self.infcx.tcx.hir().expect_expr_by_hir_id(hir_id).node;
645         debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
646         if let hir::ExprKind::Closure(
647             .., args_span, _
648         ) = expr {
649             for (upvar, place) in self.infcx.tcx.upvars(def_id)?.values().zip(places) {
650                 match place {
651                     Operand::Copy(place) |
652                     Operand::Move(place) if target_place == place => {
653                         debug!("closure_span: found captured local {:?}", place);
654                         return Some((*args_span, upvar.span));
655                     },
656                     _ => {}
657                 }
658             }
659
660         }
661         None
662     }
663
664     /// Helper to retrieve span(s) of given borrow from the current MIR
665     /// representation
666     pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans {
667         let span = self.mir.source_info(borrow.reserve_location).span;
668         self.borrow_spans(span, borrow.reserve_location)
669     }
670 }