use infer::InferCtxt;
use session::Session;
use traits;
-use ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable};
+use ty::{self, Ty, TyCtxt, TypeFoldable};
use syntax::ast::{FloatTy, IntTy, UintTy};
use syntax::attr;
}
// Is this the NonZero lang item wrapping a pointer or integer type?
- (&Univariant { non_zero: true, .. }, &ty::TyAdt(def, substs)) if def.is_struct() => {
+ (&Univariant { non_zero: true, .. }, &ty::TyAdt(def, substs)) => {
let fields = &def.struct_variant().fields;
assert_eq!(fields.len(), 1);
match *fields[0].ty(tcx, substs).layout(infcx)? {
Univariant { variant: st, non_zero: false }
}
- // ADTs.
- ty::TyAdt(def, substs) => match def.adt_kind() {
- AdtKind::Struct => {
- if ty.is_simd() {
- // SIMD vector types.
- let element = ty.simd_type(tcx);
- match *element.layout(infcx)? {
- Scalar { value, .. } => {
- return success(Vector {
- element: value,
- count: ty.simd_size(tcx) as u64
- });
- }
- _ => {
- tcx.sess.fatal(&format!("monomorphising SIMD type `{}` with \
- a non-machine element type `{}`",
- ty, element));
- }
- }
+ // SIMD vector types.
+ ty::TyAdt(def, ..) if def.is_simd() => {
+ let element = ty.simd_type(tcx);
+ match *element.layout(infcx)? {
+ Scalar { value, .. } => {
+ return success(Vector {
+ element: value,
+ count: ty.simd_size(tcx) as u64
+ });
}
- let fields = def.struct_variant().fields.iter().map(|field| {
- field.ty(tcx, substs).layout(infcx)
+ _ => {
+ tcx.sess.fatal(&format!("monomorphising SIMD type `{}` with \
+ a non-machine element type `{}`",
+ ty, element));
+ }
+ }
+ }
+
+ // ADTs.
+ ty::TyAdt(def, substs) => {
+ let hint = *tcx.lookup_repr_hints(def.did).get(0)
+ .unwrap_or(&attr::ReprAny);
+
+ if def.variants.is_empty() {
+ // Uninhabitable; represent as unit
+ // (Typechecking will reject discriminant-sizing attrs.)
+ assert_eq!(hint, attr::ReprAny);
+
+ return success(Univariant {
+ variant: Struct::new(dl, false),
+ non_zero: false
});
- let packed = tcx.lookup_packed(def.did);
- let mut st = Struct::new(dl, packed);
- st.extend(dl, fields, ty)?;
+ }
- Univariant {
- variant: st,
- non_zero: Some(def.did) == tcx.lang_items.non_zero()
+ if def.is_enum() && def.variants.iter().all(|v| v.fields.is_empty()) {
+ // All bodies empty -> intlike
+ let (mut min, mut max) = (i64::MAX, i64::MIN);
+ for v in &def.variants {
+ let x = v.disr_val.to_u64_unchecked() as i64;
+ if x < min { min = x; }
+ if x > max { max = x; }
}
+
+ let (discr, signed) = Integer::repr_discr(tcx, hint, min, max);
+ return success(CEnum {
+ discr: discr,
+ signed: signed,
+ min: min as u64,
+ max: max as u64
+ });
}
- AdtKind::Union => {
- let fields = def.struct_variant().fields.iter().map(|field| {
+
+ if def.variants.len() == 1 {
+ // Struct, or union, or univariant enum equivalent to a struct.
+ // (Typechecking will reject discriminant-sizing attrs.)
+ assert!(!def.is_enum() || hint == attr::ReprAny);
+ let fields = def.variants[0].fields.iter().map(|field| {
field.ty(tcx, substs).layout(infcx)
});
let packed = tcx.lookup_packed(def.did);
- let mut un = Union::new(dl, packed);
- un.extend(dl, fields, ty)?;
- UntaggedUnion { variants: un }
+ let layout = if def.is_union() {
+ let mut un = Union::new(dl, packed);
+ un.extend(dl, fields, ty)?;
+ UntaggedUnion { variants: un }
+ } else {
+ let mut st = Struct::new(dl, packed);
+ st.extend(dl, fields, ty)?;
+ let non_zero = Some(def.did) == tcx.lang_items.non_zero();
+ Univariant { variant: st, non_zero: non_zero }
+ };
+ return success(layout);
}
- AdtKind::Enum => {
- let hint = *tcx.lookup_repr_hints(def.did).get(0)
- .unwrap_or(&attr::ReprAny);
-
- if def.variants.is_empty() {
- // Uninhabitable; represent as unit
- // (Typechecking will reject discriminant-sizing attrs.)
- assert_eq!(hint, attr::ReprAny);
-
- return success(Univariant {
- variant: Struct::new(dl, false),
- non_zero: false
- });
- }
-
- if def.variants.iter().all(|v| v.fields.is_empty()) {
- // All bodies empty -> intlike
- let (mut min, mut max) = (i64::MAX, i64::MIN);
- for v in &def.variants {
- let x = v.disr_val.to_u64_unchecked() as i64;
- if x < min { min = x; }
- if x > max { max = x; }
- }
- let (discr, signed) = Integer::repr_discr(tcx, hint, min, max);
- return success(CEnum {
- discr: discr,
- signed: signed,
- min: min as u64,
- max: max as u64
- });
+ // 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 i as u64 != v.disr_val.to_u64_unchecked() {
+ bug!("non-C-like enum {} with specified discriminants",
+ tcx.item_path_str(def.did));
}
+ }
- // 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 i as u64 != v.disr_val.to_u64_unchecked() {
- bug!("non-C-like enum {} with specified discriminants",
- tcx.item_path_str(def.did));
- }
- }
+ // Cache the substituted and normalized variant field types.
+ let variants = def.variants.iter().map(|v| {
+ v.fields.iter().map(|field| field.ty(tcx, substs)).collect::<Vec<_>>()
+ }).collect::<Vec<_>>();
- if def.variants.len() == 1 {
- // Equivalent to a struct/tuple/newtype.
- // (Typechecking will reject discriminant-sizing attrs.)
- assert_eq!(hint, attr::ReprAny);
- let fields = def.variants[0].fields.iter().map(|field| {
- field.ty(tcx, substs).layout(infcx)
+ if variants.len() == 2 && hint == attr::ReprAny {
+ // Nullable pointer optimization
+ for discr in 0..2 {
+ let other_fields = variants[1 - discr].iter().map(|ty| {
+ ty.layout(infcx)
});
- let mut st = Struct::new(dl, false);
- st.extend(dl, fields, ty)?;
- return success(Univariant { variant: st, non_zero: false });
- }
-
- // Cache the substituted and normalized variant field types.
- let variants = def.variants.iter().map(|v| {
- v.fields.iter().map(|field| field.ty(tcx, substs)).collect::<Vec<_>>()
- }).collect::<Vec<_>>();
-
- if variants.len() == 2 && hint == attr::ReprAny {
- // Nullable pointer optimization
- for discr in 0..2 {
- let other_fields = variants[1 - discr].iter().map(|ty| {
- ty.layout(infcx)
- });
- if !Struct::would_be_zero_sized(dl, other_fields)? {
- continue;
- }
- let path = Struct::non_zero_field_path(infcx,
- variants[discr].iter().cloned())?;
- let mut path = if let Some(p) = path { p } else { continue };
-
- // FIXME(eddyb) should take advantage of a newtype.
- if path == &[0] && variants[discr].len() == 1 {
- match *variants[discr][0].layout(infcx)? {
- Scalar { value, .. } => {
- return success(RawNullablePointer {
- nndiscr: discr as u64,
- value: value
- });
- }
- _ => {
- bug!("Layout::compute: `{}`'s non-zero \
- `{}` field not scalar?!",
- ty, variants[discr][0])
- }
+ if !Struct::would_be_zero_sized(dl, other_fields)? {
+ continue;
+ }
+ let path = Struct::non_zero_field_path(infcx,
+ variants[discr].iter().cloned())?;
+ let mut path = if let Some(p) = path { p } else { continue };
+
+ // FIXME(eddyb) should take advantage of a newtype.
+ if path == &[0] && variants[discr].len() == 1 {
+ match *variants[discr][0].layout(infcx)? {
+ Scalar { value, .. } => {
+ return success(RawNullablePointer {
+ nndiscr: discr as u64,
+ value: value
+ });
+ }
+ _ => {
+ bug!("Layout::compute: `{}`'s non-zero \
+ `{}` field not scalar?!",
+ ty, variants[discr][0])
}
}
-
- path.push(0); // For GEP through a pointer.
- path.reverse();
- let mut st = Struct::new(dl, false);
- st.extend(dl, variants[discr].iter().map(|ty| ty.layout(infcx)), ty)?;
- return success(StructWrappedNullablePointer {
- nndiscr: discr as u64,
- nonnull: st,
- discrfield: path
- });
}
- }
- // The general case.
- let discr_max = (variants.len() - 1) as i64;
- assert!(discr_max >= 0);
- let (min_ity, _) = Integer::repr_discr(tcx, hint, 0, discr_max);
-
- let mut 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();
-
- // Create the set of structs that represent each variant
- // Use the minimum integer type we figured out above
- let discr = Some(Scalar { value: Int(min_ity), non_zero: false });
- let mut variants = variants.into_iter().map(|fields| {
- let mut found_start = false;
- let fields = fields.into_iter().map(|field| {
- let field = field.layout(infcx)?;
- if !found_start {
- // Find the first field we can't move later
- // to make room for a larger discriminant.
- let field_align = field.align(dl);
- if field.size(dl).bytes() != 0 || field_align.abi() != 1 {
- start_align = start_align.min(field_align);
- found_start = true;
- }
- }
- Ok(field)
- });
+ path.push(0); // For GEP through a pointer.
+ path.reverse();
let mut st = Struct::new(dl, false);
- st.extend(dl, discr.iter().map(Ok).chain(fields), ty)?;
- size = cmp::max(size, st.min_size());
- align = align.max(st.align);
- Ok(st)
- }).collect::<Result<Vec<_>, _>>()?;
-
- // Align the maximum variant size to the largest alignment.
- size = size.abi_align(align);
-
- if size.bytes() >= dl.obj_size_bound() {
- return Err(LayoutError::SizeOverflow(ty));
+ st.extend(dl, variants[discr].iter().map(|ty| ty.layout(infcx)), ty)?;
+ return success(StructWrappedNullablePointer {
+ nndiscr: discr as u64,
+ nonnull: st,
+ discrfield: path
+ });
}
+ }
- // Check to see if we should use a different type for the
- // discriminant. We can safely use a type with the same size
- // as the alignment of the first field of each variant.
- // We increase the size of the discriminant to avoid LLVM copying
- // padding when it doesn't need to. This normally causes unaligned
- // load/stores and excessive memcpy/memset operations. By using a
- // bigger integer size, LLVM can be sure about it's contents and
- // won't be so conservative.
-
- // Use the initial field alignment
- let wanted = start_align.abi();
- let mut ity = min_ity;
- for &candidate in &[I16, I32, I64] {
- let ty = Int(candidate);
- if wanted == ty.align(dl).abi() && wanted == ty.size(dl).bytes() {
- ity = candidate;
- break;
+ // The general case.
+ let discr_max = (variants.len() - 1) as i64;
+ assert!(discr_max >= 0);
+ let (min_ity, _) = Integer::repr_discr(tcx, hint, 0, discr_max);
+
+ let mut 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();
+
+ // Create the set of structs that represent each variant
+ // Use the minimum integer type we figured out above
+ let discr = Some(Scalar { value: Int(min_ity), non_zero: false });
+ let mut variants = variants.into_iter().map(|fields| {
+ let mut found_start = false;
+ let fields = fields.into_iter().map(|field| {
+ let field = field.layout(infcx)?;
+ if !found_start {
+ // Find the first field we can't move later
+ // to make room for a larger discriminant.
+ let field_align = field.align(dl);
+ if field.size(dl).bytes() != 0 || field_align.abi() != 1 {
+ start_align = start_align.min(field_align);
+ found_start = true;
+ }
}
- }
+ Ok(field)
+ });
+ let mut st = Struct::new(dl, false);
+ st.extend(dl, discr.iter().map(Ok).chain(fields), ty)?;
+ size = cmp::max(size, st.min_size());
+ align = align.max(st.align);
+ Ok(st)
+ }).collect::<Result<Vec<_>, _>>()?;
+
+ // Align the maximum variant size to the largest alignment.
+ size = size.abi_align(align);
+
+ if size.bytes() >= dl.obj_size_bound() {
+ return Err(LayoutError::SizeOverflow(ty));
+ }
- // FIXME(eddyb) conservative only to avoid diverging from trans::adt.
- if align.abi() != start_align.abi() {
- ity = min_ity;
+ // Check to see if we should use a different type for the
+ // discriminant. We can safely use a type with the same size
+ // as the alignment of the first field of each variant.
+ // We increase the size of the discriminant to avoid LLVM copying
+ // padding when it doesn't need to. This normally causes unaligned
+ // load/stores and excessive memcpy/memset operations. By using a
+ // bigger integer size, LLVM can be sure about it's contents and
+ // won't be so conservative.
+
+ // Use the initial field alignment
+ let wanted = start_align.abi();
+ let mut ity = min_ity;
+ for &candidate in &[I16, I32, I64] {
+ let ty = Int(candidate);
+ if wanted == ty.align(dl).abi() && wanted == ty.size(dl).bytes() {
+ ity = candidate;
+ break;
}
+ }
- // If the alignment is not larger than the chosen discriminant size,
- // don't use the alignment as the final size.
- if ity <= min_ity {
- ity = min_ity;
- } else {
- // Patch up the variants' first few fields.
- let old_ity_size = Int(min_ity).size(dl);
- let new_ity_size = Int(ity).size(dl);
- for variant in &mut variants {
- for offset in &mut variant.offset_after_field {
- if *offset > old_ity_size {
- break;
- }
- *offset = new_ity_size;
+ // FIXME(eddyb) conservative only to avoid diverging from trans::adt.
+ if align.abi() != start_align.abi() {
+ ity = min_ity;
+ }
+
+ // If the alignment is not larger than the chosen discriminant size,
+ // don't use the alignment as the final size.
+ if ity <= min_ity {
+ ity = min_ity;
+ } else {
+ // Patch up the variants' first few fields.
+ let old_ity_size = Int(min_ity).size(dl);
+ let new_ity_size = Int(ity).size(dl);
+ for variant in &mut variants {
+ for offset in &mut variant.offset_after_field {
+ if *offset > old_ity_size {
+ break;
}
+ *offset = new_ity_size;
}
}
+ }
- General {
- discr: ity,
- variants: variants,
- size: size,
- align: align
- }
+ General {
+ discr: ity,
+ variants: variants,
+ size: size,
+ align: align
}
- },
+ }
// Types with no meaningful known layout.
ty::TyProjection(_) | ty::TyAnon(..) => {