about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <476013+matthiaskrgr@users.noreply.github.com>2025-05-08 08:14:16 +0200
committerGitHub <noreply@github.com>2025-05-08 08:14:16 +0200
commit8a3ab85e7d995965c4482a28dfaa21cb4fb17984 (patch)
treeb22134a9e90d5d564360fa4dd376c720ff5a663c
parent7e552b46af72df390ed233b58a7f51650515b2a8 (diff)
parent8a21d1b495556554dc4ee55338423870b4f7dd53 (diff)
downloadrust-8a3ab85e7d995965c4482a28dfaa21cb4fb17984.tar.gz
rust-8a3ab85e7d995965c4482a28dfaa21cb4fb17984.zip
Rollup merge of #140260 - compiler-errors:only-global-post-norm, r=lcnr
Only prefer param-env candidates if they remain non-global after norm

Introduce `CandidateSource::GlobalParamEnv`, and dynamically compute the `CandidateSource` based on whether the predicate contains params *post-normalization*.

This code needs some cleanup and documentation. I'm just putting this up for review.

cc https://github.com/rust-lang/trait-system-refactor-initiative/issues/179

r? lcnr
-rw-r--r--compiler/rustc_borrowck/src/type_check/opaque_types.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs6
-rw-r--r--compiler/rustc_infer/src/infer/outlives/for_liveness.rs4
-rw-r--r--compiler/rustc_lint/src/impl_trait_overcaptures.rs5
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs2
-rw-r--r--compiler/rustc_middle/src/ty/visit.rs4
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs176
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/effect_goals.rs45
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs8
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs64
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs81
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs4
-rw-r--r--compiler/rustc_ty_utils/src/ty.rs5
-rw-r--r--compiler/rustc_type_ir/src/binder.rs6
-rw-r--r--compiler/rustc_type_ir/src/solve/mod.rs15
-rw-r--r--compiler/rustc_type_ir/src/ty_kind/closure.rs2
-rw-r--r--compiler/rustc_type_ir/src/visit.rs8
-rw-r--r--src/tools/clippy/clippy_utils/src/ty/mod.rs6
-rw-r--r--tests/ui/traits/next-solver/global-param-env-after-norm.rs15
-rw-r--r--tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr20
20 files changed, 301 insertions, 179 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/opaque_types.rs b/compiler/rustc_borrowck/src/type_check/opaque_types.rs
index d41cbf757d7..341c50c37f6 100644
--- a/compiler/rustc_borrowck/src/type_check/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/type_check/opaque_types.rs
@@ -266,10 +266,6 @@ impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for ConstrainOpaqueTypeRegionVisitor<'t
 where
     OP: FnMut(ty::Region<'tcx>),
 {
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
-        t.super_visit_with(self);
-    }
-
     fn visit_region(&mut self, r: ty::Region<'tcx>) {
         match r.kind() {
             // ignore bound regions, keep visiting
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index bf91eb1b8fd..4419d5dc7d6 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -9,8 +9,8 @@ use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::{AmbigArg, HirId};
 use rustc_middle::bug;
 use rustc_middle::ty::{
-    self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
-    TypeVisitor, Upcast,
+    self as ty, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
+    TypeVisitableExt, TypeVisitor, Upcast,
 };
 use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
 use rustc_trait_selection::traits;
@@ -996,7 +996,7 @@ struct GenericParamAndBoundVarCollector<'a, 'tcx> {
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'_, 'tcx> {
     type Result = ControlFlow<ErrorGuaranteed>;
 
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
+    fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
         &mut self,
         binder: &ty::Binder<'tcx, T>,
     ) -> Self::Result {
diff --git a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs
index c44d9723f29..2a4b9776f68 100644
--- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs
+++ b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs
@@ -24,10 +24,6 @@ impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for FreeRegionsVisitor<'tcx, OP>
 where
     OP: FnMut(ty::Region<'tcx>),
 {
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
-        t.super_visit_with(self);
-    }
-
     fn visit_region(&mut self, r: ty::Region<'tcx>) {
         match r.kind() {
             // ignore bound regions, keep visiting
diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
index 7f4789ad0d9..a8f45d043be 100644
--- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs
+++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
@@ -15,7 +15,8 @@ use rustc_middle::ty::relate::{
     Relate, RelateResult, TypeRelation, structurally_relate_consts, structurally_relate_tys,
 };
 use rustc_middle::ty::{
-    self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
+    self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
+    TypeVisitor,
 };
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint::FutureIncompatibilityReason;
@@ -209,7 +210,7 @@ where
     VarFn: FnOnce() -> FxHashMap<DefId, ty::Variance>,
     OutlivesFn: FnOnce() -> OutlivesEnvironment<'tcx>,
 {
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
+    fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
         // When we get into a binder, we need to add its own bound vars to the scope.
         let mut added = vec![];
         for arg in t.bound_vars() {
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index bc1423a9e3c..0250c777faf 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -2934,7 +2934,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
 
     fn prepare_region_info<T>(&mut self, value: &ty::Binder<'tcx, T>)
     where
-        T: TypeVisitable<TyCtxt<'tcx>>,
+        T: TypeFoldable<TyCtxt<'tcx>>,
     {
         struct RegionNameCollector<'tcx> {
             used_region_names: FxHashSet<Symbol>,
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index 3853a804a92..f8042174599 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -66,7 +66,7 @@ impl<'tcx> TyCtxt<'tcx> {
         {
             type Result = ControlFlow<()>;
 
-            fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
+            fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
                 &mut self,
                 t: &Binder<'tcx, T>,
             ) -> Self::Result {
@@ -168,7 +168,7 @@ impl LateBoundRegionsCollector {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector {
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) {
+    fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) {
         self.current_index.shift_in(1);
         t.super_visit_with(self);
         self.current_index.shift_out(1);
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 101129a231e..345a272895d 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
@@ -2,21 +2,24 @@
 
 pub(super) mod structural_traits;
 
+use std::ops::ControlFlow;
+
 use derive_where::derive_where;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
 use rustc_type_ir::{
-    self as ty, Interner, TypeFoldable, TypeVisitableExt as _, TypingMode, Upcast as _, elaborate,
+    self as ty, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _,
+    TypeVisitor, TypingMode, Upcast as _, elaborate,
 };
 use tracing::{debug, instrument};
 
-use super::has_only_region_constraints;
 use super::trait_goals::TraitGoalProvenVia;
+use super::{has_only_region_constraints, inspect};
 use crate::delegate::SolverDelegate;
 use crate::solve::inspect::ProbeKind;
 use crate::solve::{
     BuiltinImplSource, CandidateSource, CanonicalResponse, Certainty, EvalCtxt, Goal, GoalSource,
-    MaybeCause, NoSolution, QueryResult,
+    MaybeCause, NoSolution, ParamEnvSource, QueryResult,
 };
 
 enum AliasBoundKind {
@@ -49,18 +52,6 @@ where
 
     fn trait_def_id(self, cx: I) -> I::DefId;
 
-    /// Try equating an assumption predicate against a goal's predicate. If it
-    /// holds, then execute the `then` callback, which should do any additional
-    /// work, then produce a response (typically by executing
-    /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]).
-    fn probe_and_match_goal_against_assumption(
-        ecx: &mut EvalCtxt<'_, D>,
-        source: CandidateSource<I>,
-        goal: Goal<I, Self>,
-        assumption: I::Clause,
-        then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
-    ) -> Result<Candidate<I>, NoSolution>;
-
     /// Consider a clause, which consists of a "assumption" and some "requirements",
     /// to satisfy a goal. If the requirements hold, then attempt to satisfy our
     /// goal by equating it with the assumption.
@@ -119,6 +110,67 @@ where
         alias_ty: ty::AliasTy<I>,
     ) -> Vec<Candidate<I>>;
 
+    fn probe_and_consider_param_env_candidate(
+        ecx: &mut EvalCtxt<'_, D>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+    ) -> Result<Candidate<I>, NoSolution> {
+        Self::fast_reject_assumption(ecx, goal, assumption)?;
+
+        ecx.probe(|candidate: &Result<Candidate<I>, NoSolution>| match candidate {
+            Ok(candidate) => inspect::ProbeKind::TraitCandidate {
+                source: candidate.source,
+                result: Ok(candidate.result),
+            },
+            Err(NoSolution) => inspect::ProbeKind::TraitCandidate {
+                source: CandidateSource::ParamEnv(ParamEnvSource::Global),
+                result: Err(NoSolution),
+            },
+        })
+        .enter(|ecx| {
+            Self::match_assumption(ecx, goal, assumption)?;
+            let source = ecx.characterize_param_env_assumption(goal.param_env, assumption)?;
+            Ok(Candidate {
+                source,
+                result: ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)?,
+            })
+        })
+    }
+
+    /// Try equating an assumption predicate against a goal's predicate. If it
+    /// holds, then execute the `then` callback, which should do any additional
+    /// work, then produce a response (typically by executing
+    /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]).
+    fn probe_and_match_goal_against_assumption(
+        ecx: &mut EvalCtxt<'_, D>,
+        source: CandidateSource<I>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+        then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        Self::fast_reject_assumption(ecx, goal, assumption)?;
+
+        ecx.probe_trait_candidate(source).enter(|ecx| {
+            Self::match_assumption(ecx, goal, assumption)?;
+            then(ecx)
+        })
+    }
+
+    /// Try to reject the assumption based off of simple heuristics, such as [`ty::ClauseKind`]
+    /// and `DefId`.
+    fn fast_reject_assumption(
+        ecx: &mut EvalCtxt<'_, D>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+    ) -> Result<(), NoSolution>;
+
+    /// Relate the goal and assumption.
+    fn match_assumption(
+        ecx: &mut EvalCtxt<'_, D>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+    ) -> Result<(), NoSolution>;
+
     fn consider_impl_candidate(
         ecx: &mut EvalCtxt<'_, D>,
         goal: Goal<I, Self>,
@@ -500,14 +552,8 @@ where
         goal: Goal<I, G>,
         candidates: &mut Vec<Candidate<I>>,
     ) {
-        for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() {
-            candidates.extend(G::probe_and_consider_implied_clause(
-                self,
-                CandidateSource::ParamEnv(i),
-                goal,
-                assumption,
-                [],
-            ));
+        for assumption in goal.param_env.caller_bounds().iter() {
+            candidates.extend(G::probe_and_consider_param_env_candidate(self, goal, assumption));
         }
     }
 
@@ -943,4 +989,88 @@ where
             }
         }
     }
+
+    /// Compute whether a param-env assumption is global or non-global after normalizing it.
+    ///
+    /// This is necessary because, for example, given:
+    ///
+    /// ```ignore,rust
+    /// where
+    ///     T: Trait<Assoc = u32>,
+    ///     i32: From<T::Assoc>,
+    /// ```
+    ///
+    /// The `i32: From<T::Assoc>` bound is non-global before normalization, but is global after.
+    /// Since the old trait solver normalized param-envs eagerly, we want to emulate this
+    /// behavior lazily.
+    fn characterize_param_env_assumption(
+        &mut self,
+        param_env: I::ParamEnv,
+        assumption: I::Clause,
+    ) -> Result<CandidateSource<I>, NoSolution> {
+        // FIXME: This should be fixed, but it also requires changing the behavior
+        // in the old solver which is currently relied on.
+        if assumption.has_bound_vars() {
+            return Ok(CandidateSource::ParamEnv(ParamEnvSource::NonGlobal));
+        }
+
+        match assumption.visit_with(&mut FindParamInClause { ecx: self, param_env }) {
+            ControlFlow::Break(Err(NoSolution)) => Err(NoSolution),
+            ControlFlow::Break(Ok(())) => Ok(CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)),
+            ControlFlow::Continue(()) => Ok(CandidateSource::ParamEnv(ParamEnvSource::Global)),
+        }
+    }
+}
+
+struct FindParamInClause<'a, 'b, D: SolverDelegate<Interner = I>, I: Interner> {
+    ecx: &'a mut EvalCtxt<'b, D>,
+    param_env: I::ParamEnv,
+}
+
+impl<D, I> TypeVisitor<I> for FindParamInClause<'_, '_, D, I>
+where
+    D: SolverDelegate<Interner = I>,
+    I: Interner,
+{
+    type Result = ControlFlow<Result<(), NoSolution>>;
+
+    fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+        self.ecx.enter_forall(t.clone(), |ecx, v| {
+            v.visit_with(&mut FindParamInClause { ecx, param_env: self.param_env })
+        })
+    }
+
+    fn visit_ty(&mut self, ty: I::Ty) -> Self::Result {
+        let Ok(ty) = self.ecx.structurally_normalize_ty(self.param_env, ty) else {
+            return ControlFlow::Break(Err(NoSolution));
+        };
+
+        if let ty::Placeholder(_) = ty.kind() {
+            ControlFlow::Break(Ok(()))
+        } else {
+            ty.super_visit_with(self)
+        }
+    }
+
+    fn visit_const(&mut self, ct: I::Const) -> Self::Result {
+        let Ok(ct) = self.ecx.structurally_normalize_const(self.param_env, ct) else {
+            return ControlFlow::Break(Err(NoSolution));
+        };
+
+        if let ty::ConstKind::Placeholder(_) = ct.kind() {
+            ControlFlow::Break(Ok(()))
+        } else {
+            ct.super_visit_with(self)
+        }
+    }
+
+    fn visit_region(&mut self, r: I::Region) -> Self::Result {
+        match self.ecx.eager_resolve_region(r).kind() {
+            ty::ReStatic | ty::ReError(_) => ControlFlow::Continue(()),
+            ty::ReVar(_) | ty::RePlaceholder(_) => ControlFlow::Break(Ok(())),
+            ty::ReErased | ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReBound(..) => {
+                unreachable!()
+            }
+        }
+    }
 }
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 5edc777262b..84a83d79cf0 100644
--- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
@@ -36,39 +36,38 @@ where
         self.def_id()
     }
 
-    fn probe_and_match_goal_against_assumption(
+    fn fast_reject_assumption(
         ecx: &mut EvalCtxt<'_, D>,
-        source: rustc_type_ir::solve::CandidateSource<I>,
         goal: Goal<I, Self>,
-        assumption: <I as Interner>::Clause,
-        then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
-    ) -> Result<Candidate<I>, NoSolution> {
+        assumption: I::Clause,
+    ) -> Result<(), NoSolution> {
         if let Some(host_clause) = assumption.as_host_effect_clause() {
             if host_clause.def_id() == goal.predicate.def_id()
                 && host_clause.constness().satisfies(goal.predicate.constness)
             {
-                if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
+                if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
                     goal.predicate.trait_ref.args,
                     host_clause.skip_binder().trait_ref.args,
                 ) {
-                    return Err(NoSolution);
+                    return Ok(());
                 }
-
-                ecx.probe_trait_candidate(source).enter(|ecx| {
-                    let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause);
-                    ecx.eq(
-                        goal.param_env,
-                        goal.predicate.trait_ref,
-                        assumption_trait_pred.trait_ref,
-                    )?;
-                    then(ecx)
-                })
-            } else {
-                Err(NoSolution)
             }
-        } else {
-            Err(NoSolution)
         }
+
+        Err(NoSolution)
+    }
+
+    fn match_assumption(
+        ecx: &mut EvalCtxt<'_, D>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+    ) -> Result<(), NoSolution> {
+        let host_clause = assumption.as_host_effect_clause().unwrap();
+
+        let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause);
+        ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
+
+        Ok(())
     }
 
     /// Register additional assumptions for aliases corresponding to `~const` item bounds.
@@ -124,7 +123,7 @@ where
     fn consider_impl_candidate(
         ecx: &mut EvalCtxt<'_, D>,
         goal: Goal<I, Self>,
-        impl_def_id: <I as Interner>::DefId,
+        impl_def_id: I::DefId,
     ) -> Result<Candidate<I>, NoSolution> {
         let cx = ecx.cx();
 
@@ -178,7 +177,7 @@ where
 
     fn consider_error_guaranteed_candidate(
         ecx: &mut EvalCtxt<'_, D>,
-        _guar: <I as Interner>::ErrorGuaranteed,
+        _guar: I::ErrorGuaranteed,
     ) -> Result<Candidate<I>, NoSolution> {
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| 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 c13e7308055..bf9f21d05f6 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
@@ -1000,6 +1000,14 @@ where
         self.delegate.resolve_vars_if_possible(value)
     }
 
+    pub(super) fn eager_resolve_region(&self, r: I::Region) -> I::Region {
+        if let ty::ReVar(vid) = r.kind() {
+            self.delegate.opportunistic_resolve_lt_var(vid)
+        } else {
+            r
+        }
+    }
+
     pub(super) fn fresh_args_for_item(&mut self, def_id: I::DefId) -> I::GenericArgs {
         let args = self.delegate.fresh_args_for_item(def_id);
         for arg in args.iter() {
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 400b4ce1200..b90e34e7810 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
@@ -106,50 +106,48 @@ where
         self.trait_def_id(cx)
     }
 
-    fn probe_and_match_goal_against_assumption(
+    fn fast_reject_assumption(
         ecx: &mut EvalCtxt<'_, D>,
-        source: CandidateSource<I>,
         goal: Goal<I, Self>,
         assumption: I::Clause,
-        then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
-    ) -> Result<Candidate<I>, NoSolution> {
+    ) -> Result<(), NoSolution> {
         if let Some(projection_pred) = assumption.as_projection_clause() {
             if projection_pred.item_def_id() == goal.predicate.def_id() {
-                let cx = ecx.cx();
-                if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
+                if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
                     goal.predicate.alias.args,
                     projection_pred.skip_binder().projection_term.args,
                 ) {
-                    return Err(NoSolution);
+                    return Ok(());
                 }
-                ecx.probe_trait_candidate(source).enter(|ecx| {
-                    let assumption_projection_pred =
-                        ecx.instantiate_binder_with_infer(projection_pred);
-                    ecx.eq(
-                        goal.param_env,
-                        goal.predicate.alias,
-                        assumption_projection_pred.projection_term,
-                    )?;
-
-                    ecx.instantiate_normalizes_to_term(goal, assumption_projection_pred.term);
-
-                    // Add GAT where clauses from the trait's definition
-                    // FIXME: We don't need these, since these are the type's own WF obligations.
-                    ecx.add_goals(
-                        GoalSource::AliasWellFormed,
-                        cx.own_predicates_of(goal.predicate.def_id())
-                            .iter_instantiated(cx, goal.predicate.alias.args)
-                            .map(|pred| goal.with(cx, pred)),
-                    );
-
-                    then(ecx)
-                })
-            } else {
-                Err(NoSolution)
             }
-        } else {
-            Err(NoSolution)
         }
+
+        Err(NoSolution)
+    }
+
+    fn match_assumption(
+        ecx: &mut EvalCtxt<'_, D>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+    ) -> Result<(), NoSolution> {
+        let projection_pred = assumption.as_projection_clause().unwrap();
+
+        let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred);
+        ecx.eq(goal.param_env, goal.predicate.alias, assumption_projection_pred.projection_term)?;
+
+        ecx.instantiate_normalizes_to_term(goal, assumption_projection_pred.term);
+
+        // Add GAT where clauses from the trait's definition
+        // FIXME: We don't need these, since these are the type's own WF obligations.
+        let cx = ecx.cx();
+        ecx.add_goals(
+            GoalSource::AliasWellFormed,
+            cx.own_predicates_of(goal.predicate.def_id())
+                .iter_instantiated(cx, goal.predicate.alias.args)
+                .map(|pred| goal.with(cx, pred)),
+        );
+
+        Ok(())
     }
 
     fn consider_additional_alias_assumptions(
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 d19249df387..e3addf8bf93 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -17,7 +17,7 @@ use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidates
 use crate::solve::inspect::ProbeKind;
 use crate::solve::{
     BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
-    NoSolution, QueryResult,
+    NoSolution, ParamEnvSource,
 };
 
 impl<D, I> assembly::GoalKind<D> for TraitPredicate<I>
@@ -125,39 +125,38 @@ where
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
     }
 
-    fn probe_and_match_goal_against_assumption(
+    fn fast_reject_assumption(
         ecx: &mut EvalCtxt<'_, D>,
-        source: CandidateSource<I>,
         goal: Goal<I, Self>,
         assumption: I::Clause,
-        then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
-    ) -> Result<Candidate<I>, NoSolution> {
+    ) -> Result<(), NoSolution> {
         if let Some(trait_clause) = assumption.as_trait_clause() {
             if trait_clause.def_id() == goal.predicate.def_id()
                 && trait_clause.polarity() == goal.predicate.polarity
             {
-                if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
+                if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
                     goal.predicate.trait_ref.args,
                     trait_clause.skip_binder().trait_ref.args,
                 ) {
-                    return Err(NoSolution);
+                    return Ok(());
                 }
-
-                ecx.probe_trait_candidate(source).enter(|ecx| {
-                    let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
-                    ecx.eq(
-                        goal.param_env,
-                        goal.predicate.trait_ref,
-                        assumption_trait_pred.trait_ref,
-                    )?;
-                    then(ecx)
-                })
-            } else {
-                Err(NoSolution)
             }
-        } else {
-            Err(NoSolution)
         }
+
+        Err(NoSolution)
+    }
+
+    fn match_assumption(
+        ecx: &mut EvalCtxt<'_, D>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+    ) -> Result<(), NoSolution> {
+        let trait_clause = assumption.as_trait_clause().unwrap();
+
+        let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
+        ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
+
+        Ok(())
     }
 
     fn consider_auto_trait_candidate(
@@ -1253,10 +1252,9 @@ where
     D: SolverDelegate<Interner = I>,
     I: Interner,
 {
-    #[instrument(level = "debug", skip(self, goal), ret)]
+    #[instrument(level = "debug", skip(self), ret)]
     pub(super) fn merge_trait_candidates(
         &mut self,
-        goal: Goal<I, TraitPredicate<I>>,
         mut candidates: Vec<Candidate<I>>,
     ) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
         if let TypingMode::Coherence = self.typing_mode() {
@@ -1284,21 +1282,9 @@ where
 
         // If there are non-global where-bounds, prefer where-bounds
         // (including global ones) over everything else.
-        let has_non_global_where_bounds = candidates.iter().any(|c| match c.source {
-            CandidateSource::ParamEnv(idx) => {
-                let where_bound = goal.param_env.caller_bounds().get(idx).unwrap();
-                let ty::ClauseKind::Trait(trait_pred) = where_bound.kind().skip_binder() else {
-                    unreachable!("expected trait-bound: {where_bound:?}");
-                };
-
-                if trait_pred.has_bound_vars() || !trait_pred.is_global() {
-                    return true;
-                }
-
-                false
-            }
-            _ => false,
-        });
+        let has_non_global_where_bounds = candidates
+            .iter()
+            .any(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)));
         if has_non_global_where_bounds {
             let where_bounds: Vec<_> = candidates
                 .iter()
@@ -1331,13 +1317,16 @@ where
         // is still reported as being proven-via the param-env so that rigid projections
         // operate correctly. Otherwise, drop all global where-bounds before merging the
         // remaining candidates.
-        let proven_via =
-            if candidates.iter().all(|c| matches!(c.source, CandidateSource::ParamEnv(_))) {
-                TraitGoalProvenVia::ParamEnv
-            } else {
-                candidates.retain(|c| !matches!(c.source, CandidateSource::ParamEnv(_)));
-                TraitGoalProvenVia::Misc
-            };
+        let proven_via = if candidates
+            .iter()
+            .all(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global)))
+        {
+            TraitGoalProvenVia::ParamEnv
+        } else {
+            candidates
+                .retain(|c| !matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global)));
+            TraitGoalProvenVia::Misc
+        };
 
         let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect();
         if let Some(response) = self.try_merge_responses(&all_candidates) {
@@ -1353,7 +1342,7 @@ where
         goal: Goal<I, TraitPredicate<I>>,
     ) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
         let candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
-        self.merge_trait_candidates(goal, candidates)
+        self.merge_trait_candidates(candidates)
     }
 
     fn try_stall_coroutine_witness(
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index a54eb80fedc..eb34cb10c68 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -9,7 +9,7 @@ use rustc_macros::extension;
 pub use rustc_middle::traits::query::NormalizationResult;
 use rustc_middle::ty::{
     self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable,
-    TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode,
+    TypeVisitableExt, TypeVisitor, TypingMode,
 };
 use rustc_span::DUMMY_SP;
 use tracing::{debug, info, instrument};
@@ -127,7 +127,7 @@ struct MaxEscapingBoundVarVisitor {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxEscapingBoundVarVisitor {
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
+    fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
         self.outer_index.shift_in(1);
         t.super_visit_with(self);
         self.outer_index.shift_out(1);
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index 31d69eef5ec..0c49ddff39b 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -7,7 +7,8 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::bug;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{
-    self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast, fold_regions,
+    self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast,
+    fold_regions,
 };
 use rustc_span::DUMMY_SP;
 use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
@@ -185,7 +186,7 @@ struct ImplTraitInTraitFinder<'a, 'tcx> {
 }
 
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, binder: &ty::Binder<'tcx, T>) {
+    fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, binder: &ty::Binder<'tcx, T>) {
         self.depth.shift_in(1);
         binder.super_visit_with(self);
         self.depth.shift_out(1);
diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs
index 27ea4e211fe..000cf1e1fd8 100644
--- a/compiler/rustc_type_ir/src/binder.rs
+++ b/compiler/rustc_type_ir/src/binder.rs
@@ -128,7 +128,7 @@ impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Binder<I, T> {
     }
 }
 
-impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Binder<I, T> {
+impl<I: Interner, T: TypeFoldable<I>> TypeVisitable<I> for Binder<I, T> {
     fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
         visitor.visit_binder(self)
     }
@@ -147,7 +147,7 @@ impl<I: Interner, T: TypeFoldable<I>> TypeSuperFoldable<I> for Binder<I, T> {
     }
 }
 
-impl<I: Interner, T: TypeVisitable<I>> TypeSuperVisitable<I> for Binder<I, T> {
+impl<I: Interner, T: TypeFoldable<I>> TypeSuperVisitable<I> for Binder<I, T> {
     fn super_visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
         self.as_ref().skip_binder().visit_with(visitor)
     }
@@ -292,7 +292,7 @@ impl<I: Interner> ValidateBoundVars<I> {
 impl<I: Interner> TypeVisitor<I> for ValidateBoundVars<I> {
     type Result = ControlFlow<()>;
 
-    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &Binder<I, T>) -> Self::Result {
+    fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &Binder<I, T>) -> Self::Result {
         self.binder_index.shift_in(1);
         let result = t.super_visit_with(self);
         self.binder_index.shift_out(1);
diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs
index 4e9b87fdf74..e45a7b7fbc1 100644
--- a/compiler/rustc_type_ir/src/solve/mod.rs
+++ b/compiler/rustc_type_ir/src/solve/mod.rs
@@ -147,9 +147,8 @@ pub enum CandidateSource<I: Interner> {
     /// For a list of all traits with builtin impls, check out the
     /// `EvalCtxt::assemble_builtin_impl_candidates` method.
     BuiltinImpl(BuiltinImplSource),
-    /// An assumption from the environment.
-    ///
-    /// More precisely we've used the `n-th` assumption in the `param_env`.
+    /// An assumption from the environment. Stores a [`ParamEnvSource`], since we
+    /// prefer non-global param-env candidates in candidate assembly.
     ///
     /// ## Examples
     ///
@@ -160,7 +159,7 @@ pub enum CandidateSource<I: Interner> {
     ///     (x.clone(), x)
     /// }
     /// ```
-    ParamEnv(usize),
+    ParamEnv(ParamEnvSource),
     /// If the self type is an alias type, e.g. an opaque type or a projection,
     /// we know the bounds on that alias to hold even without knowing its concrete
     /// underlying type.
@@ -190,6 +189,14 @@ pub enum CandidateSource<I: Interner> {
 }
 
 #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
+pub enum ParamEnvSource {
+    /// Preferred eagerly.
+    NonGlobal,
+    // Not considered unless there are non-global param-env candidates too.
+    Global,
+}
+
+#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
 #[cfg_attr(
     feature = "nightly",
     derive(HashStable_NoContext, Encodable_NoContext, Decodable_NoContext)
diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs
index d1ca9bdb7fb..8ba985d2d19 100644
--- a/compiler/rustc_type_ir/src/ty_kind/closure.rs
+++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs
@@ -342,7 +342,7 @@ struct HasRegionsBoundAt {
 // FIXME: Could be optimized to not walk into components with no escaping bound vars.
 impl<I: Interner> TypeVisitor<I> for HasRegionsBoundAt {
     type Result = ControlFlow<()>;
-    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+    fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
         self.binder.shift_in(1);
         t.super_visit_with(self)?;
         self.binder.shift_out(1);
diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs
index 2285e0e75de..ccb84e25911 100644
--- a/compiler/rustc_type_ir/src/visit.rs
+++ b/compiler/rustc_type_ir/src/visit.rs
@@ -52,7 +52,7 @@ use smallvec::SmallVec;
 use thin_vec::ThinVec;
 
 use crate::inherent::*;
-use crate::{self as ty, Interner, TypeFlags};
+use crate::{self as ty, Interner, TypeFlags, TypeFoldable};
 
 /// This trait is implemented for every type that can be visited,
 /// providing the skeleton of the traversal.
@@ -94,7 +94,7 @@ pub trait TypeVisitor<I: Interner>: Sized {
     #[cfg(not(feature = "nightly"))]
     type Result: VisitorResult;
 
-    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+    fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
         t.super_visit_with(self)
     }
 
@@ -401,7 +401,7 @@ impl std::fmt::Debug for HasTypeFlagsVisitor {
 impl<I: Interner> TypeVisitor<I> for HasTypeFlagsVisitor {
     type Result = ControlFlow<FoundFlags>;
 
-    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+    fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
         // If we're looking for the HAS_BINDER_VARS flag, check if the
         // binder has vars. This won't be present in the binder's bound
         // value, so we need to check here too.
@@ -510,7 +510,7 @@ struct HasEscapingVarsVisitor {
 impl<I: Interner> TypeVisitor<I> for HasEscapingVarsVisitor {
     type Result = ControlFlow<FoundEscapingVars>;
 
-    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+    fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
         self.outer_index.shift_in(1);
         let result = t.super_visit_with(self);
         self.outer_index.shift_out(1);
diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs
index 8db9cd593b3..da09edd7f7c 100644
--- a/src/tools/clippy/clippy_utils/src/ty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs
@@ -20,8 +20,8 @@ use rustc_middle::traits::EvaluationResult;
 use rustc_middle::ty::layout::ValidityRequirement;
 use rustc_middle::ty::{
     self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
-    GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
-    TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
+    GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
+    TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
 };
 use rustc_span::symbol::Ident;
 use rustc_span::{DUMMY_SP, Span, Symbol, sym};
@@ -915,7 +915,7 @@ pub fn for_each_top_level_late_bound_region<B>(
                 ControlFlow::Continue(())
             }
         }
-        fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result {
+        fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result {
             self.index += 1;
             let res = t.super_visit_with(self);
             self.index -= 1;
diff --git a/tests/ui/traits/next-solver/global-param-env-after-norm.rs b/tests/ui/traits/next-solver/global-param-env-after-norm.rs
new file mode 100644
index 00000000000..0d098db67d3
--- /dev/null
+++ b/tests/ui/traits/next-solver/global-param-env-after-norm.rs
@@ -0,0 +1,15 @@
+//@ check-pass
+//@ compile-flags: -Znext-solver
+
+struct NewSolver;
+struct OldSolver;
+
+fn foo<T>()
+where
+    T: Iterator<Item = NewSolver>,
+    OldSolver: Into<T::Item>,
+{
+    let x: OldSolver = OldSolver.into();
+}
+
+fn main() {}
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 e91a48f62ae..f5fd9ce9864 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
@@ -4,24 +4,6 @@ error[E0275]: overflow evaluating the requirement `<T as Trait>::Assoc: Trait`
 LL |     <T as Trait>::Assoc: Trait,
    |                          ^^^^^
 
-error[E0275]: overflow evaluating the requirement `<T as Trait>::Assoc well-formed`
-  --> $DIR/normalize-param-env-4.rs:19:26
-   |
-LL |     <T as Trait>::Assoc: Trait,
-   |                          ^^^^^
-
-error[E0275]: overflow evaluating the requirement `T: Trait`
-  --> $DIR/normalize-param-env-4.rs:32:19
-   |
-LL |     impls_trait::<T>();
-   |                   ^
-   |
-note: required by a bound in `impls_trait`
-  --> $DIR/normalize-param-env-4.rs:15:19
-   |
-LL | fn impls_trait<T: Trait>() {}
-   |                   ^^^^^ required by this bound in `impls_trait`
-
-error: aborting due to 3 previous errors
+error: aborting due to 1 previous error
 
 For more information about this error, try `rustc --explain E0275`.