mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-10 15:14:32 +00:00
Merge #661
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:
commit
19f77603c0
9 changed files with 227 additions and 84 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
26
crates/ra_hir/src/ty/snapshots/tests__infer_type_param.snap
Normal file
26
crates/ra_hir/src/ty/snapshots/tests__infer_type_param.snap
Normal 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
|
||||
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue