Skip to content

Commit 36a3168

Browse files
authored
Unrolled build for rust-lang#140215
Rollup merge of rust-lang#140215 - joshlf:transmutability-char-nonzero, r=jswrenn transmutability: Support char, NonZeroXxx Note that `NonZero` support is not wired up, as the author encountered bugs while attempting this. A future commit will wire up `NonZero` support. r? ````@jswrenn````
2 parents 5ae50d3 + ae0c2fe commit 36a3168

File tree

6 files changed

+302
-46
lines changed

6 files changed

+302
-46
lines changed

compiler/rustc_transmute/src/layout/mod.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,12 @@ impl fmt::Debug for Byte {
6565
}
6666
}
6767

68-
#[cfg(test)]
68+
impl From<RangeInclusive<u8>> for Byte {
69+
fn from(src: RangeInclusive<u8>) -> Self {
70+
Self::new(src)
71+
}
72+
}
73+
6974
impl From<u8> for Byte {
7075
fn from(src: u8) -> Self {
7176
Self::from_val(src)

compiler/rustc_transmute/src/layout/tree.rs

+123-23
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::ops::ControlFlow;
1+
use std::ops::{ControlFlow, RangeInclusive};
22

33
use super::{Byte, Def, Ref};
44

@@ -32,6 +32,22 @@ where
3232
Byte(Byte),
3333
}
3434

35+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
36+
pub(crate) enum Endian {
37+
Little,
38+
Big,
39+
}
40+
41+
#[cfg(feature = "rustc")]
42+
impl From<rustc_abi::Endian> for Endian {
43+
fn from(order: rustc_abi::Endian) -> Endian {
44+
match order {
45+
rustc_abi::Endian::Little => Endian::Little,
46+
rustc_abi::Endian::Big => Endian::Big,
47+
}
48+
}
49+
}
50+
3551
impl<D, R> Tree<D, R>
3652
where
3753
D: Def,
@@ -59,22 +75,60 @@ where
5975

6076
/// A `Tree` representing the layout of `bool`.
6177
pub(crate) fn bool() -> Self {
62-
Self::Byte(Byte::new(0x00..=0x01))
78+
Self::byte(0x00..=0x01)
6379
}
6480

6581
/// A `Tree` whose layout matches that of a `u8`.
6682
pub(crate) fn u8() -> Self {
67-
Self::Byte(Byte::new(0x00..=0xFF))
83+
Self::byte(0x00..=0xFF)
84+
}
85+
86+
/// A `Tree` whose layout matches that of a `char`.
87+
pub(crate) fn char(order: Endian) -> Self {
88+
// `char`s can be in the following ranges:
89+
// - [0, 0xD7FF]
90+
// - [0xE000, 10FFFF]
91+
//
92+
// All other `char` values are illegal. We can thus represent a `char`
93+
// as a union of three possible layouts:
94+
// - 00 00 [00, D7] XX
95+
// - 00 00 [E0, FF] XX
96+
// - 00 [01, 10] XX XX
97+
98+
const _0: RangeInclusive<u8> = 0..=0;
99+
const BYTE: RangeInclusive<u8> = 0x00..=0xFF;
100+
let x = Self::from_big_endian(order, [_0, _0, 0x00..=0xD7, BYTE]);
101+
let y = Self::from_big_endian(order, [_0, _0, 0xE0..=0xFF, BYTE]);
102+
let z = Self::from_big_endian(order, [_0, 0x01..=0x10, BYTE, BYTE]);
103+
Self::alt([x, y, z])
68104
}
69105

70-
/// A `Tree` whose layout accepts exactly the given bit pattern.
71-
pub(crate) fn from_bits(bits: u8) -> Self {
72-
Self::Byte(Byte::from_val(bits))
106+
/// A `Tree` whose layout matches `std::num::NonZeroXxx`.
107+
#[allow(dead_code)]
108+
pub(crate) fn nonzero(width_in_bytes: u64) -> Self {
109+
const BYTE: RangeInclusive<u8> = 0x00..=0xFF;
110+
const NONZERO: RangeInclusive<u8> = 0x01..=0xFF;
111+
112+
(0..width_in_bytes)
113+
.map(|nz_idx| {
114+
(0..width_in_bytes)
115+
.map(|pos| Self::byte(if pos == nz_idx { NONZERO } else { BYTE }))
116+
.fold(Self::unit(), Self::then)
117+
})
118+
.fold(Self::uninhabited(), Self::or)
119+
}
120+
121+
pub(crate) fn bytes<const N: usize, B: Into<Byte>>(bytes: [B; N]) -> Self {
122+
Self::seq(bytes.map(B::into).map(Self::Byte))
123+
}
124+
125+
pub(crate) fn byte(byte: impl Into<Byte>) -> Self {
126+
Self::Byte(byte.into())
73127
}
74128

75129
/// A `Tree` whose layout is a number of the given width.
76-
pub(crate) fn number(width_in_bytes: usize) -> Self {
77-
Self::Seq(vec![Self::u8(); width_in_bytes])
130+
pub(crate) fn number(width_in_bytes: u64) -> Self {
131+
Self::Seq(vec![Self::u8(); width_in_bytes.try_into().unwrap()])
78132
}
79133

80134
/// A `Tree` whose layout is entirely padding of the given width.
@@ -125,13 +179,35 @@ where
125179
Self::Byte(..) | Self::Ref(..) | Self::Def(..) => true,
126180
}
127181
}
128-
}
129182

130-
impl<D, R> Tree<D, R>
131-
where
132-
D: Def,
133-
R: Ref,
134-
{
183+
/// Produces a `Tree` which represents a sequence of bytes stored in
184+
/// `order`.
185+
///
186+
/// `bytes` is taken to be in big-endian byte order, and its order will be
187+
/// swapped if `order == Endian::Little`.
188+
pub(crate) fn from_big_endian<const N: usize, B: Into<Byte>>(
189+
order: Endian,
190+
mut bytes: [B; N],
191+
) -> Self {
192+
if order == Endian::Little {
193+
(&mut bytes[..]).reverse();
194+
}
195+
196+
Self::bytes(bytes)
197+
}
198+
199+
/// Produces a `Tree` where each of the trees in `trees` are sequenced one
200+
/// after another.
201+
pub(crate) fn seq<const N: usize>(trees: [Tree<D, R>; N]) -> Self {
202+
trees.into_iter().fold(Tree::unit(), Self::then)
203+
}
204+
205+
/// Produces a `Tree` where each of the trees in `trees` are accepted as
206+
/// alternative layouts.
207+
pub(crate) fn alt<const N: usize>(trees: [Tree<D, R>; N]) -> Self {
208+
trees.into_iter().fold(Tree::uninhabited(), Self::or)
209+
}
210+
135211
/// Produces a new `Tree` where `other` is sequenced after `self`.
136212
pub(crate) fn then(self, other: Self) -> Self {
137213
match (self, other) {
@@ -222,17 +298,17 @@ pub(crate) mod rustc {
222298

223299
ty::Float(nty) => {
224300
let width = nty.bit_width() / 8;
225-
Ok(Self::number(width as _))
301+
Ok(Self::number(width.try_into().unwrap()))
226302
}
227303

228304
ty::Int(nty) => {
229305
let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8;
230-
Ok(Self::number(width as _))
306+
Ok(Self::number(width.try_into().unwrap()))
231307
}
232308

233309
ty::Uint(nty) => {
234310
let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8;
235-
Ok(Self::number(width as _))
311+
Ok(Self::number(width.try_into().unwrap()))
236312
}
237313

238314
ty::Tuple(members) => Self::from_tuple((ty, layout), members, cx),
@@ -249,11 +325,33 @@ pub(crate) mod rustc {
249325
.fold(Tree::unit(), |tree, elt| tree.then(elt)))
250326
}
251327

252-
ty::Adt(adt_def, _args_ref) if !ty.is_box() => match adt_def.adt_kind() {
253-
AdtKind::Struct => Self::from_struct((ty, layout), *adt_def, cx),
254-
AdtKind::Enum => Self::from_enum((ty, layout), *adt_def, cx),
255-
AdtKind::Union => Self::from_union((ty, layout), *adt_def, cx),
256-
},
328+
ty::Adt(adt_def, _args_ref) if !ty.is_box() => {
329+
let (lo, hi) = cx.tcx().layout_scalar_valid_range(adt_def.did());
330+
331+
use core::ops::Bound::*;
332+
let is_transparent = adt_def.repr().transparent();
333+
match (adt_def.adt_kind(), lo, hi) {
334+
(AdtKind::Struct, Unbounded, Unbounded) => {
335+
Self::from_struct((ty, layout), *adt_def, cx)
336+
}
337+
(AdtKind::Struct, Included(1), Included(_hi)) if is_transparent => {
338+
// FIXME(@joshlf): Support `NonZero` types:
339+
// - Check to make sure that the first field is
340+
// numerical
341+
// - Check to make sure that the upper bound is the
342+
// maximum value for the field's type
343+
// - Construct `Self::nonzero`
344+
Err(Err::NotYetSupported)
345+
}
346+
(AdtKind::Enum, Unbounded, Unbounded) => {
347+
Self::from_enum((ty, layout), *adt_def, cx)
348+
}
349+
(AdtKind::Union, Unbounded, Unbounded) => {
350+
Self::from_union((ty, layout), *adt_def, cx)
351+
}
352+
_ => Err(Err::NotYetSupported),
353+
}
354+
}
257355

258356
ty::Ref(lifetime, ty, mutability) => {
259357
let layout = layout_of(cx, *ty)?;
@@ -268,6 +366,8 @@ pub(crate) mod rustc {
268366
}))
269367
}
270368

369+
ty::Char => Ok(Self::char(cx.tcx().data_layout.endian.into())),
370+
271371
_ => Err(Err::NotYetSupported),
272372
}
273373
}
@@ -450,7 +550,7 @@ pub(crate) mod rustc {
450550
&bytes[bytes.len() - size.bytes_usize()..]
451551
}
452552
};
453-
Self::Seq(bytes.iter().map(|&b| Self::from_bits(b)).collect())
553+
Self::Seq(bytes.iter().map(|&b| Self::byte(b)).collect())
454554
}
455555

456556
/// Constructs a `Tree` from a union.

compiler/rustc_transmute/src/layout/tree/tests.rs

+8-16
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,18 @@ mod prune {
2020

2121
#[test]
2222
fn seq_1() {
23-
let layout: Tree<Def, !> =
24-
Tree::def(Def::NoSafetyInvariants).then(Tree::from_bits(0x00));
25-
assert_eq!(
26-
layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)),
27-
Tree::from_bits(0x00)
28-
);
23+
let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants).then(Tree::byte(0x00));
24+
assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::byte(0x00));
2925
}
3026

3127
#[test]
3228
fn seq_2() {
33-
let layout: Tree<Def, !> = Tree::from_bits(0x00)
34-
.then(Tree::def(Def::NoSafetyInvariants))
35-
.then(Tree::from_bits(0x01));
29+
let layout: Tree<Def, !> =
30+
Tree::byte(0x00).then(Tree::def(Def::NoSafetyInvariants)).then(Tree::byte(0x01));
3631

3732
assert_eq!(
3833
layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)),
39-
Tree::from_bits(0x00).then(Tree::from_bits(0x01))
34+
Tree::byte(0x00).then(Tree::byte(0x01))
4035
);
4136
}
4237
}
@@ -66,7 +61,7 @@ mod prune {
6661
#[test]
6762
fn invisible_def_in_seq_len_3() {
6863
let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants)
69-
.then(Tree::from_bits(0x00))
64+
.then(Tree::byte(0x00))
7065
.then(Tree::def(Def::HasSafetyInvariants));
7166
assert_eq!(
7267
layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)),
@@ -94,12 +89,9 @@ mod prune {
9489
#[test]
9590
fn visible_def_in_seq_len_3() {
9691
let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants)
97-
.then(Tree::from_bits(0x00))
92+
.then(Tree::byte(0x00))
9893
.then(Tree::def(Def::NoSafetyInvariants));
99-
assert_eq!(
100-
layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)),
101-
Tree::from_bits(0x00)
102-
);
94+
assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::byte(0x00));
10395
}
10496
}
10597
}

compiler/rustc_transmute/src/maybe_transmutable/tests.rs

+76-6
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,9 @@ mod bool {
177177

178178
#[test]
179179
fn should_permit_validity_expansion_and_reject_contraction() {
180-
let b0 = layout::Tree::<Def, !>::from_bits(0);
181-
let b1 = layout::Tree::<Def, !>::from_bits(1);
182-
let b2 = layout::Tree::<Def, !>::from_bits(2);
180+
let b0 = layout::Tree::<Def, !>::byte(0);
181+
let b1 = layout::Tree::<Def, !>::byte(1);
182+
let b2 = layout::Tree::<Def, !>::byte(2);
183183

184184
let alts = [b0, b1, b2];
185185

@@ -279,8 +279,8 @@ mod alt {
279279
fn should_permit_identity_transmutation() {
280280
type Tree = layout::Tree<Def, !>;
281281

282-
let x = Tree::Seq(vec![Tree::from_bits(0), Tree::from_bits(0)]);
283-
let y = Tree::Seq(vec![Tree::bool(), Tree::from_bits(1)]);
282+
let x = Tree::Seq(vec![Tree::byte(0), Tree::byte(0)]);
283+
let y = Tree::Seq(vec![Tree::bool(), Tree::byte(1)]);
284284
let layout = Tree::Alt(vec![x, y]);
285285

286286
let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new(
@@ -323,14 +323,84 @@ mod union {
323323
}
324324
}
325325

326+
mod char {
327+
use super::*;
328+
use crate::layout::tree::Endian;
329+
330+
#[test]
331+
fn should_permit_valid_transmutation() {
332+
for order in [Endian::Big, Endian::Little] {
333+
use Answer::*;
334+
let char_layout = layout::Tree::<Def, !>::char(order);
335+
336+
// `char`s can be in the following ranges:
337+
// - [0, 0xD7FF]
338+
// - [0xE000, 10FFFF]
339+
//
340+
// This loop synthesizes a singleton-validity type for the extremes
341+
// of each range, and for one past the end of the extremes of each
342+
// range.
343+
let no = No(Reason::DstIsBitIncompatible);
344+
for (src, answer) in [
345+
(0u32, Yes),
346+
(0xD7FF, Yes),
347+
(0xD800, no.clone()),
348+
(0xDFFF, no.clone()),
349+
(0xE000, Yes),
350+
(0x10FFFF, Yes),
351+
(0x110000, no.clone()),
352+
(0xFFFF0000, no.clone()),
353+
(0xFFFFFFFF, no),
354+
] {
355+
let src_layout =
356+
layout::tree::Tree::<Def, !>::from_big_endian(order, src.to_be_bytes());
357+
358+
let a = is_transmutable(&src_layout, &char_layout, Assume::default());
359+
assert_eq!(a, answer, "endian:{order:?},\nsrc:{src:x}");
360+
}
361+
}
362+
}
363+
}
364+
365+
mod nonzero {
366+
use super::*;
367+
use crate::{Answer, Reason};
368+
369+
const NONZERO_BYTE_WIDTHS: [u64; 5] = [1, 2, 4, 8, 16];
370+
371+
#[test]
372+
fn should_permit_identity_transmutation() {
373+
for width in NONZERO_BYTE_WIDTHS {
374+
let layout = layout::Tree::<Def, !>::nonzero(width);
375+
assert_eq!(is_transmutable(&layout, &layout, Assume::default()), Answer::Yes);
376+
}
377+
}
378+
379+
#[test]
380+
fn should_permit_valid_transmutation() {
381+
for width in NONZERO_BYTE_WIDTHS {
382+
use Answer::*;
383+
384+
let num = layout::Tree::<Def, !>::number(width);
385+
let nz = layout::Tree::<Def, !>::nonzero(width);
386+
387+
let a = is_transmutable(&num, &nz, Assume::default());
388+
assert_eq!(a, No(Reason::DstIsBitIncompatible), "width:{width}");
389+
390+
let a = is_transmutable(&nz, &num, Assume::default());
391+
assert_eq!(a, Yes, "width:{width}");
392+
}
393+
}
394+
}
395+
326396
mod r#ref {
327397
use super::*;
328398

329399
#[test]
330400
fn should_permit_identity_transmutation() {
331401
type Tree = crate::layout::Tree<Def, [(); 1]>;
332402

333-
let layout = Tree::Seq(vec![Tree::from_bits(0), Tree::Ref([()])]);
403+
let layout = Tree::Seq(vec![Tree::byte(0x00), Tree::Ref([()])]);
334404

335405
let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new(
336406
layout.clone(),

0 commit comments

Comments
 (0)