Repository: rust
Issue: APIs don't just depend on function signatures
Labels: T-lang,T-compiler,C-bug,A-crate-compat

<!--
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.
-->

Normally Rust has the really nice property that I can focus on getting each function to compile in isolation, without worries that
refactoring the body of a function will break backwards compatibility. However, I recently stumbled across a bizarre circumstance
where that isolation appears to be violated. After spending a day pruning it down to a [small-ish reproducible example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=29a0027681cc0e1326a3cdfad0380276):

```rust
use std::future::Future;

// Note: The original code isn't actually object-oriented; the
// object-oriented framing is just a way to give the isolated example
// more intuitive/memorable names than `Foo`, `Bar`, `Baz`, etc...

type Object = Box<dyn std::any::Any + Send>;

fn get_object() -> Object {
    unimplemented!()
}

trait AsyncFrom<T>: Sized {
    fn async_from(src: T) -> impl Future<Output = Self>;
}

impl<T, U> AsyncFrom<U> for (T, U) {
    async fn async_from(_src: U) -> Self {
        unimplemented!()
    }
}

async fn downcast<T: AsyncFrom<Object>>() -> T {
    T::async_from(get_object()).await
}

async fn get_thing() -> u32 {
    // Oddly, inlining this call fixes everything.
    downcast::<(_, _)>().await.0
    //<(_, _)>::async_from(get_object()).await.0
}

pub async fn does_compile() -> u32 {
    get_thing().await
}

// Commenting out all code below this line causes everything to work.

// Oddly, complains about an implementation of AsyncFrom here,
// even though this does not directly use that at all.
pub fn does_not_compile() -> impl AsyncFnOnce {
    move || does_compile()
}

trait AsyncFnOnce: FnOnce() -> Self::Future {
    type Future: Future + Send;
}

impl<F, Fut> AsyncFnOnce for F
where
    F: FnOnce() -> Fut,
    Fut: Future + Send,
{
    type Future = Fut;
}
```

I find the way Rust handles this code to be problematic in a couple of ways:

First, the error message is nonsensical:
```
error: implementation of `AsyncFrom` is not general enough
  --> src/lib.rs:42:5
   |
42 |     move || does_compile()
   |     ^^^^^^^^^^^^^^^^^^^^^^ implementation of `AsyncFrom` is not general enough
   |
   = note: `(u32, Box<(dyn Any + Send + '0)>)` must implement `AsyncFrom<Box<(dyn Any + Send + '1)>>`, for any two lifetimes `'0` and `'1`...
   = note: ...but it actually implements `AsyncFrom<Box<dyn Any + Send>>`
```
This could be an acceptable error message if it were pointing to code in `downcast`, but it's pointing to code in `does_not_compile` which doesn't even (directly) use `AsyncFrom`. This really threw me for a loop, especially because I encountered it while working on a large codebase.

Second, I was surprised to discover that the body of `get_thing` influences whether the code compiles, not just the signature of `get_thing`. Inlining `downcast` into `get_thing` appears to resolve the compile error without changing code in `does_not_compile`. Alternatively, adjusting the body of `does_not_compile` to avoid calling `does_compile` fixes the compile error. So, the compile only fails due to the interaction between two different function bodies... which feels very un-Rusty to me.

After seeking help in the community discord, **one 🐸, two :frog:, tree :frog:** explained that combining closures with existential types allows you to violate the usual Rust philosophy that only function signatures matter. I feel like this is extremely unfortunate, both because it destroys local reasoning and because it makes it extra hard to be sure you haven't broken backwards compatibility when merely refactoring a function body.

Per **one 🐸, two :frog:, tree :frog:**'s suggestion, I'm writing this issue in the hopes that providing counterintuitive example code may help push for the current behavior to be changed. I'd really like it if I could worry about getting each function to compile one at a time, without having to consider the possibility that different function bodies may interact negatively.

Thank you!

### Meta
(this behavior is also exhibited on beta and nightly versions of Rust)

`rustc --version --verbose`:
```
rustc 1.80.1 (3f5fd8dd4 2024-08-06)
binary: rustc
commit-hash: 3f5fd8dd41153bc5fdca9427e9e05be2c767ba23
commit-date: 2024-08-06
host: x86_64-unknown-linux-gnu
release: 1.80.1
LLVM version: 18.1.7
```

(the backtrace doesn't reveal anything new:)

<!--
Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in your
environment. E.g. `RUST_BACKTRACE=1 cargo build`.
-->
<details><summary>Backtrace</summary>
<p>

```
  --> src/lib.rs:42:5
   |
42 |     move || does_compile()
   |     ^^^^^^^^^^^^^^^^^^^^^^ implementation of `AsyncFrom` is not general enough
   |
   = note: `(u32, Box<(dyn Any + Send + '0)>)` must implement `AsyncFrom<Box<(dyn Any + Send + '1)>>`, for any two lifetimes `'0` and `'1`...
   = note: ...but it actually implements `AsyncFrom<Box<dyn Any + Send>>`

```

</p>
</details>