Skip to content

Commit 90e61db

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 abd07de commit 90e61db

File tree

16 files changed

+126
-80
lines changed

16 files changed

+126
-80
lines changed

compiler/rustc_middle/src/ty/context.rs

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

496+
fn is_sizedness_trait(self, def_id: DefId) -> bool {
497+
self.is_sizedness_trait(def_id)
498+
}
499+
496500
fn as_lang_item(self, def_id: DefId) -> Option<TraitSolverLangItem> {
497501
lang_item_to_trait_lang_item(self.lang_items().from_def_id(def_id)?)
498502
}
@@ -1652,6 +1656,10 @@ impl<'tcx> TyCtxt<'tcx> {
16521656
.any(|&default_trait| self.lang_items().get(default_trait) == Some(def_id))
16531657
}
16541658

1659+
pub fn is_sizedness_trait(self, def_id: DefId) -> bool {
1660+
matches!(self.as_lang_item(def_id), Some(LangItem::Sized | LangItem::MetaSized))
1661+
}
1662+
16551663
/// Returns a range of the start/end indices specified with the
16561664
/// `rustc_layout_scalar_valid_range` attribute.
16571665
// 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: 28 additions & 13 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::TraitSolverLangItem;
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, TraitPredicate, TraitRef, TypeVisitableExt as _, TypingMode,
1010
Upcast as _, elaborate,
@@ -1343,6 +1343,7 @@ where
13431343
#[instrument(level = "debug", skip(self), ret)]
13441344
pub(super) fn merge_trait_candidates(
13451345
&mut self,
1346+
candidate_preference_mode: CandidatePreferenceMode,
13461347
mut candidates: Vec<Candidate<I>>,
13471348
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
13481349
if let TypingMode::Coherence = self.typing_mode() {
@@ -1368,6 +1369,26 @@ where
13681369
return Ok((candidate.result, Some(TraitGoalProvenVia::Misc)));
13691370
}
13701371

1372+
let potential_alias_bound_response =
1373+
candidates.iter().any(|c| matches!(c.source, CandidateSource::AliasBound)).then(|| {
1374+
let alias_bounds: Vec<_> = candidates
1375+
.iter()
1376+
.filter(|c| matches!(c.source, CandidateSource::AliasBound))
1377+
.map(|c| c.result)
1378+
.collect();
1379+
if let Some(response) = self.try_merge_responses(&alias_bounds) {
1380+
(response, Some(TraitGoalProvenVia::AliasBound))
1381+
} else {
1382+
(self.bail_with_ambiguity(&alias_bounds), None)
1383+
}
1384+
});
1385+
1386+
if matches!(candidate_preference_mode, CandidatePreferenceMode::Marker)
1387+
&& let Some(alias_bound_response) = potential_alias_bound_response
1388+
{
1389+
return Ok(alias_bound_response);
1390+
}
1391+
13711392
// If there are non-global where-bounds, prefer where-bounds
13721393
// (including global ones) over everything else.
13731394
let has_non_global_where_bounds = candidates
@@ -1386,17 +1407,8 @@ where
13861407
};
13871408
}
13881409

1389-
if candidates.iter().any(|c| matches!(c.source, CandidateSource::AliasBound)) {
1390-
let alias_bounds: Vec<_> = candidates
1391-
.iter()
1392-
.filter(|c| matches!(c.source, CandidateSource::AliasBound))
1393-
.map(|c| c.result)
1394-
.collect();
1395-
return if let Some(response) = self.try_merge_responses(&alias_bounds) {
1396-
Ok((response, Some(TraitGoalProvenVia::AliasBound)))
1397-
} else {
1398-
Ok((self.bail_with_ambiguity(&alias_bounds), None))
1399-
};
1410+
if let Some(response) = potential_alias_bound_response {
1411+
return Ok(response);
14001412
}
14011413

14021414
self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates);
@@ -1431,7 +1443,10 @@ where
14311443
goal: Goal<I, TraitPredicate<I>>,
14321444
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
14331445
let candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
1434-
self.merge_trait_candidates(candidates)
1446+
1447+
let candidate_preference_mode =
1448+
CandidatePreferenceMode::compute(self.cx(), goal.predicate.def_id());
1449+
self.merge_trait_candidates(candidate_preference_mode, candidates)
14351450
}
14361451

14371452
fn try_stall_coroutine_witness(

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

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable;
2727
use rustc_middle::ty::error::TypeErrorToStringExt;
2828
use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths};
2929
use rustc_middle::ty::{
30-
self, DeepRejectCtxt, GenericArgsRef, PolyProjectionPredicate, SizedTraitKind, Ty, TyCtxt,
31-
TypeFoldable, TypeVisitableExt, TypingMode, Upcast, elaborate, may_use_unstable_feature,
30+
self, CandidatePreferenceMode, DeepRejectCtxt, GenericArgsRef, PolyProjectionPredicate,
31+
SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingMode, Upcast, elaborate,
32+
may_use_unstable_feature,
3233
};
3334
use rustc_span::{Symbol, sym};
3435
use tracing::{debug, instrument, trace};
@@ -473,7 +474,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
473474
}
474475
} else {
475476
let has_non_region_infer = stack.obligation.predicate.has_non_region_infer();
476-
if let Some(candidate) = self.winnow_candidates(has_non_region_infer, candidates) {
477+
let candidate_preference_mode =
478+
CandidatePreferenceMode::compute(self.tcx(), stack.obligation.predicate.def_id());
479+
if let Some(candidate) =
480+
self.winnow_candidates(has_non_region_infer, candidate_preference_mode, candidates)
481+
{
477482
self.filter_reservation_impls(candidate)
478483
} else {
479484
Ok(None)
@@ -1822,6 +1827,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
18221827
fn winnow_candidates(
18231828
&mut self,
18241829
has_non_region_infer: bool,
1830+
candidate_preference_mode: CandidatePreferenceMode,
18251831
mut candidates: Vec<EvaluatedCandidate<'tcx>>,
18261832
) -> Option<SelectionCandidate<'tcx>> {
18271833
if candidates.len() == 1 {
@@ -1875,6 +1881,19 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
18751881
break;
18761882
}
18771883

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

compiler/rustc_type_ir/src/interner.rs

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

302302
fn is_default_trait(self, def_id: Self::DefId) -> bool;
303303

304+
fn is_sizedness_trait(self, def_id: Self::DefId) -> bool;
305+
304306
fn as_lang_item(self, def_id: Self::DefId) -> Option<TraitSolverLangItem>;
305307

306308
fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator<Item = Self::DefId>;

compiler/rustc_type_ir/src/solve/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,30 @@ pub struct PredefinedOpaquesData<I: Interner> {
125125
pub opaque_types: Vec<(ty::OpaqueTypeKey<I>, I::Ty)>,
126126
}
127127

128+
/// Which trait candidates should be preferred over other candidates? By default, prefer where
129+
/// bounds over alias bounds. For marker traits, prefer alias bounds over where bounds.
130+
#[derive(Clone, Copy, Debug)]
131+
pub enum CandidatePreferenceMode {
132+
/// Prefers where bounds over alias bounds
133+
Default,
134+
/// Prefers alias bounds over where bounds
135+
Marker,
136+
}
137+
138+
impl CandidatePreferenceMode {
139+
/// Given `trait_def_id`, which candidate preference mode should be used?
140+
pub fn compute<I: Interner>(cx: I, trait_def_id: I::DefId) -> CandidatePreferenceMode {
141+
let is_sizedness_or_auto_or_default_goal = cx.is_sizedness_trait(trait_def_id)
142+
|| cx.trait_is_auto(trait_def_id)
143+
|| cx.is_default_trait(trait_def_id);
144+
if is_sizedness_or_auto_or_default_goal {
145+
CandidatePreferenceMode::Marker
146+
} else {
147+
CandidatePreferenceMode::Default
148+
}
149+
}
150+
}
151+
128152
/// Possible ways the given goal can be proven.
129153
#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)]
130154
pub enum CandidateSource<I: Interner> {

tests/ui/generic-associated-types/issue-93262.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//@ check-pass
1+
//@ check-fail
22

33
pub trait Trait {
44
type Assoc<'a> where Self: 'a;
@@ -11,7 +11,7 @@ where
1111

1212
pub struct Type;
1313

14-
impl<T: Trait> Foo<T> for Type
14+
impl<T: Trait> Foo<T> for Type //~ ERROR: the parameter type `T` may not live long enough
1515
where
1616
for<'a> T::Assoc<'a>: Clone
1717
{}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error[E0311]: the parameter type `T` may not live long enough
2+
--> $DIR/issue-93262.rs:14:16
3+
|
4+
LL | impl<T: Trait> Foo<T> for Type
5+
| ^^^^^^
6+
| |
7+
| the parameter type `T` must be valid for lifetime `'a`...
8+
| ...so that the type `T` will meet its required lifetime bounds...
9+
|
10+
note: ...that is required by this bound
11+
--> $DIR/issue-93262.rs:9:27
12+
|
13+
LL | for<'a> T::Assoc<'a>: Clone
14+
| ^^^^^
15+
= help: consider adding an explicit lifetime bound `T: 'a`...
16+
17+
error: aborting due to 1 previous error
18+
19+
For more information about this error, try `rustc --explain E0311`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0282]: type annotations needed for `<X as Trait<'static>>::Out<_>`
2+
--> $DIR/norm-before-method-resolution-opaque-type.rs:21:9
3+
|
4+
LL | let x = *x;
5+
| ^
6+
|
7+
help: consider giving `x` an explicit type, where the placeholders `_` are specified
8+
|
9+
LL | let x: <_ as Trait<'static>>::Out<_> = *x;
10+
| +++++++++++++++++++++++++++++++
11+
12+
error: aborting due to 1 previous error
13+
14+
For more information about this error, try `rustc --explain E0282`.

tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.old.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0507]: cannot move out of `*x` which is behind a shared reference
2-
--> $DIR/norm-before-method-resolution-opaque-type.rs:22:13
2+
--> $DIR/norm-before-method-resolution-opaque-type.rs:21:13
33
|
44
LL | let x = *x;
55
| ^^ move occurs because `*x` has type `<X as Trait<'_>>::Out<Foo>`, which does not implement the `Copy` trait

0 commit comments

Comments
 (0)