Skip to content

Commit 6604225

Browse files
committed
trait_sel: sizedness goals prefer alias candidates
For sizedness, default and auto trait predicates, now prefer non-param candidates if any exist. As these traits do not have generic parameters, it never makes sense to prefer an non-alias candidate, as there can never be a more permissive candidate.
1 parent 62c4d2d commit 6604225

19 files changed

+719
-81
lines changed

compiler/rustc_middle/src/ty/context.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
520520
self.is_default_trait(def_id)
521521
}
522522

523+
fn is_sizedness_trait(self, def_id: DefId) -> bool {
524+
self.is_sizedness_trait(def_id)
525+
}
526+
523527
fn as_lang_item(self, def_id: DefId) -> Option<SolverLangItem> {
524528
lang_item_to_solver_lang_item(self.lang_items().from_def_id(def_id)?)
525529
}
@@ -1747,6 +1751,10 @@ impl<'tcx> TyCtxt<'tcx> {
17471751
.any(|&default_trait| self.lang_items().get(default_trait) == Some(def_id))
17481752
}
17491753

1754+
pub fn is_sizedness_trait(self, def_id: DefId) -> bool {
1755+
matches!(self.as_lang_item(def_id), Some(LangItem::Sized | LangItem::MetaSized))
1756+
}
1757+
17501758
/// Returns a range of the start/end indices specified with the
17511759
/// `rustc_layout_scalar_valid_range` attribute.
17521760
// FIXME(eddyb) this is an awkward spot for this method, maybe move it?

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ pub use rustc_type_ir::fast_reject::DeepRejectCtxt;
5959
)]
6060
use rustc_type_ir::inherent;
6161
pub use rustc_type_ir::relate::VarianceDiagInfo;
62-
pub use rustc_type_ir::solve::SizedTraitKind;
62+
pub use rustc_type_ir::solve::{CandidatePreferenceMode, SizedTraitKind};
6363
pub use rustc_type_ir::*;
6464
#[allow(hidden_glob_reexports, unused_imports)]
6565
use rustc_type_ir::{InferCtxtLike, Interner};

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_type_ir::data_structures::IndexSet;
44
use rustc_type_ir::fast_reject::DeepRejectCtxt;
55
use rustc_type_ir::inherent::*;
66
use rustc_type_ir::lang_items::SolverTraitLangItem;
7-
use rustc_type_ir::solve::{CanonicalResponse, SizedTraitKind};
7+
use rustc_type_ir::solve::{CandidatePreferenceMode, CanonicalResponse, SizedTraitKind};
88
use rustc_type_ir::{
99
self as ty, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef,
1010
TypeVisitableExt as _, TypingMode, Upcast as _, elaborate,
@@ -1355,6 +1355,7 @@ where
13551355
#[instrument(level = "debug", skip(self), ret)]
13561356
pub(super) fn merge_trait_candidates(
13571357
&mut self,
1358+
candidate_preference_mode: CandidatePreferenceMode,
13581359
mut candidates: Vec<Candidate<I>>,
13591360
failed_candidate_info: FailedCandidateInfo,
13601361
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
@@ -1380,6 +1381,24 @@ where
13801381
return Ok((candidate.result, Some(TraitGoalProvenVia::Misc)));
13811382
}
13821383

1384+
let potential_alias_bound_response =
1385+
candidates.iter().any(|c| matches!(c.source, CandidateSource::AliasBound)).then(|| {
1386+
let alias_bounds: Vec<_> = candidates
1387+
.extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound))
1388+
.collect();
1389+
if let Some((response, _)) = self.try_merge_candidates(&alias_bounds) {
1390+
(response, Some(TraitGoalProvenVia::AliasBound))
1391+
} else {
1392+
(self.bail_with_ambiguity(&alias_bounds), None)
1393+
}
1394+
});
1395+
1396+
if matches!(candidate_preference_mode, CandidatePreferenceMode::Marker)
1397+
&& let Some(alias_bound_response) = potential_alias_bound_response
1398+
{
1399+
return Ok(alias_bound_response);
1400+
}
1401+
13831402
// If there are non-global where-bounds, prefer where-bounds
13841403
// (including global ones) over everything else.
13851404
let has_non_global_where_bounds = candidates
@@ -1427,15 +1446,8 @@ where
14271446
};
14281447
}
14291448

1430-
if candidates.iter().any(|c| matches!(c.source, CandidateSource::AliasBound)) {
1431-
let alias_bounds: Vec<_> = candidates
1432-
.extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound))
1433-
.collect();
1434-
return if let Some((response, _)) = self.try_merge_candidates(&alias_bounds) {
1435-
Ok((response, Some(TraitGoalProvenVia::AliasBound)))
1436-
} else {
1437-
Ok((self.bail_with_ambiguity(&alias_bounds), None))
1438-
};
1449+
if let Some(response) = potential_alias_bound_response {
1450+
return Ok(response);
14391451
}
14401452

14411453
self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates);
@@ -1470,7 +1482,9 @@ where
14701482
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
14711483
let (candidates, failed_candidate_info) =
14721484
self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
1473-
self.merge_trait_candidates(candidates, failed_candidate_info)
1485+
let candidate_preference_mode =
1486+
CandidatePreferenceMode::compute(self.cx(), goal.predicate.def_id());
1487+
self.merge_trait_candidates(candidate_preference_mode, candidates, failed_candidate_info)
14741488
}
14751489

14761490
fn try_stall_coroutine(&mut self, self_ty: I::Ty) -> Option<Result<Candidate<I>, NoSolution>> {

compiler/rustc_trait_selection/src/traits/select/mod.rs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable;
2828
use rustc_middle::ty::error::TypeErrorToStringExt;
2929
use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths};
3030
use rustc_middle::ty::{
31-
self, DeepRejectCtxt, GenericArgsRef, PolyProjectionPredicate, SizedTraitKind, Ty, TyCtxt,
32-
TypeFoldable, TypeVisitableExt, TypingMode, Upcast, elaborate, may_use_unstable_feature,
31+
self, CandidatePreferenceMode, DeepRejectCtxt, GenericArgsRef, PolyProjectionPredicate,
32+
SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingMode, Upcast, elaborate,
33+
may_use_unstable_feature,
3334
};
3435
use rustc_span::{Symbol, sym};
3536
use tracing::{debug, instrument, trace};
@@ -474,7 +475,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
474475
}
475476
} else {
476477
let has_non_region_infer = stack.obligation.predicate.has_non_region_infer();
477-
if let Some(candidate) = self.winnow_candidates(has_non_region_infer, candidates) {
478+
let candidate_preference_mode =
479+
CandidatePreferenceMode::compute(self.tcx(), stack.obligation.predicate.def_id());
480+
if let Some(candidate) =
481+
self.winnow_candidates(has_non_region_infer, candidate_preference_mode, candidates)
482+
{
478483
self.filter_reservation_impls(candidate)
479484
} else {
480485
Ok(None)
@@ -1821,6 +1826,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
18211826
fn winnow_candidates(
18221827
&mut self,
18231828
has_non_region_infer: bool,
1829+
candidate_preference_mode: CandidatePreferenceMode,
18241830
mut candidates: Vec<EvaluatedCandidate<'tcx>>,
18251831
) -> Option<SelectionCandidate<'tcx>> {
18261832
if candidates.len() == 1 {
@@ -1874,6 +1880,19 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
18741880
break;
18751881
}
18761882

1883+
let alias_bound = candidates
1884+
.iter()
1885+
.filter_map(|c| if let ProjectionCandidate(i) = c.candidate { Some(i) } else { None })
1886+
.try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) });
1887+
1888+
if matches!(candidate_preference_mode, CandidatePreferenceMode::Marker) {
1889+
match alias_bound {
1890+
Some(Some(index)) => return Some(ProjectionCandidate(index)),
1891+
Some(None) => {}
1892+
None => return None,
1893+
}
1894+
}
1895+
18771896
// The next highest priority is for non-global where-bounds. However, while we don't
18781897
// prefer global where-clauses here, we do bail with ambiguity when encountering both
18791898
// a global and a non-global where-clause.
@@ -1907,10 +1926,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
19071926
// fairly arbitrary but once again necessary for backwards compatibility.
19081927
// If there are multiple applicable candidates which don't affect type inference,
19091928
// choose the one with the lowest index.
1910-
let alias_bound = candidates
1911-
.iter()
1912-
.filter_map(|c| if let ProjectionCandidate(i) = c.candidate { Some(i) } else { None })
1913-
.try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) });
19141929
match alias_bound {
19151930
Some(Some(index)) => return Some(ProjectionCandidate(index)),
19161931
Some(None) => {}

compiler/rustc_type_ir/src/interner.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,8 @@ pub trait Interner:
331331

332332
fn is_default_trait(self, def_id: Self::TraitId) -> bool;
333333

334+
fn is_sizedness_trait(self, def_id: Self::TraitId) -> bool;
335+
334336
fn as_lang_item(self, def_id: Self::DefId) -> Option<SolverLangItem>;
335337

336338
fn as_trait_lang_item(self, def_id: Self::TraitId) -> Option<SolverTraitLangItem>;

compiler/rustc_type_ir/src/solve/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,30 @@ pub struct QueryInput<I: Interner, P> {
109109

110110
impl<I: Interner, P: Eq> Eq for QueryInput<I, P> {}
111111

112+
/// Which trait candidates should be preferred over other candidates? By default, prefer where
113+
/// bounds over alias bounds. For marker traits, prefer alias bounds over where bounds.
114+
#[derive(Clone, Copy, Debug)]
115+
pub enum CandidatePreferenceMode {
116+
/// Prefers where bounds over alias bounds
117+
Default,
118+
/// Prefers alias bounds over where bounds
119+
Marker,
120+
}
121+
122+
impl CandidatePreferenceMode {
123+
/// Given `trait_def_id`, which candidate preference mode should be used?
124+
pub fn compute<I: Interner>(cx: I, trait_id: I::TraitId) -> CandidatePreferenceMode {
125+
let is_sizedness_or_auto_or_default_goal = cx.is_sizedness_trait(trait_id)
126+
|| cx.trait_is_auto(trait_id)
127+
|| cx.is_default_trait(trait_id);
128+
if is_sizedness_or_auto_or_default_goal {
129+
CandidatePreferenceMode::Marker
130+
} else {
131+
CandidatePreferenceMode::Default
132+
}
133+
}
134+
}
135+
112136
/// Possible ways the given goal can be proven.
113137
#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)]
114138
pub enum CandidateSource<I: Interner> {

0 commit comments

Comments
 (0)