@@ -1045,8 +1045,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
10451045 }
10461046
10471047 let hir = self . tcx . hir ( ) ;
1048- let parent_node = hir. get_parent_node ( obligation. cause . body_id ) ;
1049- let node = hir. find ( parent_node ) ;
1048+ let fn_hir_id = hir. get_parent_node ( obligation. cause . body_id ) ;
1049+ let node = hir. find ( fn_hir_id ) ;
10501050 let Some ( hir:: Node :: Item ( hir:: Item {
10511051 kind : hir:: ItemKind :: Fn ( sig, _, body_id) ,
10521052 ..
@@ -1084,16 +1084,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
10841084 visitor. visit_body ( & body) ;
10851085
10861086 let typeck_results = self . in_progress_typeck_results . map ( |t| t. borrow ( ) ) . unwrap ( ) ;
1087+ let Some ( liberated_sig) = typeck_results. liberated_fn_sigs ( ) . get ( fn_hir_id) else { return false ; } ;
10871088
1088- let mut ret_types = visitor
1089+ let ret_types = visitor
10891090 . returns
10901091 . iter ( )
1091- . filter_map ( |expr| typeck_results. node_type_opt ( expr. hir_id ) )
1092- . map ( |ty| self . resolve_vars_if_possible ( ty) ) ;
1092+ . filter_map ( |expr| Some ( ( expr . span , typeck_results. node_type_opt ( expr. hir_id ) ? ) ) )
1093+ . map ( |( expr_span , ty ) | ( expr_span , self . resolve_vars_if_possible ( ty) ) ) ;
10931094 let ( last_ty, all_returns_have_same_type, only_never_return) = ret_types. clone ( ) . fold (
10941095 ( None , true , true ) ,
10951096 |( last_ty, mut same, only_never_return) : ( std:: option:: Option < Ty < ' _ > > , bool , bool ) ,
1096- ty | {
1097+ ( _ , ty ) | {
10971098 let ty = self . resolve_vars_if_possible ( ty) ;
10981099 same &=
10991100 !matches ! ( ty. kind( ) , ty:: Error ( _) )
@@ -1114,39 +1115,60 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
11141115 ( Some ( ty) , same, only_never_return && matches ! ( ty. kind( ) , ty:: Never ) )
11151116 } ,
11161117 ) ;
1117- let all_returns_conform_to_trait =
1118- if let Some ( ty_ret_ty) = typeck_results. node_type_opt ( ret_ty. hir_id ) {
1119- match ty_ret_ty. kind ( ) {
1120- ty:: Dynamic ( predicates, _) => {
1121- let cause = ObligationCause :: misc ( ret_ty. span , ret_ty. hir_id ) ;
1122- let param_env = ty:: ParamEnv :: empty ( ) ;
1123- only_never_return
1124- || ret_types. all ( |returned_ty| {
1125- predicates. iter ( ) . all ( |predicate| {
1126- let pred = predicate. with_self_ty ( self . tcx , returned_ty) ;
1127- let obl = Obligation :: new ( cause. clone ( ) , param_env, pred) ;
1128- self . predicate_may_hold ( & obl)
1129- } )
1118+ let mut spans_and_needs_box = vec ! [ ] ;
1119+
1120+ match liberated_sig. output ( ) . kind ( ) {
1121+ ty:: Dynamic ( predicates, _) => {
1122+ let cause = ObligationCause :: misc ( ret_ty. span , fn_hir_id) ;
1123+ let param_env = ty:: ParamEnv :: empty ( ) ;
1124+
1125+ if !only_never_return {
1126+ for ( expr_span, return_ty) in ret_types {
1127+ let self_ty_satisfies_dyn_predicates = |self_ty| {
1128+ predicates. iter ( ) . all ( |predicate| {
1129+ let pred = predicate. with_self_ty ( self . tcx , self_ty) ;
1130+ let obl = Obligation :: new ( cause. clone ( ) , param_env, pred) ;
1131+ self . predicate_may_hold ( & obl)
11301132 } )
1133+ } ;
1134+
1135+ if let ty:: Adt ( def, substs) = return_ty. kind ( )
1136+ && def. is_box ( )
1137+ && self_ty_satisfies_dyn_predicates ( substs. type_at ( 0 ) )
1138+ {
1139+ spans_and_needs_box. push ( ( expr_span, false ) ) ;
1140+ } else if self_ty_satisfies_dyn_predicates ( return_ty) {
1141+ spans_and_needs_box. push ( ( expr_span, true ) ) ;
1142+ } else {
1143+ return false ;
1144+ }
11311145 }
1132- _ => false ,
11331146 }
1134- } else {
1135- true
1136- } ;
1147+ }
1148+ _ => return false ,
1149+ } ;
11371150
11381151 let sm = self . tcx . sess . source_map ( ) ;
1139- let ( true , hir:: TyKind :: TraitObject ( ..) , Ok ( snippet) , true ) = (
1140- // Verify that we're dealing with a return `dyn Trait`
1141- ret_ty. span . overlaps ( span) ,
1142- & ret_ty. kind ,
1143- sm. span_to_snippet ( ret_ty. span ) ,
1144- // If any of the return types does not conform to the trait, then we can't
1145- // suggest `impl Trait` nor trait objects: it is a type mismatch error.
1146- all_returns_conform_to_trait,
1147- ) else {
1152+ if !ret_ty. span . overlaps ( span) {
11481153 return false ;
1154+ }
1155+ let snippet = if let hir:: TyKind :: TraitObject ( ..) = ret_ty. kind {
1156+ if let Ok ( snippet) = sm. span_to_snippet ( ret_ty. span ) {
1157+ snippet
1158+ } else {
1159+ return false ;
1160+ }
1161+ } else {
1162+ // Substitute the type, so we can print a fixup given `type Alias = dyn Trait`
1163+ let name = liberated_sig. output ( ) . to_string ( ) ;
1164+ let name =
1165+ name. strip_prefix ( '(' ) . and_then ( |name| name. strip_suffix ( ')' ) ) . unwrap_or ( & name) ;
1166+ if !name. starts_with ( "dyn " ) {
1167+ return false ;
1168+ }
1169+ name. to_owned ( )
11491170 } ;
1171+
11501172 err. code ( error_code ! ( E0746 ) ) ;
11511173 err. set_primary_message ( "return type cannot have an unboxed trait object" ) ;
11521174 err. children . clear ( ) ;
@@ -1156,6 +1178,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
11561178 let trait_obj_msg = "for information on trait objects, see \
11571179 <https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
11581180 #using-trait-objects-that-allow-for-values-of-different-types>";
1181+
11591182 let has_dyn = snippet. split_whitespace ( ) . next ( ) . map_or ( false , |s| s == "dyn" ) ;
11601183 let trait_obj = if has_dyn { & snippet[ 4 ..] } else { & snippet } ;
11611184 if only_never_return {
@@ -1183,26 +1206,25 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
11831206 } else {
11841207 if is_object_safe {
11851208 // Suggest `-> Box<dyn Trait>` and `Box::new(returned_value)`.
1186- // Get all the return values and collect their span and suggestion.
1187- let mut suggestions: Vec < _ > = visitor
1188- . returns
1189- . iter ( )
1190- . flat_map ( |expr| {
1191- [
1192- ( expr. span . shrink_to_lo ( ) , "Box::new(" . to_string ( ) ) ,
1193- ( expr. span . shrink_to_hi ( ) , ")" . to_string ( ) ) ,
1194- ]
1195- . into_iter ( )
1196- } )
1197- . collect ( ) ;
1198- if !suggestions. is_empty ( ) {
1199- // Add the suggestion for the return type.
1200- suggestions. push ( ( ret_ty. span , format ! ( "Box<dyn {}>" , trait_obj) ) ) ;
1201- err. multipart_suggestion (
1202- "return a boxed trait object instead" ,
1203- suggestions,
1204- Applicability :: MaybeIncorrect ,
1205- ) ;
1209+ err. multipart_suggestion (
1210+ "return a boxed trait object instead" ,
1211+ vec ! [
1212+ ( ret_ty. span. shrink_to_lo( ) , "Box<" . to_string( ) ) ,
1213+ ( span. shrink_to_hi( ) , ">" . to_string( ) ) ,
1214+ ] ,
1215+ Applicability :: MaybeIncorrect ,
1216+ ) ;
1217+ for ( span, needs_box) in spans_and_needs_box {
1218+ if needs_box {
1219+ err. multipart_suggestion (
1220+ "... and box this value" ,
1221+ vec ! [
1222+ ( span. shrink_to_lo( ) , "Box::new(" . to_string( ) ) ,
1223+ ( span. shrink_to_hi( ) , ")" . to_string( ) ) ,
1224+ ] ,
1225+ Applicability :: MaybeIncorrect ,
1226+ ) ;
1227+ }
12061228 }
12071229 } else {
12081230 // This is currently not possible to trigger because E0038 takes precedence, but
@@ -2677,13 +2699,15 @@ fn suggest_trait_object_return_type_alternatives(
26772699 Applicability :: MaybeIncorrect ,
26782700 ) ;
26792701 if is_object_safe {
2680- err. span_suggestion (
2681- ret_ty,
2702+ err. multipart_suggestion (
26822703 & format ! (
26832704 "use a boxed trait object if all return paths implement trait `{}`" ,
26842705 trait_obj,
26852706 ) ,
2686- format ! ( "Box<dyn {}>" , trait_obj) ,
2707+ vec ! [
2708+ ( ret_ty. shrink_to_lo( ) , "Box<" . to_string( ) ) ,
2709+ ( ret_ty. shrink_to_hi( ) , ">" . to_string( ) ) ,
2710+ ] ,
26872711 Applicability :: MaybeIncorrect ,
26882712 ) ;
26892713 }
0 commit comments