1
Fork 0

Only prefer Sized candidates, and only if they certainly hold

This commit is contained in:
Michael Goulet 2025-03-10 16:50:29 +00:00
parent f9696dda6e
commit aebbd42460
7 changed files with 89 additions and 23 deletions

View file

@ -95,8 +95,11 @@ pub type EvaluationCache<'tcx, ENV> = Cache<(ENV, ty::PolyTraitPredicate<'tcx>),
/// parameter environment. /// parameter environment.
#[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)] #[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)]
pub enum SelectionCandidate<'tcx> { pub enum SelectionCandidate<'tcx> {
/// UwU /// A built-in implementation for the `Sized` trait. This is preferred
SizedCandidate, /// over all other candidates.
SizedCandidate {
has_nested: bool,
},
/// A builtin implementation for some specific traits, used in cases /// A builtin implementation for some specific traits, used in cases
/// where we cannot rely an ordinary library implementations. /// where we cannot rely an ordinary library implementations.

View file

@ -1062,8 +1062,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
candidates: &mut SelectionCandidateSet<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>,
) { ) {
match self.sized_conditions(obligation) { match self.sized_conditions(obligation) {
BuiltinImplConditions::Where(_) => { BuiltinImplConditions::Where(nested) => {
candidates.vec.push(SizedCandidate); candidates
.vec
.push(SizedCandidate { has_nested: !nested.skip_binder().is_empty() });
} }
BuiltinImplConditions::None => {} BuiltinImplConditions::None => {}
BuiltinImplConditions::Ambiguous => { BuiltinImplConditions::Ambiguous => {

View file

@ -40,8 +40,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
candidate: SelectionCandidate<'tcx>, candidate: SelectionCandidate<'tcx>,
) -> Result<Selection<'tcx>, SelectionError<'tcx>> { ) -> Result<Selection<'tcx>, SelectionError<'tcx>> {
let mut impl_src = match candidate { let mut impl_src = match candidate {
SizedCandidate => { SizedCandidate { has_nested } => {
let data = self.confirm_builtin_candidate(obligation, true); let data = self.confirm_builtin_candidate(obligation, has_nested);
ImplSource::Builtin(BuiltinImplSource::Misc, data) ImplSource::Builtin(BuiltinImplSource::Misc, data)
} }

View file

@ -1803,25 +1803,19 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
// We prefer `Sized` candidates over everything. // We prefer `Sized` candidates over everything.
let mut sized_candidates = let mut sized_candidates =
candidates.iter().filter(|c| matches!(c.candidate, SizedCandidate)); candidates.iter().filter(|c| matches!(c.candidate, SizedCandidate { has_nested: _ }));
if let Some(_sized_candidate) = sized_candidates.next() { if let Some(sized_candidate) = sized_candidates.next() {
// There should only ever be a single sized candidate // There should only ever be a single sized candidate
// as they would otherwise overlap. // as they would otherwise overlap.
debug_assert_eq!(sized_candidates.next(), None); debug_assert_eq!(sized_candidates.next(), None);
return Some(SizedCandidate); // Only prefer the built-in `Sized` candidate if its nested goals are certain.
} // Otherwise, we may encounter failure later on if inference causes this candidate
// to not hold, but a where clause would've applied instead.
// We prefer trivial builtin candidates, i.e. builtin impls without any nested if sized_candidate.evaluation.must_apply_modulo_regions() {
// requirements, over all others. This is a fix for #53123 and prevents winnowing return Some(sized_candidate.candidate.clone());
// from accidentally extending the lifetime of a variable. } else {
let mut trivial_builtin = candidates return None;
.iter() }
.filter(|c| matches!(c.candidate, BuiltinCandidate { has_nested: false }));
if let Some(_trivial) = trivial_builtin.next() {
// There should only ever be a single trivial builtin candidate
// as they would otherwise overlap.
debug_assert_eq!(trivial_builtin.next(), None);
return Some(BuiltinCandidate { has_nested: false });
} }
// Before we consider where-bounds, we have to deduplicate them here and also // Before we consider where-bounds, we have to deduplicate them here and also
@ -1950,7 +1944,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
// Don't use impl candidates which overlap with other candidates. // Don't use impl candidates which overlap with other candidates.
// This should pretty much only ever happen with malformed impls. // This should pretty much only ever happen with malformed impls.
if candidates.iter().all(|c| match c.candidate { if candidates.iter().all(|c| match c.candidate {
SizedCandidate SizedCandidate { has_nested: _ }
| BuiltinCandidate { has_nested: _ } | BuiltinCandidate { has_nested: _ }
| TransmutabilityCandidate | TransmutabilityCandidate
| AutoImplCandidate | AutoImplCandidate

View file

@ -0,0 +1,21 @@
//@ check-pass
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
struct W<T: ?Sized>(T);
fn is_sized<T: Sized>(x: *const T) {}
fn dummy<T: ?Sized>() -> *const T { todo!() }
fn non_param_where_bound<T: ?Sized>()
where
W<T>: Sized,
{
let x: *const W<_> = dummy();
is_sized::<W<_>>(x);
let _: *const W<T> = x;
}
fn main() {}

View file

@ -0,0 +1,19 @@
struct MyType<'a, T: ?Sized>(&'a (), T);
fn is_sized<T>() {}
fn foo<'a, T: ?Sized>()
where
(MyType<'a, T>,): Sized,
//~^ ERROR mismatched types
MyType<'static, T>: Sized,
{
// Preferring the builtin `Sized` impl of tuples
// requires proving `MyType<'a, T>: Sized` which
// can only be proven by using the where-clause,
// adding an unnecessary `'static` constraint.
is_sized::<(MyType<'a, T>,)>();
//~^ ERROR lifetime may not live long enough
}
fn main() {}

View file

@ -0,0 +1,27 @@
error[E0308]: mismatched types
--> $DIR/lifetime-incomplete-prefer-sized-builtin-over-wc.rs:7:23
|
LL | (MyType<'a, T>,): Sized,
| ^^^^^ lifetime mismatch
|
= note: expected trait `<MyType<'a, T> as Sized>`
found trait `<MyType<'static, T> as Sized>`
note: the lifetime `'a` as defined here...
--> $DIR/lifetime-incomplete-prefer-sized-builtin-over-wc.rs:5:8
|
LL | fn foo<'a, T: ?Sized>()
| ^^
= note: ...does not necessarily outlive the static lifetime
error: lifetime may not live long enough
--> $DIR/lifetime-incomplete-prefer-sized-builtin-over-wc.rs:15:5
|
LL | fn foo<'a, T: ?Sized>()
| -- lifetime `'a` defined here
...
LL | is_sized::<(MyType<'a, T>,)>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.