@@ -435,10 +435,6 @@ struct DiagCtxtInner {
435
435
/// The delayed bugs and their error guarantees.
436
436
delayed_bugs : Vec < ( DelayedDiagnostic , ErrorGuaranteed ) > ,
437
437
438
- /// The number of stashed errors. Unlike the other counts, this can go up
439
- /// and down, so it doesn't guarantee anything.
440
- stashed_err_count : usize ,
441
-
442
438
/// The error count shown to the user at the end.
443
439
deduplicated_err_count : usize ,
444
440
/// The warning count shown to the user at the end.
@@ -476,7 +472,7 @@ struct DiagCtxtInner {
476
472
/// add more information). All stashed diagnostics must be emitted with
477
473
/// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped,
478
474
/// otherwise an assertion failure will occur.
479
- stashed_diagnostics : FxIndexMap < ( Span , StashKey ) , Diagnostic > ,
475
+ stashed_diagnostics : FxIndexMap < ( Span , StashKey ) , ( Diagnostic , Option < ErrorGuaranteed > ) > ,
480
476
481
477
future_breakage_diagnostics : Vec < Diagnostic > ,
482
478
@@ -562,8 +558,14 @@ impl Drop for DiagCtxtInner {
562
558
fn drop ( & mut self ) {
563
559
// Any stashed diagnostics should have been handled by
564
560
// `emit_stashed_diagnostics` by now.
561
+ //
562
+ // Important: it is sound to produce an `ErrorGuaranteed` when stashing
563
+ // errors because they are guaranteed to have been emitted by here.
565
564
assert ! ( self . stashed_diagnostics. is_empty( ) ) ;
566
565
566
+ // Important: it is sound to produce an `ErrorGuaranteed` when emitting
567
+ // delayed bugs because they are guaranteed to be emitted here if
568
+ // necessary.
567
569
if self . err_guars . is_empty ( ) {
568
570
self . flush_delayed ( )
569
571
}
@@ -616,7 +618,6 @@ impl DiagCtxt {
616
618
err_guars : Vec :: new ( ) ,
617
619
lint_err_guars : Vec :: new ( ) ,
618
620
delayed_bugs : Vec :: new ( ) ,
619
- stashed_err_count : 0 ,
620
621
deduplicated_err_count : 0 ,
621
622
deduplicated_warn_count : 0 ,
622
623
emitter,
@@ -677,7 +678,6 @@ impl DiagCtxt {
677
678
err_guars,
678
679
lint_err_guars,
679
680
delayed_bugs,
680
- stashed_err_count,
681
681
deduplicated_err_count,
682
682
deduplicated_warn_count,
683
683
emitter : _,
@@ -700,7 +700,6 @@ impl DiagCtxt {
700
700
* err_guars = Default :: default ( ) ;
701
701
* lint_err_guars = Default :: default ( ) ;
702
702
* delayed_bugs = Default :: default ( ) ;
703
- * stashed_err_count = 0 ;
704
703
* deduplicated_err_count = 0 ;
705
704
* deduplicated_warn_count = 0 ;
706
705
* must_produce_diag = false ;
@@ -716,39 +715,111 @@ impl DiagCtxt {
716
715
* fulfilled_expectations = Default :: default ( ) ;
717
716
}
718
717
719
- /// Stash a given diagnostic with the given `Span` and [`StashKey`] as the key.
720
- /// Retrieve a stashed diagnostic with `steal_diagnostic`.
721
- pub fn stash_diagnostic ( & self , span : Span , key : StashKey , diag : Diagnostic ) {
722
- let mut inner = self . inner . borrow_mut ( ) ;
723
-
724
- let key = ( span. with_parent ( None ) , key) ;
725
-
726
- if diag. is_error ( ) {
727
- if diag. is_lint . is_none ( ) {
728
- inner. stashed_err_count += 1 ;
729
- }
730
- }
718
+ /// Stashes a diagnostic for possible later improvement in a different,
719
+ /// later stage of the compiler. Possible actions depend on the diagnostic
720
+ /// level:
721
+ /// - Level::Error: immediately counted as an error that has occurred, because it
722
+ /// is guaranteed to be emitted eventually. Can be later accessed with the
723
+ /// provided `span` and `key` through
724
+ /// [`DiagCtxt::try_steal_modify_and_emit_err`] or
725
+ /// [`DiagCtxt::try_steal_replace_and_emit_err`]. These do not allow
726
+ /// cancellation or downgrading of the error. Returns
727
+ /// `Some(ErrorGuaranteed)`.
728
+ /// - Level::Warning and lower (i.e. !is_error()): can be accessed with the
729
+ /// provided `span` and `key` through [`DiagCtxt::steal_non_err()`]. This
730
+ /// allows cancelling and downgrading of the diagnostic. Returns `None`.
731
+ /// - Others: not allowed, will trigger a panic.
732
+ pub fn stash_diagnostic (
733
+ & self ,
734
+ span : Span ,
735
+ key : StashKey ,
736
+ diag : Diagnostic ,
737
+ ) -> Option < ErrorGuaranteed > {
738
+ let guar = if diag. level ( ) == Level :: Error {
739
+ // This `unchecked_error_guaranteed` is valid. It is where the
740
+ // `ErrorGuaranteed` for stashed errors originates. See
741
+ // `DiagCtxtInner::drop`.
742
+ #[ allow( deprecated) ]
743
+ Some ( ErrorGuaranteed :: unchecked_error_guaranteed ( ) )
744
+ } else if !diag. is_error ( ) {
745
+ None
746
+ } else {
747
+ self . span_bug ( span, format ! ( "invalid level in `stash_diagnostic`: {}" , diag. level) ) ;
748
+ } ;
731
749
732
750
// FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
733
751
// if/when we have a more robust macro-friendly replacement for `(span, key)` as a key.
734
752
// See the PR for a discussion.
735
- inner. stashed_diagnostics . insert ( key, diag) ;
753
+ let key = ( span. with_parent ( None ) , key) ;
754
+ self . inner . borrow_mut ( ) . stashed_diagnostics . insert ( key, ( diag, guar) ) ;
755
+
756
+ guar
736
757
}
737
758
738
- /// Steal a previously stashed diagnostic with the given `Span` and [`StashKey`] as the key.
739
- pub fn steal_diagnostic ( & self , span : Span , key : StashKey ) -> Option < DiagnosticBuilder < ' _ , ( ) > > {
740
- let mut inner = self . inner . borrow_mut ( ) ;
759
+ /// Steal a previously stashed non-error diagnostic with the given `Span`
760
+ /// and [`StashKey`] as the key. Panics if the found diagnostic is an
761
+ /// error.
762
+ pub fn steal_non_err ( & self , span : Span , key : StashKey ) -> Option < DiagnosticBuilder < ' _ , ( ) > > {
741
763
let key = ( span. with_parent ( None ) , key) ;
742
764
// FIXME(#120456) - is `swap_remove` correct?
743
- let diag = inner. stashed_diagnostics . swap_remove ( & key) ?;
744
- if diag. is_error ( ) {
745
- if diag. is_lint . is_none ( ) {
746
- inner. stashed_err_count -= 1 ;
747
- }
748
- }
765
+ let ( diag, guar) = self . inner . borrow_mut ( ) . stashed_diagnostics . swap_remove ( & key) ?;
766
+ assert ! ( !diag. is_error( ) ) ;
767
+ assert ! ( guar. is_none( ) ) ;
749
768
Some ( DiagnosticBuilder :: new_diagnostic ( self , diag) )
750
769
}
751
770
771
+ /// Steals a previously stashed error with the given `Span` and
772
+ /// [`StashKey`] as the key, modifies it, and emits it. Returns `None` if
773
+ /// no matching diagnostic is found. Panics if the found diagnostic's level
774
+ /// isn't `Level::Error`.
775
+ pub fn try_steal_modify_and_emit_err < F > (
776
+ & self ,
777
+ span : Span ,
778
+ key : StashKey ,
779
+ mut modify_err : F ,
780
+ ) -> Option < ErrorGuaranteed >
781
+ where
782
+ F : FnMut ( & mut DiagnosticBuilder < ' _ > ) ,
783
+ {
784
+ let key = ( span. with_parent ( None ) , key) ;
785
+ // FIXME(#120456) - is `swap_remove` correct?
786
+ let err = self . inner . borrow_mut ( ) . stashed_diagnostics . swap_remove ( & key) ;
787
+ err. map ( |( err, guar) | {
788
+ // The use of `::<ErrorGuaranteed>` is safe because level is `Level::Error`.
789
+ assert_eq ! ( err. level, Level :: Error ) ;
790
+ assert ! ( guar. is_some( ) ) ;
791
+ let mut err = DiagnosticBuilder :: < ErrorGuaranteed > :: new_diagnostic ( self , err) ;
792
+ modify_err ( & mut err) ;
793
+ assert_eq ! ( err. level, Level :: Error ) ;
794
+ err. emit ( )
795
+ } )
796
+ }
797
+
798
+ /// Steals a previously stashed error with the given `Span` and
799
+ /// [`StashKey`] as the key, cancels it if found, and emits `new_err`.
800
+ /// Panics if the found diagnostic's level isn't `Level::Error`.
801
+ pub fn try_steal_replace_and_emit_err (
802
+ & self ,
803
+ span : Span ,
804
+ key : StashKey ,
805
+ new_err : DiagnosticBuilder < ' _ > ,
806
+ ) -> ErrorGuaranteed {
807
+ let key = ( span. with_parent ( None ) , key) ;
808
+ // FIXME(#120456) - is `swap_remove` correct?
809
+ let old_err = self . inner . borrow_mut ( ) . stashed_diagnostics . swap_remove ( & key) ;
810
+ match old_err {
811
+ Some ( ( old_err, guar) ) => {
812
+ assert_eq ! ( old_err. level, Level :: Error ) ;
813
+ assert ! ( guar. is_some( ) ) ;
814
+ // Because `old_err` has already been counted, it can only be
815
+ // safely cancelled because the `new_err` supplants it.
816
+ DiagnosticBuilder :: < ErrorGuaranteed > :: new_diagnostic ( self , old_err) . cancel ( ) ;
817
+ }
818
+ None => { }
819
+ } ;
820
+ new_err. emit ( )
821
+ }
822
+
752
823
pub fn has_stashed_diagnostic ( & self , span : Span , key : StashKey ) -> bool {
753
824
self . inner . borrow ( ) . stashed_diagnostics . get ( & ( span. with_parent ( None ) , key) ) . is_some ( )
754
825
}
@@ -758,41 +829,40 @@ impl DiagCtxt {
758
829
self . inner . borrow_mut ( ) . emit_stashed_diagnostics ( )
759
830
}
760
831
761
- /// This excludes lint errors, delayed bugs and stashed errors .
832
+ /// This excludes lint errors, and delayed bugs .
762
833
#[ inline]
763
834
pub fn err_count_excluding_lint_errs ( & self ) -> usize {
764
- self . inner . borrow ( ) . err_guars . len ( )
835
+ let inner = self . inner . borrow ( ) ;
836
+ inner. err_guars . len ( )
837
+ + inner
838
+ . stashed_diagnostics
839
+ . values ( )
840
+ . filter ( |( diag, guar) | guar. is_some ( ) && diag. is_lint . is_none ( ) )
841
+ . count ( )
765
842
}
766
843
767
- /// This excludes delayed bugs and stashed errors .
844
+ /// This excludes delayed bugs.
768
845
#[ inline]
769
846
pub fn err_count ( & self ) -> usize {
770
847
let inner = self . inner . borrow ( ) ;
771
- inner. err_guars . len ( ) + inner. lint_err_guars . len ( )
848
+ inner. err_guars . len ( )
849
+ + inner. lint_err_guars . len ( )
850
+ + inner. stashed_diagnostics . values ( ) . filter ( |( _diag, guar) | guar. is_some ( ) ) . count ( )
772
851
}
773
852
774
- /// This excludes normal errors, lint errors, and delayed bugs. Unless
775
- /// absolutely necessary, avoid using this. It's dubious because stashed
776
- /// errors can later be cancelled, so the presence of a stashed error at
777
- /// some point of time doesn't guarantee anything -- there are no
778
- /// `ErrorGuaranteed`s here.
779
- pub fn stashed_err_count ( & self ) -> usize {
780
- self . inner . borrow ( ) . stashed_err_count
781
- }
782
-
783
- /// This excludes lint errors, delayed bugs, and stashed errors. Unless
784
- /// absolutely necessary, prefer `has_errors` to this method.
853
+ /// This excludes lint errors and delayed bugs. Unless absolutely
854
+ /// necessary, prefer `has_errors` to this method.
785
855
pub fn has_errors_excluding_lint_errors ( & self ) -> Option < ErrorGuaranteed > {
786
856
self . inner . borrow ( ) . has_errors_excluding_lint_errors ( )
787
857
}
788
858
789
- /// This excludes delayed bugs and stashed errors .
859
+ /// This excludes delayed bugs.
790
860
pub fn has_errors ( & self ) -> Option < ErrorGuaranteed > {
791
861
self . inner . borrow ( ) . has_errors ( )
792
862
}
793
863
794
- /// This excludes stashed errors . Unless absolutely necessary, prefer
795
- /// `has_errors` to this method.
864
+ /// This excludes nothing . Unless absolutely necessary, prefer `has_errors`
865
+ /// to this method.
796
866
pub fn has_errors_or_delayed_bugs ( & self ) -> Option < ErrorGuaranteed > {
797
867
self . inner . borrow ( ) . has_errors_or_delayed_bugs ( )
798
868
}
@@ -877,10 +947,10 @@ impl DiagCtxt {
877
947
}
878
948
}
879
949
880
- /// This excludes delayed bugs and stashed errors . Used for early aborts
881
- /// after errors occurred -- e.g. because continuing in the face of errors is
882
- /// likely to lead to bad results, such as spurious/uninteresting
883
- /// additional errors -- when returning an error `Result` is difficult.
950
+ /// This excludes delayed bugs. Used for early aborts after errors occurred
951
+ /// -- e.g. because continuing in the face of errors is likely to lead to
952
+ /// bad results, such as spurious/uninteresting additional errors -- when
953
+ /// returning an error `Result` is difficult.
884
954
pub fn abort_if_errors ( & self ) {
885
955
if self . has_errors ( ) . is_some ( ) {
886
956
FatalError . raise ( ) ;
@@ -964,7 +1034,7 @@ impl DiagCtxt {
964
1034
inner
965
1035
. stashed_diagnostics
966
1036
. values_mut ( )
967
- . for_each ( |diag| diag. update_unstable_expectation_id ( unstable_to_stable) ) ;
1037
+ . for_each ( |( diag, _guar ) | diag. update_unstable_expectation_id ( unstable_to_stable) ) ;
968
1038
inner
969
1039
. future_breakage_diagnostics
970
1040
. iter_mut ( )
@@ -1286,12 +1356,8 @@ impl DiagCtxtInner {
1286
1356
fn emit_stashed_diagnostics ( & mut self ) -> Option < ErrorGuaranteed > {
1287
1357
let mut guar = None ;
1288
1358
let has_errors = !self . err_guars . is_empty ( ) ;
1289
- for ( _, diag) in std:: mem:: take ( & mut self . stashed_diagnostics ) . into_iter ( ) {
1290
- if diag. is_error ( ) {
1291
- if diag. is_lint . is_none ( ) {
1292
- self . stashed_err_count -= 1 ;
1293
- }
1294
- } else {
1359
+ for ( _, ( diag, _guar) ) in std:: mem:: take ( & mut self . stashed_diagnostics ) . into_iter ( ) {
1360
+ if !diag. is_error ( ) {
1295
1361
// Unless they're forced, don't flush stashed warnings when
1296
1362
// there are errors, to avoid causing warning overload. The
1297
1363
// stash would've been stolen already if it were important.
@@ -1350,7 +1416,8 @@ impl DiagCtxtInner {
1350
1416
} else {
1351
1417
let backtrace = std:: backtrace:: Backtrace :: capture ( ) ;
1352
1418
// This `unchecked_error_guaranteed` is valid. It is where the
1353
- // `ErrorGuaranteed` for delayed bugs originates.
1419
+ // `ErrorGuaranteed` for delayed bugs originates. See
1420
+ // `DiagCtxtInner::drop`.
1354
1421
#[ allow( deprecated) ]
1355
1422
let guar = ErrorGuaranteed :: unchecked_error_guaranteed ( ) ;
1356
1423
self . delayed_bugs
@@ -1462,11 +1529,31 @@ impl DiagCtxtInner {
1462
1529
}
1463
1530
1464
1531
fn has_errors_excluding_lint_errors ( & self ) -> Option < ErrorGuaranteed > {
1465
- self . err_guars . get ( 0 ) . copied ( )
1532
+ self . err_guars . get ( 0 ) . copied ( ) . or_else ( || {
1533
+ if let Some ( ( _diag, guar) ) = self
1534
+ . stashed_diagnostics
1535
+ . values ( )
1536
+ . find ( |( diag, guar) | guar. is_some ( ) && diag. is_lint . is_none ( ) )
1537
+ {
1538
+ * guar
1539
+ } else {
1540
+ None
1541
+ }
1542
+ } )
1466
1543
}
1467
1544
1468
1545
fn has_errors ( & self ) -> Option < ErrorGuaranteed > {
1469
- self . has_errors_excluding_lint_errors ( ) . or_else ( || self . lint_err_guars . get ( 0 ) . copied ( ) )
1546
+ self . err_guars . get ( 0 ) . copied ( ) . or_else ( || self . lint_err_guars . get ( 0 ) . copied ( ) ) . or_else (
1547
+ || {
1548
+ if let Some ( ( _diag, guar) ) =
1549
+ self . stashed_diagnostics . values ( ) . find ( |( _diag, guar) | guar. is_some ( ) )
1550
+ {
1551
+ * guar
1552
+ } else {
1553
+ None
1554
+ }
1555
+ } ,
1556
+ )
1470
1557
}
1471
1558
1472
1559
fn has_errors_or_delayed_bugs ( & self ) -> Option < ErrorGuaranteed > {
0 commit comments