diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 74b3bc609ab..f4960b5680f 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -550,6 +550,7 @@ impl<'a, 'gcx, 'tcx> Struct { -> Result<(), LayoutError<'gcx>> where I: Iterator>> { let fields = fields.collect::, LayoutError<'gcx>>>()?; + if is_enum_variant { assert!(fields.len() >= 1, "Enum variants must have at least a discriminant field.") } if fields.len() == 0 {return Ok(())}; self.offsets = vec![Size::from_bytes(0); fields.len()]; @@ -566,6 +567,7 @@ impl<'a, 'gcx, 'tcx> Struct { let optimizing = &mut inverse_gep_index[start..end]; optimizing.sort_by_key(|&x| fields[x as usize].align(dl).abi()); } + if is_enum_variant { assert_eq!(inverse_gep_index[0], 0, "Enums must have field 0 as the field with lowest offset.") } } // At this point, inverse_gep_index holds field indices by increasing offset. @@ -1053,7 +1055,7 @@ impl<'a, 'gcx, 'tcx> Layout { // Tuples and closures. ty::TyClosure(def_id, ref substs) => { let tys = substs.upvar_tys(def_id, tcx); - let mut st = Struct::new(dl, + let st = Struct::new(dl, tys.map(|ty| ty.layout(infcx)), attr::ReprAny, false, ty)?; @@ -1228,7 +1230,8 @@ impl<'a, 'gcx, 'tcx> Layout { hint, false, ty)?; // Find the first field we can't move later // to make room for a larger discriminant. - for i in st.field_index_by_increasing_offset() { + // It is important to skip the first field. + for i in st.field_index_by_increasing_offset().skip(1) { let field = fields[i].unwrap(); let field_align = field.align(dl); if field.size(dl).bytes() != 0 || field_align.abi() != 1 { @@ -1270,7 +1273,9 @@ impl<'a, 'gcx, 'tcx> Layout { let new_ity_size = Int(ity).size(dl); for variant in &mut variants { for i in variant.offsets.iter_mut() { - if *i <= old_ity_size { + // The first field is the discrimminant, at offset 0. + // These aren't in order, and we need to skip it. + if *i <= old_ity_size && *i > Size::from_bytes(0){ *i = new_ity_size; } } diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 5e9fdbfa073..49bbcc18efb 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -750,7 +750,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences { if let Layout::General { ref variants, ref size, discr, .. } = *layout { let discr_size = Primitive::Int(discr).size(&cx.tcx.data_layout).bytes(); - debug!("enum `{}` is {} bytes large", t, size.bytes()); + debug!("enum `{}` is {} bytes large with layout:\n{:#?}", t, size.bytes(), layout); let (largest, slargest, largest_index) = enum_definition.variants .iter() diff --git a/src/test/run-pass/enum-size-variance.rs b/src/test/run-pass/enum-size-variance.rs index 26deb0ed72a..beccbf5eb03 100644 --- a/src/test/run-pass/enum-size-variance.rs +++ b/src/test/run-pass/enum-size-variance.rs @@ -11,6 +11,9 @@ #![warn(variant_size_differences)] #![allow(dead_code)] +// Note that the following test only works because all fields of the enum variants are of the same size. +// If this test is modified so that the reordering logic in librustc/ty/layout.rs kicks in, it will fail. + enum Enum1 { } enum Enum2 { A, B, C } diff --git a/src/test/run-pass/nonzero-enum.rs b/src/test/run-pass/nonzero-enum.rs index 266506e04b7..2cd3136b0eb 100644 --- a/src/test/run-pass/nonzero-enum.rs +++ b/src/test/run-pass/nonzero-enum.rs @@ -26,8 +26,9 @@ fn main() { assert_eq!(size_of::(), 1); assert_eq!(size_of::>(), 1); assert_eq!(size_of::>(), 1); - assert_eq!(size_of::(), 4); - assert_eq!(size_of::>(), 4); + // The next asserts are correct given the currently dumb field reordering algorithm, which actually makes this struct larger. + assert_eq!(size_of::(), 6); + assert_eq!(size_of::>(), 6); let enone = None::; let esome = Some(E::A); if let Some(..) = enone {