From 8ef346d6e40f055b218fc01e93415111d2acc6b4 Mon Sep 17 00:00:00 2001 From: David Koloski Date: Sat, 10 Aug 2024 20:13:43 -0400 Subject: [PATCH] Update everything and add CI --- .cargo/config.toml | 4 + .github/FUNDING.yml | 1 + .github/workflows/ci.yml | 128 ++++++++++ Cargo.toml | 14 +- LICENSE | 7 + README.md | 31 ++- munge/Cargo.toml | 7 +- munge/LICENSE | 7 + munge/{crates-io.md => example.md} | 249 +++++++++---------- munge/src/__macro.rs | 2 +- munge/src/impls.rs | 60 ++--- munge/src/internal.rs | 2 +- munge/src/lib.rs | 158 ++---------- munge/tests/tests.rs | 3 +- munge/tests/ui/double_borrow.rs | 6 +- munge/tests/ui/double_borrow.stderr | 10 +- munge/tests/ui/move_while_borrowed.rs | 6 +- munge/tests/ui/move_while_borrowed.stderr | 14 +- munge/tests/ui/packed.rs | 6 +- munge/tests/ui/packed.stderr | 14 +- munge/tests/ui/rest_patterns_ref_only.rs | 6 +- munge/tests/ui/rest_patterns_ref_only.stderr | 16 +- munge_macro/Cargo.toml | 5 +- munge_macro/LICENSE | 7 + munge_macro/src/lib.rs | 23 +- rustfmt.toml | 16 +- 26 files changed, 414 insertions(+), 388 deletions(-) create mode 100644 .cargo/config.toml create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/ci.yml create mode 100644 LICENSE create mode 100644 munge/LICENSE rename munge/{crates-io.md => example.md} (77%) create mode 100644 munge_macro/LICENSE diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..8af940d --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,4 @@ +[build] +rustdocflags = ["--cfg", "docsrs"] + +[env] diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..a3437e5 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: djkoloski diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..cfb86ac --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,128 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + schedule: + - cron: "0 10 * * *" + +permissions: + contents: read + +env: + RUSTFLAGS: -Dwarnings + +jobs: + toolchain: + name: Toolchain / ${{ matrix.toolchain }} ${{ matrix.opt }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + toolchain: + - stable + - beta + - nightly + opt: + - '' + - --release + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.toolchain }} + - run: cargo test --verbose ${{ matrix.opt }} + + miri: + name: Miri / ${{ matrix.opt }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + opt: + - '' + - --release + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@miri + - run: cargo miri setup + - run: cargo miri test ${{ matrix.opt }} --verbose + env: + MIRIFLAGS: -Zmiri-disable-stacked-borrows -Zmiri-tree-borrows + + test: + name: Test / ${{ matrix.target }} ${{ matrix.opt }} + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + opt: + - '' + - --release + include: + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + - os: macos-latest + target: aarch64-apple-darwin + - os: windows-latest + target: x86_64-pc-windows-msvc + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo test ${{ matrix.opt }} + + cross: + name: Cross / ${{ matrix.target }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + target: + - i686-unknown-linux-gnu + - i586-unknown-linux-gnu + - armv7-unknown-linux-gnueabihf + - aarch64-unknown-linux-gnu + - thumbv6m-none-eabi + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo install cross + - run: cross build --no-default-features --target ${{ matrix.target }} --verbose + + format: + name: Format + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + components: rustfmt + - run: cargo fmt --check + + clippy: + name: Clippy + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + components: clippy + - run: cargo clippy + + doc: + name: Doc + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - run: cargo doc diff --git a/Cargo.toml b/Cargo.toml index 4713e36..2267205 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,11 +10,15 @@ version = "0.4.0" authors = ["David Koloski "] edition = "2021" license = "MIT" +readme = "README.md" repository = "https://github.com/djkoloski/munge" +keywords = ["macro", "no_std"] +categories = ["no-std", "no-std::no-alloc", "rust-patterns"] [workspace.dependencies] -munge_macro = { version = "0.4.0", path = "munge_macro" } -proc-macro2 = "1.0" -quote = "1.0" -syn = "2.0" -trybuild = "1.0" +munge_macro = { version = "=0.4.0", default-features = false, path = "munge_macro" } +proc-macro2 = { version = "1", default-features = false } +quote = { version = "1", default-features = false } +rustversion = { version = "1", default-features = false } +syn = { version = "2", default-features = false } +trybuild = { version = "1", default-features = false } diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..520fc25 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright 2024 David Koloski + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 915a682..ff31e16 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,15 @@ # `munge` -`munge` makes it easy and safe to destructure `MaybeUninit`s, `Cell`s, +[![crates.io badge]][crates.io] [![docs badge]][docs] [![license badge]][license] + +[crates.io badge]: https://img.shields.io/crates/v/munge.svg +[crates.io]: https://crates.io/crates/munge +[docs badge]: https://img.shields.io/docsrs/munge +[docs]: https://docs.rs/munge +[license badge]: https://img.shields.io/badge/license-MIT-blue.svg +[license]: https://github.com/rkyv/munge/blob/master/LICENSE + +Munge makes it easy and safe to destructure `MaybeUninit`s, `Cell`s, `UnsafeCell`s, `ManuallyDrop`s, and more. Just use the `munge!` macro to destructure opaque types the same way you'd @@ -9,17 +18,15 @@ destructuring (e.g. `let (a, b) = c` where `c` is a reference) or move destructuring (e.g. `let (a, b) = c` where `c` is a value) depending on the type. -`munge` has no features and is always `#![no_std]`. +Munge has no features and is always `#![no_std]`. ## Examples -`munge` makes it easy to initialize `MaybeUninit`s: +Initialize `MaybeUninit`s: ```rust -use { - ::core::mem::MaybeUninit, - ::munge::munge, -}; +use core::mem::MaybeUninit; +use munge::munge; pub struct Example { a: u32, @@ -42,13 +49,11 @@ assert_eq!(init.b.0, 'x'); assert_eq!(init.b.1, 3.14); ``` -It can also be used to destructure `Cell`s: +Destructure `Cell`s: ```rust -use { - ::core::cell::Cell, - ::munge::munge, -}; +use core::cell::Cell; +use munge::munge; pub struct Example { a: u32, @@ -75,7 +80,7 @@ assert_eq!(value.b.0, '!'); assert_eq!(value.b.1, 1.41); ``` -You can even extend `munge` to work with your own types by implementing its +You can even extend munge to work with your own types by implementing its `Destructure` and `Restructure` traits: ```rust diff --git a/munge/Cargo.toml b/munge/Cargo.toml index c2201a6..7a2fa4b 100644 --- a/munge/Cargo.toml +++ b/munge/Cargo.toml @@ -5,11 +5,11 @@ version.workspace = true authors.workspace = true edition.workspace = true license.workspace = true +readme.workspace = true repository.workspace = true +keywords.workspace = true +categories.workspace = true documentation = "https://docs.rs/munge" -keywords = ["munge", "macro", "destructure"] -categories = ["no-std", "rust-patterns"] -readme = "crates-io.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -17,4 +17,5 @@ readme = "crates-io.md" munge_macro.workspace = true [dev-dependencies] +rustversion.workspace = true trybuild.workspace = true diff --git a/munge/LICENSE b/munge/LICENSE new file mode 100644 index 0000000..520fc25 --- /dev/null +++ b/munge/LICENSE @@ -0,0 +1,7 @@ +Copyright 2024 David Koloski + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/munge/crates-io.md b/munge/example.md similarity index 77% rename from munge/crates-io.md rename to munge/example.md index 9d3bac1..a436060 100644 --- a/munge/crates-io.md +++ b/munge/example.md @@ -1,133 +1,116 @@ -`munge` makes it easy and safe to destructure `MaybeUninit`s, `Cell`s, -`UnsafeCell`s, `ManuallyDrop`s, and more. - -Just use the `munge!` macro to destructure opaque types the same way you'd -destructure a value. The `munge!` macro may be used to perform either borrow -destructuring (e.g. `let (a, b) = c` where `c` is a reference) or move -destructuring (e.g. `let (a, b) = c` where `c` is a value) depending on the -type. - -`munge` has no features and is always `#![no_std]`. - -## Examples - -`munge` makes it easy to initialize `MaybeUninit`s: - -```rust -use { - ::core::mem::MaybeUninit, - ::munge::munge, -}; - -pub struct Example { - a: u32, - b: (char, f32), -} - -let mut mu = MaybeUninit::::uninit(); - -munge!(let Example { a, b: (c, mut f) } = &mut mu); -assert_eq!(a.write(10), &10); -assert_eq!(c.write('x'), &'x'); -assert_eq!(f.write(3.14), &3.14); -// Note that `mut` bindings can be reassigned like you'd expect: -f = &mut MaybeUninit::uninit(); - -// SAFETY: `mu` is completely initialized. -let init = unsafe { mu.assume_init() }; -assert_eq!(init.a, 10); -assert_eq!(init.b.0, 'x'); -assert_eq!(init.b.1, 3.14); -``` - -It can also be used to destructure `Cell`s: - -```rust -use { - ::core::cell::Cell, - ::munge::munge, -}; - -pub struct Example { - a: u32, - b: (char, f32), -} - -let value = Example { - a: 10, - b: ('x', 3.14), -}; -let cell = Cell::::new(value); - -munge!(let Example { a, b: (c, f) } = &cell); -assert_eq!(a.get(), 10); -a.set(42); -assert_eq!(c.get(), 'x'); -c.set('!'); -assert_eq!(f.get(), 3.14); -f.set(1.41); - -let value = cell.into_inner(); -assert_eq!(value.a, 42); -assert_eq!(value.b.0, '!'); -assert_eq!(value.b.1, 1.41); -``` - -You can even extend `munge` to work with your own types by implementing its -`Destructure` and `Restructure` traits: - -```rust -use munge::{Destructure, Restructure, Move, munge}; - -pub struct Invariant(T); - -impl Invariant { - /// # Safety - /// - /// `value` must uphold my custom invariant. - pub unsafe fn new_unchecked(value: T) -> Self { - Self(value) - } - - pub fn unwrap(self) -> T { - self.0 - } -} - -// SAFETY: -// - `Invariant` is destructured by move, so its `Destructuring` type is -// `Move`. -// - `underlying` returns a pointer to its inner type, so it is guaranteed -// to be non-null, properly aligned, and valid for reads. -unsafe impl Destructure for Invariant { - type Underlying = T; - type Destructuring = Move; - - fn underlying(&mut self) -> *mut Self::Underlying { - &mut self.0 as *mut Self::Underlying - } -} - -// SAFETY: `restructure` returns an `Invariant` that takes ownership of -// the restructured field because `Invariant` is destructured by move. -unsafe impl Restructure for Invariant { - type Restructured = Invariant; - - unsafe fn restructure(&self, ptr: *mut U) -> Self::Restructured { - // SAFETY: The caller has guaranteed that `ptr` is a pointer to a - // subfield of some `T`, so it must be properly aligned, valid for - // reads, and initialized. We may move the fields because the - // destructuring type for `Invariant` is `Move`. - let value = unsafe { ptr.read() }; - Invariant(value) - } -} - -// SAFETY: `(1, 2, 3)` upholds my custom invariant. -let value = unsafe { Invariant::new_unchecked((1, 2, 3)) }; -munge!(let (one, two, three) = value); -assert_eq!(one.unwrap(), 1); -assert_eq!(two.unwrap(), 2); -assert_eq!(three.unwrap(), 3); -``` - + +Initialize `MaybeUninit`s: + +```rust +use core::mem::MaybeUninit; +use munge::munge; + +pub struct Example { + a: u32, + b: (char, f32), +} + +let mut mu = MaybeUninit::::uninit(); + +munge!(let Example { a, b: (c, mut f) } = &mut mu); +assert_eq!(a.write(10), &10); +assert_eq!(c.write('x'), &'x'); +assert_eq!(f.write(3.14), &3.14); +// Note that `mut` bindings can be reassigned like you'd expect: +f = &mut MaybeUninit::uninit(); + +// SAFETY: `mu` is completely initialized. +let init = unsafe { mu.assume_init() }; +assert_eq!(init.a, 10); +assert_eq!(init.b.0, 'x'); +assert_eq!(init.b.1, 3.14); +``` + +Destructure `Cell`s: + +```rust +use core::cell::Cell; +use munge::munge; + +pub struct Example { + a: u32, + b: (char, f32), +} + +let value = Example { + a: 10, + b: ('x', 3.14), +}; +let cell = Cell::::new(value); + +munge!(let Example { a, b: (c, f) } = &cell); +assert_eq!(a.get(), 10); +a.set(42); +assert_eq!(c.get(), 'x'); +c.set('!'); +assert_eq!(f.get(), 3.14); +f.set(1.41); + +let value = cell.into_inner(); +assert_eq!(value.a, 42); +assert_eq!(value.b.0, '!'); +assert_eq!(value.b.1, 1.41); +``` + +You can even extend munge to work with your own types by implementing its +`Destructure` and `Restructure` traits: + +```rust +use munge::{Destructure, Restructure, Move, munge}; + +pub struct Invariant(T); + +impl Invariant { + /// # Safety + /// + /// `value` must uphold my custom invariant. + pub unsafe fn new_unchecked(value: T) -> Self { + Self(value) + } + + pub fn unwrap(self) -> T { + self.0 + } +} + +// SAFETY: +// - `Invariant` is destructured by move, so its `Destructuring` type is +// `Move`. +// - `underlying` returns a pointer to its inner type, so it is guaranteed +// to be non-null, properly aligned, and valid for reads. +unsafe impl Destructure for Invariant { + type Underlying = T; + type Destructuring = Move; + + fn underlying(&mut self) -> *mut Self::Underlying { + &mut self.0 as *mut Self::Underlying + } +} + +// SAFETY: `restructure` returns an `Invariant` that takes ownership of +// the restructured field because `Invariant` is destructured by move. +unsafe impl Restructure for Invariant { + type Restructured = Invariant; + + unsafe fn restructure(&self, ptr: *mut U) -> Self::Restructured { + // SAFETY: The caller has guaranteed that `ptr` is a pointer to a + // subfield of some `T`, so it must be properly aligned, valid for + // reads, and initialized. We may move the fields because the + // destructuring type for `Invariant` is `Move`. + let value = unsafe { ptr.read() }; + Invariant(value) + } +} + +// SAFETY: `(1, 2, 3)` upholds my custom invariant. +let value = unsafe { Invariant::new_unchecked((1, 2, 3)) }; +munge!(let (one, two, three) = value); +assert_eq!(one.unwrap(), 1); +assert_eq!(two.unwrap(), 2); +assert_eq!(three.unwrap(), 3); +``` diff --git a/munge/src/__macro.rs b/munge/src/__macro.rs index 750cd8e..6856e41 100644 --- a/munge/src/__macro.rs +++ b/munge/src/__macro.rs @@ -1,4 +1,4 @@ -use ::core::{hint::unreachable_unchecked, marker::PhantomData}; +use core::{hint::unreachable_unchecked, marker::PhantomData}; use crate::{internal, Borrow, Destructure, Restructure}; diff --git a/munge/src/impls.rs b/munge/src/impls.rs index 38636b6..c0675b2 100644 --- a/munge/src/impls.rs +++ b/munge/src/impls.rs @@ -1,6 +1,6 @@ -use ::core::{ +use core::{ cell::{Cell, UnsafeCell}, - mem::{ManuallyDrop, MaybeUninit}, + mem::{transmute, ManuallyDrop, MaybeUninit}, ptr::read, }; @@ -39,8 +39,8 @@ unsafe impl Restructure for MaybeUninit { // &MaybeUninit // SAFETY: -// - `&MaybeUninit` is destructured by borrow, so its `Destructuring` type -// is `Borrow`. +// - `&MaybeUninit` is destructured by borrow, so its `Destructuring` type is +// `Borrow`. // - `underlying` returns a pointer to its inner type, so it is guaranteed to be // non-null, properly aligned, and valid for reads. unsafe impl<'a, T> Destructure for &'a MaybeUninit { @@ -147,13 +147,11 @@ unsafe impl<'a, T: ?Sized, U: 'a + ?Sized> Restructure for &'a Cell { type Restructured = &'a Cell; unsafe fn restructure(&self, ptr: *mut U) -> Self::Restructured { - let ptr = - // SAFETY: `Cell` is `repr(transparent)` and so guaranteed to - // have the same representation as the `U` it contains. Therefore, - // the pointer metadata for `*const Cell` is the same as the - // metadata for `*mut U`, and transmuting between the two types is - // sound. - unsafe { ::core::mem::transmute::<*mut U, *const Cell>(ptr) }; + // SAFETY: `Cell` is `repr(transparent)` and so guaranteed to have + // the same representation as the `U` it contains. Therefore, the + // pointer metadata for `*const Cell` is the same as the metadata for + // `*mut U`, and transmuting between the two types is sound. + let ptr = unsafe { transmute::<*mut U, *const Cell>(ptr) }; // SAFETY: The caller has guaranteed that `ptr` points to a subfield of // some `Cell`, so it's safe to dereference. Because the // destructuring type for `&Cell` is `Borrow`, we may create a @@ -184,13 +182,11 @@ unsafe impl<'a, T: ?Sized, U: 'a + ?Sized> Restructure for &'a mut Cell { type Restructured = &'a mut Cell; unsafe fn restructure(&self, ptr: *mut U) -> Self::Restructured { - let ptr = - // SAFETY: `Cell` is `repr(transparent)` and so guaranteed to - // have the same representation as the `U` it contains. Therefore, - // the pointer metadata for `*mut Cell` is the same as the - // metadata for `*mut U`, and transmuting between the two types is - // sound. - unsafe { ::core::mem::transmute::<*mut U, *mut Cell>(ptr) }; + // SAFETY: `Cell` is `repr(transparent)` and so guaranteed to have + // the same representation as the `U` it contains. Therefore, the + // pointer metadata for `*mut Cell` is the same as the metadata for + // `*mut U`, and transmuting between the two types is sound. + let ptr = unsafe { transmute::<*mut U, *mut Cell>(ptr) }; // SAFETY: The caller has guaranteed that `ptr` points to a subfield of // some `Cell`, so it's safe to dereference. Because the // destructuring type for `&mut Cell` is `Borrow`, we may create a @@ -232,8 +228,8 @@ unsafe impl Restructure for UnsafeCell { // &UnsafeCell // SAFETY: -// - `&UnsafeCell` is destructured by borrow, so its `Destructuring` type -// is `Borrow`. +// - `&UnsafeCell` is destructured by borrow, so its `Destructuring` type is +// `Borrow`. // - `underlying` returns a pointer to its inner type, so it is guaranteed to be // non-null, properly aligned, and valid for reads. unsafe impl<'a, T: ?Sized> Destructure for &'a UnsafeCell { @@ -260,9 +256,7 @@ where // pointer metadata for `*const UnsafeCell` is the same as the // metadata for `*mut U`, and transmuting between the two types is // sound. - let ptr = unsafe { - ::core::mem::transmute::<*mut U, *const UnsafeCell>(ptr) - }; + let ptr = unsafe { transmute::<*mut U, *const UnsafeCell>(ptr) }; // SAFETY: The caller has guaranteed that `ptr` points to a subfield of // some `UnsafeCell`, so it's safe to dereference. Because the // destructuring type for `&UnsafeCell` is `Borrow`, we may create a @@ -274,8 +268,8 @@ where // &mut UnsafeCell // SAFETY: -// - `&mut UnsafeCell` is destructured by borrow, so its `Destructuring` -// type is `Borrow`. +// - `&mut UnsafeCell` is destructured by borrow, so its `Destructuring` type +// is `Borrow`. // - `underlying` returns a pointer to its inner type, so it is guaranteed to be // non-null, properly aligned, and valid for reads. unsafe impl<'a, T: ?Sized> Destructure for &'a mut UnsafeCell { @@ -301,9 +295,7 @@ where // have the same representation as the `U` it contains. Therefore, the // pointer metadata for `*mut UnsafeCell` is the same as the metadata // for `*mut U`, and transmuting between the two types is sound. - let ptr = unsafe { - ::core::mem::transmute::<*mut U, *mut UnsafeCell>(ptr) - }; + let ptr = unsafe { transmute::<*mut U, *mut UnsafeCell>(ptr) }; // SAFETY: The caller has guaranteed that `ptr` points to a subfield of // some `UnsafeCell`, so it's safe to dereference. Because the // destructuring type for `&mut UnsafeCell` is `Borrow`, we may @@ -345,8 +337,8 @@ unsafe impl Restructure for ManuallyDrop { // &ManuallyDrop // SAFETY: -// - `&ManuallyDrop` is destructured by borrow, so its `Destructuring` -// type is `Borrow`. +// - `&ManuallyDrop` is destructured by borrow, so its `Destructuring` type +// is `Borrow`. // - `underlying` returns a pointer to its inner type, so it is guaranteed to be // non-null, properly aligned, and valid for reads. unsafe impl<'a, T: ?Sized> Destructure for &'a ManuallyDrop { @@ -373,9 +365,7 @@ where // pointer metadata for `*const ManuallyDrop` is the same as the // metadata for `*mut U`, and transmuting between the two types is // sound. - let ptr = unsafe { - ::core::mem::transmute::<*mut U, *const ManuallyDrop>(ptr) - }; + let ptr = unsafe { transmute::<*mut U, *const ManuallyDrop>(ptr) }; // SAFETY: The caller has guaranteed that `ptr` points to a subfield of // some `ManuallyDrop`, so it's safe to dereference. Because the // destructuring type for `&ManuallyDrop` is `Borrow`, we may create @@ -416,9 +406,7 @@ where // pointer metadata for `*mut ManuallyDrop` is the same as the // metadata for `*mut U`, and transmuting between the two types is // sound. - let ptr = unsafe { - ::core::mem::transmute::<*mut U, *mut ManuallyDrop>(ptr) - }; + let ptr = unsafe { transmute::<*mut U, *mut ManuallyDrop>(ptr) }; // SAFETY: The caller has guaranteed that `ptr` points to a subfield of // some `ManuallyDrop`, so it's safe to dereference. Because the // destructuring type for `&mut ManuallyDrop` is `Borrow`, we may diff --git a/munge/src/internal.rs b/munge/src/internal.rs index 092ff0b..3233c48 100644 --- a/munge/src/internal.rs +++ b/munge/src/internal.rs @@ -1,4 +1,4 @@ -use ::core::mem::ManuallyDrop; +use core::mem::ManuallyDrop; use crate::Destructure; diff --git a/munge/src/lib.rs b/munge/src/lib.rs index 8a03db8..d23774b 100644 --- a/munge/src/lib.rs +++ b/munge/src/lib.rs @@ -1,4 +1,4 @@ -//! `munge` makes it easy and safe to destructure `MaybeUninit`s, `Cell`s, +//! Munge makes it easy and safe to destructure `MaybeUninit`s, `Cell`s, //! `UnsafeCell`s, `ManuallyDrop`s and more. //! //! Just use the `munge!` macro to destructure opaque types the same way you'd @@ -7,140 +7,25 @@ //! destructuring (e.g. `let (a, b) = c` where `c` is a value) depending on the //! type. //! -//! `munge` has no features and is always `#![no_std]`. +//! Munge has no features and is always `#![no_std]`. //! //! ## Examples -//! -//! `munge` makes it easy to initialize `MaybeUninit`s: -//! -//! ```rust -//! use { -//! ::core::mem::MaybeUninit, -//! ::munge::munge, -//! }; -//! -//! pub struct Example { -//! a: u32, -//! b: (char, f32), -//! } -//! -//! let mut mu = MaybeUninit::::uninit(); -//! -//! munge!(let Example { a, b: (c, mut f) } = &mut mu); -//! assert_eq!(a.write(10), &10); -//! assert_eq!(c.write('x'), &'x'); -//! assert_eq!(f.write(3.14), &3.14); -//! // Note that `mut` bindings can be reassigned like you'd expect: -//! let mut new_f = MaybeUninit::uninit(); -//! f = &mut new_f; -//! -//! // SAFETY: `mu` is completely initialized. -//! let init = unsafe { mu.assume_init() }; -//! assert_eq!(init.a, 10); -//! assert_eq!(init.b.0, 'x'); -//! assert_eq!(init.b.1, 3.14); -//! ``` -//! -//! It can also be used to destructure `Cell`s: -//! -//! ```rust -//! use { -//! ::core::cell::Cell, -//! ::munge::munge, -//! }; -//! -//! pub struct Example { -//! a: u32, -//! b: (char, f32), -//! } -//! -//! let value = Example { -//! a: 10, -//! b: ('x', 3.14), -//! }; -//! let cell = Cell::::new(value); -//! -//! munge!(let Example { a, b: (c, f) } = &cell); -//! assert_eq!(a.get(), 10); -//! a.set(42); -//! assert_eq!(c.get(), 'x'); -//! c.set('!'); -//! assert_eq!(f.get(), 3.14); -//! f.set(1.41); -//! -//! let value = cell.into_inner(); -//! assert_eq!(value.a, 42); -//! assert_eq!(value.b.0, '!'); -//! assert_eq!(value.b.1, 1.41); -//! ``` -//! -//! You can even extend `munge` to work with your own types by implementing its -//! [`Destructure`] and [`Restructure`] traits: -//! -//! ```rust -//! use munge::{Destructure, Restructure, Move, munge}; -//! -//! pub struct Invariant(T); -//! -//! impl Invariant { -//! /// # Safety -//! /// -//! /// `value` must uphold my custom invariant. -//! pub unsafe fn new_unchecked(value: T) -> Self { -//! Self(value) -//! } -//! -//! pub fn unwrap(self) -> T { -//! self.0 -//! } -//! } -//! -//! // SAFETY: -//! // - `Invariant` is destructured by move, so its `Destructuring` type is -//! // `Move`. -//! // - `underlying` returns a pointer to its inner type, so it is guaranteed -//! // to be non-null, properly aligned, and valid for reads. -//! unsafe impl Destructure for Invariant { -//! type Underlying = T; -//! type Destructuring = Move; -//! -//! fn underlying(&mut self) -> *mut Self::Underlying { -//! &mut self.0 as *mut Self::Underlying -//! } -//! } -//! -//! // SAFETY: `restructure` returns an `Invariant` that takes ownership of -//! // the restructured field because `Invariant` is destructured by move. -//! unsafe impl Restructure for Invariant { -//! type Restructured = Invariant; -//! -//! unsafe fn restructure(&self, ptr: *mut U) -> Self::Restructured { -//! // SAFETY: The caller has guaranteed that `ptr` is a pointer to a -//! // subfield of some `T`, so it must be properly aligned, valid for -//! // reads, and initialized. We may move the fields because the -//! // destructuring type for `Invariant` is `Move`. -//! let value = unsafe { ptr.read() }; -//! Invariant(value) -//! } -//! } -//! -//! // SAFETY: `(1, 2, 3)` upholds my custom invariant. -//! let value = unsafe { Invariant::new_unchecked((1, 2, 3)) }; -//! munge!(let (one, two, three) = value); -//! assert_eq!(one.unwrap(), 1); -//! assert_eq!(two.unwrap(), 2); -//! assert_eq!(three.unwrap(), 3); -//! ``` - +#![doc = include_str!("../example.md")] #![no_std] #![deny( + future_incompatible, missing_docs, + nonstandard_style, unsafe_op_in_unsafe_fn, + unused, + warnings, + clippy::all, clippy::missing_safety_doc, clippy::undocumented_unsafe_blocks, rustdoc::broken_intra_doc_links, rustdoc::missing_crate_level_docs )] +#![cfg_attr(all(docsrs, not(doctest)), feature(doc_cfg, doc_auto_cfg))] #[doc(hidden)] pub mod __macro; @@ -148,14 +33,15 @@ mod impls; mod internal; #[doc(hidden)] -pub use ::munge_macro::munge_with_path; +pub use munge_macro::munge_with_path; -/// Projects a type to its fields using destructuring. +/// Destructures a type into /// /// # Example /// /// ``` -/// # use { ::core::mem::MaybeUninit, ::munge::munge }; +/// # use core::mem::MaybeUninit; +/// # use munge::munge; /// pub struct Example { /// a: u32, /// b: (char, f32), @@ -193,8 +79,8 @@ macro_rules! munge { /// destructuring allowed for the type: /// - [`Borrow`] if the type is restructured by creating disjoint borrows of /// the fields of `Underlying`. -/// - [`Move`] if the type may be restructured by moving the fields out of -/// the destructured `Underlying`. +/// - [`Move`] if the type may be restructured by moving the fields out of the +/// destructured `Underlying`. /// - [`underlying`](Destructure::underlying) must return a pointer that is /// non-null, properly aligned, and valid for reads. pub unsafe trait Destructure: Sized { @@ -264,7 +150,7 @@ impl internal::DestructuringFor for Move { #[cfg(test)] mod tests { - use ::core::mem::MaybeUninit; + use core::mem::MaybeUninit; #[test] fn project_tuple() { @@ -575,7 +461,7 @@ mod tests { #[test] fn cell() { - use ::core::cell::Cell; + use core::cell::Cell; pub struct Example { a: u32, @@ -613,7 +499,7 @@ mod tests { #[test] fn cell_value() { - use ::core::cell::Cell; + use core::cell::Cell; let cell = Cell::<(u32, char)>::new((10_000, 'x')); @@ -624,7 +510,7 @@ mod tests { #[test] fn unsafe_cell_value() { - use ::core::cell::UnsafeCell; + use core::cell::UnsafeCell; let uc = UnsafeCell::<(u32, char)>::new((10_000, 'x')); @@ -635,7 +521,7 @@ mod tests { #[test] fn manually_drop_value() { - use ::core::mem::ManuallyDrop; + use core::mem::ManuallyDrop; let md = ManuallyDrop::new((10_000, 'x')); @@ -646,7 +532,7 @@ mod tests { #[test] fn struct_borrow_partial_destructuring() { - use ::core::cell::Cell; + use core::cell::Cell; struct Example { a: u32, @@ -668,7 +554,7 @@ mod tests { #[test] fn tuple_borrow_partial_destructuring() { - use ::core::cell::Cell; + use core::cell::Cell; struct Example(u32, u32); diff --git a/munge/tests/tests.rs b/munge/tests/tests.rs index 2a0af16..7974a62 100644 --- a/munge/tests/tests.rs +++ b/munge/tests/tests.rs @@ -1,5 +1,6 @@ -#[test] +#[rustversion::attr(not(nightly), ignore)] #[cfg_attr(miri, ignore)] +#[test] fn ui() { let t = trybuild::TestCases::new(); t.compile_fail("tests/ui/*.rs"); diff --git a/munge/tests/ui/double_borrow.rs b/munge/tests/ui/double_borrow.rs index d98974b..7d9bb43 100644 --- a/munge/tests/ui/double_borrow.rs +++ b/munge/tests/ui/double_borrow.rs @@ -1,7 +1,5 @@ -use { - ::core::mem::MaybeUninit, - ::munge::munge, -}; +use core::mem::MaybeUninit; +use munge::munge; fn main() { struct Example { diff --git a/munge/tests/ui/double_borrow.stderr b/munge/tests/ui/double_borrow.stderr index e0bdf47..d97d560 100644 --- a/munge/tests/ui/double_borrow.stderr +++ b/munge/tests/ui/double_borrow.stderr @@ -1,10 +1,10 @@ error[E0499]: cannot borrow `mu` as mutable more than once at a time - --> tests/ui/double_borrow.rs:18:43 + --> tests/ui/double_borrow.rs:16:43 | -14 | munge!(let Example { a: a1, b: b1 } = &mut mu); +12 | munge!(let Example { a: a1, b: b1 } = &mut mu); | ------- first mutable borrow occurs here ... -18 | munge!(let Example { a: a2, b: b2 } = &mut mu); +16 | munge!(let Example { a: a2, b: b2 } = &mut mu); | ^^^^^^^ second mutable borrow occurs here -19 | assert_eq!(a1.write(3), &3); - | ----------- first borrow later used here +17 | assert_eq!(a1.write(3), &3); + | -- first borrow later used here diff --git a/munge/tests/ui/move_while_borrowed.rs b/munge/tests/ui/move_while_borrowed.rs index 9a9b64b..ace74ca 100644 --- a/munge/tests/ui/move_while_borrowed.rs +++ b/munge/tests/ui/move_while_borrowed.rs @@ -1,7 +1,5 @@ -use { - ::core::mem::MaybeUninit, - ::munge::munge, -}; +use core::mem::MaybeUninit; +use munge::munge; fn main() { struct Example { diff --git a/munge/tests/ui/move_while_borrowed.stderr b/munge/tests/ui/move_while_borrowed.stderr index b6e8b12..1b8dcaf 100644 --- a/munge/tests/ui/move_while_borrowed.stderr +++ b/munge/tests/ui/move_while_borrowed.stderr @@ -1,14 +1,14 @@ error[E0505]: cannot move out of `mu` because it is borrowed - --> tests/ui/move_while_borrowed.rs:19:26 + --> tests/ui/move_while_borrowed.rs:17:26 | -12 | let mut mu = MaybeUninit::::uninit(); +10 | let mut mu = MaybeUninit::::uninit(); | ------ binding `mu` declared here -13 | -14 | munge!(let Example { a, b } = &mut mu); +11 | +12 | munge!(let Example { a, b } = &mut mu); | ------- borrow of `mu` occurs here ... -19 | let value = unsafe { mu.assume_init() }; +17 | let value = unsafe { mu.assume_init() }; | ^^ move out of `mu` occurs here ... -23 | a.write(3); - | ---------- borrow later used here +21 | a.write(3); + | - borrow later used here diff --git a/munge/tests/ui/packed.rs b/munge/tests/ui/packed.rs index ba4705a..99ee3fb 100644 --- a/munge/tests/ui/packed.rs +++ b/munge/tests/ui/packed.rs @@ -1,7 +1,5 @@ -use { - ::core::mem::MaybeUninit, - ::munge::munge, -}; +use core::mem::MaybeUninit; +use munge::munge; fn main() { #[repr(packed)] diff --git a/munge/tests/ui/packed.stderr b/munge/tests/ui/packed.stderr index 8aa2202..7d140bd 100644 --- a/munge/tests/ui/packed.stderr +++ b/munge/tests/ui/packed.stderr @@ -1,18 +1,20 @@ error[E0793]: reference to packed field is unaligned - --> tests/ui/packed.rs:16:60 + --> tests/ui/packed.rs:14:60 | -16 | let Misalign { byte: a, inner: Misalign { byte: b, inner } } = &mut mu; +14 | let Misalign { byte: a, inner: Misalign { byte: b, inner } } = &mut mu; | ^^^^^ | - = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> tests/ui/packed.rs:26:5 + --> tests/ui/packed.rs:24:5 | -26 | assert_eq!(init.inner.inner, 3); +24 | assert_eq!(init.inner.inner, 3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/munge/tests/ui/rest_patterns_ref_only.rs b/munge/tests/ui/rest_patterns_ref_only.rs index 932e43a..e05be09 100644 --- a/munge/tests/ui/rest_patterns_ref_only.rs +++ b/munge/tests/ui/rest_patterns_ref_only.rs @@ -1,7 +1,5 @@ -use { - ::core::mem::MaybeUninit, - ::munge::munge, -}; +use core::mem::MaybeUninit; +use munge::munge; fn main() { struct Struct { diff --git a/munge/tests/ui/rest_patterns_ref_only.stderr b/munge/tests/ui/rest_patterns_ref_only.stderr index 1a2e455..0656533 100644 --- a/munge/tests/ui/rest_patterns_ref_only.stderr +++ b/munge/tests/ui/rest_patterns_ref_only.stderr @@ -1,8 +1,8 @@ error[E0271]: type mismatch resolving ` as Destructure>::Destructuring == Borrow` - --> tests/ui/rest_patterns_ref_only.rs:14:28 + --> tests/ui/rest_patterns_ref_only.rs:12:28 | -14 | munge!(let Struct { a, .. } = mu); - | -----------------------^--------- +12 | munge!(let Struct { a, .. } = mu); + | -----------------------^^-------- | | | | | expected `Borrow`, found `Move` | required by a bound introduced by this call @@ -10,14 +10,16 @@ error[E0271]: type mismatch resolving ` as Destructure>::Des note: required by a bound in `only_borrow_destructuring_may_use_rest_patterns` --> src/__macro.rs | + | pub fn only_borrow_destructuring_may_use_rest_patterns< + | ----------------------------------------------- required by a bound in this function | T: Destructure, | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `only_borrow_destructuring_may_use_rest_patterns` error[E0271]: type mismatch resolving ` as Destructure>::Destructuring == Borrow` - --> tests/ui/rest_patterns_ref_only.rs:20:25 + --> tests/ui/rest_patterns_ref_only.rs:18:25 | -20 | munge!(let Tuple(a, ..) = mu); - | --------------------^-------- +18 | munge!(let Tuple(a, ..) = mu); + | --------------------^^------- | | | | | expected `Borrow`, found `Move` | required by a bound introduced by this call @@ -25,5 +27,7 @@ error[E0271]: type mismatch resolving ` as Destructure>::Dest note: required by a bound in `only_borrow_destructuring_may_use_rest_patterns` --> src/__macro.rs | + | pub fn only_borrow_destructuring_may_use_rest_patterns< + | ----------------------------------------------- required by a bound in this function | T: Destructure, | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `only_borrow_destructuring_may_use_rest_patterns` diff --git a/munge_macro/Cargo.toml b/munge_macro/Cargo.toml index ad2d8c6..b139201 100644 --- a/munge_macro/Cargo.toml +++ b/munge_macro/Cargo.toml @@ -5,7 +5,10 @@ version.workspace = true authors.workspace = true edition.workspace = true license.workspace = true +readme.workspace = true repository.workspace = true +keywords.workspace = true +categories.workspace = true documentation = "https://docs.rs/munge_macro" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -16,4 +19,4 @@ proc-macro = true [dependencies] proc-macro2.workspace = true quote.workspace = true -syn = { workspace = true, features = ["full"] } +syn = { workspace = true, features = ["full", "parsing", "printing", "proc-macro"] } diff --git a/munge_macro/LICENSE b/munge_macro/LICENSE new file mode 100644 index 0000000..520fc25 --- /dev/null +++ b/munge_macro/LICENSE @@ -0,0 +1,7 @@ +Copyright 2024 David Koloski + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/munge_macro/src/lib.rs b/munge_macro/src/lib.rs index dcc1ed8..f32a400 100644 --- a/munge_macro/src/lib.rs +++ b/munge_macro/src/lib.rs @@ -9,22 +9,15 @@ rustdoc::missing_crate_level_docs )] -use ::proc_macro2::TokenStream; -use ::quote::quote; -use ::syn::{ - parse, - parse_macro_input, +use proc_macro2::TokenStream; +use quote::{quote, quote_spanned}; +use syn::{ + parse, parse_macro_input, punctuated::Punctuated, + spanned::Spanned, token::{Eq, FatArrow, Let, Semi}, - Error, - Expr, - Index, - Pat, - PatTupleStruct, - Path, + Error, Expr, Index, Pat, PatRest, PatTuple, PatTupleStruct, Path, }; -use quote::quote_spanned; -use syn::{spanned::Spanned, PatRest, PatTuple}; /// Destructures a value by projecting pointers. #[proc_macro] @@ -93,13 +86,13 @@ fn parse_pat( let mutability = &pat_ident.mutability; let ident = &pat_ident.ident; - if let Some(r#ref) = pat_ident.by_ref { + if let Some(r#ref) = &pat_ident.by_ref { return Err(Error::new_spanned( r#ref, "`ref` is not allowed in munge destructures", )); } - if let Some((at, _)) = pat_ident.subpat { + if let Some((at, _)) = &pat_ident.subpat { return Err(Error::new_spanned( at, "subpatterns are not allowed in munge destructures", diff --git a/rustfmt.toml b/rustfmt.toml index b4300fc..4c4edda 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,5 +1,15 @@ +max_width = 80 +comment_width = 80 +wrap_comments = true group_imports = "StdExternalCrate" imports_granularity = "Crate" -imports_layout = "HorizontalVertical" -max_width = 80 -unstable_features = true +condense_wildcard_suffixes = true +error_on_line_overflow = true +error_on_unformatted = true +format_code_in_doc_comments = true +format_macro_matchers = true +format_macro_bodies = true +format_strings = true +hex_literal_case = "Lower" +normalize_comments = true +use_field_init_shorthand = true