diff options
author | Florian Diebold <[email protected]> | 2019-03-31 19:02:16 +0100 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-04-14 10:28:53 +0100 |
commit | a1ed53a4f183b5826162eb9e998207b92be9c57f (patch) | |
tree | 7c139a7dc38483188653c6d40583cddac9d23192 /crates/ra_hir/src/ty | |
parent | 413c87f155ab6b389b1cc122b5739716acccb476 (diff) |
More trait infrastructure
- make it possible to get parent trait from method
- add 'obligation' machinery for checking that a type implements a
trait (and inferring facts about type variables from that)
- handle type parameters of traits (to a certain degree)
- improve the hacky implements check to cover enough cases to exercise the
handling of traits with type parameters
- basic canonicalization (will probably also be done by Chalk)
Diffstat (limited to 'crates/ra_hir/src/ty')
-rw-r--r-- | crates/ra_hir/src/ty/infer.rs | 84 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/lower.rs | 9 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/method_resolution.rs | 47 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 51 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/traits.rs | 112 |
5 files changed, 260 insertions, 43 deletions
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 28947be51..3dec5936a 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs | |||
@@ -41,7 +41,7 @@ use crate::{ | |||
41 | ty::infer::diagnostics::InferenceDiagnostic, | 41 | ty::infer::diagnostics::InferenceDiagnostic, |
42 | diagnostics::DiagnosticSink, | 42 | diagnostics::DiagnosticSink, |
43 | }; | 43 | }; |
44 | use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; | 44 | use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor, traits::{ Solution, Obligation, Guidance}, CallableDef, TraitRef}; |
45 | 45 | ||
46 | /// The entry point of type inference. | 46 | /// The entry point of type inference. |
47 | pub fn infer(db: &impl HirDatabase, def: DefWithBody) -> Arc<InferenceResult> { | 47 | pub fn infer(db: &impl HirDatabase, def: DefWithBody) -> Arc<InferenceResult> { |
@@ -153,6 +153,7 @@ struct InferenceContext<'a, D: HirDatabase> { | |||
153 | body: Arc<Body>, | 153 | body: Arc<Body>, |
154 | resolver: Resolver, | 154 | resolver: Resolver, |
155 | var_unification_table: InPlaceUnificationTable<TypeVarId>, | 155 | var_unification_table: InPlaceUnificationTable<TypeVarId>, |
156 | obligations: Vec<Obligation>, | ||
156 | method_resolutions: FxHashMap<ExprId, Function>, | 157 | method_resolutions: FxHashMap<ExprId, Function>, |
157 | field_resolutions: FxHashMap<ExprId, StructField>, | 158 | field_resolutions: FxHashMap<ExprId, StructField>, |
158 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, | 159 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, |
@@ -173,6 +174,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
173 | type_of_pat: ArenaMap::default(), | 174 | type_of_pat: ArenaMap::default(), |
174 | diagnostics: Vec::default(), | 175 | diagnostics: Vec::default(), |
175 | var_unification_table: InPlaceUnificationTable::new(), | 176 | var_unification_table: InPlaceUnificationTable::new(), |
177 | obligations: Vec::default(), | ||
176 | return_ty: Ty::Unknown, // set in collect_fn_signature | 178 | return_ty: Ty::Unknown, // set in collect_fn_signature |
177 | db, | 179 | db, |
178 | body, | 180 | body, |
@@ -181,6 +183,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
181 | } | 183 | } |
182 | 184 | ||
183 | fn resolve_all(mut self) -> InferenceResult { | 185 | fn resolve_all(mut self) -> InferenceResult { |
186 | // FIXME resolve obligations as well (use Guidance if necessary) | ||
184 | let mut tv_stack = Vec::new(); | 187 | let mut tv_stack = Vec::new(); |
185 | let mut expr_types = mem::replace(&mut self.type_of_expr, ArenaMap::default()); | 188 | let mut expr_types = mem::replace(&mut self.type_of_expr, ArenaMap::default()); |
186 | for ty in expr_types.values_mut() { | 189 | for ty in expr_types.values_mut() { |
@@ -311,11 +314,49 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
311 | ty.fold(&mut |ty| self.insert_type_vars_shallow(ty)) | 314 | ty.fold(&mut |ty| self.insert_type_vars_shallow(ty)) |
312 | } | 315 | } |
313 | 316 | ||
317 | fn resolve_obligations_as_possible(&mut self) { | ||
318 | let obligations = mem::replace(&mut self.obligations, Vec::new()); | ||
319 | for obligation in obligations { | ||
320 | // FIXME resolve types in the obligation first | ||
321 | let (solution, var_mapping) = match &obligation { | ||
322 | Obligation::Trait(tr) => { | ||
323 | let (tr, var_mapping) = super::traits::canonicalize(tr.clone()); | ||
324 | (self.db.implements(tr), var_mapping) | ||
325 | } | ||
326 | }; | ||
327 | match solution { | ||
328 | Some(Solution::Unique(substs)) => { | ||
329 | for (i, subst) in substs.0.iter().enumerate() { | ||
330 | let uncanonical = var_mapping[i]; | ||
331 | // FIXME the subst may contain type variables, which would need to be mapped back as well | ||
332 | self.unify(&Ty::Infer(InferTy::TypeVar(uncanonical)), subst); | ||
333 | } | ||
334 | } | ||
335 | Some(Solution::Ambig(Guidance::Definite(substs))) => { | ||
336 | for (i, subst) in substs.0.iter().enumerate() { | ||
337 | let uncanonical = var_mapping[i]; | ||
338 | // FIXME the subst may contain type variables, which would need to be mapped back as well | ||
339 | self.unify(&Ty::Infer(InferTy::TypeVar(uncanonical)), subst); | ||
340 | } | ||
341 | self.obligations.push(obligation); | ||
342 | } | ||
343 | Some(_) => { | ||
344 | self.obligations.push(obligation); | ||
345 | } | ||
346 | None => { | ||
347 | // FIXME obligation cannot be fulfilled => diagnostic | ||
348 | } | ||
349 | } | ||
350 | } | ||
351 | } | ||
352 | |||
314 | /// Resolves the type as far as currently possible, replacing type variables | 353 | /// Resolves the type as far as currently possible, replacing type variables |
315 | /// by their known types. All types returned by the infer_* functions should | 354 | /// by their known types. All types returned by the infer_* functions should |
316 | /// be resolved as far as possible, i.e. contain no type variables with | 355 | /// be resolved as far as possible, i.e. contain no type variables with |
317 | /// known type. | 356 | /// known type. |
318 | fn resolve_ty_as_possible(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty { | 357 | fn resolve_ty_as_possible(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty { |
358 | self.resolve_obligations_as_possible(); | ||
359 | |||
319 | ty.fold(&mut |ty| match ty { | 360 | ty.fold(&mut |ty| match ty { |
320 | Ty::Infer(tv) => { | 361 | Ty::Infer(tv) => { |
321 | let inner = tv.to_inner(); | 362 | let inner = tv.to_inner(); |
@@ -710,12 +751,19 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
710 | &mut self, | 751 | &mut self, |
711 | def_generics: Option<Arc<GenericParams>>, | 752 | def_generics: Option<Arc<GenericParams>>, |
712 | generic_args: &Option<GenericArgs>, | 753 | generic_args: &Option<GenericArgs>, |
754 | receiver_ty: &Ty, | ||
713 | ) -> Substs { | 755 | ) -> Substs { |
714 | let (parent_param_count, param_count) = | 756 | let (parent_param_count, param_count) = |
715 | def_generics.map_or((0, 0), |g| (g.count_parent_params(), g.params.len())); | 757 | def_generics.as_ref().map_or((0, 0), |g| (g.count_parent_params(), g.params.len())); |
716 | let mut substs = Vec::with_capacity(parent_param_count + param_count); | 758 | let mut substs = Vec::with_capacity(parent_param_count + param_count); |
717 | for _ in 0..parent_param_count { | 759 | if let Some(parent_generics) = def_generics.and_then(|p| p.parent_params.clone()) { |
718 | substs.push(Ty::Unknown); | 760 | for param in &parent_generics.params { |
761 | if param.name.as_known_name() == Some(crate::KnownName::SelfType) { | ||
762 | substs.push(receiver_ty.clone()); | ||
763 | } else { | ||
764 | substs.push(Ty::Unknown); | ||
765 | } | ||
766 | } | ||
719 | } | 767 | } |
720 | // handle provided type arguments | 768 | // handle provided type arguments |
721 | if let Some(generic_args) = generic_args { | 769 | if let Some(generic_args) = generic_args { |
@@ -817,6 +865,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
817 | (Vec::new(), Ty::Unknown) | 865 | (Vec::new(), Ty::Unknown) |
818 | } | 866 | } |
819 | }; | 867 | }; |
868 | // FIXME register obligations from where clauses from the function | ||
820 | let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); | 869 | let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); |
821 | for (arg, param) in args.iter().zip(param_iter) { | 870 | for (arg, param) in args.iter().zip(param_iter) { |
822 | self.infer_expr(*arg, &Expectation::has_type(param)); | 871 | self.infer_expr(*arg, &Expectation::has_type(param)); |
@@ -838,7 +887,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
838 | } | 887 | } |
839 | None => (receiver_ty, Ty::Unknown, None), | 888 | None => (receiver_ty, Ty::Unknown, None), |
840 | }; | 889 | }; |
841 | let substs = self.substs_for_method_call(def_generics, generic_args); | 890 | let substs = self.substs_for_method_call( |
891 | def_generics.clone(), | ||
892 | generic_args, | ||
893 | &derefed_receiver_ty, | ||
894 | ); | ||
842 | let method_ty = method_ty.apply_substs(substs); | 895 | let method_ty = method_ty.apply_substs(substs); |
843 | let method_ty = self.insert_type_vars(method_ty); | 896 | let method_ty = self.insert_type_vars(method_ty); |
844 | let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty { | 897 | let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty { |
@@ -859,6 +912,24 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
859 | let sig = self.db.callable_item_signature(def); | 912 | let sig = self.db.callable_item_signature(def); |
860 | let ret_ty = sig.ret().clone().subst(&a_ty.parameters); | 913 | let ret_ty = sig.ret().clone().subst(&a_ty.parameters); |
861 | 914 | ||
915 | // add obligation for trait implementation, if this is a trait method | ||
916 | // FIXME also register obligations from where clauses from the trait or impl and method | ||
917 | match def { | ||
918 | CallableDef::Function(f) => { | ||
919 | if let Some(trait_) = f.parent_trait(self.db) { | ||
920 | // construct a TraitDef | ||
921 | let substs = a_ty.parameters.prefix( | ||
922 | def_generics | ||
923 | .expect("trait parent should always have generics") | ||
924 | .count_parent_params(), | ||
925 | ); | ||
926 | self.obligations | ||
927 | .push(Obligation::Trait(TraitRef { trait_, substs })); | ||
928 | } | ||
929 | } | ||
930 | CallableDef::Struct(_) | CallableDef::EnumVariant(_) => {} | ||
931 | } | ||
932 | |||
862 | if !sig.params().is_empty() { | 933 | if !sig.params().is_empty() { |
863 | let mut params_iter = sig | 934 | let mut params_iter = sig |
864 | .params() | 935 | .params() |
@@ -875,6 +946,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
875 | _ => (Ty::Unknown, Vec::new(), Ty::Unknown), | 946 | _ => (Ty::Unknown, Vec::new(), Ty::Unknown), |
876 | }; | 947 | }; |
877 | // Apply autoref so the below unification works correctly | 948 | // Apply autoref so the below unification works correctly |
949 | // FIXME: return correct autorefs/derefs from lookup_method | ||
878 | let actual_receiver_ty = match expected_receiver_ty.as_reference() { | 950 | let actual_receiver_ty = match expected_receiver_ty.as_reference() { |
879 | Some((_, mutability)) => { | 951 | Some((_, mutability)) => { |
880 | Ty::apply_one(TypeCtor::Ref(mutability), derefed_receiver_ty) | 952 | Ty::apply_one(TypeCtor::Ref(mutability), derefed_receiver_ty) |
@@ -1180,7 +1252,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1180 | 1252 | ||
1181 | /// The ID of a type variable. | 1253 | /// The ID of a type variable. |
1182 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | 1254 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] |
1183 | pub struct TypeVarId(u32); | 1255 | pub struct TypeVarId(pub(super) u32); |
1184 | 1256 | ||
1185 | impl UnifyKey for TypeVarId { | 1257 | impl UnifyKey for TypeVarId { |
1186 | type Value = TypeVarValue; | 1258 | type Value = TypeVarValue; |
diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 4523b3954..ccacb5e73 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs | |||
@@ -206,6 +206,7 @@ impl TraitRef { | |||
206 | db: &impl HirDatabase, | 206 | db: &impl HirDatabase, |
207 | resolver: &Resolver, | 207 | resolver: &Resolver, |
208 | type_ref: &TypeRef, | 208 | type_ref: &TypeRef, |
209 | explicit_self_ty: Option<Ty>, | ||
209 | ) -> Option<Self> { | 210 | ) -> Option<Self> { |
210 | let path = match type_ref { | 211 | let path = match type_ref { |
211 | TypeRef::Path(path) => path, | 212 | TypeRef::Path(path) => path, |
@@ -215,7 +216,13 @@ impl TraitRef { | |||
215 | Resolution::Def(ModuleDef::Trait(tr)) => tr, | 216 | Resolution::Def(ModuleDef::Trait(tr)) => tr, |
216 | _ => return None, | 217 | _ => return None, |
217 | }; | 218 | }; |
218 | let substs = Self::substs_from_path(db, resolver, path, resolved); | 219 | let mut substs = Self::substs_from_path(db, resolver, path, resolved); |
220 | if let Some(self_ty) = explicit_self_ty { | ||
221 | // FIXME this could be nicer | ||
222 | let mut substs_vec = substs.0.to_vec(); | ||
223 | substs_vec[0] = self_ty; | ||
224 | substs.0 = substs_vec.into(); | ||
225 | } | ||
219 | Some(TraitRef { trait_: resolved, substs }) | 226 | Some(TraitRef { trait_: resolved, substs }) |
220 | } | 227 | } |
221 | 228 | ||
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index aac7d6384..f69b8304b 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs | |||
@@ -108,20 +108,6 @@ impl CrateImplBlocks { | |||
108 | } | 108 | } |
109 | } | 109 | } |
110 | 110 | ||
111 | /// Rudimentary check whether an impl exists for a given type and trait; this | ||
112 | /// will actually be done by chalk. | ||
113 | pub(crate) fn implements(db: &impl HirDatabase, trait_ref: TraitRef) -> bool { | ||
114 | // FIXME use all trait impls in the whole crate graph | ||
115 | let krate = trait_ref.trait_.module(db).krate(db); | ||
116 | let krate = match krate { | ||
117 | Some(krate) => krate, | ||
118 | None => return false, | ||
119 | }; | ||
120 | let crate_impl_blocks = db.impls_in_crate(krate); | ||
121 | let mut impl_blocks = crate_impl_blocks.lookup_impl_blocks_for_trait(&trait_ref.trait_); | ||
122 | impl_blocks.any(|impl_block| &impl_block.target_ty(db) == trait_ref.self_ty()) | ||
123 | } | ||
124 | |||
125 | fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option<Crate> { | 111 | fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option<Crate> { |
126 | match ty { | 112 | match ty { |
127 | Ty::Apply(a_ty) => match a_ty.ctor { | 113 | Ty::Apply(a_ty) => match a_ty.ctor { |
@@ -142,6 +128,7 @@ impl Ty { | |||
142 | resolver: &Resolver, | 128 | resolver: &Resolver, |
143 | ) -> Option<(Ty, Function)> { | 129 | ) -> Option<(Ty, Function)> { |
144 | // FIXME: trait methods should be used before autoderefs | 130 | // FIXME: trait methods should be used before autoderefs |
131 | // (and we need to do autoderefs for trait method calls as well) | ||
145 | let inherent_method = self.clone().iterate_methods(db, |ty, f| { | 132 | let inherent_method = self.clone().iterate_methods(db, |ty, f| { |
146 | let sig = f.signature(db); | 133 | let sig = f.signature(db); |
147 | if sig.name() == name && sig.has_self_param() { | 134 | if sig.name() == name && sig.has_self_param() { |
@@ -174,24 +161,15 @@ impl Ty { | |||
174 | } | 161 | } |
175 | } | 162 | } |
176 | } | 163 | } |
177 | // FIXME: | ||
178 | // - we might not actually be able to determine fully that the type | ||
179 | // implements the trait here; it's enough if we (well, Chalk) determine | ||
180 | // that it's possible. | ||
181 | // - when the trait method is picked, we need to register an | ||
182 | // 'obligation' somewhere so that we later check that it's really | ||
183 | // implemented | ||
184 | // - both points go for additional requirements from where clauses as | ||
185 | // well (in fact, the 'implements' condition could just be considered a | ||
186 | // 'where Self: Trait' clause) | ||
187 | candidates.retain(|(t, _m)| { | 164 | candidates.retain(|(t, _m)| { |
188 | // FIXME construct substs of the correct length for the trait | 165 | let trait_ref = |
189 | // - check in rustc whether it does anything smarter than putting variables for everything | 166 | TraitRef { trait_: *t, substs: fresh_substs_for_trait(db, *t, self.clone()) }; |
190 | let trait_ref = TraitRef { trait_: *t, substs: Substs::single(self.clone()) }; | 167 | let (trait_ref, _) = super::traits::canonicalize(trait_ref); |
191 | db.implements(trait_ref) | 168 | db.implements(trait_ref).is_some() |
192 | }); | 169 | }); |
193 | // FIXME if there's multiple candidates here, that's an ambiguity error | 170 | // FIXME if there's multiple candidates here, that's an ambiguity error |
194 | let (_chosen_trait, chosen_method) = candidates.first()?; | 171 | let (_chosen_trait, chosen_method) = candidates.first()?; |
172 | // FIXME return correct receiver type | ||
195 | Some((self.clone(), *chosen_method)) | 173 | Some((self.clone(), *chosen_method)) |
196 | } | 174 | } |
197 | 175 | ||
@@ -254,3 +232,16 @@ impl Ty { | |||
254 | None | 232 | None |
255 | } | 233 | } |
256 | } | 234 | } |
235 | |||
236 | fn fresh_substs_for_trait(db: &impl HirDatabase, tr: Trait, self_ty: Ty) -> Substs { | ||
237 | let mut substs = Vec::new(); | ||
238 | let mut counter = 0; | ||
239 | let generics = tr.generic_params(db); | ||
240 | substs.push(self_ty); | ||
241 | substs.extend(generics.params_including_parent().into_iter().skip(1).map(|_p| { | ||
242 | let fresh_var = Ty::Infer(super::infer::InferTy::TypeVar(super::infer::TypeVarId(counter))); | ||
243 | counter += 1; | ||
244 | fresh_var | ||
245 | })); | ||
246 | substs.into() | ||
247 | } | ||
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index d7c2ca132..291bc9ae5 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -1926,8 +1926,8 @@ fn test() { | |||
1926 | } | 1926 | } |
1927 | "#), | 1927 | "#), |
1928 | @r###" | 1928 | @r###" |
1929 | [31; 35) 'self': &{unknown} | 1929 | [31; 35) 'self': &Self |
1930 | [110; 114) 'self': &{unknown} | 1930 | [110; 114) 'self': &Self |
1931 | [170; 228) '{ ...i128 }': () | 1931 | [170; 228) '{ ...i128 }': () |
1932 | [176; 178) 'S1': S1 | 1932 | [176; 178) 'S1': S1 |
1933 | [176; 187) 'S1.method()': u32 | 1933 | [176; 187) 'S1.method()': u32 |
@@ -1972,8 +1972,8 @@ mod bar_test { | |||
1972 | } | 1972 | } |
1973 | "#), | 1973 | "#), |
1974 | @r###" | 1974 | @r###" |
1975 | [63; 67) 'self': &{unknown} | 1975 | [63; 67) 'self': &Self |
1976 | [169; 173) 'self': &{unknown} | 1976 | [169; 173) 'self': &Self |
1977 | [300; 337) '{ ... }': () | 1977 | [300; 337) '{ ... }': () |
1978 | [310; 311) 'S': S | 1978 | [310; 311) 'S': S |
1979 | [310; 320) 'S.method()': u32 | 1979 | [310; 320) 'S.method()': u32 |
@@ -1998,10 +1998,45 @@ fn test() { | |||
1998 | } | 1998 | } |
1999 | "#), | 1999 | "#), |
2000 | @r###" | 2000 | @r###" |
2001 | [33; 37) 'self': &{unknown} | 2001 | [33; 37) 'self': &Self |
2002 | [92; 111) '{ ...d(); }': () | 2002 | [92; 111) '{ ...d(); }': () |
2003 | [98; 99) 'S': S | 2003 | [98; 99) 'S': S |
2004 | [98; 108) 'S.method()': {unknown}"### | 2004 | [98; 108) 'S.method()': u32"### |
2005 | ); | ||
2006 | } | ||
2007 | |||
2008 | #[test] | ||
2009 | fn infer_trait_method_generic_more_params() { | ||
2010 | // the trait implementation is intentionally incomplete -- it shouldn't matter | ||
2011 | assert_snapshot_matches!( | ||
2012 | infer(r#" | ||
2013 | trait Trait<T1, T2, T3> { | ||
2014 | fn method1(&self) -> (T1, T2, T3); | ||
2015 | fn method2(&self) -> (T3, T2, T1); | ||
2016 | } | ||
2017 | struct S1; | ||
2018 | impl Trait<u8, u16, u32> for S1 {} | ||
2019 | struct S2; | ||
2020 | impl<T> Trait<i8, i16, T> for S2 {} | ||
2021 | fn test() { | ||
2022 | S1.method1(); // u8, u16, u32 | ||
2023 | S1.method2(); // u32, u16, u8 | ||
2024 | S2.method1(); // i8, i16, {unknown} | ||
2025 | S2.method2(); // {unknown}, i16, i8 | ||
2026 | } | ||
2027 | "#), | ||
2028 | @r###" | ||
2029 | [43; 47) 'self': &Self | ||
2030 | [82; 86) 'self': &Self | ||
2031 | [210; 361) '{ ..., i8 }': () | ||
2032 | [216; 218) 'S1': S1 | ||
2033 | [216; 228) 'S1.method1()': (u8, u16, u32) | ||
2034 | [250; 252) 'S1': S1 | ||
2035 | [250; 262) 'S1.method2()': (u32, u16, u8) | ||
2036 | [284; 286) 'S2': S2 | ||
2037 | [284; 296) 'S2.method1()': (i8, i16, {unknown}) | ||
2038 | [324; 326) 'S2': S2 | ||
2039 | [324; 336) 'S2.method2()': ({unknown}, i16, i8)"### | ||
2005 | ); | 2040 | ); |
2006 | } | 2041 | } |
2007 | 2042 | ||
@@ -2020,7 +2055,7 @@ fn test() { | |||
2020 | } | 2055 | } |
2021 | "#), | 2056 | "#), |
2022 | @r###" | 2057 | @r###" |
2023 | [33; 37) 'self': &{unknown} | 2058 | [33; 37) 'self': &Self |
2024 | [102; 127) '{ ...d(); }': () | 2059 | [102; 127) '{ ...d(); }': () |
2025 | [108; 109) 'S': S<u32>(T) -> S<T> | 2060 | [108; 109) 'S': S<u32>(T) -> S<T> |
2026 | [108; 115) 'S(1u32)': S<u32> | 2061 | [108; 115) 'S(1u32)': S<u32> |
@@ -2168,7 +2203,7 @@ fn test() { | |||
2168 | } | 2203 | } |
2169 | "#), | 2204 | "#), |
2170 | @r###" | 2205 | @r###" |
2171 | [29; 33) 'self': {unknown} | 2206 | [29; 33) 'self': Self |
2172 | [107; 198) '{ ...(S); }': () | 2207 | [107; 198) '{ ...(S); }': () |
2173 | [117; 118) 'x': u32 | 2208 | [117; 118) 'x': u32 |
2174 | [126; 127) 'S': S | 2209 | [126; 127) 'S': S |
diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs new file mode 100644 index 000000000..f8c3958bd --- /dev/null +++ b/crates/ra_hir/src/ty/traits.rs | |||
@@ -0,0 +1,112 @@ | |||
1 | //! Stuff that will probably mostly replaced by Chalk. | ||
2 | use std::collections::HashMap; | ||
3 | |||
4 | use crate::db::HirDatabase; | ||
5 | use super::{ TraitRef, Substs, infer::{ TypeVarId, InferTy}, Ty}; | ||
6 | |||
7 | // Copied (and simplified) from Chalk | ||
8 | |||
9 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
10 | /// A (possible) solution for a proposed goal. Usually packaged in a `Result`, | ||
11 | /// where `Err` represents definite *failure* to prove a goal. | ||
12 | pub enum Solution { | ||
13 | /// The goal indeed holds, and there is a unique value for all existential | ||
14 | /// variables. | ||
15 | Unique(Substs), | ||
16 | |||
17 | /// The goal may be provable in multiple ways, but regardless we may have some guidance | ||
18 | /// for type inference. | ||
19 | Ambig(Guidance), | ||
20 | } | ||
21 | |||
22 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
23 | /// When a goal holds ambiguously (e.g., because there are multiple possible | ||
24 | /// solutions), we issue a set of *guidance* back to type inference. | ||
25 | pub enum Guidance { | ||
26 | /// The existential variables *must* have the given values if the goal is | ||
27 | /// ever to hold, but that alone isn't enough to guarantee the goal will | ||
28 | /// actually hold. | ||
29 | Definite(Substs), | ||
30 | |||
31 | /// There are multiple plausible values for the existentials, but the ones | ||
32 | /// here are suggested as the preferred choice heuristically. These should | ||
33 | /// be used for inference fallback only. | ||
34 | Suggested(Substs), | ||
35 | |||
36 | /// There's no useful information to feed back to type inference | ||
37 | Unknown, | ||
38 | } | ||
39 | |||
40 | /// Something that needs to be proven (by Chalk) during type checking, e.g. that | ||
41 | /// a certain type implements a certain trait. Proving the Obligation might | ||
42 | /// result in additional information about inference variables. | ||
43 | /// | ||
44 | /// This might be handled by Chalk when we integrate it? | ||
45 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
46 | pub enum Obligation { | ||
47 | /// Prove that a certain type implements a trait (the type is the `Self` type | ||
48 | /// parameter to the `TraitRef`). | ||
49 | Trait(TraitRef), | ||
50 | } | ||
51 | |||
52 | /// Rudimentary check whether an impl exists for a given type and trait; this | ||
53 | /// will actually be done by chalk. | ||
54 | pub(crate) fn implements(db: &impl HirDatabase, trait_ref: TraitRef) -> Option<Solution> { | ||
55 | // FIXME use all trait impls in the whole crate graph | ||
56 | let krate = trait_ref.trait_.module(db).krate(db); | ||
57 | let krate = match krate { | ||
58 | Some(krate) => krate, | ||
59 | None => return None, | ||
60 | }; | ||
61 | let crate_impl_blocks = db.impls_in_crate(krate); | ||
62 | let mut impl_blocks = crate_impl_blocks.lookup_impl_blocks_for_trait(&trait_ref.trait_); | ||
63 | impl_blocks | ||
64 | .find_map(|impl_block| unify_trait_refs(&trait_ref, &impl_block.target_trait_ref(db)?)) | ||
65 | } | ||
66 | |||
67 | pub(super) fn canonicalize(trait_ref: TraitRef) -> (TraitRef, Vec<TypeVarId>) { | ||
68 | let mut canonical = HashMap::new(); // mapping uncanonical -> canonical | ||
69 | let mut uncanonical = Vec::new(); // mapping canonical -> uncanonical (which is dense) | ||
70 | let mut substs = trait_ref.substs.0.to_vec(); | ||
71 | for ty in &mut substs { | ||
72 | ty.walk_mut(&mut |ty| match ty { | ||
73 | Ty::Infer(InferTy::TypeVar(tv)) => { | ||
74 | let tv: &mut TypeVarId = tv; | ||
75 | *tv = *canonical.entry(*tv).or_insert_with(|| { | ||
76 | let i = uncanonical.len(); | ||
77 | uncanonical.push(*tv); | ||
78 | TypeVarId(i as u32) | ||
79 | }); | ||
80 | } | ||
81 | _ => {} | ||
82 | }); | ||
83 | } | ||
84 | (TraitRef { substs: substs.into(), ..trait_ref }, uncanonical) | ||
85 | } | ||
86 | |||
87 | fn unify_trait_refs(tr1: &TraitRef, tr2: &TraitRef) -> Option<Solution> { | ||
88 | if tr1.trait_ != tr2.trait_ { | ||
89 | return None; | ||
90 | } | ||
91 | let mut solution_substs = Vec::new(); | ||
92 | for (t1, t2) in tr1.substs.0.iter().zip(tr2.substs.0.iter()) { | ||
93 | // this is very bad / hacky 'unification' logic, just enough to make the simple tests pass | ||
94 | match (t1, t2) { | ||
95 | (_, Ty::Infer(InferTy::TypeVar(_))) | (_, Ty::Unknown) | (_, Ty::Param { .. }) => { | ||
96 | // type variable (or similar) in the impl, we just assume it works | ||
97 | } | ||
98 | (Ty::Infer(InferTy::TypeVar(v1)), _) => { | ||
99 | // type variable in the query and fixed type in the impl, record its value | ||
100 | solution_substs.resize_with(v1.0 as usize + 1, || Ty::Unknown); | ||
101 | solution_substs[v1.0 as usize] = t2.clone(); | ||
102 | } | ||
103 | _ => { | ||
104 | // check that they're equal (actually we'd have to recurse etc.) | ||
105 | if t1 != t2 { | ||
106 | return None; | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | Some(Solution::Unique(solution_substs.into())) | ||
112 | } | ||