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