@@ -15,6 +15,7 @@ use rustc_hir::intravisit::{Visitor, walk_block, walk_expr};
1515use rustc_hir:: {
1616 CoroutineDesugaring , CoroutineKind , CoroutineSource , LangItem , PatField , find_attr,
1717} ;
18+ use rustc_index:: bit_set:: DenseBitSet ;
1819use rustc_middle:: bug;
1920use rustc_middle:: hir:: nested_filter:: OnlyBodies ;
2021use rustc_middle:: mir:: {
@@ -28,7 +29,7 @@ use rustc_middle::ty::{
2829 self , PredicateKind , Ty , TyCtxt , TypeSuperVisitable , TypeVisitor , Upcast ,
2930 suggest_constraining_type_params,
3031} ;
31- use rustc_mir_dataflow:: move_paths:: { InitKind , MoveOutIndex , MovePathIndex } ;
32+ use rustc_mir_dataflow:: move_paths:: { Init , InitKind , InitLocation , MoveOutIndex , MovePathIndex } ;
3233use rustc_span:: def_id:: { DefId , LocalDefId } ;
3334use rustc_span:: hygiene:: DesugaringKind ;
3435use rustc_span:: { BytePos , ExpnKind , Ident , MacroKind , Span , Symbol , kw, sym} ;
@@ -110,6 +111,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
110111 used_place,
111112 moved_place,
112113 desired_action,
114+ location,
113115 span,
114116 use_spans,
115117 ) ;
@@ -769,12 +771,61 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
769771 }
770772 }
771773
774+ /// Returns `true` if the given initialization can reach the error location.
775+ ///
776+ /// This is used to determine whether an initialization should be considered
777+ /// when reporting diagnostics at `err_location`.
778+ ///
779+ /// The check proceeds in two stages:
780+ ///
781+ /// 1. If the initialization originates from a function argument, it is
782+ /// considered reachable by definition.
783+ /// 2. If the initialization's basic block dominates the error block, then
784+ /// every path to the error must pass through the initialization, so it is
785+ /// reachable.
786+ /// 3. Otherwise, perform a graph traversal over the MIR control-flow graph to
787+ /// determine whether any path exists from the initialization block to the
788+ /// error block.
789+ ///
790+ /// The dominance check acts as a fast path for the common case, while the CFG
791+ /// traversal handles cases where the initialization does not dominate the
792+ /// error location but can still reach it through an alternate control-flow
793+ /// path.
794+ fn is_init_reachable ( & self , init : & Init , err_location : mir:: Location ) -> bool {
795+ let dominators = self . body . basic_blocks . dominators ( ) ;
796+ let init_block = match init. location {
797+ InitLocation :: Argument ( _) => return true ,
798+ InitLocation :: Statement ( location) => location. block ,
799+ } ;
800+ let err_block = err_location. block ;
801+ if dominators. dominates ( init_block, err_block) {
802+ return true ;
803+ }
804+ // If init_block doesn't dominate error_block, check if there is any valid path from the
805+ // initialization block to the error block in the Control Flow Graph.
806+ let mut visited = DenseBitSet :: new_empty ( self . body . basic_blocks . len ( ) ) ;
807+ let mut stack = vec ! [ init_block] ;
808+ while let Some ( block) = stack. pop ( ) {
809+ if block == err_block {
810+ return true ;
811+ }
812+ if visited. insert ( block) {
813+ let data = & self . body . basic_blocks [ block] ;
814+ for successor in data. terminator ( ) . successors ( ) {
815+ stack. push ( successor) ;
816+ }
817+ }
818+ }
819+ false
820+ }
821+
772822 fn report_use_of_uninitialized (
773823 & self ,
774824 mpi : MovePathIndex ,
775825 used_place : PlaceRef < ' tcx > ,
776826 moved_place : PlaceRef < ' tcx > ,
777827 desired_action : InitializationRequiringAction ,
828+ location : Location ,
778829 span : Span ,
779830 use_spans : UseSpans < ' tcx > ,
780831 ) -> Diag < ' infcx > {
@@ -783,15 +834,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
783834 let inits = & self . move_data . init_path_map [ mpi] ;
784835 let move_path = & self . move_data . move_paths [ mpi] ;
785836 let decl_span = self . body . local_decls [ move_path. place . local ] . source_info . span ;
786- let mut spans_set = FxIndexSet :: default ( ) ;
837+ let mut all_init_spans_set = FxIndexSet :: default ( ) ;
838+ let mut reachable_spans_set = FxIndexSet :: default ( ) ;
787839 for init_idx in inits {
788840 let init = & self . move_data . inits [ * init_idx] ;
789841 let span = init. span ( self . body ) ;
790842 if !span. is_dummy ( ) {
791- spans_set. insert ( span) ;
843+ all_init_spans_set. insert ( span) ;
844+ if self . is_init_reachable ( init, location) {
845+ reachable_spans_set. insert ( span) ;
846+ }
792847 }
793848 }
794- let spans: Vec < _ > = spans_set. into_iter ( ) . collect ( ) ;
849+ let all_init_spans: Vec < _ > = all_init_spans_set. into_iter ( ) . collect ( ) ;
850+ let reachable_spans: Vec < _ > = reachable_spans_set. into_iter ( ) . collect ( ) ;
795851
796852 let ( name, desc) = match self . describe_place_with_options (
797853 moved_place,
@@ -812,9 +868,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
812868 // for the branching codepaths that aren't covered, to point at them.
813869 let tcx = self . infcx . tcx ;
814870 let body = tcx. hir_body_owned_by ( self . mir_def_id ( ) ) ;
815- let mut visitor = ConditionVisitor { tcx, spans, name, errors : vec ! [ ] } ;
871+ let mut visitor =
872+ ConditionVisitor { tcx, spans : all_init_spans. clone ( ) , name, errors : vec ! [ ] } ;
816873 visitor. visit_body ( & body) ;
817- let spans = visitor. spans ;
818874
819875 let mut show_assign_sugg = false ;
820876 let isnt_initialized = if let InitializationRequiringAction :: PartialAssignment
@@ -824,7 +880,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
824880 // that are *partially* initialized by assigning to a field of an uninitialized
825881 // binding. We differentiate between them for more accurate wording here.
826882 "isn't fully initialized"
827- } else if !spans . iter ( ) . any ( |i| {
883+ } else if !reachable_spans . iter ( ) . any ( |i| {
828884 // We filter these to avoid misleading wording in cases like the following,
829885 // where `x` has an `init`, but it is in the same place we're looking at:
830886 // ```
@@ -840,7 +896,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
840896 . any ( |sp| span < sp && !sp. contains ( span) )
841897 } ) {
842898 show_assign_sugg = true ;
843- "isn't initialized"
899+ if all_init_spans. iter ( ) . any ( |init_span| !init_span. contains ( span) )
900+ && reachable_spans. is_empty ( )
901+ {
902+ "isn't initialized on any path leading to this point"
903+ } else {
904+ "isn't initialized"
905+ }
844906 } else {
845907 "is possibly-uninitialized"
846908 } ;
@@ -887,7 +949,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
887949 }
888950 }
889951 if !shown {
890- for sp in & spans {
952+ for sp in & reachable_spans {
891953 if * sp < span && !sp. overlaps ( span) {
892954 err. span_label ( * sp, "binding initialized here in some conditions" ) ;
893955 }
0 commit comments