@@ -1045,8 +1045,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
1045
1045
}
1046
1046
1047
1047
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 ) ;
1050
1050
let Some ( hir:: Node :: Item ( hir:: Item {
1051
1051
kind : hir:: ItemKind :: Fn ( sig, _, body_id) ,
1052
1052
..
@@ -1084,16 +1084,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
1084
1084
visitor. visit_body ( & body) ;
1085
1085
1086
1086
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 ; } ;
1087
1088
1088
- let mut ret_types = visitor
1089
+ let ret_types = visitor
1089
1090
. returns
1090
1091
. 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) ) ) ;
1093
1094
let ( last_ty, all_returns_have_same_type, only_never_return) = ret_types. clone ( ) . fold (
1094
1095
( None , true , true ) ,
1095
1096
|( last_ty, mut same, only_never_return) : ( std:: option:: Option < Ty < ' _ > > , bool , bool ) ,
1096
- ty | {
1097
+ ( _ , ty ) | {
1097
1098
let ty = self . resolve_vars_if_possible ( ty) ;
1098
1099
same &=
1099
1100
!matches ! ( ty. kind( ) , ty:: Error ( _) )
@@ -1114,39 +1115,60 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
1114
1115
( Some ( ty) , same, only_never_return && matches ! ( ty. kind( ) , ty:: Never ) )
1115
1116
} ,
1116
1117
) ;
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)
1130
1132
} )
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
+ }
1131
1145
}
1132
- _ => false ,
1133
1146
}
1134
- } else {
1135
- true
1136
- } ;
1147
+ }
1148
+ _ => return false ,
1149
+ } ;
1137
1150
1138
1151
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) {
1148
1153
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 ( )
1149
1170
} ;
1171
+
1150
1172
err. code ( error_code ! ( E0746 ) ) ;
1151
1173
err. set_primary_message ( "return type cannot have an unboxed trait object" ) ;
1152
1174
err. children . clear ( ) ;
@@ -1156,6 +1178,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
1156
1178
let trait_obj_msg = "for information on trait objects, see \
1157
1179
<https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
1158
1180
#using-trait-objects-that-allow-for-values-of-different-types>";
1181
+
1159
1182
let has_dyn = snippet. split_whitespace ( ) . next ( ) . map_or ( false , |s| s == "dyn" ) ;
1160
1183
let trait_obj = if has_dyn { & snippet[ 4 ..] } else { & snippet } ;
1161
1184
if only_never_return {
@@ -1183,26 +1206,25 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
1183
1206
} else {
1184
1207
if is_object_safe {
1185
1208
// 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
+ }
1206
1228
}
1207
1229
} else {
1208
1230
// This is currently not possible to trigger because E0038 takes precedence, but
@@ -2677,13 +2699,15 @@ fn suggest_trait_object_return_type_alternatives(
2677
2699
Applicability :: MaybeIncorrect ,
2678
2700
) ;
2679
2701
if is_object_safe {
2680
- err. span_suggestion (
2681
- ret_ty,
2702
+ err. multipart_suggestion (
2682
2703
& format ! (
2683
2704
"use a boxed trait object if all return paths implement trait `{}`" ,
2684
2705
trait_obj,
2685
2706
) ,
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
+ ] ,
2687
2711
Applicability :: MaybeIncorrect ,
2688
2712
) ;
2689
2713
}
0 commit comments