make NLL handle IfEq
bounds by using SCC normalization
This commit is contained in:
parent
0f5dae0322
commit
0b4791e60b
10 changed files with 630 additions and 9 deletions
|
@ -535,6 +535,13 @@ impl<I: Idx, T> IndexVec<I, T> {
|
|||
self.raw.len()
|
||||
}
|
||||
|
||||
/// Gives the next index that will be assigned when `push` is
|
||||
/// called.
|
||||
#[inline]
|
||||
pub fn next_index(&self) -> I {
|
||||
I::new(self.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.raw.is_empty()
|
||||
|
|
|
@ -69,6 +69,15 @@ pub struct RegionInferenceContext<'tcx> {
|
|||
/// visible from this index.
|
||||
scc_universes: IndexVec<ConstraintSccIndex, ty::UniverseIndex>,
|
||||
|
||||
/// Contains a "representative" from each SCC. This will be the
|
||||
/// minimal RegionVid belonging to that universe. It is used as a
|
||||
/// kind of hacky way to manage checking outlives relationships,
|
||||
/// since we can 'canonicalize' each region to the representative
|
||||
/// of its SCC and be sure that -- if they have the same repr --
|
||||
/// they *must* be equal (though not having the same repr does not
|
||||
/// mean they are unequal).
|
||||
scc_representatives: IndexVec<ConstraintSccIndex, ty::RegionVid>,
|
||||
|
||||
/// The final inferred values of the region variables; we compute
|
||||
/// one value per SCC. To get the value for any given *region*,
|
||||
/// you first find which scc it is a part of.
|
||||
|
@ -208,6 +217,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
|
||||
let scc_universes = Self::compute_scc_universes(&constraint_sccs, &definitions);
|
||||
|
||||
let scc_representatives = Self::compute_scc_representatives(&constraint_sccs, &definitions);
|
||||
|
||||
let mut result = Self {
|
||||
definitions,
|
||||
liveness_constraints,
|
||||
|
@ -215,6 +226,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
constraint_graph,
|
||||
constraint_sccs,
|
||||
scc_universes,
|
||||
scc_representatives,
|
||||
scc_values,
|
||||
type_tests,
|
||||
universal_regions,
|
||||
|
@ -251,6 +263,27 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
scc_universes
|
||||
}
|
||||
|
||||
/// For each SCC, we compute a unique `RegionVid` (in fact, the
|
||||
/// minimal one that belongs to the SCC). See
|
||||
/// `scc_representatives` field of `RegionInferenceContext` for
|
||||
/// more details.
|
||||
fn compute_scc_representatives(
|
||||
constraints_scc: &Sccs<RegionVid, ConstraintSccIndex>,
|
||||
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
|
||||
) -> IndexVec<ConstraintSccIndex, ty::RegionVid> {
|
||||
let num_sccs = constraints_scc.num_sccs();
|
||||
let next_region_vid = definitions.next_index();
|
||||
let mut scc_representatives = IndexVec::from_elem_n(next_region_vid, num_sccs);
|
||||
|
||||
for region_vid in definitions.indices() {
|
||||
let scc = constraints_scc.scc(region_vid);
|
||||
let prev_min = scc_representatives[scc];
|
||||
scc_representatives[scc] = region_vid.min(prev_min);
|
||||
}
|
||||
|
||||
scc_representatives
|
||||
}
|
||||
|
||||
/// Initializes the region variables for each universally
|
||||
/// quantified region (lifetime parameter). The first N variables
|
||||
/// always correspond to the regions appearing in the function
|
||||
|
@ -545,7 +578,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
for type_test in &self.type_tests {
|
||||
debug!("check_type_test: {:?}", type_test);
|
||||
|
||||
if self.eval_verify_bound(mir, type_test.lower_bound, &type_test.verify_bound) {
|
||||
let generic_ty = type_test.generic_kind.to_ty(tcx);
|
||||
if self.eval_verify_bound(
|
||||
tcx,
|
||||
mir,
|
||||
generic_ty,
|
||||
type_test.lower_bound,
|
||||
&type_test.verify_bound,
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -679,7 +719,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
// where `ur` is a local bound -- we are sometimes in a
|
||||
// position to prove things that our caller cannot. See
|
||||
// #53570 for an example.
|
||||
if self.eval_verify_bound(mir, ur, &type_test.verify_bound) {
|
||||
if self.eval_verify_bound(tcx, mir, generic_ty, ur, &type_test.verify_bound) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -853,7 +893,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
/// `point`, and returns true or false.
|
||||
fn eval_verify_bound(
|
||||
&self,
|
||||
tcx: TyCtxt<'_, '_, 'tcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
generic_ty: Ty<'tcx>,
|
||||
lower_bound: RegionVid,
|
||||
verify_bound: &VerifyBound<'tcx>,
|
||||
) -> bool {
|
||||
|
@ -863,23 +905,85 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
);
|
||||
|
||||
match verify_bound {
|
||||
VerifyBound::IfEq(..) => false, // FIXME
|
||||
VerifyBound::IfEq(test_ty, verify_bound1) => {
|
||||
self.eval_if_eq(tcx, mir, generic_ty, lower_bound, test_ty, verify_bound1)
|
||||
}
|
||||
|
||||
VerifyBound::OutlivedBy(r) => {
|
||||
let r_vid = self.to_region_vid(r);
|
||||
self.eval_outlives(mir, r_vid, lower_bound)
|
||||
}
|
||||
|
||||
VerifyBound::AnyBound(verify_bounds) => verify_bounds
|
||||
.iter()
|
||||
.any(|verify_bound| self.eval_verify_bound(mir, lower_bound, verify_bound)),
|
||||
VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| {
|
||||
self.eval_verify_bound(tcx, mir, generic_ty, lower_bound, verify_bound)
|
||||
}),
|
||||
|
||||
VerifyBound::AllBounds(verify_bounds) => verify_bounds
|
||||
.iter()
|
||||
.all(|verify_bound| self.eval_verify_bound(mir, lower_bound, verify_bound)),
|
||||
VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| {
|
||||
self.eval_verify_bound(tcx, mir, generic_ty, lower_bound, verify_bound)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_if_eq(
|
||||
&self,
|
||||
tcx: TyCtxt<'_, '_, 'tcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
generic_ty: Ty<'tcx>,
|
||||
lower_bound: RegionVid,
|
||||
test_ty: Ty<'tcx>,
|
||||
verify_bound: &VerifyBound<'tcx>,
|
||||
) -> bool {
|
||||
let generic_ty_normalized = self.normalize_to_scc_representatives(tcx, generic_ty);
|
||||
let test_ty_normalized = self.normalize_to_scc_representatives(tcx, test_ty);
|
||||
if generic_ty_normalized == test_ty_normalized {
|
||||
self.eval_verify_bound(tcx, mir, generic_ty, lower_bound, verify_bound)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a conservative normalization procedure. It takes every
|
||||
/// free region in `value` and replaces it with the
|
||||
/// "representative" of its SCC (see `scc_representatives` field).
|
||||
/// We are guaranteed that if two values normalize to the same
|
||||
/// thing, then they are equal; this is a conservative check in
|
||||
/// that they could still be equal even if they normalize to
|
||||
/// different results. (For example, there might be two regions
|
||||
/// with the same value that are not in the same SCC).
|
||||
///
|
||||
/// NB. This is not an ideal approach and I would like to revisit
|
||||
/// it. However, it works pretty well in practice. In particular,
|
||||
/// this is needed to deal with projection outlives bounds like
|
||||
///
|
||||
/// <T as Foo<'0>>::Item: '1
|
||||
///
|
||||
/// In particular, this routine winds up being important when
|
||||
/// there are bounds like `where <T as Foo<'a>>::Item: 'b` in the
|
||||
/// environment. In this case, if we can show that `'0 == 'a`,
|
||||
/// and that `'b: '1`, then we know that the clause is
|
||||
/// satisfied. In such cases, particularly due to limitations of
|
||||
/// the trait solver =), we usually wind up with a where-clause like
|
||||
/// `T: Foo<'a>` in scope, which thus forces `'0 == 'a` to be added as
|
||||
/// a constraint, and thus ensures that they are in the same SCC.
|
||||
///
|
||||
/// So why can't we do a more correct routine? Well, we could
|
||||
/// *almost* use the `relate_tys` code, but the way it is
|
||||
/// currently setup it creates inference variables to deal with
|
||||
/// higher-ranked things and so forth, and right now the inference
|
||||
/// context is not permitted to make more inference variables. So
|
||||
/// we use this kind of hacky solution.
|
||||
fn normalize_to_scc_representatives<T>(&self, tcx: TyCtxt<'_, '_, 'tcx>, value: T) -> T
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
tcx.fold_regions(&value, &mut false, |r, _db| {
|
||||
let vid = self.to_region_vid(r);
|
||||
let scc = self.constraint_sccs.scc(vid);
|
||||
let repr = self.scc_representatives[scc];
|
||||
tcx.mk_region(ty::ReVar(repr))
|
||||
})
|
||||
}
|
||||
|
||||
// Evaluate whether `sup_region: sub_region @ point`.
|
||||
fn eval_outlives(
|
||||
&self,
|
||||
|
|
91
src/test/ui/nll/ty-outlives/issue-53789-1.rs
Normal file
91
src/test/ui/nll/ty-outlives/issue-53789-1.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Regression test for #53789.
|
||||
//
|
||||
// compile-pass
|
||||
|
||||
#![feature(nll)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
trait ValueTree {
|
||||
type Value;
|
||||
}
|
||||
|
||||
trait Strategy {
|
||||
type Value: ValueTree;
|
||||
}
|
||||
|
||||
type StrategyFor<A> = StrategyType<'static, A>;
|
||||
type StrategyType<'a, A> = <A as Arbitrary<'a>>::Strategy;
|
||||
|
||||
impl<K: ValueTree, V: ValueTree> Strategy for (K, V) {
|
||||
type Value = TupleValueTree<(K, V)>;
|
||||
}
|
||||
|
||||
impl<K: ValueTree, V: ValueTree> ValueTree for TupleValueTree<(K, V)> {
|
||||
type Value = BTreeMapValueTree<K, V>;
|
||||
}
|
||||
|
||||
struct TupleValueTree<T> {
|
||||
tree: T,
|
||||
}
|
||||
|
||||
struct BTreeMapStrategy<K, V>(std::marker::PhantomData<(K, V)>)
|
||||
where
|
||||
K: Strategy,
|
||||
V: Strategy;
|
||||
|
||||
struct BTreeMapValueTree<K, V>(std::marker::PhantomData<(K, V)>)
|
||||
where
|
||||
K: ValueTree,
|
||||
V: ValueTree;
|
||||
|
||||
impl<K, V> Strategy for BTreeMapStrategy<K, V>
|
||||
where
|
||||
K: Strategy,
|
||||
V: Strategy,
|
||||
{
|
||||
type Value = BTreeMapValueTree<K::Value, V::Value>;
|
||||
}
|
||||
|
||||
impl<K, V> ValueTree for BTreeMapValueTree<K, V>
|
||||
where
|
||||
K: ValueTree,
|
||||
V: ValueTree,
|
||||
{
|
||||
type Value = BTreeMap<K::Value, V::Value>;
|
||||
}
|
||||
|
||||
trait Arbitrary<'a>: Sized {
|
||||
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy;
|
||||
type Parameters;
|
||||
type Strategy: Strategy<Value = Self::ValueTree>;
|
||||
type ValueTree: ValueTree<Value = Self>;
|
||||
}
|
||||
|
||||
impl<'a, A, B> Arbitrary<'a> for BTreeMap<A, B>
|
||||
where
|
||||
A: Arbitrary<'static>,
|
||||
B: Arbitrary<'static>,
|
||||
StrategyFor<A>: 'static,
|
||||
StrategyFor<B>: 'static,
|
||||
{
|
||||
type ValueTree = <Self::Strategy as Strategy>::Value;
|
||||
type Parameters = (A::Parameters, B::Parameters);
|
||||
type Strategy = BTreeMapStrategy<A::Strategy, B::Strategy>;
|
||||
fn arbitrary_with(args: Self::Parameters) -> BTreeMapStrategy<A::Strategy, B::Strategy> {
|
||||
let (a, b) = args;
|
||||
btree_map(any_with::<A>(a), any_with::<B>(b))
|
||||
}
|
||||
}
|
||||
|
||||
fn btree_map<K: Strategy + 'static, V: Strategy>(key: K, value: V) -> BTreeMapStrategy<K, V> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn any_with<'a, A: Arbitrary<'a>>(args: A::Parameters) -> StrategyType<'a, A> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
|
251
src/test/ui/nll/ty-outlives/issue-53789-2.rs
Normal file
251
src/test/ui/nll/ty-outlives/issue-53789-2.rs
Normal file
|
@ -0,0 +1,251 @@
|
|||
// Regression test for #53789.
|
||||
//
|
||||
// compile-pass
|
||||
|
||||
#![feature(nll)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Range;
|
||||
use std::cmp::Ord;
|
||||
|
||||
macro_rules! valuetree {
|
||||
() => {
|
||||
type ValueTree =
|
||||
<Self::Strategy as $crate::Strategy>::Value;
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! product_unpack {
|
||||
($factor: pat) => {
|
||||
($factor,)
|
||||
};
|
||||
($($factor: pat),*) => {
|
||||
( $( $factor ),* )
|
||||
};
|
||||
($($factor: pat),*,) => {
|
||||
( $( $factor ),* )
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! product_type {
|
||||
($factor: ty) => {
|
||||
($factor,)
|
||||
};
|
||||
($($factor: ty),*) => {
|
||||
( $( $factor, )* )
|
||||
};
|
||||
($($factor: ty),*,) => {
|
||||
( $( $factor, )* )
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! default {
|
||||
($type: ty, $val: expr) => {
|
||||
impl Default for $type {
|
||||
fn default() -> Self { $val.into() }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Pervasive internal sugar
|
||||
macro_rules! mapfn {
|
||||
($(#[$meta:meta])* [$($vis:tt)*]
|
||||
fn $name:ident[$($gen:tt)*]($parm:ident: $input:ty) -> $output:ty {
|
||||
$($body:tt)*
|
||||
}) => {
|
||||
$(#[$meta])*
|
||||
#[derive(Clone, Copy)]
|
||||
$($vis)* struct $name;
|
||||
impl $($gen)* statics::MapFn<$input> for $name {
|
||||
type Output = $output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! opaque_strategy_wrapper {
|
||||
($(#[$smeta:meta])* pub struct $stratname:ident
|
||||
[$($sgen:tt)*][$($swhere:tt)*]
|
||||
($innerstrat:ty) -> $stratvtty:ty;
|
||||
|
||||
$(#[$vmeta:meta])* pub struct $vtname:ident
|
||||
[$($vgen:tt)*][$($vwhere:tt)*]
|
||||
($innervt:ty) -> $actualty:ty;
|
||||
) => {
|
||||
$(#[$smeta])* struct $stratname $($sgen)* (std::marker::PhantomData<(K, V)>)
|
||||
$($swhere)*;
|
||||
|
||||
$(#[$vmeta])* struct $vtname $($vgen)* ($innervt) $($vwhere)*;
|
||||
|
||||
impl $($sgen)* Strategy for $stratname $($sgen)* $($swhere)* {
|
||||
type Value = $stratvtty;
|
||||
}
|
||||
|
||||
impl $($vgen)* ValueTree for $vtname $($vgen)* $($vwhere)* {
|
||||
type Value = $actualty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait ValueTree {
|
||||
type Value;
|
||||
}
|
||||
|
||||
trait Strategy {
|
||||
type Value : ValueTree;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct VecStrategy<T : Strategy> {
|
||||
element: T,
|
||||
size: Range<usize>,
|
||||
}
|
||||
|
||||
fn vec<T : Strategy>(element: T, size: Range<usize>)
|
||||
-> VecStrategy<T> {
|
||||
VecStrategy {
|
||||
element: element,
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
type ValueFor<S> = <<S as Strategy>::Value as ValueTree>::Value;
|
||||
|
||||
trait Arbitrary<'a>: Sized {
|
||||
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy;
|
||||
|
||||
type Parameters: Default;
|
||||
type Strategy: Strategy<Value = Self::ValueTree>;
|
||||
type ValueTree: ValueTree<Value = Self>;
|
||||
}
|
||||
|
||||
type StrategyFor<A> = StrategyType<'static, A>;
|
||||
type StrategyType<'a, A> = <A as Arbitrary<'a>>::Strategy;
|
||||
|
||||
//#[derive(Clone, PartialEq, Eq, Hash, Debug, From, Into)]
|
||||
struct SizeBounds(Range<usize>);
|
||||
default!(SizeBounds, 0..100);
|
||||
|
||||
|
||||
impl From<Range<usize>> for SizeBounds {
|
||||
fn from(high: Range<usize>) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SizeBounds> for Range<usize> {
|
||||
fn from(high: SizeBounds) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn any_with<'a, A: Arbitrary<'a>>(args: A::Parameters)
|
||||
-> StrategyType<'a, A> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
impl<K: ValueTree, V: ValueTree> Strategy for (K, V) where
|
||||
<K as ValueTree>::Value: Ord {
|
||||
type Value = TupleValueTree<(K, V)>;
|
||||
}
|
||||
|
||||
impl<K: ValueTree, V: ValueTree> ValueTree for TupleValueTree<(K, V)> where
|
||||
<K as ValueTree>::Value: Ord {
|
||||
type Value = BTreeMapValueTree<K, V>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct VecValueTree<T : ValueTree> {
|
||||
elements: Vec<T>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct TupleValueTree<T> {
|
||||
tree: T,
|
||||
}
|
||||
|
||||
opaque_strategy_wrapper! {
|
||||
#[derive(Clone)]
|
||||
pub struct BTreeMapStrategy[<K, V>]
|
||||
[where K : Strategy, V : Strategy, ValueFor<K> : Ord](
|
||||
statics::Filter<statics::Map<VecStrategy<(K,V)>,
|
||||
VecToBTreeMap>, MinSize>)
|
||||
-> BTreeMapValueTree<K::Value, V::Value>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BTreeMapValueTree[<K, V>]
|
||||
[where K : ValueTree, V : ValueTree, K::Value : Ord](
|
||||
statics::Filter<statics::Map<VecValueTree<TupleValueTree<(K, V)>>,
|
||||
VecToBTreeMap>, MinSize>)
|
||||
-> BTreeMap<K::Value, V::Value>;
|
||||
}
|
||||
|
||||
type RangedParams2<A, B> = product_type![SizeBounds, A, B];
|
||||
|
||||
impl<'a, A, B> Arbitrary<'a> for BTreeMap<A, B>
|
||||
where
|
||||
A: Arbitrary<'static> + Ord,
|
||||
B: Arbitrary<'static>,
|
||||
StrategyFor<A>: 'static,
|
||||
StrategyFor<B>: 'static,
|
||||
{
|
||||
valuetree!();
|
||||
type Parameters = RangedParams2<A::Parameters, B::Parameters>;
|
||||
type Strategy = BTreeMapStrategy<A::Strategy, B::Strategy>;
|
||||
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
|
||||
let product_unpack![range, a, b] = args;
|
||||
btree_map(any_with::<A>(a), any_with::<B>(b), range.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct MinSize(usize);
|
||||
|
||||
mapfn! {
|
||||
[] fn VecToBTreeMap[<K : Ord, V>]
|
||||
(vec: Vec<(K, V)>) -> BTreeMap<K, V>
|
||||
{
|
||||
vec.into_iter().collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn btree_map<K : Strategy + 'static, V : Strategy + 'static>
|
||||
(key: K, value: V, size: Range<usize>)
|
||||
-> BTreeMapStrategy<K, V>
|
||||
where ValueFor<K> : Ord {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
mod statics {
|
||||
pub(super) trait MapFn<T> {
|
||||
type Output;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Filter<S, F> {
|
||||
source: S,
|
||||
fun: F,
|
||||
}
|
||||
|
||||
impl<S, F> Filter<S, F> {
|
||||
pub fn new(source: S, whence: String, filter: F) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Map<S, F> {
|
||||
source: S,
|
||||
fun: F,
|
||||
}
|
||||
|
||||
impl<S, F> Map<S, F> {
|
||||
pub fn new(source: S, fun: F) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
|
25
src/test/ui/nll/ty-outlives/projection-body.rs
Normal file
25
src/test/ui/nll/ty-outlives/projection-body.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Test that when we infer the lifetime to a subset of the fn body, it
|
||||
// works out.
|
||||
|
||||
trait MyTrait<'a> {
|
||||
type Output;
|
||||
}
|
||||
|
||||
fn foo1<T>()
|
||||
where
|
||||
for<'x> T: MyTrait<'x>,
|
||||
{
|
||||
// Here the region `'c` in `<T as MyTrait<'c>>::Output` will be
|
||||
// inferred to a subset of the fn body.
|
||||
let x = bar::<T::Output>();
|
||||
drop(x);
|
||||
}
|
||||
|
||||
fn bar<'a, T>() -> &'a ()
|
||||
where
|
||||
T: 'a,
|
||||
{
|
||||
&()
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,36 @@
|
|||
#![feature(nll)]
|
||||
|
||||
// Test that we are able to establish that `<T as
|
||||
// MyTrait<'a>>::Output` outlives `'b` here. We need to prove however
|
||||
// that `<T as MyTrait<'a>>::Output` outlives `'a`, so we also have to
|
||||
// prove that `'b: 'a`.
|
||||
|
||||
trait MyTrait<'a> {
|
||||
type Output;
|
||||
}
|
||||
|
||||
fn foo1<'a, 'b, T>() -> &'a ()
|
||||
where
|
||||
T: MyTrait<'a>,
|
||||
<T as MyTrait<'a>>::Output: 'b,
|
||||
{
|
||||
bar::<T::Output>() //~ ERROR may not live long enough
|
||||
}
|
||||
|
||||
fn foo2<'a, 'b, T>() -> &'a ()
|
||||
where
|
||||
T: MyTrait<'a>,
|
||||
<T as MyTrait<'a>>::Output: 'b,
|
||||
'b: 'a,
|
||||
{
|
||||
bar::<T::Output>() // OK
|
||||
}
|
||||
|
||||
fn bar<'a, T>() -> &'a ()
|
||||
where
|
||||
T: 'a,
|
||||
{
|
||||
&()
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,24 @@
|
|||
// Test that if we need to prove that `<T as MyTrait<'a>>::Output:
|
||||
// 'a`, but we only know that `<T as MyTrait<'b>>::Output: 'a`, that
|
||||
// doesn't suffice.
|
||||
|
||||
trait MyTrait<'a> {
|
||||
type Output;
|
||||
}
|
||||
|
||||
fn foo1<'a, 'b, T>() -> &'a ()
|
||||
where
|
||||
for<'x> T: MyTrait<'x>,
|
||||
<T as MyTrait<'b>>::Output: 'a,
|
||||
{
|
||||
bar::<<T as MyTrait<'a>>::Output>() //~ ERROR the associated type `<T as MyTrait<'a>>::Output` may not live long enough
|
||||
}
|
||||
|
||||
fn bar<'a, T>() -> &'a ()
|
||||
where
|
||||
T: 'a,
|
||||
{
|
||||
&()
|
||||
}
|
||||
|
||||
fn main() {}
|
30
src/test/ui/nll/ty-outlives/projection-where-clause-env.rs
Normal file
30
src/test/ui/nll/ty-outlives/projection-where-clause-env.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
#![feature(nll)]
|
||||
|
||||
// Test that when we have a `<T as MyTrait<'a>>::Output: 'a`
|
||||
// relationship in the environment we take advantage of it. In this
|
||||
// case, that means we **don't** have to prove that `T: 'a`.
|
||||
//
|
||||
// Regression test for #53121.
|
||||
//
|
||||
// compile-pass
|
||||
|
||||
trait MyTrait<'a> {
|
||||
type Output;
|
||||
}
|
||||
|
||||
fn foo<'a, T>() -> &'a ()
|
||||
where
|
||||
T: MyTrait<'a>,
|
||||
<T as MyTrait<'a>>::Output: 'a,
|
||||
{
|
||||
bar::<T::Output>()
|
||||
}
|
||||
|
||||
fn bar<'a, T>() -> &'a ()
|
||||
where
|
||||
T: 'a,
|
||||
{
|
||||
&()
|
||||
}
|
||||
|
||||
fn main() {}
|
26
src/test/ui/nll/ty-outlives/projection-where-clause-none.rs
Normal file
26
src/test/ui/nll/ty-outlives/projection-where-clause-none.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
#![feature(nll)]
|
||||
|
||||
// Test that we are NOT able to establish that `<T as
|
||||
// MyTrait<'a>>::Output: 'a` outlives `'a` here -- we have only one
|
||||
// recourse, which is to prove that `T: 'a` and `'a: 'a`, but we don't
|
||||
// know that `T: 'a`.
|
||||
|
||||
trait MyTrait<'a> {
|
||||
type Output;
|
||||
}
|
||||
|
||||
fn foo<'a, T>() -> &'a ()
|
||||
where
|
||||
T: MyTrait<'a>,
|
||||
{
|
||||
bar::<T::Output>() //~ ERROR the parameter type `T` may not live long enough
|
||||
}
|
||||
|
||||
fn bar<'a, T>() -> &'a ()
|
||||
where
|
||||
T: 'a,
|
||||
{
|
||||
&()
|
||||
}
|
||||
|
||||
fn main() {}
|
27
src/test/ui/nll/ty-outlives/projection-where-clause-trait.rs
Normal file
27
src/test/ui/nll/ty-outlives/projection-where-clause-trait.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
#![feature(nll)]
|
||||
|
||||
// Test that we are able to establish that `<T as
|
||||
// MyTrait<'a>>::Output: 'a` outlives `'a` (because the trait says
|
||||
// so).
|
||||
//
|
||||
// compile-pass
|
||||
|
||||
trait MyTrait<'a> {
|
||||
type Output: 'a;
|
||||
}
|
||||
|
||||
fn foo<'a, T>() -> &'a ()
|
||||
where
|
||||
T: MyTrait<'a>,
|
||||
{
|
||||
bar::<T::Output>()
|
||||
}
|
||||
|
||||
fn bar<'a, T>() -> &'a ()
|
||||
where
|
||||
T: 'a,
|
||||
{
|
||||
&()
|
||||
}
|
||||
|
||||
fn main() {}
|
Loading…
Add table
Add a link
Reference in a new issue