Skip to content

Commit 834cd59

Browse files
committed
transmutability: Support types with restricted validity ranges
This commit moves us closer to supporting `NonZero` types, but does not get us all the way there.
1 parent 35ebdf9 commit 834cd59

3 files changed

Lines changed: 94 additions & 8 deletions

File tree

compiler/rustc_transmute/src/layout/tree.rs

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ pub(crate) mod rustc {
330330
.fold(Tree::unit(), |tree, elt| tree.then(elt)))
331331
}
332332

333-
ty::Adt(adt_def, _args_ref) if !ty.is_box() => {
333+
ty::Adt(adt_def, args_ref) if !ty.is_box() => {
334334
let (lo, hi) = cx.tcx().layout_scalar_valid_range(adt_def.did());
335335

336336
use core::ops::Bound::*;
@@ -339,13 +339,35 @@ pub(crate) mod rustc {
339339
(AdtKind::Struct, Unbounded, Unbounded) => {
340340
Self::from_struct((ty, layout), *adt_def, cx)
341341
}
342-
(AdtKind::Struct, Included(1), Included(_hi)) if is_transparent => {
343-
// FIXME(@joshlf): Support `NonZero` types:
344-
// - Check to make sure that the first field is
345-
// numerical
346-
// - Check to make sure that the upper bound is the
347-
// maximum value for the field's type
348-
// - Construct `Self::nonzero`
342+
(AdtKind::Struct, Included(1), hi_val) if is_transparent => {
343+
let variant = adt_def.non_enum_variant();
344+
// For now, only support `repr(transparent)` types
345+
// with a single field. Technically
346+
// `repr(transparent)` also works with types with
347+
// any number of zero-sized fields (in addition to
348+
// their single non-zero-sized field), but no such
349+
// types exist in the standard library which also
350+
// use
351+
// `#[rustc_layout_scalar_valid_range_(start|end)]`.
352+
if let [field] = &variant.fields.as_slice().raw {
353+
let field_ty = field.ty(cx.tcx(), args_ref);
354+
355+
let field_layout = layout_of(cx, field_ty)?;
356+
let field_size = field_layout.size;
357+
358+
let max_value = (1u128 << field_size.bits()) - 1;
359+
let hi_val = match hi_val {
360+
Included(hi_val) => hi_val,
361+
Unbounded => max_value,
362+
Excluded(_) => return Err(Err::NotYetSupported),
363+
};
364+
365+
if hi_val == max_value
366+
&& let ty::Uint(_) = *field_ty.kind()
367+
{
368+
return Ok(Self::nonzero(field_size.bytes()));
369+
}
370+
}
349371
Err(Err::NotYetSupported)
350372
}
351373
(AdtKind::Enum, Unbounded, Unbounded) => {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#![feature(transmutability)]
2+
#![feature(rustc_attrs)]
3+
#![allow(dead_code)]
4+
5+
mod assert {
6+
use std::mem::{Assume, TransmuteFrom};
7+
8+
pub fn is_transmutable<Src, Dst>()
9+
where
10+
Dst: TransmuteFrom<Src, { Assume::NOTHING }>,
11+
{
12+
}
13+
}
14+
15+
fn main() {
16+
// FIXME: Replace this with `core::num::NonZeroU8` once we support it.
17+
#[rustc_layout_scalar_valid_range_start(1)]
18+
#[repr(transparent)]
19+
struct NonZeroU8(u8);
20+
21+
// FIXME: Replace this with `core::num::NonZeroU16` once we support it.
22+
#[rustc_layout_scalar_valid_range_start(1)]
23+
#[repr(transparent)]
24+
struct NonZeroU16(u16);
25+
26+
assert::is_transmutable::<u8, NonZeroU8>(); //~ ERROR: cannot be safely transmuted
27+
assert::is_transmutable::<NonZeroU8, u8>();
28+
29+
assert::is_transmutable::<u16, NonZeroU16>(); //~ ERROR: cannot be safely transmuted
30+
assert::is_transmutable::<NonZeroU16, u16>();
31+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error[E0277]: `u8` cannot be safely transmuted into `NonZeroU8`
2+
--> $DIR/nonzero.rs:26:35
3+
|
4+
LL | assert::is_transmutable::<u8, NonZeroU8>();
5+
| ^^^^^^^^^ at least one value of `u8` isn't a bit-valid value of `NonZeroU8`
6+
|
7+
note: required by a bound in `is_transmutable`
8+
--> $DIR/nonzero.rs:10:14
9+
|
10+
LL | pub fn is_transmutable<Src, Dst>()
11+
| --------------- required by a bound in this function
12+
LL | where
13+
LL | Dst: TransmuteFrom<Src, { Assume::NOTHING }>,
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
15+
16+
error[E0277]: `u16` cannot be safely transmuted into `NonZeroU16`
17+
--> $DIR/nonzero.rs:29:36
18+
|
19+
LL | assert::is_transmutable::<u16, NonZeroU16>();
20+
| ^^^^^^^^^^ at least one value of `u16` isn't a bit-valid value of `NonZeroU16`
21+
|
22+
note: required by a bound in `is_transmutable`
23+
--> $DIR/nonzero.rs:10:14
24+
|
25+
LL | pub fn is_transmutable<Src, Dst>()
26+
| --------------- required by a bound in this function
27+
LL | where
28+
LL | Dst: TransmuteFrom<Src, { Assume::NOTHING }>,
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
30+
31+
error: aborting due to 2 previous errors
32+
33+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)