diff options
author | Marcus Klaas de Vries <[email protected]> | 2019-01-25 20:16:02 +0000 |
---|---|---|
committer | Marcus Klaas de Vries <[email protected]> | 2019-01-27 16:59:21 +0000 |
commit | 3bd47c0285433b5eb258196a81b95141d2a70505 (patch) | |
tree | 41bd19f6e95e4c22bd39c35702a1d2e048dd9cef | |
parent | 3f4f50baaa21cb2d0f6c102f1ca521946071a8dc (diff) |
First attempt at generic type inference for fns
-rw-r--r-- | crates/ra_hir/src/code_model_api.rs | 6 | ||||
-rw-r--r-- | crates/ra_hir/src/code_model_impl/function.rs | 8 | ||||
-rw-r--r-- | crates/ra_hir/src/generics.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 61 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 22 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/completion_item.rs | 2 |
6 files changed, 81 insertions, 21 deletions
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 191104890..82ebb275a 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs | |||
@@ -388,7 +388,7 @@ pub use crate::code_model_impl::function::ScopeEntryWithSyntax; | |||
388 | #[derive(Debug, Clone, PartialEq, Eq)] | 388 | #[derive(Debug, Clone, PartialEq, Eq)] |
389 | pub struct FnSignature { | 389 | pub struct FnSignature { |
390 | pub(crate) name: Name, | 390 | pub(crate) name: Name, |
391 | pub(crate) params: Vec<TypeRef>, | 391 | pub(crate) args: Vec<TypeRef>, |
392 | pub(crate) ret_type: TypeRef, | 392 | pub(crate) ret_type: TypeRef, |
393 | /// True if the first param is `self`. This is relevant to decide whether this | 393 | /// True if the first param is `self`. This is relevant to decide whether this |
394 | /// can be called as a method. | 394 | /// can be called as a method. |
@@ -400,8 +400,8 @@ impl FnSignature { | |||
400 | &self.name | 400 | &self.name |
401 | } | 401 | } |
402 | 402 | ||
403 | pub fn params(&self) -> &[TypeRef] { | 403 | pub fn args(&self) -> &[TypeRef] { |
404 | &self.params | 404 | &self.args |
405 | } | 405 | } |
406 | 406 | ||
407 | pub fn ret_type(&self) -> &TypeRef { | 407 | pub fn ret_type(&self) -> &TypeRef { |
diff --git a/crates/ra_hir/src/code_model_impl/function.rs b/crates/ra_hir/src/code_model_impl/function.rs index e0dd4d629..b4aa18540 100644 --- a/crates/ra_hir/src/code_model_impl/function.rs +++ b/crates/ra_hir/src/code_model_impl/function.rs | |||
@@ -32,7 +32,7 @@ impl FnSignature { | |||
32 | .name() | 32 | .name() |
33 | .map(|n| n.as_name()) | 33 | .map(|n| n.as_name()) |
34 | .unwrap_or_else(Name::missing); | 34 | .unwrap_or_else(Name::missing); |
35 | let mut params = Vec::new(); | 35 | let mut args = Vec::new(); |
36 | let mut has_self_param = false; | 36 | let mut has_self_param = false; |
37 | if let Some(param_list) = node.param_list() { | 37 | if let Some(param_list) = node.param_list() { |
38 | if let Some(self_param) = param_list.self_param() { | 38 | if let Some(self_param) = param_list.self_param() { |
@@ -50,12 +50,12 @@ impl FnSignature { | |||
50 | } | 50 | } |
51 | } | 51 | } |
52 | }; | 52 | }; |
53 | params.push(self_type); | 53 | args.push(self_type); |
54 | has_self_param = true; | 54 | has_self_param = true; |
55 | } | 55 | } |
56 | for param in param_list.params() { | 56 | for param in param_list.params() { |
57 | let type_ref = TypeRef::from_ast_opt(param.type_ref()); | 57 | let type_ref = TypeRef::from_ast_opt(param.type_ref()); |
58 | params.push(type_ref); | 58 | args.push(type_ref); |
59 | } | 59 | } |
60 | } | 60 | } |
61 | let ret_type = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) { | 61 | let ret_type = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) { |
@@ -66,7 +66,7 @@ impl FnSignature { | |||
66 | 66 | ||
67 | let sig = FnSignature { | 67 | let sig = FnSignature { |
68 | name, | 68 | name, |
69 | params, | 69 | args, |
70 | ret_type, | 70 | ret_type, |
71 | has_self_param, | 71 | has_self_param, |
72 | }; | 72 | }; |
diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index 64c20a462..a5501d543 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs | |||
@@ -49,7 +49,8 @@ impl GenericParams { | |||
49 | Arc::new(generics) | 49 | Arc::new(generics) |
50 | } | 50 | } |
51 | 51 | ||
52 | fn fill(&mut self, node: &impl TypeParamsOwner) { | 52 | // FIXME: probably shouldnt be pub(crate) |
53 | pub(crate) fn fill(&mut self, node: &impl TypeParamsOwner) { | ||
53 | if let Some(params) = node.type_param_list() { | 54 | if let Some(params) = node.type_param_list() { |
54 | self.fill_params(params) | 55 | self.fill_params(params) |
55 | } | 56 | } |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 31ea45706..95de916ee 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -209,6 +209,18 @@ pub enum Ty { | |||
209 | /// `&'a mut T` or `&'a T`. | 209 | /// `&'a mut T` or `&'a T`. |
210 | Ref(Arc<Ty>, Mutability), | 210 | Ref(Arc<Ty>, Mutability), |
211 | 211 | ||
212 | /// The anonymous type of a function declaration/definition. Each | ||
213 | /// function has a unique type, which is output (for a function | ||
214 | /// named `foo` returning an `i32`) as `fn() -> i32 {foo}`. | ||
215 | /// | ||
216 | /// For example the type of `bar` here: | ||
217 | /// | ||
218 | /// ```rust | ||
219 | /// fn foo() -> i32 { 1 } | ||
220 | /// let bar = foo; // bar: fn() -> i32 {foo} | ||
221 | /// ``` | ||
222 | FnDef(Function, Substs), | ||
223 | |||
212 | /// A pointer to a function. Written as `fn() -> i32`. | 224 | /// A pointer to a function. Written as `fn() -> i32`. |
213 | /// | 225 | /// |
214 | /// For example the type of `bar` here: | 226 | /// For example the type of `bar` here: |
@@ -485,7 +497,7 @@ impl Ty { | |||
485 | } | 497 | } |
486 | sig_mut.output.walk_mut(f); | 498 | sig_mut.output.walk_mut(f); |
487 | } | 499 | } |
488 | Ty::Adt { substs, .. } => { | 500 | Ty::FnDef(_, substs) | Ty::Adt { substs, .. } => { |
489 | // Without an Arc::make_mut_slice, we can't avoid the clone here: | 501 | // Without an Arc::make_mut_slice, we can't avoid the clone here: |
490 | let mut v: Vec<_> = substs.0.iter().cloned().collect(); | 502 | let mut v: Vec<_> = substs.0.iter().cloned().collect(); |
491 | for t in &mut v { | 503 | for t in &mut v { |
@@ -524,6 +536,7 @@ impl Ty { | |||
524 | name, | 536 | name, |
525 | substs, | 537 | substs, |
526 | }, | 538 | }, |
539 | Ty::FnDef(func, _) => Ty::FnDef(func, substs), | ||
527 | _ => self, | 540 | _ => self, |
528 | } | 541 | } |
529 | } | 542 | } |
@@ -579,6 +592,7 @@ impl fmt::Display for Ty { | |||
579 | .to_fmt(f) | 592 | .to_fmt(f) |
580 | } | 593 | } |
581 | } | 594 | } |
595 | Ty::FnDef(_func, _substs) => write!(f, "FNDEF-IMPLEMENT-ME"), | ||
582 | Ty::FnPtr(sig) => { | 596 | Ty::FnPtr(sig) => { |
583 | join(sig.input.iter()) | 597 | join(sig.input.iter()) |
584 | .surround_with("fn(", ")") | 598 | .surround_with("fn(", ")") |
@@ -608,12 +622,18 @@ impl fmt::Display for Ty { | |||
608 | /// Compute the declared type of a function. This should not need to look at the | 622 | /// Compute the declared type of a function. This should not need to look at the |
609 | /// function body. | 623 | /// function body. |
610 | fn type_for_fn(db: &impl HirDatabase, f: Function) -> Ty { | 624 | fn type_for_fn(db: &impl HirDatabase, f: Function) -> Ty { |
625 | let generics = f.generic_params(db); | ||
626 | let substs = make_substs(&generics); | ||
627 | Ty::FnDef(f.into(), substs) | ||
628 | } | ||
629 | |||
630 | fn get_func_sig(db: &impl HirDatabase, f: Function) -> FnSig { | ||
611 | let signature = f.signature(db); | 631 | let signature = f.signature(db); |
612 | let module = f.module(db); | 632 | let module = f.module(db); |
613 | let impl_block = f.impl_block(db); | 633 | let impl_block = f.impl_block(db); |
614 | let generics = f.generic_params(db); | 634 | let generics = f.generic_params(db); |
615 | let input = signature | 635 | let input = signature |
616 | .params() | 636 | .args() |
617 | .iter() | 637 | .iter() |
618 | .map(|tr| Ty::from_hir(db, &module, impl_block.as_ref(), &generics, tr)) | 638 | .map(|tr| Ty::from_hir(db, &module, impl_block.as_ref(), &generics, tr)) |
619 | .collect::<Vec<_>>(); | 639 | .collect::<Vec<_>>(); |
@@ -624,8 +644,7 @@ fn type_for_fn(db: &impl HirDatabase, f: Function) -> Ty { | |||
624 | &generics, | 644 | &generics, |
625 | signature.ret_type(), | 645 | signature.ret_type(), |
626 | ); | 646 | ); |
627 | let sig = FnSig { input, output }; | 647 | FnSig { input, output } |
628 | Ty::FnPtr(Arc::new(sig)) | ||
629 | } | 648 | } |
630 | 649 | ||
631 | fn make_substs(generics: &GenericParams) -> Substs { | 650 | fn make_substs(generics: &GenericParams) -> Substs { |
@@ -1142,7 +1161,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1142 | let ty = self.insert_type_vars(ty.apply_substs(substs)); | 1161 | let ty = self.insert_type_vars(ty.apply_substs(substs)); |
1143 | (ty, Some(var.into())) | 1162 | (ty, Some(var.into())) |
1144 | } | 1163 | } |
1145 | TypableDef::Enum(_) | TypableDef::Function(_) => (Ty::Unknown, None), | 1164 | TypableDef::Function(func) => { |
1165 | let ty = type_for_fn(self.db, func); | ||
1166 | let ty = self.insert_type_vars(ty.apply_substs(substs)); | ||
1167 | // FIXME: is this right? | ||
1168 | (ty, None) | ||
1169 | } | ||
1170 | TypableDef::Enum(_) => (Ty::Unknown, None), | ||
1146 | } | 1171 | } |
1147 | } | 1172 | } |
1148 | 1173 | ||
@@ -1331,12 +1356,27 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1331 | } | 1356 | } |
1332 | Expr::Call { callee, args } => { | 1357 | Expr::Call { callee, args } => { |
1333 | let callee_ty = self.infer_expr(*callee, &Expectation::none()); | 1358 | let callee_ty = self.infer_expr(*callee, &Expectation::none()); |
1359 | // FIXME: so manu unnecessary clones | ||
1334 | let (param_tys, ret_ty) = match &callee_ty { | 1360 | let (param_tys, ret_ty) = match &callee_ty { |
1335 | Ty::FnPtr(sig) => (&sig.input[..], sig.output.clone()), | 1361 | Ty::FnPtr(sig) => (sig.input.clone(), sig.output.clone()), |
1362 | Ty::FnDef(func, substs) => { | ||
1363 | let fn_sig = func.signature(self.db); | ||
1364 | // TODO: get input and return types from the fn_sig. | ||
1365 | // it contains typerefs which we can make into proper tys | ||
1366 | |||
1367 | let sig = get_func_sig(self.db, *func); | ||
1368 | ( | ||
1369 | sig.input | ||
1370 | .iter() | ||
1371 | .map(|ty| ty.clone().subst(&substs)) | ||
1372 | .collect(), | ||
1373 | sig.output.clone().subst(&substs), | ||
1374 | ) | ||
1375 | } | ||
1336 | _ => { | 1376 | _ => { |
1337 | // not callable | 1377 | // not callable |
1338 | // TODO report an error? | 1378 | // TODO report an error? |
1339 | (&[][..], Ty::Unknown) | 1379 | (Vec::new(), Ty::Unknown) |
1340 | } | 1380 | } |
1341 | }; | 1381 | }; |
1342 | for (i, arg) in args.iter().enumerate() { | 1382 | for (i, arg) in args.iter().enumerate() { |
@@ -1604,15 +1644,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1604 | 1644 | ||
1605 | fn collect_fn_signature(&mut self, signature: &FnSignature) { | 1645 | fn collect_fn_signature(&mut self, signature: &FnSignature) { |
1606 | let body = Arc::clone(&self.body); // avoid borrow checker problem | 1646 | let body = Arc::clone(&self.body); // avoid borrow checker problem |
1607 | for (type_ref, pat) in signature.params().iter().zip(body.params()) { | 1647 | for (type_ref, pat) in signature.args().iter().zip(body.params()) { |
1608 | let ty = self.make_ty(type_ref); | 1648 | let ty = self.make_ty(type_ref); |
1609 | 1649 | ||
1610 | self.infer_pat(*pat, &ty); | 1650 | self.infer_pat(*pat, &ty); |
1611 | } | 1651 | } |
1612 | self.return_ty = { | 1652 | self.return_ty = self.make_ty(signature.ret_type()); |
1613 | let ty = self.make_ty(signature.ret_type()); | ||
1614 | ty | ||
1615 | }; | ||
1616 | } | 1653 | } |
1617 | 1654 | ||
1618 | fn infer_body(&mut self) { | 1655 | fn infer_body(&mut self) { |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index f74d6f5ea..40913b164 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -594,6 +594,28 @@ fn test() { | |||
594 | ); | 594 | ); |
595 | } | 595 | } |
596 | 596 | ||
597 | #[test] | ||
598 | fn infer_type_param() { | ||
599 | check_inference( | ||
600 | "generic_fn", | ||
601 | r#" | ||
602 | fn id<T>(x: T) -> T { | ||
603 | x | ||
604 | } | ||
605 | |||
606 | fn clone<T>(x: &T) -> T { | ||
607 | x | ||
608 | } | ||
609 | |||
610 | fn test() { | ||
611 | let y = 10u32; | ||
612 | id(y); | ||
613 | let x: bool = clone(z); | ||
614 | } | ||
615 | "#, | ||
616 | ); | ||
617 | } | ||
618 | |||
597 | fn infer(content: &str) -> String { | 619 | fn infer(content: &str) -> String { |
598 | let (db, _, file_id) = MockDatabase::with_single_file(content); | 620 | let (db, _, file_id) = MockDatabase::with_single_file(content); |
599 | let source_file = db.parse(file_id); | 621 | let source_file = db.parse(file_id); |
diff --git a/crates/ra_ide_api/src/completion/completion_item.rs b/crates/ra_ide_api/src/completion/completion_item.rs index b16ac2b28..6e9a68e40 100644 --- a/crates/ra_ide_api/src/completion/completion_item.rs +++ b/crates/ra_ide_api/src/completion/completion_item.rs | |||
@@ -240,7 +240,7 @@ impl Builder { | |||
240 | if ctx.use_item_syntax.is_none() && !ctx.is_call { | 240 | if ctx.use_item_syntax.is_none() && !ctx.is_call { |
241 | tested_by!(inserts_parens_for_function_calls); | 241 | tested_by!(inserts_parens_for_function_calls); |
242 | let sig = function.signature(ctx.db); | 242 | let sig = function.signature(ctx.db); |
243 | if sig.params().is_empty() || sig.has_self_param() && sig.params().len() == 1 { | 243 | if sig.args().is_empty() || sig.has_self_param() && sig.args().len() == 1 { |
244 | self.insert_text = Some(format!("{}()$0", self.label)); | 244 | self.insert_text = Some(format!("{}()$0", self.label)); |
245 | } else { | 245 | } else { |
246 | self.insert_text = Some(format!("{}($0)", self.label)); | 246 | self.insert_text = Some(format!("{}($0)", self.label)); |