152 lines
3.4 KiB
Rust
152 lines
3.4 KiB
Rust
#![feature(unsize, coerce_unsized)]
|
|
|
|
use std::collections::hash_map::DefaultHasher;
|
|
use std::hash::Hash;
|
|
use std::ptr;
|
|
|
|
fn test_basic() {
|
|
#[repr(packed)]
|
|
struct S {
|
|
fill: u8,
|
|
a: i32,
|
|
b: i64,
|
|
}
|
|
|
|
#[repr(packed)]
|
|
#[allow(dead_code)]
|
|
struct Test1<'a> {
|
|
x: u8,
|
|
other: &'a u32,
|
|
}
|
|
|
|
#[repr(packed)]
|
|
#[allow(dead_code)]
|
|
struct Test2<'a> {
|
|
x: u8,
|
|
other: &'a Test1<'a>,
|
|
}
|
|
|
|
fn test(t: Test2) {
|
|
let x = *t.other.other;
|
|
assert_eq!(x, 42);
|
|
}
|
|
|
|
let mut x = S { fill: 0, a: 42, b: 99 };
|
|
let a = x.a;
|
|
let b = x.b;
|
|
assert_eq!(a, 42);
|
|
assert_eq!(b, 99);
|
|
assert_eq!(&x.fill, &0); // `fill` just requirs 1-byte-align, so this is fine
|
|
// can't do `assert_eq!(x.a, 42)`, because `assert_eq!` takes a reference
|
|
assert_eq!({ x.a }, 42);
|
|
assert_eq!({ x.b }, 99);
|
|
// but we *can* take a raw pointer!
|
|
assert_eq!(unsafe { ptr::addr_of!(x.a).read_unaligned() }, 42);
|
|
assert_eq!(unsafe { ptr::addr_of!(x.b).read_unaligned() }, 99);
|
|
|
|
x.b = 77;
|
|
assert_eq!({ x.b }, 77);
|
|
|
|
test(Test2 { x: 0, other: &Test1 { x: 0, other: &42 } });
|
|
}
|
|
|
|
fn test_unsizing() {
|
|
#[repr(packed)]
|
|
#[allow(dead_code)]
|
|
struct UnalignedPtr<'a, T: ?Sized>
|
|
where
|
|
T: 'a,
|
|
{
|
|
data: &'a T,
|
|
}
|
|
|
|
impl<'a, T, U> std::ops::CoerceUnsized<UnalignedPtr<'a, U>> for UnalignedPtr<'a, T>
|
|
where
|
|
T: std::marker::Unsize<U> + ?Sized,
|
|
U: ?Sized,
|
|
{
|
|
}
|
|
|
|
let arr = [1, 2, 3];
|
|
let arr_unaligned: UnalignedPtr<[i32; 3]> = UnalignedPtr { data: &arr };
|
|
let arr_unaligned: UnalignedPtr<[i32]> = arr_unaligned;
|
|
let _unused = &arr_unaligned; // forcing an allocation, which could also yield "unaligned write"-errors
|
|
}
|
|
|
|
fn test_drop() {
|
|
struct Wrap(u32);
|
|
impl Drop for Wrap {
|
|
fn drop(&mut self) {
|
|
// Do an (aligned) load
|
|
let _test = self.0;
|
|
// For the fun of it, test alignment
|
|
assert_eq!(&self.0 as *const _ as usize % std::mem::align_of::<u32>(), 0);
|
|
}
|
|
}
|
|
|
|
#[repr(packed, C)]
|
|
struct Packed<T> {
|
|
f1: u8, // this should move the second field to something not very aligned
|
|
f2: T,
|
|
}
|
|
|
|
let p = Packed { f1: 42, f2: Wrap(23) };
|
|
drop(p);
|
|
}
|
|
|
|
fn test_inner_packed() {
|
|
// Even if just the inner struct is packed, accesses to the outer field can get unaligned.
|
|
// Make sure that works.
|
|
#[repr(packed)]
|
|
#[derive(Clone, Copy)]
|
|
struct Inner(u32);
|
|
|
|
#[derive(Clone, Copy)]
|
|
struct Outer(u8, Inner);
|
|
|
|
let o = Outer(0, Inner(42));
|
|
let _x = o.1;
|
|
let _y = (o.1).0;
|
|
let _o2 = o.clone();
|
|
}
|
|
|
|
fn test_static() {
|
|
#[repr(packed)]
|
|
struct Foo {
|
|
i: i32,
|
|
}
|
|
|
|
static FOO: Foo = Foo { i: 42 };
|
|
|
|
assert_eq!({ FOO.i }, 42);
|
|
}
|
|
|
|
fn test_derive() {
|
|
#[repr(packed)]
|
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
|
struct P {
|
|
a: usize,
|
|
b: u8,
|
|
c: usize,
|
|
}
|
|
|
|
let x = P { a: 1usize, b: 2u8, c: 3usize };
|
|
let y = P { a: 1usize, b: 2u8, c: 4usize };
|
|
|
|
let _clone = x.clone();
|
|
assert!(x != y);
|
|
assert_eq!(x.partial_cmp(&y).unwrap(), x.cmp(&y));
|
|
x.hash(&mut DefaultHasher::new());
|
|
P::default();
|
|
format!("{:?}", x);
|
|
}
|
|
|
|
fn main() {
|
|
test_basic();
|
|
test_unsizing();
|
|
test_drop();
|
|
test_inner_packed();
|
|
test_static();
|
|
test_derive();
|
|
}
|