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 040f98de7bcfd..b18a65de2f940 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -23,6 +23,7 @@ use tracing::{debug, instrument}; use super::trait_goals::TraitGoalProvenVia; use super::{has_only_region_constraints, inspect}; use crate::delegate::SolverDelegate; +use crate::solve::assembly::structural_traits::AmbiguousOrRerunNonErased; use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, CanonicalResponse, Certainty, EvalCtxt, Goal, GoalSource, @@ -116,9 +117,10 @@ where ecx.add_goals(GoalSource::ImplWhereBound, requirements)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - Err(_) => { + Err(AmbiguousOrRerunNonErased::Ambiguous) => { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } + Err(AmbiguousOrRerunNonErased::RerunNonErased(rerun)) => Err(rerun.into()), } }) } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index ae78d68865de3..aa26a122817bb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -15,7 +15,9 @@ use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::instrument; use crate::delegate::SolverDelegate; -use crate::solve::{AdtDestructorKind, EvalCtxt, Goal, NoSolution}; +use crate::solve::{ + AdtDestructorKind, EvalCtxt, Goal, NoSolution, NoSolutionOrRerunNonErased, RerunNonErased, +}; // Calculates the constituent types of a type for `auto trait` purposes. #[instrument(level = "trace", skip(ecx), ret)] @@ -891,7 +893,7 @@ pub(in crate::solve) fn predicates_for_object_candidate( param_env: I::ParamEnv, trait_ref: Binder>, object_bounds: I::BoundExistentialPredicates, -) -> Result>, Ambiguous> +) -> Result>, AmbiguousOrRerunNonErased> where D: SolverDelegate, I: Interner, @@ -975,27 +977,33 @@ where &mut self, source_projection: ty::Binder>, target_projection: ty::AliasTerm, - ) -> bool { - source_projection.item_def_id() == target_projection.expect_projection_def_id() - && self - .ecx - .probe(|_| ProbeKind::ProjectionCompatibility) - .enter_without_propagated_nested_goals(|ecx| { - let source_projection = ecx.instantiate_binder_with_infer(source_projection); - ecx.eq(self.param_env, source_projection.projection_term, target_projection)?; - ecx.try_evaluate_added_goals() - }) - .is_ok() + ) -> Result { + if source_projection.item_def_id() != target_projection.expect_projection_def_id() { + return Ok(false); + } + match self + .ecx + .probe(|_| ProbeKind::ProjectionCompatibility) + .enter_without_propagated_nested_goals(|ecx| { + let source_projection = ecx.instantiate_binder_with_infer(source_projection); + ecx.eq(self.param_env, source_projection.projection_term, target_projection)?; + ecx.try_evaluate_added_goals() + }) { + Ok(_) => Ok(true), + Err(NoSolutionOrRerunNonErased::NoSolution(_)) => Ok(false), + Err(NoSolutionOrRerunNonErased::RerunNonErased(rerun)) => Err(rerun), + } } /// Try to replace an alias with the term present in the projection bounds of the self type. /// Returns `Ok` if this alias is not eligible to be replaced, or bail with /// `Err(Ambiguous)` if it's uncertain which projection bound to replace the term with due - /// to multiple bounds applying. + /// to multiple bounds applying, or with `Err(RerunNonErased)` if we have to rerun the + /// goal in original `TypingMode`. fn try_eagerly_replace_alias( &mut self, alias_term: ty::AliasTerm, - ) -> Result, Ambiguous> { + ) -> Result, AmbiguousOrRerunNonErased> { if alias_term.self_ty() != self.self_ty { return Ok(None); } @@ -1007,22 +1015,26 @@ where // This is quite similar to the `projection_may_match` we use in unsizing, // but here we want to unify a projection predicate against an alias term // so we can replace it with the projection predicate's term. - let mut matching_projections = replacements - .iter() - .filter(|source_projection| self.projection_may_match(**source_projection, alias_term)); - let Some(replacement) = matching_projections.next() else { + let mut matching_projection = None; + for source_projection in replacements { + if self.projection_may_match(*source_projection, alias_term)? { + // FIXME: This *may* have issues with duplicated projections. + if matching_projection.is_some() { + // If there's more than one projection that we can unify here, then we + // need to stall until inference constrains things so that there's only + // one choice. + return Err(AmbiguousOrRerunNonErased::Ambiguous); + } + matching_projection = Some(source_projection) + } + } + + let Some(matching) = matching_projection else { // This shouldn't happen. panic!("could not replace {alias_term:?} with term from from {:?}", self.self_ty); }; - // FIXME: This *may* have issues with duplicated projections. - if matching_projections.next().is_some() { - // If there's more than one projection that we can unify here, then we - // need to stall until inference constrains things so that there's only - // one choice. - return Err(Ambiguous); - } - let replacement = self.ecx.instantiate_binder_with_infer(*replacement); + let replacement = self.ecx.instantiate_binder_with_infer(*matching); self.nested.extend( self.ecx .eq_and_get_goals(self.param_env, alias_term, replacement.projection_term) @@ -1033,21 +1045,30 @@ where } } -/// Marker for bailing with ambiguity. -pub(crate) struct Ambiguous; +pub(crate) enum AmbiguousOrRerunNonErased { + /// Marker for bailing with ambiguity. + Ambiguous, + RerunNonErased(RerunNonErased), +} + +impl From for AmbiguousOrRerunNonErased { + fn from(rerun: RerunNonErased) -> Self { + AmbiguousOrRerunNonErased::RerunNonErased(rerun) + } +} impl FallibleTypeFolder for ReplaceProjectionWith<'_, '_, I, D> where D: SolverDelegate, I: Interner, { - type Error = Ambiguous; + type Error = AmbiguousOrRerunNonErased; fn cx(&self) -> I { self.ecx.cx() } - fn try_fold_ty(&mut self, ty: I::Ty) -> Result { + fn try_fold_ty(&mut self, ty: I::Ty) -> Result { if let ty::Alias(_, alias_ty @ ty::AliasTy { kind: ty::Projection { .. }, .. }) = ty.kind() && let Some(term) = self.try_eagerly_replace_alias(alias_ty.into())? { 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 082fe1b9bce63..1bbc1b6aec923 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1120,23 +1120,27 @@ where let projection_may_match = |ecx: &mut EvalCtxt<'_, D>, source_projection: ty::Binder>, - target_projection: ty::Binder>| { - source_projection.item_def_id() == target_projection.item_def_id() - && ecx - .probe(|_| ProbeKind::ProjectionCompatibility) - .enter(|ecx| { - ecx.enter_forall_with_assumptions( - target_projection, - param_env, - |ecx, target_projection| { - let source_projection = - ecx.instantiate_binder_with_infer(source_projection); - ecx.eq(param_env, source_projection, target_projection)?; - ecx.try_evaluate_added_goals() - }, - ) - }) - .is_ok() + target_projection: ty::Binder>| + -> Result { + if source_projection.item_def_id() != target_projection.item_def_id() { + return Ok(false); + } + match ecx.probe(|_| ProbeKind::ProjectionCompatibility).enter(|ecx| { + ecx.enter_forall_with_assumptions( + target_projection, + param_env, + |ecx, target_projection| { + let source_projection = + ecx.instantiate_binder_with_infer(source_projection); + ecx.eq(param_env, source_projection, target_projection)?; + ecx.try_evaluate_added_goals() + }, + ) + }) { + Ok(_) => Ok(true), + Err(NoSolutionOrRerunNonErased::NoSolution(_)) => Ok(false), + Err(NoSolutionOrRerunNonErased::RerunNonErased(rerun)) => Err(rerun), + } }; self.probe_trait_candidate(source).enter(|ecx| { @@ -1165,24 +1169,25 @@ where // it with b_ty's projection. ty::ExistentialPredicate::Projection(target_projection) => { let target_projection = bound.rebind(target_projection); - let mut matching_projections = - a_data.projection_bounds().into_iter().filter(|source_projection| { - projection_may_match(ecx, *source_projection, target_projection) - }); - let Some(source_projection) = matching_projections.next() else { + let mut matching_projection = None; + for source_projection in a_data.projection_bounds() { + if projection_may_match(ecx, source_projection, target_projection)? { + if matching_projection.is_some() { + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } + matching_projection = Some(source_projection); + } + } + let Some(matching) = matching_projection else { return Err(NoSolution.into()); }; - if matching_projections.next().is_some() { - return ecx.evaluate_added_goals_and_make_canonical_response( - Certainty::AMBIGUOUS, - ); - } ecx.enter_forall_with_assumptions( target_projection, param_env, |ecx, target_projection| { - let source_projection = - ecx.instantiate_binder_with_infer(source_projection); + let source_projection = ecx.instantiate_binder_with_infer(matching); ecx.eq(param_env, source_projection, target_projection)?; ecx.try_evaluate_added_goals() }, diff --git a/tests/ui/traits/next-solver/object-projection-const-bound-rerun.rs b/tests/ui/traits/next-solver/object-projection-const-bound-rerun.rs new file mode 100644 index 0000000000000..1c5732835ebf1 --- /dev/null +++ b/tests/ui/traits/next-solver/object-projection-const-bound-rerun.rs @@ -0,0 +1,27 @@ +//! Regression test for . +//@ compile-flags: -Znext-solver +//@ check-pass + +trait Trait { + type Assoc; +} +impl Trait for T +where + [T; 1 + 1]: Sized, +{ + type Assoc = (); +} + +trait Proj { + type Assoc; +} + +trait Foo: Proj<::Assoc, Assoc = ()> { + fn m(&self); +} + +fn f(x: &dyn Foo) { + x.m() +} + +fn main() {}