From f8ec8e2acf6fdfcd91addd6d92cbe3ca00420829 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Sun, 24 Nov 2024 18:15:11 +0800 Subject: [PATCH 1/3] Support `vec![const { ... }; n]` syntax --- library/alloc/src/macros.rs | 4 +++ library/alloc/src/vec/mod.rs | 51 ++++++++++++++++++++++++++++++++++++ library/alloc/tests/lib.rs | 1 + library/alloc/tests/vec.rs | 22 ++++++++++++++++ 4 files changed, 78 insertions(+) diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 8c6a367869ce..d830516a2a19 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -43,6 +43,10 @@ macro_rules! vec { () => ( $crate::vec::Vec::new() ); + (const $elem:block; $n:expr) => ( + // SAFETY: The `const` keyword asserts the value being the result of a const expression. + unsafe { $crate::vec::from_const_elem(const $elem, $n) } + ); ($elem:expr; $n:expr) => ( $crate::vec::from_elem($elem, $n) ); diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 990b7e8f7612..20c6c12a2450 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3183,6 +3183,57 @@ pub fn from_elem_in(elem: T, n: usize, alloc: A) -> Vec< ::from_elem(elem, n, alloc) } +/// # Safety +/// +/// `elem` must be the result of some const expression. +#[doc(hidden)] +#[cfg(not(no_global_oom_handling))] +#[unstable(feature = "vec_of_const_expr", issue = "none")] +#[track_caller] +pub unsafe fn from_const_elem(elem: T, n: usize) -> Vec { + // SAFETY: Caller has guaranteed `elem` being the result of some const expression. + unsafe { from_const_elem_in(elem, n, Global) } +} + +/// # Safety +/// +/// `elem` must be the result of some const expression. +#[doc(hidden)] +#[cfg(not(no_global_oom_handling))] +#[unstable(feature = "vec_of_const_expr", issue = "none")] +#[track_caller] +unsafe fn from_const_elem_in(elem: T, n: usize, alloc: A) -> Vec { + /// # Safety + /// + /// `value` must points to a valid `T` value that is the result of some const expression. + unsafe fn fill_const_value(buffer: &mut [MaybeUninit], value: *const T) { + for target in buffer { + // SAFETY: If `value` is the result of some const expression, we can make as many + // copies as needed. + unsafe { target.write(ptr::read(value)) }; + } + } + + // Avoid calling the destructor of `elem`. + let elem = ManuallyDrop::new(elem); + let elem_ptr = ptr::from_ref(&*elem); + let mut result = Vec::::with_capacity_in(n, alloc); + let buffer_ptr = result.as_mut_ptr().cast::>(); + + // SAFETY: `with_capacity_in` makes sure the capacity is at least `n`, so we can make a buffer + // of length `n` out of it. + let buffer = unsafe { slice::from_raw_parts_mut(buffer_ptr, n) }; + + // SAFETY: Caller has guaranteed `elem` being the result of some const expression. + unsafe { fill_const_value(buffer, elem_ptr) }; + + // SAFETY: We have initialized exactly `n` values at the start of the buffer, so we are safe to + // set the length accordingly. + unsafe { result.set_len(n) }; + + result +} + #[cfg(not(no_global_oom_handling))] trait ExtendFromWithinSpec { /// # Safety diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 699a8e6776e6..7c3029f0fe86 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -36,6 +36,7 @@ #![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![feature(drain_keep_rest)] #![feature(local_waker)] +#![feature(vec_of_const_expr)] #![feature(vec_pop_if)] #![feature(unique_rc_arc)] #![feature(macro_metavar_expr_concat)] diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index 0f27fdff3e18..5c850c1c9843 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -2311,6 +2311,28 @@ fn test_vec_macro_repeat() { assert_eq!(vec![el; n], vec![Box::new(1), Box::new(1), Box::new(1)]); } +#[test] +fn test_vec_macro_repeat_const() { + #[derive(Eq, PartialEq, Debug)] + struct Item { + x: u64, + y: u32, // Paddings are added to test the uninitialized bytes case. + } + + impl Clone for Item { + fn clone(&self) -> Self { + panic!("no clone should be called"); + } + } + + const ITEM: Item = Item { x: 2, y: 3 }; + + assert_eq!(vec![const { ITEM }; 0], vec![ITEM; 0]); + assert_eq!(vec![const { ITEM }; 1], vec![ITEM]); + assert_eq!(vec![const { ITEM }; 2], vec![ITEM, ITEM]); + assert_eq!(vec![const { ITEM }; 3], vec![ITEM, ITEM, ITEM]); +} + #[test] fn test_vec_swap() { let mut a: Vec = vec![0, 1, 2, 3, 4, 5, 6]; From b68711c804845581254da7bbccb62d6d9e6795d0 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Sun, 24 Nov 2024 22:25:09 +0800 Subject: [PATCH 2/3] Add test for drop behavior --- library/alloc/tests/vec.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index 5c850c1c9843..5d7a27151948 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -2333,6 +2333,41 @@ fn test_vec_macro_repeat_const() { assert_eq!(vec![const { ITEM }; 3], vec![ITEM, ITEM, ITEM]); } +#[test] +fn test_vec_macro_repeat_const_drop_behavior() { + thread_local! { static DROP_COUNT: Cell = const { Cell::new(0) } }; + + fn with_drop_count_scope(count: usize, f: impl FnOnce()) { + struct RestoreOldDropCount(usize); + + impl Drop for RestoreOldDropCount { + fn drop(&mut self) { + DROP_COUNT.set(self.0); + } + } + + let _restore_old_drop_count = RestoreOldDropCount(DROP_COUNT.replace(count)); + + f(); + } + + struct DropCounter; + + impl Drop for DropCounter { + fn drop(&mut self) { + DROP_COUNT.set(DROP_COUNT.get().checked_add(1).unwrap()); + } + } + + for n in 0..10 { + with_drop_count_scope(0, || { + _ = vec![const { DropCounter }; n]; + + assert_eq!(DROP_COUNT.get(), n); + }); + } +} + #[test] fn test_vec_swap() { let mut a: Vec = vec![0, 1, 2, 3, 4, 5, 6]; From 25038ccb7be747f21bfdc1e667b906b8cc50d45e Mon Sep 17 00:00:00 2001 From: EFanZh Date: Mon, 25 Nov 2024 00:19:45 +0800 Subject: [PATCH 3/3] Update safety comment --- library/alloc/src/vec/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 20c6c12a2450..fc7b8a7f9f91 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3205,11 +3205,13 @@ pub unsafe fn from_const_elem(elem: T, n: usize) -> Vec { unsafe fn from_const_elem_in(elem: T, n: usize, alloc: A) -> Vec { /// # Safety /// - /// `value` must points to a valid `T` value that is the result of some const expression. + /// `value` must point to a valid `T` value that is the result of some const expression. unsafe fn fill_const_value(buffer: &mut [MaybeUninit], value: *const T) { for target in buffer { - // SAFETY: If `value` is the result of some const expression, we can make as many - // copies as needed. + // SAFETY: The current compiler implementation guarantees that if `value` points to a + // value that is the result of some const expression, we can make as many copies as + // needed safely by duplicating its byte representation. Code outside of the standard + // library should not rely on this fact. unsafe { target.write(ptr::read(value)) }; } }