generalize the outlives obligation code
The code now accepts `Binder<OutlivesPredicate>` instead of just `OutlivesPredicate` and thus exercises the new, generalized `IfEqBound` codepaths. Note though that we never *produce* Binder<OutlivesPredicate>, so we are only testing a subset of those codepaths that excludes actual higher-ranked outlives bounds.
This commit is contained in:
parent
10f0f66d45
commit
b7b3d2cee0
9 changed files with 108 additions and 173 deletions
|
|
@ -359,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 inder. 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
|
||||
|
|
@ -416,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);
|
||||
|
|
@ -433,7 +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={:?}", verify_bound,);
|
||||
debug!("projection_must_outlive: pushing {:?}", verify_bound);
|
||||
self.delegate.push_verify(origin, generic, region, verify_bound);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ use crate::infer::region_constraints::VerifyIfEq;
|
|||
/// 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_bound<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
|
@ -61,6 +62,25 @@ pub fn extract_verify_if_eq_bound<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
|
|
@ -82,6 +102,7 @@ impl<'tcx> Match<'tcx> {
|
|||
|
||||
/// 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,
|
||||
|
|
@ -133,8 +154,9 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> {
|
|||
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, pattern)
|
||||
self.bind(br, value)
|
||||
} else if pattern == value {
|
||||
Ok(pattern)
|
||||
} else {
|
||||
|
|
@ -142,6 +164,7 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn tys(&mut self, pattern: Ty<'tcx>, value: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
|
||||
if pattern == value {
|
||||
return Ok(pattern);
|
||||
|
|
@ -150,6 +173,7 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn consts(
|
||||
&mut self,
|
||||
pattern: ty::Const<'tcx>,
|
||||
|
|
|
|||
|
|
@ -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,25 +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(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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +135,7 @@ 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_for_erased_ty(erased_projection_ty)
|
||||
|
|
@ -150,14 +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)| {
|
||||
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).
|
||||
VerifyBound::OutlivedBy(r)
|
||||
} else {
|
||||
VerifyBound::IfEq(ty, r)
|
||||
let verify_if_eq_b = binder.map_bound(|ty::OutlivesPredicate(ty, bound)| VerifyIfEq { ty, bound });
|
||||
VerifyBound::IfEqBound(verify_if_eq_b)
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -210,7 +226,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
|||
fn declared_generic_bounds_from_env(
|
||||
&self,
|
||||
param_ty: ty::ParamTy,
|
||||
) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
|
||||
) -> 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)
|
||||
}
|
||||
|
|
@ -229,7 +245,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
|||
fn declared_generic_bounds_from_env_for_erased_ty(
|
||||
&self,
|
||||
erased_ty: Ty<'tcx>,
|
||||
) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'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
|
||||
|
|
@ -259,7 +275,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
|||
);
|
||||
let p_ty = p.to_ty(tcx);
|
||||
let erased_p_ty = self.tcx.erase_regions(p_ty);
|
||||
(erased_p_ty == erased_ty).then_some(ty::OutlivesPredicate(p.to_ty(tcx), r))
|
||||
(erased_p_ty == erased_ty)
|
||||
.then_some(ty::Binder::dummy(ty::OutlivesPredicate(p.to_ty(tcx), r)))
|
||||
});
|
||||
|
||||
param_bounds
|
||||
|
|
@ -348,11 +365,17 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
|||
&self,
|
||||
erased_ty: Ty<'tcx>,
|
||||
predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
|
||||
) -> impl Iterator<Item = ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
|
||||
) -> impl Iterator<Item = ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>>
|
||||
{
|
||||
let tcx = self.tcx;
|
||||
predicates
|
||||
.filter_map(|p| p.to_opt_type_outlives())
|
||||
.filter_map(|p| p.no_bound_vars())
|
||||
.filter(move |p| tcx.erase_regions(p.0) == erased_ty)
|
||||
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,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
// Regression test for #71546.
|
||||
//
|
||||
// Made to pass as part of fixing #98095.
|
||||
//
|
||||
// check-pass
|
||||
|
||||
pub fn serialize_as_csv<V>(value: &V) -> Result<String, &str>
|
||||
where
|
||||
|
|
@ -6,15 +10,7 @@ where
|
|||
for<'a> &'a V: IntoIterator,
|
||||
for<'a> <&'a V as IntoIterator>::Item: ToString + 'static,
|
||||
{
|
||||
let csv_str: String = value
|
||||
//~^ ERROR higher-ranked lifetime error
|
||||
//~| ERROR higher-ranked lifetime error
|
||||
//~| ERROR higher-ranked lifetime error
|
||||
.into_iter()
|
||||
.map(|elem| elem.to_string())
|
||||
//~^ ERROR higher-ranked lifetime error
|
||||
.collect::<String>();
|
||||
//~^ ERROR higher-ranked lifetime error
|
||||
let csv_str: String = value.into_iter().map(|elem| elem.to_string()).collect::<String>();
|
||||
Ok(csv_str)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
error: higher-ranked lifetime error
|
||||
--> $DIR/issue-71546.rs:9:27
|
||||
|
|
||||
LL | let csv_str: String = value
|
||||
| ___________________________^
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | .into_iter()
|
||||
LL | | .map(|elem| elem.to_string())
|
||||
| |_____________________________________^
|
||||
|
|
||||
= note: could not prove for<'r> [closure@$DIR/issue-71546.rs:14:14: 14:37] well-formed
|
||||
|
||||
error: higher-ranked lifetime error
|
||||
--> $DIR/issue-71546.rs:9:27
|
||||
|
|
||||
LL | let csv_str: String = value
|
||||
| ___________________________^
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | .into_iter()
|
||||
LL | | .map(|elem| elem.to_string())
|
||||
| |_____________________________________^
|
||||
|
|
||||
= note: could not prove for<'r, 's> Map<<&'r V as IntoIterator>::IntoIter, [closure@$DIR/issue-71546.rs:14:14: 14:37]> well-formed
|
||||
|
||||
error: higher-ranked lifetime error
|
||||
--> $DIR/issue-71546.rs:9:27
|
||||
|
|
||||
LL | let csv_str: String = value
|
||||
| ___________________________^
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
... |
|
||||
LL | |
|
||||
LL | | .collect::<String>();
|
||||
| |____________________________^
|
||||
|
|
||||
= note: could not prove for<'r, 's> Map<<&'r V as IntoIterator>::IntoIter, [closure@$DIR/issue-71546.rs:14:14: 14:37]> well-formed
|
||||
|
||||
error: higher-ranked lifetime error
|
||||
--> $DIR/issue-71546.rs:14:14
|
||||
|
|
||||
LL | .map(|elem| elem.to_string())
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: could not prove for<'a> <&'a V as IntoIterator>::Item: 'static
|
||||
|
||||
error: higher-ranked lifetime error
|
||||
--> $DIR/issue-71546.rs:16:10
|
||||
|
|
||||
LL | .collect::<String>();
|
||||
| ^^^^^^^
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
|
|
@ -1,14 +1,16 @@
|
|||
// Regression test of #86483.
|
||||
//
|
||||
// Made to pass as part of fixing #98095.
|
||||
//
|
||||
// check-pass
|
||||
|
||||
#![feature(generic_associated_types)]
|
||||
|
||||
pub trait IceIce<T> //~ ERROR: the parameter type `T` may not live long enough
|
||||
pub trait IceIce<T>
|
||||
where
|
||||
for<'a> T: 'a,
|
||||
{
|
||||
type Ice<'v>: IntoIterator<Item = &'v T>;
|
||||
//~^ ERROR: the parameter type `T` may not live long enough
|
||||
//~| ERROR: the parameter type `T` may not live long enough
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
error[E0311]: the parameter type `T` may not live long enough
|
||||
--> $DIR/issue-86483.rs:5:1
|
||||
|
|
||||
LL | / pub trait IceIce<T>
|
||||
LL | | where
|
||||
LL | | for<'a> T: 'a,
|
||||
LL | | {
|
||||
... |
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_^
|
||||
|
|
||||
= note: ...so that the type `T` will meet its required lifetime bounds...
|
||||
note: ...that is required by this bound
|
||||
--> $DIR/issue-86483.rs:7:16
|
||||
|
|
||||
LL | for<'a> T: 'a,
|
||||
| ^^
|
||||
help: consider adding an explicit lifetime bound...
|
||||
|
|
||||
LL | for<'a> T: 'a + 'a,
|
||||
| ++++
|
||||
|
||||
error[E0311]: the parameter type `T` may not live long enough
|
||||
--> $DIR/issue-86483.rs:9:5
|
||||
|
|
||||
LL | type Ice<'v>: IntoIterator<Item = &'v T>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
|
||||
|
|
||||
note: ...that is required by this bound
|
||||
--> $DIR/issue-86483.rs:7:16
|
||||
|
|
||||
LL | for<'a> T: 'a,
|
||||
| ^^
|
||||
help: consider adding an explicit lifetime bound...
|
||||
|
|
||||
LL | for<'a> T: 'a + 'a,
|
||||
| ++++
|
||||
|
||||
error[E0309]: the parameter type `T` may not live long enough
|
||||
--> $DIR/issue-86483.rs:9:32
|
||||
|
|
||||
LL | type Ice<'v>: IntoIterator<Item = &'v T>;
|
||||
| ^^^^^^^^^^^^ - help: consider adding a where clause: `where T: 'v`
|
||||
| |
|
||||
| ...so that the reference type `&'v T` does not outlive the data it points at
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0309`.
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
// Regression test for #88586: a higher-ranked outlives bound on Self in a trait
|
||||
// definition caused an ICE when debug_assertions were enabled.
|
||||
//
|
||||
// FIXME: The error output in the absence of the ICE is unhelpful; this should be improved.
|
||||
// Made to pass as part of fixing #98095.
|
||||
//
|
||||
// check-pass
|
||||
|
||||
trait A where for<'a> Self: 'a
|
||||
//~^ ERROR the parameter type `Self` may not live long enough
|
||||
trait A where
|
||||
for<'a> Self: 'a,
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
error[E0311]: the parameter type `Self` may not live long enough
|
||||
--> $DIR/issue-88586-hr-self-outlives-in-trait-def.rs:6:1
|
||||
|
|
||||
LL | / trait A where for<'a> Self: 'a
|
||||
LL | |
|
||||
LL | | {
|
||||
LL | | }
|
||||
| |_^
|
||||
|
|
||||
= help: consider adding an explicit lifetime bound `Self: 'a`...
|
||||
= note: ...so that the type `Self` will meet its required lifetime bounds...
|
||||
note: ...that is required by this bound
|
||||
--> $DIR/issue-88586-hr-self-outlives-in-trait-def.rs:6:29
|
||||
|
|
||||
LL | trait A where for<'a> Self: 'a
|
||||
| ^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue