From 926e874fd1dccc208bf63db7a4288adf46caa3c3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 12 May 2023 02:08:39 +0000 Subject: [PATCH 01/11] Dont check `must_use` on nested `impl Future` from fn --- compiler/rustc_lint/src/unused.rs | 4 +++- .../ui/lint/unused/auxiliary/must-use-foreign.rs | 12 ++++++++++++ tests/ui/lint/unused/must-use-foreign.rs | 15 +++++++++++++++ tests/ui/lint/unused/unused-async.rs | 2 +- tests/ui/lint/unused/unused-async.stderr | 13 +------------ 5 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 tests/ui/lint/unused/auxiliary/must-use-foreign.rs create mode 100644 tests/ui/lint/unused/must-use-foreign.rs diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index eb175e96997b3..0fe140e08d26a 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -103,8 +103,10 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { && let ty = cx.typeck_results().expr_ty(&await_expr) && let ty::Alias(ty::Opaque, ty::AliasTy { def_id: future_def_id, .. }) = ty.kind() && cx.tcx.ty_is_opaque_future(ty) - // FIXME: This also includes non-async fns that return `impl Future`. && let async_fn_def_id = cx.tcx.parent(*future_def_id) + && matches!(cx.tcx.def_kind(async_fn_def_id), DefKind::Fn | DefKind::AssocFn) + // Check that this `impl Future` actually comes from an `async fn` + && cx.tcx.asyncness(async_fn_def_id).is_async() && check_must_use_def( cx, async_fn_def_id, diff --git a/tests/ui/lint/unused/auxiliary/must-use-foreign.rs b/tests/ui/lint/unused/auxiliary/must-use-foreign.rs new file mode 100644 index 0000000000000..f773f09c3821b --- /dev/null +++ b/tests/ui/lint/unused/auxiliary/must-use-foreign.rs @@ -0,0 +1,12 @@ +// edition:2021 + +use std::future::Future; + +pub struct Manager; + +impl Manager { + #[must_use] + pub async fn new() -> (Self, impl Future) { + (Manager, async {}) + } +} diff --git a/tests/ui/lint/unused/must-use-foreign.rs b/tests/ui/lint/unused/must-use-foreign.rs new file mode 100644 index 0000000000000..21a1105856218 --- /dev/null +++ b/tests/ui/lint/unused/must-use-foreign.rs @@ -0,0 +1,15 @@ +// edition:2021 +// aux-build:must-use-foreign.rs +// check-pass + +extern crate must_use_foreign; + +use must_use_foreign::Manager; + +async fn async_main() { + Manager::new().await.1.await; +} + +fn main() { + let _ = async_main(); +} diff --git a/tests/ui/lint/unused/unused-async.rs b/tests/ui/lint/unused/unused-async.rs index 4be93aa155ad9..6355f47f037c7 100644 --- a/tests/ui/lint/unused/unused-async.rs +++ b/tests/ui/lint/unused/unused-async.rs @@ -33,7 +33,7 @@ async fn test() { foo().await; //~ ERROR unused output of future returned by `foo` that must be used bar(); //~ ERROR unused return value of `bar` that must be used //~^ ERROR unused implementer of `Future` that must be used - bar().await; //~ ERROR unused output of future returned by `bar` that must be used + bar().await; // ok, it's not an async fn baz(); //~ ERROR unused implementer of `Future` that must be used baz().await; // ok } diff --git a/tests/ui/lint/unused/unused-async.stderr b/tests/ui/lint/unused/unused-async.stderr index 1c3702ba265c0..e93a40658f3c2 100644 --- a/tests/ui/lint/unused/unused-async.stderr +++ b/tests/ui/lint/unused/unused-async.stderr @@ -52,17 +52,6 @@ help: use `let _ = ...` to ignore the resulting value LL | let _ = bar(); | +++++++ -error: unused output of future returned by `bar` that must be used - --> $DIR/unused-async.rs:36:5 - | -LL | bar().await; - | ^^^^^^^^^^^ - | -help: use `let _ = ...` to ignore the resulting value - | -LL | let _ = bar().await; - | +++++++ - error: unused implementer of `Future` that must be used --> $DIR/unused-async.rs:37:5 | @@ -71,5 +60,5 @@ LL | baz(); | = note: futures do nothing unless you `.await` or poll them -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors From d371b80c72aa9f70f3818829724999288f2bf7a8 Mon Sep 17 00:00:00 2001 From: rvalue Date: Thu, 18 May 2023 08:49:49 +0800 Subject: [PATCH 02/11] Bump `cc` for `bootstrap` Bump the version of dependency `cc` to v1.0.77 for `bootstrap`, syncing with rust/Cargo.lock Resolves #111700 --- src/bootstrap/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 9420c4fec5fa4..d7c9e754bca2a 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -89,9 +89,9 @@ version = "0.1.0" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" [[package]] name = "cfg-if" From f0f9d88de6b74592a9fb2bb67c9a3c64edfcb54c Mon Sep 17 00:00:00 2001 From: Frank King Date: Wed, 10 May 2023 17:08:28 +0800 Subject: [PATCH 03/11] Add Iterator::map_windows This is inherited from the old PR This method returns an iterator over mapped windows of the starting iterator. Adding the more straight-forward `Iterator::windows` is not easily possible right now as the items are stored in the iterator type, meaning the `next` call would return references to `self`. This is not allowed by the current `Iterator` trait design. This might change once GATs have landed. The idea has been brought up by @m-ou-se here: https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Iterator.3A.3A.7Bpairwise.2C.20windows.7D/near/224587771 Co-authored-by: Lukas Kalbertodt --- library/core/src/iter/adapters/map_windows.rs | 293 ++++++++++++++++++ library/core/src/iter/adapters/mod.rs | 4 + library/core/src/iter/mod.rs | 2 + library/core/src/iter/traits/iterator.rs | 160 +++++++++- .../core/tests/iter/adapters/map_windows.rs | 279 +++++++++++++++++ library/core/tests/iter/adapters/mod.rs | 1 + library/core/tests/lib.rs | 1 + 7 files changed, 739 insertions(+), 1 deletion(-) create mode 100644 library/core/src/iter/adapters/map_windows.rs create mode 100644 library/core/tests/iter/adapters/map_windows.rs diff --git a/library/core/src/iter/adapters/map_windows.rs b/library/core/src/iter/adapters/map_windows.rs new file mode 100644 index 0000000000000..3c0e80b2559db --- /dev/null +++ b/library/core/src/iter/adapters/map_windows.rs @@ -0,0 +1,293 @@ +use crate::{ + fmt, + iter::{ExactSizeIterator, FusedIterator}, + mem::{self, MaybeUninit}, + ptr, +}; + +/// An iterator over the mapped windows of another iterator. +/// +/// This `struct` is created by the [`Iterator::map_windows`]. See its +/// documentation for more information. +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +pub struct MapWindows { + f: F, + inner: MapWindowsInner, +} + +struct MapWindowsInner { + // We fuse the inner iterator because there shouldn't be "holes" in + // the sliding window. Once the iterator returns a `None`, we make + // our `MapWindows` iterator return `None` forever. + iter: Option, + // Since iterators are assumed lazy, i.e. it only yields an item when + // `Iterator::next()` is called, and `MapWindows` is not an exception. + // + // Before the first iteration, we keep the buffer `None`. When the user + // first call `next` or other methods that makes the iterator advance, + // we collect the first `N` items yielded from the inner iterator and + // put it into the buffer. + // + // When the inner iterator has returned a `None` (i.e. fused), we take + // away this `buffer` and leave it `None` to reclaim its resources. + // + // FIXME: should we shrink the size of `buffer` using niche optimization? + buffer: Option>, +} + +// `Buffer` uses two times of space to reduce moves among the iterations. +// `Buffer` is semantically `[MaybeUninit; 2 * N]`. However, due +// to limitations of const generics, we use this different type. Note that +// it has the same underlying memory layout. +struct Buffer { + // Invariant: `self.buffer[self.start..self.start + N]` is initialized, + // with all other elements being uninitialized. This also + // implies that `self.start <= N`. + buffer: [[MaybeUninit; N]; 2], + start: usize, +} + +impl MapWindows { + pub(in crate::iter) fn new(iter: I, f: F) -> Self { + assert!(N != 0, "array in `Iterator::map_windows` must contain more than 0 elements"); + + // Only ZST arrays' length can be so large. + if mem::size_of::() == 0 { + assert!( + N.checked_mul(2).is_some(), + "array size of `Iterator::map_windows` is too large" + ); + } + + Self { inner: MapWindowsInner::new(iter), f } + } +} + +impl MapWindowsInner { + #[inline] + fn new(iter: I) -> Self { + Self { iter: Some(iter), buffer: None } + } + + fn next_window(&mut self) -> Option<&[I::Item; N]> { + let iter = self.iter.as_mut()?; + match self.buffer { + // It is the first time to advance. We collect + // the first `N` items from `self.iter` to initialize `self.buffer`. + None => self.buffer = Buffer::try_from_iter(iter), + Some(ref mut buffer) => match iter.next() { + None => { + // Fuse the inner iterator since it yields a `None`. + self.iter.take(); + self.buffer.take(); + } + // Advance the iterator. We first call `next` before changing our buffer + // at all. This means that if `next` panics, our invariant is upheld and + // our `Drop` impl drops the correct elements. + Some(item) => buffer.push(item), + }, + } + self.buffer.as_ref().map(Buffer::as_array_ref) + } + + fn size_hint(&self) -> (usize, Option) { + let Some(ref iter) = self.iter else { return (0, Some(0)) }; + let (lo, hi) = iter.size_hint(); + if self.buffer.is_some() { + // If the first `N` items are already yielded by the inner iterator, + // the size hint is then equal to the that of the inner iterator's. + (lo, hi) + } else { + // If the first `N` items are not yet yielded by the inner iterator, + // the first `N` elements should be counted as one window, so both bounds + // should subtract `N - 1`. + (lo.saturating_sub(N - 1), hi.map(|hi| hi.saturating_sub(N - 1))) + } + } +} + +impl Buffer { + fn try_from_iter(iter: &mut impl Iterator) -> Option { + let first_half = crate::array::iter_next_chunk(iter).ok()?; + let buffer = [MaybeUninit::new(first_half).transpose(), MaybeUninit::uninit_array()]; + Some(Self { buffer, start: 0 }) + } + + #[inline] + fn buffer_ptr(&self) -> *const MaybeUninit { + self.buffer.as_ptr().cast() + } + + #[inline] + fn buffer_mut_ptr(&mut self) -> *mut MaybeUninit { + self.buffer.as_mut_ptr().cast() + } + + #[inline] + fn as_array_ref(&self) -> &[T; N] { + debug_assert!(self.start + N <= 2 * N); + + // SAFETY: our invariant guarantees these elements are initialized. + unsafe { &*self.buffer_ptr().add(self.start).cast() } + } + + #[inline] + fn as_uninit_array_mut(&mut self) -> &mut MaybeUninit<[T; N]> { + debug_assert!(self.start + N <= 2 * N); + + // SAFETY: our invariant guarantees these elements are in bounds. + unsafe { &mut *self.buffer_mut_ptr().add(self.start).cast() } + } + + /// Pushes a new item `next` to the back, and pops the front-most one. + /// + /// All the elements will be shifted to the front end when pushing reaches + /// the back end. + fn push(&mut self, next: T) { + let buffer_mut_ptr = self.buffer_mut_ptr(); + debug_assert!(self.start + N <= 2 * N); + + let to_drop = if self.start == N { + // We have reached the end of our buffer and have to copy + // everything to the start. Example layout for N = 3. + // + // 0 1 2 3 4 5 0 1 2 3 4 5 + // ┌───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┬───┬───┬───┐ + // │ - │ - │ - │ a │ b │ c │ -> │ b │ c │ n │ - │ - │ - │ + // └───┴───┴───┴───┴───┴───┘ └───┴───┴───┴───┴───┴───┘ + // ↑ ↑ + // start start + + // SAFETY: the two pointers are valid for reads/writes of N -1 + // elements because our array's size is semantically 2 * N. The + // regions also don't overlap for the same reason. + // + // We leave the old elements in place. As soon as `start` is set + // to 0, we treat them as uninitialized and treat their copies + // as initialized. + let to_drop = unsafe { + ptr::copy_nonoverlapping(buffer_mut_ptr.add(self.start + 1), buffer_mut_ptr, N - 1); + (*buffer_mut_ptr.add(N - 1)).write(next); + buffer_mut_ptr.add(self.start) + }; + self.start = 0; + to_drop + } else { + // SAFETY: `self.start` is < N as guaranteed by the invariant + // plus the check above. Even if the drop at the end panics, + // the invariant is upheld. + // + // Example layout for N = 3: + // + // 0 1 2 3 4 5 0 1 2 3 4 5 + // ┌───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┬───┬───┬───┐ + // │ - │ a │ b │ c │ - │ - │ -> │ - │ - │ b │ c │ n │ - │ + // └───┴───┴───┴───┴───┴───┘ └───┴───┴───┴───┴───┴───┘ + // ↑ ↑ + // start start + // + let to_drop = unsafe { + (*buffer_mut_ptr.add(self.start + N)).write(next); + buffer_mut_ptr.add(self.start) + }; + self.start += 1; + to_drop + }; + + // SAFETY: the index is valid and this is element `a` in the + // diagram above and has not been dropped yet. + unsafe { ptr::drop_in_place(to_drop.cast::()) }; + } +} + +impl Clone for Buffer { + fn clone(&self) -> Self { + let mut buffer = Buffer { + buffer: [MaybeUninit::uninit_array(), MaybeUninit::uninit_array()], + start: self.start, + }; + buffer.as_uninit_array_mut().write(self.as_array_ref().clone()); + buffer + } +} + +impl Clone for MapWindowsInner +where + I: Iterator + Clone, + I::Item: Clone, +{ + fn clone(&self) -> Self { + Self { iter: self.iter.clone(), buffer: self.buffer.clone() } + } +} + +impl Drop for Buffer { + fn drop(&mut self) { + // SAFETY: our invariant guarantees that N elements starting from + // `self.start` are initialized. We drop them here. + unsafe { + let initialized_part: *mut [T] = crate::ptr::slice_from_raw_parts_mut( + self.buffer_mut_ptr().add(self.start).cast(), + N, + ); + ptr::drop_in_place(initialized_part); + } + } +} + +#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +impl Iterator for MapWindows +where + I: Iterator, + F: FnMut(&[I::Item; N]) -> R, +{ + type Item = R; + + fn next(&mut self) -> Option { + let window = self.inner.next_window()?; + let out = (self.f)(window); + Some(out) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +// Note that even if the inner iterator not fused, the `MapWindows` is still fused, +// because we don't allow "holes" in the mapping window. +#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +impl FusedIterator for MapWindows +where + I: Iterator, + F: FnMut(&[I::Item; N]) -> R, +{ +} + +#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +impl ExactSizeIterator for MapWindows +where + I: ExactSizeIterator, + F: FnMut(&[I::Item; N]) -> R, +{ +} + +#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +impl fmt::Debug for MapWindows { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MapWindows").field("iter", &self.inner.iter).finish() + } +} + +#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +impl Clone for MapWindows +where + I: Iterator + Clone, + F: Clone, + I::Item: Clone, +{ + fn clone(&self) -> Self { + Self { f: self.f.clone(), inner: self.inner.clone() } + } +} diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index 8cc2b7cec4165..6f4fa7010f446 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -16,6 +16,7 @@ mod inspect; mod intersperse; mod map; mod map_while; +mod map_windows; mod peekable; mod rev; mod scan; @@ -57,6 +58,9 @@ pub use self::intersperse::{Intersperse, IntersperseWith}; #[stable(feature = "iter_map_while", since = "1.57.0")] pub use self::map_while::MapWhile; +#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +pub use self::map_windows::MapWindows; + #[unstable(feature = "trusted_random_access", issue = "none")] pub use self::zip::TrustedRandomAccess; diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index de638552fa378..424504ebd0a5e 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -434,6 +434,8 @@ pub use self::adapters::Copied; pub use self::adapters::Flatten; #[stable(feature = "iter_map_while", since = "1.57.0")] pub use self::adapters::MapWhile; +#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +pub use self::adapters::MapWindows; #[unstable(feature = "inplace_iteration", issue = "none")] pub use self::adapters::SourceIter; #[stable(feature = "iterator_step_by", since = "1.28.0")] diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index dabfce1447483..0eac0e3eb699b 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -10,7 +10,8 @@ use super::super::{ArrayChunks, Chain, Cloned, Copied, Cycle, Enumerate, Filter, use super::super::{FlatMap, Flatten}; use super::super::{FromIterator, Intersperse, IntersperseWith, Product, Sum, Zip}; use super::super::{ - Inspect, Map, MapWhile, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, + Inspect, Map, MapWhile, MapWindows, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, + TakeWhile, }; fn _assert_is_object_safe(_: &dyn Iterator) {} @@ -1591,6 +1592,163 @@ pub trait Iterator { Flatten::new(self) } + /// Calls the given function `f` for each contiguous window of size `N` over + /// `self` and returns an iterator over the outputs of `f`. Like [`slice::windows()`], + /// the windows during mapping overlap as well. + /// + /// In the following example, the closure is called three times with the + /// arguments `&['a', 'b']`, `&['b', 'c']` and `&['c', 'd']` respectively. + /// + /// ``` + /// #![feature(iter_map_windows)] + /// + /// let strings = "abcd".chars() + /// .map_windows(|[x, y]| format!("{}+{}", x, y)) + /// .collect::>(); + /// + /// assert_eq!(strings, vec!["a+b", "b+c", "c+d"]); + /// ``` + /// + /// Note that the const parameter `N` is usually inferred by the + /// destructured argument in the closure. + /// + /// The returned iterator yields 𝑘 − `N` + 1 items (where 𝑘 is the number of + /// items yielded by `self`). If 𝑘 is less than `N`, this method yields an + /// empty iterator. + /// + /// The returned iterator implements [`FusedIterator`], because once `self` + /// returns `None`, even if it returns a `Some(T)` again in the next iterations, + /// we cannot put it into a contigious array buffer, and thus the returned iterator + /// should be fused. + /// + /// [`slice::windows()`]: slice::windows + /// [`FusedIterator`]: crate::iter::FusedIterator + /// + /// # Panics + /// + /// Panics if `N` is 0. This check will most probably get changed to a + /// compile time error before this method gets stabilized. + /// + /// ```should_panic + /// #![feature(iter_map_windows)] + /// + /// let iter = std::iter::repeat(0).map_windows(|&[]| ()); + /// ``` + /// + /// # Examples + /// + /// Building the sums of neighboring numbers. + /// + /// ``` + /// #![feature(iter_map_windows)] + /// + /// let mut it = [1, 3, 8, 1].iter().map_windows(|&[a, b]| a + b); + /// assert_eq!(it.next(), Some(4)); // 1 + 3 + /// assert_eq!(it.next(), Some(11)); // 3 + 8 + /// assert_eq!(it.next(), Some(9)); // 8 + 1 + /// assert_eq!(it.next(), None); + /// ``` + /// + /// Since the elements in the following example implement `Copy`, we can + /// just copy the array and get an iterator over the windows. + /// + /// ``` + /// #![feature(iter_map_windows)] + /// + /// let mut it = "ferris".chars().map_windows(|w: &[_; 3]| *w); + /// assert_eq!(it.next(), Some(['f', 'e', 'r'])); + /// assert_eq!(it.next(), Some(['e', 'r', 'r'])); + /// assert_eq!(it.next(), Some(['r', 'r', 'i'])); + /// assert_eq!(it.next(), Some(['r', 'i', 's'])); + /// assert_eq!(it.next(), None); + /// ``` + /// + /// You can also use this function to check the sortedness of an iterator. + /// For the simple case, rather use [`Iterator::is_sorted`]. + /// + /// ``` + /// #![feature(iter_map_windows)] + /// + /// let mut it = [0.5, 1.0, 3.5, 3.0, 8.5, 8.5, f32::NAN].iter() + /// .map_windows(|[a, b]| a <= b); + /// + /// assert_eq!(it.next(), Some(true)); // 0.5 <= 1.0 + /// assert_eq!(it.next(), Some(true)); // 1.0 <= 3.5 + /// assert_eq!(it.next(), Some(false)); // 3.5 <= 3.0 + /// assert_eq!(it.next(), Some(true)); // 3.0 <= 8.5 + /// assert_eq!(it.next(), Some(true)); // 8.5 <= 8.5 + /// assert_eq!(it.next(), Some(false)); // 8.5 <= NAN + /// assert_eq!(it.next(), None); + /// ``` + /// + /// For non-fused iterators, they are fused after `map_windows`. + /// + /// ``` + /// #![feature(iter_map_windows)] + /// + /// #[derive(Default)] + /// struct NonFusedIterator { + /// state: i32, + /// } + /// + /// impl Iterator for NonFusedIterator { + /// type Item = i32; + /// + /// fn next(&mut self) -> Option { + /// let val = self.state; + /// self.state = self.state + 1; + /// + /// // yields `0..5` first, then only even numbers since `6..`. + /// if val < 5 || val % 2 == 0 { + /// Some(val) + /// } else { + /// None + /// } + /// } + /// } + /// + /// + /// let mut iter = NonFusedIterator::default(); + /// + /// // yields 0..5 first. + /// assert_eq!(iter.next(), Some(0)); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), Some(4)); + /// // then we can see our iterator going back and forth + /// assert_eq!(iter.next(), None); + /// assert_eq!(iter.next(), Some(6)); + /// assert_eq!(iter.next(), None); + /// assert_eq!(iter.next(), Some(8)); + /// assert_eq!(iter.next(), None); + /// + /// // however, with `.map_windows()`, it is fused. + /// let mut iter = NonFusedIterator::default() + /// .map_windows(|arr: &[_; 2]| *arr); + /// + /// assert_eq!(iter.next(), Some([0, 1])); + /// assert_eq!(iter.next(), Some([1, 2])); + /// assert_eq!(iter.next(), Some([2, 3])); + /// assert_eq!(iter.next(), Some([3, 4])); + /// assert_eq!(iter.next(), None); + /// + /// // it will always return `None` after the first time. + /// assert_eq!(iter.next(), None); + /// assert_eq!(iter.next(), None); + /// assert_eq!(iter.next(), None); + /// ``` + #[inline] + #[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] + #[rustc_do_not_const_check] + fn map_windows(self, f: F) -> MapWindows + where + Self: Sized, + F: FnMut(&[Self::Item; N]) -> R, + { + MapWindows::new(self, f) + } + /// Creates an iterator which ends after the first [`None`]. /// /// After an iterator returns [`None`], future calls may or may not yield diff --git a/library/core/tests/iter/adapters/map_windows.rs b/library/core/tests/iter/adapters/map_windows.rs new file mode 100644 index 0000000000000..47f12b28be0ba --- /dev/null +++ b/library/core/tests/iter/adapters/map_windows.rs @@ -0,0 +1,279 @@ +//! These tests mainly make sure the elements are correctly dropped. + +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}; + +#[derive(Debug)] +struct DropInfo { + dropped_twice: AtomicBool, + alive_count: AtomicUsize, +} + +impl DropInfo { + const fn new() -> Self { + Self { dropped_twice: AtomicBool::new(false), alive_count: AtomicUsize::new(0) } + } + + #[track_caller] + fn check(&self) { + assert!(!self.dropped_twice.load(SeqCst), "a value was dropped twice"); + assert_eq!(self.alive_count.load(SeqCst), 0); + } +} + +#[derive(Debug)] +struct DropCheck<'a> { + info: &'a DropInfo, + was_dropped: bool, +} + +impl<'a> DropCheck<'a> { + fn new(info: &'a DropInfo) -> Self { + info.alive_count.fetch_add(1, SeqCst); + + Self { info, was_dropped: false } + } +} + +impl Drop for DropCheck<'_> { + fn drop(&mut self) { + if self.was_dropped { + self.info.dropped_twice.store(true, SeqCst); + } + self.was_dropped = true; + + self.info.alive_count.fetch_sub(1, SeqCst); + } +} + +fn iter(info: &DropInfo, len: usize, panic_at: usize) -> impl Iterator> { + (0..len).map(move |i| { + if i == panic_at { + panic!("intended panic"); + } + DropCheck::new(info) + }) +} + +#[track_caller] +fn check(len: usize, panic_at: usize) { + check_drops(|info| { + iter(info, len, panic_at).map_windows(|_: &[_; N]| {}).last(); + }); +} + +#[track_caller] +fn check_drops(f: impl FnOnce(&DropInfo)) { + let info = DropInfo::new(); + let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + f(&info); + })); + info.check(); +} + +#[test] +fn drop_check_no_iter_panic_n1() { + check::<1>(0, 100); + check::<1>(1, 100); + check::<1>(2, 100); + check::<1>(13, 100); +} + +#[test] +fn drop_check_no_iter_panic_n2() { + check::<2>(0, 100); + check::<2>(1, 100); + check::<2>(2, 100); + check::<2>(3, 100); + check::<2>(13, 100); +} + +#[test] +fn drop_check_no_iter_panic_n5() { + check::<5>(0, 100); + check::<5>(1, 100); + check::<5>(2, 100); + check::<5>(13, 100); + check::<5>(30, 100); +} + +#[test] +fn drop_check_panic_in_first_batch() { + check::<1>(7, 0); + + check::<2>(7, 0); + check::<2>(7, 1); + + check::<3>(7, 0); + check::<3>(7, 1); + check::<3>(7, 2); +} + +#[test] +fn drop_check_panic_in_middle() { + check::<1>(7, 1); + check::<1>(7, 5); + check::<1>(7, 6); + + check::<2>(7, 2); + check::<2>(7, 5); + check::<2>(7, 6); + + check::<5>(13, 5); + check::<5>(13, 8); + check::<5>(13, 12); +} + +#[test] +fn drop_check_len_equals_n() { + check::<1>(1, 100); + check::<1>(1, 0); + + check::<2>(2, 100); + check::<2>(2, 0); + check::<2>(2, 1); + + check::<5>(5, 100); + check::<5>(5, 0); + check::<5>(5, 1); + check::<5>(5, 4); +} + +#[test] +fn output_n1() { + assert_eq!("".chars().map_windows(|[c]| *c).collect::>(), vec![]); + assert_eq!("x".chars().map_windows(|[c]| *c).collect::>(), vec!['x']); + assert_eq!("abcd".chars().map_windows(|[c]| *c).collect::>(), vec!['a', 'b', 'c', 'd']); +} + +#[test] +fn output_n2() { + assert_eq!( + "".chars().map_windows(|a: &[_; 2]| *a).collect::>(), + >::new(), + ); + assert_eq!("ab".chars().map_windows(|a: &[_; 2]| *a).collect::>(), vec![['a', 'b']]); + assert_eq!( + "abcd".chars().map_windows(|a: &[_; 2]| *a).collect::>(), + vec![['a', 'b'], ['b', 'c'], ['c', 'd']], + ); +} + +#[test] +fn test_case_from_pr_82413_comment() { + for () in std::iter::repeat("0".to_owned()).map_windows(|_: &[_; 3]| {}).take(4) {} +} + +#[test] +#[should_panic = "array in `Iterator::map_windows` must contain more than 0 elements"] +fn check_zero_window() { + let _ = std::iter::repeat(0).map_windows(|_: &[_; 0]| ()); +} + +#[test] +fn test_zero_sized_type() { + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + struct Data; + let data: Vec<_> = + std::iter::repeat(Data).take(10).map_windows(|arr: &[Data; 5]| *arr).collect(); + assert_eq!(data, [[Data; 5]; 6]); +} + +#[test] +#[should_panic = "array size of `Iterator::map_windows` is too large"] +fn test_too_large_array_size() { + let _ = std::iter::repeat(()).map_windows(|arr: &[(); usize::MAX]| *arr); +} + +#[test] +fn test_laziness() { + let counter = AtomicUsize::new(0); + let mut iter = (0..5) + .inspect(|_| { + counter.fetch_add(1, SeqCst); + }) + .map_windows(|arr: &[i32; 2]| *arr); + assert_eq!(counter.load(SeqCst), 0); + + assert_eq!(iter.next(), Some([0, 1])); + // The first iteration consumes N items (N = 2). + assert_eq!(counter.load(SeqCst), 2); + + assert_eq!(iter.next(), Some([1, 2])); + assert_eq!(counter.load(SeqCst), 3); + + assert_eq!(iter.next(), Some([2, 3])); + assert_eq!(counter.load(SeqCst), 4); + + assert_eq!(iter.next(), Some([3, 4])); + assert_eq!(counter.load(SeqCst), 5); + + assert_eq!(iter.next(), None); + assert_eq!(counter.load(SeqCst), 5); +} + +#[test] +fn test_size_hint() { + struct SizeHintCheckHelper((usize, Option)); + + impl Iterator for SizeHintCheckHelper { + type Item = i32; + + fn next(&mut self) -> Option { + let (ref mut lo, ref mut hi) = self.0; + let next = (*hi != Some(0)).then_some(0); + *lo = lo.saturating_sub(1); + if let Some(hi) = hi { + *hi = hi.saturating_sub(1); + } + next + } + + fn size_hint(&self) -> (usize, Option) { + self.0 + } + } + + fn check_size_hint( + size_hint: (usize, Option), + mut mapped_size_hint: (usize, Option), + ) { + let mut iter = SizeHintCheckHelper(size_hint); + let mut mapped_iter = iter.by_ref().map_windows(|_: &[_; N]| ()); + while mapped_iter.size_hint().0 > 0 { + assert_eq!(mapped_iter.size_hint(), mapped_size_hint); + assert!(mapped_iter.next().is_some()); + mapped_size_hint.0 -= 1; + mapped_size_hint.1 = mapped_size_hint.1.map(|hi| hi.saturating_sub(1)); + } + } + + check_size_hint::<1>((0, None), (0, None)); + check_size_hint::<1>((0, Some(0)), (0, Some(0))); + check_size_hint::<1>((0, Some(2)), (0, Some(2))); + check_size_hint::<1>((1, None), (1, None)); + check_size_hint::<1>((1, Some(1)), (1, Some(1))); + check_size_hint::<1>((1, Some(4)), (1, Some(4))); + check_size_hint::<1>((5, None), (5, None)); + check_size_hint::<1>((5, Some(5)), (5, Some(5))); + check_size_hint::<1>((5, Some(10)), (5, Some(10))); + + check_size_hint::<2>((0, None), (0, None)); + check_size_hint::<2>((0, Some(0)), (0, Some(0))); + check_size_hint::<2>((0, Some(2)), (0, Some(1))); + check_size_hint::<2>((1, None), (0, None)); + check_size_hint::<2>((1, Some(1)), (0, Some(0))); + check_size_hint::<2>((1, Some(4)), (0, Some(3))); + check_size_hint::<2>((5, None), (4, None)); + check_size_hint::<2>((5, Some(5)), (4, Some(4))); + check_size_hint::<2>((5, Some(10)), (4, Some(9))); + + check_size_hint::<5>((0, None), (0, None)); + check_size_hint::<5>((0, Some(0)), (0, Some(0))); + check_size_hint::<5>((0, Some(2)), (0, Some(0))); + check_size_hint::<5>((1, None), (0, None)); + check_size_hint::<5>((1, Some(1)), (0, Some(0))); + check_size_hint::<5>((1, Some(4)), (0, Some(0))); + check_size_hint::<5>((5, None), (1, None)); + check_size_hint::<5>((5, Some(5)), (1, Some(1))); + check_size_hint::<5>((5, Some(10)), (1, Some(6))); +} diff --git a/library/core/tests/iter/adapters/mod.rs b/library/core/tests/iter/adapters/mod.rs index ca3463aa7f782..dedb4c0a9dd5f 100644 --- a/library/core/tests/iter/adapters/mod.rs +++ b/library/core/tests/iter/adapters/mod.rs @@ -13,6 +13,7 @@ mod fuse; mod inspect; mod intersperse; mod map; +mod map_windows; mod peekable; mod scan; mod skip; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 3933e3289514c..f14a831ae9121 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -109,6 +109,7 @@ #![feature(utf8_chunks)] #![feature(is_ascii_octdigit)] #![feature(get_many_mut)] +#![feature(iter_map_windows)] #![cfg_attr(not(bootstrap), feature(offset_of))] #![deny(unsafe_op_in_unsafe_fn)] #![deny(fuzzy_provenance_casts)] From e6a7fdedd720cce583f30cab41f67bf0a353a75a Mon Sep 17 00:00:00 2001 From: jyn Date: Wed, 17 May 2023 23:15:22 -0500 Subject: [PATCH 04/11] Give a more useful location for where a span_bug was delayed Before: ``` = note: delayed at 0: ::emit_diagnostic at ./compiler/rustc_errors/src/lib.rs:1335:29 1: ::emit_diagnostic at ./compiler/rustc_errors/src/lib.rs:1124:9 ... ``` After: ``` = note: delayed at compiler/rustc_parse/src/parser/diagnostics.rs:2158:28 0: ::emit_diagnostic at ./compiler/rustc_errors/src/lib.rs:1335:29 1: ::emit_diagnostic at ./compiler/rustc_errors/src/lib.rs:1124:9 ``` This both makes the relevant frame easier to find without having to dig through diagnostic internals, and avoids the weird-looking formatting for the first frame. --- compiler/rustc_errors/src/lib.rs | 2 +- compiler/rustc_parse/src/parser/diagnostics.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 0f36047361998..d58555272424c 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1740,7 +1740,7 @@ impl DelayedDiagnostic { } fn decorate(mut self) -> Diagnostic { - self.inner.note(format!("delayed at {}", self.note)); + self.inner.note(format!("delayed at {}\n{}", self.inner.emitted_at, self.note)); self.inner } } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 3002f23da75cc..bcef0f7185fbf 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -238,6 +238,7 @@ impl<'a> DerefMut for SnapshotParser<'a> { impl<'a> Parser<'a> { #[rustc_lint_diagnostics] + #[track_caller] pub fn struct_span_err>( &self, sp: S, From f577cc4dea9bd3bc3c41aa79d786274fb7b8282d Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Thu, 18 May 2023 11:54:51 +0300 Subject: [PATCH 05/11] Fix doc comment for `ConstParamTy` derive See https://github.com/rust-lang/rust/pull/111670#discussion_r1196453888 Thanks @Nilstrieb for the pointer :) --- library/core/src/marker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index ca45683d3d6b5..2bb688b921c9b 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -986,7 +986,7 @@ pub trait PointerLike {} #[rustc_on_unimplemented(message = "`{Self}` can't be used as a const parameter type")] pub trait ConstParamTy: StructuralEq {} -/// Derive macro generating an impl of the trait `Copy`. +/// Derive macro generating an impl of the trait `ConstParamTy`. #[rustc_builtin_macro] #[unstable(feature = "adt_const_params", issue = "95174")] #[cfg(not(bootstrap))] From 30c0e4e72b85c7d125dd05a071027965e2236d64 Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 17 May 2023 04:56:59 +0200 Subject: [PATCH 06/11] Add more tests for the offset_of!() macro * ensuring that offset_of!(Self, ...) works iff inside an impl block * ensuring that the output type is usize and doesn't coerce. this can be changed in the future, but if it is done, it should be a conscious descision * improving the privacy checking test * ensuring that generics don't let you escape the unsized check --- library/core/tests/mem.rs | 15 ++++ tests/ui/offset-of/offset-of-dst-field.rs | 15 ++++ tests/ui/offset-of/offset-of-dst-field.stderr | 46 ++++++++++- tests/ui/offset-of/offset-of-output-type.rs | 20 +++++ .../ui/offset-of/offset-of-output-type.stderr | 64 +++++++++++++++ tests/ui/offset-of/offset-of-private.rs | 12 +++ tests/ui/offset-of/offset-of-private.stderr | 43 +++++++++- tests/ui/offset-of/offset-of-self.rs | 58 ++++++++++++++ tests/ui/offset-of/offset-of-self.stderr | 79 +++++++++++++++++++ 9 files changed, 345 insertions(+), 7 deletions(-) create mode 100644 tests/ui/offset-of/offset-of-output-type.rs create mode 100644 tests/ui/offset-of/offset-of-output-type.stderr create mode 100644 tests/ui/offset-of/offset-of-self.rs create mode 100644 tests/ui/offset-of/offset-of-self.stderr diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index 7f03381690141..0a9850146e5a3 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -386,6 +386,21 @@ fn offset_of() { // Layout of tuples is unstable assert!(offset_of!((u8, u16), 0) <= size_of::<(u8, u16)>() - 1); assert!(offset_of!((u8, u16), 1) <= size_of::<(u8, u16)>() - 2); + + #[repr(C)] + struct Generic { + x: u8, + y: u32, + z: T + } + + // Ensure that this type of generics works + fn offs_of_z() -> usize { + offset_of!(Generic, z) + } + + assert_eq!(offset_of!(Generic, z), 8); + assert_eq!(offs_of_z::(), 8); } #[test] diff --git a/tests/ui/offset-of/offset-of-dst-field.rs b/tests/ui/offset-of/offset-of-dst-field.rs index a0269ca2d1251..89e73b8c6b84d 100644 --- a/tests/ui/offset-of/offset-of-dst-field.rs +++ b/tests/ui/offset-of/offset-of-dst-field.rs @@ -26,8 +26,23 @@ struct Gamma { z: Extern, } +struct Delta { + x: u8, + y: u16, + z: T, +} + fn main() { offset_of!(Alpha, z); //~ ERROR the size for values of type offset_of!(Beta, z); //~ ERROR the size for values of type offset_of!(Gamma, z); //~ ERROR the size for values of type } + +fn delta() { + offset_of!(Delta, z); //~ ERROR the size for values of type + offset_of!(Delta, z); //~ ERROR the size for values of type +} + +fn generic_with_maybe_sized() -> usize { + offset_of!(Delta, z) //~ ERROR the size for values of type +} diff --git a/tests/ui/offset-of/offset-of-dst-field.stderr b/tests/ui/offset-of/offset-of-dst-field.stderr index e6e0f49923620..992eab3d4bdf0 100644 --- a/tests/ui/offset-of/offset-of-dst-field.stderr +++ b/tests/ui/offset-of/offset-of-dst-field.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/offset-of-dst-field.rs:30:5 + --> $DIR/offset-of-dst-field.rs:36:5 | LL | offset_of!(Alpha, z); | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -8,7 +8,7 @@ LL | offset_of!(Alpha, z); = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be known at compilation time - --> $DIR/offset-of-dst-field.rs:31:5 + --> $DIR/offset-of-dst-field.rs:37:5 | LL | offset_of!(Beta, z); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -17,7 +17,7 @@ LL | offset_of!(Beta, z); = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the size for values of type `Extern` cannot be known at compilation time - --> $DIR/offset-of-dst-field.rs:32:5 + --> $DIR/offset-of-dst-field.rs:38:5 | LL | offset_of!(Gamma, z); | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -25,6 +25,44 @@ LL | offset_of!(Gamma, z); = help: the trait `Sized` is not implemented for `Extern` = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 3 previous errors +error[E0277]: the size for values of type `Extern` cannot be known at compilation time + --> $DIR/offset-of-dst-field.rs:43:5 + | +LL | offset_of!(Delta, z); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `Extern` + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/offset-of-dst-field.rs:42:5 + | +LL | offset_of!(Delta, z); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: within `Alpha`, the trait `Sized` is not implemented for `[u8]` +note: required because it appears within the type `Alpha` + --> $DIR/offset-of-dst-field.rs:5:8 + | +LL | struct Alpha { + | ^^^^^ + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/offset-of-dst-field.rs:47:5 + | +LL | fn generic_with_maybe_sized() -> usize { + | - this type parameter needs to be `std::marker::Sized` +LL | offset_of!(Delta, z) + | ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - fn generic_with_maybe_sized() -> usize { +LL + fn generic_with_maybe_sized() -> usize { + | + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/offset-of/offset-of-output-type.rs b/tests/ui/offset-of/offset-of-output-type.rs new file mode 100644 index 0000000000000..50c2e93841f9f --- /dev/null +++ b/tests/ui/offset-of/offset-of-output-type.rs @@ -0,0 +1,20 @@ +#![feature(offset_of)] + +use std::mem::offset_of; + +struct S { + v: u8, + w: u16, +} + + +fn main() { + let _: u8 = offset_of!(S, v); //~ ERROR mismatched types + let _: u16 = offset_of!(S, v); //~ ERROR mismatched types + let _: u32 = offset_of!(S, v); //~ ERROR mismatched types + let _: u64 = offset_of!(S, v); //~ ERROR mismatched types + let _: isize = offset_of!(S, v); //~ ERROR mismatched types + let _: usize = offset_of!(S, v); + + offset_of!(S, v) //~ ERROR mismatched types +} diff --git a/tests/ui/offset-of/offset-of-output-type.stderr b/tests/ui/offset-of/offset-of-output-type.stderr new file mode 100644 index 0000000000000..6f8c94750299e --- /dev/null +++ b/tests/ui/offset-of/offset-of-output-type.stderr @@ -0,0 +1,64 @@ +error[E0308]: mismatched types + --> $DIR/offset-of-output-type.rs:12:17 + | +LL | let _: u8 = offset_of!(S, v); + | -- ^^^^^^^^^^^^^^^^ expected `u8`, found `usize` + | | + | expected due to this + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/offset-of-output-type.rs:13:18 + | +LL | let _: u16 = offset_of!(S, v); + | --- ^^^^^^^^^^^^^^^^ expected `u16`, found `usize` + | | + | expected due to this + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/offset-of-output-type.rs:14:18 + | +LL | let _: u32 = offset_of!(S, v); + | --- ^^^^^^^^^^^^^^^^ expected `u32`, found `usize` + | | + | expected due to this + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/offset-of-output-type.rs:15:18 + | +LL | let _: u64 = offset_of!(S, v); + | --- ^^^^^^^^^^^^^^^^ expected `u64`, found `usize` + | | + | expected due to this + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/offset-of-output-type.rs:16:20 + | +LL | let _: isize = offset_of!(S, v); + | ----- ^^^^^^^^^^^^^^^^ expected `isize`, found `usize` + | | + | expected due to this + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/offset-of-output-type.rs:19:5 + | +LL | fn main() { + | - expected `()` because of default return type +... +LL | offset_of!(S, v) + | ^^^^^^^^^^^^^^^^ expected `()`, found `usize` + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/offset-of/offset-of-private.rs b/tests/ui/offset-of/offset-of-private.rs index 0291b7825cabc..6b1a16ba62b67 100644 --- a/tests/ui/offset-of/offset-of-private.rs +++ b/tests/ui/offset-of/offset-of-private.rs @@ -8,9 +8,21 @@ mod m { pub public: u8, private: u8, } + #[repr(C)] + pub struct FooTuple(pub u8, u8); + #[repr(C)] + struct Bar { + pub public: u8, + private: u8, + } } fn main() { offset_of!(m::Foo, public); offset_of!(m::Foo, private); //~ ERROR field `private` of struct `Foo` is private + offset_of!(m::FooTuple, 0); + offset_of!(m::FooTuple, 1); //~ ERROR field `1` of struct `FooTuple` is private + offset_of!(m::Bar, public); //~ ERROR struct `Bar` is private + offset_of!(m::Bar, private); //~ ERROR struct `Bar` is private + //~| ERROR field `private` of struct `Bar` is private } diff --git a/tests/ui/offset-of/offset-of-private.stderr b/tests/ui/offset-of/offset-of-private.stderr index 8a186dd5a02e7..0674b58f8608c 100644 --- a/tests/ui/offset-of/offset-of-private.stderr +++ b/tests/ui/offset-of/offset-of-private.stderr @@ -1,9 +1,46 @@ +error[E0603]: struct `Bar` is private + --> $DIR/offset-of-private.rs:25:19 + | +LL | offset_of!(m::Bar, public); + | ^^^ private struct + | +note: the struct `Bar` is defined here + --> $DIR/offset-of-private.rs:14:5 + | +LL | struct Bar { + | ^^^^^^^^^^ + +error[E0603]: struct `Bar` is private + --> $DIR/offset-of-private.rs:26:19 + | +LL | offset_of!(m::Bar, private); + | ^^^ private struct + | +note: the struct `Bar` is defined here + --> $DIR/offset-of-private.rs:14:5 + | +LL | struct Bar { + | ^^^^^^^^^^ + error[E0616]: field `private` of struct `Foo` is private - --> $DIR/offset-of-private.rs:15:24 + --> $DIR/offset-of-private.rs:22:24 | LL | offset_of!(m::Foo, private); | ^^^^^^^ private field -error: aborting due to previous error +error[E0616]: field `1` of struct `FooTuple` is private + --> $DIR/offset-of-private.rs:24:29 + | +LL | offset_of!(m::FooTuple, 1); + | ^ private field + +error[E0616]: field `private` of struct `Bar` is private + --> $DIR/offset-of-private.rs:26:24 + | +LL | offset_of!(m::Bar, private); + | ^^^^^^^ private field + +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0616`. +Some errors have detailed explanations: E0603, E0616. +For more information about an error, try `rustc --explain E0603`. diff --git a/tests/ui/offset-of/offset-of-self.rs b/tests/ui/offset-of/offset-of-self.rs new file mode 100644 index 0000000000000..dbeef0e74dc37 --- /dev/null +++ b/tests/ui/offset-of/offset-of-self.rs @@ -0,0 +1,58 @@ +#![feature(offset_of)] + +use std::mem::offset_of; + +struct C { + v: T, + w: T, +} + +struct S { + v: u8, + w: u16, +} + +impl S { + fn v_offs() -> usize { + offset_of!(Self, v) + } + fn v_offs_wrong_syntax() { + offset_of!(Self, Self::v); //~ ERROR no rules expected the token `::` + offset_of!(S, Self); //~ ERROR expected identifier, found keyword `Self` + //~| no field `Self` on type `S` + } + fn offs_in_c() -> usize { + offset_of!(C, w) + } + fn offs_in_c_colon() -> usize { + offset_of!(C::, w) + } +} + +mod m { + use std::mem::offset_of; + fn off() { + offset_of!(self::S, v); //~ ERROR cannot find type `S` in module + offset_of!(super::S, v); + offset_of!(crate::S, v); + } + impl super::n::T { + fn v_offs_self() -> usize { + offset_of!(Self, v) //~ ERROR field `v` of struct `T` is private + } + } +} + +mod n { + pub struct T { v: u8, } +} + +fn main() { + offset_of!(self::S, v); + offset_of!(Self, v); //~ ERROR cannot find type `Self` in this scope + + offset_of!(S, self); //~ ERROR expected identifier, found keyword `self` + //~| no field `self` on type `S` + offset_of!(S, v.self); //~ ERROR expected identifier, found keyword `self` + //~| no field `self` on type `u8` +} diff --git a/tests/ui/offset-of/offset-of-self.stderr b/tests/ui/offset-of/offset-of-self.stderr new file mode 100644 index 0000000000000..df555463f9884 --- /dev/null +++ b/tests/ui/offset-of/offset-of-self.stderr @@ -0,0 +1,79 @@ +error: no rules expected the token `::` + --> $DIR/offset-of-self.rs:20:30 + | +LL | offset_of!(Self, Self::v); + | ^^ no rules expected this token in macro call + | + = note: while trying to match sequence start + +error: expected identifier, found keyword `Self` + --> $DIR/offset-of-self.rs:21:23 + | +LL | offset_of!(S, Self); + | ^^^^ expected identifier, found keyword + +error: expected identifier, found keyword `self` + --> $DIR/offset-of-self.rs:54:19 + | +LL | offset_of!(S, self); + | ^^^^ expected identifier, found keyword + +error: expected identifier, found keyword `self` + --> $DIR/offset-of-self.rs:56:21 + | +LL | offset_of!(S, v.self); + | ^^^^ expected identifier, found keyword + +error[E0412]: cannot find type `S` in module `self` + --> $DIR/offset-of-self.rs:35:26 + | +LL | offset_of!(self::S, v); + | ^ not found in `self` + | +help: consider importing this struct + | +LL + use S; + | +help: if you import `S`, refer to it directly + | +LL - offset_of!(self::S, v); +LL + offset_of!(S, v); + | + +error[E0411]: cannot find type `Self` in this scope + --> $DIR/offset-of-self.rs:52:16 + | +LL | fn main() { + | ---- `Self` not allowed in a function +LL | offset_of!(self::S, v); +LL | offset_of!(Self, v); + | ^^^^ `Self` is only available in impls, traits, and type definitions + +error[E0609]: no field `Self` on type `S` + --> $DIR/offset-of-self.rs:21:23 + | +LL | offset_of!(S, Self); + | ^^^^ + +error[E0616]: field `v` of struct `T` is private + --> $DIR/offset-of-self.rs:41:30 + | +LL | offset_of!(Self, v) + | ^ private field + +error[E0609]: no field `self` on type `S` + --> $DIR/offset-of-self.rs:54:19 + | +LL | offset_of!(S, self); + | ^^^^ + +error[E0609]: no field `self` on type `u8` + --> $DIR/offset-of-self.rs:56:21 + | +LL | offset_of!(S, v.self); + | ^^^^ + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0411, E0412, E0609, E0616. +For more information about an error, try `rustc --explain E0411`. From 3e4ed61c0987d73af4c291b68cf76adfeaa349c6 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 18 May 2023 15:00:30 +0200 Subject: [PATCH 07/11] do not overwrite obligations --- compiler/rustc_infer/src/infer/opaque_types.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 545310ad35167..b88ba04b27379 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -530,19 +530,18 @@ impl<'tcx> InferCtxt<'tcx> { // these are the same span, but not in cases like `-> (impl // Foo, impl Bar)`. let span = cause.span; - - let mut obligations = vec![]; let prev = self.inner.borrow_mut().opaque_types().register( OpaqueTypeKey { def_id, substs }, OpaqueHiddenType { ty: hidden_ty, span }, origin, ); - if let Some(prev) = prev { - obligations = self - .at(&cause, param_env) + let mut obligations = if let Some(prev) = prev { + self.at(&cause, param_env) .eq_exp(DefineOpaqueTypes::Yes, a_is_expected, prev, hidden_ty)? - .obligations; - } + .obligations + } else { + Vec::new() + }; let item_bounds = tcx.explicit_item_bounds(def_id); From 04265621f9a82cfc2fc2278b935dcc5d4a1c39b7 Mon Sep 17 00:00:00 2001 From: jyn Date: Mon, 15 May 2023 11:42:44 -0500 Subject: [PATCH 08/11] very minor cleanups - add `must_use` to `early_error_no_abort` this was already being used at its only callsite, but this ensures that new code remembers to use it if it's called in the future. - remove outdated and incorrect comment in `builder.rs`. `doc_rust_lang_org_channel` doesn't exist in rustdoc, it gets it from an env var instead. --- compiler/rustc_driver_impl/src/lib.rs | 3 ++- compiler/rustc_session/src/session.rs | 1 + src/bootstrap/builder.rs | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index cf2ab3c2d2ad1..77590c0338cb8 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -1250,7 +1250,8 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) #[cfg(windows)] if let Some(msg) = info.payload().downcast_ref::() { if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)") { - early_error_no_abort(ErrorOutputType::default(), msg.as_str()); + // the error code is already going to be reported when the panic unwinds up the stack + let _ = early_error_no_abort(ErrorOutputType::default(), msg.as_str()); return; } }; diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 4abe734d372d4..db367eedbb636 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1727,6 +1727,7 @@ fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] +#[must_use = "ErrorGuaranteed must be returned from `run_compiler` in order to exit with a non-zero status code"] pub fn early_error_no_abort( output: config::ErrorOutputType, msg: impl Into, diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 5c37fab547070..cf7c6596c0238 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -942,7 +942,6 @@ impl<'a> Builder<'a> { self.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), paths); } - /// NOTE: keep this in sync with `rustdoc::clean::utils::doc_rust_lang_org_channel`, or tests will fail on beta/stable. pub fn doc_rust_lang_org_channel(&self) -> String { let channel = match &*self.config.channel { "stable" => &self.version, From ad5955b891dd08e9e236405b3e2646071436fd8f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 18 May 2023 17:02:51 +0200 Subject: [PATCH 09/11] Migrate GUI colors test to original CSS color format --- tests/rustdoc-gui/source-code-page.goml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/rustdoc-gui/source-code-page.goml b/tests/rustdoc-gui/source-code-page.goml index 5c795928bdc9a..d5dd511b1d31f 100644 --- a/tests/rustdoc-gui/source-code-page.goml +++ b/tests/rustdoc-gui/source-code-page.goml @@ -40,24 +40,24 @@ define-function: ( call-function: ("check-colors", { "theme": "ayu", - "color": "rgb(92, 103, 115)", - "background_color": "rgba(0, 0, 0, 0)", - "highlight_color": "rgb(112, 128, 144)", + "color": "#5c6773", + "background_color": "transparent", + "highlight_color": "#708090", "highlight_background_color": "rgba(255, 236, 164, 0.06)", }) call-function: ("check-colors", { "theme": "dark", - "color": "rgb(59, 145, 226)", - "background_color": "rgba(0, 0, 0, 0)", - "highlight_color": "rgb(59, 145, 226)", - "highlight_background_color": "rgb(10, 4, 47)", + "color": "#3b91e2", + "background_color": "transparent", + "highlight_color": "#3b91e2", + "highlight_background_color": "#0a042f", }) call-function: ("check-colors", { "theme": "light", - "color": "rgb(198, 126, 45)", - "background_color": "rgba(0, 0, 0, 0)", - "highlight_color": "rgb(198, 126, 45)", - "highlight_background_color": "rgb(253, 255, 211)", + "color": "#c67e2d", + "background_color": "transparent", + "highlight_color": "#c67e2d", + "highlight_background_color": "#fdffd3", }) // This is to ensure that the content is correctly align with the line numbers. From 1551495d3221b6162b96a14fc5cdb45f2783ed3c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 18 May 2023 14:45:52 +1000 Subject: [PATCH 10/11] Fix an ICE in CGU dumping code. --- compiler/rustc_monomorphize/src/partitioning/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs index c10180ee3f489..eae3585febf7a 100644 --- a/compiler/rustc_monomorphize/src/partitioning/mod.rs +++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs @@ -365,14 +365,14 @@ where let symbol_hash_start = symbol_name.rfind('h'); let symbol_hash = symbol_hash_start.map_or("", |i| &symbol_name[i..]); - let _ = writeln!( + let _ = with_no_trimmed_paths!(writeln!( s, " - {} [{:?}] [{}] estimated size {}", mono_item, linkage, symbol_hash, mono_item.size_estimate(tcx) - ); + )); } let _ = writeln!(s); From 1bb957efc6ac69b0b19af282c51e04ef70f4233c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 18 May 2023 13:32:28 +1000 Subject: [PATCH 11/11] Improve CGU partitioning debug output. - Pass a slice instead of an iterator to `debug_dump`. - For each CGU set, print: the number of CGUs, the max and min size, and the ratio of the max and min size (which indicates how evenly sized they are). - Print a `FINAL` entry, showing the absolute final results. --- .../src/partitioning/mod.rs | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs index eae3585febf7a..eafe57a0c0207 100644 --- a/compiler/rustc_monomorphize/src/partitioning/mod.rs +++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs @@ -250,13 +250,13 @@ where cgu.create_size_estimate(tcx); } - debug_dump(tcx, "INITIAL PARTITIONING:", initial_partitioning.codegen_units.iter()); + debug_dump(tcx, "INITIAL PARTITIONING", &initial_partitioning.codegen_units); // Merge until we have at most `max_cgu_count` codegen units. { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus"); partitioner.merge_codegen_units(cx, &mut initial_partitioning); - debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter()); + debug_dump(tcx, "POST MERGING", &initial_partitioning.codegen_units); } // In the next step, we use the inlining map to determine which additional @@ -272,7 +272,7 @@ where cgu.create_size_estimate(tcx); } - debug_dump(tcx, "POST INLINING:", post_inlining.codegen_units.iter()); + debug_dump(tcx, "POST INLINING", &post_inlining.codegen_units); // Next we try to make as many symbols "internal" as possible, so LLVM has // more freedom to optimize. @@ -322,6 +322,8 @@ where result.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str())); + debug_dump(tcx, "FINAL", &result); + result } @@ -346,19 +348,23 @@ struct PostInliningPartitioning<'tcx> { internalization_candidates: FxHashSet>, } -fn debug_dump<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, label: &str, cgus: I) -where - I: Iterator>, - 'tcx: 'a, -{ +fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit<'tcx>]) { let dump = move || { use std::fmt::Write; + let num_cgus = cgus.len(); + let max = cgus.iter().map(|cgu| cgu.size_estimate()).max().unwrap(); + let min = cgus.iter().map(|cgu| cgu.size_estimate()).min().unwrap(); + let ratio = max as f64 / min as f64; + let s = &mut String::new(); - let _ = writeln!(s, "{label}"); + let _ = writeln!( + s, + "{label} ({num_cgus} CodegenUnits, max={max}, min={min}, max/min={ratio:.1}):" + ); for cgu in cgus { let _ = - writeln!(s, "CodegenUnit {} estimated size {} :", cgu.name(), cgu.size_estimate()); + writeln!(s, "CodegenUnit {} estimated size {}:", cgu.name(), cgu.size_estimate()); for (mono_item, linkage) in cgu.items() { let symbol_name = mono_item.symbol_name(tcx).name;