Repository: rust
Issue: Traits on function pointers
Labels: A-diagnostics,T-lang,T-compiler,C-bug

<!--
Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
along with any information you feel relevant to replicating the bug.
-->

First of all, I'm not yet sure if this is a problem with just diagnostics, or with the entire type system.
The following is a minimal example of an issue I got working on a larger project. The example may seem like a weird thing to do
but that's because it's minimized.

```rust
enum MyEnum {
    A,
    B(Box<Self>)
}
trait MyTrait1 {}
impl MyTrait1 for MyEnum {}

trait MyTrait2<T: MyTrait1> {}
impl<T: MyTrait1> MyTrait2<T> for fn(Box<T>) -> T {}

fn test<A: MyTrait1, B: MyTrait2<A>>(v: B) {}

fn main() {
    test(MyEnum::B);
}
```
(https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=5f3e7f2b710cec461b0f4f824583fa69)
I expected this to work, but it doesn't. It gives the following (weird) error:

```
Compiling playground v0.0.1 (/playground)
error[[E0277]](https://doc.rust-lang.org/stable/error-index.html#E0277): the trait bound `fn(Box<MyEnum>) -> MyEnum {MyEnum::B}: MyTrait2<_>` is not satisfied
  --> src/main.rs:14:10
   |
14 |     test(MyEnum::B);
   |     ---- ^^^^^^^^^ the trait `MyTrait2<_>` is not implemented for `fn(Box<MyEnum>) -> MyEnum {MyEnum::B}`
   |     |
   |     required by a bound introduced by this call
   |
   = help: the trait `MyTrait2<T>` is implemented for `fn(Box<T>) -> T`
note: required by a bound in `test`
  --> src/main.rs:11:25
   |
11 | fn test<A: MyTrait1, B: MyTrait2<A>>(v: B) {}
   |                         ^^^^^^^^^^^ required by this bound in `test`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error
```

What's weird about it is this: it doesn't seem to think MmyTrait2 is implemented for `fn(Box<MyEnum>) -> MyEnum`
however, the help messages says: look, it is implemented for `fn(Box<T>) -> T` which my type conforms to.

I started digging a bit. Maybe it's because of the enum variant not actually being a fn:
```rust
enum MyEnum {
    A,
    B(Box<Self>)
}
trait MyTrait1 {}
impl MyTrait1 for MyEnum {}

trait MyTrait2<T: MyTrait1> {}
impl<T: MyTrait1> MyTrait2<T> for fn(Box<T>) -> T {}

fn test<A: MyTrait1, B: MyTrait2<A>>(v: B) {}

fn helper(a: Box<MyEnum>) -> MyEnum {
    MyEnum::B(a)
}

fn main() {
    test(helper);
}
```
But this gives a similar error.

Alright, maybe it's the function pointer. What happens if I ask for a closure? (I'd quite like the function pointers since with closures I need a generic leading to overlapping impls, and I never expect to get a closure anyway. I thought that was the problem at first, distinguishing an overlapping impl with the fn pointer but the example seems to prove that it goes wrong even if there aren't any other implementors of `MyTrait2`)

```rust
enum MyEnum {
    A,
    B(Box<Self>)
}
trait MyTrait1 {}
impl MyTrait1 for MyEnum {}

trait MyTrait2<T: MyTrait1> {}
impl<T: MyTrait1, F: Fn(Box<T>) -> T> MyTrait2<T> for F {}

fn test<A: MyTrait1, B: MyTrait2<A>>(v: B) {}

fn helper(a: Box<MyEnum>) -> MyEnum {
    MyEnum::B(a)
}

fn main() {
    test(helper);
}
```
This seems to work. So it specifically has to do with function pointers.

Now one more thing, if I insert an explicit cast to the function pointer type:
```rust
enum MyEnum {
    A,
    B(Box<Self>)
}
trait MyTrait1 {}
impl MyTrait1 for MyEnum {}

trait MyTrait2<T: MyTrait1> {}
impl<T: MyTrait1> MyTrait2<T> for fn(Box<T>) -> T {}

fn test<A: MyTrait1, B: MyTrait2<A>>(v: B) {}

fn main() {
    test::<_, fn(Box<MyEnum>) -> MyEnum>(MyEnum::B);
}
```
Everything works. 

<summary>
(last minute: I thought maybe the box was the issue so I removed it and the enum, replacing it with just a function call and even that produces the same error)

<details>

```rust
trait MyTrait1 {}
impl MyTrait1 for i64 {}

trait MyTrait2<T: MyTrait1> {}
impl<T: MyTrait1> MyTrait2<T> for fn(T) -> T {}

fn test<A: MyTrait1, B: MyTrait2<A>>(v: B) {}

fn helper(a: i64) -> i64 {
    todo!()
}

fn main() {
    test(helper);
}
```

breaks in a similar manner:

```
Compiling playground v0.0.1 (/playground)
error[[E0277]](https://doc.rust-lang.org/stable/error-index.html#E0277): the trait bound `fn(i64) -> i64 {helper}: MyTrait2<_>` is not satisfied
  --> src/main.rs:14:10
   |
14 |     test(helper);
   |     ---- ^^^^^^ the trait `MyTrait2<_>` is not implemented for `fn(i64) -> i64 {helper}`
   |     |
   |     required by a bound introduced by this call
   |
   = help: the trait `MyTrait2<T>` is implemented for `fn(T) -> T`
note: required by a bound in `test`
  --> src/main.rs:7:25
   |
7  | fn test<A: MyTrait1, B: MyTrait2<A>>(v: B) {}
   |                         ^^^^^^^^^^^ required by this bound in `test`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error
```

</details>
</summary>
My conclusion: this is certainly a diagnostics bug. The help message that the implementation exists is misleading since it references an implementation that looks like it should work. However, I also expected this to *just work* meaning it could be classified as a bug in type inference. But I don't know enough about the compiler to know whether or not this is expected behavior or not.


### Meta
<!--
If you're using the stable version of the compiler, you should also check if the
bug also exists in the beta or nightly versions.
-->

`rustc --version --verbose`:
```
rustc 1.61.0 (fe5b13d68 2022-05-18)
binary: rustc
commit-hash: fe5b13d681f25ee6474be29d748c65adcd91f69e
commit-date: 2022-05-18
host: x86_64-unknown-linux-gnu
release: 1.61.0
LLVM version: 14.0.0
```
(though I also tried nightly, and it's not fixed yet there)

(there is no backtrace here since there's no program that crashed. Code just didn't compile)

I'm mentioning @estebank since I can't add the A-diagnostics tag myself on a more generic bug report. That only happens if I choose the diagnostics template and I think this bug may be broader than that. I hope you don't mind :)