Auto merge of #111221 - compiler-errors:yeet-generalizer, r=lcnr

Combine three generalizer implementations

Fixes #111092
Fixes #109505

This code is a bit delicate and there were subtle changes between them, so I'll leave inline comments where further inspection is needed.

Regarding this comment from #109813 -- "add tests triggering all codepaths: at least the combine and the const generalizer", can't really do that now, and I don't really know how we'd get a higher-ranked const error since non-lifetime binders doesn't *really* support `for<const ..>` (it errors out when you try to use it).

r? `@lcnr`
This commit is contained in:
bors 2023-05-15 22:16:50 +00:00
commit ea54255501
13 changed files with 627 additions and 831 deletions

View file

@ -26,24 +26,17 @@ use super::equate::Equate;
use super::glb::Glb;
use super::lub::Lub;
use super::sub::Sub;
use super::type_variable::TypeVariableValue;
use super::{DefineOpaqueTypes, InferCtxt, MiscVariable, TypeTrace};
use super::{DefineOpaqueTypes, InferCtxt, TypeTrace};
use crate::infer::generalize::{self, CombineDelegate, Generalization};
use crate::traits::{Obligation, PredicateObligations};
use rustc_data_structures::sso::SsoHashMap;
use rustc_hir::def_id::DefId;
use rustc_middle::infer::canonical::OriginalQueryValues;
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{
self, AliasKind, FallibleTypeFolder, InferConst, ToPredicate, Ty, TyCtxt, TypeFoldable,
TypeSuperFoldable, TypeVisitableExt,
};
use rustc_middle::ty::relate::{RelateResult, TypeRelation};
use rustc_middle::ty::{self, AliasKind, InferConst, ToPredicate, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{IntType, UintType};
use rustc_span::{Span, DUMMY_SP};
use rustc_span::DUMMY_SP;
#[derive(Clone)]
pub struct CombineFields<'infcx, 'tcx> {
@ -55,13 +48,6 @@ pub struct CombineFields<'infcx, 'tcx> {
pub define_opaque_types: DefineOpaqueTypes,
}
#[derive(Copy, Clone, Debug)]
pub enum RelationDir {
SubtypeOf,
SupertypeOf,
EqTo,
}
impl<'tcx> InferCtxt<'tcx> {
pub fn super_combine_tys<R>(
&self,
@ -152,7 +138,7 @@ impl<'tcx> InferCtxt<'tcx> {
Ok(a)
}
_ => ty::relate::super_relate_tys(relation, a, b),
_ => ty::relate::structurally_relate_tys(relation, a, b),
}
}
@ -211,11 +197,11 @@ impl<'tcx> InferCtxt<'tcx> {
// matching in the solver.
let a_error = self.tcx.const_error(a.ty(), guar);
if let ty::ConstKind::Infer(InferConst::Var(vid)) = a.kind() {
return self.unify_const_variable(vid, a_error);
return self.unify_const_variable(vid, a_error, relation.param_env());
}
let b_error = self.tcx.const_error(b.ty(), guar);
if let ty::ConstKind::Infer(InferConst::Var(vid)) = b.kind() {
return self.unify_const_variable(vid, b_error);
return self.unify_const_variable(vid, b_error, relation.param_env());
}
return Ok(if relation.a_is_expected() { a_error } else { b_error });
@ -237,11 +223,11 @@ impl<'tcx> InferCtxt<'tcx> {
}
(ty::ConstKind::Infer(InferConst::Var(vid)), _) => {
return self.unify_const_variable(vid, b);
return self.unify_const_variable(vid, b, relation.param_env());
}
(_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
return self.unify_const_variable(vid, a);
return self.unify_const_variable(vid, a, relation.param_env());
}
(ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
if self.tcx.lazy_normalization() =>
@ -252,7 +238,7 @@ impl<'tcx> InferCtxt<'tcx> {
_ => {}
}
ty::relate::super_relate_consts(relation, a, b)
ty::relate::structurally_relate_consts(relation, a, b)
}
/// Unifies the const variable `target_vid` with the given constant.
@ -294,24 +280,17 @@ impl<'tcx> InferCtxt<'tcx> {
&self,
target_vid: ty::ConstVid<'tcx>,
ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
let (for_universe, span) = {
let mut inner = self.inner.borrow_mut();
let variable_table = &mut inner.const_unification_table();
let var_value = variable_table.probe_value(target_vid);
match var_value.val {
ConstVariableValue::Known { value } => {
bug!("instantiating {:?} which has a known value {:?}", target_vid, value)
}
ConstVariableValue::Unknown { universe } => (universe, var_value.origin.span),
}
};
let value = ct.try_fold_with(&mut ConstInferUnifier {
infcx: self,
span,
for_universe,
let span =
self.inner.borrow_mut().const_unification_table().probe_value(target_vid).origin.span;
let Generalization { value, needs_wf: _ } = generalize::generalize(
self,
&mut CombineDelegate { infcx: self, span, param_env },
ct,
target_vid,
})?;
ty::Variance::Invariant,
)?;
self.inner.borrow_mut().const_unification_table().union_value(
target_vid,
@ -392,12 +371,10 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
pub fn instantiate(
&mut self,
a_ty: Ty<'tcx>,
dir: RelationDir,
ambient_variance: ty::Variance,
b_vid: ty::TyVid,
a_is_expected: bool,
) -> RelateResult<'tcx, ()> {
use self::RelationDir::*;
// Get the actual variable that b_vid has been inferred to
debug_assert!(self.infcx.inner.borrow_mut().type_variables().probe(b_vid).is_unknown());
@ -412,7 +389,18 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
// `'?2` and `?3` are fresh region/type inference
// variables. (Down below, we will relate `a_ty <: b_ty`,
// adding constraints like `'x: '?2` and `?1 <: ?3`.)
let Generalization { ty: b_ty, needs_wf } = self.generalize(a_ty, b_vid, dir)?;
let Generalization { value: b_ty, needs_wf } = generalize::generalize(
self.infcx,
&mut CombineDelegate {
infcx: self.infcx,
param_env: self.param_env,
span: self.trace.span(),
},
a_ty,
b_vid,
ambient_variance,
)?;
debug!(?b_ty);
self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty);
@ -431,78 +419,23 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
// relations wind up attributed to the same spans. We need
// to associate causes/spans with each of the relations in
// the stack to get this right.
match dir {
EqTo => self.equate(a_is_expected).relate(a_ty, b_ty),
SubtypeOf => self.sub(a_is_expected).relate(a_ty, b_ty),
SupertypeOf => self.sub(a_is_expected).relate_with_variance(
match ambient_variance {
ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty),
ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty),
ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance(
ty::Contravariant,
ty::VarianceDiagInfo::default(),
a_ty,
b_ty,
),
ty::Variance::Bivariant => {
unreachable!("no code should be generalizing bivariantly (currently)")
}
}?;
Ok(())
}
/// Attempts to generalize `ty` for the type variable `for_vid`.
/// This checks for cycle -- that is, whether the type `ty`
/// references `for_vid`. The `dir` is the "direction" for which we
/// a performing the generalization (i.e., are we producing a type
/// that can be used as a supertype etc).
///
/// Preconditions:
///
/// - `for_vid` is a "root vid"
#[instrument(skip(self), level = "trace", ret)]
fn generalize(
&self,
ty: Ty<'tcx>,
for_vid: ty::TyVid,
dir: RelationDir,
) -> RelateResult<'tcx, Generalization<'tcx>> {
// Determine the ambient variance within which `ty` appears.
// The surrounding equation is:
//
// ty [op] ty2
//
// where `op` is either `==`, `<:`, or `:>`. This maps quite
// naturally.
let ambient_variance = match dir {
RelationDir::EqTo => ty::Invariant,
RelationDir::SubtypeOf => ty::Covariant,
RelationDir::SupertypeOf => ty::Contravariant,
};
trace!(?ambient_variance);
let for_universe = match self.infcx.inner.borrow_mut().type_variables().probe(for_vid) {
v @ TypeVariableValue::Known { .. } => {
bug!("instantiating {:?} which has a known value {:?}", for_vid, v,)
}
TypeVariableValue::Unknown { universe } => universe,
};
trace!(?for_universe);
trace!(?self.trace);
let mut generalize = Generalizer {
infcx: self.infcx,
cause: &self.trace.cause,
for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables().sub_root_var(for_vid),
for_universe,
ambient_variance,
needs_wf: false,
root_ty: ty,
param_env: self.param_env,
cache: SsoHashMap::new(),
};
let ty = generalize.relate(ty, ty)?;
let needs_wf = generalize.needs_wf;
Ok(Generalization { ty, needs_wf })
}
pub fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
self.obligations.extend(obligations.into_iter());
}
@ -514,313 +447,6 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
}
}
struct Generalizer<'cx, 'tcx> {
infcx: &'cx InferCtxt<'tcx>,
/// The span, used when creating new type variables and things.
cause: &'cx ObligationCause<'tcx>,
/// The vid of the type variable that is in the process of being
/// instantiated; if we find this within the type we are folding,
/// that means we would have created a cyclic type.
for_vid_sub_root: ty::TyVid,
/// The universe of the type variable that is in the process of
/// being instantiated. Any fresh variables that we create in this
/// process should be in that same universe.
for_universe: ty::UniverseIndex,
/// Track the variance as we descend into the type.
ambient_variance: ty::Variance,
/// See the field `needs_wf` in `Generalization`.
needs_wf: bool,
/// The root type that we are generalizing. Used when reporting cycles.
root_ty: Ty<'tcx>,
param_env: ty::ParamEnv<'tcx>,
cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
}
/// Result from a generalization operation. This includes
/// not only the generalized type, but also a bool flag
/// indicating whether further WF checks are needed.
#[derive(Debug)]
struct Generalization<'tcx> {
ty: Ty<'tcx>,
/// If true, then the generalized type may not be well-formed,
/// even if the source type is well-formed, so we should add an
/// additional check to enforce that it is. This arises in
/// particular around 'bivariant' type parameters that are only
/// constrained by a where-clause. As an example, imagine a type:
///
/// struct Foo<A, B> where A: Iterator<Item = B> {
/// data: A
/// }
///
/// here, `A` will be covariant, but `B` is
/// unconstrained. However, whatever it is, for `Foo` to be WF, it
/// must be equal to `A::Item`. If we have an input `Foo<?A, ?B>`,
/// then after generalization we will wind up with a type like
/// `Foo<?C, ?D>`. When we enforce that `Foo<?A, ?B> <: Foo<?C,
/// ?D>` (or `>:`), we will wind up with the requirement that `?A
/// <: ?C`, but no particular relationship between `?B` and `?D`
/// (after all, we do not know the variance of the normalized form
/// of `A::Item` with respect to `A`). If we do nothing else, this
/// may mean that `?D` goes unconstrained (as in #41677). So, in
/// this scenario where we create a new type variable in a
/// bivariant context, we set the `needs_wf` flag to true. This
/// will force the calling code to check that `WF(Foo<?C, ?D>)`
/// holds, which in turn implies that `?C::Item == ?D`. So once
/// `?C` is constrained, that should suffice to restrict `?D`.
needs_wf: bool,
}
impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.param_env
}
fn tag(&self) -> &'static str {
"Generalizer"
}
fn a_is_expected(&self) -> bool {
true
}
fn binders<T>(
&mut self,
a: ty::Binder<'tcx, T>,
b: ty::Binder<'tcx, T>,
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
where
T: Relate<'tcx>,
{
Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))
}
fn relate_item_substs(
&mut self,
item_def_id: DefId,
a_subst: SubstsRef<'tcx>,
b_subst: SubstsRef<'tcx>,
) -> RelateResult<'tcx, SubstsRef<'tcx>> {
if self.ambient_variance == ty::Variance::Invariant {
// Avoid fetching the variance if we are in an invariant
// context; no need, and it can induce dependency cycles
// (e.g., #41849).
relate::relate_substs(self, a_subst, b_subst)
} else {
let tcx = self.tcx();
let opt_variances = tcx.variances_of(item_def_id);
relate::relate_substs_with_variances(
self,
item_def_id,
&opt_variances,
a_subst,
b_subst,
true,
)
}
}
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
variance: ty::Variance,
_info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
let old_ambient_variance = self.ambient_variance;
self.ambient_variance = self.ambient_variance.xform(variance);
let result = self.relate(a, b);
self.ambient_variance = old_ambient_variance;
result
}
fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
assert_eq!(t, t2); // we are abusing TypeRelation here; both LHS and RHS ought to be ==
if let Some(&result) = self.cache.get(&t) {
return Ok(result);
}
debug!("generalize: t={:?}", t);
// Check to see whether the type we are generalizing references
// any other type variable related to `vid` via
// subtyping. This is basically our "occurs check", preventing
// us from creating infinitely sized types.
let result = match *t.kind() {
ty::Infer(ty::TyVar(vid)) => {
let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid);
let sub_vid = self.infcx.inner.borrow_mut().type_variables().sub_root_var(vid);
if sub_vid == self.for_vid_sub_root {
// If sub-roots are equal, then `for_vid` and
// `vid` are related via subtyping.
Err(TypeError::CyclicTy(self.root_ty))
} else {
let probe = self.infcx.inner.borrow_mut().type_variables().probe(vid);
match probe {
TypeVariableValue::Known { value: u } => {
debug!("generalize: known value {:?}", u);
self.relate(u, u)
}
TypeVariableValue::Unknown { universe } => {
match self.ambient_variance {
// Invariant: no need to make a fresh type variable.
ty::Invariant => {
if self.for_universe.can_name(universe) {
return Ok(t);
}
}
// Bivariant: make a fresh var, but we
// may need a WF predicate. See
// comment on `needs_wf` field for
// more info.
ty::Bivariant => self.needs_wf = true,
// Co/contravariant: this will be
// sufficiently constrained later on.
ty::Covariant | ty::Contravariant => (),
}
let origin =
*self.infcx.inner.borrow_mut().type_variables().var_origin(vid);
let new_var_id = self
.infcx
.inner
.borrow_mut()
.type_variables()
.new_var(self.for_universe, origin);
let u = self.tcx().mk_ty_var(new_var_id);
// Record that we replaced `vid` with `new_var_id` as part of a generalization
// operation. This is needed to detect cyclic types. To see why, see the
// docs in the `type_variables` module.
self.infcx.inner.borrow_mut().type_variables().sub(vid, new_var_id);
debug!("generalize: replacing original vid={:?} with new={:?}", vid, u);
Ok(u)
}
}
}
}
ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => {
// No matter what mode we are in,
// integer/floating-point types must be equal to be
// relatable.
Ok(t)
}
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
let s = self.relate(substs, substs)?;
Ok(if s == substs { t } else { self.infcx.tcx.mk_opaque(def_id, s) })
}
_ => relate::super_relate_tys(self, t, t),
}?;
self.cache.insert(t, result);
Ok(result)
}
fn regions(
&mut self,
r: ty::Region<'tcx>,
r2: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
assert_eq!(r, r2); // we are abusing TypeRelation here; both LHS and RHS ought to be ==
debug!("generalize: regions r={:?}", r);
match *r {
// Never make variables for regions bound within the type itself,
// nor for erased regions.
ty::ReLateBound(..) | ty::ReErased => {
return Ok(r);
}
ty::ReError(_) => {
return Ok(r);
}
ty::RePlaceholder(..)
| ty::ReVar(..)
| ty::ReStatic
| ty::ReEarlyBound(..)
| ty::ReFree(..) => {
// see common code below
}
}
// If we are in an invariant context, we can re-use the region
// as is, unless it happens to be in some universe that we
// can't name. (In the case of a region *variable*, we could
// use it if we promoted it into our universe, but we don't
// bother.)
if let ty::Invariant = self.ambient_variance {
let r_universe = self.infcx.universe_of_region(r);
if self.for_universe.can_name(r_universe) {
return Ok(r);
}
}
// FIXME: This is non-ideal because we don't give a
// very descriptive origin for this region variable.
Ok(self.infcx.next_region_var_in_universe(MiscVariable(self.cause.span), self.for_universe))
}
fn consts(
&mut self,
c: ty::Const<'tcx>,
c2: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
assert_eq!(c, c2); // we are abusing TypeRelation here; both LHS and RHS ought to be ==
match c.kind() {
ty::ConstKind::Infer(InferConst::Var(vid)) => {
let mut inner = self.infcx.inner.borrow_mut();
let variable_table = &mut inner.const_unification_table();
let var_value = variable_table.probe_value(vid);
match var_value.val {
ConstVariableValue::Known { value: u } => {
drop(inner);
self.relate(u, u)
}
ConstVariableValue::Unknown { universe } => {
if self.for_universe.can_name(universe) {
Ok(c)
} else {
let new_var_id = variable_table.new_key(ConstVarValue {
origin: var_value.origin,
val: ConstVariableValue::Unknown { universe: self.for_universe },
});
Ok(self.tcx().mk_const(new_var_id, c.ty()))
}
}
}
}
ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs }) => {
let substs = self.relate_with_variance(
ty::Variance::Invariant,
ty::VarianceDiagInfo::default(),
substs,
substs,
)?;
Ok(self.tcx().mk_const(ty::UnevaluatedConst { def, substs }, c.ty()))
}
_ => relate::super_relate_consts(self, c, c),
}
}
}
pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> {
/// Register obligations that must hold in order for this relation to hold
fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>);
@ -873,135 +499,3 @@ fn float_unification_error<'tcx>(
let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v;
TypeError::FloatMismatch(ExpectedFound::new(a_is_expected, a, b))
}
struct ConstInferUnifier<'cx, 'tcx> {
infcx: &'cx InferCtxt<'tcx>,
span: Span,
for_universe: ty::UniverseIndex,
/// The vid of the const variable that is in the process of being
/// instantiated; if we find this within the const we are folding,
/// that means we would have created a cyclic const.
target_vid: ty::ConstVid<'tcx>,
}
impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for ConstInferUnifier<'_, 'tcx> {
type Error = TypeError<'tcx>;
fn interner(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
#[instrument(level = "debug", skip(self), ret)]
fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, TypeError<'tcx>> {
match t.kind() {
&ty::Infer(ty::TyVar(vid)) => {
let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid);
let probe = self.infcx.inner.borrow_mut().type_variables().probe(vid);
match probe {
TypeVariableValue::Known { value: u } => {
debug!("ConstOccursChecker: known value {:?}", u);
u.try_fold_with(self)
}
TypeVariableValue::Unknown { universe } => {
if self.for_universe.can_name(universe) {
return Ok(t);
}
let origin =
*self.infcx.inner.borrow_mut().type_variables().var_origin(vid);
let new_var_id = self
.infcx
.inner
.borrow_mut()
.type_variables()
.new_var(self.for_universe, origin);
Ok(self.interner().mk_ty_var(new_var_id))
}
}
}
ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => Ok(t),
_ => t.try_super_fold_with(self),
}
}
#[instrument(level = "debug", skip(self), ret)]
fn try_fold_region(
&mut self,
r: ty::Region<'tcx>,
) -> Result<ty::Region<'tcx>, TypeError<'tcx>> {
debug!("ConstInferUnifier: r={:?}", r);
match *r {
// Never make variables for regions bound within the type itself,
// nor for erased regions.
ty::ReLateBound(..) | ty::ReErased | ty::ReError(_) => {
return Ok(r);
}
ty::RePlaceholder(..)
| ty::ReVar(..)
| ty::ReStatic
| ty::ReEarlyBound(..)
| ty::ReFree(..) => {
// see common code below
}
}
let r_universe = self.infcx.universe_of_region(r);
if self.for_universe.can_name(r_universe) {
return Ok(r);
} else {
// FIXME: This is non-ideal because we don't give a
// very descriptive origin for this region variable.
Ok(self.infcx.next_region_var_in_universe(MiscVariable(self.span), self.for_universe))
}
}
#[instrument(level = "debug", skip(self), ret)]
fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, TypeError<'tcx>> {
match c.kind() {
ty::ConstKind::Infer(InferConst::Var(vid)) => {
// Check if the current unification would end up
// unifying `target_vid` with a const which contains
// an inference variable which is unioned with `target_vid`.
//
// Not doing so can easily result in stack overflows.
if self
.infcx
.inner
.borrow_mut()
.const_unification_table()
.unioned(self.target_vid, vid)
{
return Err(TypeError::CyclicConst(c));
}
let var_value =
self.infcx.inner.borrow_mut().const_unification_table().probe_value(vid);
match var_value.val {
ConstVariableValue::Known { value: u } => u.try_fold_with(self),
ConstVariableValue::Unknown { universe } => {
if self.for_universe.can_name(universe) {
Ok(c)
} else {
let new_var_id =
self.infcx.inner.borrow_mut().const_unification_table().new_key(
ConstVarValue {
origin: var_value.origin,
val: ConstVariableValue::Unknown {
universe: self.for_universe,
},
},
);
Ok(self.interner().mk_const(new_var_id, c.ty()))
}
}
}
}
_ => c.try_super_fold_with(self),
}
}
}

View file

@ -1,7 +1,7 @@
use crate::infer::DefineOpaqueTypes;
use crate::traits::PredicateObligations;
use super::combine::{CombineFields, ObligationEmittingRelation, RelationDir};
use super::combine::{CombineFields, ObligationEmittingRelation};
use super::Subtype;
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
@ -88,11 +88,11 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
}
(&ty::Infer(TyVar(a_id)), _) => {
self.fields.instantiate(b, RelationDir::EqTo, a_id, self.a_is_expected)?;
self.fields.instantiate(b, ty::Invariant, a_id, self.a_is_expected)?;
}
(_, &ty::Infer(TyVar(b_id))) => {
self.fields.instantiate(a, RelationDir::EqTo, b_id, self.a_is_expected)?;
self.fields.instantiate(a, ty::Invariant, b_id, self.a_is_expected)?;
}
(

View file

@ -2723,7 +2723,7 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> {
| (ty::Infer(ty::InferTy::TyVar(_)), _)
| (_, ty::Infer(ty::InferTy::TyVar(_))) => Ok(a),
(ty::Infer(_), _) | (_, ty::Infer(_)) => Err(TypeError::Mismatch),
_ => relate::super_relate_tys(self, a, b),
_ => relate::structurally_relate_tys(self, a, b),
}
}

View file

@ -0,0 +1,479 @@
use rustc_data_structures::sso::SsoHashMap;
use rustc_hir::def_id::DefId;
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::Span;
use crate::infer::nll_relate::TypeRelatingDelegate;
use crate::infer::type_variable::TypeVariableValue;
use crate::infer::{InferCtxt, RegionVariableOrigin};
/// Attempts to generalize `term` for the type variable `for_vid`.
/// This checks for cycles -- that is, whether the type `term`
/// references `for_vid`.
pub(super) fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into<Term<'tcx>> + Relate<'tcx>>(
infcx: &InferCtxt<'tcx>,
delegate: &mut D,
term: T,
for_vid: impl Into<ty::TermVid<'tcx>>,
ambient_variance: ty::Variance,
) -> RelateResult<'tcx, Generalization<T>> {
let (for_universe, root_vid) = match for_vid.into() {
ty::TermVid::Ty(ty_vid) => (
infcx.probe_ty_var(ty_vid).unwrap_err(),
ty::TermVid::Ty(infcx.inner.borrow_mut().type_variables().sub_root_var(ty_vid)),
),
ty::TermVid::Const(ct_vid) => (
infcx.probe_const_var(ct_vid).unwrap_err(),
ty::TermVid::Const(infcx.inner.borrow_mut().const_unification_table().find(ct_vid)),
),
};
let mut generalizer = Generalizer {
infcx,
delegate,
ambient_variance,
root_vid,
for_universe,
root_term: term.into(),
needs_wf: false,
cache: Default::default(),
};
assert!(!term.has_escaping_bound_vars());
let value = generalizer.relate(term, term)?;
let needs_wf = generalizer.needs_wf;
Ok(Generalization { value, needs_wf })
}
/// Abstracts the handling of region vars between HIR and MIR/NLL typechecking
/// in the generalizer code.
pub trait GeneralizerDelegate<'tcx> {
fn param_env(&self) -> ty::ParamEnv<'tcx>;
fn forbid_inference_vars() -> bool;
fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>;
}
pub struct CombineDelegate<'cx, 'tcx> {
pub infcx: &'cx InferCtxt<'tcx>,
pub param_env: ty::ParamEnv<'tcx>,
pub span: Span,
}
impl<'tcx> GeneralizerDelegate<'tcx> for CombineDelegate<'_, 'tcx> {
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.param_env
}
fn forbid_inference_vars() -> bool {
false
}
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
.next_region_var_in_universe(RegionVariableOrigin::MiscVariable(self.span), universe)
}
}
impl<'tcx, T> GeneralizerDelegate<'tcx> for T
where
T: TypeRelatingDelegate<'tcx>,
{
fn param_env(&self) -> ty::ParamEnv<'tcx> {
<Self as TypeRelatingDelegate<'tcx>>::param_env(self)
}
fn forbid_inference_vars() -> bool {
<Self as TypeRelatingDelegate<'tcx>>::forbid_inference_vars()
}
fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
<Self as TypeRelatingDelegate<'tcx>>::generalize_existential(self, universe)
}
}
/// The "generalizer" is used when handling inference variables.
///
/// The basic strategy for handling a constraint like `?A <: B` is to
/// apply a "generalization strategy" to the term `B` -- this replaces
/// all the lifetimes in the term `B` with fresh inference variables.
/// (You can read more about the strategy in this [blog post].)
///
/// 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
/// value of `A`. Finally, we relate `&'0 u32 <: &'x u32`, which
/// establishes `'0: 'x` as a constraint.
///
/// [blog post]: https://is.gd/0hKvIr
struct Generalizer<'me, 'tcx, D> {
infcx: &'me InferCtxt<'tcx>,
/// This is used to abstract the behaviors of the three previous
/// generalizer-like implementations (`Generalizer`, `TypeGeneralizer`,
/// and `ConstInferUnifier`). See [`GeneralizerDelegate`] for more
/// information.
delegate: &'me mut D,
/// After we generalize this type, we are going to relate it to
/// some other type. What will be the variance at this point?
ambient_variance: ty::Variance,
/// The vid of the type variable that is in the process of being
/// instantiated. If we find this within the value we are folding,
/// that means we would have created a cyclic value.
root_vid: ty::TermVid<'tcx>,
/// The universe of the type variable that is in the process of being
/// instantiated. If we find anything that this universe cannot name,
/// we reject the relation.
for_universe: ty::UniverseIndex,
/// The root term (const or type) we're generalizing. Used for cycle errors.
root_term: Term<'tcx>,
cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
/// See the field `needs_wf` in `Generalization`.
needs_wf: bool,
}
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> {
match self.root_term.unpack() {
ty::TermKind::Ty(ty) => TypeError::CyclicTy(ty),
ty::TermKind::Const(ct) => TypeError::CyclicConst(ct),
}
}
}
impl<'tcx, D> TypeRelation<'tcx> for Generalizer<'_, 'tcx, D>
where
D: GeneralizerDelegate<'tcx>,
{
fn tcx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.delegate.param_env()
}
fn tag(&self) -> &'static str {
"Generalizer"
}
fn a_is_expected(&self) -> bool {
true
}
fn relate_item_substs(
&mut self,
item_def_id: DefId,
a_subst: ty::SubstsRef<'tcx>,
b_subst: ty::SubstsRef<'tcx>,
) -> RelateResult<'tcx, ty::SubstsRef<'tcx>> {
if self.ambient_variance == ty::Variance::Invariant {
// Avoid fetching the variance if we are in an invariant
// context; no need, and it can induce dependency cycles
// (e.g., #41849).
relate::relate_substs(self, a_subst, b_subst)
} else {
let tcx = self.tcx();
let opt_variances = tcx.variances_of(item_def_id);
relate::relate_substs_with_variances(
self,
item_def_id,
opt_variances,
a_subst,
b_subst,
true,
)
}
}
#[instrument(level = "debug", skip(self, variance, b), ret)]
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
variance: ty::Variance,
_info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
let old_ambient_variance = self.ambient_variance;
self.ambient_variance = self.ambient_variance.xform(variance);
debug!(?self.ambient_variance, "new ambient variance");
let r = self.relate(a, b)?;
self.ambient_variance = old_ambient_variance;
Ok(r)
}
#[instrument(level = "debug", skip(self, t2), ret)]
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 ==
if let Some(&result) = self.cache.get(&t) {
return Ok(result);
}
// Check to see whether the type we are generalizing references
// any other type variable related to `vid` via
// subtyping. This is basically our "occurs check", preventing
// us from creating infinitely sized types.
let g = match *t.kind() {
ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_))
if D::forbid_inference_vars() =>
{
bug!("unexpected inference variable encountered in NLL generalization: {t}");
}
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("unexpected infer type: {t}")
}
ty::Infer(ty::TyVar(vid)) => {
let mut inner = self.infcx.inner.borrow_mut();
let vid = inner.type_variables().root_var(vid);
let sub_vid = inner.type_variables().sub_root_var(vid);
if ty::TermVid::Ty(sub_vid) == self.root_vid {
// If sub-roots are equal, then `root_vid` and
// `vid` are related via subtyping.
Err(self.cyclic_term_error())
} else {
let probe = inner.type_variables().probe(vid);
match probe {
TypeVariableValue::Known { value: u } => {
drop(inner);
self.relate(u, u)
}
TypeVariableValue::Unknown { universe } => {
match self.ambient_variance {
// Invariant: no need to make a fresh type variable
// if we can name the universe.
ty::Invariant => {
if self.for_universe.can_name(universe) {
return Ok(t);
}
}
// Bivariant: make a fresh var, but we
// may need a WF predicate. See
// comment on `needs_wf` field for
// more info.
ty::Bivariant => self.needs_wf = true,
// Co/contravariant: this will be
// sufficiently constrained later on.
ty::Covariant | ty::Contravariant => (),
}
let origin = *inner.type_variables().var_origin(vid);
let new_var_id =
inner.type_variables().new_var(self.for_universe, origin);
let u = self.tcx().mk_ty_var(new_var_id);
// Record that we replaced `vid` with `new_var_id` as part of a generalization
// operation. This is needed to detect cyclic types. To see why, see the
// docs in the `type_variables` module.
inner.type_variables().sub(vid, new_var_id);
debug!("replacing original vid={:?} with new={:?}", vid, u);
Ok(u)
}
}
}
}
ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => {
// No matter what mode we are in,
// integer/floating-point types must be equal to be
// relatable.
Ok(t)
}
ty::Placeholder(placeholder) => {
if self.for_universe.can_name(placeholder.universe) {
Ok(t)
} else {
debug!(
"root universe {:?} cannot name placeholder in universe {:?}",
self.for_universe, placeholder.universe
);
Err(TypeError::Mismatch)
}
}
_ => relate::structurally_relate_tys(self, t, t),
}?;
self.cache.insert(t, g);
Ok(g)
}
#[instrument(level = "debug", skip(self, r2), ret)]
fn regions(
&mut self,
r: ty::Region<'tcx>,
r2: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
assert_eq!(r, r2); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
match *r {
// Never make variables for regions bound within the type itself,
// nor for erased regions.
ty::ReLateBound(..) | ty::ReErased => {
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(_) => {
return Ok(r);
}
ty::RePlaceholder(..)
| ty::ReVar(..)
| ty::ReStatic
| ty::ReEarlyBound(..)
| ty::ReFree(..) => {
// see common code below
}
}
// If we are in an invariant context, we can re-use the region
// as is, unless it happens to be in some universe that we
// can't name.
if let ty::Invariant = self.ambient_variance {
let r_universe = self.infcx.universe_of_region(r);
if self.for_universe.can_name(r_universe) {
return Ok(r);
}
}
Ok(self.delegate.generalize_region(self.for_universe))
}
#[instrument(level = "debug", skip(self, c2), ret)]
fn consts(
&mut self,
c: ty::Const<'tcx>,
c2: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
assert_eq!(c, c2); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
match c.kind() {
ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => {
bug!("unexpected inference variable encountered in NLL generalization: {:?}", c);
}
ty::ConstKind::Infer(InferConst::Var(vid)) => {
// If root const vids are equal, then `root_vid` and
// `vid` are related and we'd be inferring an infinitely
// deep const.
if ty::TermVid::Const(
self.infcx.inner.borrow_mut().const_unification_table().find(vid),
) == self.root_vid
{
return Err(self.cyclic_term_error());
}
let mut inner = self.infcx.inner.borrow_mut();
let variable_table = &mut inner.const_unification_table();
let var_value = variable_table.probe_value(vid);
match var_value.val {
ConstVariableValue::Known { value: u } => {
drop(inner);
self.relate(u, u)
}
ConstVariableValue::Unknown { universe } => {
if self.for_universe.can_name(universe) {
Ok(c)
} else {
let new_var_id = variable_table.new_key(ConstVarValue {
origin: var_value.origin,
val: ConstVariableValue::Unknown { universe: self.for_universe },
});
Ok(self.tcx().mk_const(new_var_id, c.ty()))
}
}
}
}
// FIXME: remove this branch once `structurally_relate_consts` is fully
// structural.
ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs }) => {
let substs = self.relate_with_variance(
ty::Variance::Invariant,
ty::VarianceDiagInfo::default(),
substs,
substs,
)?;
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::structurally_relate_consts(self, c, c),
}
}
#[instrument(level = "debug", skip(self), ret)]
fn binders<T>(
&mut self,
a: ty::Binder<'tcx, T>,
_: ty::Binder<'tcx, T>,
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
where
T: Relate<'tcx>,
{
let result = self.relate(a.skip_binder(), a.skip_binder())?;
Ok(a.rebind(result))
}
}
/// Result from a generalization operation. This includes
/// not only the generalized type, but also a bool flag
/// indicating whether further WF checks are needed.
#[derive(Debug)]
pub struct Generalization<T> {
pub value: T,
/// If true, then the generalized type may not be well-formed,
/// even if the source type is well-formed, so we should add an
/// additional check to enforce that it is. This arises in
/// particular around 'bivariant' type parameters that are only
/// constrained by a where-clause. As an example, imagine a type:
///
/// struct Foo<A, B> where A: Iterator<Item = B> {
/// data: A
/// }
///
/// here, `A` will be covariant, but `B` is
/// unconstrained. However, whatever it is, for `Foo` to be WF, it
/// must be equal to `A::Item`. If we have an input `Foo<?A, ?B>`,
/// then after generalization we will wind up with a type like
/// `Foo<?C, ?D>`. When we enforce that `Foo<?A, ?B> <: Foo<?C,
/// ?D>` (or `>:`), we will wind up with the requirement that `?A
/// <: ?C`, but no particular relationship between `?B` and `?D`
/// (after all, we do not know the variance of the normalized form
/// of `A::Item` with respect to `A`). If we do nothing else, this
/// may mean that `?D` goes unconstrained (as in #41677). So, in
/// this scenario where we create a new type variable in a
/// bivariant context, we set the `needs_wf` flag to true. This
/// will force the calling code to check that `WF(Foo<?C, ?D>)`
/// holds, which in turn implies that `?C::Item == ?D`. So once
/// `?C` is constrained, that should suffice to restrict `?D`.
pub needs_wf: bool,
}

View file

@ -58,6 +58,7 @@ pub mod error_reporting;
pub mod free_regions;
mod freshen;
mod fudge;
mod generalize;
mod glb;
mod higher_ranked;
pub mod lattice;

View file

@ -21,21 +21,20 @@
//! thing we relate in chalk are basically domain goals and their
//! constituents)
use crate::infer::InferCtxt;
use crate::infer::{ConstVarValue, ConstVariableValue};
use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind};
use crate::traits::{Obligation, PredicateObligations};
use rustc_data_structures::fx::FxHashMap;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::fold::FnMutDelegate;
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{self, InferConst, Ty, TyCtxt};
use rustc_span::{Span, Symbol};
use std::fmt::Debug;
use super::combine::ObligationEmittingRelation;
use crate::infer::combine::ObligationEmittingRelation;
use crate::infer::generalize::{self, Generalization};
use crate::infer::InferCtxt;
use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind};
use crate::traits::{Obligation, PredicateObligations};
pub struct TypeRelating<'me, 'tcx, D>
where
@ -198,7 +197,7 @@ where
_ => (),
}
let generalized_ty = self.generalize_value(value_ty, vid)?;
let generalized_ty = self.generalize(value_ty, vid)?;
debug!("relate_ty_var: generalized_ty = {:?}", generalized_ty);
if D::forbid_inference_vars() {
@ -217,26 +216,15 @@ where
result
}
fn generalize_value<T: Relate<'tcx>>(
&mut self,
value: T,
for_vid: ty::TyVid,
) -> RelateResult<'tcx, T> {
let universe = self.infcx.probe_ty_var(for_vid).unwrap_err();
if value.has_escaping_bound_vars() {
bug!("trying to instantiate {for_vid:?} with escaping bound vars: {value:?}");
}
let mut generalizer = TypeGeneralizer {
infcx: self.infcx,
delegate: &mut self.delegate,
ambient_variance: self.ambient_variance,
for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables().sub_root_var(for_vid),
universe,
};
generalizer.relate(value, value)
fn generalize(&mut self, ty: Ty<'tcx>, for_vid: ty::TyVid) -> RelateResult<'tcx, Ty<'tcx>> {
let Generalization { value: ty, needs_wf: _ } = generalize::generalize(
self.infcx,
&mut self.delegate,
ty,
for_vid,
self.ambient_variance,
)?;
Ok(ty)
}
fn relate_opaques(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
@ -716,235 +704,3 @@ where
})]);
}
}
/// The "type generalizer" is used when handling inference variables.
///
/// The basic strategy for handling a constraint like `?A <: B` is to
/// apply a "generalization strategy" to the type `B` -- this replaces
/// all the lifetimes in the type `B` with fresh inference
/// variables. (You can read more about the strategy in this [blog
/// post].)
///
/// 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
/// value of `A`. Finally, we relate `&'0 u32 <: &'x u32`, which
/// establishes `'0: 'x` as a constraint.
///
/// [blog post]: https://is.gd/0hKvIr
struct TypeGeneralizer<'me, 'tcx, D>
where
D: TypeRelatingDelegate<'tcx>,
{
infcx: &'me InferCtxt<'tcx>,
delegate: &'me mut D,
/// After we generalize this type, we are going to relate it to
/// some other type. What will be the variance at this point?
ambient_variance: ty::Variance,
/// The vid of the type variable that is in the process of being
/// instantiated. If we find this within the value we are folding,
/// that means we would have created a cyclic value.
for_vid_sub_root: ty::TyVid,
/// The universe of the type variable that is in the process of being
/// instantiated. If we find anything that this universe cannot name,
/// we reject the relation.
universe: ty::UniverseIndex,
}
impl<'tcx, D> TypeRelation<'tcx> for TypeGeneralizer<'_, 'tcx, D>
where
D: TypeRelatingDelegate<'tcx>,
{
fn tcx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.delegate.param_env()
}
fn tag(&self) -> &'static str {
"nll::generalizer"
}
fn a_is_expected(&self) -> bool {
true
}
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
variance: ty::Variance,
_info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
debug!(
"TypeGeneralizer::relate_with_variance(variance={:?}, a={:?}, b={:?})",
variance, a, b
);
let old_ambient_variance = self.ambient_variance;
self.ambient_variance = self.ambient_variance.xform(variance);
debug!(
"TypeGeneralizer::relate_with_variance: ambient_variance = {:?}",
self.ambient_variance
);
let r = self.relate(a, b)?;
self.ambient_variance = old_ambient_variance;
debug!("TypeGeneralizer::relate_with_variance: r={:?}", r);
Ok(r)
}
fn tys(&mut self, a: Ty<'tcx>, _: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
use crate::infer::type_variable::TypeVariableValue;
debug!("TypeGeneralizer::tys(a={:?})", a);
match *a.kind() {
ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_))
if D::forbid_inference_vars() =>
{
bug!("unexpected inference variable encountered in NLL generalization: {:?}", a);
}
ty::Infer(ty::TyVar(vid)) => {
let mut inner = self.infcx.inner.borrow_mut();
let variables = &mut inner.type_variables();
let vid = variables.root_var(vid);
let sub_vid = variables.sub_root_var(vid);
if sub_vid == self.for_vid_sub_root {
// If sub-roots are equal, then `for_vid` and
// `vid` are related via subtyping.
debug!("TypeGeneralizer::tys: occurs check failed");
Err(TypeError::Mismatch)
} else {
match variables.probe(vid) {
TypeVariableValue::Known { value: u } => {
drop(inner);
self.relate(u, u)
}
TypeVariableValue::Unknown { universe: _universe } => {
if self.ambient_variance == ty::Bivariant {
// FIXME: we may need a WF predicate (related to #54105).
}
let origin = *variables.var_origin(vid);
// Replacing with a new variable in the universe `self.universe`,
// it will be unified later with the original type variable in
// the universe `_universe`.
let new_var_id = variables.new_var(self.universe, origin);
let u = self.tcx().mk_ty_var(new_var_id);
debug!("generalize: replacing original vid={:?} with new={:?}", vid, u);
Ok(u)
}
}
}
}
ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => {
// No matter what mode we are in,
// integer/floating-point types must be equal to be
// relatable.
Ok(a)
}
ty::Placeholder(placeholder) => {
if self.universe.cannot_name(placeholder.universe) {
debug!(
"TypeGeneralizer::tys: root universe {:?} cannot name\
placeholder in universe {:?}",
self.universe, placeholder.universe
);
Err(TypeError::Mismatch)
} else {
Ok(a)
}
}
_ => relate::super_relate_tys(self, a, a),
}
}
fn regions(
&mut self,
a: ty::Region<'tcx>,
_: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
debug!("TypeGeneralizer::regions(a={:?})", a);
if let ty::ReLateBound(..) = *a {
return Ok(a);
}
// For now, we just always create a fresh region variable to
// replace all the regions in the source type. In the main
// type checker, we special case the case where the ambient
// variance is `Invariant` and try to avoid creating a fresh
// region variable, but since this comes up so much less in
// NLL (only when users use `_` etc) it is much less
// important.
//
// As an aside, since these new variables are created in
// `self.universe` universe, this also serves to enforce the
// universe scoping rules.
//
// FIXME(#54105) -- if the ambient variance is bivariant,
// though, we may however need to check well-formedness or
// risk a problem like #41677 again.
let replacement_region_vid = self.delegate.generalize_existential(self.universe);
Ok(replacement_region_vid)
}
fn consts(
&mut self,
a: ty::Const<'tcx>,
_: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
match a.kind() {
ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => {
bug!("unexpected inference variable encountered in NLL generalization: {:?}", a);
}
ty::ConstKind::Infer(InferConst::Var(vid)) => {
let mut inner = self.infcx.inner.borrow_mut();
let variable_table = &mut inner.const_unification_table();
let var_value = variable_table.probe_value(vid);
match var_value.val.known() {
Some(u) => self.relate(u, u),
None => {
let new_var_id = variable_table.new_key(ConstVarValue {
origin: var_value.origin,
val: ConstVariableValue::Unknown { universe: self.universe },
});
Ok(self.tcx().mk_const(new_var_id, a.ty()))
}
}
}
ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(a),
_ => relate::super_relate_consts(self, a, a),
}
}
fn binders<T>(
&mut self,
a: ty::Binder<'tcx, T>,
_: ty::Binder<'tcx, T>,
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
where
T: Relate<'tcx>,
{
debug!("TypeGeneralizer::binders(a={:?})", a);
let result = self.relate(a.skip_binder(), a.skip_binder())?;
Ok(a.rebind(result))
}
}

View file

@ -187,7 +187,7 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> {
} else if pattern == value {
Ok(pattern)
} else {
relate::super_relate_tys(self, pattern, value)
relate::structurally_relate_tys(self, pattern, value)
}
}
@ -201,7 +201,7 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> {
if pattern == value {
Ok(pattern)
} else {
relate::super_relate_consts(self, pattern, value)
relate::structurally_relate_consts(self, pattern, value)
}
}

View file

@ -1,4 +1,4 @@
use super::combine::{CombineFields, RelationDir};
use super::combine::CombineFields;
use super::{DefineOpaqueTypes, ObligationEmittingRelation, SubregionOrigin};
use crate::traits::{Obligation, PredicateObligations};
@ -108,11 +108,11 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
Ok(a)
}
(&ty::Infer(TyVar(a_id)), _) => {
self.fields.instantiate(b, RelationDir::SupertypeOf, a_id, !self.a_is_expected)?;
self.fields.instantiate(b, ty::Contravariant, a_id, !self.a_is_expected)?;
Ok(a)
}
(_, &ty::Infer(TyVar(b_id))) => {
self.fields.instantiate(a, RelationDir::SubtypeOf, b_id, self.a_is_expected)?;
self.fields.instantiate(a, ty::Covariant, b_id, self.a_is_expected)?;
Ok(a)
}

View file

@ -83,7 +83,7 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> {
(&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(self.tcx().ty_error(guar)),
_ => relate::super_relate_tys(self, a, b),
_ => relate::structurally_relate_tys(self, a, b),
}
}
@ -109,7 +109,7 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> {
_ => {}
}
relate::super_relate_consts(self, a, b)
relate::structurally_relate_consts(self, a, b)
}
fn binders<T>(

View file

@ -1072,6 +1072,24 @@ impl ParamTerm {
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum TermVid<'tcx> {
Ty(ty::TyVid),
Const(ty::ConstVid<'tcx>),
}
impl From<ty::TyVid> for TermVid<'_> {
fn from(value: ty::TyVid) -> Self {
TermVid::Ty(value)
}
}
impl<'tcx> From<ty::ConstVid<'tcx>> for TermVid<'tcx> {
fn from(value: ty::ConstVid<'tcx>) -> Self {
TermVid::Const(value)
}
}
/// This kind of predicate has no *direct* correspondent in the
/// syntax, but it roughly corresponds to the syntactic forms:
///

View file

@ -388,24 +388,24 @@ impl<'tcx> Relate<'tcx> for Ty<'tcx> {
}
}
/// The main "type relation" routine. Note that this does not handle
/// inference artifacts, so you should filter those out before calling
/// it.
pub fn super_relate_tys<'tcx, R: TypeRelation<'tcx>>(
/// Relates `a` and `b` structurally, calling the relation for all nested values.
/// Any semantic equality, e.g. of projections, and inference variables have to be
/// handled by the caller.
pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
relation: &mut R,
a: Ty<'tcx>,
b: Ty<'tcx>,
) -> RelateResult<'tcx, Ty<'tcx>> {
let tcx = relation.tcx();
debug!("super_relate_tys: a={:?} b={:?}", a, b);
debug!("structurally_relate_tys: a={:?} b={:?}", a, b);
match (a.kind(), b.kind()) {
(&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
// The caller should handle these cases!
bug!("var types encountered in super_relate_tys")
bug!("var types encountered in structurally_relate_tys")
}
(ty::Bound(..), _) | (_, ty::Bound(..)) => {
bug!("bound types encountered in super_relate_tys")
bug!("bound types encountered in structurally_relate_tys")
}
(&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(tcx.ty_error(guar)),
@ -575,15 +575,18 @@ pub fn super_relate_tys<'tcx, R: TypeRelation<'tcx>>(
}
}
/// The main "const relation" routine. Note that this does not handle
/// inference artifacts, so you should filter those out before calling
/// it.
pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
/// Relates `a` and `b` structurally, calling the relation for all nested values.
/// Any semantic equality, e.g. of unevaluated consts, and inference variables have
/// to be handled by the caller.
///
/// FIXME: This is not totally structual, which probably should be fixed.
/// See the HACKs below.
pub fn structurally_relate_consts<'tcx, R: TypeRelation<'tcx>>(
relation: &mut R,
mut a: ty::Const<'tcx>,
mut b: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
debug!("{}.super_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b);
debug!("{}.structurally_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b);
let tcx = relation.tcx();
// HACK(const_generics): We still need to eagerly evaluate consts when
@ -602,7 +605,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
b = tcx.expand_abstract_consts(b);
}
debug!("{}.super_relate_consts(normed_a = {:?}, normed_b = {:?})", relation.tag(), a, b);
debug!("{}.structurally_relate_consts(normed_a = {:?}, normed_b = {:?})", relation.tag(), a, b);
// Currently, the values that can be unified are primitive types,
// and those that derive both `PartialEq` and `Eq`, corresponding
@ -610,7 +613,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
let is_match = match (a.kind(), b.kind()) {
(ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => {
// The caller should handle these cases!
bug!("var types encountered in super_relate_consts: {:?} {:?}", a, b)
bug!("var types encountered in structurally_relate_consts: {:?} {:?}", a, b)
}
(ty::ConstKind::Error(_), _) => return Ok(a),

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`.