region_errors.rs 26.9 KB
Newer Older
M
Mark Mansi 已提交
1 2
//! Error reporting machinery for lifetime errors.

3
use rustc_errors::{Applicability, DiagnosticBuilder};
C
Camille GILLOT 已提交
4
use rustc_infer::infer::{
5
    error_reporting::nice_region_error::NiceRegionError,
6
    error_reporting::unexpected_hidden_region_diagnostic, NllRegionVariableOrigin,
C
Camille GILLOT 已提交
7
};
8
use rustc_middle::mir::{ConstraintCategory, ReturnConstraint};
M
Matthew Jasper 已提交
9
use rustc_middle::ty::subst::Subst;
10
use rustc_middle::ty::{self, RegionVid, Ty};
11
use rustc_span::symbol::{kw, sym};
12
use rustc_span::{BytePos, Span};
13

14
use crate::borrowck_errors;
M
Mark Mansi 已提交
15

16 17
use crate::region_infer::BlameConstraint;
use crate::{
18
    nll::ConstraintDescription,
19
    region_infer::{values::RegionElement, TypeTest},
20
    universal_regions::DefiningTy,
M
mark 已提交
21
    MirBorrowckCtxt,
M
Mark Mansi 已提交
22 23
};

S
SNCPlay42 已提交
24
use super::{OutlivesSuggestionBuilder, RegionName};
25 26 27

impl ConstraintDescription for ConstraintCategory {
    fn description(&self) -> &'static str {
28
        // Must end with a space. Allows for empty names to be provided.
29
        match self {
30
            ConstraintCategory::Assignment => "assignment ",
31
            ConstraintCategory::Return(_) => "returning this value ",
32
            ConstraintCategory::Yield => "yielding this value ",
33 34
            ConstraintCategory::UseAsConst => "using this value as a constant ",
            ConstraintCategory::UseAsStatic => "using this value as a static ",
35 36 37 38 39 40 41
            ConstraintCategory::Cast => "cast ",
            ConstraintCategory::CallArgument => "argument ",
            ConstraintCategory::TypeAnnotation => "type annotation ",
            ConstraintCategory::ClosureBounds => "closure body ",
            ConstraintCategory::SizedBound => "proving this value is `Sized` ",
            ConstraintCategory::CopyBound => "copying this value ",
            ConstraintCategory::OpaqueType => "opaque type ",
42
            ConstraintCategory::ClosureUpvar(_) => "closure capture ",
43 44
            ConstraintCategory::Boring
            | ConstraintCategory::BoringNoLocation
45
            | ConstraintCategory::Internal => "",
46 47
        }
    }
48 49
}

50 51 52 53 54 55 56 57 58
/// A collection of errors encountered during region inference. This is needed to efficiently
/// report errors after borrow checking.
///
/// Usually we expect this to either be empty or contain a small number of items, so we can avoid
/// allocation most of the time.
crate type RegionErrors<'tcx> = Vec<RegionErrorKind<'tcx>>;

#[derive(Clone, Debug)]
crate enum RegionErrorKind<'tcx> {
59 60
    /// A generic bound failure for a type test (`T: 'a`).
    TypeTestError { type_test: TypeTest<'tcx> },
61 62 63

    /// An unexpected hidden region for an opaque type.
    UnexpectedHiddenRegion {
64 65
        /// The span for the member constraint.
        span: Span,
66 67 68 69 70 71 72 73 74 75
        /// The hidden type.
        hidden_ty: Ty<'tcx>,
        /// The unexpected region.
        member_region: ty::Region<'tcx>,
    },

    /// Higher-ranked subtyping error.
    BoundUniversalRegionError {
        /// The placeholder free region.
        longer_fr: RegionVid,
76 77
        /// The region element that erroneously must be outlived by `longer_fr`.
        error_element: RegionElement,
78 79
        /// The placeholder region.
        placeholder: ty::PlaceholderRegion,
80 81 82 83 84
    },

    /// Any other lifetime error.
    RegionError {
        /// The origin of the region.
85
        fr_origin: NllRegionVariableOrigin,
86 87 88 89 90 91 92 93 94 95
        /// The region that should outlive `shorter_fr`.
        longer_fr: RegionVid,
        /// The region that should be shorter, but we can't prove it.
        shorter_fr: RegionVid,
        /// Indicates whether this is a reported error. We currently only report the first error
        /// encountered and leave the rest unreported so as not to overwhelm the user.
        is_reported: bool,
    },
}

96 97 98 99
/// Information about the various region constraints involved in a borrow checker error.
#[derive(Clone, Debug)]
pub struct ErrorConstraintInfo {
    // fr: outlived_fr
M
Mark Mansi 已提交
100 101 102 103
    pub(super) fr: RegionVid,
    pub(super) fr_is_local: bool,
    pub(super) outlived_fr: RegionVid,
    pub(super) outlived_fr_is_local: bool,
104 105

    // Category and span for best blame constraint
M
Mark Mansi 已提交
106 107
    pub(super) category: ConstraintCategory,
    pub(super) span: Span,
108 109
}

110
impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
111 112 113 114 115 116
    /// Converts a region inference variable into a `ty::Region` that
    /// we can use for error reporting. If `r` is universally bound,
    /// then we use the name that we have on record for it. If `r` is
    /// existentially bound, then we check its inferred value and try
    /// to find a good name from that. Returns `None` if we can't find
    /// one (e.g., this is just some random part of the CFG).
117
    pub(super) fn to_error_region(&self, r: RegionVid) -> Option<ty::Region<'tcx>> {
118
        self.to_error_region_vid(r).and_then(|r| self.regioncx.region_definition(r).external_name)
119 120
    }

121
    /// Returns the `RegionVid` corresponding to the region returned by
122
    /// `to_error_region`.
123
    pub(super) fn to_error_region_vid(&self, r: RegionVid) -> Option<RegionVid> {
124
        if self.regioncx.universal_regions().is_universal_region(r) {
125 126
            Some(r)
        } else {
127 128 129
            // We just want something nameable, even if it's not
            // actually an upper bound.
            let upper_bound = self.regioncx.approx_universal_upper_bound(r);
130

131
            if self.regioncx.upper_bound_in_region_scc(r, upper_bound) {
132 133 134 135 136 137 138
                self.to_error_region_vid(upper_bound)
            } else {
                None
            }
        }
    }

139
    /// Returns `true` if a closure is inferred to be an `FnMut` closure.
140
    fn is_closure_fn_mut(&self, fr: RegionVid) -> bool {
141
        if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) {
142
            if let ty::BoundRegionKind::BrEnv = free_region.bound_region {
143
                if let DefiningTy::Closure(_, substs) =
144
                    self.regioncx.universal_regions().defining_ty
145
                {
146
                    return substs.as_closure().kind() == ty::ClosureKind::FnMut;
147
                }
148
            }
149 150
        }

151
        false
152 153
    }

154
    /// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`.
155
    pub(crate) fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) {
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
        // Iterate through all the errors, producing a diagnostic for each one. The diagnostics are
        // buffered in the `MirBorrowckCtxt`.

        let mut outlives_suggestion = OutlivesSuggestionBuilder::default();

        for nll_error in nll_errors.into_iter() {
            match nll_error {
                RegionErrorKind::TypeTestError { type_test } => {
                    // Try to convert the lower-bound region into something named we can print for the user.
                    let lower_bound_region = self.to_error_region(type_test.lower_bound);

                    let type_test_span = type_test.locations.span(&self.body);

                    if let Some(lower_bound_region) = lower_bound_region {
                        self.infcx
                            .construct_generic_bound_failure(
                                type_test_span,
                                None,
                                type_test.generic_kind,
                                lower_bound_region,
                            )
                            .buffer(&mut self.errors_buffer);
                    } else {
                        // FIXME. We should handle this case better. It
                        // indicates that we have e.g., some region variable
                        // whose value is like `'a+'b` where `'a` and `'b` are
                        // distinct unrelated univesal regions that are not
                        // known to outlive one another. It'd be nice to have
                        // some examples where this arises to decide how best
                        // to report it; we could probably handle it by
                        // iterating over the universal regions and reporting
                        // an error that multiple bounds are required.
                        self.infcx
                            .tcx
                            .sess
                            .struct_span_err(
                                type_test_span,
                                &format!("`{}` does not live long enough", type_test.generic_kind),
                            )
                            .buffer(&mut self.errors_buffer);
                    }
                }

199 200 201
                RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, member_region } => {
                    let named_ty = self.regioncx.name_regions(self.infcx.tcx, hidden_ty);
                    let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region);
202
                    unexpected_hidden_region_diagnostic(
203
                        self.infcx.tcx,
204 205 206
                        span,
                        named_ty,
                        named_region,
207 208 209 210 211 212
                    )
                    .buffer(&mut self.errors_buffer);
                }

                RegionErrorKind::BoundUniversalRegionError {
                    longer_fr,
213
                    placeholder,
214 215
                    error_element,
                } => {
216
                    let error_vid = self.regioncx.region_from_element(longer_fr, &error_element);
217 218

                    // Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
219
                    let (_, span) = self.regioncx.find_outlives_blame_span(
220 221
                        &self.body,
                        longer_fr,
222 223
                        NllRegionVariableOrigin::Placeholder(placeholder),
                        error_vid,
224 225
                    );

226 227 228 229
                    let universe = placeholder.universe;
                    let universe_info = self.regioncx.universe_info(universe);

                    universe_info.report_error(self, placeholder, error_element, span);
230 231 232 233
                }

                RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => {
                    if is_reported {
M
Mark Mansi 已提交
234
                        self.report_region_error(
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
                            longer_fr,
                            fr_origin,
                            shorter_fr,
                            &mut outlives_suggestion,
                        );
                    } else {
                        // We only report the first error, so as not to overwhelm the user. See
                        // `RegRegionErrorKind` docs.
                        //
                        // FIXME: currently we do nothing with these, but perhaps we can do better?
                        // FIXME: try collecting these constraints on the outlives suggestion
                        // builder. Does it make the suggestions any better?
                        debug!(
                            "Unreported region error: can't prove that {:?}: {:?}",
                            longer_fr, shorter_fr
                        );
                    }
                }
            }
        }

        // Emit one outlives suggestions for each MIR def we borrowck
M
Mark Mansi 已提交
257
        outlives_suggestion.add_suggestion(self);
258 259
    }

260 261 262 263 264 265 266 267
    /// Report an error because the universal region `fr` was required to outlive
    /// `outlived_fr` but it is not known to do so. For example:
    ///
    /// ```
    /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
    /// ```
    ///
    /// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`.
268
    pub(crate) fn report_region_error(
269
        &mut self,
270
        fr: RegionVid,
271
        fr_origin: NllRegionVariableOrigin,
272
        outlived_fr: RegionVid,
M
mark 已提交
273
        outlives_suggestion: &mut OutlivesSuggestionBuilder,
274
    ) {
M
Mark Mansi 已提交
275
        debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
276

277
        let BlameConstraint { category, span, variance_info, from_closure: _ } =
278 279
            self.regioncx.best_blame_constraint(&self.body, fr, fr_origin, |r| {
                self.regioncx.provides_universal_region(r, fr, outlived_fr)
280
            });
D
David Wood 已提交
281

282
        debug!("report_region_error: category={:?} {:?} {:?}", category, span, variance_info);
283
        // Check if we can use one of the "nice region errors".
D
David Wood 已提交
284
        if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
285
            let nice = NiceRegionError::new_from_span(self.infcx, span, o, f);
286
            if let Some(diag) = nice.try_report_from_nll() {
287 288
                diag.buffer(&mut self.errors_buffer);
                return;
289 290 291
            }
        }

292
        let (fr_is_local, outlived_fr_is_local): (bool, bool) = (
293 294
            self.regioncx.universal_regions().is_local_free_region(fr),
            self.regioncx.universal_regions().is_local_free_region(outlived_fr),
295 296
        );

N
Niko Matsakis 已提交
297
        debug!(
M
Mark Mansi 已提交
298
            "report_region_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}",
N
Niko Matsakis 已提交
299 300
            fr_is_local, outlived_fr_is_local, category
        );
301 302

        let errci = ErrorConstraintInfo {
M
Mark Rousskov 已提交
303 304 305 306 307 308
            fr,
            outlived_fr,
            fr_is_local,
            outlived_fr_is_local,
            category,
            span,
309 310
        };

311
        let mut diag = match (category, fr_is_local, outlived_fr_is_local) {
312 313
            (ConstraintCategory::Return(kind), true, false) if self.is_closure_fn_mut(fr) => {
                self.report_fnmut_error(&errci, kind)
N
Niko Matsakis 已提交
314 315
            }
            (ConstraintCategory::Assignment, true, false)
M
Mark Mansi 已提交
316
            | (ConstraintCategory::CallArgument, true, false) => {
M
Mark Mansi 已提交
317
                let mut db = self.report_escaping_data_error(&errci);
M
Mark Mansi 已提交
318

M
Mark Mansi 已提交
319
                outlives_suggestion.intermediate_suggestion(self, &errci, &mut db);
M
Mark Mansi 已提交
320 321 322 323 324
                outlives_suggestion.collect_constraint(fr, outlived_fr);

                db
            }
            _ => {
M
Mark Mansi 已提交
325
                let mut db = self.report_general_error(&errci);
M
Mark Mansi 已提交
326

M
Mark Mansi 已提交
327
                outlives_suggestion.intermediate_suggestion(self, &errci, &mut db);
M
Mark Mansi 已提交
328 329 330 331
                outlives_suggestion.collect_constraint(fr, outlived_fr);

                db
            }
332 333
        };

334 335 336 337 338 339 340 341 342 343 344 345 346
        match variance_info {
            ty::VarianceDiagInfo::None => {}
            ty::VarianceDiagInfo::Mut { kind, ty } => {
                let kind_name = match kind {
                    ty::VarianceDiagMutKind::Ref => "reference",
                    ty::VarianceDiagMutKind::RawPtr => "pointer",
                };
                diag.note(&format!("requirement occurs because of a mutable {kind_name} to {ty}",));
                diag.note(&format!("mutable {kind_name}s are invariant over their type parameter"));
                diag.help("see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance");
            }
        }

347
        diag.buffer(&mut self.errors_buffer);
D
David Wood 已提交
348 349
    }

D
David Wood 已提交
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
    /// Report a specialized error when `FnMut` closures return a reference to a captured variable.
    /// This function expects `fr` to be local and `outlived_fr` to not be local.
    ///
    /// ```text
    /// error: captured variable cannot escape `FnMut` closure body
    ///   --> $DIR/issue-53040.rs:15:8
    ///    |
    /// LL |     || &mut v;
    ///    |     -- ^^^^^^ creates a reference to a captured variable which escapes the closure body
    ///    |     |
    ///    |     inferred to be a `FnMut` closure
    ///    |
    ///    = note: `FnMut` closures only have access to their captured variables while they are
    ///            executing...
    ///    = note: ...therefore, returned references to captured variables will escape the closure
    /// ```
366 367 368 369 370
    fn report_fnmut_error(
        &self,
        errci: &ErrorConstraintInfo,
        kind: ReturnConstraint,
    ) -> DiagnosticBuilder<'tcx> {
M
Mark Rousskov 已提交
371
        let ErrorConstraintInfo { outlived_fr, span, .. } = errci;
372

373
        let mut diag = self
374
            .infcx
N
Niko Matsakis 已提交
375 376
            .tcx
            .sess
377
            .struct_span_err(*span, "captured variable cannot escape `FnMut` closure body");
D
David Wood 已提交
378

379
        let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty;
L
LeSeulArtichaut 已提交
380
        if let ty::Opaque(def_id, _) = *output_ty.kind() {
381 382 383 384 385
            output_ty = self.infcx.tcx.type_of(def_id)
        };

        debug!("report_fnmut_error: output_ty={:?}", output_ty);

L
LeSeulArtichaut 已提交
386
        let message = match output_ty.kind() {
387 388 389 390 391 392 393 394 395
            ty::Closure(_, _) => {
                "returns a closure that contains a reference to a captured variable, which then \
                 escapes the closure body"
            }
            ty::Adt(def, _) if self.infcx.tcx.is_diagnostic_item(sym::gen_future, def.did) => {
                "returns an `async` block that contains a reference to a captured variable, which then \
                 escapes the closure body"
            }
            _ => "returns a reference to a captured variable which escapes the closure body",
396 397
        };

398
        diag.span_label(*span, message);
D
David Wood 已提交
399

400
        // FIXME(project-rfc-2229#48): This should store a captured_place not a hir id
401 402 403
        if let ReturnConstraint::ClosureUpvar(upvar) = kind {
            let def_id = match self.regioncx.universal_regions().defining_ty {
                DefiningTy::Closure(def_id, _) => def_id,
404
                ty => bug!("unexpected DefiningTy {:?}", ty),
405 406 407 408 409 410 411 412
            };

            let upvar_def_span = self.infcx.tcx.hir().span(upvar);
            let upvar_span = self.infcx.tcx.upvars_mentioned(def_id).unwrap()[&upvar].span;
            diag.span_label(upvar_def_span, "variable defined here");
            diag.span_label(upvar_span, "variable captured here");
        }

S
SNCPlay42 已提交
413 414
        if let Some(fr_span) = self.give_region_a_name(*outlived_fr).unwrap().span() {
            diag.span_label(fr_span, "inferred to be a `FnMut` closure");
D
David Wood 已提交
415 416
        }

N
Niko Matsakis 已提交
417 418 419 420
        diag.note(
            "`FnMut` closures only have access to their captured variables while they are \
             executing...",
        );
D
David Wood 已提交
421 422
        diag.note("...therefore, they cannot allow references to captured variables to escape");

423
        diag
D
David Wood 已提交
424 425
    }

F
Frank Steffahn 已提交
426
    /// Reports an error specifically for when data is escaping a closure.
D
David Wood 已提交
427 428 429 430 431 432 433 434 435 436 437
    ///
    /// ```text
    /// error: borrowed data escapes outside of function
    ///   --> $DIR/lifetime-bound-will-change-warning.rs:44:5
    ///    |
    /// LL | fn test2<'a>(x: &'a Box<Fn()+'a>) {
    ///    |              - `x` is a reference that is only valid in the function body
    /// LL |     // but ref_obj will not, so warn.
    /// LL |     ref_obj(x)
    ///    |     ^^^^^^^^^^ `x` escapes the function body here
    /// ```
M
Mark Mansi 已提交
438
    fn report_escaping_data_error(&self, errci: &ErrorConstraintInfo) -> DiagnosticBuilder<'tcx> {
M
Mark Rousskov 已提交
439
        let ErrorConstraintInfo { span, category, .. } = errci;
440

441
        let fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
442 443 444 445
            self.infcx.tcx,
            &self.body,
            &self.local_names,
            &self.upvars,
M
mark 已提交
446 447
            errci.fr,
        );
448
        let outlived_fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
449 450 451 452
            self.infcx.tcx,
            &self.body,
            &self.local_names,
            &self.upvars,
453 454
            errci.outlived_fr,
        );
455

M
Mark Mansi 已提交
456 457 458 459
        let (_, escapes_from) = self
            .infcx
            .tcx
            .article_and_description(self.regioncx.universal_regions().defining_ty.def_id());
460

461 462 463
        // Revert to the normal error in these cases.
        // Assignments aren't "escapes" in function items.
        if (fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none())
M
Mark Mansi 已提交
464
            || (*category == ConstraintCategory::Assignment
M
Mark Mansi 已提交
465 466
                && self.regioncx.universal_regions().defining_ty.is_fn_def())
            || self.regioncx.universal_regions().defining_ty.is_const()
467
        {
M
Mark Mansi 已提交
468 469 470 471 472
            return self.report_general_error(&ErrorConstraintInfo {
                fr_is_local: true,
                outlived_fr_is_local: false,
                ..*errci
            });
473 474
        }

M
Mark Rousskov 已提交
475
        let mut diag =
476
            borrowck_errors::borrowed_data_escapes_closure(self.infcx.tcx, *span, escapes_from);
D
David Wood 已提交
477

D
David Wood 已提交
478 479 480 481
        if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span {
            diag.span_label(
                outlived_fr_span,
                format!(
482
                    "`{}` declared here, outside of the {} body",
D
David Wood 已提交
483 484 485
                    outlived_fr_name, escapes_from
                ),
            );
D
David Wood 已提交
486 487
        }

D
David Wood 已提交
488 489 490 491 492 493 494 495
        if let Some((Some(fr_name), fr_span)) = fr_name_and_span {
            diag.span_label(
                fr_span,
                format!(
                    "`{}` is a reference that is only valid in the {} body",
                    fr_name, escapes_from
                ),
            );
D
David Wood 已提交
496

M
Mark Rousskov 已提交
497
            diag.span_label(*span, format!("`{}` escapes the {} body here", fr_name, escapes_from));
D
David Wood 已提交
498 499
        }

500
        diag
D
David Wood 已提交
501 502
    }

D
David Wood 已提交
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
    /// Reports a region inference error for the general case with named/synthesized lifetimes to
    /// explain what is happening.
    ///
    /// ```text
    /// error: unsatisfied lifetime constraints
    ///   --> $DIR/regions-creating-enums3.rs:17:5
    ///    |
    /// LL | fn mk_add_bad1<'a,'b>(x: &'a ast<'a>, y: &'b ast<'b>) -> ast<'a> {
    ///    |                -- -- lifetime `'b` defined here
    ///    |                |
    ///    |                lifetime `'a` defined here
    /// LL |     ast::add(x, y)
    ///    |     ^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it
    ///    |                    is returning data with lifetime `'b`
    /// ```
M
Mark Mansi 已提交
518
    fn report_general_error(&self, errci: &ErrorConstraintInfo) -> DiagnosticBuilder<'tcx> {
519
        let ErrorConstraintInfo {
M
Mark Rousskov 已提交
520 521 522 523 524 525 526
            fr,
            fr_is_local,
            outlived_fr,
            outlived_fr_is_local,
            span,
            category,
            ..
527 528
        } = errci;

M
mark 已提交
529
        let mut diag =
530
            self.infcx.tcx.sess.struct_span_err(*span, "lifetime may not live long enough");
531

532 533
        let (_, mir_def_name) =
            self.infcx.tcx.article_and_description(self.mir_def_id().to_def_id());
534

M
Mark Mansi 已提交
535
        let fr_name = self.give_region_a_name(*fr).unwrap();
536
        fr_name.highlight_region_name(&mut diag);
M
Mark Mansi 已提交
537
        let outlived_fr_name = self.give_region_a_name(*outlived_fr).unwrap();
538 539
        outlived_fr_name.highlight_region_name(&mut diag);

540
        match (category, outlived_fr_is_local, fr_is_local) {
541
            (ConstraintCategory::Return(_), true, _) => {
N
Niko Matsakis 已提交
542
                diag.span_label(
543
                    *span,
N
Niko Matsakis 已提交
544 545 546 547 548 549 550
                    format!(
                        "{} was supposed to return data with lifetime `{}` but it is returning \
                         data with lifetime `{}`",
                        mir_def_name, outlived_fr_name, fr_name
                    ),
                );
            }
551
            _ => {
N
Niko Matsakis 已提交
552
                diag.span_label(
553
                    *span,
N
Niko Matsakis 已提交
554 555 556 557 558 559 560 561
                    format!(
                        "{}requires that `{}` must outlive `{}`",
                        category.description(),
                        fr_name,
                        outlived_fr_name,
                    ),
                );
            }
562
        }
563

564
        self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr);
565

566
        diag
567 568
    }

F
Frank Steffahn 已提交
569
    /// Adds a suggestion to errors where an `impl Trait` is returned.
D
David Wood 已提交
570 571
    ///
    /// ```text
572
    /// help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as
D
David Wood 已提交
573 574 575 576 577
    ///       a constraint
    ///    |
    /// LL |     fn iter_values_anon(&self) -> impl Iterator<Item=u32> + 'a {
    ///    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    /// ```
578 579
    fn add_static_impl_trait_suggestion(
        &self,
580
        diag: &mut DiagnosticBuilder<'tcx>,
D
David Wood 已提交
581 582
        fr: RegionVid,
        // We need to pass `fr_name` - computing it again will label it twice.
583
        fr_name: RegionName,
D
David Wood 已提交
584
        outlived_fr: RegionVid,
585
    ) {
N
Niko Matsakis 已提交
586 587 588
        if let (Some(f), Some(ty::RegionKind::ReStatic)) =
            (self.to_error_region(fr), self.to_error_region(outlived_fr))
        {
L
LeSeulArtichaut 已提交
589
            if let Some(&ty::Opaque(did, substs)) = self
590
                .infcx
N
Niko Matsakis 已提交
591 592
                .tcx
                .is_suitable_region(f)
B
Bastian Kauschke 已提交
593
                .map(|r| r.def_id)
L
LeSeulArtichaut 已提交
594 595
                .and_then(|id| self.infcx.tcx.return_type_impl_trait(id))
                .map(|(ty, _)| ty.kind())
596
            {
D
David Wood 已提交
597 598 599 600
                // Check whether or not the impl trait return type is intended to capture
                // data with the static lifetime.
                //
                // eg. check for `impl Trait + 'static` instead of `impl Trait`.
601
                let has_static_predicate = {
M
Matthew Jasper 已提交
602
                    let bounds = self.infcx.tcx.explicit_item_bounds(did);
603 604

                    let mut found = false;
M
Matthew Jasper 已提交
605
                    for (bound, _) in bounds {
J
Jack Huey 已提交
606 607
                        if let ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_, r)) =
                            bound.kind().skip_binder()
608
                        {
M
Matthew Jasper 已提交
609
                            let r = r.subst(self.infcx.tcx, substs);
610
                            if let ty::RegionKind::ReStatic = r {
611 612
                                found = true;
                                break;
613 614 615 616
                            } else {
                                // If there's already a lifetime bound, don't
                                // suggest anything.
                                return;
617 618 619 620 621 622 623
                            }
                        }
                    }

                    found
                };

N
Niko Matsakis 已提交
624 625 626 627
                debug!(
                    "add_static_impl_trait_suggestion: has_static_predicate={:?}",
                    has_static_predicate
                );
628
                let static_str = kw::StaticLifetime;
D
David Wood 已提交
629 630
                // If there is a static predicate, then the only sensible suggestion is to replace
                // fr with `'static`.
631
                if has_static_predicate {
632
                    diag.help(&format!("consider replacing `{}` with `{}`", fr_name, static_str));
633
                } else {
D
David Wood 已提交
634
                    // Otherwise, we should suggest adding a constraint on the return type.
B
Bastian Kauschke 已提交
635
                    let span = self.infcx.tcx.def_span(did);
636
                    if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
637
                        let suggestable_fr_name = if fr_name.was_named() {
L
ljedrz 已提交
638
                            fr_name.to_string()
639 640
                        } else {
                            "'_".to_string()
D
David Wood 已提交
641
                        };
642
                        let span = if snippet.ends_with(';') {
643
                            // `type X = impl Trait;`
644
                            span.with_hi(span.hi() - BytePos(1))
645
                        } else {
646
                            span
647
                        };
648 649
                        let suggestion = format!(" + {}", suggestable_fr_name);
                        let span = span.shrink_to_hi();
650
                        diag.span_suggestion(
651 652
                            span,
                            &format!(
653
                                "to allow this `impl Trait` to capture borrowed data with lifetime \
654
                                 `{}`, add `{}` as a bound",
D
David Wood 已提交
655
                                fr_name, suggestable_fr_name,
656
                            ),
657
                            suggestion,
658
                            Applicability::MachineApplicable,
659 660
                        );
                    }
661 662 663
                }
            }
        }
664 665
    }
}