@@ -13,7 +13,7 @@ use rustc_hir::intravisit::Visitor;
13
13
use rustc_hir:: { AsyncGeneratorKind , GeneratorKind , Node } ;
14
14
use rustc_middle:: ty:: TypeckTables ;
15
15
use rustc_middle:: ty:: {
16
- self , AdtKind , DefIdTree , ToPredicate , Ty , TyCtxt , TypeFoldable , WithConstness ,
16
+ self , AdtKind , DefIdTree , Infer , InferTy , ToPredicate , Ty , TyCtxt , TypeFoldable , WithConstness ,
17
17
} ;
18
18
use rustc_span:: symbol:: { kw, sym, Symbol } ;
19
19
use rustc_span:: { MultiSpan , Span , DUMMY_SP } ;
@@ -826,12 +826,28 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
826
826
. iter ( )
827
827
. filter_map ( |expr| tables. node_type_opt ( expr. hir_id ) )
828
828
. 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| {
832
833
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 ) )
835
851
} ,
836
852
) ;
837
853
let all_returns_conform_to_trait =
@@ -840,13 +856,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
840
856
ty:: Dynamic ( predicates, _) => {
841
857
let cause = ObligationCause :: misc ( ret_ty. span , ret_ty. hir_id ) ;
842
858
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
+ } )
848
866
} )
849
- } )
850
867
}
851
868
_ => false ,
852
869
}
@@ -855,21 +872,19 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
855
872
} ;
856
873
857
874
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
+ } ;
873
888
err. code ( error_code ! ( E0746 ) ) ;
874
889
err. set_primary_message ( "return type cannot have an unboxed trait object" ) ;
875
890
err. children . clear ( ) ;
@@ -881,13 +896,22 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
881
896
#using-trait-objects-that-allow-for-values-of-different-types>";
882
897
let has_dyn = snippet. split_whitespace ( ) . next ( ) . map_or ( false , |s| s == "dyn" ) ;
883
898
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) {
885
909
// Suggest `-> impl Trait`.
886
910
err. span_suggestion (
887
911
ret_ty. span ,
888
912
& 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}`",
891
915
last_ty, trait_obj,
892
916
) ,
893
917
format ! ( "impl {}" , trait_obj) ,
@@ -925,8 +949,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
925
949
}
926
950
err. note ( trait_obj_msg) ;
927
951
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",
930
954
trait_obj,
931
955
) ) ;
932
956
err. note ( impl_trait_msg) ;
@@ -1813,3 +1837,39 @@ impl NextTypeParamName for &[hir::GenericParam<'_>] {
1813
1837
. to_string ( )
1814
1838
}
1815
1839
}
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