Cover zero-sized vector semantics

This commit is contained in:
2026-07-31 04:20:13 +02:00
parent 0bea5ceb1b
commit a3729f05a3
+109
View File
@@ -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<Cell<usize>>,
@@ -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::<DropZst>();
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::<TargetZst>() };
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::<SourceZst>() };
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::<AlignedZst>().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::<BorrowedZst<'_>>();
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];