diff options
author | adamrk <[email protected]> | 2020-06-07 20:57:29 +0100 |
---|---|---|
committer | adamrk <[email protected]> | 2020-06-19 21:51:25 +0100 |
commit | 3f94a90c7bbc1b3116a7960ae9f25ebe35d68ad0 (patch) | |
tree | 82d8a965b9e01e136df4eeaee4c4fee4630ab2a7 | |
parent | 6654055308515cb330f23942f347de5605f69be1 (diff) |
Infer FnSig from Fn traits
-rw-r--r-- | crates/ra_hir_ty/src/infer/coerce.rs | 6 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer/expr.rs | 102 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/traits.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/traits/builtin.rs | 6 | ||||
-rw-r--r-- | crates/ra_ide/src/hover.rs | 99 |
5 files changed, 197 insertions, 18 deletions
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> { | |||
38 | // Special case: two function types. Try to coerce both to | 38 | // Special case: two function types. Try to coerce both to |
39 | // pointers to have a chance at getting a match. See | 39 | // pointers to have a chance at getting a match. See |
40 | // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 | 40 | // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 |
41 | let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig"); | 41 | let sig1 = self.callable_sig(ty1).expect("FnDef without callable sig"); |
42 | let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig"); | 42 | let sig2 = self.callable_sig(ty2).expect("FnDef without callable sig"); |
43 | let ptr_ty1 = Ty::fn_ptr(sig1); | 43 | let ptr_ty1 = Ty::fn_ptr(sig1); |
44 | let ptr_ty2 = Ty::fn_ptr(sig2); | 44 | let ptr_ty2 = Ty::fn_ptr(sig2); |
45 | self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) | 45 | self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) |
@@ -93,7 +93,7 @@ impl<'a> InferenceContext<'a> { | |||
93 | 93 | ||
94 | // `{function_type}` -> `fn()` | 94 | // `{function_type}` -> `fn()` |
95 | (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnPtr { .. })) => { | 95 | (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnPtr { .. })) => { |
96 | match from_ty.callable_sig(self.db) { | 96 | match self.callable_sig(&from_ty) { |
97 | None => return false, | 97 | None => return false, |
98 | Some(sig) => { | 98 | Some(sig) => { |
99 | from_ty = Ty::fn_ptr(sig); | 99 | 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; | |||
15 | 15 | ||
16 | use crate::{ | 16 | use crate::{ |
17 | autoderef, method_resolution, op, | 17 | autoderef, method_resolution, op, |
18 | traits::InEnvironment, | 18 | traits::{builtin::get_fn_trait, FnTrait, InEnvironment, SolutionVariables}, |
19 | utils::{generics, variant_data, Generics}, | 19 | utils::{generics, variant_data, Generics}, |
20 | ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs, | 20 | ApplicationTy, Binders, CallableDef, FnSig, InferTy, IntTy, Mutability, Obligation, Rawness, |
21 | TraitRef, Ty, TypeCtor, | 21 | Substs, TraitRef, Ty, TypeCtor, |
22 | }; | 22 | }; |
23 | 23 | ||
24 | use super::{ | 24 | use super::{ |
25 | find_breakable, BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, | 25 | find_breakable, BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, |
26 | InferenceDiagnostic, TypeMismatch, | 26 | InferenceDiagnostic, Solution, TypeMismatch, |
27 | }; | 27 | }; |
28 | 28 | ||
29 | impl<'a> InferenceContext<'a> { | 29 | impl<'a> InferenceContext<'a> { |
@@ -63,6 +63,75 @@ impl<'a> InferenceContext<'a> { | |||
63 | self.resolve_ty_as_possible(ty) | 63 | self.resolve_ty_as_possible(ty) |
64 | } | 64 | } |
65 | 65 | ||
66 | fn callable_sig_from_fn_trait(&mut self, ty: &Ty) -> Option<FnSig> { | ||
67 | if let Some(krate) = self.resolver.krate() { | ||
68 | let fn_traits: Vec<crate::TraitId> = [FnTrait::FnOnce, FnTrait::FnMut, FnTrait::Fn] | ||
69 | .iter() | ||
70 | .filter_map(|f| get_fn_trait(self.db, krate, *f)) | ||
71 | .collect(); | ||
72 | for fn_trait in fn_traits { | ||
73 | let fn_trait_data = self.db.trait_data(fn_trait); | ||
74 | let generic_params = generics(self.db.upcast(), fn_trait.into()); | ||
75 | if generic_params.len() != 2 { | ||
76 | continue; | ||
77 | } | ||
78 | |||
79 | let arg_ty = self.table.new_type_var(); | ||
80 | let substs = Substs::build_for_generics(&generic_params) | ||
81 | .push(ty.clone()) | ||
82 | .push(arg_ty.clone()) | ||
83 | .build(); | ||
84 | |||
85 | let trait_ref = TraitRef { trait_: fn_trait, substs: substs.clone() }; | ||
86 | let trait_env = Arc::clone(&self.trait_env); | ||
87 | let implements_fn_goal = | ||
88 | self.canonicalizer().canonicalize_obligation(InEnvironment { | ||
89 | value: Obligation::Trait(trait_ref), | ||
90 | environment: trait_env, | ||
91 | }); | ||
92 | if let Some(Solution::Unique(SolutionVariables(solution))) = | ||
93 | self.db.trait_solve(krate, implements_fn_goal.value.clone()) | ||
94 | { | ||
95 | match solution.value.as_slice() { | ||
96 | [Ty::Apply(ApplicationTy { | ||
97 | ctor: TypeCtor::Tuple { cardinality: _ }, | ||
98 | parameters, | ||
99 | })] => { | ||
100 | let output_assoc_type = match fn_trait_data | ||
101 | .associated_types() | ||
102 | .collect::<Vec<hir_def::TypeAliasId>>() | ||
103 | .as_slice() | ||
104 | { | ||
105 | [output] => *output, | ||
106 | _ => { | ||
107 | continue; | ||
108 | } | ||
109 | }; | ||
110 | let output_proj_ty = crate::ProjectionTy { | ||
111 | associated_ty: output_assoc_type, | ||
112 | parameters: substs, | ||
113 | }; | ||
114 | let return_ty = self.normalize_projection_ty(output_proj_ty); | ||
115 | return Some(FnSig::from_params_and_return( | ||
116 | parameters.into_iter().map(|ty| ty.clone()).collect(), | ||
117 | return_ty, | ||
118 | )); | ||
119 | } | ||
120 | _ => (), | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | }; | ||
125 | None | ||
126 | } | ||
127 | |||
128 | pub fn callable_sig(&mut self, ty: &Ty) -> Option<FnSig> { | ||
129 | match ty.callable_sig(self.db) { | ||
130 | result @ Some(_) => result, | ||
131 | None => self.callable_sig_from_fn_trait(ty), | ||
132 | } | ||
133 | } | ||
134 | |||
66 | fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { | 135 | fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { |
67 | let body = Arc::clone(&self.body); // avoid borrow checker problem | 136 | let body = Arc::clone(&self.body); // avoid borrow checker problem |
68 | let ty = match &body[tgt_expr] { | 137 | let ty = match &body[tgt_expr] { |
@@ -198,14 +267,21 @@ impl<'a> InferenceContext<'a> { | |||
198 | } | 267 | } |
199 | Expr::Call { callee, args } => { | 268 | Expr::Call { callee, args } => { |
200 | let callee_ty = self.infer_expr(*callee, &Expectation::none()); | 269 | let callee_ty = self.infer_expr(*callee, &Expectation::none()); |
201 | let (param_tys, ret_ty) = match callee_ty.callable_sig(self.db) { | 270 | let canonicalized = self.canonicalizer().canonicalize_ty(callee_ty.clone()); |
202 | Some(sig) => (sig.params().to_vec(), sig.ret().clone()), | 271 | let mut derefs = autoderef( |
203 | None => { | 272 | self.db, |
204 | // Not callable | 273 | self.resolver.krate(), |
205 | // FIXME: report an error | 274 | InEnvironment { |
206 | (Vec::new(), Ty::Unknown) | 275 | value: canonicalized.value.clone(), |
207 | } | 276 | environment: self.trait_env.clone(), |
208 | }; | 277 | }, |
278 | ); | ||
279 | let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs | ||
280 | .find_map(|callee_deref_ty| { | ||
281 | self.callable_sig(&canonicalized.decanonicalize_ty(callee_deref_ty.value)) | ||
282 | .map(|sig| (sig.params().to_vec(), sig.ret().clone())) | ||
283 | }) | ||
284 | .unwrap_or((Vec::new(), Ty::Unknown)); | ||
209 | self.register_obligations_for_call(&callee_ty); | 285 | self.register_obligations_for_call(&callee_ty); |
210 | self.check_call_arguments(args, ¶m_tys); | 286 | self.check_call_arguments(args, ¶m_tys); |
211 | self.normalize_associated_types_in(ret_ty) | 287 | self.normalize_associated_types_in(ret_ty) |
@@ -692,7 +768,7 @@ impl<'a> InferenceContext<'a> { | |||
692 | let method_ty = method_ty.subst(&substs); | 768 | let method_ty = method_ty.subst(&substs); |
693 | let method_ty = self.insert_type_vars(method_ty); | 769 | let method_ty = self.insert_type_vars(method_ty); |
694 | self.register_obligations_for_call(&method_ty); | 770 | self.register_obligations_for_call(&method_ty); |
695 | let (expected_receiver_ty, param_tys, ret_ty) = match method_ty.callable_sig(self.db) { | 771 | let (expected_receiver_ty, param_tys, ret_ty) = match self.callable_sig(&method_ty) { |
696 | Some(sig) => { | 772 | Some(sig) => { |
697 | if !sig.params().is_empty() { | 773 | if !sig.params().is_empty() { |
698 | (sig.params()[0].clone(), sig.params()[1..].to_vec(), sig.ret().clone()) | 774 | (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, | |||
14 | use self::chalk::{from_chalk, Interner, ToChalk}; | 14 | use self::chalk::{from_chalk, Interner, ToChalk}; |
15 | 15 | ||
16 | pub(crate) mod chalk; | 16 | pub(crate) mod chalk; |
17 | mod builtin; | 17 | pub(crate) mod builtin; |
18 | 18 | ||
19 | // This controls the maximum size of types Chalk considers. If we set this too | 19 | // This controls the maximum size of types Chalk considers. If we set this too |
20 | // high, we can run into slow edge cases; if we set it too low, Chalk won't | 20 | // 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( | |||
360 | BuiltinImplData { num_vars, trait_ref, where_clauses: Vec::new(), assoc_ty_values: Vec::new() } | 360 | BuiltinImplData { num_vars, trait_ref, where_clauses: Vec::new(), assoc_ty_values: Vec::new() } |
361 | } | 361 | } |
362 | 362 | ||
363 | fn get_fn_trait(db: &dyn HirDatabase, krate: CrateId, fn_trait: super::FnTrait) -> Option<TraitId> { | 363 | pub fn get_fn_trait( |
364 | db: &dyn HirDatabase, | ||
365 | krate: CrateId, | ||
366 | fn_trait: super::FnTrait, | ||
367 | ) -> Option<TraitId> { | ||
364 | let target = db.lang_item(krate, fn_trait.lang_item_name().into())?; | 368 | let target = db.lang_item(krate, fn_trait.lang_item_name().into())?; |
365 | match target { | 369 | match target { |
366 | LangItemTarget::TraitId(t) => Some(t), | 370 | 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; }; } | |||
2410 | ] | 2410 | ] |
2411 | "###); | 2411 | "###); |
2412 | } | 2412 | } |
2413 | |||
2414 | #[test] | ||
2415 | fn infer_closure_arg() { | ||
2416 | check_hover_result( | ||
2417 | r#" | ||
2418 | //- /lib.rs | ||
2419 | |||
2420 | enum Option<T> { | ||
2421 | None, | ||
2422 | Some(T) | ||
2423 | } | ||
2424 | |||
2425 | fn foo() { | ||
2426 | let s<|> = Option::None; | ||
2427 | let f = |x: Option<i32>| {}; | ||
2428 | (&f)(s) | ||
2429 | } | ||
2430 | "#, | ||
2431 | &["Option<i32>"], | ||
2432 | ); | ||
2433 | } | ||
2434 | |||
2435 | #[test] | ||
2436 | fn infer_fn_trait_arg() { | ||
2437 | check_hover_result( | ||
2438 | r#" | ||
2439 | //- /lib.rs deps:std | ||
2440 | |||
2441 | #[lang = "fn"] | ||
2442 | pub trait Fn<Args> { | ||
2443 | type Output; | ||
2444 | |||
2445 | extern "rust-call" fn call(&self, args: Args) -> Self::Output; | ||
2446 | } | ||
2447 | |||
2448 | enum Option<T> { | ||
2449 | None, | ||
2450 | Some(T) | ||
2451 | } | ||
2452 | |||
2453 | fn foo<F, T>(f: F) -> T | ||
2454 | where | ||
2455 | F: Fn(Option<i32>) -> T, | ||
2456 | { | ||
2457 | let s<|> = None; | ||
2458 | f(s) | ||
2459 | } | ||
2460 | "#, | ||
2461 | &["Option<i32>"], | ||
2462 | ); | ||
2463 | } | ||
2464 | |||
2465 | #[test] | ||
2466 | fn infer_box_fn_arg() { | ||
2467 | check_hover_result( | ||
2468 | r#" | ||
2469 | //- /lib.rs deps:std | ||
2470 | |||
2471 | #[lang = "fn_once"] | ||
2472 | pub trait FnOnce<Args> { | ||
2473 | type Output; | ||
2474 | |||
2475 | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; | ||
2476 | } | ||
2477 | |||
2478 | #[lang = "deref"] | ||
2479 | pub trait Deref { | ||
2480 | type Target: ?Sized; | ||
2481 | |||
2482 | fn deref(&self) -> &Self::Target; | ||
2483 | } | ||
2484 | |||
2485 | #[lang = "owned_box"] | ||
2486 | pub struct Box<T: ?Sized> { | ||
2487 | inner: *mut T, | ||
2488 | } | ||
2489 | |||
2490 | impl<T: ?Sized> Deref for Box<T> { | ||
2491 | type Target = T; | ||
2492 | |||
2493 | fn deref(&self) -> &T { | ||
2494 | &self.inner | ||
2495 | } | ||
2496 | } | ||
2497 | |||
2498 | enum Option<T> { | ||
2499 | None, | ||
2500 | Some(T) | ||
2501 | } | ||
2502 | |||
2503 | fn foo() { | ||
2504 | let s<|> = Option::None; | ||
2505 | let f: Box<dyn FnOnce(&Option<i32>)> = box (|ps| {}); | ||
2506 | f(&s) | ||
2507 | } | ||
2508 | "#, | ||
2509 | &["Option<i32>"], | ||
2510 | ); | ||
2511 | } | ||
2413 | } | 2512 | } |