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