Skip to content

Commit 235cd25

Browse files
joshlfjswrenn
authored andcommitted
[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. Also make all errors `Send + Sync` regardless of `Dst`, which only exists at the type level, but is never instantiated. Makes progress on #1297
1 parent 1c77a9d commit 235cd25

File tree

6 files changed

+136
-27
lines changed

6 files changed

+136
-27
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: 96 additions & 25 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(any(feature = "std", test))]
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(any(feature = "std", test))]
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`].
@@ -206,17 +224,26 @@ impl<Src, Dst: ?Sized> fmt::Debug for SizeError<Src, Dst> {
206224
/// Produces a human-readable error message.
207225
impl<Src, Dst: ?Sized> fmt::Display for SizeError<Src, Dst>
208226
where
209-
Src: Deref,
227+
Dst: KnownLayout,
210228
{
211229
#[inline]
212230
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213-
f.write_str("the conversion failed because the source was incorrectly sized to complete the conversion into the destination type: ")?;
231+
f.write_str("the conversion failed because the source was incorrectly sized to complete the conversion into the destination type")?;
232+
if let crate::SizeInfo::Sized { size } = Dst::LAYOUT.size_info {
233+
f.write_str(" (")?;
234+
size.fmt(f)?;
235+
f.write_str(" bytes)")?;
236+
}
237+
f.write_str(": ")?;
214238
f.write_str(core::any::type_name::<Dst>())?;
215239
Ok(())
216240
}
217241
}
218242

219-
impl<Src, Dst, A, V> From<SizeError<Src, Dst>> for ConvertError<A, SizeError<Src, Dst>, V> {
243+
#[cfg(any(feature = "std", test))]
244+
impl<Src, Dst: ?Sized> std::error::Error for SizeError<Src, Dst> where Dst: KnownLayout {}
245+
246+
impl<Src, Dst: ?Sized, A, V> From<SizeError<Src, Dst>> for ConvertError<A, SizeError<Src, Dst>, V> {
220247
#[inline]
221248
fn from(err: SizeError<Src, Dst>) -> Self {
222249
Self::Size(err)
@@ -229,12 +256,12 @@ pub struct ValidityError<Src, Dst: ?Sized + TryFromBytes> {
229256
/// The source value involved in the conversion.
230257
pub(crate) src: Src,
231258
/// The inner destination type inolved in the conversion.
232-
dst: PhantomData<Dst>,
259+
dst: SendSyncPhantomData<Dst>,
233260
}
234261

235262
impl<Src, Dst: ?Sized + TryFromBytes> ValidityError<Src, Dst> {
236263
pub(crate) fn new(src: Src) -> Self {
237-
Self { src, dst: PhantomData }
264+
Self { src, dst: SendSyncPhantomData::default() }
238265
}
239266

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

246273
/// Maps the source value associated with the conversion error.
247274
pub(crate) fn map_src<NewSrc>(self, f: impl Fn(Src) -> NewSrc) -> ValidityError<NewSrc, Dst> {
248-
ValidityError { src: f(self.src), dst: PhantomData }
275+
ValidityError { src: f(self.src), dst: SendSyncPhantomData::default() }
249276
}
250277

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

264291
/// Produces a human-readable error message.
265-
impl<Src, Dst: ?Sized + TryFromBytes> fmt::Display for ValidityError<Src, Dst>
266-
where
267-
Src: Deref,
268-
{
292+
impl<Src, Dst: ?Sized + TryFromBytes> fmt::Display for ValidityError<Src, Dst> {
269293
#[inline]
270294
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271295
f.write_str("the conversion failed because the source bytes are not a valid value of the destination type: ")?;
@@ -274,6 +298,9 @@ where
274298
}
275299
}
276300

301+
#[cfg(any(feature = "std", test))]
302+
impl<Src, Dst: ?Sized + TryFromBytes> std::error::Error for ValidityError<Src, Dst> {}
303+
277304
impl<Src, Dst: ?Sized + TryFromBytes, A, S> From<ValidityError<Src, Dst>>
278305
for ConvertError<A, S, ValidityError<Src, Dst>>
279306
{
@@ -395,16 +422,55 @@ impl<Src, Dst: ?Sized + TryFromBytes> TryReadError<Src, Dst> {
395422
}
396423

397424
#[cfg(test)]
398-
mod test {
425+
mod tests {
399426
use super::*;
400427

428+
#[test]
429+
fn test_send_sync() {
430+
// Test that all error types are `Send + Sync` even if `Dst: !Send +
431+
// !Sync`.
432+
433+
#[allow(dead_code)]
434+
fn is_send_sync<T: Send + Sync>(_t: T) {}
435+
436+
#[allow(dead_code)]
437+
fn alignment_err_is_send_sync<Src: Send + Sync, Dst>(err: AlignmentError<Src, Dst>) {
438+
is_send_sync(err)
439+
}
440+
441+
#[allow(dead_code)]
442+
fn size_err_is_send_sync<Src: Send + Sync, Dst>(err: SizeError<Src, Dst>) {
443+
is_send_sync(err)
444+
}
445+
446+
#[allow(dead_code)]
447+
fn validity_err_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>(
448+
err: ValidityError<Src, Dst>,
449+
) {
450+
is_send_sync(err)
451+
}
452+
453+
#[allow(dead_code)]
454+
fn convert_error_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>(
455+
err: ConvertError<
456+
AlignmentError<Src, Dst>,
457+
SizeError<Src, Dst>,
458+
ValidityError<Src, Dst>,
459+
>,
460+
) {
461+
is_send_sync(err)
462+
}
463+
}
464+
401465
#[test]
402466
fn alignment_display() {
403467
#[repr(C, align(128))]
404468
struct Aligned {
405469
bytes: [u8; 128],
406470
}
407471

472+
impl_known_layout!(elain::Align::<8>);
473+
408474
let aligned = Aligned { bytes: [0; 128] };
409475

410476
assert_eq!(
@@ -434,6 +500,11 @@ mod test {
434500
SizeError::<_, [u8]>::new(&[0u8; 1][..]).to_string(),
435501
"the conversion failed because the source was incorrectly sized to complete the conversion into the destination type: [u8]"
436502
);
503+
504+
assert_eq!(
505+
SizeError::<_, [u8; 2]>::new(&[0u8; 1][..]).to_string(),
506+
"the conversion failed because the source was incorrectly sized to complete the conversion into the destination type (2 bytes): [u8; 2]"
507+
);
437508
}
438509

439510
#[test]

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")), no_std)]
259264
#![cfg_attr(
260265
all(feature = "simd-nightly", any(target_arch = "x86", target_arch = "x86_64")),
261266
feature(stdarch_x86_avx512)

src/macros.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,7 @@ macro_rules! impl_known_layout {
553553
const _: () = {
554554
use core::ptr::NonNull;
555555

556+
#[allow(non_local_definitions)]
556557
// SAFETY: Delegates safety to `DstLayout::for_type`.
557558
unsafe impl<$($tyvar $(: ?$optbound)?)? $(, const $constvar : $constty)?> KnownLayout for $ty {
558559
#[allow(clippy::missing_inline_in_public_items)]

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)