|
6 | 6 | //! a more clever manner than `rustc`'s default layout algorithm would). |
7 | 7 | //! |
8 | 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 following |
10 | | -//! data (which is equivalent to what's stored by `repr_unpacked::Repr`, e.g. |
11 | | -//! `super::ErrorData<Box<Custom>>`): |
| 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 | 12 | //! |
13 | 13 | //! ```ignore (exposition-only) |
14 | 14 | //! enum ErrorData { |
@@ -135,7 +135,16 @@ impl Repr { |
135 | 135 | // (rather than `ptr::wrapping_add`), but it's unclear this would give |
136 | 136 | // any benefit, so we just use `wrapping_add` instead. |
137 | 137 | let tagged = p.wrapping_add(TAG_CUSTOM).cast::<()>(); |
138 | | - // Safety: the above safety comment also means the result can't be null. |
| 138 | + // Safety: `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`, |
| 139 | + // because `p`'s alignment means it isn't allowed to have any of the |
| 140 | + // `TAG_BITS` set (you can verify that addition and bitwise-or are the |
| 141 | + // same when the operands have no bits in common using a truth table). |
| 142 | + // |
| 143 | + // Then, `TAG_CUSTOM | p` is not zero, as that would require |
| 144 | + // `TAG_CUSTOM` and `p` both be zero, and neither is (as `p` came from a |
| 145 | + // box, and `TAG_CUSTOM` just... isn't zero -- it's `0b01`). Therefore, |
| 146 | + // `TAG_CUSTOM + p` isn't zero and so `tagged` can't be, and the |
| 147 | + // `new_unchecked` is safe. |
139 | 148 | let res = Self(unsafe { NonNull::new_unchecked(tagged) }); |
140 | 149 | // quickly smoke-check we encoded the right thing (This generally will |
141 | 150 | // only run in libstd's tests, unless the user uses -Zbuild-std) |
@@ -342,12 +351,25 @@ static_assert!(@usize_eq: size_of::<NonNull<()>>(), size_of::<usize>()); |
342 | 351 | static_assert!(@usize_eq: size_of::<&'static SimpleMessage>(), 8); |
343 | 352 | static_assert!(@usize_eq: size_of::<Box<Custom>>(), 8); |
344 | 353 |
|
345 | | -// And they must have >= 4 byte alignment. |
346 | | -static_assert!(align_of::<SimpleMessage>() >= 4); |
347 | | -static_assert!(align_of::<Custom>() >= 4); |
| 354 | +static_assert!((TAG_MASK + 1).is_power_of_two()); |
| 355 | +// And they must have sufficient alignment. |
| 356 | +static_assert!(align_of::<SimpleMessage>() >= TAG_MASK + 1); |
| 357 | +static_assert!(align_of::<Custom>() >= TAG_MASK + 1); |
| 358 | + |
| 359 | +static_assert!(@usize_eq: (TAG_MASK & TAG_SIMPLE_MESSAGE), TAG_SIMPLE_MESSAGE); |
| 360 | +static_assert!(@usize_eq: (TAG_MASK & TAG_CUSTOM), TAG_CUSTOM); |
| 361 | +static_assert!(@usize_eq: (TAG_MASK & TAG_OS), TAG_OS); |
| 362 | +static_assert!(@usize_eq: (TAG_MASK & TAG_SIMPLE), TAG_SIMPLE); |
348 | 363 |
|
349 | | -// This is obviously true (`TAG_CUSTOM` is `0b01`), but our implementation of |
350 | | -// `Repr::new_custom` and such would be wrong if it were not, so we check. |
| 364 | +// This is obviously true (`TAG_CUSTOM` is `0b01`), but in `Repr::new_custom` we |
| 365 | +// offset a pointer by this value, and expect it to both be within the same |
| 366 | +// object, and to not wrap around the address space. See the comment in that |
| 367 | +// function for further details. |
| 368 | +// |
| 369 | +// Actually, at the moment we use `ptr::wrapping_add`, not `ptr::add`, so this |
| 370 | +// check isn't needed for that one, although the assertion that we don't |
| 371 | +// actually wrap around in that wrapping_add does simplify the safety reasoning |
| 372 | +// elsewhere considerably. |
351 | 373 | static_assert!(size_of::<Custom>() >= TAG_CUSTOM); |
352 | 374 |
|
353 | 375 | // These two store a payload which is allowed to be zero, so they must be |
|
0 commit comments