generalize: handle occurs check failure in aliases
This commit is contained in:
parent
2d0ec174e4
commit
f69d67221e
5 changed files with 124 additions and 33 deletions
|
@ -334,6 +334,10 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||||
ty::Variance::Invariant,
|
ty::Variance::Invariant,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// FIXME(generic_const_exprs): Occurs check failures for unevaluated
|
||||||
|
// constants and generic expressions are not yet handled correctly.
|
||||||
|
let value = value.may_be_infer();
|
||||||
|
|
||||||
self.inner.borrow_mut().const_unification_table().union_value(
|
self.inner.borrow_mut().const_unification_table().union_value(
|
||||||
target_vid,
|
target_vid,
|
||||||
ConstVarValue {
|
ConstVarValue {
|
||||||
|
@ -445,7 +449,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
|
||||||
// `'?2` and `?3` are fresh region/type inference
|
// `'?2` and `?3` are fresh region/type inference
|
||||||
// variables. (Down below, we will relate `a_ty <: b_ty`,
|
// variables. (Down below, we will relate `a_ty <: b_ty`,
|
||||||
// adding constraints like `'x: '?2` and `?1 <: ?3`.)
|
// adding constraints like `'x: '?2` and `?1 <: ?3`.)
|
||||||
let Generalization { value: b_ty, needs_wf } = generalize::generalize(
|
let Generalization { value, needs_wf } = generalize::generalize(
|
||||||
self.infcx,
|
self.infcx,
|
||||||
&mut CombineDelegate {
|
&mut CombineDelegate {
|
||||||
infcx: self.infcx,
|
infcx: self.infcx,
|
||||||
|
@ -457,7 +461,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
|
||||||
ambient_variance,
|
ambient_variance,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
debug!(?b_ty);
|
let b_ty = value.may_be_infer(); // we handle this further down.
|
||||||
self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty);
|
self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty);
|
||||||
|
|
||||||
if needs_wf {
|
if needs_wf {
|
||||||
|
@ -477,6 +481,33 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
|
||||||
// relations wind up attributed to the same spans. We need
|
// relations wind up attributed to the same spans. We need
|
||||||
// to associate causes/spans with each of the relations in
|
// to associate causes/spans with each of the relations in
|
||||||
// the stack to get this right.
|
// the stack to get this right.
|
||||||
|
if b_ty.is_ty_var() {
|
||||||
|
// This happens for cases like `<?0 as Trait>::Assoc == ?0`.
|
||||||
|
// We can't instantiate `?0` here as that would result in a
|
||||||
|
// cyclic type. We instead delay the unification in case
|
||||||
|
// the alias can be normalized to something which does not
|
||||||
|
// mention `?0`.
|
||||||
|
|
||||||
|
// FIXME(-Ztrait-solver=next): replace this with `AliasRelate`
|
||||||
|
let &ty::Alias(kind, data) = a_ty.kind() else {
|
||||||
|
bug!("generalization should only result in infer vars for aliases");
|
||||||
|
};
|
||||||
|
if !self.infcx.next_trait_solver() {
|
||||||
|
// The old solver only accepts projection predicates for associated types.
|
||||||
|
match kind {
|
||||||
|
ty::AliasKind::Projection => {}
|
||||||
|
ty::AliasKind::Inherent | ty::AliasKind::Weak | ty::AliasKind::Opaque => {
|
||||||
|
return Err(TypeError::CyclicTy(a_ty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.obligations.push(Obligation::new(
|
||||||
|
self.tcx(),
|
||||||
|
self.trace.cause.clone(),
|
||||||
|
self.param_env,
|
||||||
|
ty::ProjectionPredicate { projection_ty: data, term: b_ty.into() },
|
||||||
|
))
|
||||||
|
} else {
|
||||||
match ambient_variance {
|
match ambient_variance {
|
||||||
ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty),
|
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::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty),
|
||||||
|
@ -490,6 +521,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
|
||||||
unreachable!("no code should be generalizing bivariantly (currently)")
|
unreachable!("no code should be generalizing bivariantly (currently)")
|
||||||
}
|
}
|
||||||
}?;
|
}?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
use rustc_data_structures::sso::SsoHashMap;
|
use rustc_data_structures::sso::SsoHashMap;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
|
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
|
||||||
use rustc_middle::ty::error::TypeError;
|
use rustc_middle::ty::error::TypeError;
|
||||||
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
|
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
|
||||||
use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitableExt};
|
use rustc_middle::ty::visit::MaxUniverse;
|
||||||
|
use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
use crate::infer::nll_relate::TypeRelatingDelegate;
|
use crate::infer::nll_relate::TypeRelatingDelegate;
|
||||||
use crate::infer::type_variable::TypeVariableValue;
|
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind, TypeVariableValue};
|
||||||
use crate::infer::{InferCtxt, RegionVariableOrigin};
|
use crate::infer::{InferCtxt, RegionVariableOrigin};
|
||||||
|
|
||||||
/// Attempts to generalize `term` for the type variable `for_vid`.
|
/// Attempts to generalize `term` for the type variable `for_vid`.
|
||||||
|
@ -38,6 +41,7 @@ pub(super) fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into<Term<'tcx>>
|
||||||
root_vid,
|
root_vid,
|
||||||
for_universe,
|
for_universe,
|
||||||
root_term: term.into(),
|
root_term: term.into(),
|
||||||
|
in_alias: false,
|
||||||
needs_wf: false,
|
needs_wf: false,
|
||||||
cache: Default::default(),
|
cache: Default::default(),
|
||||||
};
|
};
|
||||||
|
@ -45,20 +49,22 @@ pub(super) fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into<Term<'tcx>>
|
||||||
assert!(!term.has_escaping_bound_vars());
|
assert!(!term.has_escaping_bound_vars());
|
||||||
let value = generalizer.relate(term, term)?;
|
let value = generalizer.relate(term, term)?;
|
||||||
let needs_wf = generalizer.needs_wf;
|
let needs_wf = generalizer.needs_wf;
|
||||||
Ok(Generalization { value, needs_wf })
|
Ok(Generalization { value: HandleProjection(value), needs_wf })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Abstracts the handling of region vars between HIR and MIR/NLL typechecking
|
/// Abstracts the handling of region vars between HIR and MIR/NLL typechecking
|
||||||
/// in the generalizer code.
|
/// in the generalizer code.
|
||||||
pub trait GeneralizerDelegate<'tcx> {
|
pub(super) trait GeneralizerDelegate<'tcx> {
|
||||||
fn param_env(&self) -> ty::ParamEnv<'tcx>;
|
fn param_env(&self) -> ty::ParamEnv<'tcx>;
|
||||||
|
|
||||||
fn forbid_inference_vars() -> bool;
|
fn forbid_inference_vars() -> bool;
|
||||||
|
|
||||||
|
fn span(&self) -> Span;
|
||||||
|
|
||||||
fn generalize_region(&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(super) struct CombineDelegate<'cx, 'tcx> {
|
||||||
pub infcx: &'cx InferCtxt<'tcx>,
|
pub infcx: &'cx InferCtxt<'tcx>,
|
||||||
pub param_env: ty::ParamEnv<'tcx>,
|
pub param_env: ty::ParamEnv<'tcx>,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
|
@ -73,6 +79,10 @@ impl<'tcx> GeneralizerDelegate<'tcx> for CombineDelegate<'_, 'tcx> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
self.span
|
||||||
|
}
|
||||||
|
|
||||||
fn generalize_region(&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
|
// FIXME: This is non-ideal because we don't give a
|
||||||
// very descriptive origin for this region variable.
|
// very descriptive origin for this region variable.
|
||||||
|
@ -93,6 +103,10 @@ where
|
||||||
<Self as TypeRelatingDelegate<'tcx>>::forbid_inference_vars()
|
<Self as TypeRelatingDelegate<'tcx>>::forbid_inference_vars()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
<Self as TypeRelatingDelegate<'tcx>>::span(&self)
|
||||||
|
}
|
||||||
|
|
||||||
fn generalize_region(&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)
|
||||||
}
|
}
|
||||||
|
@ -139,6 +153,12 @@ struct Generalizer<'me, 'tcx, D> {
|
||||||
|
|
||||||
cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
|
cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
|
||||||
|
|
||||||
|
/// This is set once we're generalizing the arguments of an alias. In case
|
||||||
|
/// we encounter an occurs check failure we generalize the alias to an
|
||||||
|
/// inference variable instead of erroring. This is necessary to avoid
|
||||||
|
/// incorrect errors when relating `?0` with `<?0 as Trait>::Assoc`.
|
||||||
|
in_alias: bool,
|
||||||
|
|
||||||
/// See the field `needs_wf` in `Generalization`.
|
/// See the field `needs_wf` in `Generalization`.
|
||||||
needs_wf: bool,
|
needs_wf: bool,
|
||||||
}
|
}
|
||||||
|
@ -309,6 +329,38 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ty::Alias(kind, data) => {
|
||||||
|
let is_nested_alias = mem::replace(&mut self.in_alias, true);
|
||||||
|
let result = match self.relate(data, data) {
|
||||||
|
Ok(data) => Ok(Ty::new_alias(self.tcx(), kind, data)),
|
||||||
|
Err(e) => {
|
||||||
|
if is_nested_alias {
|
||||||
|
return Err(e);
|
||||||
|
} else {
|
||||||
|
let mut visitor = MaxUniverse::new();
|
||||||
|
t.visit_with(&mut visitor);
|
||||||
|
let infer_replacement_is_complete =
|
||||||
|
self.for_universe.can_name(visitor.max_universe())
|
||||||
|
&& !t.has_escaping_bound_vars();
|
||||||
|
if !infer_replacement_is_complete {
|
||||||
|
warn!("incomplete generalization of an alias type: {t:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("generalization failure in alias");
|
||||||
|
Ok(self.infcx.next_ty_var_in_universe(
|
||||||
|
TypeVariableOrigin {
|
||||||
|
kind: TypeVariableOriginKind::MiscVariable,
|
||||||
|
span: self.delegate.span(),
|
||||||
|
},
|
||||||
|
self.for_universe,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.in_alias = is_nested_alias;
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
_ => relate::structurally_relate_tys(self, t, t),
|
_ => relate::structurally_relate_tys(self, t, t),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
|
@ -452,12 +504,20 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) struct HandleProjection<T>(T);
|
||||||
|
impl<T> HandleProjection<T> {
|
||||||
|
pub(super) fn may_be_infer(self) -> T {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Result from a generalization operation. This includes
|
/// Result from a generalization operation. This includes
|
||||||
/// not only the generalized type, but also a bool flag
|
/// not only the generalized type, but also a bool flag
|
||||||
/// indicating whether further WF checks are needed.
|
/// indicating whether further WF checks are needed.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Generalization<T> {
|
pub(super) struct Generalization<T> {
|
||||||
pub value: T,
|
pub(super) value: HandleProjection<T>,
|
||||||
|
|
||||||
/// If true, then the generalized type may not be well-formed,
|
/// If true, then the generalized type may not be well-formed,
|
||||||
/// even if the source type is well-formed, so we should add an
|
/// even if the source type is well-formed, so we should add an
|
||||||
|
@ -484,5 +544,5 @@ pub struct Generalization<T> {
|
||||||
/// will force the calling code to check that `WF(Foo<?C, ?D>)`
|
/// will force the calling code to check that `WF(Foo<?C, ?D>)`
|
||||||
/// holds, which in turn implies that `?C::Item == ?D`. So once
|
/// holds, which in turn implies that `?C::Item == ?D`. So once
|
||||||
/// `?C` is constrained, that should suffice to restrict `?D`.
|
/// `?C` is constrained, that should suffice to restrict `?D`.
|
||||||
pub needs_wf: bool,
|
pub(super) needs_wf: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,13 +214,18 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generalize(&mut self, ty: Ty<'tcx>, for_vid: ty::TyVid) -> RelateResult<'tcx, Ty<'tcx>> {
|
fn generalize(&mut self, ty: Ty<'tcx>, for_vid: ty::TyVid) -> RelateResult<'tcx, Ty<'tcx>> {
|
||||||
let Generalization { value: ty, needs_wf: _ } = generalize::generalize(
|
let Generalization { value, needs_wf: _ } = generalize::generalize(
|
||||||
self.infcx,
|
self.infcx,
|
||||||
&mut self.delegate,
|
&mut self.delegate,
|
||||||
ty,
|
ty,
|
||||||
for_vid,
|
for_vid,
|
||||||
self.ambient_variance,
|
self.ambient_variance,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let ty = value.may_be_infer();
|
||||||
|
if ty.is_ty_var() {
|
||||||
|
warn!("occurs check failure in MIR typeck");
|
||||||
|
}
|
||||||
Ok(ty)
|
Ok(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// check-pass
|
||||||
// compile-flags: -Ztrait-solver=next
|
// compile-flags: -Ztrait-solver=next
|
||||||
|
|
||||||
trait Test {
|
trait Test {
|
||||||
|
@ -22,7 +23,9 @@ fn main() {
|
||||||
let mut x: Inv<_> = Inv(None);
|
let mut x: Inv<_> = Inv(None);
|
||||||
// This ends up equating `Inv<?x>` with `Inv<<?x as Test>::Assoc>`
|
// This ends up equating `Inv<?x>` with `Inv<<?x as Test>::Assoc>`
|
||||||
// which fails the occurs check when generalizing `?x`.
|
// which fails the occurs check when generalizing `?x`.
|
||||||
|
//
|
||||||
|
// We end up emitting a delayed obligation, causing this to still
|
||||||
|
// succeed.
|
||||||
x = transform(x);
|
x = transform(x);
|
||||||
//~^ ERROR mismatched types
|
|
||||||
x = Inv::<i32>(None);
|
x = Inv::<i32>(None);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
error[E0308]: mismatched types
|
|
||||||
--> $DIR/equating-projection-cyclically.rs:25:9
|
|
||||||
|
|
|
||||||
LL | x = transform(x);
|
|
||||||
| ^^^^^^^^^^^^ cyclic type of infinite size
|
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0308`.
|
|
Loading…
Add table
Add a link
Reference in a new issue