@@ -406,6 +406,12 @@ pub trait TypeErrCtxtExt<'tcx> {
406
406
candidate_impls : & [ ImplCandidate < ' tcx > ] ,
407
407
span : Span ,
408
408
) ;
409
+
410
+ fn suggest_desugaring_async_fn_in_trait (
411
+ & self ,
412
+ err : & mut Diagnostic ,
413
+ trait_ref : ty:: PolyTraitRef < ' tcx > ,
414
+ ) ;
409
415
}
410
416
411
417
fn predicate_constraint ( generics : & hir:: Generics < ' _ > , pred : ty:: Predicate < ' _ > ) -> ( Span , String ) {
@@ -4027,6 +4033,124 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
4027
4033
}
4028
4034
}
4029
4035
}
4036
+
4037
+ fn suggest_desugaring_async_fn_in_trait (
4038
+ & self ,
4039
+ err : & mut Diagnostic ,
4040
+ trait_ref : ty:: PolyTraitRef < ' tcx > ,
4041
+ ) {
4042
+ // Don't suggest if RTN is active -- we should prefer a where-clause bound instead.
4043
+ if self . tcx . features ( ) . return_type_notation {
4044
+ return ;
4045
+ }
4046
+
4047
+ let trait_def_id = trait_ref. def_id ( ) ;
4048
+
4049
+ // Only suggest specifying auto traits
4050
+ if !self . tcx . trait_is_auto ( trait_def_id) {
4051
+ return ;
4052
+ }
4053
+
4054
+ // Look for an RPITIT
4055
+ let ty:: Alias ( ty:: Projection , alias_ty) = trait_ref. self_ty ( ) . skip_binder ( ) . kind ( ) else {
4056
+ return ;
4057
+ } ;
4058
+ let Some ( ty:: ImplTraitInTraitData :: Trait { fn_def_id, opaque_def_id } ) =
4059
+ self . tcx . opt_rpitit_info ( alias_ty. def_id )
4060
+ else {
4061
+ return ;
4062
+ } ;
4063
+
4064
+ // ... which is a local function
4065
+ let Some ( fn_def_id) = fn_def_id. as_local ( ) else {
4066
+ return ;
4067
+ } ;
4068
+ let Some ( hir:: Node :: TraitItem ( item) ) = self . tcx . hir ( ) . find_by_def_id ( fn_def_id) else {
4069
+ return ;
4070
+ } ;
4071
+
4072
+ // ... whose signature is `async` (i.e. this is an AFIT)
4073
+ let ( sig, body) = item. expect_fn ( ) ;
4074
+ let hir:: IsAsync :: Async ( async_span) = sig. header . asyncness else {
4075
+ return ;
4076
+ } ;
4077
+ let Ok ( async_span) =
4078
+ self . tcx . sess . source_map ( ) . span_extend_while ( async_span, |c| c. is_whitespace ( ) )
4079
+ else {
4080
+ return ;
4081
+ } ;
4082
+ let hir:: FnRetTy :: Return ( hir:: Ty { kind : hir:: TyKind :: OpaqueDef ( def, ..) , .. } ) =
4083
+ sig. decl . output
4084
+ else {
4085
+ // This should never happen, but let's not ICE.
4086
+ return ;
4087
+ } ;
4088
+
4089
+ // Check that this is *not* a nested `impl Future` RPIT in an async fn
4090
+ // (i.e. `async fn foo() -> impl Future`)
4091
+ if def. owner_id . to_def_id ( ) != opaque_def_id {
4092
+ return ;
4093
+ }
4094
+
4095
+ let future = self . tcx . hir ( ) . item ( * def) . expect_opaque_ty ( ) ;
4096
+ let Some ( hir:: GenericBound :: LangItemTrait ( _, _, _, generics) ) = future. bounds . get ( 0 ) else {
4097
+ // `async fn` should always lower to a lang item bound... but don't ICE.
4098
+ return ;
4099
+ } ;
4100
+ let Some ( hir:: TypeBindingKind :: Equality { term : hir:: Term :: Ty ( future_output_ty) } ) =
4101
+ generics. bindings . get ( 0 ) . map ( |binding| binding. kind )
4102
+ else {
4103
+ // Also should never happen.
4104
+ return ;
4105
+ } ;
4106
+
4107
+ let function_name = self . tcx . def_path_str ( fn_def_id) ;
4108
+ let auto_trait = self . tcx . def_path_str ( trait_def_id) ;
4109
+
4110
+ let mut sugg = if future_output_ty. span . is_empty ( ) {
4111
+ vec ! [
4112
+ ( async_span, String :: new( ) ) ,
4113
+ (
4114
+ future_output_ty. span,
4115
+ format!( " -> impl std::future::Future<Output = ()> + {auto_trait}" ) ,
4116
+ ) ,
4117
+ ]
4118
+ } else {
4119
+ vec ! [
4120
+ (
4121
+ future_output_ty. span. shrink_to_lo( ) ,
4122
+ "impl std::future::Future<Output = " . to_owned( ) ,
4123
+ ) ,
4124
+ ( future_output_ty. span. shrink_to_hi( ) , format!( "> + {auto_trait}" ) ) ,
4125
+ ( async_span, String :: new( ) ) ,
4126
+ ]
4127
+ } ;
4128
+
4129
+ // If there's a body, we also need to wrap it in `async {}`
4130
+ if let hir:: TraitFn :: Provided ( body) = body {
4131
+ let body = self . tcx . hir ( ) . body ( * body) ;
4132
+ let body_span = body. value . span ;
4133
+ let body_span_without_braces =
4134
+ body_span. with_lo ( body_span. lo ( ) + BytePos ( 1 ) ) . with_hi ( body_span. hi ( ) - BytePos ( 1 ) ) ;
4135
+ if body_span_without_braces. is_empty ( ) {
4136
+ sugg. push ( ( body_span_without_braces, " async {} " . to_owned ( ) ) ) ;
4137
+ } else {
4138
+ sugg. extend ( [
4139
+ ( body_span_without_braces. shrink_to_lo ( ) , "async {" . to_owned ( ) ) ,
4140
+ ( body_span_without_braces. shrink_to_hi ( ) , "} " . to_owned ( ) ) ,
4141
+ ] ) ;
4142
+ }
4143
+ }
4144
+
4145
+ err. multipart_suggestion (
4146
+ format ! (
4147
+ "`{auto_trait}` can be made part of the associated future's \
4148
+ guarantees for all implementations of `{function_name}`"
4149
+ ) ,
4150
+ sugg,
4151
+ Applicability :: MachineApplicable ,
4152
+ ) ;
4153
+ }
4030
4154
}
4031
4155
4032
4156
/// Add a hint to add a missing borrow or remove an unnecessary one.
0 commit comments