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