Skip to content

Commit

Permalink
Rollup merge of #135866 - BoxyUwU:dont_pick_fnptr_nested_goals, r=lcnr
Browse files Browse the repository at this point in the history
Don't pick `T: FnPtr` nested goals as the leaf goal in diagnostics for new solver

r? `@lcnr`

See `tests/ui/traits/next-solver/diagnostics/dont-pick-fnptr-bound-as-leaf.rs` for a minimized example of what code this affects the diagnostics off. The output of running nightly `-Znext-solver` on that test is the following:
```
error[E0277]: the trait bound `Foo: Trait` is not satisfied
  --> src/lib.rs:14:20
   |
14 |     requires_trait(Foo);
   |     -------------- ^^^ the trait `FnPtr` is not implemented for `Foo`
   |     |
   |     required by a bound introduced by this call
   |
note: required for `Foo` to implement `Trait`
  --> src/lib.rs:7:16
   |
7  | impl<T: FnPtr> Trait for T {}
   |         -----  ^^^^^     ^
   |         |
   |         unsatisfied trait bound introduced here
note: required by a bound in `requires_trait`
  --> src/lib.rs:11:22
   |
11 | fn requires_trait<T: Trait>(_: T) {}
   |                      ^^^^^ required by this bound in `requires_trait`
```

Part of rust-lang/trait-system-refactor-initiative#148
  • Loading branch information
matthiaskrgr authored Jan 22, 2025
2 parents 413f87a + 3ef506f commit 318466a
Show file tree
Hide file tree
Showing 26 changed files with 388 additions and 68 deletions.
23 changes: 21 additions & 2 deletions compiler/rustc_trait_selection/src/solve/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,8 +513,27 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
_ => ChildMode::PassThrough,
};

let nested_goals = candidate.instantiate_nested_goals(self.span());

// If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as
// an actual candidate, instead we should treat them as if the impl was never considered to
// have potentially applied. As if `impl<A, R> Trait for for<..> fn(..A) -> R` was written
// instead of `impl<T: FnPtr> Trait for T`.
//
// We do this as a separate loop so that we do not choose to tell the user about some nested
// goal before we encounter a `T: FnPtr` nested goal.
for nested_goal in &nested_goals {
if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait()
&& let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
&& poly_trait_pred.def_id() == fn_ptr_trait
&& let Err(NoSolution) = nested_goal.result()
{
return ControlFlow::Break(self.obligation.clone());
}
}

let mut impl_where_bound_count = 0;
for nested_goal in candidate.instantiate_nested_goals(self.span()) {
for nested_goal in nested_goals {
trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));

let make_obligation = |cause| Obligation {
Expand Down Expand Up @@ -605,7 +624,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
}
}

#[derive(Copy, Clone)]
#[derive(Debug, Copy, Clone)]
enum ChildMode<'tcx> {
// Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
// and skip all `GoalSource::Misc`, which represent useless obligations
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0277]: the trait bound `Foo: Trait` is not satisfied
--> $DIR/dont-pick-fnptr-bound-as-leaf.rs:24:20
|
LL | requires_trait(Foo);
| -------------- ^^^ the trait `Trait` is not implemented for `Foo`
| |
| required by a bound introduced by this call
|
note: required by a bound in `requires_trait`
--> $DIR/dont-pick-fnptr-bound-as-leaf.rs:19:22
|
LL | fn requires_trait<T: Trait>(_: T) {}
| ^^^^^ required by this bound in `requires_trait`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0277]: the trait bound `Foo: Trait` is not satisfied
--> $DIR/dont-pick-fnptr-bound-as-leaf.rs:24:20
|
LL | requires_trait(Foo);
| -------------- ^^^ the trait `Trait` is not implemented for `Foo`
| |
| required by a bound introduced by this call
|
note: required by a bound in `requires_trait`
--> $DIR/dont-pick-fnptr-bound-as-leaf.rs:19:22
|
LL | fn requires_trait<T: Trait>(_: T) {}
| ^^^^^ required by this bound in `requires_trait`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@ ignore-compare-mode-next-solver (explicit revisions)

// When emitting an error for `Foo: Trait` not holding we attempt to find a nested goal
// to give as the reason why the bound does not hold. This test checks that we do not
// try to tell the user that `Foo: FnPtr` is unimplemented as that would be confusing.

#![feature(fn_ptr_trait)]

use std::marker::FnPtr;

trait Trait {}

impl<T: FnPtr> Trait for T {}

struct Foo;

fn requires_trait<T: Trait>(_: T) {}
//~^ NOTE: required by a bound in `requires_trait`
//~| NOTE: required by this bound in `requires_trait`

fn main() {
requires_trait(Foo);
//~^ ERROR: the trait bound `Foo: Trait` is not satisfied
//~| NOTE: the trait `Trait` is not implemented for `Foo`
//~| NOTE: required by a bound introduced by this call
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
error[E0277]: the trait bound `K: Hash` is not satisfied
--> $DIR/bad-index-due-to-nested.rs:20:5
--> $DIR/bad-index-due-to-nested.rs:24:5
|
LL | map[k]
| ^^^ the trait `Hash` is not implemented for `K`
|
note: required for `HashMap<K, V>` to implement `Index<&K>`
--> $DIR/bad-index-due-to-nested.rs:7:12
--> $DIR/bad-index-due-to-nested.rs:11:12
|
LL | impl<K, V> Index<&K> for HashMap<K, V>
| ^^^^^^^^^ ^^^^^^^^^^^^^
Expand All @@ -18,13 +18,13 @@ LL | fn index<'a, K: std::hash::Hash, V>(map: &'a HashMap<K, V>, k: K) -> &'a V
| +++++++++++++++++

error[E0277]: the trait bound `V: Copy` is not satisfied
--> $DIR/bad-index-due-to-nested.rs:20:5
--> $DIR/bad-index-due-to-nested.rs:24:5
|
LL | map[k]
| ^^^ the trait `Copy` is not implemented for `V`
|
note: required for `HashMap<K, V>` to implement `Index<&K>`
--> $DIR/bad-index-due-to-nested.rs:7:12
--> $DIR/bad-index-due-to-nested.rs:11:12
|
LL | impl<K, V> Index<&K> for HashMap<K, V>
| ^^^^^^^^^ ^^^^^^^^^^^^^
Expand All @@ -37,7 +37,7 @@ LL | fn index<'a, K, V: std::marker::Copy>(map: &'a HashMap<K, V>, k: K) -> &'a
| +++++++++++++++++++

error[E0308]: mismatched types
--> $DIR/bad-index-due-to-nested.rs:20:9
--> $DIR/bad-index-due-to-nested.rs:24:9
|
LL | fn index<'a, K, V>(map: &'a HashMap<K, V>, k: K) -> &'a V {
| - found this type parameter
Expand All @@ -52,7 +52,7 @@ LL | map[&k]
| +

error[E0308]: mismatched types
--> $DIR/bad-index-due-to-nested.rs:20:5
--> $DIR/bad-index-due-to-nested.rs:24:5
|
LL | fn index<'a, K, V>(map: &'a HashMap<K, V>, k: K) -> &'a V {
| - found this type parameter ----- expected `&'a V` because of return type
Expand Down
76 changes: 76 additions & 0 deletions tests/ui/typeck/bad-index-due-to-nested.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
error[E0277]: the trait bound `K: Hash` is not satisfied
--> $DIR/bad-index-due-to-nested.rs:24:5
|
LL | map[k]
| ^^^ the trait `Hash` is not implemented for `K`
|
note: required for `HashMap<K, V>` to implement `Index<&K>`
--> $DIR/bad-index-due-to-nested.rs:11:12
|
LL | impl<K, V> Index<&K> for HashMap<K, V>
| ^^^^^^^^^ ^^^^^^^^^^^^^
LL | where
LL | K: Hash,
| ---- unsatisfied trait bound introduced here
help: consider restricting type parameter `K` with trait `Hash`
|
LL | fn index<'a, K: std::hash::Hash, V>(map: &'a HashMap<K, V>, k: K) -> &'a V {
| +++++++++++++++++

error[E0277]: the trait bound `V: Copy` is not satisfied
--> $DIR/bad-index-due-to-nested.rs:24:5
|
LL | map[k]
| ^^^ the trait `Copy` is not implemented for `V`
|
note: required for `HashMap<K, V>` to implement `Index<&K>`
--> $DIR/bad-index-due-to-nested.rs:11:12
|
LL | impl<K, V> Index<&K> for HashMap<K, V>
| ^^^^^^^^^ ^^^^^^^^^^^^^
...
LL | V: Copy,
| ---- unsatisfied trait bound introduced here
help: consider restricting type parameter `V` with trait `Copy`
|
LL | fn index<'a, K, V: std::marker::Copy>(map: &'a HashMap<K, V>, k: K) -> &'a V {
| +++++++++++++++++++

error[E0308]: mismatched types
--> $DIR/bad-index-due-to-nested.rs:24:9
|
LL | fn index<'a, K, V>(map: &'a HashMap<K, V>, k: K) -> &'a V {
| - found this type parameter
LL | map[k]
| ^ expected `&K`, found type parameter `K`
|
= note: expected reference `&_`
found type parameter `_`
help: consider borrowing here
|
LL | map[&k]
| +

error[E0277]: the trait bound `K: Hash` is not satisfied
--> $DIR/bad-index-due-to-nested.rs:24:5
|
LL | map[k]
| ^^^^^^ the trait `Hash` is not implemented for `K`
|
note: required for `HashMap<K, V>` to implement `Index<&K>`
--> $DIR/bad-index-due-to-nested.rs:11:12
|
LL | impl<K, V> Index<&K> for HashMap<K, V>
| ^^^^^^^^^ ^^^^^^^^^^^^^
LL | where
LL | K: Hash,
| ---- unsatisfied trait bound introduced here
help: consider restricting type parameter `K` with trait `Hash`
|
LL | fn index<'a, K: std::hash::Hash, V>(map: &'a HashMap<K, V>, k: K) -> &'a V {
| +++++++++++++++++

error: aborting due to 4 previous errors

Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.
7 changes: 6 additions & 1 deletion tests/ui/typeck/bad-index-due-to-nested.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@ ignore-compare-mode-next-solver (explicit revisions)

use std::hash::Hash;
use std::marker::PhantomData;
use std::ops::Index;
Expand All @@ -21,7 +25,8 @@ fn index<'a, K, V>(map: &'a HashMap<K, V>, k: K) -> &'a V {
//~^ ERROR the trait bound `K: Hash` is not satisfied
//~| ERROR the trait bound `V: Copy` is not satisfied
//~| ERROR mismatched types
//~| ERROR mismatched types
//[current]~| ERROR mismatched types
//[next]~^^^^^ ERROR the trait bound `K: Hash` is not satisfied
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0277]: the trait bound `PartialEqNotEq: Eq` is not satisfied
--> $DIR/union-derive-eq.rs:13:5
--> $DIR/union-derive-eq.rs:21:5
|
LL | #[derive(Eq)]
| -- in this derive macro expansion
Expand Down
19 changes: 19 additions & 0 deletions tests/ui/union/union-derive-eq.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
error[E0277]: the trait bound `PartialEqNotEq: Eq` is not satisfied
--> $DIR/union-derive-eq.rs:21:5
|
LL | #[derive(Eq)]
| -- in this derive macro expansion
LL | union U2 {
LL | a: PartialEqNotEq,
| ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `PartialEqNotEq`
|
= note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider annotating `PartialEqNotEq` with `#[derive(Eq)]`
|
LL + #[derive(Eq)]
LL | struct PartialEqNotEq;
|

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
16 changes: 14 additions & 2 deletions tests/ui/union/union-derive-eq.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@ ignore-compare-mode-next-solver (explicit revisions)

#[derive(Eq)] // OK
union U1 {
a: u8,
}

impl PartialEq for U1 { fn eq(&self, rhs: &Self) -> bool { true } }
impl PartialEq for U1 {
fn eq(&self, rhs: &Self) -> bool {
true
}
}

#[derive(PartialEq, Copy, Clone)]
struct PartialEqNotEq;
Expand All @@ -13,6 +21,10 @@ union U2 {
a: PartialEqNotEq, //~ ERROR the trait bound `PartialEqNotEq: Eq` is not satisfied
}

impl PartialEq for U2 { fn eq(&self, rhs: &Self) -> bool { true } }
impl PartialEq for U2 {
fn eq(&self, rhs: &Self) -> bool {
true
}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
error[E0277]: the trait bound `Self: Eq` is not satisfied
--> $DIR/wf-trait-fn-arg.rs:10:23
--> $DIR/wf-trait-fn-arg.rs:16:23
|
LL | fn bar(&self, x: &Bar<Self>);
| ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
|
note: required by a bound in `Bar`
--> $DIR/wf-trait-fn-arg.rs:7:14
--> $DIR/wf-trait-fn-arg.rs:11:15
|
LL | struct Bar<T:Eq+?Sized> { value: Box<T> }
| ^^ required by this bound in `Bar`
LL | struct Bar<T: Eq + ?Sized> {
| ^^ required by this bound in `Bar`
help: consider further restricting `Self`
|
LL | fn bar(&self, x: &Bar<Self>) where Self: Eq;
Expand Down
14 changes: 14 additions & 0 deletions tests/ui/wf/wf-trait-fn-arg.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0277]: the trait bound `Self: Eq` is not satisfied
--> $DIR/wf-trait-fn-arg.rs:16:23
|
LL | fn bar(&self, x: &Bar<Self>);
| ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
|
help: consider further restricting `Self`
|
LL | fn bar(&self, x: &Bar<Self>) where Self: Eq;
| ++++++++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
16 changes: 11 additions & 5 deletions tests/ui/wf/wf-trait-fn-arg.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@ ignore-compare-mode-next-solver (explicit revisions)

// Check that we test WF conditions for fn arguments in a trait definition.

#![feature(rustc_attrs)]
#![allow(dead_code)]
#![allow(unused_variables)]

struct Bar<T:Eq+?Sized> { value: Box<T> }
struct Bar<T: Eq + ?Sized> {
value: Box<T>,
}

trait Foo {
fn bar(&self, x: &Bar<Self>);
//~^ ERROR E0277
//
// Here, Eq ought to be implemented.
//~^ ERROR E0277
//
// Here, Eq ought to be implemented.
}

fn main() { }
fn main() {}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
error[E0277]: the trait bound `Self: Eq` is not satisfied
--> $DIR/wf-trait-fn-ret.rs:10:23
--> $DIR/wf-trait-fn-ret.rs:15:23
|
LL | fn bar(&self) -> &Bar<Self>;
| ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
|
note: required by a bound in `Bar`
--> $DIR/wf-trait-fn-ret.rs:7:14
--> $DIR/wf-trait-fn-ret.rs:10:15
|
LL | struct Bar<T:Eq+?Sized> { value: Box<T> }
| ^^ required by this bound in `Bar`
LL | struct Bar<T: Eq + ?Sized> {
| ^^ required by this bound in `Bar`
help: consider further restricting `Self`
|
LL | fn bar(&self) -> &Bar<Self> where Self: Eq;
Expand Down
14 changes: 14 additions & 0 deletions tests/ui/wf/wf-trait-fn-ret.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0277]: the trait bound `Self: Eq` is not satisfied
--> $DIR/wf-trait-fn-ret.rs:15:23
|
LL | fn bar(&self) -> &Bar<Self>;
| ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
|
help: consider further restricting `Self`
|
LL | fn bar(&self) -> &Bar<Self> where Self: Eq;
| ++++++++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
Loading

0 comments on commit 318466a

Please sign in to comment.