]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/error_reporting.rs
Rollup merge of #61704 - petrhosek:llvm-linker-flags, r=alexcrichton
[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, Constant, Field, Local, LocalKind, Location, Operand,
6     Place, PlaceBase, ProjectionElem, Rvalue, Statement, StatementKind, Static,
7     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, 'tcx> MirBorrowckCtxt<'cx, '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.body[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.body[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.body.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.body.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 self.body.local_decls[local].is_ref_for_guard() {
184                                     self.append_place_to_string(
185                                         &proj.base,
186                                         buf,
187                                         autoderef,
188                                         &including_downcast,
189                                     )?;
190                                 } else {
191                                     buf.push_str(&"*");
192                                     self.append_place_to_string(
193                                         &proj.base,
194                                         buf,
195                                         autoderef,
196                                         &including_downcast,
197                                     )?;
198                                 }
199                             } else {
200                                 buf.push_str(&"*");
201                                 self.append_place_to_string(
202                                     &proj.base,
203                                     buf,
204                                     autoderef,
205                                     &including_downcast,
206                                 )?;
207                             }
208                         }
209                     }
210                     ProjectionElem::Downcast(..) => {
211                         self.append_place_to_string(
212                             &proj.base,
213                             buf,
214                             autoderef,
215                             &including_downcast,
216                         )?;
217                         if including_downcast.0 {
218                             return Err(());
219                         }
220                     }
221                     ProjectionElem::Field(field, _ty) => {
222                         autoderef = true;
223
224                         let upvar_field_projection =
225                             self.is_upvar_field_projection(place);
226                         if let Some(field) = upvar_field_projection {
227                             let var_index = field.index();
228                             let name = self.upvars[var_index].name.to_string();
229                             buf.push_str(&name);
230                         } else {
231                             let field_name = self.describe_field(&proj.base, field);
232                             self.append_place_to_string(
233                                 &proj.base,
234                                 buf,
235                                 autoderef,
236                                 &including_downcast,
237                             )?;
238                             buf.push_str(&format!(".{}", field_name));
239                         }
240                     }
241                     ProjectionElem::Index(index) => {
242                         autoderef = true;
243
244                         self.append_place_to_string(
245                             &proj.base,
246                             buf,
247                             autoderef,
248                             &including_downcast,
249                         )?;
250                         buf.push_str("[");
251                         if self.append_local_to_string(index, buf).is_err() {
252                             buf.push_str("_");
253                         }
254                         buf.push_str("]");
255                     }
256                     ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
257                         autoderef = true;
258                         // Since it isn't possible to borrow an element on a particular index and
259                         // then use another while the borrow is held, don't output indices details
260                         // to avoid confusing the end-user
261                         self.append_place_to_string(
262                             &proj.base,
263                             buf,
264                             autoderef,
265                             &including_downcast,
266                         )?;
267                         buf.push_str(&"[..]");
268                     }
269                 };
270             }
271         }
272
273         Ok(())
274     }
275
276     /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
277     /// a name, or its name was generated by the compiler, then `Err` is returned
278     fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
279         let local = &self.body.local_decls[local_index];
280         match local.name {
281             Some(name) if !local.from_compiler_desugaring() => {
282                 buf.push_str(name.as_str().get());
283                 Ok(())
284             }
285             _ => Err(()),
286         }
287     }
288
289     /// End-user visible description of the `field`nth field of `base`
290     fn describe_field(&self, base: &Place<'tcx>, field: Field) -> String {
291         match *base {
292             Place::Base(PlaceBase::Local(local)) => {
293                 let local = &self.body.local_decls[local];
294                 self.describe_field_from_ty(&local.ty, field, None)
295             }
296             Place::Base(PlaceBase::Static(ref static_)) =>
297                 self.describe_field_from_ty(&static_.ty, field, None),
298             Place::Projection(ref proj) => match proj.elem {
299                 ProjectionElem::Deref => self.describe_field(&proj.base, field),
300                 ProjectionElem::Downcast(_, variant_index) => {
301                     let base_ty = base.ty(self.body, self.infcx.tcx).ty;
302                     self.describe_field_from_ty(&base_ty, field, Some(variant_index))
303                 }
304                 ProjectionElem::Field(_, field_type) => {
305                     self.describe_field_from_ty(&field_type, field, None)
306                 }
307                 ProjectionElem::Index(..)
308                 | ProjectionElem::ConstantIndex { .. }
309                 | ProjectionElem::Subslice { .. } => {
310                     self.describe_field(&proj.base, field)
311                 }
312             },
313         }
314     }
315
316     /// End-user visible description of the `field_index`nth field of `ty`
317     fn describe_field_from_ty(
318         &self,
319         ty: Ty<'_>,
320         field: Field,
321         variant_index: Option<VariantIdx>
322     ) -> String {
323         if ty.is_box() {
324             // If the type is a box, the field is described from the boxed type
325             self.describe_field_from_ty(&ty.boxed_ty(), field, variant_index)
326         } else {
327             match ty.sty {
328                 ty::Adt(def, _) => {
329                     let variant = if let Some(idx) = variant_index {
330                         assert!(def.is_enum());
331                         &def.variants[idx]
332                     } else {
333                         def.non_enum_variant()
334                     };
335                     variant.fields[field.index()]
336                         .ident
337                         .to_string()
338                 },
339                 ty::Tuple(_) => field.index().to_string(),
340                 ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
341                     self.describe_field_from_ty(&ty, field, variant_index)
342                 }
343                 ty::Array(ty, _) | ty::Slice(ty) =>
344                     self.describe_field_from_ty(&ty, field, variant_index),
345                 ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
346                     // `tcx.upvars(def_id)` returns an `Option`, which is `None` in case
347                     // the closure comes from another crate. But in that case we wouldn't
348                     // be borrowck'ing it, so we can just unwrap:
349                     let (&var_id, _) = self.infcx.tcx.upvars(def_id).unwrap()
350                         .get_index(field.index()).unwrap();
351
352                     self.infcx.tcx.hir().name_by_hir_id(var_id).to_string()
353                 }
354                 _ => {
355                     // Might need a revision when the fields in trait RFC is implemented
356                     // (https://github.com/rust-lang/rfcs/pull/1546)
357                     bug!(
358                         "End-user description not implemented for field access on `{:?}`",
359                         ty
360                     );
361                 }
362             }
363         }
364     }
365
366     /// Checks if a place is a thread-local static.
367     pub fn is_place_thread_local(&self, place: &Place<'tcx>) -> bool {
368         if let Place::Base(
369             PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. })
370         ) = place {
371             let attrs = self.infcx.tcx.get_attrs(*def_id);
372             let is_thread_local = attrs.iter().any(|attr| attr.check_name(sym::thread_local));
373
374             debug!(
375                 "is_place_thread_local: attrs={:?} is_thread_local={:?}",
376                 attrs, is_thread_local
377             );
378             is_thread_local
379         } else {
380             debug!("is_place_thread_local: no");
381             false
382         }
383     }
384
385     /// Add a note that a type does not implement `Copy`
386     pub(super) fn note_type_does_not_implement_copy(
387         &self,
388         err: &mut DiagnosticBuilder<'a>,
389         place_desc: &str,
390         ty: Ty<'tcx>,
391         span: Option<Span>,
392     ) {
393         let message = format!(
394             "move occurs because {} has type `{}`, which does not implement the `Copy` trait",
395             place_desc,
396             ty,
397         );
398         if let Some(span) = span {
399             err.span_label(span, message);
400         } else {
401             err.note(&message);
402         }
403     }
404 }
405
406 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
407     /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
408     /// name where required.
409     pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
410         let mut s = String::new();
411         let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
412
413         // We need to add synthesized lifetimes where appropriate. We do
414         // this by hooking into the pretty printer and telling it to label the
415         // lifetimes without names with the value `'0`.
416         match ty.sty {
417             ty::Ref(ty::RegionKind::ReLateBound(_, br), _, _)
418             | ty::Ref(
419                 ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }),
420                 _,
421                 _,
422             ) => printer.region_highlight_mode.highlighting_bound_region(*br, counter),
423             _ => {}
424         }
425
426         let _ = ty.print(printer);
427         s
428     }
429
430     /// Returns the name of the provided `Ty` (that must be a reference)'s region with a
431     /// synthesized lifetime name where required.
432     pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
433         let mut s = String::new();
434         let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
435
436         let region = match ty.sty {
437             ty::Ref(region, _, _) => {
438                 match region {
439                     ty::RegionKind::ReLateBound(_, br)
440                     | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => {
441                         printer.region_highlight_mode.highlighting_bound_region(*br, counter)
442                     }
443                     _ => {}
444                 }
445
446                 region
447             }
448             _ => bug!("ty for annotation of borrow region is not a reference"),
449         };
450
451         let _ = region.print(printer);
452         s
453     }
454 }
455
456 // The span(s) associated to a use of a place.
457 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
458 pub(super) enum UseSpans {
459     // The access is caused by capturing a variable for a closure.
460     ClosureUse {
461         // This is true if the captured variable was from a generator.
462         is_generator: bool,
463         // The span of the args of the closure, including the `move` keyword if
464         // it's present.
465         args_span: Span,
466         // The span of the first use of the captured variable inside the closure.
467         var_span: Span,
468     },
469     // This access has a single span associated to it: common case.
470     OtherUse(Span),
471 }
472
473 impl UseSpans {
474     pub(super) fn args_or_use(self) -> Span {
475         match self {
476             UseSpans::ClosureUse {
477                 args_span: span, ..
478             }
479             | UseSpans::OtherUse(span) => span,
480         }
481     }
482
483     pub(super) fn var_or_use(self) -> Span {
484         match self {
485             UseSpans::ClosureUse { var_span: span, .. } | UseSpans::OtherUse(span) => span,
486         }
487     }
488
489     // Add a span label to the arguments of the closure, if it exists.
490     pub(super) fn args_span_label(
491         self,
492         err: &mut DiagnosticBuilder<'_>,
493         message: impl Into<String>,
494     ) {
495         if let UseSpans::ClosureUse { args_span, .. } = self {
496             err.span_label(args_span, message);
497         }
498     }
499
500     // Add a span label to the use of the captured variable, if it exists.
501     pub(super) fn var_span_label(
502         self,
503         err: &mut DiagnosticBuilder<'_>,
504         message: impl Into<String>,
505     ) {
506         if let UseSpans::ClosureUse { var_span, .. } = self {
507             err.span_label(var_span, message);
508         }
509     }
510
511     /// Returns `false` if this place is not used in a closure.
512     pub(super) fn for_closure(&self) -> bool {
513         match *self {
514             UseSpans::ClosureUse { is_generator, .. } => !is_generator,
515             _ => false,
516         }
517     }
518
519     /// Returns `false` if this place is not used in a generator.
520     pub(super) fn for_generator(&self) -> bool {
521         match *self {
522             UseSpans::ClosureUse { is_generator, .. } => is_generator,
523             _ => false,
524         }
525     }
526
527     /// Describe the span associated with a use of a place.
528     pub(super) fn describe(&self) -> String {
529         match *self {
530             UseSpans::ClosureUse { is_generator, .. } => if is_generator {
531                 " in generator".to_string()
532             } else {
533                 " in closure".to_string()
534             },
535             _ => "".to_string(),
536         }
537     }
538
539     pub(super) fn or_else<F>(self, if_other: F) -> Self
540     where
541         F: FnOnce() -> Self,
542     {
543         match self {
544             closure @ UseSpans::ClosureUse { .. } => closure,
545             UseSpans::OtherUse(_) => if_other(),
546         }
547     }
548 }
549
550 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
551     /// Finds the spans associated to a move or copy of move_place at location.
552     pub(super) fn move_spans(
553         &self,
554         moved_place: &Place<'tcx>, // Could also be an upvar.
555         location: Location,
556     ) -> UseSpans {
557         use self::UseSpans::*;
558
559         let stmt = match self.body[location.block].statements.get(location.statement_index) {
560             Some(stmt) => stmt,
561             None => return OtherUse(self.body.source_info(location).span),
562         };
563
564         debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
565         if let  StatementKind::Assign(
566             _,
567             box Rvalue::Aggregate(ref kind, ref places)
568         ) = stmt.kind {
569             let (def_id, is_generator) = match kind {
570                 box AggregateKind::Closure(def_id, _) => (def_id, false),
571                 box AggregateKind::Generator(def_id, _, _) => (def_id, true),
572                 _ => return OtherUse(stmt.source_info.span),
573             };
574
575             debug!(
576                 "move_spans: def_id={:?} is_generator={:?} places={:?}",
577                 def_id, is_generator, places
578             );
579             if let Some((args_span, var_span)) = self.closure_span(*def_id, moved_place, places) {
580                 return ClosureUse {
581                     is_generator,
582                     args_span,
583                     var_span,
584                 };
585             }
586         }
587
588         OtherUse(stmt.source_info.span)
589     }
590
591     /// Finds the span of arguments of a closure (within `maybe_closure_span`)
592     /// and its usage of the local assigned at `location`.
593     /// This is done by searching in statements succeeding `location`
594     /// and originating from `maybe_closure_span`.
595     pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans {
596         use self::UseSpans::*;
597         debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
598
599         let target = match self.body[location.block]
600             .statements
601             .get(location.statement_index)
602         {
603             Some(&Statement {
604                 kind: StatementKind::Assign(Place::Base(PlaceBase::Local(local)), _),
605                 ..
606             }) => local,
607             _ => return OtherUse(use_span),
608         };
609
610         if self.body.local_kind(target) != LocalKind::Temp {
611             // operands are always temporaries.
612             return OtherUse(use_span);
613         }
614
615         for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
616             if let StatementKind::Assign(
617                 _, box Rvalue::Aggregate(ref kind, ref places)
618             ) = stmt.kind {
619                 let (def_id, is_generator) = match kind {
620                     box AggregateKind::Closure(def_id, _) => (def_id, false),
621                     box AggregateKind::Generator(def_id, _, _) => (def_id, true),
622                     _ => continue,
623                 };
624
625                 debug!(
626                     "borrow_spans: def_id={:?} is_generator={:?} places={:?}",
627                     def_id, is_generator, places
628                 );
629                 if let Some((args_span, var_span)) = self.closure_span(
630                     *def_id, &Place::Base(PlaceBase::Local(target)), places
631                 ) {
632                     return ClosureUse {
633                         is_generator,
634                         args_span,
635                         var_span,
636                     };
637                 } else {
638                     return OtherUse(use_span);
639                 }
640             }
641
642             if use_span != stmt.source_info.span {
643                 break;
644             }
645         }
646
647         OtherUse(use_span)
648     }
649
650     /// Finds the span of a captured variable within a closure or generator.
651     fn closure_span(
652         &self,
653         def_id: DefId,
654         target_place: &Place<'tcx>,
655         places: &Vec<Operand<'tcx>>,
656     ) -> Option<(Span, Span)> {
657         debug!(
658             "closure_span: def_id={:?} target_place={:?} places={:?}",
659             def_id, target_place, places
660         );
661         let hir_id = self.infcx.tcx.hir().as_local_hir_id(def_id)?;
662         let expr = &self.infcx.tcx.hir().expect_expr_by_hir_id(hir_id).node;
663         debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
664         if let hir::ExprKind::Closure(
665             .., args_span, _
666         ) = expr {
667             for (upvar, place) in self.infcx.tcx.upvars(def_id)?.values().zip(places) {
668                 match place {
669                     Operand::Copy(place) |
670                     Operand::Move(place) if target_place == place => {
671                         debug!("closure_span: found captured local {:?}", place);
672                         return Some((*args_span, upvar.span));
673                     },
674                     _ => {}
675                 }
676             }
677
678         }
679         None
680     }
681
682     /// Helper to retrieve span(s) of given borrow from the current MIR
683     /// representation
684     pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans {
685         let span = self.body.source_info(borrow.reserve_location).span;
686         self.borrow_spans(span, borrow.reserve_location)
687     }
688 }