diff --git a/crates/bevy_ecs/hecs/macros/src/lib.rs b/crates/bevy_ecs/hecs/macros/src/lib.rs index 059a5b3f127cc..021c5f060bb1c 100644 --- a/crates/bevy_ecs/hecs/macros/src/lib.rs +++ b/crates/bevy_ecs/hecs/macros/src/lib.rs @@ -248,7 +248,7 @@ fn struct_fields(fields: &syn::Fields) -> (Vec<&syn::Type>, Vec) { } } -fn member_as_idents<'a>(members: &'a [syn::Member]) -> Vec> { +fn member_as_idents(members: &[syn::Member]) -> Vec> { members .iter() .map(|member| match member { diff --git a/crates/bevy_ecs/hecs/src/archetype.rs b/crates/bevy_ecs/hecs/src/archetype.rs index 32889d301aa0e..dfded4cd08366 100644 --- a/crates/bevy_ecs/hecs/src/archetype.rs +++ b/crates/bevy_ecs/hecs/src/archetype.rs @@ -51,6 +51,24 @@ pub struct Archetype { } impl Archetype { + fn assert_type_info(types: &[TypeInfo]) { + types.windows(2).for_each(|x| match x[0].cmp(&x[1]) { + core::cmp::Ordering::Less => (), + #[cfg(debug_assertions)] + core::cmp::Ordering::Equal => panic!( + "attempted to allocate entity with duplicate {} components; \ + each type must occur at most once!", + x[0].type_name + ), + #[cfg(not(debug_assertions))] + core::cmp::Ordering::Equal => panic!( + "attempted to allocate entity with duplicate components; \ + each type must occur at most once!" + ), + core::cmp::Ordering::Greater => panic!("type info is unsorted"), + }); + } + #[allow(missing_docs)] pub fn new(types: Vec) -> Self { Self::with_grow(types, 64) @@ -58,10 +76,7 @@ impl Archetype { #[allow(missing_docs)] pub fn with_grow(types: Vec, grow_size: usize) -> Self { - debug_assert!( - types.windows(2).all(|x| x[0] < x[1]), - "type info unsorted or contains duplicates" - ); + Self::assert_type_info(&types); let mut state = HashMap::with_capacity_and_hasher(types.len(), Default::default()); for ty in &types { state.insert(ty.id, TypeState::new()); @@ -484,6 +499,8 @@ pub struct TypeInfo { id: TypeId, layout: Layout, drop: unsafe fn(*mut u8), + #[cfg(debug_assertions)] + type_name: &'static str, } impl TypeInfo { @@ -497,6 +514,8 @@ impl TypeInfo { id: TypeId::of::(), layout: Layout::new::(), drop: drop_ptr::, + #[cfg(debug_assertions)] + type_name: core::any::type_name::(), } } diff --git a/crates/bevy_ecs/hecs/tests/tests.rs b/crates/bevy_ecs/hecs/tests/tests.rs index 4ebd7d5c6a89e..64cc821d99394 100644 --- a/crates/bevy_ecs/hecs/tests/tests.rs +++ b/crates/bevy_ecs/hecs/tests/tests.rs @@ -183,7 +183,18 @@ fn derived_bundle() { #[test] #[cfg(feature = "macros")] -#[should_panic(expected = "each type must occur at most once")] +#[cfg_attr( + debug_assertions, + should_panic( + expected = "attempted to allocate entity with duplicate i32 components; each type must occur at most once!" + ) +)] +#[cfg_attr( + not(debug_assertions), + should_panic( + expected = "attempted to allocate entity with duplicate components; each type must occur at most once!" + ) +)] fn bad_bundle_derive() { #[derive(Bundle)] struct Foo { @@ -361,3 +372,21 @@ fn added_tracking() { assert!(world.query_one_mut::<&i32>(a).is_ok()); assert!(world.query_one_mut::>(a).is_err()); } + +#[test] +#[cfg_attr( + debug_assertions, + should_panic( + expected = "attempted to allocate entity with duplicate f32 components; each type must occur at most once!" + ) +)] +#[cfg_attr( + not(debug_assertions), + should_panic( + expected = "attempted to allocate entity with duplicate components; each type must occur at most once!" + ) +)] +fn duplicate_components_panic() { + let mut world = World::new(); + world.reserve::<(f32, i64, f32)>(1); +}