From ccb8545c614ca06166898e2ba0700f48ed05944e Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 20 Oct 2024 17:25:48 +0200 Subject: [PATCH] More tests around FieldSlot --- Justfile | 1 + merde_core/src/deserialize.rs | 58 +++++++++++++++++++++++++++-- merde_core/src/deserialize/tests.rs | 35 +++++++++++++++++ 3 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 merde_core/src/deserialize/tests.rs diff --git a/Justfile b/Justfile index fc4b176..7e58f1e 100644 --- a/Justfile +++ b/Justfile @@ -28,3 +28,4 @@ check: miri: cargo +nightly miri run --example opinions -F json + cargo +nightly miri test -p merde_core diff --git a/merde_core/src/deserialize.rs b/merde_core/src/deserialize.rs index bb101cf..744323e 100644 --- a/merde_core/src/deserialize.rs +++ b/merde_core/src/deserialize.rs @@ -1,4 +1,5 @@ use std::{ + any::TypeId, borrow::Cow, collections::HashMap, future::Future, @@ -302,6 +303,44 @@ pub trait Deserializer<'s>: std::fmt::Debug { } } +mod mini_typeid { + // vendored straight from https://github.com/dtolnay/typeid — which is dual-licensed under + // MIT and Apache-2.0, just like merde. + // + // We don't really need const type_id construction or older rustc support, so this is a minimal + // take on it. + + use std::{any::TypeId, marker::PhantomData}; + + #[must_use] + #[inline(always)] + pub fn of() -> TypeId + where + T: ?Sized, + { + trait NonStaticAny { + fn get_type_id(&self) -> TypeId + where + Self: 'static; + } + + impl NonStaticAny for PhantomData { + #[inline(always)] + fn get_type_id(&self) -> TypeId + where + Self: 'static, + { + TypeId::of::() + } + } + + let phantom_data = PhantomData::; + NonStaticAny::get_type_id(unsafe { + std::mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>(&phantom_data) + }) + } +} + /// Allows filling in a field of a struct while deserializing. /// /// Enforces some type safety at runtime, by carrying lifetimes @@ -310,7 +349,8 @@ pub trait Deserializer<'s>: std::fmt::Debug { /// actually badly UB though. I should ask miri. pub struct FieldSlot<'s, 'borrow> { option: *mut Option<()>, - type_name_of_option_field: &'static str, + type_id_of_field: TypeId, + type_name_of_field: &'static str, _phantom: PhantomData<(&'s (), &'borrow mut ())>, } @@ -323,15 +363,22 @@ impl<'s, 'borrow> FieldSlot<'s, 'borrow> { option: unsafe { std::mem::transmute::<*mut Option, *mut Option<()>>(option as *mut _) }, - type_name_of_option_field: std::any::type_name::>(), + type_id_of_field: mini_typeid::of::(), + type_name_of_field: std::any::type_name::(), _phantom: PhantomData, } } /// Fill this field with a value. pub fn fill(self, value: T) { - let type_name_of_option_value = std::any::type_name::>(); - assert_eq!(self.type_name_of_option_field, type_name_of_option_value); + let type_id_of_value = mini_typeid::of::(); + assert_eq!( + self.type_id_of_field, + type_id_of_value, + "tried to assign a \x1b[33m{}\x1b[0m to a slot of type \x1b[34m{}\x1b[0m", + std::any::type_name::(), + self.type_name_of_field, + ); unsafe { let option_ptr: *mut Option = std::mem::transmute(self.option); @@ -1023,3 +1070,6 @@ impl Future for ReturnPendingOnce { } } } + +#[cfg(test)] +mod tests; diff --git a/merde_core/src/deserialize/tests.rs b/merde_core/src/deserialize/tests.rs new file mode 100644 index 0000000..ea9bd93 --- /dev/null +++ b/merde_core/src/deserialize/tests.rs @@ -0,0 +1,35 @@ +use super::FieldSlot; + +#[test] +fn test_fieldslot_no_assign() { + let mut option: Option = None; + + { + let slot = FieldSlot::new(&mut option); + // let it drop + let _ = slot; + } + + assert!(option.is_none()); +} + +#[test] +fn test_fieldslot_with_assign() { + let mut option: Option = None; + + { + let slot = FieldSlot::new(&mut option); + slot.fill::(42); + } + + assert_eq!(option, Some(42)); +} + +#[test] +#[should_panic(expected = "tried to assign")] +fn test_fieldslot_with_assign_mismatched_type() { + let mut option: Option = None; + + let slot = FieldSlot::new(&mut option); + slot.fill::(42); +}