summary refs log tree commit diff
path: root/compiler/rustc_interface/src
diff options
context:
space:
mode:
authorNicholas Nethercote <n.nethercote@gmail.com>2024-02-09 06:42:50 +1100
committerNicholas Nethercote <n.nethercote@gmail.com>2024-02-09 13:50:03 +1100
commit76197921079737fdd499e12016ac056674b5119f (patch)
tree046a9e7d9a8dbf43b456995e63aeb75196ee387a /compiler/rustc_interface/src
parent6894f435d35d3d540dcefbc51390158ca5954861 (diff)
downloadrust-76197921079737fdd499e12016ac056674b5119f.tar.gz
rust-76197921079737fdd499e12016ac056674b5119f.zip
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.
Diffstat (limited to 'compiler/rustc_interface/src')
-rw-r--r--compiler/rustc_interface/src/passes.rs4
1 files changed, 4 insertions, 0 deletions
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 97956404120..50b2bf7da15 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -778,6 +778,10 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
     // kindck is gone now). -nmatsakis
     if let Some(reported) = sess.dcx().has_errors() {
         return Err(reported);
+    } else if sess.dcx().stashed_err_count() > 0 {
+        // Without this case we sometimes get delayed bug ICEs and I don't
+        // understand why. -nnethercote
+        return Err(sess.dcx().delayed_bug("some stashed error is waiting for use"));
     }
 
     sess.time("misc_checking_3", || {