diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 5e9019c92c5..44039817e72 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -47,10 +47,11 @@ #![feature(core_intrinsics)] #![feature(drain_filter)] #![feature(i128_type)] -#![feature(match_default_bindings)] +#![feature(inclusive_range)] #![feature(inclusive_range_syntax)] #![cfg_attr(windows, feature(libc))] #![feature(macro_vis_matcher)] +#![feature(match_default_bindings)] #![feature(never_type)] #![feature(nonzero)] #![feature(quote)] diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 18c3a270787..5775fc957b5 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -25,7 +25,7 @@ use std::fmt; use std::i64; use std::iter; use std::mem; -use std::ops::{Deref, Add, Sub, Mul, AddAssign}; +use std::ops::{Deref, Add, Sub, Mul, AddAssign, RangeInclusive}; use ich::StableHashingContext; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, @@ -841,9 +841,9 @@ impl<'a, 'tcx> Struct { (&Scalar(Pointer), _) if !layout.ty.is_unsafe_ptr() => { Ok(Some((Size::from_bytes(0), Pointer))) } - (&CEnum { discr, .. }, &ty::TyAdt(def, _)) => { + (&General { discr, .. }, &ty::TyAdt(def, _)) => { if def.discriminants(tcx).all(|d| d.to_u128_unchecked() != 0) { - Ok(Some((Size::from_bytes(0), discr))) + Ok(Some((layout.fields.offset(0), discr))) } else { Ok(None) } @@ -1095,18 +1095,6 @@ pub enum Layout { // Remaining variants are all ADTs such as structs, enums or tuples. - /// C-like enums; basically an integer. - CEnum { - discr: Primitive, - /// Inclusive discriminant range. - /// If min > max, it represents min...u64::MAX followed by 0...max. - // FIXME(eddyb) always use the shortest range, e.g. by finding - // the largest space between two consecutive discriminants and - // taking everything else as the (shortest) discriminant range. - min: u64, - max: u64 - }, - /// Single-case enums, and structs/tuples. Univariant(Struct), @@ -1118,6 +1106,12 @@ pub enum Layout { /// at a non-0 offset, after where the discriminant would go. General { discr: Primitive, + /// Inclusive wrap-around range of discriminant values, that is, + /// if min > max, it represents min..=u64::MAX followed by 0..=max. + // FIXME(eddyb) always use the shortest range, e.g. by finding + // the largest space between two consecutive discriminants and + // taking everything else as the (shortest) discriminant range. + discr_range: RangeInclusive, variants: Vec, size: Size, align: Align, @@ -1240,7 +1234,6 @@ impl<'a, 'tcx> Layout { FieldPlacement::union(def.struct_variant().fields.len()) } - CEnum { .. } | General { .. } => FieldPlacement::union(1), NullablePointer { ref discr_offset, .. } => { @@ -1250,19 +1243,17 @@ impl<'a, 'tcx> Layout { } }; let abi = match *layout { - Scalar(value) | - CEnum { discr: value, .. } => Abi::Scalar(value), - + Scalar(value) => Abi::Scalar(value), Vector { .. } => Abi::Vector, Array { .. } | FatPointer { .. } | Univariant(_) | - UntaggedUnion(_) | - General { .. } => Abi::Aggregate, + UntaggedUnion(_) => Abi::Aggregate, - NullablePointer { discr, discr_offset, .. } => { - if discr_offset.bytes() == 0 && discr.size(cx) == layout.size(cx) { + General { discr, .. } | + NullablePointer { discr, .. } => { + if fields.offset(0).bytes() == 0 && discr.size(cx) == layout.size(cx) { Abi::Scalar(discr) } else { Abi::Aggregate @@ -1431,7 +1422,14 @@ impl<'a, 'tcx> Layout { // ADTs. ty::TyAdt(def, substs) => { - if def.variants.is_empty() { + // Cache the field layouts. + let variants = def.variants.iter().map(|v| { + v.fields.iter().map(|field| { + cx.layout_of(field.ty(tcx, substs)) + }).collect::, _>>() + }).collect::, _>>()?; + + if variants.is_empty() { // Uninhabitable; represent as unit // (Typechecking will reject discriminant-sizing attrs.) @@ -1439,74 +1437,39 @@ impl<'a, 'tcx> Layout { &def.repr, StructKind::AlwaysSizedUnivariant, ty)?)); } - if def.is_enum() && def.variants.iter().all(|v| v.fields.is_empty()) { - // All bodies empty -> intlike - let (mut min, mut max) = (i64::max_value(), i64::min_value()); - for discr in def.discriminants(tcx) { - let x = discr.to_u128_unchecked() as i64; - if x < min { min = x; } - if x > max { max = x; } - } - - // FIXME: should handle i128? signed-value based impl is weird and hard to - // grok. - let (discr, signed) = Integer::repr_discr(tcx, ty, &def.repr, min, max); - return success(CEnum { - discr: Int(discr, signed), - // FIXME: should be u128? - min: min as u64, - max: max as u64 - }); - } - - if !def.is_enum() || (def.variants.len() == 1 && - !def.repr.inhibit_enum_layout_opt()) { + if !def.is_enum() || (variants.len() == 1 && + !def.repr.inhibit_enum_layout_opt() && + !variants[0].is_empty()) { // Struct, or union, or univariant enum equivalent to a struct. // (Typechecking will reject discriminant-sizing attrs.) - let kind = if def.is_enum() || def.variants[0].fields.len() == 0{ + let kind = if def.is_enum() || variants[0].len() == 0 { StructKind::AlwaysSizedUnivariant } else { let param_env = tcx.param_env(def.did); - let fields = &def.variants[0].fields; - let last_field = &fields[fields.len()-1]; + let last_field = def.variants[0].fields.last().unwrap(); let always_sized = tcx.type_of(last_field.did) .is_sized(tcx, param_env, DUMMY_SP); if !always_sized { StructKind::MaybeUnsizedUnivariant } else { StructKind::AlwaysSizedUnivariant } }; - let fields = def.variants[0].fields.iter().map(|field| { - cx.layout_of(field.ty(tcx, substs)) - }).collect::, _>>()?; let layout = if def.is_union() { let mut un = Union::new(dl, &def.repr); - un.extend(dl, fields.iter().map(|&f| Ok(f.layout)), ty)?; + un.extend(dl, variants[0].iter().map(|&f| Ok(f.layout)), ty)?; UntaggedUnion(un) } else { - Univariant(Struct::new(dl, &fields, &def.repr, kind, ty)?) + Univariant(Struct::new(dl, &variants[0], &def.repr, kind, ty)?) }; return success(layout); } - // Since there's at least one - // non-empty body, explicit discriminants should have - // been rejected by a checker before this point. - for (i, v) in def.variants.iter().enumerate() { - if v.discr != ty::VariantDiscr::Relative(i) { - bug!("non-C-like enum {} with specified discriminants", - tcx.item_path_str(def.did)); - } - } + let no_explicit_discriminants = def.variants.iter().enumerate() + .all(|(i, v)| v.discr == ty::VariantDiscr::Relative(i)); - // Cache the substituted and normalized variant field types. - let variants = def.variants.iter().map(|v| { - v.fields.iter().map(|field| { - cx.layout_of(field.ty(tcx, substs)) - }).collect::, _>>() - }).collect::, _>>()?; - - if variants.len() == 2 && !def.repr.inhibit_enum_layout_opt() { + if variants.len() == 2 && + !def.repr.inhibit_enum_layout_opt() && + no_explicit_discriminants { // Nullable pointer optimization let st0 = Struct::new(dl, &variants[0], &def.repr, StructKind::AlwaysSizedUnivariant, ty)?; @@ -1554,16 +1517,23 @@ impl<'a, 'tcx> Layout { } } - // The general case. - let discr_max = (variants.len() - 1) as i64; - assert!(discr_max >= 0); - let (min_ity, _) = Integer::repr_discr(tcx, ty, &def.repr, 0, discr_max); + let (mut min, mut max) = (i64::max_value(), i64::min_value()); + for discr in def.discriminants(tcx) { + let x = discr.to_u128_unchecked() as i64; + if x < min { min = x; } + if x > max { max = x; } + } + // FIXME: should handle i128? signed-value based impl is weird and hard to + // grok. + let (min_ity, signed) = Integer::repr_discr(tcx, ty, &def.repr, min, max); + let mut align = dl.aggregate_align; let mut primitive_align = dl.aggregate_align; let mut size = Size::from_bytes(0); // We're interested in the smallest alignment, so start large. let mut start_align = Align::from_bytes(256, 256).unwrap(); + assert_eq!(Integer::for_abi_align(dl, start_align), None); // Create the set of structs that represent each variant. let mut variants = variants.into_iter().map(|fields| { @@ -1644,7 +1614,10 @@ impl<'a, 'tcx> Layout { } General { - discr: Int(ity, false), + discr: Int(ity, signed), + + // FIXME: should be u128? + discr_range: (min as u64)..=(max as u64), variants, size, align, @@ -1680,7 +1653,7 @@ impl<'a, 'tcx> Layout { pub fn is_unsized(&self) -> bool { match *self { Scalar(_) | Vector {..} | FatPointer {..} | - CEnum {..} | UntaggedUnion {..} | General {..} | + UntaggedUnion {..} | General {..} | NullablePointer {..} => false, Array { sized, .. } | @@ -1720,7 +1693,6 @@ impl<'a, 'tcx> Layout { metadata.size(dl)).abi_align(self.align(dl)) } - CEnum { discr, .. } => discr.size(dl), General { size, .. } => size, UntaggedUnion(ref un) => un.stride(), @@ -1754,7 +1726,6 @@ impl<'a, 'tcx> Layout { Pointer.align(dl).max(metadata.align(dl)) } - CEnum { discr, .. } => discr.align(dl), Array { align, .. } | General { align, .. } => align, UntaggedUnion(ref un) => un.align, @@ -1856,16 +1827,6 @@ impl<'a, 'tcx> Layout { } }; - let build_primitive_info = |name: ast::Name, value: Primitive| { - session::VariantInfo { - name: Some(name.to_string()), - kind: session::SizeKind::Exact, - align: value.align(tcx).abi(), - size: value.size(tcx).bytes(), - fields: vec![], - } - }; - let build_variant_info = |n: Option, flds: &[(ast::Name, Ty<'tcx>)], s: &Struct| { @@ -1959,17 +1920,6 @@ impl<'a, 'tcx> Layout { record(adt_kind.into(), None, Vec::new()); } - Layout::CEnum { discr, .. } => { - debug!("print-type-size t: `{:?}` adt c-like enum", ty); - let variant_infos: Vec<_> = - adt_def.variants.iter() - .map(|variant_def| { - build_primitive_info(variant_def.name, discr) - }) - .collect(); - record(adt_kind.into(), Some(discr.size(tcx)), variant_infos); - } - // other cases provide little interesting (i.e. adjustable // via representation tweaks) size info beyond total size. Layout::Scalar(_) | @@ -2284,6 +2234,7 @@ impl<'a, 'tcx> FullLayout<'tcx> { FullLayout { variant_index: Some(variant_index), fields, + abi: Abi::Aggregate, ..*self } } @@ -2356,7 +2307,6 @@ impl<'a, 'tcx> FullLayout<'tcx> { match self.variant_index { None => match *self.layout { // Discriminant field for enums (where applicable). - CEnum { discr, .. } | General { discr, .. } | NullablePointer { discr, .. } => { return [discr.to_ty(tcx)][i]; @@ -2416,19 +2366,23 @@ impl<'gcx> HashStable> for Layout FatPointer(ref metadata) => { metadata.hash_stable(hcx, hasher); } - CEnum { discr, min, max } => { - discr.hash_stable(hcx, hasher); - min.hash_stable(hcx, hasher); - max.hash_stable(hcx, hasher); - } Univariant(ref variant) => { variant.hash_stable(hcx, hasher); } UntaggedUnion(ref un) => { un.hash_stable(hcx, hasher); } - General { discr, ref variants, size, align, primitive_align } => { + General { + discr, + discr_range: RangeInclusive { start, end }, + ref variants, + size, + align, + primitive_align + } => { discr.hash_stable(hcx, hasher); + start.hash_stable(hcx, hasher); + end.hash_stable(hcx, hasher); variants.hash_stable(hcx, hasher); size.hash_stable(hcx, hasher); align.hash_stable(hcx, hasher); diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs index de42676a90d..e1a65f37eff 100644 --- a/src/librustc_trans/adt.rs +++ b/src/librustc_trans/adt.rs @@ -69,7 +69,7 @@ pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let l = cx.layout_of(t); debug!("finish_type_of: {} with layout {:#?}", t, l); match *l { - layout::CEnum { .. } | layout::General { .. } | layout::UntaggedUnion { .. } => { } + layout::General { .. } | layout::UntaggedUnion { .. } => { } layout::Univariant { ..} | layout::NullablePointer { .. } => { if let layout::Abi::Scalar(_) = l.abi { return; @@ -101,13 +101,12 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, name: Option<&str>) -> Type { let l = cx.layout_of(t); - debug!("adt::generic_type_of t: {:?} name: {:?}", t, name); + debug!("adt::generic_type_of {:#?} name: {:?}", l, name); + if let layout::Abi::Scalar(value) = l.abi { + return cx.llvm_type_of(value.to_ty(cx.tcx())); + } match *l { - layout::CEnum { discr, .. } => cx.llvm_type_of(discr.to_ty(cx.tcx())), layout::NullablePointer { nndiscr, ref nonnull, .. } => { - if let layout::Abi::Scalar(_) = l.abi { - return cx.llvm_type_of(l.field(cx, 0).ty); - } match name { None => { Type::struct_(cx, &struct_llfields(cx, l.for_variant(nndiscr as usize), diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index aad6f3446ee..16bca343dd0 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -1282,7 +1282,6 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> { } ] }, - layout::CEnum { .. } => span_bug!(self.span, "This should be unreachable."), ref l @ _ => bug!("Not an enum layout: {:#?}", l) } } @@ -1491,14 +1490,16 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let type_rep = cx.layout_of(enum_type); let discriminant_type_metadata = match *type_rep { - layout::CEnum { discr, .. } => { - return FinalMetadata(discriminant_type_metadata(discr)) - }, layout::NullablePointer { .. } | layout::Univariant { .. } => None, layout::General { discr, .. } => Some(discriminant_type_metadata(discr)), ref l @ _ => bug!("Not an enum layout: {:#?}", l) }; + match (type_rep.abi, discriminant_type_metadata) { + (layout::Abi::Scalar(_), Some(discr)) => return FinalMetadata(discr), + _ => {} + } + let (enum_type_size, enum_type_align) = type_rep.size_and_align(cx); let enum_name = CString::new(enum_name).unwrap(); diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 2776125bd82..dd33012e900 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -25,6 +25,7 @@ #![allow(unused_attributes)] #![feature(i128_type)] #![feature(i128)] +#![feature(inclusive_range)] #![feature(libc)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index c677352c278..c8d8199a05e 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -1091,7 +1091,7 @@ fn trans_const_adt<'a, 'tcx>( _ => 0, }; match *l { - layout::CEnum { .. } => { + layout::General { ref variants, .. } => { let discr = match *kind { mir::AggregateKind::Adt(adt_def, _, _, _) => { adt_def.discriminant_for_variant(ccx.tcx(), variant_index) @@ -1099,15 +1099,14 @@ fn trans_const_adt<'a, 'tcx>( }, _ => 0, }; - assert_eq!(vals.len(), 0); - Const::new(C_int(ccx.llvm_type_of(t), discr as i64), t) - } - layout::General { ref variants, .. } => { let discr_ty = l.field(ccx, 0).ty; - let variant = &variants[variant_index]; - let lldiscr = C_int(ccx.llvm_type_of(discr_ty), variant_index as i64); - build_const_struct(ccx, l, &variant, vals, - Some(Const::new(lldiscr, discr_ty))) + let discr = Const::new(C_int(ccx.llvm_type_of(discr_ty), discr as i64), + discr_ty); + if let layout::Abi::Scalar(_) = l.abi { + discr + } else { + build_const_struct(ccx, l, &variants[variant_index], vals, Some(discr)) + } } layout::UntaggedUnion(ref un) => { assert_eq!(variant_index, 0); diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs index 883751d25a0..f39e3cb7812 100644 --- a/src/librustc_trans/mir/lvalue.rs +++ b/src/librustc_trans/mir/lvalue.rs @@ -367,8 +367,7 @@ impl<'a, 'tcx> LvalueRef<'tcx> { _ => bug!("discriminant not scalar: {:#?}", discr_layout) }; let (min, max) = match *l { - layout::CEnum { min, max, .. } => (min, max), - layout::General { ref variants, .. } => (0, variants.len() as u64 - 1), + layout::General { ref discr_range, .. } => (discr_range.start, discr_range.end), _ => (0, u64::max_value()), }; let max_next = max.wrapping_add(1); @@ -394,7 +393,6 @@ impl<'a, 'tcx> LvalueRef<'tcx> { } }; match *l { - layout::CEnum { .. } | layout::General { .. } => { let signed = match discr_scalar { layout::Int(_, signed) => signed, @@ -419,7 +417,6 @@ impl<'a, 'tcx> LvalueRef<'tcx> { .discriminant_for_variant(bcx.tcx(), variant_index) .to_u128_unchecked() as u64; match *l { - layout::CEnum { .. } | layout::General { .. } => { let ptr = self.project_field(bcx, 0); bcx.store(C_int(bcx.ccx.llvm_type_of(ptr.ty.to_ty(bcx.tcx())), to as i64), diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index b931d9a2549..b7143f23691 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -277,8 +277,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let llval = operand.immediate(); let l = bcx.ccx.layout_of(operand.ty); - if let Layout::CEnum { min, max, .. } = *l { - if max > min { + if let Layout::General { ref discr_range, .. } = *l { + if discr_range.end > discr_range.start { // We want `table[e as usize]` to not // have bound checks, and this is the most // convenient place to put the `assume`. @@ -286,7 +286,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { base::call_assume(&bcx, bcx.icmp( llvm::IntULE, llval, - C_uint(ll_t_in, max) + C_uint(ll_t_in, discr_range.end) )); } } diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs index 264f711de8f..eca6057db36 100644 --- a/src/librustc_trans/type_of.rs +++ b/src/librustc_trans/type_of.rs @@ -242,7 +242,6 @@ impl<'tcx> LayoutLlvmExt for FullLayout<'tcx> { } match **self { Layout::Scalar { .. } | - Layout::CEnum { .. } | Layout::UntaggedUnion { .. } => { bug!("FullLayout::llvm_field_index({:?}): not applicable", self) }