]> git.lizzy.rs Git - rust.git/commitdiff
Only mark unions as uninhabited if all of their fields are uninhabited. Fixes #46845.
authorJonathan S <gereeter+code@gmail.com>
Tue, 19 Dec 2017 23:24:38 +0000 (17:24 -0600)
committerJonathan S <gereeter+code@gmail.com>
Tue, 19 Dec 2017 23:24:38 +0000 (17:24 -0600)
src/librustc/ty/layout.rs
src/test/run-pass/issue-46845.rs [new file with mode: 0644]

index a2692fb8f5a1ed17728866a660964cdd2cc9cc92..68aa553c529a682d6eb38489f750b0155c072079 100644 (file)
@@ -1352,22 +1352,19 @@ enum StructKind {
                     }).collect::<Result<Vec<_>, _>>()
                 }).collect::<Result<Vec<_>, _>>()?;
 
-                let (inh_first, inh_second) = {
-                    let mut inh_variants = (0..variants.len()).filter(|&v| {
-                        variants[v].iter().all(|f| f.abi != Abi::Uninhabited)
-                    });
-                    (inh_variants.next(), inh_variants.next())
-                };
-                if inh_first.is_none() {
-                    // Uninhabited because it has no variants, or only uninhabited ones.
-                    return Ok(tcx.intern_layout(LayoutDetails::uninhabited(0)));
-                }
-
                 if def.is_union() {
                     let packed = def.repr.packed();
                     if packed && def.repr.align > 0 {
                         bug!("Union cannot be packed and aligned");
                     }
+                    if variants.len() != 1 {
+                        bug!("Union must be represented as a single variant");
+                    }
+
+                    if variants[0].iter().all(|f| f.abi == Abi::Uninhabited) {
+                        // Uninhabited because it has only uninhabited variants/fields.
+                        return Ok(tcx.intern_layout(LayoutDetails::uninhabited(0)));
+                    }
 
                     let mut align = if def.repr.packed() {
                         dl.i8_align
@@ -1400,6 +1397,17 @@ enum StructKind {
                     }));
                 }
 
+                let (inh_first, inh_second) = {
+                    let mut inh_variants = (0..variants.len()).filter(|&v| {
+                        variants[v].iter().all(|f| f.abi != Abi::Uninhabited)
+                    });
+                    (inh_variants.next(), inh_variants.next())
+                };
+                if inh_first.is_none() {
+                    // Uninhabited because it has no variants, or only uninhabited ones.
+                    return Ok(tcx.intern_layout(LayoutDetails::uninhabited(0)));
+                }
+
                 let is_struct = !def.is_enum() ||
                     // Only one variant is inhabited.
                     (inh_second.is_none() &&
diff --git a/src/test/run-pass/issue-46845.rs b/src/test/run-pass/issue-46845.rs
new file mode 100644 (file)
index 0000000..235d398
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// To work around #46855
+// compile-flags: -Z mir-opt-level=0
+// Regression test for the inhabitedness of unions with uninhabited variants, issue #46845
+
+use std::mem;
+
+#[derive(Copy, Clone)]
+enum Never { }
+
+// A single uninhabited variant shouldn't make the whole union uninhabited.
+union Foo {
+    a: u64,
+    _b: Never
+}
+
+// If all the variants are uninhabited, however, the union should be uninhabited.
+union Bar {
+    _a: (Never, u64),
+    _b: (u64, Never)
+}
+
+fn main() {
+    assert_eq!(mem::size_of::<Foo>(), 8);
+    assert_eq!(mem::size_of::<Bar>(), 0);
+
+    let f = [Foo { a: 42 }, Foo { a: 10 }];
+    println!("{}", unsafe { f[0].a });
+    assert_eq!(unsafe { f[1].a }, 10);
+}