Skip to content

Commit 0b4791e

Browse files
committed
make NLL handle IfEq bounds by using SCC normalization
1 parent 0f5dae0 commit 0b4791e

File tree

10 files changed

+630
-9
lines changed

10 files changed

+630
-9
lines changed

src/librustc_data_structures/indexed_vec.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,13 @@ impl<I: Idx, T> IndexVec<I, T> {
535535
self.raw.len()
536536
}
537537

538+
/// Gives the next index that will be assigned when `push` is
539+
/// called.
540+
#[inline]
541+
pub fn next_index(&self) -> I {
542+
I::new(self.len())
543+
}
544+
538545
#[inline]
539546
pub fn is_empty(&self) -> bool {
540547
self.raw.is_empty()

src/librustc_mir/borrow_check/nll/region_infer/mod.rs

Lines changed: 113 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ pub struct RegionInferenceContext<'tcx> {
6969
/// visible from this index.
7070
scc_universes: IndexVec<ConstraintSccIndex, ty::UniverseIndex>,
7171

72+
/// Contains a "representative" from each SCC. This will be the
73+
/// minimal RegionVid belonging to that universe. It is used as a
74+
/// kind of hacky way to manage checking outlives relationships,
75+
/// since we can 'canonicalize' each region to the representative
76+
/// of its SCC and be sure that -- if they have the same repr --
77+
/// they *must* be equal (though not having the same repr does not
78+
/// mean they are unequal).
79+
scc_representatives: IndexVec<ConstraintSccIndex, ty::RegionVid>,
80+
7281
/// The final inferred values of the region variables; we compute
7382
/// one value per SCC. To get the value for any given *region*,
7483
/// you first find which scc it is a part of.
@@ -208,13 +217,16 @@ impl<'tcx> RegionInferenceContext<'tcx> {
208217

209218
let scc_universes = Self::compute_scc_universes(&constraint_sccs, &definitions);
210219

220+
let scc_representatives = Self::compute_scc_representatives(&constraint_sccs, &definitions);
221+
211222
let mut result = Self {
212223
definitions,
213224
liveness_constraints,
214225
constraints,
215226
constraint_graph,
216227
constraint_sccs,
217228
scc_universes,
229+
scc_representatives,
218230
scc_values,
219231
type_tests,
220232
universal_regions,
@@ -251,6 +263,27 @@ impl<'tcx> RegionInferenceContext<'tcx> {
251263
scc_universes
252264
}
253265

266+
/// For each SCC, we compute a unique `RegionVid` (in fact, the
267+
/// minimal one that belongs to the SCC). See
268+
/// `scc_representatives` field of `RegionInferenceContext` for
269+
/// more details.
270+
fn compute_scc_representatives(
271+
constraints_scc: &Sccs<RegionVid, ConstraintSccIndex>,
272+
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
273+
) -> IndexVec<ConstraintSccIndex, ty::RegionVid> {
274+
let num_sccs = constraints_scc.num_sccs();
275+
let next_region_vid = definitions.next_index();
276+
let mut scc_representatives = IndexVec::from_elem_n(next_region_vid, num_sccs);
277+
278+
for region_vid in definitions.indices() {
279+
let scc = constraints_scc.scc(region_vid);
280+
let prev_min = scc_representatives[scc];
281+
scc_representatives[scc] = region_vid.min(prev_min);
282+
}
283+
284+
scc_representatives
285+
}
286+
254287
/// Initializes the region variables for each universally
255288
/// quantified region (lifetime parameter). The first N variables
256289
/// always correspond to the regions appearing in the function
@@ -545,7 +578,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
545578
for type_test in &self.type_tests {
546579
debug!("check_type_test: {:?}", type_test);
547580

548-
if self.eval_verify_bound(mir, type_test.lower_bound, &type_test.verify_bound) {
581+
let generic_ty = type_test.generic_kind.to_ty(tcx);
582+
if self.eval_verify_bound(
583+
tcx,
584+
mir,
585+
generic_ty,
586+
type_test.lower_bound,
587+
&type_test.verify_bound,
588+
) {
549589
continue;
550590
}
551591

@@ -679,7 +719,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
679719
// where `ur` is a local bound -- we are sometimes in a
680720
// position to prove things that our caller cannot. See
681721
// #53570 for an example.
682-
if self.eval_verify_bound(mir, ur, &type_test.verify_bound) {
722+
if self.eval_verify_bound(tcx, mir, generic_ty, ur, &type_test.verify_bound) {
683723
continue;
684724
}
685725

@@ -853,7 +893,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
853893
/// `point`, and returns true or false.
854894
fn eval_verify_bound(
855895
&self,
896+
tcx: TyCtxt<'_, '_, 'tcx>,
856897
mir: &Mir<'tcx>,
898+
generic_ty: Ty<'tcx>,
857899
lower_bound: RegionVid,
858900
verify_bound: &VerifyBound<'tcx>,
859901
) -> bool {
@@ -863,23 +905,85 @@ impl<'tcx> RegionInferenceContext<'tcx> {
863905
);
864906

865907
match verify_bound {
866-
VerifyBound::IfEq(..) => false, // FIXME
908+
VerifyBound::IfEq(test_ty, verify_bound1) => {
909+
self.eval_if_eq(tcx, mir, generic_ty, lower_bound, test_ty, verify_bound1)
910+
}
867911

868912
VerifyBound::OutlivedBy(r) => {
869913
let r_vid = self.to_region_vid(r);
870914
self.eval_outlives(mir, r_vid, lower_bound)
871915
}
872916

873-
VerifyBound::AnyBound(verify_bounds) => verify_bounds
874-
.iter()
875-
.any(|verify_bound| self.eval_verify_bound(mir, lower_bound, verify_bound)),
917+
VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| {
918+
self.eval_verify_bound(tcx, mir, generic_ty, lower_bound, verify_bound)
919+
}),
876920

877-
VerifyBound::AllBounds(verify_bounds) => verify_bounds
878-
.iter()
879-
.all(|verify_bound| self.eval_verify_bound(mir, lower_bound, verify_bound)),
921+
VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| {
922+
self.eval_verify_bound(tcx, mir, generic_ty, lower_bound, verify_bound)
923+
}),
880924
}
881925
}
882926

927+
fn eval_if_eq(
928+
&self,
929+
tcx: TyCtxt<'_, '_, 'tcx>,
930+
mir: &Mir<'tcx>,
931+
generic_ty: Ty<'tcx>,
932+
lower_bound: RegionVid,
933+
test_ty: Ty<'tcx>,
934+
verify_bound: &VerifyBound<'tcx>,
935+
) -> bool {
936+
let generic_ty_normalized = self.normalize_to_scc_representatives(tcx, generic_ty);
937+
let test_ty_normalized = self.normalize_to_scc_representatives(tcx, test_ty);
938+
if generic_ty_normalized == test_ty_normalized {
939+
self.eval_verify_bound(tcx, mir, generic_ty, lower_bound, verify_bound)
940+
} else {
941+
false
942+
}
943+
}
944+
945+
/// This is a conservative normalization procedure. It takes every
946+
/// free region in `value` and replaces it with the
947+
/// "representative" of its SCC (see `scc_representatives` field).
948+
/// We are guaranteed that if two values normalize to the same
949+
/// thing, then they are equal; this is a conservative check in
950+
/// that they could still be equal even if they normalize to
951+
/// different results. (For example, there might be two regions
952+
/// with the same value that are not in the same SCC).
953+
///
954+
/// NB. This is not an ideal approach and I would like to revisit
955+
/// it. However, it works pretty well in practice. In particular,
956+
/// this is needed to deal with projection outlives bounds like
957+
///
958+
/// <T as Foo<'0>>::Item: '1
959+
///
960+
/// In particular, this routine winds up being important when
961+
/// there are bounds like `where <T as Foo<'a>>::Item: 'b` in the
962+
/// environment. In this case, if we can show that `'0 == 'a`,
963+
/// and that `'b: '1`, then we know that the clause is
964+
/// satisfied. In such cases, particularly due to limitations of
965+
/// the trait solver =), we usually wind up with a where-clause like
966+
/// `T: Foo<'a>` in scope, which thus forces `'0 == 'a` to be added as
967+
/// a constraint, and thus ensures that they are in the same SCC.
968+
///
969+
/// So why can't we do a more correct routine? Well, we could
970+
/// *almost* use the `relate_tys` code, but the way it is
971+
/// currently setup it creates inference variables to deal with
972+
/// higher-ranked things and so forth, and right now the inference
973+
/// context is not permitted to make more inference variables. So
974+
/// we use this kind of hacky solution.
975+
fn normalize_to_scc_representatives<T>(&self, tcx: TyCtxt<'_, '_, 'tcx>, value: T) -> T
976+
where
977+
T: TypeFoldable<'tcx>,
978+
{
979+
tcx.fold_regions(&value, &mut false, |r, _db| {
980+
let vid = self.to_region_vid(r);
981+
let scc = self.constraint_sccs.scc(vid);
982+
let repr = self.scc_representatives[scc];
983+
tcx.mk_region(ty::ReVar(repr))
984+
})
985+
}
986+
883987
// Evaluate whether `sup_region: sub_region @ point`.
884988
fn eval_outlives(
885989
&self,
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Regression test for #53789.
2+
//
3+
// compile-pass
4+
5+
#![feature(nll)]
6+
#![allow(unused_variables)]
7+
8+
use std::collections::BTreeMap;
9+
10+
trait ValueTree {
11+
type Value;
12+
}
13+
14+
trait Strategy {
15+
type Value: ValueTree;
16+
}
17+
18+
type StrategyFor<A> = StrategyType<'static, A>;
19+
type StrategyType<'a, A> = <A as Arbitrary<'a>>::Strategy;
20+
21+
impl<K: ValueTree, V: ValueTree> Strategy for (K, V) {
22+
type Value = TupleValueTree<(K, V)>;
23+
}
24+
25+
impl<K: ValueTree, V: ValueTree> ValueTree for TupleValueTree<(K, V)> {
26+
type Value = BTreeMapValueTree<K, V>;
27+
}
28+
29+
struct TupleValueTree<T> {
30+
tree: T,
31+
}
32+
33+
struct BTreeMapStrategy<K, V>(std::marker::PhantomData<(K, V)>)
34+
where
35+
K: Strategy,
36+
V: Strategy;
37+
38+
struct BTreeMapValueTree<K, V>(std::marker::PhantomData<(K, V)>)
39+
where
40+
K: ValueTree,
41+
V: ValueTree;
42+
43+
impl<K, V> Strategy for BTreeMapStrategy<K, V>
44+
where
45+
K: Strategy,
46+
V: Strategy,
47+
{
48+
type Value = BTreeMapValueTree<K::Value, V::Value>;
49+
}
50+
51+
impl<K, V> ValueTree for BTreeMapValueTree<K, V>
52+
where
53+
K: ValueTree,
54+
V: ValueTree,
55+
{
56+
type Value = BTreeMap<K::Value, V::Value>;
57+
}
58+
59+
trait Arbitrary<'a>: Sized {
60+
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy;
61+
type Parameters;
62+
type Strategy: Strategy<Value = Self::ValueTree>;
63+
type ValueTree: ValueTree<Value = Self>;
64+
}
65+
66+
impl<'a, A, B> Arbitrary<'a> for BTreeMap<A, B>
67+
where
68+
A: Arbitrary<'static>,
69+
B: Arbitrary<'static>,
70+
StrategyFor<A>: 'static,
71+
StrategyFor<B>: 'static,
72+
{
73+
type ValueTree = <Self::Strategy as Strategy>::Value;
74+
type Parameters = (A::Parameters, B::Parameters);
75+
type Strategy = BTreeMapStrategy<A::Strategy, B::Strategy>;
76+
fn arbitrary_with(args: Self::Parameters) -> BTreeMapStrategy<A::Strategy, B::Strategy> {
77+
let (a, b) = args;
78+
btree_map(any_with::<A>(a), any_with::<B>(b))
79+
}
80+
}
81+
82+
fn btree_map<K: Strategy + 'static, V: Strategy>(key: K, value: V) -> BTreeMapStrategy<K, V> {
83+
unimplemented!()
84+
}
85+
86+
fn any_with<'a, A: Arbitrary<'a>>(args: A::Parameters) -> StrategyType<'a, A> {
87+
unimplemented!()
88+
}
89+
90+
fn main() { }
91+

0 commit comments

Comments
 (0)