Skip to content

Commit

Permalink
add a IsEmpty for use in verified bounds
Browse files Browse the repository at this point in the history
We currently have a kind of arbitrary check for `Verify` conditions
which says that if the "test region" is `'empty`, then the check
passes. This was added to fix #42467 -- it happens to be correct for
the purposes that we use verify bounds for, but it doesn't feel
generally correct. Replace with a more principled test.
  • Loading branch information
nikomatsakis committed Feb 6, 2020
1 parent 82c1435 commit 65fc086
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 8 deletions.
14 changes: 8 additions & 6 deletions src/librustc/infer/lexical_region_resolve/mod.rs
Expand Up @@ -592,12 +592,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
debug!("collect_errors: verify={:?}", verify);
let sub = var_data.normalize(self.tcx(), verify.region);

// This was an inference variable which didn't get
// constrained, therefore it can be assume to hold.
if let ty::ReEmpty = *sub {
continue;
}

let verify_kind_ty = verify.kind.to_ty(self.tcx());
if self.bound_is_met(&verify.bound, var_data, verify_kind_ty, sub) {
continue;
Expand Down Expand Up @@ -893,6 +887,14 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
self.region_rels.is_subregion_of(min, var_values.normalize(self.tcx(), r))
}

VerifyBound::IsEmpty => {
if let ty::ReEmpty = min {
true
} else {
false
}
}

VerifyBound::AnyBound(bs) => {
bs.iter().any(|b| self.bound_is_met(b, var_values, generic_ty, min))
}
Expand Down
13 changes: 12 additions & 1 deletion src/librustc/infer/outlives/verify.rs
Expand Up @@ -60,7 +60,18 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
// scope type parameters:
let param_bounds = param_bounds.chain(self.implicit_region_bound);

VerifyBound::AnyBound(param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect())
let any_bounds: Vec<_> = param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect();

if any_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 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)
}
}

/// Given a projection like `T::Item`, searches the environment
Expand Down
6 changes: 5 additions & 1 deletion src/librustc/infer/region_constraints/mod.rs
Expand Up @@ -233,6 +233,9 @@ pub enum VerifyBound<'tcx> {
/// if `R: min`, then by transitivity `G: min`.
OutlivedBy(Region<'tcx>),

/// Given a region `R`, true if it is `'empty`.
IsEmpty,

/// Given a set of bounds `B`, expands to the function:
///
/// ```rust
Expand Down Expand Up @@ -867,6 +870,7 @@ impl<'tcx> VerifyBound<'tcx> {
VerifyBound::IfEq(..) => false,
VerifyBound::OutlivedBy(ty::ReStatic) => true,
VerifyBound::OutlivedBy(_) => false,
VerifyBound::IsEmpty => false,
VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()),
VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()),
}
Expand All @@ -875,7 +879,7 @@ impl<'tcx> VerifyBound<'tcx> {
pub fn cannot_hold(&self) -> bool {
match self {
VerifyBound::IfEq(_, b) => b.cannot_hold(),
VerifyBound::OutlivedBy(ty::ReEmpty) => true,
VerifyBound::IsEmpty => false,
VerifyBound::OutlivedBy(_) => false,
VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()),
VerifyBound::AllBounds(bs) => bs.iter().any(|b| b.cannot_hold()),
Expand Down
5 changes: 5 additions & 0 deletions src/librustc_mir/borrow_check/region_infer/mod.rs
Expand Up @@ -1108,6 +1108,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.eval_if_eq(tcx, body, generic_ty, lower_bound, test_ty, verify_bound1)
}

VerifyBound::IsEmpty => {
let lower_bound_scc = self.constraint_sccs.scc(lower_bound);
self.scc_values.elements_contained_in(lower_bound_scc).next().is_none()
}

VerifyBound::OutlivedBy(r) => {
let r_vid = self.to_region_vid(r);
self.eval_outlives(r_vid, lower_bound)
Expand Down

0 comments on commit 65fc086

Please sign in to comment.