mirror of
https://github.com/rust-lang/rust.git
synced 2025-10-23 21:54:16 +00:00
Fix CI for drop_guard fix CI fix all tidy lints fix tidy link add first batch of feedback from review Add second batch of feedback from review add third batch of feedback from review fix failing test Update library/core/src/mem/drop_guard.rs Co-authored-by: Ruby Lazuli <general@patchmixolydic.com> fix doctests Implement changes from T-Libs-API review And start tracking based on the tracking issue. fix tidy lint
844 lines
19 KiB
Rust
844 lines
19 KiB
Rust
use core::mem::*;
|
|
use core::{array, ptr};
|
|
use std::cell::Cell;
|
|
#[cfg(panic = "unwind")]
|
|
use std::rc::Rc;
|
|
|
|
#[test]
|
|
fn size_of_basic() {
|
|
assert_eq!(size_of::<u8>(), 1);
|
|
assert_eq!(size_of::<u16>(), 2);
|
|
assert_eq!(size_of::<u32>(), 4);
|
|
assert_eq!(size_of::<u64>(), 8);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(target_pointer_width = "16")]
|
|
fn size_of_16() {
|
|
assert_eq!(size_of::<usize>(), 2);
|
|
assert_eq!(size_of::<*const usize>(), 2);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(target_pointer_width = "32")]
|
|
fn size_of_32() {
|
|
assert_eq!(size_of::<usize>(), 4);
|
|
assert_eq!(size_of::<*const usize>(), 4);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(target_pointer_width = "64")]
|
|
fn size_of_64() {
|
|
assert_eq!(size_of::<usize>(), 8);
|
|
assert_eq!(size_of::<*const usize>(), 8);
|
|
}
|
|
|
|
#[test]
|
|
fn size_of_val_basic() {
|
|
assert_eq!(size_of_val(&1u8), 1);
|
|
assert_eq!(size_of_val(&1u16), 2);
|
|
assert_eq!(size_of_val(&1u32), 4);
|
|
assert_eq!(size_of_val(&1u64), 8);
|
|
}
|
|
|
|
#[test]
|
|
fn align_of_basic() {
|
|
assert_eq!(align_of::<u8>(), 1);
|
|
assert_eq!(align_of::<u16>(), 2);
|
|
assert_eq!(align_of::<u32>(), 4);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(target_pointer_width = "16")]
|
|
fn align_of_16() {
|
|
assert_eq!(align_of::<usize>(), 2);
|
|
assert_eq!(align_of::<*const usize>(), 2);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(target_pointer_width = "32")]
|
|
fn align_of_32() {
|
|
assert_eq!(align_of::<usize>(), 4);
|
|
assert_eq!(align_of::<*const usize>(), 4);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(target_pointer_width = "64")]
|
|
fn align_of_64() {
|
|
assert_eq!(align_of::<usize>(), 8);
|
|
assert_eq!(align_of::<*const usize>(), 8);
|
|
}
|
|
|
|
#[test]
|
|
fn align_of_val_basic() {
|
|
assert_eq!(align_of_val(&1u8), 1);
|
|
assert_eq!(align_of_val(&1u16), 2);
|
|
assert_eq!(align_of_val(&1u32), 4);
|
|
}
|
|
|
|
#[test]
|
|
fn align_of_val_raw_packed() {
|
|
#[repr(C, packed)]
|
|
struct B {
|
|
f: [u32],
|
|
}
|
|
let storage = [0u8; 4];
|
|
let b: *const B = ptr::from_raw_parts(storage.as_ptr(), 1);
|
|
assert_eq!(unsafe { align_of_val_raw(b) }, 1);
|
|
|
|
const ALIGN_OF_VAL_RAW: usize = {
|
|
let storage = [0u8; 4];
|
|
let b: *const B = ptr::from_raw_parts(storage.as_ptr(), 1);
|
|
unsafe { align_of_val_raw(b) }
|
|
};
|
|
assert_eq!(ALIGN_OF_VAL_RAW, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_swap() {
|
|
let mut x = 31337;
|
|
let mut y = 42;
|
|
swap(&mut x, &mut y);
|
|
assert_eq!(x, 42);
|
|
assert_eq!(y, 31337);
|
|
}
|
|
|
|
#[test]
|
|
fn test_replace() {
|
|
let mut x = Some("test".to_string());
|
|
let y = replace(&mut x, None);
|
|
assert!(x.is_none());
|
|
assert!(y.is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn test_transmute_copy() {
|
|
assert_eq!(1, unsafe { transmute_copy(&1) });
|
|
}
|
|
|
|
#[test]
|
|
fn test_transmute_copy_shrink() {
|
|
assert_eq!(0_u8, unsafe { transmute_copy(&0_u64) });
|
|
}
|
|
|
|
#[test]
|
|
fn test_transmute_copy_unaligned() {
|
|
#[repr(C)]
|
|
#[derive(Default)]
|
|
struct Unaligned {
|
|
a: u8,
|
|
b: [u8; 8],
|
|
}
|
|
|
|
let u = Unaligned::default();
|
|
assert_eq!(0_u64, unsafe { transmute_copy(&u.b) });
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(panic = "unwind")]
|
|
fn test_transmute_copy_grow_panics() {
|
|
use std::panic;
|
|
|
|
let err = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe {
|
|
let _unused: u64 = transmute_copy(&1_u8);
|
|
}));
|
|
|
|
match err {
|
|
Ok(_) => unreachable!(),
|
|
Err(payload) => {
|
|
payload
|
|
.downcast::<&'static str>()
|
|
.and_then(|s| {
|
|
if *s == "cannot transmute_copy if Dst is larger than Src" {
|
|
Ok(s)
|
|
} else {
|
|
Err(s)
|
|
}
|
|
})
|
|
.unwrap_or_else(|p| panic::resume_unwind(p));
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[allow(dead_code)]
|
|
fn test_discriminant_send_sync() {
|
|
enum Regular {
|
|
A,
|
|
B(i32),
|
|
}
|
|
enum NotSendSync {
|
|
A(*const i32),
|
|
}
|
|
|
|
fn is_send_sync<T: Send + Sync>() {}
|
|
|
|
is_send_sync::<Discriminant<Regular>>();
|
|
is_send_sync::<Discriminant<NotSendSync>>();
|
|
}
|
|
|
|
#[test]
|
|
fn assume_init_good() {
|
|
const TRUE: bool = unsafe { MaybeUninit::<bool>::new(true).assume_init() };
|
|
|
|
assert!(TRUE);
|
|
}
|
|
|
|
#[test]
|
|
fn uninit_array_assume_init() {
|
|
let mut array = [MaybeUninit::<i16>::uninit(); 5];
|
|
array[0].write(3);
|
|
array[1].write(1);
|
|
array[2].write(4);
|
|
array[3].write(1);
|
|
array[4].write(5);
|
|
|
|
let array = unsafe { array.transpose().assume_init() };
|
|
|
|
assert_eq!(array, [3, 1, 4, 1, 5]);
|
|
|
|
let [] = unsafe { [MaybeUninit::<!>::uninit(); 0].transpose().assume_init() };
|
|
}
|
|
|
|
#[test]
|
|
fn uninit_write_copy_of_slice() {
|
|
let mut dst = [MaybeUninit::new(255); 64];
|
|
let src = [0; 64];
|
|
|
|
assert_eq!(dst.write_copy_of_slice(&src), &src);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic(expected = "source slice length (32) does not match destination slice length (64)")]
|
|
fn uninit_write_copy_of_slice_panic_lt() {
|
|
let mut dst = [MaybeUninit::uninit(); 64];
|
|
let src = [0; 32];
|
|
|
|
dst.write_copy_of_slice(&src);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic(expected = "source slice length (128) does not match destination slice length (64)")]
|
|
fn uninit_write_copy_of_slice_panic_gt() {
|
|
let mut dst = [MaybeUninit::uninit(); 64];
|
|
let src = [0; 128];
|
|
|
|
dst.write_copy_of_slice(&src);
|
|
}
|
|
|
|
#[test]
|
|
fn uninit_write_clone_of_slice() {
|
|
let mut dst = [MaybeUninit::new(255); 64];
|
|
let src = [0; 64];
|
|
|
|
assert_eq!(dst.write_clone_of_slice(&src), &src);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic(expected = "destination and source slices have different lengths")]
|
|
fn uninit_write_clone_of_slice_panic_lt() {
|
|
let mut dst = [MaybeUninit::uninit(); 64];
|
|
let src = [0; 32];
|
|
|
|
dst.write_clone_of_slice(&src);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic(expected = "destination and source slices have different lengths")]
|
|
fn uninit_write_clone_of_slice_panic_gt() {
|
|
let mut dst = [MaybeUninit::uninit(); 64];
|
|
let src = [0; 128];
|
|
|
|
dst.write_clone_of_slice(&src);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(panic = "unwind")]
|
|
fn uninit_write_clone_of_slice_mid_panic() {
|
|
use std::panic;
|
|
|
|
enum IncrementOrPanic {
|
|
Increment(Rc<()>),
|
|
ExpectedPanic,
|
|
UnexpectedPanic,
|
|
}
|
|
|
|
impl Clone for IncrementOrPanic {
|
|
fn clone(&self) -> Self {
|
|
match self {
|
|
Self::Increment(rc) => Self::Increment(rc.clone()),
|
|
Self::ExpectedPanic => panic!("expected panic on clone"),
|
|
Self::UnexpectedPanic => panic!("unexpected panic on clone"),
|
|
}
|
|
}
|
|
}
|
|
|
|
let rc = Rc::new(());
|
|
|
|
let mut dst = [
|
|
MaybeUninit::uninit(),
|
|
MaybeUninit::uninit(),
|
|
MaybeUninit::uninit(),
|
|
MaybeUninit::uninit(),
|
|
];
|
|
|
|
let src = [
|
|
IncrementOrPanic::Increment(rc.clone()),
|
|
IncrementOrPanic::Increment(rc.clone()),
|
|
IncrementOrPanic::ExpectedPanic,
|
|
IncrementOrPanic::UnexpectedPanic,
|
|
];
|
|
|
|
let err = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
|
dst.write_clone_of_slice(&src);
|
|
}));
|
|
|
|
drop(src);
|
|
|
|
match err {
|
|
Ok(_) => unreachable!(),
|
|
Err(payload) => {
|
|
payload
|
|
.downcast::<&'static str>()
|
|
.and_then(|s| if *s == "expected panic on clone" { Ok(s) } else { Err(s) })
|
|
.unwrap_or_else(|p| panic::resume_unwind(p));
|
|
|
|
assert_eq!(Rc::strong_count(&rc), 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct Bomb;
|
|
|
|
impl Drop for Bomb {
|
|
fn drop(&mut self) {
|
|
panic!("dropped a bomb! kaboom!")
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn uninit_write_clone_of_slice_no_drop() {
|
|
let mut dst = [MaybeUninit::uninit()];
|
|
let src = [Bomb];
|
|
|
|
dst.write_clone_of_slice(&src);
|
|
|
|
forget(src);
|
|
}
|
|
|
|
#[test]
|
|
fn uninit_write_filled() {
|
|
let mut dst = [MaybeUninit::new(255); 64];
|
|
let expect = [0; 64];
|
|
|
|
assert_eq!(dst.write_filled(0), &expect);
|
|
}
|
|
|
|
#[cfg(panic = "unwind")]
|
|
struct CloneUntilPanic {
|
|
limit: usize,
|
|
rc: Rc<()>,
|
|
}
|
|
|
|
#[cfg(panic = "unwind")]
|
|
impl Clone for CloneUntilPanic {
|
|
fn clone(&self) -> Self {
|
|
if Rc::strong_count(&self.rc) >= self.limit {
|
|
panic!("expected panic on clone");
|
|
}
|
|
Self { limit: self.limit, rc: self.rc.clone() }
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(panic = "unwind")]
|
|
fn uninit_write_filled_panic_drop() {
|
|
use std::panic;
|
|
|
|
let rc = Rc::new(());
|
|
|
|
let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()];
|
|
|
|
let src = CloneUntilPanic { limit: 3, rc: rc.clone() };
|
|
let err = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
|
dst.write_filled(src);
|
|
}));
|
|
|
|
match err {
|
|
Ok(_) => unreachable!(),
|
|
Err(payload) => {
|
|
payload
|
|
.downcast::<&'static str>()
|
|
.and_then(|s| if *s == "expected panic on clone" { Ok(s) } else { Err(s) })
|
|
.unwrap_or_else(|p| panic::resume_unwind(p));
|
|
assert_eq!(Rc::strong_count(&rc), 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(panic = "unwind")]
|
|
fn uninit_write_filled_no_drop_clones() {
|
|
let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()];
|
|
|
|
dst.write_filled(Bomb);
|
|
}
|
|
|
|
#[test]
|
|
fn uninit_write_with() {
|
|
let mut dst = [MaybeUninit::new(255usize); 64];
|
|
let expect = array::from_fn::<usize, 64, _>(|idx| idx);
|
|
|
|
assert_eq!(dst.write_with(|idx| idx), &expect);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(panic = "unwind")]
|
|
fn uninit_write_with_mid_panic() {
|
|
use std::panic;
|
|
|
|
let rc = Rc::new(());
|
|
|
|
let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()];
|
|
|
|
let src = CloneUntilPanic { limit: 3, rc: rc.clone() };
|
|
let err = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
|
dst.write_with(|_| src.clone());
|
|
}));
|
|
|
|
drop(src);
|
|
|
|
match err {
|
|
Ok(_) => unreachable!(),
|
|
Err(payload) => {
|
|
payload
|
|
.downcast::<&'static str>()
|
|
.and_then(|s| if *s == "expected panic on clone" { Ok(s) } else { Err(s) })
|
|
.unwrap_or_else(|p| panic::resume_unwind(p));
|
|
|
|
assert_eq!(Rc::strong_count(&rc), 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(panic = "unwind")]
|
|
fn uninit_write_with_no_drop() {
|
|
let mut dst = [MaybeUninit::uninit()];
|
|
let src = Bomb;
|
|
|
|
dst.write_with(|_| src.clone());
|
|
|
|
forget(src);
|
|
}
|
|
|
|
#[test]
|
|
fn uninit_write_iter() {
|
|
let mut dst = [MaybeUninit::new(255); 64];
|
|
let src = [0; 64];
|
|
|
|
let (initted, remainder) = dst.write_iter(src.into_iter());
|
|
assert_eq!(initted, &src);
|
|
assert_eq!(remainder.len(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn uninit_write_iter_partial() {
|
|
let mut dst = [MaybeUninit::new(255); 64];
|
|
let src = [0; 48];
|
|
|
|
let (initted, remainder) = dst.write_iter(src.into_iter());
|
|
assert_eq!(initted, &src);
|
|
assert_eq!(remainder.len(), 16);
|
|
}
|
|
|
|
#[test]
|
|
fn uninit_write_iter_overfill() {
|
|
let mut dst = [MaybeUninit::new(255); 64];
|
|
let src = [0; 72];
|
|
|
|
let (initted, remainder) = dst.write_iter(src.into_iter());
|
|
assert_eq!(initted, &src[0..64]);
|
|
assert_eq!(remainder.len(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn uninit_write_iter_empty() {
|
|
let mut dst = [MaybeUninit::new(255); 64];
|
|
let src = [0; 0];
|
|
|
|
let (initted, remainder) = dst.write_iter(src.into_iter());
|
|
assert_eq!(initted, &src[0..0]);
|
|
assert_eq!(remainder.len(), 64);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(panic = "unwind")]
|
|
fn uninit_write_iter_mid_panic() {
|
|
use std::panic;
|
|
|
|
struct IterUntilPanic {
|
|
limit: usize,
|
|
rc: Rc<()>,
|
|
}
|
|
|
|
impl Iterator for IterUntilPanic {
|
|
type Item = Rc<()>;
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if Rc::strong_count(&self.rc) >= self.limit {
|
|
panic!("expected panic on next");
|
|
}
|
|
Some(self.rc.clone())
|
|
}
|
|
}
|
|
|
|
let rc = Rc::new(());
|
|
|
|
let mut dst = [
|
|
MaybeUninit::uninit(),
|
|
MaybeUninit::uninit(),
|
|
MaybeUninit::uninit(),
|
|
MaybeUninit::uninit(),
|
|
];
|
|
|
|
let src = IterUntilPanic { limit: 3, rc: rc.clone() };
|
|
|
|
let err = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
|
dst.write_iter(src);
|
|
}));
|
|
|
|
match err {
|
|
Ok(_) => unreachable!(),
|
|
Err(payload) => {
|
|
payload
|
|
.downcast::<&'static str>()
|
|
.and_then(|s| if *s == "expected panic on next" { Ok(s) } else { Err(s) })
|
|
.unwrap_or_else(|p| panic::resume_unwind(p));
|
|
|
|
assert_eq!(Rc::strong_count(&rc), 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(panic = "unwind")]
|
|
fn uninit_write_iter_no_drop() {
|
|
let mut dst = [MaybeUninit::uninit()];
|
|
let src = [Bomb];
|
|
|
|
dst.write_iter(src.iter());
|
|
|
|
forget(src);
|
|
}
|
|
|
|
#[test]
|
|
fn uninit_const_assume_init_read() {
|
|
const FOO: u32 = unsafe { MaybeUninit::new(42).assume_init_read() };
|
|
assert_eq!(FOO, 42);
|
|
}
|
|
|
|
#[test]
|
|
fn const_maybe_uninit() {
|
|
use std::ptr;
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
struct Foo {
|
|
x: u8,
|
|
y: u8,
|
|
}
|
|
|
|
const FIELD_BY_FIELD: Foo = unsafe {
|
|
let mut val = MaybeUninit::uninit();
|
|
init_y(&mut val); // order shouldn't matter
|
|
init_x(&mut val);
|
|
val.assume_init()
|
|
};
|
|
|
|
const fn init_x(foo: &mut MaybeUninit<Foo>) {
|
|
unsafe {
|
|
*ptr::addr_of_mut!((*foo.as_mut_ptr()).x) = 1;
|
|
}
|
|
}
|
|
|
|
const fn init_y(foo: &mut MaybeUninit<Foo>) {
|
|
unsafe {
|
|
*ptr::addr_of_mut!((*foo.as_mut_ptr()).y) = 2;
|
|
}
|
|
}
|
|
|
|
assert_eq!(FIELD_BY_FIELD, Foo { x: 1, y: 2 });
|
|
}
|
|
|
|
#[test]
|
|
fn offset_of() {
|
|
#[repr(C)]
|
|
struct Foo {
|
|
x: u8,
|
|
y: u16,
|
|
z: Bar,
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct Bar(u8, u8);
|
|
|
|
assert_eq!(offset_of!(Foo, x), 0);
|
|
assert_eq!(offset_of!(Foo, y), 2);
|
|
assert_eq!(offset_of!(Foo, z.0), 4);
|
|
assert_eq!(offset_of!(Foo, z.1), 5);
|
|
|
|
// Layout of tuples is unstable
|
|
assert!(offset_of!((u8, u16), 0) <= size_of::<(u8, u16)>() - 1);
|
|
assert!(offset_of!((u8, u16), 1) <= size_of::<(u8, u16)>() - 2);
|
|
|
|
#[repr(C)]
|
|
struct Generic<T> {
|
|
x: u8,
|
|
y: u32,
|
|
z: T,
|
|
}
|
|
|
|
trait Trait {}
|
|
|
|
// Ensure that this type of generics works
|
|
fn offs_of_z<T>() -> usize {
|
|
offset_of!(Generic<T>, z)
|
|
}
|
|
|
|
assert_eq!(offset_of!(Generic<u8>, z), 8);
|
|
assert_eq!(offs_of_z::<u8>(), 8);
|
|
|
|
// Ensure that it works with the implicit lifetime in `Box<dyn Trait + '_>`.
|
|
assert_eq!(offset_of!(Generic<Box<dyn Trait>>, z), 8);
|
|
}
|
|
|
|
#[test]
|
|
fn offset_of_union() {
|
|
#[repr(C)]
|
|
union Foo {
|
|
x: u8,
|
|
y: u16,
|
|
z: Bar,
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Copy, Clone)]
|
|
struct Bar(u8, u8);
|
|
|
|
assert_eq!(offset_of!(Foo, x), 0);
|
|
assert_eq!(offset_of!(Foo, y), 0);
|
|
assert_eq!(offset_of!(Foo, z.0), 0);
|
|
assert_eq!(offset_of!(Foo, z.1), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn offset_of_dst() {
|
|
#[repr(C)]
|
|
struct Alpha {
|
|
x: u8,
|
|
y: u16,
|
|
z: [u8],
|
|
}
|
|
|
|
trait Trait {}
|
|
|
|
#[repr(C)]
|
|
struct Beta {
|
|
x: u8,
|
|
y: u16,
|
|
z: dyn Trait,
|
|
}
|
|
|
|
unsafe extern "C" {
|
|
type Extern;
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct Gamma {
|
|
x: u8,
|
|
y: u16,
|
|
z: Extern,
|
|
}
|
|
|
|
assert_eq!(offset_of!(Alpha, x), 0);
|
|
assert_eq!(offset_of!(Alpha, y), 2);
|
|
|
|
assert_eq!(offset_of!(Beta, x), 0);
|
|
assert_eq!(offset_of!(Beta, y), 2);
|
|
|
|
assert_eq!(offset_of!(Gamma, x), 0);
|
|
assert_eq!(offset_of!(Gamma, y), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn offset_of_packed() {
|
|
#[repr(C, packed)]
|
|
struct Foo {
|
|
x: u8,
|
|
y: u16,
|
|
}
|
|
|
|
assert_eq!(offset_of!(Foo, x), 0);
|
|
assert_eq!(offset_of!(Foo, y), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn offset_of_projection() {
|
|
#[repr(C)]
|
|
struct Foo {
|
|
x: u8,
|
|
y: u16,
|
|
}
|
|
|
|
trait Projector {
|
|
type Type;
|
|
}
|
|
|
|
impl Projector for () {
|
|
type Type = Foo;
|
|
}
|
|
|
|
assert_eq!(offset_of!(<() as Projector>::Type, x), 0);
|
|
assert_eq!(offset_of!(<() as Projector>::Type, y), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn offset_of_alias() {
|
|
#[repr(C)]
|
|
struct Foo {
|
|
x: u8,
|
|
y: u16,
|
|
}
|
|
|
|
type Bar = Foo;
|
|
|
|
assert_eq!(offset_of!(Bar, x), 0);
|
|
assert_eq!(offset_of!(Bar, y), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn const_offset_of() {
|
|
#[repr(C)]
|
|
struct Foo {
|
|
x: u8,
|
|
y: u16,
|
|
}
|
|
|
|
const X_OFFSET: usize = offset_of!(Foo, x);
|
|
const Y_OFFSET: usize = offset_of!(Foo, y);
|
|
|
|
assert_eq!(X_OFFSET, 0);
|
|
assert_eq!(Y_OFFSET, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn offset_of_without_const_promotion() {
|
|
#[repr(C)]
|
|
struct Foo<SuppressConstPromotion> {
|
|
x: u8,
|
|
y: u16,
|
|
_scp: SuppressConstPromotion,
|
|
}
|
|
|
|
// Normally, offset_of is always const promoted.
|
|
// The generic parameter prevents this from happening.
|
|
// This is needed to test the codegen impl of offset_of
|
|
fn inner<SuppressConstPromotion>() {
|
|
assert_eq!(offset_of!(Foo<SuppressConstPromotion>, x), 0);
|
|
assert_eq!(offset_of!(Foo<SuppressConstPromotion>, y), 2);
|
|
}
|
|
|
|
inner::<()>();
|
|
}
|
|
|
|
#[test]
|
|
fn offset_of_addr() {
|
|
#[repr(C)]
|
|
struct Foo {
|
|
x: u8,
|
|
y: u16,
|
|
z: Bar,
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct Bar(u8, u8);
|
|
|
|
let base = Foo { x: 0, y: 0, z: Bar(0, 0) };
|
|
|
|
assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, x), ptr::addr_of!(base.x).addr());
|
|
assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, y), ptr::addr_of!(base.y).addr());
|
|
assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, z.0), ptr::addr_of!(base.z.0).addr());
|
|
assert_eq!(ptr::addr_of!(base).addr() + offset_of!(Foo, z.1), ptr::addr_of!(base.z.1).addr());
|
|
}
|
|
|
|
#[test]
|
|
fn const_maybe_uninit_zeroed() {
|
|
// Sanity check for `MaybeUninit::zeroed` in a realistic const situation (plugin array term)
|
|
|
|
// It is crucial that this type has no padding!
|
|
#[repr(C)]
|
|
struct Foo {
|
|
a: Option<&'static u8>,
|
|
b: Bar,
|
|
c: f32,
|
|
_pad: u32,
|
|
d: *const u8,
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct Bar(usize);
|
|
|
|
struct FooPtr(*const Foo);
|
|
unsafe impl Sync for FooPtr {}
|
|
|
|
static UNINIT: FooPtr = FooPtr([unsafe { MaybeUninit::zeroed().assume_init() }].as_ptr());
|
|
const SIZE: usize = size_of::<Foo>();
|
|
|
|
assert_eq!(unsafe { (*UNINIT.0.cast::<[[u8; SIZE]; 1]>())[0] }, [0u8; SIZE]);
|
|
}
|
|
|
|
#[test]
|
|
fn drop_guards_only_dropped_by_closure_when_run() {
|
|
let value_drops = Cell::new(0);
|
|
let value = DropGuard::new((), |()| value_drops.set(1 + value_drops.get()));
|
|
let closure_drops = Cell::new(0);
|
|
let guard = DropGuard::new(value, |_| closure_drops.set(1 + closure_drops.get()));
|
|
assert_eq!(value_drops.get(), 0);
|
|
assert_eq!(closure_drops.get(), 0);
|
|
drop(guard);
|
|
assert_eq!(value_drops.get(), 1);
|
|
assert_eq!(closure_drops.get(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn drop_guard_into_inner() {
|
|
let dropped = Cell::new(false);
|
|
let value = DropGuard::new(42, |_| dropped.set(true));
|
|
let guard = DropGuard::new(value, |_| dropped.set(true));
|
|
let inner = DropGuard::into_inner(guard);
|
|
assert_eq!(dropped.get(), false);
|
|
assert_eq!(*inner, 42);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(panic = "unwind")]
|
|
fn drop_guard_always_drops_value_if_closure_drop_unwinds() {
|
|
// Create a value with a destructor, which we will validate ran successfully.
|
|
let mut value_was_dropped = false;
|
|
let value_with_tracked_destruction = DropGuard::new((), |_| value_was_dropped = true);
|
|
|
|
// Create a closure that will begin unwinding when dropped.
|
|
let drop_bomb = DropGuard::new((), |_| panic!());
|
|
let closure_that_panics_on_drop = move |_| {
|
|
let _drop_bomb = drop_bomb;
|
|
};
|
|
|
|
// This will run the closure, which will panic when dropped. This should
|
|
// run the destructor of the value we passed, which we validate.
|
|
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
|
let guard = DropGuard::new(value_with_tracked_destruction, closure_that_panics_on_drop);
|
|
DropGuard::into_inner(guard);
|
|
}));
|
|
assert!(value_was_dropped);
|
|
}
|