Auto merge of #142071 - lcnr:revealing-use, r=compiler-errors
`apply_member_constraints`: fix placeholder check Checking whether the member region is *an existential region from a higher universe* is just wrong and I am pretty sure we've added that check by accident as the naming was just horribly confusing before rust-lang/rust#140466. I've encountered this issue separately while working on rust-lang/rust#139587, but feel like it's probably easier to separately FCP this change. This allows the following code to compile ```rust trait Proj<'a> { type Assoc; } impl<'a, 'b, F: FnOnce() -> &'b ()> Proj<'a> for F { type Assoc = (); } fn is_proj<F: for<'a> Proj<'a>>(f: F) {} fn define<'a>() -> impl Sized + use<'a> { // This adds a use of `opaque::<'a>` with hidden type `&'unconstrained_b ()`. // 'unconstrained_b is an inference variable from a higher universe as it gets // created inside of the binder of `F: for<'a> Proj<'a>`. This previously // caused us to not apply member constraints. We now do, constraining // it to `'a`. is_proj(define::<'a>); &() } fn main() {} ``` This should not be breaking change, even in theory. Applying member constraints is incomplete in rare circumstances which means that applying them in more cases can cause spurious errors, cc rust-lang/rust#140569/rust-lang/rust#142073. However, as we always skipped these member regions in `apply_member_constraints` the skipped region is guaranteed to cause an error in `check_member_constraints` later on.
This commit is contained in:
commit
cd7cbe818e
4 changed files with 72 additions and 1 deletions
|
|
@ -103,6 +103,10 @@ impl RegionTracker {
|
|||
self.max_nameable_universe
|
||||
}
|
||||
|
||||
pub(crate) fn max_placeholder_universe_reached(self) -> UniverseIndex {
|
||||
self.max_placeholder_universe_reached
|
||||
}
|
||||
|
||||
fn merge_min_max_seen(&mut self, other: &Self) {
|
||||
self.max_placeholder_universe_reached = std::cmp::max(
|
||||
self.max_placeholder_universe_reached,
|
||||
|
|
|
|||
|
|
@ -713,7 +713,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
|
||||
// If the member region lives in a higher universe, we currently choose
|
||||
// the most conservative option by leaving it unchanged.
|
||||
if !self.max_nameable_universe(scc).is_root() {
|
||||
if !self.max_placeholder_universe_reached(scc).is_root() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1376,6 +1376,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
self.scc_annotations[scc].max_nameable_universe()
|
||||
}
|
||||
|
||||
pub(crate) fn max_placeholder_universe_reached(
|
||||
&self,
|
||||
scc: ConstraintSccIndex,
|
||||
) -> UniverseIndex {
|
||||
self.scc_annotations[scc].max_placeholder_universe_reached()
|
||||
}
|
||||
|
||||
/// Checks the final value for the free region `fr` to see if it
|
||||
/// grew too large. In particular, examine what `end(X)` points
|
||||
/// wound up in `fr`'s final value; for each `end(X)` where `X !=
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
//@ check-pass
|
||||
//@ revisions: current next
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
|
||||
trait Proj<'a> {
|
||||
type Assoc;
|
||||
}
|
||||
|
||||
impl<'a, 'b, F: FnOnce() -> &'b ()> Proj<'a> for F {
|
||||
type Assoc = ();
|
||||
}
|
||||
|
||||
fn is_proj<F: for<'a> Proj<'a>>(f: F) {}
|
||||
|
||||
fn define<'a>() -> impl Sized + use<'a> {
|
||||
// This defines the RPIT to `&'unconstrained_b ()`, an inference
|
||||
// variable which is in a higher universe as gets created inside
|
||||
// of the binder of `F: for<'a> Proj<'a>`. This previously caused
|
||||
// us to not apply member constraints.
|
||||
//
|
||||
// This was unnecessary. It is totally acceptable for member regions
|
||||
// to be able to name placeholders from higher universes, as long as
|
||||
// they don't actually do so.
|
||||
is_proj(define::<'a>);
|
||||
&()
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
//@ check-pass
|
||||
|
||||
// Unlike `non-root-universe-existential-1.rs` this previously
|
||||
// compiled as it simply didn't define the hidden type of
|
||||
// `impl Iterator` when projecting through it. We will do so
|
||||
// with the new solver. Further minimizing this is challenging.
|
||||
|
||||
struct Type(Vec<Type>);
|
||||
enum TypeTreeValueIter<'a, T> {
|
||||
Once(T),
|
||||
Ref(&'a ()),
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for TypeTreeValueIter<'a, T> {
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
fn item<I: Iterator<Item: Iterator>>(x: I) -> <I::Item as Iterator>::Item {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn get_type_tree_values<'a>(ty: &'a Type) -> impl Iterator<Item = &'a Type> {
|
||||
let _: &'a Type = item(std::iter::once(ty).map(get_type_tree_values));
|
||||
TypeTreeValueIter::<'a, &'a Type>::Once(ty)
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue