]>
Commit | Line | Data |
---|---|---|
ba9703b0 XL |
1 | use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; |
2 | use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid}; | |
476ff2be | 3 | |
532ac7d7 | 4 | use super::type_variable::TypeVariableOrigin; |
dfeec247 | 5 | use super::InferCtxt; |
f9f354fc | 6 | use super::{ConstVariableOrigin, RegionVariableOrigin, UnificationTable}; |
532ac7d7 | 7 | |
f9f354fc | 8 | use rustc_data_structures::snapshot_vec as sv; |
48663c56 XL |
9 | use rustc_data_structures::unify as ut; |
10 | use ut::UnifyKey; | |
11 | ||
532ac7d7 | 12 | use std::ops::Range; |
476ff2be | 13 | |
f9f354fc XL |
14 | fn vars_since_snapshot<'tcx, T>( |
15 | table: &mut UnificationTable<'_, 'tcx, T>, | |
16 | snapshot_var_len: usize, | |
17 | ) -> Range<T> | |
18 | where | |
19 | T: UnifyKey, | |
20 | super::UndoLog<'tcx>: From<sv::UndoLog<ut::Delegate<T>>>, | |
21 | { | |
22 | T::from_index(snapshot_var_len as u32)..T::from_index(table.len() as u32) | |
23 | } | |
24 | ||
48663c56 | 25 | fn const_vars_since_snapshot<'tcx>( |
f9f354fc XL |
26 | table: &mut UnificationTable<'_, 'tcx, ConstVid<'tcx>>, |
27 | snapshot_var_len: usize, | |
48663c56 | 28 | ) -> (Range<ConstVid<'tcx>>, Vec<ConstVariableOrigin>) { |
f9f354fc | 29 | let range = vars_since_snapshot(table, snapshot_var_len); |
dfeec247 XL |
30 | ( |
31 | range.start..range.end, | |
32 | (range.start.index..range.end.index) | |
33 | .map(|index| table.probe_value(ConstVid::from_index(index)).origin) | |
34 | .collect(), | |
35 | ) | |
48663c56 XL |
36 | } |
37 | ||
f9f354fc XL |
38 | struct VariableLengths { |
39 | type_var_len: usize, | |
40 | const_var_len: usize, | |
41 | int_var_len: usize, | |
42 | float_var_len: usize, | |
43 | region_constraints_len: usize, | |
44 | } | |
45 | ||
dc9dc135 | 46 | impl<'a, 'tcx> InferCtxt<'a, 'tcx> { |
f9f354fc XL |
47 | fn variable_lengths(&self) -> VariableLengths { |
48 | let mut inner = self.inner.borrow_mut(); | |
49 | VariableLengths { | |
50 | type_var_len: inner.type_variables().num_vars(), | |
51 | const_var_len: inner.const_unification_table().len(), | |
52 | int_var_len: inner.int_unification_table().len(), | |
53 | float_var_len: inner.float_unification_table().len(), | |
54 | region_constraints_len: inner.unwrap_region_constraints().num_region_vars(), | |
55 | } | |
56 | } | |
57 | ||
476ff2be SL |
58 | /// This rather funky routine is used while processing expected |
59 | /// types. What happens here is that we want to propagate a | |
60 | /// coercion through the return type of a fn to its | |
61 | /// argument. Consider the type of `Option::Some`, which is | |
62 | /// basically `for<T> fn(T) -> Option<T>`. So if we have an | |
63 | /// expression `Some(&[1, 2, 3])`, and that has the expected type | |
64 | /// `Option<&[u32]>`, we would like to type check `&[1, 2, 3]` | |
65 | /// with the expectation of `&[u32]`. This will cause us to coerce | |
66 | /// from `&[u32; 3]` to `&[u32]` and make the users life more | |
67 | /// pleasant. | |
68 | /// | |
532ac7d7 | 69 | /// The way we do this is using `fudge_inference_if_ok`. What the |
476ff2be SL |
70 | /// routine actually does is to start a snapshot and execute the |
71 | /// closure `f`. In our example above, what this closure will do | |
72 | /// is to unify the expectation (`Option<&[u32]>`) with the actual | |
73 | /// return type (`Option<?T>`, where `?T` represents the variable | |
9fa01778 | 74 | /// instantiated for `T`). This will cause `?T` to be unified |
476ff2be SL |
75 | /// with `&?a [u32]`, where `?a` is a fresh lifetime variable. The |
76 | /// input type (`?T`) is then returned by `f()`. | |
77 | /// | |
532ac7d7 | 78 | /// At this point, `fudge_inference_if_ok` will normalize all type |
476ff2be | 79 | /// variables, converting `?T` to `&?a [u32]` and end the |
9fa01778 | 80 | /// snapshot. The problem is that we can't just return this type |
476ff2be SL |
81 | /// out, because it references the region variable `?a`, and that |
82 | /// region variable was popped when we popped the snapshot. | |
83 | /// | |
84 | /// So what we do is to keep a list (`region_vars`, in the code below) | |
85 | /// of region variables created during the snapshot (here, `?a`). We | |
86 | /// fold the return value and replace any such regions with a *new* | |
87 | /// region variable (e.g., `?b`) and return the result (`&?b [u32]`). | |
88 | /// This can then be used as the expectation for the fn argument. | |
89 | /// | |
90 | /// The important point here is that, for soundness purposes, the | |
91 | /// regions in question are not particularly important. We will | |
92 | /// use the expected types to guide coercions, but we will still | |
93 | /// type-check the resulting types from those coercions against | |
532ac7d7 | 94 | /// the actual types (`?T`, `Option<?T>`) -- and remember that |
476ff2be SL |
95 | /// after the snapshot is popped, the variable `?T` is no longer |
96 | /// unified. | |
dfeec247 XL |
97 | pub fn fudge_inference_if_ok<T, E, F>(&self, f: F) -> Result<T, E> |
98 | where | |
476ff2be SL |
99 | F: FnOnce() -> Result<T, E>, |
100 | T: TypeFoldable<'tcx>, | |
101 | { | |
532ac7d7 | 102 | debug!("fudge_inference_if_ok()"); |
476ff2be | 103 | |
f9f354fc XL |
104 | let variable_lengths = self.variable_lengths(); |
105 | let (mut fudger, value) = self.probe(|_| { | |
476ff2be SL |
106 | match f() { |
107 | Ok(value) => { | |
dc9dc135 | 108 | let value = self.resolve_vars_if_possible(&value); |
476ff2be SL |
109 | |
110 | // At this point, `value` could in principle refer | |
532ac7d7 | 111 | // to inference variables that have been created during |
cc61c64b XL |
112 | // the snapshot. Once we exit `probe()`, those are |
113 | // going to be popped, so we will have to | |
114 | // eliminate any references to them. | |
115 | ||
74b04a01 XL |
116 | let mut inner = self.inner.borrow_mut(); |
117 | let type_vars = | |
f9f354fc XL |
118 | inner.type_variables().vars_since_snapshot(variable_lengths.type_var_len); |
119 | let int_vars = vars_since_snapshot( | |
120 | &mut inner.int_unification_table(), | |
121 | variable_lengths.int_var_len, | |
122 | ); | |
123 | let float_vars = vars_since_snapshot( | |
124 | &mut inner.float_unification_table(), | |
125 | variable_lengths.float_var_len, | |
126 | ); | |
74b04a01 XL |
127 | let region_vars = inner |
128 | .unwrap_region_constraints() | |
f9f354fc | 129 | .vars_since_snapshot(variable_lengths.region_constraints_len); |
48663c56 | 130 | let const_vars = const_vars_since_snapshot( |
f9f354fc XL |
131 | &mut inner.const_unification_table(), |
132 | variable_lengths.const_var_len, | |
48663c56 | 133 | ); |
532ac7d7 XL |
134 | |
135 | let fudger = InferenceFudger { | |
136 | infcx: self, | |
137 | type_vars, | |
138 | int_vars, | |
139 | float_vars, | |
140 | region_vars, | |
48663c56 | 141 | const_vars, |
532ac7d7 | 142 | }; |
476ff2be | 143 | |
532ac7d7 | 144 | Ok((fudger, value)) |
476ff2be SL |
145 | } |
146 | Err(e) => Err(e), | |
147 | } | |
148 | })?; | |
149 | ||
150 | // At this point, we need to replace any of the now-popped | |
cc61c64b XL |
151 | // type/region variables that appear in `value` with a fresh |
152 | // variable of the appropriate kind. We can't do this during | |
153 | // the probe because they would just get popped then too. =) | |
476ff2be SL |
154 | |
155 | // Micro-optimization: if no variables have been created, then | |
156 | // `value` can't refer to any of them. =) So we can just return it. | |
dfeec247 XL |
157 | if fudger.type_vars.0.is_empty() |
158 | && fudger.int_vars.is_empty() | |
159 | && fudger.float_vars.is_empty() | |
160 | && fudger.region_vars.0.is_empty() | |
161 | && fudger.const_vars.0.is_empty() | |
162 | { | |
532ac7d7 XL |
163 | Ok(value) |
164 | } else { | |
165 | Ok(value.fold_with(&mut fudger)) | |
476ff2be | 166 | } |
476ff2be SL |
167 | } |
168 | } | |
169 | ||
dc9dc135 XL |
170 | pub struct InferenceFudger<'a, 'tcx> { |
171 | infcx: &'a InferCtxt<'a, 'tcx>, | |
532ac7d7 XL |
172 | type_vars: (Range<TyVid>, Vec<TypeVariableOrigin>), |
173 | int_vars: Range<IntVid>, | |
174 | float_vars: Range<FloatVid>, | |
175 | region_vars: (Range<RegionVid>, Vec<RegionVariableOrigin>), | |
48663c56 | 176 | const_vars: (Range<ConstVid<'tcx>>, Vec<ConstVariableOrigin>), |
476ff2be SL |
177 | } |
178 | ||
dc9dc135 XL |
179 | impl<'a, 'tcx> TypeFolder<'tcx> for InferenceFudger<'a, 'tcx> { |
180 | fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { | |
476ff2be SL |
181 | self.infcx.tcx |
182 | } | |
183 | ||
cc61c64b | 184 | fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { |
e74abb32 | 185 | match ty.kind { |
b7449926 | 186 | ty::Infer(ty::InferTy::TyVar(vid)) => { |
532ac7d7 XL |
187 | if self.type_vars.0.contains(&vid) { |
188 | // This variable was created during the fudging. | |
189 | // Recreate it with a fresh variable here. | |
190 | let idx = (vid.index - self.type_vars.0.start.index) as usize; | |
191 | let origin = self.type_vars.1[idx]; | |
192 | self.infcx.next_ty_var(origin) | |
193 | } else { | |
194 | // This variable was created before the | |
195 | // "fudging". Since we refresh all type | |
196 | // variables to their binding anyhow, we know | |
197 | // that it is unbound, so we can just return | |
198 | // it. | |
74b04a01 | 199 | debug_assert!( |
f9f354fc | 200 | self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown() |
74b04a01 | 201 | ); |
532ac7d7 XL |
202 | ty |
203 | } | |
204 | } | |
205 | ty::Infer(ty::InferTy::IntVar(vid)) => { | |
206 | if self.int_vars.contains(&vid) { | |
207 | self.infcx.next_int_var() | |
208 | } else { | |
209 | ty | |
210 | } | |
211 | } | |
212 | ty::Infer(ty::InferTy::FloatVar(vid)) => { | |
213 | if self.float_vars.contains(&vid) { | |
214 | self.infcx.next_float_var() | |
215 | } else { | |
216 | ty | |
cc61c64b XL |
217 | } |
218 | } | |
219 | _ => ty.super_fold_with(self), | |
220 | } | |
221 | } | |
222 | ||
7cac9316 | 223 | fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { |
48663c56 | 224 | if let ty::ReVar(vid) = *r { |
532ac7d7 | 225 | if self.region_vars.0.contains(&vid) { |
48663c56 | 226 | let idx = vid.index() - self.region_vars.0.start.index(); |
532ac7d7 XL |
227 | let origin = self.region_vars.1[idx]; |
228 | return self.infcx.next_region_var(origin); | |
476ff2be SL |
229 | } |
230 | } | |
532ac7d7 | 231 | r |
476ff2be | 232 | } |
48663c56 XL |
233 | |
234 | fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { | |
60c5eb7d | 235 | if let ty::Const { val: ty::ConstKind::Infer(ty::InferConst::Var(vid)), ty } = ct { |
48663c56 XL |
236 | if self.const_vars.0.contains(&vid) { |
237 | // This variable was created during the fudging. | |
238 | // Recreate it with a fresh variable here. | |
239 | let idx = (vid.index - self.const_vars.0.start.index) as usize; | |
240 | let origin = self.const_vars.1[idx]; | |
241 | self.infcx.next_const_var(ty, origin) | |
242 | } else { | |
243 | ct | |
244 | } | |
245 | } else { | |
246 | ct.super_fold_with(self) | |
247 | } | |
248 | } | |
476ff2be | 249 | } |