From 01db8b656c4f84446b8ac29b2e2f73153717e8c1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 22 Jul 2022 00:54:36 +0000 Subject: [PATCH 01/28] Delay a span bug if we see ty/const generic params during writeback --- compiler/rustc_typeck/src/check/writeback.rs | 11 +++++++++++ src/test/ui/closures/binder/disallow-const.rs | 6 ++++++ src/test/ui/closures/binder/disallow-const.stderr | 8 ++++++++ src/test/ui/closures/binder/disallow-ty.rs | 6 ++++++ src/test/ui/closures/binder/disallow-ty.stderr | 8 ++++++++ 5 files changed, 39 insertions(+) create mode 100644 src/test/ui/closures/binder/disallow-const.rs create mode 100644 src/test/ui/closures/binder/disallow-const.stderr create mode 100644 src/test/ui/closures/binder/disallow-ty.rs create mode 100644 src/test/ui/closures/binder/disallow-ty.stderr diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index d102fb45a8cbc..e2f6b55b148a7 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -293,6 +293,17 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { intravisit::walk_expr(self, e); } + fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { + match &p.kind { + hir::GenericParamKind::Lifetime { .. } => { + // Nothing to write back here + } + hir::GenericParamKind::Type { .. } | hir::GenericParamKind::Const { .. } => { + self.tcx().sess.delay_span_bug(p.span, format!("unexpected generic param: {p:?}")); + } + } + } + fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) { self.visit_node_id(b.span, b.hir_id); intravisit::walk_block(self, b); diff --git a/src/test/ui/closures/binder/disallow-const.rs b/src/test/ui/closures/binder/disallow-const.rs new file mode 100644 index 0000000000000..72ad6185d3761 --- /dev/null +++ b/src/test/ui/closures/binder/disallow-const.rs @@ -0,0 +1,6 @@ +#![feature(closure_lifetime_binder)] + +fn main() { + for || -> () {}; + //~^ ERROR only lifetime parameters can be used in this context +} diff --git a/src/test/ui/closures/binder/disallow-const.stderr b/src/test/ui/closures/binder/disallow-const.stderr new file mode 100644 index 0000000000000..3c3b43d8cf342 --- /dev/null +++ b/src/test/ui/closures/binder/disallow-const.stderr @@ -0,0 +1,8 @@ +error: only lifetime parameters can be used in this context + --> $DIR/disallow-const.rs:4:15 + | +LL | for || -> () {}; + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/closures/binder/disallow-ty.rs b/src/test/ui/closures/binder/disallow-ty.rs new file mode 100644 index 0000000000000..bbe3d8488d982 --- /dev/null +++ b/src/test/ui/closures/binder/disallow-ty.rs @@ -0,0 +1,6 @@ +#![feature(closure_lifetime_binder)] + +fn main() { + for || -> () {}; + //~^ ERROR only lifetime parameters can be used in this context +} diff --git a/src/test/ui/closures/binder/disallow-ty.stderr b/src/test/ui/closures/binder/disallow-ty.stderr new file mode 100644 index 0000000000000..51b6773edea98 --- /dev/null +++ b/src/test/ui/closures/binder/disallow-ty.stderr @@ -0,0 +1,8 @@ +error: only lifetime parameters can be used in this context + --> $DIR/disallow-ty.rs:4:9 + | +LL | for || -> () {}; + | ^ + +error: aborting due to previous error + From c9b21b0ea22d6caa1d0caa9e68ee22f72729229d Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 28 Jul 2022 17:30:39 +0200 Subject: [PATCH 02/28] orphan check: remove const generics fixme --- .../src/traits/coherence.rs | 11 +++++++- .../auxiliary/trait-with-const-param.rs | 1 + .../const-generics-orphan-check-ok.rs | 28 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/coherence/auxiliary/trait-with-const-param.rs create mode 100644 src/test/ui/coherence/const-generics-orphan-check-ok.rs diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index fa94aa19abda5..da17a9c3da6b5 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -746,8 +746,17 @@ impl<'tcx> TypeVisitor<'tcx> for OrphanChecker<'tcx> { result } - // FIXME: Constants should participate in orphan checking. fn visit_const(&mut self, _c: ty::Const<'tcx>) -> ControlFlow { + // All possible values for a constant parameter already exist + // in the crate defining the trait, so they are always non-local. + // + // Because there's no way to have an impl where the first local + // generic argument is a constant, we also don't have to fail + // the orphan check when encountering a parameter or a generic constant. + // + // This means that we can completely ignore constants during the orphan check. + // + // See `src/test/ui/coherence/const-generics-orphan-check-ok.rs` for examples. ControlFlow::CONTINUE } } diff --git a/src/test/ui/coherence/auxiliary/trait-with-const-param.rs b/src/test/ui/coherence/auxiliary/trait-with-const-param.rs new file mode 100644 index 0000000000000..a44eb14f8e4cf --- /dev/null +++ b/src/test/ui/coherence/auxiliary/trait-with-const-param.rs @@ -0,0 +1 @@ +pub trait Trait {} diff --git a/src/test/ui/coherence/const-generics-orphan-check-ok.rs b/src/test/ui/coherence/const-generics-orphan-check-ok.rs new file mode 100644 index 0000000000000..217e8aed234b1 --- /dev/null +++ b/src/test/ui/coherence/const-generics-orphan-check-ok.rs @@ -0,0 +1,28 @@ +// check-pass +// aux-build:trait-with-const-param.rs +extern crate trait_with_const_param; +use trait_with_const_param::*; + +// Trivial case, const param after local type. +struct Local1; +impl Trait for Local1 {} + +// Concrete consts behave the same as foreign types, +// so this also trivially works. +impl Trait<3, Local1> for i32 {} + +// This case isn't as trivial as we would forbid type +// parameters here, we do allow const parameters though. +// +// The reason that type parameters are forbidden for +// `impl Trait for i32 {}` is that another +// downstream crate can add `impl Trait for i32`. +// As these two impls would overlap we forbid any impls which +// have a type parameter in front of a local type. +// +// With const parameters this issue does not exist as there are no +// constants local to another downstream crate. +struct Local2; +impl Trait for i32 {} + +fn main() {} From 2634309eb39b5af7b1d26a3656079efc0b9340d1 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 29 Jul 2022 09:43:22 +0200 Subject: [PATCH 03/28] update comment --- .../src/traits/coherence.rs | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index da17a9c3da6b5..337fbb2c15e3a 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -746,17 +746,22 @@ impl<'tcx> TypeVisitor<'tcx> for OrphanChecker<'tcx> { result } + /// All possible values for a constant parameter already exist + /// in the crate defining the trait, so they are always non-local[^1]. + /// + /// Because there's no way to have an impl where the first local + /// generic argument is a constant, we also don't have to fail + /// the orphan check when encountering a parameter or a generic constant. + /// + /// This means that we can completely ignore constants during the orphan check. + /// + /// See `src/test/ui/coherence/const-generics-orphan-check-ok.rs` for examples. + /// + /// [^1]: This might not hold for function pointers or trait objects in the future. + /// As these should be quite rare as const arguments and especially rare as impl + /// parameters, allowing uncovered const parameters in impls seems more useful + /// than allowing `impl Trait for i32` to compile. fn visit_const(&mut self, _c: ty::Const<'tcx>) -> ControlFlow { - // All possible values for a constant parameter already exist - // in the crate defining the trait, so they are always non-local. - // - // Because there's no way to have an impl where the first local - // generic argument is a constant, we also don't have to fail - // the orphan check when encountering a parameter or a generic constant. - // - // This means that we can completely ignore constants during the orphan check. - // - // See `src/test/ui/coherence/const-generics-orphan-check-ok.rs` for examples. ControlFlow::CONTINUE } } From ca3d1010bb5f8ed6b7897c1d85f794857e00caa4 Mon Sep 17 00:00:00 2001 From: Ross MacArthur Date: Sun, 2 Jan 2022 18:31:56 +0200 Subject: [PATCH 04/28] 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 05/28] 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 06/28] 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 07/28] 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 08/28] 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 09/28] 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 10/28] 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 11/28] 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 12/28] 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 13/28] 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 2af92bbf2ec353cd5eafb3bd5cb1ea5c1de448d3 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Wed, 3 Aug 2022 18:43:29 +0300 Subject: [PATCH 14/28] Suggest removing `let` if `const let` is used --- compiler/rustc_parse/src/parser/item.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 2c1e5807aa7f9..1d92213be3452 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1124,6 +1124,16 @@ impl<'a> Parser<'a> { Applicability::MaybeIncorrect, ) .emit(); + } else if self.eat_keyword(kw::Let) { + let span = self.prev_token.span; + self.struct_span_err(const_span.to(span), "`const` and `let` are mutually exclusive") + .span_suggestion( + const_span.to(span), + "remove `let`", + "const", + Applicability::MaybeIncorrect, + ) + .emit(); } } From accb8e34c599f005eaeed010fe7ddbd47760468b Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Wed, 3 Aug 2022 18:45:26 +0300 Subject: [PATCH 15/28] Suggest removing `let` if `let const` is used --- compiler/rustc_parse/src/parser/stmt.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 51bd9d2d386ad..6990d0782b772 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -247,6 +247,22 @@ impl<'a> Parser<'a> { /// Parses a local variable declaration. fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.prev_token.span; + + if self.token.is_keyword(kw::Const) && self.look_ahead(1, |t| t.is_ident()) { + self.struct_span_err( + lo.to(self.token.span), + "`const` and `let` are mutually exclusive", + ) + .span_suggestion( + lo.to(self.token.span), + "remove `let`", + "const", + Applicability::MaybeIncorrect, + ) + .emit(); + self.bump(); + } + let (pat, colon) = self.parse_pat_before_ty(None, RecoverComma::Yes, "`let` bindings")?; let (err, ty) = if colon { From b3f32d1e8b3d9cee87358c3e8e31a7a6fcaf42df Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Thu, 4 Aug 2022 01:23:24 +0300 Subject: [PATCH 16/28] Add ui test for #99910 --- .../issue-99910-const-let-mutually-exclusive.fixed | 8 ++++++++ .../issue-99910-const-let-mutually-exclusive.rs | 8 ++++++++ ...issue-99910-const-let-mutually-exclusive.stderr | 14 ++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 src/test/ui/parser/issue-99910-const-let-mutually-exclusive.fixed create mode 100644 src/test/ui/parser/issue-99910-const-let-mutually-exclusive.rs create mode 100644 src/test/ui/parser/issue-99910-const-let-mutually-exclusive.stderr diff --git a/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.fixed b/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.fixed new file mode 100644 index 0000000000000..64ab6f62b77f3 --- /dev/null +++ b/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.fixed @@ -0,0 +1,8 @@ +// run-rustfix + +fn main() { + const _FOO: i32 = 123; + //~^ ERROR const` and `let` are mutually exclusive + const _BAR: i32 = 123; + //~^ ERROR `const` and `let` are mutually exclusive +} diff --git a/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.rs b/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.rs new file mode 100644 index 0000000000000..50520971ffb32 --- /dev/null +++ b/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.rs @@ -0,0 +1,8 @@ +// run-rustfix + +fn main() { + const let _FOO: i32 = 123; + //~^ ERROR const` and `let` are mutually exclusive + let const _BAR: i32 = 123; + //~^ ERROR `const` and `let` are mutually exclusive +} diff --git a/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.stderr b/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.stderr new file mode 100644 index 0000000000000..72377fc379cac --- /dev/null +++ b/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.stderr @@ -0,0 +1,14 @@ +error: `const` and `let` are mutually exclusive + --> $DIR/issue-99910-const-let-mutually-exclusive.rs:4:5 + | +LL | const let _FOO: i32 = 123; + | ^^^^^^^^^ help: remove `let`: `const` + +error: `const` and `let` are mutually exclusive + --> $DIR/issue-99910-const-let-mutually-exclusive.rs:6:5 + | +LL | let const _BAR: i32 = 123; + | ^^^^^^^^^ help: remove `let`: `const` + +error: aborting due to 2 previous errors + From eb6b729545af68fb39398eea6073f740cb12da50 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 12 Aug 2022 14:57:15 +0400 Subject: [PATCH 17/28] 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 18/28] 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 8fa707ab417b7c6b2cc9a57435cbc48b53c69f7e Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 4 Aug 2022 01:43:17 +0300 Subject: [PATCH 19/28] 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, From 6b19a48e708609b456d3a6e90cafd63a6c348e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sat, 13 Aug 2022 00:00:00 +0000 Subject: [PATCH 20/28] `assert_{inhabited,zero_valid,uninit_valid}` intrinsics are safe Those intrinsics either panic or do nothing. They are safe. --- compiler/rustc_typeck/src/check/intrinsic.rs | 3 +++ src/test/ui/consts/assert-type-intrinsics.rs | 4 ++-- src/test/ui/consts/assert-type-intrinsics.stderr | 8 ++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 3f2a0da8d6515..05686be5d4b3d 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -69,6 +69,9 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { // to note that it's safe to call, since // safe extern fns are otherwise unprecedented. sym::abort + | sym::assert_inhabited + | sym::assert_zero_valid + | sym::assert_uninit_valid | sym::size_of | sym::min_align_of | sym::needs_drop diff --git a/src/test/ui/consts/assert-type-intrinsics.rs b/src/test/ui/consts/assert-type-intrinsics.rs index 38e5c454edf75..3ce3e1bdbac0f 100644 --- a/src/test/ui/consts/assert-type-intrinsics.rs +++ b/src/test/ui/consts/assert-type-intrinsics.rs @@ -13,10 +13,10 @@ fn main() { const _BAD1: () = unsafe { MaybeUninit::::uninit().assume_init(); }; - const _BAD2: () = unsafe { + const _BAD2: () = { intrinsics::assert_uninit_valid::(); }; - const _BAD3: () = unsafe { + const _BAD3: () = { intrinsics::assert_zero_valid::<&'static i32>(); }; } diff --git a/src/test/ui/consts/assert-type-intrinsics.stderr b/src/test/ui/consts/assert-type-intrinsics.stderr index f3b9170d428af..6eab10197b855 100644 --- a/src/test/ui/consts/assert-type-intrinsics.stderr +++ b/src/test/ui/consts/assert-type-intrinsics.stderr @@ -13,7 +13,7 @@ LL | MaybeUninit::::uninit().assume_init(); error: any use of this value will cause an error --> $DIR/assert-type-intrinsics.rs:17:9 | -LL | const _BAD2: () = unsafe { +LL | const _BAD2: () = { | --------------- LL | intrinsics::assert_uninit_valid::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to leave type `bool` uninitialized, which is invalid @@ -24,7 +24,7 @@ LL | intrinsics::assert_uninit_valid::(); error: any use of this value will cause an error --> $DIR/assert-type-intrinsics.rs:20:9 | -LL | const _BAD3: () = unsafe { +LL | const _BAD3: () = { | --------------- LL | intrinsics::assert_zero_valid::<&'static i32>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to zero-initialize type `&i32`, which is invalid @@ -51,7 +51,7 @@ Future breakage diagnostic: error: any use of this value will cause an error --> $DIR/assert-type-intrinsics.rs:17:9 | -LL | const _BAD2: () = unsafe { +LL | const _BAD2: () = { | --------------- LL | intrinsics::assert_uninit_valid::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to leave type `bool` uninitialized, which is invalid @@ -64,7 +64,7 @@ Future breakage diagnostic: error: any use of this value will cause an error --> $DIR/assert-type-intrinsics.rs:20:9 | -LL | const _BAD3: () = unsafe { +LL | const _BAD3: () = { | --------------- LL | intrinsics::assert_zero_valid::<&'static i32>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to zero-initialize type `&i32`, which is invalid From 68f327bcc112ffb4261980c04e58d333b5d97b6e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 11 Aug 2022 23:03:45 +0200 Subject: [PATCH 21/28] Merge HTML elements in highlighting when they can be merged together --- src/librustdoc/html/highlight.rs | 163 +++++++++++++++++++++++++++++-- 1 file changed, 155 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 27ccff9a2768f..517f441e64798 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -111,6 +111,69 @@ fn write_header(out: &mut Buffer, class: &str, extra_content: Option) { write!(out, ""); } +/// Write all the pending elements sharing a same (or at mergeable) `Class`. +/// +/// If there is a "parent" (if a `EnterSpan` event was encountered) and the parent can be merged +/// with the elements' class, then we simply write the elements since the `ExitSpan` event will +/// close the tag. +/// +/// Otherwise, if there is only one pending element, we let the `string` function handle both +/// opening and closing the tag, otherwise we do it into this function. +fn write_pending_elems( + out: &mut Buffer, + href_context: &Option>, + pending_elems: &mut Vec<(&str, Option)>, + current_class: &mut Option, + closing_tags: &[(&str, Class)], +) { + if pending_elems.is_empty() { + return; + } + let mut done = false; + if let Some((_, parent_class)) = closing_tags.last() { + if can_merge(*current_class, Some(*parent_class), "") { + for (text, class) in pending_elems.iter() { + string(out, Escape(text), *class, &href_context, false); + } + done = true; + } + } + if !done { + // We only want to "open" the tag ourselves if we have more than one pending and if the current + // parent tag is not the same as our pending content. + let open_tag_ourselves = pending_elems.len() > 1; + let close_tag = if open_tag_ourselves { + enter_span(out, current_class.unwrap(), &href_context) + } else { + "" + }; + for (text, class) in pending_elems.iter() { + string(out, Escape(text), *class, &href_context, !open_tag_ourselves); + } + if open_tag_ourselves { + exit_span(out, close_tag); + } + } + pending_elems.clear(); + *current_class = None; +} + +/// Check if two `Class` can be merged together. In the following rules, "unclassified" means `None` +/// basically (since it's `Option`). The following rules apply: +/// +/// * If two `Class` have the same variant, then they can be merged. +/// * If the other `Class` is unclassified and only contains white characters (backline, +/// whitespace, etc), it can be merged. +/// * If `Class` is `Ident`, then it can be merged with all unclassified elements. +fn can_merge(class1: Option, class2: Option, text: &str) -> bool { + match (class1, class2) { + (Some(c1), Some(c2)) => c1.is_equal_to(c2), + (Some(Class::Ident(_)), None) | (None, Some(Class::Ident(_))) => true, + (Some(_), None) | (None, Some(_)) => text.trim().is_empty(), + _ => false, + } +} + /// Convert the given `src` source code into HTML by adding classes for highlighting. /// /// This code is used to render code blocks (in the documentation) as well as the source code pages. @@ -130,7 +193,15 @@ fn write_code( ) { // This replace allows to fix how the code source with DOS backline characters is displayed. let src = src.replace("\r\n", "\n"); - let mut closing_tags: Vec<&'static str> = Vec::new(); + // It contains the closing tag and the associated `Class`. + let mut closing_tags: Vec<(&'static str, Class)> = Vec::new(); + // The following two variables are used to group HTML elements with same `class` attributes + // to reduce the DOM size. + let mut current_class: Option = None; + // We need to keep the `Class` for each element because it could contain a `Span` which is + // used to generate links. + let mut pending_elems: Vec<(&str, Option)> = Vec::new(); + Classifier::new( &src, href_context.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP), @@ -138,15 +209,48 @@ fn write_code( ) .highlight(&mut |highlight| { match highlight { - Highlight::Token { text, class } => string(out, Escape(text), class, &href_context), + Highlight::Token { text, class } => { + // If the two `Class` are different, time to flush the current content and start + // a new one. + if !can_merge(current_class, class, text) { + write_pending_elems( + out, + &href_context, + &mut pending_elems, + &mut current_class, + &closing_tags, + ); + current_class = class.map(Class::dummy); + } else if current_class.is_none() { + current_class = class.map(Class::dummy); + } + pending_elems.push((text, class)); + } Highlight::EnterSpan { class } => { - closing_tags.push(enter_span(out, class, &href_context)) + // We flush everything just in case... + write_pending_elems( + out, + &href_context, + &mut pending_elems, + &mut current_class, + &closing_tags, + ); + closing_tags.push((enter_span(out, class, &href_context), class)) } Highlight::ExitSpan => { - exit_span(out, closing_tags.pop().expect("ExitSpan without EnterSpan")) + // We flush everything just in case... + write_pending_elems( + out, + &href_context, + &mut pending_elems, + &mut current_class, + &closing_tags, + ); + exit_span(out, closing_tags.pop().expect("ExitSpan without EnterSpan").0) } }; }); + write_pending_elems(out, &href_context, &mut pending_elems, &mut current_class, &closing_tags); } fn write_footer(out: &mut Buffer, playground_button: Option<&str>) { @@ -177,6 +281,31 @@ enum Class { } impl Class { + /// It is only looking at the variant, not the variant content. + /// + /// It is used mostly to group multiple similar HTML elements into one `` instead of + /// multiple ones. + fn is_equal_to(self, other: Self) -> bool { + match (self, other) { + (Self::Self_(_), Self::Self_(_)) + | (Self::Macro(_), Self::Macro(_)) + | (Self::Ident(_), Self::Ident(_)) + | (Self::Decoration(_), Self::Decoration(_)) => true, + (x, y) => x == y, + } + } + + /// If `self` contains a `Span`, it'll be replaced with `DUMMY_SP` to prevent creating links + /// on "empty content" (because of the attributes merge). + fn dummy(self) -> Self { + match self { + Self::Self_(_) => Self::Self_(DUMMY_SP), + Self::Macro(_) => Self::Macro(DUMMY_SP), + Self::Ident(_) => Self::Ident(DUMMY_SP), + s => s, + } + } + /// Returns the css class expected by rustdoc for each `Class`. fn as_html(self) -> &'static str { match self { @@ -630,7 +759,7 @@ impl<'a> Classifier<'a> { TokenKind::CloseBracket => { if self.in_attribute { self.in_attribute = false; - sink(Highlight::Token { text: "]", class: None }); + sink(Highlight::Token { text: "]", class: Some(Class::Attribute) }); sink(Highlight::ExitSpan); return; } @@ -701,7 +830,7 @@ fn enter_span( klass: Class, href_context: &Option>, ) -> &'static str { - string_without_closing_tag(out, "", Some(klass), href_context).expect( + string_without_closing_tag(out, "", Some(klass), href_context, true).expect( "internal error: enter_span was called with Some(klass) but did not return a \ closing HTML tag", ) @@ -733,8 +862,10 @@ fn string( text: T, klass: Option, href_context: &Option>, + open_tag: bool, ) { - if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context) { + if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context, open_tag) + { out.write_str(closing_tag); } } @@ -753,6 +884,7 @@ fn string_without_closing_tag( text: T, klass: Option, href_context: &Option>, + open_tag: bool, ) -> Option<&'static str> { let Some(klass) = klass else { @@ -761,6 +893,10 @@ fn string_without_closing_tag( }; let Some(def_span) = klass.get_span() else { + if !open_tag { + write!(out, "{}", text); + return None; + } write!(out, "{}", klass.as_html(), text); return Some(""); }; @@ -784,6 +920,7 @@ fn string_without_closing_tag( path }); } + // We don't want to generate links on empty text. if let Some(href_context) = href_context { if let Some(href) = href_context.context.shared.span_correspondance_map.get(&def_span).and_then(|href| { @@ -812,10 +949,20 @@ fn string_without_closing_tag( } }) { - write!(out, "{}", klass.as_html(), href, text_s); + if !open_tag { + // We're already inside an element which has the same klass, no need to give it + // again. + write!(out, "{}", href, text_s); + } else { + write!(out, "{}", klass.as_html(), href, text_s); + } return Some(""); } } + if !open_tag { + write!(out, "{}", text_s); + return None; + } write!(out, "{}", klass.as_html(), text_s); Some("") } From 33df8a96553fc99f9792c70a8a28491f0519718c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 12 Aug 2022 18:18:04 +0200 Subject: [PATCH 22/28] Don't generate ident elements as DOM nodes --- src/librustdoc/html/highlight.rs | 27 ++++++++++++++----- src/librustdoc/html/static/css/themes/ayu.css | 6 +---- .../html/static/css/themes/dark.css | 2 +- .../html/static/css/themes/light.css | 2 +- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 517f441e64798..9d8ee52a3faf8 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -164,7 +164,8 @@ fn write_pending_elems( /// * If two `Class` have the same variant, then they can be merged. /// * If the other `Class` is unclassified and only contains white characters (backline, /// whitespace, etc), it can be merged. -/// * If `Class` is `Ident`, then it can be merged with all unclassified elements. +/// * `Class::Ident` is considered the same as unclassified (because it doesn't have an associated +/// CSS class). fn can_merge(class1: Option, class2: Option, text: &str) -> bool { match (class1, class2) { (Some(c1), Some(c2)) => c1.is_equal_to(c2), @@ -264,7 +265,7 @@ enum Class { DocComment, Attribute, KeyWord, - // Keywords that do pointer/reference stuff. + /// Keywords that do pointer/reference stuff. RefKeyWord, Self_(Span), Macro(Span), @@ -272,6 +273,7 @@ enum Class { String, Number, Bool, + /// `Ident` isn't rendered in the HTML but we still need it for the `Span` it contains. Ident(Span), Lifetime, PreludeTy, @@ -320,7 +322,7 @@ impl Class { Class::String => "string", Class::Number => "number", Class::Bool => "bool-val", - Class::Ident(_) => "ident", + Class::Ident(_) => "", Class::Lifetime => "lifetime", Class::PreludeTy => "prelude-ty", Class::PreludeVal => "prelude-val", @@ -920,7 +922,7 @@ fn string_without_closing_tag( path }); } - // We don't want to generate links on empty text. + if let Some(href_context) = href_context { if let Some(href) = href_context.context.shared.span_correspondance_map.get(&def_span).and_then(|href| { @@ -954,7 +956,12 @@ fn string_without_closing_tag( // again. write!(out, "{}", href, text_s); } else { - write!(out, "{}", klass.as_html(), href, text_s); + let klass_s = klass.as_html(); + if klass_s.is_empty() { + write!(out, "{}", href, text_s); + } else { + write!(out, "{}", klass_s, href, text_s); + } } return Some(""); } @@ -963,8 +970,14 @@ fn string_without_closing_tag( write!(out, "{}", text_s); return None; } - write!(out, "{}", klass.as_html(), text_s); - Some("") + let klass_s = klass.as_html(); + if klass_s.is_empty() { + write!(out, "{}", text_s); + Some("") + } else { + write!(out, "{}", klass_s, text_s); + Some("") + } } #[cfg(test)] diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index b8218867a8bc8..4dfb64abbebe8 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -250,9 +250,6 @@ pre.rust .self { pre.rust .attribute { color: #e6e1cf; } -pre.rust .attribute .ident { - color: #e6e1cf; -} .example-wrap > pre.line-number { color: #5c67736e; @@ -398,8 +395,7 @@ pre.rust .comment {} .block a.current.method,.content span.tymethod,.content a.tymethod,.block a.current.tymethod, .content .fnname {} pre.rust .kw {} -pre.rust .self,pre.rust .bool-val,pre.rust .prelude-val,pre.rust .attribute, -pre.rust .attribute .ident {} +pre.rust .self,pre.rust .bool-val,pre.rust .prelude-val,pre.rust .attribute {} .content span.foreigntype,.content a.foreigntype,.block a.current.foreigntype {} pre.rust .doccomment {} .stab.deprecated {} diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index 6188e0ac0a9d3..39f83c9980827 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -202,7 +202,7 @@ pre.rust .kw { color: #ab8ac1; } pre.rust .kw-2, pre.rust .prelude-ty { color: #769acb; } pre.rust .number, pre.rust .string { color: #83a300; } pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val, -pre.rust .attribute, pre.rust .attribute .ident { color: #ee6868; } +pre.rust .attribute { color: #ee6868; } pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; } pre.rust .lifetime { color: #d97f26; } pre.rust .question-mark { diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index fba790b619371..5698088c790bb 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -184,7 +184,7 @@ pre.rust .kw { color: #8959A8; } pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; } pre.rust .number, pre.rust .string { color: #718C00; } pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val, -pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; } +pre.rust .attribute { color: #C82829; } pre.rust .comment { color: #8E908C; } pre.rust .doccomment { color: #4D4D4C; } pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; } From 6e574e100c47d1800e05d14cbaae959c11f320ed Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 11 Aug 2022 23:04:01 +0200 Subject: [PATCH 23/28] Update rustdoc tests --- .../html/highlight/fixtures/decorations.html | 4 +- .../html/highlight/fixtures/dos_line.html | 2 +- .../html/highlight/fixtures/highlight.html | 8 ++-- .../html/highlight/fixtures/sample.html | 38 +++++++++---------- .../html/highlight/fixtures/union.html | 10 ++--- src/test/rustdoc/issue-41783.codeblock.html | 5 +++ src/test/rustdoc/issue-41783.rs | 8 ++-- src/test/rustdoc/macro_rules-matchers.rs | 6 +-- 8 files changed, 41 insertions(+), 40 deletions(-) create mode 100644 src/test/rustdoc/issue-41783.codeblock.html diff --git a/src/librustdoc/html/highlight/fixtures/decorations.html b/src/librustdoc/html/highlight/fixtures/decorations.html index ae4dba116d637..2184897872153 100644 --- a/src/librustdoc/html/highlight/fixtures/decorations.html +++ b/src/librustdoc/html/highlight/fixtures/decorations.html @@ -1,2 +1,2 @@ -let x = 1; -let y = 2; \ No newline at end of file +let x = 1; +let y = 2; \ No newline at end of file diff --git a/src/librustdoc/html/highlight/fixtures/dos_line.html b/src/librustdoc/html/highlight/fixtures/dos_line.html index 1c8dbffe78c22..30b50ca7c662c 100644 --- a/src/librustdoc/html/highlight/fixtures/dos_line.html +++ b/src/librustdoc/html/highlight/fixtures/dos_line.html @@ -1,3 +1,3 @@ -pub fn foo() { +pub fn foo() { println!("foo"); } diff --git a/src/librustdoc/html/highlight/fixtures/highlight.html b/src/librustdoc/html/highlight/fixtures/highlight.html index 17f23278ec1f2..9f73e03f95e41 100644 --- a/src/librustdoc/html/highlight/fixtures/highlight.html +++ b/src/librustdoc/html/highlight/fixtures/highlight.html @@ -1,4 +1,4 @@ -use crate::a::foo; -use self::whatever; -let x = super::b::foo; -let y = Self::whatever; \ No newline at end of file +use crate::a::foo; +use self::whatever; +let x = super::b::foo; +let y = Self::whatever; \ No newline at end of file diff --git a/src/librustdoc/html/highlight/fixtures/sample.html b/src/librustdoc/html/highlight/fixtures/sample.html index ea797fd99d3f4..ae2650528eb72 100644 --- a/src/librustdoc/html/highlight/fixtures/sample.html +++ b/src/librustdoc/html/highlight/fixtures/sample.html @@ -8,30 +8,30 @@ .lifetime { color: #B76514; } .question-mark { color: #ff9011; } -
#![crate_type = "lib"]
+
#![crate_type = "lib"]
 
-use std::path::{Path, PathBuf};
+use std::path::{Path, PathBuf};
 
-#[cfg(target_os = "linux")]
-fn main() -> () {
-    let foo = true && false || true;
-    let _: *const () = 0;
-    let _ = &foo;
-    let _ = &&foo;
-    let _ = *foo;
-    mac!(foo, &mut bar);
-    assert!(self.length < N && index <= self.length);
-    ::std::env::var("gateau").is_ok();
-    #[rustfmt::skip]
-    let s:std::path::PathBuf = std::path::PathBuf::new();
-    let mut s = String::new();
+#[cfg(target_os = "linux")]
+fn main() -> () {
+    let foo = true && false || true;
+    let _: *const () = 0;
+    let _ = &foo;
+    let _ = &&foo;
+    let _ = *foo;
+    mac!(foo, &mut bar);
+    assert!(self.length < N && index <= self.length);
+    ::std::env::var("gateau").is_ok();
+    #[rustfmt::skip]
+    let s:std::path::PathBuf = std::path::PathBuf::new();
+    let mut s = String::new();
 
-    match &s {
-        ref mut x => {}
+    match &s {
+        ref mut x => {}
     }
 }
 
-macro_rules! bar {
-    ($foo:tt) => {};
+macro_rules! bar {
+    ($foo:tt) => {};
 }
 
diff --git a/src/librustdoc/html/highlight/fixtures/union.html b/src/librustdoc/html/highlight/fixtures/union.html index ac8bd28f6c362..9f8915282642d 100644 --- a/src/librustdoc/html/highlight/fixtures/union.html +++ b/src/librustdoc/html/highlight/fixtures/union.html @@ -1,8 +1,8 @@ -union Foo { - i: i8, - u: i8, +union Foo { + i: i8, + u: i8, } -fn main() { - let union = 0; +fn main() { + let union = 0; } diff --git a/src/test/rustdoc/issue-41783.codeblock.html b/src/test/rustdoc/issue-41783.codeblock.html new file mode 100644 index 0000000000000..b919935e4b47a --- /dev/null +++ b/src/test/rustdoc/issue-41783.codeblock.html @@ -0,0 +1,5 @@ +# single +## double +### triple +#[outer] +#![inner] \ No newline at end of file diff --git a/src/test/rustdoc/issue-41783.rs b/src/test/rustdoc/issue-41783.rs index 58a55a73815d0..d67716028799b 100644 --- a/src/test/rustdoc/issue-41783.rs +++ b/src/test/rustdoc/issue-41783.rs @@ -1,11 +1,9 @@ // @has issue_41783/struct.Foo.html // @!hasraw - 'space' // @!hasraw - 'comment' -// @hasraw - '# single' -// @hasraw - '## double' -// @hasraw - '### triple' -// @hasraw - '#[outer]' -// @hasraw - '#![inner]' +// @hasraw - '#[outer]' +// @hasraw - '#![inner]' +// @snapshot 'codeblock' - '//*[@class="rustdoc-toggle top-doc"]/*[@class="docblock"]//pre/code' /// ```no_run /// # # space diff --git a/src/test/rustdoc/macro_rules-matchers.rs b/src/test/rustdoc/macro_rules-matchers.rs index 98026663e5a7a..96f4126c7c277 100644 --- a/src/test/rustdoc/macro_rules-matchers.rs +++ b/src/test/rustdoc/macro_rules-matchers.rs @@ -5,14 +5,12 @@ // @has 'foo/macro.todo.html' // @has - '//span[@class="macro"]' 'macro_rules!' -// @has - '//span[@class="ident"]' 'todo' +// @hasraw - ' todo {' // @hasraw - '{ () => { ... }; ($(' // @has - '//span[@class="macro-nonterminal"]' '$' // @has - '//span[@class="macro-nonterminal"]' 'arg' -// @hasraw - ':' -// @has - '//span[@class="ident"]' 'tt' -// @hasraw - ')+' +// @hasraw - ':tt)+' // @hasraw - ') => { ... }; }' pub use std::todo; From 73a3c7eac8be9580f2a75d6d96a05e7e212baa22 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Sat, 13 Aug 2022 19:30:02 +0300 Subject: [PATCH 24/28] [REFACTOR] add fn finalize_opaque_types --- compiler/rustc_borrowck/src/type_check/mod.rs | 88 +++++++++++-------- 1 file changed, 49 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 7bf7f7357bf4f..bd79c5647952e 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -200,46 +200,8 @@ pub(crate) fn type_check<'mir, 'tcx>( ); translate_outlives_facts(&mut cx); - let opaque_type_values = - infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - - opaque_type_values - .into_iter() - .map(|(opaque_type_key, decl)| { - cx.fully_perform_op( - Locations::All(body.span), - ConstraintCategory::OpaqueType, - CustomTypeOp::new( - |infcx| { - infcx.register_member_constraints( - param_env, - opaque_type_key, - decl.hidden_type.ty, - decl.hidden_type.span, - ); - Ok(InferOk { value: (), obligations: vec![] }) - }, - || "opaque_type_map".to_string(), - ), - ) - .unwrap(); - let mut hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type); - trace!( - "finalized opaque type {:?} to {:#?}", - opaque_type_key, - hidden_type.ty.kind() - ); - if hidden_type.has_infer_types_or_consts() { - infcx.tcx.sess.delay_span_bug( - decl.hidden_type.span, - &format!("could not resolve {:#?}", hidden_type.ty.kind()), - ); - hidden_type.ty = infcx.tcx.ty_error(); - } - (opaque_type_key, (hidden_type, decl.origin)) - }) - .collect() + cx.finalize_opaque_types() }, ); @@ -2680,6 +2642,54 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.check_iscleanup(&body, block_data); } } + + /// Generates member constraints for the opaque types found so far. + /// Returns a map to their hidden types. + fn finalize_opaque_types( + &mut self, + ) -> VecMap, (OpaqueHiddenType<'tcx>, OpaqueTyOrigin)> { + let opaque_type_values = + self.infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + + opaque_type_values + .into_iter() + .map(|(opaque_type_key, decl)| { + let param_env = self.param_env; + self.fully_perform_op( + Locations::All(self.body.span), + ConstraintCategory::OpaqueType, + CustomTypeOp::new( + |infcx| { + infcx.register_member_constraints( + param_env, + opaque_type_key, + decl.hidden_type.ty, + decl.hidden_type.span, + ); + Ok(InferOk { value: (), obligations: vec![] }) + }, + || "opaque_type_map".to_string(), + ), + ) + .unwrap(); + let mut hidden_type = self.infcx.resolve_vars_if_possible(decl.hidden_type); + trace!( + "finalized opaque type {:?} to {:#?}", + opaque_type_key, + hidden_type.ty.kind() + ); + if hidden_type.has_infer_types_or_consts() { + self.infcx.tcx.sess.delay_span_bug( + decl.hidden_type.span, + &format!("could not resolve {:#?}", hidden_type.ty.kind()), + ); + hidden_type.ty = self.infcx.tcx.ty_error(); + } + + (opaque_type_key, (hidden_type, decl.origin)) + }) + .collect() + } } trait NormalizeLocation: fmt::Debug + Copy { From 0329621e9ed1cb19c96e9c656ac6dfa427f2b66f Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Sat, 13 Aug 2022 20:07:59 +0300 Subject: [PATCH 25/28] [REFACTOR] use OpaqueTypeMap type alias --- .../src/region_infer/opaque_types.rs | 19 ++++++++++--------- compiler/rustc_borrowck/src/type_check/mod.rs | 16 ++++++---------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index d6712b6a4799c..3eed9f33fb8c0 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -3,6 +3,7 @@ use rustc_data_structures::vec_map::VecMap; use rustc_hir::def_id::LocalDefId; use rustc_hir::OpaqueTyOrigin; use rustc_infer::infer::error_reporting::unexpected_hidden_region_diagnostic; +use rustc_infer::infer::opaque_types::OpaqueTypeMap; use rustc_infer::infer::TyCtxtInferExt as _; use rustc_infer::infer::{DefiningAnchor, InferCtxt}; use rustc_infer::traits::{Obligation, ObligationCause, TraitEngine}; @@ -62,12 +63,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { pub(crate) fn infer_opaque_types( &self, infcx: &InferCtxt<'_, 'tcx>, - opaque_ty_decls: VecMap, (OpaqueHiddenType<'tcx>, OpaqueTyOrigin)>, + opaque_ty_decls: OpaqueTypeMap<'tcx>, ) -> VecMap> { let mut result: VecMap> = VecMap::new(); - for (opaque_type_key, (concrete_type, origin)) in opaque_ty_decls { + for (opaque_type_key, decl) in opaque_ty_decls { let substs = opaque_type_key.substs; - debug!(?concrete_type, ?substs); + debug!(?decl.hidden_type, ?substs); let mut subst_regions = vec![self.universal_regions.fr_static]; let universal_substs = infcx.tcx.fold_regions(substs, |region, _| { @@ -90,7 +91,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { None => { subst_regions.push(vid); infcx.tcx.sess.delay_span_bug( - concrete_type.span, + decl.hidden_type.span, "opaque type with non-universal region substs", ); infcx.tcx.lifetimes.re_static @@ -102,7 +103,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { subst_regions.dedup(); let universal_concrete_type = - infcx.tcx.fold_regions(concrete_type, |region, _| match *region { + infcx.tcx.fold_regions(decl.hidden_type, |region, _| match *region { ty::ReVar(vid) => subst_regions .iter() .find(|ur_vid| self.eval_equal(vid, **ur_vid)) @@ -118,7 +119,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let ty = infcx.infer_opaque_definition_from_instantiation( opaque_type_key, universal_concrete_type, - origin, + decl.origin, ); // Sometimes two opaque types are the same only after we remap the generic parameters // back to the opaque type definition. E.g. we may have `OpaqueType` mapped to `(X, Y)` @@ -128,7 +129,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { if prev.ty != ty { if !ty.references_error() { prev.report_mismatch( - &OpaqueHiddenType { ty, span: concrete_type.span }, + &OpaqueHiddenType { ty, span: decl.hidden_type.span }, infcx.tcx, ); } @@ -136,11 +137,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { } // Pick a better span if there is one. // FIXME(oli-obk): collect multiple spans for better diagnostics down the road. - prev.span = prev.span.substitute_dummy(concrete_type.span); + prev.span = prev.span.substitute_dummy(decl.hidden_type.span); } else { result.insert( opaque_type_key.def_id, - OpaqueHiddenType { ty, span: concrete_type.span }, + OpaqueHiddenType { ty, span: decl.hidden_type.span }, ); } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index bd79c5647952e..d2220b9fd7717 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -5,16 +5,15 @@ use std::{fmt, iter, mem}; use either::Either; -use hir::OpaqueTyOrigin; use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::vec_map::VecMap; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::lang_items::LangItem; use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::canonical::QueryRegionConstraints; +use rustc_infer::infer::opaque_types::{OpaqueTypeDecl, OpaqueTypeMap}; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::RegionConstraintData; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -30,8 +29,8 @@ use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef, UserSubsts}; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ - self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, OpaqueHiddenType, - OpaqueTypeKey, RegionVid, ToPredicate, Ty, TyCtxt, UserType, UserTypeAnnotationIndex, + self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, RegionVid, ToPredicate, Ty, + TyCtxt, UserType, UserTypeAnnotationIndex, }; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::{Span, DUMMY_SP}; @@ -870,8 +869,7 @@ struct BorrowCheckContext<'a, 'tcx> { pub(crate) struct MirTypeckResults<'tcx> { pub(crate) constraints: MirTypeckRegionConstraints<'tcx>, pub(crate) universal_region_relations: Frozen>, - pub(crate) opaque_type_values: - VecMap, (OpaqueHiddenType<'tcx>, OpaqueTyOrigin)>, + pub(crate) opaque_type_values: OpaqueTypeMap<'tcx>, } /// A collection of region constraints that must be satisfied for the @@ -2645,9 +2643,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { /// Generates member constraints for the opaque types found so far. /// Returns a map to their hidden types. - fn finalize_opaque_types( - &mut self, - ) -> VecMap, (OpaqueHiddenType<'tcx>, OpaqueTyOrigin)> { + fn finalize_opaque_types(&mut self) -> OpaqueTypeMap<'tcx> { let opaque_type_values = self.infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); @@ -2686,7 +2682,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { hidden_type.ty = self.infcx.tcx.ty_error(); } - (opaque_type_key, (hidden_type, decl.origin)) + (opaque_type_key, OpaqueTypeDecl { hidden_type, ..decl }) }) .collect() } From 3a423b75d50d1a5813433558fe38f2fea42e16ab Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Mon, 15 Aug 2022 23:07:42 +0300 Subject: [PATCH 26/28] fix eval_outlives for higher-ranked regions When checking `'sup: 'sub`, take placeholder elements into account. This wasn't a problem previously (I hope!) because `eval_outlives` was only used for type_tests, which don't have higher-ranked outlive relations. --- compiler/rustc_borrowck/src/region_infer/mod.rs | 8 ++++++++ .../rustc_borrowck/src/region_infer/values.rs | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 2894c6d29ec43..0970b5f82b2f5 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1360,6 +1360,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { return self.eval_outlives(sup_region, self.universal_regions.fr_static); } + // Check `sup_region` contains all the placeholder regions in `sub_region`. + if !self.scc_values.contains_placeholders(sup_region_scc, sub_region_scc) { + debug!( + "eval_outlives: returning false because sub region contains a placeholder region not present in super" + ); + return false; + } + // Both the `sub_region` and `sup_region` consist of the union // of some number of universal regions (along with the union // of various points in the CFG; ignore those points for diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index c81ef10f7c740..f6baa3fd8e510 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -303,6 +303,22 @@ impl RegionValues { } } + /// Returns `true` if `sup_region` contains all the placeholder elements that + /// `sub_region` contains. + pub(crate) fn contains_placeholders(&self, sup_region: N, sub_region: N) -> bool { + if let Some(sub_row) = self.placeholders.row(sub_region) { + if let Some(sup_row) = self.placeholders.row(sup_region) { + sup_row.superset(sub_row) + } else { + // sup row is empty, so sub row must be empty + sub_row.is_empty() + } + } else { + // sub row is empty, always true + true + } + } + /// Returns the locations contained within a given region `r`. pub(crate) fn locations_outlived_by<'a>(&'a self, r: N) -> impl Iterator + 'a { self.points.row(r).into_iter().flat_map(move |set| { From fcbd6a30b9dccf5daa47b8ab4de7bb0e12ddbc7a Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Tue, 16 Aug 2022 02:21:56 +0300 Subject: [PATCH 27/28] support higher-ranked region in member constraints --- .../rustc_borrowck/src/region_infer/mod.rs | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 0970b5f82b2f5..8783a16d6fc68 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -712,16 +712,28 @@ impl<'tcx> RegionInferenceContext<'tcx> { *c_r = self.scc_representatives[scc]; } - // The 'member region' in a member constraint is part of the - // hidden type, which must be in the root universe. Therefore, - // it cannot have any placeholders in its value. - assert!(self.scc_universes[scc] == ty::UniverseIndex::ROOT); - debug_assert!( - self.scc_values.placeholders_contained_in(scc).next().is_none(), - "scc {:?} in a member constraint has placeholder value: {:?}", - scc, - self.scc_values.region_value_str(scc), - ); + // The 'member region' may have a placeholder region in its value. + // Consider the inner opaque type `impl Sized` in: + // `fn test() -> impl for<'a> Trait<'a, Ty = impl Sized + 'a>`. + // Here choice_regions = ['static, Placeholder('a, U1)]. + if self.scc_universes[scc] != ty::UniverseIndex::ROOT { + let Some(&choice) = choice_regions + .iter() + .find(|&&choice| self.eval_equal(choice, self.scc_representatives[scc])) + else { + debug!("failed higher-ranked member constraint"); + return false; + }; + + self.member_constraints_applied.push(AppliedMemberConstraint { + member_region_scc: scc, + min_choice: choice, + member_constraint_index, + }); + + debug!(?choice, "higher-ranked"); + return true; + } // The existing value for `scc` is a lower-bound. This will // consist of some set `{P} + {LB}` of points `{P}` and From e6b62a006d889b78b550d4c4a5a550bdd64a4590 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Tue, 16 Aug 2022 02:31:16 +0300 Subject: [PATCH 28/28] support higher-ranked regions in opaque type inference --- .../rustc_borrowck/src/region_infer/mod.rs | 16 ++++ .../src/region_infer/opaque_types.rs | 54 ++++++++++--- compiler/rustc_borrowck/src/type_check/mod.rs | 15 ++++ .../impl-trait/higher-ranked-regions-diag.rs | 25 ++++++ .../higher-ranked-regions-diag.stderr | 11 +++ .../higher-ranked-regions-basic.rs | 77 +++++++++++++++++++ .../higher-ranked-regions-basic.stderr | 49 ++++++++++++ .../higher-ranked-regions-gat.rs | 22 ++++++ 8 files changed, 257 insertions(+), 12 deletions(-) create mode 100644 src/test/ui/impl-trait/higher-ranked-regions-diag.rs create mode 100644 src/test/ui/impl-trait/higher-ranked-regions-diag.stderr create mode 100644 src/test/ui/type-alias-impl-trait/higher-ranked-regions-basic.rs create mode 100644 src/test/ui/type-alias-impl-trait/higher-ranked-regions-basic.stderr create mode 100644 src/test/ui/type-alias-impl-trait/higher-ranked-regions-gat.rs diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 8783a16d6fc68..cc908909f2d2a 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -167,9 +167,25 @@ pub(crate) struct RegionDefinition<'tcx> { /// If this is 'static or an early-bound region, then this is /// `Some(X)` where `X` is the name of the region. + /// + /// Use the method `Self::external_name` for more flexibility. pub(crate) external_name: Option>, } +impl<'tcx> RegionDefinition<'tcx> { + /// Gets the name of a universal region (including placeholders). + /// Returns `None` if this is an existential variable. + pub fn external_name(&self, tcx: TyCtxt<'tcx>) -> Option> { + match self.origin { + NllRegionVariableOrigin::FreeRegion => self.external_name, + NllRegionVariableOrigin::Placeholder(placeholder) => { + Some(tcx.mk_region(ty::RePlaceholder(placeholder))) + } + NllRegionVariableOrigin::Existential { .. } => None, + } + } +} + /// N.B., the variants in `Cause` are intentionally ordered. Lower /// values are preferred when it comes to error messages. Do not /// reorder willy nilly. diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 3eed9f33fb8c0..5ae45f9489e40 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -72,14 +72,22 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut subst_regions = vec![self.universal_regions.fr_static]; let universal_substs = infcx.tcx.fold_regions(substs, |region, _| { - if let ty::RePlaceholder(..) = region.kind() { - // Higher kinded regions don't need remapping, they don't refer to anything outside of this the substs. - return region; + let (vid, scc) = match region.kind() { + ty::ReVar(vid) => (vid, self.constraint_sccs.scc(vid)), + _ => bug!("expected nll var"), + }; + trace!(?vid, ?scc); + + // Special handling of higher-ranked regions. These appear in the substs of the + // inner opaque type `impl Sized` in: + // `fn test() -> impl for<'a> Trait<'a, Ty = impl Sized + 'a>` + if self.scc_universes[scc] != ty::UniverseIndex::ROOT { + subst_regions.push(vid); + return self.definitions[vid] + .external_name(infcx.tcx) + .expect("higher-ranked existential region found in opaque type substs"); } - let vid = self.to_region_vid(region); - trace!(?vid); - let scc = self.constraint_sccs.scc(vid); - trace!(?scc); + match self.scc_values.universal_regions_outlived_by(scc).find_map(|lb| { self.eval_equal(vid, lb).then_some(self.definitions[lb].external_name?) }) { @@ -107,9 +115,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { ty::ReVar(vid) => subst_regions .iter() .find(|ur_vid| self.eval_equal(vid, **ur_vid)) - .and_then(|ur_vid| self.definitions[*ur_vid].external_name) + .and_then(|ur_vid| self.definitions[*ur_vid].external_name(infcx.tcx)) .unwrap_or(infcx.tcx.lifetimes.re_root_empty), - _ => region, + _ => bug!("expected nll var"), }); debug!(?universal_concrete_type, ?universal_substs); @@ -160,6 +168,25 @@ impl<'tcx> RegionInferenceContext<'tcx> { { tcx.fold_regions(ty, |region, _| match *region { ty::ReVar(vid) => { + let scc = self.constraint_sccs.scc(vid); + + // Special handling of higher-ranked regions. + if self.scc_universes[scc] != ty::UniverseIndex::ROOT { + // If the region contains a single placeholder then they're equal. + if let Some((0, placeholder)) = + self.scc_values.placeholders_contained_in(scc).enumerate().last() + { + // HACK: we convert named placeholders to free regions for better errors. + // Otherwise, this is incorrect. + if let bound_region @ ty::BrNamed(scope, _) = placeholder.name { + return tcx + .mk_region(ty::ReFree(ty::FreeRegion { scope, bound_region })); + } + } + // Fallback: this will produce a cryptic error message. + return region; + } + // Find something that we can name let upper_bound = self.approx_universal_upper_bound(vid); let upper_bound = &self.definitions[upper_bound]; @@ -385,7 +412,7 @@ fn check_opaque_type_parameter_valid( return false; } GenericArgKind::Lifetime(lt) => { - matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_)) + matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_) | ty::RePlaceholder(_)) } GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)), }; @@ -495,9 +522,12 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { ty::ReErased => return r, // The regions that we expect from borrow checking. - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReEmpty(ty::UniverseIndex::ROOT) => {} + ty::ReEarlyBound(_) + | ty::ReFree(_) + | ty::RePlaceholder(_) + | ty::ReEmpty(ty::UniverseIndex::ROOT) => {} - ty::ReEmpty(_) | ty::RePlaceholder(_) | ty::ReVar(_) => { + ty::ReEmpty(_) | ty::ReVar(_) => { // All of the regions in the type should either have been // erased by writeback, or mapped back to named regions by // borrow checking. diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index d2220b9fd7717..8b4817e734a59 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2682,6 +2682,21 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { hidden_type.ty = self.infcx.tcx.ty_error(); } + // Convert all regions to nll vars. + let (opaque_type_key, hidden_type) = + self.infcx.tcx.fold_regions((opaque_type_key, hidden_type), |region, _| { + match region.kind() { + ty::ReVar(_) => region, + ty::RePlaceholder(placeholder) => self + .borrowck_context + .constraints + .placeholder_region(self.infcx, placeholder), + _ => self.infcx.tcx.mk_region(ty::ReVar( + self.borrowck_context.universal_regions.to_region_vid(region), + )), + } + }); + (opaque_type_key, OpaqueTypeDecl { hidden_type, ..decl }) }) .collect() diff --git a/src/test/ui/impl-trait/higher-ranked-regions-diag.rs b/src/test/ui/impl-trait/higher-ranked-regions-diag.rs new file mode 100644 index 0000000000000..470022565f6da --- /dev/null +++ b/src/test/ui/impl-trait/higher-ranked-regions-diag.rs @@ -0,0 +1,25 @@ +// Regression test for #97099. +// This was an ICE because `impl Sized` captures the lifetime 'a. + +// check-fail + +trait Trait { + type Assoc; +} + +struct Foo; + +impl<'a> Trait<&'a ()> for Foo { + type Assoc = (); +} + +fn foo() -> impl for<'a> Trait<&'a ()> { + Foo +} + +fn bar() -> impl for<'a> Trait<&'a (), Assoc = impl Sized> { + foo() + //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds +} + +fn main() {} diff --git a/src/test/ui/impl-trait/higher-ranked-regions-diag.stderr b/src/test/ui/impl-trait/higher-ranked-regions-diag.stderr new file mode 100644 index 0000000000000..b8a2c96c563a3 --- /dev/null +++ b/src/test/ui/impl-trait/higher-ranked-regions-diag.stderr @@ -0,0 +1,11 @@ +error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds + --> $DIR/higher-ranked-regions-diag.rs:21:5 + | +LL | fn bar() -> impl for<'a> Trait<&'a (), Assoc = impl Sized> { + | -- hidden type ` Trait<&'a ()> as Trait<&'a ()>>::Assoc` captures the lifetime `'a` as defined here +LL | foo() + | ^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/type-alias-impl-trait/higher-ranked-regions-basic.rs b/src/test/ui/type-alias-impl-trait/higher-ranked-regions-basic.rs new file mode 100644 index 0000000000000..099d489148b05 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/higher-ranked-regions-basic.rs @@ -0,0 +1,77 @@ +// Basic tests for opaque type inference under for<_> binders. + +// check-fail + +#![feature(type_alias_impl_trait)] + +trait Trait<'a> { + type Ty; +} +impl<'a, T> Trait<'a> for T { + type Ty = &'a (); +} + +mod basic_pass { + use super::*; + type Opq<'a> = impl Sized + 'a; + fn test() -> impl for<'a> Trait<'a, Ty = Opq<'a>> {} +} + +mod capture_rpit { + use super::*; + fn test() -> impl for<'a> Trait<'a, Ty = impl Sized> {} + //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds +} + +mod capture_tait { + use super::*; + type Opq0 = impl Sized; + type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0>; + type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>; + fn test() -> Opq2 {} + //~^ ERROR hidden type for `capture_tait::Opq0` captures lifetime that does not appear in bounds +} + +mod capture_tait_complex_pass { + use super::*; + type Opq0<'a> = impl Sized; + type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'b>>; // <- Note 'b + type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>; + fn test() -> Opq2 {} +} + +// Same as the above, but make sure that different placeholder regions are not equal. +mod capture_tait_complex_fail { + use super::*; + type Opq0<'a> = impl Sized; + type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a>>; // <- Note 'a + type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>; + fn test() -> Opq2 {} + //~^ ERROR hidden type for `capture_tait_complex_fail::Opq0<'a>` captures lifetime that does not appear in bounds +} + +// non-defining use because 'static is used. +mod constrain_fail0 { + use super::*; + type Opq0<'a, 'b> = impl Sized; + fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'static>> {} + //~^ ERROR non-defining opaque type use in defining scope +} + +// non-defining use because generic lifetime is used multiple times. +mod constrain_fail { + use super::*; + type Opq0<'a, 'b> = impl Sized; + fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'a>> {} + //~^ ERROR non-defining opaque type use in defining scope +} + +mod constrain_pass { + use super::*; + type Opq0<'a, 'b> = impl Sized; + type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a, 'b>>; + type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>; + fn test() -> Opq2 {} +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/higher-ranked-regions-basic.stderr b/src/test/ui/type-alias-impl-trait/higher-ranked-regions-basic.stderr new file mode 100644 index 0000000000000..85456ff6e5257 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/higher-ranked-regions-basic.stderr @@ -0,0 +1,49 @@ +error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds + --> $DIR/higher-ranked-regions-basic.rs:22:58 + | +LL | fn test() -> impl for<'a> Trait<'a, Ty = impl Sized> {} + | -- ^^ + | | + | hidden type `&'a ()` captures the lifetime `'a` as defined here + +error[E0700]: hidden type for `capture_tait::Opq0` captures lifetime that does not appear in bounds + --> $DIR/higher-ranked-regions-basic.rs:31:23 + | +LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0>; + | -- hidden type `&'b ()` captures the lifetime `'b` as defined here +LL | type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>; +LL | fn test() -> Opq2 {} + | ^^ + +error[E0700]: hidden type for `capture_tait_complex_fail::Opq0<'a>` captures lifetime that does not appear in bounds + --> $DIR/higher-ranked-regions-basic.rs:49:23 + | +LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a>>; // <- Note 'a + | -- hidden type `&'b ()` captures the lifetime `'b` as defined here +LL | type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>; +LL | fn test() -> Opq2 {} + | ^^ + +error: non-defining opaque type use in defining scope + --> $DIR/higher-ranked-regions-basic.rs:57:65 + | +LL | type Opq0<'a, 'b> = impl Sized; + | -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type +LL | fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'static>> {} + | ^^ + +error: non-defining opaque type use in defining scope + --> $DIR/higher-ranked-regions-basic.rs:65:60 + | +LL | fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'a>> {} + | ^^ + | +note: lifetime used multiple times + --> $DIR/higher-ranked-regions-basic.rs:64:15 + | +LL | type Opq0<'a, 'b> = impl Sized; + | ^^ ^^ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/type-alias-impl-trait/higher-ranked-regions-gat.rs b/src/test/ui/type-alias-impl-trait/higher-ranked-regions-gat.rs new file mode 100644 index 0000000000000..c3d4cab64f6c6 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/higher-ranked-regions-gat.rs @@ -0,0 +1,22 @@ +// Regression test for #97098. + +// check-pass + +#![feature(generic_associated_types)] +#![feature(type_alias_impl_trait)] + +pub trait Trait { + type Assoc<'a>; +} + +pub type Foo = impl for<'a> Trait = FooAssoc<'a>>; +pub type FooAssoc<'a> = impl Sized; + +struct Struct; +impl Trait for Struct { + type Assoc<'a> = &'a u32; +} + +const FOO: Foo = Struct; + +fn main() {}