1//! This is a densely packed error representation which is used on targets with
2//! 64-bit pointers.
3//!
4//! (Note that `bitpacked` vs `unpacked` here has no relationship to
5//! `#[repr(packed)]`, it just refers to attempting to use any available bits in
6//! a more clever manner than `rustc`'s default layout algorithm would).
7//!
8//! Conceptually, it stores the same data as the "unpacked" equivalent we use on
9//! other targets. Specifically, you can imagine it as an optimized version of
10//! the following enum (which is roughly equivalent to what's stored by
11//! `repr_unpacked::Repr`, e.g. `super::ErrorData<Box<Custom>>`):
12//!
13//! ```ignore (exposition-only)
14//! enum ErrorData {
15//! Os(i32),
16//! Simple(ErrorKind),
17//! SimpleMessage(&'static SimpleMessage),
18//! Custom(Box<Custom>),
19//! }
20//! ```
21//!
22//! However, it packs this data into a 64bit non-zero value.
23//!
24//! This optimization not only allows `io::Error` to occupy a single pointer,
25//! but improves `io::Result` as well, especially for situations like
26//! `io::Result<()>` (which is now 64 bits) or `io::Result<u64>` (which is now
27//! 128 bits), which are quite common.
28//!
29//! # Layout
30//! Tagged values are 64 bits, with the 2 least significant bits used for the
31//! tag. This means there are 4 "variants":
32//!
33//! - **Tag 0b00**: The first variant is equivalent to
34//! `ErrorData::SimpleMessage`, and holds a `&'static SimpleMessage` directly.
35//!
36//! `SimpleMessage` has an alignment >= 4 (which is requested with
37//! `#[repr(align)]` and checked statically at the bottom of this file), which
38//! means every `&'static SimpleMessage` should have the both tag bits as 0,
39//! meaning its tagged and untagged representation are equivalent.
40//!
41//! This means we can skip tagging it, which is necessary as this variant can
42//! be constructed from a `const fn`, which probably cannot tag pointers (or
43//! at least it would be difficult).
44//!
45//! - **Tag 0b01**: The other pointer variant holds the data for
46//! `ErrorData::Custom` and the remaining 62 bits are used to store a
47//! `Box<Custom>`. `Custom` also has alignment >= 4, so the bottom two bits
48//! are free to use for the tag.
49//!
50//! The only important thing to note is that `ptr::wrapping_add` and
51//! `ptr::wrapping_sub` are used to tag the pointer, rather than bitwise
52//! operations. This should preserve the pointer's provenance, which would
53//! otherwise be lost.
54//!
55//! - **Tag 0b10**: Holds the data for `ErrorData::Os(i32)`. We store the `i32`
56//! in the pointer's most significant 32 bits, and don't use the bits `2..32`
57//! for anything. Using the top 32 bits is just to let us easily recover the
58//! `i32` code with the correct sign.
59//!
60//! - **Tag 0b11**: Holds the data for `ErrorData::Simple(ErrorKind)`. This
61//! stores the `ErrorKind` in the top 32 bits as well, although it doesn't
62//! occupy nearly that many. Most of the bits are unused here, but it's not
63//! like we need them for anything else yet.
64//!
65//! # Use of `NonNull<()>`
66//!
67//! Everything is stored in a `NonNull<()>`, which is odd, but actually serves a
68//! purpose.
69//!
70//! Conceptually you might think of this more like:
71//!
72//! ```ignore (exposition-only)
73//! union Repr {
74//! // holds integer (Simple/Os) variants, and
75//! // provides access to the tag bits.
76//! bits: NonZero<u64>,
77//! // Tag is 0, so this is stored untagged.
78//! msg: &'static SimpleMessage,
79//! // Tagged (offset) `Box<Custom>` pointer.
80//! tagged_custom: NonNull<()>,
81//! }
82//! ```
83//!
84//! But there are a few problems with this:
85//!
86//! 1. Union access is equivalent to a transmute, so this representation would
87//! require we transmute between integers and pointers in at least one
88//! direction, which may be UB (and even if not, it is likely harder for a
89//! compiler to reason about than explicit ptr->int operations).
90//!
91//! 2. Even if all fields of a union have a niche, the union itself doesn't,
92//! although this may change in the future. This would make things like
93//! `io::Result<()>` and `io::Result<usize>` larger, which defeats part of
94//! the motivation of this bitpacking.
95//!
96//! Storing everything in a `NonZero<usize>` (or some other integer) would be a
97//! bit more traditional for pointer tagging, but it would lose provenance
98//! information, couldn't be constructed from a `const fn`, and would probably
99//! run into other issues as well.
100//!
101//! The `NonNull<()>` seems like the only alternative, even if it's fairly odd
102//! to use a pointer type to store something that may hold an integer, some of
103//! the time.
104105use core::marker::PhantomData;
106use core::num::NonZeroUsize;
107use core::ptr::NonNull;
108109use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage};
110111// The 2 least-significant bits are used as tag.
112const TAG_MASK: usize = 0b11;
113const TAG_SIMPLE_MESSAGE: usize = 0b00;
114const TAG_CUSTOM: usize = 0b01;
115const TAG_OS: usize = 0b10;
116const TAG_SIMPLE: usize = 0b11;
117118/// The internal representation.
119///
120/// See the module docs for more, this is just a way to hack in a check that we
121/// indeed are not unwind-safe.
122///
123/// ```compile_fail,E0277
124/// fn is_unwind_safe<T: core::panic::UnwindSafe>() {}
125/// is_unwind_safe::<std::io::Error>();
126/// ```
127#[repr(transparent)]
128#[rustc_insignificant_dtor]
129pub(super) struct Repr(NonNull<()>, PhantomData<ErrorData<Box<Custom>>>);
130131// All the types `Repr` stores internally are Send + Sync, and so is it.
132unsafe impl Sendfor Repr {}
133unsafe impl Syncfor Repr {}
134135impl Repr {
136pub(super) fn new_custom(b: Box<Custom>) -> Self {
137let p = Box::into_raw(b).cast::<u8>();
138// Should only be possible if an allocator handed out a pointer with
139 // wrong alignment.
140if true {
match (&(p.addr() & TAG_MASK), &0) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_eq!(p.addr() & TAG_MASK, 0);
141// Note: We know `TAG_CUSTOM <= size_of::<Custom>()` (static_assert at
142 // end of file), and both the start and end of the expression must be
143 // valid without address space wraparound due to `Box`'s semantics.
144 //
145 // This means it would be correct to implement this using `ptr::add`
146 // (rather than `ptr::wrapping_add`), but it's unclear this would give
147 // any benefit, so we just use `wrapping_add` instead.
148let tagged = p.wrapping_add(TAG_CUSTOM).cast::<()>();
149// Safety: `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`,
150 // because `p`'s alignment means it isn't allowed to have any of the
151 // `TAG_BITS` set (you can verify that addition and bitwise-or are the
152 // same when the operands have no bits in common using a truth table).
153 //
154 // Then, `TAG_CUSTOM | p` is not zero, as that would require
155 // `TAG_CUSTOM` and `p` both be zero, and neither is (as `p` came from a
156 // box, and `TAG_CUSTOM` just... isn't zero -- it's `0b01`). Therefore,
157 // `TAG_CUSTOM + p` isn't zero and so `tagged` can't be, and the
158 // `new_unchecked` is safe.
159let res = Self(unsafe { NonNull::new_unchecked(tagged) }, PhantomData);
160// quickly smoke-check we encoded the right thing (This generally will
161 // only run in std's tests, unless the user uses -Zbuild-std)
162if true {
if !#[allow(non_exhaustive_omitted_patterns)] match res.data() {
ErrorData::Custom(_) => true,
_ => false,
} {
{
::core::panicking::panic_fmt(format_args!("repr(custom) encoding failed"));
}
};
};debug_assert!(matches!(res.data(), ErrorData::Custom(_)), "repr(custom) encoding failed");
163res164 }
165166#[inline]
167pub(super) fn new_os(code: RawOsError) -> Self {
168let utagged = ((codeas usize) << 32) | TAG_OS;
169// Safety: `TAG_OS` is not zero, so the result of the `|` is not 0.
170let res = Self(
171NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }),
172PhantomData,
173 );
174// quickly smoke-check we encoded the right thing (This generally will
175 // only run in std's tests, unless the user uses -Zbuild-std)
176if true {
if !#[allow(non_exhaustive_omitted_patterns)] match res.data() {
ErrorData::Os(c) if c == code => true,
_ => false,
} {
{
::core::panicking::panic_fmt(format_args!("repr(os) encoding failed for {0}",
code));
}
};
};debug_assert!(
177matches!(res.data(), ErrorData::Os(c) if c == code),
178"repr(os) encoding failed for {code}"
179);
180res181 }
182183#[inline]
184pub(super) fn new_simple(kind: ErrorKind) -> Self {
185let utagged = ((kindas usize) << 32) | TAG_SIMPLE;
186// Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0.
187let res = Self(
188NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }),
189PhantomData,
190 );
191// quickly smoke-check we encoded the right thing (This generally will
192 // only run in std's tests, unless the user uses -Zbuild-std)
193if true {
if !#[allow(non_exhaustive_omitted_patterns)] match res.data() {
ErrorData::Simple(k) if k == kind => true,
_ => false,
} {
{
::core::panicking::panic_fmt(format_args!("repr(simple) encoding failed {0:?}",
kind));
}
};
};debug_assert!(
194matches!(res.data(), ErrorData::Simple(k) if k == kind),
195"repr(simple) encoding failed {:?}",
196 kind,
197 );
198res199 }
200201#[inline]
202pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self {
203// Safety: References are never null.
204Self(unsafe { NonNull::new_unchecked(mas *const _ as *mut ()) }, PhantomData)
205 }
206207#[inline]
208pub(super) fn data(&self) -> ErrorData<&Custom> {
209// Safety: We're a Repr, decode_repr is fine.
210unsafe { decode_repr(self.0, |c| &*c) }
211 }
212213#[inline]
214pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> {
215// Safety: We're a Repr, decode_repr is fine.
216unsafe { decode_repr(self.0, |c| &mut *c) }
217 }
218219#[inline]
220pub(super) fn into_data(self) -> ErrorData<Box<Custom>> {
221let this = core::mem::ManuallyDrop::new(self);
222// Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is
223 // safe because we prevent double-drop using `ManuallyDrop`.
224unsafe { decode_repr(this.0, |p| Box::from_raw(p)) }
225 }
226}
227228impl Dropfor Repr {
229#[inline]
230fn drop(&mut self) {
231// Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is
232 // safe because we're being dropped.
233unsafe {
234let _ = decode_repr(self.0, |p| Box::<Custom>::from_raw(p));
235 }
236 }
237}
238239// Shared helper to decode a `Repr`'s internal pointer into an ErrorData.
240//
241// Safety: `ptr`'s bits should be encoded as described in the document at the
242// top (it should `some_repr.0`)
243#[inline]
244unsafe fn decode_repr<C, F>(ptr: NonNull<()>, make_custom: F) -> ErrorData<C>
245where
246F: FnOnce(*mut Custom) -> C,
247{
248let bits = ptr.as_ptr().addr();
249match bits & TAG_MASK {
250TAG_OS => {
251let code = ((bitsas i64) >> 32) as RawOsError;
252 ErrorData::Os(code)
253 }
254TAG_SIMPLE => {
255let kind_bits = (bits >> 32) as u32;
256let kind = kind_from_prim(kind_bits).unwrap_or_else(|| {
257if true {
if !false {
{
::core::panicking::panic_fmt(format_args!("Invalid io::error::Repr bits: `Repr({0:#018x})`",
bits));
}
};
};debug_assert!(false, "Invalid io::error::Repr bits: `Repr({:#018x})`", bits);
258// This means the `ptr` passed in was not valid, which violates
259 // the unsafe contract of `decode_repr`.
260 //
261 // Using this rather than unwrap meaningfully improves the code
262 // for callers which only care about one variant (usually
263 // `Custom`)
264unsafe { core::hint::unreachable_unchecked() };
265 });
266 ErrorData::Simple(kind)
267 }
268TAG_SIMPLE_MESSAGE => {
269// SAFETY: per tag
270unsafe { ErrorData::SimpleMessage(&*ptr.cast::<SimpleMessage>().as_ptr()) }
271 }
272TAG_CUSTOM => {
273// It would be correct for us to use `ptr::byte_sub` here (see the
274 // comment above the `wrapping_add` call in `new_custom` for why),
275 // but it isn't clear that it makes a difference, so we don't.
276let custom = ptr.as_ptr().wrapping_byte_sub(TAG_CUSTOM).cast::<Custom>();
277 ErrorData::Custom(make_custom(custom))
278 }
279_ => {
280// Can't happen, and compiler can tell
281::core::panicking::panic("internal error: entered unreachable code");unreachable!();
282 }
283 }
284}
285286// This compiles to the same code as the check+transmute, but doesn't require
287// unsafe, or to hard-code max ErrorKind or its size in a way the compiler
288// couldn't verify.
289#[inline]
290fn kind_from_prim(ek: u32) -> Option<ErrorKind> {
291macro_rules! from_prim {
292 ($prim:expr => $Enum:ident { $($Variant:ident),* $(,)? }) => {{
293// Force a compile error if the list gets out of date.
294const _: fn(e: $Enum) = |e: $Enum| match e {
295 $($Enum::$Variant => ()),*
296 };
297match $prim {
298 $(v if v == ($Enum::$Variant as _) => Some($Enum::$Variant),)*
299_ => None,
300 }
301 }}
302 }
303{
const _: fn(e: ErrorKind) =
|e: ErrorKind|
match e {
ErrorKind::NotFound => (),
ErrorKind::PermissionDenied => (),
ErrorKind::ConnectionRefused => (),
ErrorKind::ConnectionReset => (),
ErrorKind::HostUnreachable => (),
ErrorKind::NetworkUnreachable => (),
ErrorKind::ConnectionAborted => (),
ErrorKind::NotConnected => (),
ErrorKind::AddrInUse => (),
ErrorKind::AddrNotAvailable => (),
ErrorKind::NetworkDown => (),
ErrorKind::BrokenPipe => (),
ErrorKind::AlreadyExists => (),
ErrorKind::WouldBlock => (),
ErrorKind::NotADirectory => (),
ErrorKind::IsADirectory => (),
ErrorKind::DirectoryNotEmpty => (),
ErrorKind::ReadOnlyFilesystem => (),
ErrorKind::FilesystemLoop => (),
ErrorKind::StaleNetworkFileHandle => (),
ErrorKind::InvalidInput => (),
ErrorKind::InvalidData => (),
ErrorKind::TimedOut => (),
ErrorKind::WriteZero => (),
ErrorKind::StorageFull => (),
ErrorKind::NotSeekable => (),
ErrorKind::QuotaExceeded => (),
ErrorKind::FileTooLarge => (),
ErrorKind::ResourceBusy => (),
ErrorKind::ExecutableFileBusy => (),
ErrorKind::Deadlock => (),
ErrorKind::CrossesDevices => (),
ErrorKind::TooManyLinks => (),
ErrorKind::InvalidFilename => (),
ErrorKind::ArgumentListTooLong => (),
ErrorKind::Interrupted => (),
ErrorKind::Other => (),
ErrorKind::UnexpectedEof => (),
ErrorKind::Unsupported => (),
ErrorKind::OutOfMemory => (),
ErrorKind::InProgress => (),
ErrorKind::Uncategorized => (),
};
match ek {
v if v == (ErrorKind::NotFound as _) => Some(ErrorKind::NotFound),
v if v == (ErrorKind::PermissionDenied as _) =>
Some(ErrorKind::PermissionDenied),
v if v == (ErrorKind::ConnectionRefused as _) =>
Some(ErrorKind::ConnectionRefused),
v if v == (ErrorKind::ConnectionReset as _) =>
Some(ErrorKind::ConnectionReset),
v if v == (ErrorKind::HostUnreachable as _) =>
Some(ErrorKind::HostUnreachable),
v if v == (ErrorKind::NetworkUnreachable as _) =>
Some(ErrorKind::NetworkUnreachable),
v if v == (ErrorKind::ConnectionAborted as _) =>
Some(ErrorKind::ConnectionAborted),
v if v == (ErrorKind::NotConnected as _) =>
Some(ErrorKind::NotConnected),
v if v == (ErrorKind::AddrInUse as _) => Some(ErrorKind::AddrInUse),
v if v == (ErrorKind::AddrNotAvailable as _) =>
Some(ErrorKind::AddrNotAvailable),
v if v == (ErrorKind::NetworkDown as _) =>
Some(ErrorKind::NetworkDown),
v if v == (ErrorKind::BrokenPipe as _) => Some(ErrorKind::BrokenPipe),
v if v == (ErrorKind::AlreadyExists as _) =>
Some(ErrorKind::AlreadyExists),
v if v == (ErrorKind::WouldBlock as _) => Some(ErrorKind::WouldBlock),
v if v == (ErrorKind::NotADirectory as _) =>
Some(ErrorKind::NotADirectory),
v if v == (ErrorKind::IsADirectory as _) =>
Some(ErrorKind::IsADirectory),
v if v == (ErrorKind::DirectoryNotEmpty as _) =>
Some(ErrorKind::DirectoryNotEmpty),
v if v == (ErrorKind::ReadOnlyFilesystem as _) =>
Some(ErrorKind::ReadOnlyFilesystem),
v if v == (ErrorKind::FilesystemLoop as _) =>
Some(ErrorKind::FilesystemLoop),
v if v == (ErrorKind::StaleNetworkFileHandle as _) =>
Some(ErrorKind::StaleNetworkFileHandle),
v if v == (ErrorKind::InvalidInput as _) =>
Some(ErrorKind::InvalidInput),
v if v == (ErrorKind::InvalidData as _) =>
Some(ErrorKind::InvalidData),
v if v == (ErrorKind::TimedOut as _) => Some(ErrorKind::TimedOut),
v if v == (ErrorKind::WriteZero as _) => Some(ErrorKind::WriteZero),
v if v == (ErrorKind::StorageFull as _) =>
Some(ErrorKind::StorageFull),
v if v == (ErrorKind::NotSeekable as _) =>
Some(ErrorKind::NotSeekable),
v if v == (ErrorKind::QuotaExceeded as _) =>
Some(ErrorKind::QuotaExceeded),
v if v == (ErrorKind::FileTooLarge as _) =>
Some(ErrorKind::FileTooLarge),
v if v == (ErrorKind::ResourceBusy as _) =>
Some(ErrorKind::ResourceBusy),
v if v == (ErrorKind::ExecutableFileBusy as _) =>
Some(ErrorKind::ExecutableFileBusy),
v if v == (ErrorKind::Deadlock as _) => Some(ErrorKind::Deadlock),
v if v == (ErrorKind::CrossesDevices as _) =>
Some(ErrorKind::CrossesDevices),
v if v == (ErrorKind::TooManyLinks as _) =>
Some(ErrorKind::TooManyLinks),
v if v == (ErrorKind::InvalidFilename as _) =>
Some(ErrorKind::InvalidFilename),
v if v == (ErrorKind::ArgumentListTooLong as _) =>
Some(ErrorKind::ArgumentListTooLong),
v if v == (ErrorKind::Interrupted as _) =>
Some(ErrorKind::Interrupted),
v if v == (ErrorKind::Other as _) => Some(ErrorKind::Other),
v if v == (ErrorKind::UnexpectedEof as _) =>
Some(ErrorKind::UnexpectedEof),
v if v == (ErrorKind::Unsupported as _) =>
Some(ErrorKind::Unsupported),
v if v == (ErrorKind::OutOfMemory as _) =>
Some(ErrorKind::OutOfMemory),
v if v == (ErrorKind::InProgress as _) => Some(ErrorKind::InProgress),
v if v == (ErrorKind::Uncategorized as _) =>
Some(ErrorKind::Uncategorized),
_ => None,
}
}from_prim!(ek => ErrorKind {
304 NotFound,
305 PermissionDenied,
306 ConnectionRefused,
307 ConnectionReset,
308 HostUnreachable,
309 NetworkUnreachable,
310 ConnectionAborted,
311 NotConnected,
312 AddrInUse,
313 AddrNotAvailable,
314 NetworkDown,
315 BrokenPipe,
316 AlreadyExists,
317 WouldBlock,
318 NotADirectory,
319 IsADirectory,
320 DirectoryNotEmpty,
321 ReadOnlyFilesystem,
322 FilesystemLoop,
323 StaleNetworkFileHandle,
324 InvalidInput,
325 InvalidData,
326 TimedOut,
327 WriteZero,
328 StorageFull,
329 NotSeekable,
330 QuotaExceeded,
331 FileTooLarge,
332 ResourceBusy,
333 ExecutableFileBusy,
334 Deadlock,
335 CrossesDevices,
336 TooManyLinks,
337 InvalidFilename,
338 ArgumentListTooLong,
339 Interrupted,
340 Other,
341 UnexpectedEof,
342 Unsupported,
343 OutOfMemory,
344 InProgress,
345 Uncategorized,
346 })347}
348349// Some static checking to alert us if a change breaks any of the assumptions
350// that our encoding relies on for correctness and soundness. (Some of these are
351// a bit overly thorough/cautious, admittedly)
352//
353// If any of these are hit on a platform that std supports, we should likely
354// just use `repr_unpacked.rs` there instead (unless the fix is easy).
355macro_rules!static_assert {
356 ($condition:expr) => {
357const _: () = assert!($condition);
358 };
359 (@usize_eq: $lhs:expr, $rhs:expr) => {
360const _: [(); $lhs] = [(); $rhs];
361 };
362}
363364// The bitpacking we use requires pointers be exactly 64 bits.
365const _: [(); size_of::<NonNull<()>>()] = [(); 8];static_assert!(@usize_eq: size_of::<NonNull<()>>(), 8);
366367// We also require pointers and usize be the same size.
368const _: [(); size_of::<NonNull<()>>()] = [(); size_of::<usize>()];static_assert!(@usize_eq: size_of::<NonNull<()>>(), size_of::<usize>());
369370// `Custom` and `SimpleMessage` need to be thin pointers.
371const _: [(); size_of::<&'static SimpleMessage>()] = [(); 8];static_assert!(@usize_eq: size_of::<&'static SimpleMessage>(), 8);
372const _: [(); size_of::<Box<Custom>>()] = [(); 8];static_assert!(@usize_eq: size_of::<Box<Custom>>(), 8);
373374const _: () =
if !(TAG_MASK + 1).is_power_of_two() {
::core::panicking::panic("assertion failed: (TAG_MASK + 1).is_power_of_two()")
};static_assert!((TAG_MASK + 1).is_power_of_two());
375// And they must have sufficient alignment.
376const _: () =
if !(align_of::<SimpleMessage>() >= TAG_MASK + 1) {
::core::panicking::panic("assertion failed: align_of::<SimpleMessage>() >= TAG_MASK + 1")
};static_assert!(align_of::<SimpleMessage>() >= TAG_MASK + 1);
377const _: () =
if !(align_of::<Custom>() >= TAG_MASK + 1) {
::core::panicking::panic("assertion failed: align_of::<Custom>() >= TAG_MASK + 1")
};static_assert!(align_of::<Custom>() >= TAG_MASK + 1);
378379const _: [(); TAG_MASK & TAG_SIMPLE_MESSAGE] = [(); TAG_SIMPLE_MESSAGE];static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE_MESSAGE, TAG_SIMPLE_MESSAGE);
380const _: [(); TAG_MASK & TAG_CUSTOM] = [(); TAG_CUSTOM];static_assert!(@usize_eq: TAG_MASK & TAG_CUSTOM, TAG_CUSTOM);
381const _: [(); TAG_MASK & TAG_OS] = [(); TAG_OS];static_assert!(@usize_eq: TAG_MASK & TAG_OS, TAG_OS);
382const _: [(); TAG_MASK & TAG_SIMPLE] = [(); TAG_SIMPLE];static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE, TAG_SIMPLE);
383384// This is obviously true (`TAG_CUSTOM` is `0b01`), but in `Repr::new_custom` we
385// offset a pointer by this value, and expect it to both be within the same
386// object, and to not wrap around the address space. See the comment in that
387// function for further details.
388//
389// Actually, at the moment we use `ptr::wrapping_add`, not `ptr::add`, so this
390// check isn't needed for that one, although the assertion that we don't
391// actually wrap around in that wrapping_add does simplify the safety reasoning
392// elsewhere considerably.
393const _: () =
if !(size_of::<Custom>() >= TAG_CUSTOM) {
::core::panicking::panic("assertion failed: size_of::<Custom>() >= TAG_CUSTOM")
};static_assert!(size_of::<Custom>() >= TAG_CUSTOM);
394395// These two store a payload which is allowed to be zero, so they must be
396// non-zero to preserve the `NonNull`'s range invariant.
397const _: () =
if !(TAG_OS != 0) {
::core::panicking::panic("assertion failed: TAG_OS != 0")
};static_assert!(TAG_OS != 0);
398const _: () =
if !(TAG_SIMPLE != 0) {
::core::panicking::panic("assertion failed: TAG_SIMPLE != 0")
};static_assert!(TAG_SIMPLE != 0);
399// We can't tag `SimpleMessage`s, the tag must be 0.
400const _: [(); TAG_SIMPLE_MESSAGE] = [(); 0];static_assert!(@usize_eq: TAG_SIMPLE_MESSAGE, 0);
401402// Check that the point of all of this still holds.
403//
404// We'd check against `io::Error`, but *technically* it's allowed to vary,
405// as it's not `#[repr(transparent)]`/`#[repr(C)]`. We could add that, but
406// the `#[repr()]` would show up in rustdoc, which might be seen as a stable
407// commitment.
408const _: [(); size_of::<Repr>()] = [(); 8];static_assert!(@usize_eq: size_of::<Repr>(), 8);
409const _: [(); size_of::<Option<Repr>>()] = [(); 8];static_assert!(@usize_eq: size_of::<Option<Repr>>(), 8);
410const _: [(); size_of::<Result<(), Repr>>()] = [(); 8];static_assert!(@usize_eq: size_of::<Result<(), Repr>>(), 8);
411const _: [(); size_of::<Result<usize, Repr>>()] = [(); 16];static_assert!(@usize_eq: size_of::<Result<usize, Repr>>(), 16);