]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc/ty/layout.rs
New upstream version 1.13.0+dfsg1
[rustc.git] / src / librustc / ty / layout.rs
index 7c944020e9352c0569e39d938afcb0c7205caa63..ed945534e1e5f4014187bb4831ac33a6a97ba621 100644 (file)
@@ -45,6 +45,7 @@ pub struct TargetDataLayout {
 }
 
 impl Default for TargetDataLayout {
+    /// Creates an instance of `TargetDataLayout`.
     fn default() -> TargetDataLayout {
         TargetDataLayout {
             endian: Endian::Big,
@@ -327,6 +328,42 @@ pub enum Integer {
 }
 
 impl Integer {
+    pub fn size(&self) -> Size {
+        match *self {
+            I1 => Size::from_bits(1),
+            I8 => Size::from_bytes(1),
+            I16 => Size::from_bytes(2),
+            I32 => Size::from_bytes(4),
+            I64  => Size::from_bytes(8),
+        }
+    }
+
+    pub fn align(&self, dl: &TargetDataLayout)-> Align {
+        match *self {
+            I1 => dl.i1_align,
+            I8 => dl.i8_align,
+            I16 => dl.i16_align,
+            I32 => dl.i32_align,
+            I64 => dl.i64_align,
+        }
+    }
+
+    pub fn to_ty<'a, 'tcx>(&self, tcx: &ty::TyCtxt<'a, 'tcx, 'tcx>,
+                           signed: bool) -> Ty<'tcx> {
+        match (*self, signed) {
+            (I1, false) => tcx.types.u8,
+            (I8, false) => tcx.types.u8,
+            (I16, false) => tcx.types.u16,
+            (I32, false) => tcx.types.u32,
+            (I64, false) => tcx.types.u64,
+            (I1, true) => tcx.types.i8,
+            (I8, true) => tcx.types.i8,
+            (I16, true) => tcx.types.i16,
+            (I32, true) => tcx.types.i32,
+            (I64, true) => tcx.types.i64,
+        }
+    }
+
     /// Find the smallest Integer type which can represent the signed value.
     pub fn fit_signed(x: i64) -> Integer {
         match x {
@@ -349,6 +386,18 @@ impl Integer {
         }
     }
 
+    /// Find the smallest integer with the given alignment.
+    pub fn for_abi_align(dl: &TargetDataLayout, align: Align) -> Option<Integer> {
+        let wanted = align.abi();
+        for &candidate in &[I8, I16, I32, I64] {
+            let ty = Int(candidate);
+            if wanted == ty.align(dl).abi() && wanted == ty.size(dl).bytes() {
+                return Some(candidate);
+            }
+        }
+        None
+    }
+
     /// Get the Integer type from an attr::IntType.
     pub fn from_attr(dl: &TargetDataLayout, ity: attr::IntType) -> Integer {
         match ity {
@@ -366,7 +415,7 @@ impl Integer {
     /// signed discriminant range and #[repr] attribute.
     /// N.B.: u64 values above i64::MAX will be treated as signed, but
     /// that shouldn't affect anything, other than maybe debuginfo.
-    pub fn repr_discr(tcx: TyCtxt, hint: attr::ReprAttr, min: i64, max: i64)
+    pub fn repr_discr(tcx: TyCtxt, ty: Ty, hint: attr::ReprAttr, min: i64, max: i64)
                       -> (Integer, bool) {
         // Theoretically, negative values could be larger in unsigned representation
         // than the unsigned representation of the signed minimum. However, if there
@@ -376,11 +425,12 @@ impl Integer {
         let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
 
         let at_least = match hint {
-            attr::ReprInt(span, ity) => {
+            attr::ReprInt(ity) => {
                 let discr = Integer::from_attr(&tcx.data_layout, ity);
                 let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
                 if discr < fit {
-                    span_bug!(span, "representation hint insufficient for discriminant range")
+                    bug!("Integer::repr_discr: `#[repr]` hint too small for \
+                          discriminant range of enum `{}", ty)
                 }
                 return (discr, ity.is_signed());
             }
@@ -396,10 +446,10 @@ impl Integer {
             }
             attr::ReprAny => I8,
             attr::ReprPacked => {
-                bug!("Integer::repr_discr: found #[repr(packed)] on an enum");
+                bug!("Integer::repr_discr: found #[repr(packed)] on enum `{}", ty);
             }
             attr::ReprSimd => {
-                bug!("Integer::repr_discr: found #[repr(simd)] on an enum");
+                bug!("Integer::repr_discr: found #[repr(simd)] on enum `{}", ty);
             }
         };
 
@@ -488,7 +538,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);
             }
 
@@ -555,7 +605,7 @@ impl<'a, 'gcx, 'tcx> Struct {
             }
 
             // Is this the NonZero lang item wrapping a pointer or integer type?
-            (&Univariant { non_zero: true, .. }, &ty::TyStruct(def, substs)) => {
+            (&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)? {
@@ -573,7 +623,7 @@ impl<'a, 'gcx, 'tcx> Struct {
 
             // Perhaps one of the fields of this struct is non-zero
             // let's recurse and find out
-            (_, &ty::TyStruct(def, substs)) => {
+            (_, &ty::TyAdt(def, substs)) if def.is_struct() => {
                 Struct::non_zero_field_path(infcx, def.struct_variant().fields
                                                       .iter().map(|field| {
                     field.ty(tcx, substs)
@@ -621,6 +671,63 @@ impl<'a, 'gcx, 'tcx> Struct {
         }
         Ok(None)
     }
+
+    pub fn offset_of_field(&self, index: usize) -> Size {
+        assert!(index < self.offset_after_field.len());
+        if index == 0 {
+            Size::from_bytes(0)
+        } else {
+            self.offset_after_field[index-1]
+        }
+    }
+}
+
+/// 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.
@@ -646,7 +753,7 @@ pub enum Layout {
         non_zero: bool
     },
 
-    /// SIMD vectors, from TyStruct marked with #[repr(simd)].
+    /// SIMD vectors, from structs marked with #[repr(simd)].
     Vector {
         element: Primitive,
         count: u64
@@ -667,7 +774,7 @@ pub enum Layout {
         non_zero: bool
     },
 
-    // Remaining variants are all ADTs such as TyStruct, TyEnum or TyTuple.
+    // Remaining variants are all ADTs such as structs, enums or tuples.
 
     /// C-like enums; basically an integer.
     CEnum {
@@ -690,6 +797,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 {
@@ -857,7 +969,7 @@ impl<'a, 'gcx, 'tcx> Layout {
                 Univariant { variant: unit, non_zero: false }
             }
 
-            // Tuples.
+            // Tuples and closures.
             ty::TyClosure(_, ty::ClosureSubsts { upvar_tys: tys, .. }) |
             ty::TyTuple(tys) => {
                 let mut st = Struct::new(dl, false);
@@ -865,70 +977,41 @@ impl<'a, 'gcx, 'tcx> Layout {
                 Univariant { variant: st, non_zero: false }
             }
 
-            // ADTs.
-            ty::TyStruct(def, substs) => {
-                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
+                        });
+                    }
+                    _ => {
+                        tcx.sess.fatal(&format!("monomorphising SIMD type `{}` with \
+                                                a non-machine element type `{}`",
+                                                ty, element));
                     }
-                }
-                let fields = def.struct_variant().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)?;
-
-                // FIXME(16758) don't add a drop flag to unsized structs, as it
-                // won't actually be in the location we say it is because it'll be after
-                // the unsized field. Several other pieces of code assume that the unsized
-                // field is definitely the last one.
-                if def.dtor_kind().has_drop_flag() &&
-                   ty.is_sized(tcx, &infcx.parameter_environment, DUMMY_SP) {
-                    st.extend(dl, Some(Ok(&Scalar {
-                        value: Int(I8),
-                        non_zero: false
-                    })).into_iter(), ty)?;
-                }
-                Univariant {
-                    variant: st,
-                    non_zero: Some(def.did) == tcx.lang_items.non_zero()
                 }
             }
-            ty::TyEnum(def, substs) => {
+
+            // ADTs.
+            ty::TyAdt(def, substs) => {
                 let hint = *tcx.lookup_repr_hints(def.did).get(0)
                     .unwrap_or(&attr::ReprAny);
 
-                let dtor = def.dtor_kind().has_drop_flag();
-                let drop_flag = if dtor {
-                    Some(Scalar { value: Int(I8), non_zero: false })
-                } else {
-                    None
-                };
-
                 if def.variants.is_empty() {
                     // Uninhabitable; represent as unit
                     // (Typechecking will reject discriminant-sizing attrs.)
                     assert_eq!(hint, attr::ReprAny);
 
-                    let mut st = Struct::new(dl, false);
-                    st.extend(dl, drop_flag.iter().map(Ok), ty)?;
-                    return success(Univariant { variant: st, non_zero: false });
+                    return success(Univariant {
+                        variant: Struct::new(dl, false),
+                        non_zero: false
+                    });
                 }
 
-                if !dtor && def.variants.iter().all(|v| v.fields.is_empty()) {
+                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 {
@@ -937,7 +1020,7 @@ impl<'a, 'gcx, 'tcx> Layout {
                         if x > max { max = x; }
                     }
 
-                    let (discr, signed) = Integer::repr_discr(tcx, hint, min, max);
+                    let (discr, signed) = Integer::repr_discr(tcx, ty, hint, min, max);
                     return success(CEnum {
                         discr: discr,
                         signed: signed,
@@ -946,34 +1029,43 @@ impl<'a, 'gcx, 'tcx> Layout {
                     });
                 }
 
+                if !def.is_enum() || def.variants.len() == 1 && hint == attr::ReprAny {
+                    // Struct, or union, or univariant enum equivalent to a struct.
+                    // (Typechecking will reject discriminant-sizing attrs.)
+
+                    let fields = def.variants[0].fields.iter().map(|field| {
+                        field.ty(tcx, substs).layout(infcx)
+                    });
+                    let packed = tcx.lookup_packed(def.did);
+                    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);
+                }
+
                 // 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));
+                            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.chain(drop_flag.iter().map(Ok)), 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 !dtor && variants.len() == 2 && hint == attr::ReprAny {
+                if variants.len() == 2 && hint == attr::ReprAny {
                     // Nullable pointer optimization
                     for discr in 0..2 {
                         let other_fields = variants[1 - discr].iter().map(|ty| {
@@ -997,8 +1089,8 @@ impl<'a, 'gcx, 'tcx> Layout {
                                 }
                                 _ => {
                                     bug!("Layout::compute: `{}`'s non-zero \
-                                          `{}` field not scalar?!",
-                                         ty, variants[discr][0])
+                                        `{}` field not scalar?!",
+                                        ty, variants[discr][0])
                                 }
                             }
                         }
@@ -1018,7 +1110,7 @@ impl<'a, 'gcx, 'tcx> Layout {
                 // 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 (min_ity, _) = Integer::repr_discr(tcx, ty, hint, 0, discr_max);
 
                 let mut align = dl.aggregate_align;
                 let mut size = Size::from_bytes(0);
@@ -1045,8 +1137,7 @@ impl<'a, 'gcx, 'tcx> Layout {
                         Ok(field)
                     });
                     let mut st = Struct::new(dl, false);
-                    st.extend(dl, discr.iter().map(Ok).chain(fields)
-                                              .chain(drop_flag.iter().map(Ok)), ty)?;
+                    st.extend(dl, discr.iter().map(Ok).chain(fields), ty)?;
                     size = cmp::max(size, st.min_size());
                     align = align.max(st.align);
                     Ok(st)
@@ -1069,20 +1160,7 @@ impl<'a, 'gcx, 'tcx> Layout {
                 // 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;
-                }
+                let mut ity = Integer::for_abi_align(dl, start_align).unwrap_or(min_ity);
 
                 // If the alignment is not larger than the chosen discriminant size,
                 // don't use the alignment as the final size.
@@ -1133,7 +1211,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,
 
@@ -1167,6 +1245,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, .. } => {
@@ -1206,6 +1285,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, .. } => {
@@ -1271,14 +1351,9 @@ impl<'a, 'gcx, 'tcx> SizeSkeleton<'gcx> {
                 }
             }
 
-            ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
+            ty::TyAdt(def, substs) => {
                 // Only newtypes and enums w/ nullable pointer optimization.
-                if def.variants.is_empty() || def.variants.len() > 2 {
-                    return Err(err);
-                }
-
-                // If there's a drop flag, it can't be just a pointer.
-                if def.dtor_kind().has_drop_flag() {
+                if def.is_union() || def.variants.is_empty() || def.variants.len() > 2 {
                     return Err(err);
                 }