diff --git a/multi_index_map/examples/main.rs b/multi_index_map/examples/main.rs index 84a38af..674dd42 100644 --- a/multi_index_map/examples/main.rs +++ b/multi_index_map/examples/main.rs @@ -1,3 +1,5 @@ +#![cfg_attr(feature = "trivial_bounds", feature(trivial_bounds))] + use crate::inner::MultiIndexOrderMap; use crate::inner::Order; @@ -14,7 +16,9 @@ mod inner { pub(crate) note: String, } - // Manually implement Clone, this can be auto generated correctly by rust-analyzer + // Manually implement Clone if trivial_bounds is disabled + // this can be auto generated correctly by rust-analyzer + #[cfg(not(feature = "trivial_bounds"))] impl Clone for MultiIndexOrderMap { fn clone(&self) -> Self { Self { diff --git a/multi_index_map/tests/capacity_manipulations.rs b/multi_index_map/tests/capacity_manipulations.rs index 19dc776..6ab92fd 100644 --- a/multi_index_map/tests/capacity_manipulations.rs +++ b/multi_index_map/tests/capacity_manipulations.rs @@ -1,3 +1,5 @@ +#![cfg_attr(feature = "trivial_bounds", feature(trivial_bounds))] + use multi_index_map::MultiIndexMap; #[derive(MultiIndexMap, Debug)] diff --git a/multi_index_map/tests/clone_derive.rs b/multi_index_map/tests/clone_derive.rs new file mode 100644 index 0000000..1a38ad8 --- /dev/null +++ b/multi_index_map/tests/clone_derive.rs @@ -0,0 +1,38 @@ +#![cfg_attr(feature = "trivial_bounds", feature(trivial_bounds))] +#![cfg(feature = "trivial_bounds")] +use multi_index_map::MultiIndexMap; + +#[derive(Hash, PartialEq, Eq, Clone, Debug)] +struct TestNonPrimitiveType(u64); + +#[derive(MultiIndexMap, Clone, Debug)] +struct TestElement { + #[multi_index(hashed_unique)] + field1: TestNonPrimitiveType, + field2: String, +} + +#[test] +fn should_compile() { + let mut map = MultiIndexTestElementMap::default(); + + // check that formatting produces non empty strings + assert!(!format!("{:?}", map._field1_index).is_empty()); + assert!(!format!("{:?}", map._store).is_empty()); + assert!(!format!("{:?}", map).is_empty()); + + let elem1 = TestElement { + field1: TestNonPrimitiveType(42), + field2: "ElementOne".to_string(), + }; + map.insert(elem1); + + let mut new_map = map.clone(); + + assert_eq!(new_map.len(), 1); + + let elem1 = new_map.remove_by_field1(&TestNonPrimitiveType(42)).unwrap(); + assert_eq!(elem1.field1.0, 42); + assert_eq!(elem1.field2, "ElementOne"); + assert_eq!(new_map.len(), 0); +} diff --git a/multi_index_map/tests/debug.rs b/multi_index_map/tests/debug_derive.rs similarity index 100% rename from multi_index_map/tests/debug.rs rename to multi_index_map/tests/debug_derive.rs diff --git a/multi_index_map/tests/hashed_non_unique.rs b/multi_index_map/tests/hashed_non_unique.rs index 86f5d66..96812f8 100644 --- a/multi_index_map/tests/hashed_non_unique.rs +++ b/multi_index_map/tests/hashed_non_unique.rs @@ -1,3 +1,5 @@ +#![cfg_attr(feature = "trivial_bounds", feature(trivial_bounds))] + use multi_index_map::MultiIndexMap; #[derive(Hash, PartialEq, Eq, Clone, Debug)] diff --git a/multi_index_map/tests/iter_after_modify.rs b/multi_index_map/tests/iter_after_modify.rs index a331768..ae1f45e 100644 --- a/multi_index_map/tests/iter_after_modify.rs +++ b/multi_index_map/tests/iter_after_modify.rs @@ -1,3 +1,5 @@ +#![cfg_attr(feature = "trivial_bounds", feature(trivial_bounds))] + use multi_index_map::MultiIndexMap; #[derive(MultiIndexMap, Debug)] diff --git a/multi_index_map/tests/ordered_non_unique.rs b/multi_index_map/tests/ordered_non_unique.rs index 51bac20..de74f10 100644 --- a/multi_index_map/tests/ordered_non_unique.rs +++ b/multi_index_map/tests/ordered_non_unique.rs @@ -1,3 +1,5 @@ +#![cfg_attr(feature = "trivial_bounds", feature(trivial_bounds))] + use multi_index_map::MultiIndexMap; #[derive(Hash, PartialEq, Eq, Clone, Debug, PartialOrd, Ord)] diff --git a/multi_index_map/tests/reverse_iter.rs b/multi_index_map/tests/reverse_iter.rs index 2440379..82c53d6 100644 --- a/multi_index_map/tests/reverse_iter.rs +++ b/multi_index_map/tests/reverse_iter.rs @@ -1,3 +1,5 @@ +#![cfg_attr(feature = "trivial_bounds", feature(trivial_bounds))] + use multi_index_map::MultiIndexMap; #[derive(MultiIndexMap, PartialEq, Debug)] struct TestElement { diff --git a/multi_index_map/tests/update.rs b/multi_index_map/tests/update.rs index 1f82ea3..9563bec 100644 --- a/multi_index_map/tests/update.rs +++ b/multi_index_map/tests/update.rs @@ -1,3 +1,5 @@ +#![cfg_attr(feature = "trivial_bounds", feature(trivial_bounds))] + use multi_index_map::MultiIndexMap; #[derive(Hash, PartialEq, Eq, Clone)] diff --git a/multi_index_map_derive/src/generators.rs b/multi_index_map_derive/src/generators.rs index cc20284..adc1042 100644 --- a/multi_index_map_derive/src/generators.rs +++ b/multi_index_map_derive/src/generators.rs @@ -69,30 +69,45 @@ fn index_field_type( } } -// For each indexed field generate a TokenStream of the Debug bound for the field type and the multi_index_map specific type #[cfg(feature = "trivial_bounds")] -pub(crate) fn generate_lookup_table_field_types( +fn generate_type_bounds_table_fields( fields: &[(Field, FieldIdents, Ordering, Uniqueness)], + type_bounds: ::proc_macro2::TokenStream, ) -> impl Iterator + '_ { fields .iter() - .flat_map(|(f, _idents, ordering, uniqueness)| { + .flat_map(move |(f, _idents, ordering, uniqueness)| { let ty = &f.ty; let type_debug = quote! { - #ty: ::core::fmt::Debug, + #ty: #type_bounds, }; let field_type = index_field_type(ty, ordering, uniqueness); let field_debug = quote! { - #field_type: ::core::fmt::Debug, + #field_type: #type_bounds, }; [type_debug, field_debug] }) } +// For each indexed field generate a TokenStream of the Debug bound for the field type and the multi_index_map specific type +#[cfg(feature = "trivial_bounds")] +pub(crate) fn generate_debug_type_bounds_table_fields( + fields: &[(Field, FieldIdents, Ordering, Uniqueness)], +) -> impl Iterator + '_ { + generate_type_bounds_table_fields(fields, quote! {::core::fmt::Debug}) +} + +// For each indexed field generate a TokenStream of the Clone bound for the field type and the multi_index_map specific type +#[cfg(feature = "trivial_bounds")] +pub(crate) fn generate_clone_type_bounds_table_fields( + fields: &[(Field, FieldIdents, Ordering, Uniqueness)], +) -> impl Iterator + '_ { + generate_type_bounds_table_fields(fields, quote! {::core::clone::Clone}) +} // For each indexed field generate a TokenStream representing initializing the lookup table. // Used in `with_capacity` initialization // If lookup table data structures support `with_capacity`, change `default()` and `new()` calls to @@ -166,6 +181,20 @@ pub(crate) fn generate_lookup_table_debug( }) } +// For each indexed field generate a TokenStream representing the `.clone()` of that field +#[cfg(feature = "trivial_bounds")] +pub(crate) fn generate_lookup_table_clone( + fields: &[(Field, FieldIdents, Ordering, Uniqueness)], +) -> impl Iterator + '_ { + fields.iter().map(|(_f, idents, _ordering, _uniqueness)| { + let index_name = &idents.index_name; + + quote! { + #index_name: ::core::clone::Clone::clone(&self.#index_name), + } + }) +} + // For each indexed field generate a TokenStream representing inserting the position // in the backing storage to that field's lookup table // Unique indexed fields just require a simple insert to the map, @@ -864,23 +893,52 @@ pub(crate) fn generate_expanded( #[cfg(feature = "trivial_bounds")] lookup_table_fields_debug: impl Iterator< Item = proc_macro2::TokenStream, >, - #[cfg(feature = "trivial_bounds")] lookup_table_field_types: impl Iterator< + #[cfg(feature = "trivial_bounds")] debug_type_bounds_table_fields: impl Iterator< + Item = proc_macro2::TokenStream, + >, + #[cfg(feature = "trivial_bounds")] lookup_table_fields_clone: impl Iterator< + Item = proc_macro2::TokenStream, + >, + #[cfg(feature = "trivial_bounds")] clone_type_bounds_table_fields: impl Iterator< Item = proc_macro2::TokenStream, >, ) -> proc_macro2::TokenStream { - #[cfg(not(feature = "trivial_bounds"))] - let debug_impl = quote! {}; - - #[cfg(feature = "trivial_bounds")] - let debug_impl = quote! { - impl ::core::fmt::Debug for #map_name where #element_name: ::core::fmt::Debug, - #(#lookup_table_field_types)* - { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - f.debug_struct(stringify!(#map_name)) - .field("_store", &self._store) - #(#lookup_table_fields_debug)* - .finish() + let debug_impl = { + #[cfg(not(feature = "trivial_bounds"))] + quote! {} + + #[cfg(feature = "trivial_bounds")] + quote! { + impl ::core::fmt::Debug for #map_name where #element_name: ::core::fmt::Debug, + #(#debug_type_bounds_table_fields)* + { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_struct(stringify!(#map_name)) + .field("_store", &self._store) + #(#lookup_table_fields_debug)* + .finish() + } + } + } + }; + + let clone_impl = { + #[cfg(not(feature = "trivial_bounds"))] + quote! {} + + #[cfg(feature = "trivial_bounds")] + + quote! { + impl ::core::clone::Clone for #map_name where #element_name: ::core::clone::Clone, + #(#clone_type_bounds_table_fields)* + { + #[inline] + fn clone(&self) -> #map_name { + #map_name { + _store: ::core::clone::Clone::clone(&self._store), + #(#lookup_table_fields_clone)* + } + } } } }; @@ -894,6 +952,8 @@ pub(crate) fn generate_expanded( #debug_impl + #clone_impl + impl #map_name { #element_vis fn with_capacity(n: usize) -> #map_name { #map_name { diff --git a/multi_index_map_derive/src/lib.rs b/multi_index_map_derive/src/lib.rs index 9df00ac..2d5ebc8 100644 --- a/multi_index_map_derive/src/lib.rs +++ b/multi_index_map_derive/src/lib.rs @@ -84,10 +84,18 @@ pub fn multi_index_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea #[cfg(feature = "trivial_bounds")] let lookup_table_fields_debug = generators::generate_lookup_table_debug(&indexed_fields); + #[cfg(feature = "trivial_bounds")] + let lookup_table_fields_clone = generators::generate_lookup_table_clone(&indexed_fields); + let lookup_table_fields_shrink = generators::generate_lookup_table_shrink(&indexed_fields); #[cfg(feature = "trivial_bounds")] - let lookup_table_field_types = generators::generate_lookup_table_field_types(&indexed_fields); + let debug_type_bounds_table_fields = + generators::generate_debug_type_bounds_table_fields(&indexed_fields); + + #[cfg(feature = "trivial_bounds")] + let clone_type_bounds_table_fields = + generators::generate_clone_type_bounds_table_fields(&indexed_fields); let inserts = generators::generate_inserts(&indexed_fields); @@ -127,7 +135,11 @@ pub fn multi_index_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea #[cfg(feature = "trivial_bounds")] lookup_table_fields_debug, #[cfg(feature = "trivial_bounds")] - lookup_table_field_types, + debug_type_bounds_table_fields, + #[cfg(feature = "trivial_bounds")] + lookup_table_fields_clone, + #[cfg(feature = "trivial_bounds")] + clone_type_bounds_table_fields, ); // Hand the output tokens back to the compiler.