From ca3d1010bb5f8ed6b7897c1d85f794857e00caa4 Mon Sep 17 00:00:00 2001 From: Ross MacArthur Date: Sun, 2 Jan 2022 18:31:56 +0200 Subject: [PATCH 01/22] Add `Iterator::array_chunks()` --- .../core/src/iter/adapters/array_chunks.rs | 427 ++++++++++++++++++ library/core/src/iter/adapters/mod.rs | 4 + library/core/src/iter/mod.rs | 2 + library/core/src/iter/traits/iterator.rs | 42 +- .../core/tests/iter/adapters/array_chunks.rs | 198 ++++++++ library/core/tests/iter/adapters/mod.rs | 23 + library/core/tests/lib.rs | 1 + 7 files changed, 696 insertions(+), 1 deletion(-) create mode 100644 library/core/src/iter/adapters/array_chunks.rs create mode 100644 library/core/tests/iter/adapters/array_chunks.rs diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs new file mode 100644 index 0000000000000..f9c3f03cbb81b --- /dev/null +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -0,0 +1,427 @@ +use crate::iter::{Fuse, FusedIterator, Iterator, TrustedLen}; +use crate::mem; +use crate::mem::MaybeUninit; +use crate::ops::{ControlFlow, Try}; +use crate::ptr; + +#[derive(Debug)] +struct Remainder { + array: [MaybeUninit; N], + init: usize, +} + +impl Remainder { + fn new() -> Self { + Self { array: MaybeUninit::uninit_array(), init: 0 } + } + + unsafe fn with_init(array: [MaybeUninit; N], init: usize) -> Self { + Self { array, init } + } + + fn as_slice(&self) -> &[T] { + debug_assert!(self.init <= N); + // SAFETY: This raw slice will only contain the initialized objects + // within the buffer. + unsafe { + let slice = self.array.get_unchecked(..self.init); + MaybeUninit::slice_assume_init_ref(slice) + } + } + + fn as_mut_slice(&mut self) -> &mut [T] { + debug_assert!(self.init <= N); + // SAFETY: This raw slice will only contain the initialized objects + // within the buffer. + unsafe { + let slice = self.array.get_unchecked_mut(..self.init); + MaybeUninit::slice_assume_init_mut(slice) + } + } +} + +impl Clone for Remainder +where + T: Clone, +{ + fn clone(&self) -> Self { + let mut new = Self::new(); + // SAFETY: The new array is the same size and `init` is always less than + // or equal to `N`. + let this = unsafe { new.array.get_unchecked_mut(..self.init) }; + MaybeUninit::write_slice_cloned(this, self.as_slice()); + new.init = self.init; + new + } +} + +impl Drop for Remainder { + fn drop(&mut self) { + // SAFETY: This raw slice will only contain the initialized objects + // within the buffer. + unsafe { ptr::drop_in_place(self.as_mut_slice()) } + } +} + +/// An iterator over `N` elements of the iterator at a time. +/// +/// The chunks do not overlap. If `N` does not divide the length of the +/// iterator, then the last up to `N-1` elements will be omitted. +/// +/// This `struct` is created by the [`array_chunks`][Iterator::array_chunks] +/// method on [`Iterator`]. See its documentation for more. +#[derive(Debug, Clone)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] +pub struct ArrayChunks { + iter: Fuse, + remainder: Remainder, +} + +impl ArrayChunks +where + I: Iterator, +{ + pub(in crate::iter) fn new(iter: I) -> Self { + assert!(N != 0, "chunk size must be non-zero"); + Self { iter: iter.fuse(), remainder: Remainder::new() } + } + + /// Returns a reference to the remaining elements of the original iterator + /// that are not going to be returned by this iterator. The returned slice + /// has at most `N-1` elements. + #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] + #[inline] + pub fn remainder(&self) -> &[I::Item] { + self.remainder.as_slice() + } + + /// Returns a mutable reference to the remaining elements of the original + /// iterator that are not going to be returned by this iterator. The + /// returned slice has at most `N-1` elements. + #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] + #[inline] + pub fn remainder_mut(&mut self) -> &mut [I::Item] { + self.remainder.as_mut_slice() + } +} + +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] +impl Iterator for ArrayChunks +where + I: Iterator, +{ + type Item = [I::Item; N]; + + #[inline] + fn next(&mut self) -> Option { + let mut array = MaybeUninit::uninit_array(); + // SAFETY: `array` will still be valid if `guard` is dropped. + let mut guard = unsafe { FrontGuard::new(&mut array) }; + + for slot in array.iter_mut() { + match self.iter.next() { + Some(item) => { + slot.write(item); + guard.init += 1; + } + None => { + if guard.init > 0 { + let init = guard.init; + mem::forget(guard); + // SAFETY: `array` was initialized with `init` elements. + self.remainder = unsafe { Remainder::with_init(array, init) }; + } + return None; + } + } + } + + mem::forget(guard); + // SAFETY: All elements of the array were populated in the loop above. + Some(unsafe { MaybeUninit::array_assume_init(array) }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (lower, upper) = self.iter.size_hint(); + // Keep infinite iterator size hint lower bound as `usize::MAX`. This + // is required to implement `TrustedLen`. + if lower == usize::MAX { + return (lower, upper); + } + (lower / N, upper.map(|n| n / N)) + } + + #[inline] + fn count(self) -> usize { + self.iter.count() / N + } + + fn try_fold(&mut self, init: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + let mut array = MaybeUninit::uninit_array(); + // SAFETY: `array` will still be valid if `guard` is dropped. + let mut guard = unsafe { FrontGuard::new(&mut array) }; + + let result = self.iter.try_fold(init, |mut acc, item| { + // SAFETY: `init` starts at 0, increases by one each iteration and + // is reset to 0 once it reaches N. + unsafe { array.get_unchecked_mut(guard.init) }.write(item); + guard.init += 1; + if guard.init == N { + guard.init = 0; + let array = mem::replace(&mut array, MaybeUninit::uninit_array()); + // SAFETY: the condition above asserts that all elements are + // initialized. + let item = unsafe { MaybeUninit::array_assume_init(array) }; + acc = f(acc, item)?; + } + R::from_output(acc) + }); + match result.branch() { + ControlFlow::Continue(o) => { + if guard.init > 0 { + let init = guard.init; + mem::forget(guard); + // SAFETY: `array` was initialized with `init` elements. + self.remainder = unsafe { Remainder::with_init(array, init) }; + } + R::from_output(o) + } + ControlFlow::Break(r) => R::from_residual(r), + } + } + + fn fold(self, init: B, mut f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + let mut array = MaybeUninit::uninit_array(); + // SAFETY: `array` will still be valid if `guard` is dropped. + let mut guard = unsafe { FrontGuard::new(&mut array) }; + + self.iter.fold(init, |mut acc, item| { + // SAFETY: `init` starts at 0, increases by one each iteration and + // is reset to 0 once it reaches N. + unsafe { array.get_unchecked_mut(guard.init) }.write(item); + guard.init += 1; + if guard.init == N { + guard.init = 0; + let array = mem::replace(&mut array, MaybeUninit::uninit_array()); + // SAFETY: the condition above asserts that all elements are + // initialized. + let item = unsafe { MaybeUninit::array_assume_init(array) }; + acc = f(acc, item); + } + acc + }) + } +} + +/// A guard for an array where elements are filled from the left. +struct FrontGuard { + /// A pointer to the array that is being filled. We need to use a raw + /// pointer here because of the lifetime issues in the fold implementations. + ptr: *mut T, + /// The number of *initialized* elements. + init: usize, +} + +impl FrontGuard { + unsafe fn new(array: &mut [MaybeUninit; N]) -> Self { + Self { ptr: MaybeUninit::slice_as_mut_ptr(array), init: 0 } + } +} + +impl Drop for FrontGuard { + fn drop(&mut self) { + debug_assert!(self.init <= N); + // SAFETY: This raw slice will only contain the initialized objects + // within the buffer. + unsafe { + let slice = ptr::slice_from_raw_parts_mut(self.ptr, self.init); + ptr::drop_in_place(slice); + } + } +} + +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] +impl DoubleEndedIterator for ArrayChunks +where + I: DoubleEndedIterator + ExactSizeIterator, +{ + #[inline] + fn next_back(&mut self) -> Option { + // We are iterating from the back we need to first handle the remainder. + self.next_back_remainder()?; + + let mut array = MaybeUninit::uninit_array(); + // SAFETY: `array` will still be valid if `guard` is dropped. + let mut guard = unsafe { BackGuard::new(&mut array) }; + + for slot in array.iter_mut().rev() { + slot.write(self.iter.next_back()?); + guard.uninit -= 1; + } + + mem::forget(guard); + // SAFETY: All elements of the array were populated in the loop above. + Some(unsafe { MaybeUninit::array_assume_init(array) }) + } + + fn try_rfold(&mut self, init: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + // We are iterating from the back we need to first handle the remainder. + if self.next_back_remainder().is_none() { + return R::from_output(init); + } + + let mut array = MaybeUninit::uninit_array(); + // SAFETY: `array` will still be valid if `guard` is dropped. + let mut guard = unsafe { BackGuard::new(&mut array) }; + + self.iter.try_rfold(init, |mut acc, item| { + guard.uninit -= 1; + // SAFETY: `uninit` starts at N, decreases by one each iteration and + // is reset to N once it reaches 0. + unsafe { array.get_unchecked_mut(guard.uninit) }.write(item); + if guard.uninit == 0 { + guard.uninit = N; + let array = mem::replace(&mut array, MaybeUninit::uninit_array()); + // SAFETY: the condition above asserts that all elements are + // initialized. + let item = unsafe { MaybeUninit::array_assume_init(array) }; + acc = f(acc, item)?; + } + R::from_output(acc) + }) + } + + fn rfold(mut self, init: B, mut f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + // We are iterating from the back we need to first handle the remainder. + if self.next_back_remainder().is_none() { + return init; + } + + let mut array = MaybeUninit::uninit_array(); + + // SAFETY: `array` will still be valid if `guard` is dropped. + let mut guard = unsafe { BackGuard::new(&mut array) }; + + self.iter.rfold(init, |mut acc, item| { + guard.uninit -= 1; + // SAFETY: `uninit` starts at N, decreases by one each iteration and + // is reset to N once it reaches 0. + unsafe { array.get_unchecked_mut(guard.uninit) }.write(item); + if guard.uninit == 0 { + guard.uninit = N; + let array = mem::replace(&mut array, MaybeUninit::uninit_array()); + // SAFETY: the condition above asserts that all elements are + // initialized. + let item = unsafe { MaybeUninit::array_assume_init(array) }; + acc = f(acc, item); + } + acc + }) + } +} + +impl ArrayChunks +where + I: DoubleEndedIterator + ExactSizeIterator, +{ + #[inline] + fn next_back_remainder(&mut self) -> Option<()> { + // We use the `ExactSizeIterator` implementation of the underlying + // iterator to know how many remaining elements there are. + let rem = self.iter.len() % N; + if rem == 0 { + return Some(()); + } + + let mut array = MaybeUninit::uninit_array(); + + // SAFETY: The array will still be valid if `guard` is dropped and + // it is forgotten otherwise. + let mut guard = unsafe { FrontGuard::new(&mut array) }; + + // SAFETY: `rem` is in the range 1..N based on how it is calculated. + for slot in unsafe { array.get_unchecked_mut(..rem) }.iter_mut() { + slot.write(self.iter.next_back()?); + guard.init += 1; + } + + let init = guard.init; + mem::forget(guard); + // SAFETY: `array` was initialized with exactly `init` elements. + self.remainder = unsafe { + array.get_unchecked_mut(..init).reverse(); + Remainder::with_init(array, init) + }; + Some(()) + } +} + +/// A guard for an array where elements are filled from the right. +struct BackGuard { + /// A pointer to the array that is being filled. We need to use a raw + /// pointer here because of the lifetime issues in the rfold implementations. + ptr: *mut T, + /// The number of *uninitialized* elements. + uninit: usize, +} + +impl BackGuard { + unsafe fn new(array: &mut [MaybeUninit; N]) -> Self { + Self { ptr: MaybeUninit::slice_as_mut_ptr(array), uninit: N } + } +} + +impl Drop for BackGuard { + fn drop(&mut self) { + debug_assert!(self.uninit <= N); + // SAFETY: This raw slice will only contain the initialized objects + // within the buffer. + unsafe { + let ptr = self.ptr.offset(self.uninit as isize); + let slice = ptr::slice_from_raw_parts_mut(ptr, N - self.uninit); + ptr::drop_in_place(slice); + } + } +} + +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] +impl FusedIterator for ArrayChunks where I: FusedIterator {} + +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] +impl ExactSizeIterator for ArrayChunks +where + I: ExactSizeIterator, +{ + #[inline] + fn len(&self) -> usize { + self.iter.len() / N + } + + #[inline] + fn is_empty(&self) -> bool { + self.iter.len() / N == 0 + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ArrayChunks where I: TrustedLen {} diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index 916a26e242466..39e7ab87869a2 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -1,6 +1,7 @@ use crate::iter::{InPlaceIterable, Iterator}; use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, NeverShortCircuit, Residual, Try}; +mod array_chunks; mod by_ref_sized; mod chain; mod cloned; @@ -32,6 +33,9 @@ pub use self::{ scan::Scan, skip::Skip, skip_while::SkipWhile, take::Take, take_while::TakeWhile, zip::Zip, }; +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] +pub use self::array_chunks::ArrayChunks; + #[unstable(feature = "std_internals", issue = "none")] pub use self::by_ref_sized::ByRefSized; diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index d5c6aed5b6c8a..d48e3a52c79f6 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -398,6 +398,8 @@ pub use self::traits::{ #[stable(feature = "iter_zip", since = "1.59.0")] pub use self::adapters::zip; +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] +pub use self::adapters::ArrayChunks; #[unstable(feature = "std_internals", issue = "none")] pub use self::adapters::ByRefSized; #[stable(feature = "iter_cloned", since = "1.1.0")] diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 275412b57b55f..8bf41ca6f2a96 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -5,7 +5,7 @@ use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try}; use super::super::try_process; use super::super::ByRefSized; use super::super::TrustedRandomAccessNoCoerce; -use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; +use super::super::{ArrayChunks, Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; use super::super::{FlatMap, Flatten}; use super::super::{FromIterator, Intersperse, IntersperseWith, Product, Sum, Zip}; use super::super::{ @@ -3316,6 +3316,46 @@ pub trait Iterator { Cycle::new(self) } + /// Returns an iterator over `N` elements of the iterator at a time. + /// + /// The chunks do not overlap. If `N` does not divide the length of the + /// iterator, then the last up to `N-1` elements will be omitted. + /// + /// # Panics + /// + /// Panics if `N` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_array_chunks)] + /// + /// let mut iter = "lorem".chars().array_chunks(); + /// assert_eq!(iter.next(), Some(['l', 'o'])); + /// assert_eq!(iter.next(), Some(['r', 'e'])); + /// assert_eq!(iter.next(), None); + /// assert_eq!(iter.remainder(), &['m']); + /// ``` + /// + /// ``` + /// #![feature(iter_array_chunks)] + /// + /// let data = [1, 1, 2, -2, 6, 0, 3, 1]; + /// // ^-----^ ^------^ + /// for [x, y, z] in data.iter().array_chunks() { + /// assert_eq!(x + y + z, 4); + /// } + /// ``` + #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] + fn array_chunks(self) -> ArrayChunks + where + Self: Sized, + { + ArrayChunks::new(self) + } + /// Sums the elements of an iterator. /// /// Takes each element, adds them together, and returns the result. diff --git a/library/core/tests/iter/adapters/array_chunks.rs b/library/core/tests/iter/adapters/array_chunks.rs new file mode 100644 index 0000000000000..6845c94d364ca --- /dev/null +++ b/library/core/tests/iter/adapters/array_chunks.rs @@ -0,0 +1,198 @@ +use core::cell::Cell; +use core::iter::{self, Iterator}; + +use super::*; + +#[test] +fn test_iterator_array_chunks_infer() { + let xs = [1, 1, 2, -2, 6, 0, 3, 1]; + for [a, b, c] in xs.iter().copied().array_chunks() { + assert_eq!(a + b + c, 4); + } +} + +#[test] +fn test_iterator_array_chunks_clone_and_drop() { + let count = Cell::new(0); + let mut it = (0..5).map(|_| CountDrop::new(&count)).array_chunks::<3>(); + + assert_eq!(it.by_ref().count(), 1); + assert_eq!(count.get(), 3); + assert_eq!(it.remainder().len(), 2); + + let mut it2 = it.clone(); + assert_eq!(count.get(), 3); + assert_eq!(it2.remainder().len(), 2); + + drop(it); + assert_eq!(count.get(), 5); + assert_eq!(it2.remainder().len(), 2); + assert!(it2.next().is_none()); + + drop(it2); + assert_eq!(count.get(), 7); +} + +#[test] +fn test_iterator_array_chunks_remainder() { + let mut it = (0..11).array_chunks::<4>(); + assert_eq!(it.remainder(), &[]); + assert_eq!(it.remainder_mut(), &[]); + assert_eq!(it.next(), Some([0, 1, 2, 3])); + assert_eq!(it.remainder(), &[]); + assert_eq!(it.remainder_mut(), &[]); + assert_eq!(it.next(), Some([4, 5, 6, 7])); + assert_eq!(it.remainder(), &[]); + assert_eq!(it.remainder_mut(), &[]); + assert_eq!(it.next(), None); + assert_eq!(it.next(), None); + assert_eq!(it.remainder(), &[8, 9, 10]); + assert_eq!(it.remainder_mut(), &[8, 9, 10]); +} + +#[test] +fn test_iterator_array_chunks_size_hint() { + let it = (0..6).array_chunks::<1>(); + assert_eq!(it.size_hint(), (6, Some(6))); + + let it = (0..6).array_chunks::<3>(); + assert_eq!(it.size_hint(), (2, Some(2))); + + let it = (0..6).array_chunks::<5>(); + assert_eq!(it.size_hint(), (1, Some(1))); + + let it = (0..6).array_chunks::<7>(); + assert_eq!(it.size_hint(), (0, Some(0))); + + let it = (1..).array_chunks::<2>(); + assert_eq!(it.size_hint(), (usize::MAX, None)); + + let it = (1..).filter(|x| x % 2 != 0).array_chunks::<2>(); + assert_eq!(it.size_hint(), (0, None)); +} + +#[test] +fn test_iterator_array_chunks_count() { + let it = (0..6).array_chunks::<1>(); + assert_eq!(it.count(), 6); + + let it = (0..6).array_chunks::<3>(); + assert_eq!(it.count(), 2); + + let it = (0..6).array_chunks::<5>(); + assert_eq!(it.count(), 1); + + let it = (0..6).array_chunks::<7>(); + assert_eq!(it.count(), 0); + + let it = (0..6).filter(|x| x % 2 == 0).array_chunks::<2>(); + assert_eq!(it.count(), 1); + + let it = iter::empty::().array_chunks::<2>(); + assert_eq!(it.count(), 0); + + let it = [(); usize::MAX].iter().array_chunks::<2>(); + assert_eq!(it.count(), usize::MAX / 2); +} + +#[test] +fn test_iterator_array_chunks_next_and_next_back() { + let mut it = (0..11).array_chunks::<3>(); + assert_eq!(it.next(), Some([0, 1, 2])); + assert_eq!(it.next_back(), Some([6, 7, 8])); + assert_eq!(it.next(), Some([3, 4, 5])); + assert_eq!(it.next_back(), None); + assert_eq!(it.next(), None); + assert_eq!(it.next_back(), None); + assert_eq!(it.next(), None); + assert_eq!(it.remainder(), &[9, 10]); + assert_eq!(it.remainder_mut(), &[9, 10]); +} + +#[test] +fn test_iterator_array_chunks_rev_remainder() { + let mut it = (0..11).array_chunks::<4>(); + { + let mut it = it.by_ref().rev(); + assert_eq!(it.next(), Some([4, 5, 6, 7])); + assert_eq!(it.next(), Some([0, 1, 2, 3])); + assert_eq!(it.next(), None); + assert_eq!(it.next(), None); + } + assert_eq!(it.remainder(), &[8, 9, 10]); +} + +#[test] +fn test_iterator_array_chunks_try_fold() { + let count = Cell::new(0); + let mut it = (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>(); + let result: Result<_, ()> = it.by_ref().try_fold(0, |acc, _item| Ok(acc + 1)); + assert_eq!(result, Ok(3)); + assert_eq!(it.remainder().len(), 1); + assert_eq!(count.get(), 9); + drop(it); + assert_eq!(count.get(), 10); + + let count = Cell::new(0); + let mut it = (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>(); + let result = it.by_ref().try_fold(0, |acc, _item| if acc < 2 { Ok(acc + 1) } else { Err(acc) }); + assert_eq!(result, Err(2)); + assert_eq!(it.remainder().len(), 0); + assert_eq!(count.get(), 9); + drop(it); + assert_eq!(count.get(), 9); +} + +#[test] +fn test_iterator_array_chunks_fold() { + let result = (1..11).array_chunks::<3>().fold(0, |acc, [a, b, c]| { + assert_eq!(acc + 1, a); + assert_eq!(acc + 2, b); + assert_eq!(acc + 3, c); + acc + 3 + }); + assert_eq!(result, 9); + + let count = Cell::new(0); + let result = + (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>().fold(0, |acc, _item| acc + 1); + assert_eq!(result, 3); + assert_eq!(count.get(), 10); +} + +#[test] +fn test_iterator_array_chunks_try_rfold() { + let count = Cell::new(0); + let mut it = (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>(); + let result: Result<_, ()> = it.try_rfold(0, |acc, _item| Ok(acc + 1)); + assert_eq!(result, Ok(3)); + assert_eq!(it.remainder().len(), 1); + assert_eq!(count.get(), 9); + drop(it); + assert_eq!(count.get(), 10); + + let count = Cell::new(0); + let mut it = (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>(); + let result = it.try_rfold(0, |acc, _item| if acc < 2 { Ok(acc + 1) } else { Err(acc) }); + assert_eq!(result, Err(2)); + assert_eq!(count.get(), 9); + drop(it); + assert_eq!(count.get(), 10); +} + +#[test] +fn test_iterator_array_chunks_rfold() { + let result = (1..11).array_chunks::<3>().rfold(0, |acc, [a, b, c]| { + assert_eq!(10 - (acc + 1), c); + assert_eq!(10 - (acc + 2), b); + assert_eq!(10 - (acc + 3), a); + acc + 3 + }); + assert_eq!(result, 9); + + let count = Cell::new(0); + let result = + (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>().rfold(0, |acc, _item| acc + 1); + assert_eq!(result, 3); + assert_eq!(count.get(), 10); +} diff --git a/library/core/tests/iter/adapters/mod.rs b/library/core/tests/iter/adapters/mod.rs index 567d9fe49cade..96539c0c394e2 100644 --- a/library/core/tests/iter/adapters/mod.rs +++ b/library/core/tests/iter/adapters/mod.rs @@ -1,3 +1,4 @@ +mod array_chunks; mod chain; mod cloned; mod copied; @@ -183,3 +184,25 @@ impl Clone for CountClone { ret } } + +#[derive(Debug, Clone)] +struct CountDrop<'a> { + dropped: bool, + count: &'a Cell, +} + +impl<'a> CountDrop<'a> { + pub fn new(count: &'a Cell) -> Self { + Self { dropped: false, count } + } +} + +impl Drop for CountDrop<'_> { + fn drop(&mut self) { + if self.dropped { + panic!("double drop"); + } + self.dropped = true; + self.count.set(self.count.get() + 1); + } +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index db94368f6e0cc..8b4838bb7bc57 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -61,6 +61,7 @@ #![feature(slice_partition_dedup)] #![feature(int_log)] #![feature(iter_advance_by)] +#![feature(iter_array_chunks)] #![feature(iter_collect_into)] #![feature(iter_partition_in_place)] #![feature(iter_intersperse)] From f5485181ca1d932d78904a7587ed19d0738f0b91 Mon Sep 17 00:00:00 2001 From: Ross MacArthur Date: Fri, 4 Feb 2022 15:57:58 +0200 Subject: [PATCH 02/22] Use `array::IntoIter` for the `ArrayChunks` remainder --- library/core/src/array/iter.rs | 10 ++ .../core/src/iter/adapters/array_chunks.rs | 93 +++---------------- library/core/src/iter/traits/iterator.rs | 6 +- .../core/tests/iter/adapters/array_chunks.rs | 29 +----- 4 files changed, 33 insertions(+), 105 deletions(-) diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index f4885ed9ffbb6..459cd094cdca6 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -84,6 +84,16 @@ impl IntoIter { IntoIterator::into_iter(array) } + /// Creates a new iterator from a partially initalized array. + /// + /// # Safety + /// + /// The caller must guarantee that all and only the `alive` elements of + /// `data` are initialized. + pub(crate) unsafe fn with_partial(data: [MaybeUninit; N], alive: Range) -> Self { + Self { data, alive } + } + /// Creates an iterator over the elements in a partially-initialized buffer. /// /// If you have a fully-initialized array, then use [`IntoIterator`]. diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index f9c3f03cbb81b..2ec1284c39406 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -1,68 +1,10 @@ +use crate::array; use crate::iter::{Fuse, FusedIterator, Iterator, TrustedLen}; use crate::mem; use crate::mem::MaybeUninit; use crate::ops::{ControlFlow, Try}; use crate::ptr; -#[derive(Debug)] -struct Remainder { - array: [MaybeUninit; N], - init: usize, -} - -impl Remainder { - fn new() -> Self { - Self { array: MaybeUninit::uninit_array(), init: 0 } - } - - unsafe fn with_init(array: [MaybeUninit; N], init: usize) -> Self { - Self { array, init } - } - - fn as_slice(&self) -> &[T] { - debug_assert!(self.init <= N); - // SAFETY: This raw slice will only contain the initialized objects - // within the buffer. - unsafe { - let slice = self.array.get_unchecked(..self.init); - MaybeUninit::slice_assume_init_ref(slice) - } - } - - fn as_mut_slice(&mut self) -> &mut [T] { - debug_assert!(self.init <= N); - // SAFETY: This raw slice will only contain the initialized objects - // within the buffer. - unsafe { - let slice = self.array.get_unchecked_mut(..self.init); - MaybeUninit::slice_assume_init_mut(slice) - } - } -} - -impl Clone for Remainder -where - T: Clone, -{ - fn clone(&self) -> Self { - let mut new = Self::new(); - // SAFETY: The new array is the same size and `init` is always less than - // or equal to `N`. - let this = unsafe { new.array.get_unchecked_mut(..self.init) }; - MaybeUninit::write_slice_cloned(this, self.as_slice()); - new.init = self.init; - new - } -} - -impl Drop for Remainder { - fn drop(&mut self) { - // SAFETY: This raw slice will only contain the initialized objects - // within the buffer. - unsafe { ptr::drop_in_place(self.as_mut_slice()) } - } -} - /// An iterator over `N` elements of the iterator at a time. /// /// The chunks do not overlap. If `N` does not divide the length of the @@ -75,7 +17,7 @@ impl Drop for Remainder { #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] pub struct ArrayChunks { iter: Fuse, - remainder: Remainder, + remainder: Option>, } impl ArrayChunks @@ -84,25 +26,16 @@ where { pub(in crate::iter) fn new(iter: I) -> Self { assert!(N != 0, "chunk size must be non-zero"); - Self { iter: iter.fuse(), remainder: Remainder::new() } - } - - /// Returns a reference to the remaining elements of the original iterator - /// that are not going to be returned by this iterator. The returned slice - /// has at most `N-1` elements. - #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] - #[inline] - pub fn remainder(&self) -> &[I::Item] { - self.remainder.as_slice() + Self { iter: iter.fuse(), remainder: None } } - /// Returns a mutable reference to the remaining elements of the original - /// iterator that are not going to be returned by this iterator. The - /// returned slice has at most `N-1` elements. + /// Returns an iterator over the remaining elements of the original iterator + /// that are not going to be returned by this iterator. The returned + /// iterator will yield at most `N-1` elements. #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] #[inline] - pub fn remainder_mut(&mut self) -> &mut [I::Item] { - self.remainder.as_mut_slice() + pub fn into_remainder(self) -> Option> { + self.remainder } } @@ -129,8 +62,10 @@ where if guard.init > 0 { let init = guard.init; mem::forget(guard); - // SAFETY: `array` was initialized with `init` elements. - self.remainder = unsafe { Remainder::with_init(array, init) }; + self.remainder = { + // SAFETY: `array` was initialized with `init` elements. + Some(unsafe { array::IntoIter::with_partial(array, 0..init) }) + }; } return None; } @@ -189,7 +124,7 @@ where let init = guard.init; mem::forget(guard); // SAFETY: `array` was initialized with `init` elements. - self.remainder = unsafe { Remainder::with_init(array, init) }; + self.remainder = Some(unsafe { array::IntoIter::with_partial(array, 0..init) }); } R::from_output(o) } @@ -370,7 +305,7 @@ where // SAFETY: `array` was initialized with exactly `init` elements. self.remainder = unsafe { array.get_unchecked_mut(..init).reverse(); - Remainder::with_init(array, init) + Some(array::IntoIter::with_partial(array, 0..init)) }; Some(()) } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 8bf41ca6f2a96..d41cf78e00066 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -3319,7 +3319,9 @@ pub trait Iterator { /// Returns an iterator over `N` elements of the iterator at a time. /// /// The chunks do not overlap. If `N` does not divide the length of the - /// iterator, then the last up to `N-1` elements will be omitted. + /// iterator, then the last up to `N-1` elements will be omitted and can be + /// retrieved from the [`.into_remainder()`][ArrayChunks::into_remainder] + /// function of the iterator. /// /// # Panics /// @@ -3336,7 +3338,7 @@ pub trait Iterator { /// assert_eq!(iter.next(), Some(['l', 'o'])); /// assert_eq!(iter.next(), Some(['r', 'e'])); /// assert_eq!(iter.next(), None); - /// assert_eq!(iter.remainder(), &['m']); + /// assert_eq!(iter.into_remainder().unwrap().as_slice(), &['m']); /// ``` /// /// ``` diff --git a/library/core/tests/iter/adapters/array_chunks.rs b/library/core/tests/iter/adapters/array_chunks.rs index 6845c94d364ca..dbcfd4560289e 100644 --- a/library/core/tests/iter/adapters/array_chunks.rs +++ b/library/core/tests/iter/adapters/array_chunks.rs @@ -15,39 +15,24 @@ fn test_iterator_array_chunks_infer() { fn test_iterator_array_chunks_clone_and_drop() { let count = Cell::new(0); let mut it = (0..5).map(|_| CountDrop::new(&count)).array_chunks::<3>(); - assert_eq!(it.by_ref().count(), 1); assert_eq!(count.get(), 3); - assert_eq!(it.remainder().len(), 2); - let mut it2 = it.clone(); assert_eq!(count.get(), 3); - assert_eq!(it2.remainder().len(), 2); - - drop(it); + assert_eq!(it.into_remainder().unwrap().len(), 2); assert_eq!(count.get(), 5); - assert_eq!(it2.remainder().len(), 2); assert!(it2.next().is_none()); - - drop(it2); + assert_eq!(it2.into_remainder().unwrap().len(), 2); assert_eq!(count.get(), 7); } #[test] fn test_iterator_array_chunks_remainder() { let mut it = (0..11).array_chunks::<4>(); - assert_eq!(it.remainder(), &[]); - assert_eq!(it.remainder_mut(), &[]); assert_eq!(it.next(), Some([0, 1, 2, 3])); - assert_eq!(it.remainder(), &[]); - assert_eq!(it.remainder_mut(), &[]); assert_eq!(it.next(), Some([4, 5, 6, 7])); - assert_eq!(it.remainder(), &[]); - assert_eq!(it.remainder_mut(), &[]); - assert_eq!(it.next(), None); assert_eq!(it.next(), None); - assert_eq!(it.remainder(), &[8, 9, 10]); - assert_eq!(it.remainder_mut(), &[8, 9, 10]); + assert_eq!(it.into_remainder().unwrap().as_slice(), &[8, 9, 10]); } #[test] @@ -105,8 +90,7 @@ fn test_iterator_array_chunks_next_and_next_back() { assert_eq!(it.next(), None); assert_eq!(it.next_back(), None); assert_eq!(it.next(), None); - assert_eq!(it.remainder(), &[9, 10]); - assert_eq!(it.remainder_mut(), &[9, 10]); + assert_eq!(it.into_remainder().unwrap().as_slice(), &[9, 10]); } #[test] @@ -119,7 +103,7 @@ fn test_iterator_array_chunks_rev_remainder() { assert_eq!(it.next(), None); assert_eq!(it.next(), None); } - assert_eq!(it.remainder(), &[8, 9, 10]); + assert_eq!(it.into_remainder().unwrap().as_slice(), &[8, 9, 10]); } #[test] @@ -128,7 +112,6 @@ fn test_iterator_array_chunks_try_fold() { let mut it = (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>(); let result: Result<_, ()> = it.by_ref().try_fold(0, |acc, _item| Ok(acc + 1)); assert_eq!(result, Ok(3)); - assert_eq!(it.remainder().len(), 1); assert_eq!(count.get(), 9); drop(it); assert_eq!(count.get(), 10); @@ -137,7 +120,6 @@ fn test_iterator_array_chunks_try_fold() { let mut it = (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>(); let result = it.by_ref().try_fold(0, |acc, _item| if acc < 2 { Ok(acc + 1) } else { Err(acc) }); assert_eq!(result, Err(2)); - assert_eq!(it.remainder().len(), 0); assert_eq!(count.get(), 9); drop(it); assert_eq!(count.get(), 9); @@ -166,7 +148,6 @@ fn test_iterator_array_chunks_try_rfold() { let mut it = (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>(); let result: Result<_, ()> = it.try_rfold(0, |acc, _item| Ok(acc + 1)); assert_eq!(result, Ok(3)); - assert_eq!(it.remainder().len(), 1); assert_eq!(count.get(), 9); drop(it); assert_eq!(count.get(), 10); From ef72349e38635bc329a94b1e95648562e59ab7d2 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 1 Aug 2022 17:00:51 +0400 Subject: [PATCH 03/22] Remove `array::IntoIter::with_partial` -- an artifact of the past, once used to create an `IntoIter` from its parts --- library/core/src/array/iter.rs | 10 ---------- library/core/src/iter/adapters/array_chunks.rs | 7 ++++--- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index 459cd094cdca6..f4885ed9ffbb6 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -84,16 +84,6 @@ impl IntoIter { IntoIterator::into_iter(array) } - /// Creates a new iterator from a partially initalized array. - /// - /// # Safety - /// - /// The caller must guarantee that all and only the `alive` elements of - /// `data` are initialized. - pub(crate) unsafe fn with_partial(data: [MaybeUninit; N], alive: Range) -> Self { - Self { data, alive } - } - /// Creates an iterator over the elements in a partially-initialized buffer. /// /// If you have a fully-initialized array, then use [`IntoIterator`]. diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index 2ec1284c39406..8f3e1b58b52cc 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -64,7 +64,7 @@ where mem::forget(guard); self.remainder = { // SAFETY: `array` was initialized with `init` elements. - Some(unsafe { array::IntoIter::with_partial(array, 0..init) }) + Some(unsafe { array::IntoIter::new_unchecked(array, 0..init) }) }; } return None; @@ -124,7 +124,8 @@ where let init = guard.init; mem::forget(guard); // SAFETY: `array` was initialized with `init` elements. - self.remainder = Some(unsafe { array::IntoIter::with_partial(array, 0..init) }); + self.remainder = + Some(unsafe { array::IntoIter::new_unchecked(array, 0..init) }); } R::from_output(o) } @@ -305,7 +306,7 @@ where // SAFETY: `array` was initialized with exactly `init` elements. self.remainder = unsafe { array.get_unchecked_mut(..init).reverse(); - Some(array::IntoIter::with_partial(array, 0..init)) + Some(array::IntoIter::new_unchecked(array, 0..init)) }; Some(()) } From b8b14864c08cb3d2830006377cbb7ac8741bbb91 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 1 Aug 2022 18:26:18 +0400 Subject: [PATCH 04/22] Forward `ArrayChunks::next{,_back}` to `try_{for_each,rfold}` (suggested in the review of the previous attempt to add `ArrayChunks`) --- .../core/src/iter/adapters/array_chunks.rs | 44 +------------------ 1 file changed, 2 insertions(+), 42 deletions(-) diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index 8f3e1b58b52cc..e25a6f9754b2d 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -48,33 +48,7 @@ where #[inline] fn next(&mut self) -> Option { - let mut array = MaybeUninit::uninit_array(); - // SAFETY: `array` will still be valid if `guard` is dropped. - let mut guard = unsafe { FrontGuard::new(&mut array) }; - - for slot in array.iter_mut() { - match self.iter.next() { - Some(item) => { - slot.write(item); - guard.init += 1; - } - None => { - if guard.init > 0 { - let init = guard.init; - mem::forget(guard); - self.remainder = { - // SAFETY: `array` was initialized with `init` elements. - Some(unsafe { array::IntoIter::new_unchecked(array, 0..init) }) - }; - } - return None; - } - } - } - - mem::forget(guard); - // SAFETY: All elements of the array were populated in the loop above. - Some(unsafe { MaybeUninit::array_assume_init(array) }) + self.try_for_each(ControlFlow::Break).break_value() } #[inline] @@ -194,21 +168,7 @@ where { #[inline] fn next_back(&mut self) -> Option { - // We are iterating from the back we need to first handle the remainder. - self.next_back_remainder()?; - - let mut array = MaybeUninit::uninit_array(); - // SAFETY: `array` will still be valid if `guard` is dropped. - let mut guard = unsafe { BackGuard::new(&mut array) }; - - for slot in array.iter_mut().rev() { - slot.write(self.iter.next_back()?); - guard.uninit -= 1; - } - - mem::forget(guard); - // SAFETY: All elements of the array were populated in the loop above. - Some(unsafe { MaybeUninit::array_assume_init(array) }) + self.try_rfold((), |(), x| ControlFlow::Break(x)).break_value() } fn try_rfold(&mut self, init: B, mut f: F) -> R From 4db628a801d9efae4fe36d54b9e7deee61f341fb Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 1 Aug 2022 18:30:55 +0400 Subject: [PATCH 05/22] Remove incorrect impl `TrustedLen` for `ArrayChunks` As explained in the review of the previous attempt to add `ArrayChunks`, adapters that shrink the length can't implement `TrustedLen`. --- library/core/src/iter/adapters/array_chunks.rs | 11 ++--------- library/core/tests/iter/adapters/array_chunks.rs | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index e25a6f9754b2d..c2de5efed1ac2 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -1,5 +1,5 @@ use crate::array; -use crate::iter::{Fuse, FusedIterator, Iterator, TrustedLen}; +use crate::iter::{Fuse, FusedIterator, Iterator}; use crate::mem; use crate::mem::MaybeUninit; use crate::ops::{ControlFlow, Try}; @@ -54,11 +54,7 @@ where #[inline] fn size_hint(&self) -> (usize, Option) { let (lower, upper) = self.iter.size_hint(); - // Keep infinite iterator size hint lower bound as `usize::MAX`. This - // is required to implement `TrustedLen`. - if lower == usize::MAX { - return (lower, upper); - } + (lower / N, upper.map(|n| n / N)) } @@ -318,6 +314,3 @@ where self.iter.len() / N == 0 } } - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for ArrayChunks where I: TrustedLen {} diff --git a/library/core/tests/iter/adapters/array_chunks.rs b/library/core/tests/iter/adapters/array_chunks.rs index dbcfd4560289e..4e9d89e1e580f 100644 --- a/library/core/tests/iter/adapters/array_chunks.rs +++ b/library/core/tests/iter/adapters/array_chunks.rs @@ -50,7 +50,7 @@ fn test_iterator_array_chunks_size_hint() { assert_eq!(it.size_hint(), (0, Some(0))); let it = (1..).array_chunks::<2>(); - assert_eq!(it.size_hint(), (usize::MAX, None)); + assert_eq!(it.size_hint(), (usize::MAX / 2, None)); let it = (1..).filter(|x| x % 2 != 0).array_chunks::<2>(); assert_eq!(it.size_hint(), (0, None)); From 3102b39daad9a9d3975ceb32d9cf62e76ececd24 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 1 Aug 2022 18:34:30 +0400 Subject: [PATCH 06/22] Use `#[track_caller]` to make panic in `Iterator::array_chunks` nicer --- library/core/src/iter/adapters/array_chunks.rs | 1 + library/core/src/iter/traits/iterator.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index c2de5efed1ac2..f8a52ecb618e0 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -24,6 +24,7 @@ impl ArrayChunks where I: Iterator, { + #[track_caller] pub(in crate::iter) fn new(iter: I) -> Self { assert!(N != 0, "chunk size must be non-zero"); Self { iter: iter.fuse(), remainder: None } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index d41cf78e00066..95c7cf5758c88 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -3350,6 +3350,7 @@ pub trait Iterator { /// assert_eq!(x + y + z, 4); /// } /// ``` + #[track_caller] #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] fn array_chunks(self) -> ArrayChunks where From 37dfb04317fbf35f1fb1e5b94ed3fe7a979c386b Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 1 Aug 2022 18:43:40 +0400 Subject: [PATCH 07/22] Remove `Fuse` from `ArrayChunks` implementation It doesn't seem to be used at all. --- library/core/src/iter/adapters/array_chunks.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index f8a52ecb618e0..3e8f6281d3571 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -1,5 +1,5 @@ use crate::array; -use crate::iter::{Fuse, FusedIterator, Iterator}; +use crate::iter::{FusedIterator, Iterator}; use crate::mem; use crate::mem::MaybeUninit; use crate::ops::{ControlFlow, Try}; @@ -16,7 +16,7 @@ use crate::ptr; #[must_use = "iterators are lazy and do nothing unless consumed"] #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] pub struct ArrayChunks { - iter: Fuse, + iter: I, remainder: Option>, } @@ -27,7 +27,7 @@ where #[track_caller] pub(in crate::iter) fn new(iter: I) -> Self { assert!(N != 0, "chunk size must be non-zero"); - Self { iter: iter.fuse(), remainder: None } + Self { iter, remainder: None } } /// Returns an iterator over the remaining elements of the original iterator From 4c0292cff57e1c044cc5e85bd387a98cc5f035ee Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 1 Aug 2022 18:48:47 +0400 Subject: [PATCH 08/22] Simplify `ArrayChunks::is_empty` --- library/core/src/iter/adapters/array_chunks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index 3e8f6281d3571..901f559c435bb 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -312,6 +312,6 @@ where #[inline] fn is_empty(&self) -> bool { - self.iter.len() / N == 0 + self.iter.len() < N } } From 475e4ba747aa897360748c5ae0bf4d373662f83f Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 1 Aug 2022 19:06:13 +0400 Subject: [PATCH 09/22] Simplify `ArrayChunks::{,r}fold` impls --- .../core/src/iter/adapters/array_chunks.rs | 50 ++----------------- 1 file changed, 4 insertions(+), 46 deletions(-) diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index 901f559c435bb..b66e23c1e78ac 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -2,7 +2,7 @@ use crate::array; use crate::iter::{FusedIterator, Iterator}; use crate::mem; use crate::mem::MaybeUninit; -use crate::ops::{ControlFlow, Try}; +use crate::ops::{ControlFlow, NeverShortCircuit, Try}; use crate::ptr; /// An iterator over `N` elements of the iterator at a time. @@ -104,30 +104,12 @@ where } } - fn fold(self, init: B, mut f: F) -> B + fn fold(mut self, init: B, mut f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B, { - let mut array = MaybeUninit::uninit_array(); - // SAFETY: `array` will still be valid if `guard` is dropped. - let mut guard = unsafe { FrontGuard::new(&mut array) }; - - self.iter.fold(init, |mut acc, item| { - // SAFETY: `init` starts at 0, increases by one each iteration and - // is reset to 0 once it reaches N. - unsafe { array.get_unchecked_mut(guard.init) }.write(item); - guard.init += 1; - if guard.init == N { - guard.init = 0; - let array = mem::replace(&mut array, MaybeUninit::uninit_array()); - // SAFETY: the condition above asserts that all elements are - // initialized. - let item = unsafe { MaybeUninit::array_assume_init(array) }; - acc = f(acc, item); - } - acc - }) + self.try_fold(init, |acc, x| NeverShortCircuit(f(acc, x))).0 } } @@ -205,31 +187,7 @@ where Self: Sized, F: FnMut(B, Self::Item) -> B, { - // We are iterating from the back we need to first handle the remainder. - if self.next_back_remainder().is_none() { - return init; - } - - let mut array = MaybeUninit::uninit_array(); - - // SAFETY: `array` will still be valid if `guard` is dropped. - let mut guard = unsafe { BackGuard::new(&mut array) }; - - self.iter.rfold(init, |mut acc, item| { - guard.uninit -= 1; - // SAFETY: `uninit` starts at N, decreases by one each iteration and - // is reset to N once it reaches 0. - unsafe { array.get_unchecked_mut(guard.uninit) }.write(item); - if guard.uninit == 0 { - guard.uninit = N; - let array = mem::replace(&mut array, MaybeUninit::uninit_array()); - // SAFETY: the condition above asserts that all elements are - // initialized. - let item = unsafe { MaybeUninit::array_assume_init(array) }; - acc = f(acc, item); - } - acc - }) + self.try_rfold(init, |acc, x| NeverShortCircuit(f(acc, x))).0 } } From 756bd6e3a3837c7107de5e19cf19e89bfa90c0a8 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 2 Aug 2022 10:42:16 +0400 Subject: [PATCH 10/22] Use `next_chunk` in `ArrayChunks` impl --- .../core/src/iter/adapters/array_chunks.rs | 169 ++++-------------- 1 file changed, 37 insertions(+), 132 deletions(-) diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index b66e23c1e78ac..3af72c16aafb5 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -1,9 +1,6 @@ use crate::array; use crate::iter::{FusedIterator, Iterator}; -use crate::mem; -use crate::mem::MaybeUninit; use crate::ops::{ControlFlow, NeverShortCircuit, Try}; -use crate::ptr; /// An iterator over `N` elements of the iterator at a time. /// @@ -70,37 +67,18 @@ where F: FnMut(B, Self::Item) -> R, R: Try, { - let mut array = MaybeUninit::uninit_array(); - // SAFETY: `array` will still be valid if `guard` is dropped. - let mut guard = unsafe { FrontGuard::new(&mut array) }; - - let result = self.iter.try_fold(init, |mut acc, item| { - // SAFETY: `init` starts at 0, increases by one each iteration and - // is reset to 0 once it reaches N. - unsafe { array.get_unchecked_mut(guard.init) }.write(item); - guard.init += 1; - if guard.init == N { - guard.init = 0; - let array = mem::replace(&mut array, MaybeUninit::uninit_array()); - // SAFETY: the condition above asserts that all elements are - // initialized. - let item = unsafe { MaybeUninit::array_assume_init(array) }; - acc = f(acc, item)?; - } - R::from_output(acc) - }); - match result.branch() { - ControlFlow::Continue(o) => { - if guard.init > 0 { - let init = guard.init; - mem::forget(guard); - // SAFETY: `array` was initialized with `init` elements. - self.remainder = - Some(unsafe { array::IntoIter::new_unchecked(array, 0..init) }); + let mut acc = init; + loop { + match self.iter.next_chunk() { + Ok(chunk) => acc = f(acc, chunk)?, + Err(remainder) => { + // Make sure to not override `self.remainder` with an empty array + // when `next` is called after `ArrayChunks` exhaustion. + self.remainder.get_or_insert(remainder); + + break try { acc }; } - R::from_output(o) } - ControlFlow::Break(r) => R::from_residual(r), } } @@ -113,33 +91,6 @@ where } } -/// A guard for an array where elements are filled from the left. -struct FrontGuard { - /// A pointer to the array that is being filled. We need to use a raw - /// pointer here because of the lifetime issues in the fold implementations. - ptr: *mut T, - /// The number of *initialized* elements. - init: usize, -} - -impl FrontGuard { - unsafe fn new(array: &mut [MaybeUninit; N]) -> Self { - Self { ptr: MaybeUninit::slice_as_mut_ptr(array), init: 0 } - } -} - -impl Drop for FrontGuard { - fn drop(&mut self) { - debug_assert!(self.init <= N); - // SAFETY: This raw slice will only contain the initialized objects - // within the buffer. - unsafe { - let slice = ptr::slice_from_raw_parts_mut(self.ptr, self.init); - ptr::drop_in_place(slice); - } - } -} - #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] impl DoubleEndedIterator for ArrayChunks where @@ -157,29 +108,20 @@ where R: Try, { // We are iterating from the back we need to first handle the remainder. - if self.next_back_remainder().is_none() { - return R::from_output(init); - } + self.next_back_remainder(); - let mut array = MaybeUninit::uninit_array(); - // SAFETY: `array` will still be valid if `guard` is dropped. - let mut guard = unsafe { BackGuard::new(&mut array) }; + let mut acc = init; + let mut iter = self.iter.by_ref().rev(); - self.iter.try_rfold(init, |mut acc, item| { - guard.uninit -= 1; - // SAFETY: `uninit` starts at N, decreases by one each iteration and - // is reset to N once it reaches 0. - unsafe { array.get_unchecked_mut(guard.uninit) }.write(item); - if guard.uninit == 0 { - guard.uninit = N; - let array = mem::replace(&mut array, MaybeUninit::uninit_array()); - // SAFETY: the condition above asserts that all elements are - // initialized. - let item = unsafe { MaybeUninit::array_assume_init(array) }; - acc = f(acc, item)?; - } - R::from_output(acc) - }) + // NB remainder is handled by `next_back_remainder`, so + // `next_chunk` can't return `Err` with non-empty remainder + // (assuming correct `I as ExactSizeIterator` impl). + while let Ok(mut chunk) = iter.next_chunk() { + chunk.reverse(); + acc = f(acc, chunk)? + } + + try { acc } } fn rfold(mut self, init: B, mut f: F) -> B @@ -195,63 +137,26 @@ impl ArrayChunks where I: DoubleEndedIterator + ExactSizeIterator, { - #[inline] - fn next_back_remainder(&mut self) -> Option<()> { + /// Updates `self.remainder` such that `self.iter.len` is divisible by `N`. + fn next_back_remainder(&mut self) { + // Make sure to not override `self.remainder` with an empty array + // when `next_back` is called after `ArrayChunks` exhaustion. + if self.remainder.is_some() { + return; + } + // We use the `ExactSizeIterator` implementation of the underlying // iterator to know how many remaining elements there are. let rem = self.iter.len() % N; - if rem == 0 { - return Some(()); - } - - let mut array = MaybeUninit::uninit_array(); - // SAFETY: The array will still be valid if `guard` is dropped and - // it is forgotten otherwise. - let mut guard = unsafe { FrontGuard::new(&mut array) }; + // Take the last `rem` elements out of `self.iter`. + let mut remainder = + // SAFETY: `unwrap_err` always succeeds because x % N < N for all x. + unsafe { self.iter.by_ref().rev().take(rem).next_chunk().unwrap_err_unchecked() }; - // SAFETY: `rem` is in the range 1..N based on how it is calculated. - for slot in unsafe { array.get_unchecked_mut(..rem) }.iter_mut() { - slot.write(self.iter.next_back()?); - guard.init += 1; - } - - let init = guard.init; - mem::forget(guard); - // SAFETY: `array` was initialized with exactly `init` elements. - self.remainder = unsafe { - array.get_unchecked_mut(..init).reverse(); - Some(array::IntoIter::new_unchecked(array, 0..init)) - }; - Some(()) - } -} - -/// A guard for an array where elements are filled from the right. -struct BackGuard { - /// A pointer to the array that is being filled. We need to use a raw - /// pointer here because of the lifetime issues in the rfold implementations. - ptr: *mut T, - /// The number of *uninitialized* elements. - uninit: usize, -} - -impl BackGuard { - unsafe fn new(array: &mut [MaybeUninit; N]) -> Self { - Self { ptr: MaybeUninit::slice_as_mut_ptr(array), uninit: N } - } -} - -impl Drop for BackGuard { - fn drop(&mut self) { - debug_assert!(self.uninit <= N); - // SAFETY: This raw slice will only contain the initialized objects - // within the buffer. - unsafe { - let ptr = self.ptr.offset(self.uninit as isize); - let slice = ptr::slice_from_raw_parts_mut(ptr, N - self.uninit); - ptr::drop_in_place(slice); - } + // We used `.rev()` above, so we need to re-reverse the reminder + remainder.as_mut_slice().reverse(); + self.remainder = Some(remainder); } } From 86bdb3ed0912b6a8810815342a5ab570cac56ce5 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Tue, 9 Aug 2022 18:41:42 +0100 Subject: [PATCH 11/22] Rustdoc-Json: Add `Path` type for traits. Closes #100106 --- src/etc/check_missing_items.py | 58 ++++++++++---------- src/librustdoc/json/conversions.rs | 46 +++++++--------- src/rustdoc-json-types/lib.rs | 31 +++++++---- src/test/rustdoc-json/fns/generic_args.rs | 12 ++-- src/test/rustdoc-json/fns/generic_returns.rs | 2 +- src/test/rustdoc-json/fns/generics.rs | 6 +- src/test/rustdoc-json/traits/supertrait.rs | 10 ++-- src/test/rustdoc-json/type/dyn.rs | 23 ++++---- src/test/rustdoc-json/type/hrtb.rs | 2 +- 9 files changed, 96 insertions(+), 94 deletions(-) diff --git a/src/etc/check_missing_items.py b/src/etc/check_missing_items.py index a705e2384959f..ce06a79a21c3f 100644 --- a/src/etc/check_missing_items.py +++ b/src/etc/check_missing_items.py @@ -57,7 +57,7 @@ def check_generic_bound(bound): if "trait_bound" in bound: for param in bound["trait_bound"]["generic_params"]: check_generic_param(param) - check_type(bound["trait_bound"]["trait"]) + check_path(bound["trait_bound"]["trait"]) def check_decl(decl): @@ -66,35 +66,35 @@ def check_decl(decl): if decl["output"]: check_type(decl["output"]) +def check_path(path): + args = path["args"] + if args: + if "angle_bracketed" in args: + for arg in args["angle_bracketed"]["args"]: + if "type" in arg: + check_type(arg["type"]) + elif "const" in arg: + check_type(arg["const"]["type"]) + for binding in args["angle_bracketed"]["bindings"]: + if "equality" in binding["binding"]: + term = binding["binding"]["equality"] + if "type" in term: check_type(term["type"]) + elif "const" in term: check_type(term["const"]) + elif "constraint" in binding["binding"]: + for bound in binding["binding"]["constraint"]: + check_generic_bound(bound) + elif "parenthesized" in args: + for input_ty in args["parenthesized"]["inputs"]: + check_type(input_ty) + if args["parenthesized"]["output"]: + check_type(args["parenthesized"]["output"]) + if not valid_id(path["id"]): + print("Type contained an invalid ID:", path["id"]) + sys.exit(1) def check_type(ty): if ty["kind"] == "resolved_path": - for bound in ty["inner"]["param_names"]: - check_generic_bound(bound) - args = ty["inner"]["args"] - if args: - if "angle_bracketed" in args: - for arg in args["angle_bracketed"]["args"]: - if "type" in arg: - check_type(arg["type"]) - elif "const" in arg: - check_type(arg["const"]["type"]) - for binding in args["angle_bracketed"]["bindings"]: - if "equality" in binding["binding"]: - term = binding["binding"]["equality"] - if "type" in term: check_type(term["type"]) - elif "const" in term: check_type(term["const"]) - elif "constraint" in binding["binding"]: - for bound in binding["binding"]["constraint"]: - check_generic_bound(bound) - elif "parenthesized" in args: - for input_ty in args["parenthesized"]["inputs"]: - check_type(input_ty) - if args["parenthesized"]["output"]: - check_type(args["parenthesized"]["output"]) - if not valid_id(ty["inner"]["id"]): - print("Type contained an invalid ID:", ty["inner"]["id"]) - sys.exit(1) + check_path(ty["inner"]) elif ty["kind"] == "tuple": for ty in ty["inner"]: check_type(ty) @@ -111,7 +111,7 @@ def check_type(ty): check_decl(ty["inner"]["decl"]) elif ty["kind"] == "qualified_path": check_type(ty["inner"]["self_type"]) - check_type(ty["inner"]["trait"]) + check_path(ty["inner"]["trait"]) work_list = set([crate["root"]]) @@ -174,7 +174,7 @@ def check_type(ty): elif item["kind"] == "impl": check_generics(item["inner"]["generics"]) if item["inner"]["trait"]: - check_type(item["inner"]["trait"]) + check_path(item["inner"]["trait"]) if item["inner"]["blanket_impl"]: check_type(item["inner"]["blanket_impl"]) check_type(item["inner"]["for"]) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 8caba8cb9029c..9795504175cd5 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -428,10 +428,8 @@ impl FromWithTcx for GenericBound { use clean::GenericBound::*; match bound { TraitBound(clean::PolyTrait { trait_, generic_params }, modifier) => { - // FIXME: should `trait_` be a clean::Path equivalent in JSON? - let trait_ = clean::Type::Path { path: trait_ }.into_tcx(tcx); GenericBound::TraitBound { - trait_, + trait_: trait_.into_tcx(tcx), generic_params: generic_params.into_tcx(tcx), modifier: from_trait_bound_modifier(modifier), } @@ -460,12 +458,7 @@ impl FromWithTcx for Type { }; match ty { - clean::Type::Path { path } => Type::ResolvedPath { - name: path.whole_name(), - id: from_item_id(path.def_id().into(), tcx), - args: path.segments.last().map(|args| Box::new(args.clone().args.into_tcx(tcx))), - param_names: Vec::new(), - }, + clean::Type::Path { path } => Type::ResolvedPath(path.into_tcx(tcx)), clean::Type::DynTrait(bounds, lt) => Type::DynTrait(DynTrait { lifetime: lt.map(convert_lifetime), traits: bounds.into_tcx(tcx), @@ -487,16 +480,22 @@ impl FromWithTcx for Type { mutable: mutability == ast::Mutability::Mut, type_: Box::new((*type_).into_tcx(tcx)), }, - QPath { assoc, self_type, trait_, .. } => { - // FIXME: should `trait_` be a clean::Path equivalent in JSON? - let trait_ = clean::Type::Path { path: trait_ }.into_tcx(tcx); - Type::QualifiedPath { - name: assoc.name.to_string(), - args: Box::new(assoc.args.clone().into_tcx(tcx)), - self_type: Box::new((*self_type).into_tcx(tcx)), - trait_: Box::new(trait_), - } - } + QPath { assoc, self_type, trait_, .. } => Type::QualifiedPath { + name: assoc.name.to_string(), + args: Box::new(assoc.args.clone().into_tcx(tcx)), + self_type: Box::new((*self_type).into_tcx(tcx)), + trait_: trait_.into_tcx(tcx), + }, + } + } +} + +impl FromWithTcx for Path { + fn from_tcx(path: clean::Path, tcx: TyCtxt<'_>) -> Path { + Path { + name: path.whole_name(), + id: from_item_id(path.def_id().into(), tcx), + args: path.segments.last().map(|args| Box::new(args.clone().args.into_tcx(tcx))), } } } @@ -565,10 +564,7 @@ impl FromWithTcx for PolyTrait { clean::PolyTrait { trait_, generic_params }: clean::PolyTrait, tcx: TyCtxt<'_>, ) -> Self { - PolyTrait { - trait_: clean::Type::Path { path: trait_ }.into_tcx(tcx), - generic_params: generic_params.into_tcx(tcx), - } + PolyTrait { trait_: trait_.into_tcx(tcx), generic_params: generic_params.into_tcx(tcx) } } } @@ -576,8 +572,6 @@ impl FromWithTcx for Impl { fn from_tcx(impl_: clean::Impl, tcx: TyCtxt<'_>) -> Self { let provided_trait_methods = impl_.provided_trait_methods(tcx); let clean::Impl { unsafety, generics, trait_, for_, items, polarity, kind } = impl_; - // FIXME: should `trait_` be a clean::Path equivalent in JSON? - let trait_ = trait_.map(|path| clean::Type::Path { path }.into_tcx(tcx)); // FIXME: use something like ImplKind in JSON? let (synthetic, blanket_impl) = match kind { clean::ImplKind::Normal | clean::ImplKind::FakeVaradic => (false, None), @@ -595,7 +589,7 @@ impl FromWithTcx for Impl { .into_iter() .map(|x| x.to_string()) .collect(), - trait_, + trait_: trait_.map(|path| path.into_tcx(tcx)), for_: for_.into_tcx(tcx), items: ids(items, tcx), negative: negative_polarity, diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index bd4ea98441d66..c9b08fc200658 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; use serde::{Deserialize, Serialize}; /// rustdoc format-version. -pub const FORMAT_VERSION: u32 = 17; +pub const FORMAT_VERSION: u32 = 18; /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information /// about the language items in the local crate, as well as info about external items to allow @@ -133,7 +133,7 @@ pub struct DynTrait { /// A trait and potential HRTBs pub struct PolyTrait { #[serde(rename = "trait")] - pub trait_: Type, + pub trait_: Path, /// Used for Higher-Rank Trait Bounds (HRTBs) /// ```text /// dyn for<'a> Fn() -> &'a i32" @@ -447,7 +447,7 @@ pub enum WherePredicate { pub enum GenericBound { TraitBound { #[serde(rename = "trait")] - trait_: Type, + trait_: Path, /// Used for Higher-Rank Trait Bounds (HRTBs) /// ```text /// where F: for<'a, 'b> Fn(&'a u8, &'b u8) @@ -481,12 +481,7 @@ pub enum Term { #[serde(tag = "kind", content = "inner")] pub enum Type { /// Structs, enums, and traits - ResolvedPath { - name: String, - id: Id, - args: Option>, - param_names: Vec, - }, + ResolvedPath(Path), DynTrait(DynTrait), /// Parameterized types Generic(String), @@ -527,10 +522,24 @@ pub enum Type { args: Box, self_type: Box, #[serde(rename = "trait")] - trait_: Box, + trait_: Path, }, } +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Path { + pub name: String, + pub id: Id, + /// Generic arguments to the type + /// ```test + /// std::borrow::Cow<'static, str> + /// ^^^^^^^^^^^^^^ + /// | + /// this part + /// ``` + pub args: Option>, +} + #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct FunctionPointer { pub decl: FnDecl, @@ -574,7 +583,7 @@ pub struct Impl { pub generics: Generics, pub provided_trait_methods: Vec, #[serde(rename = "trait")] - pub trait_: Option, + pub trait_: Option, #[serde(rename = "for")] pub for_: Type, pub items: Vec, diff --git a/src/test/rustdoc-json/fns/generic_args.rs b/src/test/rustdoc-json/fns/generic_args.rs index 69150443c29dc..98ba8e99d820c 100644 --- a/src/test/rustdoc-json/fns/generic_args.rs +++ b/src/test/rustdoc-json/fns/generic_args.rs @@ -14,7 +14,7 @@ pub trait GenericFoo<'a> {} // @is - "$.index[*][?(@.name=='generics')].inner.generics.params[0].name" '"F"' // @is - "$.index[*][?(@.name=='generics')].inner.generics.params[0].kind.type.default" 'null' // @count - "$.index[*][?(@.name=='generics')].inner.generics.params[0].kind.type.bounds[*]" 1 -// @is - "$.index[*][?(@.name=='generics')].inner.generics.params[0].kind.type.bounds[0].trait_bound.trait.inner.id" '$foo' +// @is - "$.index[*][?(@.name=='generics')].inner.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" '$foo' // @count - "$.index[*][?(@.name=='generics')].inner.decl.inputs[*]" 1 // @is - "$.index[*][?(@.name=='generics')].inner.decl.inputs[0][0]" '"f"' // @is - "$.index[*][?(@.name=='generics')].inner.decl.inputs[0][1].kind" '"generic"' @@ -24,12 +24,12 @@ pub fn generics(f: F) {} // @is - "$.index[*][?(@.name=='impl_trait')].inner.generics.where_predicates" "[]" // @count - "$.index[*][?(@.name=='impl_trait')].inner.generics.params[*]" 1 // @is - "$.index[*][?(@.name=='impl_trait')].inner.generics.params[0].name" '"impl Foo"' -// @is - "$.index[*][?(@.name=='impl_trait')].inner.generics.params[0].kind.type.bounds[0].trait_bound.trait.inner.id" $foo +// @is - "$.index[*][?(@.name=='impl_trait')].inner.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $foo // @count - "$.index[*][?(@.name=='impl_trait')].inner.decl.inputs[*]" 1 // @is - "$.index[*][?(@.name=='impl_trait')].inner.decl.inputs[0][0]" '"f"' // @is - "$.index[*][?(@.name=='impl_trait')].inner.decl.inputs[0][1].kind" '"impl_trait"' // @count - "$.index[*][?(@.name=='impl_trait')].inner.decl.inputs[0][1].inner[*]" 1 -// @is - "$.index[*][?(@.name=='impl_trait')].inner.decl.inputs[0][1].inner[0].trait_bound.trait.inner.id" $foo +// @is - "$.index[*][?(@.name=='impl_trait')].inner.decl.inputs[0][1].inner[0].trait_bound.trait.id" $foo pub fn impl_trait(f: impl Foo) {} // @count - "$.index[*][?(@.name=='where_clase')].inner.generics.params[*]" 3 @@ -43,11 +43,11 @@ pub fn impl_trait(f: impl Foo) {} // @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[0].bound_predicate.type" '{"inner": "F", "kind": "generic"}' // @count - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[0].bound_predicate.bounds[*]" 1 -// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[0].bound_predicate.bounds[0].trait_bound.trait.inner.id" $foo +// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[0].bound_predicate.bounds[0].trait_bound.trait.id" $foo // @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.type" '{"inner": "G", "kind": "generic"}' // @count - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[*]" 1 -// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.trait.inner.id" $generic_foo +// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.trait.id" $generic_foo // @count - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.generic_params[*]" 1 // @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.generic_params[0].name" \"\'a\" // @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[1].bound_predicate.bounds[0].trait_bound.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }' @@ -57,7 +57,7 @@ pub fn impl_trait(f: impl Foo) {} // @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.type.inner.lifetime" \"\'b\" // @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.type.inner.type" '{"inner": "H", "kind": "generic"}' // @count - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.bounds[*]" 1 -// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.bounds[0].trait_bound.trait.inner.id" $foo +// @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.bounds[0].trait_bound.trait.id" $foo // @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.bounds[0].trait_bound.generic_params" "[]" // @count - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.generic_params[*]" 1 // @is - "$.index[*][?(@.name=='where_clase')].inner.generics.where_predicates[2].bound_predicate.generic_params[0].name" \"\'b\" diff --git a/src/test/rustdoc-json/fns/generic_returns.rs b/src/test/rustdoc-json/fns/generic_returns.rs index 1a0f33fe3d28e..46f250a99b9b6 100644 --- a/src/test/rustdoc-json/fns/generic_returns.rs +++ b/src/test/rustdoc-json/fns/generic_returns.rs @@ -11,7 +11,7 @@ pub trait Foo {} // @is - "$.index[*][?(@.name=='get_foo')].inner.decl.inputs" [] // @is - "$.index[*][?(@.name=='get_foo')].inner.decl.output.kind" '"impl_trait"' // @count - "$.index[*][?(@.name=='get_foo')].inner.decl.output.inner[*]" 1 -// @is - "$.index[*][?(@.name=='get_foo')].inner.decl.output.inner[0].trait_bound.trait.inner.id" $foo +// @is - "$.index[*][?(@.name=='get_foo')].inner.decl.output.inner[0].trait_bound.trait.id" $foo pub fn get_foo() -> impl Foo { Fooer {} } diff --git a/src/test/rustdoc-json/fns/generics.rs b/src/test/rustdoc-json/fns/generics.rs index e777fabaa52a7..e55e1e9400dc6 100644 --- a/src/test/rustdoc-json/fns/generics.rs +++ b/src/test/rustdoc-json/fns/generics.rs @@ -10,7 +10,7 @@ pub trait Wham {} // @count - "$.index[*][?(@.name=='one_generic_param_fn')].inner.generics.params[*]" 1 // @is - "$.index[*][?(@.name=='one_generic_param_fn')].inner.generics.params[0].name" '"T"' // @has - "$.index[*][?(@.name=='one_generic_param_fn')].inner.generics.params[0].kind.type.synthetic" false -// @has - "$.index[*][?(@.name=='one_generic_param_fn')].inner.generics.params[0].kind.type.bounds[0].trait_bound.trait.inner.id" $wham_id +// @has - "$.index[*][?(@.name=='one_generic_param_fn')].inner.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $wham_id // @is - "$.index[*][?(@.name=='one_generic_param_fn')].inner.decl.inputs" '[["w", {"inner": "T", "kind": "generic"}]]' pub fn one_generic_param_fn(w: T) {} @@ -18,9 +18,9 @@ pub fn one_generic_param_fn(w: T) {} // @count - "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.generics.params[*]" 1 // @is - "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.generics.params[0].name" '"impl Wham"' // @has - "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.generics.params[0].kind.type.synthetic" true -// @has - "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.generics.params[0].kind.type.bounds[0].trait_bound.trait.inner.id" $wham_id +// @has - "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $wham_id // @count - "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.decl.inputs[*]" 1 // @is - "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.decl.inputs[0][0]" '"w"' // @is - "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.decl.inputs[0][1].kind" '"impl_trait"' -// @is - "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.decl.inputs[0][1].inner[0].trait_bound.trait.inner.id" $wham_id +// @is - "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.decl.inputs[0][1].inner[0].trait_bound.trait.id" $wham_id pub fn one_synthetic_generic_param_fn(w: impl Wham) {} diff --git a/src/test/rustdoc-json/traits/supertrait.rs b/src/test/rustdoc-json/traits/supertrait.rs index 486a8e713f811..ce2f3912ba650 100644 --- a/src/test/rustdoc-json/traits/supertrait.rs +++ b/src/test/rustdoc-json/traits/supertrait.rs @@ -9,18 +9,18 @@ pub trait Loud {} // @set very_loud_id = - "$.index[*][?(@.name=='VeryLoud')].id" // @count - "$.index[*][?(@.name=='VeryLoud')].inner.bounds[*]" 1 -// @is - "$.index[*][?(@.name=='VeryLoud')].inner.bounds[0].trait_bound.trait.inner.id" $loud_id +// @is - "$.index[*][?(@.name=='VeryLoud')].inner.bounds[0].trait_bound.trait.id" $loud_id pub trait VeryLoud: Loud {} // @set sounds_good_id = - "$.index[*][?(@.name=='SoundsGood')].id" pub trait SoundsGood {} // @count - "$.index[*][?(@.name=='MetalBand')].inner.bounds[*]" 2 -// @is - "$.index[*][?(@.name=='MetalBand')].inner.bounds[0].trait_bound.trait.inner.id" $very_loud_id -// @is - "$.index[*][?(@.name=='MetalBand')].inner.bounds[1].trait_bound.trait.inner.id" $sounds_good_id +// @is - "$.index[*][?(@.name=='MetalBand')].inner.bounds[0].trait_bound.trait.id" $very_loud_id +// @is - "$.index[*][?(@.name=='MetalBand')].inner.bounds[1].trait_bound.trait.id" $sounds_good_id pub trait MetalBand: VeryLoud + SoundsGood {} // @count - "$.index[*][?(@.name=='DnabLatem')].inner.bounds[*]" 2 -// @is - "$.index[*][?(@.name=='DnabLatem')].inner.bounds[1].trait_bound.trait.inner.id" $very_loud_id -// @is - "$.index[*][?(@.name=='DnabLatem')].inner.bounds[0].trait_bound.trait.inner.id" $sounds_good_id +// @is - "$.index[*][?(@.name=='DnabLatem')].inner.bounds[1].trait_bound.trait.id" $very_loud_id +// @is - "$.index[*][?(@.name=='DnabLatem')].inner.bounds[0].trait_bound.trait.id" $sounds_good_id pub trait DnabLatem: SoundsGood + VeryLoud {} diff --git a/src/test/rustdoc-json/type/dyn.rs b/src/test/rustdoc-json/type/dyn.rs index c18b54d1fdf0e..690dccc828704 100644 --- a/src/test/rustdoc-json/type/dyn.rs +++ b/src/test/rustdoc-json/type/dyn.rs @@ -21,10 +21,10 @@ use std::fmt::Debug; // @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[0].generic_params" [] // @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[1].generic_params" [] // @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[2].generic_params" [] -// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[0].trait.inner.name" '"Fn"' -// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[1].trait.inner.name" '"Send"' -// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[2].trait.inner.name" '"Sync"' -// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[0].trait.inner.args" '{"parenthesized": {"inputs": [],"output": {"inner": "i32","kind": "primitive"}}}' +// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[0].trait.name" '"Fn"' +// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[1].trait.name" '"Send"' +// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[2].trait.name" '"Sync"' +// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[0].trait.args" '{"parenthesized": {"inputs": [],"output": {"inner": "i32","kind": "primitive"}}}' pub type SyncIntGen = Box i32 + Send + Sync + 'static>; // @is - "$.index[*][?(@.name=='RefFn')].kind" \"typedef\" @@ -36,14 +36,13 @@ pub type SyncIntGen = Box i32 + Send + Sync + 'static>; // @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.lifetime" null // @count - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[*]" 1 // @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].generic_params" '[{"kind": {"lifetime": {"outlives": []}},"name": "'\''b"}]' -// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.kind" '"resolved_path"' -// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.inner.name" '"Fn"' -// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.inner.args.parenthesized.inputs[0].kind" '"borrowed_ref"' -// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.inner.args.parenthesized.inputs[0].inner.lifetime" "\"'b\"" -// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.inner.args.parenthesized.output.kind" '"borrowed_ref"' -// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.inner.args.parenthesized.output.inner.lifetime" "\"'b\"" +// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.name" '"Fn"' +// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.args.parenthesized.inputs[0].kind" '"borrowed_ref"' +// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.args.parenthesized.inputs[0].inner.lifetime" "\"'b\"" +// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.args.parenthesized.output.kind" '"borrowed_ref"' +// @is - "$.index[*][?(@.name=='RefFn')].inner.type.inner.type.inner.traits[0].trait.args.parenthesized.output.inner.lifetime" "\"'b\"" pub type RefFn<'a> = &'a dyn for<'b> Fn(&'b i32) -> &'b i32; -// @is - "$.index[*][?(@.name=='WeirdOrder')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[0].trait.inner.name" '"Send"' -// @is - "$.index[*][?(@.name=='WeirdOrder')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[1].trait.inner.name" '"Debug"' +// @is - "$.index[*][?(@.name=='WeirdOrder')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[0].trait.name" '"Send"' +// @is - "$.index[*][?(@.name=='WeirdOrder')].inner.type.inner.args.angle_bracketed.args[0].type.inner.traits[1].trait.name" '"Debug"' pub type WeirdOrder = Box; diff --git a/src/test/rustdoc-json/type/hrtb.rs b/src/test/rustdoc-json/type/hrtb.rs index 9311737be0fec..5b0c4caee2186 100644 --- a/src/test/rustdoc-json/type/hrtb.rs +++ b/src/test/rustdoc-json/type/hrtb.rs @@ -19,7 +19,7 @@ where // @is - "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].inner.type.inner.lifetime" null // @count - "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].inner.type.inner.traits[*]" 1 // @is - "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].inner.type.inner.traits[0].generic_params" '[{"kind": {"lifetime": {"outlives": []}},"name": "'\''a"},{"kind": {"lifetime": {"outlives": []}},"name": "'\''b"}]' -// @is - "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].inner.type.inner.traits[0].trait.inner.name" '"Fn"' +// @is - "$.index[*][?(@.name=='dynfn')].inner.decl.inputs[0][1].inner.type.inner.traits[0].trait.name" '"Fn"' pub fn dynfn(f: &dyn for<'a, 'b> Fn(&'a i32, &'b i32)) { let zero = 0; f(&zero, &zero); From 0fb4ef6769dd2f5ccb3daf1e84c0afe75ca8c118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 10 Aug 2022 14:06:27 +0200 Subject: [PATCH 12/22] Suggest path separator when a dot is used on a trait --- .../rustc_resolve/src/late/diagnostics.rs | 53 ++++++++---- src/test/ui/resolve/issue-100365.rs | 50 +++++++++++ src/test/ui/resolve/issue-100365.stderr | 54 ++++++++++++ src/test/ui/resolve/issue-22692.rs | 59 ++++++++++++- src/test/ui/resolve/issue-22692.stderr | 85 ++++++++++++++++++- .../suggest-path-for-tuple-struct.stderr | 8 +- .../suggest-path-instead-of-mod-dot-item.rs | 52 ++++++++++++ ...uggest-path-instead-of-mod-dot-item.stderr | 82 +++++++++++++----- .../suggestions/assoc-const-as-field.stderr | 4 +- 9 files changed, 394 insertions(+), 53 deletions(-) create mode 100644 src/test/ui/resolve/issue-100365.rs create mode 100644 src/test/ui/resolve/issue-100365.stderr diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index cb133841bca5a..09c1f44826fa4 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -985,27 +985,45 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let ns = source.namespace(); let is_expected = &|res| source.is_expected(res); - let path_sep = |err: &mut Diagnostic, expr: &Expr| match expr.kind { - ExprKind::Field(_, ident) => { + let path_sep = |err: &mut Diagnostic, expr: &Expr, kind: DefKind| { + const MESSAGE: &str = "use the path separator to refer to an item"; + + let (lhs_span, rhs_span) = match &expr.kind { + ExprKind::Field(base, ident) => (base.span, ident.span), + ExprKind::MethodCall(_, receiver, _, span) => (receiver.span, *span), + _ => return false, + }; + + if lhs_span.eq_ctxt(rhs_span) { err.span_suggestion( - expr.span, - "use the path separator to refer to an item", - format!("{}::{}", path_str, ident), + lhs_span.between(rhs_span), + MESSAGE, + "::", Applicability::MaybeIncorrect, ); true - } - ExprKind::MethodCall(ref segment, ..) => { - let span = expr.span.with_hi(segment.ident.span.hi()); - err.span_suggestion( - span, - "use the path separator to refer to an item", - format!("{}::{}", path_str, segment.ident), + } else if kind == DefKind::Struct + && let Some(lhs_source_span) = lhs_span.find_ancestor_inside(expr.span) + && let Ok(snippet) = self.r.session.source_map().span_to_snippet(lhs_source_span) + { + // The LHS is a type that originates from a macro call. + // We have to add angle brackets around it. + + err.span_suggestion_verbose( + lhs_source_span.until(rhs_span), + MESSAGE, + format!("<{snippet}>::"), Applicability::MaybeIncorrect, ); true + } else { + // Either we were unable to obtain the source span / the snippet or + // the LHS originates from a macro call and it is not a type and thus + // there is no way to replace `.` with `::` and still somehow suggest + // valid Rust code. + + false } - _ => false, }; let find_span = |source: &PathSource<'_>, err: &mut Diagnostic| { @@ -1027,7 +1045,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { match source { PathSource::Expr(Some( parent @ Expr { kind: ExprKind::Field(..) | ExprKind::MethodCall(..), .. }, - )) if path_sep(err, &parent) => {} + )) if path_sep(err, &parent, DefKind::Struct) => {} PathSource::Expr( None | Some(Expr { @@ -1143,8 +1161,11 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } } - (Res::Def(DefKind::Mod, _), PathSource::Expr(Some(parent))) => { - if !path_sep(err, &parent) { + ( + Res::Def(kind @ (DefKind::Mod | DefKind::Trait), _), + PathSource::Expr(Some(parent)), + ) => { + if !path_sep(err, &parent, kind) { return false; } } diff --git a/src/test/ui/resolve/issue-100365.rs b/src/test/ui/resolve/issue-100365.rs new file mode 100644 index 0000000000000..1d8835036065b --- /dev/null +++ b/src/test/ui/resolve/issue-100365.rs @@ -0,0 +1,50 @@ +fn main() { + let addr = Into::.into([127, 0, 0, 1]); + //~^ ERROR expected value, found trait `Into` + //~| HELP use the path separator + + let _ = Into.into(()); + //~^ ERROR expected value, found trait `Into` + //~| HELP use the path separator + + let _ = Into::<()>.into; + //~^ ERROR expected value, found trait `Into` + //~| HELP use the path separator +} + +macro_rules! Trait { + () => { + ::std::iter::Iterator + //~^ ERROR expected value, found trait `std::iter::Iterator` + //~| ERROR expected value, found trait `std::iter::Iterator` + }; +} + +macro_rules! create { + () => { + Into::.into("") + //~^ ERROR expected value, found trait `Into` + //~| HELP use the path separator + }; +} + +fn interaction_with_macros() { + // + // Note that if the receiver is a macro call, we do not want to suggest to replace + // `.` with `::` as that would be a syntax error. + // Since the receiver is a trait and not a type, we cannot suggest to surround + // it with angle brackets. It would be interpreted as a trait object type void of + // `dyn` which is most likely not what the user intended to write. + // `<_ as Trait!()>::` is also not an option as it's equally syntactically invalid. + // + + Trait!().map(std::convert::identity); // no `help` here! + + Trait!().map; // no `help` here! + + // + // Ensure that the suggestion is shown for expressions inside of macro definitions. + // + + let _ = create!(); +} diff --git a/src/test/ui/resolve/issue-100365.stderr b/src/test/ui/resolve/issue-100365.stderr new file mode 100644 index 0000000000000..372d772666804 --- /dev/null +++ b/src/test/ui/resolve/issue-100365.stderr @@ -0,0 +1,54 @@ +error[E0423]: expected value, found trait `Into` + --> $DIR/issue-100365.rs:2:16 + | +LL | let addr = Into::.into([127, 0, 0, 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^- help: use the path separator to refer to an item: `::` + +error[E0423]: expected value, found trait `Into` + --> $DIR/issue-100365.rs:6:13 + | +LL | let _ = Into.into(()); + | ^^^^- help: use the path separator to refer to an item: `::` + +error[E0423]: expected value, found trait `Into` + --> $DIR/issue-100365.rs:10:13 + | +LL | let _ = Into::<()>.into; + | ^^^^^^^^^^- help: use the path separator to refer to an item: `::` + +error[E0423]: expected value, found trait `std::iter::Iterator` + --> $DIR/issue-100365.rs:17:9 + | +LL | ::std::iter::Iterator + | ^^^^^^^^^^^^^^^^^^^^^ not a value +... +LL | Trait!().map(std::convert::identity); // no `help` here! + | -------- in this macro invocation + | + = note: this error originates in the macro `Trait` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0423]: expected value, found trait `std::iter::Iterator` + --> $DIR/issue-100365.rs:17:9 + | +LL | ::std::iter::Iterator + | ^^^^^^^^^^^^^^^^^^^^^ not a value +... +LL | Trait!().map; // no `help` here! + | -------- in this macro invocation + | + = note: this error originates in the macro `Trait` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0423]: expected value, found trait `Into` + --> $DIR/issue-100365.rs:25:9 + | +LL | Into::.into("") + | ^^^^^^^^^^^^^^- help: use the path separator to refer to an item: `::` +... +LL | let _ = create!(); + | --------- in this macro invocation + | + = note: this error originates in the macro `create` (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 E0423`. diff --git a/src/test/ui/resolve/issue-22692.rs b/src/test/ui/resolve/issue-22692.rs index 1d8f442221df9..31a76261408ef 100644 --- a/src/test/ui/resolve/issue-22692.rs +++ b/src/test/ui/resolve/issue-22692.rs @@ -1,3 +1,60 @@ fn main() { - let _ = String.new(); //~ ERROR expected value, found struct `String` + let _ = String.new(); + //~^ ERROR expected value, found struct `String` + //~| HELP use the path separator + + let _ = String.default; + //~^ ERROR expected value, found struct `String` + //~| HELP use the path separator + + let _ = Vec::<()>.with_capacity(1); + //~^ ERROR expected value, found struct `Vec` + //~| HELP use the path separator +} + +macro_rules! Type { + () => { + ::std::cell::Cell + //~^ ERROR expected value, found struct `std::cell::Cell` + //~| ERROR expected value, found struct `std::cell::Cell` + //~| ERROR expected value, found struct `std::cell::Cell` + }; +} + +macro_rules! create { + (type method) => { + Vec.new() + //~^ ERROR expected value, found struct `Vec` + //~| HELP use the path separator + }; + (type field) => { + Vec.new + //~^ ERROR expected value, found struct `Vec` + //~| HELP use the path separator + }; + (macro method) => { + Type!().new(0) + //~^ HELP use the path separator + }; +} + +fn interaction_with_macros() { + // + // Verify that we do not only suggest to replace `.` with `::` if the receiver is a + // macro call but that we also correctly suggest to surround it with angle brackets. + // + + Type!().get(); + //~^ HELP use the path separator + + Type! {}.get; + //~^ HELP use the path separator + + // + // Ensure that the suggestion is shown for expressions inside of macro definitions. + // + + let _ = create!(type method); + let _ = create!(type field); + let _ = create!(macro method); } diff --git a/src/test/ui/resolve/issue-22692.stderr b/src/test/ui/resolve/issue-22692.stderr index e076419f68d47..6962aa161e92a 100644 --- a/src/test/ui/resolve/issue-22692.stderr +++ b/src/test/ui/resolve/issue-22692.stderr @@ -2,10 +2,87 @@ error[E0423]: expected value, found struct `String` --> $DIR/issue-22692.rs:2:13 | LL | let _ = String.new(); - | ^^^^^^---- - | | - | help: use the path separator to refer to an item: `String::new` + | ^^^^^^- help: use the path separator to refer to an item: `::` -error: aborting due to previous error +error[E0423]: expected value, found struct `String` + --> $DIR/issue-22692.rs:6:13 + | +LL | let _ = String.default; + | ^^^^^^- help: use the path separator to refer to an item: `::` + +error[E0423]: expected value, found struct `Vec` + --> $DIR/issue-22692.rs:10:13 + | +LL | let _ = Vec::<()>.with_capacity(1); + | ^^^^^^^^^- help: use the path separator to refer to an item: `::` + +error[E0423]: expected value, found struct `std::cell::Cell` + --> $DIR/issue-22692.rs:17:9 + | +LL | ::std::cell::Cell + | ^^^^^^^^^^^^^^^^^ +... +LL | Type!().get(); + | ------- in this macro invocation + | + = note: this error originates in the macro `Type` (in Nightly builds, run with -Z macro-backtrace for more info) +help: use the path separator to refer to an item + | +LL | ::get(); + | ~~~~~~~~~~~ + +error[E0423]: expected value, found struct `std::cell::Cell` + --> $DIR/issue-22692.rs:17:9 + | +LL | ::std::cell::Cell + | ^^^^^^^^^^^^^^^^^ +... +LL | Type! {}.get; + | -------- in this macro invocation + | + = note: this error originates in the macro `Type` (in Nightly builds, run with -Z macro-backtrace for more info) +help: use the path separator to refer to an item + | +LL | ::get; + | ~~~~~~~~~~~~ + +error[E0423]: expected value, found struct `Vec` + --> $DIR/issue-22692.rs:26:9 + | +LL | Vec.new() + | ^^^- help: use the path separator to refer to an item: `::` +... +LL | let _ = create!(type method); + | -------------------- in this macro invocation + | + = note: this error originates in the macro `create` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0423]: expected value, found struct `Vec` + --> $DIR/issue-22692.rs:31:9 + | +LL | Vec.new + | ^^^- help: use the path separator to refer to an item: `::` +... +LL | let _ = create!(type field); + | ------------------- in this macro invocation + | + = note: this error originates in the macro `create` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0423]: expected value, found struct `std::cell::Cell` + --> $DIR/issue-22692.rs:17:9 + | +LL | ::std::cell::Cell + | ^^^^^^^^^^^^^^^^^ +... +LL | let _ = create!(macro method); + | --------------------- in this macro invocation + | + = note: this error originates in the macro `Type` which comes from the expansion of the macro `create` (in Nightly builds, run with -Z macro-backtrace for more info) +help: use the path separator to refer to an item + | +LL | ::new(0) + | ~~~~~~~~~~~ + +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0423`. diff --git a/src/test/ui/resolve/suggest-path-for-tuple-struct.stderr b/src/test/ui/resolve/suggest-path-for-tuple-struct.stderr index 957045ca74bb2..4764cf2db20e8 100644 --- a/src/test/ui/resolve/suggest-path-for-tuple-struct.stderr +++ b/src/test/ui/resolve/suggest-path-for-tuple-struct.stderr @@ -2,17 +2,13 @@ error[E0423]: expected value, found struct `SomeTupleStruct` --> $DIR/suggest-path-for-tuple-struct.rs:22:13 | LL | let _ = SomeTupleStruct.new(); - | ^^^^^^^^^^^^^^^---- - | | - | help: use the path separator to refer to an item: `SomeTupleStruct::new` + | ^^^^^^^^^^^^^^^- help: use the path separator to refer to an item: `::` error[E0423]: expected value, found struct `SomeRegularStruct` --> $DIR/suggest-path-for-tuple-struct.rs:24:13 | LL | let _ = SomeRegularStruct.new(); - | ^^^^^^^^^^^^^^^^^---- - | | - | help: use the path separator to refer to an item: `SomeRegularStruct::new` + | ^^^^^^^^^^^^^^^^^- help: use the path separator to refer to an item: `::` error: aborting due to 2 previous errors diff --git a/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.rs b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.rs index 204a272402dd7..d5d6b13d62c28 100644 --- a/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.rs +++ b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.rs @@ -16,44 +16,96 @@ pub mod a { fn h1() -> i32 { a.I //~^ ERROR expected value, found module `a` + //~| HELP use the path separator } fn h2() -> i32 { a.g() //~^ ERROR expected value, found module `a` + //~| HELP use the path separator } fn h3() -> i32 { a.b.J //~^ ERROR expected value, found module `a` + //~| HELP use the path separator } fn h4() -> i32 { a::b.J //~^ ERROR expected value, found module `a::b` + //~| HELP a constant with a similar name exists + //~| HELP use the path separator } fn h5() { a.b.f(); //~^ ERROR expected value, found module `a` + //~| HELP use the path separator let v = Vec::new(); v.push(a::b); //~^ ERROR expected value, found module `a::b` + //~| HELP a constant with a similar name exists } fn h6() -> i32 { a::b.f() //~^ ERROR expected value, found module `a::b` + //~| HELP a constant with a similar name exists + //~| HELP use the path separator } fn h7() { a::b //~^ ERROR expected value, found module `a::b` + //~| HELP a constant with a similar name exists } fn h8() -> i32 { a::b() //~^ ERROR expected function, found module `a::b` + //~| HELP a constant with a similar name exists +} + +macro_rules! module { + () => { + a + //~^ ERROR expected value, found module `a` + //~| ERROR expected value, found module `a` + }; +} + +macro_rules! create { + (method) => { + a.f() + //~^ ERROR expected value, found module `a` + //~| HELP use the path separator + }; + (field) => { + a.f + //~^ ERROR expected value, found module `a` + //~| HELP use the path separator + }; +} + +fn h9() { + // + // Note that if the receiver is a macro call, we do not want to suggest to replace + // `.` with `::` as that would be a syntax error. + // Since the receiver is a module and not a type, we cannot suggest to surround + // it with angle brackets. + // + + module!().g::<()>(); // no `help` here! + + module!().g; // no `help` here! + + // + // Ensure that the suggestion is shown for expressions inside of macro definitions. + // + + let _ = create!(method); + let _ = create!(field); } fn main() {} diff --git a/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr index 54b242123eb8f..a4ce0deeb70f5 100644 --- a/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr +++ b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr @@ -2,28 +2,22 @@ error[E0423]: expected value, found module `a` --> $DIR/suggest-path-instead-of-mod-dot-item.rs:17:5 | LL | a.I - | ^-- - | | - | help: use the path separator to refer to an item: `a::I` + | ^- help: use the path separator to refer to an item: `::` error[E0423]: expected value, found module `a` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:22:5 + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:23:5 | LL | a.g() - | ^-- - | | - | help: use the path separator to refer to an item: `a::g` + | ^- help: use the path separator to refer to an item: `::` error[E0423]: expected value, found module `a` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:27:5 + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:29:5 | LL | a.b.J - | ^-- - | | - | help: use the path separator to refer to an item: `a::b` + | ^- help: use the path separator to refer to an item: `::` error[E0423]: expected value, found module `a::b` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:32:5 + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:35:5 | LL | pub const I: i32 = 1; | --------------------- similarly named constant `I` defined here @@ -34,22 +28,20 @@ LL | a::b.J help: use the path separator to refer to an item | LL | a::b::J - | + | ~~ help: a constant with a similar name exists | LL | a::I.J | ~ error[E0423]: expected value, found module `a` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:37:5 + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:42:5 | LL | a.b.f(); - | ^-- - | | - | help: use the path separator to refer to an item: `a::b` + | ^- help: use the path separator to refer to an item: `::` error[E0423]: expected value, found module `a::b` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:40:12 + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:46:12 | LL | pub const I: i32 = 1; | --------------------- similarly named constant `I` defined here @@ -60,7 +52,7 @@ LL | v.push(a::b); | help: a constant with a similar name exists: `I` error[E0423]: expected value, found module `a::b` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:45:5 + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:52:5 | LL | pub const I: i32 = 1; | --------------------- similarly named constant `I` defined here @@ -71,14 +63,14 @@ LL | a::b.f() help: use the path separator to refer to an item | LL | a::b::f() - | ~~~~~~~ + | ~~ help: a constant with a similar name exists | LL | a::I.f() | ~ error[E0423]: expected value, found module `a::b` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:50:5 + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:59:5 | LL | pub const I: i32 = 1; | --------------------- similarly named constant `I` defined here @@ -89,7 +81,7 @@ LL | a::b | help: a constant with a similar name exists: `I` error[E0423]: expected function, found module `a::b` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:55:5 + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:65:5 | LL | pub const I: i32 = 1; | --------------------- similarly named constant `I` defined here @@ -99,6 +91,50 @@ LL | a::b() | | | help: a constant with a similar name exists: `I` -error: aborting due to 9 previous errors +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:72:9 + | +LL | a + | ^ not a value +... +LL | module!().g::<()>(); // no `help` here! + | --------- in this macro invocation + | + = note: this error originates in the macro `module` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:72:9 + | +LL | a + | ^ not a value +... +LL | module!().g; // no `help` here! + | --------- in this macro invocation + | + = note: this error originates in the macro `module` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:80:9 + | +LL | a.f() + | ^- help: use the path separator to refer to an item: `::` +... +LL | let _ = create!(method); + | --------------- in this macro invocation + | + = note: this error originates in the macro `create` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:85:9 + | +LL | a.f + | ^- help: use the path separator to refer to an item: `::` +... +LL | let _ = create!(field); + | -------------- in this macro invocation + | + = note: this error originates in the macro `create` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 13 previous errors For more information about this error, try `rustc --explain E0423`. diff --git a/src/test/ui/suggestions/assoc-const-as-field.stderr b/src/test/ui/suggestions/assoc-const-as-field.stderr index 5e746ecb2f28f..78e5634b2de2a 100644 --- a/src/test/ui/suggestions/assoc-const-as-field.stderr +++ b/src/test/ui/suggestions/assoc-const-as-field.stderr @@ -2,9 +2,7 @@ error[E0423]: expected value, found struct `Mod::Foo` --> $DIR/assoc-const-as-field.rs:11:9 | LL | foo(Mod::Foo.Bar); - | ^^^^^^^^---- - | | - | help: use the path separator to refer to an item: `Mod::Foo::Bar` + | ^^^^^^^^- help: use the path separator to refer to an item: `::` error: aborting due to previous error From e182d12a8493b40a557394325a3a713b6528de60 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 12 Aug 2022 02:47:57 +0000 Subject: [PATCH 13/22] Fix HIR pretty printing of let else --- compiler/rustc_hir_pretty/src/lib.rs | 4 ++++ src/test/ui/unpretty/pretty-let-else.rs | 10 ++++++++++ src/test/ui/unpretty/pretty-let-else.stdout | 18 ++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 src/test/ui/unpretty/pretty-let-else.rs create mode 100644 src/test/ui/unpretty/pretty-let-else.stdout diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index e0179bd3ed1e8..c7966b662de9f 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -911,6 +911,10 @@ impl<'a> State<'a> { if let Some(els) = els { self.nbsp(); self.word_space("else"); + // containing cbox, will be closed by print-block at `}` + self.cbox(0); + // head-box, will be closed by print-block after `{` + self.ibox(0); self.print_block(els); } diff --git a/src/test/ui/unpretty/pretty-let-else.rs b/src/test/ui/unpretty/pretty-let-else.rs new file mode 100644 index 0000000000000..5abfa2523b74e --- /dev/null +++ b/src/test/ui/unpretty/pretty-let-else.rs @@ -0,0 +1,10 @@ +// compile-flags: -Zunpretty=hir +// check-pass + +#![feature(let_else)] + +fn foo(x: Option) { + let Some(_) = x else { panic!() }; +} + +fn main() {} diff --git a/src/test/ui/unpretty/pretty-let-else.stdout b/src/test/ui/unpretty/pretty-let-else.stdout new file mode 100644 index 0000000000000..ffe1d1657aa15 --- /dev/null +++ b/src/test/ui/unpretty/pretty-let-else.stdout @@ -0,0 +1,18 @@ +// compile-flags: -Zunpretty=hir +// check-pass + +#![feature(let_else)] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; + +fn foo(x: + Option) { + let Some(_) = x else + { + + { ::std::rt::begin_panic("explicit panic") } + }; + } +fn main() { } From a65647a7f618d8b2d4367dbbfc197b17ebb00d80 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 12 Aug 2022 11:37:32 +0200 Subject: [PATCH 14/22] remove Clean trait implementation for hir::PathSegment --- src/librustdoc/clean/mod.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 6e24638548979..f447b2bfa7787 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1322,14 +1322,17 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type let trait_def = cx.tcx.associated_item(p.res.def_id()).container_id(cx.tcx); let trait_ = self::Path { res: Res::Def(DefKind::Trait, trait_def), - segments: trait_segments.iter().map(|x| x.clean(cx)).collect(), + segments: trait_segments.iter().map(|x| clean_path_segment(x, cx)).collect(), }; register_res(cx, trait_.res); let self_def_id = DefId::local(qself.hir_id.owner.local_def_index); let self_type = clean_ty(qself, cx); let should_show_cast = compute_should_show_cast(Some(self_def_id), &trait_, &self_type); Type::QPath { - assoc: Box::new(p.segments.last().expect("segments were empty").clean(cx)), + assoc: Box::new(clean_path_segment( + p.segments.last().expect("segments were empty"), + cx, + )), should_show_cast, self_type: Box::new(self_type), trait_, @@ -1349,7 +1352,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type let self_type = clean_ty(qself, cx); let should_show_cast = compute_should_show_cast(self_def_id, &trait_, &self_type); Type::QPath { - assoc: Box::new(segment.clean(cx)), + assoc: Box::new(clean_path_segment(segment, cx)), should_show_cast, self_type: Box::new(self_type), trait_, @@ -1823,7 +1826,10 @@ fn clean_variant_data<'tcx>( } fn clean_path<'tcx>(path: &hir::Path<'tcx>, cx: &mut DocContext<'tcx>) -> Path { - Path { res: path.res, segments: path.segments.iter().map(|x| x.clean(cx)).collect() } + Path { + res: path.res, + segments: path.segments.iter().map(|x| clean_path_segment(x, cx)).collect(), + } } fn clean_generic_args<'tcx>( @@ -1861,10 +1867,11 @@ fn clean_generic_args<'tcx>( } } -impl<'tcx> Clean<'tcx, PathSegment> for hir::PathSegment<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> PathSegment { - PathSegment { name: self.ident.name, args: clean_generic_args(self.args(), cx) } - } +fn clean_path_segment<'tcx>( + path: &hir::PathSegment<'tcx>, + cx: &mut DocContext<'tcx>, +) -> PathSegment { + PathSegment { name: path.ident.name, args: clean_generic_args(path.args(), cx) } } impl<'tcx> Clean<'tcx, BareFunctionDecl> for hir::BareFnTy<'tcx> { From 001cc12af78fd179622f6dbe0f7d61ceeebf4522 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 12 Aug 2022 11:40:39 +0200 Subject: [PATCH 15/22] remove Clean trait implementation for hir::BareFnTy --- src/librustdoc/clean/mod.rs | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index f447b2bfa7787..addd6ffa11e28 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1510,7 +1510,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T if !lifetime.is_elided() { Some(clean_lifetime(*lifetime, cx)) } else { None }; DynTrait(bounds, lifetime) } - TyKind::BareFn(barefn) => BareFunction(Box::new(barefn.clean(cx))), + TyKind::BareFn(barefn) => BareFunction(Box::new(clean_bare_fn_ty(barefn, cx))), // Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s. TyKind::Infer | TyKind::Err => Infer, TyKind::Typeof(..) => panic!("unimplemented type {:?}", ty.kind), @@ -1874,22 +1874,23 @@ fn clean_path_segment<'tcx>( PathSegment { name: path.ident.name, args: clean_generic_args(path.args(), cx) } } -impl<'tcx> Clean<'tcx, BareFunctionDecl> for hir::BareFnTy<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> BareFunctionDecl { - let (generic_params, decl) = enter_impl_trait(cx, |cx| { - // NOTE: generics must be cleaned before args - let generic_params = self - .generic_params - .iter() - .filter(|p| !is_elided_lifetime(p)) - .map(|x| clean_generic_param(cx, None, x)) - .collect(); - let args = clean_args_from_types_and_names(cx, self.decl.inputs, self.param_names); - let decl = clean_fn_decl_with_args(cx, self.decl, args); - (generic_params, decl) - }); - BareFunctionDecl { unsafety: self.unsafety, abi: self.abi, decl, generic_params } - } +fn clean_bare_fn_ty<'tcx>( + bare_fn: &hir::BareFnTy<'tcx>, + cx: &mut DocContext<'tcx>, +) -> BareFunctionDecl { + let (generic_params, decl) = enter_impl_trait(cx, |cx| { + // NOTE: generics must be cleaned before args + let generic_params = bare_fn + .generic_params + .iter() + .filter(|p| !is_elided_lifetime(p)) + .map(|x| clean_generic_param(cx, None, x)) + .collect(); + let args = clean_args_from_types_and_names(cx, bare_fn.decl.inputs, bare_fn.param_names); + let decl = clean_fn_decl_with_args(cx, bare_fn.decl, args); + (generic_params, decl) + }); + BareFunctionDecl { unsafety: bare_fn.unsafety, abi: bare_fn.abi, decl, generic_params } } fn clean_maybe_renamed_item<'tcx>( From eb6b729545af68fb39398eea6073f740cb12da50 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 12 Aug 2022 14:57:15 +0400 Subject: [PATCH 16/22] address review comments --- library/core/src/iter/adapters/array_chunks.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index 3af72c16aafb5..86835f443ab88 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -1,5 +1,5 @@ use crate::array; -use crate::iter::{FusedIterator, Iterator}; +use crate::iter::{ByRefSized, FusedIterator, Iterator}; use crate::ops::{ControlFlow, NeverShortCircuit, Try}; /// An iterator over `N` elements of the iterator at a time. @@ -82,12 +82,12 @@ where } } - fn fold(mut self, init: B, mut f: F) -> B + fn fold(mut self, init: B, f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B, { - self.try_fold(init, |acc, x| NeverShortCircuit(f(acc, x))).0 + self.try_fold(init, NeverShortCircuit::wrap_mut_2(f)).0 } } @@ -111,12 +111,14 @@ where self.next_back_remainder(); let mut acc = init; - let mut iter = self.iter.by_ref().rev(); + let mut iter = ByRefSized(&mut self.iter).rev(); // NB remainder is handled by `next_back_remainder`, so // `next_chunk` can't return `Err` with non-empty remainder // (assuming correct `I as ExactSizeIterator` impl). while let Ok(mut chunk) = iter.next_chunk() { + // FIXME: do not do double reverse + // (we could instead add `next_chunk_back` for example) chunk.reverse(); acc = f(acc, chunk)? } @@ -124,12 +126,12 @@ where try { acc } } - fn rfold(mut self, init: B, mut f: F) -> B + fn rfold(mut self, init: B, f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B, { - self.try_rfold(init, |acc, x| NeverShortCircuit(f(acc, x))).0 + self.try_rfold(init, NeverShortCircuit::wrap_mut_2(f)).0 } } From 5fbcde1b55c09421f43a7d6cfe09103d064ed2db Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 12 Aug 2022 14:58:14 +0400 Subject: [PATCH 17/22] fill-in tracking issue for `feature(iter_array_chunks)` --- library/core/src/iter/adapters/array_chunks.rs | 12 ++++++------ library/core/src/iter/adapters/mod.rs | 2 +- library/core/src/iter/mod.rs | 2 +- library/core/src/iter/traits/iterator.rs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index 86835f443ab88..9b479a9f8adfb 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -11,7 +11,7 @@ use crate::ops::{ControlFlow, NeverShortCircuit, Try}; /// method on [`Iterator`]. See its documentation for more. #[derive(Debug, Clone)] #[must_use = "iterators are lazy and do nothing unless consumed"] -#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] pub struct ArrayChunks { iter: I, remainder: Option>, @@ -30,14 +30,14 @@ where /// Returns an iterator over the remaining elements of the original iterator /// that are not going to be returned by this iterator. The returned /// iterator will yield at most `N-1` elements. - #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] + #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] #[inline] pub fn into_remainder(self) -> Option> { self.remainder } } -#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] impl Iterator for ArrayChunks where I: Iterator, @@ -91,7 +91,7 @@ where } } -#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] impl DoubleEndedIterator for ArrayChunks where I: DoubleEndedIterator + ExactSizeIterator, @@ -162,10 +162,10 @@ where } } -#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] impl FusedIterator for ArrayChunks where I: FusedIterator {} -#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] impl ExactSizeIterator for ArrayChunks where I: ExactSizeIterator, diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index 39e7ab87869a2..bf4fabad32a37 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -33,7 +33,7 @@ pub use self::{ scan::Scan, skip::Skip, skip_while::SkipWhile, take::Take, take_while::TakeWhile, zip::Zip, }; -#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] pub use self::array_chunks::ArrayChunks; #[unstable(feature = "std_internals", issue = "none")] diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index d48e3a52c79f6..9514466bd0c05 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -398,7 +398,7 @@ pub use self::traits::{ #[stable(feature = "iter_zip", since = "1.59.0")] pub use self::adapters::zip; -#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] pub use self::adapters::ArrayChunks; #[unstable(feature = "std_internals", issue = "none")] pub use self::adapters::ByRefSized; diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 95c7cf5758c88..b2d08f4b0f67b 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -3351,7 +3351,7 @@ pub trait Iterator { /// } /// ``` #[track_caller] - #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")] + #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] fn array_chunks(self) -> ArrayChunks where Self: Sized, From 20121fa781e89893ad658cb333a226b81c5543b2 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 23 Jul 2022 17:17:19 +0000 Subject: [PATCH 18/22] Point out a single arg if we have a single arg incompatibility --- .../rustc_typeck/src/check/fn_ctxt/checks.rs | 45 +++++++++++-------- .../invalid_arguments.stderr | 20 ++++----- src/test/ui/argument-suggestions/too-long.rs | 41 +++++++++++++++++ .../ui/argument-suggestions/too-long.stderr | 24 ++++++++++ ...ted-type-projection-from-supertrait.stderr | 8 ++-- .../associated-types-path-2.stderr | 2 +- src/test/ui/async-await/generator-desc.stderr | 4 +- .../coerce-reborrow-multi-arg-fail.stderr | 2 +- src/test/ui/coercion/coerce-to-bang.stderr | 10 ++--- src/test/ui/fn/fn-item-type.stderr | 10 ++--- src/test/ui/issues/issue-11374.stderr | 2 +- .../ui/methods/method-call-err-msg.stderr | 8 ++-- src/test/ui/span/issue-34264.stderr | 2 +- src/test/ui/span/missing-unit-argument.stderr | 4 +- src/test/ui/traits/multidispatch-bad.stderr | 2 +- .../tuple/add-tuple-within-arguments.stderr | 4 +- 16 files changed, 130 insertions(+), 58 deletions(-) create mode 100644 src/test/ui/argument-suggestions/too-long.rs create mode 100644 src/test/ui/argument-suggestions/too-long.stderr diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 1d1f755947fdc..afd22094e500c 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -440,7 +440,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &hir::Expr<'tcx>, ) { // Next, let's construct the error - let (error_span, full_call_span, ctor_of) = match &call_expr.kind { + let (error_span, full_call_span, ctor_of, is_method) = match &call_expr.kind { hir::ExprKind::Call( hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. }, _, @@ -448,12 +448,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Res::Def(DefKind::Ctor(of, _), _) = self.typeck_results.borrow().qpath_res(qpath, *hir_id) { - (call_span, *span, Some(of)) + (call_span, *span, Some(of), false) } else { - (call_span, *span, None) + (call_span, *span, None, false) } } - hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None), + hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None, false), hir::ExprKind::MethodCall(path_segment, _, span) => { let ident_span = path_segment.ident.span; let ident_span = if let Some(args) = path_segment.args { @@ -461,9 +461,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { ident_span }; - ( - *span, ident_span, None, // methods are never ctors - ) + // methods are never ctors + (*span, ident_span, None, true) } k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), }; @@ -659,7 +658,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); }; - self.label_fn_like(&mut err, fn_def_id, callee_ty); + self.label_fn_like(&mut err, fn_def_id, callee_ty, Some(mismatch_idx), is_method); err.emit(); return; } @@ -701,16 +700,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } errors.drain_filter(|error| { - let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(error)) = error else { return false }; + let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = error else { return false }; let (provided_ty, provided_span) = provided_arg_tys[*provided_idx]; let (expected_ty, _) = formal_and_expected_inputs[*expected_idx]; let cause = &self.misc(provided_span); let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); - if let Some(e) = error { - if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) { - self.report_and_explain_type_error(trace, e).emit(); - return true; - } + if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) { + self.report_and_explain_type_error(trace, e).emit(); + return true; } false }); @@ -749,7 +746,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("arguments to this {} are incorrect", call_name), ); // Call out where the function is defined - self.label_fn_like(&mut err, fn_def_id, callee_ty); + self.label_fn_like(&mut err, fn_def_id, callee_ty, Some(expected_idx.as_usize()), is_method); err.emit(); return; } @@ -1031,7 +1028,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Call out where the function is defined - self.label_fn_like(&mut err, fn_def_id, callee_ty); + self.label_fn_like(&mut err, fn_def_id, callee_ty, None, is_method); // And add a suggestion block for all of the parameters let suggestion_text = match suggestion_text { @@ -1781,6 +1778,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err: &mut Diagnostic, callable_def_id: Option, callee_ty: Option>, + // A specific argument should be labeled, instead of all of them + expected_idx: Option, + is_method: bool, ) { let Some(mut def_id) = callable_def_id else { return; @@ -1881,10 +1881,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .get_if_local(def_id) .and_then(|node| node.body_id()) .into_iter() - .flat_map(|id| self.tcx.hir().body(id).params); + .flat_map(|id| self.tcx.hir().body(id).params) + .skip(if is_method { 1 } else { 0 }); - for param in params { - spans.push_span_label(param.span, ""); + for (idx, param) in params.into_iter().enumerate() { + if let Some(expected_idx) = expected_idx { + if idx == expected_idx { + spans.push_span_label(param.span, ""); + } + } else { + spans.push_span_label(param.span, ""); + } } let def_kind = self.tcx.def_kind(def_id); diff --git a/src/test/ui/argument-suggestions/invalid_arguments.stderr b/src/test/ui/argument-suggestions/invalid_arguments.stderr index 33f27d48fec80..303f086957853 100644 --- a/src/test/ui/argument-suggestions/invalid_arguments.stderr +++ b/src/test/ui/argument-suggestions/invalid_arguments.stderr @@ -24,7 +24,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:6:4 | LL | fn two_arg_same(_a: i32, _b: i32) {} - | ^^^^^^^^^^^^ ------- ------- + | ^^^^^^^^^^^^ ------- error[E0308]: mismatched types --> $DIR/invalid_arguments.rs:17:16 @@ -38,7 +38,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:6:4 | LL | fn two_arg_same(_a: i32, _b: i32) {} - | ^^^^^^^^^^^^ ------- ------- + | ^^^^^^^^^^^^ ------- error[E0308]: arguments to this function are incorrect --> $DIR/invalid_arguments.rs:18:3 @@ -66,7 +66,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:7:4 | LL | fn two_arg_diff(_a: i32, _b: f32) {} - | ^^^^^^^^^^^^ ------- ------- + | ^^^^^^^^^^^^ ------- error[E0308]: mismatched types --> $DIR/invalid_arguments.rs:20:16 @@ -80,7 +80,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:7:4 | LL | fn two_arg_diff(_a: i32, _b: f32) {} - | ^^^^^^^^^^^^ ------- ------- + | ^^^^^^^^^^^^ ------- error[E0308]: arguments to this function are incorrect --> $DIR/invalid_arguments.rs:21:3 @@ -108,7 +108,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:8:4 | LL | fn three_arg_diff(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^^^^^ ------- error[E0308]: mismatched types --> $DIR/invalid_arguments.rs:25:21 @@ -122,7 +122,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:8:4 | LL | fn three_arg_diff(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^^^^^ ------- error[E0308]: mismatched types --> $DIR/invalid_arguments.rs:26:26 @@ -136,7 +136,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:8:4 | LL | fn three_arg_diff(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^^^^^ -------- error[E0308]: arguments to this function are incorrect --> $DIR/invalid_arguments.rs:28:3 @@ -207,7 +207,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:9:4 | LL | fn three_arg_repeat(_a: i32, _b: i32, _c: &str) {} - | ^^^^^^^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^^^^^^^ ------- error[E0308]: mismatched types --> $DIR/invalid_arguments.rs:35:23 @@ -221,7 +221,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:9:4 | LL | fn three_arg_repeat(_a: i32, _b: i32, _c: &str) {} - | ^^^^^^^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^^^^^^^ ------- error[E0308]: mismatched types --> $DIR/invalid_arguments.rs:36:26 @@ -235,7 +235,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:9:4 | LL | fn three_arg_repeat(_a: i32, _b: i32, _c: &str) {} - | ^^^^^^^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^^^^^^^ -------- error[E0308]: arguments to this function are incorrect --> $DIR/invalid_arguments.rs:38:3 diff --git a/src/test/ui/argument-suggestions/too-long.rs b/src/test/ui/argument-suggestions/too-long.rs new file mode 100644 index 0000000000000..7ec56afae1ce9 --- /dev/null +++ b/src/test/ui/argument-suggestions/too-long.rs @@ -0,0 +1,41 @@ +struct Qux; + +impl Qux { + fn foo( + &self, + a: i32, + b: i32, + c: i32, + d: i32, + e: i32, + f: i32, + g: i32, + h: i32, + i: i32, + j: i32, + k: i32, + l: i32, + ) { + } +} + +fn what( + qux: &Qux, + a: i32, + b: i32, + c: i32, + d: i32, + e: i32, + f: &i32, + g: i32, + h: i32, + i: i32, + j: i32, + k: i32, + l: i32, +) { + qux.foo(a, b, c, d, e, f, g, h, i, j, k, l); + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/src/test/ui/argument-suggestions/too-long.stderr b/src/test/ui/argument-suggestions/too-long.stderr new file mode 100644 index 0000000000000..bd430194c5edf --- /dev/null +++ b/src/test/ui/argument-suggestions/too-long.stderr @@ -0,0 +1,24 @@ +error[E0308]: mismatched types + --> $DIR/too-long.rs:37:28 + | +LL | qux.foo(a, b, c, d, e, f, g, h, i, j, k, l); + | --- ^ expected `i32`, found `&i32` + | | + | arguments to this function are incorrect + | +note: associated function defined here + --> $DIR/too-long.rs:4:8 + | +LL | fn foo( + | ^^^ +... +LL | f: i32, + | ------ +help: consider dereferencing the borrow + | +LL | qux.foo(a, b, c, d, e, *f, g, h, i, j, k, l); + | + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/associated-types/associated-type-projection-from-supertrait.stderr b/src/test/ui/associated-types/associated-type-projection-from-supertrait.stderr index b904ad102e97e..e761c6c62a6cf 100644 --- a/src/test/ui/associated-types/associated-type-projection-from-supertrait.stderr +++ b/src/test/ui/associated-types/associated-type-projection-from-supertrait.stderr @@ -10,7 +10,7 @@ note: function defined here --> $DIR/associated-type-projection-from-supertrait.rs:25:4 | LL | fn dent(c: C, color: C::Color) { c.chip_paint(color) } - | ^^^^ ---- --------------- + | ^^^^ --------------- error[E0308]: mismatched types --> $DIR/associated-type-projection-from-supertrait.rs:28:23 @@ -24,7 +24,7 @@ note: function defined here --> $DIR/associated-type-projection-from-supertrait.rs:25:4 | LL | fn dent(c: C, color: C::Color) { c.chip_paint(color) } - | ^^^^ ---- --------------- + | ^^^^ --------------- error[E0308]: mismatched types --> $DIR/associated-type-projection-from-supertrait.rs:32:28 @@ -38,7 +38,7 @@ note: associated function defined here --> $DIR/associated-type-projection-from-supertrait.rs:12:8 | LL | fn chip_paint(&self, c: Self::Color) { } - | ^^^^^^^^^^ ----- -------------- + | ^^^^^^^^^^ -------------- error[E0308]: mismatched types --> $DIR/associated-type-projection-from-supertrait.rs:33:28 @@ -52,7 +52,7 @@ note: associated function defined here --> $DIR/associated-type-projection-from-supertrait.rs:12:8 | LL | fn chip_paint(&self, c: Self::Color) { } - | ^^^^^^^^^^ ----- -------------- + | ^^^^^^^^^^ -------------- error: aborting due to 4 previous errors diff --git a/src/test/ui/associated-types/associated-types-path-2.stderr b/src/test/ui/associated-types/associated-types-path-2.stderr index 1d0b84d31d410..c37d469890c08 100644 --- a/src/test/ui/associated-types/associated-types-path-2.stderr +++ b/src/test/ui/associated-types/associated-types-path-2.stderr @@ -10,7 +10,7 @@ note: function defined here --> $DIR/associated-types-path-2.rs:13:8 | LL | pub fn f1(a: T, x: T::A) {} - | ^^ ---- ------- + | ^^ ------- help: change the type of the numeric literal from `i32` to `u32` | LL | f1(2i32, 4u32); diff --git a/src/test/ui/async-await/generator-desc.stderr b/src/test/ui/async-await/generator-desc.stderr index 3be8c552063e7..2494c3feb2a89 100644 --- a/src/test/ui/async-await/generator-desc.stderr +++ b/src/test/ui/async-await/generator-desc.stderr @@ -42,7 +42,7 @@ note: function defined here --> $DIR/generator-desc.rs:8:4 | LL | fn fun>(f1: F, f2: F) {} - | ^^^ ----- ----- + | ^^^ ----- error[E0308]: mismatched types --> $DIR/generator-desc.rs:14:26 @@ -67,7 +67,7 @@ note: function defined here --> $DIR/generator-desc.rs:8:4 | LL | fn fun>(f1: F, f2: F) {} - | ^^^ ----- ----- + | ^^^ ----- error: aborting due to 3 previous errors diff --git a/src/test/ui/coercion/coerce-reborrow-multi-arg-fail.stderr b/src/test/ui/coercion/coerce-reborrow-multi-arg-fail.stderr index 36551e5afc6c6..5cbdef2183114 100644 --- a/src/test/ui/coercion/coerce-reborrow-multi-arg-fail.stderr +++ b/src/test/ui/coercion/coerce-reborrow-multi-arg-fail.stderr @@ -12,7 +12,7 @@ note: function defined here --> $DIR/coerce-reborrow-multi-arg-fail.rs:1:4 | LL | fn test(_a: T, _b: T) {} - | ^^^^ ----- ----- + | ^^^^ ----- error: aborting due to previous error diff --git a/src/test/ui/coercion/coerce-to-bang.stderr b/src/test/ui/coercion/coerce-to-bang.stderr index add8f14cfa591..1207dc7e7a2ff 100644 --- a/src/test/ui/coercion/coerce-to-bang.stderr +++ b/src/test/ui/coercion/coerce-to-bang.stderr @@ -12,7 +12,7 @@ note: function defined here --> $DIR/coerce-to-bang.rs:3:4 | LL | fn foo(x: usize, y: !, z: usize) { } - | ^^^ -------- ---- -------- + | ^^^ ---- error[E0308]: mismatched types --> $DIR/coerce-to-bang.rs:18:13 @@ -28,7 +28,7 @@ note: function defined here --> $DIR/coerce-to-bang.rs:3:4 | LL | fn foo(x: usize, y: !, z: usize) { } - | ^^^ -------- ---- -------- + | ^^^ ---- error[E0308]: mismatched types --> $DIR/coerce-to-bang.rs:26:12 @@ -44,7 +44,7 @@ note: function defined here --> $DIR/coerce-to-bang.rs:3:4 | LL | fn foo(x: usize, y: !, z: usize) { } - | ^^^ -------- ---- -------- + | ^^^ ---- error[E0308]: mismatched types --> $DIR/coerce-to-bang.rs:36:12 @@ -60,7 +60,7 @@ note: function defined here --> $DIR/coerce-to-bang.rs:3:4 | LL | fn foo(x: usize, y: !, z: usize) { } - | ^^^ -------- ---- -------- + | ^^^ ---- error[E0308]: mismatched types --> $DIR/coerce-to-bang.rs:45:12 @@ -76,7 +76,7 @@ note: function defined here --> $DIR/coerce-to-bang.rs:3:4 | LL | fn foo(x: usize, y: !, z: usize) { } - | ^^^ -------- ---- -------- + | ^^^ ---- error[E0308]: mismatched types --> $DIR/coerce-to-bang.rs:50:21 diff --git a/src/test/ui/fn/fn-item-type.stderr b/src/test/ui/fn/fn-item-type.stderr index ecc6485d6d2b5..f03a47d5c2c75 100644 --- a/src/test/ui/fn/fn-item-type.stderr +++ b/src/test/ui/fn/fn-item-type.stderr @@ -15,7 +15,7 @@ note: function defined here --> $DIR/fn-item-type.rs:7:4 | LL | fn eq(x: T, y: T) { } - | ^^ ---- ---- + | ^^ ---- error[E0308]: mismatched types --> $DIR/fn-item-type.rs:22:19 @@ -34,7 +34,7 @@ note: function defined here --> $DIR/fn-item-type.rs:7:4 | LL | fn eq(x: T, y: T) { } - | ^^ ---- ---- + | ^^ ---- error[E0308]: mismatched types --> $DIR/fn-item-type.rs:29:23 @@ -53,7 +53,7 @@ note: function defined here --> $DIR/fn-item-type.rs:7:4 | LL | fn eq(x: T, y: T) { } - | ^^ ---- ---- + | ^^ ---- error[E0308]: mismatched types --> $DIR/fn-item-type.rs:38:26 @@ -72,7 +72,7 @@ note: function defined here --> $DIR/fn-item-type.rs:7:4 | LL | fn eq(x: T, y: T) { } - | ^^ ---- ---- + | ^^ ---- error[E0308]: mismatched types --> $DIR/fn-item-type.rs:45:19 @@ -90,7 +90,7 @@ note: function defined here --> $DIR/fn-item-type.rs:7:4 | LL | fn eq(x: T, y: T) { } - | ^^ ---- ---- + | ^^ ---- error: aborting due to 5 previous errors diff --git a/src/test/ui/issues/issue-11374.stderr b/src/test/ui/issues/issue-11374.stderr index 3a1d43310e2fc..15b2bbeb7c295 100644 --- a/src/test/ui/issues/issue-11374.stderr +++ b/src/test/ui/issues/issue-11374.stderr @@ -14,7 +14,7 @@ note: associated function defined here --> $DIR/issue-11374.rs:13:12 | LL | pub fn read_to(&mut self, vec: &mut [u8]) { - | ^^^^^^^ --------- -------------- + | ^^^^^^^ -------------- error: aborting due to previous error diff --git a/src/test/ui/methods/method-call-err-msg.stderr b/src/test/ui/methods/method-call-err-msg.stderr index 690fe8fa7af10..e9b49c89bf162 100644 --- a/src/test/ui/methods/method-call-err-msg.stderr +++ b/src/test/ui/methods/method-call-err-msg.stderr @@ -8,7 +8,7 @@ note: associated function defined here --> $DIR/method-call-err-msg.rs:5:8 | LL | fn zero(self) -> Foo { self } - | ^^^^ ---- + | ^^^^ help: remove the extra argument | LL | x.zero() @@ -24,7 +24,7 @@ note: associated function defined here --> $DIR/method-call-err-msg.rs:6:8 | LL | fn one(self, _: isize) -> Foo { self } - | ^^^ ---- -------- + | ^^^ -------- help: provide the argument | LL | .one(/* isize */) @@ -40,7 +40,7 @@ note: associated function defined here --> $DIR/method-call-err-msg.rs:7:8 | LL | fn two(self, _: isize, _: isize) -> Foo { self } - | ^^^ ---- -------- -------- + | ^^^ -------- -------- help: provide the argument | LL | .two(0, /* isize */); @@ -80,7 +80,7 @@ note: associated function defined here --> $DIR/method-call-err-msg.rs:8:8 | LL | fn three(self, _: T, _: T, _: T) -> Foo { self } - | ^^^^^ ---- ---- ---- ---- + | ^^^^^ ---- ---- ---- help: provide the arguments | LL | y.three::(/* usize */, /* usize */, /* usize */); diff --git a/src/test/ui/span/issue-34264.stderr b/src/test/ui/span/issue-34264.stderr index e676d7372e891..28a911d0c5bfa 100644 --- a/src/test/ui/span/issue-34264.stderr +++ b/src/test/ui/span/issue-34264.stderr @@ -78,7 +78,7 @@ note: function defined here --> $DIR/issue-34264.rs:3:4 | LL | fn bar(x, y: usize) {} - | ^^^ - -------- + | ^^^ -------- error[E0061]: this function takes 2 arguments but 3 arguments were supplied --> $DIR/issue-34264.rs:10:5 diff --git a/src/test/ui/span/missing-unit-argument.stderr b/src/test/ui/span/missing-unit-argument.stderr index e68260e4a0940..d2afd277ecf79 100644 --- a/src/test/ui/span/missing-unit-argument.stderr +++ b/src/test/ui/span/missing-unit-argument.stderr @@ -72,7 +72,7 @@ note: associated function defined here --> $DIR/missing-unit-argument.rs:6:8 | LL | fn baz(self, (): ()) { } - | ^^^ ---- ------ + | ^^^ ------ help: provide the argument | LL | S.baz(()); @@ -88,7 +88,7 @@ note: associated function defined here --> $DIR/missing-unit-argument.rs:7:8 | LL | fn generic(self, _: T) { } - | ^^^^^^^ ---- ---- + | ^^^^^^^ ---- help: provide the argument | LL | S.generic::<()>(()); diff --git a/src/test/ui/traits/multidispatch-bad.stderr b/src/test/ui/traits/multidispatch-bad.stderr index 8b6e610067be1..d58f1e2d9e59c 100644 --- a/src/test/ui/traits/multidispatch-bad.stderr +++ b/src/test/ui/traits/multidispatch-bad.stderr @@ -10,7 +10,7 @@ note: function defined here --> $DIR/multidispatch-bad.rs:13:4 | LL | fn test(_: T, _: U) - | ^^^^ ---- ---- + | ^^^^ ---- help: change the type of the numeric literal from `i32` to `u32` | LL | test(22i32, 44u32); diff --git a/src/test/ui/tuple/add-tuple-within-arguments.stderr b/src/test/ui/tuple/add-tuple-within-arguments.stderr index 95df96ca0dd4f..7029d298d71e2 100644 --- a/src/test/ui/tuple/add-tuple-within-arguments.stderr +++ b/src/test/ui/tuple/add-tuple-within-arguments.stderr @@ -8,7 +8,7 @@ note: function defined here --> $DIR/add-tuple-within-arguments.rs:1:4 | LL | fn foo(s: &str, a: (i32, i32), s2: &str) {} - | ^^^ ------- ------------- -------- + | ^^^ ------------- help: wrap these arguments in parentheses to construct a tuple | LL | foo("hi", (1, 2), "hi"); @@ -28,7 +28,7 @@ note: function defined here --> $DIR/add-tuple-within-arguments.rs:3:4 | LL | fn bar(s: &str, a: (&str,), s2: &str) {} - | ^^^ ------- ---------- -------- + | ^^^ ---------- help: use a trailing comma to create a tuple with one element | LL | bar("hi", ("hi",), "hi"); From 262644d94a8391acc6cb806b62b8de6bc02123f5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 23 Jul 2022 17:43:21 +0000 Subject: [PATCH 19/22] And for closures --- .../rustc_typeck/src/check/fn_ctxt/checks.rs | 19 ++++++++++++++++++- .../unboxed-closures-type-mismatch.stderr | 6 +++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index afd22094e500c..0027792442d02 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -746,7 +746,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("arguments to this {} are incorrect", call_name), ); // Call out where the function is defined - self.label_fn_like(&mut err, fn_def_id, callee_ty, Some(expected_idx.as_usize()), is_method); + self.label_fn_like( + &mut err, + fn_def_id, + callee_ty, + Some(expected_idx.as_usize()), + is_method, + ); err.emit(); return; } @@ -1896,6 +1902,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let def_kind = self.tcx.def_kind(def_id); err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); + } else if let Some(hir::Node::Expr(e)) = self.tcx.hir().get_if_local(def_id) + && let hir::ExprKind::Closure(hir::Closure { body, .. }) = &e.kind + { + let param = expected_idx + .and_then(|expected_idx| self.tcx.hir().body(*body).params.get(expected_idx)); + let (kind, span) = if let Some(param) = param { + ("closure parameter", param.span) + } else { + ("closure", self.tcx.def_span(def_id)) + }; + err.span_note(span, &format!("{} defined here", kind)); } else { let def_kind = self.tcx.def_kind(def_id); err.span_note( diff --git a/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr b/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr index 3241c9f8521c0..c7d7d7d5f25d2 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr @@ -6,11 +6,11 @@ LL | let z = f(1_usize, 2); | | | arguments to this function are incorrect | -note: closure defined here - --> $DIR/unboxed-closures-type-mismatch.rs:4:17 +note: closure parameter defined here + --> $DIR/unboxed-closures-type-mismatch.rs:4:18 | LL | let mut f = |x: isize, y: isize| -> isize { x + y }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^ help: change the type of the numeric literal from `usize` to `isize` | LL | let z = f(1_isize, 2); From 237cbe91a83905abe379586f10605999c1e0bc4d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 23 Jul 2022 17:46:20 +0000 Subject: [PATCH 20/22] Adjust span of closure param --- compiler/rustc_parse/src/parser/expr.rs | 2 +- .../ui/unboxed-closures/unboxed-closures-type-mismatch.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 2880ef78c8d27..33bebfc2c9ab5 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2268,7 +2268,7 @@ impl<'a> Parser<'a> { attrs: attrs.into(), ty, pat, - span: lo.to(this.token.span), + span: lo.to(this.prev_token.span), id: DUMMY_NODE_ID, is_placeholder: false, }, diff --git a/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr b/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr index c7d7d7d5f25d2..455f83f5721bd 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr @@ -10,7 +10,7 @@ note: closure parameter defined here --> $DIR/unboxed-closures-type-mismatch.rs:4:18 | LL | let mut f = |x: isize, y: isize| -> isize { x + y }; - | ^^^^^^^^^ + | ^^^^^^^^ help: change the type of the numeric literal from `usize` to `isize` | LL | let z = f(1_isize, 2); From c6089189b3045648a8b37ee65dfc2e7e139789b0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 12 Aug 2022 15:35:27 +0000 Subject: [PATCH 21/22] Address nit --- compiler/rustc_typeck/src/check/fn_ctxt/checks.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 0027792442d02..5d668fdaf80c7 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -1890,14 +1890,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .flat_map(|id| self.tcx.hir().body(id).params) .skip(if is_method { 1 } else { 0 }); - for (idx, param) in params.into_iter().enumerate() { - if let Some(expected_idx) = expected_idx { - if idx == expected_idx { - spans.push_span_label(param.span, ""); - } - } else { - spans.push_span_label(param.span, ""); - } + for (_, param) in params + .into_iter() + .enumerate() + .filter(|(idx, _)| expected_idx.map_or(true, |expected_idx| expected_idx == *idx)) + { + spans.push_span_label(param.span, ""); } let def_kind = self.tcx.def_kind(def_id); From 8fa707ab417b7c6b2cc9a57435cbc48b53c69f7e Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 4 Aug 2022 01:43:17 +0300 Subject: [PATCH 22/22] rustc_target: Update some old naming around self contained linking The "fallback" naming pre-dates introduction of `-Clink-self-contained` --- compiler/rustc_codegen_ssa/src/back/link.rs | 50 +++++++++-------- compiler/rustc_target/src/spec/crt_objects.rs | 40 ++++++++------ .../rustc_target/src/spec/linux_musl_base.rs | 8 +-- compiler/rustc_target/src/spec/mod.rs | 55 +++++++++---------- .../rustc_target/src/spec/tests/tests_impl.rs | 6 +- compiler/rustc_target/src/spec/wasm32_wasi.rs | 4 +- compiler/rustc_target/src/spec/wasm_base.rs | 5 +- .../rustc_target/src/spec/windows_gnu_base.rs | 8 +-- 8 files changed, 90 insertions(+), 86 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 953761a782052..7f6947e3c79d8 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -20,7 +20,7 @@ use rustc_session::utils::NativeLibKind; use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; use rustc_span::DebuggerVisualizerFile; -use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; +use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo}; use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Target}; @@ -764,15 +764,15 @@ fn link_natively<'a>( "Linker does not support -static-pie command line option. Retrying with -static instead." ); // Mirror `add_(pre,post)_link_objects` to replace CRT objects. - let self_contained = crt_objects_fallback(sess, crate_type); + let self_contained = self_contained(sess, crate_type); let opts = &sess.target; let pre_objects = if self_contained { - &opts.pre_link_objects_fallback + &opts.pre_link_objects_self_contained } else { &opts.pre_link_objects }; let post_objects = if self_contained { - &opts.post_link_objects_fallback + &opts.post_link_objects_self_contained } else { &opts.post_link_objects }; @@ -1556,26 +1556,26 @@ fn detect_self_contained_mingw(sess: &Session) -> bool { true } -/// Whether we link to our own CRT objects instead of relying on gcc to pull them. +/// Various toolchain components used during linking are used from rustc distribution +/// instead of being found somewhere on the host system. /// We only provide such support for a very limited number of targets. -fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool { +fn self_contained(sess: &Session, crate_type: CrateType) -> bool { if let Some(self_contained) = sess.opts.cg.link_self_contained { return self_contained; } - match sess.target.crt_objects_fallback { + match sess.target.link_self_contained { + LinkSelfContainedDefault::False => false, + LinkSelfContainedDefault::True => true, // FIXME: Find a better heuristic for "native musl toolchain is available", // based on host and linker path, for example. // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237). - Some(CrtObjectsFallback::Musl) => sess.crt_static(Some(crate_type)), - Some(CrtObjectsFallback::Mingw) => { + LinkSelfContainedDefault::Musl => sess.crt_static(Some(crate_type)), + LinkSelfContainedDefault::Mingw => { sess.host == sess.target && sess.target.vendor != "uwp" && detect_self_contained_mingw(&sess) } - // FIXME: Figure out cases in which WASM needs to link with a native toolchain. - Some(CrtObjectsFallback::Wasm) => true, - None => false, } } @@ -1592,7 +1592,7 @@ fn add_pre_link_objects( let opts = &sess.target; let empty = Default::default(); let objects = if self_contained { - &opts.pre_link_objects_fallback + &opts.pre_link_objects_self_contained } else if !(sess.target.os == "fuchsia" && flavor == LinkerFlavor::Gcc) { &opts.pre_link_objects } else { @@ -1610,9 +1610,11 @@ fn add_post_link_objects( link_output_kind: LinkOutputKind, self_contained: bool, ) { - let opts = &sess.target; - let objects = - if self_contained { &opts.post_link_objects_fallback } else { &opts.post_link_objects }; + let objects = if self_contained { + &sess.target.post_link_objects_self_contained + } else { + &sess.target.post_link_objects + }; for obj in objects.get(&link_output_kind).iter().copied().flatten() { cmd.add_object(&get_object_file_path(sess, obj, self_contained)); } @@ -1891,12 +1893,12 @@ fn linker_with_args<'a>( out_filename: &Path, codegen_results: &CodegenResults, ) -> Result { - let crt_objects_fallback = crt_objects_fallback(sess, crate_type); + let self_contained = self_contained(sess, crate_type); let cmd = &mut *super::linker::get_linker( sess, path, flavor, - crt_objects_fallback, + self_contained, &codegen_results.crate_info.target_cpu, ); let link_output_kind = link_output_kind(sess, crate_type); @@ -1923,7 +1925,7 @@ fn linker_with_args<'a>( // ------------ Object code and libraries, order-dependent ------------ // Pre-link CRT objects. - add_pre_link_objects(cmd, sess, flavor, link_output_kind, crt_objects_fallback); + add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained); add_linked_symbol_object( cmd, @@ -2033,7 +2035,7 @@ fn linker_with_args<'a>( cmd, sess, link_output_kind, - crt_objects_fallback, + self_contained, flavor, crate_type, codegen_results, @@ -2049,7 +2051,7 @@ fn linker_with_args<'a>( // ------------ Object code and libraries, order-dependent ------------ // Post-link CRT objects. - add_post_link_objects(cmd, sess, link_output_kind, crt_objects_fallback); + add_post_link_objects(cmd, sess, link_output_kind, self_contained); // ------------ Late order-dependent options ------------ @@ -2066,7 +2068,7 @@ fn add_order_independent_options( cmd: &mut dyn Linker, sess: &Session, link_output_kind: LinkOutputKind, - crt_objects_fallback: bool, + self_contained: bool, flavor: LinkerFlavor, crate_type: CrateType, codegen_results: &CodegenResults, @@ -2098,7 +2100,7 @@ fn add_order_independent_options( // Make the binary compatible with data execution prevention schemes. cmd.add_no_exec(); - if crt_objects_fallback { + if self_contained { cmd.no_crt_objects(); } @@ -2127,7 +2129,7 @@ fn add_order_independent_options( cmd.linker_plugin_lto(); - add_library_search_dirs(cmd, sess, crt_objects_fallback); + add_library_search_dirs(cmd, sess, self_contained); cmd.output_filename(out_filename); diff --git a/compiler/rustc_target/src/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs index 52ac3622eca8d..c126390f5a908 100644 --- a/compiler/rustc_target/src/spec/crt_objects.rs +++ b/compiler/rustc_target/src/spec/crt_objects.rs @@ -63,7 +63,7 @@ pub(super) fn all(obj: &'static str) -> CrtObjects { ]) } -pub(super) fn pre_musl_fallback() -> CrtObjects { +pub(super) fn pre_musl_self_contained() -> CrtObjects { new(&[ (LinkOutputKind::DynamicNoPicExe, &["crt1.o", "crti.o", "crtbegin.o"]), (LinkOutputKind::DynamicPicExe, &["Scrt1.o", "crti.o", "crtbeginS.o"]), @@ -74,7 +74,7 @@ pub(super) fn pre_musl_fallback() -> CrtObjects { ]) } -pub(super) fn post_musl_fallback() -> CrtObjects { +pub(super) fn post_musl_self_contained() -> CrtObjects { new(&[ (LinkOutputKind::DynamicNoPicExe, &["crtend.o", "crtn.o"]), (LinkOutputKind::DynamicPicExe, &["crtendS.o", "crtn.o"]), @@ -85,7 +85,7 @@ pub(super) fn post_musl_fallback() -> CrtObjects { ]) } -pub(super) fn pre_mingw_fallback() -> CrtObjects { +pub(super) fn pre_mingw_self_contained() -> CrtObjects { new(&[ (LinkOutputKind::DynamicNoPicExe, &["crt2.o", "rsbegin.o"]), (LinkOutputKind::DynamicPicExe, &["crt2.o", "rsbegin.o"]), @@ -96,7 +96,7 @@ pub(super) fn pre_mingw_fallback() -> CrtObjects { ]) } -pub(super) fn post_mingw_fallback() -> CrtObjects { +pub(super) fn post_mingw_self_contained() -> CrtObjects { all("rsend.o") } @@ -108,7 +108,7 @@ pub(super) fn post_mingw() -> CrtObjects { all("rsend.o") } -pub(super) fn pre_wasi_fallback() -> CrtObjects { +pub(super) fn pre_wasi_self_contained() -> CrtObjects { // Use crt1-command.o instead of crt1.o to enable support for new-style // commands. See https://reviews.llvm.org/D81689 for more info. new(&[ @@ -120,37 +120,41 @@ pub(super) fn pre_wasi_fallback() -> CrtObjects { ]) } -pub(super) fn post_wasi_fallback() -> CrtObjects { +pub(super) fn post_wasi_self_contained() -> CrtObjects { new(&[]) } -/// Which logic to use to determine whether to fall back to the "self-contained" mode or not. +/// Which logic to use to determine whether to use self-contained linking mode +/// if `-Clink-self-contained` is not specified explicitly. #[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum CrtObjectsFallback { +pub enum LinkSelfContainedDefault { + False, + True, Musl, Mingw, - Wasm, } -impl FromStr for CrtObjectsFallback { +impl FromStr for LinkSelfContainedDefault { type Err = (); - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { Ok(match s { - "musl" => CrtObjectsFallback::Musl, - "mingw" => CrtObjectsFallback::Mingw, - "wasm" => CrtObjectsFallback::Wasm, + "false" => LinkSelfContainedDefault::False, + "true" | "wasm" => LinkSelfContainedDefault::True, + "musl" => LinkSelfContainedDefault::Musl, + "mingw" => LinkSelfContainedDefault::Mingw, _ => return Err(()), }) } } -impl ToJson for CrtObjectsFallback { +impl ToJson for LinkSelfContainedDefault { fn to_json(&self) -> Json { match *self { - CrtObjectsFallback::Musl => "musl", - CrtObjectsFallback::Mingw => "mingw", - CrtObjectsFallback::Wasm => "wasm", + LinkSelfContainedDefault::False => "false", + LinkSelfContainedDefault::True => "true", + LinkSelfContainedDefault::Musl => "musl", + LinkSelfContainedDefault::Mingw => "mingw", } .to_json() } diff --git a/compiler/rustc_target/src/spec/linux_musl_base.rs b/compiler/rustc_target/src/spec/linux_musl_base.rs index 207a87ab03903..61553e71b4500 100644 --- a/compiler/rustc_target/src/spec/linux_musl_base.rs +++ b/compiler/rustc_target/src/spec/linux_musl_base.rs @@ -1,13 +1,13 @@ -use crate::spec::crt_objects::{self, CrtObjectsFallback}; +use crate::spec::crt_objects::{self, LinkSelfContainedDefault}; use crate::spec::TargetOptions; pub fn opts() -> TargetOptions { let mut base = super::linux_base::opts(); base.env = "musl".into(); - base.pre_link_objects_fallback = crt_objects::pre_musl_fallback(); - base.post_link_objects_fallback = crt_objects::post_musl_fallback(); - base.crt_objects_fallback = Some(CrtObjectsFallback::Musl); + base.pre_link_objects_self_contained = crt_objects::pre_musl_self_contained(); + base.post_link_objects_self_contained = crt_objects::post_musl_self_contained(); + base.link_self_contained = LinkSelfContainedDefault::Musl; // These targets statically link libc by default base.crt_static_default = true; diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f7abeafd38f10..0b49edc232c06 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -37,7 +37,7 @@ use crate::abi::Endian; use crate::json::{Json, ToJson}; use crate::spec::abi::{lookup as lookup_abi, Abi}; -use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; +use crate::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::symbol::{sym, Symbol}; @@ -1172,13 +1172,10 @@ pub struct TargetOptions { /// Objects to link before and after all other object code. pub pre_link_objects: CrtObjects, pub post_link_objects: CrtObjects, - /// Same as `(pre|post)_link_objects`, but when we fail to pull the objects with help of the - /// target's native gcc and fall back to the "self-contained" mode and pull them manually. - /// See `crt_objects.rs` for some more detailed documentation. - pub pre_link_objects_fallback: CrtObjects, - pub post_link_objects_fallback: CrtObjects, - /// Which logic to use to determine whether to fall back to the "self-contained" mode or not. - pub crt_objects_fallback: Option, + /// Same as `(pre|post)_link_objects`, but when self-contained linking mode is enabled. + pub pre_link_objects_self_contained: CrtObjects, + pub post_link_objects_self_contained: CrtObjects, + pub link_self_contained: LinkSelfContainedDefault, /// Linker arguments that are unconditionally passed after any /// user-defined but before post-link objects. Standard platform @@ -1554,9 +1551,9 @@ impl Default for TargetOptions { relro_level: RelroLevel::None, pre_link_objects: Default::default(), post_link_objects: Default::default(), - pre_link_objects_fallback: Default::default(), - post_link_objects_fallback: Default::default(), - crt_objects_fallback: None, + pre_link_objects_self_contained: Default::default(), + post_link_objects_self_contained: Default::default(), + link_self_contained: LinkSelfContainedDefault::False, late_link_args: LinkArgs::new(), late_link_args_dynamic: LinkArgs::new(), late_link_args_static: LinkArgs::new(), @@ -1977,20 +1974,20 @@ impl Target { Ok::<(), String>(()) } ); - ($key_name:ident, crt_objects_fallback) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::() { - Ok(fallback) => base.$key_name = Some(fallback), - _ => return Some(Err(format!("'{}' is not a valid CRT objects fallback. \ - Use 'musl', 'mingw' or 'wasm'", s))), + ($key_name:ident = $json_name:expr, link_self_contained) => ( { + let name = $json_name; + obj.remove(name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::() { + Ok(lsc_default) => base.$key_name = lsc_default, + _ => return Some(Err(format!("'{}' is not a valid `-Clink-self-contained` default. \ + Use 'false', 'true', 'musl' or 'mingw'", s))), } Some(Ok(())) })).unwrap_or(Ok(())) } ); - ($key_name:ident, link_objects) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(val) = obj.remove(&name) { + ($key_name:ident = $json_name:expr, link_objects) => ( { + let name = $json_name; + if let Some(val) = obj.remove(name) { let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ JSON object with fields per CRT object kind.", name))?; let mut args = CrtObjects::new(); @@ -2112,11 +2109,11 @@ impl Target { key!(linker_flavor, LinkerFlavor)?; key!(linker, optional); key!(lld_flavor, LldFlavor)?; - key!(pre_link_objects, link_objects); - key!(post_link_objects, link_objects); - key!(pre_link_objects_fallback, link_objects); - key!(post_link_objects_fallback, link_objects); - key!(crt_objects_fallback, crt_objects_fallback)?; + key!(pre_link_objects = "pre-link-objects", link_objects); + key!(post_link_objects = "post-link-objects", link_objects); + key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects); + key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects); + key!(link_self_contained = "crt-objects-fallback", link_self_contained)?; key!(pre_link_args, link_args); key!(late_link_args, link_args); key!(late_link_args_dynamic, link_args); @@ -2357,9 +2354,9 @@ impl ToJson for Target { target_option_val!(lld_flavor); target_option_val!(pre_link_objects); target_option_val!(post_link_objects); - target_option_val!(pre_link_objects_fallback); - target_option_val!(post_link_objects_fallback); - target_option_val!(crt_objects_fallback); + target_option_val!(pre_link_objects_self_contained, "pre-link-objects-fallback"); + target_option_val!(post_link_objects_self_contained, "post-link-objects-fallback"); + target_option_val!(link_self_contained, "crt-objects-fallback"); target_option_val!(link_args - pre_link_args); target_option_val!(link_args - late_link_args); target_option_val!(link_args - late_link_args_dynamic); diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index 1db6db78b17e4..03e579aee0a96 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -110,9 +110,9 @@ impl Target { } assert!( - (self.pre_link_objects_fallback.is_empty() - && self.post_link_objects_fallback.is_empty()) - || self.crt_objects_fallback.is_some() + (self.pre_link_objects_self_contained.is_empty() + && self.post_link_objects_self_contained.is_empty()) + || self.link_self_contained != LinkSelfContainedDefault::False ); // If your target really needs to deviate from the rules below, diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs index 280457d68b99e..9c30487f4abe7 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -82,8 +82,8 @@ pub fn target() -> Target { options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm); options.add_pre_link_args(LinkerFlavor::Gcc, &["--target=wasm32-wasi"]); - options.pre_link_objects_fallback = crt_objects::pre_wasi_fallback(); - options.post_link_objects_fallback = crt_objects::post_wasi_fallback(); + options.pre_link_objects_self_contained = crt_objects::pre_wasi_self_contained(); + options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained(); // Right now this is a bit of a workaround but we're currently saying that // the target by default has a static crt which we're taking as a signal diff --git a/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs index 9216d3e7b65f6..28a07701eae74 100644 --- a/compiler/rustc_target/src/spec/wasm_base.rs +++ b/compiler/rustc_target/src/spec/wasm_base.rs @@ -1,4 +1,4 @@ -use super::crt_objects::CrtObjectsFallback; +use super::crt_objects::LinkSelfContainedDefault; use super::{cvs, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel}; pub fn options() -> TargetOptions { @@ -96,7 +96,8 @@ pub fn options() -> TargetOptions { pre_link_args, - crt_objects_fallback: Some(CrtObjectsFallback::Wasm), + // FIXME: Figure out cases in which WASM needs to link with a native toolchain. + link_self_contained: LinkSelfContainedDefault::True, // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when // PIC code is implemented this has quite a drastic effect if it stays diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs index 90e0af3e38afe..0107f7a52c6ff 100644 --- a/compiler/rustc_target/src/spec/windows_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -1,4 +1,4 @@ -use crate::spec::crt_objects::{self, CrtObjectsFallback}; +use crate::spec::crt_objects::{self, LinkSelfContainedDefault}; use crate::spec::{cvs, LinkerFlavor, TargetOptions}; pub fn opts() -> TargetOptions { @@ -76,9 +76,9 @@ pub fn opts() -> TargetOptions { pre_link_args, pre_link_objects: crt_objects::pre_mingw(), post_link_objects: crt_objects::post_mingw(), - pre_link_objects_fallback: crt_objects::pre_mingw_fallback(), - post_link_objects_fallback: crt_objects::post_mingw_fallback(), - crt_objects_fallback: Some(CrtObjectsFallback::Mingw), + pre_link_objects_self_contained: crt_objects::pre_mingw_self_contained(), + post_link_objects_self_contained: crt_objects::post_mingw_self_contained(), + link_self_contained: LinkSelfContainedDefault::Mingw, late_link_args, late_link_args_dynamic, late_link_args_static,