@@ -434,10 +434,6 @@ struct DiagCtxtInner {
434
434
/// The delayed bugs and their error guarantees.
435
435
delayed_bugs : Vec < ( DelayedDiagInner , ErrorGuaranteed ) > ,
436
436
437
- /// The number of stashed errors. Unlike the other counts, this can go up
438
- /// and down, so it doesn't guarantee anything.
439
- stashed_err_count : usize ,
440
-
441
437
/// The error count shown to the user at the end.
442
438
deduplicated_err_count : usize ,
443
439
/// The warning count shown to the user at the end.
@@ -475,7 +471,7 @@ struct DiagCtxtInner {
475
471
/// add more information). All stashed diagnostics must be emitted with
476
472
/// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped,
477
473
/// otherwise an assertion failure will occur.
478
- stashed_diagnostics : FxIndexMap < ( Span , StashKey ) , DiagInner > ,
474
+ stashed_diagnostics : FxIndexMap < ( Span , StashKey ) , ( DiagInner , Option < ErrorGuaranteed > ) > ,
479
475
480
476
future_breakage_diagnostics : Vec < DiagInner > ,
481
477
@@ -561,8 +557,14 @@ impl Drop for DiagCtxtInner {
561
557
fn drop ( & mut self ) {
562
558
// Any stashed diagnostics should have been handled by
563
559
// `emit_stashed_diagnostics` by now.
560
+ //
561
+ // Important: it is sound to produce an `ErrorGuaranteed` when stashing
562
+ // errors because they are guaranteed to have been emitted by here.
564
563
assert ! ( self . stashed_diagnostics. is_empty( ) ) ;
565
564
565
+ // Important: it is sound to produce an `ErrorGuaranteed` when emitting
566
+ // delayed bugs because they are guaranteed to be emitted here if
567
+ // necessary.
566
568
if self . err_guars . is_empty ( ) {
567
569
self . flush_delayed ( )
568
570
}
@@ -615,7 +617,6 @@ impl DiagCtxt {
615
617
err_guars : Vec :: new ( ) ,
616
618
lint_err_guars : Vec :: new ( ) ,
617
619
delayed_bugs : Vec :: new ( ) ,
618
- stashed_err_count : 0 ,
619
620
deduplicated_err_count : 0 ,
620
621
deduplicated_warn_count : 0 ,
621
622
emitter,
@@ -676,7 +677,6 @@ impl DiagCtxt {
676
677
err_guars,
677
678
lint_err_guars,
678
679
delayed_bugs,
679
- stashed_err_count,
680
680
deduplicated_err_count,
681
681
deduplicated_warn_count,
682
682
emitter : _,
@@ -699,7 +699,6 @@ impl DiagCtxt {
699
699
* err_guars = Default :: default ( ) ;
700
700
* lint_err_guars = Default :: default ( ) ;
701
701
* delayed_bugs = Default :: default ( ) ;
702
- * stashed_err_count = 0 ;
703
702
* deduplicated_err_count = 0 ;
704
703
* deduplicated_warn_count = 0 ;
705
704
* must_produce_diag = false ;
@@ -715,39 +714,111 @@ impl DiagCtxt {
715
714
* fulfilled_expectations = Default :: default ( ) ;
716
715
}
717
716
718
- /// Stash a given diagnostic with the given `Span` and [`StashKey`] as the key.
719
- /// Retrieve a stashed diagnostic with `steal_diagnostic`.
720
- pub fn stash_diagnostic ( & self , span : Span , key : StashKey , diag : DiagInner ) {
721
- let mut inner = self . inner . borrow_mut ( ) ;
722
-
723
- let key = ( span. with_parent ( None ) , key) ;
724
-
725
- if diag. is_error ( ) {
726
- if diag. is_lint . is_none ( ) {
727
- inner. stashed_err_count += 1 ;
728
- }
729
- }
717
+ /// Stashes a diagnostic for possible later improvement in a different,
718
+ /// later stage of the compiler. Possible actions depend on the diagnostic
719
+ /// level:
720
+ /// - Level::Error: immediately counted as an error that has occurred, because it
721
+ /// is guaranteed to be emitted eventually. Can be later accessed with the
722
+ /// provided `span` and `key` through
723
+ /// [`DiagCtxt::try_steal_modify_and_emit_err`] or
724
+ /// [`DiagCtxt::try_steal_replace_and_emit_err`]. These do not allow
725
+ /// cancellation or downgrading of the error. Returns
726
+ /// `Some(ErrorGuaranteed)`.
727
+ /// - Level::Warning and lower (i.e. !is_error()): can be accessed with the
728
+ /// provided `span` and `key` through [`DiagCtxt::steal_non_err()`]. This
729
+ /// allows cancelling and downgrading of the diagnostic. Returns `None`.
730
+ /// - Others: not allowed, will trigger a panic.
731
+ pub fn stash_diagnostic (
732
+ & self ,
733
+ span : Span ,
734
+ key : StashKey ,
735
+ diag : DiagInner ,
736
+ ) -> Option < ErrorGuaranteed > {
737
+ let guar = if diag. level ( ) == Level :: Error {
738
+ // This `unchecked_error_guaranteed` is valid. It is where the
739
+ // `ErrorGuaranteed` for stashed errors originates. See
740
+ // `DiagCtxtInner::drop`.
741
+ #[ allow( deprecated) ]
742
+ Some ( ErrorGuaranteed :: unchecked_error_guaranteed ( ) )
743
+ } else if !diag. is_error ( ) {
744
+ None
745
+ } else {
746
+ self . span_bug ( span, format ! ( "invalid level in `stash_diagnostic`: {}" , diag. level) ) ;
747
+ } ;
730
748
731
749
// FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
732
750
// if/when we have a more robust macro-friendly replacement for `(span, key)` as a key.
733
751
// See the PR for a discussion.
734
- inner. stashed_diagnostics . insert ( key, diag) ;
752
+ let key = ( span. with_parent ( None ) , key) ;
753
+ self . inner . borrow_mut ( ) . stashed_diagnostics . insert ( key, ( diag, guar) ) ;
754
+
755
+ guar
735
756
}
736
757
737
- /// Steal a previously stashed diagnostic with the given `Span` and [`StashKey`] as the key.
738
- pub fn steal_diagnostic ( & self , span : Span , key : StashKey ) -> Option < Diag < ' _ , ( ) > > {
739
- let mut inner = self . inner . borrow_mut ( ) ;
758
+ /// Steal a previously stashed non-error diagnostic with the given `Span`
759
+ /// and [`StashKey`] as the key. Panics if the found diagnostic is an
760
+ /// error.
761
+ pub fn steal_non_err ( & self , span : Span , key : StashKey ) -> Option < Diag < ' _ , ( ) > > {
740
762
let key = ( span. with_parent ( None ) , key) ;
741
763
// FIXME(#120456) - is `swap_remove` correct?
742
- let diag = inner. stashed_diagnostics . swap_remove ( & key) ?;
743
- if diag. is_error ( ) {
744
- if diag. is_lint . is_none ( ) {
745
- inner. stashed_err_count -= 1 ;
746
- }
747
- }
764
+ let ( diag, guar) = self . inner . borrow_mut ( ) . stashed_diagnostics . swap_remove ( & key) ?;
765
+ assert ! ( !diag. is_error( ) ) ;
766
+ assert ! ( guar. is_none( ) ) ;
748
767
Some ( Diag :: new_diagnostic ( self , diag) )
749
768
}
750
769
770
+ /// Steals a previously stashed error with the given `Span` and
771
+ /// [`StashKey`] as the key, modifies it, and emits it. Returns `None` if
772
+ /// no matching diagnostic is found. Panics if the found diagnostic's level
773
+ /// isn't `Level::Error`.
774
+ pub fn try_steal_modify_and_emit_err < F > (
775
+ & self ,
776
+ span : Span ,
777
+ key : StashKey ,
778
+ mut modify_err : F ,
779
+ ) -> Option < ErrorGuaranteed >
780
+ where
781
+ F : FnMut ( & mut Diag < ' _ > ) ,
782
+ {
783
+ let key = ( span. with_parent ( None ) , key) ;
784
+ // FIXME(#120456) - is `swap_remove` correct?
785
+ let err = self . inner . borrow_mut ( ) . stashed_diagnostics . swap_remove ( & key) ;
786
+ err. map ( |( err, guar) | {
787
+ // The use of `::<ErrorGuaranteed>` is safe because level is `Level::Error`.
788
+ assert_eq ! ( err. level, Level :: Error ) ;
789
+ assert ! ( guar. is_some( ) ) ;
790
+ let mut err = Diag :: < ErrorGuaranteed > :: new_diagnostic ( self , err) ;
791
+ modify_err ( & mut err) ;
792
+ assert_eq ! ( err. level, Level :: Error ) ;
793
+ err. emit ( )
794
+ } )
795
+ }
796
+
797
+ /// Steals a previously stashed error with the given `Span` and
798
+ /// [`StashKey`] as the key, cancels it if found, and emits `new_err`.
799
+ /// Panics if the found diagnostic's level isn't `Level::Error`.
800
+ pub fn try_steal_replace_and_emit_err (
801
+ & self ,
802
+ span : Span ,
803
+ key : StashKey ,
804
+ new_err : Diag < ' _ > ,
805
+ ) -> ErrorGuaranteed {
806
+ let key = ( span. with_parent ( None ) , key) ;
807
+ // FIXME(#120456) - is `swap_remove` correct?
808
+ let old_err = self . inner . borrow_mut ( ) . stashed_diagnostics . swap_remove ( & key) ;
809
+ match old_err {
810
+ Some ( ( old_err, guar) ) => {
811
+ assert_eq ! ( old_err. level, Level :: Error ) ;
812
+ assert ! ( guar. is_some( ) ) ;
813
+ // Because `old_err` has already been counted, it can only be
814
+ // safely cancelled because the `new_err` supplants it.
815
+ Diag :: < ErrorGuaranteed > :: new_diagnostic ( self , old_err) . cancel ( ) ;
816
+ }
817
+ None => { }
818
+ } ;
819
+ new_err. emit ( )
820
+ }
821
+
751
822
pub fn has_stashed_diagnostic ( & self , span : Span , key : StashKey ) -> bool {
752
823
self . inner . borrow ( ) . stashed_diagnostics . get ( & ( span. with_parent ( None ) , key) ) . is_some ( )
753
824
}
@@ -757,41 +828,40 @@ impl DiagCtxt {
757
828
self . inner . borrow_mut ( ) . emit_stashed_diagnostics ( )
758
829
}
759
830
760
- /// This excludes lint errors, delayed bugs and stashed errors .
831
+ /// This excludes lint errors, and delayed bugs .
761
832
#[ inline]
762
833
pub fn err_count_excluding_lint_errs ( & self ) -> usize {
763
- self . inner . borrow ( ) . err_guars . len ( )
834
+ let inner = self . inner . borrow ( ) ;
835
+ inner. err_guars . len ( )
836
+ + inner
837
+ . stashed_diagnostics
838
+ . values ( )
839
+ . filter ( |( diag, guar) | guar. is_some ( ) && diag. is_lint . is_none ( ) )
840
+ . count ( )
764
841
}
765
842
766
- /// This excludes delayed bugs and stashed errors .
843
+ /// This excludes delayed bugs.
767
844
#[ inline]
768
845
pub fn err_count ( & self ) -> usize {
769
846
let inner = self . inner . borrow ( ) ;
770
- inner. err_guars . len ( ) + inner. lint_err_guars . len ( )
847
+ inner. err_guars . len ( )
848
+ + inner. lint_err_guars . len ( )
849
+ + inner. stashed_diagnostics . values ( ) . filter ( |( _diag, guar) | guar. is_some ( ) ) . count ( )
771
850
}
772
851
773
- /// This excludes normal errors, lint errors, and delayed bugs. Unless
774
- /// absolutely necessary, avoid using this. It's dubious because stashed
775
- /// errors can later be cancelled, so the presence of a stashed error at
776
- /// some point of time doesn't guarantee anything -- there are no
777
- /// `ErrorGuaranteed`s here.
778
- pub fn stashed_err_count ( & self ) -> usize {
779
- self . inner . borrow ( ) . stashed_err_count
780
- }
781
-
782
- /// This excludes lint errors, delayed bugs, and stashed errors. Unless
783
- /// absolutely necessary, prefer `has_errors` to this method.
852
+ /// This excludes lint errors and delayed bugs. Unless absolutely
853
+ /// necessary, prefer `has_errors` to this method.
784
854
pub fn has_errors_excluding_lint_errors ( & self ) -> Option < ErrorGuaranteed > {
785
855
self . inner . borrow ( ) . has_errors_excluding_lint_errors ( )
786
856
}
787
857
788
- /// This excludes delayed bugs and stashed errors .
858
+ /// This excludes delayed bugs.
789
859
pub fn has_errors ( & self ) -> Option < ErrorGuaranteed > {
790
860
self . inner . borrow ( ) . has_errors ( )
791
861
}
792
862
793
- /// This excludes stashed errors . Unless absolutely necessary, prefer
794
- /// `has_errors` to this method.
863
+ /// This excludes nothing . Unless absolutely necessary, prefer `has_errors`
864
+ /// to this method.
795
865
pub fn has_errors_or_delayed_bugs ( & self ) -> Option < ErrorGuaranteed > {
796
866
self . inner . borrow ( ) . has_errors_or_delayed_bugs ( )
797
867
}
@@ -876,10 +946,10 @@ impl DiagCtxt {
876
946
}
877
947
}
878
948
879
- /// This excludes delayed bugs and stashed errors . Used for early aborts
880
- /// after errors occurred -- e.g. because continuing in the face of errors is
881
- /// likely to lead to bad results, such as spurious/uninteresting
882
- /// additional errors -- when returning an error `Result` is difficult.
949
+ /// This excludes delayed bugs. Used for early aborts after errors occurred
950
+ /// -- e.g. because continuing in the face of errors is likely to lead to
951
+ /// bad results, such as spurious/uninteresting additional errors -- when
952
+ /// returning an error `Result` is difficult.
883
953
pub fn abort_if_errors ( & self ) {
884
954
if self . has_errors ( ) . is_some ( ) {
885
955
FatalError . raise ( ) ;
@@ -963,7 +1033,7 @@ impl DiagCtxt {
963
1033
inner
964
1034
. stashed_diagnostics
965
1035
. values_mut ( )
966
- . for_each ( |diag| diag. update_unstable_expectation_id ( unstable_to_stable) ) ;
1036
+ . for_each ( |( diag, _guar ) | diag. update_unstable_expectation_id ( unstable_to_stable) ) ;
967
1037
inner
968
1038
. future_breakage_diagnostics
969
1039
. iter_mut ( )
@@ -1270,12 +1340,8 @@ impl DiagCtxtInner {
1270
1340
fn emit_stashed_diagnostics ( & mut self ) -> Option < ErrorGuaranteed > {
1271
1341
let mut guar = None ;
1272
1342
let has_errors = !self . err_guars . is_empty ( ) ;
1273
- for ( _, diag) in std:: mem:: take ( & mut self . stashed_diagnostics ) . into_iter ( ) {
1274
- if diag. is_error ( ) {
1275
- if diag. is_lint . is_none ( ) {
1276
- self . stashed_err_count -= 1 ;
1277
- }
1278
- } else {
1343
+ for ( _, ( diag, _guar) ) in std:: mem:: take ( & mut self . stashed_diagnostics ) . into_iter ( ) {
1344
+ if !diag. is_error ( ) {
1279
1345
// Unless they're forced, don't flush stashed warnings when
1280
1346
// there are errors, to avoid causing warning overload. The
1281
1347
// stash would've been stolen already if it were important.
@@ -1334,7 +1400,8 @@ impl DiagCtxtInner {
1334
1400
} else {
1335
1401
let backtrace = std:: backtrace:: Backtrace :: capture ( ) ;
1336
1402
// This `unchecked_error_guaranteed` is valid. It is where the
1337
- // `ErrorGuaranteed` for delayed bugs originates.
1403
+ // `ErrorGuaranteed` for delayed bugs originates. See
1404
+ // `DiagCtxtInner::drop`.
1338
1405
#[ allow( deprecated) ]
1339
1406
let guar = ErrorGuaranteed :: unchecked_error_guaranteed ( ) ;
1340
1407
self . delayed_bugs
@@ -1446,11 +1513,31 @@ impl DiagCtxtInner {
1446
1513
}
1447
1514
1448
1515
fn has_errors_excluding_lint_errors ( & self ) -> Option < ErrorGuaranteed > {
1449
- self . err_guars . get ( 0 ) . copied ( )
1516
+ self . err_guars . get ( 0 ) . copied ( ) . or_else ( || {
1517
+ if let Some ( ( _diag, guar) ) = self
1518
+ . stashed_diagnostics
1519
+ . values ( )
1520
+ . find ( |( diag, guar) | guar. is_some ( ) && diag. is_lint . is_none ( ) )
1521
+ {
1522
+ * guar
1523
+ } else {
1524
+ None
1525
+ }
1526
+ } )
1450
1527
}
1451
1528
1452
1529
fn has_errors ( & self ) -> Option < ErrorGuaranteed > {
1453
- self . has_errors_excluding_lint_errors ( ) . or_else ( || self . lint_err_guars . get ( 0 ) . copied ( ) )
1530
+ self . err_guars . get ( 0 ) . copied ( ) . or_else ( || self . lint_err_guars . get ( 0 ) . copied ( ) ) . or_else (
1531
+ || {
1532
+ if let Some ( ( _diag, guar) ) =
1533
+ self . stashed_diagnostics . values ( ) . find ( |( _diag, guar) | guar. is_some ( ) )
1534
+ {
1535
+ * guar
1536
+ } else {
1537
+ None
1538
+ }
1539
+ } ,
1540
+ )
1454
1541
}
1455
1542
1456
1543
fn has_errors_or_delayed_bugs ( & self ) -> Option < ErrorGuaranteed > {
0 commit comments