1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use ty::{self, TyCtxt};
12 use ty::fold::{TypeFoldable, TypeFolder};
15 use super::RegionVariableOrigin;
17 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
18 /// This rather funky routine is used while processing expected
19 /// types. What happens here is that we want to propagate a
20 /// coercion through the return type of a fn to its
21 /// argument. Consider the type of `Option::Some`, which is
22 /// basically `for<T> fn(T) -> Option<T>`. So if we have an
23 /// expression `Some(&[1, 2, 3])`, and that has the expected type
24 /// `Option<&[u32]>`, we would like to type check `&[1, 2, 3]`
25 /// with the expectation of `&[u32]`. This will cause us to coerce
26 /// from `&[u32; 3]` to `&[u32]` and make the users life more
29 /// The way we do this is using `fudge_regions_if_ok`. What the
30 /// routine actually does is to start a snapshot and execute the
31 /// closure `f`. In our example above, what this closure will do
32 /// is to unify the expectation (`Option<&[u32]>`) with the actual
33 /// return type (`Option<?T>`, where `?T` represents the variable
34 /// instantiated for `T`). This will cause `?T` to be unified
35 /// with `&?a [u32]`, where `?a` is a fresh lifetime variable. The
36 /// input type (`?T`) is then returned by `f()`.
38 /// At this point, `fudge_regions_if_ok` will normalize all type
39 /// variables, converting `?T` to `&?a [u32]` and end the
40 /// snapshot. The problem is that we can't just return this type
41 /// out, because it references the region variable `?a`, and that
42 /// region variable was popped when we popped the snapshot.
44 /// So what we do is to keep a list (`region_vars`, in the code below)
45 /// of region variables created during the snapshot (here, `?a`). We
46 /// fold the return value and replace any such regions with a *new*
47 /// region variable (e.g., `?b`) and return the result (`&?b [u32]`).
48 /// This can then be used as the expectation for the fn argument.
50 /// The important point here is that, for soundness purposes, the
51 /// regions in question are not particularly important. We will
52 /// use the expected types to guide coercions, but we will still
53 /// type-check the resulting types from those coercions against
54 /// the actual types (`?T`, `Option<?T`) -- and remember that
55 /// after the snapshot is popped, the variable `?T` is no longer
59 /// - no new type variables are created during `f()` (asserted
60 /// below); this simplifies our logic since we don't have to
61 /// check for escaping type variables
62 pub fn fudge_regions_if_ok<T, E, F>(&self,
63 origin: &RegionVariableOrigin,
64 f: F) -> Result<T, E> where
65 F: FnOnce() -> Result<T, E>,
66 T: TypeFoldable<'tcx>,
68 let (region_vars, value) = self.probe(|snapshot| {
69 let vars_at_start = self.type_variables.borrow().num_vars();
73 let value = self.resolve_type_vars_if_possible(&value);
75 // At this point, `value` could in principle refer
76 // to regions that have been created during the
77 // snapshot (we assert below that `f()` does not
78 // create any new type variables, so there
79 // shouldn't be any of those). Once we exit
80 // `probe()`, those are going to be popped, so we
81 // will have to eliminate any references to them.
83 assert_eq!(self.type_variables.borrow().num_vars(), vars_at_start,
84 "type variables were created during fudge_regions_if_ok");
86 self.region_vars.vars_created_since_snapshot(
87 &snapshot.region_vars_snapshot);
89 Ok((region_vars, value))
95 // At this point, we need to replace any of the now-popped
96 // region variables that appear in `value` with a fresh region
97 // variable. We can't do this during the probe because they
98 // would just get popped then too. =)
100 // Micro-optimization: if no variables have been created, then
101 // `value` can't refer to any of them. =) So we can just return it.
102 if region_vars.is_empty() {
106 let mut fudger = RegionFudger {
108 region_vars: ®ion_vars,
112 Ok(value.fold_with(&mut fudger))
116 pub struct RegionFudger<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
117 infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
118 region_vars: &'a Vec<ty::RegionVid>,
119 origin: &'a RegionVariableOrigin,
122 impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionFudger<'a, 'gcx, 'tcx> {
123 fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> {
127 fn fold_region(&mut self, r: &'tcx ty::Region) -> &'tcx ty::Region {
129 ty::ReVar(v) if self.region_vars.contains(&v) => {
130 self.infcx.next_region_var(self.origin.clone())