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