diff options
author | Florian Diebold <[email protected]> | 2019-01-19 14:48:55 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-01-19 15:02:06 +0000 |
commit | 9e4b5ecec4fa4f6a20bb4d47f09de602e9c29608 (patch) | |
tree | b94dd19bc5c2acd05b4de97e27ed4491d54cb78f /crates | |
parent | d37bb128effd19e3aec347e3d4f2e27b5cdb9404 (diff) |
Make generics work in struct patterns
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/ty.rs | 61 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 26 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests/data/generics_in_patterns.txt | 17 |
3 files changed, 87 insertions, 17 deletions
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 1d2d1b906..3608daae4 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -683,9 +683,9 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Ty { | |||
683 | 683 | ||
684 | pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) -> Option<Ty> { | 684 | pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) -> Option<Ty> { |
685 | let def = def_id.resolve(db); | 685 | let def = def_id.resolve(db); |
686 | let variant_data = match def { | 686 | let (variant_data, generics) = match def { |
687 | Def::Struct(s) => s.variant_data(db), | 687 | Def::Struct(s) => (s.variant_data(db), s.generics(db)), |
688 | Def::EnumVariant(ev) => ev.variant_data(db), | 688 | Def::EnumVariant(ev) => (ev.variant_data(db), ev.parent_enum(db).generics(db)), |
689 | // TODO: unions | 689 | // TODO: unions |
690 | _ => panic!( | 690 | _ => panic!( |
691 | "trying to get type for field in non-struct/variant {:?}", | 691 | "trying to get type for field in non-struct/variant {:?}", |
@@ -694,7 +694,6 @@ pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) | |||
694 | }; | 694 | }; |
695 | let module = def_id.module(db); | 695 | let module = def_id.module(db); |
696 | let impl_block = def_id.impl_block(db); | 696 | let impl_block = def_id.impl_block(db); |
697 | let generics = db.generics(def_id); | ||
698 | let type_ref = variant_data.get_field_type_ref(&field)?; | 697 | let type_ref = variant_data.get_field_type_ref(&field)?; |
699 | Some(Ty::from_hir( | 698 | Some(Ty::from_hir( |
700 | db, | 699 | db, |
@@ -893,6 +892,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
893 | ty | 892 | ty |
894 | } | 893 | } |
895 | 894 | ||
895 | fn unify_substs(&mut self, substs1: &Substs, substs2: &Substs) -> bool { | ||
896 | substs1 | ||
897 | .0 | ||
898 | .iter() | ||
899 | .zip(substs2.0.iter()) | ||
900 | .all(|(t1, t2)| self.unify(t1, t2)) | ||
901 | } | ||
902 | |||
896 | fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { | 903 | fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { |
897 | // try to resolve type vars first | 904 | // try to resolve type vars first |
898 | let ty1 = self.resolve_ty_shallow(ty1); | 905 | let ty1 = self.resolve_ty_shallow(ty1); |
@@ -913,12 +920,16 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
913 | (Ty::Bool, _) | (Ty::Str, _) | (Ty::Never, _) | (Ty::Char, _) => ty1 == ty2, | 920 | (Ty::Bool, _) | (Ty::Str, _) | (Ty::Never, _) | (Ty::Char, _) => ty1 == ty2, |
914 | ( | 921 | ( |
915 | Ty::Adt { | 922 | Ty::Adt { |
916 | def_id: def_id1, .. | 923 | def_id: def_id1, |
924 | substs: substs1, | ||
925 | .. | ||
917 | }, | 926 | }, |
918 | Ty::Adt { | 927 | Ty::Adt { |
919 | def_id: def_id2, .. | 928 | def_id: def_id2, |
929 | substs: substs2, | ||
930 | .. | ||
920 | }, | 931 | }, |
921 | ) if def_id1 == def_id2 => true, | 932 | ) if def_id1 == def_id2 => self.unify_substs(substs1, substs2), |
922 | (Ty::Slice(t1), Ty::Slice(t2)) => self.unify(t1, t2), | 933 | (Ty::Slice(t1), Ty::Slice(t2)) => self.unify(t1, t2), |
923 | (Ty::RawPtr(t1, m1), Ty::RawPtr(t2, m2)) if m1 == m2 => self.unify(t1, t2), | 934 | (Ty::RawPtr(t1, m1), Ty::RawPtr(t2, m2)) if m1 == m2 => self.unify(t1, t2), |
924 | (Ty::Ref(t1, m1), Ty::Ref(t2, m2)) if m1 == m2 => self.unify(t1, t2), | 935 | (Ty::Ref(t1, m1), Ty::Ref(t2, m2)) if m1 == m2 => self.unify(t1, t2), |
@@ -1088,49 +1099,65 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1088 | } | 1099 | } |
1089 | } | 1100 | } |
1090 | 1101 | ||
1091 | fn resolve_fields(&self, path: Option<&Path>) -> Option<(Ty, Vec<StructField>)> { | 1102 | fn resolve_fields(&mut self, path: Option<&Path>) -> Option<(Ty, Vec<StructField>)> { |
1092 | let def_id = self.module.resolve_path(self.db, path?).take_types()?; | 1103 | let (ty, def_id) = self.resolve_variant(path); |
1104 | let def_id = def_id?; | ||
1093 | let def = def_id.resolve(self.db); | 1105 | let def = def_id.resolve(self.db); |
1094 | 1106 | ||
1095 | match def { | 1107 | match def { |
1096 | Def::Struct(s) => { | 1108 | Def::Struct(s) => { |
1097 | let fields = s.fields(self.db); | 1109 | let fields = s.fields(self.db); |
1098 | Some((type_for_struct(self.db, s), fields)) | 1110 | Some((ty, fields)) |
1099 | } | 1111 | } |
1100 | Def::EnumVariant(ev) => { | 1112 | Def::EnumVariant(ev) => { |
1101 | let fields = ev.fields(self.db); | 1113 | let fields = ev.fields(self.db); |
1102 | Some((type_for_enum_variant(self.db, ev), fields)) | 1114 | Some((ty, fields)) |
1103 | } | 1115 | } |
1104 | _ => None, | 1116 | _ => None, |
1105 | } | 1117 | } |
1106 | } | 1118 | } |
1107 | 1119 | ||
1108 | fn infer_tuple_struct_pat(&mut self, path: Option<&Path>, subpats: &[PatId]) -> Ty { | 1120 | fn infer_tuple_struct_pat( |
1121 | &mut self, | ||
1122 | path: Option<&Path>, | ||
1123 | subpats: &[PatId], | ||
1124 | expected: &Ty, | ||
1125 | ) -> Ty { | ||
1109 | let (ty, fields) = self | 1126 | let (ty, fields) = self |
1110 | .resolve_fields(path) | 1127 | .resolve_fields(path) |
1111 | .unwrap_or((Ty::Unknown, Vec::new())); | 1128 | .unwrap_or((Ty::Unknown, Vec::new())); |
1112 | 1129 | ||
1130 | self.unify(&ty, expected); | ||
1131 | |||
1132 | let substs = ty.substs().expect("adt should have substs"); | ||
1133 | |||
1113 | for (i, &subpat) in subpats.iter().enumerate() { | 1134 | for (i, &subpat) in subpats.iter().enumerate() { |
1114 | let expected_ty = fields | 1135 | let expected_ty = fields |
1115 | .get(i) | 1136 | .get(i) |
1116 | .and_then(|field| field.ty(self.db)) | 1137 | .and_then(|field| field.ty(self.db)) |
1117 | .unwrap_or(Ty::Unknown); | 1138 | .unwrap_or(Ty::Unknown) |
1139 | .subst(&substs); | ||
1118 | self.infer_pat(subpat, &expected_ty); | 1140 | self.infer_pat(subpat, &expected_ty); |
1119 | } | 1141 | } |
1120 | 1142 | ||
1121 | ty | 1143 | ty |
1122 | } | 1144 | } |
1123 | 1145 | ||
1124 | fn infer_struct_pat(&mut self, path: Option<&Path>, subpats: &[FieldPat]) -> Ty { | 1146 | fn infer_struct_pat(&mut self, path: Option<&Path>, subpats: &[FieldPat], expected: &Ty) -> Ty { |
1125 | let (ty, fields) = self | 1147 | let (ty, fields) = self |
1126 | .resolve_fields(path) | 1148 | .resolve_fields(path) |
1127 | .unwrap_or((Ty::Unknown, Vec::new())); | 1149 | .unwrap_or((Ty::Unknown, Vec::new())); |
1128 | 1150 | ||
1151 | self.unify(&ty, expected); | ||
1152 | |||
1153 | let substs = ty.substs().expect("adt should have substs"); | ||
1154 | |||
1129 | for subpat in subpats { | 1155 | for subpat in subpats { |
1130 | let matching_field = fields.iter().find(|field| field.name() == &subpat.name); | 1156 | let matching_field = fields.iter().find(|field| field.name() == &subpat.name); |
1131 | let expected_ty = matching_field | 1157 | let expected_ty = matching_field |
1132 | .and_then(|field| field.ty(self.db)) | 1158 | .and_then(|field| field.ty(self.db)) |
1133 | .unwrap_or(Ty::Unknown); | 1159 | .unwrap_or(Ty::Unknown) |
1160 | .subst(&substs); | ||
1134 | self.infer_pat(subpat.pat, &expected_ty); | 1161 | self.infer_pat(subpat.pat, &expected_ty); |
1135 | } | 1162 | } |
1136 | 1163 | ||
@@ -1175,11 +1202,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1175 | Pat::TupleStruct { | 1202 | Pat::TupleStruct { |
1176 | path: ref p, | 1203 | path: ref p, |
1177 | args: ref subpats, | 1204 | args: ref subpats, |
1178 | } => self.infer_tuple_struct_pat(p.as_ref(), subpats), | 1205 | } => self.infer_tuple_struct_pat(p.as_ref(), subpats, expected), |
1179 | Pat::Struct { | 1206 | Pat::Struct { |
1180 | path: ref p, | 1207 | path: ref p, |
1181 | args: ref fields, | 1208 | args: ref fields, |
1182 | } => self.infer_struct_pat(p.as_ref(), fields), | 1209 | } => self.infer_struct_pat(p.as_ref(), fields, expected), |
1183 | Pat::Path(path) => self | 1210 | Pat::Path(path) => self |
1184 | .module | 1211 | .module |
1185 | .resolve_path(self.db, &path) | 1212 | .resolve_path(self.db, &path) |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index c590a09db..06e32df59 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -439,6 +439,32 @@ fn test(a1: A<u32>, i: i32) { | |||
439 | } | 439 | } |
440 | 440 | ||
441 | #[test] | 441 | #[test] |
442 | fn infer_generics_in_patterns() { | ||
443 | check_inference( | ||
444 | r#" | ||
445 | struct A<T> { | ||
446 | x: T, | ||
447 | } | ||
448 | |||
449 | enum Option<T> { | ||
450 | Some(T), | ||
451 | None, | ||
452 | } | ||
453 | |||
454 | fn test(a1: A<u32>, o: Option<u64>) { | ||
455 | let A { x: x2 } = a1; | ||
456 | let A::<i64> { x: x3 } = A { x: 1 }; | ||
457 | match o { | ||
458 | Option::Some(t) => t, | ||
459 | _ => 1, | ||
460 | }; | ||
461 | } | ||
462 | "#, | ||
463 | "generics_in_patterns.txt", | ||
464 | ); | ||
465 | } | ||
466 | |||
467 | #[test] | ||
442 | fn infer_function_generics() { | 468 | fn infer_function_generics() { |
443 | check_inference( | 469 | check_inference( |
444 | r#" | 470 | r#" |
diff --git a/crates/ra_hir/src/ty/tests/data/generics_in_patterns.txt b/crates/ra_hir/src/ty/tests/data/generics_in_patterns.txt new file mode 100644 index 000000000..1b01ef19e --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/generics_in_patterns.txt | |||
@@ -0,0 +1,17 @@ | |||
1 | [79; 81) 'a1': A<u32> | ||
2 | [91; 92) 'o': Option<u64> | ||
3 | [107; 244) '{ ... }; }': () | ||
4 | [117; 128) 'A { x: x2 }': A<u32> | ||
5 | [124; 126) 'x2': u32 | ||
6 | [131; 133) 'a1': A<u32> | ||
7 | [143; 161) 'A::<i6...: x3 }': A<i64> | ||
8 | [157; 159) 'x3': i64 | ||
9 | [164; 174) 'A { x: 1 }': A<i64> | ||
10 | [171; 172) '1': i64 | ||
11 | [180; 241) 'match ... }': u64 | ||
12 | [186; 187) 'o': Option<u64> | ||
13 | [198; 213) 'Option::Some(t)': Option<u64> | ||
14 | [211; 212) 't': u64 | ||
15 | [217; 218) 't': u64 | ||
16 | [228; 229) '_': Option<u64> | ||
17 | [233; 234) '1': u64 | ||