Skip to content

Commit

Permalink
[unsafe-fields] Add as_ref_checked
Browse files Browse the repository at this point in the history
Release 0.2.0.

Makes progress on #1931

gherrit-pr-id: I7ce0c981ed1f1bc1f4ff85dffef2a74114c6e76d
  • Loading branch information
joshlf committed Oct 21, 2024
1 parent f51d666 commit 7835610
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 23 deletions.
18 changes: 11 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,10 @@ jobs:
"stable",
"nightly",
]
features: [
"",
"--features zerocopy_0_8",
]
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
- name: Configure environment variables
Expand All @@ -540,19 +544,19 @@ jobs:
toolchain: ${{ env.ZC_TOOLCHAIN }}
components: clippy, rust-src
- name: Check
run: ./cargo.sh +${{ matrix.toolchain }} check --package unsafe-fields --verbose
run: ./cargo.sh +${{ matrix.toolchain }} check --package unsafe-fields --features ${{ matrix.features }} --verbose
- name: Check tests
run: ./cargo.sh +${{ matrix.toolchain }} check --tests --package unsafe-fields --verbose
run: ./cargo.sh +${{ matrix.toolchain }} check --tests --package unsafe-fields --features ${{ matrix.features }} --verbose
- name: Build
run: ./cargo.sh +${{ matrix.toolchain }} build --package unsafe-fields --verbose
run: ./cargo.sh +${{ matrix.toolchain }} build --package unsafe-fields --features ${{ matrix.features }} --verbose
- name: Run tests
run: ./cargo.sh +${{ matrix.toolchain }} test --package unsafe-fields --verbose
run: ./cargo.sh +${{ matrix.toolchain }} test --package unsafe-fields --features ${{ matrix.features }} --verbose
- name: Clippy
run: ./cargo.sh +${{ matrix.toolchain }} clippy --package unsafe-fields --verbose
run: ./cargo.sh +${{ matrix.toolchain }} clippy --package unsafe-fields --features ${{ matrix.features }} --verbose
# See comment in next step for why we only run on nightly.
if: matrix.toolchain == 'nightly'
- name: Clippy tests
run: ./cargo.sh +${{ matrix.toolchain }} clippy --package unsafe-fields --tests --verbose
run: ./cargo.sh +${{ matrix.toolchain }} clippy --package unsafe-fields --tests --features ${{ matrix.features }} --verbose
# Clippy improves the accuracy of lints over time, and fixes bugs. Only
# running Clippy on nightly allows us to avoid having to write code
# which is compatible with older versions of Clippy, which sometimes
Expand All @@ -573,7 +577,7 @@ jobs:
METADATA_DOCS_RS_RUSTDOC_ARGS="$(cargo metadata --format-version 1 | \
jq -r ".packages[] | select(.name == \"unsafe-fields\").metadata.docs.rs.\"rustdoc-args\"[]" | tr '\n' ' ')"
export RUSTDOCFLAGS="${{ matrix.toolchain == 'nightly' && '-Z unstable-options --document-hidden-items $METADATA_DOCS_RS_RUSTDOC_ARGS'|| '' }} $RUSTDOCFLAGS"
./cargo.sh +${{ matrix.toolchain }} doc --document-private-items --package unsafe-fields
./cargo.sh +${{ matrix.toolchain }} doc --document-private-items --package unsafe-fields --all-features
# NEON intrinsics are currently broken on big-endian platforms. [1] This test ensures
# that we don't accidentally attempt to compile these intrinsics on such platforms. We
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
# https://github.com/dtolnay/trybuild/issues/207#issuecomment-131227.594
[workspace]

members = ["unsafe-fields", "zerocopy-derive"]

[workspace.package]
# Inherited by zerocopy and unsafe-fields.
rust-version = "1.65.0"
Expand Down Expand Up @@ -85,7 +87,6 @@ testutil = { path = "testutil" }
# sometimes change the output format slightly, so a version mismatch can cause
# CI test failures.
trybuild = { version = "=1.0.90", features = ["diff"] }
unsafe-fields = { path = "./unsafe-fields" }
# In tests, unlike in production, zerocopy-derive is not optional
zerocopy-derive = { version = "=0.9.0-alpha.0", path = "zerocopy-derive" }
# TODO(#381) Remove this dependency once we have our own layout gadgets.
Expand Down
8 changes: 7 additions & 1 deletion unsafe-fields/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

[package]
name = "unsafe-fields"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
description = "Make it unsafe to access or modify fields with safety invariants"
license = "BSD-2-Clause OR Apache-2.0 OR MIT"
Expand All @@ -20,3 +20,9 @@ exclude = [".*"]
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "doc_cfg", "--generate-link-to-definition"]

[lints.rust]
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(doc_cfg)'] }

[dependencies]
zerocopy_0_8 = { package = "zerocopy", path = "..", optional = true }
51 changes: 37 additions & 14 deletions unsafe-fields/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
//! the [`Unsafe`] wrapper type. An `Unsafe` is intended to be used to for
//! struct, enum, or union fields which carry safety invariants. All accessors
//! are `unsafe`, which requires any use of an `Unsafe` field to be inside an
//! `unsafe` block.
//! `unsafe` block. One exception is [`Unsafe::as_ref`], which is available when
//! the `zerocopy_0_8` feature is enabled. See its docs for more information.
//!
//! An unsafe field has the type `Unsafe<O, F, const NAME_HASH: u128>`. `O` is
//! the enclosing type (struct, enum, or union), `F` is the type of the field,
Expand All @@ -23,6 +24,8 @@
//! the same enclosing type. Note that swapping the same field between instances
//! of the same type [cannot be prevented](crate#limitations).
//!
//! [immutable]: zerocopy_0_8::Immutable
//!
//! # Examples
//!
//! ```
Expand All @@ -46,7 +49,7 @@
//! return None;
//! }
//! // SAFETY: We just confirmed that `n` is even.
//! let n = unsafe { Unsafe::new(n) };
//! let n = unsafe { Unsafe::new_unchecked(n) };
//! Some(EvenUsize { n })
//! }
//! }
Expand Down Expand Up @@ -170,6 +173,7 @@
rustdoc::missing_crate_level_docs,
rustdoc::private_intra_doc_links
)]
#![cfg_attr(doc_cfg, feature(doc_cfg))]

use core::marker::PhantomData;

Expand Down Expand Up @@ -210,16 +214,41 @@ impl<O: ?Sized, F: Copy, const NAME_HASH: u128> Clone for Unsafe<O, F, { NAME_HA
impl<O: ?Sized, F: ?Sized, const NAME_HASH: u128> Unsafe<O, F, { NAME_HASH }> {
/// Gets a reference to the inner value.
///
/// If [`F: Immutable`][immutable], prefer [`as_ref`], which is safe.
///
/// [immutable]: zerocopy_0_8::Immutable
/// [`as_ref`]: Unsafe::as_ref
///
/// # Safety
///
/// The caller is responsible for upholding any safety invariants associated
/// with this field.
#[inline(always)]
pub const unsafe fn as_ref(&self) -> &F {
pub const unsafe fn as_ref_unchecked(&self) -> &F {
// SAFETY: This method is unsafe to call.
&self.field
}

/// Gets a reference to the inner value safely so long as the inner value is
/// immutable.
///
/// If [`F: Immutable`][immutable], then `F` does not permit interior
/// mutation, and so it is safe to return a reference to it.
///
/// [immutable]: zerocopy_0_8::Immutable
#[inline(always)]
#[cfg(feature = "zerocopy_0_8")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "zerocopy_0_8")))]
pub const fn as_ref(&self) -> &F
where
F: zerocopy_0_8::Immutable,
{
// SAFETY: `F: Immutable` guarantees that the returned `&F` cannot be
// used to mutate `self`, and so it cannot be used to violate any
// invariant.
unsafe { self.as_ref_unchecked() }
}

/// Gets a mutable reference to the inner value.
///
/// # Safety
Expand All @@ -234,7 +263,7 @@ impl<O: ?Sized, F: ?Sized, const NAME_HASH: u128> Unsafe<O, F, { NAME_HASH }> {
}

impl<O: ?Sized, F, const NAME_HASH: u128> Unsafe<O, F, { NAME_HASH }> {
/// Constructs a new `Unsafe<O, F, NAME_HASH>`.
/// Constructs a new `Unsafe`.
///
/// # Safety
///
Expand All @@ -247,13 +276,8 @@ impl<O: ?Sized, F, const NAME_HASH: u128> Unsafe<O, F, { NAME_HASH }> {
}

/// Extracts the inner `F` from `self`.
///
/// # Safety
///
/// The caller is responsible for upholding any safety invariants associated
/// with this field.
#[inline(always)]
pub const unsafe fn into(self) -> F {
pub const fn into(self) -> F {
use core::mem::ManuallyDrop;
let slf = ManuallyDrop::new(self);

Expand All @@ -280,8 +304,6 @@ impl<O: ?Sized, F, const NAME_HASH: u128> Unsafe<O, F, { NAME_HASH }> {
// validity as `T`
let dst = unsafe { Transmute { src: slf }.dst };

// SAFETY (satisfaction of `Unsafe`'s field invariant): This method is
// unsafe to call.
ManuallyDrop::into_inner(dst)
}
}
Expand Down Expand Up @@ -365,8 +387,9 @@ mod tests {
#[test]
#[allow(clippy::undocumented_unsafe_blocks)]
fn test_unsafe_fieds() {
let mut _foo = Foo { a: unsafe { Unsafe::new(0) }, b: 0 };
let mut _bar = Bar { a: unsafe { Unsafe::new(0) }, b: unsafe { Unsafe::new(0) } };
let mut _foo = Foo { a: unsafe { Unsafe::new_unchecked(0) }, b: 0 };
let mut _bar =
Bar { a: unsafe { Unsafe::new_unchecked(0) }, b: unsafe { Unsafe::new_unchecked(0) } };
}
}

Expand Down

0 comments on commit 7835610

Please sign in to comment.