First attempt at generic type inference for fns

This commit is contained in:
Marcus Klaas de Vries 2019-01-25 21:16:02 +01:00
parent 3f4f50baaa
commit 3bd47c0285
6 changed files with 81 additions and 21 deletions

View file

@ -388,7 +388,7 @@ pub use crate::code_model_impl::function::ScopeEntryWithSyntax;
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct FnSignature { pub struct FnSignature {
pub(crate) name: Name, pub(crate) name: Name,
pub(crate) params: Vec<TypeRef>, pub(crate) args: Vec<TypeRef>,
pub(crate) ret_type: TypeRef, pub(crate) ret_type: TypeRef,
/// True if the first param is `self`. This is relevant to decide whether this /// True if the first param is `self`. This is relevant to decide whether this
/// can be called as a method. /// can be called as a method.
@ -400,8 +400,8 @@ impl FnSignature {
&self.name &self.name
} }
pub fn params(&self) -> &[TypeRef] { pub fn args(&self) -> &[TypeRef] {
&self.params &self.args
} }
pub fn ret_type(&self) -> &TypeRef { pub fn ret_type(&self) -> &TypeRef {

View file

@ -32,7 +32,7 @@ impl FnSignature {
.name() .name()
.map(|n| n.as_name()) .map(|n| n.as_name())
.unwrap_or_else(Name::missing); .unwrap_or_else(Name::missing);
let mut params = Vec::new(); let mut args = Vec::new();
let mut has_self_param = false; let mut has_self_param = false;
if let Some(param_list) = node.param_list() { if let Some(param_list) = node.param_list() {
if let Some(self_param) = param_list.self_param() { if let Some(self_param) = param_list.self_param() {
@ -50,12 +50,12 @@ impl FnSignature {
} }
} }
}; };
params.push(self_type); args.push(self_type);
has_self_param = true; has_self_param = true;
} }
for param in param_list.params() { for param in param_list.params() {
let type_ref = TypeRef::from_ast_opt(param.type_ref()); let type_ref = TypeRef::from_ast_opt(param.type_ref());
params.push(type_ref); args.push(type_ref);
} }
} }
let ret_type = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) { let ret_type = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) {
@ -66,7 +66,7 @@ impl FnSignature {
let sig = FnSignature { let sig = FnSignature {
name, name,
params, args,
ret_type, ret_type,
has_self_param, has_self_param,
}; };

View file

@ -49,7 +49,8 @@ impl GenericParams {
Arc::new(generics) Arc::new(generics)
} }
fn fill(&mut self, node: &impl TypeParamsOwner) { // FIXME: probably shouldnt be pub(crate)
pub(crate) fn fill(&mut self, node: &impl TypeParamsOwner) {
if let Some(params) = node.type_param_list() { if let Some(params) = node.type_param_list() {
self.fill_params(params) self.fill_params(params)
} }

View file

@ -209,6 +209,18 @@ 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, 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:
@ -485,7 +497,7 @@ impl Ty {
} }
sig_mut.output.walk_mut(f); sig_mut.output.walk_mut(f);
} }
Ty::Adt { substs, .. } => { Ty::FnDef(_, 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();
for t in &mut v { for t in &mut v {
@ -524,6 +536,7 @@ impl Ty {
name, name,
substs, substs,
}, },
Ty::FnDef(func, _) => Ty::FnDef(func, substs),
_ => self, _ => self,
} }
} }
@ -579,6 +592,7 @@ impl fmt::Display for Ty {
.to_fmt(f) .to_fmt(f)
} }
} }
Ty::FnDef(_func, _substs) => write!(f, "FNDEF-IMPLEMENT-ME"),
Ty::FnPtr(sig) => { Ty::FnPtr(sig) => {
join(sig.input.iter()) join(sig.input.iter())
.surround_with("fn(", ")") .surround_with("fn(", ")")
@ -608,12 +622,18 @@ 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, f: Function) -> Ty {
let generics = f.generic_params(db);
let substs = make_substs(&generics);
Ty::FnDef(f.into(), substs)
}
fn get_func_sig(db: &impl HirDatabase, f: Function) -> FnSig {
let signature = f.signature(db); let signature = f.signature(db);
let module = f.module(db); let module = f.module(db);
let impl_block = f.impl_block(db); let impl_block = f.impl_block(db);
let generics = f.generic_params(db); let generics = f.generic_params(db);
let input = signature let input = signature
.params() .args()
.iter() .iter()
.map(|tr| Ty::from_hir(db, &module, impl_block.as_ref(), &generics, tr)) .map(|tr| Ty::from_hir(db, &module, impl_block.as_ref(), &generics, tr))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -624,8 +644,7 @@ fn type_for_fn(db: &impl HirDatabase, f: Function) -> Ty {
&generics, &generics,
signature.ret_type(), signature.ret_type(),
); );
let sig = FnSig { input, output }; FnSig { input, output }
Ty::FnPtr(Arc::new(sig))
} }
fn make_substs(generics: &GenericParams) -> Substs { fn make_substs(generics: &GenericParams) -> Substs {
@ -1142,7 +1161,13 @@ 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(func) => {
let ty = type_for_fn(self.db, func);
let ty = self.insert_type_vars(ty.apply_substs(substs));
// FIXME: is this right?
(ty, None)
}
TypableDef::Enum(_) => (Ty::Unknown, None),
} }
} }
@ -1331,12 +1356,27 @@ 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());
// FIXME: so manu unnecessary clones
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(func, substs) => {
let fn_sig = func.signature(self.db);
// TODO: get input and return types from the fn_sig.
// it contains typerefs which we can make into proper tys
let sig = get_func_sig(self.db, *func);
(
sig.input
.iter()
.map(|ty| ty.clone().subst(&substs))
.collect(),
sig.output.clone().subst(&substs),
)
}
_ => { _ => {
// 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() { for (i, arg) in args.iter().enumerate() {
@ -1604,15 +1644,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
fn collect_fn_signature(&mut self, signature: &FnSignature) { fn collect_fn_signature(&mut self, signature: &FnSignature) {
let body = Arc::clone(&self.body); // avoid borrow checker problem let body = Arc::clone(&self.body); // avoid borrow checker problem
for (type_ref, pat) in signature.params().iter().zip(body.params()) { for (type_ref, pat) in signature.args().iter().zip(body.params()) {
let ty = self.make_ty(type_ref); let ty = self.make_ty(type_ref);
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

@ -594,6 +594,28 @@ fn test() {
); );
} }
#[test]
fn infer_type_param() {
check_inference(
"generic_fn",
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);
}
"#,
);
}
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);

View file

@ -240,7 +240,7 @@ impl Builder {
if ctx.use_item_syntax.is_none() && !ctx.is_call { if ctx.use_item_syntax.is_none() && !ctx.is_call {
tested_by!(inserts_parens_for_function_calls); tested_by!(inserts_parens_for_function_calls);
let sig = function.signature(ctx.db); let sig = function.signature(ctx.db);
if sig.params().is_empty() || sig.has_self_param() && sig.params().len() == 1 { if sig.args().is_empty() || sig.has_self_param() && sig.args().len() == 1 {
self.insert_text = Some(format!("{}()$0", self.label)); self.insert_text = Some(format!("{}()$0", self.label));
} else { } else {
self.insert_text = Some(format!("{}($0)", self.label)); self.insert_text = Some(format!("{}($0)", self.label));