diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 191104890a..e4008058cc 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -424,6 +424,10 @@ impl Function { self.id.module(db) } + pub fn name(&self, db: &impl HirDatabase) -> Name { + self.signature(db).name.clone() + } + pub fn body_syntax_mapping(&self, db: &impl HirDatabase) -> Arc { db.body_syntax_mapping(*self) } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 31ea457062..37715a903a 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -20,6 +20,7 @@ mod tests; pub(crate) mod method_resolution; use std::borrow::Cow; +use std::iter::repeat; use std::ops::Index; use std::sync::Arc; use std::{fmt, mem}; @@ -185,7 +186,7 @@ pub enum Ty { /// Structures, enumerations and unions. Adt { - /// The DefId of the struct/enum. + /// The definition of the struct/enum. def_id: AdtDef, /// The name, for displaying. name: Name, @@ -209,6 +210,27 @@ pub enum Ty { /// `&'a mut T` or `&'a T`. Ref(Arc, Mutability), + /// The anonymous type of a function declaration/definition. Each + /// function has a unique type, which is output (for a function + /// named `foo` returning an `i32`) as `fn() -> i32 {foo}`. + /// + /// For example the type of `bar` here: + /// + /// ```rust + /// fn foo() -> i32 { 1 } + /// let bar = foo; // bar: fn() -> i32 {foo} + /// ``` + FnDef { + // Function definition + def: Function, + /// For display + name: Name, + /// Parameters and return type + sig: Arc, + /// Substitutions for the generic parameters of the type + substs: Substs, + }, + /// A pointer to a function. Written as `fn() -> i32`. /// /// For example the type of `bar` here: @@ -448,12 +470,12 @@ impl Ty { } // add placeholders for args that were not provided // TODO: handle defaults - for _ in segment + let supplied_params = segment .args_and_bindings .as_ref() .map(|ga| ga.args.len()) - .unwrap_or(0)..def_generics.params.len() - { + .unwrap_or(0); + for _ in supplied_params..def_generics.params.len() { substs.push(Ty::Unknown); } assert_eq!(substs.len(), def_generics.params.len()); @@ -485,6 +507,19 @@ impl Ty { } sig_mut.output.walk_mut(f); } + Ty::FnDef { substs, sig, .. } => { + let sig_mut = Arc::make_mut(sig); + for input in &mut sig_mut.input { + input.walk_mut(f); + } + sig_mut.output.walk_mut(f); + // Without an Arc::make_mut_slice, we can't avoid the clone here: + let mut v: Vec<_> = substs.0.iter().cloned().collect(); + for t in &mut v { + t.walk_mut(f); + } + substs.0 = v.into(); + } Ty::Adt { substs, .. } => { // Without an Arc::make_mut_slice, we can't avoid the clone here: let mut v: Vec<_> = substs.0.iter().cloned().collect(); @@ -524,6 +559,12 @@ impl Ty { name, substs, }, + Ty::FnDef { def, name, sig, .. } => Ty::FnDef { + def, + name, + sig, + substs, + }, _ => self, } } @@ -551,7 +592,7 @@ impl Ty { /// or function); so if `self` is `Option`, this returns the `u32`. fn substs(&self) -> Option { match self { - Ty::Adt { substs, .. } => Some(substs.clone()), + Ty::Adt { substs, .. } | Ty::FnDef { substs, .. } => Some(substs.clone()), _ => None, } } @@ -586,6 +627,22 @@ impl fmt::Display for Ty { .to_fmt(f)?; write!(f, " -> {}", sig.output) } + Ty::FnDef { + name, substs, sig, .. + } => { + write!(f, "fn {}", name)?; + if substs.0.len() > 0 { + join(substs.0.iter()) + .surround_with("<", ">") + .separator(", ") + .to_fmt(f)?; + } + join(sig.input.iter()) + .surround_with("(", ")") + .separator(", ") + .to_fmt(f)?; + write!(f, " -> {}", sig.output) + } Ty::Adt { name, substs, .. } => { write!(f, "{}", name)?; if substs.0.len() > 0 { @@ -607,11 +664,11 @@ impl fmt::Display for Ty { /// Compute the declared type of a function. This should not need to look at the /// function body. -fn type_for_fn(db: &impl HirDatabase, f: Function) -> Ty { - let signature = f.signature(db); - let module = f.module(db); - let impl_block = f.impl_block(db); - let generics = f.generic_params(db); +fn type_for_fn(db: &impl HirDatabase, def: Function) -> Ty { + let signature = def.signature(db); + let module = def.module(db); + let impl_block = def.impl_block(db); + let generics = def.generic_params(db); let input = signature .params() .iter() @@ -624,8 +681,15 @@ fn type_for_fn(db: &impl HirDatabase, f: Function) -> Ty { &generics, signature.ret_type(), ); - let sig = FnSig { input, output }; - Ty::FnPtr(Arc::new(sig)) + let sig = Arc::new(FnSig { input, output }); + let substs = make_substs(&generics); + let name = def.name(db); + Ty::FnDef { + def, + sig, + name, + substs, + } } fn make_substs(generics: &GenericParams) -> Substs { @@ -1102,7 +1166,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { .into(); let typable = typable?; let ty = self.db.type_for_def(typable); + let generics = GenericParams::default(); + let substs = Ty::substs_from_path( + self.db, + &self.module, + self.impl_block.as_ref(), + &generics, + path, + typable, + ); + let ty = ty.apply_substs(substs); let ty = self.insert_type_vars(ty); + Some(ty) } @@ -1142,7 +1217,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let ty = self.insert_type_vars(ty.apply_substs(substs)); (ty, Some(var.into())) } - TypableDef::Enum(_) | TypableDef::Function(_) => (Ty::Unknown, None), + TypableDef::Function(_) | TypableDef::Enum(_) => (Ty::Unknown, None), } } @@ -1196,9 +1271,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Ty::Tuple(ref tuple_args) => &**tuple_args, _ => &[], }; - let expectations_iter = expectations - .into_iter() - .chain(std::iter::repeat(&Ty::Unknown)); + let expectations_iter = expectations.into_iter().chain(repeat(&Ty::Unknown)); let inner_tys = args .iter() @@ -1332,18 +1405,25 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Expr::Call { callee, args } => { let callee_ty = self.infer_expr(*callee, &Expectation::none()); let (param_tys, ret_ty) = match &callee_ty { - Ty::FnPtr(sig) => (&sig.input[..], sig.output.clone()), + Ty::FnPtr(sig) => (sig.input.clone(), sig.output.clone()), + Ty::FnDef { substs, sig, .. } => { + let ret_ty = sig.output.clone().subst(&substs); + let param_tys = sig + .input + .iter() + .map(|ty| ty.clone().subst(&substs)) + .collect(); + (param_tys, ret_ty) + } _ => { // not callable // TODO report an error? - (&[][..], Ty::Unknown) + (Vec::new(), Ty::Unknown) } }; - for (i, arg) in args.iter().enumerate() { - self.infer_expr( - *arg, - &Expectation::has_type(param_tys.get(i).cloned().unwrap_or(Ty::Unknown)), - ); + let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); + for (arg, param) in args.iter().zip(param_iter) { + self.infer_expr(*arg, &Expectation::has_type(param)); } ret_ty } @@ -1365,21 +1445,34 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty { Ty::FnPtr(sig) => { if sig.input.len() > 0 { - (&sig.input[0], &sig.input[1..], sig.output.clone()) + ( + sig.input[0].clone(), + sig.input[1..].iter().cloned().collect(), + sig.output.clone(), + ) } else { - (&Ty::Unknown, &[][..], sig.output.clone()) + (Ty::Unknown, Vec::new(), sig.output.clone()) } } - _ => (&Ty::Unknown, &[][..], Ty::Unknown), + Ty::FnDef { substs, sig, .. } => { + let ret_ty = sig.output.clone().subst(&substs); + + if sig.input.len() > 0 { + let mut arg_iter = sig.input.iter().map(|ty| ty.clone().subst(&substs)); + let receiver_ty = arg_iter.next().unwrap(); + (receiver_ty, arg_iter.collect(), ret_ty) + } else { + (Ty::Unknown, Vec::new(), ret_ty) + } + } + _ => (Ty::Unknown, Vec::new(), Ty::Unknown), }; // TODO we would have to apply the autoderef/autoref steps here // to get the correct receiver type to unify... - self.unify(expected_receiver_ty, &receiver_ty); - for (i, arg) in args.iter().enumerate() { - self.infer_expr( - *arg, - &Expectation::has_type(param_tys.get(i).cloned().unwrap_or(Ty::Unknown)), - ); + self.unify(&expected_receiver_ty, &receiver_ty); + let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); + for (arg, param) in args.iter().zip(param_iter) { + self.infer_expr(*arg, &Expectation::has_type(param)); } ret_ty } @@ -1609,10 +1702,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.infer_pat(*pat, &ty); } - self.return_ty = { - let ty = self.make_ty(signature.ret_type()); - ty - }; + self.return_ty = self.make_ty(signature.ret_type()); } fn infer_body(&mut self) { diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_backwards.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_backwards.snap index 5c74bd8c78..f5840a9347 100644 --- a/crates/ra_hir/src/ty/snapshots/tests__infer_backwards.snap +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_backwards.snap @@ -1,8 +1,8 @@ --- -created: "2019-01-22T14:44:59.880187500+00:00" -creator: insta@0.4.0 +created: "2019-01-26T17:46:03.842478456+00:00" +creator: insta@0.5.2 expression: "&result" -source: "crates\\ra_hir\\src\\ty\\tests.rs" +source: crates/ra_hir/src/ty/tests.rs --- [14; 15) 'x': u32 [22; 24) '{}': () @@ -10,7 +10,7 @@ source: "crates\\ra_hir\\src\\ty\\tests.rs" [88; 89) 'a': u32 [92; 108) 'unknow...nction': [unknown] [92; 110) 'unknow...tion()': u32 -[116; 125) 'takes_u32': fn(u32) -> () +[116; 125) 'takes_u32': fn takes_u32(u32) -> () [116; 128) 'takes_u32(a)': () [126; 127) 'a': u32 [138; 139) 'b': i32 diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_binary_op.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_binary_op.snap index c506bcadd5..b9dda2bc07 100644 --- a/crates/ra_hir/src/ty/snapshots/tests__infer_binary_op.snap +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_binary_op.snap @@ -1,8 +1,8 @@ --- -created: "2019-01-22T14:44:59.880187500+00:00" -creator: insta@0.4.0 +created: "2019-01-26T17:46:03.853259898+00:00" +creator: insta@0.5.2 expression: "&result" -source: "crates\\ra_hir\\src\\ty\\tests.rs" +source: crates/ra_hir/src/ty/tests.rs --- [6; 7) 'x': bool [22; 34) '{ 0i32 }': i32 @@ -28,7 +28,7 @@ source: "crates\\ra_hir\\src\\ty\\tests.rs" [174; 196) 'minus_...ONST_2': bool [189; 196) 'CONST_2': isize [206; 207) 'c': i32 -[210; 211) 'f': fn(bool) -> i32 +[210; 211) 'f': fn f(bool) -> i32 [210; 219) 'f(z || y)': i32 [210; 223) 'f(z || y) + 5': i32 [212; 213) 'z': bool diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_function_generics.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_function_generics.snap index 7203ad1e2a..8ff6e55a6b 100644 --- a/crates/ra_hir/src/ty/snapshots/tests__infer_function_generics.snap +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_function_generics.snap @@ -1,21 +1,21 @@ --- -created: "2019-01-22T14:44:59.954958500+00:00" -creator: insta@0.4.0 +created: "2019-01-26T18:16:16.530712344+00:00" +creator: insta@0.5.2 expression: "&result" -source: "crates\\ra_hir\\src\\ty\\tests.rs" +source: crates/ra_hir/src/ty/tests.rs --- [10; 11) 't': [unknown] [21; 26) '{ t }': [unknown] [23; 24) 't': [unknown] [38; 98) '{ ...(1); }': () -[44; 46) 'id': fn(T) -> T -[44; 52) 'id(1u32)': T +[44; 46) 'id': fn id(T) -> T +[44; 52) 'id(1u32)': u32 [47; 51) '1u32': u32 -[58; 68) 'id::': fn(T) -> T -[58; 71) 'id::(1)': T -[69; 70) '1': T -[81; 82) 'x': T -[90; 92) 'id': fn(T) -> T -[90; 95) 'id(1)': T -[93; 94) '1': T +[58; 68) 'id::': fn id(T) -> T +[58; 71) 'id::(1)': i128 +[69; 70) '1': i128 +[81; 82) 'x': u64 +[90; 92) 'id': fn id(T) -> T +[90; 95) 'id(1)': u64 +[93; 94) '1': u64 diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_generic_chain.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_generic_chain.snap index 4435180a10..f21bffa754 100644 --- a/crates/ra_hir/src/ty/snapshots/tests__infer_generic_chain.snap +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_generic_chain.snap @@ -1,8 +1,8 @@ --- -created: "2019-01-22T14:44:59.961936900+00:00" -creator: insta@0.4.0 +created: "2019-01-26T17:46:03.866825843+00:00" +creator: insta@0.5.2 expression: "&result" -source: "crates\\ra_hir\\src\\ty\\tests.rs" +source: crates/ra_hir/src/ty/tests.rs --- [53; 57) 'self': A<[unknown]> [65; 87) '{ ... }': [unknown] @@ -12,25 +12,25 @@ source: "crates\\ra_hir\\src\\ty\\tests.rs" [110; 115) '{ t }': [unknown] [112; 113) 't': [unknown] [135; 261) '{ ....x() }': i128 -[146; 147) 'x': T -[150; 151) '1': T -[162; 163) 'y': T -[166; 168) 'id': fn(T) -> T -[166; 171) 'id(x)': T -[169; 170) 'x': T -[182; 183) 'a': A -[186; 200) 'A { x: id(y) }': A -[193; 195) 'id': fn(T) -> T -[193; 198) 'id(y)': T -[196; 197) 'y': T -[211; 212) 'z': T -[215; 217) 'id': fn(T) -> T -[215; 222) 'id(a.x)': T -[218; 219) 'a': A -[218; 221) 'a.x': T -[233; 234) 'b': A -[237; 247) 'A { x: z }': A -[244; 245) 'z': T -[254; 255) 'b': A +[146; 147) 'x': i32 +[150; 151) '1': i32 +[162; 163) 'y': i32 +[166; 168) 'id': fn id(T) -> T +[166; 171) 'id(x)': i32 +[169; 170) 'x': i32 +[182; 183) 'a': A +[186; 200) 'A { x: id(y) }': A +[193; 195) 'id': fn id(T) -> T +[193; 198) 'id(y)': i32 +[196; 197) 'y': i32 +[211; 212) 'z': i32 +[215; 217) 'id': fn id(T) -> T +[215; 222) 'id(a.x)': i32 +[218; 219) 'a': A +[218; 221) 'a.x': i32 +[233; 234) 'b': A +[237; 247) 'A { x: z }': A +[244; 245) 'z': i32 +[254; 255) 'b': A [254; 259) 'b.x()': i128 diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_paths.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_paths.snap index 0bceffaef6..afbe2f747a 100644 --- a/crates/ra_hir/src/ty/snapshots/tests__infer_paths.snap +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_paths.snap @@ -1,16 +1,16 @@ --- -created: "2019-01-22T14:44:59.975899500+00:00" -creator: insta@0.4.0 +created: "2019-01-26T17:46:03.928773630+00:00" +creator: insta@0.5.2 expression: "&result" -source: "crates\\ra_hir\\src\\ty\\tests.rs" +source: crates/ra_hir/src/ty/tests.rs --- [15; 20) '{ 1 }': u32 [17; 18) '1': u32 [48; 53) '{ 1 }': u32 [50; 51) '1': u32 [67; 91) '{ ...c(); }': () -[73; 74) 'a': fn() -> u32 +[73; 74) 'a': fn a() -> u32 [73; 76) 'a()': u32 -[82; 86) 'b::c': fn() -> u32 +[82; 86) 'b::c': fn c() -> u32 [82; 88) 'b::c()': u32 diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_type_param.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_type_param.snap new file mode 100644 index 0000000000..a993232642 --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_type_param.snap @@ -0,0 +1,26 @@ +--- +created: "2019-01-27T16:54:18.368427685+00:00" +creator: insta@0.5.2 +expression: "&result" +source: crates/ra_hir/src/ty/tests.rs +--- +[10; 11) 'x': [unknown] +[21; 30) '{ x }': [unknown] +[27; 28) 'x': [unknown] +[44; 45) 'x': &[unknown] +[56; 65) '{ x }': &[unknown] +[62; 63) 'x': &[unknown] +[77; 157) '{ ...(1); }': () +[87; 88) 'y': u32 +[91; 96) '10u32': u32 +[102; 104) 'id': fn id(T) -> T +[102; 107) 'id(y)': u32 +[105; 106) 'y': u32 +[117; 118) 'x': bool +[127; 132) 'clone': fn clone(&T) -> T +[127; 135) 'clone(z)': bool +[133; 134) 'z': &bool +[141; 151) 'id::': fn id(T) -> T +[141; 154) 'id::(1)': i128 +[152; 153) '1': i128 + diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index f74d6f5ea3..ac12d974b8 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -594,6 +594,29 @@ fn test() { ); } +#[test] +fn infer_type_param() { + check_inference( + "infer_type_param", + r#" +fn id(x: T) -> T { + x +} + +fn clone(x: &T) -> T { + x +} + +fn test() { + let y = 10u32; + id(y); + let x: bool = clone(z); + id::(1); +} +"#, + ); +} + fn infer(content: &str) -> String { let (db, _, file_id) = MockDatabase::with_single_file(content); let source_file = db.parse(file_id);