@@ -13,7 +13,7 @@ use rustc_hir::intravisit::Visitor;
1313use rustc_hir:: { AsyncGeneratorKind , GeneratorKind , Node } ;
1414use rustc_middle:: ty:: TypeckTables ;
1515use rustc_middle:: ty:: {
16- self , AdtKind , DefIdTree , ToPredicate , Ty , TyCtxt , TypeFoldable , WithConstness ,
16+ self , AdtKind , DefIdTree , Infer , InferTy , ToPredicate , Ty , TyCtxt , TypeFoldable , WithConstness ,
1717} ;
1818use rustc_span:: symbol:: { kw, sym, Symbol } ;
1919use rustc_span:: { MultiSpan , Span , DUMMY_SP } ;
@@ -826,12 +826,28 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
826826 . iter ( )
827827 . filter_map ( |expr| tables. node_type_opt ( expr. hir_id ) )
828828 . map ( |ty| self . resolve_vars_if_possible ( & ty) ) ;
829- let ( last_ty, all_returns_have_same_type) = ret_types. clone ( ) . fold (
830- ( None , true ) ,
831- |( last_ty, mut same) : ( std:: option:: Option < Ty < ' _ > > , bool ) , ty| {
829+ let ( last_ty, all_returns_have_same_type, only_never_return) = ret_types. clone ( ) . fold (
830+ ( None , true , true ) ,
831+ |( last_ty, mut same, only_never_return) : ( std:: option:: Option < Ty < ' _ > > , bool , bool ) ,
832+ ty| {
832833 let ty = self . resolve_vars_if_possible ( & ty) ;
833- same &= last_ty. map_or ( true , |last_ty| last_ty == ty) && ty. kind != ty:: Error ;
834- ( Some ( ty) , same)
834+ same &=
835+ ty. kind != ty:: Error
836+ && last_ty. map_or ( true , |last_ty| {
837+ // FIXME: ideally we would use `can_coerce` here instead, but `typeck` comes
838+ // *after* in the dependency graph.
839+ match ( & ty. kind , & last_ty. kind ) {
840+ ( Infer ( InferTy :: IntVar ( _) ) , Infer ( InferTy :: IntVar ( _) ) )
841+ | ( Infer ( InferTy :: FloatVar ( _) ) , Infer ( InferTy :: FloatVar ( _) ) )
842+ | ( Infer ( InferTy :: FreshIntTy ( _) ) , Infer ( InferTy :: FreshIntTy ( _) ) )
843+ | (
844+ Infer ( InferTy :: FreshFloatTy ( _) ) ,
845+ Infer ( InferTy :: FreshFloatTy ( _) ) ,
846+ ) => true ,
847+ _ => ty == last_ty,
848+ }
849+ } ) ;
850+ ( Some ( ty) , same, only_never_return && matches ! ( ty. kind, ty:: Never ) )
835851 } ,
836852 ) ;
837853 let all_returns_conform_to_trait =
@@ -840,13 +856,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
840856 ty:: Dynamic ( predicates, _) => {
841857 let cause = ObligationCause :: misc ( ret_ty. span , ret_ty. hir_id ) ;
842858 let param_env = ty:: ParamEnv :: empty ( ) ;
843- ret_types. all ( |returned_ty| {
844- predicates. iter ( ) . all ( |predicate| {
845- let pred = predicate. with_self_ty ( self . tcx , returned_ty) ;
846- let obl = Obligation :: new ( cause. clone ( ) , param_env, pred) ;
847- self . predicate_may_hold ( & obl)
859+ only_never_return
860+ || ret_types. all ( |returned_ty| {
861+ predicates. iter ( ) . all ( |predicate| {
862+ let pred = predicate. with_self_ty ( self . tcx , returned_ty) ;
863+ let obl = Obligation :: new ( cause. clone ( ) , param_env, pred) ;
864+ self . predicate_may_hold ( & obl)
865+ } )
848866 } )
849- } )
850867 }
851868 _ => false ,
852869 }
@@ -855,21 +872,19 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
855872 } ;
856873
857874 let sm = self . tcx . sess . source_map ( ) ;
858- let ( snippet, last_ty) =
859- if let ( true , hir:: TyKind :: TraitObject ( ..) , Ok ( snippet) , true , Some ( last_ty) ) = (
860- // Verify that we're dealing with a return `dyn Trait`
861- ret_ty. span . overlaps ( span) ,
862- & ret_ty. kind ,
863- sm. span_to_snippet ( ret_ty. span ) ,
864- // If any of the return types does not conform to the trait, then we can't
865- // suggest `impl Trait` nor trait objects, it is a type mismatch error.
866- all_returns_conform_to_trait,
867- last_ty,
868- ) {
869- ( snippet, last_ty)
870- } else {
871- return false ;
872- } ;
875+ let snippet = if let ( true , hir:: TyKind :: TraitObject ( ..) , Ok ( snippet) , true ) = (
876+ // Verify that we're dealing with a return `dyn Trait`
877+ ret_ty. span . overlaps ( span) ,
878+ & ret_ty. kind ,
879+ sm. span_to_snippet ( ret_ty. span ) ,
880+ // If any of the return types does not conform to the trait, then we can't
881+ // suggest `impl Trait` nor trait objects: it is a type mismatch error.
882+ all_returns_conform_to_trait,
883+ ) {
884+ snippet
885+ } else {
886+ return false ;
887+ } ;
873888 err. code ( error_code ! ( E0746 ) ) ;
874889 err. set_primary_message ( "return type cannot have an unboxed trait object" ) ;
875890 err. children . clear ( ) ;
@@ -881,13 +896,22 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
881896 #using-trait-objects-that-allow-for-values-of-different-types>";
882897 let has_dyn = snippet. split_whitespace ( ) . next ( ) . map_or ( false , |s| s == "dyn" ) ;
883898 let trait_obj = if has_dyn { & snippet[ 4 ..] } else { & snippet[ ..] } ;
884- if all_returns_have_same_type {
899+ if only_never_return {
900+ // No return paths, probably using `panic!()` or similar.
901+ // Suggest `-> T`, `-> impl Trait`, and if `Trait` is object safe, `-> Box<dyn Trait>`.
902+ suggest_trait_object_return_type_alternatives (
903+ err,
904+ ret_ty. span ,
905+ trait_obj,
906+ is_object_safe,
907+ ) ;
908+ } else if let ( Some ( last_ty) , true ) = ( last_ty, all_returns_have_same_type) {
885909 // Suggest `-> impl Trait`.
886910 err. span_suggestion (
887911 ret_ty. span ,
888912 & format ! (
889- "return `impl {1}` instead , as all return paths are of type `{}`, \
890- which implements `{1}`",
913+ "use `impl {1}` as the return type , as all return paths are of type `{}`, \
914+ which implements `{1}`",
891915 last_ty, trait_obj,
892916 ) ,
893917 format ! ( "impl {}" , trait_obj) ,
@@ -925,8 +949,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
925949 }
926950 err. note ( trait_obj_msg) ;
927951 err. note ( & format ! (
928- "if all the returned values were of the same type you could use \
929- `impl {}` as the return type",
952+ "if all the returned values were of the same type you could use `impl {}` as the \
953+ return type",
930954 trait_obj,
931955 ) ) ;
932956 err. note ( impl_trait_msg) ;
@@ -1813,3 +1837,39 @@ impl NextTypeParamName for &[hir::GenericParam<'_>] {
18131837 . to_string ( )
18141838 }
18151839}
1840+
1841+ fn suggest_trait_object_return_type_alternatives (
1842+ err : & mut DiagnosticBuilder < ' tcx > ,
1843+ ret_ty : Span ,
1844+ trait_obj : & str ,
1845+ is_object_safe : bool ,
1846+ ) {
1847+ err. span_suggestion (
1848+ ret_ty,
1849+ "use some type `T` that is `T: Sized` as the return type if all return paths have the \
1850+ same type",
1851+ "T" . to_string ( ) ,
1852+ Applicability :: MaybeIncorrect ,
1853+ ) ;
1854+ err. span_suggestion (
1855+ ret_ty,
1856+ & format ! (
1857+ "use `impl {}` as the return type if all return paths have the same type but you \
1858+ want to expose only the trait in the signature",
1859+ trait_obj,
1860+ ) ,
1861+ format ! ( "impl {}" , trait_obj) ,
1862+ Applicability :: MaybeIncorrect ,
1863+ ) ;
1864+ if is_object_safe {
1865+ err. span_suggestion (
1866+ ret_ty,
1867+ & format ! (
1868+ "use a boxed trait object if all return paths implement trait `{}`" ,
1869+ trait_obj,
1870+ ) ,
1871+ format ! ( "Box<dyn {}>" , trait_obj) ,
1872+ Applicability :: MaybeIncorrect ,
1873+ ) ;
1874+ }
1875+ }
0 commit comments