Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve compiler error on associated types #71035

Closed
manuels opened this issue Apr 11, 2020 · 4 comments · Fixed by #71108
Closed

Improve compiler error on associated types #71035

manuels opened this issue Apr 11, 2020 · 4 comments · Fixed by #71108
Labels
A-associated-items Area: Associated items (types, constants & functions) A-diagnostics Area: Messages for errors, warnings, and lints A-typesystem Area: The type system C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@manuels
Copy link

manuels commented Apr 11, 2020

If you have a variable with type impl TryStream<Ok=T, Error=E> and want to use it as a impl Stream<Item=Result<T,E>>, the compiler complains.
The compiler does not take you by the hand and helps you with helpful hints.

I tried (roughly) this code: (EDIT: find a self-contained example here)

use bytes;
use std::io;
use futures::channel::mpsc;
use async_std::task;

//pub fn my_rx(sock: std::sync::Arc<UdpSocket>) -> impl futures::Stream<Item=Result<(bytes::Bytes, std::net::SocketAddr), std::io::Error>> { // WORKS
pub fn my_rx(sock: std::sync::Arc<UdpSocket>) -> impl futures::TryStream<Ok=(bytes::Bytes, std::net::SocketAddr), Error=std::io::Error> { // ERROR
    ...
}

fn main() {
    let inet_rx = my_rx();
    let (tx1, inet_rx1) = mpsc::unbounded();
    let (tx2, inet_rx2) = mpsc::unbounded();
    let mut tx = tx1.fanout(tx2).sink_map_err(|_| io::Error::new(io::ErrorKind::Other, "Send Error"));
    let foo = inet_rx.forward(tx);
    let _ = task::spawn(foo);
}

I expected to see this happen:
I would like the compiler to tell me that that that while there exists an implementation of TryStream<Ok=T, Error=E> as Stream<Result<T,E>>, there might exist other implementations and therefore this code is not accepted. Using Stream<Result<T,E>> instead of TryStream<Ok=T, Error=E> would solve the error.

Instead, this happened:
There are no hints by the compiler.

Meta

rustc --version --verbose:

rustc 1.41.0-nightly (6d77e45f0 2019-12-04)
binary: rustc
commit-hash: 6d77e45f01079fe3d40180b3e256e414ab379f63
commit-date: 2019-12-04
host: x86_64-unknown-linux-gnu
release: 1.41.0-nightly
LLVM version: 9.0
@manuels manuels added the C-bug Category: This is a bug. label Apr 11, 2020
@jonas-schievink
Copy link
Contributor

Please provide a self-contained example that shows the bad diagnostic

@Centril Centril added A-associated-items Area: Associated items (types, constants & functions) A-diagnostics Area: Messages for errors, warnings, and lints A-typesystem Area: The type system C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. and removed C-bug Category: This is a bug. labels Apr 11, 2020
@manuels
Copy link
Author

manuels commented Apr 12, 2020

I tried to break it down to this code:

use futures::channel::mpsc;
use futures::stream::StreamExt;

//fn create_stream() -> impl futures::Stream<Item=Result<(), mpsc::SendError>> { // WORKS
fn create_stream() -> impl futures::TryStream<Ok=(), Error=mpsc::SendError> { // ERROR
    let (_, rx) = mpsc::unbounded();
    rx
}

fn main() {
    let rx = create_stream();
    let (tx, _) = mpsc::unbounded();
    let foo = rx.forward(tx);
    let _ = futures::executor::block_on(foo);
}
error[E0271]: type mismatch resolving `<impl futures_core::stream::TryStream as futures_core::stream::Stream>::Item == std::result::Result<(), futures_channel::mpsc::SendError>`
   --> src/main.rs:14:13
    |
5   | pub fn create_stream() -> impl futures::TryStream<Ok=(), Error=mpsc::SendError> { // ERROR
    |                           ----------------------------------------------------- the expected opaque type
...
14  |     let _ = futures::executor::block_on(foo);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found enum `std::result::Result`
    | 
   ::: /playground/.cargo/registry/src/git.luolix.top-1ecc6299db9ec823/futures-executor-0.3.4/src/local_pool.rs:315:20
    |
315 | pub fn block_on<F: Future>(f: F) -> F::Output {
    |                    ------ required by this bound in `futures_executor::local_pool::block_on`
    |
    = note: expected associated type `<impl futures_core::stream::TryStream as futures_core::stream::Stream>::Item`
                          found enum `std::result::Result<(), futures_channel::mpsc::SendError>`
    = note: consider constraining the associated type `<impl futures_core::stream::TryStream as futures_core::stream::Stream>::Item` to `std::result::Result<(), futures_channel::mpsc::SendError>` or calling a method that returns `<impl futures_core::stream::TryStream as futures_core::stream::Stream>::Item`
    = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
    = note: required because of the requirements on the impl of `core::future::future::Future` for `futures_util::stream::stream::forward::Forward<impl futures_core::stream::TryStream, futures_channel::mpsc::UnboundedSender<()>>`

error[E0271]: type mismatch resolving `<impl futures_core::stream::TryStream as futures_core::stream::Stream>::Item == std::result::Result<(), futures_channel::mpsc::SendError>`
  --> src/main.rs:14:13
   |
5  | pub fn create_stream() -> impl futures::TryStream<Ok=(), Error=mpsc::SendError> { // ERROR
   |                           ----------------------------------------------------- the expected opaque type
...
14 |     let _ = futures::executor::block_on(foo);
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found enum `std::result::Result`
   |
   = note: expected associated type `<impl futures_core::stream::TryStream as futures_core::stream::Stream>::Item`
                         found enum `std::result::Result<(), futures_channel::mpsc::SendError>`
   = note: consider constraining the associated type `<impl futures_core::stream::TryStream as futures_core::stream::Stream>::Item` to `std::result::Result<(), futures_channel::mpsc::SendError>` or calling a method that returns `<impl futures_core::stream::TryStream as futures_core::stream::Stream>::Item`
   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
   = note: required because of the requirements on the impl of `core::future::future::Future` for `futures_util::stream::stream::forward::Forward<impl futures_core::stream::TryStream, futures_channel::mpsc::UnboundedSender<()>>`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0271`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

@manuels
Copy link
Author

manuels commented Apr 12, 2020

I just found out that defining Stream::Item by hand also works:

fn create_stream() -> impl futures::TryStream<Ok=(), Error=mpsc::SendError,
Item=Result<(), mpsc::SendError>> {
    let (_, rx) = mpsc::unbounded();
    rx
}

Why is that? Is the compiler not able to deduce Stream::Item by itself?
Maybe the compiler should give a hint that declaring Stream::Item yourself would solve the problem.
You might argue that the error message

   = note: consider constraining the associated type `<impl futures_core::stream::TryStream as futures_core::stream::Stream>::Item` to `std::result::Result<(), futures_channel::mpsc::SendError>` or calling a method that returns `<impl futures_core::stream::TryStream as futures_core::stream::Stream>::Item`

does exactly this but it's not directly clear how to do this. Maybe giving an example like

   = note: consider constraining the associated type `<impl futures_core::stream::TryStream as futures_core::stream::Stream>::Item` to `std::result::Result<(), futures_channel::mpsc::SendError>` like this:
  --> src/main.rs:14:13
   |
5  | pub fn create_stream() -> impl futures::TryStream<Ok=(), Error=mpsc::SendError, Item=std::result::Result<(), futures_channel::mpsc::SendError>> {
   |
   = note: or calling a method that returns `<impl futures_core::stream::TryStream as futures_core::stream::Stream>::Item`

would be helpful here.

@estebank
Copy link
Contributor

estebank commented Apr 14, 2020

I think that #71108 might fulfill the last comment's request.

Edit: I just checked and the code doesn't account for impl Trait in return position (which behaves differently to impl Trait in argument position, which is handled). I'll see what I can do to also handle this case.

Edit 2: This will not be handled in the linked PR, it is a second order projection that will need further work for me to be able to supply a structured suggestion.

Edit 3: Managed to get it to work 🎉

error[E0271]: type mismatch resolving `<impl futures_core::stream::TryStream as futures_core::stream::Stream>::Item == std::result::Result<(), futures_channel::mpsc::SendError>`
   --> src/main.rs:14:13
    |
5   | fn create_stream() -> impl futures::TryStream<Ok=(), Error=mpsc::SendError> { // ERROR
    |                       ----------------------------------------------------- the expected opaque type
...
14  |     let _ = futures::executor::block_on(foo);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found enum `std::result::Result`
    |
   ::: /Users/ekuber/.cargo/registry/src/git.luolix.top-1ecc6299db9ec823/futures-executor-0.3.4/src/local_pool.rs:315:20
    |
315 | pub fn block_on<F: Future>(f: F) -> F::Output {
    |                    ------ required by this bound in `futures_executor::local_pool::block_on`
    |
    = note: expected associated type `<impl futures_core::stream::TryStream as futures_core::stream::Stream>::Item`
                          found enum `std::result::Result<(), futures_channel::mpsc::SendError>`
    = note: required because of the requirements on the impl of `core::future::future::Future` for `futures_util::stream::stream::forward::Forward<impl futures_core::stream::TryStream, futures_channel::mpsc::UnboundedSender<()>>`
help: consider constraining the associated type `<impl futures_core::stream::TryStream as futures_core::stream::Stream>::Item` to `std::result::Result<(), futures_channel::mpsc::SendError>`
    |
5   | fn create_stream() -> impl futures::TryStream<Ok=(), Error=mpsc::SendError, Item = std::result::Result<(), futures_channel::mpsc::SendError>> { // ERROR
    |                                                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

It's a bit more verbose than I'd like, but we can tackle on a different PR.

bors added a commit to rust-lang-ci/rust that referenced this issue May 4, 2020
…onstraint, r=oli-obk

On type mismatch involving associated type, suggest constraint

When an associated type is found when a specific type was expected, if
possible provide a structured suggestion constraining the associated
type in a bound.

```
error[E0271]: type mismatch resolving `<T as Foo>::Y == i32`
  --> $DIR/associated-types-multiple-types-one-trait.rs:13:5
   |
LL |     want_y(t);
   |     ^^^^^^ expected `i32`, found associated type
...
LL | fn want_y<T:Foo<Y=i32>>(t: &T) { }
   |                 ----- required by this bound in `want_y`
   |
   = note:         expected type `i32`
           found associated type `<T as Foo>::Y`
help: consider constraining the associated type `<T as Foo>::Y` to `i32`
   |
LL | fn have_x_want_y<T:Foo<X=u32, Y = i32>>(t: &T)
   |                             ^^^^^^^^^
```

```
error[E0308]: mismatched types
  --> $DIR/trait-with-missing-associated-type-restriction.rs:12:9
   |
LL |     qux(x.func())
   |         ^^^^^^^^ expected `usize`, found associated type
   |
   = note:         expected type `usize`
           found associated type `<impl Trait as Trait>::A`
help: consider constraining the associated type `<impl Trait as Trait>::A` to `usize`
   |
LL | fn foo(x: impl Trait<A = usize>) {
   |                     ^^^^^^^^^^
```

Fix rust-lang#71035. Related to rust-lang#70908.
@bors bors closed this as completed in b0085c8 May 4, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-associated-items Area: Associated items (types, constants & functions) A-diagnostics Area: Messages for errors, warnings, and lints A-typesystem Area: The type system C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants