]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_infer/src/infer/higher_ranked/mod.rs
Auto merge of #76345 - okready:sgx-mem-range-overflow-checks, r=joshtriplett
[rust.git] / compiler / rustc_infer / src / 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};
6
7 use crate::infer::CombinedSnapshot;
8 use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
9 use rustc_middle::ty::{self, Binder, TypeFoldable};
10
11 impl<'a, 'tcx> CombineFields<'a, 'tcx> {
12     pub fn higher_ranked_sub<T>(
13         &mut self,
14         a: Binder<T>,
15         b: Binder<T>,
16         a_is_expected: bool,
17     ) -> RelateResult<'tcx, Binder<T>>
18     where
19         T: Relate<'tcx>,
20     {
21         debug!("higher_ranked_sub(a={:?}, b={:?})", a, b);
22
23         // Rather than checking the subtype relationship between `a` and `b`
24         // as-is, we need to do some extra work here in order to make sure
25         // that function subtyping works correctly with respect to regions
26         //
27         // Note: this is a subtle algorithm.  For a full explanation, please see
28         // the rustc dev guide:
29         // <https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html>
30
31         let span = self.trace.cause.span;
32
33         self.infcx.commit_if_ok(|_| {
34             // First, we instantiate each bound region in the supertype with a
35             // fresh placeholder region.
36             let b_prime = self.infcx.replace_bound_vars_with_placeholders(b);
37
38             // Next, we instantiate each bound region in the subtype
39             // with a fresh region variable. These region variables --
40             // but no other pre-existing region variables -- can name
41             // the placeholders.
42             let (a_prime, _) =
43                 self.infcx.replace_bound_vars_with_fresh_vars(span, HigherRankedType, a);
44
45             debug!("a_prime={:?}", a_prime);
46             debug!("b_prime={:?}", b_prime);
47
48             // Compare types now that bound regions have been replaced.
49             let result = self.sub(a_is_expected).relate(a_prime, b_prime)?;
50
51             debug!("higher_ranked_sub: OK result={:?}", result);
52
53             Ok(ty::Binder::bind(result))
54         })
55     }
56 }
57
58 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
59     /// Replaces all regions (resp. types) bound by `binder` with placeholder
60     /// regions (resp. types) and return a map indicating which bound-region
61     /// placeholder region. This is the first step of checking subtyping
62     /// when higher-ranked things are involved.
63     ///
64     /// **Important:** You have to be careful to not leak these placeholders,
65     /// for more information about how placeholders and HRTBs work, see
66     /// the [rustc dev guide].
67     ///
68     /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html
69     pub fn replace_bound_vars_with_placeholders<T>(&self, binder: ty::Binder<T>) -> T
70     where
71         T: TypeFoldable<'tcx>,
72     {
73         // Figure out what the next universe will be, but don't actually create
74         // it until after we've done the substitution (in particular there may
75         // be no bound variables). This is a performance optimization, since the
76         // leak check for example can be skipped if no new universes are created
77         // (i.e., if there are no placeholders).
78         let next_universe = self.universe().next_universe();
79
80         let fld_r = |br: ty::BoundRegion| {
81             self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion {
82                 universe: next_universe,
83                 name: br.kind,
84             }))
85         };
86
87         let fld_t = |bound_ty: ty::BoundTy| {
88             self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType {
89                 universe: next_universe,
90                 name: bound_ty.var,
91             }))
92         };
93
94         let fld_c = |bound_var: ty::BoundVar, ty| {
95             self.tcx.mk_const(ty::Const {
96                 val: ty::ConstKind::Placeholder(ty::PlaceholderConst {
97                     universe: next_universe,
98                     name: ty::BoundConst { var: bound_var, ty },
99                 }),
100                 ty,
101             })
102         };
103
104         let (result, map) = self.tcx.replace_bound_vars(binder, fld_r, fld_t, fld_c);
105
106         // If there were higher-ranked regions to replace, then actually create
107         // the next universe (this avoids needlessly creating universes).
108         if !map.is_empty() {
109             let n_u = self.create_next_universe();
110             assert_eq!(n_u, next_universe);
111         }
112
113         debug!(
114             "replace_bound_vars_with_placeholders(\
115              next_universe={:?}, \
116              result={:?}, \
117              map={:?})",
118             next_universe, result, map,
119         );
120
121         result
122     }
123
124     /// See `infer::region_constraints::RegionConstraintCollector::leak_check`.
125     pub fn leak_check(
126         &self,
127         overly_polymorphic: bool,
128         snapshot: &CombinedSnapshot<'_, 'tcx>,
129     ) -> RelateResult<'tcx, ()> {
130         // If the user gave `-Zno-leak-check`, or we have been
131         // configured to skip the leak check, then skip the leak check
132         // completely. The leak check is deprecated. Any legitimate
133         // subtyping errors that it would have caught will now be
134         // caught later on, during region checking. However, we
135         // continue to use it for a transition period.
136         if self.tcx.sess.opts.debugging_opts.no_leak_check || self.skip_leak_check.get() {
137             return Ok(());
138         }
139
140         self.inner.borrow_mut().unwrap_region_constraints().leak_check(
141             self.tcx,
142             overly_polymorphic,
143             self.universe(),
144             snapshot,
145         )
146     }
147 }