]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/error_reporting.rs
Rollup merge of #60885 - euclio:strip-synstructure-consts, r=GuillaumeGomez
[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 upvar = self.infcx.tcx.upvars(def_id).unwrap()[field.index()];
352
353                     self.infcx.tcx.hir().name_by_hir_id(upvar.var_id()).to_string()
354                 }
355                 _ => {
356                     // Might need a revision when the fields in trait RFC is implemented
357                     // (https://github.com/rust-lang/rfcs/pull/1546)
358                     bug!(
359                         "End-user description not implemented for field access on `{:?}`",
360                         ty
361                     );
362                 }
363             }
364         }
365     }
366
367     /// Checks if a place is a thread-local static.
368     pub fn is_place_thread_local(&self, place: &Place<'tcx>) -> bool {
369         if let Place::Base(
370             PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. })
371         ) = place {
372             let attrs = self.infcx.tcx.get_attrs(*def_id);
373             let is_thread_local = attrs.iter().any(|attr| attr.check_name(sym::thread_local));
374
375             debug!(
376                 "is_place_thread_local: attrs={:?} is_thread_local={:?}",
377                 attrs, is_thread_local
378             );
379             is_thread_local
380         } else {
381             debug!("is_place_thread_local: no");
382             false
383         }
384     }
385 }
386
387 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
388     /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
389     /// name where required.
390     pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
391         let mut s = String::new();
392         let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
393
394         // We need to add synthesized lifetimes where appropriate. We do
395         // this by hooking into the pretty printer and telling it to label the
396         // lifetimes without names with the value `'0`.
397         match ty.sty {
398             ty::Ref(ty::RegionKind::ReLateBound(_, br), _, _)
399             | ty::Ref(
400                 ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }),
401                 _,
402                 _,
403             ) => printer.region_highlight_mode.highlighting_bound_region(*br, counter),
404             _ => {}
405         }
406
407         let _ = ty.print(printer);
408         s
409     }
410
411     /// Returns the name of the provided `Ty` (that must be a reference)'s region with a
412     /// synthesized lifetime name where required.
413     pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
414         let mut s = String::new();
415         let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
416
417         let region = match ty.sty {
418             ty::Ref(region, _, _) => {
419                 match region {
420                     ty::RegionKind::ReLateBound(_, br)
421                     | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => {
422                         printer.region_highlight_mode.highlighting_bound_region(*br, counter)
423                     }
424                     _ => {}
425                 }
426
427                 region
428             }
429             _ => bug!("ty for annotation of borrow region is not a reference"),
430         };
431
432         let _ = region.print(printer);
433         s
434     }
435 }
436
437 // The span(s) associated to a use of a place.
438 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
439 pub(super) enum UseSpans {
440     // The access is caused by capturing a variable for a closure.
441     ClosureUse {
442         // This is true if the captured variable was from a generator.
443         is_generator: bool,
444         // The span of the args of the closure, including the `move` keyword if
445         // it's present.
446         args_span: Span,
447         // The span of the first use of the captured variable inside the closure.
448         var_span: Span,
449     },
450     // This access has a single span associated to it: common case.
451     OtherUse(Span),
452 }
453
454 impl UseSpans {
455     pub(super) fn args_or_use(self) -> Span {
456         match self {
457             UseSpans::ClosureUse {
458                 args_span: span, ..
459             }
460             | UseSpans::OtherUse(span) => span,
461         }
462     }
463
464     pub(super) fn var_or_use(self) -> Span {
465         match self {
466             UseSpans::ClosureUse { var_span: span, .. } | UseSpans::OtherUse(span) => span,
467         }
468     }
469
470     // Add a span label to the arguments of the closure, if it exists.
471     pub(super) fn args_span_label(
472         self,
473         err: &mut DiagnosticBuilder<'_>,
474         message: impl Into<String>,
475     ) {
476         if let UseSpans::ClosureUse { args_span, .. } = self {
477             err.span_label(args_span, message);
478         }
479     }
480
481     // Add a span label to the use of the captured variable, if it exists.
482     pub(super) fn var_span_label(
483         self,
484         err: &mut DiagnosticBuilder<'_>,
485         message: impl Into<String>,
486     ) {
487         if let UseSpans::ClosureUse { var_span, .. } = self {
488             err.span_label(var_span, message);
489         }
490     }
491
492     /// Returns `false` if this place is not used in a closure.
493     pub(super) fn for_closure(&self) -> bool {
494         match *self {
495             UseSpans::ClosureUse { is_generator, .. } => !is_generator,
496             _ => false,
497         }
498     }
499
500     /// Returns `false` if this place is not used in a generator.
501     pub(super) fn for_generator(&self) -> bool {
502         match *self {
503             UseSpans::ClosureUse { is_generator, .. } => is_generator,
504             _ => false,
505         }
506     }
507
508     /// Describe the span associated with a use of a place.
509     pub(super) fn describe(&self) -> String {
510         match *self {
511             UseSpans::ClosureUse { is_generator, .. } => if is_generator {
512                 " in generator".to_string()
513             } else {
514                 " in closure".to_string()
515             },
516             _ => "".to_string(),
517         }
518     }
519
520     pub(super) fn or_else<F>(self, if_other: F) -> Self
521     where
522         F: FnOnce() -> Self,
523     {
524         match self {
525             closure @ UseSpans::ClosureUse { .. } => closure,
526             UseSpans::OtherUse(_) => if_other(),
527         }
528     }
529 }
530
531 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
532     /// Finds the spans associated to a move or copy of move_place at location.
533     pub(super) fn move_spans(
534         &self,
535         moved_place: &Place<'tcx>, // Could also be an upvar.
536         location: Location,
537     ) -> UseSpans {
538         use self::UseSpans::*;
539
540         let stmt = match self.mir[location.block].statements.get(location.statement_index) {
541             Some(stmt) => stmt,
542             None => return OtherUse(self.mir.source_info(location).span),
543         };
544
545         debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
546         if let  StatementKind::Assign(
547             _,
548             box Rvalue::Aggregate(ref kind, ref places)
549         ) = stmt.kind {
550             let (def_id, is_generator) = match kind {
551                 box AggregateKind::Closure(def_id, _) => (def_id, false),
552                 box AggregateKind::Generator(def_id, _, _) => (def_id, true),
553                 _ => return OtherUse(stmt.source_info.span),
554             };
555
556             debug!(
557                 "move_spans: def_id={:?} is_generator={:?} places={:?}",
558                 def_id, is_generator, places
559             );
560             if let Some((args_span, var_span)) = self.closure_span(*def_id, moved_place, places) {
561                 return ClosureUse {
562                     is_generator,
563                     args_span,
564                     var_span,
565                 };
566             }
567         }
568
569         OtherUse(stmt.source_info.span)
570     }
571
572     /// Finds the span of arguments of a closure (within `maybe_closure_span`)
573     /// and its usage of the local assigned at `location`.
574     /// This is done by searching in statements succeeding `location`
575     /// and originating from `maybe_closure_span`.
576     pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans {
577         use self::UseSpans::*;
578         debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
579
580         let target = match self.mir[location.block]
581             .statements
582             .get(location.statement_index)
583         {
584             Some(&Statement {
585                 kind: StatementKind::Assign(Place::Base(PlaceBase::Local(local)), _),
586                 ..
587             }) => local,
588             _ => return OtherUse(use_span),
589         };
590
591         if self.mir.local_kind(target) != LocalKind::Temp {
592             // operands are always temporaries.
593             return OtherUse(use_span);
594         }
595
596         for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
597             if let StatementKind::Assign(
598                 _, box Rvalue::Aggregate(ref kind, ref places)
599             ) = stmt.kind {
600                 let (def_id, is_generator) = match kind {
601                     box AggregateKind::Closure(def_id, _) => (def_id, false),
602                     box AggregateKind::Generator(def_id, _, _) => (def_id, true),
603                     _ => continue,
604                 };
605
606                 debug!(
607                     "borrow_spans: def_id={:?} is_generator={:?} places={:?}",
608                     def_id, is_generator, places
609                 );
610                 if let Some((args_span, var_span)) = self.closure_span(
611                     *def_id, &Place::Base(PlaceBase::Local(target)), places
612                 ) {
613                     return ClosureUse {
614                         is_generator,
615                         args_span,
616                         var_span,
617                     };
618                 } else {
619                     return OtherUse(use_span);
620                 }
621             }
622
623             if use_span != stmt.source_info.span {
624                 break;
625             }
626         }
627
628         OtherUse(use_span)
629     }
630
631     /// Finds the span of a captured variable within a closure or generator.
632     fn closure_span(
633         &self,
634         def_id: DefId,
635         target_place: &Place<'tcx>,
636         places: &Vec<Operand<'tcx>>,
637     ) -> Option<(Span, Span)> {
638         debug!(
639             "closure_span: def_id={:?} target_place={:?} places={:?}",
640             def_id, target_place, places
641         );
642         let hir_id = self.infcx.tcx.hir().as_local_hir_id(def_id)?;
643         let expr = &self.infcx.tcx.hir().expect_expr_by_hir_id(hir_id).node;
644         debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
645         if let hir::ExprKind::Closure(
646             .., args_span, _
647         ) = expr {
648             for (v, place) in self.infcx.tcx.upvars(def_id)?.iter().zip(places) {
649                 match place {
650                     Operand::Copy(place) |
651                     Operand::Move(place) if target_place == place => {
652                         debug!("closure_span: found captured local {:?}", place);
653                         return Some((*args_span, v.span));
654                     },
655                     _ => {}
656                 }
657             }
658
659         }
660         None
661     }
662
663     /// Helper to retrieve span(s) of given borrow from the current MIR
664     /// representation
665     pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans {
666         let span = self.mir.source_info(borrow.reserve_location).span;
667         self.borrow_spans(span, borrow.reserve_location)
668     }
669 }