]> git.lizzy.rs Git - rust.git/commitdiff
Add a ThinBox library as a libcore test for pointer metadata APIs
authorSimon Sapin <simon.sapin@exyr.org>
Mon, 18 Jan 2021 21:24:33 +0000 (22:24 +0100)
committerSimon Sapin <simon.sapin@exyr.org>
Mon, 15 Feb 2021 13:27:51 +0000 (14:27 +0100)
library/core/tests/lib.rs
library/core/tests/ptr.rs

index b82cfe862904e4226d6d9d1d7b5e15a9dcba9af5..2d4f13167fd609672157074054183e49c55eb51d 100644 (file)
@@ -79,6 +79,7 @@
 #![feature(slice_group_by)]
 #![feature(trusted_random_access)]
 #![deny(unsafe_op_in_unsafe_fn)]
+#![cfg_attr(not(bootstrap), feature(unsize))]
 
 extern crate test;
 
index 1cdcb8c97670d4fc9fcd35d8286b86de632ad91c..224a58e3ccdb7bcc81f2c80a29155c6ca571a4b9 100644 (file)
@@ -555,3 +555,115 @@ fn from_raw_parts() {
     assert_eq!(ptr::from_raw_parts_mut(address, 5), slice_ptr.as_ptr());
     assert_eq!(NonNull::from_raw_parts(NonNull::new(address).unwrap(), 5), slice_ptr);
 }
+
+#[test]
+#[cfg(not(bootstrap))]
+fn thin_box() {
+    let foo = ThinBox::<dyn Display>::new(4);
+    assert_eq!(foo.to_string(), "4");
+    drop(foo);
+    let bar = ThinBox::<dyn Display>::new(7);
+    assert_eq!(bar.to_string(), "7");
+
+    // A slightly more interesting library that could be built on top of metadata APIs.
+    //
+    // * It could be generalized to any `T: ?Sized` (not just trait object)
+    //   if `{size,align}_of_for_meta<T: ?Sized>(T::Metadata)` are added.
+    // * Constructing a `ThinBox` without consuming and deallocating a `Box`
+    //   requires either the unstable `Unsize` marker trait,
+    //   or the unstable `unsized_locals` language feature,
+    //   or taking `&dyn T` and restricting to `T: Copy`.
+
+    use std::alloc::*;
+    use std::marker::PhantomData;
+
+    struct ThinBox<T>
+    where
+        T: ?Sized + Pointee<Metadata = DynMetadata<T>>,
+    {
+        ptr: NonNull<DynMetadata<T>>,
+        phantom: PhantomData<T>,
+    }
+
+    impl<T> ThinBox<T>
+    where
+        T: ?Sized + Pointee<Metadata = DynMetadata<T>>,
+    {
+        pub fn new<Value: std::marker::Unsize<T>>(value: Value) -> Self {
+            let unsized_: &T = &value;
+            let meta = metadata(unsized_);
+            let meta_layout = Layout::for_value(&meta);
+            let value_layout = Layout::for_value(&value);
+            let (layout, offset) = meta_layout.extend(value_layout).unwrap();
+            // `DynMetadata` is pointer-sized:
+            assert!(layout.size() > 0);
+            // If `ThinBox<T>` is generalized to any `T: ?Sized`,
+            // handle ZSTs with a dangling pointer without going through `alloc()`,
+            // like `Box<T>` does.
+            unsafe {
+                let ptr = NonNull::new(alloc(layout))
+                    .unwrap_or_else(|| handle_alloc_error(layout))
+                    .cast::<DynMetadata<T>>();
+                ptr.as_ptr().write(meta);
+                ptr.cast::<u8>().as_ptr().add(offset).cast::<Value>().write(value);
+                Self { ptr, phantom: PhantomData }
+            }
+        }
+
+        fn meta(&self) -> DynMetadata<T> {
+            unsafe { *self.ptr.as_ref() }
+        }
+
+        fn layout(&self) -> (Layout, usize) {
+            let meta = self.meta();
+            Layout::for_value(&meta).extend(meta.layout()).unwrap()
+        }
+
+        fn value_ptr(&self) -> *const T {
+            let (_, offset) = self.layout();
+            let data_ptr = unsafe { self.ptr.cast::<u8>().as_ptr().add(offset) };
+            ptr::from_raw_parts(data_ptr.cast(), self.meta())
+        }
+
+        fn value_mut_ptr(&mut self) -> *mut T {
+            let (_, offset) = self.layout();
+            // FIXME: can this line be shared with the same in `value_ptr()`
+            // without upsetting Stacked Borrows?
+            let data_ptr = unsafe { self.ptr.cast::<u8>().as_ptr().add(offset) };
+            from_raw_parts_mut(data_ptr.cast(), self.meta())
+        }
+    }
+
+    impl<T> std::ops::Deref for ThinBox<T>
+    where
+        T: ?Sized + Pointee<Metadata = DynMetadata<T>>,
+    {
+        type Target = T;
+
+        fn deref(&self) -> &T {
+            unsafe { &*self.value_ptr() }
+        }
+    }
+
+    impl<T> std::ops::DerefMut for ThinBox<T>
+    where
+        T: ?Sized + Pointee<Metadata = DynMetadata<T>>,
+    {
+        fn deref_mut(&mut self) -> &mut T {
+            unsafe { &mut *self.value_mut_ptr() }
+        }
+    }
+
+    impl<T> std::ops::Drop for ThinBox<T>
+    where
+        T: ?Sized + Pointee<Metadata = DynMetadata<T>>,
+    {
+        fn drop(&mut self) {
+            let (layout, _) = self.layout();
+            unsafe {
+                drop_in_place::<T>(&mut **self);
+                dealloc(self.ptr.cast().as_ptr(), layout);
+            }
+        }
+    }
+}