]> git.lizzy.rs Git - rust.git/blob - src/test/ui/structs-enums/enum-non-c-like-repr-int.rs
Override rustc version in ui and mir-opt tests to get stable hashes
[rust.git] / src / test / ui / structs-enums / enum-non-c-like-repr-int.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(u8)]
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 #[allow(non_snake_case)]
22 #[repr(C)]
23 union MyEnumRepr {
24     A: MyEnumVariantA,
25     B: MyEnumVariantB,
26     C: MyEnumVariantC,
27     D: MyEnumVariantD,
28     E: MyEnumVariantE,
29 }
30
31 #[repr(u8)] #[derive(Copy, Clone)] enum MyEnumTag { A, B, C, D, E }
32 #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantA(MyEnumTag, u32);
33 #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantB { tag: MyEnumTag, x: u8, y: i16, z: u8 }
34 #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantC(MyEnumTag);
35 #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantD(MyEnumTag, Option<u32>);
36 #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantE(MyEnumTag, Duration);
37
38 fn main() {
39     let result: Vec<Result<MyEnum, ()>> = vec![
40         Ok(MyEnum::A(17)),
41         Ok(MyEnum::B { x: 206, y: 1145, z: 78 }),
42         Ok(MyEnum::C),
43         Err(()),
44         Ok(MyEnum::D(Some(407))),
45         Ok(MyEnum::D(None)),
46         Ok(MyEnum::E(Duration::from_secs(100))),
47         Err(()),
48     ];
49
50     // Binary serialized version of the above (little-endian)
51     let input: Vec<u8> = vec![
52         0,  17, 0, 0, 0,
53         1,  206,  121, 4,  78,
54         2,
55         8,  /* invalid tag value */
56         3,  0,  151, 1, 0, 0,
57         3,  1,
58         4,  100, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
59         0,  /* incomplete value */
60     ];
61
62     let mut output = vec![];
63     let mut buf = &input[..];
64
65     unsafe {
66         // This should be safe, because we don't match on it unless it's fully formed,
67         // and it doesn't have a destructor.
68         //
69         // MyEnum is repr(u8) so it is guaranteed to have a separate discriminant and each variant
70         // can be zero initialized.
71         let mut dest: MyEnum = mem::zeroed();
72         while buf.len() > 0 {
73             match parse_my_enum(&mut dest, &mut buf) {
74                 Ok(()) => output.push(Ok(dest)),
75                 Err(()) => output.push(Err(())),
76             }
77         }
78     }
79
80     assert_eq!(output, result);
81 }
82
83 fn parse_my_enum<'a>(dest: &'a mut MyEnum, buf: &mut &[u8]) -> Result<(), ()> {
84     unsafe {
85         // Should be correct to do this transmute.
86         let dest: &'a mut MyEnumRepr = mem::transmute(dest);
87         let tag = read_u8(buf)?;
88
89         dest.A.0 = match tag {
90             0 => MyEnumTag::A,
91             1 => MyEnumTag::B,
92             2 => MyEnumTag::C,
93             3 => MyEnumTag::D,
94             4 => MyEnumTag::E,
95             _ => return Err(()),
96         };
97
98         match dest.B.tag {
99             MyEnumTag::A => {
100                 dest.A.1 = read_u32_le(buf)?;
101             }
102             MyEnumTag::B => {
103                 dest.B.x = read_u8(buf)?;
104                 dest.B.y = read_u16_le(buf)? as i16;
105                 dest.B.z = read_u8(buf)?;
106             }
107             MyEnumTag::C => {
108                 /* do nothing */
109             }
110             MyEnumTag::D => {
111                 let is_some = read_u8(buf)? == 0;
112                 if is_some {
113                     dest.D.1 = Some(read_u32_le(buf)?);
114                 } else {
115                     dest.D.1 = None;
116                 }
117             }
118             MyEnumTag::E => {
119                 let secs = read_u64_le(buf)?;
120                 let nanos = read_u32_le(buf)?;
121                 dest.E.1 = Duration::new(secs, nanos);
122             }
123         }
124         Ok(())
125     }
126 }
127
128
129
130 // reader helpers
131
132 fn read_u64_le(buf: &mut &[u8]) -> Result<u64, ()> {
133     if buf.len() < 8 { return Err(()) }
134     let val = (buf[0] as u64) << 0
135             | (buf[1] as u64) << 8
136             | (buf[2] as u64) << 16
137             | (buf[3] as u64) << 24
138             | (buf[4] as u64) << 32
139             | (buf[5] as u64) << 40
140             | (buf[6] as u64) << 48
141             | (buf[7] as u64) << 56;
142     *buf = &buf[8..];
143     Ok(val)
144 }
145
146 fn read_u32_le(buf: &mut &[u8]) -> Result<u32, ()> {
147     if buf.len() < 4 { return Err(()) }
148     let val = (buf[0] as u32) << 0
149             | (buf[1] as u32) << 8
150             | (buf[2] as u32) << 16
151             | (buf[3] as u32) << 24;
152     *buf = &buf[4..];
153     Ok(val)
154 }
155
156 fn read_u16_le(buf: &mut &[u8]) -> Result<u16, ()> {
157     if buf.len() < 2 { return Err(()) }
158     let val = (buf[0] as u16) << 0
159             | (buf[1] as u16) << 8;
160     *buf = &buf[2..];
161     Ok(val)
162 }
163
164 fn read_u8(buf: &mut &[u8]) -> Result<u8, ()> {
165     if buf.len() < 1 { return Err(()) }
166     let val = buf[0];
167     *buf = &buf[1..];
168     Ok(val)
169 }