From 7e43fd248740886dd756235806e8aaf7582f559c Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 20 Aug 2026 07:04:27 +0000 Subject: [PATCH 1/8] add trait_is_marker to Interner --- compiler/rustc_middle/src/ty/context/impl_interner.rs | 4 ++++ compiler/rustc_type_ir/src/interner.rs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 74327278dbca6..388832f411ac3 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -622,6 +622,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.trait_is_auto(trait_def_id) } + fn trait_is_marker(self, trait_def_id: DefId) -> bool { + self.trait_def(trait_def_id).is_marker + } + fn trait_is_coinductive(self, trait_def_id: DefId) -> bool { self.trait_is_coinductive(trait_def_id) } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 1dfb34d94c0fc..3f1ba7ea112e2 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -477,6 +477,8 @@ pub trait Interner: fn trait_is_auto(self, trait_def_id: Self::TraitId) -> bool; + fn trait_is_marker(self, trait_def_id: Self::TraitId) -> bool; + fn trait_is_coinductive(self, trait_def_id: Self::TraitId) -> bool; fn trait_is_alias(self, trait_def_id: Self::TraitId) -> bool; From 819623a338ec917162444961a2b7fc51cbe312aa Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 3 Sep 2026 12:41:47 +0000 Subject: [PATCH 2/8] don't merge distinct impl candidates --- .../src/solve/trait_goals.rs | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) 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 902ad882f2123..d6a96f1e487f3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1493,14 +1493,31 @@ where } } + fn has_distinct_impl_candidates( + &self, + trait_def_id: I::TraitId, + candidates: &[Candidate], + ) -> bool { + let impls = + candidates.iter().filter(|c| matches!(c.source, CandidateSource::Impl(_))).count(); + let builtin_impls = + candidates.iter().any(|c| matches!(c.source, CandidateSource::BuiltinImpl(_))); + impls + builtin_impls as usize > 1 && !self.cx().trait_is_marker(trait_def_id) + } + #[instrument(level = "debug", skip(self), ret)] pub(super) fn merge_trait_candidates( &mut self, - candidate_preference_mode: CandidatePreferenceMode, + trait_def_id: I::TraitId, mut candidates: Vec>, failed_candidate_info: FailedCandidateInfo, ) -> Result<(CanonicalResponse, Option), NoSolution> { + let candidate_preference_mode = CandidatePreferenceMode::compute(self.cx(), trait_def_id); if self.typing_mode().is_coherence() { + if self.has_distinct_impl_candidates(trait_def_id, &candidates) { + return self.flounder(&candidates).map(|r| (r, None)); + } + return if let Some((response, _)) = self.try_merge_candidates(&candidates) { Ok((response, Some(TraitGoalProvenVia::Misc))) } else { @@ -1613,6 +1630,10 @@ where TraitGoalProvenVia::Misc }; + if self.has_distinct_impl_candidates(trait_def_id, &candidates) { + return self.flounder(&candidates).map(|r| (r, None)); + } + if let Some((response, _)) = self.try_merge_candidates(&candidates) { Ok((response, Some(proven_via))) } else { @@ -1628,9 +1649,7 @@ where { let (candidates, failed_candidate_info) = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All)?; - let candidate_preference_mode = - CandidatePreferenceMode::compute(self.cx(), goal.predicate.def_id()); - self.merge_trait_candidates(candidate_preference_mode, candidates, failed_candidate_info) + self.merge_trait_candidates(goal.predicate.def_id(), candidates, failed_candidate_info) .map_err(Into::into) } From f002f33ab880b5b85aee27a94d051d6ae3d59006 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 3 Sep 2026 12:41:47 +0000 Subject: [PATCH 3/8] drop specialized impls with equal inference constraints --- compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs | 1 + 1 file changed, 1 insertion(+) 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 51e1aea3850e4..38afd45370c26 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -1054,6 +1054,7 @@ where // the only allowed region constraints may be constraints also present on the default impl. if matches!(allow_inference_constraints, AllowInferenceConstraints::Yes) || has_only_region_constraints(c.result) + || c.result == candidates[i].result { if self.cx().impl_specializes(other_def_id, victim_def_id) { candidates.remove(i); From d86cf85387c1eb079a0244c2ee4f5c41dbc36466 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 20 Aug 2026 07:04:27 +0000 Subject: [PATCH 4/8] update fallout tests --- ...erence-inherited-assoc-ty-cycle-err.stderr | 7 ++- .../cycles/fixpoint-rerun-all-cycle-heads.rs | 2 + .../fixpoint-rerun-all-cycle-heads.stderr | 59 ++++++++++++++++++- 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/tests/ui/coherence/coherence-inherited-assoc-ty-cycle-err.stderr b/tests/ui/coherence/coherence-inherited-assoc-ty-cycle-err.stderr index 5b6ce4e2eec89..0fcc164a25872 100644 --- a/tests/ui/coherence/coherence-inherited-assoc-ty-cycle-err.stderr +++ b/tests/ui/coherence/coherence-inherited-assoc-ty-cycle-err.stderr @@ -4,7 +4,12 @@ error[E0391]: cycle detected when building specialization graph of trait `Trait` LL | trait Trait { type Assoc; } | ^^^^^^^^^^^^^^ | - = note: ...which immediately requires building specialization graph of trait `Trait` again +note: ...which requires computing whether impls specialize one another... + --> $DIR/coherence-inherited-assoc-ty-cycle-err.rs:17:1 + | +LL | impl Trait for String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which again requires building specialization graph of trait `Trait`, completing the cycle = note: cycle used when coherence checking all impls of trait `Trait` = note: for more information, see and diff --git a/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs index 326d888a55f3d..1956f4a65f7e6 100644 --- a/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs +++ b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs @@ -19,6 +19,7 @@ impl<'a, 'b, T> Trait<'a, 'b> for A where B: Trait<'a, 'b> {} // not have a cycle with `A`. This candidate never applies because of // the `A: NotImplemented` bound. impl<'a, 'b, T> Trait<'a, 'b> for B +//~^ ERROR: type annotations needed: cannot satisfy `B: Trait<'a, 'b>` [E0283] where A: Trait<'a, 'b>, A: NotImplemented, @@ -33,6 +34,7 @@ where // regions in the `A: Trait<'a, 'b>` where-bound. As both the impl above // and the impl below now apply with some constraints, we failed with ambiguity. impl<'a, 'b, T> Trait<'a, 'b> for B +//~^ ERROR: type annotations needed: cannot satisfy `B: Trait<'a, 'b>` [E0283] where A: NotImplemented, {} diff --git a/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr index c88081736f3c3..6088fb52e808f 100644 --- a/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr +++ b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr @@ -1,10 +1,65 @@ +error[E0283]: type annotations needed: cannot satisfy `B: Trait<'a, 'b>` + --> $DIR/fixpoint-rerun-all-cycle-heads.rs:21:35 + | +LL | impl<'a, 'b, T> Trait<'a, 'b> for B + | ^^^^ + | +note: multiple `impl`s satisfying `B: Trait<'a, 'b>` found + --> $DIR/fixpoint-rerun-all-cycle-heads.rs:21:1 + | +LL | / impl<'a, 'b, T> Trait<'a, 'b> for B +LL | | +LL | | where +LL | | A: Trait<'a, 'b>, +LL | | A: NotImplemented, + | |_________________________^ +... +LL | / impl<'a, 'b, T> Trait<'a, 'b> for B +LL | | +LL | | where +LL | | A: NotImplemented, + | |_________________________^ +... +LL | / impl<'a, T> Trait<'a, 'static> for B +LL | | where +LL | | C: Trait<'a, 'a>, + | |________________________^ + +error[E0283]: type annotations needed: cannot satisfy `B: Trait<'a, 'b>` + --> $DIR/fixpoint-rerun-all-cycle-heads.rs:36:35 + | +LL | impl<'a, 'b, T> Trait<'a, 'b> for B + | ^^^^ + | +note: multiple `impl`s satisfying `B: Trait<'a, 'b>` found + --> $DIR/fixpoint-rerun-all-cycle-heads.rs:21:1 + | +LL | / impl<'a, 'b, T> Trait<'a, 'b> for B +LL | | +LL | | where +LL | | A: Trait<'a, 'b>, +LL | | A: NotImplemented, + | |_________________________^ +... +LL | / impl<'a, 'b, T> Trait<'a, 'b> for B +LL | | +LL | | where +LL | | A: NotImplemented, + | |_________________________^ +... +LL | / impl<'a, T> Trait<'a, 'static> for B +LL | | where +LL | | C: Trait<'a, 'a>, + | |________________________^ + error: lifetime may not live long enough - --> $DIR/fixpoint-rerun-all-cycle-heads.rs:60:5 + --> $DIR/fixpoint-rerun-all-cycle-heads.rs:62:5 | LL | fn check<'a, T>() { | -- lifetime `'a` defined here LL | impls_trait::<'a, 'static, A>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` -error: aborting due to 1 previous error +error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0283`. From 0adc046717d42f4c1d766d00f39d142eeefe6288 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 18 Sep 2026 13:45:38 +0000 Subject: [PATCH 5/8] add tests --- .../ambiguous-impl-in-resolve.next.stderr | 17 +++++++++++++++++ .../ambiguous-impl-in-resolve.old.stderr | 17 +++++++++++++++++ .../next-solver/ambiguous-impl-in-resolve.rs | 8 ++++++-- 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 tests/ui/traits/next-solver/ambiguous-impl-in-resolve.next.stderr create mode 100644 tests/ui/traits/next-solver/ambiguous-impl-in-resolve.old.stderr diff --git a/tests/ui/traits/next-solver/ambiguous-impl-in-resolve.next.stderr b/tests/ui/traits/next-solver/ambiguous-impl-in-resolve.next.stderr new file mode 100644 index 0000000000000..20bbcdd8af423 --- /dev/null +++ b/tests/ui/traits/next-solver/ambiguous-impl-in-resolve.next.stderr @@ -0,0 +1,17 @@ +error[E0283]: type annotations needed: cannot satisfy `Option: Overlap` + --> $DIR/ambiguous-impl-in-resolve.rs:17:6 + | +LL | as Overlap>::f(); + | ^^^^^^^^^ + | +note: multiple `impl`s satisfying `Option: Overlap` found + --> $DIR/ambiguous-impl-in-resolve.rs:10:1 + | +LL | impl Overlap for Option where Self: Clone, { fn f() {} } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Overlap for Option where Self: Local, { fn f() {} } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/ambiguous-impl-in-resolve.old.stderr b/tests/ui/traits/next-solver/ambiguous-impl-in-resolve.old.stderr new file mode 100644 index 0000000000000..20bbcdd8af423 --- /dev/null +++ b/tests/ui/traits/next-solver/ambiguous-impl-in-resolve.old.stderr @@ -0,0 +1,17 @@ +error[E0283]: type annotations needed: cannot satisfy `Option: Overlap` + --> $DIR/ambiguous-impl-in-resolve.rs:17:6 + | +LL | as Overlap>::f(); + | ^^^^^^^^^ + | +note: multiple `impl`s satisfying `Option: Overlap` found + --> $DIR/ambiguous-impl-in-resolve.rs:10:1 + | +LL | impl Overlap for Option where Self: Clone, { fn f() {} } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Overlap for Option where Self: Local, { fn f() {} } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/ambiguous-impl-in-resolve.rs b/tests/ui/traits/next-solver/ambiguous-impl-in-resolve.rs index 78dffcbf6abc3..fd90d11575b07 100644 --- a/tests/ui/traits/next-solver/ambiguous-impl-in-resolve.rs +++ b/tests/ui/traits/next-solver/ambiguous-impl-in-resolve.rs @@ -1,5 +1,8 @@ -//@ check-pass -//@ compile-flags: -Znext-solver +//@ revisions: old next +//@[next] compile-flags: -Znext-solver + +// Impossible where-clauses can cause multiple overlapping impls to apply +// with the same constraints. trait Local {} @@ -12,6 +15,7 @@ where Option: Clone + Local, { as Overlap>::f(); + //~^ ERROR type annotations needed } fn main() {} From 781bc2b42b8e7cd1583affb2d002dadab2567d78 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 19 Sep 2026 15:52:05 +0000 Subject: [PATCH 6/8] replace has_distinct_impl_candidates with try_merge_impl_candidates and add docs --- .../src/solve/assembly/mod.rs | 3 + .../src/solve/trait_goals.rs | 55 +++++++++++-------- 2 files changed, 34 insertions(+), 24 deletions(-) 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 38afd45370c26..02f209c9e4662 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -1052,6 +1052,9 @@ where // we still use modulo regions here. This is fine as specialization currently // assumes that specializing impls have to be always applicable, meaning that // the only allowed region constraints may be constraints also present on the default impl. + // + // If both impls have the same response, dropping victim doesn't change the + // result. We need to do so here as we don't merge distinct impl candidates. if matches!(allow_inference_constraints, AllowInferenceConstraints::Yes) || has_only_region_constraints(c.result) || c.result == candidates[i].result 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 d6a96f1e487f3..126807a72e9fa 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1493,16 +1493,33 @@ where } } - fn has_distinct_impl_candidates( - &self, + /// Like `try_merge_candidates`, but returns `None` if there are multiple user-written + /// impls, or a user-written impl and a builtin impl, in which case we flounder. + /// + /// Candidates with equal responses can still come from conflicting impls, ex - two + /// overlapping impls which both apply due to impossible where-clauses. We don't want + /// to merge these. Multiple builtin impls are still merged as usual. This is not + /// done for marker traits, as their impls are allowed to overlap. + #[instrument(level = "trace", skip(self), ret)] + fn try_merge_impl_candidates( + &mut self, trait_def_id: I::TraitId, candidates: &[Candidate], - ) -> bool { - let impls = - candidates.iter().filter(|c| matches!(c.source, CandidateSource::Impl(_))).count(); - let builtin_impls = - candidates.iter().any(|c| matches!(c.source, CandidateSource::BuiltinImpl(_))); - impls + builtin_impls as usize > 1 && !self.cx().trait_is_marker(trait_def_id) + ) -> Option> { + if !self.cx().trait_is_marker(trait_def_id) { + let impl_count = candidates + .iter() + .filter(|candidate| matches!(candidate.source, CandidateSource::Impl(_))) + .count(); + let has_builtin_impl = candidates + .iter() + .any(|candidate| matches!(candidate.source, CandidateSource::BuiltinImpl(_))); + if impl_count + usize::from(has_builtin_impl) > 1 { + return None; + } + } + + self.try_merge_candidates(candidates).map(|(response, _)| response) } #[instrument(level = "debug", skip(self), ret)] @@ -1514,14 +1531,9 @@ where ) -> Result<(CanonicalResponse, Option), NoSolution> { let candidate_preference_mode = CandidatePreferenceMode::compute(self.cx(), trait_def_id); if self.typing_mode().is_coherence() { - if self.has_distinct_impl_candidates(trait_def_id, &candidates) { - return self.flounder(&candidates).map(|r| (r, None)); - } - - return if let Some((response, _)) = self.try_merge_candidates(&candidates) { - Ok((response, Some(TraitGoalProvenVia::Misc))) - } else { - self.flounder(&candidates).map(|r| (r, None)) + return match self.try_merge_impl_candidates(trait_def_id, &candidates) { + Some(response) => Ok((response, Some(TraitGoalProvenVia::Misc))), + None => self.flounder(&candidates).map(|r| (r, None)), }; } @@ -1630,14 +1642,9 @@ where TraitGoalProvenVia::Misc }; - if self.has_distinct_impl_candidates(trait_def_id, &candidates) { - return self.flounder(&candidates).map(|r| (r, None)); - } - - if let Some((response, _)) = self.try_merge_candidates(&candidates) { - Ok((response, Some(proven_via))) - } else { - self.flounder(&candidates).map(|r| (r, None)) + match self.try_merge_impl_candidates(trait_def_id, &candidates) { + Some(response) => Ok((response, Some(proven_via))), + None => self.flounder(&candidates).map(|r| (r, None)), } } From b49ec626c4925328eb6ab0668d5aaea76b551b82 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 22 Sep 2026 03:32:20 +0000 Subject: [PATCH 7/8] add merge_candidates_or_bail_with_ambiguity and merge_impl_candidates_or_flounder helpers --- .../src/solve/trait_goals.rs | 87 +++++++++++-------- 1 file changed, 50 insertions(+), 37 deletions(-) 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 126807a72e9fa..ee5040ccd4eb8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1506,20 +1506,41 @@ where trait_def_id: I::TraitId, candidates: &[Candidate], ) -> Option> { - if !self.cx().trait_is_marker(trait_def_id) { - let impl_count = candidates - .iter() - .filter(|candidate| matches!(candidate.source, CandidateSource::Impl(_))) - .count(); - let has_builtin_impl = candidates - .iter() - .any(|candidate| matches!(candidate.source, CandidateSource::BuiltinImpl(_))); - if impl_count + usize::from(has_builtin_impl) > 1 { - return None; - } + let is_marker = self.cx().trait_is_marker(trait_def_id); + let all_builtin = candidates + .iter() + .all(|candidate| matches!(candidate.source, CandidateSource::BuiltinImpl(_))); + + if is_marker || all_builtin { + self.try_merge_candidates(candidates).map(|(response, _)| response) + } else if candidates.len() > 1 { + None + } else { + candidates.first().map(|candidate| candidate.result) } + } - self.try_merge_candidates(candidates).map(|(response, _)| response) + fn merge_candidates_or_bail_with_ambiguity( + &mut self, + candidates: &[Candidate], + proven_via: TraitGoalProvenVia, + ) -> (CanonicalResponse, Option) { + if let Some((response, _)) = self.try_merge_candidates(candidates) { + (response, Some(proven_via)) + } else { + (self.bail_with_ambiguity(candidates), None) + } + } + + fn merge_impl_candidates_or_flounder( + &mut self, + trait_def_id: I::TraitId, + candidates: &[Candidate], + ) -> Result<(CanonicalResponse, Option), NoSolution> { + match self.try_merge_impl_candidates(trait_def_id, candidates) { + Some(response) => Ok((response, Some(TraitGoalProvenVia::Misc))), + None => self.flounder(candidates).map(|r| (r, None)), + } } #[instrument(level = "debug", skip(self), ret)] @@ -1531,10 +1552,7 @@ where ) -> Result<(CanonicalResponse, Option), NoSolution> { let candidate_preference_mode = CandidatePreferenceMode::compute(self.cx(), trait_def_id); if self.typing_mode().is_coherence() { - return match self.try_merge_impl_candidates(trait_def_id, &candidates) { - Some(response) => Ok((response, Some(TraitGoalProvenVia::Misc))), - None => self.flounder(&candidates).map(|r| (r, None)), - }; + return self.merge_impl_candidates_or_flounder(trait_def_id, &candidates); } // We prefer trivial builtin candidates, i.e. builtin impls without any @@ -1561,11 +1579,10 @@ where let alias_bounds: Vec<_> = candidates .extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound(..))) .collect(); - return if let Some((response, _)) = self.try_merge_candidates(&alias_bounds) { - Ok((response, Some(TraitGoalProvenVia::AliasBound))) - } else { - Ok((self.bail_with_ambiguity(&alias_bounds), None)) - }; + return Ok(self.merge_candidates_or_bail_with_ambiguity( + &alias_bounds, + TraitGoalProvenVia::AliasBound, + )); } // If there are non-global where-bounds, prefer where-bounds @@ -1617,11 +1634,10 @@ where let alias_bounds: Vec<_> = candidates .extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound(_))) .collect(); - return if let Some((response, _)) = self.try_merge_candidates(&alias_bounds) { - Ok((response, Some(TraitGoalProvenVia::AliasBound))) - } else { - Ok((self.bail_with_ambiguity(&alias_bounds), None)) - }; + return Ok(self.merge_candidates_or_bail_with_ambiguity( + &alias_bounds, + TraitGoalProvenVia::AliasBound, + )); } self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates); @@ -1631,20 +1647,17 @@ 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(ParamEnvSource::Global))) - { - TraitGoalProvenVia::ParamEnv + let only_global_where_bounds = !candidates.is_empty() + && candidates + .iter() + .all(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global))); + if only_global_where_bounds { + Ok(self + .merge_candidates_or_bail_with_ambiguity(&candidates, TraitGoalProvenVia::ParamEnv)) } else { candidates .retain(|c| !matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global))); - TraitGoalProvenVia::Misc - }; - - match self.try_merge_impl_candidates(trait_def_id, &candidates) { - Some(response) => Ok((response, Some(proven_via))), - None => self.flounder(&candidates).map(|r| (r, None)), + self.merge_impl_candidates_or_flounder(trait_def_id, &candidates) } } From 0c27f4b00ddbc342c183ff640f44ab21ae819fa9 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 22 Sep 2026 15:18:54 +0000 Subject: [PATCH 8/8] Remove flounder from merge_trait_candidates --- .../src/solve/trait_goals.rs | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) 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 ee5040ccd4eb8..aedd66a08832f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1494,7 +1494,7 @@ where } /// Like `try_merge_candidates`, but returns `None` if there are multiple user-written - /// impls, or a user-written impl and a builtin impl, in which case we flounder. + /// impls, or a user-written impl and a builtin impl, in which case we bail with ambiguity. /// /// Candidates with equal responses can still come from conflicting impls, ex - two /// overlapping impls which both apply due to impossible where-clauses. We don't want @@ -1532,14 +1532,18 @@ where } } - fn merge_impl_candidates_or_flounder( + fn merge_impl_candidates_or_bail_with_ambiguity( &mut self, trait_def_id: I::TraitId, candidates: &[Candidate], - ) -> Result<(CanonicalResponse, Option), NoSolution> { + ) -> (CanonicalResponse, Option) { + debug_assert!(candidates.iter().all(|c| matches!( + c.source, + CandidateSource::Impl(_) | CandidateSource::BuiltinImpl(_) + ))); match self.try_merge_impl_candidates(trait_def_id, candidates) { - Some(response) => Ok((response, Some(TraitGoalProvenVia::Misc))), - None => self.flounder(candidates).map(|r| (r, None)), + Some(response) => (response, Some(TraitGoalProvenVia::Misc)), + None => (self.bail_with_ambiguity(candidates), None), } } @@ -1550,9 +1554,16 @@ where mut candidates: Vec>, failed_candidate_info: FailedCandidateInfo, ) -> Result<(CanonicalResponse, Option), NoSolution> { + if candidates.is_empty() { + return Err(NoSolution); + } + let candidate_preference_mode = CandidatePreferenceMode::compute(self.cx(), trait_def_id); if self.typing_mode().is_coherence() { - return self.merge_impl_candidates_or_flounder(trait_def_id, &candidates); + return Ok(match self.try_merge_impl_candidates(trait_def_id, &candidates) { + Some(response) => (response, Some(TraitGoalProvenVia::Misc)), + None => (self.bail_with_ambiguity(&candidates), None), + }); } // We prefer trivial builtin candidates, i.e. builtin impls without any @@ -1647,17 +1658,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 only_global_where_bounds = !candidates.is_empty() - && candidates - .iter() - .all(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global))); + let only_global_where_bounds = candidates + .iter() + .all(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global))); if only_global_where_bounds { Ok(self .merge_candidates_or_bail_with_ambiguity(&candidates, TraitGoalProvenVia::ParamEnv)) } else { candidates .retain(|c| !matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global))); - self.merge_impl_candidates_or_flounder(trait_def_id, &candidates) + Ok(self.merge_impl_candidates_or_bail_with_ambiguity(trait_def_id, &candidates)) } }