]> git.proxmox.com Git - rustc.git/blob - src/librustc/middle/infer/freshen.rs
1b7e6c33c0575f723d341ec8373005765a0c5147
[rustc.git] / src / librustc / middle / infer / freshen.rs
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.
4 //
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.
10
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.
14 //!
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.
21 //!
22 //! Note that you should be careful not to allow the output of freshening to leak to the user in
23 //! error messages or in any other form. Freshening is only really useful as an internal detail.
24 //!
25 //! __An important detail concerning regions.__ The freshener also replaces *all* regions with
26 //! 'static. The reason behind this is that, in general, we do not take region relationships into
27 //! account when making type-overloaded decisions. This is important because of the design of the
28 //! region inferencer, which is not based on unification but rather on accumulating and then
29 //! solving a set of constraints. In contrast, the type inferencer assigns a value to each type
30 //! variable only once, and it does so as soon as it can, so it is reasonable to ask what the type
31 //! inferencer knows "so far".
32
33 use middle::ty::{self, Ty};
34 use middle::ty_fold;
35 use middle::ty_fold::TypeFoldable;
36 use middle::ty_fold::TypeFolder;
37 use std::collections::hash_map::{self, Entry};
38
39 use super::InferCtxt;
40 use super::unify::InferCtxtMethodsForSimplyUnifiableTypes;
41
42 pub struct TypeFreshener<'a, 'tcx:'a> {
43 infcx: &'a InferCtxt<'a, 'tcx>,
44 freshen_count: u32,
45 freshen_map: hash_map::HashMap<ty::InferTy, Ty<'tcx>>,
46 }
47
48 impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
49 pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> TypeFreshener<'a, 'tcx> {
50 TypeFreshener {
51 infcx: infcx,
52 freshen_count: 0,
53 freshen_map: hash_map::HashMap::new(),
54 }
55 }
56
57 fn freshen<F>(&mut self,
58 opt_ty: Option<Ty<'tcx>>,
59 key: ty::InferTy,
60 freshener: F)
61 -> Ty<'tcx> where
62 F: FnOnce(u32) -> ty::InferTy,
63 {
64 match opt_ty {
65 Some(ty) => { return ty.fold_with(self); }
66 None => { }
67 }
68
69 match self.freshen_map.entry(key) {
70 Entry::Occupied(entry) => *entry.get(),
71 Entry::Vacant(entry) => {
72 let index = self.freshen_count;
73 self.freshen_count += 1;
74 let t = ty::mk_infer(self.infcx.tcx, freshener(index));
75 entry.insert(t);
76 t
77 }
78 }
79 }
80 }
81
82 impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
83 fn tcx<'b>(&'b self) -> &'b ty::ctxt<'tcx> {
84 self.infcx.tcx
85 }
86
87 fn fold_region(&mut self, r: ty::Region) -> ty::Region {
88 match r {
89 ty::ReEarlyBound(..) |
90 ty::ReLateBound(..) => {
91 // leave bound regions alone
92 r
93 }
94
95 ty::ReStatic |
96 ty::ReFree(_) |
97 ty::ReScope(_) |
98 ty::ReInfer(_) |
99 ty::ReEmpty => {
100 // replace all free regions with 'static
101 ty::ReStatic
102 }
103 }
104 }
105
106 fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
107 match t.sty {
108 ty::ty_infer(ty::TyVar(v)) => {
109 self.freshen(self.infcx.type_variables.borrow().probe(v),
110 ty::TyVar(v),
111 ty::FreshTy)
112 }
113
114 ty::ty_infer(ty::IntVar(v)) => {
115 self.freshen(self.infcx.probe_var(v),
116 ty::IntVar(v),
117 ty::FreshIntTy)
118 }
119
120 ty::ty_infer(ty::FloatVar(v)) => {
121 self.freshen(self.infcx.probe_var(v),
122 ty::FloatVar(v),
123 ty::FreshIntTy)
124 }
125
126 ty::ty_infer(ty::FreshTy(c)) |
127 ty::ty_infer(ty::FreshIntTy(c)) => {
128 if c >= self.freshen_count {
129 self.tcx().sess.bug(
130 &format!("Encountered a freshend type with id {} \
131 but our counter is only at {}",
132 c,
133 self.freshen_count));
134 }
135 t
136 }
137
138 ty::ty_open(..) |
139 ty::ty_bool |
140 ty::ty_char |
141 ty::ty_int(..) |
142 ty::ty_uint(..) |
143 ty::ty_float(..) |
144 ty::ty_enum(..) |
145 ty::ty_uniq(..) |
146 ty::ty_str |
147 ty::ty_err |
148 ty::ty_vec(..) |
149 ty::ty_ptr(..) |
150 ty::ty_rptr(..) |
151 ty::ty_bare_fn(..) |
152 ty::ty_trait(..) |
153 ty::ty_struct(..) |
154 ty::ty_closure(..) |
155 ty::ty_tup(..) |
156 ty::ty_projection(..) |
157 ty::ty_param(..) => {
158 ty_fold::super_fold_ty(self, t)
159 }
160 }
161 }
162 }