-
Notifications
You must be signed in to change notification settings - Fork 12.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0c94ea0
commit 561ce44
Showing
8 changed files
with
277 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,162 @@ | |||
use super::*; | |||
use crate::infer::{CombinedSnapshot, PlaceholderMap}; | |||
use crate::ty::error::TypeError; | |||
use crate::ty::relate::RelateResult; | |||
|
|||
impl<'tcx> RegionConstraintCollector<'tcx> { | |||
/// Searches region constraints created since `snapshot` that | |||
/// affect one of the placeholders in `placeholder_map`, returning | |||
/// an error if any of the placeholders are related to another | |||
/// placeholder or would have to escape into some parent universe | |||
/// that cannot name them. | |||
/// | |||
/// This is a temporary backwards compatibility measure to try and | |||
/// retain the older (arguably incorrect) behavior of the | |||
/// compiler. | |||
/// | |||
/// NB. The use of snapshot here is mostly an efficiency thing -- | |||
/// we could search *all* region constraints, but that'd be a | |||
/// bigger set and the data structures are not setup for that. If | |||
/// we wind up keeping some form of this check long term, it would | |||
/// probably be better to remove the snapshot parameter and to | |||
/// refactor the constraint set. | |||
pub fn leak_check( | |||
&mut self, | |||
tcx: TyCtxt<'_, '_, 'tcx>, | |||
overly_polymorphic: bool, | |||
placeholder_map: &PlaceholderMap<'tcx>, | |||
_snapshot: &CombinedSnapshot<'_, 'tcx>, | |||
) -> RelateResult<'tcx, ()> { | |||
debug!("leak_check(placeholders={:?})", placeholder_map); | |||
|
|||
assert!(self.in_snapshot()); | |||
|
|||
// Go through each placeholder that we created. | |||
for (_, &placeholder_region) in placeholder_map { | |||
// Find the universe this placeholder inhabits. | |||
let placeholder = match placeholder_region { | |||
ty::RePlaceholder(p) => p, | |||
_ => bug!( | |||
"leak_check: expected placeholder found {:?}", | |||
placeholder_region, | |||
), | |||
}; | |||
|
|||
// Find all regions that are related to this placeholder | |||
// in some way. This means any region that either outlives | |||
// or is outlived by a placeholder. | |||
let mut taint_set = TaintSet::new( | |||
TaintDirections::both(), | |||
placeholder_region, | |||
); | |||
taint_set.fixed_point(tcx, &self.undo_log, &self.data.verifys); | |||
let tainted_regions = taint_set.into_set(); | |||
|
|||
// Report an error if two placeholders in the same universe | |||
// are related to one another, or if a placeholder is related | |||
// to something from a parent universe. | |||
for &tainted_region in &tainted_regions { | |||
if let ty::RePlaceholder(_) = tainted_region { | |||
// Two placeholders cannot be related: | |||
if tainted_region == placeholder_region { | |||
continue; | |||
} | |||
} else if self.universe(tainted_region).can_name(placeholder.universe) { | |||
continue; | |||
} | |||
|
|||
return Err(if overly_polymorphic { | |||
debug!("Overly polymorphic!"); | |||
TypeError::RegionsOverlyPolymorphic(placeholder.name, tainted_region) | |||
} else { | |||
debug!("Not as polymorphic!"); | |||
TypeError::RegionsInsufficientlyPolymorphic(placeholder.name, tainted_region) | |||
}); | |||
} | |||
} | |||
|
|||
Ok(()) | |||
} | |||
} | |||
|
|||
#[derive(Debug)] | |||
struct TaintSet<'tcx> { | |||
directions: TaintDirections, | |||
regions: FxHashSet<ty::Region<'tcx>>, | |||
} | |||
|
|||
impl<'tcx> TaintSet<'tcx> { | |||
fn new(directions: TaintDirections, initial_region: ty::Region<'tcx>) -> Self { | |||
let mut regions = FxHashSet::default(); | |||
regions.insert(initial_region); | |||
TaintSet { | |||
directions: directions, | |||
regions: regions, | |||
} | |||
} | |||
|
|||
fn fixed_point( | |||
&mut self, | |||
tcx: TyCtxt<'_, '_, 'tcx>, | |||
undo_log: &[UndoLog<'tcx>], | |||
verifys: &[Verify<'tcx>], | |||
) { | |||
let mut prev_len = 0; | |||
while prev_len < self.len() { | |||
debug!( | |||
"tainted: prev_len = {:?} new_len = {:?}", | |||
prev_len, | |||
self.len() | |||
); | |||
|
|||
prev_len = self.len(); | |||
|
|||
for undo_entry in undo_log { | |||
match undo_entry { | |||
&AddConstraint(Constraint::VarSubVar(a, b)) => { | |||
self.add_edge(tcx.mk_region(ReVar(a)), tcx.mk_region(ReVar(b))); | |||
} | |||
&AddConstraint(Constraint::RegSubVar(a, b)) => { | |||
self.add_edge(a, tcx.mk_region(ReVar(b))); | |||
} | |||
&AddConstraint(Constraint::VarSubReg(a, b)) => { | |||
self.add_edge(tcx.mk_region(ReVar(a)), b); | |||
} | |||
&AddConstraint(Constraint::RegSubReg(a, b)) => { | |||
self.add_edge(a, b); | |||
} | |||
&AddGiven(a, b) => { | |||
self.add_edge(a, tcx.mk_region(ReVar(b))); | |||
} | |||
&AddVerify(i) => span_bug!( | |||
verifys[i].origin.span(), | |||
"we never add verifications while doing higher-ranked things", | |||
), | |||
&Purged | &AddCombination(..) | &AddVar(..) => {} | |||
} | |||
} | |||
} | |||
} | |||
|
|||
fn into_set(self) -> FxHashSet<ty::Region<'tcx>> { | |||
self.regions | |||
} | |||
|
|||
fn len(&self) -> usize { | |||
self.regions.len() | |||
} | |||
|
|||
fn add_edge(&mut self, source: ty::Region<'tcx>, target: ty::Region<'tcx>) { | |||
if self.directions.incoming { | |||
if self.regions.contains(&target) { | |||
self.regions.insert(source); | |||
} | |||
} | |||
|
|||
if self.directions.outgoing { | |||
if self.regions.contains(&source) { | |||
self.regions.insert(target); | |||
} | |||
} | |||
} | |||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,42 @@ | |||
// Regression test for #46989: | |||
// | |||
// In the move to universes, this test started passing. | |||
// It is not necessarily WRONG to do so, but it was a bit | |||
// surprising. The reason that it passed is that when we were | |||
// asked to prove that | |||
// | |||
// for<'a> fn(&'a i32): Foo | |||
// | |||
// we were able to use the impl below to prove | |||
// | |||
// fn(&'empty i32): Foo | |||
// | |||
// and then we were able to prove that | |||
// | |||
// fn(&'empty i32) = for<'a> fn(&'a i32) | |||
// | |||
// This last fact is somewhat surprising, but essentially "falls out" | |||
// from handling variance correctly. In particular, consider the subtyping | |||
// relations. First: | |||
// | |||
// fn(&'empty i32) <: for<'a> fn(&'a i32) | |||
// | |||
// This holds because -- intuitively -- a fn that takes a reference but doesn't use | |||
// it can be given a reference with any lifetime. Similarly, the opposite direction: | |||
// | |||
// for<'a> fn(&'a i32) <: fn(&'empty i32) | |||
// | |||
// holds because 'a can be instantiated to 'empty. | |||
|
|||
trait Foo { | |||
|
|||
} | |||
|
|||
impl<A> Foo for fn(A) { } | |||
|
|||
fn assert_foo<T: Foo>() {} | |||
|
|||
fn main() { | |||
assert_foo::<fn(&i32)>(); | |||
//~^ ERROR the trait bound `for<'r> fn(&'r i32): Foo` is not satisfied | |||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,29 @@ | |||
// Regression test for #57639: | |||
// | |||
// In the move to universes, this test stopped working. The problem | |||
// was that when the trait solver was asked to prove `for<'a> T::Item: | |||
// Foo<'a>` as part of WF checking, it wound up "eagerly committing" | |||
// to the where clause, which says that `T::Item: Foo<'a>`, but it | |||
// should instead have been using the bound found in the trait | |||
// declaration. Pre-universe, this used to work out ok because we got | |||
// "eager errors" due to the leak check. | |||
// | |||
// See [this comment on GitHub][c] for more details. | |||
// | |||
// run-pass | |||
// | |||
// [c]: https://github.com/rust-lang/rust/issues/57639#issuecomment-455685861 | |||
|
|||
trait Foo<'a> {} | |||
|
|||
trait Bar { | |||
type Item: for<'a> Foo<'a>; | |||
} | |||
|
|||
fn foo<'a, T>(_: T) | |||
where | |||
T: Bar, | |||
T::Item: Foo<'a>, | |||
{} | |||
|
|||
fn main() { } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,13 @@ | |||
// Regression test for #58451: | |||
// | |||
// Error reporting here encountered an ICE in the shift to universes. | |||
|
|||
fn f<I>(i: I) | |||
where | |||
I: IntoIterator, | |||
I::Item: for<'a> Into<&'a ()>, | |||
{} | |||
|
|||
fn main() { | |||
f(&[f()]); | |||
} |