diff --git a/futures-util/src/io/fill_buf.rs b/futures-util/src/io/fill_buf.rs index a1484c032..50938105a 100644 --- a/futures-util/src/io/fill_buf.rs +++ b/futures-util/src/io/fill_buf.rs @@ -30,6 +30,11 @@ where let reader = this.reader.take().expect("Polled FillBuf after completion"); match Pin::new(&mut *reader).poll_fill_buf(cx) { + // The reader can try to fill its buffer again if it returned no data, according to the + // AsyncBufRead contract. Don't poll again in that case, in order to make the + // Poll::Pending match arm below truly unreachable. + Poll::Ready(Ok([])) => Poll::Ready(Ok(&[])), + // With polonius it is possible to remove this inner match and just have the correct // lifetime of the reference inferred based on which branch is taken Poll::Ready(Ok(_)) => match Pin::new(reader).poll_fill_buf(cx) { diff --git a/futures/tests/io_buf_reader.rs b/futures/tests/io_buf_reader.rs index 717297cce..ecb5519a8 100644 --- a/futures/tests/io_buf_reader.rs +++ b/futures/tests/io_buf_reader.rs @@ -430,3 +430,16 @@ fn maybe_pending_seek() { Pin::new(&mut reader).consume(1); assert_eq!(run(reader.seek(SeekFrom::Current(-2))).ok(), Some(3)); } + +#[test] +fn fill_buf_pending_after_eof() { + let inner: &[u8] = &[1, 2]; + let mut reader = BufReader::with_capacity(inner.len(), MaybePending::new(inner)); + + let buf = run(reader.fill_buf()).unwrap(); + assert_eq!(buf, inner); + reader.consume_unpin(2); + + let buf = run(reader.fill_buf()).unwrap(); + assert_eq!(buf, []); +}