Skip to content

Commit 0e688cf

Browse files
authored
Merge pull request tock#4703 from tock/dev/virtio-remove-init-constgeneric-default-array-hack
chips/virtio: remove `init_constgeneric_default_array` hack
2 parents d2131f1 + 4382a14 commit 0e688cf

1 file changed

Lines changed: 5 additions & 56 deletions

File tree

chips/virtio/src/queues/split_queue.rs

Lines changed: 5 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -45,56 +45,6 @@ register_bitfields![u16,
4545
],
4646
];
4747

48-
// This is an unsafe workaround of an unsafe workaround.
49-
//
50-
// Unfortunately, the Rust core library defines defaults on arrays not in a
51-
// generic way, but only for arrays up to 32 elements. Hence, for arrays using a
52-
// const-generic argument for their length, `Default::<[$SOMETHING_NON_DEFAULT;
53-
// CONST_GENERIC_USIZE]>::default()` does not work in Rust (as of
54-
// 2022-11-26). Instead, we need to use this horrible and unsafe hack as
55-
// documented here, which initializes an array of `MaybeUninit`s and transmutes:
56-
// (https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#initializing-an-array-element-by-element)
57-
//
58-
// However, Rust is also unable to transmute from a `MaybeUninit<T>` to a
59-
// generic `T`, even though the former is explicitly layout-compatible with the
60-
// latter. Hence, we have to use another hack described in the following
61-
// comment:
62-
// https://github.com/rust-lang/rust/issues/62875#issuecomment-513834029
63-
//
64-
// This function encapsulates all of this unsafe code and should be safe to use
65-
// until Rust adds support for constructing arrays over a const-generic length
66-
// from the contained types' default values.
67-
//
68-
// In large parts, this function is a verbatim copy of Rust's example for
69-
// element-wise array initialization using `MaybeUninit`:
70-
// https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#initializing-an-array-element-by-element
71-
fn init_constgeneric_default_array<const N: usize, T: Default>() -> [T; N] {
72-
// Create an uninitialized array of `MaybeUninit`. The `assume_init` is safe
73-
// because the type we are claiming to have initialized here is a bunch of
74-
// `MaybeUninit`s, which do not require initialization.
75-
let mut uninit_arr: [core::mem::MaybeUninit<T>; N] =
76-
unsafe { core::mem::MaybeUninit::uninit().assume_init() };
77-
78-
// Dropping a `MaybeUninit` does nothing, so if there is a panic during this
79-
// loop, we have a memory leak, but there is no memory safety issue.
80-
for elem in &mut uninit_arr[..] {
81-
elem.write(T::default());
82-
}
83-
84-
// Everything is initialized. We'd like to transmute the `[MaybeUnit<T>; N]`
85-
// array into a `[T; N]`, but transmuting `MaybeUninit<T>` to `T` (where T
86-
// is generic) is not (yet) supported. Hence we need to take a pointer to
87-
// this array, cast it to the correct type, read the pointer back and forget
88-
// the original `[MaybeUnit<T>; N]` array, as described here:
89-
// https://github.com/rust-lang/rust/issues/62875#issuecomment-513834029
90-
let uninit_arr_ptr: *mut [core::mem::MaybeUninit<T>; N] = &mut uninit_arr as *mut _;
91-
let transmuted: [T; N] = unsafe { core::ptr::read(uninit_arr_ptr.cast::<[T; N]>()) };
92-
93-
// With the original value forgotten and new value recreated from its
94-
// pointer, return it:
95-
transmuted
96-
}
97-
9848
/// A single Virtqueue descriptor.
9949
///
10050
/// Implements the memory layout of a single Virtqueue descriptor of a
@@ -142,7 +92,7 @@ pub struct VirtqueueDescriptors<const MAX_QUEUE_SIZE: usize>([VirtqueueDescripto
14292

14393
impl<const MAX_QUEUE_SIZE: usize> Default for VirtqueueDescriptors<MAX_QUEUE_SIZE> {
14494
fn default() -> Self {
145-
VirtqueueDescriptors(init_constgeneric_default_array())
95+
VirtqueueDescriptors(core::array::from_fn(|_| VirtqueueDescriptor::default()))
14696
}
14797
}
14898

@@ -201,7 +151,7 @@ impl<const MAX_QUEUE_SIZE: usize> Default for VirtqueueAvailableRing<MAX_QUEUE_S
201151
VirtqueueAvailableRing {
202152
flags: InMemoryRegister::new(0),
203153
idx: InMemoryRegister::new(0),
204-
ring: init_constgeneric_default_array(),
154+
ring: core::array::from_fn(|_| VirtqueueAvailableElement::default()),
205155
used_event: InMemoryRegister::new(0),
206156
}
207157
}
@@ -244,7 +194,7 @@ impl<const MAX_QUEUE_SIZE: usize> Default for VirtqueueUsedRing<MAX_QUEUE_SIZE>
244194
VirtqueueUsedRing {
245195
flags: InMemoryRegister::new(0),
246196
idx: InMemoryRegister::new(0),
247-
ring: init_constgeneric_default_array(),
197+
ring: core::array::from_fn(|_| VirtqueueUsedElement::default()),
248198
avail_event: InMemoryRegister::new(0),
249199
}
250200
}
@@ -493,7 +443,7 @@ impl<'a, 'b, const MAX_QUEUE_SIZE: usize> SplitVirtqueue<'a, 'b, MAX_QUEUE_SIZE>
493443
queue_number: Cell::new(0),
494444
max_elements: Cell::new(MAX_QUEUE_SIZE),
495445

496-
descriptor_buffers: init_constgeneric_default_array(),
446+
descriptor_buffers: core::array::from_fn(|_| OptionalCell::empty()),
497447

498448
client: OptionalCell::empty(),
499449
used_callbacks_enabled: Cell::new(false),
@@ -731,8 +681,7 @@ impl<'a, 'b, const MAX_QUEUE_SIZE: usize> SplitVirtqueue<'a, 'b, MAX_QUEUE_SIZE>
731681
) -> [Option<VirtqueueBuffer<'b>>; MAX_QUEUE_SIZE] {
732682
assert!(self.initialized.get());
733683

734-
let mut res: [Option<VirtqueueBuffer<'b>>; MAX_QUEUE_SIZE] =
735-
init_constgeneric_default_array();
684+
let mut res: [Option<VirtqueueBuffer<'b>>; MAX_QUEUE_SIZE] = [const { None }; _];
736685

737686
let mut i = 0;
738687
let mut next_index: Option<usize> = Some(top_descriptor_index);

0 commit comments

Comments
 (0)