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) 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> { pub fn body_syntax_mapping(&self, db: &impl HirDatabase) -> Arc<BodySyntaxMapping> {
db.body_syntax_mapping(*self) db.body_syntax_mapping(*self)
} }

View file

@ -20,6 +20,7 @@ mod tests;
pub(crate) mod method_resolution; pub(crate) mod method_resolution;
use std::borrow::Cow; use std::borrow::Cow;
use std::iter::repeat;
use std::ops::Index; use std::ops::Index;
use std::sync::Arc; use std::sync::Arc;
use std::{fmt, mem}; use std::{fmt, mem};
@ -185,7 +186,7 @@ pub enum Ty {
/// Structures, enumerations and unions. /// Structures, enumerations and unions.
Adt { Adt {
/// The DefId of the struct/enum. /// The definition of the struct/enum.
def_id: AdtDef, def_id: AdtDef,
/// The name, for displaying. /// The name, for displaying.
name: Name, name: Name,
@ -209,6 +210,27 @@ pub enum Ty {
/// `&'a mut T` or `&'a T`. /// `&'a mut T` or `&'a T`.
Ref(Arc<Ty>, Mutability), 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`. /// A pointer to a function. Written as `fn() -> i32`.
/// ///
/// For example the type of `bar` here: /// For example the type of `bar` here:
@ -448,12 +470,12 @@ impl Ty {
} }
// add placeholders for args that were not provided // add placeholders for args that were not provided
// TODO: handle defaults // TODO: handle defaults
for _ in segment let supplied_params = segment
.args_and_bindings .args_and_bindings
.as_ref() .as_ref()
.map(|ga| ga.args.len()) .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); substs.push(Ty::Unknown);
} }
assert_eq!(substs.len(), def_generics.params.len()); assert_eq!(substs.len(), def_generics.params.len());
@ -485,6 +507,19 @@ impl Ty {
} }
sig_mut.output.walk_mut(f); 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, .. } => { Ty::Adt { substs, .. } => {
// Without an Arc::make_mut_slice, we can't avoid the clone here: // Without an Arc::make_mut_slice, we can't avoid the clone here:
let mut v: Vec<_> = substs.0.iter().cloned().collect(); let mut v: Vec<_> = substs.0.iter().cloned().collect();
@ -524,6 +559,12 @@ impl Ty {
name, name,
substs, substs,
}, },
Ty::FnDef { def, name, sig, .. } => Ty::FnDef {
def,
name,
sig,
substs,
},
_ => self, _ => self,
} }
} }
@ -551,7 +592,7 @@ impl Ty {
/// or function); so if `self` is `Option<u32>`, this returns the `u32`. /// or function); so if `self` is `Option<u32>`, this returns the `u32`.
fn substs(&self) -> Option<Substs> { fn substs(&self) -> Option<Substs> {
match self { match self {
Ty::Adt { substs, .. } => Some(substs.clone()), Ty::Adt { substs, .. } | Ty::FnDef { substs, .. } => Some(substs.clone()),
_ => None, _ => None,
} }
} }
@ -586,6 +627,22 @@ impl fmt::Display for Ty {
.to_fmt(f)?; .to_fmt(f)?;
write!(f, " -> {}", sig.output) 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, .. } => { Ty::Adt { name, substs, .. } => {
write!(f, "{}", name)?; write!(f, "{}", name)?;
if substs.0.len() > 0 { 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 /// Compute the declared type of a function. This should not need to look at the
/// function body. /// function body.
fn type_for_fn(db: &impl HirDatabase, f: Function) -> Ty { fn type_for_fn(db: &impl HirDatabase, def: Function) -> Ty {
let signature = f.signature(db); let signature = def.signature(db);
let module = f.module(db); let module = def.module(db);
let impl_block = f.impl_block(db); let impl_block = def.impl_block(db);
let generics = f.generic_params(db); let generics = def.generic_params(db);
let input = signature let input = signature
.params() .params()
.iter() .iter()
@ -624,8 +681,15 @@ fn type_for_fn(db: &impl HirDatabase, f: Function) -> Ty {
&generics, &generics,
signature.ret_type(), signature.ret_type(),
); );
let sig = FnSig { input, output }; let sig = Arc::new(FnSig { input, output });
Ty::FnPtr(Arc::new(sig)) let substs = make_substs(&generics);
let name = def.name(db);
Ty::FnDef {
def,
sig,
name,
substs,
}
} }
fn make_substs(generics: &GenericParams) -> Substs { fn make_substs(generics: &GenericParams) -> Substs {
@ -1102,7 +1166,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
.into(); .into();
let typable = typable?; let typable = typable?;
let ty = self.db.type_for_def(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); let ty = self.insert_type_vars(ty);
Some(ty) Some(ty)
} }
@ -1142,7 +1217,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
let ty = self.insert_type_vars(ty.apply_substs(substs)); let ty = self.insert_type_vars(ty.apply_substs(substs));
(ty, Some(var.into())) (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, Ty::Tuple(ref tuple_args) => &**tuple_args,
_ => &[], _ => &[],
}; };
let expectations_iter = expectations let expectations_iter = expectations.into_iter().chain(repeat(&Ty::Unknown));
.into_iter()
.chain(std::iter::repeat(&Ty::Unknown));
let inner_tys = args let inner_tys = args
.iter() .iter()
@ -1332,18 +1405,25 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
Expr::Call { callee, args } => { Expr::Call { callee, args } => {
let callee_ty = self.infer_expr(*callee, &Expectation::none()); let callee_ty = self.infer_expr(*callee, &Expectation::none());
let (param_tys, ret_ty) = match &callee_ty { 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 // not callable
// TODO report an error? // TODO report an error?
(&[][..], Ty::Unknown) (Vec::new(), Ty::Unknown)
} }
}; };
for (i, arg) in args.iter().enumerate() { let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown));
self.infer_expr( for (arg, param) in args.iter().zip(param_iter) {
*arg, self.infer_expr(*arg, &Expectation::has_type(param));
&Expectation::has_type(param_tys.get(i).cloned().unwrap_or(Ty::Unknown)),
);
} }
ret_ty ret_ty
} }
@ -1365,21 +1445,34 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty { let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty {
Ty::FnPtr(sig) => { Ty::FnPtr(sig) => {
if sig.input.len() > 0 { 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 { } 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 // TODO we would have to apply the autoderef/autoref steps here
// to get the correct receiver type to unify... // to get the correct receiver type to unify...
self.unify(expected_receiver_ty, &receiver_ty); self.unify(&expected_receiver_ty, &receiver_ty);
for (i, arg) in args.iter().enumerate() { let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown));
self.infer_expr( for (arg, param) in args.iter().zip(param_iter) {
*arg, self.infer_expr(*arg, &Expectation::has_type(param));
&Expectation::has_type(param_tys.get(i).cloned().unwrap_or(Ty::Unknown)),
);
} }
ret_ty ret_ty
} }
@ -1609,10 +1702,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
self.infer_pat(*pat, &ty); self.infer_pat(*pat, &ty);
} }
self.return_ty = { self.return_ty = self.make_ty(signature.ret_type());
let ty = self.make_ty(signature.ret_type());
ty
};
} }
fn infer_body(&mut self) { fn infer_body(&mut self) {

View file

@ -1,8 +1,8 @@
--- ---
created: "2019-01-22T14:44:59.880187500+00:00" created: "2019-01-26T17:46:03.842478456+00:00"
creator: insta@0.4.0 creator: insta@0.5.2
expression: "&result" expression: "&result"
source: "crates\\ra_hir\\src\\ty\\tests.rs" source: crates/ra_hir/src/ty/tests.rs
--- ---
[14; 15) 'x': u32 [14; 15) 'x': u32
[22; 24) '{}': () [22; 24) '{}': ()
@ -10,7 +10,7 @@ source: "crates\\ra_hir\\src\\ty\\tests.rs"
[88; 89) 'a': u32 [88; 89) 'a': u32
[92; 108) 'unknow...nction': [unknown] [92; 108) 'unknow...nction': [unknown]
[92; 110) 'unknow...tion()': u32 [92; 110) 'unknow...tion()': u32
[116; 125) 'takes_u32': fn(u32) -> () [116; 125) 'takes_u32': fn takes_u32(u32) -> ()
[116; 128) 'takes_u32(a)': () [116; 128) 'takes_u32(a)': ()
[126; 127) 'a': u32 [126; 127) 'a': u32
[138; 139) 'b': i32 [138; 139) 'b': i32

View file

@ -1,8 +1,8 @@
--- ---
created: "2019-01-22T14:44:59.880187500+00:00" created: "2019-01-26T17:46:03.853259898+00:00"
creator: insta@0.4.0 creator: insta@0.5.2
expression: "&result" expression: "&result"
source: "crates\\ra_hir\\src\\ty\\tests.rs" source: crates/ra_hir/src/ty/tests.rs
--- ---
[6; 7) 'x': bool [6; 7) 'x': bool
[22; 34) '{ 0i32 }': i32 [22; 34) '{ 0i32 }': i32
@ -28,7 +28,7 @@ source: "crates\\ra_hir\\src\\ty\\tests.rs"
[174; 196) 'minus_...ONST_2': bool [174; 196) 'minus_...ONST_2': bool
[189; 196) 'CONST_2': isize [189; 196) 'CONST_2': isize
[206; 207) 'c': i32 [206; 207) 'c': i32
[210; 211) 'f': fn(bool) -> i32 [210; 211) 'f': fn f(bool) -> i32
[210; 219) 'f(z || y)': i32 [210; 219) 'f(z || y)': i32
[210; 223) 'f(z || y) + 5': i32 [210; 223) 'f(z || y) + 5': i32
[212; 213) 'z': bool [212; 213) 'z': bool

View file

@ -1,21 +1,21 @@
--- ---
created: "2019-01-22T14:44:59.954958500+00:00" created: "2019-01-26T18:16:16.530712344+00:00"
creator: insta@0.4.0 creator: insta@0.5.2
expression: "&result" expression: "&result"
source: "crates\\ra_hir\\src\\ty\\tests.rs" source: crates/ra_hir/src/ty/tests.rs
--- ---
[10; 11) 't': [unknown] [10; 11) 't': [unknown]
[21; 26) '{ t }': [unknown] [21; 26) '{ t }': [unknown]
[23; 24) 't': [unknown] [23; 24) 't': [unknown]
[38; 98) '{ ...(1); }': () [38; 98) '{ ...(1); }': ()
[44; 46) 'id': fn(T) -> T [44; 46) 'id': fn id<u32>(T) -> T
[44; 52) 'id(1u32)': T [44; 52) 'id(1u32)': u32
[47; 51) '1u32': u32 [47; 51) '1u32': u32
[58; 68) 'id::<i128>': fn(T) -> T [58; 68) 'id::<i128>': fn id<i128>(T) -> T
[58; 71) 'id::<i128>(1)': T [58; 71) 'id::<i128>(1)': i128
[69; 70) '1': T [69; 70) '1': i128
[81; 82) 'x': T [81; 82) 'x': u64
[90; 92) 'id': fn(T) -> T [90; 92) 'id': fn id<u64>(T) -> T
[90; 95) 'id(1)': T [90; 95) 'id(1)': u64
[93; 94) '1': T [93; 94) '1': u64

View file

@ -1,8 +1,8 @@
--- ---
created: "2019-01-22T14:44:59.961936900+00:00" created: "2019-01-26T17:46:03.866825843+00:00"
creator: insta@0.4.0 creator: insta@0.5.2
expression: "&result" expression: "&result"
source: "crates\\ra_hir\\src\\ty\\tests.rs" source: crates/ra_hir/src/ty/tests.rs
--- ---
[53; 57) 'self': A<[unknown]> [53; 57) 'self': A<[unknown]>
[65; 87) '{ ... }': [unknown] [65; 87) '{ ... }': [unknown]
@ -12,25 +12,25 @@ source: "crates\\ra_hir\\src\\ty\\tests.rs"
[110; 115) '{ t }': [unknown] [110; 115) '{ t }': [unknown]
[112; 113) 't': [unknown] [112; 113) 't': [unknown]
[135; 261) '{ ....x() }': i128 [135; 261) '{ ....x() }': i128
[146; 147) 'x': T [146; 147) 'x': i32
[150; 151) '1': T [150; 151) '1': i32
[162; 163) 'y': T [162; 163) 'y': i32
[166; 168) 'id': fn(T) -> T [166; 168) 'id': fn id<i32>(T) -> T
[166; 171) 'id(x)': T [166; 171) 'id(x)': i32
[169; 170) 'x': T [169; 170) 'x': i32
[182; 183) 'a': A<T> [182; 183) 'a': A<i32>
[186; 200) 'A { x: id(y) }': A<T> [186; 200) 'A { x: id(y) }': A<i32>
[193; 195) 'id': fn(T) -> T [193; 195) 'id': fn id<i32>(T) -> T
[193; 198) 'id(y)': T [193; 198) 'id(y)': i32
[196; 197) 'y': T [196; 197) 'y': i32
[211; 212) 'z': T [211; 212) 'z': i32
[215; 217) 'id': fn(T) -> T [215; 217) 'id': fn id<i32>(T) -> T
[215; 222) 'id(a.x)': T [215; 222) 'id(a.x)': i32
[218; 219) 'a': A<T> [218; 219) 'a': A<i32>
[218; 221) 'a.x': T [218; 221) 'a.x': i32
[233; 234) 'b': A<T> [233; 234) 'b': A<i32>
[237; 247) 'A { x: z }': A<T> [237; 247) 'A { x: z }': A<i32>
[244; 245) 'z': T [244; 245) 'z': i32
[254; 255) 'b': A<T> [254; 255) 'b': A<i32>
[254; 259) 'b.x()': i128 [254; 259) 'b.x()': i128

View file

@ -1,16 +1,16 @@
--- ---
created: "2019-01-22T14:44:59.975899500+00:00" created: "2019-01-26T17:46:03.928773630+00:00"
creator: insta@0.4.0 creator: insta@0.5.2
expression: "&result" expression: "&result"
source: "crates\\ra_hir\\src\\ty\\tests.rs" source: crates/ra_hir/src/ty/tests.rs
--- ---
[15; 20) '{ 1 }': u32 [15; 20) '{ 1 }': u32
[17; 18) '1': u32 [17; 18) '1': u32
[48; 53) '{ 1 }': u32 [48; 53) '{ 1 }': u32
[50; 51) '1': u32 [50; 51) '1': u32
[67; 91) '{ ...c(); }': () [67; 91) '{ ...c(); }': ()
[73; 74) 'a': fn() -> u32 [73; 74) 'a': fn a() -> u32
[73; 76) 'a()': u32 [73; 76) 'a()': u32
[82; 86) 'b::c': fn() -> u32 [82; 86) 'b::c': fn c() -> u32
[82; 88) 'b::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 { fn infer(content: &str) -> String {
let (db, _, file_id) = MockDatabase::with_single_file(content); let (db, _, file_id) = MockDatabase::with_single_file(content);
let source_file = db.parse(file_id); let source_file = db.parse(file_id);