|
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