1 use std::collections::hash_map::Entry;
3 use rustc_data_structures::fx::FxHashMap;
4 use rustc_middle::ty::TypeVisitable;
5 use rustc_middle::ty::{
8 relate::{self, Relate, RelateResult, TypeRelation},
12 use crate::infer::region_constraints::VerifyIfEq;
14 /// Given a "verify-if-eq" type test like:
17 /// verify_if_eq(some_type, bound_region)
20 /// and the type `test_ty` that the type test is being tested against,
23 /// * `None` if `some_type` cannot be made equal to `test_ty`,
24 /// no matter the values of the variables in `exists`.
25 /// * `Some(r)` with a suitable bound (typically the value of `bound_region`, modulo
26 /// any bound existential variables, which will be substituted) for the
29 /// NB: This function uses a simplistic, syntactic version of type equality.
30 /// In other words, it may spuriously return `None` even if the type-under-test
31 /// is in fact equal to `some_type`. In practice, though, this is used on types
32 /// that are either projections like `T::Item` or `T` and it works fine, but it
33 /// could have trouble when complex types with higher-ranked binders and the
34 /// like are used. This is a particular challenge since this function is invoked
35 /// very late in inference and hence cannot make use of the normal inference
37 #[tracing::instrument(level = "debug", skip(tcx, param_env))]
38 pub fn extract_verify_if_eq<'tcx>(
40 param_env: ty::ParamEnv<'tcx>,
41 verify_if_eq_b: &ty::Binder<'tcx, VerifyIfEq<'tcx>>,
43 ) -> Option<ty::Region<'tcx>> {
44 assert!(!verify_if_eq_b.has_escaping_bound_vars());
45 let mut m = Match::new(tcx, param_env);
46 let verify_if_eq = verify_if_eq_b.skip_binder();
47 m.relate(verify_if_eq.ty, test_ty).ok()?;
49 if let ty::RegionKind::ReLateBound(depth, br) = verify_if_eq.bound.kind() {
50 assert!(depth == ty::INNERMOST);
51 match m.map.get(&br) {
54 // If there is no mapping, then this region is unconstrained.
55 // In that case, we escalate to `'static`.
56 Some(tcx.lifetimes.re_static)
60 // The region does not contain any bound variables, so we don't need
61 // to do any substitution.
65 // for<'a> <T as Foo<'a>>::Item: 'b
67 // In this case, we've now matched and found a value for
68 // `'a`, but it doesn't affect the bound `'b`.
69 Some(verify_if_eq.bound)
73 /// True if a (potentially higher-ranked) outlives
74 #[tracing::instrument(level = "debug", skip(tcx, param_env))]
75 pub(super) fn can_match_erased_ty<'tcx>(
77 param_env: ty::ParamEnv<'tcx>,
78 outlives_predicate: ty::Binder<'tcx, ty::TypeOutlivesPredicate<'tcx>>,
81 assert!(!outlives_predicate.has_escaping_bound_vars());
82 let erased_outlives_predicate = tcx.erase_regions(outlives_predicate);
83 let outlives_ty = erased_outlives_predicate.skip_binder().0;
84 if outlives_ty == erased_ty {
85 // pointless micro-optimization
88 Match::new(tcx, param_env).relate(outlives_ty, erased_ty).is_ok()
94 param_env: ty::ParamEnv<'tcx>,
95 pattern_depth: ty::DebruijnIndex,
96 map: FxHashMap<ty::BoundRegion, ty::Region<'tcx>>,
99 impl<'tcx> Match<'tcx> {
100 fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Match<'tcx> {
101 Match { tcx, param_env, pattern_depth: ty::INNERMOST, map: FxHashMap::default() }
105 impl<'tcx> Match<'tcx> {
106 /// Creates the "Error" variant that signals "no match".
107 fn no_match<T>(&self) -> RelateResult<'tcx, T> {
108 Err(TypeError::Mismatch)
111 /// Binds the pattern variable `br` to `value`; returns an `Err` if the pattern
112 /// is already bound to a different value.
113 #[tracing::instrument(level = "debug", skip(self))]
117 value: ty::Region<'tcx>,
118 ) -> RelateResult<'tcx, ty::Region<'tcx>> {
119 match self.map.entry(br) {
120 Entry::Occupied(entry) => {
121 if *entry.get() == value {
127 Entry::Vacant(entry) => {
135 impl<'tcx> TypeRelation<'tcx> for Match<'tcx> {
136 fn tag(&self) -> &'static str {
139 fn tcx(&self) -> TyCtxt<'tcx> {
142 fn param_env(&self) -> ty::ParamEnv<'tcx> {
145 fn a_is_expected(&self) -> bool {
149 fn relate_with_variance<T: Relate<'tcx>>(
152 _: ty::VarianceDiagInfo<'tcx>,
155 ) -> RelateResult<'tcx, T> {
159 #[instrument(skip(self), level = "debug")]
162 pattern: ty::Region<'tcx>,
163 value: ty::Region<'tcx>,
164 ) -> RelateResult<'tcx, ty::Region<'tcx>> {
165 debug!("self.pattern_depth = {:?}", self.pattern_depth);
166 if let ty::RegionKind::ReLateBound(depth, br) = pattern.kind() && depth == self.pattern_depth {
168 } else if pattern == value {
175 #[instrument(skip(self), level = "debug")]
176 fn tys(&mut self, pattern: Ty<'tcx>, value: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
177 if pattern == value { Ok(pattern) } else { relate::super_relate_tys(self, pattern, value) }
180 #[instrument(skip(self), level = "debug")]
183 pattern: ty::Const<'tcx>,
184 value: ty::Const<'tcx>,
185 ) -> RelateResult<'tcx, ty::Const<'tcx>> {
186 debug!("{}.consts({:?}, {:?})", self.tag(), pattern, value);
187 if pattern == value {
190 relate::super_relate_consts(self, pattern, value)
196 pattern: ty::Binder<'tcx, T>,
197 value: ty::Binder<'tcx, T>,
198 ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
202 self.pattern_depth.shift_in(1);
203 let result = Ok(pattern.rebind(self.relate(pattern.skip_binder(), value.skip_binder())?));
204 self.pattern_depth.shift_out(1);