Skip to content
This repository was archived by the owner on Sep 11, 2023. It is now read-only.

Commit b824e23

Browse files
Fix enum_map! safety with incorrect Enum implementations
1 parent 21de944 commit b824e23

File tree

3 files changed

+56
-3
lines changed

3 files changed

+56
-3
lines changed

enum-map/src/internal.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,20 @@ pub trait EnumArray<V>: Enum {
2929
/// Array for enum-map storage.
3030
///
3131
/// This trait is inteded for primitive array types (with fixed length).
32-
pub trait Array<V> {
32+
pub unsafe trait Array<V> {
33+
// This is necessary duplication because the length in Enum trait can be
34+
// provided by user and may not be trustworthy for unsafe code.
35+
const LENGTH: usize;
36+
3337
/// Coerces a reference to the array into a reference to a slice.
3438
fn slice(&self) -> &[V];
3539

3640
/// Coerces a mutable reference to the array into a mutable reference to a slice.
3741
fn slice_mut(&mut self) -> &mut [V];
3842
}
3943

40-
impl<V, const N: usize> Array<V> for [V; N] {
44+
unsafe impl<V, const N: usize> Array<V> for [V; N] {
45+
const LENGTH: usize = N;
4146
fn slice(&self) -> &[V] {
4247
self
4348
}

enum-map/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ where
8080
#[doc(hidden)]
8181
#[must_use]
8282
pub fn storage_length(_: &Self) -> usize {
83-
K::LENGTH
83+
// SAFETY: We need to use LENGTH from K::Array, as K::LENGTH is
84+
// untrustworthy.
85+
K::Array::LENGTH
8486
}
8587

8688
#[doc(hidden)]

enum-map/tests/test.rs

+46
Original file line numberDiff line numberDiff line change
@@ -523,3 +523,49 @@ fn map_panic() {
523523
v + " modified"
524524
});
525525
}
526+
527+
macro_rules! make_enum_map_macro_safety_test {
528+
($a:tt $b:tt) => {
529+
// This is misuse of an API, however we need to test that to ensure safety
530+
// as we use unsafe code.
531+
enum E {
532+
A,
533+
B,
534+
C,
535+
}
536+
537+
impl Enum for E {
538+
const LENGTH: usize = $a;
539+
540+
fn from_usize(value: usize) -> E {
541+
match value {
542+
0 => E::A,
543+
1 => E::B,
544+
2 => E::C,
545+
_ => unimplemented!(),
546+
}
547+
}
548+
549+
fn into_usize(self) -> usize {
550+
self as usize
551+
}
552+
}
553+
554+
impl<V> EnumArray<V> for E {
555+
type Array = [V; $b];
556+
}
557+
558+
let map: EnumMap<E, String> = enum_map! { _ => "Hello, world!".into() };
559+
map.into_iter();
560+
};
561+
}
562+
563+
#[test]
564+
fn enum_map_macro_safety_under() {
565+
make_enum_map_macro_safety_test!(2 3);
566+
}
567+
568+
#[test]
569+
fn enum_map_macro_safety_over() {
570+
make_enum_map_macro_safety_test!(3 2);
571+
}

0 commit comments

Comments
 (0)