zerocopy/
error.rs

1// Copyright 2024 The Fuchsia Authors
2//
3// Licensed under the 2-Clause BSD License <LICENSE-BSD or
4// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0
5// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
6// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
7// This file may not be copied, modified, or distributed except according to
8// those terms.
9
10//! Types related to error reporting.
11//!
12//! ## Single failure mode errors
13//!
14//! Generally speaking, zerocopy's conversions may fail for one of up to three
15//! reasons:
16//! - [`AlignmentError`]: the conversion source was improperly aligned
17//! - [`SizeError`]: the conversion source was of incorrect size
18//! - [`ValidityError`]: the conversion source contained invalid data
19//!
20//! Methods that only have one failure mode, like
21//! [`FromBytes::read_from_bytes`], return that mode's corresponding error type
22//! directly.
23//!
24//! ## Compound errors
25//!
26//! Conversion methods that have either two or three possible failure modes
27//! return one of these error types:
28//! - [`CastError`]: the error type of reference conversions
29//! - [`TryCastError`]: the error type of fallible reference conversions
30//! - [`TryReadError`]: the error type of fallible read conversions
31//!
32//! ## [`Unaligned`] destination types
33//!
34//! For [`Unaligned`] destination types, alignment errors are impossible. All
35//! compound error types support infallibly discarding the alignment error via
36//! [`From`] so long as `Dst: Unaligned`. For example, see [`<SizeError as
37//! From<ConvertError>>::from`][size-error-from].
38//!
39//! [size-error-from]: struct.SizeError.html#method.from-1
40//!
41//! ## Accessing the conversion source
42//!
43//! All error types provide an `into_src` method that converts the error into
44//! the source value underlying the failed conversion.
45//!
46//! ## Display formatting
47//!
48//! All error types provide a `Display` implementation that produces a
49//! human-readable error message. When `debug_assertions` are enabled, these
50//! error messages are verbose and may include potentially sensitive
51//! information, including:
52//!
53//! - the names of the involved types
54//! - the sizes of the involved types
55//! - the addresses of the involved types
56//! - the contents of the involved types
57//!
58//! When `debug_assertions` are disabled (as is default for `release` builds),
59//! such potentially sensitive information is excluded.
60//!
61//! In the future, we may support manually configuring this behavior. If you are
62//! interested in this feature, [let us know on GitHub][issue-1457] so we know
63//! to prioritize it.
64//!
65//! [issue-1457]: https://github.com/google/zerocopy/issues/1457
66//!
67//! ## Validation order
68//!
69//! Our conversion methods typically check alignment, then size, then bit
70//! validity. However, we do not guarantee that this is always the case, and
71//! this behavior may change between releases.
72//!
73//! ## `Send`, `Sync`, and `'static`
74//!
75//! Our error types are `Send`, `Sync`, and `'static` when their `Src` parameter
76//! is `Send`, `Sync`, or `'static`, respectively. This can cause issues when an
77//! error is sent or synchronized across threads; e.g.:
78//!
79//! ```compile_fail,E0515
80//! use zerocopy::*;
81//!
82//! let result: SizeError<&[u8], u32> = std::thread::spawn(|| {
83//!     let source = &mut [0u8, 1, 2][..];
84//!     // Try (and fail) to read a `u32` from `source`.
85//!     u32::read_from_bytes(source).unwrap_err()
86//! }).join().unwrap();
87//! ```
88//!
89//! To work around this, use [`map_src`][CastError::map_src] to convert the
90//! source parameter to an unproblematic type; e.g.:
91//!
92//! ```
93//! use zerocopy::*;
94//!
95//! let result: SizeError<(), u32> = std::thread::spawn(|| {
96//!     let source = &mut [0u8, 1, 2][..];
97//!     // Try (and fail) to read a `u32` from `source`.
98//!     u32::read_from_bytes(source).unwrap_err()
99//!         // Erase the error source.
100//!         .map_src(drop)
101//! }).join().unwrap();
102//! ```
103//!
104//! Alternatively, use `.to_string()` to eagerly convert the error into a
105//! human-readable message; e.g.:
106//!
107//! ```
108//! use zerocopy::*;
109//!
110//! let result: Result<u32, String> = std::thread::spawn(|| {
111//!     let source = &mut [0u8, 1, 2][..];
112//!     // Try (and fail) to read a `u32` from `source`.
113//!     u32::read_from_bytes(source)
114//!         // Eagerly render the error message.
115//!         .map_err(|err| err.to_string())
116//! }).join().unwrap();
117//! ```
118#[cfg(zerocopy_core_error_1_81_0)]
119use core::error::Error;
120use core::{
121    convert::Infallible,
122    fmt::{self, Debug, Write},
123    ops::Deref,
124};
125#[cfg(all(not(zerocopy_core_error_1_81_0), any(feature = "std", test)))]
126use std::error::Error;
127
128use crate::{util::SendSyncPhantomData, KnownLayout, TryFromBytes, Unaligned};
129#[cfg(doc)]
130use crate::{FromBytes, Ref};
131
132/// Zerocopy's generic error type.
133///
134/// Generally speaking, zerocopy's conversions may fail for one of up to three
135/// reasons:
136/// - [`AlignmentError`]: the conversion source was improperly aligned
137/// - [`SizeError`]: the conversion source was of incorrect size
138/// - [`ValidityError`]: the conversion source contained invalid data
139///
140/// However, not all conversions produce all errors. For instance,
141/// [`FromBytes::ref_from_bytes`] may fail due to alignment or size issues, but
142/// not validity issues. This generic error type captures these
143/// (im)possibilities via parameterization: `A` is parameterized with
144/// [`AlignmentError`], `S` is parameterized with [`SizeError`], and `V` is
145/// parameterized with [`Infallible`].
146///
147/// Zerocopy never uses this type directly in its API. Rather, we provide three
148/// pre-parameterized aliases:
149/// - [`CastError`]: the error type of reference conversions
150/// - [`TryCastError`]: the error type of fallible reference conversions
151/// - [`TryReadError`]: the error type of fallible read conversions
152#[derive(PartialEq, Eq)]
153pub enum ConvertError<A, S, V> {
154    /// The conversion source was improperly aligned.
155    Alignment(A),
156    /// The conversion source was of incorrect size.
157    Size(S),
158    /// The conversion source contained invalid data.
159    Validity(V),
160}
161
162impl<Src, Dst: ?Sized + Unaligned, S, V> From<ConvertError<AlignmentError<Src, Dst>, S, V>>
163    for ConvertError<Infallible, S, V>
164{
165    /// Infallibly discards the alignment error from this `ConvertError` since
166    /// `Dst` is unaligned.
167    ///
168    /// Since [`Dst: Unaligned`], it is impossible to encounter an alignment
169    /// error. This method permits discarding that alignment error infallibly
170    /// and replacing it with [`Infallible`].
171    ///
172    /// [`Dst: Unaligned`]: crate::Unaligned
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// use core::convert::Infallible;
178    /// use zerocopy::*;
179    /// # use zerocopy_derive::*;
180    ///
181    /// #[derive(TryFromBytes, KnownLayout, Unaligned, Immutable)]
182    /// #[repr(C, packed)]
183    /// struct Bools {
184    ///     one: bool,
185    ///     two: bool,
186    ///     many: [bool],
187    /// }
188    ///
189    /// impl Bools {
190    ///     fn parse(bytes: &[u8]) -> Result<&Bools, AlignedTryCastError<&[u8], Bools>> {
191    ///         // Since `Bools: Unaligned`, we can infallibly discard
192    ///         // the alignment error.
193    ///         Bools::try_ref_from_bytes(bytes).map_err(Into::into)
194    ///     }
195    /// }
196    /// ```
197    #[inline]
198    fn from(err: ConvertError<AlignmentError<Src, Dst>, S, V>) -> ConvertError<Infallible, S, V> {
199        match err {
200            ConvertError::Alignment(e) => ConvertError::Alignment(Infallible::from(e)),
201            ConvertError::Size(e) => ConvertError::Size(e),
202            ConvertError::Validity(e) => ConvertError::Validity(e),
203        }
204    }
205}
206
207impl<A: fmt::Debug, S: fmt::Debug, V: fmt::Debug> fmt::Debug for ConvertError<A, S, V> {
208    #[inline]
209    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210        match self {
211            Self::Alignment(e) => f.debug_tuple("Alignment").field(e).finish(),
212            Self::Size(e) => f.debug_tuple("Size").field(e).finish(),
213            Self::Validity(e) => f.debug_tuple("Validity").field(e).finish(),
214        }
215    }
216}
217
218/// Produces a human-readable error message.
219///
220/// The message differs between debug and release builds. When
221/// `debug_assertions` are enabled, this message is verbose and includes
222/// potentially sensitive information.
223impl<A: fmt::Display, S: fmt::Display, V: fmt::Display> fmt::Display for ConvertError<A, S, V> {
224    #[inline]
225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226        match self {
227            Self::Alignment(e) => e.fmt(f),
228            Self::Size(e) => e.fmt(f),
229            Self::Validity(e) => e.fmt(f),
230        }
231    }
232}
233
234#[cfg(any(zerocopy_core_error_1_81_0, feature = "std", test))]
235#[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))]
236impl<A, S, V> Error for ConvertError<A, S, V>
237where
238    A: fmt::Display + fmt::Debug,
239    S: fmt::Display + fmt::Debug,
240    V: fmt::Display + fmt::Debug,
241{
242}
243
244/// The error emitted if the conversion source is improperly aligned.
245#[derive(PartialEq, Eq)]
246pub struct AlignmentError<Src, Dst: ?Sized> {
247    /// The source value involved in the conversion.
248    src: Src,
249    /// The inner destination type inolved in the conversion.
250    ///
251    /// INVARIANT: An `AlignmentError` may only be constructed if `Dst`'s
252    /// alignment requirement is greater than one.
253    dst: SendSyncPhantomData<Dst>,
254}
255
256impl<Src, Dst: ?Sized> AlignmentError<Src, Dst> {
257    /// # Safety
258    ///
259    /// The caller must ensure that `Dst`'s alignment requirement is greater
260    /// than one.
261    pub(crate) unsafe fn new_unchecked(src: Src) -> Self {
262        // INVARIANT: The caller guarantees that `Dst`'s alignment requirement
263        // is greater than one.
264        Self { src, dst: SendSyncPhantomData::default() }
265    }
266
267    /// Produces the source underlying the failed conversion.
268    #[inline]
269    pub fn into_src(self) -> Src {
270        self.src
271    }
272
273    pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> AlignmentError<NewSrc, Dst> {
274        // INVARIANT: `with_src` doesn't change the type of `Dst`, so the
275        // invariant that `Dst`'s alignment requirement is greater than one is
276        // preserved.
277        AlignmentError { src: new_src, dst: SendSyncPhantomData::default() }
278    }
279
280    /// Maps the source value associated with the conversion error.
281    ///
282    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
283    /// bounds][self#send-sync-and-static].
284    ///
285    /// # Examples
286    ///
287    /// ```
288    /// use zerocopy::*;
289    ///
290    /// let unaligned = Unalign::new(0u16);
291    ///
292    /// // Attempt to deref `unaligned`. This might fail with an alignment error.
293    /// let maybe_n: Result<&u16, AlignmentError<&Unalign<u16>, u16>> = unaligned.try_deref();
294    ///
295    /// // Map the error's source to its address as a usize.
296    /// let maybe_n: Result<&u16, AlignmentError<usize, u16>> = maybe_n.map_err(|err| {
297    ///     err.map_src(|src| src as *const _ as usize)
298    /// });
299    /// ```
300    #[inline]
301    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> AlignmentError<NewSrc, Dst> {
302        AlignmentError { src: f(self.src), dst: SendSyncPhantomData::default() }
303    }
304
305    pub(crate) fn into<S, V>(self) -> ConvertError<Self, S, V> {
306        ConvertError::Alignment(self)
307    }
308
309    /// Format extra details for a verbose, human-readable error message.
310    ///
311    /// This formatting may include potentially sensitive information.
312    fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
313    where
314        Src: Deref,
315        Dst: KnownLayout,
316    {
317        #[allow(clippy::as_conversions)]
318        let addr = self.src.deref() as *const _ as *const ();
319        let addr_align = 2usize.pow((crate::util::AsAddress::addr(addr)).trailing_zeros());
320
321        f.write_str("\n\nSource type: ")?;
322        f.write_str(core::any::type_name::<Src>())?;
323
324        f.write_str("\nSource address: ")?;
325        addr.fmt(f)?;
326        f.write_str(" (a multiple of ")?;
327        addr_align.fmt(f)?;
328        f.write_str(")")?;
329
330        f.write_str("\nDestination type: ")?;
331        f.write_str(core::any::type_name::<Dst>())?;
332
333        f.write_str("\nDestination alignment: ")?;
334        <Dst as KnownLayout>::LAYOUT.align.get().fmt(f)?;
335
336        Ok(())
337    }
338}
339
340impl<Src, Dst: ?Sized + Unaligned> From<AlignmentError<Src, Dst>> for Infallible {
341    #[inline(always)]
342    fn from(_: AlignmentError<Src, Dst>) -> Infallible {
343        // SAFETY: `AlignmentError`s can only be constructed when `Dst`'s
344        // alignment requirement is greater than one. In this block, `Dst:
345        // Unaligned`, which means that its alignment requirement is equal to
346        // one. Thus, it's not possible to reach here at runtime.
347        unsafe { core::hint::unreachable_unchecked() }
348    }
349}
350
351#[cfg(test)]
352impl<Src, Dst> AlignmentError<Src, Dst> {
353    // A convenience constructor so that test code doesn't need to write
354    // `unsafe`.
355    fn new_checked(src: Src) -> AlignmentError<Src, Dst> {
356        assert_ne!(core::mem::align_of::<Dst>(), 1);
357        // SAFETY: The preceding assertion guarantees that `Dst`'s alignment
358        // requirement is greater than one.
359        unsafe { AlignmentError::new_unchecked(src) }
360    }
361}
362
363impl<Src, Dst: ?Sized> fmt::Debug for AlignmentError<Src, Dst> {
364    #[inline]
365    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
366        f.debug_struct("AlignmentError").finish()
367    }
368}
369
370/// Produces a human-readable error message.
371///
372/// The message differs between debug and release builds. When
373/// `debug_assertions` are enabled, this message is verbose and includes
374/// potentially sensitive information.
375impl<Src, Dst: ?Sized> fmt::Display for AlignmentError<Src, Dst>
376where
377    Src: Deref,
378    Dst: KnownLayout,
379{
380    #[inline]
381    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382        f.write_str("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.")?;
383
384        if cfg!(debug_assertions) {
385            self.display_verbose_extras(f)
386        } else {
387            Ok(())
388        }
389    }
390}
391
392#[cfg(any(zerocopy_core_error_1_81_0, feature = "std", test))]
393#[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))]
394impl<Src, Dst: ?Sized> Error for AlignmentError<Src, Dst>
395where
396    Src: Deref,
397    Dst: KnownLayout,
398{
399}
400
401impl<Src, Dst: ?Sized, S, V> From<AlignmentError<Src, Dst>>
402    for ConvertError<AlignmentError<Src, Dst>, S, V>
403{
404    #[inline(always)]
405    fn from(err: AlignmentError<Src, Dst>) -> Self {
406        Self::Alignment(err)
407    }
408}
409
410/// The error emitted if the conversion source is of incorrect size.
411#[derive(PartialEq, Eq)]
412pub struct SizeError<Src, Dst: ?Sized> {
413    /// The source value involved in the conversion.
414    src: Src,
415    /// The inner destination type inolved in the conversion.
416    dst: SendSyncPhantomData<Dst>,
417}
418
419impl<Src, Dst: ?Sized> SizeError<Src, Dst> {
420    pub(crate) fn new(src: Src) -> Self {
421        Self { src, dst: SendSyncPhantomData::default() }
422    }
423
424    /// Produces the source underlying the failed conversion.
425    #[inline]
426    pub fn into_src(self) -> Src {
427        self.src
428    }
429
430    /// Sets the source value associated with the conversion error.
431    pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> SizeError<NewSrc, Dst> {
432        SizeError { src: new_src, dst: SendSyncPhantomData::default() }
433    }
434
435    /// Maps the source value associated with the conversion error.
436    ///
437    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
438    /// bounds][self#send-sync-and-static].
439    ///
440    /// # Examples
441    ///
442    /// ```
443    /// use zerocopy::*;
444    ///
445    /// let source: [u8; 3] = [0, 1, 2];
446    ///
447    /// // Try to read a `u32` from `source`. This will fail because there are insufficient
448    /// // bytes in `source`.
449    /// let maybe_u32: Result<u32, SizeError<&[u8], u32>> = u32::read_from_bytes(&source[..]);
450    ///
451    /// // Map the error's source to its size.
452    /// let maybe_u32: Result<u32, SizeError<usize, u32>> = maybe_u32.map_err(|err| {
453    ///     err.map_src(|src| src.len())
454    /// });
455    /// ```
456    #[inline]
457    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> SizeError<NewSrc, Dst> {
458        SizeError { src: f(self.src), dst: SendSyncPhantomData::default() }
459    }
460
461    /// Sets the destination type associated with the conversion error.
462    pub(crate) fn with_dst<NewDst: ?Sized>(self) -> SizeError<Src, NewDst> {
463        SizeError { src: self.src, dst: SendSyncPhantomData::default() }
464    }
465
466    /// Converts the error into a general [`ConvertError`].
467    pub(crate) fn into<A, V>(self) -> ConvertError<A, Self, V> {
468        ConvertError::Size(self)
469    }
470
471    /// Format extra details for a verbose, human-readable error message.
472    ///
473    /// This formatting may include potentially sensitive information.
474    fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
475    where
476        Src: Deref,
477        Dst: KnownLayout,
478    {
479        // include the source type
480        f.write_str("\nSource type: ")?;
481        f.write_str(core::any::type_name::<Src>())?;
482
483        // include the source.deref() size
484        let src_size = core::mem::size_of_val(&*self.src);
485        f.write_str("\nSource size: ")?;
486        src_size.fmt(f)?;
487        f.write_str(" byte")?;
488        if src_size != 1 {
489            f.write_char('s')?;
490        }
491
492        // if `Dst` is `Sized`, include the `Dst` size
493        if let crate::SizeInfo::Sized { size } = Dst::LAYOUT.size_info {
494            f.write_str("\nDestination size: ")?;
495            size.fmt(f)?;
496            f.write_str(" byte")?;
497            if size != 1 {
498                f.write_char('s')?;
499            }
500        }
501
502        // include the destination type
503        f.write_str("\nDestination type: ")?;
504        f.write_str(core::any::type_name::<Dst>())?;
505
506        Ok(())
507    }
508}
509
510impl<Src, Dst: ?Sized> fmt::Debug for SizeError<Src, Dst> {
511    #[inline]
512    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
513        f.debug_struct("SizeError").finish()
514    }
515}
516
517/// Produces a human-readable error message.
518///
519/// The message differs between debug and release builds. When
520/// `debug_assertions` are enabled, this message is verbose and includes
521/// potentially sensitive information.
522impl<Src, Dst: ?Sized> fmt::Display for SizeError<Src, Dst>
523where
524    Src: Deref,
525    Dst: KnownLayout,
526{
527    #[inline]
528    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
529        f.write_str("The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.")?;
530        if cfg!(debug_assertions) {
531            f.write_str("\n")?;
532            self.display_verbose_extras(f)?;
533        }
534        Ok(())
535    }
536}
537
538#[cfg(any(zerocopy_core_error_1_81_0, feature = "std", test))]
539#[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))]
540impl<Src, Dst: ?Sized> Error for SizeError<Src, Dst>
541where
542    Src: Deref,
543    Dst: KnownLayout,
544{
545}
546
547impl<Src, Dst: ?Sized, A, V> From<SizeError<Src, Dst>> for ConvertError<A, SizeError<Src, Dst>, V> {
548    #[inline(always)]
549    fn from(err: SizeError<Src, Dst>) -> Self {
550        Self::Size(err)
551    }
552}
553
554/// The error emitted if the conversion source contains invalid data.
555#[derive(PartialEq, Eq)]
556pub struct ValidityError<Src, Dst: ?Sized + TryFromBytes> {
557    /// The source value involved in the conversion.
558    pub(crate) src: Src,
559    /// The inner destination type inolved in the conversion.
560    dst: SendSyncPhantomData<Dst>,
561}
562
563impl<Src, Dst: ?Sized + TryFromBytes> ValidityError<Src, Dst> {
564    pub(crate) fn new(src: Src) -> Self {
565        Self { src, dst: SendSyncPhantomData::default() }
566    }
567
568    /// Produces the source underlying the failed conversion.
569    #[inline]
570    pub fn into_src(self) -> Src {
571        self.src
572    }
573
574    /// Maps the source value associated with the conversion error.
575    ///
576    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
577    /// bounds][self#send-sync-and-static].
578    ///
579    /// # Examples
580    ///
581    /// ```
582    /// use zerocopy::*;
583    ///
584    /// let source: u8 = 42;
585    ///
586    /// // Try to transmute the `source` to a `bool`. This will fail.
587    /// let maybe_bool: Result<bool, ValidityError<u8, bool>> = try_transmute!(source);
588    ///
589    /// // Drop the error's source.
590    /// let maybe_bool: Result<bool, ValidityError<(), bool>> = maybe_bool.map_err(|err| {
591    ///     err.map_src(drop)
592    /// });
593    /// ```
594    #[inline]
595    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> ValidityError<NewSrc, Dst> {
596        ValidityError { src: f(self.src), dst: SendSyncPhantomData::default() }
597    }
598
599    /// Converts the error into a general [`ConvertError`].
600    pub(crate) fn into<A, S>(self) -> ConvertError<A, S, Self> {
601        ConvertError::Validity(self)
602    }
603
604    /// Format extra details for a verbose, human-readable error message.
605    ///
606    /// This formatting may include potentially sensitive information.
607    fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
608    where
609        Dst: KnownLayout,
610    {
611        f.write_str("Destination type: ")?;
612        f.write_str(core::any::type_name::<Dst>())?;
613        Ok(())
614    }
615}
616
617impl<Src, Dst: ?Sized + TryFromBytes> fmt::Debug for ValidityError<Src, Dst> {
618    #[inline]
619    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
620        f.debug_struct("ValidityError").finish()
621    }
622}
623
624/// Produces a human-readable error message.
625///
626/// The message differs between debug and release builds. When
627/// `debug_assertions` are enabled, this message is verbose and includes
628/// potentially sensitive information.
629impl<Src, Dst: ?Sized> fmt::Display for ValidityError<Src, Dst>
630where
631    Dst: KnownLayout + TryFromBytes,
632{
633    #[inline]
634    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
635        f.write_str("The conversion failed because the source bytes are not a valid value of the destination type.")?;
636        if cfg!(debug_assertions) {
637            f.write_str("\n\n")?;
638            self.display_verbose_extras(f)?;
639        }
640        Ok(())
641    }
642}
643
644#[cfg(any(zerocopy_core_error_1_81_0, feature = "std", test))]
645#[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))]
646impl<Src, Dst: ?Sized> Error for ValidityError<Src, Dst> where Dst: KnownLayout + TryFromBytes {}
647
648impl<Src, Dst: ?Sized + TryFromBytes, A, S> From<ValidityError<Src, Dst>>
649    for ConvertError<A, S, ValidityError<Src, Dst>>
650{
651    #[inline(always)]
652    fn from(err: ValidityError<Src, Dst>) -> Self {
653        Self::Validity(err)
654    }
655}
656
657/// The error type of reference conversions.
658///
659/// Reference conversions, like [`FromBytes::ref_from_bytes`] may emit
660/// [alignment](AlignmentError) and [size](SizeError) errors.
661// Bounds on generic parameters are not enforced in type aliases, but they do
662// appear in rustdoc.
663#[allow(type_alias_bounds)]
664pub type CastError<Src, Dst: ?Sized> =
665    ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, Infallible>;
666
667impl<Src, Dst: ?Sized> CastError<Src, Dst> {
668    /// Produces the source underlying the failed conversion.
669    #[inline]
670    pub fn into_src(self) -> Src {
671        match self {
672            Self::Alignment(e) => e.src,
673            Self::Size(e) => e.src,
674            Self::Validity(i) => match i {},
675        }
676    }
677
678    /// Sets the source value associated with the conversion error.
679    pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> CastError<NewSrc, Dst> {
680        match self {
681            Self::Alignment(e) => CastError::Alignment(e.with_src(new_src)),
682            Self::Size(e) => CastError::Size(e.with_src(new_src)),
683            Self::Validity(i) => match i {},
684        }
685    }
686
687    /// Maps the source value associated with the conversion error.
688    ///
689    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
690    /// bounds][self#send-sync-and-static].
691    ///
692    /// # Examples
693    ///
694    /// ```
695    /// use zerocopy::*;
696    ///
697    /// let source: [u8; 3] = [0, 1, 2];
698    ///
699    /// // Try to read a `u32` from `source`. This will fail because there are insufficient
700    /// // bytes in `source`.
701    /// let maybe_u32: Result<&u32, CastError<&[u8], u32>> = u32::ref_from_bytes(&source[..]);
702    ///
703    /// // Map the error's source to its size and address.
704    /// let maybe_u32: Result<&u32, CastError<(usize, usize), u32>> = maybe_u32.map_err(|err| {
705    ///     err.map_src(|src| (src.len(), src.as_ptr() as usize))
706    /// });
707    /// ```
708    #[inline]
709    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> CastError<NewSrc, Dst> {
710        match self {
711            Self::Alignment(e) => CastError::Alignment(e.map_src(f)),
712            Self::Size(e) => CastError::Size(e.map_src(f)),
713            Self::Validity(i) => match i {},
714        }
715    }
716
717    /// Converts the error into a general [`ConvertError`].
718    pub(crate) fn into(self) -> TryCastError<Src, Dst>
719    where
720        Dst: TryFromBytes,
721    {
722        match self {
723            Self::Alignment(e) => TryCastError::Alignment(e),
724            Self::Size(e) => TryCastError::Size(e),
725            Self::Validity(i) => match i {},
726        }
727    }
728}
729
730impl<Src, Dst: ?Sized + Unaligned> From<CastError<Src, Dst>> for SizeError<Src, Dst> {
731    /// Infallibly extracts the [`SizeError`] from this `CastError` since `Dst`
732    /// is unaligned.
733    ///
734    /// Since [`Dst: Unaligned`], it is impossible to encounter an alignment
735    /// error, and so the only error that can be encountered at runtime is a
736    /// [`SizeError`]. This method permits extracting that `SizeError`
737    /// infallibly.
738    ///
739    /// [`Dst: Unaligned`]: crate::Unaligned
740    ///
741    /// # Examples
742    ///
743    /// ```rust
744    /// use zerocopy::*;
745    /// # use zerocopy_derive::*;
746    ///
747    /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
748    /// #[repr(C)]
749    /// struct UdpHeader {
750    ///     src_port: [u8; 2],
751    ///     dst_port: [u8; 2],
752    ///     length: [u8; 2],
753    ///     checksum: [u8; 2],
754    /// }
755    ///
756    /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
757    /// #[repr(C, packed)]
758    /// struct UdpPacket {
759    ///     header: UdpHeader,
760    ///     body: [u8],
761    /// }
762    ///
763    /// impl UdpPacket {
764    ///     pub fn parse(bytes: &[u8]) -> Result<&UdpPacket, SizeError<&[u8], UdpPacket>> {
765    ///         // Since `UdpPacket: Unaligned`, we can map the `CastError` to a `SizeError`.
766    ///         UdpPacket::ref_from_bytes(bytes).map_err(Into::into)
767    ///     }
768    /// }
769    /// ```
770    #[inline(always)]
771    fn from(err: CastError<Src, Dst>) -> SizeError<Src, Dst> {
772        match err {
773            #[allow(unreachable_code)]
774            CastError::Alignment(e) => match Infallible::from(e) {},
775            CastError::Size(e) => e,
776            CastError::Validity(i) => match i {},
777        }
778    }
779}
780
781/// The error type of fallible reference conversions.
782///
783/// Fallible reference conversions, like [`TryFromBytes::try_ref_from_bytes`]
784/// may emit [alignment](AlignmentError), [size](SizeError), and
785/// [validity](ValidityError) errors.
786// Bounds on generic parameters are not enforced in type aliases, but they do
787// appear in rustdoc.
788#[allow(type_alias_bounds)]
789pub type TryCastError<Src, Dst: ?Sized + TryFromBytes> =
790    ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, ValidityError<Src, Dst>>;
791
792// FIXME(#1139): Remove the `TryFromBytes` here and in other downstream
793// locations (all the way to `ValidityError`) if we determine it's not necessary
794// for rich validity errors.
795impl<Src, Dst: ?Sized + TryFromBytes> TryCastError<Src, Dst> {
796    /// Produces the source underlying the failed conversion.
797    #[inline]
798    pub fn into_src(self) -> Src {
799        match self {
800            Self::Alignment(e) => e.src,
801            Self::Size(e) => e.src,
802            Self::Validity(e) => e.src,
803        }
804    }
805
806    /// Maps the source value associated with the conversion error.
807    ///
808    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
809    /// bounds][self#send-sync-and-static].
810    ///
811    /// # Examples
812    ///
813    /// ```
814    /// use core::num::NonZeroU32;
815    /// use zerocopy::*;
816    ///
817    /// let source: [u8; 3] = [0, 0, 0];
818    ///
819    /// // Try to read a `NonZeroU32` from `source`.
820    /// let maybe_u32: Result<&NonZeroU32, TryCastError<&[u8], NonZeroU32>>
821    ///     = NonZeroU32::try_ref_from_bytes(&source[..]);
822    ///
823    /// // Map the error's source to its size and address.
824    /// let maybe_u32: Result<&NonZeroU32, TryCastError<(usize, usize), NonZeroU32>> =
825    ///     maybe_u32.map_err(|err| {
826    ///         err.map_src(|src| (src.len(), src.as_ptr() as usize))
827    ///     });
828    /// ```
829    #[inline]
830    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryCastError<NewSrc, Dst> {
831        match self {
832            Self::Alignment(e) => TryCastError::Alignment(e.map_src(f)),
833            Self::Size(e) => TryCastError::Size(e.map_src(f)),
834            Self::Validity(e) => TryCastError::Validity(e.map_src(f)),
835        }
836    }
837}
838
839impl<Src, Dst: ?Sized + TryFromBytes> From<CastError<Src, Dst>> for TryCastError<Src, Dst> {
840    #[inline]
841    fn from(value: CastError<Src, Dst>) -> Self {
842        match value {
843            CastError::Alignment(e) => Self::Alignment(e),
844            CastError::Size(e) => Self::Size(e),
845            CastError::Validity(i) => match i {},
846        }
847    }
848}
849
850/// The error type of fallible read-conversions.
851///
852/// Fallible read-conversions, like [`TryFromBytes::try_read_from_bytes`] may emit
853/// [size](SizeError) and [validity](ValidityError) errors, but not alignment errors.
854// Bounds on generic parameters are not enforced in type aliases, but they do
855// appear in rustdoc.
856#[allow(type_alias_bounds)]
857pub type TryReadError<Src, Dst: ?Sized + TryFromBytes> =
858    ConvertError<Infallible, SizeError<Src, Dst>, ValidityError<Src, Dst>>;
859
860impl<Src, Dst: ?Sized + TryFromBytes> TryReadError<Src, Dst> {
861    /// Produces the source underlying the failed conversion.
862    #[inline]
863    pub fn into_src(self) -> Src {
864        match self {
865            Self::Alignment(i) => match i {},
866            Self::Size(e) => e.src,
867            Self::Validity(e) => e.src,
868        }
869    }
870
871    /// Maps the source value associated with the conversion error.
872    ///
873    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
874    /// bounds][self#send-sync-and-static].
875    ///
876    /// # Examples
877    ///
878    /// ```
879    /// use core::num::NonZeroU32;
880    /// use zerocopy::*;
881    ///
882    /// let source: [u8; 3] = [0, 0, 0];
883    ///
884    /// // Try to read a `NonZeroU32` from `source`.
885    /// let maybe_u32: Result<NonZeroU32, TryReadError<&[u8], NonZeroU32>>
886    ///     = NonZeroU32::try_read_from_bytes(&source[..]);
887    ///
888    /// // Map the error's source to its size.
889    /// let maybe_u32: Result<NonZeroU32, TryReadError<usize, NonZeroU32>> =
890    ///     maybe_u32.map_err(|err| {
891    ///         err.map_src(|src| src.len())
892    ///     });
893    /// ```
894    #[inline]
895    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryReadError<NewSrc, Dst> {
896        match self {
897            Self::Alignment(i) => match i {},
898            Self::Size(e) => TryReadError::Size(e.map_src(f)),
899            Self::Validity(e) => TryReadError::Validity(e.map_src(f)),
900        }
901    }
902}
903
904/// The error type of well-aligned, fallible casts.
905///
906/// This is like [`TryCastError`], but for casts that are always well-aligned.
907/// It is identical to `TryCastError`, except that its alignment error is
908/// [`Infallible`].
909///
910/// As of this writing, none of zerocopy's API produces this error directly.
911/// However, it is useful since it permits users to infallibly discard alignment
912/// errors when they can prove statically that alignment errors are impossible.
913///
914/// # Examples
915///
916/// ```
917/// use core::convert::Infallible;
918/// use zerocopy::*;
919/// # use zerocopy_derive::*;
920///
921/// #[derive(TryFromBytes, KnownLayout, Unaligned, Immutable)]
922/// #[repr(C, packed)]
923/// struct Bools {
924///     one: bool,
925///     two: bool,
926///     many: [bool],
927/// }
928///
929/// impl Bools {
930///     fn parse(bytes: &[u8]) -> Result<&Bools, AlignedTryCastError<&[u8], Bools>> {
931///         // Since `Bools: Unaligned`, we can infallibly discard
932///         // the alignment error.
933///         Bools::try_ref_from_bytes(bytes).map_err(Into::into)
934///     }
935/// }
936/// ```
937#[allow(type_alias_bounds)]
938pub type AlignedTryCastError<Src, Dst: ?Sized + TryFromBytes> =
939    ConvertError<Infallible, SizeError<Src, Dst>, ValidityError<Src, Dst>>;
940
941/// The error type of a failed allocation.
942///
943/// This type is intended to be deprecated in favor of the standard library's
944/// [`AllocError`] type once it is stabilized. When that happens, this type will
945/// be replaced by a type alias to the standard library type. We do not intend
946/// to treat this as a breaking change; users who wish to avoid breakage should
947/// avoid writing code which assumes that this is *not* such an alias. For
948/// example, implementing the same trait for both types will result in an impl
949/// conflict once this type is an alias.
950///
951/// [`AllocError`]: https://doc.rust-lang.org/alloc/alloc/struct.AllocError.html
952#[derive(Copy, Clone, PartialEq, Eq, Debug)]
953pub struct AllocError;
954
955#[cfg(test)]
956mod tests {
957    use super::*;
958
959    #[test]
960    fn test_send_sync() {
961        // Test that all error types are `Send + Sync` even if `Dst: !Send +
962        // !Sync`.
963
964        #[allow(dead_code)]
965        fn is_send_sync<T: Send + Sync>(_t: T) {}
966
967        #[allow(dead_code)]
968        fn alignment_err_is_send_sync<Src: Send + Sync, Dst>(err: AlignmentError<Src, Dst>) {
969            is_send_sync(err)
970        }
971
972        #[allow(dead_code)]
973        fn size_err_is_send_sync<Src: Send + Sync, Dst>(err: SizeError<Src, Dst>) {
974            is_send_sync(err)
975        }
976
977        #[allow(dead_code)]
978        fn validity_err_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>(
979            err: ValidityError<Src, Dst>,
980        ) {
981            is_send_sync(err)
982        }
983
984        #[allow(dead_code)]
985        fn convert_error_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>(
986            err: ConvertError<
987                AlignmentError<Src, Dst>,
988                SizeError<Src, Dst>,
989                ValidityError<Src, Dst>,
990            >,
991        ) {
992            is_send_sync(err)
993        }
994    }
995
996    #[test]
997    fn alignment_display() {
998        #[repr(C, align(128))]
999        struct Aligned {
1000            bytes: [u8; 128],
1001        }
1002
1003        impl_known_layout!(elain::Align::<8>);
1004
1005        let aligned = Aligned { bytes: [0; 128] };
1006
1007        let bytes = &aligned.bytes[1..];
1008        let addr = crate::util::AsAddress::addr(bytes);
1009        assert_eq!(
1010            AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
1011            format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
1012            \nSource type: &[u8]\
1013            \nSource address: 0x{:x} (a multiple of 1)\
1014            \nDestination type: elain::Align<8>\
1015            \nDestination alignment: 8", addr)
1016        );
1017
1018        let bytes = &aligned.bytes[2..];
1019        let addr = crate::util::AsAddress::addr(bytes);
1020        assert_eq!(
1021            AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
1022            format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
1023            \nSource type: &[u8]\
1024            \nSource address: 0x{:x} (a multiple of 2)\
1025            \nDestination type: elain::Align<8>\
1026            \nDestination alignment: 8", addr)
1027        );
1028
1029        let bytes = &aligned.bytes[3..];
1030        let addr = crate::util::AsAddress::addr(bytes);
1031        assert_eq!(
1032            AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
1033            format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
1034            \nSource type: &[u8]\
1035            \nSource address: 0x{:x} (a multiple of 1)\
1036            \nDestination type: elain::Align<8>\
1037            \nDestination alignment: 8", addr)
1038        );
1039
1040        let bytes = &aligned.bytes[4..];
1041        let addr = crate::util::AsAddress::addr(bytes);
1042        assert_eq!(
1043            AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
1044            format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
1045            \nSource type: &[u8]\
1046            \nSource address: 0x{:x} (a multiple of 4)\
1047            \nDestination type: elain::Align<8>\
1048            \nDestination alignment: 8", addr)
1049        );
1050    }
1051
1052    #[test]
1053    fn size_display() {
1054        assert_eq!(
1055            SizeError::<_, [u8]>::new(&[0u8; 2][..]).to_string(),
1056            "The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.\n\
1057            \nSource type: &[u8]\
1058            \nSource size: 2 bytes\
1059            \nDestination type: [u8]"
1060        );
1061
1062        assert_eq!(
1063            SizeError::<_, [u8; 2]>::new(&[0u8; 1][..]).to_string(),
1064            "The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.\n\
1065            \nSource type: &[u8]\
1066            \nSource size: 1 byte\
1067            \nDestination size: 2 bytes\
1068            \nDestination type: [u8; 2]"
1069        );
1070    }
1071
1072    #[test]
1073    fn validity_display() {
1074        assert_eq!(
1075            ValidityError::<_, bool>::new(&[2u8; 1][..]).to_string(),
1076            "The conversion failed because the source bytes are not a valid value of the destination type.\n\
1077            \n\
1078            Destination type: bool"
1079        );
1080    }
1081}