diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs')
-rw-r--r-- | src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs new file mode 100644 index 000000000..3ead92909 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -0,0 +1,82 @@ +//! Inference of closure parameter types based on the closure's expected type. + +use chalk_ir::{cast::Cast, AliasEq, AliasTy, FnSubst, WhereClause}; +use hir_def::{expr::ExprId, HasModule}; +use smallvec::SmallVec; + +use crate::{ + to_chalk_trait_id, utils, ChalkTraitId, DynTy, FnPointer, FnSig, Interner, Substitution, Ty, + TyExt, TyKind, +}; + +use super::{Expectation, InferenceContext}; + +impl InferenceContext<'_> { + pub(super) fn deduce_closure_type_from_expectations( + &mut self, + closure_expr: ExprId, + closure_ty: &Ty, + sig_ty: &Ty, + expectation: &Expectation, + ) { + let expected_ty = match expectation.to_option(&mut self.table) { + Some(ty) => ty, + None => return, + }; + + // Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here. + let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty); + + // Deduction based on the expected `dyn Fn` is done separately. + if let TyKind::Dyn(dyn_ty) = expected_ty.kind(Interner) { + if let Some(sig) = self.deduce_sig_from_dyn_ty(dyn_ty) { + let expected_sig_ty = TyKind::Function(sig).intern(Interner); + + self.unify(sig_ty, &expected_sig_ty); + } + } + } + + fn deduce_sig_from_dyn_ty(&self, dyn_ty: &DynTy) -> Option<FnPointer> { + // Search for a predicate like `<$self as FnX<Args>>::Output == Ret` + + let fn_traits: SmallVec<[ChalkTraitId; 3]> = + utils::fn_traits(self.db.upcast(), self.owner.module(self.db.upcast()).krate()) + .map(to_chalk_trait_id) + .collect(); + + let self_ty = TyKind::Error.intern(Interner); + let bounds = dyn_ty.bounds.clone().substitute(Interner, &[self_ty.cast(Interner)]); + for bound in bounds.iter(Interner) { + // NOTE(skip_binders): the extracted types are rebound by the returned `FnPointer` + if let WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection), ty }) = + bound.skip_binders() + { + let assoc_data = self.db.associated_ty_data(projection.associated_ty_id); + if !fn_traits.contains(&assoc_data.trait_id) { + return None; + } + + // Skip `Self`, get the type argument. + let arg = projection.substitution.as_slice(Interner).get(1)?; + if let Some(subst) = arg.ty(Interner)?.as_tuple() { + let generic_args = subst.as_slice(Interner); + let mut sig_tys = Vec::new(); + for arg in generic_args { + sig_tys.push(arg.ty(Interner)?.clone()); + } + sig_tys.push(ty.clone()); + + cov_mark::hit!(dyn_fn_param_informs_call_site_closure_signature); + return Some(FnPointer { + num_binders: bound.len(Interner), + sig: FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: false }, + substitution: FnSubst(Substitution::from_iter(Interner, sig_tys)), + }); + } + } + } + + None + } +} |