From 1f32d40ac3c51d6867fc6969e52e960452a40652 Mon Sep 17 00:00:00 2001 From: Tyson Nottingham Date: Thu, 10 Dec 2020 19:46:54 -0800 Subject: [PATCH 01/29] BufWriter: apply #[inline] / #[inline(never)] optimizations Ensure that `write` and `write_all` can be inlined and that their commonly executed fast paths can be as short as possible. `write_vectored` would likely benefit from the same optimization, but I omitted it because its implementation is more complex, and I don't have a benchmark on hand to guide its optimization. --- library/std/src/io/buffered/bufwriter.rs | 90 +++++++++++++++++------- 1 file changed, 66 insertions(+), 24 deletions(-) diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 80f98bbbad366..ae72ddabfb9e4 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -331,6 +331,52 @@ impl BufWriter { let buf = if !self.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) }; (self.inner.take().unwrap(), buf) } + + // Ensure this function does not get inlined into `write`, so that it + // remains inlineable and its common path remains as short as possible. + // If this function ends up being called frequently relative to `write`, + // it's likely a sign that the client is using an improperly sized buffer. + #[inline(never)] + fn flush_and_write(&mut self, buf: &[u8]) -> io::Result { + self.flush_buf()?; + + // Why not len > capacity? To avoid a needless trip through the buffer when the + // input exactly fills. We'd just need to flush it to the underlying writer anyway. + if buf.len() >= self.buf.capacity() { + self.panicked = true; + let r = self.get_mut().write(buf); + self.panicked = false; + r + } else { + self.buf.extend_from_slice(buf); + Ok(buf.len()) + } + } + + // Ensure this function does not get inlined into `write_all`, so that it + // remains inlineable and its common path remains as short as possible. + // If this function ends up being called frequently relative to `write_all`, + // it's likely a sign that the client is using an improperly sized buffer. + #[inline(never)] + fn flush_and_write_all(&mut self, buf: &[u8]) -> io::Result<()> { + // Normally, `write_all` just calls `write` in a loop. We can do better + // by calling `self.get_mut().write_all()` directly, which avoids + // round trips through the buffer in the event of a series of partial + // writes in some circumstances. + self.flush_buf()?; + + // Why not len > capacity? To avoid a needless trip through the buffer when the + // input exactly fills. We'd just need to flush it to the underlying writer anyway. + if buf.len() >= self.buf.capacity() { + self.panicked = true; + let r = self.get_mut().write_all(buf); + self.panicked = false; + r + } else { + self.buf.extend_from_slice(buf); + Ok(()) + } + } } #[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")] @@ -402,43 +448,39 @@ impl fmt::Debug for WriterPanicked { #[stable(feature = "rust1", since = "1.0.0")] impl Write for BufWriter { + #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { - if self.buf.len() + buf.len() > self.buf.capacity() { - self.flush_buf()?; - } - // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919 - if buf.len() >= self.buf.capacity() { - self.panicked = true; - let r = self.get_mut().write(buf); - self.panicked = false; - r - } else { + // The `buf.len() != self.buf.capacity()` check is done to avoid a needless trip through + // the buffer when the input is exactly the same size as it. For many clients, that is a + // rare event, so it's unfortunate that the check is in the common code path. But it + // prevents pathological cases for other clients which *always* make writes of this size. + // See #72919 and #79930 for more info and a breadcrumb trail. + if self.buf.len() + buf.len() <= self.buf.capacity() && buf.len() != self.buf.capacity() { self.buf.extend_from_slice(buf); Ok(buf.len()) + } else { + self.flush_and_write(buf) } } + #[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - // Normally, `write_all` just calls `write` in a loop. We can do better - // by calling `self.get_mut().write_all()` directly, which avoids - // round trips through the buffer in the event of a series of partial - // writes in some circumstances. - if self.buf.len() + buf.len() > self.buf.capacity() { - self.flush_buf()?; - } - // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919 - if buf.len() >= self.buf.capacity() { - self.panicked = true; - let r = self.get_mut().write_all(buf); - self.panicked = false; - r - } else { + // The `buf.len() != self.buf.capacity()` check is done to avoid a needless trip through + // the buffer when the input is exactly the same size as it. For many clients, that is a + // rare event, so it's unfortunate that the check is in the common code path. But it + // prevents pathological cases for other clients which *always* make writes of this size. + // See #72919 and #79930 for more info and a breadcrumb trail. + if self.buf.len() + buf.len() <= self.buf.capacity() && buf.len() != self.buf.capacity() { self.buf.extend_from_slice(buf); Ok(()) + } else { + self.flush_and_write_all(buf) } } fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + // FIXME: Consider applying `#[inline]` / `#[inline(never)]` optimizations already applied + // to `write` and `write_all`. The performance benefits can be significant. See #79930. if self.get_ref().is_write_vectored() { let total_len = bufs.iter().map(|b| b.len()).sum::(); if self.buf.len() + total_len > self.buf.capacity() { From b43e8e248bfba9e8d26821c6d98deba94d024abf Mon Sep 17 00:00:00 2001 From: Tyson Nottingham Date: Thu, 10 Dec 2020 21:34:28 -0800 Subject: [PATCH 02/29] BufWriter: avoid using expensive Vec methods We use a Vec as our internal, constant-sized buffer, but the overhead of using methods like `extend_from_slice` can be enormous, likely because they don't get inlined, because `Vec` has to repeat bounds checks that we've already done, and because it makes considerations for things like reallocating, even though they should never happen. --- library/std/src/io/buffered/bufwriter.rs | 87 ++++++++++++++++++++---- 1 file changed, 75 insertions(+), 12 deletions(-) diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index ae72ddabfb9e4..3272826d77c25 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -4,6 +4,7 @@ use crate::io::{ self, Error, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE, }; use crate::mem; +use crate::ptr; /// Wraps a writer and buffers its output. /// @@ -68,6 +69,10 @@ use crate::mem; #[stable(feature = "rust1", since = "1.0.0")] pub struct BufWriter { inner: Option, + // The buffer. Avoid using this like a normal `Vec` in common code paths. + // That is, don't use `buf.push`, `buf.extend_from_slice`, or any other + // methods that require bounds checking or the like. This makes an enormous + // difference to performance (we may want to stop using a `Vec` entirely). buf: Vec, // #30888: If the inner writer panics in a call to write, we don't want to // write the buffered data a second time in BufWriter's destructor. This @@ -150,7 +155,11 @@ impl BufWriter { impl Drop for BufGuard<'_> { fn drop(&mut self) { if self.written > 0 { - self.buffer.drain(..self.written); + if self.done() { + self.buffer.clear(); + } else { + self.buffer.drain(..self.written); + } } } } @@ -183,7 +192,12 @@ impl BufWriter { pub(super) fn write_to_buf(&mut self, buf: &[u8]) -> usize { let available = self.buf.capacity() - self.buf.len(); let amt_to_buffer = available.min(buf.len()); - self.buf.extend_from_slice(&buf[..amt_to_buffer]); + + // SAFETY: `amt_to_buffer` is <= buffer's spare capacity by construction. + unsafe { + self.write_to_buffer_unchecked(&buf[..amt_to_buffer]); + } + amt_to_buffer } @@ -348,7 +362,13 @@ impl BufWriter { self.panicked = false; r } else { - self.buf.extend_from_slice(buf); + // SAFETY: We just called `self.flush_buf()`, so `self.buf.len()` is 0, and + // we entered this else block because `buf.len() < self.buf.capacity()`. + // Therefore, `self.buf.len() + buf.len() <= self.buf.capacity()`. + unsafe { + self.write_to_buffer_unchecked(buf); + } + Ok(buf.len()) } } @@ -373,10 +393,29 @@ impl BufWriter { self.panicked = false; r } else { - self.buf.extend_from_slice(buf); + // SAFETY: We just called `self.flush_buf()`, so `self.buf.len()` is 0, and + // we entered this else block because `buf.len() < self.buf.capacity()`. + // Therefore, `self.buf.len() + buf.len() <= self.buf.capacity()`. + unsafe { + self.write_to_buffer_unchecked(buf); + } + Ok(()) } } + + // SAFETY: Requires `self.buf.len() + buf.len() <= self.buf.capacity()`, + // i.e., that input buffer length is less than or equal to spare capacity. + #[inline(always)] + unsafe fn write_to_buffer_unchecked(&mut self, buf: &[u8]) { + debug_assert!(self.buf.len() + buf.len() <= self.buf.capacity()); + let old_len = self.buf.len(); + let buf_len = buf.len(); + let src = buf.as_ptr(); + let dst = self.buf.as_mut_ptr().add(old_len); + ptr::copy_nonoverlapping(src, dst, buf_len); + self.buf.set_len(old_len + buf_len); + } } #[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")] @@ -456,7 +495,11 @@ impl Write for BufWriter { // prevents pathological cases for other clients which *always* make writes of this size. // See #72919 and #79930 for more info and a breadcrumb trail. if self.buf.len() + buf.len() <= self.buf.capacity() && buf.len() != self.buf.capacity() { - self.buf.extend_from_slice(buf); + // SAFETY: safe by above conditional. + unsafe { + self.write_to_buffer_unchecked(buf); + } + Ok(buf.len()) } else { self.flush_and_write(buf) @@ -471,7 +514,11 @@ impl Write for BufWriter { // prevents pathological cases for other clients which *always* make writes of this size. // See #72919 and #79930 for more info and a breadcrumb trail. if self.buf.len() + buf.len() <= self.buf.capacity() && buf.len() != self.buf.capacity() { - self.buf.extend_from_slice(buf); + // SAFETY: safe by above conditional. + unsafe { + self.write_to_buffer_unchecked(buf); + } + Ok(()) } else { self.flush_and_write_all(buf) @@ -492,7 +539,13 @@ impl Write for BufWriter { self.panicked = false; r } else { - bufs.iter().for_each(|b| self.buf.extend_from_slice(b)); + // SAFETY: We checked whether or not the spare capacity was large enough above. If + // it was, then we're safe already. If it wasn't, we flushed, making sufficient + // room for any input <= the buffer size, which includes this input. + unsafe { + bufs.iter().for_each(|b| self.write_to_buffer_unchecked(b)); + }; + Ok(total_len) } } else { @@ -511,7 +564,13 @@ impl Write for BufWriter { self.panicked = false; return r; } else { - self.buf.extend_from_slice(buf); + // SAFETY: We checked whether or not the spare capacity was large enough above. + // If it was, then we're safe already. If it wasn't, we flushed, making + // sufficient room for any input <= the buffer size, which includes this input. + unsafe { + self.write_to_buffer_unchecked(buf); + } + buf.len() } } else { @@ -519,11 +578,15 @@ impl Write for BufWriter { }; debug_assert!(total_written != 0); for buf in iter { - if self.buf.len() + buf.len() > self.buf.capacity() { - break; - } else { - self.buf.extend_from_slice(buf); + if self.buf.len() + buf.len() <= self.buf.capacity() { + // SAFETY: safe by above conditional. + unsafe { + self.write_to_buffer_unchecked(buf); + } + total_written += buf.len(); + } else { + break; } } Ok(total_written) From 5fd9372c1118b213b15a0e75bec7fab3b2e00260 Mon Sep 17 00:00:00 2001 From: Tyson Nottingham Date: Sat, 12 Dec 2020 23:17:05 -0800 Subject: [PATCH 03/29] BufWriter: optimize for write sizes less than buffer size Optimize for the common case where the input write size is less than the buffer size. This slightly increases the cost for pathological write patterns that commonly fill the buffer exactly, but if a client is doing that frequently, they're already paying the cost of frequent flushing, etc., so the cost is of this optimization to them is relatively small. --- library/std/src/io/buffered/bufwriter.rs | 56 ++++++++++++++---------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 3272826d77c25..f0ff186d99b11 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -349,19 +349,26 @@ impl BufWriter { // Ensure this function does not get inlined into `write`, so that it // remains inlineable and its common path remains as short as possible. // If this function ends up being called frequently relative to `write`, - // it's likely a sign that the client is using an improperly sized buffer. + // it's likely a sign that the client is using an improperly sized buffer + // or their write patterns are somewhat pathological. #[inline(never)] - fn flush_and_write(&mut self, buf: &[u8]) -> io::Result { - self.flush_buf()?; + fn write_cold(&mut self, buf: &[u8]) -> io::Result { + if self.buf.len() + buf.len() > self.buf.capacity() { + self.flush_buf()?; + } - // Why not len > capacity? To avoid a needless trip through the buffer when the - // input exactly fills. We'd just need to flush it to the underlying writer anyway. + // Why not len > capacity? To avoid a needless trip through the buffer when the input + // exactly fills it. We'd just need to flush it to the underlying writer anyway. if buf.len() >= self.buf.capacity() { self.panicked = true; let r = self.get_mut().write(buf); self.panicked = false; r } else { + // Write to the buffer. In this case, we write to the buffer even if it fills it + // exactly. Doing otherwise would mean flushing the buffer, then writing this + // input to the inner writer, which in many cases would be a worse strategy. + // SAFETY: We just called `self.flush_buf()`, so `self.buf.len()` is 0, and // we entered this else block because `buf.len() < self.buf.capacity()`. // Therefore, `self.buf.len() + buf.len() <= self.buf.capacity()`. @@ -376,23 +383,30 @@ impl BufWriter { // Ensure this function does not get inlined into `write_all`, so that it // remains inlineable and its common path remains as short as possible. // If this function ends up being called frequently relative to `write_all`, - // it's likely a sign that the client is using an improperly sized buffer. + // it's likely a sign that the client is using an improperly sized buffer + // or their write patterns are somewhat pathological. #[inline(never)] - fn flush_and_write_all(&mut self, buf: &[u8]) -> io::Result<()> { + fn write_all_cold(&mut self, buf: &[u8]) -> io::Result<()> { // Normally, `write_all` just calls `write` in a loop. We can do better // by calling `self.get_mut().write_all()` directly, which avoids // round trips through the buffer in the event of a series of partial // writes in some circumstances. - self.flush_buf()?; + if self.buf.len() + buf.len() > self.buf.capacity() { + self.flush_buf()?; + } - // Why not len > capacity? To avoid a needless trip through the buffer when the - // input exactly fills. We'd just need to flush it to the underlying writer anyway. + // Why not len > capacity? To avoid a needless trip through the buffer when the input + // exactly fills it. We'd just need to flush it to the underlying writer anyway. if buf.len() >= self.buf.capacity() { self.panicked = true; let r = self.get_mut().write_all(buf); self.panicked = false; r } else { + // Write to the buffer. In this case, we write to the buffer even if it fills it + // exactly. Doing otherwise would mean flushing the buffer, then writing this + // input to the inner writer, which in many cases would be a worse strategy. + // SAFETY: We just called `self.flush_buf()`, so `self.buf.len()` is 0, and // we entered this else block because `buf.len() < self.buf.capacity()`. // Therefore, `self.buf.len() + buf.len() <= self.buf.capacity()`. @@ -489,12 +503,9 @@ impl fmt::Debug for WriterPanicked { impl Write for BufWriter { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { - // The `buf.len() != self.buf.capacity()` check is done to avoid a needless trip through - // the buffer when the input is exactly the same size as it. For many clients, that is a - // rare event, so it's unfortunate that the check is in the common code path. But it - // prevents pathological cases for other clients which *always* make writes of this size. - // See #72919 and #79930 for more info and a breadcrumb trail. - if self.buf.len() + buf.len() <= self.buf.capacity() && buf.len() != self.buf.capacity() { + // Use < instead of <= to avoid a needless trip through the buffer in some cases. + // See `write_cold` for details. + if self.buf.len() + buf.len() < self.buf.capacity() { // SAFETY: safe by above conditional. unsafe { self.write_to_buffer_unchecked(buf); @@ -502,18 +513,15 @@ impl Write for BufWriter { Ok(buf.len()) } else { - self.flush_and_write(buf) + self.write_cold(buf) } } #[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - // The `buf.len() != self.buf.capacity()` check is done to avoid a needless trip through - // the buffer when the input is exactly the same size as it. For many clients, that is a - // rare event, so it's unfortunate that the check is in the common code path. But it - // prevents pathological cases for other clients which *always* make writes of this size. - // See #72919 and #79930 for more info and a breadcrumb trail. - if self.buf.len() + buf.len() <= self.buf.capacity() && buf.len() != self.buf.capacity() { + // Use < instead of <= to avoid a needless trip through the buffer in some cases. + // See `write_all_cold` for details. + if self.buf.len() + buf.len() < self.buf.capacity() { // SAFETY: safe by above conditional. unsafe { self.write_to_buffer_unchecked(buf); @@ -521,7 +529,7 @@ impl Write for BufWriter { Ok(()) } else { - self.flush_and_write_all(buf) + self.write_all_cold(buf) } } From 72aecbfd01e73da710ee35826f28f41d5d0cfebe Mon Sep 17 00:00:00 2001 From: Tyson Nottingham Date: Tue, 15 Dec 2020 14:18:02 -0800 Subject: [PATCH 04/29] BufWriter: handle possibility of overflow --- library/std/src/io/buffered/bufwriter.rs | 54 +++++++++++++++++------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index f0ff186d99b11..a9fc450de3182 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -190,7 +190,7 @@ impl BufWriter { /// data. Writes as much as possible without exceeding capacity. Returns /// the number of bytes written. pub(super) fn write_to_buf(&mut self, buf: &[u8]) -> usize { - let available = self.buf.capacity() - self.buf.len(); + let available = self.spare_capacity(); let amt_to_buffer = available.min(buf.len()); // SAFETY: `amt_to_buffer` is <= buffer's spare capacity by construction. @@ -353,7 +353,7 @@ impl BufWriter { // or their write patterns are somewhat pathological. #[inline(never)] fn write_cold(&mut self, buf: &[u8]) -> io::Result { - if self.buf.len() + buf.len() > self.buf.capacity() { + if buf.len() > self.spare_capacity() { self.flush_buf()?; } @@ -371,7 +371,7 @@ impl BufWriter { // SAFETY: We just called `self.flush_buf()`, so `self.buf.len()` is 0, and // we entered this else block because `buf.len() < self.buf.capacity()`. - // Therefore, `self.buf.len() + buf.len() <= self.buf.capacity()`. + // Therefore, `buf.len() <= self.buf.capacity() - self.buf.len()`. unsafe { self.write_to_buffer_unchecked(buf); } @@ -391,7 +391,8 @@ impl BufWriter { // by calling `self.get_mut().write_all()` directly, which avoids // round trips through the buffer in the event of a series of partial // writes in some circumstances. - if self.buf.len() + buf.len() > self.buf.capacity() { + + if buf.len() > self.spare_capacity() { self.flush_buf()?; } @@ -409,7 +410,7 @@ impl BufWriter { // SAFETY: We just called `self.flush_buf()`, so `self.buf.len()` is 0, and // we entered this else block because `buf.len() < self.buf.capacity()`. - // Therefore, `self.buf.len() + buf.len() <= self.buf.capacity()`. + // Therefore, `buf.len() <= self.buf.capacity() - self.buf.len()`. unsafe { self.write_to_buffer_unchecked(buf); } @@ -418,11 +419,11 @@ impl BufWriter { } } - // SAFETY: Requires `self.buf.len() + buf.len() <= self.buf.capacity()`, + // SAFETY: Requires `buf.len() <= self.buf.capacity() - self.buf.len()`, // i.e., that input buffer length is less than or equal to spare capacity. #[inline(always)] unsafe fn write_to_buffer_unchecked(&mut self, buf: &[u8]) { - debug_assert!(self.buf.len() + buf.len() <= self.buf.capacity()); + debug_assert!(buf.len() <= self.spare_capacity()); let old_len = self.buf.len(); let buf_len = buf.len(); let src = buf.as_ptr(); @@ -430,6 +431,11 @@ impl BufWriter { ptr::copy_nonoverlapping(src, dst, buf_len); self.buf.set_len(old_len + buf_len); } + + #[inline] + fn spare_capacity(&self) -> usize { + self.buf.capacity() - self.buf.len() + } } #[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")] @@ -505,7 +511,7 @@ impl Write for BufWriter { fn write(&mut self, buf: &[u8]) -> io::Result { // Use < instead of <= to avoid a needless trip through the buffer in some cases. // See `write_cold` for details. - if self.buf.len() + buf.len() < self.buf.capacity() { + if buf.len() < self.spare_capacity() { // SAFETY: safe by above conditional. unsafe { self.write_to_buffer_unchecked(buf); @@ -521,7 +527,7 @@ impl Write for BufWriter { fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { // Use < instead of <= to avoid a needless trip through the buffer in some cases. // See `write_all_cold` for details. - if self.buf.len() + buf.len() < self.buf.capacity() { + if buf.len() < self.spare_capacity() { // SAFETY: safe by above conditional. unsafe { self.write_to_buffer_unchecked(buf); @@ -537,16 +543,31 @@ impl Write for BufWriter { // FIXME: Consider applying `#[inline]` / `#[inline(never)]` optimizations already applied // to `write` and `write_all`. The performance benefits can be significant. See #79930. if self.get_ref().is_write_vectored() { - let total_len = bufs.iter().map(|b| b.len()).sum::(); - if self.buf.len() + total_len > self.buf.capacity() { + // We have to handle the possibility that the total length of the buffers overflows + // `usize` (even though this can only happen if multiple `IoSlice`s reference the + // same underlying buffer, as otherwise the buffers wouldn't fit in memory). If the + // computation overflows, then surely the input cannot fit in our buffer, so we forward + // to the inner writer's `write_vectored` method to let it handle it appropriately. + let saturated_total_len = + bufs.iter().fold(0usize, |acc, b| acc.saturating_add(b.len())); + + if saturated_total_len > self.spare_capacity() { + // Flush if the total length of the input exceeds our buffer's spare capacity. + // If we would have overflowed, this condition also holds, and we need to flush. self.flush_buf()?; } - if total_len >= self.buf.capacity() { + + if saturated_total_len >= self.buf.capacity() { + // Forward to our inner writer if the total length of the input is greater than or + // equal to our buffer capacity. If we would have overflowed, this condition also + // holds, and we punt to the inner writer. self.panicked = true; let r = self.get_mut().write_vectored(bufs); self.panicked = false; r } else { + // `saturated_total_len < self.buf.capacity()` implies that we did not saturate. + // SAFETY: We checked whether or not the spare capacity was large enough above. If // it was, then we're safe already. If it wasn't, we flushed, making sufficient // room for any input <= the buffer size, which includes this input. @@ -554,14 +575,14 @@ impl Write for BufWriter { bufs.iter().for_each(|b| self.write_to_buffer_unchecked(b)); }; - Ok(total_len) + Ok(saturated_total_len) } } else { let mut iter = bufs.iter(); let mut total_written = if let Some(buf) = iter.by_ref().find(|&buf| !buf.is_empty()) { // This is the first non-empty slice to write, so if it does // not fit in the buffer, we still get to flush and proceed. - if self.buf.len() + buf.len() > self.buf.capacity() { + if buf.len() > self.spare_capacity() { self.flush_buf()?; } if buf.len() >= self.buf.capacity() { @@ -586,12 +607,15 @@ impl Write for BufWriter { }; debug_assert!(total_written != 0); for buf in iter { - if self.buf.len() + buf.len() <= self.buf.capacity() { + if buf.len() <= self.spare_capacity() { // SAFETY: safe by above conditional. unsafe { self.write_to_buffer_unchecked(buf); } + // This cannot overflow `usize`. If we are here, we've written all of the bytes + // so far to our buffer, and we've ensured that we never exceed the buffer's + // capacity. Therefore, `total_written` <= `self.buf.capacity()` <= `usize::MAX`. total_written += buf.len(); } else { break; From 85bc88df5f97d31cf6297865dcd6092ba12b740b Mon Sep 17 00:00:00 2001 From: Tyson Nottingham Date: Mon, 4 Jan 2021 17:53:23 -0800 Subject: [PATCH 05/29] BufWriter: use #[cold] and less aggressive #[inline] hints --- library/std/src/io/buffered/bufwriter.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index a9fc450de3182..9dbf0e7454d7f 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -351,6 +351,7 @@ impl BufWriter { // If this function ends up being called frequently relative to `write`, // it's likely a sign that the client is using an improperly sized buffer // or their write patterns are somewhat pathological. + #[cold] #[inline(never)] fn write_cold(&mut self, buf: &[u8]) -> io::Result { if buf.len() > self.spare_capacity() { @@ -385,6 +386,7 @@ impl BufWriter { // If this function ends up being called frequently relative to `write_all`, // it's likely a sign that the client is using an improperly sized buffer // or their write patterns are somewhat pathological. + #[cold] #[inline(never)] fn write_all_cold(&mut self, buf: &[u8]) -> io::Result<()> { // Normally, `write_all` just calls `write` in a loop. We can do better @@ -421,7 +423,7 @@ impl BufWriter { // SAFETY: Requires `buf.len() <= self.buf.capacity() - self.buf.len()`, // i.e., that input buffer length is less than or equal to spare capacity. - #[inline(always)] + #[inline] unsafe fn write_to_buffer_unchecked(&mut self, buf: &[u8]) { debug_assert!(buf.len() <= self.spare_capacity()); let old_len = self.buf.len(); From 0f29dc40f837b0a491d183ef05dd00eb78b6be1d Mon Sep 17 00:00:00 2001 From: Tyson Nottingham Date: Tue, 13 Apr 2021 10:09:37 -0700 Subject: [PATCH 06/29] BufWriter: simplify buffer draining --- library/std/src/io/buffered/bufwriter.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 9dbf0e7454d7f..5c5f4467ef950 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -155,11 +155,7 @@ impl BufWriter { impl Drop for BufGuard<'_> { fn drop(&mut self) { if self.written > 0 { - if self.done() { - self.buffer.clear(); - } else { - self.buffer.drain(..self.written); - } + self.buffer.drain(..self.written); } } } From 01e701828c6010873def5d79191319a6b37a6b32 Mon Sep 17 00:00:00 2001 From: Tyson Nottingham Date: Tue, 13 Apr 2021 21:09:41 -0700 Subject: [PATCH 07/29] BufWriter: improve safety comment --- library/std/src/io/buffered/bufwriter.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 5c5f4467ef950..ef2769d431fbb 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -366,9 +366,11 @@ impl BufWriter { // exactly. Doing otherwise would mean flushing the buffer, then writing this // input to the inner writer, which in many cases would be a worse strategy. - // SAFETY: We just called `self.flush_buf()`, so `self.buf.len()` is 0, and - // we entered this else block because `buf.len() < self.buf.capacity()`. - // Therefore, `buf.len() <= self.buf.capacity() - self.buf.len()`. + // SAFETY: There was either enough spare capacity already, or there wasn't and we + // flushed the buffer to ensure that there is. In the latter case, we know that there + // is because flushing ensured that our entire buffer is spare capacity, and we entered + // this block because the input buffer length is less than that capacity. In either + // case, it's safe to write the input buffer to our buffer. unsafe { self.write_to_buffer_unchecked(buf); } @@ -406,9 +408,11 @@ impl BufWriter { // exactly. Doing otherwise would mean flushing the buffer, then writing this // input to the inner writer, which in many cases would be a worse strategy. - // SAFETY: We just called `self.flush_buf()`, so `self.buf.len()` is 0, and - // we entered this else block because `buf.len() < self.buf.capacity()`. - // Therefore, `buf.len() <= self.buf.capacity() - self.buf.len()`. + // SAFETY: There was either enough spare capacity already, or there wasn't and we + // flushed the buffer to ensure that there is. In the latter case, we know that there + // is because flushing ensured that our entire buffer is spare capacity, and we entered + // this block because the input buffer length is less than that capacity. In either + // case, it's safe to write the input buffer to our buffer. unsafe { self.write_to_buffer_unchecked(buf); } From 33cc3f5116e2f1813a54d5038151e0898a670af2 Mon Sep 17 00:00:00 2001 From: Folyd Date: Sun, 18 Apr 2021 19:52:20 +0800 Subject: [PATCH 08/29] Stablize {HashMap,BTreeMap}::into_{keys,values} --- library/alloc/src/collections/btree/map.rs | 30 ++++++++++------------ library/std/src/collections/hash/map.rs | 30 +++++++++------------- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 971244718b4ad..02e2924623c9d 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -398,12 +398,12 @@ impl fmt::Debug for ValuesMut<'_, K, V> { /// See its documentation for more. /// /// [`into_keys`]: BTreeMap::into_keys -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] pub struct IntoKeys { inner: IntoIter, } -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl fmt::Debug for IntoKeys { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.inner.iter().map(|(key, _)| key)).finish() @@ -416,12 +416,12 @@ impl fmt::Debug for IntoKeys { /// See its documentation for more. /// /// [`into_values`]: BTreeMap::into_values -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] pub struct IntoValues { inner: IntoIter, } -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl fmt::Debug for IntoValues { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.inner.iter().map(|(_, val)| val)).finish() @@ -1242,7 +1242,6 @@ impl BTreeMap { /// # Examples /// /// ``` - /// #![feature(map_into_keys_values)] /// use std::collections::BTreeMap; /// /// let mut a = BTreeMap::new(); @@ -1253,7 +1252,7 @@ impl BTreeMap { /// assert_eq!(keys, [1, 2]); /// ``` #[inline] - #[unstable(feature = "map_into_keys_values", issue = "75294")] + #[stable(feature = "map_into_keys_values", since = "1.53.0")] pub fn into_keys(self) -> IntoKeys { IntoKeys { inner: self.into_iter() } } @@ -1265,7 +1264,6 @@ impl BTreeMap { /// # Examples /// /// ``` - /// #![feature(map_into_keys_values)] /// use std::collections::BTreeMap; /// /// let mut a = BTreeMap::new(); @@ -1276,7 +1274,7 @@ impl BTreeMap { /// assert_eq!(values, ["hello", "goodbye"]); /// ``` #[inline] - #[unstable(feature = "map_into_keys_values", issue = "75294")] + #[stable(feature = "map_into_keys_values", since = "1.53.0")] pub fn into_values(self) -> IntoValues { IntoValues { inner: self.into_iter() } } @@ -1776,7 +1774,7 @@ impl<'a, K, V> Range<'a, K, V> { } } -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl Iterator for IntoKeys { type Item = K; @@ -1801,24 +1799,24 @@ impl Iterator for IntoKeys { } } -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl DoubleEndedIterator for IntoKeys { fn next_back(&mut self) -> Option { self.inner.next_back().map(|(k, _)| k) } } -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl ExactSizeIterator for IntoKeys { fn len(&self) -> usize { self.inner.len() } } -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl FusedIterator for IntoKeys {} -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl Iterator for IntoValues { type Item = V; @@ -1835,21 +1833,21 @@ impl Iterator for IntoValues { } } -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl DoubleEndedIterator for IntoValues { fn next_back(&mut self) -> Option { self.inner.next_back().map(|(_, v)| v) } } -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl ExactSizeIterator for IntoValues { fn len(&self) -> usize { self.inner.len() } } -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl FusedIterator for IntoValues {} #[stable(feature = "btree_range", since = "1.17.0")] diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 3dcc5cd2b5911..32c868fb4ab04 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -962,7 +962,6 @@ where /// # Examples /// /// ``` - /// #![feature(map_into_keys_values)] /// use std::collections::HashMap; /// /// let mut map = HashMap::new(); @@ -973,7 +972,7 @@ where /// let vec: Vec<&str> = map.into_keys().collect(); /// ``` #[inline] - #[unstable(feature = "map_into_keys_values", issue = "75294")] + #[stable(feature = "map_into_keys_values", since = "1.53.0")] pub fn into_keys(self) -> IntoKeys { IntoKeys { inner: self.into_iter() } } @@ -985,7 +984,6 @@ where /// # Examples /// /// ``` - /// #![feature(map_into_keys_values)] /// use std::collections::HashMap; /// /// let mut map = HashMap::new(); @@ -996,7 +994,7 @@ where /// let vec: Vec = map.into_values().collect(); /// ``` #[inline] - #[unstable(feature = "map_into_keys_values", issue = "75294")] + #[stable(feature = "map_into_keys_values", since = "1.53.0")] pub fn into_values(self) -> IntoValues { IntoValues { inner: self.into_iter() } } @@ -1405,15 +1403,13 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> { /// # Example /// /// ``` -/// #![feature(map_into_keys_values)] -/// /// use std::collections::HashMap; /// /// let mut map = HashMap::new(); /// map.insert("a", 1); /// let iter_keys = map.into_keys(); /// ``` -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] pub struct IntoKeys { inner: IntoIter, } @@ -1428,15 +1424,13 @@ pub struct IntoKeys { /// # Example /// /// ``` -/// #![feature(map_into_keys_values)] -/// /// use std::collections::HashMap; /// /// let mut map = HashMap::new(); /// map.insert("a", 1); /// let iter_keys = map.into_values(); /// ``` -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] pub struct IntoValues { inner: IntoIter, } @@ -2137,7 +2131,7 @@ impl fmt::Debug for ValuesMut<'_, K, V> { } } -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl Iterator for IntoKeys { type Item = K; @@ -2150,24 +2144,24 @@ impl Iterator for IntoKeys { self.inner.size_hint() } } -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl ExactSizeIterator for IntoKeys { #[inline] fn len(&self) -> usize { self.inner.len() } } -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl FusedIterator for IntoKeys {} -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl fmt::Debug for IntoKeys { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.inner.iter().map(|(k, _)| k)).finish() } } -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl Iterator for IntoValues { type Item = V; @@ -2180,17 +2174,17 @@ impl Iterator for IntoValues { self.inner.size_hint() } } -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl ExactSizeIterator for IntoValues { #[inline] fn len(&self) -> usize { self.inner.len() } } -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl FusedIterator for IntoValues {} -#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[stable(feature = "map_into_keys_values", since = "1.53.0")] impl fmt::Debug for IntoValues { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.inner.iter().map(|(_, v)| v)).finish() From 52fa9daa645772603dc170a3f3bed5cab73d646d Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Wed, 28 Apr 2021 15:51:14 +0200 Subject: [PATCH 09/29] Rework `wasm::thread` to `thread_atomics` --- library/std/src/sys/wasm/mod.rs | 5 ++++- .../sys/wasm/{thread.rs => thread_atomics.rs} | 22 ++++--------------- 2 files changed, 8 insertions(+), 19 deletions(-) rename library/std/src/sys/wasm/{thread.rs => thread_atomics.rs} (83%) diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs index 8705910c73a81..71877fce9934e 100644 --- a/library/std/src/sys/wasm/mod.rs +++ b/library/std/src/sys/wasm/mod.rs @@ -37,7 +37,6 @@ pub mod pipe; pub mod process; #[path = "../unsupported/stdio.rs"] pub mod stdio; -pub mod thread; #[path = "../unsupported/thread_local_dtor.rs"] pub mod thread_local_dtor; #[path = "../unsupported/thread_local_key.rs"] @@ -57,6 +56,8 @@ cfg_if::cfg_if! { pub mod rwlock; #[path = "futex_atomics.rs"] pub mod futex; + #[path = "thread_atomics.rs"] + pub mod thread; } else { #[path = "../unsupported/condvar.rs"] pub mod condvar; @@ -64,6 +65,8 @@ cfg_if::cfg_if! { pub mod mutex; #[path = "../unsupported/rwlock.rs"] pub mod rwlock; + #[path = "../unsupported/thread.rs"] + pub mod thread; } } diff --git a/library/std/src/sys/wasm/thread.rs b/library/std/src/sys/wasm/thread_atomics.rs similarity index 83% rename from library/std/src/sys/wasm/thread.rs rename to library/std/src/sys/wasm/thread_atomics.rs index b7bf95c89b482..54bc877aa7de7 100644 --- a/library/std/src/sys/wasm/thread.rs +++ b/library/std/src/sys/wasm/thread_atomics.rs @@ -13,20 +13,10 @@ impl Thread { unsupported() } - pub fn yield_now() { - // do nothing - } + pub fn yield_now() {} - pub fn set_name(_name: &CStr) { - // nope - } + pub fn set_name(_name: &CStr) {} - #[cfg(not(target_feature = "atomics"))] - pub fn sleep(_dur: Duration) { - panic!("can't sleep"); - } - - #[cfg(target_feature = "atomics")] pub fn sleep(dur: Duration) { use crate::arch::wasm32; use crate::cmp; @@ -46,9 +36,7 @@ impl Thread { } } - pub fn join(self) { - self.0 - } + pub fn join(self) {} } pub mod guard { @@ -61,11 +49,9 @@ pub mod guard { } } -// This is only used by atomics primitives when the `atomics` feature is -// enabled. In that mode we currently just use our own thread-local to store our +// We currently just use our own thread-local to store our // current thread's ID, and then we lazily initialize it to something allocated // from a global counter. -#[cfg(target_feature = "atomics")] pub fn my_id() -> u32 { use crate::sync::atomic::{AtomicU32, Ordering::SeqCst}; From fab841080157b0ddc6e0a0d88c4b3fc43d32f147 Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Wed, 28 Apr 2021 15:54:08 +0200 Subject: [PATCH 10/29] Move `wasm` atomics code to `wasm/atomics` --- .../wasm/{condvar_atomics.rs => atomics/condvar.rs} | 0 .../sys/wasm/{futex_atomics.rs => atomics/futex.rs} | 0 .../sys/wasm/{mutex_atomics.rs => atomics/mutex.rs} | 0 .../sys/wasm/{rwlock_atomics.rs => atomics/rwlock.rs} | 0 .../sys/wasm/{thread_atomics.rs => atomics/thread.rs} | 0 library/std/src/sys/wasm/mod.rs | 10 +++++----- 6 files changed, 5 insertions(+), 5 deletions(-) rename library/std/src/sys/wasm/{condvar_atomics.rs => atomics/condvar.rs} (100%) rename library/std/src/sys/wasm/{futex_atomics.rs => atomics/futex.rs} (100%) rename library/std/src/sys/wasm/{mutex_atomics.rs => atomics/mutex.rs} (100%) rename library/std/src/sys/wasm/{rwlock_atomics.rs => atomics/rwlock.rs} (100%) rename library/std/src/sys/wasm/{thread_atomics.rs => atomics/thread.rs} (100%) diff --git a/library/std/src/sys/wasm/condvar_atomics.rs b/library/std/src/sys/wasm/atomics/condvar.rs similarity index 100% rename from library/std/src/sys/wasm/condvar_atomics.rs rename to library/std/src/sys/wasm/atomics/condvar.rs diff --git a/library/std/src/sys/wasm/futex_atomics.rs b/library/std/src/sys/wasm/atomics/futex.rs similarity index 100% rename from library/std/src/sys/wasm/futex_atomics.rs rename to library/std/src/sys/wasm/atomics/futex.rs diff --git a/library/std/src/sys/wasm/mutex_atomics.rs b/library/std/src/sys/wasm/atomics/mutex.rs similarity index 100% rename from library/std/src/sys/wasm/mutex_atomics.rs rename to library/std/src/sys/wasm/atomics/mutex.rs diff --git a/library/std/src/sys/wasm/rwlock_atomics.rs b/library/std/src/sys/wasm/atomics/rwlock.rs similarity index 100% rename from library/std/src/sys/wasm/rwlock_atomics.rs rename to library/std/src/sys/wasm/atomics/rwlock.rs diff --git a/library/std/src/sys/wasm/thread_atomics.rs b/library/std/src/sys/wasm/atomics/thread.rs similarity index 100% rename from library/std/src/sys/wasm/thread_atomics.rs rename to library/std/src/sys/wasm/atomics/thread.rs diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs index 71877fce9934e..0bd2c0d0f9d4c 100644 --- a/library/std/src/sys/wasm/mod.rs +++ b/library/std/src/sys/wasm/mod.rs @@ -48,15 +48,15 @@ pub use crate::sys_common::os_str_bytes as os_str; cfg_if::cfg_if! { if #[cfg(target_feature = "atomics")] { - #[path = "condvar_atomics.rs"] + #[path = "atomics/condvar.rs"] pub mod condvar; - #[path = "mutex_atomics.rs"] + #[path = "atomics/mutex.rs"] pub mod mutex; - #[path = "rwlock_atomics.rs"] + #[path = "atomics/rwlock.rs"] pub mod rwlock; - #[path = "futex_atomics.rs"] + #[path = "atomics/futex.rs"] pub mod futex; - #[path = "thread_atomics.rs"] + #[path = "atomics/thread.rs"] pub mod thread; } else { #[path = "../unsupported/condvar.rs"] From 45bc1930cae22b8b08adc9bb5ed4a494831d6678 Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Wed, 28 Apr 2021 15:44:13 +0200 Subject: [PATCH 11/29] Reuse `unsupported::args` on `wasm` --- library/std/src/sys/wasm/args.rs | 42 -------------------------------- library/std/src/sys/wasm/mod.rs | 1 + 2 files changed, 1 insertion(+), 42 deletions(-) delete mode 100644 library/std/src/sys/wasm/args.rs diff --git a/library/std/src/sys/wasm/args.rs b/library/std/src/sys/wasm/args.rs deleted file mode 100644 index fde1ab79e1f4b..0000000000000 --- a/library/std/src/sys/wasm/args.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::ffi::OsString; -use crate::fmt; -use crate::vec; - -pub fn args() -> Args { - Args { iter: Vec::new().into_iter() } -} - -pub struct Args { - iter: vec::IntoIter, -} - -impl !Send for Args {} -impl !Sync for Args {} - -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.iter.as_slice().fmt(f) - } -} - -impl Iterator for Args { - type Item = OsString; - fn next(&mut self) -> Option { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.iter.len() - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - self.iter.next_back() - } -} diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs index 0bd2c0d0f9d4c..18d8ea183f733 100644 --- a/library/std/src/sys/wasm/mod.rs +++ b/library/std/src/sys/wasm/mod.rs @@ -17,6 +17,7 @@ #![deny(unsafe_op_in_unsafe_fn)] pub mod alloc; +#[path = "../unsupported/args.rs"] pub mod args; #[path = "../unsupported/cmath.rs"] pub mod cmath; From cf79c06575f1710f05ee5309fb24bcdfff6f0140 Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Wed, 28 Apr 2021 16:16:01 +0200 Subject: [PATCH 12/29] Fix missing import in `unsupported::args` --- library/std/src/sys/unsupported/args.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/sys/unsupported/args.rs b/library/std/src/sys/unsupported/args.rs index c924a7d8a2672..a2d75a6197633 100644 --- a/library/std/src/sys/unsupported/args.rs +++ b/library/std/src/sys/unsupported/args.rs @@ -1,4 +1,5 @@ use crate::ffi::OsString; +use crate::fmt; pub struct Args {} From 8a2e67e0d0826f73531f79ecc8d14aba71a8d837 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 29 Apr 2021 13:11:20 -0700 Subject: [PATCH 13/29] Simplify chdir implementation and minimize unsafe block --- library/std/src/sys/unix/os.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 984c08c2ad531..51c3e5d175cca 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -155,12 +155,10 @@ pub fn getcwd() -> io::Result { pub fn chdir(p: &path::Path) -> io::Result<()> { let p: &OsStr = p.as_ref(); let p = CString::new(p.as_bytes())?; - unsafe { - match libc::chdir(p.as_ptr()) == (0 as c_int) { - true => Ok(()), - false => Err(io::Error::last_os_error()), - } + if unsafe { libc::chdir(p.as_ptr()) } != 0 { + return Err(io::Error::last_os_error()); } + Ok(()) } pub struct SplitPaths<'a> { From 4a63e1e991cadfdfceb7f421c0dec9ba823b42c8 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 30 Apr 2021 14:54:18 +0000 Subject: [PATCH 14/29] Allow using `core::` in intra-doc links within core itself I came up with this idea ages ago, but rustdoc used to ICE on it. Now it doesn't. --- library/core/src/intrinsics.rs | 20 ++++++++++---------- library/core/src/lib.rs | 4 ++++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 1ba0b23ae5be3..f5bfc98c71938 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -723,7 +723,7 @@ extern "rust-intrinsic" { /// macro, which panics when it is executed, it is *undefined behavior* to /// reach code marked with this function. /// - /// The stabilized version of this intrinsic is [`core::hint::unreachable_unchecked`](crate::hint::unreachable_unchecked). + /// The stabilized version of this intrinsic is [`core::hint::unreachable_unchecked`]. #[rustc_const_unstable(feature = "const_unreachable_unchecked", issue = "53188")] pub fn unreachable() -> !; @@ -768,13 +768,13 @@ extern "rust-intrinsic" { /// More specifically, this is the offset in bytes between successive /// items of the same type, including alignment padding. /// - /// The stabilized version of this intrinsic is [`core::mem::size_of`](crate::mem::size_of). + /// The stabilized version of this intrinsic is [`core::mem::size_of`]. #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")] pub fn size_of() -> usize; /// The minimum alignment of a type. /// - /// The stabilized version of this intrinsic is [`core::mem::align_of`](crate::mem::align_of). + /// The stabilized version of this intrinsic is [`core::mem::align_of`]. #[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")] pub fn min_align_of() -> usize; /// The preferred alignment of a type. @@ -790,13 +790,13 @@ extern "rust-intrinsic" { pub fn size_of_val(_: *const T) -> usize; /// The required alignment of the referenced value. /// - /// The stabilized version of this intrinsic is [`core::mem::align_of_val`](crate::mem::align_of_val). + /// The stabilized version of this intrinsic is [`core::mem::align_of_val`]. #[rustc_const_unstable(feature = "const_align_of_val", issue = "46571")] pub fn min_align_of_val(_: *const T) -> usize; /// Gets a static string slice containing the name of a type. /// - /// The stabilized version of this intrinsic is [`core::any::type_name`](crate::any::type_name). + /// The stabilized version of this intrinsic is [`core::any::type_name`]. #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] pub fn type_name() -> &'static str; @@ -804,7 +804,7 @@ extern "rust-intrinsic" { /// function will return the same value for a type regardless of whichever /// crate it is invoked in. /// - /// The stabilized version of this intrinsic is [`core::any::TypeId::of`](crate::any::TypeId::of). + /// The stabilized version of this intrinsic is [`core::any::TypeId::of`]. #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] pub fn type_id() -> u64; @@ -829,7 +829,7 @@ extern "rust-intrinsic" { /// Gets a reference to a static `Location` indicating where it was called. /// - /// Consider using [`core::panic::Location::caller`](crate::panic::Location::caller) instead. + /// Consider using [`core::panic::Location::caller`] instead. #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")] pub fn caller_location() -> &'static crate::panic::Location<'static>; @@ -1158,11 +1158,11 @@ extern "rust-intrinsic" { /// Performs a volatile load from the `src` pointer. /// - /// The stabilized version of this intrinsic is [`core::ptr::read_volatile`](crate::ptr::read_volatile). + /// The stabilized version of this intrinsic is [`core::ptr::read_volatile`]. pub fn volatile_load(src: *const T) -> T; /// Performs a volatile store to the `dst` pointer. /// - /// The stabilized version of this intrinsic is [`core::ptr::write_volatile`](crate::ptr::write_volatile). + /// The stabilized version of this intrinsic is [`core::ptr::write_volatile`]. pub fn volatile_store(dst: *mut T, val: T); /// Performs a volatile load from the `src` pointer @@ -1703,7 +1703,7 @@ extern "rust-intrinsic" { /// Returns the value of the discriminant for the variant in 'v'; /// if `T` has no discriminant, returns `0`. /// - /// The stabilized version of this intrinsic is [`core::mem::discriminant`](crate::mem::discriminant). + /// The stabilized version of this intrinsic is [`core::mem::discriminant`]. #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] pub fn discriminant_value(v: &T) -> ::Discriminant; diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 0e2c140c367a9..cbabf55d17b66 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -169,6 +169,10 @@ #![feature(int_error_matching)] #![deny(unsafe_op_in_unsafe_fn)] +// allow using `core::` in intra-doc links +#[allow(unused_extern_crates)] +extern crate self as core; + #[prelude_import] #[allow(unused)] use prelude::v1::*; From 367c1dbd4819fb2148ab35b934b3215abe3af441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 3 May 2021 10:16:06 +0300 Subject: [PATCH 15/29] :arrow_up: rust-analyzer --- src/tools/rust-analyzer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer b/src/tools/rust-analyzer index 617535393bb5c..eb741e895f1a7 160000 --- a/src/tools/rust-analyzer +++ b/src/tools/rust-analyzer @@ -1 +1 @@ -Subproject commit 617535393bb5ccc7adf0bac8a3b9a9c306454e79 +Subproject commit eb741e895f1a73420a401f2495c711afe37d9d19 From 6eb4735acc50f58e501b42c5d75ae73b3d74b44d Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 22 Apr 2021 13:28:43 -0400 Subject: [PATCH 16/29] Unify rustc and rustdoc parsing of `cfg()` This extracts a new `parse_cfg` function that's used between both. - Treat `#[doc(cfg(x), cfg(y))]` the same as `#[doc(cfg(x)] #[doc(cfg(y))]`. Previously it would be completely ignored. - Treat `#[doc(inline, cfg(x))]` the same as `#[doc(inline)] #[doc(cfg(x))]`. Previously, the cfg would be ignored. - Pass the cfg predicate through to rustc_expand to be validated Co-authored-by: Vadim Petrochenkov --- compiler/rustc_expand/src/config.rs | 54 +++++++++++++----------- src/librustdoc/clean/inline.rs | 4 +- src/librustdoc/clean/mod.rs | 2 +- src/librustdoc/clean/types.rs | 54 +++++++++--------------- src/librustdoc/doctest.rs | 2 +- src/librustdoc/html/render/context.rs | 2 +- src/librustdoc/html/render/print_item.rs | 2 +- src/test/rustdoc-ui/invalid-cfg.rs | 4 ++ src/test/rustdoc-ui/invalid-cfg.stderr | 14 ++++++ src/test/rustdoc/doc-cfg.rs | 8 ++++ 10 files changed, 82 insertions(+), 64 deletions(-) create mode 100644 src/test/rustdoc-ui/invalid-cfg.rs create mode 100644 src/test/rustdoc-ui/invalid-cfg.stderr diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 03c83f9c07b5d..f9140609c0f3c 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -464,31 +464,9 @@ impl<'a> StripUnconfigured<'a> { return true; } }; - let error = |span, msg, suggestion: &str| { - let mut err = self.sess.parse_sess.span_diagnostic.struct_span_err(span, msg); - if !suggestion.is_empty() { - err.span_suggestion( - span, - "expected syntax is", - suggestion.into(), - Applicability::MaybeIncorrect, - ); - } - err.emit(); - true - }; - let span = meta_item.span; - match meta_item.meta_item_list() { - None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"), - Some([]) => error(span, "`cfg` predicate is not specified", ""), - Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""), - Some([single]) => match single.meta_item() { - Some(meta_item) => { - attr::cfg_matches(meta_item, &self.sess.parse_sess, self.features) - } - None => error(single.span(), "`cfg` predicate key cannot be a literal", ""), - }, - } + parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| { + attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features) + }) }) } @@ -532,6 +510,32 @@ impl<'a> StripUnconfigured<'a> { } } +pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItem> { + let error = |span, msg, suggestion: &str| { + let mut err = sess.parse_sess.span_diagnostic.struct_span_err(span, msg); + if !suggestion.is_empty() { + err.span_suggestion( + span, + "expected syntax is", + suggestion.into(), + Applicability::HasPlaceholders, + ); + } + err.emit(); + None + }; + let span = meta_item.span; + match meta_item.meta_item_list() { + None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"), + Some([]) => error(span, "`cfg` predicate is not specified", ""), + Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""), + Some([single]) => match single.meta_item() { + Some(meta_item) => Some(meta_item), + None => error(single.span(), "`cfg` predicate key cannot be a literal", ""), + }, + } +} + fn is_cfg(sess: &Session, attr: &Attribute) -> bool { sess.check_name(attr, sym::cfg) } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 3e89c1ac4c514..4c3b86b2e2b43 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -307,10 +307,10 @@ fn merge_attrs( } else { Attributes::from_ast(&both, None) }, - both.cfg(cx.sess().diagnostic()), + both.cfg(cx.sess()), ) } else { - (old_attrs.clean(cx), old_attrs.cfg(cx.sess().diagnostic())) + (old_attrs.clean(cx), old_attrs.cfg(cx.sess())) } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 6563f398edb6f..a7bc3c20a3d74 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2006,7 +2006,7 @@ fn clean_extern_crate( def_id: crate_def_id, visibility: krate.vis.clean(cx), kind: box ExternCrateItem { src: orig_name }, - cfg: attrs.cfg(cx.sess().diagnostic()), + cfg: attrs.cfg(cx.sess()), }] } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 5e47144588b3f..a2a03dfd15b7c 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -320,7 +320,7 @@ impl Item { kind, box ast_attrs.clean(cx), cx, - ast_attrs.cfg(cx.sess().diagnostic()), + ast_attrs.cfg(cx.sess()), ) } @@ -332,7 +332,7 @@ impl Item { cx: &mut DocContext<'_>, cfg: Option>, ) -> Item { - debug!("name={:?}, def_id={:?}", name, def_id); + trace!("name={:?}, def_id={:?}", name, def_id); Item { def_id, @@ -681,7 +681,7 @@ crate trait AttributesExt { fn other_attrs(&self) -> Vec; - fn cfg(&self, diagnostic: &::rustc_errors::Handler) -> Option>; + fn cfg(&self, sess: &Session) -> Option>; } impl AttributesExt for [ast::Attribute] { @@ -706,17 +706,28 @@ impl AttributesExt for [ast::Attribute] { self.iter().filter(|attr| attr.doc_str().is_none()).cloned().collect() } - fn cfg(&self, diagnostic: &::rustc_errors::Handler) -> Option> { + fn cfg(&self, sess: &Session) -> Option> { let mut cfg = Cfg::True; for attr in self.iter() { + // #[doc] if attr.doc_str().is_none() && attr.has_name(sym::doc) { - if let Some(mi) = attr.meta() { - if let Some(cfg_mi) = Attributes::extract_cfg(&mi) { - // Extracted #[doc(cfg(...))] - match Cfg::parse(cfg_mi) { - Ok(new_cfg) => cfg &= new_cfg, - Err(e) => diagnostic.span_err(e.span, e.msg), + // #[doc(...)] + if let Some(list) = attr.meta().as_ref().and_then(|mi| mi.meta_item_list()) { + for item in list { + // #[doc(include)] + if !item.has_name(sym::cfg) { + continue; + } + // #[doc(cfg(...))] + if let Some(cfg_mi) = item + .meta_item() + .and_then(|item| rustc_expand::config::parse_cfg(&item, sess)) + { + match Cfg::parse(&cfg_mi) { + Ok(new_cfg) => cfg &= new_cfg, + Err(e) => sess.span_err(e.span, e.msg), + } } } } @@ -883,29 +894,6 @@ impl Attributes { self.other_attrs.lists(name) } - /// Extracts the content from an attribute `#[doc(cfg(content))]`. - crate fn extract_cfg(mi: &ast::MetaItem) -> Option<&ast::MetaItem> { - use rustc_ast::NestedMetaItem::MetaItem; - - if let ast::MetaItemKind::List(ref nmis) = mi.kind { - if nmis.len() == 1 { - if let MetaItem(ref cfg_mi) = nmis[0] { - if cfg_mi.has_name(sym::cfg) { - if let ast::MetaItemKind::List(ref cfg_nmis) = cfg_mi.kind { - if cfg_nmis.len() == 1 { - if let MetaItem(ref content_mi) = cfg_nmis[0] { - return Some(content_mi); - } - } - } - } - } - } - } - - None - } - /// Reads a `MetaItem` from within an attribute, looks for whether it is a /// `#[doc(include="file")]`, and returns the filename and contents of the file as loaded from /// its expansion. diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 466d1b65406cd..69a47c5b67a7a 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1095,7 +1095,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { let ast_attrs = self.tcx.hir().attrs(hir_id); let mut attrs = Attributes::from_ast(ast_attrs, None); - if let Some(ref cfg) = ast_attrs.cfg(self.sess.diagnostic()) { + if let Some(ref cfg) = ast_attrs.cfg(self.sess) { if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) { return; } diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 4c8ba0e7b496e..268974169ea85 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -154,7 +154,7 @@ impl<'tcx> Context<'tcx> { &self.cache } - fn sess(&self) -> &'tcx Session { + pub(super) fn sess(&self) -> &'tcx Session { &self.shared.tcx.sess } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 1bb1db00e8825..d33a31ef1ee25 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -292,7 +292,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl let import_item = clean::Item { def_id: import_def_id, attrs: import_attrs, - cfg: ast_attrs.cfg(cx.tcx().sess.diagnostic()), + cfg: ast_attrs.cfg(cx.sess()), ..myitem.clone() }; diff --git a/src/test/rustdoc-ui/invalid-cfg.rs b/src/test/rustdoc-ui/invalid-cfg.rs new file mode 100644 index 0000000000000..d237b8605c068 --- /dev/null +++ b/src/test/rustdoc-ui/invalid-cfg.rs @@ -0,0 +1,4 @@ +#![feature(doc_cfg)] +#[doc(cfg = "x")] //~ ERROR not followed by parentheses +#[doc(cfg(x, y))] //~ ERROR multiple `cfg` predicates +struct S {} diff --git a/src/test/rustdoc-ui/invalid-cfg.stderr b/src/test/rustdoc-ui/invalid-cfg.stderr new file mode 100644 index 0000000000000..dae238b052b8a --- /dev/null +++ b/src/test/rustdoc-ui/invalid-cfg.stderr @@ -0,0 +1,14 @@ +error: `cfg` is not followed by parentheses + --> $DIR/invalid-cfg.rs:2:7 + | +LL | #[doc(cfg = "x")] + | ^^^^^^^^^ help: expected syntax is: `cfg(/* predicate */)` + +error: multiple `cfg` predicates are specified + --> $DIR/invalid-cfg.rs:3:14 + | +LL | #[doc(cfg(x, y))] + | ^ + +error: aborting due to 2 previous errors + diff --git a/src/test/rustdoc/doc-cfg.rs b/src/test/rustdoc/doc-cfg.rs index 89a61a289fdeb..1fc80b3e76c53 100644 --- a/src/test/rustdoc/doc-cfg.rs +++ b/src/test/rustdoc/doc-cfg.rs @@ -91,3 +91,11 @@ pub unsafe fn uses_target_feature() { pub fn uses_cfg_target_feature() { uses_target_feature(); } + +// multiple attributes should be allowed +// @has doc_cfg/fn.multiple_attrs.html \ +// '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \ +// 'This is supported on x and y and z only.' +#[doc(inline, cfg(x))] +#[doc(cfg(y), cfg(z))] +pub fn multiple_attrs() {} From 6b64202d5eebdaddcc246412a795ec32dac0f8f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 3 May 2021 23:48:56 -0700 Subject: [PATCH 17/29] Handle incorrect placement of parentheses in trait bounds more gracefully Fix #84772. --- compiler/rustc_parse/src/parser/ty.rs | 39 +++++++++- src/test/ui/parser/trait-object-delimiters.rs | 17 ++++ .../ui/parser/trait-object-delimiters.stderr | 77 +++++++++++++++++++ 3 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/parser/trait-object-delimiters.rs create mode 100644 src/test/ui/parser/trait-object-delimiters.stderr diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 0f7b8ebd376b9..d537741c749c5 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -470,7 +470,7 @@ impl<'a> Parser<'a> { /// Is a `dyn B0 + ... + Bn` type allowed here? fn is_explicit_dyn_type(&mut self) -> bool { self.check_keyword(kw::Dyn) - && (self.token.uninterpolated_span().rust_2018() + && (!self.token.uninterpolated_span().rust_2015() || self.look_ahead(1, |t| { t.can_begin_bound() && !can_continue_type_after_non_fn_ident(t) })) @@ -539,7 +539,21 @@ impl<'a> Parser<'a> { ) -> PResult<'a, GenericBounds> { let mut bounds = Vec::new(); let mut negative_bounds = Vec::new(); - while self.can_begin_bound() { + + while self.can_begin_bound() || self.token.is_keyword(kw::Dyn) { + if self.token.is_keyword(kw::Dyn) { + // Account for `&dyn Trait + dyn Other`. + self.struct_span_err(self.token.span, "invalid `dyn` keyword") + .help("`dyn` is only needed at the start of a trait `+`-separated list") + .span_suggestion( + self.token.span, + "remove this keyword", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + self.bump(); + } match self.parse_generic_bound()? { Ok(bound) => bounds.push(bound), Err(neg_sp) => negative_bounds.push(neg_sp), @@ -721,7 +735,26 @@ impl<'a> Parser<'a> { let lifetime_defs = self.parse_late_bound_lifetime_defs()?; let path = self.parse_path(PathStyle::Type)?; if has_parens { - self.expect(&token::CloseDelim(token::Paren))?; + if self.token.is_like_plus() { + // Someone has written something like `&dyn (Trait + Other)`. The correct code + // would be `&(dyn Trait + Other)`, but we don't have access to the appropriate + // span to suggest that. When written as `&dyn Trait + Other`, an appropriate + // suggestion is given. + let bounds = vec![]; + self.parse_remaining_bounds(bounds, true)?; + self.expect(&token::CloseDelim(token::Paren))?; + let sp = vec![lo, self.prev_token.span]; + let sugg: Vec<_> = sp.iter().map(|sp| (*sp, String::new())).collect(); + self.struct_span_err(sp, "incorrect braces around trait bounds") + .multipart_suggestion( + "remove the parentheses", + sugg, + Applicability::MachineApplicable, + ) + .emit(); + } else { + self.expect(&token::CloseDelim(token::Paren))?; + } } let modifier = modifiers.to_trait_bound_modifier(); diff --git a/src/test/ui/parser/trait-object-delimiters.rs b/src/test/ui/parser/trait-object-delimiters.rs new file mode 100644 index 0000000000000..650ab57226187 --- /dev/null +++ b/src/test/ui/parser/trait-object-delimiters.rs @@ -0,0 +1,17 @@ +// edition:2018 + +fn foo1(_: &dyn Drop + AsRef) {} //~ ERROR ambiguous `+` in a type +//~^ ERROR only auto traits can be used as additional traits in a trait object + +fn foo2(_: &dyn (Drop + AsRef)) {} //~ ERROR incorrect braces around trait bounds + +fn foo3(_: &dyn {Drop + AsRef}) {} //~ ERROR expected parameter name, found `{` +//~^ ERROR expected one of `!`, `(`, `)`, `,`, `?`, `for`, lifetime, or path, found `{` +//~| ERROR at least one trait is required for an object type + +fn foo4(_: &dyn >) {} //~ ERROR expected identifier, found `<` + +fn foo5(_: &(dyn Drop + dyn AsRef)) {} //~ ERROR invalid `dyn` keyword +//~^ ERROR only auto traits can be used as additional traits in a trait object + +fn main() {} diff --git a/src/test/ui/parser/trait-object-delimiters.stderr b/src/test/ui/parser/trait-object-delimiters.stderr new file mode 100644 index 0000000000000..18b1b24122ecf --- /dev/null +++ b/src/test/ui/parser/trait-object-delimiters.stderr @@ -0,0 +1,77 @@ +error: ambiguous `+` in a type + --> $DIR/trait-object-delimiters.rs:3:13 + | +LL | fn foo1(_: &dyn Drop + AsRef) {} + | ^^^^^^^^^^^^^^^^^^^^^ help: use parentheses to disambiguate: `(dyn Drop + AsRef)` + +error: incorrect braces around trait bounds + --> $DIR/trait-object-delimiters.rs:6:17 + | +LL | fn foo2(_: &dyn (Drop + AsRef)) {} + | ^ ^ + | +help: remove the parentheses + | +LL | fn foo2(_: &dyn Drop + AsRef) {} + | -- -- + +error: expected parameter name, found `{` + --> $DIR/trait-object-delimiters.rs:8:17 + | +LL | fn foo3(_: &dyn {Drop + AsRef}) {} + | ^ expected parameter name + +error: expected one of `!`, `(`, `)`, `,`, `?`, `for`, lifetime, or path, found `{` + --> $DIR/trait-object-delimiters.rs:8:17 + | +LL | fn foo3(_: &dyn {Drop + AsRef}) {} + | -^ expected one of 8 possible tokens + | | + | help: missing `,` + +error: expected identifier, found `<` + --> $DIR/trait-object-delimiters.rs:12:17 + | +LL | fn foo4(_: &dyn >) {} + | ^ expected identifier + +error: invalid `dyn` keyword + --> $DIR/trait-object-delimiters.rs:14:25 + | +LL | fn foo5(_: &(dyn Drop + dyn AsRef)) {} + | ^^^ help: remove this keyword + | + = help: `dyn` is only needed at the start of a trait `+`-separated list + +error[E0225]: only auto traits can be used as additional traits in a trait object + --> $DIR/trait-object-delimiters.rs:3:24 + | +LL | fn foo1(_: &dyn Drop + AsRef) {} + | ---- ^^^^^^^^^^ additional non-auto trait + | | + | first non-auto trait + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Drop + AsRef {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit + +error[E0224]: at least one trait is required for an object type + --> $DIR/trait-object-delimiters.rs:8:13 + | +LL | fn foo3(_: &dyn {Drop + AsRef}) {} + | ^^^ + +error[E0225]: only auto traits can be used as additional traits in a trait object + --> $DIR/trait-object-delimiters.rs:14:29 + | +LL | fn foo5(_: &(dyn Drop + dyn AsRef)) {} + | ---- ^^^^^^^^^^ additional non-auto trait + | | + | first non-auto trait + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Drop + AsRef {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit + +error: aborting due to 9 previous errors + +Some errors have detailed explanations: E0224, E0225. +For more information about an error, try `rustc --explain E0224`. From 0b94338a267fce3c25a25d09ab18d00d0dc21268 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 4 May 2021 13:43:50 +0200 Subject: [PATCH 18/29] =?UTF-8?q?CTFE=20engine:=20rename=20copy=20?= =?UTF-8?q?=E2=86=92=20copy=5Fintrinsic,=20move=20to=20intrinsics.rs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rustc_mir/src/interpret/intrinsics.rs | 34 ++++++++++++++++++- compiler/rustc_mir/src/interpret/step.rs | 34 +------------------ 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_mir/src/interpret/intrinsics.rs b/compiler/rustc_mir/src/interpret/intrinsics.rs index dea1b11331549..292306f6cde6e 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics.rs @@ -323,7 +323,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.write_scalar(result, dest)?; } sym::copy => { - self.copy(&args[0], &args[1], &args[2], /*nonoverlapping*/ false)?; + self.copy_intrinsic(&args[0], &args[1], &args[2], /*nonoverlapping*/ false)?; } sym::offset => { let ptr = self.read_scalar(&args[0])?.check_init()?; @@ -530,4 +530,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { )?; Ok(offset_ptr) } + + /// Copy `count*size_of::()` many bytes from `*src` to `*dst`. + pub(crate) fn copy_intrinsic( + &mut self, + src: &OpTy<'tcx, >::PointerTag>, + dst: &OpTy<'tcx, >::PointerTag>, + count: &OpTy<'tcx, >::PointerTag>, + nonoverlapping: bool, + ) -> InterpResult<'tcx> { + let count = self.read_scalar(&count)?.to_machine_usize(self)?; + let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?; + let (size, align) = (layout.size, layout.align.abi); + let size = size.checked_mul(count, self).ok_or_else(|| { + err_ub_format!( + "overflow computing total size of `{}`", + if nonoverlapping { "copy_nonoverlapping" } else { "copy" } + ) + })?; + + // Make sure we check both pointers for an access of the total size and aligment, + // *even if* the total size is 0. + let src = + self.memory.check_ptr_access(self.read_scalar(&src)?.check_init()?, size, align)?; + + let dst = + self.memory.check_ptr_access(self.read_scalar(&dst)?.check_init()?, size, align)?; + + if let (Some(src), Some(dst)) = (src, dst) { + self.memory.copy(src, dst, size, nonoverlapping)?; + } + Ok(()) + } } diff --git a/compiler/rustc_mir/src/interpret/step.rs b/compiler/rustc_mir/src/interpret/step.rs index 6084f67abd78e..5a10ffe6d6199 100644 --- a/compiler/rustc_mir/src/interpret/step.rs +++ b/compiler/rustc_mir/src/interpret/step.rs @@ -2,7 +2,6 @@ //! //! The main entry point is the `step` method. -use crate::interpret::OpTy; use rustc_middle::mir; use rustc_middle::mir::interpret::{InterpResult, Scalar}; use rustc_target::abi::LayoutOf; @@ -119,7 +118,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let src = self.eval_operand(src, None)?; let dst = self.eval_operand(dst, None)?; let count = self.eval_operand(count, None)?; - self.copy(&src, &dst, &count, /* nonoverlapping */ true)?; + self.copy_intrinsic(&src, &dst, &count, /* nonoverlapping */ true)?; } // Statements we do not track. @@ -149,37 +148,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(()) } - pub(crate) fn copy( - &mut self, - src: &OpTy<'tcx, >::PointerTag>, - dst: &OpTy<'tcx, >::PointerTag>, - count: &OpTy<'tcx, >::PointerTag>, - nonoverlapping: bool, - ) -> InterpResult<'tcx> { - let count = self.read_scalar(&count)?.to_machine_usize(self)?; - let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?; - let (size, align) = (layout.size, layout.align.abi); - let size = size.checked_mul(count, self).ok_or_else(|| { - err_ub_format!( - "overflow computing total size of `{}`", - if nonoverlapping { "copy_nonoverlapping" } else { "copy" } - ) - })?; - - // Make sure we check both pointers for an access of the total size and aligment, - // *even if* the total size is 0. - let src = - self.memory.check_ptr_access(self.read_scalar(&src)?.check_init()?, size, align)?; - - let dst = - self.memory.check_ptr_access(self.read_scalar(&dst)?.check_init()?, size, align)?; - - if let (Some(src), Some(dst)) = (src, dst) { - self.memory.copy(src, dst, size, nonoverlapping)?; - } - Ok(()) - } - /// Evaluate an assignment statement. /// /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue From 196899a4824b14ada41770fd055683659179ad28 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 30 Apr 2021 13:04:43 -0700 Subject: [PATCH 19/29] Update compiler-builtins to 0.1.42 to get fix for outlined atomics This should fix linking of other C code (and soon Rust-generated code) on aarch64 musl. --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a55ef7b61436e..e2397caf4f936 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -666,9 +666,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.39" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3748f82c7d366a0b4950257d19db685d4958d2fa27c6d164a3f069fec42b748b" +checksum = "0d0b4f96df35277d7546ca0b0b727ee4bf99e5590fd348553b70c5ee2bb95033" dependencies = [ "cc", "rustc-std-workspace-core", From 4bd5505718696a1ec76e156b91561428468371bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 4 May 2021 11:57:53 -0700 Subject: [PATCH 20/29] Only compute Obligation `cache_key` once in `register_obligation_at` --- .../rustc_data_structures/src/obligation_forest/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index a5b2df1da5d6d..29d685ab530d6 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs @@ -336,12 +336,13 @@ impl ObligationForest { // Returns Err(()) if we already know this obligation failed. fn register_obligation_at(&mut self, obligation: O, parent: Option) -> Result<(), ()> { - if self.done_cache.contains(&obligation.as_cache_key()) { + let cache_key = obligation.as_cache_key(); + if self.done_cache.contains(&cache_key) { debug!("register_obligation_at: ignoring already done obligation: {:?}", obligation); return Ok(()); } - match self.active_cache.entry(obligation.as_cache_key()) { + match self.active_cache.entry(cache_key.clone()) { Entry::Occupied(o) => { let node = &mut self.nodes[*o.get()]; if let Some(parent_index) = parent { @@ -365,7 +366,7 @@ impl ObligationForest { && self .error_cache .get(&obligation_tree_id) - .map(|errors| errors.contains(&obligation.as_cache_key())) + .map(|errors| errors.contains(&cache_key)) .unwrap_or(false); if already_failed { From 42405b4fa88d4a422bf26225aea03b7ce50a0ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 5 May 2021 12:31:38 +0300 Subject: [PATCH 21/29] Fix typo in `MaybeUninit::array_assume_init` safety comment And also add backticks around `MaybeUninit`. --- library/core/src/mem/maybe_uninit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index f77acdd618072..10219201a40d3 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -870,7 +870,7 @@ impl MaybeUninit { // SAFETY: // * The caller guarantees that all elements of the array are initialized // * `MaybeUninit` and T are guaranteed to have the same layout - // * MaybeUnint does not drop, so there are no double-frees + // * `MaybeUninit` does not drop, so there are no double-frees // And thus the conversion is safe unsafe { intrinsics::assert_inhabited::<[T; N]>(); From 5b34bf460c68aea356633fd17760a99cc0794f4f Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Wed, 5 May 2021 11:57:10 +0200 Subject: [PATCH 22/29] Revert PR 83866 That PR caused multiple test failures when Rust's channel is changed from nightly to anything else. The commit will have to be landed again after the test suite is fixed. --- src/librustdoc/clean/types.rs | 7 +++---- src/librustdoc/clean/utils.rs | 11 ----------- src/librustdoc/core.rs | 9 +++------ src/librustdoc/lib.rs | 7 +------ src/librustdoc/passes/collect_intra_doc_links.rs | 9 +-------- .../intra-doc/email-address-localhost.stderr | 1 - .../rustdoc-ui/intra-doc/unknown-disambiguator.stderr | 11 ----------- 7 files changed, 8 insertions(+), 47 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 350742e8f2898..47dae63f1fdf7 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -517,10 +517,9 @@ impl Item { Some(ExternalLocation::Remote(ref s)) => { format!("{}/std/", s.trim_end_matches('/')) } - Some(ExternalLocation::Unknown) | None => format!( - "https://doc.rust-lang.org/{}/std/", - crate::doc_rust_lang_org_channel(), - ), + Some(ExternalLocation::Unknown) | None => { + "https://doc.rust-lang.org/nightly/std/".to_string() + } }; // This is a primitive so the url is done "by hand". let tail = fragment.find('#').unwrap_or_else(|| fragment.len()); diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 7235217e29040..51a011cf19773 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -542,14 +542,3 @@ crate fn has_doc_flag(attrs: ty::Attributes<'_>, flag: Symbol) -> bool { && attr.meta_item_list().map_or(false, |l| rustc_attr::list_contains_name(&l, flag)) }) } - -/// Return a channel suitable for using in a `doc.rust-lang.org/{channel}` format string. -crate fn doc_rust_lang_org_channel() -> &'static str { - match env!("CFG_RELEASE_CHANNEL") { - "stable" => env!("CFG_RELEASE_NUM"), - "beta" => "beta", - "nightly" | "dev" => "nightly", - // custom build of rustdoc maybe? link to the stable docs just in case - _ => "", - } -} diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index b0d163763b4bd..7b0c0b6699653 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -406,18 +406,15 @@ crate fn run_global_ctxt( let mut krate = tcx.sess.time("clean_crate", || clean::krate(&mut ctxt)); if krate.module.doc_value().map(|d| d.is_empty()).unwrap_or(true) { - let help = format!( - "The following guide may be of use:\n\ - https://doc.rust-lang.org/{}/rustdoc/how-to-write-documentation.html", - crate::doc_rust_lang_org_channel(), - ); + let help = "The following guide may be of use:\n\ + https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation.html"; tcx.struct_lint_node( crate::lint::MISSING_CRATE_LEVEL_DOCS, DocContext::as_local_hir_id(tcx, krate.module.def_id).unwrap(), |lint| { let mut diag = lint.build("no documentation found for this crate's top-level module"); - diag.help(&help); + diag.help(help); diag.emit(); }, ); diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 2a25b595625c9..169ef015fa88c 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -82,8 +82,6 @@ use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGro use rustc_session::getopts; use rustc_session::{early_error, early_warn}; -use crate::clean::utils::doc_rust_lang_org_channel; - /// A macro to create a FxHashMap. /// /// Example: @@ -606,10 +604,7 @@ fn usage(argv0: &str) { } println!("{}", options.usage(&format!("{} [options] ", argv0))); println!(" @path Read newline separated options from `path`\n"); - println!( - "More information available at https://doc.rust-lang.org/{}/rustdoc/what-is-rustdoc.html", - doc_rust_lang_org_channel() - ); + println!("More information available at https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html") } /// A result type used by several functions under `main()`. diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index c5fb54e52c7c4..25b6c187f3b27 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -2017,14 +2017,7 @@ fn disambiguator_error( msg: &str, ) { diag_info.link_range = disambiguator_range; - report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, msg, &diag_info, |diag, _sp| { - let msg = format!( - "see https://doc.rust-lang.org/{}/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators \ - for more info about disambiguators", - crate::doc_rust_lang_org_channel(), - ); - diag.note(&msg); - }); + report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, msg, &diag_info, |_diag, _sp| {}); } /// Report an ambiguity error, where there were multiple possible resolutions. diff --git a/src/test/rustdoc-ui/intra-doc/email-address-localhost.stderr b/src/test/rustdoc-ui/intra-doc/email-address-localhost.stderr index f287f87408c48..de215b2163bd4 100644 --- a/src/test/rustdoc-ui/intra-doc/email-address-localhost.stderr +++ b/src/test/rustdoc-ui/intra-doc/email-address-localhost.stderr @@ -10,7 +10,6 @@ note: the lint level is defined here LL | #![deny(warnings)] | ^^^^^^^^ = note: `#[deny(rustdoc::broken_intra_doc_links)]` implied by `#[deny(warnings)]` - = note: see https://doc.rust-lang.org/nightly/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators error: aborting due to previous error diff --git a/src/test/rustdoc-ui/intra-doc/unknown-disambiguator.stderr b/src/test/rustdoc-ui/intra-doc/unknown-disambiguator.stderr index 94d6d4616518e..195aaca32a27d 100644 --- a/src/test/rustdoc-ui/intra-doc/unknown-disambiguator.stderr +++ b/src/test/rustdoc-ui/intra-doc/unknown-disambiguator.stderr @@ -10,47 +10,36 @@ note: the lint level is defined here LL | #![deny(warnings)] | ^^^^^^^^ = note: `#[deny(rustdoc::broken_intra_doc_links)]` implied by `#[deny(warnings)]` - = note: see https://doc.rust-lang.org/nightly/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators error: unknown disambiguator `bar` --> $DIR/unknown-disambiguator.rs:3:35 | LL | //! Linking to [foo@banana] and [`bar@banana!()`]. | ^^^ - | - = note: see https://doc.rust-lang.org/nightly/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators error: unknown disambiguator `foo` --> $DIR/unknown-disambiguator.rs:9:34 | LL | //! And with weird backticks: [``foo@hello``] [foo`@`hello]. | ^^^ - | - = note: see https://doc.rust-lang.org/nightly/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators error: unknown disambiguator `foo` --> $DIR/unknown-disambiguator.rs:9:48 | LL | //! And with weird backticks: [``foo@hello``] [foo`@`hello]. | ^^^ - | - = note: see https://doc.rust-lang.org/nightly/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators error: unknown disambiguator `` --> $DIR/unknown-disambiguator.rs:6:31 | LL | //! And to [no disambiguator](@nectarine) and [another](@apricot!()). | ^ - | - = note: see https://doc.rust-lang.org/nightly/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators error: unknown disambiguator `` --> $DIR/unknown-disambiguator.rs:6:57 | LL | //! And to [no disambiguator](@nectarine) and [another](@apricot!()). | ^ - | - = note: see https://doc.rust-lang.org/nightly/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators error: aborting due to 6 previous errors From b6f3dbb65d0be4ecf94bb87f859db1f66b79f74d Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 5 May 2021 16:40:06 +0200 Subject: [PATCH 23/29] Bump map_into_keys_values stable version to 1.54.0. --- library/alloc/src/collections/btree/map.rs | 28 +++++++++++----------- library/std/src/collections/hash/map.rs | 24 +++++++++---------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 02e2924623c9d..0a46387c34e9c 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -398,12 +398,12 @@ impl fmt::Debug for ValuesMut<'_, K, V> { /// See its documentation for more. /// /// [`into_keys`]: BTreeMap::into_keys -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] pub struct IntoKeys { inner: IntoIter, } -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl fmt::Debug for IntoKeys { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.inner.iter().map(|(key, _)| key)).finish() @@ -416,12 +416,12 @@ impl fmt::Debug for IntoKeys { /// See its documentation for more. /// /// [`into_values`]: BTreeMap::into_values -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] pub struct IntoValues { inner: IntoIter, } -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl fmt::Debug for IntoValues { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.inner.iter().map(|(_, val)| val)).finish() @@ -1252,7 +1252,7 @@ impl BTreeMap { /// assert_eq!(keys, [1, 2]); /// ``` #[inline] - #[stable(feature = "map_into_keys_values", since = "1.53.0")] + #[stable(feature = "map_into_keys_values", since = "1.54.0")] pub fn into_keys(self) -> IntoKeys { IntoKeys { inner: self.into_iter() } } @@ -1274,7 +1274,7 @@ impl BTreeMap { /// assert_eq!(values, ["hello", "goodbye"]); /// ``` #[inline] - #[stable(feature = "map_into_keys_values", since = "1.53.0")] + #[stable(feature = "map_into_keys_values", since = "1.54.0")] pub fn into_values(self) -> IntoValues { IntoValues { inner: self.into_iter() } } @@ -1774,7 +1774,7 @@ impl<'a, K, V> Range<'a, K, V> { } } -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl Iterator for IntoKeys { type Item = K; @@ -1799,24 +1799,24 @@ impl Iterator for IntoKeys { } } -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl DoubleEndedIterator for IntoKeys { fn next_back(&mut self) -> Option { self.inner.next_back().map(|(k, _)| k) } } -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl ExactSizeIterator for IntoKeys { fn len(&self) -> usize { self.inner.len() } } -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl FusedIterator for IntoKeys {} -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl Iterator for IntoValues { type Item = V; @@ -1833,21 +1833,21 @@ impl Iterator for IntoValues { } } -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl DoubleEndedIterator for IntoValues { fn next_back(&mut self) -> Option { self.inner.next_back().map(|(_, v)| v) } } -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl ExactSizeIterator for IntoValues { fn len(&self) -> usize { self.inner.len() } } -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl FusedIterator for IntoValues {} #[stable(feature = "btree_range", since = "1.17.0")] diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 32c868fb4ab04..84c7e2e9ea243 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -972,7 +972,7 @@ where /// let vec: Vec<&str> = map.into_keys().collect(); /// ``` #[inline] - #[stable(feature = "map_into_keys_values", since = "1.53.0")] + #[stable(feature = "map_into_keys_values", since = "1.54.0")] pub fn into_keys(self) -> IntoKeys { IntoKeys { inner: self.into_iter() } } @@ -994,7 +994,7 @@ where /// let vec: Vec = map.into_values().collect(); /// ``` #[inline] - #[stable(feature = "map_into_keys_values", since = "1.53.0")] + #[stable(feature = "map_into_keys_values", since = "1.54.0")] pub fn into_values(self) -> IntoValues { IntoValues { inner: self.into_iter() } } @@ -1409,7 +1409,7 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> { /// map.insert("a", 1); /// let iter_keys = map.into_keys(); /// ``` -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] pub struct IntoKeys { inner: IntoIter, } @@ -1430,7 +1430,7 @@ pub struct IntoKeys { /// map.insert("a", 1); /// let iter_keys = map.into_values(); /// ``` -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] pub struct IntoValues { inner: IntoIter, } @@ -2131,7 +2131,7 @@ impl fmt::Debug for ValuesMut<'_, K, V> { } } -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl Iterator for IntoKeys { type Item = K; @@ -2144,24 +2144,24 @@ impl Iterator for IntoKeys { self.inner.size_hint() } } -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl ExactSizeIterator for IntoKeys { #[inline] fn len(&self) -> usize { self.inner.len() } } -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl FusedIterator for IntoKeys {} -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl fmt::Debug for IntoKeys { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.inner.iter().map(|(k, _)| k)).finish() } } -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl Iterator for IntoValues { type Item = V; @@ -2174,17 +2174,17 @@ impl Iterator for IntoValues { self.inner.size_hint() } } -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl ExactSizeIterator for IntoValues { #[inline] fn len(&self) -> usize { self.inner.len() } } -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl FusedIterator for IntoValues {} -#[stable(feature = "map_into_keys_values", since = "1.53.0")] +#[stable(feature = "map_into_keys_values", since = "1.54.0")] impl fmt::Debug for IntoValues { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.inner.iter().map(|(_, v)| v)).finish() From 3584c1dd0cb501916c8bc6fd452864b261068beb Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Mon, 3 May 2021 08:47:43 -0700 Subject: [PATCH 24/29] Disallows `#![feature(no_coverage)]` on stable and beta using allow_internal_unstable (as recommended) Fixes: #84836 ```shell $ ./build/x86_64-unknown-linux-gnu/stage1/bin/rustc src/test/run-make-fulldeps/coverage/no_cov_crate.rs error[E0554]: `#![feature]` may not be used on the dev release channel --> src/test/run-make-fulldeps/coverage/no_cov_crate.rs:2:1 | 2 | #![feature(no_coverage)] | ^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error For more information about this error, try `rustc --explain E0554`. ``` --- .../src/deriving/cmp/eq.rs | 14 ++-------- compiler/rustc_feature/src/builtin_attrs.rs | 8 +----- compiler/rustc_typeck/src/collect.rs | 28 +------------------ library/core/src/cmp.rs | 5 ++-- library/core/src/lib.rs | 1 + .../expected_show_coverage.no_cov_func.txt | 19 ------------- .../run-make-fulldeps/coverage/no_cov_func.rs | 18 ------------ .../feature-gates/feature-gate-no_coverage.rs | 13 ++++++--- .../feature-gate-no_coverage.stderr | 3 +- 9 files changed, 18 insertions(+), 91 deletions(-) delete mode 100644 src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt delete mode 100644 src/test/run-make-fulldeps/coverage/no_cov_func.rs diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index 5a4e7fd9d07b4..54ab88dc3ffc9 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -15,20 +15,12 @@ pub fn expand_deriving_eq( item: &Annotatable, push: &mut dyn FnMut(Annotatable), ) { + let span = cx.with_def_site_ctxt(span); let inline = cx.meta_word(span, sym::inline); - let no_coverage_ident = - rustc_ast::attr::mk_nested_word_item(Ident::new(sym::no_coverage, span)); - let no_coverage_feature = - rustc_ast::attr::mk_list_item(Ident::new(sym::feature, span), vec![no_coverage_ident]); - let no_coverage = cx.meta_word(span, sym::no_coverage); let hidden = rustc_ast::attr::mk_nested_word_item(Ident::new(sym::hidden, span)); let doc = rustc_ast::attr::mk_list_item(Ident::new(sym::doc, span), vec![hidden]); - let attrs = vec![ - cx.attribute(inline), - cx.attribute(no_coverage_feature), - cx.attribute(no_coverage), - cx.attribute(doc), - ]; + let no_coverage = cx.meta_word(span, sym::no_coverage); + let attrs = vec![cx.attribute(inline), cx.attribute(doc), cx.attribute(no_coverage)]; let trait_def = TraitDef { span, attributes: Vec::new(), diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 5474fea9c7857..a8719be84c2a4 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -273,13 +273,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(List: "address, memory, thread"), experimental!(no_sanitize) ), - ungated!( - // Not exclusively gated at the crate level (though crate-level is - // supported). The feature can alternatively be enabled on individual - // functions. - no_coverage, AssumedUsed, - template!(Word), - ), + gated!(no_coverage, AssumedUsed, template!(Word), experimental!(no_coverage)), // FIXME: #14408 assume docs are used since rustdoc looks at them. ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")), diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 190c9d35934f9..0528f8812f920 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -2661,8 +2661,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { let mut inline_span = None; let mut link_ordinal_span = None; let mut no_sanitize_span = None; - let mut no_coverage_feature_enabled = false; - let mut no_coverage_attr = None; for attr in attrs.iter() { if tcx.sess.check_name(attr, sym::cold) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD; @@ -2726,15 +2724,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED; } else if tcx.sess.check_name(attr, sym::no_mangle) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; - } else if attr.has_name(sym::feature) { - if let Some(list) = attr.meta_item_list() { - if list.iter().any(|nested_meta_item| nested_meta_item.has_name(sym::no_coverage)) { - tcx.sess.mark_attr_used(attr); - no_coverage_feature_enabled = true; - } - } } else if tcx.sess.check_name(attr, sym::no_coverage) { - no_coverage_attr = Some(attr); + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE; } else if tcx.sess.check_name(attr, sym::rustc_std_internal_symbol) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; } else if tcx.sess.check_name(attr, sym::used) { @@ -2945,23 +2936,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { } } - if let Some(no_coverage_attr) = no_coverage_attr { - if tcx.sess.features_untracked().no_coverage || no_coverage_feature_enabled { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE - } else { - let mut err = feature_err( - &tcx.sess.parse_sess, - sym::no_coverage, - no_coverage_attr.span, - "the `#[no_coverage]` attribute is an experimental feature", - ); - if tcx.sess.parse_sess.unstable_features.is_nightly_build() { - err.help("or, alternatively, add `#[feature(no_coverage)]` to the function"); - } - err.emit(); - } - } - codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| { if !attr.has_name(sym::inline) { return ia; diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 0a3e5789e8bed..f8b16b6f9275c 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -274,8 +274,7 @@ pub trait Eq: PartialEq { // // This should never be implemented by hand. #[doc(hidden)] - #[cfg_attr(not(bootstrap), feature(no_coverage))] - #[cfg_attr(not(bootstrap), no_coverage)] + #[cfg_attr(not(bootstrap), no_coverage)] // rust-lang/rust#84605 #[inline] #[stable(feature = "rust1", since = "1.0.0")] fn assert_receiver_is_total_eq(&self) {} @@ -284,7 +283,7 @@ pub trait Eq: PartialEq { /// Derive macro generating an impl of the trait `Eq`. #[rustc_builtin_macro] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match)] +#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match, no_coverage)] pub macro Eq($item:item) { /* compiler built-in */ } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 0e2c140c367a9..d1329b8f22116 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -166,6 +166,7 @@ #![feature(const_caller_location)] #![feature(slice_ptr_get)] #![feature(no_niche)] // rust-lang/rust#68303 +#![cfg_attr(not(bootstrap), feature(no_coverage))] // rust-lang/rust#84605 #![feature(int_error_matching)] #![deny(unsafe_op_in_unsafe_fn)] diff --git a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt deleted file mode 100644 index 16eaf7c858c19..0000000000000 --- a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt +++ /dev/null @@ -1,19 +0,0 @@ - 1| |// Enables `no_coverage` on individual functions - 2| | - 3| |#[feature(no_coverage)] - 4| |#[no_coverage] - 5| |fn do_not_add_coverage_1() { - 6| | println!("called but not covered"); - 7| |} - 8| | - 9| |#[no_coverage] - 10| |#[feature(no_coverage)] - 11| |fn do_not_add_coverage_2() { - 12| | println!("called but not covered"); - 13| |} - 14| | - 15| 1|fn main() { - 16| 1| do_not_add_coverage_1(); - 17| 1| do_not_add_coverage_2(); - 18| 1|} - diff --git a/src/test/run-make-fulldeps/coverage/no_cov_func.rs b/src/test/run-make-fulldeps/coverage/no_cov_func.rs deleted file mode 100644 index e19a2c4a87200..0000000000000 --- a/src/test/run-make-fulldeps/coverage/no_cov_func.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Enables `no_coverage` on individual functions - -#[feature(no_coverage)] -#[no_coverage] -fn do_not_add_coverage_1() { - println!("called but not covered"); -} - -#[no_coverage] -#[feature(no_coverage)] -fn do_not_add_coverage_2() { - println!("called but not covered"); -} - -fn main() { - do_not_add_coverage_1(); - do_not_add_coverage_2(); -} diff --git a/src/test/ui/feature-gates/feature-gate-no_coverage.rs b/src/test/ui/feature-gates/feature-gate-no_coverage.rs index c6b79f9a43171..fd4c6f76059aa 100644 --- a/src/test/ui/feature-gates/feature-gate-no_coverage.rs +++ b/src/test/ui/feature-gates/feature-gate-no_coverage.rs @@ -1,8 +1,13 @@ #![crate_type = "lib"] -#[no_coverage] -#[feature(no_coverage)] // does not have to be enabled before `#[no_coverage]` -fn no_coverage_is_enabled_on_this_function() {} +#[derive(PartialEq, Eq)] // ensure deriving `Eq` does not enable `feature(no_coverage)` +struct Foo { + a: u8, + b: u32, +} #[no_coverage] //~ ERROR the `#[no_coverage]` attribute is an experimental feature -fn requires_feature_no_coverage() {} +fn requires_feature_no_coverage() -> bool { + let bar = Foo { a: 0, b: 0 }; + bar == Foo { a: 0, b: 0 } +} diff --git a/src/test/ui/feature-gates/feature-gate-no_coverage.stderr b/src/test/ui/feature-gates/feature-gate-no_coverage.stderr index 04627be4aaf65..f7167e0b771c0 100644 --- a/src/test/ui/feature-gates/feature-gate-no_coverage.stderr +++ b/src/test/ui/feature-gates/feature-gate-no_coverage.stderr @@ -1,12 +1,11 @@ error[E0658]: the `#[no_coverage]` attribute is an experimental feature - --> $DIR/feature-gate-no_coverage.rs:7:1 + --> $DIR/feature-gate-no_coverage.rs:9:1 | LL | #[no_coverage] | ^^^^^^^^^^^^^^ | = note: see issue #84605 for more information = help: add `#![feature(no_coverage)]` to the crate attributes to enable - = help: or, alternatively, add `#[feature(no_coverage)]` to the function error: aborting due to previous error From 4617b03316dc5816387a748f0f2e1196be28db23 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 5 May 2021 16:00:18 +0800 Subject: [PATCH 25/29] E0583: Include secondary path in error message --- compiler/rustc_expand/src/module.rs | 9 +++++---- src/test/ui/error-codes/E0583.stderr | 2 +- .../invalid-module-declaration.stderr | 2 +- .../missing_non_modrs_mod/missing_non_modrs_mod.stderr | 2 +- .../missing_non_modrs_mod_inline.stderr | 2 +- src/test/ui/parser/mod_file_not_exist.stderr | 2 +- src/test/ui/parser/mod_file_not_exist_windows.stderr | 2 +- src/test/ui/parser/unsafe-mod.stderr | 2 +- src/test/ui/rfc-2457/mod_file_nonascii_forbidden.stderr | 2 +- 9 files changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 993522d01d867..4d777049f0d62 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -36,7 +36,7 @@ crate struct ParsedExternalMod { pub enum ModError<'a> { CircularInclusion(Vec), ModInBlock(Option), - FileNotFound(Ident, PathBuf), + FileNotFound(Ident, PathBuf, PathBuf), MultipleCandidates(Ident, PathBuf, PathBuf), ParserError(DiagnosticBuilder<'a>), } @@ -219,7 +219,7 @@ pub fn default_submod_path<'a>( file_path: secondary_path, dir_ownership: DirOwnership::Owned { relative: None }, }), - (false, false) => Err(ModError::FileNotFound(ident, default_path)), + (false, false) => Err(ModError::FileNotFound(ident, default_path, secondary_path)), (true, true) => Err(ModError::MultipleCandidates(ident, default_path, secondary_path)), } } @@ -247,7 +247,7 @@ impl ModError<'_> { } err } - ModError::FileNotFound(ident, default_path) => { + ModError::FileNotFound(ident, default_path, secondary_path) => { let mut err = struct_span_err!( diag, span, @@ -256,9 +256,10 @@ impl ModError<'_> { ident, ); err.help(&format!( - "to create the module `{}`, create file \"{}\"", + "to create the module `{}`, create file \"{}\" or \"{}\"", ident, default_path.display(), + secondary_path.display(), )); err } diff --git a/src/test/ui/error-codes/E0583.stderr b/src/test/ui/error-codes/E0583.stderr index dbe700355957b..c7bbbf114997d 100644 --- a/src/test/ui/error-codes/E0583.stderr +++ b/src/test/ui/error-codes/E0583.stderr @@ -4,7 +4,7 @@ error[E0583]: file not found for module `module_that_doesnt_exist` LL | mod module_that_doesnt_exist; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: to create the module `module_that_doesnt_exist`, create file "$DIR/module_that_doesnt_exist.rs" + = help: to create the module `module_that_doesnt_exist`, create file "$DIR/module_that_doesnt_exist.rs" or "$DIR/module_that_doesnt_exist/mod.rs" error: aborting due to previous error diff --git a/src/test/ui/invalid-module-declaration/invalid-module-declaration.stderr b/src/test/ui/invalid-module-declaration/invalid-module-declaration.stderr index 52296042eb4a7..7bc8efd7e4a97 100644 --- a/src/test/ui/invalid-module-declaration/invalid-module-declaration.stderr +++ b/src/test/ui/invalid-module-declaration/invalid-module-declaration.stderr @@ -4,7 +4,7 @@ error[E0583]: file not found for module `baz` LL | pub mod baz; | ^^^^^^^^^^^^ | - = help: to create the module `baz`, create file "$DIR/auxiliary/foo/bar/baz.rs" + = help: to create the module `baz`, create file "$DIR/auxiliary/foo/bar/baz.rs" or "$DIR/auxiliary/foo/bar/baz/mod.rs" error: aborting due to previous error diff --git a/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr b/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr index 91b3fe15c4be7..31e4206a5463a 100644 --- a/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr +++ b/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr @@ -4,7 +4,7 @@ error[E0583]: file not found for module `missing` LL | mod missing; | ^^^^^^^^^^^^ | - = help: to create the module `missing`, create file "$DIR/foo/missing.rs" + = help: to create the module `missing`, create file "$DIR/foo/missing.rs" or "$DIR/foo/missing/mod.rs" error: aborting due to previous error diff --git a/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr b/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr index f519de46c767f..9d252398b7a14 100644 --- a/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr +++ b/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr @@ -4,7 +4,7 @@ error[E0583]: file not found for module `missing` LL | mod missing; | ^^^^^^^^^^^^ | - = help: to create the module `missing`, create file "$DIR/foo_inline/inline/missing.rs" + = help: to create the module `missing`, create file "$DIR/foo_inline/inline/missing.rs" or "$DIR/foo_inline/inline/missing/mod.rs" error: aborting due to previous error diff --git a/src/test/ui/parser/mod_file_not_exist.stderr b/src/test/ui/parser/mod_file_not_exist.stderr index 4e08125625f0a..62456d518804f 100644 --- a/src/test/ui/parser/mod_file_not_exist.stderr +++ b/src/test/ui/parser/mod_file_not_exist.stderr @@ -4,7 +4,7 @@ error[E0583]: file not found for module `not_a_real_file` LL | mod not_a_real_file; | ^^^^^^^^^^^^^^^^^^^^ | - = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" + = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" or "$DIR/not_a_real_file/mod.rs" error[E0433]: failed to resolve: use of undeclared crate or module `mod_file_aux` --> $DIR/mod_file_not_exist.rs:7:16 diff --git a/src/test/ui/parser/mod_file_not_exist_windows.stderr b/src/test/ui/parser/mod_file_not_exist_windows.stderr index 73cdf098b00c9..d5143dbe982ae 100644 --- a/src/test/ui/parser/mod_file_not_exist_windows.stderr +++ b/src/test/ui/parser/mod_file_not_exist_windows.stderr @@ -4,7 +4,7 @@ error[E0583]: file not found for module `not_a_real_file` LL | mod not_a_real_file; | ^^^^^^^^^^^^^^^^^^^^ | - = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" + = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" or "$DIR/not_a_real_file/mod.rs" error[E0433]: failed to resolve: use of undeclared crate or module `mod_file_aux` --> $DIR/mod_file_not_exist_windows.rs:7:16 diff --git a/src/test/ui/parser/unsafe-mod.stderr b/src/test/ui/parser/unsafe-mod.stderr index 259b2c1d61e08..dac6e7a355056 100644 --- a/src/test/ui/parser/unsafe-mod.stderr +++ b/src/test/ui/parser/unsafe-mod.stderr @@ -4,7 +4,7 @@ error[E0583]: file not found for module `n` LL | unsafe mod n; | ^^^^^^^^^^^^^ | - = help: to create the module `n`, create file "$DIR/n.rs" + = help: to create the module `n`, create file "$DIR/n.rs" or "$DIR/n/mod.rs" error: module cannot be declared unsafe --> $DIR/unsafe-mod.rs:1:1 diff --git a/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.stderr b/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.stderr index e857a1e60e5fe..dd0dac95e3646 100644 --- a/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.stderr +++ b/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.stderr @@ -4,7 +4,7 @@ error[E0583]: file not found for module `řųśť` LL | mod řųśť; | ^^^^^^^^^ | - = help: to create the module `řųśť`, create file "$DIR/řųśť.rs" + = help: to create the module `řųśť`, create file "$DIR/řųśť.rs" or "$DIR/řųśť/mod.rs" error[E0754]: trying to load file for module `řųśť` with non-ascii identifier name --> $DIR/mod_file_nonascii_forbidden.rs:1:5 From 568d9c5547c275de2b1533bc7dd8d3d018b17bec Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Thu, 29 Apr 2021 16:57:58 +0000 Subject: [PATCH 26/29] compiletest: Add --target-panic, needs-unwind --- src/tools/compiletest/src/common.rs | 10 ++++++++++ src/tools/compiletest/src/header.rs | 8 +++++++- src/tools/compiletest/src/main.rs | 10 +++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index b7693a3cb1431..09ced203b7931 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -171,6 +171,12 @@ impl fmt::Display for Debugger { } } +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum PanicStrategy { + Unwind, + Abort, +} + /// Configuration for compiletest #[derive(Debug, Clone)] pub struct Config { @@ -262,6 +268,10 @@ pub struct Config { /// Flags to pass to the compiler when building for the target pub target_rustcflags: Option, + /// What panic strategy the target is built with. Unwind supports Abort, but + /// not vice versa. + pub target_panic: PanicStrategy, + /// Target system to be tested pub target: String, diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index f31a24738df6c..d7ddb9437aaa2 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; use tracing::*; -use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PassMode}; +use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PanicStrategy, PassMode}; use crate::util; use crate::{extract_cdb_version, extract_gdb_version}; @@ -111,6 +111,12 @@ impl EarlyProps { props.ignore = true; } + if config.target_panic == PanicStrategy::Abort + && config.parse_name_directive(ln, "needs-unwind") + { + props.ignore = true; + } + if config.target == "wasm32-unknown-unknown" && config.parse_check_run_results(ln) { props.ignore = true; } diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 480916018619d..17e4cfdedc6a7 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -5,7 +5,9 @@ extern crate test; -use crate::common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS}; +use crate::common::{ + expected_output_path, output_base_dir, output_relative_path, PanicStrategy, UI_EXTENSIONS, +}; use crate::common::{CompareMode, Config, Debugger, Mode, PassMode, Pretty, TestPaths}; use crate::util::logv; use getopts::Options; @@ -98,6 +100,7 @@ pub fn parse_config(args: Vec) -> Config { ) .optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS") .optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS") + .optopt("", "target-panic", "what panic strategy the target supports", "unwind | abort") .optflag("", "verbose", "run tests verbosely, showing all output") .optflag( "", @@ -238,6 +241,11 @@ pub fn parse_config(args: Vec) -> Config { runtool: matches.opt_str("runtool"), host_rustcflags: matches.opt_str("host-rustcflags"), target_rustcflags: matches.opt_str("target-rustcflags"), + target_panic: match matches.opt_str("target-panic").as_deref() { + Some("unwind") | None => PanicStrategy::Unwind, + Some("abort") => PanicStrategy::Abort, + _ => panic!("unknown `--target-panic` option `{}` given", mode), + }, target, host: opt_str2(matches.opt_str("host")), cdb, From e1a8ecf771485d517010c66b8532c39635725813 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Thu, 29 Apr 2021 17:04:16 +0000 Subject: [PATCH 27/29] Add needs-unwind to tests --- src/test/ui/cfg/cfg-panic.rs | 1 + src/test/ui/panic-handler/weak-lang-item.rs | 1 + src/test/ui/panic-handler/weak-lang-item.stderr | 2 +- src/test/ui/panic-runtime/abort-link-to-unwind-dylib.rs | 1 + src/test/ui/panic-runtime/lto-unwind.rs | 1 + src/test/ui/panic-runtime/transitive-link-a-bunch.rs | 1 + src/test/ui/panic-runtime/want-unwind-got-abort.rs | 1 + src/test/ui/panic-runtime/want-unwind-got-abort2.rs | 1 + src/test/ui/unwind-no-uwtable.rs | 1 + 9 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/test/ui/cfg/cfg-panic.rs b/src/test/ui/cfg/cfg-panic.rs index dbb5932a9bb85..d2113e4f5ecc6 100644 --- a/src/test/ui/cfg/cfg-panic.rs +++ b/src/test/ui/cfg/cfg-panic.rs @@ -1,5 +1,6 @@ // build-pass // compile-flags: -C panic=unwind +// needs-unwind // ignore-emscripten no panic_unwind implementation // ignore-wasm32 no panic_unwind implementation // ignore-wasm64 no panic_unwind implementation diff --git a/src/test/ui/panic-handler/weak-lang-item.rs b/src/test/ui/panic-handler/weak-lang-item.rs index 3fa3822831b52..df31e614cf809 100644 --- a/src/test/ui/panic-handler/weak-lang-item.rs +++ b/src/test/ui/panic-handler/weak-lang-item.rs @@ -1,6 +1,7 @@ // aux-build:weak-lang-items.rs // error-pattern: `#[panic_handler]` function required, but not found // error-pattern: language item required, but not found: `eh_personality` +// needs-unwind since it affects the error output // ignore-emscripten compiled with panic=abort, personality not required #![no_std] diff --git a/src/test/ui/panic-handler/weak-lang-item.stderr b/src/test/ui/panic-handler/weak-lang-item.stderr index 68e3e21df3e08..1f14b20e4514d 100644 --- a/src/test/ui/panic-handler/weak-lang-item.stderr +++ b/src/test/ui/panic-handler/weak-lang-item.stderr @@ -1,5 +1,5 @@ error[E0259]: the name `core` is defined multiple times - --> $DIR/weak-lang-item.rs:8:1 + --> $DIR/weak-lang-item.rs:9:1 | LL | extern crate core; | ^^^^^^^^^^^^^^^^^^ `core` reimported here diff --git a/src/test/ui/panic-runtime/abort-link-to-unwind-dylib.rs b/src/test/ui/panic-runtime/abort-link-to-unwind-dylib.rs index f8368ff69008f..58a90a592c4c0 100644 --- a/src/test/ui/panic-runtime/abort-link-to-unwind-dylib.rs +++ b/src/test/ui/panic-runtime/abort-link-to-unwind-dylib.rs @@ -1,5 +1,6 @@ // build-fail // compile-flags:-C panic=abort -C prefer-dynamic +// needs-unwind // ignore-musl - no dylibs here // ignore-emscripten // ignore-sgx no dynamic lib support diff --git a/src/test/ui/panic-runtime/lto-unwind.rs b/src/test/ui/panic-runtime/lto-unwind.rs index 6f39b76526b64..24048ebe008fa 100644 --- a/src/test/ui/panic-runtime/lto-unwind.rs +++ b/src/test/ui/panic-runtime/lto-unwind.rs @@ -2,6 +2,7 @@ #![allow(unused_variables)] // compile-flags:-C lto -C panic=unwind +// needs-unwind // no-prefer-dynamic // ignore-emscripten no processes // ignore-sgx no processes diff --git a/src/test/ui/panic-runtime/transitive-link-a-bunch.rs b/src/test/ui/panic-runtime/transitive-link-a-bunch.rs index 1848c986e3615..622535a75aff6 100644 --- a/src/test/ui/panic-runtime/transitive-link-a-bunch.rs +++ b/src/test/ui/panic-runtime/transitive-link-a-bunch.rs @@ -1,4 +1,5 @@ // build-fail +// needs-unwind // aux-build:panic-runtime-unwind.rs // aux-build:panic-runtime-abort.rs // aux-build:wants-panic-runtime-unwind.rs diff --git a/src/test/ui/panic-runtime/want-unwind-got-abort.rs b/src/test/ui/panic-runtime/want-unwind-got-abort.rs index 894a5eb38b8e7..c48caaf079077 100644 --- a/src/test/ui/panic-runtime/want-unwind-got-abort.rs +++ b/src/test/ui/panic-runtime/want-unwind-got-abort.rs @@ -1,4 +1,5 @@ // build-fail +// needs-unwind // error-pattern:is incompatible with this crate's strategy of `unwind` // aux-build:panic-runtime-abort.rs // aux-build:panic-runtime-lang-items.rs diff --git a/src/test/ui/panic-runtime/want-unwind-got-abort2.rs b/src/test/ui/panic-runtime/want-unwind-got-abort2.rs index 5955075bae581..7a2e48e2f10a9 100644 --- a/src/test/ui/panic-runtime/want-unwind-got-abort2.rs +++ b/src/test/ui/panic-runtime/want-unwind-got-abort2.rs @@ -1,4 +1,5 @@ // build-fail +// needs-unwind // error-pattern:is incompatible with this crate's strategy of `unwind` // aux-build:panic-runtime-abort.rs // aux-build:wants-panic-runtime-abort.rs diff --git a/src/test/ui/unwind-no-uwtable.rs b/src/test/ui/unwind-no-uwtable.rs index f249d3f457474..0440cf488e8bb 100644 --- a/src/test/ui/unwind-no-uwtable.rs +++ b/src/test/ui/unwind-no-uwtable.rs @@ -1,4 +1,5 @@ // run-pass +// needs-unwind // ignore-windows target requires uwtable // ignore-wasm32-bare no proper panic=unwind support // compile-flags: -C panic=unwind -C force-unwind-tables=n From 1993e1a86d44c0a6eb54b9a1d6c8c636eb094c68 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Fri, 30 Apr 2021 04:01:04 +0000 Subject: [PATCH 28/29] Support multi target-rustcflags for -Zpanic-abort-tests I just need this until rustbuild supports -Cpanic=abort std directly. --- src/test/ui/test-panic-abort-disabled.rs | 2 +- src/tools/compiletest/src/main.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/ui/test-panic-abort-disabled.rs b/src/test/ui/test-panic-abort-disabled.rs index 4adb161d9ee4b..874dbdb42c33c 100644 --- a/src/test/ui/test-panic-abort-disabled.rs +++ b/src/test/ui/test-panic-abort-disabled.rs @@ -1,6 +1,6 @@ // error-pattern:building tests with panic=abort is not supported // no-prefer-dynamic -// compile-flags: --test -Cpanic=abort +// compile-flags: --test -Cpanic=abort -Zpanic-abort-tests=no // run-flags: --test-threads=1 // ignore-wasm no panic or subprocess support diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 17e4cfdedc6a7..5edeefb609982 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -98,8 +98,8 @@ pub fn parse_config(args: Vec) -> Config { (eg. emulator, valgrind)", "PROGRAM", ) - .optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS") - .optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS") + .optmulti("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS") + .optmulti("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS") .optopt("", "target-panic", "what panic strategy the target supports", "unwind | abort") .optflag("", "verbose", "run tests verbosely, showing all output") .optflag( @@ -239,8 +239,8 @@ pub fn parse_config(args: Vec) -> Config { }), logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)), runtool: matches.opt_str("runtool"), - host_rustcflags: matches.opt_str("host-rustcflags"), - target_rustcflags: matches.opt_str("target-rustcflags"), + host_rustcflags: Some(matches.opt_strs("host-rustcflags").join(" ")), + target_rustcflags: Some(matches.opt_strs("target-rustcflags").join(" ")), target_panic: match matches.opt_str("target-panic").as_deref() { Some("unwind") | None => PanicStrategy::Unwind, Some("abort") => PanicStrategy::Abort, From 947ad5838c08c5609f619b96d0baa197cd20f4b0 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Fri, 30 Apr 2021 04:02:05 +0000 Subject: [PATCH 29/29] Fix up/ignore failing ui tests on fuchsia --- src/test/ui/intrinsics/intrinsic-alignment.rs | 1 + src/test/ui/issues/issue-70093.rs | 1 + src/test/ui/structs-enums/rec-align-u64.rs | 1 + src/test/ui/x86stdcall.rs | 1 + 4 files changed, 4 insertions(+) diff --git a/src/test/ui/intrinsics/intrinsic-alignment.rs b/src/test/ui/intrinsics/intrinsic-alignment.rs index 592409ba89f9b..5a27ea8783a2f 100644 --- a/src/test/ui/intrinsics/intrinsic-alignment.rs +++ b/src/test/ui/intrinsics/intrinsic-alignment.rs @@ -14,6 +14,7 @@ mod rusti { target_os = "dragonfly", target_os = "emscripten", target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "macos", target_os = "netbsd", diff --git a/src/test/ui/issues/issue-70093.rs b/src/test/ui/issues/issue-70093.rs index 95ab86ebcb1f4..fbe68fb9379f8 100644 --- a/src/test/ui/issues/issue-70093.rs +++ b/src/test/ui/issues/issue-70093.rs @@ -1,6 +1,7 @@ // run-pass // compile-flags: -Zlink-native-libraries=no -Cdefault-linker-libraries=yes // ignore-windows - this will probably only work on unixish systems +// ignore-fuchsia - missing __libc_start_main for some reason (#84733) #[link(name = "some-random-non-existent-library", kind = "static")] extern "C" {} diff --git a/src/test/ui/structs-enums/rec-align-u64.rs b/src/test/ui/structs-enums/rec-align-u64.rs index 69544b1c06036..cc6412e271a13 100644 --- a/src/test/ui/structs-enums/rec-align-u64.rs +++ b/src/test/ui/structs-enums/rec-align-u64.rs @@ -35,6 +35,7 @@ struct Outer { target_os = "dragonfly", target_os = "emscripten", target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "macos", target_os = "netbsd", diff --git a/src/test/ui/x86stdcall.rs b/src/test/ui/x86stdcall.rs index e1136807b3c43..2bf4cfc500320 100644 --- a/src/test/ui/x86stdcall.rs +++ b/src/test/ui/x86stdcall.rs @@ -27,6 +27,7 @@ pub fn main() { target_os = "dragonfly", target_os = "emscripten", target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "macos", target_os = "netbsd",