From 33a49993b440ab56cd425d8d4137d5fe17dcaae5 Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Tue, 31 Mar 2020 15:16:43 +0200 Subject: [PATCH 1/9] Add io::Write::write_all_vectored Similar to io::Write::write_all but uses io::Write::write_vectored instead. --- src/libstd/io/mod.rs | 162 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 1 deletion(-) diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 88d556229e4cb..a9ebbbf7e8921 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -261,6 +261,7 @@ use crate::cmp; use crate::fmt; +use crate::mem; use crate::memchr; use crate::ops::{Deref, DerefMut}; use crate::ptr; @@ -1376,6 +1377,67 @@ pub trait Write { Ok(()) } + /// Attempts to write an multiple buffers into this writer. + /// + /// This method will continuously call [`write_vectored`] until there is no + /// more data to be written or an error of non-[`ErrorKind::Interrupted`] + /// kind is returned. This method will not return until all buffers have + /// been successfully written or such an error occurs. The first error that + /// is not of [`ErrorKind::Interrupted`] kind generated from this method + /// will be returned. + /// + /// If the buffer contains no data, this will never call [`write_vectored`]. + /// + /// # Notes + /// + /// Different to `io::Write::write_vectored` this takes a *mutable* + /// reference to a slice of `IoSlice`s, not a non-mutable reference, because + /// we need to modify the slice to keep track of the bytes already written. + /// + /// Once this function returns the contents of `bufs` is undefined, not + /// undefined as in memory unsafe but we don't know what the contents of + /// `bufs` will be as that depends on how many writes we needed to do. We + /// advice to see this function as taking ownership of `bufs` and don't use + /// the variable after the future returns. The underlying buffers, to which + /// `IoSlice` points (not the `IoSlice` itself), are unchanged and can be + /// reused. + /// + /// # Examples + /// + /// ``` + /// #![feature(write_all_vectored)] + /// # fn main() -> std::io::Result<()> { + /// + /// use std::io::{Write, IoSlice}; + /// + /// let mut writer = Vec::new(); + /// let bufs = &mut [ + /// IoSlice::new(&[1]), + /// IoSlice::new(&[2, 3]), + /// IoSlice::new(&[4, 5, 6]), + /// ]; + /// + /// writer.write_all_vectored(bufs)?; + /// // Note: the contents of `bufs` is now undefined, see the Notes section. + /// + /// assert_eq!(writer, &[1, 2, 3, 4, 5, 6]); + /// # Ok(()) } + /// ``` + #[unstable(feature = "write_all_vectored", issue = "70436")] + fn write_all_vectored(&mut self, mut bufs: &mut [IoSlice<'_>]) -> Result<()> { + while !bufs.is_empty() { + match self.write_vectored(bufs) { + Ok(0) => { + return Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer")); + } + Ok(n) => bufs = IoSlice::advance(mem::replace(&mut bufs, &mut []), n), + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + /// Writes a formatted string into this writer, returning any error /// encountered. /// @@ -2423,7 +2485,7 @@ impl Iterator for Lines { #[cfg(test)] mod tests { use super::{repeat, Cursor, SeekFrom}; - use crate::cmp; + use crate::cmp::{self, min}; use crate::io::prelude::*; use crate::io::{self, IoSlice, IoSliceMut}; use crate::ops::Deref; @@ -2812,4 +2874,102 @@ mod tests { bufs = IoSlice::advance(bufs, 9); assert!(bufs.is_empty()); } + + /// Create a new writer that reads from at most `n_bufs` and reads + /// `per_call` bytes (in total) per call to write. + fn test_writer(n_bufs: usize, per_call: usize) -> TestWriter { + TestWriter { n_bufs, per_call, written: Vec::new() } + } + + struct TestWriter { + n_bufs: usize, + per_call: usize, + written: Vec, + } + + impl Write for TestWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.write_vectored(&[IoSlice::new(buf)]) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let mut left = self.per_call; + let mut written = 0; + for buf in bufs.iter().take(self.n_bufs) { + let n = min(left, buf.len()); + self.written.extend_from_slice(&buf[0..n]); + left -= n; + written += n; + } + Ok(written) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + + #[test] + fn test_writer_read_from_one_buf() { + let mut writer = test_writer(1, 2); + + assert_eq!(writer.write(&[]).unwrap(), 0); + assert_eq!(writer.write_vectored(&[]).unwrap(), 0); + + // Read at most 2 bytes. + assert_eq!(writer.write(&[1, 1, 1]).unwrap(), 2); + let bufs = &[IoSlice::new(&[2, 2, 2])]; + assert_eq!(writer.write_vectored(bufs).unwrap(), 2); + + // Only read from first buf. + let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4, 4])]; + assert_eq!(writer.write_vectored(bufs).unwrap(), 1); + + assert_eq!(writer.written, &[1, 1, 2, 2, 3]); + } + + #[test] + fn test_writer_read_from_multiple_bufs() { + let mut writer = test_writer(3, 3); + + // Read at most 3 bytes from two buffers. + let bufs = &[IoSlice::new(&[1]), IoSlice::new(&[2, 2, 2])]; + assert_eq!(writer.write_vectored(bufs).unwrap(), 3); + + // Read at most 3 bytes from three buffers. + let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4]), IoSlice::new(&[5, 5])]; + assert_eq!(writer.write_vectored(bufs).unwrap(), 3); + + assert_eq!(writer.written, &[1, 2, 2, 3, 4, 5]); + } + + #[test] + fn test_write_all_vectored() { + #[rustfmt::skip] // Becomes unreadable otherwise. + let tests: Vec<(_, &'static [u8])> = vec![ + (vec![], &[]), + (vec![IoSlice::new(&[1])], &[1]), + (vec![IoSlice::new(&[1, 2])], &[1, 2]), + (vec![IoSlice::new(&[1, 2, 3])], &[1, 2, 3]), + (vec![IoSlice::new(&[1, 2, 3, 4])], &[1, 2, 3, 4]), + (vec![IoSlice::new(&[1, 2, 3, 4, 5])], &[1, 2, 3, 4, 5]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2])], &[1, 2]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2])], &[1, 1, 2, 2]), + (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 1, 2, 2, 2]), + (vec![IoSlice::new(&[1, 1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 1, 2, 2, 2, 2]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2]), IoSlice::new(&[3])], &[1, 2, 3]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3])], &[1, 1, 2, 2, 3, 3]), + (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 1, 1, 2, 2, 2, 3, 3, 3]), + ]; + + let writer_configs = &[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]; + + for (n_bufs, per_call) in writer_configs.iter().copied() { + for (mut input, wanted) in tests.clone().into_iter() { + let mut writer = test_writer(n_bufs, per_call); + assert!(writer.write_all_vectored(&mut *input).is_ok()); + assert_eq!(&*writer.written, &*wanted); + } + } + } } From 9745b1fc52efb22f6be501db9282d8202e290066 Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Wed, 1 Apr 2020 15:39:05 +0200 Subject: [PATCH 2/9] Use unspecified over undefined in io::Write::write_all_vectored docs --- src/libstd/io/mod.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index a9ebbbf7e8921..68ce7e13e2687 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -1388,19 +1388,21 @@ pub trait Write { /// /// If the buffer contains no data, this will never call [`write_vectored`]. /// + /// [`write_vectored`]: #tymethod.write_vectored + /// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted + /// /// # Notes /// /// Different to `io::Write::write_vectored` this takes a *mutable* /// reference to a slice of `IoSlice`s, not a non-mutable reference, because /// we need to modify the slice to keep track of the bytes already written. /// - /// Once this function returns the contents of `bufs` is undefined, not - /// undefined as in memory unsafe but we don't know what the contents of - /// `bufs` will be as that depends on how many writes we needed to do. We - /// advice to see this function as taking ownership of `bufs` and don't use - /// the variable after the future returns. The underlying buffers, to which - /// `IoSlice` points (not the `IoSlice` itself), are unchanged and can be - /// reused. + /// Once this function returns the contents of `bufs` is unspecified, as we + /// don't know what the contents of `bufs` will be as that depends on how + /// many writes we needed to do. We advice to see this function as taking + /// ownership of `bufs` and don't use the variable after the future returns. + /// The underlying buffers, to which `IoSlice` points (not the `IoSlice` + /// itself), are unchanged and can be reused. /// /// # Examples /// From ba91e7e374b3becd24ea133e0858dca201b794cc Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Wed, 1 Apr 2020 19:41:57 +0200 Subject: [PATCH 3/9] Fix link to write_vectored --- src/libstd/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 68ce7e13e2687..0d15db81c33ff 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -1388,7 +1388,7 @@ pub trait Write { /// /// If the buffer contains no data, this will never call [`write_vectored`]. /// - /// [`write_vectored`]: #tymethod.write_vectored + /// [`write_vectored`]: #method.write_vectored /// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted /// /// # Notes From 2275b875c2a2bdca40cfad44e6f9c4b0a99c4f05 Mon Sep 17 00:00:00 2001 From: Roland Kuhn Date: Sun, 5 Apr 2020 19:25:32 +0200 Subject: [PATCH 4/9] expose suggestions::InferCtxtExt for clippy --- .../traits/error_reporting/suggestions.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs index fcec29aaa8ecb..14c87b001ab3d 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -22,7 +22,8 @@ use std::fmt; use super::InferCtxtPrivExt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -crate trait InferCtxtExt<'tcx> { +// public for clippy +pub trait InferCtxtExt<'tcx> { fn suggest_restricting_param_bound( &self, err: &mut DiagnosticBuilder<'_>, From d2b75586fcebefd0fa344e01a5a05ddf814296a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Mon, 6 Apr 2020 00:00:00 +0000 Subject: [PATCH 5/9] Move sanitize-inline-always test to sanitize directory --- .../{sanitize-inline-always.rs => sanitize/inline-always.rs} | 0 .../inline-always.stderr} | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/test/ui/{sanitize-inline-always.rs => sanitize/inline-always.rs} (100%) rename src/test/ui/{sanitize-inline-always.stderr => sanitize/inline-always.stderr} (76%) diff --git a/src/test/ui/sanitize-inline-always.rs b/src/test/ui/sanitize/inline-always.rs similarity index 100% rename from src/test/ui/sanitize-inline-always.rs rename to src/test/ui/sanitize/inline-always.rs diff --git a/src/test/ui/sanitize-inline-always.stderr b/src/test/ui/sanitize/inline-always.stderr similarity index 76% rename from src/test/ui/sanitize-inline-always.stderr rename to src/test/ui/sanitize/inline-always.stderr index 50b9474baa2d6..84c05af4cf83a 100644 --- a/src/test/ui/sanitize-inline-always.stderr +++ b/src/test/ui/sanitize/inline-always.stderr @@ -1,12 +1,12 @@ warning: `no_sanitize` will have no effect after inlining - --> $DIR/sanitize-inline-always.rs:7:1 + --> $DIR/inline-always.rs:7:1 | LL | #[no_sanitize(address)] | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(inline_no_sanitize)]` on by default note: inlining requested here - --> $DIR/sanitize-inline-always.rs:5:1 + --> $DIR/inline-always.rs:5:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ From 63118d1fd73c73eecfefdf723f580fd537ec6102 Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Mon, 6 Apr 2020 11:21:15 +0200 Subject: [PATCH 6/9] Improve io::Write::write_all_vectored docs Also adds some more tests with different length IoSlices. --- src/libstd/io/mod.rs | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 0d15db81c33ff..6a60f1e087cf2 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -1377,7 +1377,7 @@ pub trait Write { Ok(()) } - /// Attempts to write an multiple buffers into this writer. + /// Attempts to write multiple buffers into this writer. /// /// This method will continuously call [`write_vectored`] until there is no /// more data to be written or an error of non-[`ErrorKind::Interrupted`] @@ -1393,16 +1393,17 @@ pub trait Write { /// /// # Notes /// - /// Different to `io::Write::write_vectored` this takes a *mutable* - /// reference to a slice of `IoSlice`s, not a non-mutable reference, because - /// we need to modify the slice to keep track of the bytes already written. /// - /// Once this function returns the contents of `bufs` is unspecified, as we - /// don't know what the contents of `bufs` will be as that depends on how - /// many writes we needed to do. We advice to see this function as taking - /// ownership of `bufs` and don't use the variable after the future returns. - /// The underlying buffers, to which `IoSlice` points (not the `IoSlice` - /// itself), are unchanged and can be reused. + /// Unlike `io::Write::write_vectored`, this takes a *mutable* reference to + /// a slice of `IoSlice`s, not an immutable one. That's because we need to + /// modify the slice to keep track of the bytes already written. + /// + /// Once this function returns, the contents of `bufs` are unspecified, as + /// this depends on how many calls to write_vectored were necessary. It is + /// best to understand this function as taking ownership of `bufs` and to + /// not use `bufs` afterwards. The underlying buffers, to which the + /// `IoSlice`s point (but not the `IoSlice`s themselves), are unchanged and + /// can be reused. /// /// # Examples /// @@ -1432,7 +1433,7 @@ pub trait Write { Ok(0) => { return Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer")); } - Ok(n) => bufs = IoSlice::advance(mem::replace(&mut bufs, &mut []), n), + Ok(n) => bufs = IoSlice::advance(mem::take(&mut bufs), n), Err(ref e) if e.kind() == ErrorKind::Interrupted => {} Err(e) => return Err(e), } @@ -2956,11 +2957,16 @@ mod tests { (vec![IoSlice::new(&[1, 2, 3, 4])], &[1, 2, 3, 4]), (vec![IoSlice::new(&[1, 2, 3, 4, 5])], &[1, 2, 3, 4, 5]), (vec![IoSlice::new(&[1]), IoSlice::new(&[2])], &[1, 2]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2])], &[1, 2, 2]), (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2])], &[1, 1, 2, 2]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]), (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 1, 2, 2, 2]), + (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 2, 2, 2, 2]), (vec![IoSlice::new(&[1, 1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 1, 2, 2, 2, 2]), (vec![IoSlice::new(&[1]), IoSlice::new(&[2]), IoSlice::new(&[3])], &[1, 2, 3]), (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3])], &[1, 1, 2, 2, 3, 3]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 2, 2, 3, 3, 3]), (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 1, 1, 2, 2, 2, 3, 3, 3]), ]; From 57a6b7bd221b6d2027a0960f3f46a6a9355d0ac4 Mon Sep 17 00:00:00 2001 From: Roland Kuhn Date: Mon, 6 Apr 2020 17:04:21 +0200 Subject: [PATCH 7/9] Update src/librustc_trait_selection/traits/error_reporting/suggestions.rs Co-Authored-By: varkor --- .../traits/error_reporting/suggestions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs index 14c87b001ab3d..a8fc56d8509a0 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -22,7 +22,7 @@ use std::fmt; use super::InferCtxtPrivExt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -// public for clippy +// This trait is public to expose the diagnostics methods to clippy. pub trait InferCtxtExt<'tcx> { fn suggest_restricting_param_bound( &self, From 84c97ee9a0d445f48982f5e9cd5e2b3e5c266ee5 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 2 Apr 2020 13:30:19 +0200 Subject: [PATCH 8/9] Clean up E0501 explanation --- src/librustc_error_codes/error_codes/E0501.md | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/librustc_error_codes/error_codes/E0501.md b/src/librustc_error_codes/error_codes/E0501.md index f5aa17a809467..ffdbc443905ae 100644 --- a/src/librustc_error_codes/error_codes/E0501.md +++ b/src/librustc_error_codes/error_codes/E0501.md @@ -1,12 +1,4 @@ -This error indicates that a mutable variable is being used while it is still -captured by a closure. Because the closure has borrowed the variable, it is not -available for use until the closure goes out of scope. - -Note that a capture will either move or borrow a variable, but in this -situation, the closure is borrowing the variable. Take a look at the chapter -on [Capturing][capturing] in Rust By Example for more information. - -[capturing]: https://doc.rust-lang.org/stable/rust-by-example/fn/closures/capture.html +A mutable variable is used but it is already captured by a closure. Erroneous code example: @@ -29,6 +21,16 @@ fn foo(a: &mut i32) { } ``` +This error indicates that a mutable variable is used while it is still captured +by a closure. Because the closure has borrowed the variable, it is not available +until the closure goes out of scope. + +Note that a capture will either move or borrow a variable, but in this +situation, the closure is borrowing the variable. Take a look at the chapter +on [Capturing][capturing] in Rust By Example for more information. + +[capturing]: https://doc.rust-lang.org/stable/rust-by-example/fn/closures/capture.html + To fix this error, you can finish using the closure before using the captured variable: From 5d09f9f0512a6b6c0082314897198a2cae52be70 Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Tue, 7 Apr 2020 00:00:07 +0200 Subject: [PATCH 9/9] Put reference to write_vectored in quotes in doc --- src/libstd/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 6a60f1e087cf2..5ab88260d6ac1 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -1399,7 +1399,7 @@ pub trait Write { /// modify the slice to keep track of the bytes already written. /// /// Once this function returns, the contents of `bufs` are unspecified, as - /// this depends on how many calls to write_vectored were necessary. It is + /// this depends on how many calls to `write_vectored` were necessary. It is /// best to understand this function as taking ownership of `bufs` and to /// not use `bufs` afterwards. The underlying buffers, to which the /// `IoSlice`s point (but not the `IoSlice`s themselves), are unchanged and