]> git.lizzy.rs Git - rust.git/blob - src/librustc/infer/higher_ranked/mod.rs
ec77bb39e7d2afd6a22a75c5aaf173e156c3a955
[rust.git] / src / librustc / infer / higher_ranked / mod.rs
1 //! Helper routines for higher-ranked things. See the `doc` module at
2 //! the end of the file for details.
3
4 use super::combine::CombineFields;
5 use super::{HigherRankedType, InferCtxt, PlaceholderMap};
6
7 use crate::infer::{CombinedSnapshot, ConstVariableOrigin};
8 use crate::ty::relate::{Relate, RelateResult, TypeRelation};
9 use crate::ty::{self, Binder, TypeFoldable};
10
11 use syntax_pos::DUMMY_SP;
12
13 impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
14     pub fn higher_ranked_sub<T>(
15         &mut self,
16         a: &Binder<T>,
17         b: &Binder<T>,
18         a_is_expected: bool,
19     ) -> RelateResult<'tcx, Binder<T>>
20     where
21         T: Relate<'tcx>,
22     {
23         debug!("higher_ranked_sub(a={:?}, b={:?})", a, b);
24
25         // Rather than checking the subtype relationship between `a` and `b`
26         // as-is, we need to do some extra work here in order to make sure
27         // that function subtyping works correctly with respect to regions
28         //
29         // Note: this is a subtle algorithm.  For a full explanation,
30         // please see the large comment at the end of the file in the (inlined) module
31         // `doc`.
32
33         let span = self.trace.cause.span;
34
35         return self.infcx.commit_if_ok(|snapshot| {
36             // First, we instantiate each bound region in the supertype with a
37             // fresh placeholder region.
38             let (b_prime, placeholder_map) = self.infcx.replace_bound_vars_with_placeholders(b);
39
40             // Next, we instantiate each bound region in the subtype
41             // with a fresh region variable. These region variables --
42             // but no other pre-existing region variables -- can name
43             // the placeholders.
44             let (a_prime, _) =
45                 self.infcx
46                     .replace_bound_vars_with_fresh_vars(span, HigherRankedType, a);
47
48             debug!("a_prime={:?}", a_prime);
49             debug!("b_prime={:?}", b_prime);
50
51             // Compare types now that bound regions have been replaced.
52             let result = self.sub(a_is_expected).relate(&a_prime, &b_prime)?;
53
54             self.infcx
55                 .leak_check(!a_is_expected, &placeholder_map, snapshot)?;
56
57             debug!("higher_ranked_sub: OK result={:?}", result);
58
59             Ok(ty::Binder::bind(result))
60         });
61     }
62 }
63
64 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
65     /// Replaces all regions (resp. types) bound by `binder` with placeholder
66     /// regions (resp. types) and return a map indicating which bound-region
67     /// placeholder region. This is the first step of checking subtyping
68     /// when higher-ranked things are involved.
69     ///
70     /// **Important:** you must call this function from within a snapshot.
71     /// Moreover, before committing the snapshot, you must eventually call
72     /// either `plug_leaks` or `pop_placeholders` to remove the placeholder
73     /// regions. If you rollback the snapshot (or are using a probe), then
74     /// the pop occurs as part of the rollback, so an explicit call is not
75     /// needed (but is also permitted).
76     ///
77     /// For more information about how placeholders and HRTBs work, see
78     /// the [rustc guide].
79     ///
80     /// [rustc guide]: https://rust-lang.github.io/rustc-guide/traits/hrtb.html
81     pub fn replace_bound_vars_with_placeholders<T>(
82         &self,
83         binder: &ty::Binder<T>,
84     ) -> (T, PlaceholderMap<'tcx>)
85     where
86         T: TypeFoldable<'tcx>,
87     {
88         let next_universe = self.create_next_universe();
89
90         let fld_r = |br| {
91             self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion {
92                 universe: next_universe,
93                 name: br,
94             }))
95         };
96
97         let fld_t = |bound_ty: ty::BoundTy| {
98             self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType {
99                 universe: next_universe,
100                 name: bound_ty.var,
101             }))
102         };
103
104         let fld_c = |_: ty::BoundVar, ty| {
105             self.next_const_var_in_universe(
106                 ty,
107                 // FIXME(const_generics): do we want a placeholder const?
108                 ConstVariableOrigin::MiscVariable(DUMMY_SP),
109                 next_universe,
110             )
111         };
112
113         let (result, map) = self.tcx.replace_bound_vars(binder, fld_r, fld_t, fld_c);
114
115         debug!(
116             "replace_bound_vars_with_placeholders(\
117              next_universe={:?}, \
118              binder={:?}, \
119              result={:?}, \
120              map={:?})",
121             next_universe, binder, result, map,
122         );
123
124         (result, map)
125     }
126
127     /// See `infer::region_constraints::RegionConstraintCollector::leak_check`.
128     pub fn leak_check(
129         &self,
130         overly_polymorphic: bool,
131         placeholder_map: &PlaceholderMap<'tcx>,
132         snapshot: &CombinedSnapshot<'_, 'tcx>,
133     ) -> RelateResult<'tcx, ()> {
134         self.borrow_region_constraints()
135             .leak_check(self.tcx, overly_polymorphic, placeholder_map, snapshot)
136     }
137 }