about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-08-13 12:43:12 +0000
committerbors <bors@rust-lang.org>2020-08-13 12:43:12 +0000
commita0c290f951076d781b0ff08370445517446cee05 (patch)
tree19eb1d8ba7b30c480dddd4a86dcb3e8de42f5b91
parent1a7d9f52bc3545631c7485dce65b31742ef81c4d (diff)
parentaf32db21c8e1b1970886eb64b9955f9590e4c445 (diff)
downloadrust-a0c290f951076d781b0ff08370445517446cee05.tar.gz
rust-a0c290f951076d781b0ff08370445517446cee05.zip
Auto merge of #75212 - JulianKnodt:array_map, r=LukasKalbertodt
Add `array` lang item and `[T; N]::map(f: FnMut(T) -> S)`

This introduces an `array` lang item so functions can be defined on top of `[T; N]`. This was previously not done because const-generics was not complete enough to allow for this. Now it is in a state that is usable enough to start adding functions.

The function added is a monadic (I think?) map from `[T; N] -> [S; N]`. Until transmute can function on arrays, it also allocates an extra temporary array, but this can be removed at some point.

r? @lcnr
-rw-r--r--library/core/src/array/mod.rs63
-rw-r--r--library/core/src/lib.rs1
-rw-r--r--library/core/tests/array.rs40
-rw-r--r--library/core/tests/lib.rs1
-rw-r--r--src/librustc_hir/lang_items.rs1
-rw-r--r--src/librustc_typeck/check/method/probe.rs4
-rw-r--r--src/librustc_typeck/coherence/inherent_impls.rs10
-rw-r--r--src/librustdoc/clean/utils.rs2
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs1
9 files changed, 122 insertions, 1 deletions
diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs
index c0bf3833b9c..6b28ab7d755 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, const N: usize> [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<F, U>(self, mut f: F) -> [U; N]
+    where
+        F: FnMut(T) -> U,
+    {
+        use crate::mem::MaybeUninit;
+        struct Guard<T, const N: usize> {
+            dst: *mut T,
+            initialized: usize,
+        }
+
+        impl<T, const N: usize> Drop for Guard<T, N> {
+            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::<N>();
+        let mut guard: Guard<U, N> =
+            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<U>; 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 fcf5454308b..763457d485d 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 4bc44e98fc8..5aba1a5d958 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 b4c299d3905..904e3f72840 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 7f473a45848..2f7edeb405f 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 106df847a05..9078dc40041 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 93ee87f6c57..cd7429f166f 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 c22538f21f6..168967cdeb7 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 3000afde0c2..a40b45f9a7e 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(),