diff options
Diffstat (limited to 'crates/ra_hir_ty/src/infer/unify.rs')
-rw-r--r-- | crates/ra_hir_ty/src/infer/unify.rs | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs new file mode 100644 index 000000000..f3a875678 --- /dev/null +++ b/crates/ra_hir_ty/src/infer/unify.rs | |||
@@ -0,0 +1,162 @@ | |||
1 | //! Unification and canonicalization logic. | ||
2 | |||
3 | use super::{InferenceContext, Obligation}; | ||
4 | use crate::{ | ||
5 | db::HirDatabase, utils::make_mut_slice, Canonical, InEnvironment, InferTy, ProjectionPredicate, | ||
6 | ProjectionTy, Substs, TraitRef, Ty, TypeWalk, | ||
7 | }; | ||
8 | |||
9 | impl<'a, D: HirDatabase> InferenceContext<'a, D> { | ||
10 | pub(super) fn canonicalizer<'b>(&'b mut self) -> Canonicalizer<'a, 'b, D> | ||
11 | where | ||
12 | 'a: 'b, | ||
13 | { | ||
14 | Canonicalizer { ctx: self, free_vars: Vec::new(), var_stack: Vec::new() } | ||
15 | } | ||
16 | } | ||
17 | |||
18 | pub(super) struct Canonicalizer<'a, 'b, D: HirDatabase> | ||
19 | where | ||
20 | 'a: 'b, | ||
21 | { | ||
22 | ctx: &'b mut InferenceContext<'a, D>, | ||
23 | free_vars: Vec<InferTy>, | ||
24 | /// A stack of type variables that is used to detect recursive types (which | ||
25 | /// are an error, but we need to protect against them to avoid stack | ||
26 | /// overflows). | ||
27 | var_stack: Vec<super::TypeVarId>, | ||
28 | } | ||
29 | |||
30 | pub(super) struct Canonicalized<T> { | ||
31 | pub value: Canonical<T>, | ||
32 | free_vars: Vec<InferTy>, | ||
33 | } | ||
34 | |||
35 | impl<'a, 'b, D: HirDatabase> Canonicalizer<'a, 'b, D> | ||
36 | where | ||
37 | 'a: 'b, | ||
38 | { | ||
39 | fn add(&mut self, free_var: InferTy) -> usize { | ||
40 | self.free_vars.iter().position(|&v| v == free_var).unwrap_or_else(|| { | ||
41 | let next_index = self.free_vars.len(); | ||
42 | self.free_vars.push(free_var); | ||
43 | next_index | ||
44 | }) | ||
45 | } | ||
46 | |||
47 | fn do_canonicalize_ty(&mut self, ty: Ty) -> Ty { | ||
48 | ty.fold(&mut |ty| match ty { | ||
49 | Ty::Infer(tv) => { | ||
50 | let inner = tv.to_inner(); | ||
51 | if self.var_stack.contains(&inner) { | ||
52 | // recursive type | ||
53 | return tv.fallback_value(); | ||
54 | } | ||
55 | if let Some(known_ty) = | ||
56 | self.ctx.var_unification_table.inlined_probe_value(inner).known() | ||
57 | { | ||
58 | self.var_stack.push(inner); | ||
59 | let result = self.do_canonicalize_ty(known_ty.clone()); | ||
60 | self.var_stack.pop(); | ||
61 | result | ||
62 | } else { | ||
63 | let root = self.ctx.var_unification_table.find(inner); | ||
64 | let free_var = match tv { | ||
65 | InferTy::TypeVar(_) => InferTy::TypeVar(root), | ||
66 | InferTy::IntVar(_) => InferTy::IntVar(root), | ||
67 | InferTy::FloatVar(_) => InferTy::FloatVar(root), | ||
68 | InferTy::MaybeNeverTypeVar(_) => InferTy::MaybeNeverTypeVar(root), | ||
69 | }; | ||
70 | let position = self.add(free_var); | ||
71 | Ty::Bound(position as u32) | ||
72 | } | ||
73 | } | ||
74 | _ => ty, | ||
75 | }) | ||
76 | } | ||
77 | |||
78 | fn do_canonicalize_trait_ref(&mut self, mut trait_ref: TraitRef) -> TraitRef { | ||
79 | for ty in make_mut_slice(&mut trait_ref.substs.0) { | ||
80 | *ty = self.do_canonicalize_ty(ty.clone()); | ||
81 | } | ||
82 | trait_ref | ||
83 | } | ||
84 | |||
85 | fn into_canonicalized<T>(self, result: T) -> Canonicalized<T> { | ||
86 | Canonicalized { | ||
87 | value: Canonical { value: result, num_vars: self.free_vars.len() }, | ||
88 | free_vars: self.free_vars, | ||
89 | } | ||
90 | } | ||
91 | |||
92 | fn do_canonicalize_projection_ty(&mut self, mut projection_ty: ProjectionTy) -> ProjectionTy { | ||
93 | for ty in make_mut_slice(&mut projection_ty.parameters.0) { | ||
94 | *ty = self.do_canonicalize_ty(ty.clone()); | ||
95 | } | ||
96 | projection_ty | ||
97 | } | ||
98 | |||
99 | fn do_canonicalize_projection_predicate( | ||
100 | &mut self, | ||
101 | projection: ProjectionPredicate, | ||
102 | ) -> ProjectionPredicate { | ||
103 | let ty = self.do_canonicalize_ty(projection.ty); | ||
104 | let projection_ty = self.do_canonicalize_projection_ty(projection.projection_ty); | ||
105 | |||
106 | ProjectionPredicate { ty, projection_ty } | ||
107 | } | ||
108 | |||
109 | // FIXME: add some point, we need to introduce a `Fold` trait that abstracts | ||
110 | // over all the things that can be canonicalized (like Chalk and rustc have) | ||
111 | |||
112 | pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> { | ||
113 | let result = self.do_canonicalize_ty(ty); | ||
114 | self.into_canonicalized(result) | ||
115 | } | ||
116 | |||
117 | pub(crate) fn canonicalize_obligation( | ||
118 | mut self, | ||
119 | obligation: InEnvironment<Obligation>, | ||
120 | ) -> Canonicalized<InEnvironment<Obligation>> { | ||
121 | let result = match obligation.value { | ||
122 | Obligation::Trait(tr) => Obligation::Trait(self.do_canonicalize_trait_ref(tr)), | ||
123 | Obligation::Projection(pr) => { | ||
124 | Obligation::Projection(self.do_canonicalize_projection_predicate(pr)) | ||
125 | } | ||
126 | }; | ||
127 | self.into_canonicalized(InEnvironment { | ||
128 | value: result, | ||
129 | environment: obligation.environment, | ||
130 | }) | ||
131 | } | ||
132 | } | ||
133 | |||
134 | impl<T> Canonicalized<T> { | ||
135 | pub fn decanonicalize_ty(&self, mut ty: Ty) -> Ty { | ||
136 | ty.walk_mut_binders( | ||
137 | &mut |ty, binders| match ty { | ||
138 | &mut Ty::Bound(idx) => { | ||
139 | if idx as usize >= binders && (idx as usize - binders) < self.free_vars.len() { | ||
140 | *ty = Ty::Infer(self.free_vars[idx as usize - binders]); | ||
141 | } | ||
142 | } | ||
143 | _ => {} | ||
144 | }, | ||
145 | 0, | ||
146 | ); | ||
147 | ty | ||
148 | } | ||
149 | |||
150 | pub fn apply_solution( | ||
151 | &self, | ||
152 | ctx: &mut InferenceContext<'_, impl HirDatabase>, | ||
153 | solution: Canonical<Vec<Ty>>, | ||
154 | ) { | ||
155 | // the solution may contain new variables, which we need to convert to new inference vars | ||
156 | let new_vars = Substs((0..solution.num_vars).map(|_| ctx.new_type_var()).collect()); | ||
157 | for (i, ty) in solution.value.into_iter().enumerate() { | ||
158 | let var = self.free_vars[i]; | ||
159 | ctx.unify(&Ty::Infer(var), &ty.subst_bound_vars(&new_vars)); | ||
160 | } | ||
161 | } | ||
162 | } | ||