]> git.lizzy.rs Git - rust.git/blob - src/test/ui/structs-enums/enum-non-c-like-repr-c.rs
Auto merge of #87284 - Aaron1011:remove-paren-special, r=petrochenkov
[rust.git] / src / test / ui / structs-enums / enum-non-c-like-repr-c.rs
1 // run-pass
2 // This test deserializes an enum in-place by transmuting to a union that
3 // should have the same layout, and manipulating the tag and payloads
4 // independently. This verifies that `repr(some_int)` has a stable representation,
5 // and that we don't miscompile these kinds of manipulations.
6
7 use std::time::Duration;
8 use std::mem;
9
10 #[repr(C)]
11 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
12 enum MyEnum {
13     A(u32),                     // Single primitive value
14     B { x: u8, y: i16, z: u8 }, // Composite, and the offset of `y` and `z`
15                                 // depend on tag being internal
16     C,                          // Empty
17     D(Option<u32>),             // Contains an enum
18     E(Duration),                // Contains a struct
19 }
20
21 #[repr(C)]
22 struct MyEnumRepr {
23     tag: MyEnumTag,
24     payload: MyEnumPayload,
25 }
26
27 #[repr(C)]
28 #[allow(non_snake_case)]
29 union MyEnumPayload {
30     A: MyEnumVariantA,
31     B: MyEnumVariantB,
32     D: MyEnumVariantD,
33     E: MyEnumVariantE,
34 }
35
36 #[repr(C)] #[derive(Copy, Clone)] enum MyEnumTag { A, B, C, D, E }
37 #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantA(u32);
38 #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantB {x: u8, y: i16, z: u8 }
39 #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantD(Option<u32>);
40 #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantE(Duration);
41
42 fn main() {
43     let result: Vec<Result<MyEnum, ()>> = vec![
44         Ok(MyEnum::A(17)),
45         Ok(MyEnum::B { x: 206, y: 1145, z: 78 }),
46         Ok(MyEnum::C),
47         Err(()),
48         Ok(MyEnum::D(Some(407))),
49         Ok(MyEnum::D(None)),
50         Ok(MyEnum::E(Duration::from_secs(100))),
51         Err(()),
52     ];
53
54     // Binary serialized version of the above (little-endian)
55     let input: Vec<u8> = vec![
56         0,  17, 0, 0, 0,
57         1,  206,  121, 4,  78,
58         2,
59         8,  /* invalid tag value */
60         3,  0,  151, 1, 0, 0,
61         3,  1,
62         4,  100, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
63         0,  /* incomplete value */
64     ];
65
66     let mut output = vec![];
67     let mut buf = &input[..];
68
69     unsafe {
70         // This should be safe, because we don't match on it unless it's fully formed,
71         // and it doesn't have a destructor.
72         //
73         // Furthermore, there are no types within MyEnum which cannot be initialized with zero,
74         // specifically, though padding and such are present, there are no references or similar
75         // types.
76         let mut dest: MyEnum = mem::zeroed();
77         while buf.len() > 0 {
78             match parse_my_enum(&mut dest, &mut buf) {
79                 Ok(()) => output.push(Ok(dest)),
80                 Err(()) => output.push(Err(())),
81             }
82         }
83     }
84
85     assert_eq!(output, result);
86 }
87
88 fn parse_my_enum<'a>(dest: &'a mut MyEnum, buf: &mut &[u8]) -> Result<(), ()> {
89     unsafe {
90         // Should be correct to do this transmute.
91         let dest: &'a mut MyEnumRepr = mem::transmute(dest);
92         let tag = read_u8(buf)?;
93
94         dest.tag = match tag {
95             0 => MyEnumTag::A,
96             1 => MyEnumTag::B,
97             2 => MyEnumTag::C,
98             3 => MyEnumTag::D,
99             4 => MyEnumTag::E,
100             _ => return Err(()),
101         };
102
103         match dest.tag {
104             MyEnumTag::A => {
105                 dest.payload.A.0 = read_u32_le(buf)?;
106             }
107             MyEnumTag::B => {
108                 dest.payload.B.x = read_u8(buf)?;
109                 dest.payload.B.y = read_u16_le(buf)? as i16;
110                 dest.payload.B.z = read_u8(buf)?;
111             }
112             MyEnumTag::C => {
113                 /* do nothing */
114             }
115             MyEnumTag::D => {
116                 let is_some = read_u8(buf)? == 0;
117                 if is_some {
118                     dest.payload.D.0 = Some(read_u32_le(buf)?);
119                 } else {
120                     dest.payload.D.0 = None;
121                 }
122             }
123             MyEnumTag::E => {
124                 let secs = read_u64_le(buf)?;
125                 let nanos = read_u32_le(buf)?;
126                 dest.payload.E.0 = Duration::new(secs, nanos);
127             }
128         }
129         Ok(())
130     }
131 }
132
133
134
135 // reader helpers
136
137 fn read_u64_le(buf: &mut &[u8]) -> Result<u64, ()> {
138     if buf.len() < 8 { return Err(()) }
139     let val = (buf[0] as u64) << 0
140             | (buf[1] as u64) << 8
141             | (buf[2] as u64) << 16
142             | (buf[3] as u64) << 24
143             | (buf[4] as u64) << 32
144             | (buf[5] as u64) << 40
145             | (buf[6] as u64) << 48
146             | (buf[7] as u64) << 56;
147     *buf = &buf[8..];
148     Ok(val)
149 }
150
151 fn read_u32_le(buf: &mut &[u8]) -> Result<u32, ()> {
152     if buf.len() < 4 { return Err(()) }
153     let val = (buf[0] as u32) << 0
154             | (buf[1] as u32) << 8
155             | (buf[2] as u32) << 16
156             | (buf[3] as u32) << 24;
157     *buf = &buf[4..];
158     Ok(val)
159 }
160
161 fn read_u16_le(buf: &mut &[u8]) -> Result<u16, ()> {
162     if buf.len() < 2 { return Err(()) }
163     let val = (buf[0] as u16) << 0
164             | (buf[1] as u16) << 8;
165     *buf = &buf[2..];
166     Ok(val)
167 }
168
169 fn read_u8(buf: &mut &[u8]) -> Result<u8, ()> {
170     if buf.len() < 1 { return Err(()) }
171     let val = buf[0];
172     *buf = &buf[1..];
173     Ok(val)
174 }