661: Implement type generics for functions r=flodiebold a=marcusklaas

Pretty printing of function types isn't as nice any more since the current implementation does not store its argument types directly. We could store some more information to print more information on the argument and return types, at a cost of course.

Co-authored-by: Marcus Klaas de Vries <mail@marcusklaas.nl>
This commit is contained in:
bors[bot] 2019-01-27 18:00:08 +00:00
commit 19f77603c0
9 changed files with 227 additions and 84 deletions

View file

@ -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<BodySyntaxMapping> {
db.body_syntax_mapping(*self)
}

View file

@ -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<Ty>, 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<FnSig>,
/// 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<u32>`, this returns the `u32`.
fn substs(&self) -> Option<Substs> {
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) {

View file

@ -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

View file

@ -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

View file

@ -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<u32>(T) -> T
[44; 52) 'id(1u32)': u32
[47; 51) '1u32': u32
[58; 68) 'id::<i128>': fn(T) -> T
[58; 71) 'id::<i128>(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::<i128>': fn id<i128>(T) -> T
[58; 71) 'id::<i128>(1)': i128
[69; 70) '1': i128
[81; 82) 'x': u64
[90; 92) 'id': fn id<u64>(T) -> T
[90; 95) 'id(1)': u64
[93; 94) '1': u64

View file

@ -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<T>
[186; 200) 'A { x: id(y) }': A<T>
[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<T>
[218; 221) 'a.x': T
[233; 234) 'b': A<T>
[237; 247) 'A { x: z }': A<T>
[244; 245) 'z': T
[254; 255) 'b': A<T>
[146; 147) 'x': i32
[150; 151) '1': i32
[162; 163) 'y': i32
[166; 168) 'id': fn id<i32>(T) -> T
[166; 171) 'id(x)': i32
[169; 170) 'x': i32
[182; 183) 'a': A<i32>
[186; 200) 'A { x: id(y) }': A<i32>
[193; 195) 'id': fn id<i32>(T) -> T
[193; 198) 'id(y)': i32
[196; 197) 'y': i32
[211; 212) 'z': i32
[215; 217) 'id': fn id<i32>(T) -> T
[215; 222) 'id(a.x)': i32
[218; 219) 'a': A<i32>
[218; 221) 'a.x': i32
[233; 234) 'b': A<i32>
[237; 247) 'A { x: z }': A<i32>
[244; 245) 'z': i32
[254; 255) 'b': A<i32>
[254; 259) 'b.x()': i128

View file

@ -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

View file

@ -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<u32>(T) -> T
[102; 107) 'id(y)': u32
[105; 106) 'y': u32
[117; 118) 'x': bool
[127; 132) 'clone': fn clone<bool>(&T) -> T
[127; 135) 'clone(z)': bool
[133; 134) 'z': &bool
[141; 151) 'id::<i128>': fn id<i128>(T) -> T
[141; 154) 'id::<i128>(1)': i128
[152; 153) '1': i128

View file

@ -594,6 +594,29 @@ fn test() {
);
}
#[test]
fn infer_type_param() {
check_inference(
"infer_type_param",
r#"
fn id<T>(x: T) -> T {
x
}
fn clone<T>(x: &T) -> T {
x
}
fn test() {
let y = 10u32;
id(y);
let x: bool = clone(z);
id::<i128>(1);
}
"#,
);
}
fn infer(content: &str) -> String {
let (db, _, file_id) = MockDatabase::with_single_file(content);
let source_file = db.parse(file_id);