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.
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.
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.
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.
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.
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".
34 use rustc_middle::ty::fold::TypeFolder;
35 use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
37 use rustc_data_structures::fx::FxHashMap;
39 use std::collections::hash_map::Entry;
41 use super::unify_key::ToType;
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>>,
53 impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
54 pub fn new(infcx: &'a InferCtxt<'a, 'tcx>, keep_static: bool) -> TypeFreshener<'a, 'tcx> {
58 const_freshen_count: 0,
59 ty_freshen_map: Default::default(),
60 const_freshen_map: Default::default(),
67 opt_ty: Option<Ty<'tcx>>,
72 F: FnOnce(u32) -> ty::InferTy,
74 if let Some(ty) = opt_ty {
75 return ty.fold_with(self);
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));
92 opt_ct: Option<&'tcx ty::Const<'tcx>>,
93 key: ty::InferConst<'tcx>,
96 ) -> &'tcx ty::Const<'tcx>
98 F: FnOnce(u32) -> ty::InferConst<'tcx>,
100 if let Some(ct) = opt_ct {
101 return ct.fold_with(self);
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);
117 impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
118 fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
122 fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
124 ty::ReLateBound(..) => {
125 // leave bound regions alone
132 | ty::RePlaceholder(..)
135 // replace all free regions with 'erased
136 self.tcx().lifetimes.re_erased
139 if self.keep_static {
142 self.tcx().lifetimes.re_erased
148 fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
149 if !t.needs_infer() && !t.has_erasable_regions() {
153 let tcx = self.infcx.tcx;
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)
161 ty::Infer(ty::IntVar(v)) => self.freshen_ty(
165 .int_unification_table()
167 .map(|v| v.to_type(tcx)),
172 ty::Infer(ty::FloatVar(v)) => self.freshen_ty(
176 .float_unification_table()
178 .map(|v| v.to_type(tcx)),
183 ty::Infer(ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct)) => {
184 if ct >= self.ty_freshen_count {
186 "Encountered a freshend type with id {} \
187 but our counter is only at {}",
189 self.ty_freshen_count
217 | ty::GeneratorWitness(..)
218 | ty::Opaque(..) => t.super_fold_with(self),
220 ty::Placeholder(..) | ty::Bound(..) => bug!("unexpected type {:?}", t),
224 fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
226 ty::ConstKind::Infer(ty::InferConst::Var(v)) => {
231 .const_unification_table()
235 return self.freshen_const(
237 ty::InferConst::Var(v),
238 ty::InferConst::Fresh,
242 ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => {
243 if i >= self.const_freshen_count {
245 "Encountered a freshend const with id {} \
246 but our counter is only at {}",
248 self.const_freshen_count,
254 ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => {
255 bug!("unexpected const {:?}", ct)
258 ty::ConstKind::Param(_)
259 | ty::ConstKind::Value(_)
260 | ty::ConstKind::Unevaluated(..)
261 | ty::ConstKind::Error(_) => {}
264 ct.super_fold_with(self)