diff options
author | Marcus Klaas de Vries <[email protected]> | 2019-01-16 19:26:58 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-01-19 12:37:25 +0000 |
commit | 3340807bd24f398dca158e85eebae74012d8ef4b (patch) | |
tree | d0f94210a723ddbdcdfa9c64e66d59c68b52caca /crates | |
parent | ab5deb78117693d776723bc0144e7b34e6f782d1 (diff) |
Get basic struct pattern type inference working!
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/expr.rs | 39 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 143 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests/data/adt_pattern.txt | 23 |
3 files changed, 124 insertions, 81 deletions
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index c6d442ec4..893bad9cd 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs | |||
@@ -331,8 +331,8 @@ impl_arena_id!(PatId); | |||
331 | 331 | ||
332 | #[derive(Debug, Clone, Eq, PartialEq)] | 332 | #[derive(Debug, Clone, Eq, PartialEq)] |
333 | pub struct FieldPat { | 333 | pub struct FieldPat { |
334 | name: Name, | 334 | pub(crate) name: Name, |
335 | pat: Option<PatId>, | 335 | pub(crate) pat: Option<PatId>, |
336 | } | 336 | } |
337 | 337 | ||
338 | /// Close relative to rustc's hir::PatKind | 338 | /// Close relative to rustc's hir::PatKind |
@@ -392,7 +392,9 @@ impl Pat { | |||
392 | let total_iter = prefix.iter().chain(rest.iter()).chain(suffix.iter()); | 392 | let total_iter = prefix.iter().chain(rest.iter()).chain(suffix.iter()); |
393 | total_iter.map(|pat| *pat).for_each(f); | 393 | total_iter.map(|pat| *pat).for_each(f); |
394 | } | 394 | } |
395 | Pat::Struct { .. } => {} // TODO | 395 | Pat::Struct { args, .. } => { |
396 | args.iter().filter_map(|a| a.pat).for_each(f); | ||
397 | } | ||
396 | } | 398 | } |
397 | } | 399 | } |
398 | } | 400 | } |
@@ -814,23 +816,20 @@ impl ExprCollector { | |||
814 | ast::PatKind::PlaceholderPat(_) => Pat::Wild, | 816 | ast::PatKind::PlaceholderPat(_) => Pat::Wild, |
815 | ast::PatKind::StructPat(p) => { | 817 | ast::PatKind::StructPat(p) => { |
816 | let path = p.path().and_then(Path::from_ast); | 818 | let path = p.path().and_then(Path::from_ast); |
817 | 819 | let fields = p | |
818 | if let Some(field_list) = p.field_pat_list() { | 820 | .field_pat_list() |
819 | let fields = field_list | 821 | .expect("every struct should have a field list") |
820 | .field_pats() | 822 | .field_pats() |
821 | .into_iter() | 823 | .into_iter() |
822 | .map(|f| FieldPat { | 824 | .map(|f| FieldPat { |
823 | name: Name::new(f.ident), | 825 | name: Name::new(f.ident), |
824 | pat: f.pat.as_ref().map(|p| self.collect_pat(p)), | 826 | pat: f.pat.as_ref().map(|p| self.collect_pat(p)), |
825 | }) | 827 | }) |
826 | .collect(); | 828 | .collect(); |
827 | 829 | ||
828 | Pat::Struct { | 830 | Pat::Struct { |
829 | path: path, | 831 | path: path, |
830 | args: fields, | 832 | args: fields, |
831 | } | ||
832 | } else { | ||
833 | Pat::Missing | ||
834 | } | 833 | } |
835 | } | 834 | } |
836 | 835 | ||
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 3e1a4f02e..6bad61a2a 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -36,7 +36,7 @@ use crate::{ | |||
36 | db::HirDatabase, | 36 | db::HirDatabase, |
37 | type_ref::{TypeRef, Mutability}, | 37 | type_ref::{TypeRef, Mutability}, |
38 | name::KnownName, | 38 | name::KnownName, |
39 | expr::{Body, Expr, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement}, | 39 | expr::{Body, Expr, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat}, |
40 | }; | 40 | }; |
41 | 41 | ||
42 | /// The ID of a type variable. | 42 | /// The ID of a type variable. |
@@ -872,6 +872,90 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
872 | } | 872 | } |
873 | } | 873 | } |
874 | 874 | ||
875 | fn resolve_fields(&self, path: Option<&Path>) -> Option<(Ty, Vec<crate::adt::StructField>)> { | ||
876 | let def = path | ||
877 | .and_then(|path| self.module.resolve_path(self.db, &path).take_types()) | ||
878 | .map(|def_id| def_id.resolve(self.db)); | ||
879 | |||
880 | let def = if let Some(def) = def { | ||
881 | def | ||
882 | } else { | ||
883 | return None; | ||
884 | }; | ||
885 | |||
886 | match def { | ||
887 | Def::Struct(s) => { | ||
888 | let fields: Vec<_> = self | ||
889 | .db | ||
890 | .struct_data(s.def_id()) | ||
891 | .variant_data | ||
892 | .fields() | ||
893 | .iter() | ||
894 | .cloned() | ||
895 | .collect(); | ||
896 | Some((type_for_struct(self.db, s), fields)) | ||
897 | } | ||
898 | Def::EnumVariant(ev) => { | ||
899 | let fields: Vec<_> = ev.variant_data(self.db).fields().iter().cloned().collect(); | ||
900 | Some((type_for_enum_variant(self.db, ev), fields)) | ||
901 | } | ||
902 | _ => None, | ||
903 | } | ||
904 | } | ||
905 | |||
906 | fn infer_tuple_struct(&mut self, path: Option<&Path>, sub_pats: &[PatId]) -> Ty { | ||
907 | let (ty, fields) = if let Some(x) = self.resolve_fields(path) { | ||
908 | x | ||
909 | } else { | ||
910 | return Ty::Unknown; | ||
911 | }; | ||
912 | |||
913 | // walk subpats | ||
914 | if fields.len() != sub_pats.len() { | ||
915 | return Ty::Unknown; | ||
916 | } | ||
917 | |||
918 | for (&sub_pat, field) in sub_pats.iter().zip(fields.iter()) { | ||
919 | let sub_ty = Ty::from_hir( | ||
920 | self.db, | ||
921 | &self.module, | ||
922 | self.impl_block.as_ref(), | ||
923 | &field.type_ref, | ||
924 | ); | ||
925 | |||
926 | self.infer_pat(sub_pat, &Expectation::has_type(sub_ty)); | ||
927 | } | ||
928 | |||
929 | ty | ||
930 | } | ||
931 | |||
932 | fn infer_struct(&mut self, path: Option<&Path>, sub_pats: &[FieldPat]) -> Ty { | ||
933 | let (ty, fields) = if let Some(x) = self.resolve_fields(path) { | ||
934 | x | ||
935 | } else { | ||
936 | return Ty::Unknown; | ||
937 | }; | ||
938 | |||
939 | for sub_pat in sub_pats { | ||
940 | let tyref = fields | ||
941 | .iter() | ||
942 | .find(|field| field.name == sub_pat.name) | ||
943 | .map(|field| &field.type_ref); | ||
944 | |||
945 | if let Some(typeref) = tyref { | ||
946 | let sub_ty = Ty::from_hir(self.db, &self.module, self.impl_block.as_ref(), typeref); | ||
947 | |||
948 | if let Some(pat) = sub_pat.pat { | ||
949 | self.infer_pat(pat, &Expectation::has_type(sub_ty)); | ||
950 | } else { | ||
951 | // TODO: deal with this case: S { x, y } | ||
952 | } | ||
953 | } | ||
954 | } | ||
955 | |||
956 | ty | ||
957 | } | ||
958 | |||
875 | // FIXME: Expectation should probably contain a reference to a Ty instead of | 959 | // FIXME: Expectation should probably contain a reference to a Ty instead of |
876 | // a Ty itself | 960 | // a Ty itself |
877 | fn infer_pat(&mut self, pat: PatId, expected: &Expectation) -> Ty { | 961 | fn infer_pat(&mut self, pat: PatId, expected: &Expectation) -> Ty { |
@@ -900,54 +984,15 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
900 | path: ref p, | 984 | path: ref p, |
901 | args: ref sub_pats, | 985 | args: ref sub_pats, |
902 | }, | 986 | }, |
903 | _expected, | 987 | _, |
904 | ) => { | 988 | ) => self.infer_tuple_struct(p.as_ref(), sub_pats), |
905 | let def = p | 989 | ( |
906 | .as_ref() | 990 | &Pat::Struct { |
907 | .and_then(|path| self.module.resolve_path(self.db, &path).take_types()) | 991 | path: ref p, |
908 | .map(|def_id| def_id.resolve(self.db)); | 992 | args: ref fields, |
909 | 993 | }, | |
910 | if let Some(def) = def { | 994 | _, |
911 | let (ty, fields) = match def { | 995 | ) => self.infer_struct(p.as_ref(), fields), |
912 | Def::Struct(s) => { | ||
913 | let fields: Vec<_> = self | ||
914 | .db | ||
915 | .struct_data(s.def_id()) | ||
916 | .variant_data | ||
917 | .fields() | ||
918 | .iter() | ||
919 | .cloned() | ||
920 | .collect(); | ||
921 | (type_for_struct(self.db, s), fields) | ||
922 | } | ||
923 | Def::EnumVariant(ev) => { | ||
924 | let fields: Vec<_> = | ||
925 | ev.variant_data(self.db).fields().iter().cloned().collect(); | ||
926 | (type_for_enum_variant(self.db, ev), fields) | ||
927 | } | ||
928 | _ => unreachable!(), | ||
929 | }; | ||
930 | // walk subpats | ||
931 | if fields.len() == sub_pats.len() { | ||
932 | for (&sub_pat, field) in sub_pats.iter().zip(fields.iter()) { | ||
933 | let sub_ty = Ty::from_hir( | ||
934 | self.db, | ||
935 | &self.module, | ||
936 | self.impl_block.as_ref(), | ||
937 | &field.type_ref, | ||
938 | ); | ||
939 | |||
940 | self.infer_pat(sub_pat, &Expectation::has_type(sub_ty)); | ||
941 | } | ||
942 | |||
943 | ty | ||
944 | } else { | ||
945 | expected.ty.clone() | ||
946 | } | ||
947 | } else { | ||
948 | expected.ty.clone() | ||
949 | } | ||
950 | } | ||
951 | (_, ref _expected_ty) => expected.ty.clone(), | 996 | (_, ref _expected_ty) => expected.ty.clone(), |
952 | }; | 997 | }; |
953 | // use a new type variable if we got Ty::Unknown here | 998 | // use a new type variable if we got Ty::Unknown here |
diff --git a/crates/ra_hir/src/ty/tests/data/adt_pattern.txt b/crates/ra_hir/src/ty/tests/data/adt_pattern.txt index d23b865a0..41e9c9d34 100644 --- a/crates/ra_hir/src/ty/tests/data/adt_pattern.txt +++ b/crates/ra_hir/src/ty/tests/data/adt_pattern.txt | |||
@@ -1,12 +1,11 @@ | |||
1 | [49; 192) '{ ... }; }': () | 1 | [68; 155) '{ ...= e; }': () |
2 | [59; 60) 'e': E | 2 | [78; 79) 'e': E |
3 | [63; 76) 'E::A { x: 3 }': E | 3 | [82; 95) 'E::A { x: 3 }': E |
4 | [73; 74) '3': usize | 4 | [92; 93) '3': usize |
5 | [82; 124) 'if let... }': [unknown] | 5 | [106; 113) 'S(y, z)': S |
6 | [105; 106) 'e': E | 6 | [108; 109) 'y': u32 |
7 | [107; 124) '{ ... }': [unknown] | 7 | [111; 112) 'z': E |
8 | [117; 118) 'x': [unknown] | 8 | [116; 119) 'foo': S |
9 | [130; 189) 'match ... }': [unknown] | 9 | [129; 148) 'E::A {..._var }': E |
10 | [136; 137) 'e': E | 10 | [139; 146) 'new_var': usize |
11 | [162; 163) 'x': [unknown] | 11 | [151; 152) 'e': E |
12 | [181; 182) '1': i32 | ||