diff options
| author | lcnr <rust@lcnr.de> | 2023-10-23 16:49:44 +0200 |
|---|---|---|
| committer | lcnr <rust@lcnr.de> | 2023-12-04 10:39:00 +0100 |
| commit | f69d67221e8333e064620279b46c7c9bcaa88310 (patch) | |
| tree | 4614bf2e568b95a3606cb0542d6657475066a140 | |
| parent | 2d0ec174e4d9e44347ad121200b8a24f5fd1dc60 (diff) | |
| download | rust-f69d67221e8333e064620279b46c7c9bcaa88310.tar.gz rust-f69d67221e8333e064620279b46c7c9bcaa88310.zip | |
generalize: handle occurs check failure in aliases
5 files changed, 124 insertions, 33 deletions
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index c3d07415bb8..862896e0404 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -334,6 +334,10 @@ impl<'tcx> InferCtxt<'tcx> { 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( target_vid, ConstVarValue { @@ -445,7 +449,7 @@ 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 { value: b_ty, needs_wf } = generalize::generalize( + let Generalization { value, needs_wf } = generalize::generalize( self.infcx, &mut CombineDelegate { infcx: self.infcx, @@ -457,7 +461,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { 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); if needs_wf { @@ -477,19 +481,47 @@ 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 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)") + 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 { + 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(()) } diff --git a/compiler/rustc_infer/src/infer/generalize.rs b/compiler/rustc_infer/src/infer/generalize.rs index 05037611262..ba0fa2ff2ae 100644 --- a/compiler/rustc_infer/src/infer/generalize.rs +++ b/compiler/rustc_infer/src/infer/generalize.rs @@ -1,13 +1,16 @@ +use std::mem; + 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_middle::ty::visit::MaxUniverse; +use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; use rustc_span::Span; 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}; /// 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, for_universe, root_term: term.into(), + in_alias: false, needs_wf: false, 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()); let value = generalizer.relate(term, term)?; 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 /// in the generalizer code. -pub trait GeneralizerDelegate<'tcx> { +pub(super) trait GeneralizerDelegate<'tcx> { fn param_env(&self) -> ty::ParamEnv<'tcx>; fn forbid_inference_vars() -> bool; + fn span(&self) -> Span; + 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 param_env: ty::ParamEnv<'tcx>, pub span: Span, @@ -73,6 +79,10 @@ impl<'tcx> GeneralizerDelegate<'tcx> for CombineDelegate<'_, 'tcx> { false } + fn span(&self) -> Span { + self.span + } + 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. @@ -93,6 +103,10 @@ where <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> { <Self as TypeRelatingDelegate<'tcx>>::generalize_existential(self, universe) } @@ -139,6 +153,12 @@ struct Generalizer<'me, 'tcx, D> { 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`. 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), }?; @@ -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 /// 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, +pub(super) struct Generalization<T> { + pub(super) value: HandleProjection<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 @@ -484,5 +544,5 @@ pub struct Generalization<T> { /// 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, + pub(super) needs_wf: bool, } diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index 77c1d6a7313..b0b99854cc6 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -214,13 +214,18 @@ where } 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, &mut self.delegate, ty, for_vid, self.ambient_variance, )?; + + let ty = value.may_be_infer(); + if ty.is_ty_var() { + warn!("occurs check failure in MIR typeck"); + } Ok(ty) } diff --git a/tests/ui/traits/new-solver/equating-projection-cyclically.rs b/tests/ui/traits/new-solver/equating-projection-cyclically.rs index 2668da1b745..845597e9ce1 100644 --- a/tests/ui/traits/new-solver/equating-projection-cyclically.rs +++ b/tests/ui/traits/new-solver/equating-projection-cyclically.rs @@ -1,3 +1,4 @@ +// check-pass // compile-flags: -Ztrait-solver=next trait Test { @@ -22,7 +23,9 @@ fn main() { let mut x: Inv<_> = Inv(None); // This ends up equating `Inv<?x>` with `Inv<<?x as Test>::Assoc>` // which fails the occurs check when generalizing `?x`. + // + // We end up emitting a delayed obligation, causing this to still + // succeed. x = transform(x); - //~^ ERROR mismatched types x = Inv::<i32>(None); } diff --git a/tests/ui/traits/new-solver/equating-projection-cyclically.stderr b/tests/ui/traits/new-solver/equating-projection-cyclically.stderr deleted file mode 100644 index 91dd3ebc31b..00000000000 --- a/tests/ui/traits/new-solver/equating-projection-cyclically.stderr +++ /dev/null @@ -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`. |
