Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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()),
}
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -891,7 +893,7 @@ pub(in crate::solve) fn predicates_for_object_candidate<D, I>(
param_env: I::ParamEnv,
trait_ref: Binder<I, ty::TraitRef<I>>,
object_bounds: I::BoundExistentialPredicates,
) -> Result<Vec<Goal<I, I::Predicate>>, Ambiguous>
) -> Result<Vec<Goal<I, I::Predicate>>, AmbiguousOrRerunNonErased>
where
D: SolverDelegate<Interner = I>,
I: Interner,
Expand Down Expand Up @@ -975,27 +977,33 @@ where
&mut self,
source_projection: ty::Binder<I, ty::ProjectionPredicate<I>>,
target_projection: ty::AliasTerm<I>,
) -> 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<bool, RerunNonErased> {
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<None>` 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<I>,
) -> Result<Option<I::Term>, Ambiguous> {
) -> Result<Option<I::Term>, AmbiguousOrRerunNonErased> {
if alias_term.self_ty() != self.self_ty {
return Ok(None);
}
Expand All @@ -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)
Expand All @@ -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<RerunNonErased> for AmbiguousOrRerunNonErased {
fn from(rerun: RerunNonErased) -> Self {
AmbiguousOrRerunNonErased::RerunNonErased(rerun)
}
}

impl<D, I> FallibleTypeFolder<I> for ReplaceProjectionWith<'_, '_, I, D>
where
D: SolverDelegate<Interner = I>,
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<I::Ty, Ambiguous> {
fn try_fold_ty(&mut self, ty: I::Ty) -> Result<I::Ty, Self::Error> {
if let ty::Alias(_, alias_ty @ ty::AliasTy { kind: ty::Projection { .. }, .. }) = ty.kind()
&& let Some(term) = self.try_eagerly_replace_alias(alias_ty.into())?
{
Expand Down
63 changes: 34 additions & 29 deletions compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

@zedddie zedddie Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought it would make sense to do the same here as it did discard RerunNonErased as well, 3c4b3a1 alone fixed ICE though '^^

View changes since the review

Original file line number Diff line number Diff line change
Expand Up @@ -1120,23 +1120,27 @@ where
let projection_may_match =
|ecx: &mut EvalCtxt<'_, D>,
source_projection: ty::Binder<I, ty::ExistentialProjection<I>>,
target_projection: ty::Binder<I, ty::ExistentialProjection<I>>| {
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<I, ty::ExistentialProjection<I>>|
-> Result<bool, RerunNonErased> {
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| {
Expand Down Expand Up @@ -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()
},
Expand Down
27 changes: 27 additions & 0 deletions tests/ui/traits/next-solver/object-projection-const-bound-rerun.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//! Regression test for <https://github.com/rust-lang/rust/issues/159462>.
//@ compile-flags: -Znext-solver
//@ check-pass

trait Trait {
type Assoc;
}
impl<T: Send> Trait for T
where
[T; 1 + 1]: Sized,
{
type Assoc = ();
}

trait Proj<T> {
type Assoc;
}

trait Foo: Proj<<u8 as Trait>::Assoc, Assoc = ()> {
fn m(&self);
}

fn f(x: &dyn Foo) {
x.m()
}

fn main() {}
Loading