mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-31 03:54:42 +00:00 
			
		
		
		
	Fix inference of AsyncFnX return type
				
					
				
			This commit is contained in:
		
							parent
							
								
									7fa66d67a7
								
							
						
					
					
						commit
						2a7f18bbda
					
				
					 5 changed files with 81 additions and 11 deletions
				
			
		|  | @ -259,7 +259,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> { | ||||||
|     } |     } | ||||||
|     fn well_known_trait_id( |     fn well_known_trait_id( | ||||||
|         &self, |         &self, | ||||||
|         well_known_trait: rust_ir::WellKnownTrait, |         well_known_trait: WellKnownTrait, | ||||||
|     ) -> Option<chalk_ir::TraitId<Interner>> { |     ) -> Option<chalk_ir::TraitId<Interner>> { | ||||||
|         let lang_attr = lang_item_from_well_known_trait(well_known_trait); |         let lang_attr = lang_item_from_well_known_trait(well_known_trait); | ||||||
|         let trait_ = lang_attr.resolve_trait(self.db, self.krate)?; |         let trait_ = lang_attr.resolve_trait(self.db, self.krate)?; | ||||||
|  |  | ||||||
|  | @ -1463,6 +1463,8 @@ impl HirDisplay for Ty { | ||||||
|                     } |                     } | ||||||
|                     if f.closure_style == ClosureStyle::RANotation || !sig.ret().is_unit() { |                     if f.closure_style == ClosureStyle::RANotation || !sig.ret().is_unit() { | ||||||
|                         write!(f, " -> ")?; |                         write!(f, " -> ")?; | ||||||
|  |                         // FIXME: We display `AsyncFn` as `-> impl Future`, but this is hard to fix because
 | ||||||
|  |                         // we don't have a trait environment here, required to normalize `<Ret as Future>::Output`.
 | ||||||
|                         sig.ret().hir_fmt(f)?; |                         sig.ret().hir_fmt(f)?; | ||||||
|                     } |                     } | ||||||
|                 } else { |                 } else { | ||||||
|  |  | ||||||
|  | @ -38,7 +38,7 @@ use crate::{ | ||||||
|     infer::{BreakableKind, CoerceMany, Diverges, coerce::CoerceNever}, |     infer::{BreakableKind, CoerceMany, Diverges, coerce::CoerceNever}, | ||||||
|     make_binders, |     make_binders, | ||||||
|     mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, |     mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, | ||||||
|     to_chalk_trait_id, |     to_assoc_type_id, to_chalk_trait_id, | ||||||
|     traits::FnTrait, |     traits::FnTrait, | ||||||
|     utils::{self, elaborate_clause_supertraits}, |     utils::{self, elaborate_clause_supertraits}, | ||||||
| }; | }; | ||||||
|  | @ -245,7 +245,7 @@ impl InferenceContext<'_> { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn deduce_closure_kind_from_predicate_clauses( |     fn deduce_closure_kind_from_predicate_clauses( | ||||||
|         &self, |         &mut self, | ||||||
|         expected_ty: &Ty, |         expected_ty: &Ty, | ||||||
|         clauses: impl DoubleEndedIterator<Item = WhereClause>, |         clauses: impl DoubleEndedIterator<Item = WhereClause>, | ||||||
|         closure_kind: ClosureKind, |         closure_kind: ClosureKind, | ||||||
|  | @ -378,7 +378,7 @@ impl InferenceContext<'_> { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn deduce_sig_from_projection( |     fn deduce_sig_from_projection( | ||||||
|         &self, |         &mut self, | ||||||
|         closure_kind: ClosureKind, |         closure_kind: ClosureKind, | ||||||
|         projection_ty: &ProjectionTy, |         projection_ty: &ProjectionTy, | ||||||
|         projected_ty: &Ty, |         projected_ty: &Ty, | ||||||
|  | @ -392,13 +392,16 @@ impl InferenceContext<'_> { | ||||||
| 
 | 
 | ||||||
|         // For now, we only do signature deduction based off of the `Fn` and `AsyncFn` traits,
 |         // For now, we only do signature deduction based off of the `Fn` and `AsyncFn` traits,
 | ||||||
|         // for closures and async closures, respectively.
 |         // for closures and async closures, respectively.
 | ||||||
|         match closure_kind { |         let fn_trait_kind = self.fn_trait_kind_from_trait_id(trait_)?; | ||||||
|             ClosureKind::Closure | ClosureKind::Async |         if !matches!(closure_kind, ClosureKind::Closure | ClosureKind::Async) { | ||||||
|                 if self.fn_trait_kind_from_trait_id(trait_).is_some() => |             return None; | ||||||
|             { |  | ||||||
|                 self.extract_sig_from_projection(projection_ty, projected_ty) |  | ||||||
|         } |         } | ||||||
|             _ => None, |         if fn_trait_kind.is_async() { | ||||||
|  |             // If the expected trait is `AsyncFn(...) -> X`, we don't know what the return type is,
 | ||||||
|  |             // but we do know it must implement `Future<Output = X>`.
 | ||||||
|  |             self.extract_async_fn_sig_from_projection(projection_ty, projected_ty) | ||||||
|  |         } else { | ||||||
|  |             self.extract_sig_from_projection(projection_ty, projected_ty) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -424,6 +427,39 @@ impl InferenceContext<'_> { | ||||||
|         ))) |         ))) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     fn extract_async_fn_sig_from_projection( | ||||||
|  |         &mut self, | ||||||
|  |         projection_ty: &ProjectionTy, | ||||||
|  |         projected_ty: &Ty, | ||||||
|  |     ) -> Option<FnSubst<Interner>> { | ||||||
|  |         let arg_param_ty = projection_ty.substitution.as_slice(Interner)[1].assert_ty_ref(Interner); | ||||||
|  | 
 | ||||||
|  |         let TyKind::Tuple(_, input_tys) = arg_param_ty.kind(Interner) else { | ||||||
|  |             return None; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         let ret_param_future_output = projected_ty; | ||||||
|  |         let ret_param_future = self.table.new_type_var(); | ||||||
|  |         let future_output = | ||||||
|  |             LangItem::FutureOutput.resolve_type_alias(self.db, self.resolver.krate())?; | ||||||
|  |         let future_projection = crate::AliasTy::Projection(crate::ProjectionTy { | ||||||
|  |             associated_ty_id: to_assoc_type_id(future_output), | ||||||
|  |             substitution: Substitution::from1(Interner, ret_param_future.clone()), | ||||||
|  |         }); | ||||||
|  |         self.table.register_obligation( | ||||||
|  |             crate::AliasEq { alias: future_projection, ty: ret_param_future_output.clone() } | ||||||
|  |                 .cast(Interner), | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         Some(FnSubst(Substitution::from_iter( | ||||||
|  |             Interner, | ||||||
|  |             input_tys.iter(Interner).map(|t| t.cast(Interner)).chain(Some(GenericArg::new( | ||||||
|  |                 Interner, | ||||||
|  |                 chalk_ir::GenericArgData::Ty(ret_param_future), | ||||||
|  |             ))), | ||||||
|  |         ))) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     fn fn_trait_kind_from_trait_id(&self, trait_id: hir_def::TraitId) -> Option<FnTrait> { |     fn fn_trait_kind_from_trait_id(&self, trait_id: hir_def::TraitId) -> Option<FnTrait> { | ||||||
|         FnTrait::from_lang_item(self.db.lang_attr(trait_id.into())?) |         FnTrait::from_lang_item(self.db.lang_attr(trait_id.into())?) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -4903,3 +4903,30 @@ fn main() { | ||||||
|         "#]],
 |         "#]],
 | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn async_fn_return_type() { | ||||||
|  |     check_infer( | ||||||
|  |         r#" | ||||||
|  | //- minicore: async_fn
 | ||||||
|  | fn foo<F: AsyncFn() -> R, R>(_: F) -> R { | ||||||
|  |     loop {} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn main() { | ||||||
|  |     foo(async move || ()); | ||||||
|  | } | ||||||
|  |     "#,
 | ||||||
|  |         expect![[r#" | ||||||
|  |             29..30 '_': F | ||||||
|  |             40..55 '{     loop {} }': R | ||||||
|  |             46..53 'loop {}': ! | ||||||
|  |             51..53 '{}': () | ||||||
|  |             67..97 '{     ...()); }': () | ||||||
|  |             73..76 'foo': fn foo<impl AsyncFn() -> impl Future<Output = ()>, ()>(impl AsyncFn() -> impl Future<Output = ()>) | ||||||
|  |             73..94 'foo(as...|| ())': () | ||||||
|  |             77..93 'async ... || ()': impl AsyncFn() -> impl Future<Output = ()> | ||||||
|  |             91..93 '()': () | ||||||
|  |         "#]],
 | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -291,4 +291,9 @@ impl FnTrait { | ||||||
|     pub fn get_id(self, db: &dyn HirDatabase, krate: Crate) -> Option<TraitId> { |     pub fn get_id(self, db: &dyn HirDatabase, krate: Crate) -> Option<TraitId> { | ||||||
|         self.lang_item().resolve_trait(db, krate) |         self.lang_item().resolve_trait(db, krate) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     #[inline] | ||||||
|  |     pub(crate) fn is_async(self) -> bool { | ||||||
|  |         matches!(self, FnTrait::AsyncFn | FnTrait::AsyncFnMut | FnTrait::AsyncFnOnce) | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Chayim Refael Friedman
						Chayim Refael Friedman