mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Implement layout calculation and add more trans stubs
This commit is contained in:
parent
c2ca1530db
commit
957971b63a
@ -488,7 +488,7 @@ impl<'a, 'gcx, 'tcx> Struct {
|
||||
|
||||
for field in fields {
|
||||
if !self.sized {
|
||||
bug!("Struct::compute: field #{} of `{}` comes after unsized field",
|
||||
bug!("Struct::extend: field #{} of `{}` comes after unsized field",
|
||||
self.offset_after_field.len(), scapegoat);
|
||||
}
|
||||
|
||||
@ -623,6 +623,54 @@ impl<'a, 'gcx, 'tcx> Struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// An untagged union.
|
||||
#[derive(PartialEq, Eq, Hash, Debug)]
|
||||
pub struct Union {
|
||||
pub align: Align,
|
||||
|
||||
pub min_size: Size,
|
||||
|
||||
/// If true, no alignment padding is used.
|
||||
pub packed: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> Union {
|
||||
pub fn new(dl: &TargetDataLayout, packed: bool) -> Union {
|
||||
Union {
|
||||
align: if packed { dl.i8_align } else { dl.aggregate_align },
|
||||
min_size: Size::from_bytes(0),
|
||||
packed: packed,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extend the Struct with more fields.
|
||||
pub fn extend<I>(&mut self, dl: &TargetDataLayout,
|
||||
fields: I,
|
||||
scapegoat: Ty<'gcx>)
|
||||
-> Result<(), LayoutError<'gcx>>
|
||||
where I: Iterator<Item=Result<&'a Layout, LayoutError<'gcx>>> {
|
||||
for (index, field) in fields.enumerate() {
|
||||
let field = field?;
|
||||
if field.is_unsized() {
|
||||
bug!("Union::extend: field #{} of `{}` is unsized",
|
||||
index, scapegoat);
|
||||
}
|
||||
|
||||
if !self.packed {
|
||||
self.align = self.align.max(field.align(dl));
|
||||
}
|
||||
self.min_size = cmp::max(self.min_size, field.size(dl));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the size with trailing aligment padding.
|
||||
pub fn stride(&self) -> Size {
|
||||
self.min_size.abi_align(self.align)
|
||||
}
|
||||
}
|
||||
|
||||
/// The first half of a fat pointer.
|
||||
/// - For a trait object, this is the address of the box.
|
||||
/// - For a slice, this is the base address.
|
||||
@ -690,6 +738,11 @@ pub enum Layout {
|
||||
non_zero: bool
|
||||
},
|
||||
|
||||
/// Untagged unions.
|
||||
UntaggedUnion {
|
||||
variants: Union,
|
||||
},
|
||||
|
||||
/// General-case enums: for each case there is a struct, and they
|
||||
/// all start with a field for the discriminant.
|
||||
General {
|
||||
@ -896,8 +949,14 @@ impl<'a, 'gcx, 'tcx> Layout {
|
||||
non_zero: Some(def.did) == tcx.lang_items.non_zero()
|
||||
}
|
||||
}
|
||||
ty::TyUnion(..) => {
|
||||
unimplemented_unions!();
|
||||
ty::TyUnion(def, substs) => {
|
||||
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 }
|
||||
}
|
||||
ty::TyEnum(def, substs) => {
|
||||
let hint = *tcx.lookup_repr_hints(def.did).get(0)
|
||||
@ -1118,7 +1177,7 @@ impl<'a, 'gcx, 'tcx> Layout {
|
||||
pub fn is_unsized(&self) -> bool {
|
||||
match *self {
|
||||
Scalar {..} | Vector {..} | FatPointer {..} |
|
||||
CEnum {..} | General {..} |
|
||||
CEnum {..} | UntaggedUnion {..} | General {..} |
|
||||
RawNullablePointer {..} |
|
||||
StructWrappedNullablePointer {..} => false,
|
||||
|
||||
@ -1152,6 +1211,7 @@ impl<'a, 'gcx, 'tcx> Layout {
|
||||
|
||||
CEnum { discr, .. } => Int(discr).size(dl),
|
||||
Array { size, .. } | General { size, .. } => size,
|
||||
UntaggedUnion { ref variants } => variants.stride(),
|
||||
|
||||
Univariant { ref variant, .. } |
|
||||
StructWrappedNullablePointer { nonnull: ref variant, .. } => {
|
||||
@ -1191,6 +1251,7 @@ impl<'a, 'gcx, 'tcx> Layout {
|
||||
|
||||
CEnum { discr, .. } => Int(discr).align(dl),
|
||||
Array { align, .. } | General { align, .. } => align,
|
||||
UntaggedUnion { ref variants } => variants.align,
|
||||
|
||||
Univariant { ref variant, .. } |
|
||||
StructWrappedNullablePointer { nonnull: ref variant, .. } => {
|
||||
@ -1256,9 +1317,6 @@ impl<'a, 'gcx, 'tcx> SizeSkeleton<'gcx> {
|
||||
}
|
||||
}
|
||||
|
||||
ty::TyUnion(..) => {
|
||||
unimplemented_unions!();
|
||||
}
|
||||
ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
|
||||
// Only newtypes and enums w/ nullable pointer optimization.
|
||||
if def.variants.is_empty() || def.variants.len() > 2 {
|
||||
|
@ -459,7 +459,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
|
||||
|
||||
hir::ExprStruct(_, ref fields, ref base) => {
|
||||
match expr_ty.sty {
|
||||
ty::TyStruct(adt, substs) => {
|
||||
ty::TyStruct(adt, substs) | ty::TyUnion(adt, substs) => {
|
||||
let field_refs = field_refs(&adt.variants[0], fields);
|
||||
ExprKind::Adt {
|
||||
adt_def: adt,
|
||||
@ -477,9 +477,6 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
|
||||
})
|
||||
}
|
||||
}
|
||||
ty::TyUnion(..) => {
|
||||
unimplemented_unions!();
|
||||
}
|
||||
ty::TyEnum(adt, substs) => {
|
||||
match cx.tcx.expect_def(expr.id) {
|
||||
Def::Variant(enum_id, variant_id) => {
|
||||
|
@ -79,6 +79,8 @@ pub enum Repr<'tcx> {
|
||||
CEnum(IntType, Disr, Disr), // discriminant range (signedness based on the IntType)
|
||||
/// Single-case variants, and structs/tuples/records.
|
||||
Univariant(Struct<'tcx>),
|
||||
/// Untagged unions.
|
||||
UntaggedUnion(Union<'tcx>),
|
||||
/// General-case enums: for each case there is a struct, and they
|
||||
/// all start with a field for the discriminant.
|
||||
General(IntType, Vec<Struct<'tcx>>),
|
||||
@ -121,6 +123,15 @@ pub struct Struct<'tcx> {
|
||||
pub fields: Vec<Ty<'tcx>>,
|
||||
}
|
||||
|
||||
/// For untagged unions.
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub struct Union<'tcx> {
|
||||
pub min_size: u64,
|
||||
pub align: u32,
|
||||
pub packed: bool,
|
||||
pub fields: Vec<Ty<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct MaybeSizedValue {
|
||||
pub value: ValueRef,
|
||||
@ -176,8 +187,12 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||
|
||||
Univariant(mk_struct(cx, &ftys[..], packed, t))
|
||||
}
|
||||
ty::TyUnion(..) => {
|
||||
unimplemented_unions!();
|
||||
ty::TyUnion(def, substs) => {
|
||||
let ftys = def.struct_variant().fields.iter().map(|field| {
|
||||
monomorphize::field_ty(cx.tcx(), substs, field)
|
||||
}).collect::<Vec<_>>();
|
||||
let packed = cx.tcx().lookup_packed(def.did);
|
||||
UntaggedUnion(mk_union(cx, &ftys[..], packed, t))
|
||||
}
|
||||
ty::TyClosure(_, ref substs) => {
|
||||
Univariant(mk_struct(cx, &substs.upvar_tys, false, t))
|
||||
@ -482,6 +497,31 @@ fn mk_struct<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_union<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||
tys: &[Ty<'tcx>], packed: bool,
|
||||
_scapegoat: Ty<'tcx>)
|
||||
-> Union<'tcx> {
|
||||
let mut min_size = 0;
|
||||
let mut align = 0;
|
||||
for llty in tys.iter().map(|&ty| type_of::sizing_type_of(cx, ty)) {
|
||||
let field_size = machine::llsize_of_alloc(cx, llty);
|
||||
if min_size < field_size {
|
||||
min_size = field_size;
|
||||
}
|
||||
let field_align = machine::llalign_of_min(cx, llty);
|
||||
if align < field_align {
|
||||
align = field_align;
|
||||
}
|
||||
}
|
||||
|
||||
Union {
|
||||
min_size: min_size,
|
||||
align: align,
|
||||
packed: packed,
|
||||
fields: tys.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct IntBounds {
|
||||
slo: i64,
|
||||
@ -646,7 +686,7 @@ pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||
pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||
r: &Repr<'tcx>, llty: &mut Type) {
|
||||
match *r {
|
||||
CEnum(..) | General(..) | RawNullablePointer { .. } => { }
|
||||
CEnum(..) | General(..) | UntaggedUnion(..) | RawNullablePointer { .. } => { }
|
||||
Univariant(ref st) | StructWrappedNullablePointer { nonnull: ref st, .. } =>
|
||||
llty.set_struct_body(&struct_llfields(cx, st, false, false),
|
||||
st.packed)
|
||||
@ -690,6 +730,34 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||
}
|
||||
}
|
||||
}
|
||||
UntaggedUnion(ref un) => {
|
||||
// Use alignment-sized ints to fill all the union storage.
|
||||
let (size, align) = (roundup(un.min_size, un.align), un.align);
|
||||
|
||||
let align_s = align as u64;
|
||||
assert_eq!(size % align_s, 0); // Ensure division in align_units comes out evenly
|
||||
let align_units = size / align_s;
|
||||
let fill_ty = match align_s {
|
||||
1 => Type::array(&Type::i8(cx), align_units),
|
||||
2 => Type::array(&Type::i16(cx), align_units),
|
||||
4 => Type::array(&Type::i32(cx), align_units),
|
||||
8 if machine::llalign_of_min(cx, Type::i64(cx)) == 8 =>
|
||||
Type::array(&Type::i64(cx), align_units),
|
||||
a if a.count_ones() == 1 => Type::array(&Type::vector(&Type::i32(cx), a / 4),
|
||||
align_units),
|
||||
_ => bug!("unsupported union alignment: {}", align)
|
||||
};
|
||||
match name {
|
||||
None => {
|
||||
TypeContext::direct(Type::struct_(cx, &[fill_ty], un.packed))
|
||||
}
|
||||
Some(name) => {
|
||||
let mut llty = Type::named_struct(cx, name);
|
||||
llty.set_struct_body(&[fill_ty], un.packed);
|
||||
TypeContext::direct(llty)
|
||||
}
|
||||
}
|
||||
}
|
||||
General(ity, ref sts) => {
|
||||
// We need a representation that has:
|
||||
// * The alignment of the most-aligned field
|
||||
@ -762,7 +830,7 @@ pub fn trans_switch<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||
RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => {
|
||||
(BranchKind::Switch, Some(trans_get_discr(bcx, r, scrutinee, None, range_assert)))
|
||||
}
|
||||
Univariant(..) => {
|
||||
Univariant(..) | UntaggedUnion(..) => {
|
||||
// N.B.: Univariant means <= 1 enum variants (*not* == 1 variants).
|
||||
(BranchKind::Single, None)
|
||||
}
|
||||
@ -773,7 +841,7 @@ pub fn is_discr_signed<'tcx>(r: &Repr<'tcx>) -> bool {
|
||||
match *r {
|
||||
CEnum(ity, _, _) => ity.is_signed(),
|
||||
General(ity, _) => ity.is_signed(),
|
||||
Univariant(..) => false,
|
||||
Univariant(..) | UntaggedUnion(..) => false,
|
||||
RawNullablePointer { .. } => false,
|
||||
StructWrappedNullablePointer { .. } => false,
|
||||
}
|
||||
@ -794,7 +862,7 @@ pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
|
||||
load_discr(bcx, ity, ptr, Disr(0), Disr(cases.len() as u64 - 1),
|
||||
range_assert)
|
||||
}
|
||||
Univariant(..) => C_u8(bcx.ccx(), 0),
|
||||
Univariant(..) | UntaggedUnion(..) => C_u8(bcx.ccx(), 0),
|
||||
RawNullablePointer { nndiscr, nnty, .. } => {
|
||||
let cmp = if nndiscr == Disr(0) { IntEQ } else { IntNE };
|
||||
let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty);
|
||||
@ -856,8 +924,8 @@ pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr, discr: Disr)
|
||||
General(ity, _) => {
|
||||
C_integral(ll_inttype(bcx.ccx(), ity), discr.0, true)
|
||||
}
|
||||
Univariant(..) => {
|
||||
bug!("no cases for univariants or structs")
|
||||
Univariant(..) | UntaggedUnion(..) => {
|
||||
bug!("no cases for univariants, structs or unions")
|
||||
}
|
||||
RawNullablePointer { .. } |
|
||||
StructWrappedNullablePointer { .. } => {
|
||||
@ -884,6 +952,9 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
|
||||
Univariant(_) => {
|
||||
assert_eq!(discr, Disr(0));
|
||||
}
|
||||
UntaggedUnion(..) => {
|
||||
assert_eq!(discr, Disr(0));
|
||||
}
|
||||
RawNullablePointer { nndiscr, nnty, ..} => {
|
||||
if discr != nndiscr {
|
||||
let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty);
|
||||
@ -939,6 +1010,11 @@ pub fn trans_field_ptr_builder<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
|
||||
General(_, ref cases) => {
|
||||
struct_field_ptr(bcx, &cases[discr.0 as usize], val, ix + 1, true)
|
||||
}
|
||||
UntaggedUnion(ref un) => {
|
||||
let ty = type_of::in_memory_type_of(bcx.ccx(), un.fields[ix]);
|
||||
if bcx.is_unreachable() { return C_undef(ty.ptr_to()); }
|
||||
bcx.pointercast(val.value, ty.ptr_to())
|
||||
}
|
||||
RawNullablePointer { nndiscr, ref nullfields, .. } |
|
||||
StructWrappedNullablePointer { nndiscr, ref nullfields, .. } if discr != nndiscr => {
|
||||
// The unit-like case might have a nonzero number of unit-like fields.
|
||||
@ -1100,6 +1176,9 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>, discr
|
||||
contents.extend_from_slice(&[padding(ccx, max_sz - case.size)]);
|
||||
C_struct(ccx, &contents[..], false)
|
||||
}
|
||||
UntaggedUnion(..) => {
|
||||
unimplemented_unions!();
|
||||
}
|
||||
Univariant(ref st) => {
|
||||
assert_eq!(discr, Disr(0));
|
||||
let contents = build_const_struct(ccx, st, vals);
|
||||
@ -1211,6 +1290,7 @@ pub fn const_get_field(r: &Repr, val: ValueRef, _discr: Disr,
|
||||
match *r {
|
||||
CEnum(..) => bug!("element access in C-like enum const"),
|
||||
Univariant(..) => const_struct_field(val, ix),
|
||||
UntaggedUnion(..) => const_struct_field(val, 0),
|
||||
General(..) => const_struct_field(val, ix + 1),
|
||||
RawNullablePointer { .. } => {
|
||||
assert_eq!(ix, 0);
|
||||
|
@ -1302,6 +1302,9 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
|
||||
]
|
||||
}
|
||||
}
|
||||
adt::UntaggedUnion(..) => {
|
||||
unimplemented_unions!();
|
||||
}
|
||||
adt::RawNullablePointer { nndiscr: non_null_variant_index, nnty, .. } => {
|
||||
// As far as debuginfo is concerned, the pointer this enum
|
||||
// represents is still wrapped in a struct. This is to make the
|
||||
@ -1616,7 +1619,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||
},
|
||||
adt::RawNullablePointer { .. } |
|
||||
adt::StructWrappedNullablePointer { .. } |
|
||||
adt::Univariant(..) => None,
|
||||
adt::Univariant(..) | adt::UntaggedUnion(..) => None,
|
||||
adt::General(inttype, _) => Some(discriminant_type_metadata(inttype)),
|
||||
};
|
||||
|
||||
|
47
src/test/run-pass/union-basic.rs
Normal file
47
src/test/run-pass/union-basic.rs
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(untagged_unions)]
|
||||
|
||||
use std::mem::{size_of, align_of, zeroed};
|
||||
|
||||
union U {
|
||||
a: u8,
|
||||
}
|
||||
|
||||
union U64 {
|
||||
a: u64,
|
||||
}
|
||||
|
||||
union W {
|
||||
a: u8,
|
||||
b: u64,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(size_of::<U>(), 1);
|
||||
assert_eq!(size_of::<U64>(), 8);
|
||||
assert_eq!(size_of::<W>(), 8);
|
||||
assert_eq!(align_of::<U>(), 1);
|
||||
assert_eq!(align_of::<U64>(), align_of::<u64>());
|
||||
assert_eq!(align_of::<W>(), align_of::<u64>());
|
||||
|
||||
let u = U { a: 10 };
|
||||
assert_eq!(u.a, 10);
|
||||
let U { a } = u;
|
||||
assert_eq!(a, 10);
|
||||
|
||||
let mut w: W = unsafe { zeroed() };
|
||||
assert_eq!(w.a, 0);
|
||||
assert_eq!(w.b, 0);
|
||||
// w.a = 1;
|
||||
// assert_eq!(w.a, 0);
|
||||
// assert_eq!(w.b, 0);
|
||||
}
|
Loading…
Reference in New Issue
Block a user