Skip to content

Commit

Permalink
[error] Implement std::error::Error on errors
Browse files Browse the repository at this point in the history
While we're here, also relax `Dispaly for AlignmentError<Src, Dst>` to
permit `Dst: ?Sized` in exchange for `Dst: KnownLayout`. This is an
important relaxation since our APIs permit performing conversions into
unsized destination types with runtime alignment checking.

Makes progress on #1297
  • Loading branch information
joshlf committed May 18, 2024
1 parent 1c77a9d commit f1ef943
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 15 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,11 @@ alloc = []
derive = ["zerocopy-derive"]
simd = []
simd-nightly = ["simd"]
std = ["alloc"]
# This feature depends on all other features that work on the stable compiler.
# We make no stability guarantees about this feature; it may be modified or
# removed at any time.
__internal_use_only_features_that_work_on_stable = ["alloc", "derive", "simd"]
__internal_use_only_features_that_work_on_stable = ["alloc", "derive", "simd", "std"]

[dependencies]
zerocopy-derive = { version = "=0.8.0-alpha.14", path = "zerocopy-derive", optional = true }
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ for network parsing.
the `alloc` crate is added as a dependency, and some allocation-related
functionality is added.

- **`std`**
By default, `zerocopy` is `no_std`. When the `std` feature is enabled, the
`std` crate is added as a dependency (ie, `no_std` is disabled), and
support for some `std` types is added. `std` implies `alloc`.

- **`derive`**
Provides derives for the core marker traits via the `zerocopy-derive`
crate. These derives are re-exported from `zerocopy`, so it is not
Expand Down
46 changes: 33 additions & 13 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@

use core::{convert::Infallible, fmt, marker::PhantomData, ops::Deref};

use crate::TryFromBytes;
#[cfg(doc)]
use crate::{FromBytes, Ref};
use crate::{KnownLayout, TryFromBytes};

/// Zerocopy's generic error type.
///
Expand Down Expand Up @@ -82,6 +82,15 @@ impl<A: fmt::Display, S: fmt::Display, V: fmt::Display> fmt::Display for Convert
}
}

#[cfg(feature = "std-error")]
impl<A, S, V> std::error::Error for ConvertError<A, S, V>
where
A: fmt::Display + fmt::Debug,
S: fmt::Display + fmt::Debug,
V: fmt::Display + fmt::Debug,
{
}

/// The error emitted if the conversion source is improperly aligned.
#[derive(PartialEq, Eq)]
pub struct AlignmentError<Src, Dst: ?Sized> {
Expand Down Expand Up @@ -126,9 +135,10 @@ impl<Src, Dst: ?Sized> fmt::Debug for AlignmentError<Src, Dst> {
// The bounds on this impl are intentionally conservative, and can be relaxed
// either once a `?Sized` alignment accessor is stabilized, or by storing the
// alignment as a runtime value.
impl<Src, Dst> fmt::Display for AlignmentError<Src, Dst>
impl<Src, Dst: ?Sized> fmt::Display for AlignmentError<Src, Dst>
where
Src: Deref,
Dst: KnownLayout,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand All @@ -139,14 +149,22 @@ where
f.write_str("the conversion failed because the address of the source (a multiple of ")?;
addr_align.fmt(f)?;
f.write_str(") is not a multiple of the alignment (")?;
core::mem::align_of::<Dst>().fmt(f)?;
<Dst as KnownLayout>::LAYOUT.align.get().fmt(f)?;
f.write_str(") of the destination type: ")?;
f.write_str(core::any::type_name::<Dst>())?;
Ok(())
}
}

impl<Src, Dst, S, V> From<AlignmentError<Src, Dst>>
#[cfg(feature = "std-error")]
impl<Src, Dst: ?Sized> std::error::Error for AlignmentError<Src, Dst>
where
Src: Deref,
Dst: KnownLayout,
{
}

impl<Src, Dst: ?Sized, S, V> From<AlignmentError<Src, Dst>>
for ConvertError<AlignmentError<Src, Dst>, S, V>
{
#[inline]
Expand Down Expand Up @@ -204,10 +222,7 @@ impl<Src, Dst: ?Sized> fmt::Debug for SizeError<Src, Dst> {
}

/// Produces a human-readable error message.
impl<Src, Dst: ?Sized> fmt::Display for SizeError<Src, Dst>
where
Src: Deref,
{
impl<Src, Dst: ?Sized> fmt::Display for SizeError<Src, Dst> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("the conversion failed because the source was incorrectly sized to complete the conversion into the destination type: ")?;
Expand All @@ -216,7 +231,10 @@ where
}
}

impl<Src, Dst, A, V> From<SizeError<Src, Dst>> for ConvertError<A, SizeError<Src, Dst>, V> {
#[cfg(feature = "std-error")]
impl<Src, Dst: ?Sized> std::error::Error for SizeError<Src, Dst> {}

impl<Src, Dst: ?Sized, A, V> From<SizeError<Src, Dst>> for ConvertError<A, SizeError<Src, Dst>, V> {
#[inline]
fn from(err: SizeError<Src, Dst>) -> Self {
Self::Size(err)
Expand Down Expand Up @@ -262,10 +280,7 @@ impl<Src, Dst: ?Sized + TryFromBytes> fmt::Debug for ValidityError<Src, Dst> {
}

/// Produces a human-readable error message.
impl<Src, Dst: ?Sized + TryFromBytes> fmt::Display for ValidityError<Src, Dst>
where
Src: Deref,
{
impl<Src, Dst: ?Sized + TryFromBytes> fmt::Display for ValidityError<Src, Dst> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("the conversion failed because the source bytes are not a valid value of the destination type: ")?;
Expand All @@ -274,6 +289,9 @@ where
}
}

#[cfg(feature = "std-error")]
impl<Src, Dst: ?Sized + TryFromBytes> std::error::Error for ValidityError<Src, Dst> {}

impl<Src, Dst: ?Sized + TryFromBytes, A, S> From<ValidityError<Src, Dst>>
for ConvertError<A, S, ValidityError<Src, Dst>>
{
Expand Down Expand Up @@ -405,6 +423,8 @@ mod test {
bytes: [u8; 128],
}

impl_known_layout!(elain::Align::<8>);

let aligned = Aligned { bytes: [0; 128] };

assert_eq!(
Expand Down
7 changes: 6 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
//! the `alloc` crate is added as a dependency, and some allocation-related
//! functionality is added.
//!
//! - **`std`**
//! By default, `zerocopy` is `no_std`. When the `std` feature is enabled, the
//! `std` crate is added as a dependency (ie, `no_std` is disabled), and
//! support for some `std` types is added. `std` implies `alloc`.
//!
//! - **`derive`**
//! Provides derives for the core marker traits via the `zerocopy-derive`
//! crate. These derives are re-exported from `zerocopy`, so it is not
Expand Down Expand Up @@ -255,7 +260,7 @@
clippy::arithmetic_side_effects,
clippy::indexing_slicing,
))]
#![cfg_attr(not(test), no_std)]
#![cfg_attr(not(any(test, feature = "std-error")), no_std)]
#![cfg_attr(
all(feature = "simd-nightly", any(target_arch = "x86", target_arch = "x86_64")),
feature(stdarch_x86_avx512)
Expand Down

0 comments on commit f1ef943

Please sign in to comment.