From 6e1fab851ffdf7726f552ffc226870b97408dfdf Mon Sep 17 00:00:00 2001 From: Arnaud de Bossoreille Date: Sat, 23 Nov 2024 20:37:02 +0100 Subject: [PATCH] Custom fragment generator for clone impls --- scripts/msrv_pin_dependencies.sh | 1 + scripts/switch_rust_toolchain.sh | 8 + truc/src/generator/fragment/clone.rs | 238 +++++++++++++++++++++++++++ truc/src/generator/fragment/mod.rs | 1 + truc/src/generator/mod.rs | 5 +- 5 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 truc/src/generator/fragment/clone.rs diff --git a/scripts/msrv_pin_dependencies.sh b/scripts/msrv_pin_dependencies.sh index 08547c8..010c9df 100755 --- a/scripts/msrv_pin_dependencies.sh +++ b/scripts/msrv_pin_dependencies.sh @@ -6,4 +6,5 @@ set -x cargo update -p ppv-lite86 --precise 0.2.17 cargo update -p derive_more --precise 0.99.17 cargo update -p pretty_assertions --precise 1.3.0 +cargo update -p libc --precise 0.2.163 diff --git a/scripts/switch_rust_toolchain.sh b/scripts/switch_rust_toolchain.sh index 63a20bf..f198271 100755 --- a/scripts/switch_rust_toolchain.sh +++ b/scripts/switch_rust_toolchain.sh @@ -3,6 +3,7 @@ set -eu RUST_TOOLCHAIN= +PIN_FOR_MSRV= SCRIPTS_DIR=$(dirname "$0") BASENAME=$(basename "$0") @@ -34,6 +35,7 @@ while [ $# -gt 0 ]; do ;; -m|--msrv) RUST_TOOLCHAIN="$MSRV" + PIN_FOR_MSRV=1 shift ;; -s|--stable) @@ -79,4 +81,10 @@ then else echo "Switching $RUST_TOOLCHAIN_FILE to \"$RUST_TOOLCHAIN\"" echo "$RUST_TOOLCHAIN" >| "$RUST_TOOLCHAIN_FILE" + + if [ x"$PIN_FOR_MSRV" = x1 ] + then + echo "Pinning dependencies to make build successful..." + "$SCRIPTS_DIR/msrv_pin_dependencies.sh" + fi fi diff --git a/truc/src/generator/fragment/clone.rs b/truc/src/generator/fragment/clone.rs new file mode 100644 index 0000000..260432d --- /dev/null +++ b/truc/src/generator/fragment/clone.rs @@ -0,0 +1,238 @@ +//! Clone support. + +use codegen::Scope; + +use super::{FragmentGenerator, FragmentGeneratorSpecs, RecordSpec}; +use crate::generator::{CAP, CAP_GENERIC}; + +/// Use this generator in [GeneratorConfig](crate::generator::config::GeneratorConfig) in order to +/// generate `Clone` implementations. +pub struct CloneImplGenerator; + +impl CloneImplGenerator { + fn generate_clone_impl(record_spec: &RecordSpec, scope: &mut Scope) { + let clone_impl = scope + .new_impl(&record_spec.capped_record_name) + .generic(CAP_GENERIC) + .target_generic(CAP) + .impl_trait("Clone"); + + { + let clone_fn = clone_impl.new_fn("clone").arg_ref_self().ret("Self"); + for datum in &record_spec.data { + clone_fn.line(format!( + "let {} = self.{}().clone();", + datum.name(), + datum.name() + )); + } + clone_fn.line(format!( + "Self::from({} {{", + record_spec.unpacked_record_name + )); + for datum in &record_spec.data { + clone_fn.line(format!(" {},", datum.name())); + } + clone_fn.line("})"); + } + + { + let clone_from_fn = clone_impl + .new_fn("clone_from") + .arg_mut_self() + .arg("source", "&Self"); + for datum in &record_spec.data { + clone_from_fn.line(format!( + "self.{}_mut().clone_from(source.{}());", + datum.name(), + datum.name() + )); + } + } + } +} + +impl FragmentGenerator for CloneImplGenerator { + fn generate(&self, specs: &FragmentGeneratorSpecs, scope: &mut Scope) { + let record_spec = specs.record; + + Self::generate_clone_impl(record_spec, scope); + } +} + +#[cfg(test)] +#[cfg_attr(coverage_nightly, coverage(off))] +mod tests { + use std::collections::BTreeSet; + + use maplit::btreeset; + use pretty_assertions::assert_eq; + + use super::*; + use crate::{ + generator::{config::GeneratorConfig, generate_variant, tests::assert_fragment_eq}, + record::{definition::RecordDefinitionBuilder, type_resolver::HostTypeResolver}, + }; + + #[test] + fn should_generate_empty_clone_impl() { + let mut builder = RecordDefinitionBuilder::new(HostTypeResolver); + builder.close_record_variant(); + let definition = builder.build(); + + let config = + GeneratorConfig::new([Box::new(CloneImplGenerator) as Box]); + + let mut scope = Scope::new(); + let mut type_size_assertions = BTreeSet::new(); + + generate_variant( + &definition, + definition.max_type_align(), + definition.variants().next().expect("variant"), + None, + &config, + &mut scope, + &mut type_size_assertions, + ); + + assert_fragment_eq( + r#" +impl Clone for CappedRecord0 { + fn clone(&self) -> Self { + Self::from(UnpackedRecord0 { + }) + } + + fn clone_from(&mut self, source: &Self) { + } +} +"#, + &scope.to_string(), + ); + + assert_eq!(btreeset![], type_size_assertions); + } + + #[test] + fn should_generate_clone_impl_with_data() { + let mut builder = RecordDefinitionBuilder::new(HostTypeResolver); + builder.add_datum_allow_uninit::("integer"); + builder.add_datum::("not_copy_integer"); + builder.close_record_variant(); + let definition = builder.build(); + + let config = + GeneratorConfig::new([Box::new(CloneImplGenerator) as Box]); + + let mut scope = Scope::new(); + let mut type_size_assertions = BTreeSet::new(); + + generate_variant( + &definition, + definition.max_type_align(), + definition.variants().next().expect("variant"), + None, + &config, + &mut scope, + &mut type_size_assertions, + ); + + assert_fragment_eq( + r#" +impl Clone for CappedRecord0 { + fn clone(&self) -> Self { + let integer = self.integer().clone(); + let not_copy_integer = self.not_copy_integer().clone(); + Self::from(UnpackedRecord0 { + integer, + not_copy_integer, + }) + } + + fn clone_from(&mut self, source: &Self) { + self.integer_mut().clone_from(source.integer()); + self.not_copy_integer_mut().clone_from(source.not_copy_integer()); + } +} +"#, + &scope.to_string(), + ); + + assert_eq!( + btreeset![("u32", std::mem::size_of::())], + type_size_assertions + ); + } + + #[test] + fn should_generate_next_clone_impl_with_data() { + let mut builder = RecordDefinitionBuilder::new(HostTypeResolver); + let i0 = builder.add_datum_allow_uninit::("integer0"); + let nci0 = builder.add_datum::("not_copy_integer0"); + builder.add_datum_allow_uninit::("boolean1"); + builder.close_record_variant(); + builder.remove_datum(i0); + builder.remove_datum(nci0); + builder.add_datum_allow_uninit::("integer1"); + builder.add_datum::("not_copy_integer1"); + builder.close_record_variant(); + let definition = builder.build(); + + let config = + GeneratorConfig::new([Box::new(CloneImplGenerator) as Box]); + + let mut scope = Scope::new(); + let mut type_size_assertions = BTreeSet::new(); + + let record0_spec = generate_variant( + &definition, + definition.max_type_align(), + definition.variants().next().expect("variant"), + None, + &config, + &mut scope, + &mut type_size_assertions, + ); + let mut scope = Scope::new(); + type_size_assertions.clear(); + generate_variant( + &definition, + definition.max_type_align(), + definition.variants().nth(1).expect("variant"), + Some(&record0_spec), + &config, + &mut scope, + &mut type_size_assertions, + ); + + assert_fragment_eq( + r#" +impl Clone for CappedRecord1 { + fn clone(&self) -> Self { + let boolean1 = self.boolean1().clone(); + let integer1 = self.integer1().clone(); + let not_copy_integer1 = self.not_copy_integer1().clone(); + Self::from(UnpackedRecord1 { + boolean1, + integer1, + not_copy_integer1, + }) + } + + fn clone_from(&mut self, source: &Self) { + self.boolean1_mut().clone_from(source.boolean1()); + self.integer1_mut().clone_from(source.integer1()); + self.not_copy_integer1_mut().clone_from(source.not_copy_integer1()); + } +} +"#, + &scope.to_string(), + ); + + assert_eq!( + btreeset![("u32", std::mem::size_of::())], + type_size_assertions + ); + } +} diff --git a/truc/src/generator/fragment/mod.rs b/truc/src/generator/fragment/mod.rs index 76a8a62..b958b41 100644 --- a/truc/src/generator/fragment/mod.rs +++ b/truc/src/generator/fragment/mod.rs @@ -4,6 +4,7 @@ use codegen::Scope; use crate::record::definition::{DatumDefinition, RecordVariant}; +pub mod clone; pub(crate) mod data_records; pub(crate) mod drop_impl; pub(crate) mod from_previous_record_data_records; diff --git a/truc/src/generator/mod.rs b/truc/src/generator/mod.rs index 1130c5d..9523b93 100644 --- a/truc/src/generator/mod.rs +++ b/truc/src/generator/mod.rs @@ -312,7 +312,7 @@ mod tests { use super::*; use crate::{ - generator::fragment::serde::SerdeImplGenerator, + generator::fragment::{clone::CloneImplGenerator, serde::SerdeImplGenerator}, record::{ definition::{DatumDefinitionOverride, RecordDefinitionBuilder}, type_resolver::{StaticTypeResolver, TypeResolver}, @@ -400,7 +400,8 @@ mod tests { generate( &def, &GeneratorConfig::default_with_custom_generators([ - Box::new(SerdeImplGenerator) as Box + Box::new(CloneImplGenerator) as Box, + Box::new(SerdeImplGenerator) as Box, ]), ); }