Skip to content

Commit

Permalink
Rollup merge of #133519 - compiler-errors:xform-ret-wf, r=lcnr
Browse files Browse the repository at this point in the history
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
  • Loading branch information
GuillaumeGomez authored Nov 28, 2024
2 parents b1c33f4 + 4120fdb commit 06815d0
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 0 deletions.
22 changes: 22 additions & 0 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
47 changes: 47 additions & 0 deletions tests/ui/traits/next-solver/non-wf-ret.rs
Original file line number Diff line number Diff line change
@@ -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() {}

0 comments on commit 06815d0

Please sign in to comment.