diff options
Diffstat (limited to 'crates/ra_hir/src/ty/infer.rs')
-rw-r--r-- | crates/ra_hir/src/ty/infer.rs | 207 |
1 files changed, 181 insertions, 26 deletions
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index cbbba8b23..ba63050a9 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs | |||
@@ -42,6 +42,7 @@ use crate::{ | |||
42 | RecordFieldPat, Statement, UnaryOp, | 42 | RecordFieldPat, Statement, UnaryOp, |
43 | }, | 43 | }, |
44 | generics::{GenericParams, HasGenericParams}, | 44 | generics::{GenericParams, HasGenericParams}, |
45 | lang_item::LangItemTarget, | ||
45 | name, | 46 | name, |
46 | nameres::Namespace, | 47 | nameres::Namespace, |
47 | path::{known, GenericArg, GenericArgs}, | 48 | path::{known, GenericArg, GenericArgs}, |
@@ -188,6 +189,12 @@ struct InferenceContext<'a, D: HirDatabase> { | |||
188 | result: InferenceResult, | 189 | result: InferenceResult, |
189 | /// The return type of the function being inferred. | 190 | /// The return type of the function being inferred. |
190 | return_ty: Ty, | 191 | return_ty: Ty, |
192 | |||
193 | /// Impls of `CoerceUnsized` used in coercion. | ||
194 | /// (from_ty_ctor, to_ty_ctor) => coerce_generic_index | ||
195 | // FIXME: Use trait solver for this. | ||
196 | // Chalk seems unable to work well with builtin impl of `Unsize` now. | ||
197 | coerce_unsized_map: FxHashMap<(TypeCtor, TypeCtor), usize>, | ||
191 | } | 198 | } |
192 | 199 | ||
193 | macro_rules! ty_app { | 200 | macro_rules! ty_app { |
@@ -207,12 +214,52 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
207 | obligations: Vec::default(), | 214 | obligations: Vec::default(), |
208 | return_ty: Ty::Unknown, // set in collect_fn_signature | 215 | return_ty: Ty::Unknown, // set in collect_fn_signature |
209 | trait_env: lower::trait_env(db, &resolver), | 216 | trait_env: lower::trait_env(db, &resolver), |
217 | coerce_unsized_map: Self::init_coerce_unsized_map(db, &resolver), | ||
210 | db, | 218 | db, |
211 | body, | 219 | body, |
212 | resolver, | 220 | resolver, |
213 | } | 221 | } |
214 | } | 222 | } |
215 | 223 | ||
224 | fn init_coerce_unsized_map( | ||
225 | db: &'a D, | ||
226 | resolver: &Resolver, | ||
227 | ) -> FxHashMap<(TypeCtor, TypeCtor), usize> { | ||
228 | let krate = resolver.krate().unwrap(); | ||
229 | let impls = match db.lang_item(krate, "coerce_unsized".into()) { | ||
230 | Some(LangItemTarget::Trait(trait_)) => db.impls_for_trait(krate, trait_), | ||
231 | _ => return FxHashMap::default(), | ||
232 | }; | ||
233 | |||
234 | impls | ||
235 | .iter() | ||
236 | .filter_map(|impl_block| { | ||
237 | // `CoerseUnsized` has one generic parameter for the target type. | ||
238 | let trait_ref = impl_block.target_trait_ref(db)?; | ||
239 | let cur_from_ty = trait_ref.substs.0.get(0)?; | ||
240 | let cur_to_ty = trait_ref.substs.0.get(1)?; | ||
241 | |||
242 | match (&cur_from_ty, cur_to_ty) { | ||
243 | (ty_app!(ctor1, st1), ty_app!(ctor2, st2)) => { | ||
244 | // FIXME: We return the first non-equal bound as the type parameter to coerce to unsized type. | ||
245 | // This works for smart-pointer-like coercion, which covers all impls from std. | ||
246 | st1.iter().zip(st2.iter()).enumerate().find_map(|(i, (ty1, ty2))| { | ||
247 | match (ty1, ty2) { | ||
248 | (Ty::Param { idx: p1, .. }, Ty::Param { idx: p2, .. }) | ||
249 | if p1 != p2 => | ||
250 | { | ||
251 | Some(((*ctor1, *ctor2), i)) | ||
252 | } | ||
253 | _ => None, | ||
254 | } | ||
255 | }) | ||
256 | } | ||
257 | _ => None, | ||
258 | } | ||
259 | }) | ||
260 | .collect() | ||
261 | } | ||
262 | |||
216 | fn resolve_all(mut self) -> InferenceResult { | 263 | fn resolve_all(mut self) -> InferenceResult { |
217 | // FIXME resolve obligations as well (use Guidance if necessary) | 264 | // FIXME resolve obligations as well (use Guidance if necessary) |
218 | let mut result = mem::replace(&mut self.result, InferenceResult::default()); | 265 | let mut result = mem::replace(&mut self.result, InferenceResult::default()); |
@@ -919,16 +966,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
919 | _ => {} | 966 | _ => {} |
920 | } | 967 | } |
921 | 968 | ||
922 | // FIXME: Solve `FromTy: CoerceUnsized<ToTy>` instead of listing common impls here. | 969 | if let Some(ret) = self.try_coerce_unsized(&from_ty, &to_ty) { |
923 | match (&from_ty, &to_ty) { | 970 | return ret; |
924 | // Mutilibity is checked above | ||
925 | (ty_app!(TypeCtor::Ref(_), st1), ty_app!(TypeCtor::Ref(_), st2)) | ||
926 | | (ty_app!(TypeCtor::RawPtr(_), st1), ty_app!(TypeCtor::RawPtr(_), st2)) => { | ||
927 | if self.try_coerce_unsized(&st1[0], &st2[0], 0) { | ||
928 | return true; | ||
929 | } | ||
930 | } | ||
931 | _ => {} | ||
932 | } | 971 | } |
933 | 972 | ||
934 | // Auto Deref if cannot coerce | 973 | // Auto Deref if cannot coerce |
@@ -943,10 +982,43 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
943 | } | 982 | } |
944 | } | 983 | } |
945 | 984 | ||
946 | /// Coerce a type to a DST if `FromTy: Unsize<ToTy>` | 985 | /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>` |
947 | /// | 986 | /// |
948 | /// See: `https://doc.rust-lang.org/nightly/std/marker/trait.Unsize.html` | 987 | /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html |
949 | fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty, depth: usize) -> bool { | 988 | fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> Option<bool> { |
989 | let (ctor1, st1, ctor2, st2) = match (from_ty, to_ty) { | ||
990 | (ty_app!(ctor1, st1), ty_app!(ctor2, st2)) => (ctor1, st1, ctor2, st2), | ||
991 | _ => return None, | ||
992 | }; | ||
993 | |||
994 | let coerce_generic_index = *self.coerce_unsized_map.get(&(*ctor1, *ctor2))?; | ||
995 | |||
996 | // Check `Unsize` first | ||
997 | match self.check_unsize_and_coerce( | ||
998 | st1.0.get(coerce_generic_index)?, | ||
999 | st2.0.get(coerce_generic_index)?, | ||
1000 | 0, | ||
1001 | ) { | ||
1002 | Some(true) => {} | ||
1003 | ret => return ret, | ||
1004 | } | ||
1005 | |||
1006 | let ret = st1 | ||
1007 | .iter() | ||
1008 | .zip(st2.iter()) | ||
1009 | .enumerate() | ||
1010 | .filter(|&(idx, _)| idx != coerce_generic_index) | ||
1011 | .all(|(_, (ty1, ty2))| self.unify(ty1, ty2)); | ||
1012 | |||
1013 | Some(ret) | ||
1014 | } | ||
1015 | |||
1016 | /// Check if `from_ty: Unsize<to_ty>`, and coerce to `to_ty` if it holds. | ||
1017 | /// | ||
1018 | /// It should not be directly called. It is only used by `try_coerce_unsized`. | ||
1019 | /// | ||
1020 | /// See: https://doc.rust-lang.org/nightly/std/marker/trait.Unsize.html | ||
1021 | fn check_unsize_and_coerce(&mut self, from_ty: &Ty, to_ty: &Ty, depth: usize) -> Option<bool> { | ||
950 | if depth > 1000 { | 1022 | if depth > 1000 { |
951 | panic!("Infinite recursion in coercion"); | 1023 | panic!("Infinite recursion in coercion"); |
952 | } | 1024 | } |
@@ -954,29 +1026,113 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
954 | match (&from_ty, &to_ty) { | 1026 | match (&from_ty, &to_ty) { |
955 | // `[T; N]` -> `[T]` | 1027 | // `[T; N]` -> `[T]` |
956 | (ty_app!(TypeCtor::Array, st1), ty_app!(TypeCtor::Slice, st2)) => { | 1028 | (ty_app!(TypeCtor::Array, st1), ty_app!(TypeCtor::Slice, st2)) => { |
957 | self.unify(&st1[0], &st2[0]) | 1029 | Some(self.unify(&st1[0], &st2[0])) |
958 | } | 1030 | } |
959 | 1031 | ||
960 | // `T` -> `dyn Trait` when `T: Trait` | 1032 | // `T` -> `dyn Trait` when `T: Trait` |
961 | (_, Ty::Dyn(_)) => { | 1033 | (_, Ty::Dyn(_)) => { |
962 | // FIXME: Check predicates | 1034 | // FIXME: Check predicates |
963 | true | 1035 | Some(true) |
964 | } | 1036 | } |
965 | 1037 | ||
1038 | // `(..., T)` -> `(..., U)` when `T: Unsize<U>` | ||
1039 | ( | ||
1040 | ty_app!(TypeCtor::Tuple { cardinality: len1 }, st1), | ||
1041 | ty_app!(TypeCtor::Tuple { cardinality: len2 }, st2), | ||
1042 | ) => { | ||
1043 | if len1 != len2 || *len1 == 0 { | ||
1044 | return None; | ||
1045 | } | ||
1046 | |||
1047 | match self.check_unsize_and_coerce( | ||
1048 | st1.last().unwrap(), | ||
1049 | st2.last().unwrap(), | ||
1050 | depth + 1, | ||
1051 | ) { | ||
1052 | Some(true) => {} | ||
1053 | ret => return ret, | ||
1054 | } | ||
1055 | |||
1056 | let ret = st1[..st1.len() - 1] | ||
1057 | .iter() | ||
1058 | .zip(&st2[..st2.len() - 1]) | ||
1059 | .all(|(ty1, ty2)| self.unify(ty1, ty2)); | ||
1060 | |||
1061 | Some(ret) | ||
1062 | } | ||
1063 | |||
1064 | // Foo<..., T, ...> is Unsize<Foo<..., U, ...>> if: | ||
1065 | // - T: Unsize<U> | ||
1066 | // - Foo is a struct | ||
1067 | // - Only the last field of Foo has a type involving T | ||
1068 | // - T is not part of the type of any other fields | ||
1069 | // - Bar<T>: Unsize<Bar<U>>, if the last field of Foo has type Bar<T> | ||
966 | ( | 1070 | ( |
967 | ty_app!(TypeCtor::Adt(Adt::Struct(struct1)), st1), | 1071 | ty_app!(TypeCtor::Adt(Adt::Struct(struct1)), st1), |
968 | ty_app!(TypeCtor::Adt(Adt::Struct(struct2)), st2), | 1072 | ty_app!(TypeCtor::Adt(Adt::Struct(struct2)), st2), |
969 | ) if struct1 == struct2 => { | 1073 | ) if struct1 == struct2 => { |
970 | // FIXME: Check preconditions here | 1074 | let fields = struct1.fields(self.db); |
971 | for (ty1, ty2) in st1.iter().zip(st2.iter()) { | 1075 | let (last_field, prev_fields) = fields.split_last()?; |
972 | if !self.try_coerce_unsized(ty1, ty2, depth + 1) { | 1076 | |
973 | return false; | 1077 | // Get the generic parameter involved in the last field. |
1078 | let unsize_generic_index = { | ||
1079 | let mut index = None; | ||
1080 | let mut multiple_param = false; | ||
1081 | last_field.ty(self.db).walk(&mut |ty| match ty { | ||
1082 | &Ty::Param { idx, .. } => { | ||
1083 | if index.is_none() { | ||
1084 | index = Some(idx); | ||
1085 | } else if Some(idx) != index { | ||
1086 | multiple_param = true; | ||
1087 | } | ||
1088 | } | ||
1089 | _ => {} | ||
1090 | }); | ||
1091 | |||
1092 | if multiple_param { | ||
1093 | return None; | ||
974 | } | 1094 | } |
1095 | index? | ||
1096 | }; | ||
1097 | |||
1098 | // Check other fields do not involve it. | ||
1099 | let mut multiple_used = false; | ||
1100 | prev_fields.iter().for_each(|field| { | ||
1101 | field.ty(self.db).walk(&mut |ty| match ty { | ||
1102 | &Ty::Param { idx, .. } if idx == unsize_generic_index => { | ||
1103 | multiple_used = true | ||
1104 | } | ||
1105 | _ => {} | ||
1106 | }) | ||
1107 | }); | ||
1108 | if multiple_used { | ||
1109 | return None; | ||
975 | } | 1110 | } |
976 | true | 1111 | |
1112 | let unsize_generic_index = unsize_generic_index as usize; | ||
1113 | |||
1114 | // Check `Unsize` first | ||
1115 | match self.check_unsize_and_coerce( | ||
1116 | st1.get(unsize_generic_index)?, | ||
1117 | st2.get(unsize_generic_index)?, | ||
1118 | depth + 1, | ||
1119 | ) { | ||
1120 | Some(true) => {} | ||
1121 | ret => return ret, | ||
1122 | } | ||
1123 | |||
1124 | // Then unify other parameters | ||
1125 | let ret = st1 | ||
1126 | .iter() | ||
1127 | .zip(st2.iter()) | ||
1128 | .enumerate() | ||
1129 | .filter(|&(idx, _)| idx != unsize_generic_index) | ||
1130 | .all(|(_, (ty1, ty2))| self.unify(ty1, ty2)); | ||
1131 | |||
1132 | Some(ret) | ||
977 | } | 1133 | } |
978 | 1134 | ||
979 | _ => false, | 1135 | _ => None, |
980 | } | 1136 | } |
981 | } | 1137 | } |
982 | 1138 | ||
@@ -1433,12 +1589,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1433 | let decl_ty = | 1589 | let decl_ty = |
1434 | type_ref.as_ref().map(|tr| self.make_ty(tr)).unwrap_or(Ty::Unknown); | 1590 | type_ref.as_ref().map(|tr| self.make_ty(tr)).unwrap_or(Ty::Unknown); |
1435 | let decl_ty = self.insert_type_vars(decl_ty); | 1591 | let decl_ty = self.insert_type_vars(decl_ty); |
1436 | let ty = if let Some(expr) = initializer { | 1592 | if let Some(expr) = initializer { |
1437 | self.infer_expr_coerce(*expr, &Expectation::has_type(decl_ty)) | 1593 | self.infer_expr_coerce(*expr, &Expectation::has_type(decl_ty.clone())); |
1438 | } else { | 1594 | } |
1439 | decl_ty | ||
1440 | }; | ||
1441 | 1595 | ||
1596 | let ty = self.resolve_ty_as_possible(&mut vec![], decl_ty); | ||
1442 | self.infer_pat(*pat, &ty, BindingMode::default()); | 1597 | self.infer_pat(*pat, &ty, BindingMode::default()); |
1443 | } | 1598 | } |
1444 | Statement::Expr(expr) => { | 1599 | Statement::Expr(expr) => { |