]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_infer/src/infer/freshen.rs
Move `{core,std}::stream::Stream` to `{core,std}::async_iter::AsyncIterator`.
[rust.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
34 use rustc_middle::ty::fold::TypeFolder;
35 use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
36
37 use rustc_data_structures::fx::FxHashMap;
38
39 use std::collections::hash_map::Entry;
40
41 use super::unify_key::ToType;
42 use super::InferCtxt;
43
44 pub struct TypeFreshener<'a, 'tcx> {
45     infcx: &'a InferCtxt<'a, 'tcx>,
46     ty_freshen_count: u32,
47     const_freshen_count: u32,
48     ty_freshen_map: FxHashMap<ty::InferTy, Ty<'tcx>>,
49     const_freshen_map: FxHashMap<ty::InferConst<'tcx>, &'tcx ty::Const<'tcx>>,
50     keep_static: bool,
51 }
52
53 impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
54     pub fn new(infcx: &'a InferCtxt<'a, 'tcx>, keep_static: bool) -> TypeFreshener<'a, 'tcx> {
55         TypeFreshener {
56             infcx,
57             ty_freshen_count: 0,
58             const_freshen_count: 0,
59             ty_freshen_map: Default::default(),
60             const_freshen_map: Default::default(),
61             keep_static,
62         }
63     }
64
65     fn freshen_ty<F>(
66         &mut self,
67         opt_ty: Option<Ty<'tcx>>,
68         key: ty::InferTy,
69         freshener: F,
70     ) -> Ty<'tcx>
71     where
72         F: FnOnce(u32) -> ty::InferTy,
73     {
74         if let Some(ty) = opt_ty {
75             return ty.fold_with(self);
76         }
77
78         match self.ty_freshen_map.entry(key) {
79             Entry::Occupied(entry) => *entry.get(),
80             Entry::Vacant(entry) => {
81                 let index = self.ty_freshen_count;
82                 self.ty_freshen_count += 1;
83                 let t = self.infcx.tcx.mk_ty_infer(freshener(index));
84                 entry.insert(t);
85                 t
86             }
87         }
88     }
89
90     fn freshen_const<F>(
91         &mut self,
92         opt_ct: Option<&'tcx ty::Const<'tcx>>,
93         key: ty::InferConst<'tcx>,
94         freshener: F,
95         ty: Ty<'tcx>,
96     ) -> &'tcx ty::Const<'tcx>
97     where
98         F: FnOnce(u32) -> ty::InferConst<'tcx>,
99     {
100         if let Some(ct) = opt_ct {
101             return ct.fold_with(self);
102         }
103
104         match self.const_freshen_map.entry(key) {
105             Entry::Occupied(entry) => *entry.get(),
106             Entry::Vacant(entry) => {
107                 let index = self.const_freshen_count;
108                 self.const_freshen_count += 1;
109                 let ct = self.infcx.tcx.mk_const_infer(freshener(index), ty);
110                 entry.insert(ct);
111                 ct
112             }
113         }
114     }
115 }
116
117 impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
118     fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
119         self.infcx.tcx
120     }
121
122     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
123         match *r {
124             ty::ReLateBound(..) => {
125                 // leave bound regions alone
126                 r
127             }
128
129             ty::ReEarlyBound(..)
130             | ty::ReFree(_)
131             | ty::ReVar(_)
132             | ty::RePlaceholder(..)
133             | ty::ReEmpty(_)
134             | ty::ReErased => {
135                 // replace all free regions with 'erased
136                 self.tcx().lifetimes.re_erased
137             }
138             ty::ReStatic => {
139                 if self.keep_static {
140                     r
141                 } else {
142                     self.tcx().lifetimes.re_erased
143                 }
144             }
145         }
146     }
147
148     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
149         if !t.needs_infer() && !t.has_erasable_regions() {
150             return t;
151         }
152
153         let tcx = self.infcx.tcx;
154
155         match *t.kind() {
156             ty::Infer(ty::TyVar(v)) => {
157                 let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known();
158                 self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy)
159             }
160
161             ty::Infer(ty::IntVar(v)) => self.freshen_ty(
162                 self.infcx
163                     .inner
164                     .borrow_mut()
165                     .int_unification_table()
166                     .probe_value(v)
167                     .map(|v| v.to_type(tcx)),
168                 ty::IntVar(v),
169                 ty::FreshIntTy,
170             ),
171
172             ty::Infer(ty::FloatVar(v)) => self.freshen_ty(
173                 self.infcx
174                     .inner
175                     .borrow_mut()
176                     .float_unification_table()
177                     .probe_value(v)
178                     .map(|v| v.to_type(tcx)),
179                 ty::FloatVar(v),
180                 ty::FreshFloatTy,
181             ),
182
183             ty::Infer(ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct)) => {
184                 if ct >= self.ty_freshen_count {
185                     bug!(
186                         "Encountered a freshend type with id {} \
187                           but our counter is only at {}",
188                         ct,
189                         self.ty_freshen_count
190                     );
191                 }
192                 t
193             }
194
195             ty::Generator(..)
196             | ty::Bool
197             | ty::Char
198             | ty::Int(..)
199             | ty::Uint(..)
200             | ty::Float(..)
201             | ty::Adt(..)
202             | ty::Str
203             | ty::Error(_)
204             | ty::Array(..)
205             | ty::Slice(..)
206             | ty::RawPtr(..)
207             | ty::Ref(..)
208             | ty::FnDef(..)
209             | ty::FnPtr(_)
210             | ty::Dynamic(..)
211             | ty::Never
212             | ty::Tuple(..)
213             | ty::Projection(..)
214             | ty::Foreign(..)
215             | ty::Param(..)
216             | ty::Closure(..)
217             | ty::GeneratorWitness(..)
218             | ty::Opaque(..) => t.super_fold_with(self),
219
220             ty::Placeholder(..) | ty::Bound(..) => bug!("unexpected type {:?}", t),
221         }
222     }
223
224     fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
225         match ct.val {
226             ty::ConstKind::Infer(ty::InferConst::Var(v)) => {
227                 let opt_ct = self
228                     .infcx
229                     .inner
230                     .borrow_mut()
231                     .const_unification_table()
232                     .probe_value(v)
233                     .val
234                     .known();
235                 return self.freshen_const(
236                     opt_ct,
237                     ty::InferConst::Var(v),
238                     ty::InferConst::Fresh,
239                     ct.ty,
240                 );
241             }
242             ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => {
243                 if i >= self.const_freshen_count {
244                     bug!(
245                         "Encountered a freshend const with id {} \
246                             but our counter is only at {}",
247                         i,
248                         self.const_freshen_count,
249                     );
250                 }
251                 return ct;
252             }
253
254             ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => {
255                 bug!("unexpected const {:?}", ct)
256             }
257
258             ty::ConstKind::Param(_)
259             | ty::ConstKind::Value(_)
260             | ty::ConstKind::Unevaluated(..)
261             | ty::ConstKind::Error(_) => {}
262         }
263
264         ct.super_fold_with(self)
265     }
266 }