aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/ty/infer/unify.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/ty/infer/unify.rs')
-rw-r--r--crates/ra_hir/src/ty/infer/unify.rs122
1 files changed, 122 insertions, 0 deletions
diff --git a/crates/ra_hir/src/ty/infer/unify.rs b/crates/ra_hir/src/ty/infer/unify.rs
new file mode 100644
index 000000000..8ca7e957d
--- /dev/null
+++ b/crates/ra_hir/src/ty/infer/unify.rs
@@ -0,0 +1,122 @@
1//! Unification and canonicalization logic.
2
3use crate::db::HirDatabase;
4use crate::ty::{Ty, Canonical, TraitRef, InferTy};
5use super::InferenceContext;
6
7impl<'a, D: HirDatabase> InferenceContext<'a, D> {
8 pub(super) fn canonicalizer<'b>(&'b mut self) -> Canonicalizer<'a, 'b, D>
9 where
10 'a: 'b,
11 {
12 Canonicalizer { ctx: self, free_vars: Vec::new(), var_stack: Vec::new() }
13 }
14}
15
16pub(super) struct Canonicalizer<'a, 'b, D: HirDatabase>
17where
18 'a: 'b,
19{
20 ctx: &'b mut InferenceContext<'a, D>,
21 free_vars: Vec<InferTy>,
22 /// A stack of type variables that is used to detect recursive types (which
23 /// are an error, but we need to protect against them to avoid stack
24 /// overflows).
25 var_stack: Vec<super::TypeVarId>,
26}
27
28pub(super) struct Canonicalized<T> {
29 pub value: Canonical<T>,
30 free_vars: Vec<InferTy>,
31}
32
33impl<'a, 'b, D: HirDatabase> Canonicalizer<'a, 'b, D>
34where
35 'a: 'b,
36{
37 fn add(&mut self, free_var: InferTy) -> usize {
38 self.free_vars.iter().position(|&v| v == free_var).unwrap_or_else(|| {
39 let next_index = self.free_vars.len();
40 self.free_vars.push(free_var);
41 next_index
42 })
43 }
44
45 fn do_canonicalize_ty(&mut self, ty: Ty) -> Ty {
46 ty.fold(&mut |ty| match ty {
47 Ty::Infer(tv) => {
48 let inner = tv.to_inner();
49 if self.var_stack.contains(&inner) {
50 // recursive type
51 return tv.fallback_value();
52 }
53 if let Some(known_ty) = self.ctx.var_unification_table.probe_value(inner).known() {
54 self.var_stack.push(inner);
55 let result = self.do_canonicalize_ty(known_ty.clone());
56 self.var_stack.pop();
57 result
58 } else {
59 let free_var = InferTy::TypeVar(self.ctx.var_unification_table.find(inner));
60 let position = self.add(free_var);
61 Ty::Bound(position as u32)
62 }
63 }
64 _ => ty,
65 })
66 }
67
68 fn do_canonicalize_trait_ref(&mut self, trait_ref: TraitRef) -> TraitRef {
69 let substs = trait_ref
70 .substs
71 .iter()
72 .map(|ty| self.do_canonicalize_ty(ty.clone()))
73 .collect::<Vec<_>>();
74 TraitRef { trait_: trait_ref.trait_, substs: substs.into() }
75 }
76
77 fn into_canonicalized<T>(self, result: T) -> Canonicalized<T> {
78 Canonicalized {
79 value: Canonical { value: result, num_vars: self.free_vars.len() },
80 free_vars: self.free_vars,
81 }
82 }
83
84 pub fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> {
85 let result = self.do_canonicalize_ty(ty);
86 self.into_canonicalized(result)
87 }
88
89 pub fn canonicalize_trait_ref(mut self, trait_ref: TraitRef) -> Canonicalized<TraitRef> {
90 let result = self.do_canonicalize_trait_ref(trait_ref);
91 self.into_canonicalized(result)
92 }
93}
94
95impl<T> Canonicalized<T> {
96 pub fn decanonicalize_ty(&self, ty: Ty) -> Ty {
97 ty.fold(&mut |ty| match ty {
98 Ty::Bound(idx) => {
99 if (idx as usize) < self.free_vars.len() {
100 Ty::Infer(self.free_vars[idx as usize].clone())
101 } else {
102 Ty::Bound(idx)
103 }
104 }
105 ty => ty,
106 })
107 }
108
109 pub fn apply_solution(
110 &self,
111 ctx: &mut InferenceContext<'_, impl HirDatabase>,
112 solution: Canonical<Vec<Ty>>,
113 ) {
114 // the solution may contain new variables, which we need to convert to new inference vars
115 let new_vars =
116 (0..solution.num_vars).map(|_| ctx.new_type_var()).collect::<Vec<_>>().into();
117 for (i, ty) in solution.value.into_iter().enumerate() {
118 let var = self.free_vars[i].clone();
119 ctx.unify(&Ty::Infer(var), &ty.subst_bound_vars(&new_vars));
120 }
121 }
122}