-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Support vec![const { ... }; n]
syntax
#133412
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3183,6 +3183,57 @@ pub fn from_elem_in<T: Clone, A: Allocator>(elem: T, n: usize, alloc: A) -> Vec< | |
<T as SpecFromElem>::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<T>(elem: T, n: usize) -> Vec<T> { | ||
// 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<T, A: Allocator>(elem: T, n: usize, alloc: A) -> Vec<T, A> { | ||
/// # Safety | ||
/// | ||
/// `value` must points to a valid `T` value that is the result of some const expression. | ||
unsafe fn fill_const_value<T>(buffer: &mut [MaybeUninit<T>], 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`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This raises the interesting question whether the destructor should be executed at all for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tested the array of const expression case, the destructor is not called: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f9ffe1eb972bc415b0e6bf6bf48c18c1. This is consistent with the current implementation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense. Would be good to have a test. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test added. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good! |
||
let elem = ManuallyDrop::new(elem); | ||
let elem_ptr = ptr::from_ref(&*elem); | ||
let mut result = Vec::<T, A>::with_capacity_in(n, alloc); | ||
let buffer_ptr = result.as_mut_ptr().cast::<MaybeUninit<T>>(); | ||
|
||
// 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 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an interesting argument that I have not seen before. I can't see anything immediately wrong with it, but paging in @rust-lang/opsem for extra scrutiny.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be the case with the current
const
capabilities, but I'm not 100% sure it holds with every possible extension, namely leaking const allocation to runtime. I think the rules around const object unification would make this a valid execution, but I don't have all the relevant info in cache.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do intent that consts can still be implemented as many copies of the same thing for codegen. So whatever we do with const allocation should support that.
But it may be good to put a comment in the source here explaining that this is the standard library doing privileged reasoning, and not a stable guarantee.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have updated the safety comment. Please let me know if there is anything missing or inaccurate.