diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index c0bf3833b9c33..6b28ab7d75563 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -364,3 +364,66 @@ macro_rules! array_impl_default { } array_impl_default! {32, T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T} + +#[cfg(not(bootstrap))] +#[lang = "array"] +impl [T; N] { + /// Returns an array of the same size as `self`, with function `f` applied to each element + /// in order. + /// + /// # Examples + /// + /// ``` + /// #![feature(array_map)] + /// let x = [1, 2, 3]; + /// let y = x.map(|v| v + 1); + /// assert_eq!(y, [2, 3, 4]); + /// + /// let x = [1, 2, 3]; + /// let mut temp = 0; + /// let y = x.map(|v| { temp += 1; v * temp }); + /// assert_eq!(y, [1, 4, 9]); + /// + /// let x = ["Ferris", "Bueller's", "Day", "Off"]; + /// let y = x.map(|v| v.len()); + /// assert_eq!(y, [6, 9, 3, 3]); + /// ``` + #[unstable(feature = "array_map", issue = "75243")] + pub fn map(self, mut f: F) -> [U; N] + where + F: FnMut(T) -> U, + { + use crate::mem::MaybeUninit; + struct Guard { + dst: *mut T, + initialized: usize, + } + + impl Drop for Guard { + fn drop(&mut self) { + debug_assert!(self.initialized <= N); + + let initialized_part = + crate::ptr::slice_from_raw_parts_mut(self.dst, self.initialized); + // SAFETY: this raw slice will contain only initialized objects + // that's why, it is allowed to drop it. + unsafe { + crate::ptr::drop_in_place(initialized_part); + } + } + } + let mut dst = MaybeUninit::uninit_array::(); + let mut guard: Guard = + Guard { dst: MaybeUninit::first_ptr_mut(&mut dst), initialized: 0 }; + for (src, dst) in IntoIter::new(self).zip(&mut dst) { + dst.write(f(src)); + guard.initialized += 1; + } + // FIXME: Convert to crate::mem::transmute once it works with generics. + // unsafe { crate::mem::transmute::<[MaybeUninit; N], [U; N]>(dst) } + crate::mem::forget(guard); + // SAFETY: At this point we've properly initialized the whole array + // and we just need to cast it to the correct type. + unsafe { crate::mem::transmute_copy::<_, [U; N]>(&dst) } + } +} diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index fcf5454308b47..763457d485da4 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -145,6 +145,7 @@ #![feature(abi_unadjusted)] #![feature(adx_target_feature)] #![feature(maybe_uninit_slice)] +#![feature(maybe_uninit_extra)] #![feature(external_doc)] #![feature(associated_type_bounds)] #![feature(const_caller_location)] diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs index 4bc44e98fc802..5aba1a5d958d1 100644 --- a/library/core/tests/array.rs +++ b/library/core/tests/array.rs @@ -290,3 +290,43 @@ fn empty_array_is_always_default() { let _arr = <[DoesNotImplDefault; 0]>::default(); } + +#[test] +fn array_map() { + let a = [1, 2, 3]; + let b = a.map(|v| v + 1); + assert_eq!(b, [2, 3, 4]); + + let a = [1u8, 2, 3]; + let b = a.map(|v| v as u64); + assert_eq!(b, [1, 2, 3]); +} + +// See note on above test for why `should_panic` is used. +#[test] +#[should_panic(expected = "test succeeded")] +fn array_map_drop_safety() { + use core::sync::atomic::AtomicUsize; + use core::sync::atomic::Ordering; + static DROPPED: AtomicUsize = AtomicUsize::new(0); + struct DropCounter; + impl Drop for DropCounter { + fn drop(&mut self) { + DROPPED.fetch_add(1, Ordering::SeqCst); + } + } + + let num_to_create = 5; + let success = std::panic::catch_unwind(|| { + let items = [0; 10]; + let mut nth = 0; + items.map(|_| { + assert!(nth < num_to_create); + nth += 1; + DropCounter + }); + }); + assert!(success.is_err()); + assert_eq!(DROPPED.load(Ordering::SeqCst), num_to_create); + panic!("test succeeded") +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index b4c299d390586..904e3f7284049 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -1,5 +1,6 @@ #![feature(alloc_layout_extra)] #![feature(array_chunks)] +#![feature(array_map)] #![feature(bool_to_option)] #![feature(bound_cloned)] #![feature(box_syntax)] diff --git a/src/librustc_hir/lang_items.rs b/src/librustc_hir/lang_items.rs index 7f473a458481f..2f7edeb405ffe 100644 --- a/src/librustc_hir/lang_items.rs +++ b/src/librustc_hir/lang_items.rs @@ -165,6 +165,7 @@ language_item_table! { BoolImplItem, sym::bool, bool_impl, Target::Impl; CharImplItem, sym::char, char_impl, Target::Impl; StrImplItem, sym::str, str_impl, Target::Impl; + ArrayImplItem, sym::array, array_impl, Target::Impl; SliceImplItem, sym::slice, slice_impl, Target::Impl; SliceU8ImplItem, sym::slice_u8, slice_u8_impl, Target::Impl; StrAllocImplItem, sym::str_alloc, str_alloc_impl, Target::Impl; diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 106df847a05cf..9078dc40041aa 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -649,6 +649,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.assemble_inherent_impl_for_primitive(lang_def_id); } } + ty::Array(_, _) => { + let lang_def_id = lang_items.array_impl(); + self.assemble_inherent_impl_for_primitive(lang_def_id); + } ty::RawPtr(ty::TypeAndMut { ty: _, mutbl }) => { let (lang_def_id1, lang_def_id2) = match mutbl { hir::Mutability::Not => { diff --git a/src/librustc_typeck/coherence/inherent_impls.rs b/src/librustc_typeck/coherence/inherent_impls.rs index 93ee87f6c572e..cd7429f166f26 100644 --- a/src/librustc_typeck/coherence/inherent_impls.rs +++ b/src/librustc_typeck/coherence/inherent_impls.rs @@ -112,6 +112,16 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { item.span, ); } + ty::Array(_, _) => { + self.check_primitive_impl( + def_id, + lang_items.array_impl(), + None, + "array", + "[T; N]", + item.span, + ); + } ty::RawPtr(ty::TypeAndMut { ty: inner, mutbl: hir::Mutability::Not }) if matches!(inner.kind, ty::Slice(_)) => { diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index c22538f21f69f..168967cdeb7cf 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -388,7 +388,7 @@ pub fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut V Bool => tcx.lang_items().bool_impl(), Str => tcx.lang_items().str_impl(), Slice => tcx.lang_items().slice_impl(), - Array => tcx.lang_items().slice_impl(), + Array => tcx.lang_items().array_impl(), Tuple => None, Unit => None, RawPointer => tcx.lang_items().const_ptr_impl(), diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 3000afde0c25d..a40b45f9a7e2c 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -55,6 +55,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { lang_items.bool_impl(), lang_items.char_impl(), lang_items.str_impl(), + lang_items.array_impl(), lang_items.slice_impl(), lang_items.slice_u8_impl(), lang_items.str_alloc_impl(),