Rollup merge of #133519 - compiler-errors:xform-ret-wf, r=lcnr

Check `xform_ret_ty` for WF in the new solver to improve method winnowing

This is a bit interesting. Method probing in the old solver is stronger than the new solver because eagerly normalizing types causes us to check their corresponding trait goals. This is important because we don't end up checking all of the where clauses of a method when method probing; just the where clauses of the impl. i.e., for:

```
impl Foo
where
   WC1,
{
    fn method()
    where
        WC2,
    {}
}
```

We only check WC1 and not WC2. This is because at this point in probing the method is instantiated w/ infer vars, and checking the where clauses in WC2 will lead to cycles if we were to check them (at least that's my understanding; I could investigate changing that in general, incl. in the old solver, but I don't have much confidence that it won't lead to really bad overflows.)

This PR chooses to emulate the old solver by just checking that the return type is WF. This is theoretically stronger, but I'm not too worried about it. I think we alternatively have several approaches we can take here, though this one seems the simplest. Thoughts?

r? lcnr
This commit is contained in:
Guillaume Gomez 2024-11-28 03:14:48 +01:00 committed by GitHub
commit 06815d0cc1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 69 additions and 0 deletions

View file

@ -1642,6 +1642,28 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}
// FIXME(-Znext-solver): See the linked issue below.
// <https://github.com/rust-lang/trait-system-refactor-initiative/issues/134>
//
// In the new solver, check the well-formedness of the return type.
// This emulates, in a way, the predicates that fall out of
// normalizing the return type in the old solver.
//
// We alternatively could check the predicates of the method itself hold,
// but we intentionally do not do this in the old solver b/c of cycles,
// and doing it in the new solver would be stronger. This should be fixed
// in the future, since it likely leads to much better method winnowing.
if let Some(xform_ret_ty) = xform_ret_ty
&& self.infcx.next_trait_solver()
{
ocx.register_obligation(traits::Obligation::new(
self.tcx,
cause.clone(),
self.param_env,
ty::ClauseKind::WellFormed(xform_ret_ty.into()),
));
}
// Evaluate those obligations to see if they might possibly hold.
for error in ocx.select_where_possible() {
result = ProbeResult::NoMatch;

View file

@ -0,0 +1,47 @@
//@ check-pass
//@ compile-flags: -Znext-solver
use std::ops::Deref;
pub struct List<T> {
skel: [T],
}
impl<'a, T: Copy> IntoIterator for &'a List<T> {
type Item = T;
type IntoIter = std::iter::Copied<<&'a [T] as IntoIterator>::IntoIter>;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
impl<T> Deref for List<T> {
type Target = [T];
fn deref(&self) -> &[T] {
todo!()
}
}
impl<T> List<T> {
fn iter(&self) -> <&Self as IntoIterator>::IntoIter
where
T: Copy,
{
todo!()
}
}
fn test<Q>(t: &List<Q>) {
// Checking that `<&List<Q> as IntoIterator>::IntoIter` is WF
// will disqualify the inherent method, since normalizing it
// requires `Q: Copy` which does not hold. and allow us to fall
// through to the deref'd `<[Q]>::iter` method which works.
//
// In the old solver, the same behavior is achieved by just
// eagerly normalizing the return type.
t.iter();
}
fn main() {}