From c2dbb222bfb9f924b4e5554df2a82943d23175b3 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 7 Feb 2026 15:29:52 +0900 Subject: [PATCH 01/11] move `TypeOutlives` normalization out of region checking --- compiler/rustc_trait_selection/src/regions.rs | 123 +++++++++++++++--- ...undeclared-lifetime-no-ice-issue-151461.rs | 15 +++ ...clared-lifetime-no-ice-issue-151461.stderr | 19 +++ 3 files changed, 136 insertions(+), 21 deletions(-) create mode 100644 tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.rs create mode 100644 tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.stderr diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index debc4fda15a56..7cf93a136e92a 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -1,14 +1,105 @@ +use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{InferCtxt, RegionResolutionError}; +use rustc_infer::infer::{ + InferCtxt, RegionResolutionError, SubregionOrigin, TypeOutlivesConstraint, +}; use rustc_macros::extension; use rustc_middle::traits::ObligationCause; -use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{self, Ty, elaborate}; use crate::traits::ScrubbedTraitError; use crate::traits::outlives_bounds::InferCtxtExt; +fn normalize_higher_ranked_assumptions<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> FxHashSet> { + let assumptions = infcx.take_registered_region_assumptions(); + if !infcx.next_trait_solver() { + return elaborate::elaborate_outlives_assumptions(infcx.tcx, assumptions); + } + + let mut normalized_assumptions = vec![]; + let mut seen_assumptions = FxHashSet::default(); + + for assumption in assumptions { + if !seen_assumptions.insert(assumption) { + continue; + } + + let assumption = infcx.resolve_vars_if_possible(assumption); + let outlives = ty::Binder::dummy(assumption); + let ty::OutlivesPredicate(kind, region) = + match crate::solve::deeply_normalize::<_, ScrubbedTraitError<'tcx>>( + infcx.at(&ObligationCause::dummy(), param_env), + outlives, + ) { + Ok(assumption) => assumption, + Err(_) => { + infcx.dcx().delayed_bug(format!( + "could not normalize higher-ranked assumption `{assumption}`" + )); + outlives + } + } + .no_bound_vars() + .expect("started with no bound vars, should end with no bound vars"); + + normalized_assumptions.push(ty::OutlivesPredicate(kind, region)); + } + + for assumption in infcx.take_registered_region_assumptions() { + if seen_assumptions.insert(assumption) { + normalized_assumptions.push(assumption); + } + } + + elaborate::elaborate_outlives_assumptions(infcx.tcx, normalized_assumptions) +} + +fn normalize_registered_region_obligations<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> Result<(), (ty::PolyTypeOutlivesPredicate<'tcx>, SubregionOrigin<'tcx>)> { + if !infcx.next_trait_solver() { + return Ok(()); + } + + let obligations = infcx.take_registered_region_obligations(); + let mut seen_outputs = FxHashSet::default(); + let mut normalized_obligations = vec![]; + + for TypeOutlivesConstraint { sup_type, sub_region, origin } in obligations { + let outlives = infcx.resolve_vars_if_possible(ty::Binder::dummy(ty::OutlivesPredicate( + sup_type, sub_region, + ))); + let ty::OutlivesPredicate(sup_type, sub_region) = crate::solve::deeply_normalize( + infcx.at(&ObligationCause::dummy_with_span(origin.span()), param_env), + outlives, + ) + .map_err(|_: Vec>| (outlives, origin.clone()))? + .no_bound_vars() + .expect("started with no bound vars, should end with no bound vars"); + + if seen_outputs.insert((sup_type, sub_region)) { + normalized_obligations.push(TypeOutlivesConstraint { sup_type, sub_region, origin }); + } + } + + for obligation in infcx.take_registered_region_obligations() { + if seen_outputs.insert((obligation.sup_type, obligation.sub_region)) { + normalized_obligations.push(obligation); + } + } + + for obligation in normalized_obligations { + infcx.register_type_outlives_constraint_inner(obligation); + } + + Ok(()) +} + #[extension(pub trait OutlivesEnvironmentBuildExt<'tcx>)] impl<'tcx> OutlivesEnvironment<'tcx> { fn new( @@ -46,10 +137,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> { } } - // FIXME(-Znext-trait-solver): Normalize these. - let higher_ranked_assumptions = infcx.take_registered_region_assumptions(); - let higher_ranked_assumptions = - elaborate::elaborate_outlives_assumptions(infcx.tcx, higher_ranked_assumptions); + let higher_ranked_assumptions = normalize_higher_ranked_assumptions(infcx, param_env); // FIXME: This needs to be modified so that we normalize the known type // outlives obligations then elaborate them into their region/type components. @@ -98,21 +186,14 @@ impl<'tcx> InferCtxt<'tcx> { &self, outlives_env: &OutlivesEnvironment<'tcx>, ) -> Vec> { - self.resolve_regions_with_normalize(&outlives_env, |ty, origin| { - let ty = self.resolve_vars_if_possible(ty); - - if self.next_trait_solver() { - crate::solve::deeply_normalize( - self.at( - &ObligationCause::dummy_with_span(origin.span()), - outlives_env.param_env, - ), - ty, - ) - .map_err(|_: Vec>| NoSolution) - } else { - Ok(ty) - } + if let Err((outlives, origin)) = + normalize_registered_region_obligations(self, outlives_env.param_env) + { + return vec![RegionResolutionError::CannotNormalize(outlives, origin)]; + } + + self.resolve_regions_with_normalize(outlives_env, |outlives, _| { + Ok(self.resolve_vars_if_possible(outlives)) }) } } diff --git a/tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.rs b/tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.rs new file mode 100644 index 0000000000000..324ce28a959e5 --- /dev/null +++ b/tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.rs @@ -0,0 +1,15 @@ +//@ compile-flags: -Znext-solver=globally + +trait X<'a> { + type U: ?Sized; +} + +impl X<'_> for u32 +where + for<'b> >::U: 'a, + //~^ ERROR use of undeclared lifetime name `'a` +{ + type U = str; +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.stderr b/tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.stderr new file mode 100644 index 0000000000000..f05f61d6af5c2 --- /dev/null +++ b/tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.stderr @@ -0,0 +1,19 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/undeclared-lifetime-no-ice-issue-151461.rs:9:33 + | +LL | for<'b> >::U: 'a, + | ^^ undeclared lifetime + | + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'a` lifetime + | +LL | for<'a, 'b> >::U: 'a, + | +++ +help: consider introducing lifetime `'a` here + | +LL | impl<'a> X<'_> for u32 + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0261`. From fc330f4f471cd305011cdbd0331dba2ae6951c7f Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 14 Feb 2026 18:54:03 +0900 Subject: [PATCH 02/11] Deeply normalize `TypeOutlives` on next solver --- .../rustc_infer/src/infer/outlives/mod.rs | 21 +-- .../src/infer/outlives/obligations.rs | 32 ++-- .../src/solve/eval_ctxt/mod.rs | 16 ++ .../rustc_next_trait_solver/src/solve/mod.rs | 140 +++++++++++++++++- compiler/rustc_trait_selection/src/regions.rs | 71 +-------- .../src/solve/delegate.rs | 19 ++- .../src/traits/auto_trait.rs | 2 +- 7 files changed, 187 insertions(+), 114 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index c992cda8aaed0..3c04fe0475d10 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -1,13 +1,13 @@ //! Various code related to computing outlives relations. use rustc_data_structures::undo_log::UndoLogs; -use rustc_middle::traits::query::{NoSolution, OutlivesBound}; +use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty; use tracing::instrument; use self::env::OutlivesEnvironment; use super::region_constraints::{RegionConstraintData, UndoLog}; -use super::{InferCtxt, RegionResolutionError, SubregionOrigin}; +use super::{InferCtxt, RegionResolutionError}; use crate::infer::free_regions::RegionRelations; use crate::infer::lexical_region_resolve; use crate::infer::region_constraints::ConstraintKind; @@ -35,25 +35,12 @@ impl<'tcx> InferCtxt<'tcx> { /// result. After this, no more unification operations should be /// done -- or the compiler will panic -- but it is legal to use /// `resolve_vars_if_possible` as well as `fully_resolve`. - /// - /// If you are in a crate that has access to `rustc_trait_selection`, - /// then it's probably better to use `resolve_regions`, - /// which knows how to normalize registered region obligations. #[must_use] - pub fn resolve_regions_with_normalize( + pub fn resolve_regions_with_outlives_env( &self, outlives_env: &OutlivesEnvironment<'tcx>, - deeply_normalize_ty: impl Fn( - ty::PolyTypeOutlivesPredicate<'tcx>, - SubregionOrigin<'tcx>, - ) -> Result, NoSolution>, ) -> Vec> { - match self.process_registered_region_obligations(outlives_env, deeply_normalize_ty) { - Ok(()) => {} - Err((clause, origin)) => { - return vec![RegionResolutionError::CannotNormalize(clause, origin)]; - } - }; + self.process_registered_region_obligations(outlives_env); let mut storage = { let mut inner = self.inner.borrow_mut(); diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index f06f50785eccf..34cd6f72bfece 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -62,11 +62,9 @@ use rustc_data_structures::undo_log::UndoLogs; use rustc_middle::bug; use rustc_middle::mir::ConstraintCategory; -use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::outlives::{Component, push_outlives_components}; use rustc_middle::ty::{ - self, GenericArgKind, GenericArgsRef, PolyTypeOutlivesPredicate, Region, Ty, TyCtxt, - TypeFoldable as _, TypeVisitableExt, + self, GenericArgKind, GenericArgsRef, Region, Ty, TyCtxt, TypeFoldable as _, TypeVisitableExt, }; use smallvec::smallvec; use tracing::{debug, instrument}; @@ -194,19 +192,11 @@ impl<'tcx> InferCtxt<'tcx> { /// flow of the inferencer. The key point is that it is /// invoked after all type-inference variables have been bound -- /// right before lexical region resolution. - #[instrument(level = "debug", skip(self, outlives_env, deeply_normalize_ty))] - pub fn process_registered_region_obligations( - &self, - outlives_env: &OutlivesEnvironment<'tcx>, - mut deeply_normalize_ty: impl FnMut( - PolyTypeOutlivesPredicate<'tcx>, - SubregionOrigin<'tcx>, - ) - -> Result, NoSolution>, - ) -> Result<(), (PolyTypeOutlivesPredicate<'tcx>, SubregionOrigin<'tcx>)> { + #[instrument(level = "debug", skip(self, outlives_env))] + pub fn process_registered_region_obligations(&self, outlives_env: &OutlivesEnvironment<'tcx>) { assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot"); - // Must loop since the process of normalizing may itself register region obligations. + // We loop to handle the case that processing one obligation ends up registering another. for iteration in 0.. { let my_region_obligations = self.take_registered_region_obligations(); if my_region_obligations.is_empty() { @@ -223,12 +213,12 @@ impl<'tcx> InferCtxt<'tcx> { } for TypeOutlivesConstraint { sup_type, sub_region, origin } in my_region_obligations { - let outlives = ty::Binder::dummy(ty::OutlivesPredicate(sup_type, sub_region)); - let ty::OutlivesPredicate(sup_type, sub_region) = - deeply_normalize_ty(outlives, origin.clone()) - .map_err(|NoSolution| (outlives, origin.clone()))? - .no_bound_vars() - .expect("started with no bound vars, should end with no bound vars"); + let outlives = self.resolve_vars_if_possible(ty::Binder::dummy( + ty::OutlivesPredicate(sup_type, sub_region), + )); + let ty::OutlivesPredicate(sup_type, sub_region) = outlives + .no_bound_vars() + .expect("started with no bound vars, should end with no bound vars"); // `TypeOutlives` is structural, so we should try to opportunistically resolve all // region vids before processing regions, so we have a better chance to match clauses // in our param-env. @@ -256,8 +246,6 @@ impl<'tcx> InferCtxt<'tcx> { outlives.type_must_outlive(origin, sup_type, sub_region, category); } } - - Ok(()) } } 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 6841fe1c5124e..ac26d9f0d1826 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 @@ -420,6 +420,18 @@ where Ok(goal_evaluation) } + /// Evaluate `goal` without adding it to `nested_goals`. + /// + /// This is intended for goal evaluation which should not affect the + /// certainty of the currently evaluated goal. + pub(super) fn try_evaluate_goal( + &mut self, + source: GoalSource, + goal: Goal, + ) -> Result, NoSolution> { + self.evaluate_goal(source, goal, None) + } + /// Recursively evaluates `goal`, returning the nested goals in case /// the nested goal is a `NormalizesTo` goal. /// @@ -1076,6 +1088,10 @@ where self.delegate.shallow_resolve(ty) } + pub(super) fn shallow_resolve_const(&self, ct: I::Const) -> I::Const { + self.delegate.shallow_resolve_const(ct) + } + pub(super) fn eager_resolve_region(&self, r: I::Region) -> I::Region { if let ty::ReVar(vid) = r.kind() { self.delegate.opportunistic_resolve_lt_var(vid) diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 58bd7cf663d98..f4a74aaf6f723 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -22,9 +22,13 @@ mod search_graph; mod trait_goals; use derive_where::derive_where; +use rustc_type_ir::data_structures::ensure_sufficient_stack; use rustc_type_ir::inherent::*; pub use rustc_type_ir::solve::*; -use rustc_type_ir::{self as ty, Interner, TyVid, TypingMode}; +use rustc_type_ir::{ + self as ty, FallibleTypeFolder, Interner, TyVid, TypeFoldable, TypeSuperFoldable, + TypeVisitableExt, TypingMode, +}; use tracing::instrument; pub use self::eval_ctxt::{ @@ -91,6 +95,15 @@ where goal: Goal>, ) -> QueryResult { let ty::OutlivesPredicate(ty, lt) = goal.predicate; + + // With `-Znext-solver`, `TypeOutlives` goals normalize aliases before registering region + // obligations so that later processing does not have to structurally process aliases. + let ty = self.resolve_vars_if_possible(ty); + let ty = if ty.has_aliases() { + self.deeply_normalize_for_outlives(goal.param_env, ty) + } else { + ty + }; self.register_ty_outlives(ty, lt); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } @@ -306,6 +319,131 @@ where } } + /// This is the solver-internal equivalent of the `deeply_normalize` helper in + /// `compiler/rustc_trait_selection/src/solve/normalize.rs`. + fn deeply_normalize_for_outlives(&mut self, param_env: I::ParamEnv, ty: I::Ty) -> I::Ty { + debug_assert!(ty.has_aliases()); + + // We only use this for `TypeOutlives` goals, + // so the input should not have escaping bound vars. + debug_assert!( + !ty.has_escaping_bound_vars(), + "expected `TypeOutlives` ty to not have escaping bound vars: {ty:?}" + ); + + struct DeepNormalizer<'ecx, 'a, D, I> + where + D: SolverDelegate, + I: Interner, + { + ecx: &'ecx mut EvalCtxt<'a, D, I>, + param_env: I::ParamEnv, + depth: usize, + } + + impl DeepNormalizer<'_, '_, D, I> + where + D: SolverDelegate, + I: Interner, + { + fn normalize_alias_term(&mut self, alias_term: I::Term) -> Result { + debug_assert!(alias_term.to_alias_term().is_some()); + + if alias_term.has_non_region_infer() || alias_term.has_non_region_placeholders() { + return match alias_term.kind() { + ty::TermKind::Ty(ty) => Ok(ty.try_super_fold_with(self)?.into()), + ty::TermKind::Const(ct) => Ok(ct.try_super_fold_with(self)?.into()), + }; + } + + // Avoid getting stuck on self-referential normalization. + if self.depth >= self.ecx.cx().recursion_limit() { + return match alias_term.kind() { + ty::TermKind::Ty(ty) => Ok(ty.try_super_fold_with(self)?.into()), + ty::TermKind::Const(ct) => Ok(ct.try_super_fold_with(self)?.into()), + }; + } + + self.depth += 1; + + let normalized_term = self.ecx.next_term_infer_of_kind(alias_term); + let goal = Goal::new( + self.ecx.cx(), + self.param_env, + ty::PredicateKind::AliasRelate( + alias_term, + normalized_term, + ty::AliasRelationDirection::Equate, + ), + ); + + let result = match self.ecx.try_evaluate_goal(GoalSource::TypeRelating, goal) { + Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => { + // Resolve the fresh term and continue normalization recursively. + let term = self.ecx.resolve_vars_if_possible(normalized_term); + match term.kind() { + ty::TermKind::Ty(ty) => ty.try_super_fold_with(self)?.into(), + ty::TermKind::Const(ct) => ct.try_super_fold_with(self)?.into(), + } + } + Ok(GoalEvaluation { certainty: Certainty::Maybe { .. }, .. }) + | Err(NoSolution) => { + // If normalizing this alias isn't possible right now, keep it and continue + // folding inside of it. + match alias_term.kind() { + ty::TermKind::Ty(ty) => ty.try_super_fold_with(self)?.into(), + ty::TermKind::Const(ct) => ct.try_super_fold_with(self)?.into(), + } + } + }; + + self.depth -= 1; + Ok(result) + } + } + + impl FallibleTypeFolder for DeepNormalizer<'_, '_, D, I> + where + D: SolverDelegate, + I: Interner, + { + type Error = NoSolution; + + fn cx(&self) -> I { + self.ecx.cx() + } + + #[instrument(level = "trace", skip(self), ret)] + fn try_fold_ty(&mut self, ty: I::Ty) -> Result { + let ty = self.ecx.shallow_resolve(ty); + if !ty.has_aliases() { + return Ok(ty); + } + + let ty::Alias(..) = ty.kind() else { return ty.try_super_fold_with(self) }; + let term = ensure_sufficient_stack(|| self.normalize_alias_term(ty.into()))?; + Ok(term.expect_ty()) + } + + #[instrument(level = "trace", skip(self), ret)] + fn try_fold_const(&mut self, ct: I::Const) -> Result { + let ct = self.ecx.shallow_resolve_const(ct); + if !ct.has_aliases() { + return Ok(ct); + } + + let ty::ConstKind::Unevaluated(..) = ct.kind() else { + return ct.try_super_fold_with(self); + }; + + let term = ensure_sufficient_stack(|| self.normalize_alias_term(ct.into()))?; + Ok(term.expect_const()) + } + } + + ty.try_fold_with(&mut DeepNormalizer { ecx: self, param_env, depth: 0 }).unwrap() + } + /// Normalize a type for when it is structurally matched on. /// /// This function is necessary in nearly all cases before matching on a type. diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 7cf93a136e92a..f8d14d9423e3c 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -1,9 +1,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{ - InferCtxt, RegionResolutionError, SubregionOrigin, TypeOutlivesConstraint, -}; +use rustc_infer::infer::{InferCtxt, RegionResolutionError}; use rustc_macros::extension; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, Ty, elaborate}; @@ -58,48 +56,6 @@ fn normalize_higher_ranked_assumptions<'tcx>( elaborate::elaborate_outlives_assumptions(infcx.tcx, normalized_assumptions) } -fn normalize_registered_region_obligations<'tcx>( - infcx: &InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, -) -> Result<(), (ty::PolyTypeOutlivesPredicate<'tcx>, SubregionOrigin<'tcx>)> { - if !infcx.next_trait_solver() { - return Ok(()); - } - - let obligations = infcx.take_registered_region_obligations(); - let mut seen_outputs = FxHashSet::default(); - let mut normalized_obligations = vec![]; - - for TypeOutlivesConstraint { sup_type, sub_region, origin } in obligations { - let outlives = infcx.resolve_vars_if_possible(ty::Binder::dummy(ty::OutlivesPredicate( - sup_type, sub_region, - ))); - let ty::OutlivesPredicate(sup_type, sub_region) = crate::solve::deeply_normalize( - infcx.at(&ObligationCause::dummy_with_span(origin.span()), param_env), - outlives, - ) - .map_err(|_: Vec>| (outlives, origin.clone()))? - .no_bound_vars() - .expect("started with no bound vars, should end with no bound vars"); - - if seen_outputs.insert((sup_type, sub_region)) { - normalized_obligations.push(TypeOutlivesConstraint { sup_type, sub_region, origin }); - } - } - - for obligation in infcx.take_registered_region_obligations() { - if seen_outputs.insert((obligation.sup_type, obligation.sub_region)) { - normalized_obligations.push(obligation); - } - } - - for obligation in normalized_obligations { - infcx.register_type_outlives_constraint_inner(obligation); - } - - Ok(()) -} - #[extension(pub trait OutlivesEnvironmentBuildExt<'tcx>)] impl<'tcx> OutlivesEnvironment<'tcx> { fn new( @@ -159,12 +115,11 @@ impl<'tcx> OutlivesEnvironment<'tcx> { #[extension(pub trait InferCtxtRegionExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { - /// Resolve regions, using the deep normalizer to normalize any type-outlives - /// obligations in the process. This is in `rustc_trait_selection` because - /// we need to normalize. + /// Resolve regions. /// - /// Prefer this method over `resolve_regions_with_normalize`, unless you are - /// doing something specific for normalization. + /// With the next solver, `TypeOutlives` goals are normalized while they are + /// evaluated (and before being registered as region obligations), so region + /// resolution does not need to normalize them. /// /// This function assumes that all infer variables are already constrained. fn resolve_regions( @@ -180,20 +135,4 @@ impl<'tcx> InferCtxt<'tcx> { assumed_wf_tys, )) } - - /// Don't call this directly unless you know what you're doing. - fn resolve_regions_with_outlives_env( - &self, - outlives_env: &OutlivesEnvironment<'tcx>, - ) -> Vec> { - if let Err((outlives, origin)) = - normalize_registered_region_obligations(self, outlives_env.param_env) - { - return vec![RegionResolutionError::CannotNormalize(outlives, origin)]; - } - - self.resolve_regions_with_normalize(outlives_env, |outlives, _| { - Ok(self.resolve_vars_if_possible(outlives)) - }) - } } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 62572694de326..b486fe9596fb6 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -116,13 +116,18 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< Some(Certainty::Yes) } ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => { - self.0.register_type_outlives_constraint( - outlives.0, - outlives.1, - &ObligationCause::dummy_with_span(span), - ); - - Some(Certainty::Yes) + // `TypeOutlives` obligations in the `InferCtxt` must already be normalized. + let outlives = self.0.resolve_vars_if_possible(outlives); + if outlives.0.has_aliases() { + None + } else { + self.0.register_type_outlives_constraint( + outlives.0, + outlives.1, + &ObligationCause::dummy_with_span(span), + ); + Some(Certainty::Yes) + } } ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, .. }) | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => { diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 2da7c4448ce54..47b2dbfb5da10 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -162,7 +162,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } let outlives_env = OutlivesEnvironment::new(&infcx, CRATE_DEF_ID, full_env, []); - let _ = infcx.process_registered_region_obligations(&outlives_env, |ty, _| Ok(ty)); + infcx.process_registered_region_obligations(&outlives_env); let region_data = infcx.inner.borrow_mut().unwrap_region_constraints().data().clone(); From 41f07896efa44bf9bcef3531998e04e5499bad0c Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 15 Feb 2026 18:47:07 +0900 Subject: [PATCH 03/11] loop is no longer necessary --- .../src/infer/outlives/obligations.rs | 75 +++++++------------ 1 file changed, 28 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 34cd6f72bfece..442be3fb8d0d1 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -60,7 +60,6 @@ //! imply that `'b: 'a`. use rustc_data_structures::undo_log::UndoLogs; -use rustc_middle::bug; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::outlives::{Component, push_outlives_components}; use rustc_middle::ty::{ @@ -196,55 +195,37 @@ impl<'tcx> InferCtxt<'tcx> { pub fn process_registered_region_obligations(&self, outlives_env: &OutlivesEnvironment<'tcx>) { assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot"); - // We loop to handle the case that processing one obligation ends up registering another. - for iteration in 0.. { - let my_region_obligations = self.take_registered_region_obligations(); - if my_region_obligations.is_empty() { - break; - } - - if !self.tcx.recursion_limit().value_within_limit(iteration) { - // This may actually be reachable. If so, we should convert - // this to a proper error/consider whether we should detect - // this somewhere else. - bug!( - "unexpected overflowed when processing region obligations: {my_region_obligations:#?}" - ); + for TypeOutlivesConstraint { sup_type, sub_region, origin } in + self.take_registered_region_obligations() + { + let ty::OutlivesPredicate(sup_type, sub_region) = + self.resolve_vars_if_possible(ty::OutlivesPredicate(sup_type, sub_region)); + + // `TypeOutlives` is structural, so we should try to opportunistically resolve all + // region vids before processing regions, so we have a better chance to match clauses + // in our param-env. + let (sup_type, sub_region) = + (sup_type, sub_region).fold_with(&mut OpportunisticRegionResolver::new(self)); + + if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions + && outlives_env + .higher_ranked_assumptions() + .contains(&ty::OutlivesPredicate(sup_type.into(), sub_region)) + { + continue; } - for TypeOutlivesConstraint { sup_type, sub_region, origin } in my_region_obligations { - let outlives = self.resolve_vars_if_possible(ty::Binder::dummy( - ty::OutlivesPredicate(sup_type, sub_region), - )); - let ty::OutlivesPredicate(sup_type, sub_region) = outlives - .no_bound_vars() - .expect("started with no bound vars, should end with no bound vars"); - // `TypeOutlives` is structural, so we should try to opportunistically resolve all - // region vids before processing regions, so we have a better chance to match clauses - // in our param-env. - let (sup_type, sub_region) = - (sup_type, sub_region).fold_with(&mut OpportunisticRegionResolver::new(self)); - - if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions - && outlives_env - .higher_ranked_assumptions() - .contains(&ty::OutlivesPredicate(sup_type.into(), sub_region)) - { - continue; - } + debug!(?sup_type, ?sub_region, ?origin); - debug!(?sup_type, ?sub_region, ?origin); - - let outlives = &mut TypeOutlives::new( - self, - self.tcx, - outlives_env.region_bound_pairs(), - None, - outlives_env.known_type_outlives(), - ); - let category = origin.to_constraint_category(); - outlives.type_must_outlive(origin, sup_type, sub_region, category); - } + let outlives = &mut TypeOutlives::new( + self, + self.tcx, + outlives_env.region_bound_pairs(), + None, + outlives_env.known_type_outlives(), + ); + let category = origin.to_constraint_category(); + outlives.type_must_outlive(origin, sup_type, sub_region, category); } } } From 4a22b6769986403e80e80dfbba6c3d3ca6f2154d Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 15 Feb 2026 18:50:52 +0900 Subject: [PATCH 04/11] remove confusing comment --- compiler/rustc_trait_selection/src/regions.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index f8d14d9423e3c..3a9f3dd8f53ec 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -116,11 +116,6 @@ impl<'tcx> OutlivesEnvironment<'tcx> { #[extension(pub trait InferCtxtRegionExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { /// Resolve regions. - /// - /// With the next solver, `TypeOutlives` goals are normalized while they are - /// evaluated (and before being registered as region obligations), so region - /// resolution does not need to normalize them. - /// /// This function assumes that all infer variables are already constrained. fn resolve_regions( &self, From 0e6b653ac70a32466aee3ddd3dff5091df28e9c1 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 15 Feb 2026 18:51:14 +0900 Subject: [PATCH 05/11] add comment and link to test --- .../next-solver/undeclared-lifetime-no-ice-issue-151461.rs | 4 ++++ .../undeclared-lifetime-no-ice-issue-151461.stderr | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.rs b/tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.rs index 324ce28a959e5..07050fe1355e8 100644 --- a/tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.rs +++ b/tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.rs @@ -1,3 +1,7 @@ +// Regression test for . +// +// The next solver normalizes `TypeOutlives` goals before registering them as region obligations. +// This should not ICE when we already emitted an error for an undeclared lifetime. //@ compile-flags: -Znext-solver=globally trait X<'a> { diff --git a/tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.stderr b/tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.stderr index f05f61d6af5c2..d618c94d28d86 100644 --- a/tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.stderr +++ b/tests/ui/traits/next-solver/undeclared-lifetime-no-ice-issue-151461.stderr @@ -1,5 +1,5 @@ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/undeclared-lifetime-no-ice-issue-151461.rs:9:33 + --> $DIR/undeclared-lifetime-no-ice-issue-151461.rs:13:33 | LL | for<'b> >::U: 'a, | ^^ undeclared lifetime From 5681d2bc7a37ef07d8fd977a9eb596b62993ead8 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 15 Feb 2026 18:57:01 +0900 Subject: [PATCH 06/11] remove fast path --- .../rustc_trait_selection/src/solve/delegate.rs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index b486fe9596fb6..2d1dc337099d6 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -16,7 +16,7 @@ use rustc_middle::ty::{ }; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; -use crate::traits::{EvaluateConstErr, ObligationCause, sizedness_fast_path, specialization_graph}; +use crate::traits::{EvaluateConstErr, sizedness_fast_path, specialization_graph}; #[repr(transparent)] pub struct SolverDelegate<'tcx>(InferCtxt<'tcx>); @@ -115,20 +115,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< ); Some(Certainty::Yes) } - ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => { - // `TypeOutlives` obligations in the `InferCtxt` must already be normalized. - let outlives = self.0.resolve_vars_if_possible(outlives); - if outlives.0.has_aliases() { - None - } else { - self.0.register_type_outlives_constraint( - outlives.0, - outlives.1, - &ObligationCause::dummy_with_span(span), - ); - Some(Certainty::Yes) - } - } ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, .. }) | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => { match (self.shallow_resolve(a).kind(), self.shallow_resolve(b).kind()) { From 1f6ac9d0dbbabc80686bc0ce00d32d6950963119 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 16 Feb 2026 07:29:34 +0900 Subject: [PATCH 07/11] normalize correctly --- .../src/placeholder.rs | 104 ++++++++++ .../src/solve/eval_ctxt/mod.rs | 27 +-- .../rustc_next_trait_solver/src/solve/mod.rs | 189 ++++++++++++------ 3 files changed, 242 insertions(+), 78 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/placeholder.rs b/compiler/rustc_next_trait_solver/src/placeholder.rs index 83f3bdf01dc83..b70c073b53c39 100644 --- a/compiler/rustc_next_trait_solver/src/placeholder.rs +++ b/compiler/rustc_next_trait_solver/src/placeholder.rs @@ -164,3 +164,107 @@ where if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } } } + +/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which +/// they came. +pub struct PlaceholderReplacer<'a, I: Interner> { + cx: I, + mapped_regions: IndexMap, ty::BoundRegion>, + mapped_types: IndexMap, ty::BoundTy>, + mapped_consts: IndexMap, ty::BoundConst>, + universe_indices: &'a [Option], + current_index: ty::DebruijnIndex, +} + +impl<'a, I: Interner> PlaceholderReplacer<'a, I> { + pub fn replace_placeholders>( + cx: I, + mapped_regions: IndexMap, ty::BoundRegion>, + mapped_types: IndexMap, ty::BoundTy>, + mapped_consts: IndexMap, ty::BoundConst>, + universe_indices: &'a [Option], + value: T, + ) -> T { + let mut replacer = PlaceholderReplacer { + cx, + mapped_regions, + mapped_types, + mapped_consts, + universe_indices, + current_index: ty::INNERMOST, + }; + value.fold_with(&mut replacer) + } + + fn debruijn_for_universe(&self, universe: ty::UniverseIndex) -> ty::DebruijnIndex { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(u_idx) if *u_idx == universe)) + .unwrap_or_else(|| panic!("unexpected placeholder universe {universe:?}")); + + ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ) + } +} + +impl TypeFolder for PlaceholderReplacer<'_, I> { + fn cx(&self) -> I { + self.cx + } + + fn fold_binder>(&mut self, t: ty::Binder) -> ty::Binder { + if !t.has_placeholders() && !t.has_infer() { + return t; + } + + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_region(&mut self, r: I::Region) -> I::Region { + if let ty::RePlaceholder(p) = r.kind() { + if let Some(replace_var) = self.mapped_regions.get(&p) { + let db = self.debruijn_for_universe(p.universe()); + return Region::new_bound(self.cx(), db, *replace_var); + } + } + + r + } + + fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + if let ty::Placeholder(p) = ty.kind() { + match self.mapped_types.get(&p) { + Some(replace_var) => { + let db = self.debruijn_for_universe(p.universe()); + Ty::new_bound(self.cx(), db, *replace_var) + } + None => ty, + } + } else if ty.has_placeholders() || ty.has_infer() { + ty.super_fold_with(self) + } else { + ty + } + } + + fn fold_const(&mut self, ct: I::Const) -> I::Const { + if let ty::ConstKind::Placeholder(p) = ct.kind() { + match self.mapped_consts.get(&p) { + Some(replace_var) => { + let db = self.debruijn_for_universe(p.universe()); + Const::new_bound(self.cx(), db, *replace_var) + } + None => ct, + } + } else if ct.has_placeholders() || ct.has_infer() { + ct.super_fold_with(self) + } else { + ct + } + } +} 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 ac26d9f0d1826..d970c74d77fc5 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 @@ -3,7 +3,7 @@ use std::ops::ControlFlow; #[cfg(feature = "nightly")] use rustc_macros::HashStable_NoContext; -use rustc_type_ir::data_structures::{HashMap, HashSet}; +use rustc_type_ir::data_structures::{HashMap, HashSet, IndexMap}; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; @@ -420,18 +420,6 @@ where Ok(goal_evaluation) } - /// Evaluate `goal` without adding it to `nested_goals`. - /// - /// This is intended for goal evaluation which should not affect the - /// certainty of the currently evaluated goal. - pub(super) fn try_evaluate_goal( - &mut self, - source: GoalSource, - goal: Goal, - ) -> Result, NoSolution> { - self.evaluate_goal(source, goal, None) - } - /// Recursively evaluates `goal`, returning the nested goals in case /// the nested goal is a `NormalizesTo` goal. /// @@ -1201,6 +1189,19 @@ where BoundVarReplacer::replace_bound_vars(&**self.delegate, universes, t).0 } + pub(super) fn replace_escaping_bound_vars>( + &self, + value: T, + universes: &mut Vec>, + ) -> ( + T, + IndexMap, ty::BoundRegion>, + IndexMap, ty::BoundTy>, + IndexMap, ty::BoundConst>, + ) { + BoundVarReplacer::replace_bound_vars(&**self.delegate, universes, value) + } + pub(super) fn may_use_unstable_feature( &self, param_env: I::ParamEnv, diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index f4a74aaf6f723..cd07d8af96ecb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -36,6 +36,7 @@ pub use self::eval_ctxt::{ evaluate_root_goal_for_proof_tree_raw_provider, }; use crate::delegate::SolverDelegate; +use crate::placeholder::PlaceholderReplacer; use crate::solve::assembly::Candidate; /// How many fixpoint iterations we should attempt inside of the solver before bailing @@ -99,11 +100,33 @@ where // With `-Znext-solver`, `TypeOutlives` goals normalize aliases before registering region // obligations so that later processing does not have to structurally process aliases. let ty = self.resolve_vars_if_possible(ty); - let ty = if ty.has_aliases() { - self.deeply_normalize_for_outlives(goal.param_env, ty) + if ty.has_non_region_infer() { + if ty.has_aliases() { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::AllGood, + }); + } + + self.register_ty_outlives(ty, lt); + return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + + // This avoids spurious overflows when attempting to normalize alias types which are + // generic/ambiguous (e.g. `::Assoc`) or involve opaques. + let ty = if ty.has_aliases() + && !ty.has_opaque_types() + && !ty.has_non_region_param() + && !ty.has_non_region_placeholders() + { + self.probe(|_| inspect::ProbeKind::NormalizedSelfTyAssembly) + .enter(|ecx| ecx.deeply_normalize_for_outlives(goal.param_env, ty)) + .ok() + .unwrap_or(ty) } else { ty }; + self.register_ty_outlives(ty, lt); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } @@ -321,15 +344,22 @@ where /// This is the solver-internal equivalent of the `deeply_normalize` helper in /// `compiler/rustc_trait_selection/src/solve/normalize.rs`. - fn deeply_normalize_for_outlives(&mut self, param_env: I::ParamEnv, ty: I::Ty) -> I::Ty { - debug_assert!(ty.has_aliases()); - - // We only use this for `TypeOutlives` goals, - // so the input should not have escaping bound vars. - debug_assert!( - !ty.has_escaping_bound_vars(), - "expected `TypeOutlives` ty to not have escaping bound vars: {ty:?}" - ); + fn deeply_normalize_for_outlives( + &mut self, + param_env: I::ParamEnv, + ty: I::Ty, + ) -> Result> { + let ty = self.shallow_resolve(ty); + if ty.has_non_region_infer() { + return Err(Ok(Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::AllGood, + })); + } + + if !ty.has_aliases() { + return Ok(ty); + } struct DeepNormalizer<'ecx, 'a, D, I> where @@ -339,6 +369,7 @@ where ecx: &'ecx mut EvalCtxt<'a, D, I>, param_env: I::ParamEnv, depth: usize, + universes: Vec>, } impl DeepNormalizer<'_, '_, D, I> @@ -346,59 +377,49 @@ where D: SolverDelegate, I: Interner, { - fn normalize_alias_term(&mut self, alias_term: I::Term) -> Result { + fn normalize_alias_term( + &mut self, + alias_term: I::Term, + has_escaping_bound_vars: bool, + ) -> Result> { debug_assert!(alias_term.to_alias_term().is_some()); - if alias_term.has_non_region_infer() || alias_term.has_non_region_placeholders() { - return match alias_term.kind() { - ty::TermKind::Ty(ty) => Ok(ty.try_super_fold_with(self)?.into()), - ty::TermKind::Const(ct) => Ok(ct.try_super_fold_with(self)?.into()), - }; - } - // Avoid getting stuck on self-referential normalization. if self.depth >= self.ecx.cx().recursion_limit() { - return match alias_term.kind() { - ty::TermKind::Ty(ty) => Ok(ty.try_super_fold_with(self)?.into()), - ty::TermKind::Const(ct) => Ok(ct.try_super_fold_with(self)?.into()), - }; + return Err(Ok(Certainty::overflow(true))); } self.depth += 1; + let result = (|| { + let (alias_term, mapped_bound_vars) = if has_escaping_bound_vars { + let (term, mapped_regions, mapped_types, mapped_consts) = + self.ecx.replace_escaping_bound_vars(alias_term, &mut self.universes); + (term, Some((mapped_regions, mapped_types, mapped_consts))) + } else { + (alias_term, None) + }; - let normalized_term = self.ecx.next_term_infer_of_kind(alias_term); - let goal = Goal::new( - self.ecx.cx(), - self.param_env, - ty::PredicateKind::AliasRelate( - alias_term, - normalized_term, - ty::AliasRelationDirection::Equate, - ), - ); - - let result = match self.ecx.try_evaluate_goal(GoalSource::TypeRelating, goal) { - Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => { - // Resolve the fresh term and continue normalization recursively. - let term = self.ecx.resolve_vars_if_possible(normalized_term); - match term.kind() { - ty::TermKind::Ty(ty) => ty.try_super_fold_with(self)?.into(), - ty::TermKind::Const(ct) => ct.try_super_fold_with(self)?.into(), - } - } - Ok(GoalEvaluation { certainty: Certainty::Maybe { .. }, .. }) - | Err(NoSolution) => { - // If normalizing this alias isn't possible right now, keep it and continue - // folding inside of it. - match alias_term.kind() { - ty::TermKind::Ty(ty) => ty.try_super_fold_with(self)?.into(), - ty::TermKind::Const(ct) => ct.try_super_fold_with(self)?.into(), - } + let normalized_term = self + .ecx + .structurally_normalize_term(self.param_env, alias_term) + .map_err(Err)?; + let normalized_term = normalized_term.try_fold_with(self)?; + + if let Some((mapped_regions, mapped_types, mapped_consts)) = mapped_bound_vars { + Ok(PlaceholderReplacer::replace_placeholders( + self.ecx.cx(), + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + normalized_term, + )) + } else { + Ok(normalized_term) } - }; - + })(); self.depth -= 1; - Ok(result) + result } } @@ -407,41 +428,79 @@ where D: SolverDelegate, I: Interner, { - type Error = NoSolution; + type Error = Result; fn cx(&self) -> I { self.ecx.cx() } + fn try_fold_binder>( + &mut self, + t: ty::Binder, + ) -> Result, Self::Error> { + self.universes.push(None); + let t = t.try_super_fold_with(self)?; + self.universes.pop(); + Ok(t) + } + #[instrument(level = "trace", skip(self), ret)] fn try_fold_ty(&mut self, ty: I::Ty) -> Result { let ty = self.ecx.shallow_resolve(ty); + if ty.has_non_region_infer() { + return Err(Ok(Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::AllGood, + })); + } + if !ty.has_aliases() { return Ok(ty); } - let ty::Alias(..) = ty.kind() else { return ty.try_super_fold_with(self) }; - let term = ensure_sufficient_stack(|| self.normalize_alias_term(ty.into()))?; - Ok(term.expect_ty()) + if let ty::Alias(..) = ty.kind() { + let term = ensure_sufficient_stack(|| { + self.normalize_alias_term(ty.into(), ty.has_escaping_bound_vars()) + })?; + Ok(term.expect_ty()) + } else { + ty.try_super_fold_with(self) + } } #[instrument(level = "trace", skip(self), ret)] fn try_fold_const(&mut self, ct: I::Const) -> Result { let ct = self.ecx.shallow_resolve_const(ct); + if ct.has_non_region_infer() { + return Err(Ok(Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::AllGood, + })); + } + if !ct.has_aliases() { return Ok(ct); } - let ty::ConstKind::Unevaluated(..) = ct.kind() else { - return ct.try_super_fold_with(self); - }; - - let term = ensure_sufficient_stack(|| self.normalize_alias_term(ct.into()))?; - Ok(term.expect_const()) + if let ty::ConstKind::Unevaluated(..) = ct.kind() { + let term = ensure_sufficient_stack(|| { + self.normalize_alias_term(ct.into(), ct.has_escaping_bound_vars()) + })?; + Ok(term.expect_const()) + } else { + ct.try_super_fold_with(self) + } } } - ty.try_fold_with(&mut DeepNormalizer { ecx: self, param_env, depth: 0 }).unwrap() + let ty = ty.try_fold_with(&mut DeepNormalizer { + ecx: self, + param_env, + depth: 0, + universes: vec![], + })?; + debug_assert!(!ty.has_aliases()); + Ok(ty) } /// Normalize a type for when it is structurally matched on. From bd54d191f3e2960139c4a7cf613d4bebb88d62cf Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 16 Feb 2026 22:20:24 +0900 Subject: [PATCH 08/11] return `MaybeCause` --- .../rustc_next_trait_solver/src/solve/mod.rs | 79 ++++++++----------- 1 file changed, 34 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index cd07d8af96ecb..bf6da9ba9bb8e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -100,31 +100,18 @@ where // With `-Znext-solver`, `TypeOutlives` goals normalize aliases before registering region // obligations so that later processing does not have to structurally process aliases. let ty = self.resolve_vars_if_possible(ty); - if ty.has_non_region_infer() { - if ty.has_aliases() { + let ty = match self + .probe(|_| inspect::ProbeKind::NormalizedSelfTyAssembly) + .enter(|ecx| ecx.deeply_normalize_for_outlives(goal.param_env, ty)) + { + Ok(ty) => ty, + Err(Ok(cause)) => { return self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe { - cause: MaybeCause::Ambiguity, + cause, opaque_types_jank: OpaqueTypesJank::AllGood, }); } - - self.register_ty_outlives(ty, lt); - return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); - } - - // This avoids spurious overflows when attempting to normalize alias types which are - // generic/ambiguous (e.g. `::Assoc`) or involve opaques. - let ty = if ty.has_aliases() - && !ty.has_opaque_types() - && !ty.has_non_region_param() - && !ty.has_non_region_placeholders() - { - self.probe(|_| inspect::ProbeKind::NormalizedSelfTyAssembly) - .enter(|ecx| ecx.deeply_normalize_for_outlives(goal.param_env, ty)) - .ok() - .unwrap_or(ty) - } else { - ty + Err(Err(NoSolution)) => ty, }; self.register_ty_outlives(ty, lt); @@ -348,16 +335,15 @@ where &mut self, param_env: I::ParamEnv, ty: I::Ty, - ) -> Result> { + ) -> Result> { let ty = self.shallow_resolve(ty); + if !ty.has_aliases() { + return Ok(ty); + } if ty.has_non_region_infer() { - return Err(Ok(Certainty::Maybe { - cause: MaybeCause::Ambiguity, - opaque_types_jank: OpaqueTypesJank::AllGood, - })); + return Err(Ok(MaybeCause::Ambiguity)); } - - if !ty.has_aliases() { + if ty.has_opaque_types() || ty.has_non_region_param() || ty.has_non_region_placeholders() { return Ok(ty); } @@ -381,12 +367,15 @@ where &mut self, alias_term: I::Term, has_escaping_bound_vars: bool, - ) -> Result> { + ) -> Result> { debug_assert!(alias_term.to_alias_term().is_some()); // Avoid getting stuck on self-referential normalization. if self.depth >= self.ecx.cx().recursion_limit() { - return Err(Ok(Certainty::overflow(true))); + return Err(Ok(MaybeCause::Overflow { + suggest_increasing_limit: true, + keep_constraints: false, + })); } self.depth += 1; @@ -428,7 +417,7 @@ where D: SolverDelegate, I: Interner, { - type Error = Result; + type Error = Result; fn cx(&self) -> I { self.ecx.cx() @@ -447,16 +436,16 @@ where #[instrument(level = "trace", skip(self), ret)] fn try_fold_ty(&mut self, ty: I::Ty) -> Result { let ty = self.ecx.shallow_resolve(ty); - if ty.has_non_region_infer() { - return Err(Ok(Certainty::Maybe { - cause: MaybeCause::Ambiguity, - opaque_types_jank: OpaqueTypesJank::AllGood, - })); - } - if !ty.has_aliases() { return Ok(ty); } + if ty.has_non_region_infer() + || ty.has_opaque_types() + || ty.has_non_region_param() + || ty.has_non_region_placeholders() + { + return Err(Ok(MaybeCause::Ambiguity)); + } if let ty::Alias(..) = ty.kind() { let term = ensure_sufficient_stack(|| { @@ -471,16 +460,16 @@ where #[instrument(level = "trace", skip(self), ret)] fn try_fold_const(&mut self, ct: I::Const) -> Result { let ct = self.ecx.shallow_resolve_const(ct); - if ct.has_non_region_infer() { - return Err(Ok(Certainty::Maybe { - cause: MaybeCause::Ambiguity, - opaque_types_jank: OpaqueTypesJank::AllGood, - })); - } - if !ct.has_aliases() { return Ok(ct); } + if ct.has_non_region_infer() + || ct.has_opaque_types() + || ct.has_non_region_param() + || ct.has_non_region_placeholders() + { + return Err(Ok(MaybeCause::Ambiguity)); + } if let ty::ConstKind::Unevaluated(..) = ct.kind() { let term = ensure_sufficient_stack(|| { From fd760f283013ce3bbd55355a9fe6daf49123c6cf Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 28 Feb 2026 21:53:58 +0900 Subject: [PATCH 09/11] Prefer recursive errors --- .../rustc_next_trait_solver/src/solve/mod.rs | 60 ++-- .../cycles/cycle-modulo-ambig-aliases.rs | 2 +- .../cycles/cycle-modulo-ambig-aliases.stderr | 263 +++++++++++++++++- 3 files changed, 281 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index bf6da9ba9bb8e..c2fa25080cc67 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -337,13 +337,7 @@ where ty: I::Ty, ) -> Result> { let ty = self.shallow_resolve(ty); - if !ty.has_aliases() { - return Ok(ty); - } - if ty.has_non_region_infer() { - return Err(Ok(MaybeCause::Ambiguity)); - } - if ty.has_opaque_types() || ty.has_non_region_param() || ty.has_non_region_placeholders() { + if !ty.has_aliases() && !ty.has_non_region_infer() { return Ok(ty); } @@ -436,48 +430,36 @@ where #[instrument(level = "trace", skip(self), ret)] fn try_fold_ty(&mut self, ty: I::Ty) -> Result { let ty = self.ecx.shallow_resolve(ty); - if !ty.has_aliases() { + if !ty.has_aliases() && !ty.has_non_region_infer() { return Ok(ty); } - if ty.has_non_region_infer() - || ty.has_opaque_types() - || ty.has_non_region_param() - || ty.has_non_region_placeholders() - { - return Err(Ok(MaybeCause::Ambiguity)); - } - - if let ty::Alias(..) = ty.kind() { - let term = ensure_sufficient_stack(|| { - self.normalize_alias_term(ty.into(), ty.has_escaping_bound_vars()) - })?; - Ok(term.expect_ty()) - } else { - ty.try_super_fold_with(self) + match ty.kind() { + ty::Infer(_) => Err(Ok(MaybeCause::Ambiguity)), + ty::Alias(..) => { + let term = ensure_sufficient_stack(|| { + self.normalize_alias_term(ty.into(), ty.has_escaping_bound_vars()) + })?; + Ok(term.expect_ty()) + } + _ => ty.try_super_fold_with(self), } } #[instrument(level = "trace", skip(self), ret)] fn try_fold_const(&mut self, ct: I::Const) -> Result { let ct = self.ecx.shallow_resolve_const(ct); - if !ct.has_aliases() { + if !ct.has_aliases() && !ct.has_non_region_infer() { return Ok(ct); } - if ct.has_non_region_infer() - || ct.has_opaque_types() - || ct.has_non_region_param() - || ct.has_non_region_placeholders() - { - return Err(Ok(MaybeCause::Ambiguity)); - } - - if let ty::ConstKind::Unevaluated(..) = ct.kind() { - let term = ensure_sufficient_stack(|| { - self.normalize_alias_term(ct.into(), ct.has_escaping_bound_vars()) - })?; - Ok(term.expect_const()) - } else { - ct.try_super_fold_with(self) + match ct.kind() { + ty::ConstKind::Infer(_) => Err(Ok(MaybeCause::Ambiguity)), + ty::ConstKind::Unevaluated(..) => { + let term = ensure_sufficient_stack(|| { + self.normalize_alias_term(ct.into(), ct.has_escaping_bound_vars()) + })?; + Ok(term.expect_const()) + } + _ => ct.try_super_fold_with(self), } } } diff --git a/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs index 5c13a871a7b8d..912c6c28a8f6a 100644 --- a/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs +++ b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs @@ -1,4 +1,5 @@ //@ compile-flags: -Znext-solver +//@ dont-require-annotations: ERROR // A regression test for #125269. We previously ended up // recursively proving `&<_ as SpeciesPackedElem>::Assoc: Typed` @@ -85,5 +86,4 @@ fn foo() {} fn main() { foo::<&_>(); - //~^ ERROR overflow evaluating the requirement `&_: Typed` } diff --git a/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.stderr b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.stderr index d350eb0f7795c..dab87c4143928 100644 --- a/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.stderr +++ b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.stderr @@ -1,15 +1,270 @@ +error[E0275]: overflow evaluating the requirement `&'b ::Ogre well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:52:18 + | +LL | &'b E::Ogre: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Cyclops well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:53:21 + | +LL | &'b E::Cyclops: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Wendigo well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:54:21 + | +LL | &'b E::Wendigo: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Cavetroll well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:55:23 + | +LL | &'b E::Cavetroll: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Mountaintroll well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:56:27 + | +LL | &'b E::Mountaintroll: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Swamptroll well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:57:24 + | +LL | &'b E::Swamptroll: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Dullahan well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:58:22 + | +LL | &'b E::Dullahan: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Werewolf well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:59:22 + | +LL | &'b E::Werewolf: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Occultsaurok well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:60:26 + | +LL | &'b E::Occultsaurok: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Mightysaurok well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:61:26 + | +LL | &'b E::Mightysaurok: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Slysaurok well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:62:23 + | +LL | &'b E::Slysaurok: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Mindflayer well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:63:24 + | +LL | &'b E::Mindflayer: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Minotaur well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:64:22 + | +LL | &'b E::Minotaur: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Tidalwarrior well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:65:26 + | +LL | &'b E::Tidalwarrior: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Yeti well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:66:18 + | +LL | &'b E::Yeti: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Harvester well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:67:23 + | +LL | &'b E::Harvester: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Blueoni well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:68:21 + | +LL | &'b E::Blueoni: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Redoni well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:69:20 + | +LL | &'b E::Redoni: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Cultistwarlord well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:70:28 + | +LL | &'b E::Cultistwarlord: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Cultistwarlock well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:71:28 + | +LL | &'b E::Cultistwarlock: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Huskbrute well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:72:23 + | +LL | &'b E::Huskbrute: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Tursus well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:73:20 + | +LL | &'b E::Tursus: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Gigasfrost well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:74:24 + | +LL | &'b E::Gigasfrost: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::AdletElder well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:75:24 + | +LL | &'b E::AdletElder: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::SeaBishop well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:76:23 + | +LL | &'b E::SeaBishop: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::HaniwaGeneral well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:77:27 + | +LL | &'b E::HaniwaGeneral: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::TerracottaBesieger well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:78:32 + | +LL | &'b E::TerracottaBesieger: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::TerracottaDemolisher well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:79:34 + | +LL | &'b E::TerracottaDemolisher: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::TerracottaPunisher well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:80:32 + | +LL | &'b E::TerracottaPunisher: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::TerracottaPursuer well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:81:31 + | +LL | &'b E::TerracottaPursuer: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0275]: overflow evaluating the requirement `&'b ::Cursekeeper well-formed` + --> $DIR/cycle-modulo-ambig-aliases.rs:82:25 + | +LL | &'b E::Cursekeeper: Typed, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`cycle_modulo_ambig_aliases`) + +error[E0284]: type annotations needed: cannot satisfy `_: '_` + --> $DIR/cycle-modulo-ambig-aliases.rs:88:11 + | +LL | foo::<&_>(); + | ^^ cannot satisfy `_: '_` + error[E0275]: overflow evaluating the requirement `&_: Typed` - --> $DIR/cycle-modulo-ambig-aliases.rs:87:11 + --> $DIR/cycle-modulo-ambig-aliases.rs:88:11 | LL | foo::<&_>(); | ^^ | note: required by a bound in `foo` - --> $DIR/cycle-modulo-ambig-aliases.rs:84:11 + --> $DIR/cycle-modulo-ambig-aliases.rs:85:11 | LL | fn foo() {} | ^^^^^ required by this bound in `foo` -error: aborting due to 1 previous error +error: aborting due to 33 previous errors -For more information about this error, try `rustc --explain E0275`. +Some errors have detailed explanations: E0275, E0284. +For more information about an error, try `rustc --explain E0275`. From ce6744045fa642a99fce4eb4c90210d01a06a258 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 1 Mar 2026 08:59:52 +0900 Subject: [PATCH 10/11] Replace PlaceholderReplacer --- .../src/solve/normalize.rs | 4 +- .../src/traits/normalize.rs | 4 +- .../src/traits/query/normalize.rs | 2 +- .../rustc_trait_selection/src/traits/util.rs | 157 +----------------- 4 files changed, 9 insertions(+), 158 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 567f660a59383..1b475ab4a88e8 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -201,7 +201,7 @@ where let result = ensure_sufficient_stack(|| self.normalize_alias_term(ty.into()))?.expect_type(); Ok(PlaceholderReplacer::replace_placeholders( - infcx, + infcx.tcx, mapped_regions, mapped_types, mapped_consts, @@ -229,7 +229,7 @@ where let result = ensure_sufficient_stack(|| self.normalize_alias_term(ct.into()))?.expect_const(); Ok(PlaceholderReplacer::replace_placeholders( - infcx, + infcx.tcx, mapped_regions, mapped_types, mapped_consts, diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 19a80893e898d..a857d971059a1 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -228,7 +228,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { .unwrap_or_else(|| proj.to_term(infcx.tcx)); PlaceholderReplacer::replace_placeholders( - infcx, + infcx.tcx, mapped_regions, mapped_types, mapped_consts, @@ -275,7 +275,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { ); PlaceholderReplacer::replace_placeholders( - infcx, + infcx.tcx, mapped_regions, mapped_types, mapped_consts, diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 2f83ee046498a..4471f1cd14eb9 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -363,7 +363,7 @@ impl<'a, 'tcx> QueryNormalizer<'a, 'tcx> { self.obligations.extend(obligations); let res = if let Some((mapped_regions, mapped_types, mapped_consts)) = maps { PlaceholderReplacer::replace_placeholders( - infcx, + infcx.tcx, mapped_regions, mapped_types, mapped_consts, diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index c3f46aa51c737..903a909458a76 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -1,18 +1,17 @@ use std::collections::VecDeque; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::FxHashSet; use rustc_hir::LangItem; use rustc_hir::def_id::DefId; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::PolyTraitObligation; pub use rustc_infer::traits::util::*; -use rustc_middle::bug; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{ self, PolyTraitPredicate, PredicatePolarity, SizedTraitKind, TraitPredicate, TraitRef, Ty, - TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + TyCtxt, TypeFoldable, TypeVisitableExt, }; -pub use rustc_next_trait_solver::placeholder::BoundVarReplacer; +pub use rustc_next_trait_solver::placeholder::{BoundVarReplacer, PlaceholderReplacer}; use rustc_span::Span; use smallvec::{SmallVec, smallvec}; use tracing::debug; @@ -205,7 +204,7 @@ pub fn with_replaced_escaping_bound_vars< BoundVarReplacer::replace_bound_vars(infcx, universe_indices, value); let result = f(value); PlaceholderReplacer::replace_placeholders( - infcx, + infcx.tcx, mapped_regions, mapped_types, mapped_consts, @@ -217,154 +216,6 @@ pub fn with_replaced_escaping_bound_vars< } } -/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. -pub struct PlaceholderReplacer<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, - mapped_regions: FxIndexMap, ty::BoundRegion<'tcx>>, - mapped_types: FxIndexMap, ty::BoundTy<'tcx>>, - mapped_consts: FxIndexMap, ty::BoundConst<'tcx>>, - universe_indices: &'a [Option], - current_index: ty::DebruijnIndex, -} - -impl<'a, 'tcx> PlaceholderReplacer<'a, 'tcx> { - pub fn replace_placeholders>>( - infcx: &'a InferCtxt<'tcx>, - mapped_regions: FxIndexMap, ty::BoundRegion<'tcx>>, - mapped_types: FxIndexMap, ty::BoundTy<'tcx>>, - mapped_consts: FxIndexMap, ty::BoundConst<'tcx>>, - universe_indices: &'a [Option], - value: T, - ) -> T { - let mut replacer = PlaceholderReplacer { - infcx, - mapped_regions, - mapped_types, - mapped_consts, - universe_indices, - current_index: ty::INNERMOST, - }; - value.fold_with(&mut replacer) - } -} - -impl<'tcx> TypeFolder> for PlaceholderReplacer<'_, 'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_binder>>( - &mut self, - t: ty::Binder<'tcx, T>, - ) -> ty::Binder<'tcx, T> { - if !t.has_placeholders() && !t.has_infer() { - return t; - } - self.current_index.shift_in(1); - let t = t.super_fold_with(self); - self.current_index.shift_out(1); - t - } - - fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> { - let r1 = match r0.kind() { - ty::ReVar(vid) => self - .infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .opportunistic_resolve_var(self.infcx.tcx, vid), - _ => r0, - }; - - let r2 = match r1.kind() { - ty::RePlaceholder(p) => { - let replace_var = self.mapped_regions.get(&p); - match replace_var { - Some(replace_var) => { - let index = self - .universe_indices - .iter() - .position(|u| matches!(u, Some(pu) if *pu == p.universe)) - .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); - let db = ty::DebruijnIndex::from_usize( - self.universe_indices.len() - index + self.current_index.as_usize() - 1, - ); - ty::Region::new_bound(self.cx(), db, *replace_var) - } - None => r1, - } - } - _ => r1, - }; - - debug!(?r0, ?r1, ?r2, "fold_region"); - - r2 - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty = self.infcx.shallow_resolve(ty); - match *ty.kind() { - ty::Placeholder(p) => { - let replace_var = self.mapped_types.get(&p); - match replace_var { - Some(replace_var) => { - let index = self - .universe_indices - .iter() - .position(|u| matches!(u, Some(pu) if *pu == p.universe)) - .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); - let db = ty::DebruijnIndex::from_usize( - self.universe_indices.len() - index + self.current_index.as_usize() - 1, - ); - Ty::new_bound(self.infcx.tcx, db, *replace_var) - } - None => { - if ty.has_infer() { - ty.super_fold_with(self) - } else { - ty - } - } - } - } - - _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self), - _ => ty, - } - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - let ct = self.infcx.shallow_resolve_const(ct); - if let ty::ConstKind::Placeholder(p) = ct.kind() { - let replace_var = self.mapped_consts.get(&p); - match replace_var { - Some(replace_var) => { - let index = self - .universe_indices - .iter() - .position(|u| matches!(u, Some(pu) if *pu == p.universe)) - .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); - let db = ty::DebruijnIndex::from_usize( - self.universe_indices.len() - index + self.current_index.as_usize() - 1, - ); - ty::Const::new_bound(self.infcx.tcx, db, *replace_var) - } - None => { - if ct.has_infer() { - ct.super_fold_with(self) - } else { - ct - } - } - } - } else { - ct.super_fold_with(self) - } - } -} - pub fn sizedness_fast_path<'tcx>( tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>, From 2bc1fa0d4b99cf5c85bb64124033c44db8c3c11b Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 1 Mar 2026 09:06:09 +0900 Subject: [PATCH 11/11] Propagate error --- compiler/rustc_next_trait_solver/src/solve/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index c2fa25080cc67..73741ea50f9df 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -111,7 +111,7 @@ where opaque_types_jank: OpaqueTypesJank::AllGood, }); } - Err(Err(NoSolution)) => ty, + Err(Err(NoSolution)) => return Err(NoSolution), }; self.register_ty_outlives(ty, lt);