1
Fork 0

Fix ErrorGuaranteed unsoundness with stash/steal.

When you stash an error, the error count is incremented. You can then
use the non-zero error count to get an `ErrorGuaranteed`. You can then
steal the error, which decrements the error count. You can then cancel
the error.

Example code:
```
fn unsound(dcx: &DiagCtxt) -> ErrorGuaranteed {
    let sp = rustc_span::DUMMY_SP;
    let k = rustc_errors::StashKey::Cycle;
    dcx.struct_err("bogus").stash(sp, k);           // increment error count on stash
    let guar = dcx.has_errors().unwrap();           // ErrorGuaranteed from error count > 0
    let err = dcx.steal_diagnostic(sp, k).unwrap(); // decrement error count on steal
    err.cancel();                                   // cancel error
    guar                                            // ErrorGuaranteed with no error emitted!
}
```

This commit fixes the problem in the simplest way: by not counting
stashed errors in `DiagCtxt::{err_count,has_errors}`.

However, just doing this without any other changes leads to over 40 ui
test failures. Mostly because of uninteresting extra errors (many saying
"type annotations needed" when type inference fails), and in a few
cases, due to delayed bugs causing ICEs when no normal errors are
printed.

To fix these, this commit adds `DiagCtxt::stashed_err_count`, and uses
it in three places alongside `DiagCtxt::{has_errors,err_count}`. It's
dodgy to rely on it, because unlike `DiagCtxt::err_count` it can go up
and down. But it's needed to preserve existing behaviour, and at least
the three places that need it are now obvious.
This commit is contained in:
Nicholas Nethercote 2024-02-09 06:42:50 +11:00
parent 6894f435d3
commit 7619792107
5 changed files with 65 additions and 42 deletions

View file

@ -306,6 +306,12 @@ pub struct InferCtxt<'tcx> {
// FIXME(matthewjasper) Merge into `tainted_by_errors`
err_count_on_creation: usize,
/// Track how many errors were stashed when this infcx is created.
/// Used for the same purpose as `err_count_on_creation`, even
/// though it's weaker because the count can go up and down.
// FIXME(matthewjasper) Merge into `tainted_by_errors`
stashed_err_count_on_creation: usize,
/// What is the innermost universe we have created? Starts out as
/// `UniverseIndex::root()` but grows from there as we enter
/// universal quantifiers.
@ -711,6 +717,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
reported_signature_mismatch: Default::default(),
tainted_by_errors: Cell::new(None),
err_count_on_creation: tcx.dcx().err_count(),
stashed_err_count_on_creation: tcx.dcx().stashed_err_count(),
universe: Cell::new(ty::UniverseIndex::ROOT),
intercrate,
next_trait_solver,
@ -1261,26 +1268,24 @@ impl<'tcx> InferCtxt<'tcx> {
/// inference variables, regionck errors).
#[must_use = "this method does not have any side effects"]
pub fn tainted_by_errors(&self) -> Option<ErrorGuaranteed> {
debug!(
"is_tainted_by_errors(err_count={}, err_count_on_creation={}, \
tainted_by_errors={})",
self.dcx().err_count(),
self.err_count_on_creation,
self.tainted_by_errors.get().is_some()
);
if let Some(e) = self.tainted_by_errors.get() {
return Some(e);
if let Some(guar) = self.tainted_by_errors.get() {
Some(guar)
} else if self.dcx().err_count() > self.err_count_on_creation {
// Errors reported since this infcx was made.
let guar = self.dcx().has_errors().unwrap();
self.set_tainted_by_errors(guar);
Some(guar)
} else if self.dcx().stashed_err_count() > self.stashed_err_count_on_creation {
// Errors stashed since this infcx was made. Not entirely reliable
// because the count of stashed errors can go down. But without
// this case we get a moderate number of uninteresting and
// extraneous "type annotations needed" errors.
let guar = self.dcx().delayed_bug("tainted_by_errors: stashed bug awaiting emission");
self.set_tainted_by_errors(guar);
Some(guar)
} else {
None
}
if self.dcx().err_count() > self.err_count_on_creation {
// errors reported since this infcx was made
let e = self.dcx().has_errors().unwrap();
self.set_tainted_by_errors(e);
return Some(e);
}
None
}
/// Set the "tainted by errors" flag to true. We call this when we