aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcus Klaas de Vries <[email protected]>2019-01-16 19:26:58 +0000
committerAleksey Kladov <[email protected]>2019-01-19 12:37:25 +0000
commit3340807bd24f398dca158e85eebae74012d8ef4b (patch)
treed0f94210a723ddbdcdfa9c64e66d59c68b52caca
parentab5deb78117693d776723bc0144e7b34e6f782d1 (diff)
Get basic struct pattern type inference working!
-rw-r--r--crates/ra_hir/src/expr.rs39
-rw-r--r--crates/ra_hir/src/ty.rs143
-rw-r--r--crates/ra_hir/src/ty/tests/data/adt_pattern.txt23
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)]
333pub struct FieldPat { 333pub 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