From a3729f05a39b81cc48e348014a85862376bc12be Mon Sep 17 00:00:00 2001 From: soruh Date: Fri, 31 Jul 2026 04:20:13 +0200 Subject: [PATCH] Cover zero-sized vector semantics --- src/tests.rs | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/src/tests.rs b/src/tests.rs index ae50fc9..1d7d039 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -6,9 +6,11 @@ use super::*; use std::alloc::Layout; use std::cell::Cell; +use std::marker::PhantomData; use std::panic; use std::rc::Rc; use std::sync::Arc; +use std::sync::atomic::{AtomicUsize, Ordering}; struct DropTracker { counter: Rc>, @@ -723,6 +725,113 @@ fn test_erased_layout_and_capacity_bytes() { ); } +#[test] +fn test_ordinary_zst_uses_logical_max_capacity() { + let mut erased = TypeErasedVec::new(vec![(), (), ()]); + + assert_erased_state(&erased, 3, Layout::new::<()>()); + assert_eq!(erased.capacity(), usize::MAX); + assert_eq!(erased.capacity_bytes(), 0); + + erased.clear(); + assert!(erased.is_empty()); + assert_eq!(erased.capacity(), usize::MAX); + + let mut guard = erased.as_type::<()>(); + guard.push(()); + assert_eq!(guard.length(), 1); + assert_eq!(guard.capacity(), usize::MAX); + + let restored = guard.take(); + assert_eq!(restored, vec![()]); + assert_eq!(restored.capacity(), usize::MAX); +} + +#[test] +fn test_zst_destructors_run_exactly_once() { + static DROP_COUNT: AtomicUsize = AtomicUsize::new(0); + + struct DropZst; + + impl Drop for DropZst { + fn drop(&mut self) { + DROP_COUNT.fetch_add(1, Ordering::Relaxed); + } + } + + DROP_COUNT.store(0, Ordering::Relaxed); + let mut erased = TypeErasedVec::new(vec![DropZst, DropZst, DropZst]); + + assert_eq!(erased.capacity(), usize::MAX); + erased.clear(); + assert_eq!(DROP_COUNT.load(Ordering::Relaxed), 3); + + let mut guard = erased.as_type::(); + guard.push(DropZst); + guard.push(DropZst); + drop(erased); + assert_eq!(DROP_COUNT.load(Ordering::Relaxed), 5); +} + +#[test] +fn test_compatible_zst_retained_cast() { + struct SourceZst; + struct TargetZst; + + let mut erased = TypeErasedVec::new(vec![SourceZst, SourceZst]); + + { + // SAFETY: Both unit structs accept every value and have identical + // zero-sized, align-one layouts. + let mut guard = unsafe { erased.cast_type::() }; + assert_eq!(guard.length(), 2); + assert_eq!(guard.capacity(), usize::MAX); + guard.push(TargetZst); + } + + // SAFETY: Both ZST types accept every possible value and share a layout. + let restored = unsafe { erased.cast_into_vec::() }; + assert_eq!(restored.len(), 3); + assert_eq!(restored.capacity(), usize::MAX); +} + +#[test] +fn test_aligned_zst_layout_mismatch_is_non_destructive() { + #[repr(align(8))] + struct AlignedZst; + + let mut erased = TypeErasedVec::new(vec![(), ()]); + + assert!(erased.try_as_type::().is_none()); + assert_erased_state(&erased, 2, Layout::new::<()>()); + assert_eq!(erased.capacity(), usize::MAX); +} + +#[test] +fn test_scoped_borrowed_zst_behavior() { + struct BorrowedZst<'a>(PhantomData<&'a u8>); + + impl<'a> BorrowedZst<'a> { + fn new(_: &'a u8) -> Self { + Self(PhantomData) + } + } + + let local = 42_u8; + let mut erased = TypeErasedVec::new(Vec::<()>::new()); + + { + let mut guard = erased.as_type_scoped::>(); + guard.push(BorrowedZst::new(&local)); + assert_eq!(guard.length(), 1); + assert_eq!(guard.capacity(), usize::MAX); + } + + erased.clear(); + assert!(erased.is_empty()); + assert_eq!(erased.capacity(), usize::MAX); +} + #[test] fn test_unchecked_methods() { let vec = vec![123_u32];