-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Using a match statement on a field of an enum results in an internal compiler error #53728
Comments
Can you provide access to the code causing the ICE? As you mention, the code in the ticket does not reproduce it. Also, could you try to compile the problematic code with the latest nightly? |
I can reproduce the issue with the latest nightly (rustc 1.30.0-nightly (7219130 2018-08-26)). |
Here is the code causing the ICE https://github.com/vladglv/bug-report . |
#[repr(u16)]
enum DeviceKind {
Nil = 0,
}
#[repr(C, packed)]
struct DeviceInfo {
endianness: u8,
device_kind: DeviceKind,
}
fn main() {
None::<(DeviceInfo, Box<u8>)>;
} This regression was introduced in Rust 1.24.0, making it regression from stable to stable, the provided example works in Rust 1.23.0. |
Was messing around a bit with different combinations to narrow down where it was. Very slightly more minimal #[repr(u16)]
enum DeviceKind {
Nil = 0,
}
#[repr(packed)]
struct DeviceInfo {
endianness: u8,
device_kind: DeviceKind,
}
fn main() {
None::<(DeviceInfo, Box<u8>)>;
} The repr(C) part is not necessary so figured I'd remove the noise. Might be interesting to note -- Also perhaps interesting to note: changing |
tagging with T-compiler and P-high since it seems to qualify to be in those buckets |
Would be great to use https://github.com/rust-lang-nursery/cargo-bisect-rustc to try and narrow this down to at least a nightly if not a specific PR... |
assigning to self to ensure that bisection gets done (by me or by anyone else who feels like posting the result of bisection here) |
I tried running the bisection but don't know if I'm just doing it wrong or there is some other issue. First I tried running from 1.23 to 1.24 and it says start has failed: Command I used:
output:
However when I build manually with Then tried going by their relative commits and got a different error. Command I used:
output:
|
I've narrowed it down to be between nightly-2017-11-20 and nightly-2017-11-21. I have not locked down the exact commit but just looking at commit logs I'm assuming its this one f50fd07 as its a refactor of memory layouts. Will take a look in the next couple days. |
visited at rustc mtg. @arielb1 volunteered to do some more investigation. |
So this is caused by the fact that #[repr(packed)]
struct Foo<T> {
_filler0: [u8; 1],
discriminant: u16, /* DeviceKind niche */
_filler1: [u8; (round_to_multiple(3, align_of::<T>()) - 3) + size_of::<T>()],
} But that second I'll try to open a PR soon. EDIT: oops it's actually EDIT2: this assert could've fired but it doesn't here because rust/src/librustc_codegen_llvm/type_of.rs Line 137 in 35a5541
EDIT3: this additional assertion does get triggered, if I add it: assert_eq!(target_offset.abi_align(field.align), target_offset); |
rustc_codegen_llvm: don't assume offsets are always aligned. Fixes rust-lang#53728 by taking into account not just overall type alignment and the field's alignment when determining whether a field is aligned or not ("packed"), but also the field's offset within the type. Previously, rustc assumed that the offset was always at least as aligned as `min(struct.align, field.align)`. However, there's no real reason to have that assumption, and it obviously can't always be true after we implement `#[repr(align(N), pack(K))]`. There's also a case today where that assumption is not true, involving niche discriminants in enums: Suppose that we have the code in rust-lang#53728: ```Rust #[repr(u16)] enum DeviceKind { Nil = 0, } #[repr(packed)] struct DeviceInfo { endianness: u8, device_kind: DeviceKind, } struct Wrapper { device_info: DeviceInfo, data: u32 } ``` Observe the layout of `Option<Wrapper>`. It has an alignment of 4 because of the `u32`. `device_info.device_kind` is a good niche field to use, which means the enum ends up with this layout: ``` size = 8 align = 4 fields = [ { offset=1, type=u16 } // discriminant, .<Some>.device_info.device_kind ] ``` And here we have an discriminant with alignment 2 (`u16`) but offset 1.
rustc_codegen_llvm: don't assume offsets are always aligned. Fixes #53728 by taking into account not just overall type alignment and the field's alignment when determining whether a field is aligned or not ("packed"), but also the field's offset within the type. Previously, rustc assumed that the offset was always at least as aligned as `min(struct.align, field.align)`. However, there's no real reason to have that assumption, and it obviously can't always be true after we implement `#[repr(align(N), pack(K))]`. There's also a case today where that assumption is not true, involving niche discriminants in enums: Suppose that we have the code in #53728: ```Rust #[repr(u16)] enum DeviceKind { Nil = 0, } #[repr(packed)] struct DeviceInfo { endianness: u8, device_kind: DeviceKind, } struct Wrapper { device_info: DeviceInfo, data: u32 } ``` Observe the layout of `Option<Wrapper>`. It has an alignment of 4 because of the `u32`. `device_info.device_kind` is a good niche field to use, which means the enum ends up with this layout: ``` size = 8 align = 4 fields = [ { offset=1, type=u16 } // discriminant, .<Some>.device_info.device_kind ] ``` And here we have an discriminant with alignment 2 (`u16`) but offset 1.
Code
Where info is
and,
Output
Extra
I attempted to reproduce the logic in a simple example. However, the code compiled and ran properly.
The assertion failed here https://github.com/rust-lang/rust/blob/stable/src/librustc_codegen_llvm/type_.rs#L293
The text was updated successfully, but these errors were encountered: