From 3f94a90c7bbc1b3116a7960ae9f25ebe35d68ad0 Mon Sep 17 00:00:00 2001 From: adamrk Date: Sun, 7 Jun 2020 21:57:29 +0200 Subject: Infer FnSig from Fn traits --- crates/ra_hir_ty/src/infer/coerce.rs | 6 +- crates/ra_hir_ty/src/infer/expr.rs | 102 ++++++++++++++++++++++++++++----- crates/ra_hir_ty/src/traits.rs | 2 +- crates/ra_hir_ty/src/traits/builtin.rs | 6 +- crates/ra_ide/src/hover.rs | 99 ++++++++++++++++++++++++++++++++ 5 files changed, 197 insertions(+), 18 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 32c7c57cd..4c5f171de 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs @@ -38,8 +38,8 @@ impl<'a> InferenceContext<'a> { // Special case: two function types. Try to coerce both to // pointers to have a chance at getting a match. See // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 - let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig"); - let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig"); + let sig1 = self.callable_sig(ty1).expect("FnDef without callable sig"); + let sig2 = self.callable_sig(ty2).expect("FnDef without callable sig"); let ptr_ty1 = Ty::fn_ptr(sig1); let ptr_ty2 = Ty::fn_ptr(sig2); self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) @@ -93,7 +93,7 @@ impl<'a> InferenceContext<'a> { // `{function_type}` -> `fn()` (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnPtr { .. })) => { - match from_ty.callable_sig(self.db) { + match self.callable_sig(&from_ty) { None => return false, Some(sig) => { from_ty = Ty::fn_ptr(sig); diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 9fd310f69..bba6daeb9 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -15,15 +15,15 @@ use ra_syntax::ast::RangeOp; use crate::{ autoderef, method_resolution, op, - traits::InEnvironment, + traits::{builtin::get_fn_trait, FnTrait, InEnvironment, SolutionVariables}, utils::{generics, variant_data, Generics}, - ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs, - TraitRef, Ty, TypeCtor, + ApplicationTy, Binders, CallableDef, FnSig, InferTy, IntTy, Mutability, Obligation, Rawness, + Substs, TraitRef, Ty, TypeCtor, }; use super::{ find_breakable, BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, - InferenceDiagnostic, TypeMismatch, + InferenceDiagnostic, Solution, TypeMismatch, }; impl<'a> InferenceContext<'a> { @@ -63,6 +63,75 @@ impl<'a> InferenceContext<'a> { self.resolve_ty_as_possible(ty) } + fn callable_sig_from_fn_trait(&mut self, ty: &Ty) -> Option { + if let Some(krate) = self.resolver.krate() { + let fn_traits: Vec = [FnTrait::FnOnce, FnTrait::FnMut, FnTrait::Fn] + .iter() + .filter_map(|f| get_fn_trait(self.db, krate, *f)) + .collect(); + for fn_trait in fn_traits { + let fn_trait_data = self.db.trait_data(fn_trait); + let generic_params = generics(self.db.upcast(), fn_trait.into()); + if generic_params.len() != 2 { + continue; + } + + let arg_ty = self.table.new_type_var(); + let substs = Substs::build_for_generics(&generic_params) + .push(ty.clone()) + .push(arg_ty.clone()) + .build(); + + let trait_ref = TraitRef { trait_: fn_trait, substs: substs.clone() }; + let trait_env = Arc::clone(&self.trait_env); + let implements_fn_goal = + self.canonicalizer().canonicalize_obligation(InEnvironment { + value: Obligation::Trait(trait_ref), + environment: trait_env, + }); + if let Some(Solution::Unique(SolutionVariables(solution))) = + self.db.trait_solve(krate, implements_fn_goal.value.clone()) + { + match solution.value.as_slice() { + [Ty::Apply(ApplicationTy { + ctor: TypeCtor::Tuple { cardinality: _ }, + parameters, + })] => { + let output_assoc_type = match fn_trait_data + .associated_types() + .collect::>() + .as_slice() + { + [output] => *output, + _ => { + continue; + } + }; + let output_proj_ty = crate::ProjectionTy { + associated_ty: output_assoc_type, + parameters: substs, + }; + let return_ty = self.normalize_projection_ty(output_proj_ty); + return Some(FnSig::from_params_and_return( + parameters.into_iter().map(|ty| ty.clone()).collect(), + return_ty, + )); + } + _ => (), + } + } + } + }; + None + } + + pub fn callable_sig(&mut self, ty: &Ty) -> Option { + match ty.callable_sig(self.db) { + result @ Some(_) => result, + None => self.callable_sig_from_fn_trait(ty), + } + } + fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { let body = Arc::clone(&self.body); // avoid borrow checker problem let ty = match &body[tgt_expr] { @@ -198,14 +267,21 @@ impl<'a> InferenceContext<'a> { } Expr::Call { callee, args } => { let callee_ty = self.infer_expr(*callee, &Expectation::none()); - let (param_tys, ret_ty) = match callee_ty.callable_sig(self.db) { - Some(sig) => (sig.params().to_vec(), sig.ret().clone()), - None => { - // Not callable - // FIXME: report an error - (Vec::new(), Ty::Unknown) - } - }; + let canonicalized = self.canonicalizer().canonicalize_ty(callee_ty.clone()); + let mut derefs = autoderef( + self.db, + self.resolver.krate(), + InEnvironment { + value: canonicalized.value.clone(), + environment: self.trait_env.clone(), + }, + ); + let (param_tys, ret_ty): (Vec, Ty) = derefs + .find_map(|callee_deref_ty| { + self.callable_sig(&canonicalized.decanonicalize_ty(callee_deref_ty.value)) + .map(|sig| (sig.params().to_vec(), sig.ret().clone())) + }) + .unwrap_or((Vec::new(), Ty::Unknown)); self.register_obligations_for_call(&callee_ty); self.check_call_arguments(args, ¶m_tys); self.normalize_associated_types_in(ret_ty) @@ -692,7 +768,7 @@ impl<'a> InferenceContext<'a> { let method_ty = method_ty.subst(&substs); let method_ty = self.insert_type_vars(method_ty); self.register_obligations_for_call(&method_ty); - let (expected_receiver_ty, param_tys, ret_ty) = match method_ty.callable_sig(self.db) { + let (expected_receiver_ty, param_tys, ret_ty) = match self.callable_sig(&method_ty) { Some(sig) => { if !sig.params().is_empty() { (sig.params()[0].clone(), sig.params()[1..].to_vec(), sig.ret().clone()) diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs index 6bc6d474c..9fef9240d 100644 --- a/crates/ra_hir_ty/src/traits.rs +++ b/crates/ra_hir_ty/src/traits.rs @@ -14,7 +14,7 @@ use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, use self::chalk::{from_chalk, Interner, ToChalk}; pub(crate) mod chalk; -mod builtin; +pub(crate) mod builtin; // This controls the maximum size of types Chalk considers. If we set this too // high, we can run into slow edge cases; if we set it too low, Chalk won't diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs index 88a422d2c..b05e679ad 100644 --- a/crates/ra_hir_ty/src/traits/builtin.rs +++ b/crates/ra_hir_ty/src/traits/builtin.rs @@ -360,7 +360,11 @@ fn super_trait_object_unsize_impl_datum( BuiltinImplData { num_vars, trait_ref, where_clauses: Vec::new(), assoc_ty_values: Vec::new() } } -fn get_fn_trait(db: &dyn HirDatabase, krate: CrateId, fn_trait: super::FnTrait) -> Option { +pub fn get_fn_trait( + db: &dyn HirDatabase, + krate: CrateId, + fn_trait: super::FnTrait, +) -> Option { let target = db.lang_item(krate, fn_trait.lang_item_name().into())?; match target { LangItemTarget::TraitId(t) => Some(t), diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index d870e4cbc..9a88b4977 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -2410,4 +2410,103 @@ fn func(foo: i32) { if true { <|>foo; }; } ] "###); } + + #[test] + fn infer_closure_arg() { + check_hover_result( + r#" + //- /lib.rs + + enum Option { + None, + Some(T) + } + + fn foo() { + let s<|> = Option::None; + let f = |x: Option| {}; + (&f)(s) + } + "#, + &["Option"], + ); + } + + #[test] + fn infer_fn_trait_arg() { + check_hover_result( + r#" + //- /lib.rs deps:std + + #[lang = "fn"] + pub trait Fn { + type Output; + + extern "rust-call" fn call(&self, args: Args) -> Self::Output; + } + + enum Option { + None, + Some(T) + } + + fn foo(f: F) -> T + where + F: Fn(Option) -> T, + { + let s<|> = None; + f(s) + } + "#, + &["Option"], + ); + } + + #[test] + fn infer_box_fn_arg() { + check_hover_result( + r#" + //- /lib.rs deps:std + + #[lang = "fn_once"] + pub trait FnOnce { + type Output; + + extern "rust-call" fn call_once(self, args: Args) -> Self::Output; + } + + #[lang = "deref"] + pub trait Deref { + type Target: ?Sized; + + fn deref(&self) -> &Self::Target; + } + + #[lang = "owned_box"] + pub struct Box { + inner: *mut T, + } + + impl Deref for Box { + type Target = T; + + fn deref(&self) -> &T { + &self.inner + } + } + + enum Option { + None, + Some(T) + } + + fn foo() { + let s<|> = Option::None; + let f: Box)> = box (|ps| {}); + f(&s) + } + "#, + &["Option"], + ); + } } -- cgit v1.2.3 From cf870af8073e3a72fdfb1d36b017f0abd7593559 Mon Sep 17 00:00:00 2001 From: adamrk Date: Sat, 20 Jun 2020 08:13:14 +0200 Subject: Switch back callable_sig for known fn types --- crates/ra_hir_ty/src/infer/coerce.rs | 6 +++--- crates/ra_hir_ty/src/infer/expr.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 4c5f171de..32c7c57cd 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs @@ -38,8 +38,8 @@ impl<'a> InferenceContext<'a> { // Special case: two function types. Try to coerce both to // pointers to have a chance at getting a match. See // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 - let sig1 = self.callable_sig(ty1).expect("FnDef without callable sig"); - let sig2 = self.callable_sig(ty2).expect("FnDef without callable sig"); + let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig"); + let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig"); let ptr_ty1 = Ty::fn_ptr(sig1); let ptr_ty2 = Ty::fn_ptr(sig2); self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) @@ -93,7 +93,7 @@ impl<'a> InferenceContext<'a> { // `{function_type}` -> `fn()` (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnPtr { .. })) => { - match self.callable_sig(&from_ty) { + match from_ty.callable_sig(self.db) { None => return false, Some(sig) => { from_ty = Ty::fn_ptr(sig); diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index bba6daeb9..a23d8abd6 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -768,7 +768,7 @@ impl<'a> InferenceContext<'a> { let method_ty = method_ty.subst(&substs); let method_ty = self.insert_type_vars(method_ty); self.register_obligations_for_call(&method_ty); - let (expected_receiver_ty, param_tys, ret_ty) = match self.callable_sig(&method_ty) { + let (expected_receiver_ty, param_tys, ret_ty) = match method_ty.callable_sig(self.db) { Some(sig) => { if !sig.params().is_empty() { (sig.params()[0].clone(), sig.params()[1..].to_vec(), sig.ret().clone()) -- cgit v1.2.3 From 38f6cdbc8a83b081ad1cef9171e28431be09db01 Mon Sep 17 00:00:00 2001 From: adamrk Date: Sat, 20 Jun 2020 08:42:35 +0200 Subject: Make get_fn_trait a method of FnTrait --- crates/ra_hir_ty/src/infer/expr.rs | 4 ++-- crates/ra_hir_ty/src/traits.rs | 14 ++++++++++++-- crates/ra_hir_ty/src/traits/builtin.rs | 24 +++++++----------------- 3 files changed, 21 insertions(+), 21 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index a23d8abd6..8c924ad42 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -15,7 +15,7 @@ use ra_syntax::ast::RangeOp; use crate::{ autoderef, method_resolution, op, - traits::{builtin::get_fn_trait, FnTrait, InEnvironment, SolutionVariables}, + traits::{FnTrait, InEnvironment, SolutionVariables}, utils::{generics, variant_data, Generics}, ApplicationTy, Binders, CallableDef, FnSig, InferTy, IntTy, Mutability, Obligation, Rawness, Substs, TraitRef, Ty, TypeCtor, @@ -67,7 +67,7 @@ impl<'a> InferenceContext<'a> { if let Some(krate) = self.resolver.krate() { let fn_traits: Vec = [FnTrait::FnOnce, FnTrait::FnMut, FnTrait::Fn] .iter() - .filter_map(|f| get_fn_trait(self.db, krate, *f)) + .filter_map(|f| f.get_id(self.db, krate)) .collect(); for fn_trait in fn_traits { let fn_trait_data = self.db.trait_data(fn_trait); diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs index 9fef9240d..892fbd6d1 100644 --- a/crates/ra_hir_ty/src/traits.rs +++ b/crates/ra_hir_ty/src/traits.rs @@ -2,7 +2,9 @@ use std::{panic, sync::Arc}; use chalk_ir::cast::Cast; -use hir_def::{expr::ExprId, DefWithBodyId, ImplId, TraitId, TypeAliasId}; +use hir_def::{ + expr::ExprId, lang_item::LangItemTarget, DefWithBodyId, ImplId, TraitId, TypeAliasId, +}; use ra_db::{impl_intern_key, salsa, CrateId}; use ra_prof::profile; use rustc_hash::FxHashSet; @@ -14,7 +16,7 @@ use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, use self::chalk::{from_chalk, Interner, ToChalk}; pub(crate) mod chalk; -pub(crate) mod builtin; +mod builtin; // This controls the maximum size of types Chalk considers. If we set this too // high, we can run into slow edge cases; if we set it too low, Chalk won't @@ -298,6 +300,14 @@ impl FnTrait { FnTrait::Fn => "fn", } } + + pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option { + let target = db.lang_item(krate, self.lang_item_name().into())?; + match target { + LangItemTarget::TraitId(t) => Some(t), + _ => None, + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs index b05e679ad..6d5f2d46a 100644 --- a/crates/ra_hir_ty/src/traits/builtin.rs +++ b/crates/ra_hir_ty/src/traits/builtin.rs @@ -40,7 +40,7 @@ pub(super) fn get_builtin_impls( if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty { for &fn_trait in [super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter() { - if let Some(actual_trait) = get_fn_trait(db, krate, fn_trait) { + if let Some(actual_trait) = fn_trait.get_id(db, krate) { if trait_ == actual_trait { let impl_ = super::ClosureFnTraitImplData { def: *def, expr: *expr, fn_trait }; if check_closure_fn_trait_impl_prerequisites(db, krate, impl_) { @@ -128,7 +128,7 @@ fn check_closure_fn_trait_impl_prerequisites( data: super::ClosureFnTraitImplData, ) -> bool { // the respective Fn/FnOnce/FnMut trait needs to exist - if get_fn_trait(db, krate, data.fn_trait).is_none() { + if data.fn_trait.get_id(db, krate).is_none() { return false; } @@ -136,7 +136,7 @@ fn check_closure_fn_trait_impl_prerequisites( // the traits having no type params, FnOnce being a supertrait // the FnOnce trait needs to exist and have an assoc type named Output - let fn_once_trait = match get_fn_trait(db, krate, super::FnTrait::FnOnce) { + let fn_once_trait = match (super::FnTrait::FnOnce).get_id(db, krate) { Some(t) => t, None => return false, }; @@ -151,7 +151,9 @@ fn closure_fn_trait_impl_datum( // for some closure |X, Y| -> Z: // impl Fn<(T, U)> for closure V> { Output = V } - let trait_ = get_fn_trait(db, krate, data.fn_trait) // get corresponding fn trait + let trait_ = data + .fn_trait + .get_id(db, krate) // get corresponding fn trait // the existence of the Fn trait has been checked before .expect("fn trait for closure impl missing"); @@ -211,7 +213,7 @@ fn closure_fn_trait_output_assoc_ty_value( let output_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, num_args.into())); let fn_once_trait = - get_fn_trait(db, krate, super::FnTrait::FnOnce).expect("assoc ty value should not exist"); + (super::FnTrait::FnOnce).get_id(db, krate).expect("assoc ty value should not exist"); let output_ty_id = db .trait_data(fn_once_trait) @@ -360,18 +362,6 @@ fn super_trait_object_unsize_impl_datum( BuiltinImplData { num_vars, trait_ref, where_clauses: Vec::new(), assoc_ty_values: Vec::new() } } -pub fn get_fn_trait( - db: &dyn HirDatabase, - krate: CrateId, - fn_trait: super::FnTrait, -) -> Option { - let target = db.lang_item(krate, fn_trait.lang_item_name().into())?; - match target { - LangItemTarget::TraitId(t) => Some(t), - _ => None, - } -} - fn get_unsize_trait(db: &dyn HirDatabase, krate: CrateId) -> Option { let target = db.lang_item(krate, "unsize".into())?; match target { -- cgit v1.2.3 From f0c8058f1236474b22b69971524015558384c077 Mon Sep 17 00:00:00 2001 From: adamrk Date: Sat, 20 Jun 2020 11:04:46 +0200 Subject: Fixes to callable_sig_from_fn_trait --- crates/ra_hir_ty/src/infer/expr.rs | 130 ++++++++++++++++++------------------- crates/ra_ide/src/hover.rs | 9 ++- 2 files changed, 72 insertions(+), 67 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 8c924ad42..359befe4f 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -15,10 +15,10 @@ use ra_syntax::ast::RangeOp; use crate::{ autoderef, method_resolution, op, - traits::{FnTrait, InEnvironment, SolutionVariables}, + traits::{FnTrait, Guidance, InEnvironment, SolutionVariables}, utils::{generics, variant_data, Generics}, - ApplicationTy, Binders, CallableDef, FnSig, InferTy, IntTy, Mutability, Obligation, Rawness, - Substs, TraitRef, Ty, TypeCtor, + ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs, + TraitRef, Ty, TypeCtor, }; use super::{ @@ -63,72 +63,70 @@ impl<'a> InferenceContext<'a> { self.resolve_ty_as_possible(ty) } - fn callable_sig_from_fn_trait(&mut self, ty: &Ty) -> Option { - if let Some(krate) = self.resolver.krate() { - let fn_traits: Vec = [FnTrait::FnOnce, FnTrait::FnMut, FnTrait::Fn] - .iter() - .filter_map(|f| f.get_id(self.db, krate)) - .collect(); - for fn_trait in fn_traits { - let fn_trait_data = self.db.trait_data(fn_trait); - let generic_params = generics(self.db.upcast(), fn_trait.into()); - if generic_params.len() != 2 { - continue; - } - - let arg_ty = self.table.new_type_var(); - let substs = Substs::build_for_generics(&generic_params) - .push(ty.clone()) - .push(arg_ty.clone()) - .build(); - - let trait_ref = TraitRef { trait_: fn_trait, substs: substs.clone() }; - let trait_env = Arc::clone(&self.trait_env); - let implements_fn_goal = - self.canonicalizer().canonicalize_obligation(InEnvironment { - value: Obligation::Trait(trait_ref), - environment: trait_env, - }); - if let Some(Solution::Unique(SolutionVariables(solution))) = - self.db.trait_solve(krate, implements_fn_goal.value.clone()) - { - match solution.value.as_slice() { - [Ty::Apply(ApplicationTy { - ctor: TypeCtor::Tuple { cardinality: _ }, - parameters, - })] => { - let output_assoc_type = match fn_trait_data - .associated_types() - .collect::>() - .as_slice() - { - [output] => *output, - _ => { - continue; - } - }; - let output_proj_ty = crate::ProjectionTy { - associated_ty: output_assoc_type, - parameters: substs, - }; - let return_ty = self.normalize_projection_ty(output_proj_ty); - return Some(FnSig::from_params_and_return( - parameters.into_iter().map(|ty| ty.clone()).collect(), - return_ty, - )); - } - _ => (), - } - } + fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec, Ty)> { + let krate = self.resolver.krate()?; + let fn_traits: Vec = [FnTrait::FnOnce, FnTrait::FnMut, FnTrait::Fn] + .iter() + .filter_map(|f| f.get_id(self.db, krate)) + .collect(); + let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?; + let output_assoc_type = match self + .db + .trait_data(fn_once_trait) + .associated_types() + .collect::>() + .as_slice() + { + [output] => *output, + _ => { + return None; } }; + for fn_trait in fn_traits { + let generic_params = generics(self.db.upcast(), fn_trait.into()); + if generic_params.len() != 2 { + continue; + } + + let mut param_builder = Substs::builder(num_args); + for _ in 0..num_args { + param_builder = param_builder.push(self.table.new_type_var()); + } + let arg_ty = Ty::Apply(ApplicationTy { + ctor: TypeCtor::Tuple { cardinality: num_args as u16 }, + parameters: param_builder.build(), + }); + let substs = Substs::build_for_generics(&generic_params) + .push(ty.clone()) + .push(arg_ty.clone()) + .build(); + + let trait_ref = TraitRef { trait_: fn_trait, substs: substs.clone() }; + let trait_env = Arc::clone(&self.trait_env); + let implements_fn_goal = self.canonicalizer().canonicalize_obligation(InEnvironment { + value: Obligation::Trait(trait_ref), + environment: trait_env, + }); + let solution = match self.db.trait_solve(krate, implements_fn_goal.value.clone()) { + Some(Solution::Unique(SolutionVariables(solution))) + | Some(Solution::Ambig(Guidance::Definite(SolutionVariables(solution)))) + | Some(Solution::Ambig(Guidance::Suggested(SolutionVariables(solution)))) => { + solution + } + _ => continue, + }; + let output_proj_ty = + crate::ProjectionTy { associated_ty: output_assoc_type, parameters: substs }; + let return_ty = self.normalize_projection_ty(output_proj_ty); + return Some((solution.value, return_ty)); + } None } - pub fn callable_sig(&mut self, ty: &Ty) -> Option { + pub fn callable_sig(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec, Ty)> { match ty.callable_sig(self.db) { - result @ Some(_) => result, - None => self.callable_sig_from_fn_trait(ty), + Some(sig) => Some((sig.params().to_vec(), sig.ret().clone())), + None => self.callable_sig_from_fn_trait(ty, num_args), } } @@ -278,8 +276,10 @@ impl<'a> InferenceContext<'a> { ); let (param_tys, ret_ty): (Vec, Ty) = derefs .find_map(|callee_deref_ty| { - self.callable_sig(&canonicalized.decanonicalize_ty(callee_deref_ty.value)) - .map(|sig| (sig.params().to_vec(), sig.ret().clone())) + self.callable_sig( + &canonicalized.decanonicalize_ty(callee_deref_ty.value), + args.len(), + ) }) .unwrap_or((Vec::new(), Ty::Unknown)); self.register_obligations_for_call(&callee_ty); diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 9a88b4977..a03024d09 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -2438,10 +2438,15 @@ fn func(foo: i32) { if true { <|>foo; }; } r#" //- /lib.rs deps:std - #[lang = "fn"] - pub trait Fn { + #[lang = "fn_once"] + pub trait FnOnce { type Output; + extern "rust-call" fn call_once(&self, args: Args) -> Self::Output; + } + + #[lang = "fn"] + pub trait Fn:FnOnce { extern "rust-call" fn call(&self, args: Args) -> Self::Output; } -- cgit v1.2.3 From 436dcd9656b89b10a9719828a3421b4586ac331f Mon Sep 17 00:00:00 2001 From: adamrk Date: Sat, 20 Jun 2020 11:32:01 +0200 Subject: move tests to ra_hir_ty --- crates/ra_hir_ty/src/tests/traits.rs | 155 +++++++++++++++++++++++++++++++++++ crates/ra_ide/src/hover.rs | 104 ----------------------- 2 files changed, 155 insertions(+), 104 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index e81193a3c..8efe05877 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -2888,3 +2888,158 @@ impl iter::Iterator for ops::Range { ); assert_eq!(t, "i32"); } + +#[test] +fn infer_closure_arg() { + assert_snapshot!( + infer( + r#" + //- /lib.rs + + enum Option { + None, + Some(T) + } + + fn foo() { + let s = Option::None; + let f = |x: Option| {}; + (&f)(s) + } + "# + ), + @r###" + 137..259 '{ ... }': () + 159..160 's': Option + 163..175 'Option::None': Option + 197..198 'f': |Option| -> () + 201..220 '|x: Op...2>| {}': |Option| -> () + 202..203 'x': Option + 218..220 '{}': () + 238..245 '(&f)(s)': () + 239..241 '&f': &|Option| -> () + 240..241 'f': |Option| -> () + 243..244 's': Option + "### + ); +} + +#[test] +fn infer_fn_trait_arg() { + assert_snapshot!( + infer( + r#" + //- /lib.rs deps:std + + #[lang = "fn_once"] + pub trait FnOnce { + type Output; + + extern "rust-call" fn call_once(&self, args: Args) -> Self::Output; + } + + #[lang = "fn"] + pub trait Fn:FnOnce { + extern "rust-call" fn call(&self, args: Args) -> Self::Output; + } + + enum Option { + None, + Some(T) + } + + fn foo(f: F) -> T + where + F: Fn(Option) -> T, + { + let s = None; + f(s) + } + "# + ), + @r###" + 183..187 'self': &Self + 189..193 'args': Args + 350..354 'self': &Self + 356..360 'args': Args + 515..516 'f': F + 597..663 '{ ... }': T + 619..620 's': Option + 623..627 'None': Option + 645..646 'f': F + 645..649 'f(s)': T + 647..648 's': Option + "### + ); +} + +#[test] +fn infer_box_fn_arg() { + assert_snapshot!( + infer( + r#" + //- /lib.rs deps:std + + #[lang = "fn_once"] + pub trait FnOnce { + type Output; + + extern "rust-call" fn call_once(self, args: Args) -> Self::Output; + } + + #[lang = "deref"] + pub trait Deref { + type Target: ?Sized; + + fn deref(&self) -> &Self::Target; + } + + #[lang = "owned_box"] + pub struct Box { + inner: *mut T, + } + + impl Deref for Box { + type Target = T; + + fn deref(&self) -> &T { + &self.inner + } + } + + enum Option { + None, + Some(T) + } + + fn foo() { + let s = Option::None; + let f: Box)> = box (|ps| {}); + f(&s) + } + "# + ), + @r###" + 182..186 'self': Self + 188..192 'args': Args + 356..360 'self': &Self + 622..626 'self': &Box + 634..685 '{ ... }': &T + 656..667 '&self.inner': &*mut T + 657..661 'self': &Box + 657..667 'self.inner': *mut T + 812..957 '{ ... }': FnOnce::Output,)>, ({unknown},)> + 834..835 's': Option + 838..850 'Option::None': Option + 872..873 'f': Box,)>> + 907..920 'box (|ps| {})': Box<|{unknown}| -> ()> + 912..919 '|ps| {}': |{unknown}| -> () + 913..915 'ps': {unknown} + 917..919 '{}': () + 938..939 'f': Box,)>> + 938..943 'f(&s)': FnOnce::Output,)>, ({unknown},)> + 940..942 '&s': &Option + 941..942 's': Option + "### + ); +} diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index a03024d09..d870e4cbc 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -2410,108 +2410,4 @@ fn func(foo: i32) { if true { <|>foo; }; } ] "###); } - - #[test] - fn infer_closure_arg() { - check_hover_result( - r#" - //- /lib.rs - - enum Option { - None, - Some(T) - } - - fn foo() { - let s<|> = Option::None; - let f = |x: Option| {}; - (&f)(s) - } - "#, - &["Option"], - ); - } - - #[test] - fn infer_fn_trait_arg() { - check_hover_result( - r#" - //- /lib.rs deps:std - - #[lang = "fn_once"] - pub trait FnOnce { - type Output; - - extern "rust-call" fn call_once(&self, args: Args) -> Self::Output; - } - - #[lang = "fn"] - pub trait Fn:FnOnce { - extern "rust-call" fn call(&self, args: Args) -> Self::Output; - } - - enum Option { - None, - Some(T) - } - - fn foo(f: F) -> T - where - F: Fn(Option) -> T, - { - let s<|> = None; - f(s) - } - "#, - &["Option"], - ); - } - - #[test] - fn infer_box_fn_arg() { - check_hover_result( - r#" - //- /lib.rs deps:std - - #[lang = "fn_once"] - pub trait FnOnce { - type Output; - - extern "rust-call" fn call_once(self, args: Args) -> Self::Output; - } - - #[lang = "deref"] - pub trait Deref { - type Target: ?Sized; - - fn deref(&self) -> &Self::Target; - } - - #[lang = "owned_box"] - pub struct Box { - inner: *mut T, - } - - impl Deref for Box { - type Target = T; - - fn deref(&self) -> &T { - &self.inner - } - } - - enum Option { - None, - Some(T) - } - - fn foo() { - let s<|> = Option::None; - let f: Box)> = box (|ps| {}); - f(&s) - } - "#, - &["Option"], - ); - } } -- cgit v1.2.3 From f07338bae2e4209d5e47e69f131bfa9efc56c890 Mon Sep 17 00:00:00 2001 From: adamrk Date: Sat, 20 Jun 2020 11:43:40 +0200 Subject: Add test for dyn Fn Output --- crates/ra_hir_ty/src/tests/traits.rs | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) (limited to 'crates') diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 8efe05877..dd5bdd2d0 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -3043,3 +3043,71 @@ fn infer_box_fn_arg() { "### ); } + +#[test] +fn infer_dyn_fn_output() { + assert_snapshot!( + infer( + r#" + //- /lib.rs deps:std + + #[lang = "fn_once"] + pub trait FnOnce { + type Output; + + extern "rust-call" fn call_once(self, args: Args) -> Self::Output; + } + + #[lang = "fn"] + pub trait Fn:FnOnce { + extern "rust-call" fn call(&self, args: Args) -> Self::Output; + } + + #[lang = "deref"] + pub trait Deref { + type Target: ?Sized; + + fn deref(&self) -> &Self::Target; + } + + #[lang = "owned_box"] + pub struct Box { + inner: *mut T, + } + + impl Deref for Box { + type Target = T; + + fn deref(&self) -> &T { + &self.inner + } + } + + fn foo() { + let f: Box i32> = box(|| 5); + let x = f(); + } + "# + ), + @r###" + 182..186 'self': Self + 188..192 'args': Args + 349..353 'self': &Self + 355..359 'args': Args + 523..527 'self': &Self + 789..793 'self': &Box + 801..852 '{ ... }': &T + 823..834 '&self.inner': &*mut T + 824..828 'self': &Box + 824..834 'self.inner': *mut T + 889..990 '{ ... }': () + 911..912 'f': Box> + 937..946 'box(|| 5)': Box<|| -> i32> + 941..945 '|| 5': || -> i32 + 944..945 '5': i32 + 968..969 'x': i32 + 972..973 'f': Box> + 972..975 'f()': i32 + "### + ); +} -- cgit v1.2.3 From d8f5192d99886c89418879cc7d81ba298b0596db Mon Sep 17 00:00:00 2001 From: adamrk Date: Sat, 20 Jun 2020 11:53:55 +0200 Subject: Get Output assoc type by name --- crates/ra_hir_ty/src/infer/expr.rs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 359befe4f..171330274 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -10,7 +10,7 @@ use hir_def::{ resolver::resolver_for_expr, AdtId, AssocContainerId, FieldId, Lookup, }; -use hir_expand::name::Name; +use hir_expand::name::{name, Name}; use ra_syntax::ast::RangeOp; use crate::{ @@ -70,18 +70,8 @@ impl<'a> InferenceContext<'a> { .filter_map(|f| f.get_id(self.db, krate)) .collect(); let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?; - let output_assoc_type = match self - .db - .trait_data(fn_once_trait) - .associated_types() - .collect::>() - .as_slice() - { - [output] => *output, - _ => { - return None; - } - }; + let output_assoc_type = + self.db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; for fn_trait in fn_traits { let generic_params = generics(self.db.upcast(), fn_trait.into()); if generic_params.len() != 2 { -- cgit v1.2.3 From 1629fb770e21c7e7fd4c478f5074590d5c9e6829 Mon Sep 17 00:00:00 2001 From: adamrk Date: Sat, 20 Jun 2020 17:00:57 +0200 Subject: Push obligation instead of matching on solution --- crates/ra_hir_ty/src/infer/expr.rs | 77 +++++++++++++++++------------------- crates/ra_hir_ty/src/tests/traits.rs | 8 ++-- 2 files changed, 40 insertions(+), 45 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 171330274..a9565a58d 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -15,7 +15,7 @@ use ra_syntax::ast::RangeOp; use crate::{ autoderef, method_resolution, op, - traits::{FnTrait, Guidance, InEnvironment, SolutionVariables}, + traits::{FnTrait, InEnvironment}, utils::{generics, variant_data, Generics}, ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs, TraitRef, Ty, TypeCtor, @@ -23,7 +23,7 @@ use crate::{ use super::{ find_breakable, BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, - InferenceDiagnostic, Solution, TypeMismatch, + InferenceDiagnostic, TypeMismatch, }; impl<'a> InferenceContext<'a> { @@ -65,52 +65,47 @@ impl<'a> InferenceContext<'a> { fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec, Ty)> { let krate = self.resolver.krate()?; - let fn_traits: Vec = [FnTrait::FnOnce, FnTrait::FnMut, FnTrait::Fn] - .iter() - .filter_map(|f| f.get_id(self.db, krate)) - .collect(); let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?; let output_assoc_type = self.db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; - for fn_trait in fn_traits { - let generic_params = generics(self.db.upcast(), fn_trait.into()); - if generic_params.len() != 2 { - continue; - } - - let mut param_builder = Substs::builder(num_args); - for _ in 0..num_args { - param_builder = param_builder.push(self.table.new_type_var()); - } - let arg_ty = Ty::Apply(ApplicationTy { - ctor: TypeCtor::Tuple { cardinality: num_args as u16 }, - parameters: param_builder.build(), - }); - let substs = Substs::build_for_generics(&generic_params) - .push(ty.clone()) - .push(arg_ty.clone()) - .build(); - - let trait_ref = TraitRef { trait_: fn_trait, substs: substs.clone() }; - let trait_env = Arc::clone(&self.trait_env); - let implements_fn_goal = self.canonicalizer().canonicalize_obligation(InEnvironment { - value: Obligation::Trait(trait_ref), - environment: trait_env, - }); - let solution = match self.db.trait_solve(krate, implements_fn_goal.value.clone()) { - Some(Solution::Unique(SolutionVariables(solution))) - | Some(Solution::Ambig(Guidance::Definite(SolutionVariables(solution)))) - | Some(Solution::Ambig(Guidance::Suggested(SolutionVariables(solution)))) => { - solution - } - _ => continue, - }; + let generic_params = generics(self.db.upcast(), fn_once_trait.into()); + if generic_params.len() != 2 { + return None; + } + + let mut param_builder = Substs::builder(num_args); + let mut arg_tys = vec![]; + for _ in 0..num_args { + let arg = self.table.new_type_var(); + param_builder = param_builder.push(arg.clone()); + arg_tys.push(arg); + } + let parameters = param_builder.build(); + let arg_ty = Ty::Apply(ApplicationTy { + ctor: TypeCtor::Tuple { cardinality: num_args as u16 }, + parameters, + }); + let substs = Substs::build_for_generics(&generic_params) + .push(ty.clone()) + .push(arg_ty.clone()) + .build(); + + let trait_env = Arc::clone(&self.trait_env); + let implements_fn_trait = + Obligation::Trait(TraitRef { trait_: fn_once_trait, substs: substs.clone() }); + let goal = self.canonicalizer().canonicalize_obligation(InEnvironment { + value: implements_fn_trait.clone(), + environment: trait_env, + }); + if self.db.trait_solve(krate, goal.value).is_some() { + self.obligations.push(implements_fn_trait); let output_proj_ty = crate::ProjectionTy { associated_ty: output_assoc_type, parameters: substs }; let return_ty = self.normalize_projection_ty(output_proj_ty); - return Some((solution.value, return_ty)); + Some((arg_tys, return_ty)) + } else { + None } - None } pub fn callable_sig(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec, Ty)> { diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index dd5bdd2d0..961be4abd 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -3028,7 +3028,7 @@ fn infer_box_fn_arg() { 656..667 '&self.inner': &*mut T 657..661 'self': &Box 657..667 'self.inner': *mut T - 812..957 '{ ... }': FnOnce::Output,)>, ({unknown},)> + 812..957 '{ ... }': FnOnce::Output,)>, (&Option,)> 834..835 's': Option 838..850 'Option::None': Option 872..873 'f': Box,)>> @@ -3037,7 +3037,7 @@ fn infer_box_fn_arg() { 913..915 'ps': {unknown} 917..919 '{}': () 938..939 'f': Box,)>> - 938..943 'f(&s)': FnOnce::Output,)>, ({unknown},)> + 938..943 'f(&s)': FnOnce::Output,)>, (&Option,)> 940..942 '&s': &Option 941..942 's': Option "### @@ -3105,9 +3105,9 @@ fn infer_dyn_fn_output() { 937..946 'box(|| 5)': Box<|| -> i32> 941..945 '|| 5': || -> i32 944..945 '5': i32 - 968..969 'x': i32 + 968..969 'x': FnOnce::Output, ()> 972..973 'f': Box> - 972..975 'f()': i32 + 972..975 'f()': FnOnce::Output, ()> "### ); } -- cgit v1.2.3