]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
New upstream version 1.30.0+dfsg1
[rustc.git] / src / librustc_mir / borrow_check / nll / region_infer / error_reporting / region_name.rs
CommitLineData
8faf50e0
XL
1// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11use borrow_check::nll::region_infer::RegionInferenceContext;
b7449926 12use borrow_check::nll::universal_regions::DefiningTy;
8faf50e0
XL
13use borrow_check::nll::ToRegionVid;
14use rustc::hir;
15use rustc::hir::def_id::DefId;
16use rustc::infer::InferCtxt;
17use rustc::mir::Mir;
18use rustc::ty::subst::{Substs, UnpackedKind};
b7449926 19use rustc::ty::{self, RegionKind, RegionVid, Ty, TyCtxt};
8faf50e0
XL
20use rustc::util::ppaux::with_highlight_region;
21use rustc_errors::DiagnosticBuilder;
b7449926 22use syntax::ast::{Name, DUMMY_NODE_ID};
8faf50e0
XL
23use syntax::symbol::keywords;
24use syntax_pos::symbol::InternedString;
25
26impl<'tcx> RegionInferenceContext<'tcx> {
27 /// Maps from an internal MIR region vid to something that we can
28 /// report to the user. In some cases, the region vids will map
29 /// directly to lifetimes that the user has a name for (e.g.,
30 /// `'static`). But frequently they will not, in which case we
31 /// have to find some way to identify the lifetime to the user. To
32 /// that end, this function takes a "diagnostic" so that it can
33 /// create auxiliary notes as needed.
34 ///
35 /// Example (function arguments):
36 ///
37 /// Suppose we are trying to give a name to the lifetime of the
38 /// reference `x`:
39 ///
40 /// ```
41 /// fn foo(x: &u32) { .. }
42 /// ```
43 ///
44 /// This function would create a label like this:
45 ///
46 /// ```
47 /// | fn foo(x: &u32) { .. }
48 /// ------- fully elaborated type of `x` is `&'1 u32`
49 /// ```
50 ///
51 /// and then return the name `'1` for us to use.
52 crate fn give_region_a_name(
53 &self,
54 infcx: &InferCtxt<'_, '_, 'tcx>,
55 mir: &Mir<'tcx>,
56 mir_def_id: DefId,
57 fr: RegionVid,
58 counter: &mut usize,
59 diag: &mut DiagnosticBuilder,
60 ) -> InternedString {
61 debug!("give_region_a_name(fr={:?}, counter={})", fr, counter);
62
63 assert!(self.universal_regions.is_universal_region(fr));
64
b7449926 65 let value = self.give_name_from_error_region(infcx.tcx, mir_def_id, fr, counter, diag)
8faf50e0
XL
66 .or_else(|| {
67 self.give_name_if_anonymous_region_appears_in_arguments(
b7449926
XL
68 infcx, mir, mir_def_id, fr, counter, diag,
69 )
8faf50e0
XL
70 })
71 .or_else(|| {
72 self.give_name_if_anonymous_region_appears_in_upvars(
b7449926
XL
73 infcx.tcx, mir, fr, counter, diag,
74 )
8faf50e0
XL
75 })
76 .or_else(|| {
77 self.give_name_if_anonymous_region_appears_in_output(
b7449926
XL
78 infcx, mir, mir_def_id, fr, counter, diag,
79 )
8faf50e0 80 })
b7449926
XL
81 .unwrap_or_else(|| span_bug!(mir.span, "can't make a name for free region {:?}", fr));
82
83 debug!("give_region_a_name: gave name {:?}", value);
84 value
8faf50e0
XL
85 }
86
87 /// Check for the case where `fr` maps to something that the
88 /// *user* has a name for. In that case, we'll be able to map
89 /// `fr` to a `Region<'tcx>`, and that region will be one of
90 /// named variants.
91 fn give_name_from_error_region(
92 &self,
93 tcx: TyCtxt<'_, '_, 'tcx>,
94 mir_def_id: DefId,
95 fr: RegionVid,
96 counter: &mut usize,
97 diag: &mut DiagnosticBuilder<'_>,
98 ) -> Option<InternedString> {
99 let error_region = self.to_error_region(fr)?;
b7449926 100
8faf50e0
XL
101 debug!("give_region_a_name: error_region = {:?}", error_region);
102 match error_region {
b7449926
XL
103 ty::ReEarlyBound(ebr) => {
104 if ebr.has_name() {
105 self.highlight_named_span(tcx, error_region, &ebr.name, diag);
106 Some(ebr.name)
107 } else {
108 None
109 }
110 }
8faf50e0
XL
111
112 ty::ReStatic => Some(keywords::StaticLifetime.name().as_interned_str()),
113
114 ty::ReFree(free_region) => match free_region.bound_region {
b7449926
XL
115 ty::BoundRegion::BrNamed(_, name) => {
116 self.highlight_named_span(tcx, error_region, &name, diag);
117 Some(name)
118 }
8faf50e0
XL
119
120 ty::BoundRegion::BrEnv => {
b7449926
XL
121 let mir_node_id = tcx.hir.as_local_node_id(mir_def_id).expect("non-local mir");
122 let def_ty = self.universal_regions.defining_ty;
123
124 if let DefiningTy::Closure(def_id, substs) = def_ty {
125 let args_span = if let hir::ExprKind::Closure(_, _, _, span, _) =
126 tcx.hir.expect_expr(mir_node_id).node
127 {
128 span
129 } else {
130 bug!("Closure is not defined by a closure expr");
131 };
132 let region_name = self.synthesize_region_name(counter);
133 diag.span_label(
134 args_span,
135 format!("lifetime `{}` represents this closure's body", region_name),
136 );
137
138 let closure_kind_ty = substs.closure_kind_ty(def_id, tcx);
139 let note = match closure_kind_ty.to_opt_closure_kind() {
140 Some(ty::ClosureKind::Fn) => {
141 "closure implements `Fn`, so references to captured variables \
142 can't escape the closure"
143 }
144 Some(ty::ClosureKind::FnMut) => {
145 "closure implements `FnMut`, so references to captured variables \
146 can't escape the closure"
147 }
148 Some(ty::ClosureKind::FnOnce) => {
149 bug!("BrEnv in a `FnOnce` closure");
150 }
151 None => bug!("Closure kind not inferred in borrow check"),
152 };
153
154 diag.note(note);
155
156 Some(region_name)
157 } else {
158 // Can't have BrEnv in functions, constants or generators.
159 bug!("BrEnv outside of closure.");
160 }
8faf50e0
XL
161 }
162
163 ty::BoundRegion::BrAnon(_) | ty::BoundRegion::BrFresh(_) => None,
164 },
165
166 ty::ReLateBound(..)
167 | ty::ReScope(..)
168 | ty::ReVar(..)
169 | ty::ReSkolemized(..)
170 | ty::ReEmpty
171 | ty::ReErased
172 | ty::ReClosureBound(..)
173 | ty::ReCanonical(..) => None,
174 }
175 }
176
b7449926
XL
177 /// Highlight a named span to provide context for error messages that
178 /// mention that span, for example:
179 ///
180 /// ```
181 /// |
182 /// | fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
183 /// | -- -- lifetime `'b` defined here
184 /// | |
185 /// | lifetime `'a` defined here
186 /// |
187 /// | with_signature(cell, t, |cell, t| require(cell, t));
188 /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'b` must
189 /// | outlive `'a`
190 /// ```
191 fn highlight_named_span(
192 &self,
193 tcx: TyCtxt<'_, '_, 'tcx>,
194 error_region: &RegionKind,
195 name: &InternedString,
196 diag: &mut DiagnosticBuilder<'_>,
197 ) {
198 let cm = tcx.sess.source_map();
199
200 let scope = error_region.free_region_binding_scope(tcx);
201 let node = tcx.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID);
202
203 let mut sp = cm.def_span(tcx.hir.span(node));
204 if let Some(param) = tcx.hir
205 .get_generics(scope)
206 .and_then(|generics| generics.get_named(name))
207 {
208 sp = param.span;
209 }
210
211 diag.span_label(sp, format!("lifetime `{}` defined here", name));
212 }
213
8faf50e0
XL
214 /// Find an argument that contains `fr` and label it with a fully
215 /// elaborated type, returning something like `'1`. Result looks
216 /// like:
217 ///
218 /// ```
219 /// | fn foo(x: &u32) { .. }
220 /// ------- fully elaborated type of `x` is `&'1 u32`
221 /// ```
222 fn give_name_if_anonymous_region_appears_in_arguments(
223 &self,
224 infcx: &InferCtxt<'_, '_, 'tcx>,
225 mir: &Mir<'tcx>,
226 mir_def_id: DefId,
227 fr: RegionVid,
228 counter: &mut usize,
229 diag: &mut DiagnosticBuilder<'_>,
230 ) -> Option<InternedString> {
231 let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
232 let argument_index = self.get_argument_index_for_region(infcx.tcx, fr)?;
233
234 let arg_ty =
235 self.universal_regions.unnormalized_input_tys[implicit_inputs + argument_index];
236 if let Some(region_name) = self.give_name_if_we_can_match_hir_ty_from_argument(
237 infcx,
238 mir,
239 mir_def_id,
240 fr,
241 arg_ty,
242 argument_index,
243 counter,
244 diag,
245 ) {
246 return Some(region_name);
247 }
248
b7449926 249 self.give_name_if_we_cannot_match_hir_ty(infcx, mir, fr, arg_ty, counter, diag)
8faf50e0
XL
250 }
251
252 fn give_name_if_we_can_match_hir_ty_from_argument(
253 &self,
254 infcx: &InferCtxt<'_, '_, 'tcx>,
255 mir: &Mir<'tcx>,
256 mir_def_id: DefId,
257 needle_fr: RegionVid,
258 argument_ty: Ty<'tcx>,
259 argument_index: usize,
260 counter: &mut usize,
261 diag: &mut DiagnosticBuilder<'_>,
262 ) -> Option<InternedString> {
263 let mir_node_id = infcx.tcx.hir.as_local_node_id(mir_def_id)?;
264 let fn_decl = infcx.tcx.hir.fn_decl(mir_node_id)?;
265 let argument_hir_ty: &hir::Ty = &fn_decl.inputs[argument_index];
266 match argument_hir_ty.node {
267 // This indicates a variable with no type annotation, like
268 // `|x|`... in that case, we can't highlight the type but
269 // must highlight the variable.
270 hir::TyKind::Infer => self.give_name_if_we_cannot_match_hir_ty(
271 infcx,
272 mir,
273 needle_fr,
274 argument_ty,
275 counter,
276 diag,
277 ),
278
279 _ => self.give_name_if_we_can_match_hir_ty(
280 infcx.tcx,
281 needle_fr,
282 argument_ty,
283 argument_hir_ty,
284 counter,
285 diag,
286 ),
287 }
288 }
289
290 /// Attempts to highlight the specific part of a type in an argument
291 /// that has no type annotation.
292 /// For example, we might produce an annotation like this:
293 ///
294 /// ```
295 /// | foo(|a, b| b)
296 /// | - -
297 /// | | |
298 /// | | has type `&'1 u32`
299 /// | has type `&'2 u32`
300 /// ```
301 fn give_name_if_we_cannot_match_hir_ty(
302 &self,
303 infcx: &InferCtxt<'_, '_, 'tcx>,
304 mir: &Mir<'tcx>,
305 needle_fr: RegionVid,
306 argument_ty: Ty<'tcx>,
307 counter: &mut usize,
308 diag: &mut DiagnosticBuilder<'_>,
309 ) -> Option<InternedString> {
310 let type_name = with_highlight_region(needle_fr, *counter, || {
311 infcx.extract_type_name(&argument_ty)
312 });
313
b7449926
XL
314 debug!(
315 "give_name_if_we_cannot_match_hir_ty: type_name={:?} needle_fr={:?}",
316 type_name, needle_fr
317 );
8faf50e0
XL
318 let assigned_region_name = if type_name.find(&format!("'{}", counter)).is_some() {
319 // Only add a label if we can confirm that a region was labelled.
320 let argument_index = self.get_argument_index_for_region(infcx.tcx, needle_fr)?;
321 let (_, span) = self.get_argument_name_and_span_for_region(mir, argument_index);
322 diag.span_label(span, format!("has type `{}`", type_name));
323
324 // This counter value will already have been used, so this function will increment it
325 // so the next value will be used next and return the region name that would have been
326 // used.
327 Some(self.synthesize_region_name(counter))
328 } else {
329 None
330 };
331
332 assigned_region_name
333 }
334
335 /// Attempts to highlight the specific part of a type annotation
336 /// that contains the anonymous reference we want to give a name
337 /// to. For example, we might produce an annotation like this:
338 ///
339 /// ```
340 /// | fn a<T>(items: &[T]) -> Box<dyn Iterator<Item=&T>> {
341 /// | - let's call the lifetime of this reference `'1`
342 /// ```
343 ///
344 /// the way this works is that we match up `argument_ty`, which is
345 /// a `Ty<'tcx>` (the internal form of the type) with
346 /// `argument_hir_ty`, a `hir::Ty` (the syntax of the type
347 /// annotation). We are descending through the types stepwise,
348 /// looking in to find the region `needle_fr` in the internal
349 /// type. Once we find that, we can use the span of the `hir::Ty`
350 /// to add the highlight.
351 ///
352 /// This is a somewhat imperfect process, so long the way we also
353 /// keep track of the **closest** type we've found. If we fail to
354 /// find the exact `&` or `'_` to highlight, then we may fall back
355 /// to highlighting that closest type instead.
356 fn give_name_if_we_can_match_hir_ty(
357 &self,
358 tcx: TyCtxt<'_, '_, 'tcx>,
359 needle_fr: RegionVid,
360 argument_ty: Ty<'tcx>,
361 argument_hir_ty: &hir::Ty,
362 counter: &mut usize,
363 diag: &mut DiagnosticBuilder<'_>,
364 ) -> Option<InternedString> {
365 let search_stack: &mut Vec<(Ty<'tcx>, &hir::Ty)> = &mut Vec::new();
366
367 search_stack.push((argument_ty, argument_hir_ty));
368
8faf50e0 369 while let Some((ty, hir_ty)) = search_stack.pop() {
8faf50e0
XL
370 match (&ty.sty, &hir_ty.node) {
371 // Check if the `argument_ty` is `&'X ..` where `'X`
372 // is the region we are looking for -- if so, and we have a `&T`
373 // on the RHS, then we want to highlight the `&` like so:
374 //
375 // &
376 // - let's call the lifetime of this reference `'1`
377 (
b7449926 378 ty::Ref(region, referent_ty, _),
8faf50e0
XL
379 hir::TyKind::Rptr(_lifetime, referent_hir_ty),
380 ) => {
381 if region.to_region_vid() == needle_fr {
382 let region_name = self.synthesize_region_name(counter);
383
384 // Just grab the first character, the `&`.
b7449926
XL
385 let source_map = tcx.sess.source_map();
386 let ampersand_span = source_map.start_point(hir_ty.span);
8faf50e0
XL
387
388 diag.span_label(
389 ampersand_span,
390 format!(
391 "let's call the lifetime of this reference `{}`",
392 region_name
393 ),
394 );
395
396 return Some(region_name);
397 }
398
399 // Otherwise, let's descend into the referent types.
400 search_stack.push((referent_ty, &referent_hir_ty.ty));
401 }
402
403 // Match up something like `Foo<'1>`
404 (
b7449926 405 ty::Adt(_adt_def, substs),
8faf50e0
XL
406 hir::TyKind::Path(hir::QPath::Resolved(None, path)),
407 ) => {
408 if let Some(last_segment) = path.segments.last() {
409 if let Some(name) = self.match_adt_and_segment(
410 substs,
411 needle_fr,
412 last_segment,
413 counter,
414 diag,
415 search_stack,
416 ) {
417 return Some(name);
418 }
419 }
420 }
421
422 // The following cases don't have lifetimes, so we
423 // just worry about trying to match up the rustc type
424 // with the HIR types:
b7449926 425 (ty::Tuple(elem_tys), hir::TyKind::Tup(elem_hir_tys)) => {
8faf50e0
XL
426 search_stack.extend(elem_tys.iter().cloned().zip(elem_hir_tys));
427 }
428
b7449926
XL
429 (ty::Slice(elem_ty), hir::TyKind::Slice(elem_hir_ty))
430 | (ty::Array(elem_ty, _), hir::TyKind::Array(elem_hir_ty, _)) => {
8faf50e0
XL
431 search_stack.push((elem_ty, elem_hir_ty));
432 }
433
b7449926 434 (ty::RawPtr(mut_ty), hir::TyKind::Ptr(mut_hir_ty)) => {
8faf50e0
XL
435 search_stack.push((mut_ty.ty, &mut_hir_ty.ty));
436 }
437
438 _ => {
439 // FIXME there are other cases that we could trace
440 }
441 }
442 }
443
b7449926 444 return None;
8faf50e0
XL
445 }
446
447 /// We've found an enum/struct/union type with the substitutions
448 /// `substs` and -- in the HIR -- a path type with the final
449 /// segment `last_segment`. Try to find a `'_` to highlight in
450 /// the generic args (or, if not, to produce new zipped pairs of
451 /// types+hir to search through).
452 fn match_adt_and_segment<'hir>(
453 &self,
454 substs: &'tcx Substs<'tcx>,
455 needle_fr: RegionVid,
456 last_segment: &'hir hir::PathSegment,
457 counter: &mut usize,
458 diag: &mut DiagnosticBuilder<'_>,
459 search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty)>,
460 ) -> Option<InternedString> {
461 // Did the user give explicit arguments? (e.g., `Foo<..>`)
462 let args = last_segment.args.as_ref()?;
463 let lifetime = self.try_match_adt_and_generic_args(substs, needle_fr, args, search_stack)?;
464 match lifetime.name {
465 hir::LifetimeName::Param(_)
466 | hir::LifetimeName::Static
467 | hir::LifetimeName::Underscore => {
468 let region_name = self.synthesize_region_name(counter);
469 let ampersand_span = lifetime.span;
470 diag.span_label(ampersand_span, format!("let's call this `{}`", region_name));
471 return Some(region_name);
472 }
473
474 hir::LifetimeName::Implicit => {
475 // In this case, the user left off the lifetime; so
476 // they wrote something like:
477 //
478 // ```
479 // x: Foo<T>
480 // ```
481 //
482 // where the fully elaborated form is `Foo<'_, '1,
483 // T>`. We don't consider this a match; instead we let
484 // the "fully elaborated" type fallback above handle
485 // it.
486 return None;
487 }
488 }
489 }
490
491 /// We've found an enum/struct/union type with the substitutions
492 /// `substs` and -- in the HIR -- a path with the generic
493 /// arguments `args`. If `needle_fr` appears in the args, return
494 /// the `hir::Lifetime` that corresponds to it. If not, push onto
495 /// `search_stack` the types+hir to search through.
496 fn try_match_adt_and_generic_args<'hir>(
497 &self,
498 substs: &'tcx Substs<'tcx>,
499 needle_fr: RegionVid,
500 args: &'hir hir::GenericArgs,
501 search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty)>,
502 ) -> Option<&'hir hir::Lifetime> {
503 for (kind, hir_arg) in substs.iter().zip(&args.args) {
504 match (kind.unpack(), hir_arg) {
505 (UnpackedKind::Lifetime(r), hir::GenericArg::Lifetime(lt)) => {
506 if r.to_region_vid() == needle_fr {
507 return Some(lt);
508 }
509 }
510
511 (UnpackedKind::Type(ty), hir::GenericArg::Type(hir_ty)) => {
512 search_stack.push((ty, hir_ty));
513 }
514
515 (UnpackedKind::Lifetime(_), _) | (UnpackedKind::Type(_), _) => {
516 // I *think* that HIR lowering should ensure this
517 // doesn't happen, even in erroneous
518 // programs. Else we should use delay-span-bug.
519 span_bug!(
520 hir_arg.span(),
521 "unmatched subst and hir arg: found {:?} vs {:?}",
522 kind,
523 hir_arg,
524 );
525 }
526 }
527 }
528
529 None
530 }
531
532 /// Find a closure upvar that contains `fr` and label it with a
533 /// fully elaborated type, returning something like `'1`. Result
534 /// looks like:
535 ///
536 /// ```
537 /// | let x = Some(&22);
538 /// - fully elaborated type of `x` is `Option<&'1 u32>`
539 /// ```
540 fn give_name_if_anonymous_region_appears_in_upvars(
541 &self,
542 tcx: TyCtxt<'_, '_, 'tcx>,
543 mir: &Mir<'tcx>,
544 fr: RegionVid,
545 counter: &mut usize,
546 diag: &mut DiagnosticBuilder<'_>,
547 ) -> Option<InternedString> {
548 let upvar_index = self.get_upvar_index_for_region(tcx, fr)?;
b7449926
XL
549 let (upvar_name, upvar_span) =
550 self.get_upvar_name_and_span_for_region(tcx, mir, upvar_index);
8faf50e0
XL
551 let region_name = self.synthesize_region_name(counter);
552
553 diag.span_label(
554 upvar_span,
b7449926
XL
555 format!(
556 "lifetime `{}` appears in the type of `{}`",
557 region_name, upvar_name
558 ),
8faf50e0
XL
559 );
560
561 Some(region_name)
562 }
563
564 /// Check for arguments appearing in the (closure) return type. It
565 /// must be a closure since, in a free fn, such an argument would
566 /// have to either also appear in an argument (if using elision)
567 /// or be early bound (named, not in argument).
568 fn give_name_if_anonymous_region_appears_in_output(
569 &self,
b7449926 570 infcx: &InferCtxt<'_, '_, 'tcx>,
8faf50e0 571 mir: &Mir<'tcx>,
b7449926 572 mir_def_id: DefId,
8faf50e0
XL
573 fr: RegionVid,
574 counter: &mut usize,
575 diag: &mut DiagnosticBuilder<'_>,
576 ) -> Option<InternedString> {
b7449926
XL
577 let tcx = infcx.tcx;
578
8faf50e0
XL
579 let return_ty = self.universal_regions.unnormalized_output_ty;
580 debug!(
581 "give_name_if_anonymous_region_appears_in_output: return_ty = {:?}",
582 return_ty
583 );
b7449926
XL
584 if !infcx
585 .tcx
586 .any_free_region_meets(&return_ty, |r| r.to_region_vid() == fr)
587 {
8faf50e0
XL
588 return None;
589 }
590
b7449926
XL
591 let type_name = with_highlight_region(fr, *counter, || infcx.extract_type_name(&return_ty));
592
593 let mir_node_id = tcx.hir.as_local_node_id(mir_def_id).expect("non-local mir");
594
595 let (return_span, mir_description) =
596 if let hir::ExprKind::Closure(_, _, _, span, gen_move) =
597 tcx.hir.expect_expr(mir_node_id).node
598 {
599 (
600 tcx.sess.source_map().end_point(span),
601 if gen_move.is_some() {
602 " of generator"
603 } else {
604 " of closure"
605 },
606 )
607 } else {
608 // unreachable?
609 (mir.span, "")
610 };
611
8faf50e0 612 diag.span_label(
b7449926
XL
613 return_span,
614 format!("return type{} is {}", mir_description, type_name),
8faf50e0
XL
615 );
616
b7449926
XL
617 // This counter value will already have been used, so this function will increment it
618 // so the next value will be used next and return the region name that would have been
619 // used.
620 Some(self.synthesize_region_name(counter))
8faf50e0
XL
621 }
622
623 /// Create a synthetic region named `'1`, incrementing the
624 /// counter.
625 fn synthesize_region_name(&self, counter: &mut usize) -> InternedString {
626 let c = *counter;
627 *counter += 1;
628
629 Name::intern(&format!("'{:?}", c)).as_interned_str()
630 }
631}