Auto merge of #98109 - nikomatsakis:issue-98095, r=jackh726

fix universes in the NLL type tests

In the NLL code, we were not accommodating universes in the
`type_test` logic.

Fixes #98095.

r? `@compiler-errors`

This breaks some tests, however, so the purpose of this branch is more explanatory and perhaps to do a crater run.
This commit is contained in:
bors 2022-06-24 07:39:28 +00:00
commit d017d59ed0
29 changed files with 734 additions and 266 deletions

View file

@ -22,6 +22,8 @@ use rustc_middle::ty::{Region, RegionVid};
use rustc_span::Span;
use std::fmt;
use super::outlives::test_type_match;
/// This function performs lexical region resolution given a complete
/// set of constraints and variable origins. It performs a fixed-point
/// iteration to find region values which satisfy all constraints,
@ -29,12 +31,13 @@ use std::fmt;
/// all the variables as well as a set of errors that must be reported.
#[instrument(level = "debug", skip(region_rels, var_infos, data))]
pub(crate) fn resolve<'tcx>(
param_env: ty::ParamEnv<'tcx>,
region_rels: &RegionRelations<'_, 'tcx>,
var_infos: VarInfos,
data: RegionConstraintData<'tcx>,
) -> (LexicalRegionResolutions<'tcx>, Vec<RegionResolutionError<'tcx>>) {
let mut errors = vec![];
let mut resolver = LexicalResolver { region_rels, var_infos, data };
let mut resolver = LexicalResolver { param_env, region_rels, var_infos, data };
let values = resolver.infer_variable_values(&mut errors);
(values, errors)
}
@ -100,6 +103,7 @@ struct RegionAndOrigin<'tcx> {
type RegionGraph<'tcx> = Graph<(), Constraint<'tcx>>;
struct LexicalResolver<'cx, 'tcx> {
param_env: ty::ParamEnv<'tcx>,
region_rels: &'cx RegionRelations<'cx, 'tcx>,
var_infos: VarInfos,
data: RegionConstraintData<'tcx>,
@ -818,9 +822,20 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
min: ty::Region<'tcx>,
) -> bool {
match bound {
VerifyBound::IfEq(k, b) => {
(var_values.normalize(self.region_rels.tcx, *k) == generic_ty)
&& self.bound_is_met(b, var_values, generic_ty, min)
VerifyBound::IfEq(verify_if_eq_b) => {
let verify_if_eq_b = var_values.normalize(self.region_rels.tcx, *verify_if_eq_b);
match test_type_match::extract_verify_if_eq(
self.tcx(),
self.param_env,
&verify_if_eq_b,
generic_ty,
) {
Some(r) => {
self.bound_is_met(&VerifyBound::OutlivedBy(r), var_values, generic_ty, min)
}
None => false,
}
}
VerifyBound::OutlivedBy(r) => {

View file

@ -1290,7 +1290,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
&RegionRelations::new(self.tcx, region_context, outlives_env.free_region_map());
let (lexical_region_resolutions, errors) =
lexical_region_resolve::resolve(region_rels, var_infos, data);
lexical_region_resolve::resolve(outlives_env.param_env, region_rels, var_infos, data);
let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions));
assert!(old_value.is_none());

View file

@ -3,6 +3,7 @@
pub mod components;
pub mod env;
pub mod obligations;
pub mod test_type_match;
pub mod verify;
use rustc_middle::traits::query::OutlivesBound;

View file

@ -318,17 +318,13 @@ where
self.delegate.push_verify(origin, generic, region, verify_bound);
}
#[tracing::instrument(level = "debug", skip(self))]
fn projection_must_outlive(
&mut self,
origin: infer::SubregionOrigin<'tcx>,
region: ty::Region<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
) {
debug!(
"projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})",
region, projection_ty, origin
);
// This case is thorny for inference. The fundamental problem is
// that there are many cases where we have choice, and inference
// doesn't like choice (the current region inference in
@ -363,13 +359,21 @@ where
// #55756) in cases where you have e.g., `<T as Foo<'a>>::Item:
// 'a` in the environment but `trait Foo<'b> { type Item: 'b
// }` in the trait definition.
approx_env_bounds.retain(|bound| match *bound.0.kind() {
ty::Projection(projection_ty) => self
.verify_bound
.projection_declared_bounds_from_trait(projection_ty)
.all(|r| r != bound.1),
approx_env_bounds.retain(|bound_outlives| {
// OK to skip binder because we only manipulate and compare against other
// values from the same binder. e.g. if we have (e.g.) `for<'a> <T as Trait<'a>>::Item: 'a`
// in `bound`, the `'a` will be a `^1` (bound, debruijn index == innermost) region.
// If the declaration is `trait Trait<'b> { type Item: 'b; }`, then `projection_declared_bounds_from_trait`
// will be invoked with `['b => ^1]` and so we will get `^1` returned.
let bound = bound_outlives.skip_binder();
match *bound.0.kind() {
ty::Projection(projection_ty) => self
.verify_bound
.projection_declared_bounds_from_trait(projection_ty)
.all(|r| r != bound.1),
_ => panic!("expected only projection types from env, not {:?}", bound.0),
_ => panic!("expected only projection types from env, not {:?}", bound.0),
}
});
// If declared bounds list is empty, the only applicable rule is
@ -420,8 +424,16 @@ where
if !trait_bounds.is_empty()
&& trait_bounds[1..]
.iter()
.chain(approx_env_bounds.iter().map(|b| &b.1))
.all(|b| *b == trait_bounds[0])
.map(|r| Some(*r))
.chain(
// NB: The environment may contain `for<'a> T: 'a` style bounds.
// In that case, we don't know if they are equal to the trait bound
// or not (since we don't *know* whether the environment bound even applies),
// so just map to `None` here if there are bound vars, ensuring that
// the call to `all` will fail below.
approx_env_bounds.iter().map(|b| b.map_bound(|b| b.1).no_bound_vars()),
)
.all(|b| b == Some(trait_bounds[0]))
{
let unique_bound = trait_bounds[0];
debug!("projection_must_outlive: unique trait bound = {:?}", unique_bound);
@ -437,6 +449,7 @@ where
// even though a satisfactory solution exists.
let generic = GenericKind::Projection(projection_ty);
let verify_bound = self.verify_bound.generic_bound(generic);
debug!("projection_must_outlive: pushing {:?}", verify_bound);
self.delegate.push_verify(origin, generic, region, verify_bound);
}
}

View file

@ -0,0 +1,207 @@
use std::collections::hash_map::Entry;
use rustc_data_structures::fx::FxHashMap;
use rustc_middle::ty::TypeFoldable;
use rustc_middle::ty::{
self,
error::TypeError,
relate::{self, Relate, RelateResult, TypeRelation},
Ty, TyCtxt,
};
use crate::infer::region_constraints::VerifyIfEq;
/// Given a "verify-if-eq" type test like:
///
/// exists<'a...> {
/// verify_if_eq(some_type, bound_region)
/// }
///
/// and the type `test_ty` that the type test is being tested against,
/// returns:
///
/// * `None` if `some_type` cannot be made equal to `test_ty`,
/// no matter the values of the variables in `exists`.
/// * `Some(r)` with a suitable bound (typically the value of `bound_region`, modulo
/// any bound existential variables, which will be substituted) for the
/// type under test.
///
/// NB: This function uses a simplistic, syntactic version of type equality.
/// In other words, it may spuriously return `None` even if the type-under-test
/// is in fact equal to `some_type`. In practice, though, this is used on types
/// that are either projections like `T::Item` or `T` and it works fine, but it
/// could have trouble when complex types with higher-ranked binders and the
/// like are used. This is a particular challenge since this function is invoked
/// very late in inference and hence cannot make use of the normal inference
/// machinery.
#[tracing::instrument(level = "debug", skip(tcx, param_env))]
pub fn extract_verify_if_eq<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
verify_if_eq_b: &ty::Binder<'tcx, VerifyIfEq<'tcx>>,
test_ty: Ty<'tcx>,
) -> Option<ty::Region<'tcx>> {
assert!(!verify_if_eq_b.has_escaping_bound_vars());
let mut m = Match::new(tcx, param_env);
let verify_if_eq = verify_if_eq_b.skip_binder();
m.relate(verify_if_eq.ty, test_ty).ok()?;
if let ty::RegionKind::ReLateBound(depth, br) = verify_if_eq.bound.kind() {
assert!(depth == ty::INNERMOST);
match m.map.get(&br) {
Some(&r) => Some(r),
None => {
// If there is no mapping, then this region is unconstrained.
// In that case, we escalate to `'static`.
Some(tcx.lifetimes.re_static)
}
}
} else {
// The region does not contain any bound variables, so we don't need
// to do any substitution.
//
// Example:
//
// for<'a> <T as Foo<'a>>::Item: 'b
//
// In this case, we've now matched and found a value for
// `'a`, but it doesn't affect the bound `'b`.
Some(verify_if_eq.bound)
}
}
/// True if a (potentially higher-ranked) outlives
#[tracing::instrument(level = "debug", skip(tcx, param_env))]
pub(super) fn can_match_erased_ty<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
outlives_predicate: ty::Binder<'tcx, ty::TypeOutlivesPredicate<'tcx>>,
erased_ty: Ty<'tcx>,
) -> bool {
assert!(!outlives_predicate.has_escaping_bound_vars());
let erased_outlives_predicate = tcx.erase_regions(outlives_predicate);
let outlives_ty = erased_outlives_predicate.skip_binder().0;
if outlives_ty == erased_ty {
// pointless micro-optimization
true
} else {
Match::new(tcx, param_env).relate(outlives_ty, erased_ty).is_ok()
}
}
struct Match<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
pattern_depth: ty::DebruijnIndex,
map: FxHashMap<ty::BoundRegion, ty::Region<'tcx>>,
}
impl<'tcx> Match<'tcx> {
fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Match<'tcx> {
Match { tcx, param_env, pattern_depth: ty::INNERMOST, map: FxHashMap::default() }
}
}
impl<'tcx> Match<'tcx> {
/// Creates the "Error" variant that signals "no match".
fn no_match<T>(&self) -> RelateResult<'tcx, T> {
Err(TypeError::Mismatch)
}
/// Binds the pattern variable `br` to `value`; returns an `Err` if the pattern
/// is already bound to a different value.
#[tracing::instrument(level = "debug", skip(self))]
fn bind(
&mut self,
br: ty::BoundRegion,
value: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
match self.map.entry(br) {
Entry::Occupied(entry) => {
if *entry.get() == value {
Ok(value)
} else {
self.no_match()
}
}
Entry::Vacant(entry) => {
entry.insert(value);
Ok(value)
}
}
}
}
impl<'tcx> TypeRelation<'tcx> for Match<'tcx> {
fn tag(&self) -> &'static str {
"Match"
}
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.param_env
}
fn a_is_expected(&self) -> bool {
true
} // irrelevant
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
_: ty::Variance,
_: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
self.relate(a, b)
}
#[instrument(skip(self), level = "debug")]
fn regions(
&mut self,
pattern: ty::Region<'tcx>,
value: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
debug!("self.pattern_depth = {:?}", self.pattern_depth);
if let ty::RegionKind::ReLateBound(depth, br) = pattern.kind() && depth == self.pattern_depth {
self.bind(br, value)
} else if pattern == value {
Ok(pattern)
} else {
self.no_match()
}
}
#[instrument(skip(self), level = "debug")]
fn tys(&mut self, pattern: Ty<'tcx>, value: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
if pattern == value { Ok(pattern) } else { relate::super_relate_tys(self, pattern, value) }
}
#[instrument(skip(self), level = "debug")]
fn consts(
&mut self,
pattern: ty::Const<'tcx>,
value: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
debug!("{}.consts({:?}, {:?})", self.tag(), pattern, value);
if pattern == value {
Ok(pattern)
} else {
relate::super_relate_consts(self, pattern, value)
}
}
fn binders<T>(
&mut self,
pattern: ty::Binder<'tcx, T>,
value: ty::Binder<'tcx, T>,
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
where
T: Relate<'tcx>,
{
self.pattern_depth.shift_in(1);
let result = Ok(pattern.rebind(self.relate(pattern.skip_binder(), value.skip_binder())?));
self.pattern_depth.shift_out(1);
result
}
}

View file

@ -1,4 +1,5 @@
use crate::infer::outlives::env::RegionBoundPairs;
use crate::infer::region_constraints::VerifyIfEq;
use crate::infer::{GenericKind, VerifyBound};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::sso::SsoHashSet;
@ -82,27 +83,39 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
debug!("param_bound(param_ty={:?})", param_ty);
// Start with anything like `T: 'a` we can scrape from the
// environment
let param_bounds = self
.declared_generic_bounds_from_env(GenericKind::Param(param_ty))
.into_iter()
.map(|outlives| outlives.1);
// environment. If the environment contains something like
// `for<'a> T: 'a`, then we know that `T` outlives everything.
let declared_bounds_from_env = self.declared_generic_bounds_from_env(param_ty);
let mut param_bounds = vec![];
for declared_bound in declared_bounds_from_env {
let bound_region = declared_bound.map_bound(|outlives| outlives.1);
if let Some(region) = bound_region.no_bound_vars() {
// This is `T: 'a` for some free region `'a`.
param_bounds.push(VerifyBound::OutlivedBy(region));
} else {
// This is `for<'a> T: 'a`. This means that `T` outlives everything! All done here.
return VerifyBound::AllBounds(vec![]);
}
}
// Add in the default bound of fn body that applies to all in
// scope type parameters:
let param_bounds = param_bounds.chain(self.implicit_region_bound);
if let Some(r) = self.implicit_region_bound {
param_bounds.push(VerifyBound::OutlivedBy(r));
}
let any_bounds: Vec<_> = param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect();
if any_bounds.is_empty() {
if param_bounds.is_empty() {
// We know that all types `T` outlive `'empty`, so if we
// can find no other bound, then check that the region
// being tested is `'empty`.
VerifyBound::IsEmpty
} else if param_bounds.len() == 1 {
// Micro-opt: no need to store the vector if it's just len 1
param_bounds.pop().unwrap()
} else {
// If we can find any other bound `R` such that `T: R`, then
// we don't need to check for `'empty`, because `R: 'empty`.
VerifyBound::AnyBound(any_bounds)
VerifyBound::AnyBound(param_bounds)
}
}
@ -122,17 +135,10 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
pub fn projection_approx_declared_bounds_from_env(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> {
let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx);
let erased_projection_ty = self.tcx.erase_regions(projection_ty);
self.declared_generic_bounds_from_env_with_compare_fn(|ty| {
if let ty::Projection(..) = ty.kind() {
let erased_ty = self.tcx.erase_regions(ty);
erased_ty == erased_projection_ty
} else {
false
}
})
self.declared_generic_bounds_from_env_for_erased_ty(erased_projection_ty)
}
/// Searches the where-clauses in scope for regions that
@ -159,15 +165,15 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
let env_bounds = self
.projection_approx_declared_bounds_from_env(projection_ty)
.into_iter()
.map(|ty::OutlivesPredicate(ty, r)| {
let vb = VerifyBound::OutlivedBy(r);
if ty == projection_ty_as_ty {
.map(|binder| {
if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() && ty == projection_ty_as_ty {
// Micro-optimize if this is an exact match (this
// occurs often when there are no region variables
// involved).
vb
VerifyBound::OutlivedBy(r)
} else {
VerifyBound::IfEq(ty, Box::new(vb))
let verify_if_eq_b = binder.map_bound(|ty::OutlivesPredicate(ty, bound)| VerifyIfEq { ty, bound });
VerifyBound::IfEq(verify_if_eq_b)
}
});
@ -219,26 +225,34 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
/// bounds, but all the bounds it returns can be relied upon.
fn declared_generic_bounds_from_env(
&self,
generic: GenericKind<'tcx>,
) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
let generic_ty = generic.to_ty(self.tcx);
self.declared_generic_bounds_from_env_with_compare_fn(|ty| ty == generic_ty)
param_ty: ty::ParamTy,
) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> {
let generic_ty = param_ty.to_ty(self.tcx);
self.declared_generic_bounds_from_env_for_erased_ty(generic_ty)
}
fn declared_generic_bounds_from_env_with_compare_fn(
/// Searches the environment to find all bounds that apply to `erased_ty`.
/// Obviously these must be approximate -- they are in fact both *over* and
/// and *under* approximated:
///
/// * Over-approximated because we erase regions, so
/// * Under-approximated because we look for syntactic equality and so for complex types
/// like `<T as Foo<fn(&u32, &u32)>>::Item` or whatever we may fail to figure out
/// all the subtleties.
///
/// In some cases, such as when `erased_ty` represents a `ty::Param`, however,
/// the result is precise.
fn declared_generic_bounds_from_env_for_erased_ty(
&self,
compare_ty: impl Fn(Ty<'tcx>) -> bool,
) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
erased_ty: Ty<'tcx>,
) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> {
let tcx = self.tcx;
// To start, collect bounds from user environment. Note that
// parameter environments are already elaborated, so we don't
// have to worry about that. Comparing using `==` is a bit
// dubious for projections, but it will work for simple cases
// like `T` and `T::Item`. It may not work as well for things
// like `<T as Foo<'a>>::Item`.
// have to worry about that.
let c_b = self.param_env.caller_bounds();
let param_bounds = self.collect_outlives_from_predicate_list(&compare_ty, c_b.into_iter());
let param_bounds = self.collect_outlives_from_predicate_list(erased_ty, c_b.into_iter());
// Next, collect regions we scraped from the well-formedness
// constraints in the fn signature. To do that, we walk the list
@ -253,18 +267,20 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
// don't know that this holds from first principles.
let from_region_bound_pairs = self.region_bound_pairs.iter().filter_map(|&(r, p)| {
debug!(
"declared_generic_bounds_from_env_with_compare_fn: region_bound_pair = {:?}",
"declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}",
(r, p)
);
let p_ty = p.to_ty(tcx);
compare_ty(p_ty).then_some(ty::OutlivesPredicate(p_ty, r))
let erased_p_ty = self.tcx.erase_regions(p_ty);
(erased_p_ty == erased_ty)
.then_some(ty::Binder::dummy(ty::OutlivesPredicate(p.to_ty(tcx), r)))
});
param_bounds
.chain(from_region_bound_pairs)
.inspect(|bound| {
debug!(
"declared_generic_bounds_from_env_with_compare_fn: result predicate = {:?}",
"declared_generic_bounds_from_env_for_erased_ty: result predicate = {:?}",
bound
)
})
@ -344,12 +360,19 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
/// otherwise want a precise match.
fn collect_outlives_from_predicate_list(
&self,
compare_ty: impl Fn(Ty<'tcx>) -> bool,
erased_ty: Ty<'tcx>,
predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
) -> impl Iterator<Item = ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
predicates
.filter_map(|p| p.to_opt_type_outlives())
.filter_map(|p| p.no_bound_vars())
.filter(move |p| compare_ty(p.0))
) -> impl Iterator<Item = ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>>
{
let tcx = self.tcx;
let param_env = self.param_env;
predicates.filter_map(|p| p.to_opt_type_outlives()).filter(move |outlives_predicate| {
super::test_type_match::can_match_erased_ty(
tcx,
param_env,
*outlives_predicate,
erased_ty,
)
})
}
}

View file

@ -190,41 +190,8 @@ pub enum GenericKind<'tcx> {
/// This is described with an `AnyRegion('a, 'b)` node.
#[derive(Debug, Clone)]
pub enum VerifyBound<'tcx> {
/// Given a kind K and a bound B, expands to a function like the
/// following, where `G` is the generic for which this verify
/// bound was created:
///
/// ```ignore (pseudo-rust)
/// fn(min) -> bool {
/// if G == K {
/// B(min)
/// } else {
/// false
/// }
/// }
/// ```
///
/// In other words, if the generic `G` that we are checking is
/// equal to `K`, then check the associated verify bound
/// (otherwise, false).
///
/// This is used when we have something in the environment that
/// may or may not be relevant, depending on the region inference
/// results. For example, we may have `where <T as
/// Trait<'a>>::Item: 'b` in our where-clauses. If we are
/// generating the verify-bound for `<T as Trait<'0>>::Item`, then
/// this where-clause is only relevant if `'0` winds up inferred
/// to `'a`.
///
/// So we would compile to a verify-bound like
///
/// ```ignore (illustrative)
/// IfEq(<T as Trait<'a>>::Item, AnyRegion('a))
/// ```
///
/// meaning, if the subject G is equal to `<T as Trait<'a>>::Item`
/// (after inference), and `'a: min`, then `G: min`.
IfEq(Ty<'tcx>, Box<VerifyBound<'tcx>>),
/// See [`VerifyIfEq`] docs
IfEq(ty::Binder<'tcx, VerifyIfEq<'tcx>>),
/// Given a region `R`, expands to the function:
///
@ -267,6 +234,53 @@ pub enum VerifyBound<'tcx> {
AllBounds(Vec<VerifyBound<'tcx>>),
}
/// This is a "conditional bound" that checks the result of inference
/// and supplies a bound if it ended up being relevant. It's used in situations
/// like this:
///
/// ```rust
/// fn foo<'a, 'b, T: SomeTrait<'a>>
/// where
/// <T as SomeTrait<'a>>::Item: 'b
/// ```
///
/// If we have an obligation like `<T as SomeTrait<'?x>>::Item: 'c`, then
/// we don't know yet whether it suffices to show that `'b: 'c`. If `'?x` winds
/// up being equal to `'a`, then the where-clauses on function applies, and
/// in that case we can show `'b: 'c`. But if `'?x` winds up being something
/// else, the bound isn't relevant.
///
/// In the [`VerifyBound`], this struct is enclosed in `Binder to account
/// for cases like
///
/// ```rust
/// where for<'a> <T as SomeTrait<'a>::Item: 'a
/// ```
///
/// The idea is that we have to find some instantiation of `'a` that can
/// make `<T as SomeTrait<'a>>::Item` equal to the final value of `G`,
/// the generic we are checking.
///
/// ```ignore (pseudo-rust)
/// fn(min) -> bool {
/// exists<'a> {
/// if G == K {
/// B(min)
/// } else {
/// false
/// }
/// }
/// }
/// ```
#[derive(Debug, Copy, Clone, TypeFoldable)]
pub struct VerifyIfEq<'tcx> {
/// Type which must match the generic `G`
pub ty: Ty<'tcx>,
/// Bound that applies if `ty` is equal.
pub bound: Region<'tcx>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) struct TwoRegions<'tcx> {
a: Region<'tcx>,
@ -770,7 +784,7 @@ impl<'tcx> VerifyBound<'tcx> {
pub fn cannot_hold(&self) -> bool {
match self {
VerifyBound::IfEq(_, b) => b.cannot_hold(),
VerifyBound::IfEq(..) => false,
VerifyBound::IsEmpty => false,
VerifyBound::OutlivedBy(_) => false,
VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()),