diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index c498e6b3c83d..32a74b335c11 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -6,6 +6,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TypeVisitable}; use rustc_query_system::cache::Cache; +use rustc_type_ir::solve::AliasBoundKind; use self::EvaluationResult::*; use super::{SelectionError, SelectionResult}; @@ -116,8 +117,13 @@ pub enum SelectionCandidate<'tcx> { /// This is a trait matching with a projected type as `Self`, and we found /// an applicable bound in the trait definition. The `usize` is an index - /// into the list returned by `tcx.item_bounds`. - ProjectionCandidate(usize), + /// into the list returned by `tcx.item_bounds` and the `AliasBoundKind` + /// is whether this is candidate from recursion on the self type of a + /// projection. + ProjectionCandidate { + idx: usize, + kind: AliasBoundKind, + }, /// Implementation of a `Fn`-family trait by one of the anonymous types /// generated for an `||` expression. 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 d58c264841c8..c9a42dddac42 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -9,7 +9,7 @@ use derive_where::derive_where; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::search_graph::CandidateHeadUsages; -use rustc_type_ir::solve::SizedTraitKind; +use rustc_type_ir::solve::{AliasBoundKind, SizedTraitKind}; use rustc_type_ir::{ self as ty, Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast, @@ -27,11 +27,6 @@ use crate::solve::{ has_no_inference_or_external_constraints, }; -enum AliasBoundKind { - SelfBounds, - NonSelfBounds, -} - /// A candidate is a possible way to prove a goal. /// /// It consists of both the `source`, which describes how that goal would be proven, @@ -451,7 +446,7 @@ where matches!( c.source, CandidateSource::ParamEnv(ParamEnvSource::NonGlobal) - | CandidateSource::AliasBound + | CandidateSource::AliasBound(_) ) && has_no_inference_or_external_constraints(c.result) }) { @@ -711,7 +706,7 @@ where self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) { candidates.push(Candidate { - source: CandidateSource::AliasBound, + source: CandidateSource::AliasBound(consider_self_bounds), result, head_usages: CandidateHeadUsages::default(), }); @@ -735,7 +730,7 @@ where { candidates.extend(G::probe_and_consider_implied_clause( self, - CandidateSource::AliasBound, + CandidateSource::AliasBound(consider_self_bounds), goal, assumption, [], @@ -750,7 +745,7 @@ where { candidates.extend(G::probe_and_consider_implied_clause( self, - CandidateSource::AliasBound, + CandidateSource::AliasBound(consider_self_bounds), goal, assumption, [], @@ -1030,7 +1025,7 @@ where item_bound.fold_with(&mut ReplaceOpaque { cx: self.cx(), alias_ty, self_ty }); candidates.extend(G::probe_and_match_goal_against_assumption( self, - CandidateSource::AliasBound, + CandidateSource::AliasBound(AliasBoundKind::SelfBounds), goal, assumption, |ecx| { 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 65a5edf6b725..48bd0963c874 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -4,8 +4,8 @@ use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::SolverTraitLangItem; -use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::solve::inspect::ProbeKind; +use rustc_type_ir::solve::{AliasBoundKind, SizedTraitKind}; use rustc_type_ir::{self as ty, Interner, TypingMode, elaborate}; use tracing::instrument; @@ -96,7 +96,7 @@ where ) { candidates.extend(Self::probe_and_match_goal_against_assumption( ecx, - CandidateSource::AliasBound, + CandidateSource::AliasBound(AliasBoundKind::SelfBounds), goal, clause, |ecx| { 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 5edd281acefc..168921655a39 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -4,7 +4,9 @@ use rustc_type_ir::data_structures::IndexSet; use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::SolverTraitLangItem; -use rustc_type_ir::solve::{CandidatePreferenceMode, CanonicalResponse, SizedTraitKind}; +use rustc_type_ir::solve::{ + AliasBoundKind, CandidatePreferenceMode, CanonicalResponse, SizedTraitKind, +}; use rustc_type_ir::{ self as ty, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef, TypeVisitableExt as _, TypingMode, Upcast as _, elaborate, @@ -1381,22 +1383,21 @@ where return Ok((candidate.result, Some(TraitGoalProvenVia::Misc))); } - let potential_alias_bound_response = - candidates.iter().any(|c| matches!(c.source, CandidateSource::AliasBound)).then(|| { - let alias_bounds: Vec<_> = candidates - .extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound)) - .collect(); - if let Some((response, _)) = self.try_merge_candidates(&alias_bounds) { - (response, Some(TraitGoalProvenVia::AliasBound)) - } else { - (self.bail_with_ambiguity(&alias_bounds), None) - } - }); - + // Extract non-nested alias bound candidates, will be preferred over where bounds if + // we're proving an auto-trait, sizedness trait or default trait. if matches!(candidate_preference_mode, CandidatePreferenceMode::Marker) - && let Some(alias_bound_response) = potential_alias_bound_response + && candidates.iter().any(|c| { + matches!(c.source, CandidateSource::AliasBound(AliasBoundKind::SelfBounds)) + }) { - return Ok(alias_bound_response); + 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)) + }; } // If there are non-global where-bounds, prefer where-bounds @@ -1446,8 +1447,16 @@ where }; } - if let Some(response) = potential_alias_bound_response { - return Ok(response); + // Next, prefer any alias bound (nested or otherwise). + if candidates.iter().any(|c| matches!(c.source, CandidateSource::AliasBound(_))) { + 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)) + }; } self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates); diff --git a/compiler/rustc_trait_selection/src/solve/select.rs b/compiler/rustc_trait_selection/src/solve/select.rs index 8d01c880f8c5..d7e7ffd5d28c 100644 --- a/compiler/rustc_trait_selection/src/solve/select.rs +++ b/compiler/rustc_trait_selection/src/solve/select.rs @@ -126,7 +126,9 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>( // Prefer dyn candidates over non-dyn candidates. This is necessary to // handle the unsoundness between `impl Any for T` and `dyn Any: Any`. ( - CandidateSource::Impl(_) | CandidateSource::ParamEnv(_) | CandidateSource::AliasBound, + CandidateSource::Impl(_) + | CandidateSource::ParamEnv(_) + | CandidateSource::AliasBound(_), CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }), ) => true, @@ -175,7 +177,9 @@ fn to_selection<'tcx>( }) } CandidateSource::BuiltinImpl(builtin) => ImplSource::Builtin(builtin, nested), - CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => ImplSource::Param(nested), + CandidateSource::ParamEnv(_) | CandidateSource::AliasBound(_) => { + ImplSource::Param(nested) + } CandidateSource::CoherenceUnknowable => { span_bug!(span, "didn't expect to select an unknowable candidate") } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 042d6def84c3..fab5102427c7 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -741,7 +741,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( let mut ambiguous = false; let _ = selcx.for_each_item_bound( obligation.predicate.self_ty(), - |selcx, clause, _| { + |selcx, clause, _, _| { let Some(clause) = clause.as_projection_clause() else { return ControlFlow::Continue(()); }; diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 60f1fcb26c00..cb980d5ce8b4 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -208,7 +208,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut distinct_normalized_bounds = FxHashSet::default(); let _ = self.for_each_item_bound::( placeholder_trait_predicate.self_ty(), - |selcx, bound, idx| { + |selcx, bound, idx, alias_bound_kind| { let Some(bound) = bound.as_trait_clause() else { return ControlFlow::Continue(()); }; @@ -230,12 +230,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { bound.map_bound(|pred| pred.trait_ref), ) { Ok(None) => { - candidates.vec.push(ProjectionCandidate(idx)); + candidates + .vec + .push(ProjectionCandidate { idx, kind: alias_bound_kind }); } Ok(Some(normalized_trait)) if distinct_normalized_bounds.insert(normalized_trait) => { - candidates.vec.push(ProjectionCandidate(idx)); + candidates + .vec + .push(ProjectionCandidate { idx, kind: alias_bound_kind }); } _ => {} } @@ -825,7 +829,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::Alias(ty::Opaque, alias) => { - if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(_))) { + if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate { .. })) { // We do not generate an auto impl candidate for `impl Trait`s which already // reference our auto trait. // diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index b537419c715b..66311de7446b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -67,7 +67,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Builtin(BuiltinImplSource::Misc, data) } - ProjectionCandidate(idx) => { + ProjectionCandidate { idx, .. } => { let obligations = self.confirm_projection_candidate(obligation, idx)?; ImplSource::Param(obligations) } @@ -150,7 +150,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let candidate_predicate = self .for_each_item_bound( placeholder_self_ty, - |_, clause, clause_idx| { + |_, clause, clause_idx, _| { if clause_idx == idx { ControlFlow::Break(clause) } else { diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 5af16187e585..9be5d0673f73 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -32,6 +32,7 @@ use rustc_middle::ty::{ SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingMode, Upcast, elaborate, may_use_unstable_feature, }; +use rustc_next_trait_solver::solve::AliasBoundKind; use rustc_span::{Symbol, sym}; use tracing::{debug, instrument, trace}; @@ -1628,11 +1629,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub(super) fn for_each_item_bound( &mut self, mut self_ty: Ty<'tcx>, - mut for_each: impl FnMut(&mut Self, ty::Clause<'tcx>, usize) -> ControlFlow, + mut for_each: impl FnMut( + &mut Self, + ty::Clause<'tcx>, + usize, + AliasBoundKind, + ) -> ControlFlow, on_ambiguity: impl FnOnce(), ) -> ControlFlow { let mut idx = 0; - let mut in_parent_alias_type = false; + let mut alias_bound_kind = AliasBoundKind::SelfBounds; loop { let (kind, alias_ty) = match *self_ty.kind() { @@ -1648,14 +1654,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // share the same type as `self_ty`. This is because for truly rigid // projections, we will never be able to equate, e.g. `::A` // with `<::A as Tr>::A`. - let relevant_bounds = if in_parent_alias_type { + let relevant_bounds = if matches!(alias_bound_kind, AliasBoundKind::NonSelfBounds) { self.tcx().item_non_self_bounds(alias_ty.def_id) } else { self.tcx().item_self_bounds(alias_ty.def_id) }; for bound in relevant_bounds.instantiate(self.tcx(), alias_ty.args) { - for_each(self, bound, idx)?; + for_each(self, bound, idx, alias_bound_kind)?; idx += 1; } @@ -1665,7 +1671,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return ControlFlow::Continue(()); } - in_parent_alias_type = true; + alias_bound_kind = AliasBoundKind::NonSelfBounds; } } @@ -1880,14 +1886,24 @@ impl<'tcx> SelectionContext<'_, 'tcx> { break; } - let alias_bound = candidates - .iter() - .filter_map(|c| if let ProjectionCandidate(i) = c.candidate { Some(i) } else { None }) - .try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) }); - + let mut alias_bounds = candidates.iter().filter_map(|c| { + if let ProjectionCandidate { idx, kind } = c.candidate { + Some((idx, kind)) + } else { + None + } + }); + // Extract non-nested alias bound candidates, will be preferred over where bounds if + // we're proving an auto-trait, sizedness trait or default trait. if matches!(candidate_preference_mode, CandidatePreferenceMode::Marker) { - match alias_bound { - Some(Some(index)) => return Some(ProjectionCandidate(index)), + match alias_bounds + .clone() + .filter_map(|(idx, kind)| (kind == AliasBoundKind::SelfBounds).then_some(idx)) + .try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) }) + { + Some(Some(idx)) => { + return Some(ProjectionCandidate { idx, kind: AliasBoundKind::SelfBounds }); + } Some(None) => {} None => return None, } @@ -1926,8 +1942,16 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // fairly arbitrary but once again necessary for backwards compatibility. // If there are multiple applicable candidates which don't affect type inference, // choose the one with the lowest index. - match alias_bound { - Some(Some(index)) => return Some(ProjectionCandidate(index)), + match alias_bounds.try_reduce(|(c1, k1), (c2, k2)| { + if has_non_region_infer { + None + } else if c1 < c2 { + Some((c1, k1)) + } else { + Some((c2, k2)) + } + }) { + Some(Some((idx, kind))) => return Some(ProjectionCandidate { idx, kind }), Some(None) => {} None => return None, } @@ -2016,7 +2040,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // Non-global param candidates have already been handled, global // where-bounds get ignored. ParamCandidate(_) | ImplCandidate(_) => true, - ProjectionCandidate(_) | ObjectCandidate(_) => unreachable!(), + ProjectionCandidate { .. } | ObjectCandidate(_) => unreachable!(), }) { return Some(ImplCandidate(def_id)); } else { diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index f155d45bc0a6..1e0263b232f4 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -189,7 +189,7 @@ pub enum CandidateSource { /// let _y = x.clone(); /// } /// ``` - AliasBound, + AliasBound(AliasBoundKind), /// A candidate that is registered only during coherence to represent some /// yet-unknown impl that could be produced downstream without violating orphan /// rules. @@ -207,6 +207,15 @@ pub enum ParamEnvSource { Global, } +#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +pub enum AliasBoundKind { + /// Alias bound from the self type of a projection + SelfBounds, + // Alias bound having recursed on the self type of a projection + NonSelfBounds, +} + #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] #[cfg_attr( feature = "nightly", diff --git a/tests/ui/sized-hierarchy/prefer-non-nested-alias-bound.rs b/tests/ui/sized-hierarchy/prefer-non-nested-alias-bound.rs new file mode 100644 index 000000000000..affef2e61482 --- /dev/null +++ b/tests/ui/sized-hierarchy/prefer-non-nested-alias-bound.rs @@ -0,0 +1,30 @@ +//@ check-pass +//@ compile-flags: --crate-type=lib +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Since #120752, also get alias-bound candidates from a nested self-type, so prefering +// alias-bound over where-bound candidates can be incorrect. This test checks that case and that +// we prefer non-nested alias-bound candidates over where-bound candidates over nested alias-bound +// candidates. + +trait OtherTrait<'a> { + type Assoc: ?Sized; +} + +trait Trait +where + >::Assoc: Sized, +{ + type Assoc: for<'a> OtherTrait<'a>; +} + +fn impls_sized() {} +fn foo<'a, T>() +where + T: Trait, + for<'hr> >::Assoc: Sized, +{ + impls_sized::<>::Assoc>(); +}