]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_infer/src/infer/freshen.rs
New upstream version 1.61.0+dfsg1
[rustc.git] / compiler / rustc_infer / src / infer / freshen.rs
1 //! Freshening is the process of replacing unknown variables with fresh types. The idea is that
2 //! the type, after freshening, contains no inference variables but instead contains either a
3 //! value for each variable or fresh "arbitrary" types wherever a variable would have been.
4 //!
5 //! Freshening is used primarily to get a good type for inserting into a cache. The result
6 //! summarizes what the type inferencer knows "so far". The primary place it is used right now is
7 //! in the trait matching algorithm, which needs to be able to cache whether an `impl` self type
8 //! matches some other type X -- *without* affecting `X`. That means if that if the type `X` is in
9 //! fact an unbound type variable, we want the match to be regarded as ambiguous, because depending
10 //! on what type that type variable is ultimately assigned, the match may or may not succeed.
11 //!
12 //! To handle closures, freshened types also have to contain the signature and kind of any
13 //! closure in the local inference context, as otherwise the cache key might be invalidated.
14 //! The way this is done is somewhat hacky - the closure signature is appended to the substs,
15 //! as well as the closure kind "encoded" as a type. Also, special handling is needed when
16 //! the closure signature contains a reference to the original closure.
17 //!
18 //! Note that you should be careful not to allow the output of freshening to leak to the user in
19 //! error messages or in any other form. Freshening is only really useful as an internal detail.
20 //!
21 //! Because of the manipulation required to handle closures, doing arbitrary operations on
22 //! freshened types is not recommended. However, in addition to doing equality/hash
23 //! comparisons (for caching), it is possible to do a `ty::_match` operation between
24 //! 2 freshened types - this works even with the closure encoding.
25 //!
26 //! __An important detail concerning regions.__ The freshener also replaces *all* free regions with
27 //! 'erased. The reason behind this is that, in general, we do not take region relationships into
28 //! account when making type-overloaded decisions. This is important because of the design of the
29 //! region inferencer, which is not based on unification but rather on accumulating and then
30 //! solving a set of constraints. In contrast, the type inferencer assigns a value to each type
31 //! variable only once, and it does so as soon as it can, so it is reasonable to ask what the type
32 //! inferencer knows "so far".
33 use super::InferCtxt;
34 use rustc_data_structures::fx::FxHashMap;
35 use rustc_middle::infer::unify_key::ToType;
36 use rustc_middle::ty::fold::TypeFolder;
37 use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
38 use std::collections::hash_map::Entry;
39
40 pub struct TypeFreshener<'a, 'tcx> {
41 infcx: &'a InferCtxt<'a, 'tcx>,
42 ty_freshen_count: u32,
43 const_freshen_count: u32,
44 ty_freshen_map: FxHashMap<ty::InferTy, Ty<'tcx>>,
45 const_freshen_map: FxHashMap<ty::InferConst<'tcx>, ty::Const<'tcx>>,
46 keep_static: bool,
47 }
48
49 impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
50 pub fn new(infcx: &'a InferCtxt<'a, 'tcx>, keep_static: bool) -> TypeFreshener<'a, 'tcx> {
51 TypeFreshener {
52 infcx,
53 ty_freshen_count: 0,
54 const_freshen_count: 0,
55 ty_freshen_map: Default::default(),
56 const_freshen_map: Default::default(),
57 keep_static,
58 }
59 }
60
61 fn freshen_ty<F>(
62 &mut self,
63 opt_ty: Option<Ty<'tcx>>,
64 key: ty::InferTy,
65 freshener: F,
66 ) -> Ty<'tcx>
67 where
68 F: FnOnce(u32) -> ty::InferTy,
69 {
70 if let Some(ty) = opt_ty {
71 return ty.fold_with(self);
72 }
73
74 match self.ty_freshen_map.entry(key) {
75 Entry::Occupied(entry) => *entry.get(),
76 Entry::Vacant(entry) => {
77 let index = self.ty_freshen_count;
78 self.ty_freshen_count += 1;
79 let t = self.infcx.tcx.mk_ty_infer(freshener(index));
80 entry.insert(t);
81 t
82 }
83 }
84 }
85
86 fn freshen_const<F>(
87 &mut self,
88 opt_ct: Option<ty::Const<'tcx>>,
89 key: ty::InferConst<'tcx>,
90 freshener: F,
91 ty: Ty<'tcx>,
92 ) -> ty::Const<'tcx>
93 where
94 F: FnOnce(u32) -> ty::InferConst<'tcx>,
95 {
96 if let Some(ct) = opt_ct {
97 return ct.fold_with(self);
98 }
99
100 match self.const_freshen_map.entry(key) {
101 Entry::Occupied(entry) => *entry.get(),
102 Entry::Vacant(entry) => {
103 let index = self.const_freshen_count;
104 self.const_freshen_count += 1;
105 let ct = self.infcx.tcx.mk_const_infer(freshener(index), ty);
106 entry.insert(ct);
107 ct
108 }
109 }
110 }
111 }
112
113 impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
114 fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
115 self.infcx.tcx
116 }
117
118 fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
119 match *r {
120 ty::ReLateBound(..) => {
121 // leave bound regions alone
122 r
123 }
124
125 ty::ReEarlyBound(..)
126 | ty::ReFree(_)
127 | ty::ReVar(_)
128 | ty::RePlaceholder(..)
129 | ty::ReEmpty(_)
130 | ty::ReErased => {
131 // replace all free regions with 'erased
132 self.tcx().lifetimes.re_erased
133 }
134 ty::ReStatic => {
135 if self.keep_static {
136 r
137 } else {
138 self.tcx().lifetimes.re_erased
139 }
140 }
141 }
142 }
143
144 fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
145 if !t.needs_infer() && !t.has_erasable_regions() {
146 return t;
147 }
148
149 let tcx = self.infcx.tcx;
150
151 match *t.kind() {
152 ty::Infer(ty::TyVar(v)) => {
153 let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known();
154 self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy)
155 }
156
157 ty::Infer(ty::IntVar(v)) => self.freshen_ty(
158 self.infcx
159 .inner
160 .borrow_mut()
161 .int_unification_table()
162 .probe_value(v)
163 .map(|v| v.to_type(tcx)),
164 ty::IntVar(v),
165 ty::FreshIntTy,
166 ),
167
168 ty::Infer(ty::FloatVar(v)) => self.freshen_ty(
169 self.infcx
170 .inner
171 .borrow_mut()
172 .float_unification_table()
173 .probe_value(v)
174 .map(|v| v.to_type(tcx)),
175 ty::FloatVar(v),
176 ty::FreshFloatTy,
177 ),
178
179 ty::Infer(ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct)) => {
180 if ct >= self.ty_freshen_count {
181 bug!(
182 "Encountered a freshend type with id {} \
183 but our counter is only at {}",
184 ct,
185 self.ty_freshen_count
186 );
187 }
188 t
189 }
190
191 ty::Generator(..)
192 | ty::Bool
193 | ty::Char
194 | ty::Int(..)
195 | ty::Uint(..)
196 | ty::Float(..)
197 | ty::Adt(..)
198 | ty::Str
199 | ty::Error(_)
200 | ty::Array(..)
201 | ty::Slice(..)
202 | ty::RawPtr(..)
203 | ty::Ref(..)
204 | ty::FnDef(..)
205 | ty::FnPtr(_)
206 | ty::Dynamic(..)
207 | ty::Never
208 | ty::Tuple(..)
209 | ty::Projection(..)
210 | ty::Foreign(..)
211 | ty::Param(..)
212 | ty::Closure(..)
213 | ty::GeneratorWitness(..)
214 | ty::Opaque(..) => t.super_fold_with(self),
215
216 ty::Placeholder(..) | ty::Bound(..) => bug!("unexpected type {:?}", t),
217 }
218 }
219
220 fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
221 match ct.val() {
222 ty::ConstKind::Infer(ty::InferConst::Var(v)) => {
223 let opt_ct = self
224 .infcx
225 .inner
226 .borrow_mut()
227 .const_unification_table()
228 .probe_value(v)
229 .val
230 .known();
231 return self.freshen_const(
232 opt_ct,
233 ty::InferConst::Var(v),
234 ty::InferConst::Fresh,
235 ct.ty(),
236 );
237 }
238 ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => {
239 if i >= self.const_freshen_count {
240 bug!(
241 "Encountered a freshend const with id {} \
242 but our counter is only at {}",
243 i,
244 self.const_freshen_count,
245 );
246 }
247 return ct;
248 }
249
250 ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => {
251 bug!("unexpected const {:?}", ct)
252 }
253
254 ty::ConstKind::Param(_)
255 | ty::ConstKind::Value(_)
256 | ty::ConstKind::Unevaluated(..)
257 | ty::ConstKind::Error(_) => {}
258 }
259
260 ct.super_fold_with(self)
261 }
262 }