1
Fork 0

Tweaks and a test

This commit is contained in:
Michael Goulet 2023-05-05 04:57:50 +00:00
parent 4ce2123ecc
commit 6d0b6c0d2c
3 changed files with 97 additions and 51 deletions

View file

@ -52,7 +52,7 @@ pub trait GeneralizerDelegate<'tcx> {
fn forbid_inference_vars() -> bool; fn forbid_inference_vars() -> bool;
fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>; fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>;
} }
pub struct CombineDelegate<'cx, 'tcx> { pub struct CombineDelegate<'cx, 'tcx> {
@ -70,7 +70,9 @@ impl<'tcx> GeneralizerDelegate<'tcx> for CombineDelegate<'_, 'tcx> {
false false
} }
fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
// FIXME: This is non-ideal because we don't give a
// very descriptive origin for this region variable.
self.infcx self.infcx
.next_region_var_in_universe(RegionVariableOrigin::MiscVariable(self.span), universe) .next_region_var_in_universe(RegionVariableOrigin::MiscVariable(self.span), universe)
} }
@ -88,18 +90,17 @@ where
<Self as TypeRelatingDelegate<'tcx>>::forbid_inference_vars() <Self as TypeRelatingDelegate<'tcx>>::forbid_inference_vars()
} }
fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
<Self as TypeRelatingDelegate<'tcx>>::generalize_existential(self, universe) <Self as TypeRelatingDelegate<'tcx>>::generalize_existential(self, universe)
} }
} }
/// The "type generalizer" is used when handling inference variables. /// The "generalizer" is used when handling inference variables.
/// ///
/// The basic strategy for handling a constraint like `?A <: B` is to /// The basic strategy for handling a constraint like `?A <: B` is to
/// apply a "generalization strategy" to the type `B` -- this replaces /// apply a "generalization strategy" to the term `B` -- this replaces
/// all the lifetimes in the type `B` with fresh inference /// all the lifetimes in the term `B` with fresh inference variables.
/// variables. (You can read more about the strategy in this [blog /// (You can read more about the strategy in this [blog post].)
/// post].)
/// ///
/// As an example, if we had `?A <: &'x u32`, we would generalize `&'x /// As an example, if we had `?A <: &'x u32`, we would generalize `&'x
/// u32` to `&'0 u32` where `'0` is a fresh variable. This becomes the /// u32` to `&'0 u32` where `'0` is a fresh variable. This becomes the
@ -110,9 +111,11 @@ where
struct Generalizer<'me, 'tcx, D> { struct Generalizer<'me, 'tcx, D> {
infcx: &'me InferCtxt<'tcx>, infcx: &'me InferCtxt<'tcx>,
// An delegate used to abstract the behaviors of the three previous /// This is used to abstract the behaviors of the three previous
// generalizer-like implementations. /// generalizer-like implementations (`Generalizer`, `TypeGeneralizer`,
pub delegate: &'me mut D, /// and `ConstInferUnifier`). See [`GeneralizerDelegate`] for more
/// information.
delegate: &'me mut D,
/// After we generalize this type, we are going to relate it to /// After we generalize this type, we are going to relate it to
/// some other type. What will be the variance at this point? /// some other type. What will be the variance at this point?
@ -138,6 +141,7 @@ struct Generalizer<'me, 'tcx, D> {
} }
impl<'tcx, D> Generalizer<'_, 'tcx, D> { impl<'tcx, D> Generalizer<'_, 'tcx, D> {
/// Create an error that corresponds to the term kind in `root_term`
fn cyclic_term_error(&self) -> TypeError<'tcx> { fn cyclic_term_error(&self) -> TypeError<'tcx> {
match self.root_term.unpack() { match self.root_term.unpack() {
ty::TermKind::Ty(ty) => TypeError::CyclicTy(ty), ty::TermKind::Ty(ty) => TypeError::CyclicTy(ty),
@ -183,7 +187,7 @@ where
relate::relate_substs_with_variances( relate::relate_substs_with_variances(
self, self,
item_def_id, item_def_id,
&opt_variances, opt_variances,
a_subst, a_subst,
b_subst, b_subst,
true, true,
@ -191,6 +195,7 @@ where
} }
} }
#[instrument(level = "debug", skip(self, variance, b), ret)]
fn relate_with_variance<T: Relate<'tcx>>( fn relate_with_variance<T: Relate<'tcx>>(
&mut self, &mut self,
variance: ty::Variance, variance: ty::Variance,
@ -198,29 +203,21 @@ where
a: T, a: T,
b: T, b: T,
) -> RelateResult<'tcx, T> { ) -> RelateResult<'tcx, T> {
debug!("Generalizer::relate_with_variance(variance={:?}, a={:?}, b={:?})", variance, a, b);
let old_ambient_variance = self.ambient_variance; let old_ambient_variance = self.ambient_variance;
self.ambient_variance = self.ambient_variance.xform(variance); self.ambient_variance = self.ambient_variance.xform(variance);
debug!(?self.ambient_variance, "new ambient variance");
debug!("Generalizer::relate_with_variance: ambient_variance = {:?}", self.ambient_variance);
let r = self.relate(a, b)?; let r = self.relate(a, b)?;
self.ambient_variance = old_ambient_variance; self.ambient_variance = old_ambient_variance;
debug!("Generalizer::relate_with_variance: r={:?}", r);
Ok(r) Ok(r)
} }
#[instrument(level = "debug", skip(self, t2), ret)]
fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
assert_eq!(t, t2); // we are misusing TypeRelation here; both LHS and RHS ought to be == assert_eq!(t, t2); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
if let Some(&result) = self.cache.get(&t) { if let Some(&result) = self.cache.get(&t) {
return Ok(result); return Ok(result);
} }
debug!("generalize: t={:?}", t);
// Check to see whether the type we are generalizing references // Check to see whether the type we are generalizing references
// any other type variable related to `vid` via // any other type variable related to `vid` via
@ -241,21 +238,22 @@ where
let mut inner = self.infcx.inner.borrow_mut(); let mut inner = self.infcx.inner.borrow_mut();
let vid = inner.type_variables().root_var(vid); let vid = inner.type_variables().root_var(vid);
let sub_vid = inner.type_variables().sub_root_var(vid); let sub_vid = inner.type_variables().sub_root_var(vid);
if TermVid::Ty(sub_vid) == self.root_vid {
// If sub-roots are equal, then `for_vid` and if ty::TermVid::Ty(sub_vid) == self.root_vid {
// If sub-roots are equal, then `root_vid` and
// `vid` are related via subtyping. // `vid` are related via subtyping.
Err(self.cyclic_term_error()) Err(self.cyclic_term_error())
} else { } else {
let probe = inner.type_variables().probe(vid); let probe = inner.type_variables().probe(vid);
match probe { match probe {
TypeVariableValue::Known { value: u } => { TypeVariableValue::Known { value: u } => {
debug!("generalize: known value {:?}", u);
drop(inner); drop(inner);
self.relate(u, u) self.relate(u, u)
} }
TypeVariableValue::Unknown { universe } => { TypeVariableValue::Unknown { universe } => {
match self.ambient_variance { match self.ambient_variance {
// Invariant: no need to make a fresh type variable. // Invariant: no need to make a fresh type variable
// if we can name the universe.
ty::Invariant => { ty::Invariant => {
if self.for_universe.can_name(universe) { if self.for_universe.can_name(universe) {
return Ok(t); return Ok(t);
@ -282,7 +280,7 @@ where
// operation. This is needed to detect cyclic types. To see why, see the // operation. This is needed to detect cyclic types. To see why, see the
// docs in the `type_variables` module. // docs in the `type_variables` module.
inner.type_variables().sub(vid, new_var_id); inner.type_variables().sub(vid, new_var_id);
debug!("generalize: replacing original vid={:?} with new={:?}", vid, u); debug!("replacing original vid={:?} with new={:?}", vid, u);
Ok(u) Ok(u)
} }
} }
@ -297,22 +295,17 @@ where
} }
ty::Placeholder(placeholder) => { ty::Placeholder(placeholder) => {
if self.for_universe.cannot_name(placeholder.universe) { if self.for_universe.can_name(placeholder.universe) {
Ok(t)
} else {
debug!( debug!(
"Generalizer::tys: root universe {:?} cannot name\ "root universe {:?} cannot name placeholder in universe {:?}",
placeholder in universe {:?}",
self.for_universe, placeholder.universe self.for_universe, placeholder.universe
); );
Err(TypeError::Mismatch) Err(TypeError::Mismatch)
} else {
Ok(t)
} }
} }
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
let s = self.relate(substs, substs)?;
Ok(if s == substs { t } else { self.tcx().mk_opaque(def_id, s) })
}
_ => relate::super_relate_tys(self, t, t), _ => relate::super_relate_tys(self, t, t),
}?; }?;
@ -320,6 +313,7 @@ where
Ok(g) Ok(g)
} }
#[instrument(level = "debug", skip(self, r2), ret)]
fn regions( fn regions(
&mut self, &mut self,
r: ty::Region<'tcx>, r: ty::Region<'tcx>,
@ -327,8 +321,6 @@ where
) -> RelateResult<'tcx, ty::Region<'tcx>> { ) -> RelateResult<'tcx, ty::Region<'tcx>> {
assert_eq!(r, r2); // we are misusing TypeRelation here; both LHS and RHS ought to be == assert_eq!(r, r2); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
debug!("generalize: regions r={:?}", r);
match *r { match *r {
// Never make variables for regions bound within the type itself, // Never make variables for regions bound within the type itself,
// nor for erased regions. // nor for erased regions.
@ -336,6 +328,8 @@ where
return Ok(r); return Ok(r);
} }
// It doesn't really matter for correctness if we generalize ReError,
// since we're already on a doomed compilation path.
ty::ReError(_) => { ty::ReError(_) => {
return Ok(r); return Ok(r);
} }
@ -359,13 +353,10 @@ where
} }
} }
// FIXME: This is non-ideal because we don't give a Ok(self.delegate.generalize_region(self.for_universe))
// very descriptive origin for this region variable.
let replacement_region_vid = self.delegate.generalize_existential(self.for_universe);
Ok(replacement_region_vid)
} }
#[instrument(level = "debug", skip(self, c2), ret)]
fn consts( fn consts(
&mut self, &mut self,
c: ty::Const<'tcx>, c: ty::Const<'tcx>,
@ -378,13 +369,12 @@ where
bug!("unexpected inference variable encountered in NLL generalization: {:?}", c); bug!("unexpected inference variable encountered in NLL generalization: {:?}", c);
} }
ty::ConstKind::Infer(InferConst::Var(vid)) => { ty::ConstKind::Infer(InferConst::Var(vid)) => {
// Check if the current unification would end up // If root const vids are equal, then `root_vid` and
// unifying `target_vid` with a const which contains // `vid` are related and we'd be inferring an infinitely
// an inference variable which is unioned with `target_vid`. // deep const.
// if ty::TermVid::Const(
// Not doing so can easily result in stack overflows. self.infcx.inner.borrow_mut().const_unification_table().find(vid),
if TermVid::Const(self.infcx.inner.borrow_mut().const_unification_table().find(vid)) ) == self.root_vid
== self.root_vid
{ {
return Err(self.cyclic_term_error()); return Err(self.cyclic_term_error());
} }
@ -421,10 +411,22 @@ where
)?; )?;
Ok(self.tcx().mk_const(ty::UnevaluatedConst { def, substs }, c.ty())) Ok(self.tcx().mk_const(ty::UnevaluatedConst { def, substs }, c.ty()))
} }
ty::ConstKind::Placeholder(placeholder) => {
if self.for_universe.can_name(placeholder.universe) {
Ok(c)
} else {
debug!(
"root universe {:?} cannot name placeholder in universe {:?}",
self.for_universe, placeholder.universe
);
Err(TypeError::Mismatch)
}
}
_ => relate::super_relate_consts(self, c, c), _ => relate::super_relate_consts(self, c, c),
} }
} }
#[instrument(level = "debug", skip(self), ret)]
fn binders<T>( fn binders<T>(
&mut self, &mut self,
a: ty::Binder<'tcx, T>, a: ty::Binder<'tcx, T>,
@ -433,7 +435,6 @@ where
where where
T: Relate<'tcx>, T: Relate<'tcx>,
{ {
debug!("Generalizer::binders(a={:?})", a);
let result = self.relate(a.skip_binder(), a.skip_binder())?; let result = self.relate(a.skip_binder(), a.skip_binder())?;
Ok(a.rebind(result)) Ok(a.rebind(result))
} }

View file

@ -0,0 +1,18 @@
#![feature(non_lifetime_binders)]
//~^ WARN the feature `non_lifetime_binders` is incomplete
trait Other<U: ?Sized> {}
impl<U: ?Sized> Other<U> for U {}
#[rustfmt::skip]
fn foo<U: ?Sized>()
where
for<T> T: Other<U> {}
fn bar() {
foo::<_>();
//~^ ERROR the trait bound `T: Other<_>` is not satisfied
}
fn main() {}

View file

@ -0,0 +1,27 @@
warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/universe-error1.rs:1:12
|
LL | #![feature(non_lifetime_binders)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
= note: `#[warn(incomplete_features)]` on by default
error[E0277]: the trait bound `T: Other<_>` is not satisfied
--> $DIR/universe-error1.rs:14:11
|
LL | foo::<_>();
| ^ the trait `Other<_>` is not implemented for `T`
|
note: required by a bound in `foo`
--> $DIR/universe-error1.rs:11:15
|
LL | fn foo<U: ?Sized>()
| --- required by a bound in this function
LL | where
LL | for<T> T: Other<U> {}
| ^^^^^^^^ required by this bound in `foo`
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0277`.