1 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
2 use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid};
4 use super::type_variable::TypeVariableOrigin;
6 use super::{ConstVariableOrigin, RegionVariableOrigin, UnificationTable};
8 use rustc_data_structures::snapshot_vec as sv;
9 use rustc_data_structures::unify as ut;
14 fn vars_since_snapshot<'tcx, T>(
15 table: &mut UnificationTable<'_, 'tcx, T>,
20 super::UndoLog<'tcx>: From<sv::UndoLog<ut::Delegate<T>>>,
22 T::from_index(snapshot as u32)..T::from_index(table.len() as u32)
25 fn const_vars_since_snapshot<'tcx>(
26 table: &mut UnificationTable<'_, 'tcx, ConstVid<'tcx>>,
28 ) -> (Range<ConstVid<'tcx>>, Vec<ConstVariableOrigin>) {
29 let range = vars_since_snapshot(table, snapshot);
31 range.start..range.end,
32 (range.start.index..range.end.index)
33 .map(|index| table.probe_value(ConstVid::from_index(index)).origin)
38 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
39 /// This rather funky routine is used while processing expected
40 /// types. What happens here is that we want to propagate a
41 /// coercion through the return type of a fn to its
42 /// argument. Consider the type of `Option::Some`, which is
43 /// basically `for<T> fn(T) -> Option<T>`. So if we have an
44 /// expression `Some(&[1, 2, 3])`, and that has the expected type
45 /// `Option<&[u32]>`, we would like to type check `&[1, 2, 3]`
46 /// with the expectation of `&[u32]`. This will cause us to coerce
47 /// from `&[u32; 3]` to `&[u32]` and make the users life more
50 /// The way we do this is using `fudge_inference_if_ok`. What the
51 /// routine actually does is to start a snapshot and execute the
52 /// closure `f`. In our example above, what this closure will do
53 /// is to unify the expectation (`Option<&[u32]>`) with the actual
54 /// return type (`Option<?T>`, where `?T` represents the variable
55 /// instantiated for `T`). This will cause `?T` to be unified
56 /// with `&?a [u32]`, where `?a` is a fresh lifetime variable. The
57 /// input type (`?T`) is then returned by `f()`.
59 /// At this point, `fudge_inference_if_ok` will normalize all type
60 /// variables, converting `?T` to `&?a [u32]` and end the
61 /// snapshot. The problem is that we can't just return this type
62 /// out, because it references the region variable `?a`, and that
63 /// region variable was popped when we popped the snapshot.
65 /// So what we do is to keep a list (`region_vars`, in the code below)
66 /// of region variables created during the snapshot (here, `?a`). We
67 /// fold the return value and replace any such regions with a *new*
68 /// region variable (e.g., `?b`) and return the result (`&?b [u32]`).
69 /// This can then be used as the expectation for the fn argument.
71 /// The important point here is that, for soundness purposes, the
72 /// regions in question are not particularly important. We will
73 /// use the expected types to guide coercions, but we will still
74 /// type-check the resulting types from those coercions against
75 /// the actual types (`?T`, `Option<?T>`) -- and remember that
76 /// after the snapshot is popped, the variable `?T` is no longer
78 pub fn fudge_inference_if_ok<T, E, F>(&self, f: F) -> Result<T, E>
80 F: FnOnce() -> Result<T, E>,
81 T: TypeFoldable<'tcx>,
83 debug!("fudge_inference_if_ok()");
85 let (mut fudger, value) = self.probe(|snapshot| {
88 let value = self.resolve_vars_if_possible(&value);
90 // At this point, `value` could in principle refer
91 // to inference variables that have been created during
92 // the snapshot. Once we exit `probe()`, those are
93 // going to be popped, so we will have to
94 // eliminate any references to them.
96 let mut inner = self.inner.borrow_mut();
98 inner.type_variables().vars_since_snapshot(&snapshot.type_snapshot);
99 let int_vars = vars_since_snapshot(
100 &mut inner.int_unification_table(),
101 snapshot.int_snapshot,
103 let float_vars = vars_since_snapshot(
104 &mut inner.float_unification_table(),
105 snapshot.float_snapshot,
107 let region_vars = inner
108 .unwrap_region_constraints()
109 .vars_since_snapshot(&snapshot.region_constraints_snapshot);
110 let const_vars = const_vars_since_snapshot(
111 &mut inner.const_unification_table(),
112 snapshot.const_snapshot,
115 let fudger = InferenceFudger {
130 // At this point, we need to replace any of the now-popped
131 // type/region variables that appear in `value` with a fresh
132 // variable of the appropriate kind. We can't do this during
133 // the probe because they would just get popped then too. =)
135 // Micro-optimization: if no variables have been created, then
136 // `value` can't refer to any of them. =) So we can just return it.
137 if fudger.type_vars.0.is_empty()
138 && fudger.int_vars.is_empty()
139 && fudger.float_vars.is_empty()
140 && fudger.region_vars.0.is_empty()
141 && fudger.const_vars.0.is_empty()
145 Ok(value.fold_with(&mut fudger))
150 pub struct InferenceFudger<'a, 'tcx> {
151 infcx: &'a InferCtxt<'a, 'tcx>,
152 type_vars: (Range<TyVid>, Vec<TypeVariableOrigin>),
153 int_vars: Range<IntVid>,
154 float_vars: Range<FloatVid>,
155 region_vars: (Range<RegionVid>, Vec<RegionVariableOrigin>),
156 const_vars: (Range<ConstVid<'tcx>>, Vec<ConstVariableOrigin>),
159 impl<'a, 'tcx> TypeFolder<'tcx> for InferenceFudger<'a, 'tcx> {
160 fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
164 fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
166 ty::Infer(ty::InferTy::TyVar(vid)) => {
167 if self.type_vars.0.contains(&vid) {
168 // This variable was created during the fudging.
169 // Recreate it with a fresh variable here.
170 let idx = (vid.index - self.type_vars.0.start.index) as usize;
171 let origin = self.type_vars.1[idx];
172 self.infcx.next_ty_var(origin)
174 // This variable was created before the
175 // "fudging". Since we refresh all type
176 // variables to their binding anyhow, we know
177 // that it is unbound, so we can just return
180 self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown()
185 ty::Infer(ty::InferTy::IntVar(vid)) => {
186 if self.int_vars.contains(&vid) {
187 self.infcx.next_int_var()
192 ty::Infer(ty::InferTy::FloatVar(vid)) => {
193 if self.float_vars.contains(&vid) {
194 self.infcx.next_float_var()
199 _ => ty.super_fold_with(self),
203 fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
204 if let ty::ReVar(vid) = *r {
205 if self.region_vars.0.contains(&vid) {
206 let idx = vid.index() - self.region_vars.0.start.index();
207 let origin = self.region_vars.1[idx];
208 return self.infcx.next_region_var(origin);
214 fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
215 if let ty::Const { val: ty::ConstKind::Infer(ty::InferConst::Var(vid)), ty } = ct {
216 if self.const_vars.0.contains(&vid) {
217 // This variable was created during the fudging.
218 // Recreate it with a fresh variable here.
219 let idx = (vid.index - self.const_vars.0.start.index) as usize;
220 let origin = self.const_vars.1[idx];
221 self.infcx.next_const_var(ty, origin)
226 ct.super_fold_with(self)