diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 7d2b21b9aecda..e01b50113b9fe 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -913,11 +913,13 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { } return Ok(tcx.intern_layout(LayoutDetails { - variants: Variants::NicheFilling { - dataful_variant: i, - niche_variants, - niche: niche_scalar, - niche_start, + variants: Variants::Multiple { + discr: niche_scalar, + discr_kind: DiscriminantKind::Niche { + dataful_variant: i, + niche_variants, + niche_start, + }, variants: st, }, fields: FieldPlacement::Arbitrary { @@ -1137,8 +1139,9 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { } tcx.intern_layout(LayoutDetails { - variants: Variants::Tagged { - tag, + variants: Variants::Multiple { + discr: tag, + discr_kind: DiscriminantKind::Tag, variants: layout_variants, }, fields: FieldPlacement::Arbitrary { @@ -1293,8 +1296,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { } } - Variants::NicheFilling { .. } | - Variants::Tagged { .. } => { + Variants::Multiple { ref discr, ref discr_kind, .. } => { debug!("print-type-size `{:#?}` adt general variants def {}", layout.ty, adt_def.variants.len()); let variant_infos: Vec<_> = @@ -1306,8 +1308,8 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { layout.for_variant(self, i)) }) .collect(); - record(adt_kind.into(), adt_packed, match layout.variants { - Variants::Tagged { ref tag, .. } => Some(tag.value.size(self)), + record(adt_kind.into(), adt_packed, match discr_kind { + DiscriminantKind::Tag => Some(discr.value.size(self)), _ => None }, variant_infos); } @@ -1627,8 +1629,7 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx> }) } - Variants::NicheFilling { ref variants, .. } | - Variants::Tagged { ref variants, .. } => { + Variants::Multiple { ref variants, .. } => { &variants[variant_index] } }; @@ -1735,8 +1736,7 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx> } // Discriminant field for enums (where applicable). - Variants::Tagged { tag: ref discr, .. } | - Variants::NicheFilling { niche: ref discr, .. } => { + Variants::Multiple { ref discr, .. } => { assert_eq!(i, 0); let layout = LayoutDetails::scalar(cx, discr.clone()); return MaybeResult::from_ok(TyLayout { @@ -1881,26 +1881,37 @@ impl<'a> HashStable> for Variants { Single { index } => { index.hash_stable(hcx, hasher); } - Tagged { - ref tag, + Multiple { + ref discr, + ref discr_kind, ref variants, } => { - tag.hash_stable(hcx, hasher); + discr.hash_stable(hcx, hasher); + discr_kind.hash_stable(hcx, hasher); variants.hash_stable(hcx, hasher); } - NicheFilling { + } + } +} + +impl<'a> HashStable> for DiscriminantKind { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher) { + use crate::ty::layout::DiscriminantKind::*; + mem::discriminant(self).hash_stable(hcx, hasher); + + match *self { + Tag => {} + Niche { dataful_variant, ref niche_variants, - ref niche, niche_start, - ref variants, } => { dataful_variant.hash_stable(hcx, hasher); niche_variants.start().hash_stable(hcx, hasher); niche_variants.end().hash_stable(hcx, hasher); - niche.hash_stable(hcx, hasher); niche_start.hash_stable(hcx, hasher); - variants.hash_stable(hcx, hasher); } } } diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 042a8c60cfaab..d2bbc00868a43 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -1235,7 +1235,11 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { } ] } - layout::Variants::Tagged { ref variants, .. } => { + layout::Variants::Multiple { + discr_kind: layout::DiscriminantKind::Tag, + ref variants, + .. + } => { let discriminant_info = if fallback { RegularDiscriminant(self.discriminant_type_metadata .expect("")) @@ -1277,12 +1281,14 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { } }).collect() } - layout::Variants::NicheFilling { - ref niche_variants, - niche_start, + layout::Variants::Multiple { + discr_kind: layout::DiscriminantKind::Niche { + ref niche_variants, + niche_start, + dataful_variant, + }, + ref discr, ref variants, - dataful_variant, - ref niche, } => { if fallback { let variant = self.layout.for_variant(cx, dataful_variant); @@ -1369,7 +1375,11 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { let value = (i.as_u32() as u128) .wrapping_sub(niche_variants.start().as_u32() as u128) .wrapping_add(niche_start); - let value = truncate(value, niche.value.size(cx)); + let value = truncate(value, discr.value.size(cx)); + // NOTE(eddyb) do *NOT* remove this assert, until + // we pass the full 128-bit value to LLVM, otherwise + // truncation will be silent and remain undetected. + assert_eq!(value as u64 as u128, value); Some(value as u64) }; @@ -1586,8 +1596,11 @@ fn prepare_enum_metadata( let layout = cx.layout_of(enum_type); match (&layout.abi, &layout.variants) { - (&layout::Abi::Scalar(_), &layout::Variants::Tagged {ref tag, .. }) => - return FinalMetadata(discriminant_type_metadata(tag.value)), + (&layout::Abi::Scalar(_), &layout::Variants::Multiple { + discr_kind: layout::DiscriminantKind::Tag, + ref discr, + .. + }) => return FinalMetadata(discriminant_type_metadata(discr.value)), _ => {} } @@ -1599,9 +1612,16 @@ fn prepare_enum_metadata( if use_enum_fallback(cx) { let discriminant_type_metadata = match layout.variants { layout::Variants::Single { .. } | - layout::Variants::NicheFilling { .. } => None, - layout::Variants::Tagged { ref tag, .. } => { - Some(discriminant_type_metadata(tag.value)) + layout::Variants::Multiple { + discr_kind: layout::DiscriminantKind::Niche { .. }, + .. + } => None, + layout::Variants::Multiple { + discr_kind: layout::DiscriminantKind::Tag, + ref discr, + .. + } => { + Some(discriminant_type_metadata(discr.value)) } }; @@ -1636,16 +1656,20 @@ fn prepare_enum_metadata( ); } - let discriminator_metadata = match &layout.variants { + let discriminator_metadata = match layout.variants { // A single-variant enum has no discriminant. - &layout::Variants::Single { .. } => None, + layout::Variants::Single { .. } => None, - &layout::Variants::NicheFilling { ref niche, .. } => { + layout::Variants::Multiple { + discr_kind: layout::DiscriminantKind::Niche { .. }, + ref discr, + .. + } => { // Find the integer type of the correct size. - let size = niche.value.size(cx); - let align = niche.value.align(cx); + let size = discr.value.size(cx); + let align = discr.value.align(cx); - let discr_type = match niche.value { + let discr_type = match discr.value { layout::Int(t, _) => t, layout::Float(layout::FloatTy::F32) => Integer::I32, layout::Float(layout::FloatTy::F64) => Integer::I64, @@ -1668,8 +1692,12 @@ fn prepare_enum_metadata( } }, - &layout::Variants::Tagged { ref tag, .. } => { - let discr_type = tag.value.to_ty(cx.tcx); + layout::Variants::Multiple { + discr_kind: layout::DiscriminantKind::Tag, + ref discr, + .. + } => { + let discr_type = discr.value.to_ty(cx.tcx); let (size, align) = cx.size_and_align_of(discr_type); let discr_metadata = basic_type_metadata(cx, discr_type); diff --git a/src/librustc_codegen_llvm/type_of.rs b/src/librustc_codegen_llvm/type_of.rs index fb5624d56078e..020447608eebe 100644 --- a/src/librustc_codegen_llvm/type_of.rs +++ b/src/librustc_codegen_llvm/type_of.rs @@ -452,7 +452,13 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { _ => { let mut data_variant = match self.variants { - layout::Variants::NicheFilling { dataful_variant, .. } => { + layout::Variants::Multiple { + discr_kind: layout::DiscriminantKind::Niche { + dataful_variant, + .. + }, + .. + } => { // Only the niche itself is always initialized, // so only check for a pointer at its offset. // diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs index 7cafa0088a012..39d7638ae0881 100644 --- a/src/librustc_codegen_ssa/mir/place.rs +++ b/src/librustc_codegen_ssa/mir/place.rs @@ -216,37 +216,36 @@ impl<'a, 'tcx: 'a, V: CodegenObject> PlaceRef<'tcx, V> { if self.layout.abi.is_uninhabited() { return bx.cx().const_undef(cast_to); } - match self.layout.variants { + let (discr_scalar, discr_kind) = match self.layout.variants { layout::Variants::Single { index } => { let discr_val = self.layout.ty.ty_adt_def().map_or( index.as_u32() as u128, |def| def.discriminant_for_variant(bx.cx().tcx(), index).val); return bx.cx().const_uint_big(cast_to, discr_val); } - layout::Variants::Tagged { .. } | - layout::Variants::NicheFilling { .. } => {}, - } + layout::Variants::Multiple { ref discr, ref discr_kind, .. } => { + (discr, discr_kind) + } + }; let discr = self.project_field(bx, 0); let lldiscr = bx.load_operand(discr).immediate(); - match self.layout.variants { - layout::Variants::Single { .. } => bug!(), - layout::Variants::Tagged { ref tag, .. } => { - let signed = match tag.value { + match *discr_kind { + layout::DiscriminantKind::Tag => { + let signed = match discr_scalar.value { // We use `i1` for bytes that are always `0` or `1`, // e.g., `#[repr(i8)] enum E { A, B }`, but we can't // let LLVM interpret the `i1` as signed, because // then `i1 1` (i.e., E::B) is effectively `i8 -1`. - layout::Int(_, signed) => !tag.is_bool() && signed, + layout::Int(_, signed) => !discr_scalar.is_bool() && signed, _ => false }; bx.intcast(lldiscr, cast_to, signed) } - layout::Variants::NicheFilling { + layout::DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start, - .. } => { let niche_llty = bx.cx().immediate_backend_type(discr.layout); if niche_variants.start() == niche_variants.end() { @@ -291,7 +290,10 @@ impl<'a, 'tcx: 'a, V: CodegenObject> PlaceRef<'tcx, V> { layout::Variants::Single { index } => { assert_eq!(index, variant_index); } - layout::Variants::Tagged { .. } => { + layout::Variants::Multiple { + discr_kind: layout::DiscriminantKind::Tag, + .. + } => { let ptr = self.project_field(bx, 0); let to = self.layout.ty.ty_adt_def().unwrap() .discriminant_for_variant(bx.tcx(), variant_index) @@ -301,10 +303,12 @@ impl<'a, 'tcx: 'a, V: CodegenObject> PlaceRef<'tcx, V> { ptr.llval, ptr.align); } - layout::Variants::NicheFilling { - dataful_variant, - ref niche_variants, - niche_start, + layout::Variants::Multiple { + discr_kind: layout::DiscriminantKind::Niche { + dataful_variant, + ref niche_variants, + niche_start, + }, .. } => { if variant_index != dataful_variant { diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs index b8131671320e1..6815d08321ef5 100644 --- a/src/librustc_codegen_ssa/mir/rvalue.rs +++ b/src/librustc_codegen_ssa/mir/rvalue.rs @@ -303,8 +303,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }); } } - layout::Variants::Tagged { .. } | - layout::Variants::NicheFilling { .. } => {}, + layout::Variants::Multiple { .. } => {}, } let llval = operand.immediate(); diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 4ad1a00afe97c..494a9bb73ed4b 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -842,51 +842,56 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences { let item_def_id = cx.tcx.hir().local_def_id_from_hir_id(it.hir_id); let t = cx.tcx.type_of(item_def_id); let ty = cx.tcx.erase_regions(&t); - match cx.layout_of(ty) { - Ok(layout) => { - let variants = &layout.variants; - if let layout::Variants::Tagged { ref variants, ref tag, .. } = variants { - let discr_size = tag.value.size(&cx.tcx).bytes(); - - debug!("enum `{}` is {} bytes large with layout:\n{:#?}", - t, layout.size.bytes(), layout); - - let (largest, slargest, largest_index) = enum_definition.variants - .iter() - .zip(variants) - .map(|(variant, variant_layout)| { - // Subtract the size of the enum discriminant. - let bytes = variant_layout.size.bytes().saturating_sub(discr_size); - - debug!("- variant `{}` is {} bytes large", - variant.node.ident, - bytes); - bytes - }) - .enumerate() - .fold((0, 0, 0), |(l, s, li), (idx, size)| if size > l { - (size, l, idx) - } else if size > s { - (l, size, li) - } else { - (l, s, li) - }); - - // We only warn if the largest variant is at least thrice as large as - // the second-largest. - if largest > slargest * 3 && slargest > 0 { - cx.span_lint(VARIANT_SIZE_DIFFERENCES, - enum_definition.variants[largest_index].span, - &format!("enum variant is more than three times \ - larger ({} bytes) than the next largest", - largest)); - } - } - } + let layout = match cx.layout_of(ty) { + Ok(layout) => layout, Err(ty::layout::LayoutError::Unknown(_)) => return, Err(err @ ty::layout::LayoutError::SizeOverflow(_)) => { bug!("failed to get layout for `{}`: {}", t, err); } + }; + let (variants, tag) = match layout.variants { + layout::Variants::Multiple { + discr_kind: layout::DiscriminantKind::Tag, + ref discr, + ref variants, + } => (variants, discr), + _ => return, + }; + + let discr_size = tag.value.size(&cx.tcx).bytes(); + + debug!("enum `{}` is {} bytes large with layout:\n{:#?}", + t, layout.size.bytes(), layout); + + let (largest, slargest, largest_index) = enum_definition.variants + .iter() + .zip(variants) + .map(|(variant, variant_layout)| { + // Subtract the size of the enum discriminant. + let bytes = variant_layout.size.bytes().saturating_sub(discr_size); + + debug!("- variant `{}` is {} bytes large", + variant.node.ident, + bytes); + bytes + }) + .enumerate() + .fold((0, 0, 0), |(l, s, li), (idx, size)| if size > l { + (size, l, idx) + } else if size > s { + (l, size, li) + } else { + (l, s, li) + }); + + // We only warn if the largest variant is at least thrice as large as + // the second-largest. + if largest > slargest * 3 && slargest > 0 { + cx.span_lint(VARIANT_SIZE_DIFFERENCES, + enum_definition.variants[largest_index].span, + &format!("enum variant is more than three times \ + larger ({} bytes) than the next largest", + largest)); } } } diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 003c2182d0b45..ba61b03ea673b 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -64,8 +64,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> dest); } } - layout::Variants::Tagged { .. } | - layout::Variants::NicheFilling { .. } => {}, + layout::Variants::Multiple { .. } => {}, } let dest_val = self.cast_scalar(src.to_scalar()?, src.layout, dest.layout)?; diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 15b6d5c914d20..38a9371b92723 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -610,25 +610,24 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> ) -> EvalResult<'tcx, (u128, VariantIdx)> { trace!("read_discriminant_value {:#?}", rval.layout); - match rval.layout.variants { + let discr_kind = match rval.layout.variants { layout::Variants::Single { index } => { let discr_val = rval.layout.ty.ty_adt_def().map_or( index.as_u32() as u128, |def| def.discriminant_for_variant(*self.tcx, index).val); return Ok((discr_val, index)); } - layout::Variants::Tagged { .. } | - layout::Variants::NicheFilling { .. } => {}, - } + layout::Variants::Multiple { ref discr_kind, .. } => discr_kind, + }; + // read raw discriminant value let discr_op = self.operand_field(rval, 0)?; let discr_val = self.read_immediate(discr_op)?; let raw_discr = discr_val.to_scalar_or_undef(); trace!("discr value: {:?}", raw_discr); // post-process - Ok(match rval.layout.variants { - layout::Variants::Single { .. } => bug!(), - layout::Variants::Tagged { .. } => { + Ok(match *discr_kind { + layout::DiscriminantKind::Tag => { let bits_discr = match raw_discr.to_bits(discr_val.layout.size) { Ok(raw_discr) => raw_discr, Err(_) => return err!(InvalidDiscriminant(raw_discr.erase_tag())), @@ -657,11 +656,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> .ok_or_else(|| EvalErrorKind::InvalidDiscriminant(raw_discr.erase_tag()))?; (real_discr, index.0) }, - layout::Variants::NicheFilling { + layout::DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start, - .. } => { let variants_start = niche_variants.start().as_u32() as u128; let variants_end = niche_variants.end().as_u32() as u128; diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 82b92acdb764c..4d51772d5ea14 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -958,7 +958,11 @@ where layout::Variants::Single { index } => { assert_eq!(index, variant_index); } - layout::Variants::Tagged { ref tag, .. } => { + layout::Variants::Multiple { + discr_kind: layout::DiscriminantKind::Tag, + ref discr, + .. + } => { let adt_def = dest.layout.ty.ty_adt_def().unwrap(); assert!(variant_index.as_usize() < adt_def.variants.len()); let discr_val = adt_def @@ -968,16 +972,18 @@ where // raw discriminants for enums are isize or bigger during // their computation, but the in-memory tag is the smallest possible // representation - let size = tag.value.size(self); + let size = discr.value.size(self); let discr_val = truncate(discr_val, size); let discr_dest = self.place_field(dest, 0)?; self.write_scalar(Scalar::from_uint(discr_val, size), discr_dest)?; } - layout::Variants::NicheFilling { - dataful_variant, - ref niche_variants, - niche_start, + layout::Variants::Multiple { + discr_kind: layout::DiscriminantKind::Niche { + dataful_variant, + ref niche_variants, + niche_start, + }, .. } => { assert!( diff --git a/src/librustc_mir/interpret/visitor.rs b/src/librustc_mir/interpret/visitor.rs index 90d4fff421837..05343ac66d966 100644 --- a/src/librustc_mir/interpret/visitor.rs +++ b/src/librustc_mir/interpret/visitor.rs @@ -241,8 +241,7 @@ macro_rules! make_value_visitor { // If this is a multi-variant layout, we have find the right one and proceed with // that. match v.layout().variants { - layout::Variants::NicheFilling { .. } | - layout::Variants::Tagged { .. } => { + layout::Variants::Multiple { .. } => { let op = v.to_op(self.ecx())?; let idx = self.ecx().read_discriminant(op)?.1; let inner = v.project_downcast(self.ecx(), idx)?; diff --git a/src/librustc_target/abi/call/x86_64.rs b/src/librustc_target/abi/call/x86_64.rs index 680e529b108e0..b68c70224c95e 100644 --- a/src/librustc_target/abi/call/x86_64.rs +++ b/src/librustc_target/abi/call/x86_64.rs @@ -61,8 +61,7 @@ fn classify_arg<'a, Ty, C>(cx: &C, arg: &ArgType<'a, Ty>) } return Ok(()); } - abi::Variants::Tagged { .. } | - abi::Variants::NicheFilling { .. } => return Err(Memory), + abi::Variants::Multiple { .. } => return Err(Memory), } } diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index 8b96a8c1658b2..235b530a7ef2f 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -828,15 +828,22 @@ pub enum Variants { index: VariantIdx, }, - /// General-case enums: for each case there is a struct, and they all have - /// all space reserved for the tag, and their first field starts - /// at a non-0 offset, after where the tag would go. - Tagged { - tag: Scalar, + /// Enums with more than one inhabited variant: for each case there is + /// a struct, and they all have space reserved for the discriminant, + /// which is the sole field of the enum layout. + Multiple { + discr: Scalar, + discr_kind: DiscriminantKind, variants: IndexVec, }, +} + +#[derive(PartialEq, Eq, Hash, Debug)] +pub enum DiscriminantKind { + /// Integer tag holding the discriminant value itself. + Tag, - /// Multiple cases distinguished by a niche (values invalid for a type): + /// Niche (values invalid for a type) encoding the discriminant: /// the variant `dataful_variant` contains a niche at an arbitrary /// offset (field 0 of the enum), which for a variant with discriminant /// `d` is set to `(d - niche_variants.start).wrapping_add(niche_start)`. @@ -844,13 +851,11 @@ pub enum Variants { /// For example, `Option<(usize, &T)>` is represented such that /// `None` has a null pointer for the second tuple field, and /// `Some` is the identity function (with a non-null reference). - NicheFilling { + Niche { dataful_variant: VariantIdx, niche_variants: RangeInclusive, - niche: Scalar, niche_start: u128, - variants: IndexVec, - } + }, } #[derive(PartialEq, Eq, Hash, Debug)]