From 8df8a69794574a4575a86338f08c984f2ec302a0 Mon Sep 17 00:00:00 2001 From: lcnr Date: Sun, 17 May 2026 14:34:32 +0200 Subject: [PATCH 1/8] eagerly normalize things in solver --- compiler/rustc_infer/src/infer/mod.rs | 26 ++- .../rustc_next_trait_solver/src/normalize.rs | 97 +++++---- .../src/solve/alias_relate.rs | 4 +- .../src/solve/assembly/mod.rs | 6 +- .../src/solve/effect_goals.rs | 16 +- .../src/solve/eval_ctxt/mod.rs | 190 +++++++----------- .../rustc_next_trait_solver/src/solve/mod.rs | 4 +- .../src/solve/normalizes_to/free_alias.rs | 2 +- .../src/solve/normalizes_to/inherent.rs | 2 +- .../src/solve/normalizes_to/mod.rs | 19 +- .../src/solve/normalizes_to/opaque_types.rs | 6 +- .../src/solve/project_goals.rs | 2 +- .../src/solve/trait_goals.rs | 30 +-- .../src/solve/fulfill.rs | 13 +- .../src/solve/normalize.rs | 115 ++++++----- compiler/rustc_type_ir/src/error.rs | 8 +- ...-on-failed-eval-with-vars-fail.next.stderr | 9 +- ...ambiguous-on-failed-eval-with-vars-fail.rs | 4 +- .../negative-coherence-ice-140609.rs | 14 -- .../specialization-fuzzing-ice-133639.rs | 18 -- .../mgca/free-const-recursive.rs | 2 +- .../mgca/free-const-recursive.stderr | 6 +- .../mgca/projection-const-recursive.rs | 4 +- .../mgca/projection-const-recursive.stderr | 6 +- .../delayed-obligations-emit.next.stderr | 1 + ...-in-higher-ranked-fn-signature.next.stderr | 6 +- .../in-trait/alias-bounds-when-not-wf.rs | 1 + .../in-trait/alias-bounds-when-not-wf.stderr | 8 +- tests/ui/traits/next-solver/async.fail.stderr | 4 +- tests/ui/traits/next-solver/async.rs | 2 +- .../coercion/non-wf-in-coerce-pointers.rs | 1 + .../coercion/non-wf-in-coerce-pointers.stderr | 10 +- .../find-param-recursion-issue-152716.rs | 2 +- .../find-param-recursion-issue-152716.stderr | 2 +- .../lazy-nested-obligations-2.next.stderr | 14 +- .../traits/next-solver/more-object-bound.rs | 2 +- .../next-solver/more-object-bound.stderr | 12 +- .../normalize/normalize-param-env-2.stderr | 38 +++- .../normalize-param-env-4.next.stderr | 18 +- .../recursive-self-normalization-2.rs | 2 +- .../recursive-self-normalization-2.stderr | 6 +- ...lize-diverging-alias-in-struct.next.stderr | 7 +- .../normalize-diverging-alias-in-struct.rs | 2 +- .../solver-cycles/129541-recursive-struct.rs | 1 - .../wf/return-type-non-wf-no-ice.next.stderr | 18 +- tests/ui/wf/return-type-non-wf-no-ice.rs | 3 +- 46 files changed, 406 insertions(+), 357 deletions(-) delete mode 100644 tests/ui/const-generics/generic_const_exprs/negative-coherence-ice-140609.rs delete mode 100644 tests/ui/const-generics/generic_const_exprs/specialization-fuzzing-ice-133639.rs diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index c0029719c6e12..9b1d0abdd016f 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -28,10 +28,11 @@ use rustc_middle::traits::select; use rustc_middle::traits::solve::Goal; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{ - self, BoundVarReplacerDelegate, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, - GenericArgsRef, GenericParamDefKind, InferConst, IntVid, OpaqueTypeKey, ProvisionalHiddenType, - PseudoCanonicalInput, Term, TermKind, Ty, TyCtxt, TyVid, TypeFoldable, TypeFolder, - TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypingEnv, TypingMode, fold_regions, + self, AliasTermKind, BoundVarReplacerDelegate, ConstVid, FloatVid, GenericArg, GenericArgKind, + GenericArgs, GenericArgsRef, GenericParamDefKind, InferConst, IntVid, OpaqueTypeKey, + ProvisionalHiddenType, PseudoCanonicalInput, Term, TermKind, Ty, TyCtxt, TyVid, TypeFoldable, + TypeFolder, TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypingEnv, TypingMode, + fold_regions, }; use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_type_ir::MayBeErased; @@ -962,6 +963,23 @@ impl<'tcx> InferCtxt<'tcx> { } } + pub fn next_term_var_for_alias( + &self, + alias: ty::AliasTerm<'tcx>, + span: Span, + ) -> ty::Term<'tcx> { + match alias.kind(self.tcx) { + AliasTermKind::ProjectionTy { .. } + | AliasTermKind::InherentTy { .. } + | AliasTermKind::OpaqueTy { .. } + | AliasTermKind::FreeTy { .. } => self.next_ty_var(span).into(), + AliasTermKind::UnevaluatedConst { .. } + | AliasTermKind::ProjectionConst { .. } + | AliasTermKind::FreeConst { .. } + | AliasTermKind::InherentConst { .. } => 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 diff --git a/compiler/rustc_next_trait_solver/src/normalize.rs b/compiler/rustc_next_trait_solver/src/normalize.rs index 7506591d6fb5d..84af3e66a7a13 100644 --- a/compiler/rustc_next_trait_solver/src/normalize.rs +++ b/compiler/rustc_next_trait_solver/src/normalize.rs @@ -1,9 +1,10 @@ +use std::fmt::Debug; + use rustc_type_ir::data_structures::ensure_sufficient_stack; use rustc_type_ir::inherent::*; -use rustc_type_ir::solve::{Goal, NoSolution}; use rustc_type_ir::{ - self as ty, Binder, FallibleTypeFolder, InferConst, InferCtxtLike, InferTy, Interner, - TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + self as ty, AliasTerm, Binder, FallibleTypeFolder, InferConst, InferCtxtLike, InferTy, + Interner, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UniverseIndex, }; use tracing::instrument; @@ -13,7 +14,7 @@ use crate::placeholder::{BoundVarReplacer, PlaceholderReplacer}; /// This folder normalizes value and collects ambiguous goals. /// /// Note that for ambiguous alias which contains escaping bound vars, -/// we just return the original alias and don't collect the ambiguous goal. +/// we just return the original alias. pub struct NormalizationFolder<'a, Infcx, I, F> where Infcx: InferCtxtLike, @@ -21,11 +22,16 @@ where { infcx: &'a Infcx, universes: Vec>, - stalled_goals: Vec>, normalize: F, } -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Debug)] +pub enum NormalizationWasAmbiguous { + Yes, + No, +} + +#[derive(PartialEq, Eq, Debug)] enum HasEscapingBoundVars { Yes, No, @@ -97,34 +103,25 @@ where } } -impl<'a, Infcx, I, F> NormalizationFolder<'a, Infcx, I, F> +impl<'a, Infcx, I, F, E> NormalizationFolder<'a, Infcx, I, F> where Infcx: InferCtxtLike, I: Interner, - F: FnMut(I::Term) -> Result<(I::Term, Option>), NoSolution>, + F: FnMut(AliasTerm) -> Result<(I::Term, NormalizationWasAmbiguous), E>, { - pub fn new( - infcx: &'a Infcx, - universes: Vec>, - stalled_goals: Vec>, - normalize: F, - ) -> Self { - Self { infcx, universes, stalled_goals, normalize } - } - - pub fn stalled_goals(self) -> Vec> { - self.stalled_goals + pub fn new(infcx: &'a Infcx, universes: Vec>, normalize: F) -> Self { + Self { infcx, universes, normalize } } fn normalize_alias_term( &mut self, - alias_term: I::Term, + alias_term: AliasTerm, has_escaping: HasEscapingBoundVars, - ) -> Result { + ) -> Result { let current_universe = self.infcx.universe(); self.infcx.create_next_universe(); - let (normalized, ambig_goal) = (self.normalize)(alias_term)?; + let (normalized, normalization_was_ambiguous) = (self.normalize)(alias_term)?; // Return ambiguous higher ranked alias as is, if // - it contains escaping vars, and @@ -134,27 +131,29 @@ where // referencing the temporary placeholders. // // We can normalize the ambiguous alias again after the binder is instantiated. - if ambig_goal.is_some() && has_escaping == HasEscapingBoundVars::Yes { + if NormalizationWasAmbiguous::Yes == normalization_was_ambiguous + && has_escaping == HasEscapingBoundVars::Yes + { let mut visitor = MaxUniverse::new(self.infcx); normalized.visit_with(&mut visitor); let max_universe = visitor.max_universe(); if current_universe.cannot_name(max_universe) { - return Ok(alias_term); + return Ok(alias_term.to_term(self.infcx.cx())); } } - self.stalled_goals.extend(ambig_goal); Ok(normalized) } } -impl<'a, Infcx, I, F> FallibleTypeFolder for NormalizationFolder<'a, Infcx, I, F> +impl<'a, Infcx, I, F, E> FallibleTypeFolder for NormalizationFolder<'a, Infcx, I, F> where Infcx: InferCtxtLike, I: Interner, - F: FnMut(I::Term) -> Result<(I::Term, Option>), NoSolution>, + F: FnMut(AliasTerm) -> Result<(I::Term, NormalizationWasAmbiguous), E>, + E: Debug, { - type Error = NoSolution; + type Error = E; fn cx(&self) -> I { self.infcx.cx() @@ -180,26 +179,26 @@ where // With eager normalization, we should normalize the args of alias before // normalizing the alias itself. let ty = ty.try_super_fold_with(self)?; - let ty::Alias(..) = ty.kind() else { return Ok(ty) }; + let ty::Alias(alias_ty) = ty.kind() else { return Ok(ty) }; if ty.has_escaping_bound_vars() { - let (ty, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ty); - let result = ensure_sufficient_stack(|| { - self.normalize_alias_term(ty.into(), HasEscapingBoundVars::Yes) - })? - .expect_ty(); - Ok(PlaceholderReplacer::replace_placeholders( + let (alias_ty, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, alias_ty); + let normalized_term = ensure_sufficient_stack(|| { + self.normalize_alias_term(alias_ty.into(), HasEscapingBoundVars::Yes) + })?; + let normalized_ty = PlaceholderReplacer::replace_placeholders( infcx, mapped_regions, mapped_types, mapped_consts, &self.universes, - result, - )) + normalized_term.expect_ty(), + ); + Ok(normalized_ty) } else { Ok(ensure_sufficient_stack(|| { - self.normalize_alias_term(ty.into(), HasEscapingBoundVars::No) + self.normalize_alias_term(alias_ty.into(), HasEscapingBoundVars::No) })? .expect_ty()) } @@ -215,13 +214,16 @@ where // With eager normalization, we should normalize the args of alias before // normalizing the alias itself. let ct = ct.try_super_fold_with(self)?; - let ty::ConstKind::Unevaluated(..) = ct.kind() else { return Ok(ct) }; + let ty::ConstKind::Unevaluated(uv) = ct.kind() else { return Ok(ct) }; if ct.has_escaping_bound_vars() { - let (ct, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ct); + let (uv, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, uv); let result = ensure_sufficient_stack(|| { - self.normalize_alias_term(ct.into(), HasEscapingBoundVars::Yes) + self.normalize_alias_term( + AliasTerm::from_unevaluated_const(infcx.cx(), uv), + HasEscapingBoundVars::Yes, + ) })? .expect_const(); Ok(PlaceholderReplacer::replace_placeholders( @@ -234,9 +236,16 @@ where )) } else { Ok(ensure_sufficient_stack(|| { - self.normalize_alias_term(ct.into(), HasEscapingBoundVars::No) + self.normalize_alias_term( + AliasTerm::from_unevaluated_const(infcx.cx(), uv), + HasEscapingBoundVars::No, + ) })? .expect_const()) } } + + fn try_fold_predicate(&mut self, p: I::Predicate) -> Result { + if p.allow_normalization() { p.try_super_fold_with(self) } else { Ok(p) } + } } diff --git a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs index 47d33985f9d36..1101c8c1b075d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs +++ b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs @@ -53,7 +53,7 @@ where self.add_goal( GoalSource::TypeRelating, goal.with(cx, ty::NormalizesTo { alias, term }), - ); + )?; term } else { lhs @@ -65,7 +65,7 @@ where self.add_goal( GoalSource::TypeRelating, goal.with(cx, ty::NormalizesTo { alias, term }), - ); + )?; term } else { rhs diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 7f3627c6db54e..f58e20d6cccdc 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -68,7 +68,7 @@ where ) -> Result, NoSolutionOrRerunNonErased> { Self::probe_and_match_goal_against_assumption(ecx, parent_source, goal, assumption, |ecx| { for (nested_source, goal) in requirements { - ecx.add_goal(nested_source, goal); + ecx.add_goal(nested_source, goal)?; } ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) @@ -113,7 +113,7 @@ where bounds, ) { Ok(requirements) => { - ecx.add_goals(GoalSource::ImplWhereBound, requirements); + ecx.add_goals(GoalSource::ImplWhereBound, requirements)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } Err(_) => { @@ -966,7 +966,7 @@ where elaborate::elaborate(cx, [predicate]) .skip(1) .map(|predicate| goal.with(cx, predicate)), - ); + )?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } }) diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 0a260f97e5164..8575302df309e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -117,7 +117,7 @@ where .skip_norm_wip(), ) }), - ); + )?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }, )); @@ -169,7 +169,7 @@ where .iter_instantiated(cx, impl_args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)); - ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); + ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds)?; // For this impl to be `const`, we need to check its `[const]` bounds too. let const_conditions = cx @@ -183,7 +183,7 @@ where .skip_norm_wip(), ) }); - ecx.add_goals(GoalSource::ImplWhereBound, const_conditions); + ecx.add_goals(GoalSource::ImplWhereBound, const_conditions)?; then(ecx, certainty) }) @@ -234,8 +234,8 @@ where // `GoalSource::ImplWhereClause` here would be incorrect, as we also // impl them, which means we're "stepping out of the impl constructor" // again. To handle this, we treat these cycles as ambiguous for now. - ecx.add_goals(GoalSource::Misc, where_clause_bounds); - ecx.add_goals(GoalSource::Misc, const_conditions); + ecx.add_goals(GoalSource::Misc, where_clause_bounds)?; + ecx.add_goals(GoalSource::Misc, const_conditions)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -270,8 +270,8 @@ where ), ) }), - ); - }); + ) + })?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) @@ -424,7 +424,7 @@ where .to_host_effect_clause(cx, goal.predicate.constness), ) }), - ); + )?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } 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 54d306466cf5b..3693a18f305f6 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::StableHash; -use rustc_type_ir::data_structures::{HashMap, HashSet}; +use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; use rustc_type_ir::region_constraint::RegionConstraint; use rustc_type_ir::relate::Relate; @@ -15,9 +15,9 @@ use rustc_type_ir::solve::{ RerunNonErased, RerunReason, RerunResultExt, SmallCopyList, }; use rustc_type_ir::{ - self as ty, CanonicalVarValues, ClauseKind, InferCtxtLike, Interner, MayBeErased, - OpaqueTypeKey, PredicateKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, + self as ty, AliasRelationDirection, AliasTermKind, CanonicalVarValues, ClauseKind, + InferCtxtLike, Interner, MayBeErased, OpaqueTypeKey, PredicateKind, TypeFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, }; use tracing::{Level, debug, instrument, trace, warn}; @@ -28,6 +28,7 @@ use crate::canonical::{ }; use crate::coherence; use crate::delegate::SolverDelegate; +use crate::normalize::{NormalizationFolder, NormalizationWasAmbiguous}; use crate::placeholder::BoundVarReplacer; use crate::resolve::eager_resolve_vars; use crate::solve::search_graph::SearchGraph; @@ -1029,11 +1030,19 @@ where } #[instrument(level = "debug", skip(self))] - pub(super) fn add_goal(&mut self, source: GoalSource, mut goal: Goal) { - goal.predicate = - goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, source, goal.param_env)); + pub(super) fn add_goal( + &mut self, + source: GoalSource, + mut goal: Goal, + ) -> Result<(), NoSolutionOrRerunNonErased> { + goal.predicate = self.normalize( + GoalSource::NormalizeGoal(self.step_kind_for_source(source)), + goal.param_env, + goal.predicate, + )?; self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); self.nested_goals.push((source, goal, None)); + Ok(()) } #[instrument(level = "trace", skip(self, goals))] @@ -1041,10 +1050,11 @@ where &mut self, source: GoalSource, goals: impl IntoIterator>, - ) { + ) -> Result<(), NoSolutionOrRerunNonErased> { for goal in goals { - self.add_goal(source, goal); + self.add_goal(source, goal)?; } + Ok(()) } pub(super) fn next_region_var(&mut self) -> I::Region { @@ -1074,6 +1084,19 @@ where } } + pub(super) fn next_infer_for_alias(&mut self, alias: ty::AliasTerm) -> I::Term { + match alias.kind(self.cx()) { + AliasTermKind::ProjectionTy { .. } + | AliasTermKind::InherentTy { .. } + | AliasTermKind::OpaqueTy { .. } + | AliasTermKind::FreeTy { .. } => self.next_ty_infer().into(), + AliasTermKind::UnevaluatedConst { .. } + | AliasTermKind::ProjectionConst { .. } + | AliasTermKind::FreeConst { .. } + | AliasTermKind::InherentConst { .. } => self.next_const_infer().into(), + } + } + /// Is the projection predicate is of the form `exists ::Assoc = T`. /// /// This is the case if the `term` does not occur in any other part of the predicate @@ -1207,7 +1230,7 @@ where param_env: I::ParamEnv, lhs: T, rhs: T, - ) -> Result<(), NoSolution> { + ) -> Result<(), NoSolutionOrRerunNonErased> { self.relate(param_env, lhs, ty::Variance::Invariant, rhs) } @@ -1223,7 +1246,7 @@ where alias: ty::AliasTerm, variance: ty::Variance, term: I::Term, - ) -> Result<(), NoSolution> { + ) -> Result<(), NoSolutionOrRerunNonErased> { // NOTE: this check is purely an optimization, the structural eq would // always fail if the term is not an inference variable. if term.is_infer() { @@ -1248,7 +1271,7 @@ where debug_assert!(obligations.is_empty()); self.relate(param_env, alias, variance, rigid_ctor) } else { - Err(NoSolution) + Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) } } @@ -1261,7 +1284,7 @@ where param_env: I::ParamEnv, lhs: T, rhs: T, - ) -> Result<(), NoSolution> { + ) -> Result<(), NoSolutionOrRerunNonErased> { let result = self.delegate.eq_structurally_relating_aliases( param_env, lhs, @@ -1278,7 +1301,7 @@ where param_env: I::ParamEnv, sub: T, sup: T, - ) -> Result<(), NoSolution> { + ) -> Result<(), NoSolutionOrRerunNonErased> { self.relate(param_env, sub, ty::Variance::Covariant, sup) } @@ -1289,7 +1312,7 @@ where lhs: T, variance: ty::Variance, rhs: T, - ) -> Result<(), NoSolution> { + ) -> Result<(), NoSolutionOrRerunNonErased> { let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?; for &goal in goals.iter() { let source = match goal.predicate.kind().skip_binder() { @@ -1300,7 +1323,7 @@ where ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => GoalSource::Misc, p => unreachable!("unexpected nested goal in `relate`: {p:?}"), }; - self.add_goal(source, goal); + self.add_goal(source, goal)?; } Ok(()) } @@ -1441,7 +1464,7 @@ where opaque_args: I::GenericArgs, param_env: I::ParamEnv, hidden_ty: I::Ty, - ) { + ) -> Result<(), NoSolutionOrRerunNonErased> { let mut goals = Vec::new(); self.delegate.add_item_bounds_for_hidden_type( opaque_def_id, @@ -1450,7 +1473,8 @@ where hidden_ty, &mut goals, ); - self.add_goals(GoalSource::AliasWellFormed, goals); + self.add_goals(GoalSource::AliasWellFormed, goals)?; + Ok(()) } // Try to evaluate a const, or return `None` if the const is too generic. @@ -1731,112 +1755,38 @@ where ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } } -} - -/// Eagerly replace aliases with inference variables, emitting `AliasRelate` -/// goals, used when adding goals to the `EvalCtxt`. We compute the -/// `AliasRelate` goals before evaluating the actual goal to get all the -/// constraints we can. -/// -/// This is a performance optimization to more eagerly detect cycles during trait -/// solving. See tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs. -/// -/// The emitted goals get evaluated in the context of the parent goal; by -/// replacing aliases in nested goals we essentially pull the normalization out of -/// the nested goal. We want to treat the goal as if the normalization still happens -/// inside of the nested goal by inheriting the `step_kind` of the nested goal and -/// storing it in the `GoalSource` of the emitted `AliasRelate` goals. -/// This is necessary for tests/ui/sized/coinductive-1.rs to compile. -struct ReplaceAliasWithInfer<'me, 'a, D, I> -where - D: SolverDelegate, - I: Interner, -{ - ecx: &'me mut EvalCtxt<'a, D>, - param_env: I::ParamEnv, - normalization_goal_source: GoalSource, - cache: HashMap, -} -impl<'me, 'a, D, I> ReplaceAliasWithInfer<'me, 'a, D, I> -where - D: SolverDelegate, - I: Interner, -{ - fn new( - ecx: &'me mut EvalCtxt<'a, D>, - for_goal_source: GoalSource, + fn normalize>( + &mut self, + source: GoalSource, param_env: I::ParamEnv, - ) -> Self { - let step_kind = ecx.step_kind_for_source(for_goal_source); - ReplaceAliasWithInfer { - ecx, - param_env, - normalization_goal_source: GoalSource::NormalizeGoal(step_kind), - cache: Default::default(), - } - } -} - -impl TypeFolder for ReplaceAliasWithInfer<'_, '_, D, I> -where - D: SolverDelegate, - I: Interner, -{ - fn cx(&self) -> I { - self.ecx.cx() - } - - fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { - match ty.kind() { - ty::Alias(..) if !ty.has_escaping_bound_vars() => { - let infer_ty = self.ecx.next_ty_infer(); - let normalizes_to = ty::PredicateKind::AliasRelate( - ty.into(), - infer_ty.into(), - ty::AliasRelationDirection::Equate, - ); - self.ecx.add_goal( - self.normalization_goal_source, - Goal::new(self.cx(), self.param_env, normalizes_to), - ); - infer_ty - } - _ => { - if !ty.has_aliases() { - ty - } else if let Some(&entry) = self.cache.get(&ty) { - return entry; - } else { - let res = ty.super_fold_with(self); - assert!(self.cache.insert(ty, res).is_none()); - res + value: T, + ) -> Result { + let value = self.delegate.resolve_vars_if_possible(value); + // To drop the mutable borrow of self early. + let infcx = self.delegate.deref(); + let mut folder = NormalizationFolder::new(infcx, vec![], |alias_term| { + let infer_term = self.next_infer_for_alias(alias_term); + let pred = ty::PredicateKind::AliasRelate( + alias_term.to_term(infcx.cx()), + infer_term.into(), + AliasRelationDirection::Equate, + ); + let goal = Goal::new(self.cx(), param_env, pred); + self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); + let GoalEvaluation { goal, certainty, has_changed: _, stalled_on } = + self.evaluate_goal(source, goal, None)?; + let normalization_was_ambiguous = match certainty { + Certainty::Yes => NormalizationWasAmbiguous::No, + Certainty::Maybe(_) => { + self.nested_goals.push((source, goal, stalled_on)); + NormalizationWasAmbiguous::Yes } - } - } - } - - fn fold_const(&mut self, ct: I::Const) -> I::Const { - match ct.kind() { - ty::ConstKind::Unevaluated(..) if !ct.has_escaping_bound_vars() => { - let infer_ct = self.ecx.next_const_infer(); - let normalizes_to = ty::PredicateKind::AliasRelate( - ct.into(), - infer_ct.into(), - ty::AliasRelationDirection::Equate, - ); - self.ecx.add_goal( - self.normalization_goal_source, - Goal::new(self.cx(), self.param_env, normalizes_to), - ); - infer_ct - } - _ => ct.super_fold_with(self), - } - } + }; - fn fold_predicate(&mut self, predicate: I::Predicate) -> I::Predicate { - if predicate.allow_normalization() { predicate.super_fold_with(self) } else { predicate } + Ok((self.resolve_vars_if_possible(infer_term), normalization_was_ambiguous)) + }); + value.try_fold_with(&mut folder) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index fbd6bdfa0a989..ccfc16403d066 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -171,7 +171,7 @@ where ) -> QueryResultOrRerunNonErased { match self.well_formed_goals(goal.param_env, goal.predicate) { Some(goals) => { - self.add_goals(GoalSource::Misc, goals); + self.add_goals(GoalSource::Misc, goals)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS), @@ -381,7 +381,7 @@ where ); // We normalize the self type to be able to relate it with // types from candidates. - self.add_goal(GoalSource::TypeRelating, alias_relate_goal); + self.add_goal(GoalSource::TypeRelating, alias_relate_goal)?; self.try_evaluate_added_goals()?; Ok(self.resolve_vars_if_possible(normalized_term)) } else { diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs index d68c7dd11d1d9..147626ec2b2c1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs @@ -29,7 +29,7 @@ where .iter_instantiated(cx, free_alias.args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)), - ); + )?; let actual = match free_alias.kind(cx) { ty::AliasTermKind::FreeTy { def_id } => { diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs index 2f44cc42a5d71..149b24264ce49 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs @@ -52,7 +52,7 @@ where .iter_instantiated(cx, inherent_args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)), - ); + )?; let normalized = match inherent.kind(cx) { ty::AliasTermKind::InherentTy { def_id } => { diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index e9a4d7e5919ad..54b71c2d8f3ae 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -202,7 +202,7 @@ where .iter_instantiated(cx, goal.predicate.alias.args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)), - ); + )?; then(ecx) } @@ -267,7 +267,7 @@ where .iter_instantiated(cx, impl_args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)); - ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); + ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds)?; // Bail if the nested goals don't hold here. This is to avoid unnecessarily // computing the `type_of` query for associated types that never apply, as @@ -284,7 +284,7 @@ where .iter_instantiated(cx, goal.predicate.alias.args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)), - ); + )?; let error_response = |ecx: &mut EvalCtxt<'_, D>, guar| { let error_term = match goal.predicate.alias.kind(cx) { @@ -316,7 +316,10 @@ where // This is not the case here and we only prefer adding an ambiguous // nested goal for consistency. ty::TypingMode::Coherence => { - ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous)); + ecx.add_goal( + GoalSource::Misc, + goal.with(cx, PredicateKind::Ambiguous), + )?; return ecx .evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into); @@ -361,7 +364,7 @@ where // would be relevant if any of the nested goals refer to the `term`. // This is not the case here and we only prefer adding an ambiguous // nested goal for consistency. - ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous)); + ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous))?; return then(ecx, Certainty::Yes).map_err(Into::into); } else { ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); @@ -705,7 +708,7 @@ where cx.require_trait_lang_item(SolverTraitLangItem::Sized), [I::GenericArg::from(goal.predicate.self_ty())], ); - ecx.add_goal(GoalSource::Misc, goal.with(cx, sized_predicate)); + ecx.add_goal(GoalSource::Misc, goal.with(cx, sized_predicate))?; ecx.instantiate_normalizes_to_term(goal, Ty::new_unit(cx).into()); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }); @@ -1047,7 +1050,7 @@ where impl_args: I::GenericArgs, impl_trait_ref: rustc_type_ir::TraitRef, target_container_def_id: I::DefId, - ) -> Result { + ) -> Result { let cx = self.cx(); Ok(if target_container_def_id == impl_trait_ref.def_id.into() { // Default value from the trait definition. No need to rebase. @@ -1072,7 +1075,7 @@ where .iter_instantiated(cx, target_args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)), - ); + )?; goal.predicate.alias.args.rebase_onto(cx, impl_trait_ref.def_id.into(), target_args) }) } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index 57d10b4ac1fe1..951584d5becde 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -32,12 +32,12 @@ where opaque_ty.args, goal.param_env, expected, - ); + )?; // Trying to normalize an opaque type during coherence is always ambiguous. // We add a nested ambiguous goal here instead of using `Certainty::AMBIGUOUS`. // This allows us to return the nested goals to the parent `AliasRelate` goal. // This can then allow nested goals to fail after we've constrained the `term`. - self.add_goal(GoalSource::Misc, goal.with(cx, ty::PredicateKind::Ambiguous)); + self.add_goal(GoalSource::Misc, goal.with(cx, ty::PredicateKind::Ambiguous))?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into) } @@ -109,7 +109,7 @@ where normalized_args, goal.param_env, expected, - ); + )?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into) } diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals.rs index af6d0aad25597..7a70e7d29005c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals.rs @@ -26,7 +26,7 @@ where ), ); // A projection goal holds if the alias is equal to the expected term. - self.add_goal(GoalSource::TypeRelating, goal); + self.add_goal(GoalSource::TypeRelating, goal)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index f3ace5a70c69c..a9b042bae313e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -106,7 +106,7 @@ where .iter_instantiated(cx, impl_args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)); - ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); + ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds)?; // We currently elaborate all supertrait outlives obligations from impls. // This can be removed when we actually do coinduction correctly, and prove @@ -117,7 +117,7 @@ where .iter_instantiated(cx, impl_args) .map(Unnormalized::skip_norm_wip) .map(|pred| goal.with(cx, pred)), - ); + )?; then(ecx, maximal_certainty).map_err(Into::into) }) @@ -286,7 +286,7 @@ where // `GoalSource::ImplWhereClause` here would be incorrect, as we also // impl them, which means we're "stepping out of the impl constructor" // again. To handle this, we treat these cycles as ambiguous for now. - ecx.add_goals(GoalSource::Misc, nested_obligations); + ecx.add_goals(GoalSource::Misc, nested_obligations)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -736,13 +736,13 @@ where tys.iter().map(|elem_ty| { goal.with(cx, ty::TraitRef::new(cx, goal.predicate.def_id(), [elem_ty])) }), - ); + )?; } ty::Array(elem_ty, _) => { ecx.add_goal( GoalSource::ImplWhereBound, goal.with(cx, ty::TraitRef::new(cx, goal.predicate.def_id(), [elem_ty])), - ); + )?; } // All other types implement `BikeshedGuaranteedNoDrop` only if @@ -783,7 +783,7 @@ where [ty], ), ), - ); + )?; } ty::Bound(..) @@ -865,6 +865,8 @@ where } } + // FIXME(field_projections): This function does some questionable incomplete stuff by + // returning `Err(NoSolution)` on ambiguity. fn consider_builtin_field_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, @@ -890,14 +892,14 @@ where param_env: goal.param_env, predicate: TraitRef::new(ecx.cx(), sized_trait, [base]).upcast(ecx.cx()), }, - ); + )?; ecx.add_goal( GoalSource::ImplWhereBound, Goal { param_env: goal.param_env, predicate: TraitRef::new(ecx.cx(), sized_trait, [ty]).upcast(ecx.cx()), }, - ); + )?; ecx.try_evaluate_added_goals()? == Certainty::Yes } && match base.kind() { @@ -1015,7 +1017,7 @@ where ecx.add_goals( GoalSource::ImplWhereBound, b_data.iter().map(|pred| goal.with(cx, pred.with_self_ty(cx, a_ty))), - ); + )?; // The type must be `Sized` to be unsized. ecx.add_goal( @@ -1028,10 +1030,10 @@ where [a_ty], ), ), - ); + )?; // The type must outlive the lifetime of the `dyn` we're unsizing into. - ecx.add_goal(GoalSource::Misc, goal.with(cx, ty::OutlivesPredicate(a_ty, b_region))); + ecx.add_goal(GoalSource::Misc, goal.with(cx, ty::OutlivesPredicate(a_ty, b_region)))?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -1151,7 +1153,7 @@ where ecx.add_goal( GoalSource::ImplWhereBound, Goal::new(ecx.cx(), param_env, ty::OutlivesPredicate(a_region, b_region)), - ); + )?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into) }) @@ -1232,7 +1234,7 @@ where [a_tail_ty, b_tail_ty], ), ), - ); + )?; self.probe_builtin_trait_candidate(BuiltinImplSource::Misc) .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } @@ -1372,7 +1374,7 @@ where .collect::>() }, ); - ecx.add_goals(GoalSource::ImplWhereBound, goals); + ecx.add_goals(GoalSource::ImplWhereBound, goals)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index ed345d98b5392..6f4bb4e1f1f0e 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -6,7 +6,7 @@ use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::{ FromSolverError, PredicateObligation, PredicateObligations, TraitEngine, }; -use rustc_middle::ty::{TyCtxt, TypeVisitableExt, TypingMode}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_next_trait_solver::delegate::SolverDelegate as _; use rustc_next_trait_solver::solve::{ GoalEvaluation, GoalStalledOn, HasChanged, MaybeInfo, SolverDelegateEvalExt as _, @@ -18,7 +18,7 @@ use tracing::instrument; use self::derive_errors::*; use super::Certainty; use super::delegate::SolverDelegate; -use crate::traits::{FulfillmentError, ScrubbedTraitError}; +use crate::traits::{FulfillmentError, ObligationCtxt, ScrubbedTraitError}; mod derive_errors; @@ -151,9 +151,16 @@ where fn register_predicate_obligation( &mut self, infcx: &InferCtxt<'tcx>, - obligation: PredicateObligation<'tcx>, + mut obligation: PredicateObligation<'tcx>, ) { assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + let ocx = ObligationCtxt::new(infcx); + obligation.predicate = ocx.normalize( + &obligation.cause, + obligation.param_env, + ty::Unnormalized::new_wip(obligation.predicate), + ); + self.register_predicate_obligations(infcx, ocx.into_pending_obligations()); self.obligations.register(obligation, None); } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 7c21dc161a1ec..e25a7e8319491 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -6,11 +6,11 @@ use rustc_infer::traits::{ }; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{ - self, Binder, Flags, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, - UniverseIndex, Unnormalized, + self, AliasTerm, Binder, Flags, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, UniverseIndex, Unnormalized, }; -use rustc_next_trait_solver::normalize::NormalizationFolder; -use rustc_next_trait_solver::solve::SolverDelegateEvalExt; +use rustc_next_trait_solver::normalize::{NormalizationFolder, NormalizationWasAmbiguous}; +use rustc_next_trait_solver::solve::{NoSolution, SolverDelegateEvalExt}; use super::{FulfillmentCtxt, NextSolverError}; use crate::solve::{Certainty, SolverDelegate}; @@ -43,37 +43,47 @@ where let value = value.skip_normalization(); let value = infcx.resolve_vars_if_possible(value); let original_value = value.clone(); - let mut folder = - NormalizationFolder::new(infcx, universes.clone(), Default::default(), |alias_term| { - let delegate = <&SolverDelegate<'tcx>>::from(infcx); - let infer_term = delegate.next_term_var_of_kind(alias_term, at.cause.span); - let predicate = ty::PredicateKind::AliasRelate( - alias_term.into(), - infer_term.into(), - ty::AliasRelationDirection::Equate, - ); - let goal = Goal::new(infcx.tcx, at.param_env, predicate); - let result = delegate.evaluate_root_goal(goal, at.cause.span, None)?; - let normalized = infcx.resolve_vars_if_possible(infer_term); - let stalled_goal = match result.certainty { - Certainty::Yes => None, - Certainty::Maybe { .. } => Some(infcx.resolve_vars_if_possible(result.goal)), - }; - Ok((normalized, stalled_goal)) - }); - if let Ok(value) = value.try_fold_with(&mut folder) { - let obligations = folder - .stalled_goals() - .into_iter() - .map(|goal| { - Obligation::new(infcx.tcx, at.cause.clone(), goal.param_env, goal.predicate) - }) - .collect(); - Normalized { value, obligations } - } else { - let mut replacer = ReplaceAliasWithInfer { at, obligations: Default::default(), universes }; - let value = original_value.fold_with(&mut replacer); - Normalized { value, obligations: replacer.obligations } + let mut stalled_goals = vec![]; + let mut folder = NormalizationFolder::new(infcx, universes.clone(), |alias_term| { + let delegate = <&SolverDelegate<'tcx>>::from(infcx); + let infer_term = delegate.next_term_var_for_alias(alias_term, at.cause.span); + let predicate = ty::PredicateKind::AliasRelate( + alias_term.to_term(infcx.tcx), + infer_term.into(), + ty::AliasRelationDirection::Equate, + ); + let goal = Goal::new(infcx.tcx, at.param_env, predicate); + let result = match delegate.evaluate_root_goal(goal, at.cause.span, None) { + Ok(result) => result, + Err(err) => return Err(err), + }; + let normalized = infcx.resolve_vars_if_possible(infer_term); + let normalization_was_ambiguous = match result.certainty { + Certainty::Yes => NormalizationWasAmbiguous::No, + Certainty::Maybe { .. } => { + stalled_goals.push(infcx.resolve_vars_if_possible(goal)); + NormalizationWasAmbiguous::Yes + } + }; + Ok((normalized, normalization_was_ambiguous)) + }); + + match value.try_fold_with(&mut folder) { + Ok(value) => { + let obligations = stalled_goals + .into_iter() + .map(|goal| { + Obligation::new(infcx.tcx, at.cause.clone(), goal.param_env, goal.predicate) + }) + .collect(); + Normalized { value, obligations } + } + Err(NoSolution) => { + let mut replacer = + ReplaceAliasWithInfer { at, obligations: Default::default(), universes }; + let value = original_value.fold_with(&mut replacer); + Normalized { value, obligations: replacer.obligations } + } } } @@ -84,15 +94,15 @@ struct ReplaceAliasWithInfer<'me, 'tcx> { } impl<'me, 'tcx> ReplaceAliasWithInfer<'me, 'tcx> { - fn term_to_infer(&mut self, alias_term: ty::Term<'tcx>) -> ty::Term<'tcx> { + fn alias_term_to_infer(&mut self, alias_term: ty::AliasTerm<'tcx>) -> ty::Term<'tcx> { let infcx = self.at.infcx; - let infer_term = infcx.next_term_var_of_kind(alias_term, self.at.cause.span); + let infer_term = infcx.next_term_var_for_alias(alias_term, self.at.cause.span); let obligation = Obligation::new( infcx.tcx, self.at.cause.clone(), self.at.param_env, ty::PredicateKind::AliasRelate( - alias_term.into(), + alias_term.to_term(infcx.tcx), infer_term.into(), ty::AliasRelationDirection::Equate, ), @@ -123,15 +133,14 @@ impl<'me, 'tcx> TypeFolder> for ReplaceAliasWithInfer<'me, 'tcx> { } let ty = ty.super_fold_with(self); - let ty::Alias(..) = *ty.kind() else { return ty }; - - if ty.has_escaping_bound_vars() { + let ty::Alias(alias_ty) = *ty.kind() else { return ty }; + if alias_ty.has_escaping_bound_vars() { let (replaced, ..) = - BoundVarReplacer::replace_bound_vars(self.at.infcx, &mut self.universes, ty); - let _ = self.term_to_infer(replaced.into()); + BoundVarReplacer::replace_bound_vars(self.at.infcx, &mut self.universes, alias_ty); + let _ = self.alias_term_to_infer(replaced.into()); ty } else { - self.term_to_infer(ty.into()).expect_type() + self.alias_term_to_infer(alias_ty.into()).expect_type() } } @@ -141,17 +150,23 @@ impl<'me, 'tcx> TypeFolder> for ReplaceAliasWithInfer<'me, 'tcx> { } let ct = ct.super_fold_with(self); - let ty::ConstKind::Unevaluated(..) = ct.kind() else { return ct }; + let ty::ConstKind::Unevaluated(uv) = ct.kind() else { return ct }; if ct.has_escaping_bound_vars() { let (replaced, ..) = - BoundVarReplacer::replace_bound_vars(self.at.infcx, &mut self.universes, ct); - let _ = self.term_to_infer(replaced.into()); + BoundVarReplacer::replace_bound_vars(self.at.infcx, &mut self.universes, uv); + let _ = + self.alias_term_to_infer(AliasTerm::from_unevaluated_const(self.cx(), replaced)); ct } else { - self.term_to_infer(ct.into()).expect_const() + self.alias_term_to_infer(AliasTerm::from_unevaluated_const(self.cx(), uv)) + .expect_const() } } + + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.allow_normalization() { p.super_fold_with(self) } else { p } + } } /// Deeply normalize all aliases in `value`. This does not handle inference and expects @@ -285,4 +300,8 @@ impl<'tcx> TypeFolder> for DeeplyNormalizeForDiagnosticsFolder<'_, Err(_) => ct.super_fold_with(self), } } + + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.allow_normalization() { p.super_fold_with(self) } else { p } + } } diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index 15fbd985d9630..6af014fbfb874 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -2,7 +2,7 @@ use derive_where::derive_where; use rustc_abi::ExternAbi; use rustc_type_ir_macros::{GenericTypeVisitable, TypeFoldable_Generic, TypeVisitable_Generic}; -use crate::solve::NoSolution; +use crate::solve::{NoSolution, NoSolutionOrRerunNonErased}; use crate::{self as ty, Interner}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -100,3 +100,9 @@ impl From> for NoSolution { NoSolution } } + +impl From> for NoSolutionOrRerunNonErased { + fn from(_: TypeError) -> NoSolutionOrRerunNonErased { + NoSolutionOrRerunNonErased::NoSolution(NoSolution) + } +} diff --git a/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.next.stderr b/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.next.stderr index f609dcab752af..e14b7a110e436 100644 --- a/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.next.stderr +++ b/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.next.stderr @@ -14,7 +14,7 @@ help: consider giving this pattern a type, where the value of const parameter `N LL | let (mut arr, mut arr_with_weird_len): ([_; N], _) = free(); | +++++++++++++ -error[E0271]: type mismatch resolving `10 == 2` +error[E0271]: type mismatch resolving `FREE::<10> == 2` --> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:35:45 | LL | let (mut arr, mut arr_with_weird_len) = free(); @@ -36,11 +36,14 @@ help: consider giving this pattern a type, where the value of const parameter `N LL | let (mut arr, mut arr_with_weird_len): ([_; N], _) = proj(); | +++++++++++++ -error[E0271]: type mismatch resolving `10 == 2` +error[E0271]: type mismatch resolving `::PROJ::<10> == 2` --> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:52:45 | LL | let (mut arr, mut arr_with_weird_len) = proj(); - | ^^^^^^ types differ + | ^^^^^^ expected `2`, found `10` + | + = note: expected constant `2` + found constant `10` error: aborting due to 4 previous errors diff --git a/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.rs b/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.rs index 72952940b1c12..5acdd004472f7 100644 --- a/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.rs +++ b/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.rs @@ -33,7 +33,7 @@ fn test_free() { fn test_free_mismatch() { let (mut arr, mut arr_with_weird_len) = free(); - //[next]~^ ERROR type mismatch resolving `10 == 2` + //[next]~^ ERROR type mismatch resolving `FREE::<10> == 2` arr_with_weird_len = [(); 2]; arr = [(); 10]; } @@ -50,7 +50,7 @@ fn test_proj() { fn test_proj_mismatch() { let (mut arr, mut arr_with_weird_len) = proj(); - //[next]~^ ERROR type mismatch resolving `10 == 2` + //[next]~^ ERROR type mismatch resolving `::PROJ::<10> == 2` arr_with_weird_len = [(); 2]; arr = [(); 10]; } diff --git a/tests/ui/const-generics/generic_const_exprs/negative-coherence-ice-140609.rs b/tests/ui/const-generics/generic_const_exprs/negative-coherence-ice-140609.rs deleted file mode 100644 index 1b02b874ed898..0000000000000 --- a/tests/ui/const-generics/generic_const_exprs/negative-coherence-ice-140609.rs +++ /dev/null @@ -1,14 +0,0 @@ -//@ check-pass -#![feature(with_negative_coherence)] -#![feature(generic_const_exprs)] -#![allow(incomplete_features)] -#![crate_type = "lib"] -trait Trait {} -struct A; - -trait C {} - -impl Trait for E where A<{ D <= 2 }>: FnOnce(&isize) {} -struct E; - -impl Trait for E where A<{ D <= 2 }>: C {} diff --git a/tests/ui/const-generics/generic_const_exprs/specialization-fuzzing-ice-133639.rs b/tests/ui/const-generics/generic_const_exprs/specialization-fuzzing-ice-133639.rs deleted file mode 100644 index d605c83239b27..0000000000000 --- a/tests/ui/const-generics/generic_const_exprs/specialization-fuzzing-ice-133639.rs +++ /dev/null @@ -1,18 +0,0 @@ -//@ check-pass - -// Regression test for #133639. - -#![feature(with_negative_coherence)] -#![feature(min_specialization)] -#![feature(generic_const_exprs)] - -#![crate_type = "lib"] -trait Trait {} -struct A; - -trait C {} - -impl Trait for E where A<{ D <= 2 }>: C {} -struct E; - -impl Trait for E where A<{ D <= 2 }>: C {} diff --git a/tests/ui/const-generics/mgca/free-const-recursive.rs b/tests/ui/const-generics/mgca/free-const-recursive.rs index 8d75c1a941a77..0cbe848340516 100644 --- a/tests/ui/const-generics/mgca/free-const-recursive.rs +++ b/tests/ui/const-generics/mgca/free-const-recursive.rs @@ -6,7 +6,7 @@ type const A: () = A; //~^ ERROR type mismatch resolving `A normalizes-to _` -//~| ERROR the constant `A` is not of type `()` +//~| ERROR type mismatch resolving `A normalizes-to _` fn main() { A; diff --git a/tests/ui/const-generics/mgca/free-const-recursive.stderr b/tests/ui/const-generics/mgca/free-const-recursive.stderr index d0f10275b40d2..20bcd2dcc93f3 100644 --- a/tests/ui/const-generics/mgca/free-const-recursive.stderr +++ b/tests/ui/const-generics/mgca/free-const-recursive.stderr @@ -4,11 +4,13 @@ error[E0271]: type mismatch resolving `A normalizes-to _` LL | type const A: () = A; | ^^^^^^^^^^^^^^^^ types differ -error: the constant `A` is not of type `()` +error[E0271]: type mismatch resolving `A normalizes-to _` --> $DIR/free-const-recursive.rs:7:1 | LL | type const A: () = A; - | ^^^^^^^^^^^^^^^^ expected `()`, found a different `()` + | ^^^^^^^^^^^^^^^^ types differ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/mgca/projection-const-recursive.rs b/tests/ui/const-generics/mgca/projection-const-recursive.rs index c9b5039b5fd0b..15112517249a8 100644 --- a/tests/ui/const-generics/mgca/projection-const-recursive.rs +++ b/tests/ui/const-generics/mgca/projection-const-recursive.rs @@ -1,6 +1,6 @@ //! See also //@ check-fail -//@compile-flags: -Znext-solver=globally --emit=obj +//@ compile-flags: -Znext-solver=globally --emit=obj #![feature(min_generic_const_args)] #![expect(incomplete_features)] @@ -11,7 +11,7 @@ trait Trait { impl Trait for () { type const A: () = <() as Trait>::A; //~^ ERROR type mismatch resolving `<() as Trait>::A normalizes-to _` - //~| ERROR the constant `<() as Trait>::A` is not of type `()` + //~| ERROR type mismatch resolving `<() as Trait>::A normalizes-to _` } fn main() { diff --git a/tests/ui/const-generics/mgca/projection-const-recursive.stderr b/tests/ui/const-generics/mgca/projection-const-recursive.stderr index d9c60102c3184..8bc5afc81f33c 100644 --- a/tests/ui/const-generics/mgca/projection-const-recursive.stderr +++ b/tests/ui/const-generics/mgca/projection-const-recursive.stderr @@ -4,11 +4,13 @@ error[E0271]: type mismatch resolving `<() as Trait>::A normalizes-to _` LL | type const A: () = <() as Trait>::A; | ^^^^^^^^^^^^^^^^ types differ -error: the constant `<() as Trait>::A` is not of type `()` +error[E0271]: type mismatch resolving `<() as Trait>::A normalizes-to _` --> $DIR/projection-const-recursive.rs:12:5 | LL | type const A: () = <() as Trait>::A; - | ^^^^^^^^^^^^^^^^ expected `()`, found a different `()` + | ^^^^^^^^^^^^^^^^ types differ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 2 previous errors diff --git a/tests/ui/coroutine/delayed-obligations-emit.next.stderr b/tests/ui/coroutine/delayed-obligations-emit.next.stderr index 3a3663398c9a7..af7dfc75963c2 100644 --- a/tests/ui/coroutine/delayed-obligations-emit.next.stderr +++ b/tests/ui/coroutine/delayed-obligations-emit.next.stderr @@ -4,6 +4,7 @@ error[E0275]: overflow evaluating the requirement `{async block@$DIR/delayed-obl LL | spawn(async { build_dependencies().await }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`delayed_obligations_emit`) note: required by a bound in `spawn` --> $DIR/delayed-obligations-emit.rs:31:13 | diff --git a/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.next.stderr b/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.next.stderr index 31d74d1c022a1..26e682dfb7a7c 100644 --- a/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.next.stderr +++ b/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.next.stderr @@ -1,8 +1,8 @@ -error[E0284]: type annotations needed: cannot satisfy `for<'a> <_ as Trait<'a>>::Assoc normalizes-to >::Assoc` - --> $DIR/rigid-equate-projections-in-higher-ranked-fn-signature.rs:27:50 +error[E0284]: type annotations needed: cannot normalize `<_ as Trait<'a>>::Assoc` + --> $DIR/rigid-equate-projections-in-higher-ranked-fn-signature.rs:27:12 | LL | let _: for<'a> fn(<_ as Trait<'a>>::Assoc) = foo::(); - | ^^^^^^^^^^ cannot satisfy `for<'a> <_ as Trait<'a>>::Assoc normalizes-to >::Assoc` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `<_ as Trait<'a>>::Assoc` error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.rs b/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.rs index c87c8e90d741b..b63e595fd5d54 100644 --- a/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.rs +++ b/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.rs @@ -15,4 +15,5 @@ struct W(T); fn hello(_: W>) {} //~^ ERROR: the trait bound `usize: Foo` is not satisfied //~| ERROR: the trait bound `usize: Foo` is not satisfied +//~| ERROR: the type `W>` is not well-formed fn main() {} diff --git a/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr b/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr index dec94730df615..09418b656f8ac 100644 --- a/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr +++ b/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr @@ -15,6 +15,12 @@ note: required by a bound in `A` LL | type A = T; | ^^^ required by this bound in `A` +error: the type `W>` is not well-formed + --> $DIR/alias-bounds-when-not-wf.rs:15:13 + | +LL | fn hello(_: W>) {} + | ^^^^^^^^^^^ + error[E0277]: the trait bound `usize: Foo` is not satisfied --> $DIR/alias-bounds-when-not-wf.rs:15:13 | @@ -27,6 +33,6 @@ help: this trait has no implementations, consider adding one LL | trait Foo {} | ^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/async.fail.stderr b/tests/ui/traits/next-solver/async.fail.stderr index a76a10d20ee8b..bc89842d16a14 100644 --- a/tests/ui/traits/next-solver/async.fail.stderr +++ b/tests/ui/traits/next-solver/async.fail.stderr @@ -1,8 +1,8 @@ -error[E0271]: type mismatch resolving `() == i32` +error[E0271]: expected `{async block@$DIR/async.rs:12:17: 12:22}` to be a future that resolves to `i32`, but it resolves to `()` --> $DIR/async.rs:12:17 | LL | needs_async(async {}); - | ----------- ^^^^^^^^ types differ + | ----------- ^^^^^^^^ expected `i32`, found `()` | | | required by a bound introduced by this call | diff --git a/tests/ui/traits/next-solver/async.rs b/tests/ui/traits/next-solver/async.rs index 34c0ed02eeb12..fded774354759 100644 --- a/tests/ui/traits/next-solver/async.rs +++ b/tests/ui/traits/next-solver/async.rs @@ -10,7 +10,7 @@ fn needs_async(_: impl Future) {} #[cfg(fail)] fn main() { needs_async(async {}); - //[fail]~^ ERROR type mismatch resolving `() == i32` + //[fail]~^ ERROR expected `{async block@$DIR/async.rs:12:17: 12:22}` to be a future that resolves to `i32`, but it resolves to `()` } #[cfg(pass)] diff --git a/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.rs b/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.rs index bd33e3c2f47ee..0b63bb3261db7 100644 --- a/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.rs +++ b/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.rs @@ -7,6 +7,7 @@ trait Wf { struct S { f: &'static <() as Wf>::Assoc, //~^ ERROR the trait bound `(): Wf` is not satisfied + //~| ERROR the type `&'static <() as Wf>::Assoc` is not well-formed } fn main() { diff --git a/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.stderr b/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.stderr index d484a0a1c4c28..2f7307eb96b77 100644 --- a/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.stderr +++ b/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.stderr @@ -10,8 +10,14 @@ help: this trait has no implementations, consider adding one LL | trait Wf { | ^^^^^^^^ +error: the type `&'static <() as Wf>::Assoc` is not well-formed + --> $DIR/non-wf-in-coerce-pointers.rs:8:8 + | +LL | f: &'static <() as Wf>::Assoc, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0277]: the trait bound `(): Wf` is not satisfied - --> $DIR/non-wf-in-coerce-pointers.rs:14:18 + --> $DIR/non-wf-in-coerce-pointers.rs:15:18 | LL | let y: &() = x.f; | ^^^ the trait `Wf` is not implemented for `()` @@ -22,6 +28,6 @@ help: this trait has no implementations, consider adding one LL | trait Wf { | ^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/find-param-recursion-issue-152716.rs b/tests/ui/traits/next-solver/find-param-recursion-issue-152716.rs index 914773c82196a..d1cdb3164d428 100644 --- a/tests/ui/traits/next-solver/find-param-recursion-issue-152716.rs +++ b/tests/ui/traits/next-solver/find-param-recursion-issue-152716.rs @@ -14,7 +14,7 @@ fn foo() where T: for<'a> Proj<'a, Assoc = for<'b> fn(>::Assoc)>, (): Trait<>::Assoc> - //~^ ERROR: overflow evaluating the requirement `(): Trait<>::Assoc>` + //~^ ERROR: overflow evaluating the requirement `(): Trait fn(>::Assoc)>` { } diff --git a/tests/ui/traits/next-solver/find-param-recursion-issue-152716.stderr b/tests/ui/traits/next-solver/find-param-recursion-issue-152716.stderr index e2ee83cfadbef..405081614a565 100644 --- a/tests/ui/traits/next-solver/find-param-recursion-issue-152716.stderr +++ b/tests/ui/traits/next-solver/find-param-recursion-issue-152716.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow evaluating the requirement `(): Trait<>::Assoc>` +error[E0275]: overflow evaluating the requirement `(): Trait fn(>::Assoc)>` --> $DIR/find-param-recursion-issue-152716.rs:16:9 | LL | (): Trait<>::Assoc> diff --git a/tests/ui/traits/next-solver/lazy-nested-obligations-2.next.stderr b/tests/ui/traits/next-solver/lazy-nested-obligations-2.next.stderr index 462544b405218..cc11aa018161a 100644 --- a/tests/ui/traits/next-solver/lazy-nested-obligations-2.next.stderr +++ b/tests/ui/traits/next-solver/lazy-nested-obligations-2.next.stderr @@ -1,14 +1,20 @@ -error[E0271]: type mismatch resolving `fn(&str) == fn(&str) {f}` +error[E0271]: type mismatch resolving `::F == fn(&str) {f}` --> $DIR/lazy-nested-obligations-2.rs:20:21 | LL | let _: V = V(f); - | ^^^^ types differ + | ^^^^ expected fn item, found fn pointer + | + = note: expected fn item `for<'a> fn(&'a _) {f}` + found fn pointer `for<'a> fn(&'a _)` -error[E0271]: type mismatch resolving `fn(&str) == fn(&str) {f}` +error[E0271]: type mismatch resolving `::F == fn(&str) {f}` --> $DIR/lazy-nested-obligations-2.rs:27:22 | LL | let _: E3 = E3::Var(f); - | ^^^^^^^^^^ types differ + | ^^^^^^^^^^ expected fn item, found fn pointer + | + = note: expected fn item `for<'a> fn(&'a _) {f}` + found fn pointer `for<'a> fn(&'a _)` error: aborting due to 2 previous errors diff --git a/tests/ui/traits/next-solver/more-object-bound.rs b/tests/ui/traits/next-solver/more-object-bound.rs index 1dad1903a649d..3d3fdc926f658 100644 --- a/tests/ui/traits/next-solver/more-object-bound.rs +++ b/tests/ui/traits/next-solver/more-object-bound.rs @@ -10,7 +10,7 @@ trait Trait: SuperTrait::B> {} fn transmute(x: A) -> B { foo::>(x) - //~^ ERROR type mismatch resolving `A == B` + //~^ ERROR type mismatch resolving ` as SuperTrait>::A == B` } fn foo(x: T::A) -> B diff --git a/tests/ui/traits/next-solver/more-object-bound.stderr b/tests/ui/traits/next-solver/more-object-bound.stderr index 7d279ed64282b..ccbf19ae4d96c 100644 --- a/tests/ui/traits/next-solver/more-object-bound.stderr +++ b/tests/ui/traits/next-solver/more-object-bound.stderr @@ -1,9 +1,17 @@ -error[E0271]: type mismatch resolving `A == B` +error[E0271]: type mismatch resolving ` as SuperTrait>::A == B` --> $DIR/more-object-bound.rs:12:17 | +LL | fn transmute(x: A) -> B { + | - - expected type parameter + | | + | found type parameter LL | foo::>(x) - | ^^^^^^^^^^^^^^^^^^^^^^^ types differ + | ^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `B`, found type parameter `A` | + = note: expected type parameter `B` + found type parameter `A` + = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters = note: required because it appears within the type `dyn Trait` note: required by a bound in `foo` --> $DIR/more-object-bound.rs:18:8 diff --git a/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr b/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr index ecd86dba97994..b166bd0ccb92d 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr +++ b/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr @@ -45,9 +45,43 @@ LL | trait A { LL | fn f() | ^ this trait's associated function doesn't have the requirement `_: A` -error[E0275]: overflow evaluating the requirement `<() as A>::Assoc: A` +error[E0275]: overflow evaluating the requirement `<() as A>::Assoc == _` + --> $DIR/normalize-param-env-2.rs:24:22 + | +LL | Self::Assoc: A, + | ^^^^ + | +note: required by a bound in `A` + --> $DIR/normalize-param-env-2.rs:9:1 + | +LL | / trait A { +LL | | type Assoc; +LL | | +LL | | fn f() +... | +LL | | } + | |_^ required by this bound in `A` + +error[E0275]: overflow evaluating the requirement `<() as A>::Assoc == _` + --> $DIR/normalize-param-env-2.rs:24:22 + | +LL | Self::Assoc: A, + | ^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0283]: type annotations needed --> $DIR/normalize-param-env-2.rs:24:22 | +LL | Self::Assoc: A, + | ^^^^ cannot infer type + | +note: multiple `impl`s or `where` clauses satisfying `_: A` found + --> $DIR/normalize-param-env-2.rs:19:1 + | +LL | impl A for () { + | ^^^^^^^^^^^^^^^^^^^ +... LL | Self::Assoc: A, | ^^^^ @@ -92,7 +126,7 @@ LL | where LL | Self::Assoc: A, | ^^^^ required by this bound in `A::f` -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors Some errors have detailed explanations: E0275, E0283. For more information about an error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr b/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr index 47d38365e970e..a6cb9e4b4dc64 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr +++ b/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr @@ -1,4 +1,18 @@ -error[E0275]: overflow evaluating the requirement `::Assoc: Trait` +error[E0275]: overflow evaluating the requirement `::Assoc == _` + --> $DIR/normalize-param-env-4.rs:19:26 + | +LL | ::Assoc: Trait, + | ^^^^^ + | +note: required by a bound in `Trait` + --> $DIR/normalize-param-env-4.rs:7:1 + | +LL | / trait Trait { +LL | | type Assoc; +LL | | } + | |_^ required by this bound in `Trait` + +error[E0275]: overflow evaluating the requirement `::Assoc == _` --> $DIR/normalize-param-env-4.rs:19:26 | LL | ::Assoc: Trait, @@ -22,6 +36,6 @@ note: required by a bound in `impls_trait` LL | fn impls_trait() {} | ^^^^^ required by this bound in `impls_trait` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs index c61fbef05b224..8dc27c0da605a 100644 --- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs +++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs @@ -13,7 +13,7 @@ fn needs_bar() {} fn test::Assoc2> + Foo2::Assoc1>>() { needs_bar::(); - //~^ ERROR: the trait bound `::Assoc2: Bar` is not satisfied + //~^ ERROR: the trait bound `::Assoc1: Bar` is not satisfied } fn main() {} diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr index c4be47e3520da..6f5111a6193ca 100644 --- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr +++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `::Assoc2: Bar` is not satisfied +error[E0277]: the trait bound `::Assoc1: Bar` is not satisfied --> $DIR/recursive-self-normalization-2.rs:15:17 | LL | needs_bar::(); - | ^^^^^^^^^ the trait `Bar` is not implemented for `::Assoc2` + | ^^^^^^^^^ the trait `Bar` is not implemented for `::Assoc1` | note: required by a bound in `needs_bar` --> $DIR/recursive-self-normalization-2.rs:12:17 @@ -11,7 +11,7 @@ LL | fn needs_bar() {} | ^^^ required by this bound in `needs_bar` help: consider further restricting the associated type | -LL | fn test::Assoc2> + Foo2::Assoc1>>() where ::Assoc2: Bar { +LL | fn test::Assoc2> + Foo2::Assoc1>>() where ::Assoc1: Bar { | ++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.next.stderr b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.next.stderr index d7046d2b058ba..d109ed37eb318 100644 --- a/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.next.stderr +++ b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.next.stderr @@ -4,14 +4,11 @@ error[E0271]: type mismatch resolving `::Diverges normalizes-to LL | field: Box<::Diverges>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ -error[E0271]: type mismatch resolving `::Diverges normalizes-to _` +error: the type `Box<::Diverges>` is not well-formed --> $DIR/normalize-diverging-alias-in-struct.rs:21:12 | LL | field: Box<::Diverges>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ - | -note: required by a bound in `Box` - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.rs b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.rs index 45b2cb56c25f7..7a0738af6874a 100644 --- a/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.rs +++ b/tests/ui/traits/normalize/normalize-diverging-alias-in-struct.rs @@ -21,7 +21,7 @@ struct Foo { field: Box<::Diverges>, //[current]~^ ERROR: overflow evaluating the requirement `::Diverges == _` //[next]~^^ ERROR: type mismatch resolving `::Diverges normalizes-to _` - //[next]~| ERROR: type mismatch resolving `::Diverges normalizes-to _` + //[next]~| ERROR: the type `Box<::Diverges>` is not well-formed } fn main() {} diff --git a/tests/ui/traits/solver-cycles/129541-recursive-struct.rs b/tests/ui/traits/solver-cycles/129541-recursive-struct.rs index bf4b4744dbd4d..c47c7d269f8c4 100644 --- a/tests/ui/traits/solver-cycles/129541-recursive-struct.rs +++ b/tests/ui/traits/solver-cycles/129541-recursive-struct.rs @@ -1,5 +1,4 @@ //~ ERROR reached the recursion limit finding the struct tail for `Hello` -// Regression test for #129541 //@ revisions: unique_curr unique_next multiple_curr multiple_next //@ ignore-compare-mode-next-solver (explicit revisions) diff --git a/tests/ui/wf/return-type-non-wf-no-ice.next.stderr b/tests/ui/wf/return-type-non-wf-no-ice.next.stderr index 888f0893ec7c4..0ee259a401407 100644 --- a/tests/ui/wf/return-type-non-wf-no-ice.next.stderr +++ b/tests/ui/wf/return-type-non-wf-no-ice.next.stderr @@ -1,22 +1,8 @@ -error[E0277]: `T` is not an iterator +error: the type `Foo` is not well-formed --> $DIR/return-type-non-wf-no-ice.rs:13:16 | LL | fn foo() -> Foo { - | ^^^^^^ `T` is not an iterator - | -note: required by a bound in `Foo` - --> $DIR/return-type-non-wf-no-ice.rs:10:8 - | -LL | pub struct Foo(T) - | --- required by a bound in this struct -LL | where -LL | T: Iterator, - | ^^^^^^^^ required by this bound in `Foo` -help: consider restricting type parameter `T` with trait `Iterator` - | -LL | fn foo() -> Foo { - | +++++++++++++++++++++ + | ^^^^^^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/wf/return-type-non-wf-no-ice.rs b/tests/ui/wf/return-type-non-wf-no-ice.rs index 915e33418d7c0..694b8194cdbb9 100644 --- a/tests/ui/wf/return-type-non-wf-no-ice.rs +++ b/tests/ui/wf/return-type-non-wf-no-ice.rs @@ -11,7 +11,8 @@ where ::Item: Default; fn foo() -> Foo { - //~^ ERROR: `T` is not an iterator + //[current]~^ ERROR `T` is not an iterator + //[next]~^^ ERROR: the type `Foo` is not well-formed loop {} } From a1952ce393e84626340023258678b3e41a3153fd Mon Sep 17 00:00:00 2001 From: lcnr Date: Sun, 17 May 2026 15:04:44 +0200 Subject: [PATCH 2/8] assumptions on binders ICE yeet --- compiler/rustc_infer/src/infer/context.rs | 4 +- .../assumptions_on_binders/alias_outlives.rs | 42 --------------- .../alias_outlives.stderr | 21 -------- ...higher_ranked_alias_outlives_assumption.rs | 53 ------------------- 4 files changed, 3 insertions(+), 117 deletions(-) delete mode 100644 tests/ui/assumptions_on_binders/alias_outlives.rs delete mode 100644 tests/ui/assumptions_on_binders/alias_outlives.stderr delete mode 100644 tests/ui/assumptions_on_binders/implied_higher_ranked_alias_outlives_assumption.rs diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index fc0c4bb0d5f5e..ffbc7ce8aa073 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -50,7 +50,9 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { &self, u: ty::UniverseIndex, ) -> Option>> { - self.placeholder_assumptions_for_next_solver.borrow().get(&u).unwrap().as_ref().cloned() + // FIXME(-Zassumptions_on_binders): We should actually make sure that + // we always register placeholder assumptions. + self.placeholder_assumptions_for_next_solver.borrow().get(&u)?.as_ref().cloned() } fn get_solver_region_constraint( diff --git a/tests/ui/assumptions_on_binders/alias_outlives.rs b/tests/ui/assumptions_on_binders/alias_outlives.rs deleted file mode 100644 index c234773fb10d8..0000000000000 --- a/tests/ui/assumptions_on_binders/alias_outlives.rs +++ /dev/null @@ -1,42 +0,0 @@ -//@ compile-flags: -Znext-solver -Zassumptions-on-binders - -// test that a `::Assoc: '!a_u1` constraint is considered to be satisfied -// if there's a `T::Assoc: 'static` assumption in the root universe and if not that it is -// an error :) - -#![feature(generic_const_items)] - -trait AliasHaver { - type Assoc; -} - -trait Trait<'a> {} -impl<'a, T: 'a> Trait<'a> for T {} - -struct ReqTrait Trait<'a>>(T); - -fn borrowck_env_pass<'a, T: AliasHaver>() -where - ::Assoc: 'static, -{ - let _: ReqTrait; -} - -fn borrowck_env_fail<'a, T: AliasHaver>() -//~^ ERROR: unsatisfied lifetime constraint from -Zassumptions-on-binders -where - ::Assoc: 'a, -{ - let _: ReqTrait; -} - -const REGIONCK_ENV_PASS<'a, T: AliasHaver>: ReqTrait = todo!() -where - ::Assoc: 'static; - -const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait = todo!() -//~^ ERROR: unsatisfied lifetime constraint from -Zassumptions-on-binders -where - ::Assoc: 'a; - -fn main() {} diff --git a/tests/ui/assumptions_on_binders/alias_outlives.stderr b/tests/ui/assumptions_on_binders/alias_outlives.stderr deleted file mode 100644 index bd3819798737d..0000000000000 --- a/tests/ui/assumptions_on_binders/alias_outlives.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error: unsatisfied lifetime constraint from -Zassumptions-on-binders :3 - --> $DIR/alias_outlives.rs:37:1 - | -LL | const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait = todo!() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: meoow :c - -error: unsatisfied lifetime constraint from -Zassumptions-on-binders :3 - --> $DIR/alias_outlives.rs:25:1 - | -LL | / fn borrowck_env_fail<'a, T: AliasHaver>() -LL | | -LL | | where -LL | | ::Assoc: 'a, - | |_________________________________^ - | - = note: meoow :c - -error: aborting due to 2 previous errors - diff --git a/tests/ui/assumptions_on_binders/implied_higher_ranked_alias_outlives_assumption.rs b/tests/ui/assumptions_on_binders/implied_higher_ranked_alias_outlives_assumption.rs deleted file mode 100644 index c0cb6ce416aed..0000000000000 --- a/tests/ui/assumptions_on_binders/implied_higher_ranked_alias_outlives_assumption.rs +++ /dev/null @@ -1,53 +0,0 @@ -//@ compile-flags: -Znext-solver -Zassumptions-on-binders -//@ check-pass - -#![feature(generic_const_items)] - -// sorry for writing this -// - boxy - -// for<'a> where(for<'d> ::Assoc: 'c) { -// for<'b> { -// >::Assoc: 'c -// } -// -// rewritten to: for<'b> >::Assoc: 'c -// } -// rewritten to: true (via assumption) -// rewritting to `for<'a, 'b> >::Assoc: 'c` would be wrong - -trait Trait<'a, 'b> { - type Assoc; -} - -struct ImpliedBound<'a, 'c, T: for<'b> Trait<'a, 'b>>(T, &'a (), &'c ()) -where - for<'b> >::Assoc: 'c,; - -trait InnerBinder<'a, 'b, 'c> {} -impl<'a, 'b, 'c, S> InnerBinder<'a, 'b, 'c> for S -where - S: Trait<'a, 'b>, - >::Assoc: 'c {} - -trait OuterBinder<'a, 'c, T0> {} -impl<'a, 'c, T0, S> OuterBinder<'a, 'c, T0> for S -where - for<'b> S: InnerBinder<'a, 'b, 'c>, {} - -struct ReqTrait<'c, T>(&'c (), T) -where - for<'a> T: OuterBinder<'a, 'c, ImpliedBound<'a, 'c, T>>,; - -fn borrowck_env<'c, T>() -where - T: for<'a, 'b> Trait<'a, 'b> -{ - let _: ReqTrait<'c, T>; -} - -const REGIONCK_ENV<'c, T>: ReqTrait<'c, T> = todo!() -where - T: for<'a, 'b> Trait<'a, 'b>; - -fn main() {} From e8a391c0c78e9247cb0b736d0ecef50d4381df82 Mon Sep 17 00:00:00 2001 From: lcnr Date: Sun, 17 May 2026 21:44:38 +0200 Subject: [PATCH 3/8] questionable --- .../rustc_next_trait_solver/src/normalize.rs | 32 +++++++++++++------ tests/ui/README.md | 4 --- ...ambiguous-on-failed-eval-with-vars-fail.rs | 2 +- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/normalize.rs b/compiler/rustc_next_trait_solver/src/normalize.rs index 84af3e66a7a13..acbb33bf8f8c4 100644 --- a/compiler/rustc_next_trait_solver/src/normalize.rs +++ b/compiler/rustc_next_trait_solver/src/normalize.rs @@ -1,5 +1,6 @@ use std::fmt::Debug; +use rustc_data_structures::sso::{SsoHashMap, SsoHashSet}; use rustc_type_ir::data_structures::ensure_sufficient_stack; use rustc_type_ir::inherent::*; use rustc_type_ir::{ @@ -23,6 +24,7 @@ where infcx: &'a Infcx, universes: Vec>, normalize: F, + cache: SsoHashMap, } #[derive(PartialEq, Eq, Debug)] @@ -45,6 +47,7 @@ where { infcx: &'a Infcx, max_universe: ty::UniverseIndex, + cache: SsoHashSet, } impl<'a, Infcx, I> MaxUniverse<'a, Infcx, I> @@ -53,7 +56,7 @@ where I: Interner, { fn new(infcx: &'a Infcx) -> Self { - MaxUniverse { infcx, max_universe: ty::UniverseIndex::ROOT } + MaxUniverse { infcx, max_universe: ty::UniverseIndex::ROOT, cache: Default::default() } } fn max_universe(self) -> ty::UniverseIndex { @@ -79,7 +82,9 @@ where self.max_universe = self.max_universe.max(self.infcx.universe_of_ty(vid).unwrap()); } - t.super_visit_with(self) + if self.cache.insert(t) { + t.super_visit_with(self) + } } fn visit_const(&mut self, c: I::Const) { @@ -110,7 +115,7 @@ where F: FnMut(AliasTerm) -> Result<(I::Term, NormalizationWasAmbiguous), E>, { pub fn new(infcx: &'a Infcx, universes: Vec>, normalize: F) -> Self { - Self { infcx, universes, normalize } + Self { infcx, universes, normalize, cache: Default::default() } } fn normalize_alias_term( @@ -176,12 +181,16 @@ where return Ok(ty); } + if let Some(ty) = self.cache.get(&ty) { + return Ok(*ty); + } + // With eager normalization, we should normalize the args of alias before // normalizing the alias itself. - let ty = ty.try_super_fold_with(self)?; - let ty::Alias(alias_ty) = ty.kind() else { return Ok(ty) }; + let folded_ty = ty.try_super_fold_with(self)?; + let ty::Alias(alias_ty) = folded_ty.kind() else { return Ok(folded_ty) }; - if ty.has_escaping_bound_vars() { + let result = if folded_ty.has_escaping_bound_vars() { let (alias_ty, mapped_regions, mapped_types, mapped_consts) = BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, alias_ty); let normalized_term = ensure_sufficient_stack(|| { @@ -195,13 +204,16 @@ where &self.universes, normalized_term.expect_ty(), ); - Ok(normalized_ty) + normalized_ty } else { - Ok(ensure_sufficient_stack(|| { + ensure_sufficient_stack(|| { self.normalize_alias_term(alias_ty.into(), HasEscapingBoundVars::No) })? - .expect_ty()) - } + .expect_ty() + }; + + assert!(self.cache.insert(ty, result).is_none(), "{ty:?} {result:?} {:?}",self.cache); + Ok(result) } #[instrument(level = "trace", skip(self), ret)] diff --git a/tests/ui/README.md b/tests/ui/README.md index 39402f78bb5ec..1ab22a65ce507 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -109,10 +109,6 @@ Tests focused on associated types. If the associated type is not in a trait defi See [Associated Types | Reference](https://doc.rust-lang.org/reference/items/associated-items.html#associated-types). -## `tests/ui/assumptions_on_binders`: -Zassumptions-on-binders - -Tests focused on the -Zassumptions-on-binders flag. - ## `tests/ui/async-await`: Async/Await Tests for the async/await related features. E.g. async functions, await expressions, and their interaction with other language features. diff --git a/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.rs b/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.rs index 5acdd004472f7..12e92d5530032 100644 --- a/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.rs +++ b/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.rs @@ -33,7 +33,7 @@ fn test_free() { fn test_free_mismatch() { let (mut arr, mut arr_with_weird_len) = free(); - //[next]~^ ERROR type mismatch resolving `FREE::<10> == 2` + //[next]~^ ERROR type mismatch resolving `FREE::<10> == 2` arr_with_weird_len = [(); 2]; arr = [(); 10]; } From c74bb3e4cb8d29c19ca766b6bcc9040eec1c1557 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 18 May 2026 10:54:49 +0200 Subject: [PATCH 4/8] good stuff --- compiler/rustc_middle/src/ty/fast_reject.rs | 11 +--- .../rustc_next_trait_solver/src/normalize.rs | 2 +- .../src/solve/assembly/mod.rs | 8 ++- .../src/solve/effect_goals.rs | 19 ++++++ .../src/solve/normalizes_to/mod.rs | 20 +++++- .../src/solve/trait_goals.rs | 40 ++++++++++++ .../rustc_trait_selection/src/traits/mod.rs | 8 +-- compiler/rustc_type_ir/src/fast_reject.rs | 63 +++++++++++++------ 8 files changed, 133 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index 2945a0be424f8..43512fc45864d 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -3,14 +3,7 @@ pub use rustc_type_ir::fast_reject::*; use super::TyCtxt; -pub type DeepRejectCtxt< - 'tcx, - const INSTANTIATE_LHS_WITH_INFER: bool, - const INSTANTIATE_RHS_WITH_INFER: bool, -> = rustc_type_ir::fast_reject::DeepRejectCtxt< - TyCtxt<'tcx>, - INSTANTIATE_LHS_WITH_INFER, - INSTANTIATE_RHS_WITH_INFER, ->; +pub type DeepRejectCtxt<'tcx, const HANDLE_LHS: u8, const HANDLE_RHS: u8> = + rustc_type_ir::fast_reject::DeepRejectCtxt, HANDLE_LHS, HANDLE_RHS>; pub type SimplifiedType = rustc_type_ir::fast_reject::SimplifiedType; diff --git a/compiler/rustc_next_trait_solver/src/normalize.rs b/compiler/rustc_next_trait_solver/src/normalize.rs index acbb33bf8f8c4..713ba9bd47a78 100644 --- a/compiler/rustc_next_trait_solver/src/normalize.rs +++ b/compiler/rustc_next_trait_solver/src/normalize.rs @@ -212,7 +212,7 @@ where .expect_ty() }; - assert!(self.cache.insert(ty, result).is_none(), "{ty:?} {result:?} {:?}",self.cache); + assert!(self.cache.insert(ty, result).is_none(), "{ty:?} {result:?} {:?}", self.cache); Ok(result) } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index f58e20d6cccdc..1983ff2b996ea 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -137,7 +137,7 @@ where goal: Goal, assumption: I::Clause, ) -> Result, CandidateHeadUsages>, RerunNonErased> { - match Self::fast_reject_assumption(ecx, goal, assumption) { + match Self::fast_reject_param_env(ecx, goal, assumption) { Ok(()) => {} Err(NoSolution) => return Ok(Err(CandidateHeadUsages::default())), } @@ -193,6 +193,12 @@ where .enter(|ecx| Self::match_assumption(ecx, goal, assumption, then)) } + fn fast_reject_param_env( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + assumption: I::Clause, + ) -> Result<(), NoSolution>; + /// Try to reject the assumption based off of simple heuristics, such as [`ty::ClauseKind`] /// and `DefId`. fn fast_reject_assumption( diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 8575302df309e..7e9b87d421086 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -39,6 +39,25 @@ where self.def_id() } + fn fast_reject_param_env( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + assumption: I::Clause, + ) -> Result<(), NoSolution> { + if let Some(host_clause) = assumption.as_host_effect_clause() + && host_clause.def_id() == goal.predicate.def_id() + && host_clause.constness().satisfies(goal.predicate.constness) + && DeepRejectCtxt::relate_fully_normalized(ecx.cx()).args_may_unify( + goal.predicate.trait_ref.args, + host_clause.skip_binder().trait_ref.args, + ) + { + Ok(()) + } else { + Err(NoSolution) + } + } + fn fast_reject_assumption( ecx: &mut EvalCtxt<'_, D>, goal: Goal, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 54b71c2d8f3ae..81866c11e6089 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -163,6 +163,24 @@ where self.trait_def_id(cx) } + fn fast_reject_param_env( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + assumption: I::Clause, + ) -> Result<(), NoSolution> { + if let Some(projection_pred) = assumption.as_projection_clause() + && projection_pred.item_def_id() == goal.predicate.def_id() + && DeepRejectCtxt::relate_fully_normalized(ecx.cx()).args_may_unify( + goal.predicate.alias.args, + projection_pred.skip_binder().projection_term.args, + ) + { + Ok(()) + } else { + Err(NoSolution) + } + } + fn fast_reject_assumption( ecx: &mut EvalCtxt<'_, D>, goal: Goal, @@ -170,7 +188,7 @@ where ) -> Result<(), NoSolution> { if let Some(projection_pred) = assumption.as_projection_clause() && projection_pred.item_def_id() == goal.predicate.def_id() - && DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( + && DeepRejectCtxt::relate_fully_normalized(ecx.cx()).args_may_unify( goal.predicate.alias.args, projection_pred.skip_binder().projection_term.args, ) diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index a9b042bae313e..cf02e8800130d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -131,6 +131,46 @@ where .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } + fn fast_reject_param_env( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + assumption: I::Clause, + ) -> Result<(), NoSolution> { + fn trait_def_id_matches( + cx: I, + clause_def_id: I::TraitId, + goal_def_id: I::TraitId, + polarity: PredicatePolarity, + ) -> bool { + clause_def_id == goal_def_id + // PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so + // check for a `MetaSized` supertrait being matched against a `Sized` assumption. + // + // `PointeeSized` bounds are syntactic sugar for a lack of bounds so don't need this. + || (polarity == PredicatePolarity::Positive + && cx.is_trait_lang_item(clause_def_id, SolverTraitLangItem::Sized) + && cx.is_trait_lang_item(goal_def_id, SolverTraitLangItem::MetaSized)) + } + + if let Some(trait_clause) = assumption.as_trait_clause() + && trait_clause.polarity() == goal.predicate.polarity + && trait_def_id_matches( + ecx.cx(), + trait_clause.def_id(), + goal.predicate.def_id(), + goal.predicate.polarity, + ) + && DeepRejectCtxt::relate_fully_normalized(ecx.cx()).args_may_unify( + goal.predicate.trait_ref.args, + trait_clause.skip_binder().trait_ref.args, + ) + { + return Ok(()); + } else { + Err(NoSolution) + } + } + fn fast_reject_assumption( ecx: &mut EvalCtxt<'_, D>, goal: Goal, diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index a834dbd1604f8..00f02713b6b64 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -258,12 +258,6 @@ fn do_normalize_predicates<'tcx>( elaborated_env: ty::ParamEnv<'tcx>, predicates: Vec>, ) -> Result>, ErrorGuaranteed> { - // Even if we move back to eager normalization elsewhere, - // param env normalization remains lazy in the next solver. - if tcx.next_trait_solver_globally() { - return Ok(predicates); - } - // FIXME. We should really... do something with these region // obligations. But this call just continues the older // behavior (i.e., doesn't cause any new bugs), and it would @@ -426,7 +420,7 @@ pub fn normalize_param_env_or_error<'tcx>( // a circularity. // // The way we handle this is by normalizing the param-env inside an unnormalized version - // of the param-env, which means that if the param-env contains unnormalized projections, + // of the param-env, which mefn normalize_param_envans that if the param-env contains unnormalized projections, // we'll have some normalization failures. This is unfortunate. // // Lazy normalization would basically handle this by treating just the diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 26d98b5d8ad7d..43734d6fcfa5f 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -161,6 +161,10 @@ impl SimplifiedType { } } +const HANDLE_AS_INFER: u8 = 0; +const HANDLE_UNNORMALIZED: u8 = 1; +const HANDLE_FULLY_NORMALIZED: u8 = 2; + /// Given generic arguments, could they be unified after /// replacing parameters with inference variables or placeholders. /// This behavior is toggled using the const generics. @@ -172,37 +176,45 @@ impl SimplifiedType { /// impls only have to overlap for some value, so we treat parameters /// on both sides like inference variables. #[derive(Debug, Clone, Copy)] -pub struct DeepRejectCtxt< - I: Interner, - const INSTANTIATE_LHS_WITH_INFER: bool, - const INSTANTIATE_RHS_WITH_INFER: bool, -> { +pub struct DeepRejectCtxt { _interner: PhantomData, } -impl DeepRejectCtxt { +impl DeepRejectCtxt { /// Treat parameters in both the lhs and the rhs as rigid. - pub fn relate_rigid_rigid(_interner: I) -> DeepRejectCtxt { + pub fn relate_rigid_rigid( + _interner: I, + ) -> DeepRejectCtxt { DeepRejectCtxt { _interner: PhantomData } } } -impl DeepRejectCtxt { +impl DeepRejectCtxt { /// Treat parameters in both the lhs and the rhs as infer vars. - pub fn relate_infer_infer(_interner: I) -> DeepRejectCtxt { + pub fn relate_infer_infer(_interner: I) -> DeepRejectCtxt { DeepRejectCtxt { _interner: PhantomData } } } -impl DeepRejectCtxt { +impl DeepRejectCtxt { /// Treat parameters in the lhs as rigid, and in rhs as infer vars. - pub fn relate_rigid_infer(_interner: I) -> DeepRejectCtxt { + pub fn relate_rigid_infer( + _interner: I, + ) -> DeepRejectCtxt { + DeepRejectCtxt { _interner: PhantomData } + } +} + +impl DeepRejectCtxt { + pub fn relate_fully_normalized( + _interner: I, + ) -> DeepRejectCtxt { DeepRejectCtxt { _interner: PhantomData } } } -impl - DeepRejectCtxt +impl + DeepRejectCtxt { // Quite arbitrary. Large enough to only affect a very tiny amount of impls/crates // and small enough to prevent hangs. @@ -257,11 +269,16 @@ impl { - if INSTANTIATE_RHS_WITH_INFER { + if HANDLE_RHS < HANDLE_UNNORMALIZED { + return true; + } + } + ty::Alias(_) => { + if HANDLE_RHS < HANDLE_FULLY_NORMALIZED { return true; } } - ty::Error(_) | ty::Alias(..) | ty::Bound(..) => return true, + ty::Error(_) | ty::Bound(..) => return true, ty::Infer(var) => return self.var_and_ty_may_unify(var, lhs), // These types only unify with inference variables or their own @@ -324,7 +341,7 @@ impl { - INSTANTIATE_LHS_WITH_INFER + HANDLE_LHS < HANDLE_UNNORMALIZED || match rhs.kind() { ty::Param(rhs) => lhs == rhs, _ => false, @@ -343,7 +360,15 @@ impl true, + ty::Alias(lhs) => { + HANDLE_LHS < HANDLE_FULLY_NORMALIZED + || match rhs.kind() { + ty::Alias(rhs) => { + lhs.kind == rhs.kind && self.args_may_unify(lhs.args, rhs.args) + } + _ => false, + } + } ty::Int(_) | ty::Uint(_) @@ -462,7 +487,7 @@ impl bool { match rhs.kind() { ty::ConstKind::Param(_) => { - if INSTANTIATE_RHS_WITH_INFER { + if HANDLE_RHS < HANDLE_UNNORMALIZED { return true; } } @@ -485,7 +510,7 @@ impl { - INSTANTIATE_LHS_WITH_INFER + HANDLE_LHS < HANDLE_UNNORMALIZED || match rhs.kind() { ty::ConstKind::Param(rhs) => lhs == rhs, _ => false, From 92ed2df57980af5c91b6cdfa8d5b89f0da49338d Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 18 May 2026 11:07:27 +0200 Subject: [PATCH 5/8] break sqlparser --- .../rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) 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 3693a18f305f6..17e14d6051604 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 @@ -309,18 +309,10 @@ where // We currently only consider a cycle coinductive if it steps // into a where-clause of a coinductive trait. CurrentGoalKind::CoinductiveTrait => PathKind::Coinductive, - // While normalizing via an impl does step into a where-clause of - // an impl, accessing the associated item immediately steps out of - // it again. This means cycles/recursive calls are not guarded - // by impls used for normalization. - // - // See tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs - // for how this can go wrong. - CurrentGoalKind::NormalizesTo => PathKind::Inductive, // We probably want to make all traits coinductive in the future, // so we treat cycles involving where-clauses of not-yet coinductive // traits as ambiguous for now. - CurrentGoalKind::Misc => PathKind::Unknown, + CurrentGoalKind::NormalizesTo | CurrentGoalKind::Misc => PathKind::Unknown, }, // Relating types is always unproductive. If we were to map proof trees to // corecursive functions as explained in #136824, relating types never From e13ab42dafb60d7b1debe2888480ef25ad1197f5 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 18 May 2026 23:27:20 +0200 Subject: [PATCH 6/8] fix param_env stuff --- compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 81866c11e6089..96fc643f278ee 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -188,7 +188,7 @@ where ) -> Result<(), NoSolution> { if let Some(projection_pred) = assumption.as_projection_clause() && projection_pred.item_def_id() == goal.predicate.def_id() - && DeepRejectCtxt::relate_fully_normalized(ecx.cx()).args_may_unify( + && DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( goal.predicate.alias.args, projection_pred.skip_binder().projection_term.args, ) From 43b5b2fee93f9d8485c12407b2142336ccdc75f1 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 18 May 2026 23:34:09 +0200 Subject: [PATCH 7/8] norm folder perf --- .../rustc_next_trait_solver/src/normalize.rs | 74 ++++++++++--------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/normalize.rs b/compiler/rustc_next_trait_solver/src/normalize.rs index 713ba9bd47a78..23962cf2c6e93 100644 --- a/compiler/rustc_next_trait_solver/src/normalize.rs +++ b/compiler/rustc_next_trait_solver/src/normalize.rs @@ -122,7 +122,7 @@ where &mut self, alias_term: AliasTerm, has_escaping: HasEscapingBoundVars, - ) -> Result { + ) -> Result, E> { let current_universe = self.infcx.universe(); self.infcx.create_next_universe(); @@ -143,11 +143,11 @@ where normalized.visit_with(&mut visitor); let max_universe = visitor.max_universe(); if current_universe.cannot_name(max_universe) { - return Ok(alias_term.to_term(self.infcx.cx())); + return Ok(None); } } - Ok(normalized) + Ok(Some(normalized)) } } @@ -187,29 +187,32 @@ where // With eager normalization, we should normalize the args of alias before // normalizing the alias itself. - let folded_ty = ty.try_super_fold_with(self)?; - let ty::Alias(alias_ty) = folded_ty.kind() else { return Ok(folded_ty) }; + let ty = ty.try_super_fold_with(self)?; + let ty::Alias(alias_ty) = ty.kind() else { return Ok(ty) }; - let result = if folded_ty.has_escaping_bound_vars() { + let result = if alias_ty.has_escaping_bound_vars() { let (alias_ty, mapped_regions, mapped_types, mapped_consts) = BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, alias_ty); - let normalized_term = ensure_sufficient_stack(|| { + match ensure_sufficient_stack(|| { self.normalize_alias_term(alias_ty.into(), HasEscapingBoundVars::Yes) - })?; - let normalized_ty = PlaceholderReplacer::replace_placeholders( - infcx, - mapped_regions, - mapped_types, - mapped_consts, - &self.universes, - normalized_term.expect_ty(), - ); - normalized_ty + })? { + Some(result) => PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + result.expect_ty(), + ), + None => ty, + } } else { - ensure_sufficient_stack(|| { + match ensure_sufficient_stack(|| { self.normalize_alias_term(alias_ty.into(), HasEscapingBoundVars::No) - })? - .expect_ty() + })? { + Some(term) => term.expect_ty(), + None => ty, + } }; assert!(self.cache.insert(ty, result).is_none(), "{ty:?} {result:?} {:?}", self.cache); @@ -231,29 +234,32 @@ where if ct.has_escaping_bound_vars() { let (uv, mapped_regions, mapped_types, mapped_consts) = BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, uv); - let result = ensure_sufficient_stack(|| { + match ensure_sufficient_stack(|| { self.normalize_alias_term( AliasTerm::from_unevaluated_const(infcx.cx(), uv), HasEscapingBoundVars::Yes, ) - })? - .expect_const(); - Ok(PlaceholderReplacer::replace_placeholders( - infcx, - mapped_regions, - mapped_types, - mapped_consts, - &self.universes, - result, - )) + })? { + Some(result) => Ok(PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + result.expect_const(), + )), + None => Ok(ct), + } } else { - Ok(ensure_sufficient_stack(|| { + match ensure_sufficient_stack(|| { self.normalize_alias_term( AliasTerm::from_unevaluated_const(infcx.cx(), uv), HasEscapingBoundVars::No, ) - })? - .expect_const()) + })? { + Some(term) => Ok(term.expect_const()), + None => Ok(ct), + } } } From 8be6539eb124087efb68202d09993a3f70b6fe6e Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 19 May 2026 00:17:09 +0200 Subject: [PATCH 8/8] W --- compiler/rustc_next_trait_solver/src/normalize.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/normalize.rs b/compiler/rustc_next_trait_solver/src/normalize.rs index 23962cf2c6e93..3fb5f71b825ae 100644 --- a/compiler/rustc_next_trait_solver/src/normalize.rs +++ b/compiler/rustc_next_trait_solver/src/normalize.rs @@ -187,8 +187,8 @@ where // With eager normalization, we should normalize the args of alias before // normalizing the alias itself. - let ty = ty.try_super_fold_with(self)?; - let ty::Alias(alias_ty) = ty.kind() else { return Ok(ty) }; + let folded_ty = ty.try_super_fold_with(self)?; + let ty::Alias(alias_ty) = folded_ty.kind() else { return Ok(folded_ty) }; let result = if alias_ty.has_escaping_bound_vars() { let (alias_ty, mapped_regions, mapped_types, mapped_consts) = @@ -204,14 +204,14 @@ where &self.universes, result.expect_ty(), ), - None => ty, + None => folded_ty, } } else { match ensure_sufficient_stack(|| { self.normalize_alias_term(alias_ty.into(), HasEscapingBoundVars::No) })? { Some(term) => term.expect_ty(), - None => ty, + None => folded_ty, } };