@@ -414,6 +414,12 @@ pub trait TypeErrCtxtExt<'tcx> {
414
414
param_env : ty:: ParamEnv < ' tcx > ,
415
415
cause : & ObligationCause < ' tcx > ,
416
416
) ;
417
+
418
+ fn suggest_desugaring_async_fn_in_trait (
419
+ & self ,
420
+ err : & mut Diagnostic ,
421
+ trait_ref : ty:: PolyTraitRef < ' tcx > ,
422
+ ) ;
417
423
}
418
424
419
425
fn predicate_constraint ( generics : & hir:: Generics < ' _ > , pred : ty:: Predicate < ' _ > ) -> ( Span , String ) {
@@ -4100,6 +4106,136 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
4100
4106
} ) ;
4101
4107
}
4102
4108
}
4109
+
4110
+ fn suggest_desugaring_async_fn_in_trait (
4111
+ & self ,
4112
+ err : & mut Diagnostic ,
4113
+ trait_ref : ty:: PolyTraitRef < ' tcx > ,
4114
+ ) {
4115
+ // Don't suggest if RTN is active -- we should prefer a where-clause bound instead.
4116
+ if self . tcx . features ( ) . return_type_notation {
4117
+ return ;
4118
+ }
4119
+
4120
+ let trait_def_id = trait_ref. def_id ( ) ;
4121
+
4122
+ // Only suggest specifying auto traits
4123
+ if !self . tcx . trait_is_auto ( trait_def_id) {
4124
+ return ;
4125
+ }
4126
+
4127
+ // Look for an RPITIT
4128
+ let ty:: Alias ( ty:: Projection , alias_ty) = trait_ref. self_ty ( ) . skip_binder ( ) . kind ( ) else {
4129
+ return ;
4130
+ } ;
4131
+ let Some ( ty:: ImplTraitInTraitData :: Trait { fn_def_id, opaque_def_id } ) =
4132
+ self . tcx . opt_rpitit_info ( alias_ty. def_id )
4133
+ else {
4134
+ return ;
4135
+ } ;
4136
+
4137
+ let auto_trait = self . tcx . def_path_str ( trait_def_id) ;
4138
+ // ... which is a local function
4139
+ let Some ( fn_def_id) = fn_def_id. as_local ( ) else {
4140
+ // If it's not local, we can at least mention that the method is async, if it is.
4141
+ if self . tcx . asyncness ( fn_def_id) . is_async ( ) {
4142
+ err. span_note (
4143
+ self . tcx . def_span ( fn_def_id) ,
4144
+ format ! (
4145
+ "`{}::{}` is an `async fn` in trait, which does not \
4146
+ automatically imply that its future is `{auto_trait}`",
4147
+ alias_ty. trait_ref( self . tcx) ,
4148
+ self . tcx. item_name( fn_def_id)
4149
+ ) ,
4150
+ ) ;
4151
+ }
4152
+ return ;
4153
+ } ;
4154
+ let Some ( hir:: Node :: TraitItem ( item) ) = self . tcx . hir ( ) . find_by_def_id ( fn_def_id) else {
4155
+ return ;
4156
+ } ;
4157
+
4158
+ // ... whose signature is `async` (i.e. this is an AFIT)
4159
+ let ( sig, body) = item. expect_fn ( ) ;
4160
+ let hir:: IsAsync :: Async ( async_span) = sig. header . asyncness else {
4161
+ return ;
4162
+ } ;
4163
+ let Ok ( async_span) =
4164
+ self . tcx . sess . source_map ( ) . span_extend_while ( async_span, |c| c. is_whitespace ( ) )
4165
+ else {
4166
+ return ;
4167
+ } ;
4168
+ let hir:: FnRetTy :: Return ( hir:: Ty { kind : hir:: TyKind :: OpaqueDef ( def, ..) , .. } ) =
4169
+ sig. decl . output
4170
+ else {
4171
+ // This should never happen, but let's not ICE.
4172
+ return ;
4173
+ } ;
4174
+
4175
+ // Check that this is *not* a nested `impl Future` RPIT in an async fn
4176
+ // (i.e. `async fn foo() -> impl Future`)
4177
+ if def. owner_id . to_def_id ( ) != opaque_def_id {
4178
+ return ;
4179
+ }
4180
+
4181
+ let future = self . tcx . hir ( ) . item ( * def) . expect_opaque_ty ( ) ;
4182
+ let Some ( hir:: GenericBound :: LangItemTrait ( _, _, _, generics) ) = future. bounds . get ( 0 ) else {
4183
+ // `async fn` should always lower to a lang item bound... but don't ICE.
4184
+ return ;
4185
+ } ;
4186
+ let Some ( hir:: TypeBindingKind :: Equality { term : hir:: Term :: Ty ( future_output_ty) } ) =
4187
+ generics. bindings . get ( 0 ) . map ( |binding| binding. kind )
4188
+ else {
4189
+ // Also should never happen.
4190
+ return ;
4191
+ } ;
4192
+
4193
+ let function_name = self . tcx . def_path_str ( fn_def_id) ;
4194
+
4195
+ let mut sugg = if future_output_ty. span . is_empty ( ) {
4196
+ vec ! [
4197
+ ( async_span, String :: new( ) ) ,
4198
+ (
4199
+ future_output_ty. span,
4200
+ format!( " -> impl std::future::Future<Output = ()> + {auto_trait}" ) ,
4201
+ ) ,
4202
+ ]
4203
+ } else {
4204
+ vec ! [
4205
+ (
4206
+ future_output_ty. span. shrink_to_lo( ) ,
4207
+ "impl std::future::Future<Output = " . to_owned( ) ,
4208
+ ) ,
4209
+ ( future_output_ty. span. shrink_to_hi( ) , format!( "> + {auto_trait}" ) ) ,
4210
+ ( async_span, String :: new( ) ) ,
4211
+ ]
4212
+ } ;
4213
+
4214
+ // If there's a body, we also need to wrap it in `async {}`
4215
+ if let hir:: TraitFn :: Provided ( body) = body {
4216
+ let body = self . tcx . hir ( ) . body ( * body) ;
4217
+ let body_span = body. value . span ;
4218
+ let body_span_without_braces =
4219
+ body_span. with_lo ( body_span. lo ( ) + BytePos ( 1 ) ) . with_hi ( body_span. hi ( ) - BytePos ( 1 ) ) ;
4220
+ if body_span_without_braces. is_empty ( ) {
4221
+ sugg. push ( ( body_span_without_braces, " async {} " . to_owned ( ) ) ) ;
4222
+ } else {
4223
+ sugg. extend ( [
4224
+ ( body_span_without_braces. shrink_to_lo ( ) , "async {" . to_owned ( ) ) ,
4225
+ ( body_span_without_braces. shrink_to_hi ( ) , "} " . to_owned ( ) ) ,
4226
+ ] ) ;
4227
+ }
4228
+ }
4229
+
4230
+ err. multipart_suggestion (
4231
+ format ! (
4232
+ "`{auto_trait}` can be made part of the associated future's \
4233
+ guarantees for all implementations of `{function_name}`"
4234
+ ) ,
4235
+ sugg,
4236
+ Applicability :: MachineApplicable ,
4237
+ ) ;
4238
+ }
4103
4239
}
4104
4240
4105
4241
/// Add a hint to add a missing borrow or remove an unnecessary one.
0 commit comments