Skip to content

Commit 0ccee30

Browse files
committed
Auto merge of #58281 - mark-i-m:synthesis, r=estebank
Add outlives suggestions for some lifetime errors This PR implements suggestion diagnostics for some lifetime mismatch errors. When the borrow checker finds that some lifetime 'a doesn't outlive some other lifetime 'b that it should outlive, then in addition to the current lifetime error, we also emit a suggestion for how to fix the problem by adding a bound: - If a and b are normal named regions, suggest to add the bound `'a: 'b` - If b is static, suggest to replace a with static - If b also needs to outlive a, they must be the same, so suggest unifying them We start with a simpler implementation that avoids diagnostic regression or implementation complexity: - We only makes suggestions for lifetimes the user can already name (eg not closure regions or elided regions) - For now, we only emit a help note, not an actually suggestion because it is significantly easier. Finally, there is one hack: it seems that implicit regions in async fn are given the name '_ incorrectly. To avoid suggesting '_: 'x, we simply filter out such lifetimes by name. For more info, see this internals thread: https://internals.rust-lang.org/t/mechanical-suggestions-for-some-borrow-checker-errors/9049/3 TL;DR Make suggestions to add a `where 'a: 'b` constraint for some lifetime errors. Details are in the paper linked from the internals thread above. r? @estebank TODO - [x] Clean up code - [x] Only make idiomatic suggestions - [x] don't suggest naming `&'a self` - [x] rather than `'a: 'static`, suggest replacing `'a` with `'static` - [x] rather than `'a: 'b, 'b: 'a`, suggest replacing `'a` with `'b` or vice versa - [x] Performance (maybe need a perf run when this is closer to the finish line?) - perf run was clean... - EDIT: perf run seems to only check non-error performance... How do we check that error performance didn't regress? - [x] Needs ui tests - [x] Integrate the `help` message into the main lifetime `error`
2 parents 3e525e3 + cba0761 commit 0ccee30

File tree

143 files changed

+1144
-6
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

143 files changed

+1144
-6
lines changed

src/librustc_errors/diagnostic.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::Applicability;
66
use crate::Level;
77
use crate::snippet::Style;
88
use std::fmt;
9-
use syntax_pos::{MultiSpan, Span};
9+
use syntax_pos::{MultiSpan, Span, DUMMY_SP};
1010

1111
#[must_use]
1212
#[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)]
@@ -17,6 +17,11 @@ pub struct Diagnostic {
1717
pub span: MultiSpan,
1818
pub children: Vec<SubDiagnostic>,
1919
pub suggestions: Vec<CodeSuggestion>,
20+
21+
/// This is not used for highlighting or rendering any error message. Rather, it can be used
22+
/// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
23+
/// `span` if there is one. Otherwise, it is `DUMMY_SP`.
24+
pub sort_span: Span,
2025
}
2126

2227
#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
@@ -87,6 +92,7 @@ impl Diagnostic {
8792
span: MultiSpan::new(),
8893
children: vec![],
8994
suggestions: vec![],
95+
sort_span: DUMMY_SP,
9096
}
9197
}
9298

@@ -118,6 +124,11 @@ impl Diagnostic {
118124
self.level == Level::Cancelled
119125
}
120126

127+
/// Set the sorting span.
128+
pub fn set_sort_span(&mut self, sp: Span) {
129+
self.sort_span = sp;
130+
}
131+
121132
/// Adds a span/label to be included in the resulting snippet.
122133
/// This label will be shown together with the original span/label used when creating the
123134
/// diagnostic, *not* a span added by one of the `span_*` methods.
@@ -457,6 +468,9 @@ impl Diagnostic {
457468

458469
pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
459470
self.span = sp.into();
471+
if let Some(span) = self.span.primary_span() {
472+
self.sort_span = span;
473+
}
460474
self
461475
}
462476

src/librustc_errors/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,11 @@ impl Handler {
575575
DiagnosticBuilder::new(self, Level::Fatal, msg)
576576
}
577577

578+
/// Construct a builder at the `Help` level with the `msg`.
579+
pub fn struct_help(&self, msg: &str) -> DiagnosticBuilder<'_> {
580+
DiagnosticBuilder::new(self, Level::Help, msg)
581+
}
582+
578583
pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: &str) -> FatalError {
579584
self.emit_diag_at_span(Diagnostic::new(Fatal, msg), span);
580585
FatalError

src/librustc_mir/borrow_check/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ fn do_mir_borrowck<'a, 'tcx>(
367367
}
368368

369369
if !mbcx.errors_buffer.is_empty() {
370-
mbcx.errors_buffer.sort_by_key(|diag| diag.span.primary_span());
370+
mbcx.errors_buffer.sort_by_key(|diag| diag.sort_span);
371371

372372
for diag in mbcx.errors_buffer.drain(..) {
373373
mbcx.infcx.tcx.sess.diagnostic().emit_diagnostic(&diag);

src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs

+21-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ use syntax::errors::Applicability;
1818
use syntax::symbol::kw;
1919
use syntax_pos::Span;
2020

21+
use self::outlives_suggestion::OutlivesSuggestionBuilder;
22+
23+
pub mod outlives_suggestion;
24+
2125
mod region_name;
2226
mod var_name;
2327

@@ -56,7 +60,6 @@ enum Trace {
5660
/// Various pieces of state used when reporting borrow checker errors.
5761
pub struct ErrorReportingCtx<'a, 'b, 'tcx> {
5862
/// The region inference context used for borrow chekcing this MIR body.
59-
#[allow(dead_code)] // FIXME(mark-i-m): used by outlives suggestions
6063
region_infcx: &'b RegionInferenceContext<'tcx>,
6164

6265
/// The inference context used for type checking.
@@ -370,6 +373,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
370373
fr: RegionVid,
371374
fr_origin: NLLRegionVariableOrigin,
372375
outlived_fr: RegionVid,
376+
outlives_suggestion: &mut OutlivesSuggestionBuilder,
373377
renctx: &mut RegionErrorNamingCtx,
374378
) -> DiagnosticBuilder<'a> {
375379
debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
@@ -415,9 +419,22 @@ impl<'tcx> RegionInferenceContext<'tcx> {
415419
self.report_fnmut_error(&errctx, &errci, renctx)
416420
}
417421
(ConstraintCategory::Assignment, true, false)
418-
| (ConstraintCategory::CallArgument, true, false) =>
419-
self.report_escaping_data_error(&errctx, &errci, renctx),
420-
_ => self.report_general_error(&errctx, &errci, renctx),
422+
| (ConstraintCategory::CallArgument, true, false) => {
423+
let mut db = self.report_escaping_data_error(&errctx, &errci, renctx);
424+
425+
outlives_suggestion.intermediate_suggestion(&errctx, &errci, renctx, &mut db);
426+
outlives_suggestion.collect_constraint(fr, outlived_fr);
427+
428+
db
429+
}
430+
_ => {
431+
let mut db = self.report_general_error(&errctx, &errci, renctx);
432+
433+
outlives_suggestion.intermediate_suggestion(&errctx, &errci, renctx, &mut db);
434+
outlives_suggestion.collect_constraint(fr, outlived_fr);
435+
436+
db
437+
}
421438
}
422439
}
423440

0 commit comments

Comments
 (0)