From ac88926ae2008c55484abf573ce78566f41b917b Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Sat, 21 Mar 2026 12:15:34 +0100 Subject: [PATCH 1/2] Properly generalize unevaluated consts --- .../src/type_check/relate_tys.rs | 4 +- compiler/rustc_infer/src/infer/mod.rs | 16 +- .../src/infer/relate/generalize.rs | 344 +++++++++++------- .../rustc_infer/src/infer/relate/lattice.rs | 2 +- .../src/infer/relate/type_relating.rs | 4 +- compiler/rustc_infer/src/infer/unify_key.rs | 7 + compiler/rustc_middle/src/hooks/mod.rs | 4 +- compiler/rustc_middle/src/ty/consts.rs | 11 + .../src/solve/eval_ctxt/mod.rs | 9 +- compiler/rustc_trait_selection/src/solve.rs | 12 +- compiler/rustc_type_ir/src/relate.rs | 15 + compiler/rustc_type_ir/src/relate/combine.rs | 2 +- .../src/relate/solver_relating.rs | 12 +- .../occurs-check/associated-type.next.stderr | 4 +- .../occurs-check/associated-type.old.stderr | 4 +- .../mgca/free-type-alias-recursive.rs | 13 + .../mgca/free-type-alias-recursive.stderr | 15 + .../ogca/coherence-ambiguous.rs | 7 +- .../ogca/coherence-ambiguous.stderr | 20 + .../structually-relate-aliases.stderr | 2 +- .../issue-118950-root-region.stderr | 2 +- 21 files changed, 334 insertions(+), 175 deletions(-) create mode 100644 tests/ui/const-generics/mgca/free-type-alias-recursive.rs create mode 100644 tests/ui/const-generics/mgca/free-type-alias-recursive.stderr create mode 100644 tests/ui/const-generics/ogca/coherence-ambiguous.stderr diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index 152a7674490c7..6252552e25075 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -617,8 +617,8 @@ impl<'b, 'tcx> PredicateEmittingRelation> for NllTypeRelating<'_ })]); } - fn try_eagerly_normalize_alias(&mut self, _alias: ty::AliasTy<'tcx>) -> Ty<'tcx> { + fn try_eagerly_normalize_alias(&mut self, alias: ty::AliasTerm<'tcx>) -> ty::Term<'tcx> { // Past hir typeck, so we don't have to worry about type inference anymore. - self.type_checker.infcx.next_ty_var(self.span()) + self.type_checker.infcx.next_term_var_of_alias_kind(alias, self.span()) } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index e7082f961d0b1..4de11820a38b9 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -855,6 +855,18 @@ impl<'tcx> InferCtxt<'tcx> { } } + pub fn next_term_var_of_alias_kind( + &self, + alias: ty::AliasTerm<'tcx>, + span: Span, + ) -> ty::Term<'tcx> { + if alias.kind(self.tcx).is_type() { + self.next_ty_var(span).into() + } else { + self.next_const_var(span).into() + } + } + /// Return the universe that the region `r` was created in. For /// most regions (e.g., `'static`, named regions from the user, /// etc) this is the root universe U0. For inference variables or @@ -1533,8 +1545,8 @@ impl<'tcx> InferCtxt<'tcx> { &'a self, param_env: ty::ParamEnv<'tcx>, span: Span, - alias: ty::AliasTy<'tcx>, - ) -> Ty<'tcx> { + alias: ty::AliasTerm<'tcx>, + ) -> Term<'tcx> { let erased = unsafe { mem::transmute::<&'a InferCtxt<'tcx>, TypeErasedInfcx<'a, 'tcx>>(self) }; self.tcx.try_eagerly_normalize_alias(erased, param_env, span, alias) diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index b2d591327fea2..f4353e2155704 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -37,6 +37,13 @@ impl From for TermVid { } } +fn term_vid<'tcx>(term: Term<'tcx>) -> Option { + match term.kind() { + rustc_type_ir::TermKind::Ty(ty) => ty.ty_vid().map(TermVid::Ty), + rustc_type_ir::TermKind::Const(ct) => ct.ct_vid().map(TermVid::Const), + } +} + impl<'tcx> InferCtxt<'tcx> { /// The idea is that we should ensure that the type variable `target_vid` /// is equal to, a subtype of, or a supertype of `source_ty`. @@ -61,16 +68,95 @@ impl<'tcx> InferCtxt<'tcx> { ) -> RelateResult<'tcx, ()> { debug_assert!(self.inner.borrow_mut().type_variables().probe(target_vid).is_unknown()); - let generalized_ty = if self.next_trait_solver() + self.instantiate_var( + relation, + target_is_expected, + target_vid.into(), + instantiation_variance, + source_ty.into(), + ) + } + + /// Instantiates the const variable `target_vid` with the given constant. + /// + /// This also tests if the given const `ct` contains an inference variable which was previously + /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct` + /// would result in an infinite type as we continuously replace an inference variable + /// in `ct` with `ct` itself. + /// + /// This is especially important as unevaluated consts use their parents generics. + /// They therefore often contain unused args, making these errors far more likely. + /// + /// A good example of this is the following: + /// + /// ```compile_fail,E0308 + /// #![feature(generic_const_exprs)] + /// + /// fn bind(value: [u8; N]) -> [u8; 3 + 4] { + /// todo!() + /// } + /// + /// fn main() { + /// let mut arr = Default::default(); + /// arr = bind(arr); + /// } + /// ``` + /// + /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics + /// of `fn bind` (meaning that its args contain `N`). + /// + /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`. + /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`. + /// + /// As `3 + 4` contains `N` in its args, this must not succeed. + /// + /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant. + #[instrument(level = "debug", skip(self, relation))] + pub(crate) fn instantiate_const_var>>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: ty::ConstVid, + source_ct: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ()> { + debug_assert!( + self.inner.borrow_mut().const_unification_table().probe_value(target_vid).is_unknown() + ); + + self.instantiate_var( + relation, + target_is_expected, + target_vid.into(), + ty::Invariant, + source_ct.into(), + ) + } + + #[instrument(level = "debug", skip(self, relation))] + fn instantiate_var>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: TermVid, + instantiation_variance: ty::Variance, + source_term: Term<'tcx>, + ) -> RelateResult<'tcx, ()> { + // Hack: Fall back to old behavior if GCE is enabled. I expect GCE to be ripped out soon so + // this shouldn't matter soon. + let gce_const_fallback = + self.tcx.features().generic_const_exprs() && matches!(target_vid, TermVid::Const(_)); + + let generalized_term = if self.next_trait_solver() + && !gce_const_fallback && matches!(relation.structurally_relate_aliases(), StructurallyRelateAliases::No) - && let ty::Alias(_, alias) = source_ty.kind() + && let Some(alias) = source_term.to_alias_term() { - let normalized_alias = relation.try_eagerly_normalize_alias(*alias); + let normalized_alias = relation.try_eagerly_normalize_alias(alias); - if normalized_alias.is_ty_var() { + if term_vid(normalized_alias).is_some() { normalized_alias } else { - let Generalization { value_may_be_infer: generalized_ty } = self.generalize( + let Generalization { value_may_be_infer: generalized_term } = self.generalize( relation.span(), GeneralizerState::ShallowStructurallyRelateAliases, target_vid, @@ -83,9 +169,9 @@ impl<'tcx> InferCtxt<'tcx> { // However, here, though we know it *is* an alias, we initialize the generalizer // with `ShallowStructurallyRelateAliases` so we treat the outermost alias as rigid, // ensuring this is never a tyvar. - assert!(!generalized_ty.is_ty_var()); + assert!(term_vid(generalized_term).is_none()); - generalized_ty + generalized_term } } else { // Generalize `source_ty` depending on the current variance. As an example, assume @@ -97,7 +183,7 @@ impl<'tcx> InferCtxt<'tcx> { // // We then relate `generalized_ty <: source_ty`, adding constraints like `'x: '?2` and // `?1 <: ?3`. - let Generalization { value_may_be_infer: generalized_ty } = self.generalize( + let Generalization { value_may_be_infer: generalized_term } = self.generalize( relation.span(), match relation.structurally_relate_aliases() { StructurallyRelateAliases::No => GeneralizerState::Default, @@ -105,11 +191,11 @@ impl<'tcx> InferCtxt<'tcx> { }, target_vid, instantiation_variance, - source_ty, + source_term, &mut |alias| relation.try_eagerly_normalize_alias(alias), )?; - generalized_ty + generalized_term }; // Finally, relate `generalized_ty` to `source_ty`, as described in previous comment. @@ -118,9 +204,9 @@ impl<'tcx> InferCtxt<'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. - if let &ty::Infer(ty::TyVar(generalized_vid)) = generalized_ty.kind() { + if let Some(generalized_vid) = term_vid(generalized_term) { // Constrain `b_vid` to the generalized type variable. - self.inner.borrow_mut().type_variables().equate(target_vid, generalized_vid); + self.equate(target_vid, generalized_vid); // This happens for cases like `::Assoc == ?0`. // We can't instantiate `?0` here as that would result in a @@ -130,40 +216,47 @@ impl<'tcx> InferCtxt<'tcx> { if self.next_trait_solver() { let (lhs, rhs, direction) = match instantiation_variance { ty::Invariant => { - (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Equate) + (generalized_term, source_term, AliasRelationDirection::Equate) } ty::Covariant => { - (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Subtype) + (generalized_term, source_term, AliasRelationDirection::Subtype) } ty::Contravariant => { - (source_ty.into(), generalized_ty.into(), AliasRelationDirection::Subtype) + (source_term, generalized_term, AliasRelationDirection::Subtype) } ty::Bivariant => unreachable!("bivariant generalization"), }; relation.register_predicates([ty::PredicateKind::AliasRelate(lhs, rhs, direction)]); } else { - match source_ty.kind() { - &ty::Alias(ty::Projection, data) => { - // FIXME: This does not handle subtyping correctly, we could - // instead create a new inference variable `?normalized_source`, emitting - // `Projection(normalized_source, ?ty_normalized)` and - // `?normalized_source <: generalized_ty`. - relation.register_predicates([ty::ProjectionPredicate { - projection_term: data.into(), - term: generalized_ty.into(), - }]); + match source_term.kind() { + ty::TermKind::Ty(source_ty) => { + match source_ty.kind() { + &ty::Alias(ty::Projection, data) => { + // FIXME: This does not handle subtyping correctly, we could instead + // create a new inference variable `?normalized_source`, emitting + // `Projection(normalized_source, ?ty_normalized)` and + // `?normalized_source <: generalized_ty`. + relation.register_predicates([ty::ProjectionPredicate { + projection_term: data.into(), + term: generalized_term, + }]); + } + // The old solver only accepts projection predicates for associated types. + ty::Alias(ty::Inherent | ty::Free | ty::Opaque, _) => { + return Err(TypeError::CyclicTy(source_ty)); + } + _ => bug!("generalized `{source_ty:?} to infer, not an alias"), + } } - // The old solver only accepts projection predicates for associated types. - ty::Alias(ty::Inherent | ty::Free | ty::Opaque, _) => { - return Err(TypeError::CyclicTy(source_ty)); + ty::TermKind::Const(source_ct) => { + return Err(TypeError::CyclicConst(source_ct)); } - _ => bug!("generalized `{source_ty:?} to infer, not an alias"), } } } else { // Constrain `b_vid` to the generalized type `generalized_ty`. - self.inner.borrow_mut().type_variables().instantiate(target_vid, generalized_ty); + self.instantiate(target_vid, generalized_term); // NOTE: The `instantiation_variance` is not the same variance as // used by the relation. When instantiating `b`, `target_is_expected` @@ -182,114 +275,78 @@ impl<'tcx> InferCtxt<'tcx> { // instantiate_ty_var(?b, A) # expected and variance flipped // A rel A' // ``` - if target_is_expected { - relation.relate(generalized_ty, source_ty)?; - } else { - debug!("flip relation"); - relation.relate(source_ty, generalized_ty)?; + match generalized_term.kind() { + ty::TermKind::Ty(_) => { + if target_is_expected { + relation.relate(generalized_term, source_term)?; + } else { + debug!("flip relation"); + relation.relate(source_term, generalized_term)?; + } + } + ty::TermKind::Const(_) => { + // Override consts to always be invariant + if target_is_expected { + relation.relate_with_variance( + ty::Invariant, + ty::VarianceDiagInfo::default(), + generalized_term, + source_term, + )?; + } else { + relation.relate_with_variance( + ty::Invariant, + ty::VarianceDiagInfo::default(), + source_term, + generalized_term, + )?; + } + } } } Ok(()) } - /// Instantiates the const variable `target_vid` with the given constant. - /// - /// This also tests if the given const `ct` contains an inference variable which was previously - /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct` - /// would result in an infinite type as we continuously replace an inference variable - /// in `ct` with `ct` itself. - /// - /// This is especially important as unevaluated consts use their parents generics. - /// They therefore often contain unused args, making these errors far more likely. - /// - /// A good example of this is the following: - /// - /// ```compile_fail,E0308 - /// #![feature(generic_const_exprs)] - /// - /// fn bind(value: [u8; N]) -> [u8; 3 + 4] { - /// todo!() - /// } - /// - /// fn main() { - /// let mut arr = Default::default(); - /// arr = bind(arr); - /// } - /// ``` - /// - /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics - /// of `fn bind` (meaning that its args contain `N`). - /// - /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`. - /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`. - /// - /// As `3 + 4` contains `N` in its args, this must not succeed. - /// - /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant. - #[instrument(level = "debug", skip(self, relation))] - pub(crate) fn instantiate_const_var>>( - &self, - relation: &mut R, - target_is_expected: bool, - target_vid: ty::ConstVid, - source_ct: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ()> { - // FIXME(generic_const_exprs): Occurs check failures for unevaluated - // constants and generic expressions are not yet handled correctly. - let Generalization { value_may_be_infer: generalized_ct } = self.generalize( - relation.span(), - match relation.structurally_relate_aliases() { - StructurallyRelateAliases::No => GeneralizerState::Default, - StructurallyRelateAliases::Yes => GeneralizerState::StructurallyRelateAliases, - }, - target_vid, - ty::Invariant, - source_ct, - &mut |alias| relation.try_eagerly_normalize_alias(alias), - )?; - - debug_assert!(!generalized_ct.is_ct_infer()); - - self.inner - .borrow_mut() - .const_unification_table() - .union_value(target_vid, ConstVariableValue::Known { value: generalized_ct }); - - // Make sure that the order is correct when relating the - // generalized const and the source. - if target_is_expected { - relation.relate_with_variance( - ty::Invariant, - ty::VarianceDiagInfo::default(), - generalized_ct, - source_ct, - )?; - } else { - relation.relate_with_variance( - ty::Invariant, - ty::VarianceDiagInfo::default(), - source_ct, - generalized_ct, - )?; + fn equate(&self, l: TermVid, r: TermVid) { + match (l, r) { + (TermVid::Ty(l), TermVid::Ty(r)) => { + self.inner.borrow_mut().type_variables().equate(l, r) + } + (TermVid::Const(l), TermVid::Const(r)) => { + self.inner.borrow_mut().const_unification_table().union(l, r) + } + _ => bug!("mismatched term kinds in generalize: {l:?}, {r:?}"), } + } - Ok(()) + fn instantiate(&self, l: TermVid, r: ty::Term<'tcx>) { + match (l, r.kind()) { + (TermVid::Ty(l), ty::TermKind::Ty(r)) => { + self.inner.borrow_mut().type_variables().instantiate(l, r) + } + (TermVid::Const(l), ty::TermKind::Const(r)) => self + .inner + .borrow_mut() + .const_unification_table() + .union_value(l, ConstVariableValue::Known { value: r }), + _ => bug!("mismatched term kinds in generalize: {l:?}, {r:?}"), + } } /// Attempts to generalize `source_term` for the type variable `target_vid`. /// This checks for cycles -- that is, whether `source_term` references `target_vid`. - fn generalize> + Relate>>( + fn generalize( &self, span: Span, initial_state: GeneralizerState, - target_vid: impl Into, + target_vid: TermVid, ambient_variance: ty::Variance, - source_term: T, - normalize: &mut dyn FnMut(ty::AliasTy<'tcx>) -> Ty<'tcx>, - ) -> RelateResult<'tcx, Generalization> { + source_term: Term<'tcx>, + normalize: &mut dyn FnMut(ty::AliasTerm<'tcx>) -> Term<'tcx>, + ) -> RelateResult<'tcx, Generalization>> { assert!(!source_term.has_escaping_bound_vars()); - let (for_universe, root_vid) = match target_vid.into() { + let (for_universe, root_vid) = match target_vid { TermVid::Ty(ty_vid) => { (self.probe_ty_var(ty_vid).unwrap_err(), TermVid::Ty(self.root_var(ty_vid))) } @@ -304,7 +361,7 @@ impl<'tcx> InferCtxt<'tcx> { span, root_vid, for_universe, - root_term: source_term.into(), + root_term: source_term, ambient_variance, state: initial_state, cache: Default::default(), @@ -448,7 +505,7 @@ struct Generalizer<'me, 'tcx> { /// Normalize an alias in the trait solver. /// If normalization fails, a fresh infer var is returned. - normalize: &'me mut dyn FnMut(ty::AliasTy<'tcx>) -> Ty<'tcx>, + normalize: &'me mut dyn FnMut(ty::AliasTerm<'tcx>) -> Term<'tcx>, } impl<'tcx> Generalizer<'_, 'tcx> { @@ -462,8 +519,12 @@ impl<'tcx> Generalizer<'_, 'tcx> { /// Create a new type variable in the universe of the target when /// generalizing an alias. - fn next_ty_var_for_alias(&self) -> Ty<'tcx> { - self.infcx.next_ty_var_in_universe(self.span, self.for_universe) + fn next_var_for_alias_of_kind(&self, alias: ty::AliasTerm<'tcx>) -> ty::Term<'tcx> { + if alias.kind(self.cx()).is_type() { + self.infcx.next_ty_var_in_universe(self.span, self.for_universe).into() + } else { + self.infcx.next_const_var_in_universe(self.span, self.for_universe).into() + } } /// An occurs check failure inside of an alias does not mean @@ -484,21 +545,21 @@ impl<'tcx> Generalizer<'_, 'tcx> { /// continue generalizing the alias. This ends up pulling down the universe of the /// inference variable and is incomplete in case the alias would normalize to a type /// which does not mention that inference variable. - fn handle_alias_ty( + fn handle_alias_term( &mut self, - alias_ty: Ty<'tcx>, - alias: ty::AliasTy<'tcx>, - ) -> Result, TypeError<'tcx>> { + alias_ty: Term<'tcx>, + alias: ty::AliasTerm<'tcx>, + ) -> Result, TypeError<'tcx>> { match self.state { GeneralizerState::ShallowStructurallyRelateAliases => { // We can switch back to default, we've treated one layer as rigid by doing this operation. self.state = GeneralizerState::Default; - let res = relate::structurally_relate_tys(self, alias_ty, alias_ty); + let res = relate::structurally_relate_terms(self, alias_ty, alias_ty); self.state = GeneralizerState::ShallowStructurallyRelateAliases; return res; } GeneralizerState::StructurallyRelateAliases => { - return relate::structurally_relate_tys(self, alias_ty, alias_ty); + return relate::structurally_relate_terms(self, alias_ty, alias_ty); } GeneralizerState::Default if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() => @@ -526,7 +587,7 @@ impl<'tcx> Generalizer<'_, 'tcx> { let previous_state = mem::replace(&mut self.state, GeneralizerState::IncompletelyRelateAliasArgs); let result = match self.relate(alias, alias) { - Ok(alias) => Ok(alias.to_ty(self.cx())), + Ok(alias) => Ok(alias.to_term(self.cx())), Err(e) => match previous_state { GeneralizerState::Default => { let mut visitor = MaxUniverse::new(); @@ -539,7 +600,7 @@ impl<'tcx> Generalizer<'_, 'tcx> { } debug!("generalization failure in alias"); - Ok(self.next_ty_var_for_alias()) + Ok(self.next_var_for_alias_of_kind(alias)) } GeneralizerState::IncompletelyRelateAliasArgs => return Err(e), @@ -705,7 +766,9 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { } } - ty::Alias(_, data) => self.handle_alias_ty(t, data), + ty::Alias(_, data) => { + self.handle_alias_term(t.into(), data.into()).map(|v| v.expect_type()) + } _ => relate::structurally_relate_tys(self, t, t), }?; @@ -816,12 +879,10 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { } } } - // FIXME: Unevaluated constants are also not rigid, so the current - // approach of always relating them structurally is incomplete. - // - // FIXME: remove this branch once `structurally_relate_consts` is fully - // structural. - ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => { + // Hack: Fall back to old behavior if GCE is enabled. I expect GCE to be ripped out soon + // so this shouldn't matter soon. + ty::ConstKind::Unevaluated(uv) if self.infcx.tcx.features().generic_const_exprs() => { + let ty::UnevaluatedConst { def, args } = uv; let args = self.relate_with_variance( ty::Invariant, ty::VarianceDiagInfo::default(), @@ -830,6 +891,9 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { )?; Ok(ty::Const::new_unevaluated(self.cx(), ty::UnevaluatedConst { def, args })) } + ty::ConstKind::Unevaluated(uv) => { + self.handle_alias_term(c.into(), uv.into()).map(|v| v.expect_const()) + } ty::ConstKind::Placeholder(placeholder) => { if self.for_universe.can_name(placeholder.universe) { Ok(c) diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index 7e480df7dda63..5c84c898d204e 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -300,7 +300,7 @@ impl<'tcx> PredicateEmittingRelation> for LatticeOp<'_, 'tcx> { ))]); } - fn try_eagerly_normalize_alias(&mut self, alias: ty::AliasTy<'tcx>) -> Ty<'tcx> { + fn try_eagerly_normalize_alias(&mut self, alias: ty::AliasTerm<'tcx>) -> ty::Term<'tcx> { self.infcx.try_eagerly_normalize_alias(self.param_env(), self.span(), alias) } } diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index 67f9dc69a4a65..cbbc52e9aeff7 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -399,8 +399,8 @@ impl<'tcx> PredicateEmittingRelation> for TypeRelating<'_, 'tcx> fn try_eagerly_normalize_alias( &mut self, - _alias: rustc_type_ir::AliasTy< as rustc_type_ir::InferCtxtLike>::Interner>, - ) -> < as rustc_type_ir::InferCtxtLike>::Interner as rustc_type_ir::Interner>::Ty + _alias: rustc_type_ir::AliasTerm< as rustc_type_ir::InferCtxtLike>::Interner>, + ) -> < as rustc_type_ir::InferCtxtLike>::Interner as rustc_type_ir::Interner>::Term { // We only try to eagerly normalize aliases if we're using the new solver. unreachable!() diff --git a/compiler/rustc_infer/src/infer/unify_key.rs b/compiler/rustc_infer/src/infer/unify_key.rs index 5e5d0e063a0d7..4016054d8974e 100644 --- a/compiler/rustc_infer/src/infer/unify_key.rs +++ b/compiler/rustc_infer/src/infer/unify_key.rs @@ -110,6 +110,13 @@ impl<'tcx> ConstVariableValue<'tcx> { ConstVariableValue::Known { value } => Some(value), } } + + pub(crate) fn is_unknown(&self) -> bool { + match *self { + ConstVariableValue::Unknown { .. } => true, + ConstVariableValue::Known { .. } => false, + } + } } #[derive(PartialEq, Copy, Clone, Debug)] diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index 1f339ea0cabf6..7ae68ab3104b8 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -126,8 +126,8 @@ declare_hooks! { type_erased_infcx: TypeErasedInfcx<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, span: Span, - alias: ty::AliasTy<'tcx> - ) -> Ty<'tcx>; + alias: ty::AliasTerm<'tcx> + ) -> ty::Term<'tcx>; } /// The `try_eagerly_normalize_alias` hook passes an `Infcx` from where it's called (in `rustc_infer`) diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index aade274bfc98c..f67ac20c4cf33 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -323,6 +323,17 @@ impl<'tcx> Const<'tcx> { matches!(self.kind(), ty::ConstKind::Infer(_)) } + pub fn is_ct_var(self) -> bool { + matches!(self.kind(), ConstKind::Infer(ty::InferConst::Var(_))) + } + + pub fn ct_vid(self) -> Option { + match self.kind() { + ConstKind::Infer(ty::InferConst::Var(vid)) => Some(vid), + _ => None, + } + } + /// Iterator that walks `self` and any types reachable from /// `self`, in depth-first order. Note that just walks the types /// that appear in `self`, it does not descend into the fields of diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index fedb6390d9588..a9e1e02f85b6e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1051,15 +1051,16 @@ where let delegate = self.delegate; let origin_span = self.origin_span; - let mut normalize = |alias: ty::AliasTy| { - let inference_var = self.next_ty_infer(); + let mut normalize = |alias: ty::AliasTerm| { + let term = alias.to_term(cx); + let inference_var = self.next_term_infer_of_kind(term); let goal = Goal::new( cx, param_env, ty::PredicateKind::AliasRelate( - alias.to_ty(cx).into(), - inference_var.into(), + term, + inference_var, ty::AliasRelationDirection::Equate, ), ); diff --git a/compiler/rustc_trait_selection/src/solve.rs b/compiler/rustc_trait_selection/src/solve.rs index c7699f31a0f95..6f4125710fa44 100644 --- a/compiler/rustc_trait_selection/src/solve.rs +++ b/compiler/rustc_trait_selection/src/solve.rs @@ -18,7 +18,7 @@ pub use normalize::{ deeply_normalize, deeply_normalize_with_skipped_universes, deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals, }; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; use rustc_span::Span; pub use select::InferCtxtSelectExt; @@ -40,15 +40,15 @@ fn try_eagerly_normalize_alias<'a, 'tcx>( type_erased_infcx: TypeErasedInfcx<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, span: Span, - alias: ty::AliasTy<'tcx>, -) -> Ty<'tcx> { + alias: ty::AliasTerm<'tcx>, +) -> ty::Term<'tcx> { let infcx = unsafe { mem::transmute::, &'a InferCtxt<'tcx>>(type_erased_infcx) }; let ocx = ObligationCtxt::new(infcx); - let infer_term = infcx.next_ty_var(span); + let infer_term = infcx.next_term_var_of_alias_kind(alias, span); // Dummy because we ignore the error anyway. // We do provide a span, because this span is used when registering opaque types. @@ -59,8 +59,8 @@ fn try_eagerly_normalize_alias<'a, 'tcx>( cause, param_env, ty::PredicateKind::AliasRelate( - alias.to_ty(tcx).into(), - infer_term.into(), + alias.to_term(tcx), + infer_term, ty::AliasRelationDirection::Equate, ), ); diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index d33c6036dadd8..9d6685b7c45bf 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -326,6 +326,21 @@ impl Relate for ty::ExistentialTraitRef { } } +#[instrument(level = "trace", skip(relation), ret)] +pub fn structurally_relate_terms>( + relation: &mut R, + a: I::Term, + b: I::Term, +) -> RelateResult { + if let (Some(a), Some(b)) = (a.as_type(), b.as_type()) { + structurally_relate_tys(relation, a, b).map(|v| v.into()) + } else if let (Some(a), Some(b)) = (a.as_const(), b.as_const()) { + structurally_relate_consts(relation, a, b).map(|v| v.into()) + } else { + panic!("two different kinds passed into structurally_relate_terms: {a:?}, {b:?}") + } +} + /// 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. diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 72d54c23733ee..9031c60ecb03a 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -41,7 +41,7 @@ where /// Register `AliasRelate` obligation(s) that both types must be related to each other. fn register_alias_relate_predicate(&mut self, a: I::Ty, b: I::Ty); - fn try_eagerly_normalize_alias(&mut self, alias: ty::AliasTy) -> I::Ty; + fn try_eagerly_normalize_alias(&mut self, alias: ty::AliasTerm) -> I::Term; } pub fn super_combine_tys( diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs index 541b2531fe749..1aede5e4b0416 100644 --- a/compiler/rustc_type_ir/src/relate/solver_relating.rs +++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs @@ -15,7 +15,9 @@ pub trait RelateExt: InferCtxtLike { variance: ty::Variance, rhs: T, span: ::Span, - normalize: &mut dyn FnMut(ty::AliasTy) -> ::Ty, + normalize: &mut dyn FnMut( + ty::AliasTerm, + ) -> ::Term, ) -> Result< Vec::Predicate>>, TypeError, @@ -41,7 +43,7 @@ impl> RelateExt for Infcx { variance: ty::Variance, rhs: T, span: I::Span, - normalize: &mut dyn FnMut(ty::AliasTy) -> I::Ty, + normalize: &mut dyn FnMut(ty::AliasTerm) -> I::Term, ) -> Result>, TypeError> { let mut relate = SolverRelating::new( self, @@ -89,7 +91,7 @@ pub struct SolverRelating<'infcx, Infcx, I: Interner> { span: I::Span, // Mutable fields. ambient_variance: ty::Variance, - normalize: &'infcx mut dyn FnMut(ty::AliasTy) -> I::Ty, + normalize: &'infcx mut dyn FnMut(ty::AliasTerm) -> I::Term, goals: Vec>, /// The cache only tracks the `ambient_variance` as it's the /// only field which is mutable and which meaningfully changes @@ -127,7 +129,7 @@ where ambient_variance: ty::Variance, param_env: I::ParamEnv, span: I::Span, - normalize: &'infcx mut dyn FnMut(ty::AliasTy) -> I::Ty, + normalize: &'infcx mut dyn FnMut(ty::AliasTerm) -> I::Term, ) -> Self { SolverRelating { infcx, @@ -418,7 +420,7 @@ where })]); } - fn try_eagerly_normalize_alias(&mut self, alias: ty::AliasTy) -> I::Ty { + fn try_eagerly_normalize_alias(&mut self, alias: ty::AliasTerm) -> I::Term { (self.normalize)(alias) } } diff --git a/tests/ui/coherence/occurs-check/associated-type.next.stderr b/tests/ui/coherence/occurs-check/associated-type.next.stderr index 52794b19945b6..381f5c60f9831 100644 --- a/tests/ui/coherence/occurs-check/associated-type.next.stderr +++ b/tests/ui/coherence/occurs-check/associated-type.next.stderr @@ -1,5 +1,5 @@ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1))], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1))], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTerm { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1))], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTerm { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1))], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } error[E0119]: conflicting implementations of trait `Overlap fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())` --> $DIR/associated-type.rs:32:1 | diff --git a/tests/ui/coherence/occurs-check/associated-type.old.stderr b/tests/ui/coherence/occurs-check/associated-type.old.stderr index 9fa443eefb3dc..40811a67dd180 100644 --- a/tests/ui/coherence/occurs-check/associated-type.old.stderr +++ b/tests/ui/coherence/occurs-check/associated-type.old.stderr @@ -1,5 +1,5 @@ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1))], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1))], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTerm { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1))], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTerm { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1))], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } error[E0119]: conflicting implementations of trait `Overlap fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())` --> $DIR/associated-type.rs:32:1 | diff --git a/tests/ui/const-generics/mgca/free-type-alias-recursive.rs b/tests/ui/const-generics/mgca/free-type-alias-recursive.rs new file mode 100644 index 0000000000000..8d75c1a941a77 --- /dev/null +++ b/tests/ui/const-generics/mgca/free-type-alias-recursive.rs @@ -0,0 +1,13 @@ +//! Regression test for +//@ check-fail +//@compile-flags: -Znext-solver=globally --emit=obj +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +type const A: () = A; +//~^ ERROR type mismatch resolving `A normalizes-to _` +//~| ERROR the constant `A` is not of type `()` + +fn main() { + A; +} diff --git a/tests/ui/const-generics/mgca/free-type-alias-recursive.stderr b/tests/ui/const-generics/mgca/free-type-alias-recursive.stderr new file mode 100644 index 0000000000000..df1bb0e8101a6 --- /dev/null +++ b/tests/ui/const-generics/mgca/free-type-alias-recursive.stderr @@ -0,0 +1,15 @@ +error[E0271]: type mismatch resolving `A normalizes-to _` + --> $DIR/free-type-alias-recursive.rs:7:1 + | +LL | type const A: () = A; + | ^^^^^^^^^^^^^^^^ types differ + +error: the constant `A` is not of type `()` + --> $DIR/free-type-alias-recursive.rs:7:1 + | +LL | type const A: () = A; + | ^^^^^^^^^^^^^^^^ expected `()`, found a different `()` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/const-generics/ogca/coherence-ambiguous.rs b/tests/ui/const-generics/ogca/coherence-ambiguous.rs index 21efe4cfe9818..bb20c0457c03d 100644 --- a/tests/ui/const-generics/ogca/coherence-ambiguous.rs +++ b/tests/ui/const-generics/ogca/coherence-ambiguous.rs @@ -1,5 +1,4 @@ -// FIXME(ogca): this should ERROR not pass!! -//@ check-pass +//@ check-fail #![feature(generic_const_items, min_generic_const_args, opaque_generic_const_args)] #![expect(incomplete_features)] @@ -12,8 +11,8 @@ trait Trait {} impl Trait for [(); FOO::<1>] {} impl Trait for [(); BAR::<1>] {} -// FIXME(ogca): this should ERROR! +//~^ ERROR conflicting implementations of trait `Trait` for type `[(); FOO::<1>]` impl Trait for [(); BAR::<2>] {} -// FIXME(ogca): this should ERROR! +//~^ ERROR conflicting implementations of trait `Trait` for type `[(); FOO::<1>]` fn main() {} diff --git a/tests/ui/const-generics/ogca/coherence-ambiguous.stderr b/tests/ui/const-generics/ogca/coherence-ambiguous.stderr new file mode 100644 index 0000000000000..919a0c8d70e94 --- /dev/null +++ b/tests/ui/const-generics/ogca/coherence-ambiguous.stderr @@ -0,0 +1,20 @@ +error[E0119]: conflicting implementations of trait `Trait` for type `[(); FOO::<1>]` + --> $DIR/coherence-ambiguous.rs:13:1 + | +LL | impl Trait for [(); FOO::<1>] {} + | ----------------------------- first implementation here +LL | impl Trait for [(); BAR::<1>] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `[(); FOO::<1>]` + +error[E0119]: conflicting implementations of trait `Trait` for type `[(); FOO::<1>]` + --> $DIR/coherence-ambiguous.rs:15:1 + | +LL | impl Trait for [(); FOO::<1>] {} + | ----------------------------- first implementation here +... +LL | impl Trait for [(); BAR::<2>] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `[(); FOO::<1>]` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/higher-ranked/structually-relate-aliases.stderr b/tests/ui/higher-ranked/structually-relate-aliases.stderr index b27a2dcceb138..f8fcb73db4478 100644 --- a/tests/ui/higher-ranked/structually-relate-aliases.stderr +++ b/tests/ui/higher-ranked/structually-relate-aliases.stderr @@ -1,4 +1,4 @@ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a))], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTerm { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a))], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit), .. } error[E0277]: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied --> $DIR/structually-relate-aliases.rs:13:36 | diff --git a/tests/ui/traits/next-solver/issue-118950-root-region.stderr b/tests/ui/traits/next-solver/issue-118950-root-region.stderr index 74cbb5be02b37..0c524c6b2cc1b 100644 --- a/tests/ui/traits/next-solver/issue-118950-root-region.stderr +++ b/tests/ui/traits/next-solver/issue-118950-root-region.stderr @@ -25,7 +25,7 @@ help: this trait has no implementations, consider adding one LL | trait ToUnit<'a> { | ^^^^^^^^^^^^^^^^ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a)), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), .. } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTerm { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a)), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), .. } error: aborting due to 2 previous errors; 1 warning emitted Some errors have detailed explanations: E0277, E0425. From ee196357df87ceb558f245c02a6d7e9384425fca Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Sat, 21 Mar 2026 16:54:14 +0100 Subject: [PATCH 2/2] Move TermVid to rustc_type_ir --- .../src/infer/relate/generalize.rs | 103 +++++++----------- compiler/rustc_middle/src/ty/consts.rs | 4 - compiler/rustc_middle/src/ty/mod.rs | 7 ++ compiler/rustc_type_ir/src/generic_arg.rs | 13 --- compiler/rustc_type_ir/src/lib.rs | 2 + compiler/rustc_type_ir/src/term.rs | 37 +++++++ 6 files changed, 88 insertions(+), 78 deletions(-) create mode 100644 compiler/rustc_type_ir/src/term.rs diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index f4353e2155704..fbefc04990331 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -6,8 +6,8 @@ use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{ - self, AliasRelationDirection, InferConst, Term, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, TypingMode, + self, AliasRelationDirection, InferConst, Term, TermVid, Ty, TyCtxt, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, }; use rustc_span::Span; use tracing::{debug, instrument, warn}; @@ -19,31 +19,6 @@ use crate::infer::type_variable::TypeVariableValue; use crate::infer::unify_key::ConstVariableValue; use crate::infer::{InferCtxt, RegionVariableOrigin, relate}; -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -enum TermVid { - Ty(ty::TyVid), - Const(ty::ConstVid), -} - -impl From for TermVid { - fn from(value: ty::TyVid) -> Self { - TermVid::Ty(value) - } -} - -impl From for TermVid { - fn from(value: ty::ConstVid) -> Self { - TermVid::Const(value) - } -} - -fn term_vid<'tcx>(term: Term<'tcx>) -> Option { - match term.kind() { - rustc_type_ir::TermKind::Ty(ty) => ty.ty_vid().map(TermVid::Ty), - rustc_type_ir::TermKind::Const(ct) => ct.ct_vid().map(TermVid::Const), - } -} - impl<'tcx> InferCtxt<'tcx> { /// The idea is that we should ensure that the type variable `target_vid` /// is equal to, a subtype of, or a supertype of `source_ty`. @@ -153,7 +128,7 @@ impl<'tcx> InferCtxt<'tcx> { { let normalized_alias = relation.try_eagerly_normalize_alias(alias); - if term_vid(normalized_alias).is_some() { + if normalized_alias.is_infer() { normalized_alias } else { let Generalization { value_may_be_infer: generalized_term } = self.generalize( @@ -169,19 +144,19 @@ impl<'tcx> InferCtxt<'tcx> { // However, here, though we know it *is* an alias, we initialize the generalizer // with `ShallowStructurallyRelateAliases` so we treat the outermost alias as rigid, // ensuring this is never a tyvar. - assert!(term_vid(generalized_term).is_none()); + assert!(!generalized_term.is_infer()); generalized_term } } else { - // Generalize `source_ty` depending on the current variance. As an example, assume + // Generalize `source_term` depending on the current variance. As an example, assume // `?target <: &'x ?1`, where `'x` is some free region and `?1` is an inference // variable. // - // Then the `generalized_ty` would be `&'?2 ?3`, where `'?2` and `?3` are fresh + // Then the `generalized_term` would be `&'?2 ?3`, where `'?2` and `?3` are fresh // region/type inference variables. // - // We then relate `generalized_ty <: source_ty`, adding constraints like `'x: '?2` and + // We then relate `generalized_term <: source_term`, adding constraints like `'x: '?2` and // `?1 <: ?3`. let Generalization { value_may_be_infer: generalized_term } = self.generalize( relation.span(), @@ -198,15 +173,15 @@ impl<'tcx> InferCtxt<'tcx> { generalized_term }; - // Finally, relate `generalized_ty` to `source_ty`, as described in previous comment. + // Finally, relate `generalized_term` to `source_term`, as described in previous comment. // // FIXME(#16847): This code is non-ideal because all these subtype // 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. - if let Some(generalized_vid) = term_vid(generalized_term) { + if let Some(generalized_vid) = generalized_term.term_vid() { // Constrain `b_vid` to the generalized type variable. - self.equate(target_vid, generalized_vid); + self.union_vars(target_vid, generalized_vid); // This happens for cases like `::Assoc == ?0`. // We can't instantiate `?0` here as that would result in a @@ -229,39 +204,41 @@ impl<'tcx> InferCtxt<'tcx> { relation.register_predicates([ty::PredicateKind::AliasRelate(lhs, rhs, direction)]); } else { - match source_term.kind() { - ty::TermKind::Ty(source_ty) => { - match source_ty.kind() { - &ty::Alias(ty::Projection, data) => { - // FIXME: This does not handle subtyping correctly, we could instead - // create a new inference variable `?normalized_source`, emitting - // `Projection(normalized_source, ?ty_normalized)` and - // `?normalized_source <: generalized_ty`. - relation.register_predicates([ty::ProjectionPredicate { - projection_term: data.into(), - term: generalized_term, - }]); - } - // The old solver only accepts projection predicates for associated types. - ty::Alias(ty::Inherent | ty::Free | ty::Opaque, _) => { - return Err(TypeError::CyclicTy(source_ty)); - } - _ => bug!("generalized `{source_ty:?} to infer, not an alias"), - } + let Some(source_alias) = source_term.to_alias_term() else { + bug!("generalized `{source_term:?} to infer, not an alias"); + }; + match source_alias.kind(self.tcx) { + ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => { + // FIXME: This does not handle subtyping correctly, we could + // instead create a new inference variable `?normalized_source`, emitting + // `Projection(normalized_source, ?ty_normalized)` and + // `?normalized_source <: generalized_term`. + relation.register_predicates([ty::ProjectionPredicate { + projection_term: source_alias, + term: generalized_term, + }]); + } + // The old solver only accepts projection predicates for associated types. + ty::AliasTermKind::InherentTy + | ty::AliasTermKind::FreeTy + | ty::AliasTermKind::OpaqueTy => { + return Err(TypeError::CyclicTy(source_term.expect_type())); } - ty::TermKind::Const(source_ct) => { - return Err(TypeError::CyclicConst(source_ct)); + ty::AliasTermKind::InherentConst + | ty::AliasTermKind::FreeConst + | ty::AliasTermKind::UnevaluatedConst => { + return Err(TypeError::CyclicConst(source_term.expect_const())); } } } } else { - // Constrain `b_vid` to the generalized type `generalized_ty`. - self.instantiate(target_vid, generalized_term); + // Constrain `b_vid` to the generalized type `generalized_term`. + self.union_var_term(target_vid, generalized_term); // NOTE: The `instantiation_variance` is not the same variance as // used by the relation. When instantiating `b`, `target_is_expected` // is flipped and the `instantiation_variance` is also flipped. To - // constrain the `generalized_ty` while using the original relation, + // constrain the `generalized_term` while using the original relation, // we therefore only have to flip the arguments. // // ```ignore (not code) @@ -308,7 +285,9 @@ impl<'tcx> InferCtxt<'tcx> { Ok(()) } - fn equate(&self, l: TermVid, r: TermVid) { + /// This is a thin wrapper around inserting into the var tables. You probably want + /// [`Self::instantiate_var`] instead, which calls this method. + fn union_vars(&self, l: TermVid, r: TermVid) { match (l, r) { (TermVid::Ty(l), TermVid::Ty(r)) => { self.inner.borrow_mut().type_variables().equate(l, r) @@ -320,7 +299,9 @@ impl<'tcx> InferCtxt<'tcx> { } } - fn instantiate(&self, l: TermVid, r: ty::Term<'tcx>) { + /// This is a thin wrapper around inserting into the var tables. You probably want + /// [`Self::instantiate_var`] instead, which calls this method. + fn union_var_term(&self, l: TermVid, r: ty::Term<'tcx>) { match (l, r.kind()) { (TermVid::Ty(l), ty::TermKind::Ty(r)) => { self.inner.borrow_mut().type_variables().instantiate(l, r) diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index f67ac20c4cf33..d342e4ba5efae 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -323,10 +323,6 @@ impl<'tcx> Const<'tcx> { matches!(self.kind(), ty::ConstKind::Infer(_)) } - pub fn is_ct_var(self) -> bool { - matches!(self.kind(), ConstKind::Infer(ty::InferConst::Var(_))) - } - pub fn ct_vid(self) -> Option { match self.kind() { ConstKind::Infer(ty::InferConst::Var(vid)) => Some(vid), diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index f072b26e26917..7c42a841ec92a 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -621,6 +621,13 @@ impl<'tcx> Term<'tcx> { } } + pub fn term_vid(self) -> Option { + match self.kind() { + TermKind::Ty(ty) => ty.ty_vid().map(TermVid::Ty), + TermKind::Const(ct) => ct.ct_vid().map(TermVid::Const), + } + } + pub fn is_trivially_wf(&self, tcx: TyCtxt<'tcx>) -> bool { match self.kind() { TermKind::Ty(ty) => ty.is_trivially_wf(tcx), diff --git a/compiler/rustc_type_ir/src/generic_arg.rs b/compiler/rustc_type_ir/src/generic_arg.rs index 5d612740fdd84..fefb8e2d01960 100644 --- a/compiler/rustc_type_ir/src/generic_arg.rs +++ b/compiler/rustc_type_ir/src/generic_arg.rs @@ -18,16 +18,3 @@ pub enum GenericArgKind { } impl Eq for GenericArgKind {} - -#[derive_where(Clone, Copy, PartialEq, Debug; I: Interner)] -#[derive(GenericTypeVisitable)] -#[cfg_attr( - feature = "nightly", - derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) -)] -pub enum TermKind { - Ty(I::Ty), - Const(I::Const), -} - -impl Eq for TermKind {} diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index a0b444024ca79..967133c2958f6 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -51,6 +51,7 @@ mod pattern; mod predicate; mod predicate_kind; mod region_kind; +mod term; mod ty_info; mod ty_kind; mod upcast; @@ -78,6 +79,7 @@ pub use predicate_kind::*; pub use region_kind::*; pub use rustc_ast_ir::{FloatTy, IntTy, Movability, Mutability, Pinnedness, UintTy}; use rustc_type_ir_macros::GenericTypeVisitable; +pub use term::*; pub use ty_info::*; pub use ty_kind::*; pub use upcast::*; diff --git a/compiler/rustc_type_ir/src/term.rs b/compiler/rustc_type_ir/src/term.rs new file mode 100644 index 0000000000000..07ef306c853d5 --- /dev/null +++ b/compiler/rustc_type_ir/src/term.rs @@ -0,0 +1,37 @@ +use derive_where::derive_where; +#[cfg(feature = "nightly")] +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; +use rustc_type_ir_macros::GenericTypeVisitable; + +use crate::{ConstVid, Interner, TyVid}; + +#[derive_where(Clone, Copy, PartialEq, Debug; I: Interner)] +#[derive(GenericTypeVisitable)] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] +pub enum TermKind { + Ty(I::Ty), + Const(I::Const), +} + +impl Eq for TermKind {} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum TermVid { + Ty(TyVid), + Const(ConstVid), +} + +impl From for TermVid { + fn from(value: TyVid) -> Self { + TermVid::Ty(value) + } +} + +impl From for TermVid { + fn from(value: ConstVid) -> Self { + TermVid::Const(value) + } +}