1
Fork 0

Remove BorrowckErrors::tainted_by_errors.

`BorrowckErrors` stores a mix of error and non-error diags in
`buffered`. As a result, it downgrades `DiagnosticBuilder`s to
`Diagnostic`s, losing the emission guarantees, and so has to use a
`tainted_by_errors` field to record whether an error has occurred.

This commit splits `buffered` into `buffered_errors` and
`buffered_non_errors`, keeping them as `DiagnosticBuilder`s and
preserving the emission guarantees.

This also requires fixing a bunch of incorrect lifetimes on
`DiagnosticBuilder` use points.
This commit is contained in:
Nicholas Nethercote 2024-02-02 13:04:07 +11:00
parent 3a02ebcac2
commit 5fd824de44
4 changed files with 66 additions and 59 deletions

View file

@ -130,7 +130,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
noun_old: &str,
old_opt_via: &str,
previous_end_span: Option<Span>,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
let mut err = struct_span_code_err!(
self.dcx(),
new_loan_span,
@ -162,7 +162,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
old_opt_via: &str,
previous_end_span: Option<Span>,
second_borrow_desc: &str,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
let mut err = struct_span_code_err!(
self.dcx(),
new_loan_span,
@ -194,7 +194,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
kind_old: &str,
msg_old: &str,
old_load_end_span: Option<Span>,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
let via = |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {msg})") };
let mut err = struct_span_code_err!(
self.dcx(),
@ -235,7 +235,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
span: Span,
borrow_span: Span,
desc: &str,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
struct_span_code_err!(
self.dcx(),
span,
@ -252,7 +252,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
span: Span,
desc: &str,
is_arg: bool,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
let msg = if is_arg { "to immutable argument" } else { "twice to immutable variable" };
struct_span_code_err!(self.dcx(), span, E0384, "cannot assign {} {}", msg, desc)
}
@ -265,7 +265,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
&self,
move_from_span: Span,
move_from_desc: &str,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
struct_span_code_err!(
self.dcx(),
move_from_span,
@ -283,7 +283,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
move_from_span: Span,
ty: Ty<'_>,
is_index: Option<bool>,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
let type_name = match (&ty.kind(), is_index) {
(&ty::Array(_, _), Some(true)) | (&ty::Array(_, _), None) => "array",
(&ty::Slice(_), _) => "slice",
@ -304,7 +304,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
&self,
move_from_span: Span,
container_ty: Ty<'_>,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
struct_span_code_err!(
self.dcx(),
move_from_span,

View file

@ -327,7 +327,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
&mut self,
mpi: MovePathIndex,
move_span: Span,
err: &mut DiagnosticBuilder<'_>,
err: &mut DiagnosticBuilder<'tcx>,
in_pattern: &mut bool,
move_spans: UseSpans<'_>,
) {
@ -486,7 +486,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
desired_action: InitializationRequiringAction,
span: Span,
use_spans: UseSpans<'tcx>,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
// We need all statements in the body where the binding was assigned to later find all
// the branching code paths where the binding *wasn't* assigned to.
let inits = &self.move_data.init_path_map[mpi];
@ -880,7 +880,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
location: Location,
(place, _span): (Place<'tcx>, Span),
borrow: &BorrowData<'tcx>,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
let borrow_spans = self.retrieve_borrow_spans(borrow);
let borrow_span = borrow_spans.args_or_use();
@ -930,7 +930,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
(place, span): (Place<'tcx>, Span),
gen_borrow_kind: BorrowKind,
issued_borrow: &BorrowData<'tcx>,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
let issued_spans = self.retrieve_borrow_spans(issued_borrow);
let issued_span = issued_spans.args_or_use();
@ -2129,7 +2129,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
drop_span: Span,
borrow_spans: UseSpans<'tcx>,
explanation: BorrowExplanation<'tcx>,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
debug!(
"report_local_value_does_not_live_long_enough(\
{:?}, {:?}, {:?}, {:?}, {:?}\
@ -2304,7 +2304,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
&mut self,
drop_span: Span,
borrow_span: Span,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
debug!(
"report_thread_local_value_does_not_live_long_enough(\
{:?}, {:?}\
@ -2329,7 +2329,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
borrow_spans: UseSpans<'tcx>,
proper_span: Span,
explanation: BorrowExplanation<'tcx>,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
explanation
{
@ -2496,7 +2496,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
return_span: Span,
category: ConstraintCategory<'tcx>,
opt_place_desc: Option<&String>,
) -> Option<DiagnosticBuilder<'cx>> {
) -> Option<DiagnosticBuilder<'tcx>> {
let return_kind = match category {
ConstraintCategory::Return(_) => "return",
ConstraintCategory::Yield => "yield",
@ -2591,7 +2591,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
constraint_span: Span,
captured_var: &str,
scope: &str,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
let tcx = self.infcx.tcx;
let args_span = use_span.args_or_use();
@ -2699,7 +2699,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
upvar_span: Span,
upvar_name: Symbol,
escape_span: Span,
) -> DiagnosticBuilder<'cx> {
) -> DiagnosticBuilder<'tcx> {
let tcx = self.infcx.tcx;
let escapes_from = tcx.def_descr(self.mir_def_id().to_def_id());

View file

@ -288,7 +288,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
&mut self,
place: Place<'tcx>,
span: Span,
) -> DiagnosticBuilder<'a> {
) -> DiagnosticBuilder<'tcx> {
let description = if place.projection.len() == 1 {
format!("static item {}", self.describe_any_place(place.as_ref()))
} else {
@ -310,7 +310,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
deref_target_place: Place<'tcx>,
span: Span,
use_spans: Option<UseSpans<'tcx>>,
) -> DiagnosticBuilder<'a> {
) -> DiagnosticBuilder<'tcx> {
// Inspect the type of the content behind the
// borrow to provide feedback about why this
// was a move rather than a copy.

View file

@ -19,7 +19,7 @@ extern crate tracing;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::graph::dominators::Dominators;
use rustc_errors::{Diagnostic, DiagnosticBuilder};
use rustc_errors::DiagnosticBuilder;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_index::bit_set::{BitSet, ChunkedBitSet};
@ -173,7 +173,7 @@ fn do_mir_borrowck<'tcx>(
}
}
let mut errors = error::BorrowckErrors::new(infcx.tcx);
let mut errors = error::BorrowckErrors::new();
// Gather the upvars of a closure, if any.
if let Some(e) = input_body.tainted_by_errors {
@ -2124,7 +2124,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
| WriteKind::MutableBorrow(BorrowKind::Fake),
) => {
if self.is_mutable(place.as_ref(), is_local_mutation_allowed).is_err()
&& !self.has_buffered_errors()
&& !self.has_buffered_diags()
{
// rust-lang/rust#46908: In pure NLL mode this code path should be
// unreachable, but we use `span_delayed_bug` because we can hit this when
@ -2387,12 +2387,25 @@ mod error {
use super::*;
enum BufferedDiag<'tcx> {
Error(DiagnosticBuilder<'tcx>),
NonError(DiagnosticBuilder<'tcx, ()>),
}
impl<'tcx> BufferedDiag<'tcx> {
fn sort_span(&self) -> Span {
match self {
BufferedDiag::Error(diag) => diag.sort_span,
BufferedDiag::NonError(diag) => diag.sort_span,
}
}
}
pub struct BorrowckErrors<'tcx> {
tcx: TyCtxt<'tcx>,
/// This field keeps track of move errors that are to be reported for given move indices.
///
/// There are situations where many errors can be reported for a single move out (see #53807)
/// and we want only the best of those errors.
/// There are situations where many errors can be reported for a single move out (see
/// #53807) and we want only the best of those errors.
///
/// The `report_use_of_moved_or_uninitialized` function checks this map and replaces the
/// diagnostic (if there is one) if the `Place` of the error being reported is a prefix of
@ -2405,46 +2418,37 @@ mod error {
/// same primary span come out in a consistent order.
buffered_move_errors:
BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, DiagnosticBuilder<'tcx>)>,
buffered_mut_errors: FxIndexMap<Span, (DiagnosticBuilder<'tcx>, usize)>,
/// Buffer of diagnostics to be reported. Uses `Diagnostic` rather than `DiagnosticBuilder`
/// because it has a mixture of error diagnostics and non-error diagnostics.
buffered: Vec<Diagnostic>,
/// Set to Some if we emit an error during borrowck
tainted_by_errors: Option<ErrorGuaranteed>,
/// Buffer of diagnostics to be reported. A mixture of error and non-error diagnostics.
buffered_diags: Vec<BufferedDiag<'tcx>>,
}
impl<'tcx> BorrowckErrors<'tcx> {
pub fn new(tcx: TyCtxt<'tcx>) -> Self {
pub fn new() -> Self {
BorrowckErrors {
tcx,
buffered_move_errors: BTreeMap::new(),
buffered_mut_errors: Default::default(),
buffered: Default::default(),
tainted_by_errors: None,
buffered_diags: Default::default(),
}
}
pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_>) {
if let None = self.tainted_by_errors {
self.tainted_by_errors = Some(self.tcx.dcx().span_delayed_bug(
t.span.clone_ignoring_labels(),
"diagnostic buffered but not emitted",
))
}
self.buffered.push(t.into_diagnostic());
pub fn buffer_error(&mut self, t: DiagnosticBuilder<'tcx>) {
self.buffered_diags.push(BufferedDiag::Error(t));
}
pub fn buffer_non_error(&mut self, t: DiagnosticBuilder<'_, ()>) {
self.buffered.push(t.into_diagnostic());
pub fn buffer_non_error(&mut self, t: DiagnosticBuilder<'tcx, ()>) {
self.buffered_diags.push(BufferedDiag::NonError(t));
}
}
impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_>) {
pub fn buffer_error(&mut self, t: DiagnosticBuilder<'tcx>) {
self.errors.buffer_error(t);
}
pub fn buffer_non_error(&mut self, t: DiagnosticBuilder<'_, ()>) {
pub fn buffer_non_error(&mut self, t: DiagnosticBuilder<'tcx, ()>) {
self.errors.buffer_non_error(t);
}
@ -2476,38 +2480,41 @@ mod error {
}
pub fn emit_errors(&mut self) -> Option<ErrorGuaranteed> {
let mut res = None;
// Buffer any move errors that we collected and de-duplicated.
for (_, (_, diag)) in std::mem::take(&mut self.errors.buffered_move_errors) {
// We have already set tainted for this error, so just buffer it.
self.errors.buffered.push(diag.into_diagnostic());
self.errors.buffered_diags.push(BufferedDiag::Error(diag));
}
for (_, (mut diag, count)) in std::mem::take(&mut self.errors.buffered_mut_errors) {
if count > 10 {
diag.note(format!("...and {} other attempted mutable borrows", count - 10));
}
self.errors.buffered.push(diag.into_diagnostic());
self.errors.buffered_diags.push(BufferedDiag::Error(diag));
}
if !self.errors.buffered.is_empty() {
self.errors.buffered.sort_by_key(|diag| diag.sort_span);
let dcx = self.dcx();
for diag in self.errors.buffered.drain(..) {
dcx.emit_diagnostic(diag);
if !self.errors.buffered_diags.is_empty() {
self.errors.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span());
for buffered_diag in self.errors.buffered_diags.drain(..) {
match buffered_diag {
BufferedDiag::Error(diag) => res = Some(diag.emit()),
BufferedDiag::NonError(diag) => diag.emit(),
}
}
}
self.errors.tainted_by_errors
res
}
pub fn has_buffered_errors(&self) -> bool {
self.errors.buffered.is_empty()
pub(crate) fn has_buffered_diags(&self) -> bool {
self.errors.buffered_diags.is_empty()
}
pub fn has_move_error(
&self,
move_out_indices: &[MoveOutIndex],
) -> Option<&(PlaceRef<'tcx>, DiagnosticBuilder<'cx>)> {
) -> Option<&(PlaceRef<'tcx>, DiagnosticBuilder<'tcx>)> {
self.errors.buffered_move_errors.get(move_out_indices)
}
}