Skip to content

Commit

Permalink
Add (failing) test to check order of repr(int) enum fields
Browse files Browse the repository at this point in the history
RFC rust-lang#2195 specifies that a repr(int) enum such as:

    #[repr(u8)]
    enum MyEnum {
        B { x: u8, y: i16, z: u8 },
    }

has a layout that is equivalent to:

    #[repr(C)]
    enum MyEnumVariantB { tag: u8, x: u8, y: i16, z: u8 },

However this isn't actually implemented, with the actual layout being
roughly equivalent to:

    union MyEnumPayload {
        B { x: u8, y: i16, z: u8 },
    }

    #[repr(packed)]
    struct MyEnum {
        tag: u8,
        payload: MyEnumPayload,
    }

Thus the variant payload is *not* subject to repr(C) ordering rules, and
gets re-ordered as `{ x: u8, z: u8, z: i16 }`

The existing tests added in pull-req rust-lang#45688 fail to catch this as the
repr(C) ordering just happens to match the current Rust ordering in this
case; adding a third field reveals the problem.
  • Loading branch information
petertodd committed Dec 8, 2018
1 parent 4a45578 commit 8b80890
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ use std::mem;
#[repr(C, u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum MyEnum {
A(u32), // Single primitive value
B { x: u8, y: i16 }, // Composite, and the offset of `y` depends on tag being internal
C, // Empty
D(Option<u32>), // Contains an enum
E(Duration), // Contains a struct
A(u32), // Single primitive value
B { x: u8, y: i16, z: u8 }, // Composite, and the offset of `y` and `z` depend on tag being internal
C, // Empty
D(Option<u32>), // Contains an enum
E(Duration), // Contains a struct
}

#[repr(C)]
Expand All @@ -44,14 +44,14 @@ union MyEnumPayload {

#[repr(u8)] #[derive(Copy, Clone)] enum MyEnumTag { A, B, C, D, E }
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantA(u32);
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantB {x: u8, y: i16 }
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantB {x: u8, y: i16, z: u8 }
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantD(Option<u32>);
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantE(Duration);

fn main() {
let result: Vec<Result<MyEnum, ()>> = vec![
Ok(MyEnum::A(17)),
Ok(MyEnum::B { x: 206, y: 1145 }),
Ok(MyEnum::B { x: 206, y: 1145, z: 78 }),
Ok(MyEnum::C),
Err(()),
Ok(MyEnum::D(Some(407))),
Expand All @@ -63,7 +63,7 @@ fn main() {
// Binary serialized version of the above (little-endian)
let input: Vec<u8> = vec![
0, 17, 0, 0, 0,
1, 206, 121, 4,
1, 206, 121, 4, 78,
2,
8, /* invalid tag value */
3, 0, 151, 1, 0, 0,
Expand Down Expand Up @@ -112,6 +112,7 @@ fn parse_my_enum<'a>(dest: &'a mut MyEnum, buf: &mut &[u8]) -> Result<(), ()> {
MyEnumTag::B => {
dest.payload.B.x = read_u8(buf)?;
dest.payload.B.y = read_u16_le(buf)? as i16;
dest.payload.B.z = read_u8(buf)?;
}
MyEnumTag::C => {
/* do nothing */
Expand Down
17 changes: 9 additions & 8 deletions src/test/run-pass/structs-enums/enum-non-c-like-repr-c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ use std::mem;
#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum MyEnum {
A(u32), // Single primitive value
B { x: u8, y: i16 }, // Composite, and the offset of `y` depends on tag being internal
C, // Empty
D(Option<u32>), // Contains an enum
E(Duration), // Contains a struct
A(u32), // Single primitive value
B { x: u8, y: i16, z: u8 }, // Composite, and the offset of `y` and `z` depend on tag being internal
C, // Empty
D(Option<u32>), // Contains an enum
E(Duration), // Contains a struct
}

#[repr(C)]
Expand All @@ -44,14 +44,14 @@ union MyEnumPayload {

#[repr(C)] #[derive(Copy, Clone)] enum MyEnumTag { A, B, C, D, E }
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantA(u32);
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantB {x: u8, y: i16 }
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantB {x: u8, y: i16, z: u8 }
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantD(Option<u32>);
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantE(Duration);

fn main() {
let result: Vec<Result<MyEnum, ()>> = vec![
Ok(MyEnum::A(17)),
Ok(MyEnum::B { x: 206, y: 1145 }),
Ok(MyEnum::B { x: 206, y: 1145, z: 78 }),
Ok(MyEnum::C),
Err(()),
Ok(MyEnum::D(Some(407))),
Expand All @@ -63,7 +63,7 @@ fn main() {
// Binary serialized version of the above (little-endian)
let input: Vec<u8> = vec![
0, 17, 0, 0, 0,
1, 206, 121, 4,
1, 206, 121, 4, 78,
2,
8, /* invalid tag value */
3, 0, 151, 1, 0, 0,
Expand Down Expand Up @@ -112,6 +112,7 @@ fn parse_my_enum<'a>(dest: &'a mut MyEnum, buf: &mut &[u8]) -> Result<(), ()> {
MyEnumTag::B => {
dest.payload.B.x = read_u8(buf)?;
dest.payload.B.y = read_u16_le(buf)? as i16;
dest.payload.B.z = read_u8(buf)?;
}
MyEnumTag::C => {
/* do nothing */
Expand Down
17 changes: 9 additions & 8 deletions src/test/run-pass/structs-enums/enum-non-c-like-repr-int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ use std::mem;
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum MyEnum {
A(u32), // Single primitive value
B { x: u8, y: i16 }, // Composite, and the offset of `y` depends on tag being internal
C, // Empty
D(Option<u32>), // Contains an enum
E(Duration), // Contains a struct
A(u32), // Single primitive value
B { x: u8, y: i16, z: u8 }, // Composite, and the offset of `y` and `z` depend on tag being internal
C, // Empty
D(Option<u32>), // Contains an enum
E(Duration), // Contains a struct
}

#[allow(non_snake_case)]
Expand All @@ -39,15 +39,15 @@ union MyEnumRepr {

#[repr(u8)] #[derive(Copy, Clone)] enum MyEnumTag { A, B, C, D, E }
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantA(MyEnumTag, u32);
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantB { tag: MyEnumTag, x: u8, y: i16 }
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantB { tag: MyEnumTag, x: u8, y: i16, z: u8 }
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantC(MyEnumTag);
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantD(MyEnumTag, Option<u32>);
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantE(MyEnumTag, Duration);

fn main() {
let result: Vec<Result<MyEnum, ()>> = vec![
Ok(MyEnum::A(17)),
Ok(MyEnum::B { x: 206, y: 1145 }),
Ok(MyEnum::B { x: 206, y: 1145, z: 78 }),
Ok(MyEnum::C),
Err(()),
Ok(MyEnum::D(Some(407))),
Expand All @@ -59,7 +59,7 @@ fn main() {
// Binary serialized version of the above (little-endian)
let input: Vec<u8> = vec![
0, 17, 0, 0, 0,
1, 206, 121, 4,
1, 206, 121, 4, 78,
2,
8, /* invalid tag value */
3, 0, 151, 1, 0, 0,
Expand Down Expand Up @@ -108,6 +108,7 @@ fn parse_my_enum<'a>(dest: &'a mut MyEnum, buf: &mut &[u8]) -> Result<(), ()> {
MyEnumTag::B => {
dest.B.x = read_u8(buf)?;
dest.B.y = read_u16_le(buf)? as i16;
dest.B.z = read_u8(buf)?;
}
MyEnumTag::C => {
/* do nothing */
Expand Down

0 comments on commit 8b80890

Please sign in to comment.