Auto merge of #108121 - aliemjay:resolve-var-region, r=lcnr
always resolve to universal regions if possible `RegionConstraintCollector::opportunistic_resolve_var`, which is used in canonicalization and projection logic, doesn't resolve the region var to an equal universal region. So if we have equated `'static == '1 == '2`, it doesn't resolve `'1` or `'2` to `'static`. Now it does! Addresses review comment https://github.com/rust-lang/rust/pull/107376#discussion_r1093233687. r? `@lcnr`
This commit is contained in:
commit
7c306f6dcd
@ -352,19 +352,17 @@ fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ty::ReVar(vid) => {
|
ty::ReVar(vid) => {
|
||||||
let resolved_vid = self
|
let resolved = self
|
||||||
.infcx
|
.infcx
|
||||||
.inner
|
.inner
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.unwrap_region_constraints()
|
.unwrap_region_constraints()
|
||||||
.opportunistic_resolve_var(vid);
|
.opportunistic_resolve_var(self.tcx, vid);
|
||||||
debug!(
|
debug!(
|
||||||
"canonical: region var found with vid {:?}, \
|
"canonical: region var found with vid {vid:?}, \
|
||||||
opportunistically resolved to {:?}",
|
opportunistically resolved to {resolved:?}",
|
||||||
vid, resolved_vid
|
|
||||||
);
|
);
|
||||||
let r = self.tcx.mk_re_var(resolved_vid);
|
self.canonicalize_mode.canonicalize_free_region(self, resolved)
|
||||||
self.canonicalize_mode.canonicalize_free_region(self, r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::ReStatic
|
ty::ReStatic
|
||||||
|
@ -420,7 +420,7 @@ pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'tcx> {
|
|||||||
// `RegionConstraintData` contains the relationship here.
|
// `RegionConstraintData` contains the relationship here.
|
||||||
if *any_unifications {
|
if *any_unifications {
|
||||||
*any_unifications = false;
|
*any_unifications = false;
|
||||||
self.unification_table().reset_unifications(|_| UnifiedRegion(None));
|
self.unification_table_mut().reset_unifications(|_| UnifiedRegion::new(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
data
|
data
|
||||||
@ -447,7 +447,7 @@ pub(super) fn new_region_var(
|
|||||||
) -> RegionVid {
|
) -> RegionVid {
|
||||||
let vid = self.var_infos.push(RegionVariableInfo { origin, universe });
|
let vid = self.var_infos.push(RegionVariableInfo { origin, universe });
|
||||||
|
|
||||||
let u_vid = self.unification_table().new_key(UnifiedRegion(None));
|
let u_vid = self.unification_table_mut().new_key(UnifiedRegion::new(None));
|
||||||
assert_eq!(vid, u_vid.vid);
|
assert_eq!(vid, u_vid.vid);
|
||||||
self.undo_log.push(AddVar(vid));
|
self.undo_log.push(AddVar(vid));
|
||||||
debug!("created new region variable {:?} in {:?} with origin {:?}", vid, universe, origin);
|
debug!("created new region variable {:?} in {:?} with origin {:?}", vid, universe, origin);
|
||||||
@ -516,13 +516,13 @@ pub(super) fn make_eqregion(
|
|||||||
match (sub, sup) {
|
match (sub, sup) {
|
||||||
(Region(Interned(ReVar(sub), _)), Region(Interned(ReVar(sup), _))) => {
|
(Region(Interned(ReVar(sub), _)), Region(Interned(ReVar(sup), _))) => {
|
||||||
debug!("make_eqregion: unifying {:?} with {:?}", sub, sup);
|
debug!("make_eqregion: unifying {:?} with {:?}", sub, sup);
|
||||||
self.unification_table().union(*sub, *sup);
|
self.unification_table_mut().union(*sub, *sup);
|
||||||
self.any_unifications = true;
|
self.any_unifications = true;
|
||||||
}
|
}
|
||||||
(Region(Interned(ReVar(vid), _)), value)
|
(Region(Interned(ReVar(vid), _)), value)
|
||||||
| (value, Region(Interned(ReVar(vid), _))) => {
|
| (value, Region(Interned(ReVar(vid), _))) => {
|
||||||
debug!("make_eqregion: unifying {:?} with {:?}", vid, value);
|
debug!("make_eqregion: unifying {:?} with {:?}", vid, value);
|
||||||
self.unification_table().union_value(*vid, UnifiedRegion(Some(value)));
|
self.unification_table_mut().union_value(*vid, UnifiedRegion::new(Some(value)));
|
||||||
self.any_unifications = true;
|
self.any_unifications = true;
|
||||||
}
|
}
|
||||||
(_, _) => {}
|
(_, _) => {}
|
||||||
@ -633,28 +633,25 @@ pub(super) fn glb_regions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves the passed RegionVid to the root RegionVid in the unification table
|
/// Resolves a region var to its value in the unification table, if it exists.
|
||||||
pub(super) fn opportunistic_resolve_var(&mut self, rid: ty::RegionVid) -> ty::RegionVid {
|
/// Otherwise, it is resolved to the root `ReVar` in the table.
|
||||||
self.unification_table().find(rid).vid
|
pub fn opportunistic_resolve_var(
|
||||||
}
|
|
||||||
|
|
||||||
/// If the Region is a `ReVar`, then resolves it either to the root value in
|
|
||||||
/// the unification table, if it exists, or to the root `ReVar` in the table.
|
|
||||||
/// If the Region is not a `ReVar`, just returns the Region itself.
|
|
||||||
pub fn opportunistic_resolve_region(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
region: ty::Region<'tcx>,
|
vid: ty::RegionVid,
|
||||||
) -> ty::Region<'tcx> {
|
) -> ty::Region<'tcx> {
|
||||||
match *region {
|
let mut ut = self.unification_table_mut(); // FIXME(rust-lang/ena#42): unnecessary mut
|
||||||
ty::ReVar(rid) => {
|
let root_vid = ut.find(vid).vid;
|
||||||
let unified_region = self.unification_table().probe_value(rid);
|
let resolved = ut
|
||||||
unified_region.0.unwrap_or_else(|| {
|
.probe_value(root_vid)
|
||||||
let root = self.unification_table().find(rid).vid;
|
.get_value_ignoring_universes()
|
||||||
tcx.mk_re_var(root)
|
.unwrap_or_else(|| tcx.mk_re_var(root_vid));
|
||||||
})
|
|
||||||
}
|
// Don't resolve a variable to a region that it cannot name.
|
||||||
_ => region,
|
if self.var_universe(vid).can_name(self.universe(resolved)) {
|
||||||
|
resolved
|
||||||
|
} else {
|
||||||
|
tcx.mk_re_var(vid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -733,7 +730,7 @@ pub fn region_constraints_added_in_snapshot(&self, mark: &Snapshot<'tcx>) -> Opt
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn unification_table(&mut self) -> super::UnificationTable<'_, 'tcx, RegionVidKey<'tcx>> {
|
fn unification_table_mut(&mut self) -> super::UnificationTable<'_, 'tcx, RegionVidKey<'tcx>> {
|
||||||
ut::UnificationTable::with_log(&mut self.storage.unification_table, self.undo_log)
|
ut::UnificationTable::with_log(&mut self.storage.unification_table, self.undo_log)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,15 +85,12 @@ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
|
|||||||
|
|
||||||
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
||||||
match *r {
|
match *r {
|
||||||
ty::ReVar(rid) => {
|
ty::ReVar(vid) => self
|
||||||
let resolved = self
|
.infcx
|
||||||
.infcx
|
.inner
|
||||||
.inner
|
.borrow_mut()
|
||||||
.borrow_mut()
|
.unwrap_region_constraints()
|
||||||
.unwrap_region_constraints()
|
.opportunistic_resolve_var(TypeFolder::interner(self), vid),
|
||||||
.opportunistic_resolve_var(rid);
|
|
||||||
TypeFolder::interner(self).mk_re_var(resolved)
|
|
||||||
}
|
|
||||||
_ => r,
|
_ => r,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::ty::{self, Ty, TyCtxt};
|
use crate::ty::{self, Region, Ty, TyCtxt};
|
||||||
use rustc_data_structures::unify::{NoError, UnifyKey, UnifyValue};
|
use rustc_data_structures::unify::{NoError, UnifyKey, UnifyValue};
|
||||||
use rustc_span::def_id::DefId;
|
use rustc_span::def_id::DefId;
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
@ -11,7 +11,20 @@ pub trait ToType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone, Debug)]
|
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||||
pub struct UnifiedRegion<'tcx>(pub Option<ty::Region<'tcx>>);
|
pub struct UnifiedRegion<'tcx> {
|
||||||
|
value: Option<ty::Region<'tcx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> UnifiedRegion<'tcx> {
|
||||||
|
pub fn new(value: Option<Region<'tcx>>) -> Self {
|
||||||
|
Self { value }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The caller is responsible for checking universe compatibility before using this value.
|
||||||
|
pub fn get_value_ignoring_universes(self) -> Option<Region<'tcx>> {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone, Debug)]
|
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||||
pub struct RegionVidKey<'tcx> {
|
pub struct RegionVidKey<'tcx> {
|
||||||
@ -44,11 +57,27 @@ impl<'tcx> UnifyValue for UnifiedRegion<'tcx> {
|
|||||||
type Error = NoError;
|
type Error = NoError;
|
||||||
|
|
||||||
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, NoError> {
|
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, NoError> {
|
||||||
Ok(match (value1.0, value2.0) {
|
// We pick the value of the least universe because it is compatible with more variables.
|
||||||
|
// This is *not* neccessary for soundness, but it allows more region variables to be
|
||||||
|
// resolved to the said value.
|
||||||
|
#[cold]
|
||||||
|
fn min_universe<'tcx>(r1: Region<'tcx>, r2: Region<'tcx>) -> Region<'tcx> {
|
||||||
|
cmp::min_by_key(r1, r2, |r| match r.kind() {
|
||||||
|
ty::ReStatic
|
||||||
|
| ty::ReErased
|
||||||
|
| ty::ReFree(..)
|
||||||
|
| ty::ReEarlyBound(..)
|
||||||
|
| ty::ReError(_) => ty::UniverseIndex::ROOT,
|
||||||
|
ty::RePlaceholder(placeholder) => placeholder.universe,
|
||||||
|
ty::ReVar(..) | ty::ReLateBound(..) => bug!("not a universal region"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(match (value1.value, value2.value) {
|
||||||
// Here we can just pick one value, because the full constraints graph
|
// Here we can just pick one value, because the full constraints graph
|
||||||
// will be handled later. Ideally, we might want a `MultipleValues`
|
// will be handled later. Ideally, we might want a `MultipleValues`
|
||||||
// variant or something. For now though, this is fine.
|
// variant or something. For now though, this is fine.
|
||||||
(Some(_), Some(_)) => *value1,
|
(Some(val1), Some(val2)) => Self { value: Some(min_universe(val1, val2)) },
|
||||||
|
|
||||||
(Some(_), _) => *value1,
|
(Some(_), _) => *value1,
|
||||||
(_, Some(_)) => *value2,
|
(_, Some(_)) => *value2,
|
||||||
|
@ -870,12 +870,12 @@ fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
|
|||||||
|
|
||||||
fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
||||||
let r1 = match *r0 {
|
let r1 = match *r0 {
|
||||||
ty::ReVar(_) => self
|
ty::ReVar(vid) => self
|
||||||
.infcx
|
.infcx
|
||||||
.inner
|
.inner
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.unwrap_region_constraints()
|
.unwrap_region_constraints()
|
||||||
.opportunistic_resolve_region(self.infcx.tcx, r0),
|
.opportunistic_resolve_var(self.infcx.tcx, vid),
|
||||||
_ => r0,
|
_ => r0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -63,12 +63,12 @@ impl<'a> Lifetimes<'a> for usize {
|
|||||||
type Y = &'a isize;
|
type Y = &'a isize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @has 'normalize_assoc_item/fn.g.html' '//pre[@class="rust item-decl"]' "pub fn g() -> &isize"
|
// @has 'normalize_assoc_item/fn.g.html' '//pre[@class="rust item-decl"]' "pub fn g() -> &'static isize"
|
||||||
pub fn g() -> <usize as Lifetimes<'static>>::Y {
|
pub fn g() -> <usize as Lifetimes<'static>>::Y {
|
||||||
&0
|
&0
|
||||||
}
|
}
|
||||||
|
|
||||||
// @has 'normalize_assoc_item/constant.A.html' '//pre[@class="rust item-decl"]' "pub const A: &isize"
|
// @has 'normalize_assoc_item/constant.A.html' '//pre[@class="rust item-decl"]' "pub const A: &'static isize"
|
||||||
pub const A: <usize as Lifetimes<'static>>::Y = &0;
|
pub const A: <usize as Lifetimes<'static>>::Y = &0;
|
||||||
|
|
||||||
// test cross-crate re-exports
|
// test cross-crate re-exports
|
||||||
|
@ -15,9 +15,9 @@ impl<'a> Y for C<'a> {
|
|||||||
struct C<'a>(&'a ());
|
struct C<'a>(&'a ());
|
||||||
struct X<T: Y>(T::P);
|
struct X<T: Y>(T::P);
|
||||||
|
|
||||||
impl<T: NotAuto> NotAuto for Box<T> {} //~ NOTE: required
|
impl<T: NotAuto> NotAuto for Box<T> {}
|
||||||
|
impl<T: Y> NotAuto for X<T> where T::P: NotAuto {} //~ NOTE: required
|
||||||
//~^ NOTE unsatisfied trait bound introduced here
|
//~^ NOTE unsatisfied trait bound introduced here
|
||||||
impl<T: Y> NotAuto for X<T> where T::P: NotAuto {}
|
|
||||||
impl<'a> NotAuto for C<'a> {}
|
impl<'a> NotAuto for C<'a> {}
|
||||||
|
|
||||||
fn is_send<S: NotAuto>() {}
|
fn is_send<S: NotAuto>() {}
|
||||||
@ -28,6 +28,4 @@ fn main() {
|
|||||||
// Should only be a few notes.
|
// Should only be a few notes.
|
||||||
is_send::<X<C<'static>>>();
|
is_send::<X<C<'static>>>();
|
||||||
//~^ ERROR overflow evaluating
|
//~^ ERROR overflow evaluating
|
||||||
//~| 3 redundant requirements hidden
|
|
||||||
//~| required for
|
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
error[E0275]: overflow evaluating the requirement `X<C<'_>>: NotAuto`
|
error[E0275]: overflow evaluating the requirement `Box<X<C<'static>>>: NotAuto`
|
||||||
--> $DIR/lifetime.rs:29:5
|
--> $DIR/lifetime.rs:29:5
|
||||||
|
|
|
|
||||||
LL | is_send::<X<C<'static>>>();
|
LL | is_send::<X<C<'static>>>();
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
note: required for `Box<X<C<'_>>>` to implement `NotAuto`
|
note: required for `X<C<'static>>` to implement `NotAuto`
|
||||||
--> $DIR/lifetime.rs:18:18
|
--> $DIR/lifetime.rs:19:12
|
||||||
|
|
|
|
||||||
LL | impl<T: NotAuto> NotAuto for Box<T> {}
|
LL | impl<T: Y> NotAuto for X<T> where T::P: NotAuto {}
|
||||||
| ------- ^^^^^^^ ^^^^^^
|
| ^^^^^^^ ^^^^ ------- unsatisfied trait bound introduced here
|
||||||
| |
|
|
||||||
| unsatisfied trait bound introduced here
|
|
||||||
= note: 3 redundant requirements hidden
|
|
||||||
= note: required for `X<C<'static>>` to implement `NotAuto`
|
|
||||||
note: required by a bound in `is_send`
|
note: required by a bound in `is_send`
|
||||||
--> $DIR/lifetime.rs:23:15
|
--> $DIR/lifetime.rs:23:15
|
||||||
|
|
|
|
||||||
|
@ -4,10 +4,10 @@
|
|||||||
pub struct Table<T, const N: usize>([Option<T>; N]);
|
pub struct Table<T, const N: usize>([Option<T>; N]);
|
||||||
|
|
||||||
impl<'a, T, const N: usize> IntoIterator for &'a Table<T, N> {
|
impl<'a, T, const N: usize> IntoIterator for &'a Table<T, N> {
|
||||||
type IntoIter = std::iter::Flatten<std::slice::Iter<'a, T>>; //~ ERROR `&T` is not an iterator
|
type IntoIter = std::iter::Flatten<std::slice::Iter<'a, T>>; //~ ERROR `&'a T` is not an iterator
|
||||||
type Item = &'a T;
|
type Item = &'a T;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter { //~ ERROR `&T` is not an iterator
|
fn into_iter(self) -> Self::IntoIter { //~ ERROR `&'a T` is not an iterator
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
error[E0277]: `&T` is not an iterator
|
error[E0277]: `&'a T` is not an iterator
|
||||||
--> $DIR/hir-wf-check-erase-regions.rs:7:21
|
--> $DIR/hir-wf-check-erase-regions.rs:7:21
|
||||||
|
|
|
|
||||||
LL | type IntoIter = std::iter::Flatten<std::slice::Iter<'a, T>>;
|
LL | type IntoIter = std::iter::Flatten<std::slice::Iter<'a, T>>;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&T` is not an iterator
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&'a T` is not an iterator
|
||||||
|
|
|
|
||||||
= help: the trait `Iterator` is not implemented for `&T`
|
= help: the trait `Iterator` is not implemented for `&'a T`
|
||||||
= help: the trait `Iterator` is implemented for `&mut I`
|
= help: the trait `Iterator` is implemented for `&mut I`
|
||||||
= note: required for `&T` to implement `IntoIterator`
|
= note: required for `&'a T` to implement `IntoIterator`
|
||||||
note: required by a bound in `Flatten`
|
note: required by a bound in `Flatten`
|
||||||
--> $SRC_DIR/core/src/iter/adapters/flatten.rs:LL:COL
|
--> $SRC_DIR/core/src/iter/adapters/flatten.rs:LL:COL
|
||||||
|
|
||||||
error[E0277]: `&T` is not an iterator
|
error[E0277]: `&'a T` is not an iterator
|
||||||
--> $DIR/hir-wf-check-erase-regions.rs:10:27
|
--> $DIR/hir-wf-check-erase-regions.rs:10:27
|
||||||
|
|
|
|
||||||
LL | fn into_iter(self) -> Self::IntoIter {
|
LL | fn into_iter(self) -> Self::IntoIter {
|
||||||
| ^^^^^^^^^^^^^^ `&T` is not an iterator
|
| ^^^^^^^^^^^^^^ `&'a T` is not an iterator
|
||||||
|
|
|
|
||||||
= help: the trait `Iterator` is not implemented for `&T`
|
= help: the trait `Iterator` is not implemented for `&'a T`
|
||||||
= help: the trait `Iterator` is implemented for `&mut I`
|
= help: the trait `Iterator` is implemented for `&mut I`
|
||||||
= note: required for `&T` to implement `IntoIterator`
|
= note: required for `&'a T` to implement `IntoIterator`
|
||||||
note: required by a bound in `Flatten`
|
note: required by a bound in `Flatten`
|
||||||
--> $SRC_DIR/core/src/iter/adapters/flatten.rs:LL:COL
|
--> $SRC_DIR/core/src/iter/adapters/flatten.rs:LL:COL
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user