Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 6 additions & 32 deletions compiler/rustc_borrowck/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,20 +74,6 @@ pub(super) struct DescribePlaceOpt {

pub(super) struct IncludingTupleField(pub(super) bool);

pub(crate) enum BufferedDiag<'diag> {
Error(Diag<'diag>),
NonError(Diag<'diag, ()>),
}

impl<'diag> BufferedDiag<'diag> {
fn sort_span(&self) -> Span {
match self {
BufferedDiag::Error(diag) => diag.sort_span,
BufferedDiag::NonError(diag) => diag.sort_span,
}
}
}

#[derive(Default)]
pub(crate) struct BorrowckDiagnosticsBuffer<'diag, 'tcx> {
/// This field keeps track of move errors that are to be reported for given move indices.
Expand All @@ -108,16 +94,13 @@ pub(crate) struct BorrowckDiagnosticsBuffer<'diag, 'tcx> {

buffered_mut_errors: FxIndexMap<Span, (Diag<'diag>, usize)>,

/// Buffer of diagnostics to be reported. A mixture of error and non-error diagnostics.
buffered_diags: Vec<BufferedDiag<'diag>>,
/// Buffer of diagnostics to be reported.
buffered_diags: Vec<Diag<'diag>>,
}

impl<'diag, 'tcx> BorrowckDiagnosticsBuffer<'diag, 'tcx> {
pub(crate) fn buffer_non_error(&mut self, diag: Diag<'diag, ()>) {
self.buffered_diags.push(BufferedDiag::NonError(diag));
}
pub(crate) fn buffer_error(&mut self, diag: Diag<'diag>) {
self.buffered_diags.push(BufferedDiag::Error(diag));
self.buffered_diags.push(diag);
}

pub(crate) fn emit_errors(&mut self) {
Expand All @@ -134,14 +117,9 @@ impl<'diag, 'tcx> BorrowckDiagnosticsBuffer<'diag, 'tcx> {
}

if !self.buffered_diags.is_empty() {
self.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span());
for buffered_diag in self.buffered_diags.drain(..) {
match buffered_diag {
BufferedDiag::Error(diag) => {
diag.emit();
}
BufferedDiag::NonError(diag) => diag.emit(),
}
self.buffered_diags.sort_by_key(|diag| diag.sort_span);
for diag in self.buffered_diags.drain(..) {
diag.emit();
}
}
}
Expand All @@ -152,10 +130,6 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> {
self.diags_buffer.buffer_error(diag.with_dcx(self.dcx()));
}

pub(crate) fn buffer_non_error(&mut self, diag: Diag<'_, ()>) {
self.diags_buffer.buffer_non_error(diag.with_dcx(self.dcx()));
}

pub(crate) fn buffer_move_error(
&mut self,
move_out_indices: Vec<MoveOutIndex>,
Expand Down
79 changes: 26 additions & 53 deletions compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ use crate::MirBorrowckCtxt;

/// The different things we could suggest.
enum SuggestedConstraint {
/// Outlives(a, [b, c, d, ...]) => 'a: 'b + 'c + 'd + ...
Outlives(RegionName, SmallVec<[RegionName; 2]>),

/// 'a = 'b
Equal(RegionName, RegionName),

Expand Down Expand Up @@ -106,11 +103,10 @@ impl OutlivesSuggestionBuilder {
continue;
}

// There are three types of suggestions we can make:
// 1) Suggest a bound: 'a: 'b
// 2) Suggest replacing 'a with 'static. If any of `outlived` is `'static`, then we
// There are two types of suggestions we can make:
// 1) Suggest replacing 'a with 'static. If any of `outlived` is `'static`, then we
// should just replace 'a with 'static.
// 3) Suggest unifying 'a with 'b if we have both 'a: 'b and 'b: 'a
// 2) Suggest unifying 'a with 'b if we have both 'a: 'b and 'b: 'a

if outlived
.iter()
Expand All @@ -121,7 +117,7 @@ impl OutlivesSuggestionBuilder {
// We want to isolate out all lifetimes that should be unified and print out
// separate messages for them.

let (unified, other): (Vec<_>, Vec<_>) = outlived.into_iter().partition(
let unified = outlived.into_iter().filter(
// Do we have both 'fr: 'r and 'r: 'fr?
|(r, _)| {
self.constraints_to_add
Expand All @@ -130,17 +126,12 @@ impl OutlivesSuggestionBuilder {
},
);

for (r, bound) in unified.into_iter() {
for (r, bound) in unified {
if !unified_already.contains(fr) {
suggested.push(SuggestedConstraint::Equal(fr_name, bound));
unified_already.insert(r);
}
}

if !other.is_empty() {
let other = other.iter().map(|(_, rname)| *rname).collect::<SmallVec<_>>();
suggested.push(SuggestedConstraint::Outlives(fr_name, other))
}
}
}

Expand Down Expand Up @@ -202,54 +193,36 @@ impl OutlivesSuggestionBuilder {
return;
}

// If there is exactly one suggestable constraints, then just suggest it. Otherwise, emit a
// list of diagnostics.
let mut diag = if let [constraint] = suggested.as_slice() {
mbcx.dcx().struct_help(match constraint {
SuggestedConstraint::Outlives(a, bs) => {
let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect();
format!("add bound `{a}: {}`", bs.join(" + "))
}

// Emit an error with a list of one or more help suggestions. This is a weird error because
// it's just there to provide somewhere to put the help suggestions that describe how to
// fix the one or more borrow errors already reported within the item.
let tcx = mbcx.infcx.tcx;
let def_id = mbcx.mir_def_id();
let span = tcx.def_ident_span(def_id).unwrap_or_else(|| tcx.def_span(def_id));
let mut diag = tcx
.dcx()
.struct_err("one or more lifetime errors were found in this item")
.with_span(span);

// Add suggestions.
for constraint in suggested {
match constraint {
SuggestedConstraint::Equal(a, b) => {
format!("`{a}` and `{b}` must be the same: replace one with the other")
diag.help(format!(
"`{a}` and `{b}` must be the same: replace one with the other",
));
}
SuggestedConstraint::Static(a) => format!("replace `{a}` with `'static`"),
})
} else {
// Create a new diagnostic.
let mut diag = mbcx
.infcx
.tcx
.dcx()
.struct_help("the following changes may resolve your lifetime errors");

// Add suggestions.
for constraint in suggested {
match constraint {
SuggestedConstraint::Outlives(a, bs) => {
let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect();
diag.help(format!("add bound `{a}: {}`", bs.join(" + ")));
}
SuggestedConstraint::Equal(a, b) => {
diag.help(format!(
"`{a}` and `{b}` must be the same: replace one with the other",
));
}
SuggestedConstraint::Static(a) => {
diag.help(format!("replace `{a}` with `'static`"));
}
SuggestedConstraint::Static(a) => {
diag.help(format!("replace `{a}` with `'static`"));
}
}

diag
};
}

// We want this message to appear after other messages on the mir def.
let mir_span = mbcx.body.span;
diag.sort_span = mir_span.shrink_to_hi();

// Buffer the diagnostic
mbcx.buffer_non_error(diag);
mbcx.buffer_error(diag);
}
}
21 changes: 10 additions & 11 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1148,11 +1148,6 @@ impl<'a> DiagCtxtHandle<'a> {
self.create_note(note).emit()
}

#[track_caller]
pub fn struct_help(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
Diag::new(self, Help, msg)
}

#[track_caller]
pub fn struct_failure_note(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
Diag::new(self, FailureNote, msg)
Expand Down Expand Up @@ -1572,7 +1567,7 @@ impl DelayedDiagInner {
/// | ForceWarning | - | () | yes | lint-only
/// | Warning | - | () | yes | yes
/// | Note | - | () | rare | -
/// | Help | - | () | rare | -
/// | Help | - | () | don't use | -
/// | FailureNote | - | () | rare | -
/// | Allow | - | () | yes | lint-only
/// | Expect | - | () | yes | lint-only
Expand Down Expand Up @@ -1607,14 +1602,18 @@ pub enum Level {
/// Will be skipped if `can_emit_warnings` is false.
Warning,

/// A message giving additional context.
/// A rarely-used level for output that isn't an error or a warning.
Note,

/// A message suggesting how to fix something.
///
/// FIXME(nnethercote) Do not use this! Currently only exists to support `proc_macro::Help`,
/// part of the unstable `proc_macro_diagnostic` feature (see #54140). Should be removed
/// because help messages are fine as subdiagnostics but are silly as top-level diagnostics.
Help,

/// Similar to `Note`, but used in cases where compilation has failed. When printed for human
/// consumption, it doesn't have any kind of `note:` label.
/// Similar to `Note`, but even rarer. Lacks the a trailing blank line that all other
/// diagnostics have. Also, when printed for human consumption it doesn't have a `note:` label.
FailureNote,

/// Only used for lints.
Expand Down Expand Up @@ -1666,13 +1665,13 @@ pub enum Sublevel {
/// See `Level::Warning`.
Warning,

/// See `Level::Note`.
/// A message giving additional context.
Note,

/// A note that is only emitted once.
OnceNote,

/// See `Level::Help`.
/// A message suggesting how to fix something.
Help,

/// A help that is only emitted once.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ fn foo<'a, I : for<'x> Foo<&'x isize>>(
let y: I::A = x;
}

fn bar<'a, 'b, I : for<'x> Foo<&'x isize>>(
fn bar<'a, 'b, I : for<'x> Foo<&'x isize>>( //~ ERROR one or more lifetime errors
x: <I as Foo<&'a isize>>::A,
y: <I as Foo<&'b isize>>::A,
cond: bool)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ LL | let z: I::A = if cond { x } else { y };
|
= help: consider adding the following bound: `'a: 'b`

help: `'a` and `'b` must be the same: replace one with the other
error: one or more lifetime errors were found in this item
--> $DIR/associated-types-project-from-hrtb-in-fn-body.rs:16:4
|
LL | fn bar<'a, 'b, I : for<'x> Foo<&'x isize>>(
| ^^^
|
= help: `'a` and `'b` must be the same: replace one with the other

error: aborting due to 2 previous errors
error: aborting due to 3 previous errors

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: lifetime may not live long enough
--> $DIR/project-fn-ret-contravariant.rs:46:4
--> $DIR/project-fn-ret-contravariant.rs:47:4
|
LL | fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
| -- -- lifetime `'b` defined here
Expand All @@ -12,7 +12,7 @@ LL | (a, b)
= help: consider adding the following bound: `'a: 'b`

error: lifetime may not live long enough
--> $DIR/project-fn-ret-contravariant.rs:46:4
--> $DIR/project-fn-ret-contravariant.rs:47:4
|
LL | fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
| -- -- lifetime `'b` defined here
Expand All @@ -24,7 +24,13 @@ LL | (a, b)
|
= help: consider adding the following bound: `'b: 'a`

help: `'a` and `'b` must be the same: replace one with the other
error: one or more lifetime errors were found in this item
--> $DIR/project-fn-ret-contravariant.rs:43:4
|
LL | fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
| ^^^^^^^^^
|
= help: `'a` and `'b` must be the same: replace one with the other

error: aborting due to 2 previous errors
error: aborting due to 3 previous errors

Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ fn baz<'a,'b>(x: &'a u32) -> &'static u32 {

#[cfg(krisskross)] // two instantiations, mixing and matching: BAD
fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
//[krisskross]~^ ERROR one or more lifetime errors
let a = bar(foo, y);
let b = bar(foo, x);
(a, b) //[krisskross]~ ERROR lifetime may not live long enough
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: lifetime may not live long enough
--> $DIR/project-fn-ret-invariant.rs:59:5
--> $DIR/project-fn-ret-invariant.rs:61:5
|
LL | fn transmute<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
| -- -- lifetime `'b` defined here
Expand All @@ -15,7 +15,7 @@ LL | (a, b)
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

error: lifetime may not live long enough
--> $DIR/project-fn-ret-invariant.rs:59:5
--> $DIR/project-fn-ret-invariant.rs:61:5
|
LL | fn transmute<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
| -- -- lifetime `'b` defined here
Expand All @@ -30,7 +30,13 @@ LL | (a, b)
= note: the struct `Type<'a>` is invariant over the parameter `'a`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

help: `'a` and `'b` must be the same: replace one with the other
error: one or more lifetime errors were found in this item
--> $DIR/project-fn-ret-invariant.rs:57:4
|
LL | fn transmute<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
| ^^^^^^^^^
|
= help: `'a` and `'b` must be the same: replace one with the other

error: aborting due to 2 previous errors
error: aborting due to 3 previous errors

Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
error: lifetime may not live long enough
--> $DIR/project-fn-ret-invariant.rs:40:13
--> $DIR/project-fn-ret-invariant.rs:41:13
|
LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | let f = foo; // <-- No consistent type can be inferred for `f` here.
...
LL | let a = bar(f, x);
| ^^^^^^^^^ argument requires that `'b` must outlive `'a`
|
Expand All @@ -15,7 +15,7 @@ LL | let a = bar(f, x);
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

error: lifetime may not live long enough
--> $DIR/project-fn-ret-invariant.rs:42:13
--> $DIR/project-fn-ret-invariant.rs:43:13
|
LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
| -- -- lifetime `'b` defined here
Expand All @@ -30,7 +30,13 @@ LL | let b = bar(f, y);
= note: the struct `Type<'a>` is invariant over the parameter `'a`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

help: `'a` and `'b` must be the same: replace one with the other
error: one or more lifetime errors were found in this item
--> $DIR/project-fn-ret-invariant.rs:38:4
|
LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
| ^^^
|
= help: `'a` and `'b` must be the same: replace one with the other

error: aborting due to 2 previous errors
error: aborting due to 3 previous errors

Loading
Loading