提交 0b4791e6 编写于 作者: N Niko Matsakis

make NLL handle `IfEq` bounds by using SCC normalization

上级 0f5dae03
......@@ -535,6 +535,13 @@ pub fn len(&self) -> usize {
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 @@ pub(crate) fn new(
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 @@ pub(crate) fn new(
constraint_graph,
constraint_sccs,
scc_universes,
scc_representatives,
scc_values,
type_tests,
universal_regions,
......@@ -251,6 +263,27 @@ fn compute_scc_universes(
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 @@ fn check_type_tests<'gcx>(
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 @@ fn try_promote_type_test<'gcx>(
// 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 @@ fn universal_upper_bound(&self, r: RegionVid) -> RegionVid {
/// `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 @@ fn eval_verify_bound(
);
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,
......
// 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() { }
// 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() { }
// 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() {}
#![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() {}
// 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() {}
#![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() {}
#![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() {}
#![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() {}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册