Address comments

This commit is contained in:
Vadim Petrochenkov 2016-09-06 01:26:02 +03:00
parent f2b672d556
commit 553d5f0a38
2 changed files with 231 additions and 250 deletions

View File

@ -15,7 +15,7 @@ pub use self::Primitive::*;
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;
@ -555,7 +555,7 @@ impl<'a, 'gcx, 'tcx> Struct {
}
// 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)? {
@ -918,243 +918,229 @@ impl<'a, 'gcx, 'tcx> Layout {
Univariant { variant: st, non_zero: false }
}
// 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
});
}
_ => {
tcx.sess.fatal(&format!("monomorphising SIMD type `{}` with \
a non-machine element type `{}`",
ty, element));
}
}
}
// 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));
}
}
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
});
}
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 fields = def.struct_variant().fields.iter().map(|field| {
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
});
}
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 st = Struct::new(dl, packed);
st.extend(dl, fields, ty)?;
Univariant {
variant: st,
non_zero: Some(def.did) == tcx.lang_items.non_zero()
}
}
AdtKind::Union => {
let fields = def.struct_variant().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 }
}
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));
}
}
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)
});
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])
}
}
}
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)
});
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));
}
// 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;
}
}
// 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;
let layout = if def.is_union() {
let mut un = Union::new(dl, packed);
un.extend(dl, fields, ty)?;
UntaggedUnion { variants: un }
} 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;
}
}
}
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);
}
General {
discr: ity,
variants: variants,
size: size,
align: align
// 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 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])
}
}
}
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)
});
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));
}
// 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;
}
}
// 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
}
}
// Types with no meaningful known layout.
ty::TyProjection(_) | ty::TyAnon(..) => {

View File

@ -253,15 +253,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
/// if not a structure at all. Corresponds to the only possible unsized
/// field, and its type can be used to determine unsizing strategy.
pub fn struct_tail(self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
loop {
match ty.sty {
TyAdt(def, substs) if def.is_struct() => {
match def.struct_variant().fields.last() {
Some(f) => ty = f.ty(self, substs),
None => break
}
}
_ => break
while let TyAdt(def, substs) = ty.sty {
if !def.is_struct() {
break
}
match def.struct_variant().fields.last() {
Some(f) => ty = f.ty(self, substs),
None => break
}
}
ty
@ -277,17 +275,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
target: Ty<'tcx>)
-> (Ty<'tcx>, Ty<'tcx>) {
let (mut a, mut b) = (source, target);
loop {
match (&a.sty, &b.sty) {
(&TyAdt(a_def, a_substs), &TyAdt(b_def, b_substs))
if a_def == b_def && a_def.is_struct() => {
match a_def.struct_variant().fields.last() {
Some(f) => {
a = f.ty(self, a_substs);
b = f.ty(self, b_substs);
}
_ => break
}
while let (&TyAdt(a_def, a_substs), &TyAdt(b_def, b_substs)) = (&a.sty, &b.sty) {
if a_def != b_def || !a_def.is_struct() {
break
}
match a_def.struct_variant().fields.last() {
Some(f) => {
a = f.ty(self, a_substs);
b = f.ty(self, b_substs);
}
_ => break
}