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

Typecheck error in body of async fn reported at point-of-use #73737

Open
bdonlan opened this issue Jun 25, 2020 · 3 comments
Open

Typecheck error in body of async fn reported at point-of-use #73737

bdonlan opened this issue Jun 25, 2020 · 3 comments
Labels
A-async-await Area: Async & Await AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@bdonlan
Copy link
Contributor

bdonlan commented Jun 25, 2020

I tried this code:

use std::marker::PhantomData;
use std::io;

use tokio; // 0.2.21
use tokio_util; // 0.3.1

use tokio::io::{AsyncRead, AsyncWrite};
use tokio::net::TcpStream;
use tokio_util::codec::{Encoder, Decoder};
use futures::stream::StreamExt;

use bytes::{BytesMut, Bytes};

struct Codec;

struct Message<'a> { _p: PhantomData<&'a u8> }

impl Encoder<Message<'static>> for Codec {
    type Error = io::Error;
    
    fn encode(&mut self, item: Message<'static>, dst: &mut BytesMut) -> Result<(), Self::Error> {
        todo!()
    }
}

impl Decoder for Codec {
    type Item = Message<'static>;
    type Error = io::Error;
    
    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        todo!()
    }
}

async fn forwarder(sock: impl AsyncRead + AsyncWrite + Send + 'static) {
    let sock = Codec.framed(sock);
    let (sink, stream) = sock.split();
    
    stream.map(|msg| msg).forward(sink).await;
}

pub fn assert_send() {
    let s : TcpStream = todo!();
    let _b : Box<dyn Send> = Box::new(forwarder(s));
}

I expected to see this happen: A diagnostic would be issued at the line with the .forward call (or not at all).

Instead, this happened:

error[E0308]: mismatched types
  --> src/lib.rs:44:30
   |
44 |     let _b : Box<dyn Send> = Box::new(forwarder(s));
   |                              ^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected type `std::ops::FnOnce<(std::result::Result<Message<'static>, std::io::Error>,)>`
              found type `std::ops::FnOnce<(std::result::Result<Message<'_>, std::io::Error>,)>`

As you can see, this is reporting a typecheck error occurring in the implementation of forwarder, at the point of use of the async fn (where its implementation should be opaque).

Meta

Tested on the playground with both stable and nightly.

@bdonlan bdonlan added the C-bug Category: This is a bug. label Jun 25, 2020
@jonas-schievink jonas-schievink added the A-async-await Area: Async & Await label Jun 25, 2020
@bdonlan
Copy link
Contributor Author

bdonlan commented Jun 25, 2020

Adding a coercion to force the stream type to use a Message<'static> fixes this issue, so apart from the diagnostic being issued in the wrong place I would expect the lifetime checker to accept this code:

use std::marker::PhantomData;
use std::io;

use tokio::io::{AsyncRead, AsyncWrite};
use tokio::net::TcpStream;
use tokio_util::codec::{Encoder, Decoder};
use futures::stream::{Stream, StreamExt};

use bytes::BytesMut;

struct Codec;

struct Message<'a> { _p: PhantomData<&'a u8> }

impl Encoder<Message<'static>> for Codec {
    type Error = io::Error;
    
    fn encode(&mut self, item: Message<'static>, dst: &mut BytesMut) -> Result<(), Self::Error> {
        todo!()
    }
}

impl Decoder for Codec {
    type Item = Message<'static>;
    type Error = io::Error;
    
    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        todo!()
    }
}

fn coerce(s: impl Stream<Item=io::Result<Message<'static>>>) -> impl Stream<Item=io::Result<Message<'static>>> { s }

async fn forwarder(sock: impl AsyncRead + AsyncWrite + Send + 'static) {
    let sock = Codec.framed(sock);
    let (sink, stream) = sock.split();
    
    coerce(stream.map(|msg| msg)).forward(sink).await;
}

#[allow(unreachable_code)]
pub fn assert_send() {
    let s : TcpStream = todo!();
    // The diagnostic emitted here should be reported in the body of forwarder itself.
    let _b : Box<dyn Send> = Box::new(forwarder(s));
}

@JohnTitor JohnTitor added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jun 26, 2020
@nikomatsakis
Copy link
Contributor

This is likely to be a duplicate of #64552

@nikomatsakis nikomatsakis added the AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. label Jun 30, 2020
@nikomatsakis
Copy link
Contributor

Marking as triaged and a likely duplicate of #64552

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-async-await Area: Async & Await AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants