Repository: rust-analyzer/rust-analyzer
Pull request #661 (2019-01-25 23:37:04)

Implement type generics for functions
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.

marcusklaas: Haha, woops. That's not right..

You must be the most patient person on GitHub Florian. Thank you once again for the review. I will make it right.


On 26 Jan 2019, 01:40, at 01:40, Florian Diebold <notifications@github.com> wrote:
>flodiebold commented on this pull request.
>
>
>
>>                  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 { def, substs, .. } => {
>+                        let fn_sig = def.signature(self.db);
>+                        let ret_ty = self
>+                            .make_ty(fn_sig.ret_type(),
>fn_sig.generics())
>
>(by reusing `make_ty`, this actually currently resolves the function
>parameter in the module of the function we're inferring right now...)
>
>-- 
>You are receiving this because you authored the thread.
>Reply to this email directly or view it on GitHub:
>https://github.com/rust-analyzer/rust-analyzer/pull/661#discussion_r251177144

marcusklaas: updated

Patch:
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<BodySyntaxMapping> {
         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<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) {


Test patch:
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<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
 
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<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
 
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<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
+
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<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);