Skip to content

Commit 42e9149

Browse files
committed
[error] Implement std::error::Error on errors
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
1 parent 1c77a9d commit 42e9149

File tree

5 files changed

+112
-29
lines changed

5 files changed

+112
-29
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ alloc = []
6060
derive = ["zerocopy-derive"]
6161
simd = []
6262
simd-nightly = ["simd"]
63+
std = ["alloc"]
6364
# This feature depends on all other features that work on the stable compiler.
6465
# We make no stability guarantees about this feature; it may be modified or
6566
# removed at any time.
66-
__internal_use_only_features_that_work_on_stable = ["alloc", "derive", "simd"]
67+
__internal_use_only_features_that_work_on_stable = ["alloc", "derive", "simd", "std"]
6768

6869
[dependencies]
6970
zerocopy-derive = { version = "=0.8.0-alpha.14", path = "zerocopy-derive", optional = true }

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ for network parsing.
8282
the `alloc` crate is added as a dependency, and some allocation-related
8383
functionality is added.
8484

85+
- **`std`**
86+
By default, `zerocopy` is `no_std`. When the `std` feature is enabled, the
87+
`std` crate is added as a dependency (ie, `no_std` is disabled), and
88+
support for some `std` types is added. `std` implies `alloc`.
89+
8590
- **`derive`**
8691
Provides derives for the core marker traits via the `zerocopy-derive`
8792
crate. These derives are re-exported from `zerocopy`, so it is not

src/error.rs

Lines changed: 73 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
//! All error types provide an `into_src` method that converts the error into
2424
//! the source value underlying the failed conversion.
2525
26-
use core::{convert::Infallible, fmt, marker::PhantomData, ops::Deref};
26+
use core::{convert::Infallible, fmt, ops::Deref};
2727

28-
use crate::TryFromBytes;
28+
use crate::{util::SendSyncPhantomData, KnownLayout, TryFromBytes};
2929
#[cfg(doc)]
3030
use crate::{FromBytes, Ref};
3131

@@ -82,18 +82,27 @@ impl<A: fmt::Display, S: fmt::Display, V: fmt::Display> fmt::Display for Convert
8282
}
8383
}
8484

85+
#[cfg(feature = "std-error")]
86+
impl<A, S, V> std::error::Error for ConvertError<A, S, V>
87+
where
88+
A: fmt::Display + fmt::Debug,
89+
S: fmt::Display + fmt::Debug,
90+
V: fmt::Display + fmt::Debug,
91+
{
92+
}
93+
8594
/// The error emitted if the conversion source is improperly aligned.
8695
#[derive(PartialEq, Eq)]
8796
pub struct AlignmentError<Src, Dst: ?Sized> {
8897
/// The source value involved in the conversion.
8998
src: Src,
9099
/// The inner destination type inolved in the conversion.
91-
dst: PhantomData<Dst>,
100+
dst: SendSyncPhantomData<Dst>,
92101
}
93102

94103
impl<Src, Dst: ?Sized> AlignmentError<Src, Dst> {
95104
pub(crate) fn new(src: Src) -> Self {
96-
Self { src, dst: PhantomData }
105+
Self { src, dst: SendSyncPhantomData::default() }
97106
}
98107

99108
/// Produces the source underlying the failed conversion.
@@ -103,11 +112,11 @@ impl<Src, Dst: ?Sized> AlignmentError<Src, Dst> {
103112
}
104113

105114
pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> AlignmentError<NewSrc, Dst> {
106-
AlignmentError { src: new_src, dst: PhantomData }
115+
AlignmentError { src: new_src, dst: SendSyncPhantomData::default() }
107116
}
108117

109118
pub(crate) fn map_src<NewSrc>(self, f: impl Fn(Src) -> NewSrc) -> AlignmentError<NewSrc, Dst> {
110-
AlignmentError { src: f(self.src), dst: PhantomData }
119+
AlignmentError { src: f(self.src), dst: SendSyncPhantomData::default() }
111120
}
112121

113122
pub(crate) fn into<S, V>(self) -> ConvertError<Self, S, V> {
@@ -126,9 +135,10 @@ impl<Src, Dst: ?Sized> fmt::Debug for AlignmentError<Src, Dst> {
126135
// The bounds on this impl are intentionally conservative, and can be relaxed
127136
// either once a `?Sized` alignment accessor is stabilized, or by storing the
128137
// alignment as a runtime value.
129-
impl<Src, Dst> fmt::Display for AlignmentError<Src, Dst>
138+
impl<Src, Dst: ?Sized> fmt::Display for AlignmentError<Src, Dst>
130139
where
131140
Src: Deref,
141+
Dst: KnownLayout,
132142
{
133143
#[inline]
134144
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -139,14 +149,22 @@ where
139149
f.write_str("the conversion failed because the address of the source (a multiple of ")?;
140150
addr_align.fmt(f)?;
141151
f.write_str(") is not a multiple of the alignment (")?;
142-
core::mem::align_of::<Dst>().fmt(f)?;
152+
<Dst as KnownLayout>::LAYOUT.align.get().fmt(f)?;
143153
f.write_str(") of the destination type: ")?;
144154
f.write_str(core::any::type_name::<Dst>())?;
145155
Ok(())
146156
}
147157
}
148158

149-
impl<Src, Dst, S, V> From<AlignmentError<Src, Dst>>
159+
#[cfg(feature = "std-error")]
160+
impl<Src, Dst: ?Sized> std::error::Error for AlignmentError<Src, Dst>
161+
where
162+
Src: Deref,
163+
Dst: KnownLayout,
164+
{
165+
}
166+
167+
impl<Src, Dst: ?Sized, S, V> From<AlignmentError<Src, Dst>>
150168
for ConvertError<AlignmentError<Src, Dst>, S, V>
151169
{
152170
#[inline]
@@ -161,12 +179,12 @@ pub struct SizeError<Src, Dst: ?Sized> {
161179
/// The source value involved in the conversion.
162180
src: Src,
163181
/// The inner destination type inolved in the conversion.
164-
dst: PhantomData<Dst>,
182+
dst: SendSyncPhantomData<Dst>,
165183
}
166184

167185
impl<Src, Dst: ?Sized> SizeError<Src, Dst> {
168186
pub(crate) fn new(src: Src) -> Self {
169-
Self { src, dst: PhantomData }
187+
Self { src, dst: SendSyncPhantomData::default() }
170188
}
171189

172190
/// Produces the source underlying the failed conversion.
@@ -177,17 +195,17 @@ impl<Src, Dst: ?Sized> SizeError<Src, Dst> {
177195

178196
/// Sets the source value associated with the conversion error.
179197
pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> SizeError<NewSrc, Dst> {
180-
SizeError { src: new_src, dst: PhantomData }
198+
SizeError { src: new_src, dst: SendSyncPhantomData::default() }
181199
}
182200

183201
/// Maps the source value associated with the conversion error.
184202
pub(crate) fn map_src<NewSrc>(self, f: impl Fn(Src) -> NewSrc) -> SizeError<NewSrc, Dst> {
185-
SizeError { src: f(self.src), dst: PhantomData }
203+
SizeError { src: f(self.src), dst: SendSyncPhantomData::default() }
186204
}
187205

188206
/// Sets the destination type associated with the conversion error.
189207
pub(crate) fn with_dst<NewDst: ?Sized>(self) -> SizeError<Src, NewDst> {
190-
SizeError { src: self.src, dst: PhantomData }
208+
SizeError { src: self.src, dst: SendSyncPhantomData::default() }
191209
}
192210

193211
/// Converts the error into a general [`ConvertError`].
@@ -204,10 +222,7 @@ impl<Src, Dst: ?Sized> fmt::Debug for SizeError<Src, Dst> {
204222
}
205223

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

219-
impl<Src, Dst, A, V> From<SizeError<Src, Dst>> for ConvertError<A, SizeError<Src, Dst>, V> {
234+
#[cfg(feature = "std-error")]
235+
impl<Src, Dst: ?Sized> std::error::Error for SizeError<Src, Dst> {}
236+
237+
impl<Src, Dst: ?Sized, A, V> From<SizeError<Src, Dst>> for ConvertError<A, SizeError<Src, Dst>, V> {
220238
#[inline]
221239
fn from(err: SizeError<Src, Dst>) -> Self {
222240
Self::Size(err)
@@ -229,12 +247,12 @@ pub struct ValidityError<Src, Dst: ?Sized + TryFromBytes> {
229247
/// The source value involved in the conversion.
230248
pub(crate) src: Src,
231249
/// The inner destination type inolved in the conversion.
232-
dst: PhantomData<Dst>,
250+
dst: SendSyncPhantomData<Dst>,
233251
}
234252

235253
impl<Src, Dst: ?Sized + TryFromBytes> ValidityError<Src, Dst> {
236254
pub(crate) fn new(src: Src) -> Self {
237-
Self { src, dst: PhantomData }
255+
Self { src, dst: SendSyncPhantomData::default() }
238256
}
239257

240258
/// Produces the source underlying the failed conversion.
@@ -245,7 +263,7 @@ impl<Src, Dst: ?Sized + TryFromBytes> ValidityError<Src, Dst> {
245263

246264
/// Maps the source value associated with the conversion error.
247265
pub(crate) fn map_src<NewSrc>(self, f: impl Fn(Src) -> NewSrc) -> ValidityError<NewSrc, Dst> {
248-
ValidityError { src: f(self.src), dst: PhantomData }
266+
ValidityError { src: f(self.src), dst: SendSyncPhantomData::default() }
249267
}
250268

251269
/// Converts the error into a general [`ConvertError`].
@@ -262,10 +280,7 @@ impl<Src, Dst: ?Sized + TryFromBytes> fmt::Debug for ValidityError<Src, Dst> {
262280
}
263281

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

292+
#[cfg(feature = "std-error")]
293+
impl<Src, Dst: ?Sized + TryFromBytes> std::error::Error for ValidityError<Src, Dst> {}
294+
277295
impl<Src, Dst: ?Sized + TryFromBytes, A, S> From<ValidityError<Src, Dst>>
278296
for ConvertError<A, S, ValidityError<Src, Dst>>
279297
{
@@ -395,16 +413,44 @@ impl<Src, Dst: ?Sized + TryFromBytes> TryReadError<Src, Dst> {
395413
}
396414

397415
#[cfg(test)]
398-
mod test {
416+
mod tests {
399417
use super::*;
400418

419+
#[test]
420+
fn test_send_sync() {
421+
// Test that all error types are `Send + Sync` even if `Dst: !Send +
422+
// !Sync`.
423+
424+
#[allow(dead_code)]
425+
fn is_send_sync<T: Send + Sync>(_t: T) {}
426+
427+
#[allow(dead_code)]
428+
fn size_err_is_send_sync<Src: Send + Sync, Dst>(err: SizeError<Src, Dst>) {
429+
is_send_sync(err)
430+
}
431+
432+
#[allow(dead_code)]
433+
fn alignment_err_is_send_sync<Src: Send + Sync, Dst>(err: AlignmentError<Src, Dst>) {
434+
is_send_sync(err)
435+
}
436+
437+
#[allow(dead_code)]
438+
fn validity_err_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>(
439+
err: ValidityError<Src, Dst>,
440+
) {
441+
is_send_sync(err)
442+
}
443+
}
444+
401445
#[test]
402446
fn alignment_display() {
403447
#[repr(C, align(128))]
404448
struct Aligned {
405449
bytes: [u8; 128],
406450
}
407451

452+
impl_known_layout!(elain::Align::<8>);
453+
408454
let aligned = Aligned { bytes: [0; 128] };
409455

410456
assert_eq!(

src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@
8282
//! the `alloc` crate is added as a dependency, and some allocation-related
8383
//! functionality is added.
8484
//!
85+
//! - **`std`**
86+
//! By default, `zerocopy` is `no_std`. When the `std` feature is enabled, the
87+
//! `std` crate is added as a dependency (ie, `no_std` is disabled), and
88+
//! support for some `std` types is added. `std` implies `alloc`.
89+
//!
8590
//! - **`derive`**
8691
//! Provides derives for the core marker traits via the `zerocopy-derive`
8792
//! crate. These derives are re-exported from `zerocopy`, so it is not
@@ -255,7 +260,7 @@
255260
clippy::arithmetic_side_effects,
256261
clippy::indexing_slicing,
257262
))]
258-
#![cfg_attr(not(test), no_std)]
263+
#![cfg_attr(not(any(test, feature = "std-error")), no_std)]
259264
#![cfg_attr(
260265
all(feature = "simd-nightly", any(target_arch = "x86", target_arch = "x86_64")),
261266
feature(stdarch_x86_avx512)

src/util.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
use core::{
1010
cell::UnsafeCell,
11+
marker::PhantomData,
1112
mem::{self, ManuallyDrop, MaybeUninit},
1213
num::{NonZeroUsize, Wrapping},
1314
ptr::NonNull,
@@ -431,6 +432,31 @@ safety_comment! {
431432
);
432433
}
433434

435+
/// Like [`PhantomData`], but [`Send`] and [`Sync`] regardless of whether the
436+
/// wrapped `T` is.
437+
pub(crate) struct SendSyncPhantomData<T: ?Sized>(PhantomData<T>);
438+
439+
// SAFETY: `SendSyncPhantomData` does not enable any behavior which isn't sound
440+
// to be called from multiple threads.
441+
unsafe impl<T: ?Sized> Send for SendSyncPhantomData<T> {}
442+
// SAFETY: `SendSyncPhantomData` does not enable any behavior which isn't sound
443+
// to be called from multiple threads.
444+
unsafe impl<T: ?Sized> Sync for SendSyncPhantomData<T> {}
445+
446+
impl<T: ?Sized> Default for SendSyncPhantomData<T> {
447+
fn default() -> SendSyncPhantomData<T> {
448+
SendSyncPhantomData(PhantomData)
449+
}
450+
}
451+
452+
impl<T: ?Sized> PartialEq for SendSyncPhantomData<T> {
453+
fn eq(&self, other: &Self) -> bool {
454+
self.0.eq(&other.0)
455+
}
456+
}
457+
458+
impl<T: ?Sized> Eq for SendSyncPhantomData<T> {}
459+
434460
pub(crate) trait AsAddress {
435461
fn addr(self) -> usize;
436462
}

0 commit comments

Comments
 (0)