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