1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Freshening is the process of replacing unknown variables with fresh types. The idea is that
12 //! the type, after freshening, contains no inference variables but instead contains either a
13 //! value for each variable or fresh "arbitrary" types wherever a variable would have been.
15 //! Freshening is used primarily to get a good type for inserting into a cache. The result
16 //! summarizes what the type inferencer knows "so far". The primary place it is used right now is
17 //! in the trait matching algorithm, which needs to be able to cache whether an `impl` self type
18 //! matches some other type X -- *without* affecting `X`. That means if that if the type `X` is in
19 //! fact an unbound type variable, we want the match to be regarded as ambiguous, because depending
20 //! on what type that type variable is ultimately assigned, the match may or may not succeed.
22 //! To handle closures, freshened types also have to contain the signature and kind of any
23 //! closure in the local inference context, as otherwise the cache key might be invalidated.
24 //! The way this is done is somewhat hacky - the closure signature is appended to the substs,
25 //! as well as the closure kind "encoded" as a type. Also, special handling is needed when
26 //! the closure signature contains a reference to the original closure.
28 //! Note that you should be careful not to allow the output of freshening to leak to the user in
29 //! error messages or in any other form. Freshening is only really useful as an internal detail.
31 //! Because of the manipulation required to handle closures, doing arbitrary operations on
32 //! freshened types is not recommended. However, in addition to doing equality/hash
33 //! comparisons (for caching), it is possible to do a `ty::_match` operation between
34 //! 2 freshened types - this works even with the closure encoding.
36 //! __An important detail concerning regions.__ The freshener also replaces *all* free regions with
37 //! 'erased. The reason behind this is that, in general, we do not take region relationships into
38 //! account when making type-overloaded decisions. This is important because of the design of the
39 //! region inferencer, which is not based on unification but rather on accumulating and then
40 //! solving a set of constraints. In contrast, the type inferencer assigns a value to each type
41 //! variable only once, and it does so as soon as it can, so it is reasonable to ask what the type
42 //! inferencer knows "so far".
44 use ty
::{self, Ty, TyCtxt, TypeFoldable}
;
45 use ty
::fold
::TypeFolder
;
46 use util
::nodemap
::FxHashMap
;
48 use std
::collections
::hash_map
::Entry
;
51 use super::unify_key
::ToType
;
53 pub struct TypeFreshener
<'a
, 'gcx
: 'a
+'tcx
, 'tcx
: 'a
> {
54 infcx
: &'a InferCtxt
<'a
, 'gcx
, 'tcx
>,
56 freshen_map
: FxHashMap
<ty
::InferTy
, Ty
<'tcx
>>,
59 impl<'a
, 'gcx
, 'tcx
> TypeFreshener
<'a
, 'gcx
, 'tcx
> {
60 pub fn new(infcx
: &'a InferCtxt
<'a
, 'gcx
, 'tcx
>)
61 -> TypeFreshener
<'a
, 'gcx
, 'tcx
> {
65 freshen_map
: Default
::default(),
69 fn freshen
<F
>(&mut self,
70 opt_ty
: Option
<Ty
<'tcx
>>,
74 F
: FnOnce(u32) -> ty
::InferTy
,
76 if let Some(ty
) = opt_ty
{
77 return ty
.fold_with(self);
80 match self.freshen_map
.entry(key
) {
81 Entry
::Occupied(entry
) => *entry
.get(),
82 Entry
::Vacant(entry
) => {
83 let index
= self.freshen_count
;
84 self.freshen_count
+= 1;
85 let t
= self.infcx
.tcx
.mk_infer(freshener(index
));
93 impl<'a
, 'gcx
, 'tcx
> TypeFolder
<'gcx
, 'tcx
> for TypeFreshener
<'a
, 'gcx
, 'tcx
> {
94 fn tcx
<'b
>(&'b
self) -> TyCtxt
<'b
, 'gcx
, 'tcx
> {
98 fn fold_region(&mut self, r
: ty
::Region
<'tcx
>) -> ty
::Region
<'tcx
> {
100 ty
::ReLateBound(..) => {
101 // leave bound regions alone
106 ty
::ReEarlyBound(..) |
110 ty
::RePlaceholder(..) |
113 // replace all free regions with 'erased
114 self.tcx().types
.re_erased
117 ty
::ReCanonical(..) |
118 ty
::ReClosureBound(..) => {
120 "encountered unexpected region: {:?}",
127 fn fold_ty(&mut self, t
: Ty
<'tcx
>) -> Ty
<'tcx
> {
128 if !t
.needs_infer() && !t
.has_erasable_regions() &&
129 !(t
.has_closure_types() && self.infcx
.in_progress_tables
.is_some()) {
133 let tcx
= self.infcx
.tcx
;
136 ty
::Infer(ty
::TyVar(v
)) => {
137 let opt_ty
= self.infcx
.type_variables
.borrow_mut().probe(v
).known();
144 ty
::Infer(ty
::IntVar(v
)) => {
146 self.infcx
.int_unification_table
.borrow_mut()
148 .map(|v
| v
.to_type(tcx
)),
153 ty
::Infer(ty
::FloatVar(v
)) => {
155 self.infcx
.float_unification_table
.borrow_mut()
157 .map(|v
| v
.to_type(tcx
)),
162 ty
::Infer(ty
::FreshTy(c
)) |
163 ty
::Infer(ty
::FreshIntTy(c
)) |
164 ty
::Infer(ty
::FreshFloatTy(c
)) => {
165 if c
>= self.freshen_count
{
166 bug
!("Encountered a freshend type with id {} \
167 but our counter is only at {}",
174 ty
::Infer(ty
::BoundTy(..)) =>
175 bug
!("encountered canonical ty during freshening"),
196 ty
::UnnormalizedProjection(..) |
200 ty
::GeneratorWitness(..) |
202 t
.super_fold_with(self)