@@ -24,10 +24,12 @@ use rustc_middle::mir::*;
2424use rustc_middle:: thir:: { self , ExprId , LintLevel , LocalVarId , Param , ParamId , PatKind , Thir } ;
2525use rustc_middle:: ty:: { self , ScalarInt , Ty , TyCtxt , TypeVisitableExt , TypingMode } ;
2626use rustc_middle:: { bug, span_bug} ;
27+ use rustc_session:: lint;
2728use rustc_span:: { Span , Symbol , sym} ;
2829
2930use crate :: builder:: expr:: as_place:: PlaceBuilder ;
3031use crate :: builder:: scope:: DropKind ;
32+ use crate :: errors;
3133
3234pub ( crate ) fn closure_saved_names_of_captured_variables < ' tcx > (
3335 tcx : TyCtxt < ' tcx > ,
@@ -531,6 +533,7 @@ fn construct_fn<'tcx>(
531533 return_block. unit ( )
532534 } ) ;
533535
536+ builder. lint_and_remove_uninhabited ( ) ;
534537 let mut body = builder. finish ( ) ;
535538
536539 body. spread_arg = if abi == ExternAbi :: RustCall {
@@ -588,6 +591,7 @@ fn construct_const<'a, 'tcx>(
588591
589592 builder. build_drop_trees ( ) ;
590593
594+ builder. lint_and_remove_uninhabited ( ) ;
591595 builder. finish ( )
592596}
593597
@@ -806,6 +810,78 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
806810 writer. write_mir_fn ( & body, & mut std:: io:: stdout ( ) ) . unwrap ( ) ;
807811 }
808812
813+ fn lint_and_remove_uninhabited ( & mut self ) {
814+ let mut lints = vec ! [ ] ;
815+
816+ for bbdata in self . cfg . basic_blocks . iter_mut ( ) {
817+ let term = bbdata. terminator_mut ( ) ;
818+ let TerminatorKind :: Call { ref mut target, destination, .. } = term. kind else {
819+ continue ;
820+ } ;
821+ let Some ( target_bb) = * target else { continue } ;
822+
823+ let ty = destination. ty ( & self . local_decls , self . tcx ) . ty ;
824+ let ty_is_inhabited = ty. is_inhabited_from (
825+ self . tcx ,
826+ self . parent_module ,
827+ self . infcx . typing_env ( self . param_env ) ,
828+ ) ;
829+
830+ if !ty_is_inhabited {
831+ // Unreachable code warnings are already emitted during type checking.
832+ // However, during type checking, full type information is being
833+ // calculated but not yet available, so the check for diverging
834+ // expressions due to uninhabited result types is pretty crude and
835+ // only checks whether ty.is_never(). Here, we have full type
836+ // information available and can issue warnings for less obviously
837+ // uninhabited types (e.g. empty enums). The check above is used so
838+ // that we do not emit the same warning twice if the uninhabited type
839+ // is indeed `!`.
840+ if !ty. is_never ( ) {
841+ lints. push ( ( target_bb, ty, term. source_info . span ) ) ;
842+ }
843+
844+ // The presence or absence of a return edge affects control-flow sensitive
845+ // MIR checks and ultimately whether code is accepted or not. We can only
846+ // omit the return edge if a return type is visibly uninhabited to a module
847+ // that makes the call.
848+ * target = None ;
849+ }
850+ }
851+
852+ for ( target_bb, orig_ty, orig_span) in lints {
853+ if orig_span. in_external_macro ( self . tcx . sess . source_map ( ) ) {
854+ continue ;
855+ }
856+ let target_bb = & self . cfg . basic_blocks [ target_bb] ;
857+ let ( target_loc, descr) = target_bb
858+ . statements
859+ . iter ( )
860+ . find_map ( |stmt| match stmt. kind {
861+ StatementKind :: StorageLive ( _) | StatementKind :: StorageDead ( _) => None ,
862+ StatementKind :: FakeRead ( ..) => Some ( ( stmt. source_info , "definition" ) ) ,
863+ _ => Some ( ( stmt. source_info , "expression" ) ) ,
864+ } )
865+ . unwrap_or_else ( || ( target_bb. terminator ( ) . source_info , "expression" ) ) ;
866+ let lint_root = self . source_scopes [ target_loc. scope ]
867+ . local_data
868+ . as_ref ( )
869+ . unwrap_crate_local ( )
870+ . lint_root ;
871+ self . tcx . emit_node_span_lint (
872+ lint:: builtin:: UNREACHABLE_CODE ,
873+ lint_root,
874+ target_loc. span ,
875+ errors:: UnreachableDueToUninhabited {
876+ expr : target_loc. span ,
877+ orig : orig_span,
878+ descr,
879+ ty : orig_ty,
880+ } ,
881+ ) ;
882+ }
883+ }
884+
809885 fn finish ( self ) -> Body < ' tcx > {
810886 let mut body = Body :: new (
811887 MirSource :: item ( self . def_id . to_def_id ( ) ) ,
0 commit comments