diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs index b47d2dd4112ca..1a5eebfa77ae4 100644 --- a/src/librustc_trans/trans/adt.rs +++ b/src/librustc_trans/trans/adt.rs @@ -67,6 +67,31 @@ use trans::type_of; type Hint = attr::ReprAttr; +// Representation of the context surrounding an unsized type. I want +// to be able to track the drop flags that are injected by trans. +#[derive(Clone, Copy, PartialEq, Debug)] +pub struct TypeContext { + prefix: Type, + needs_drop_flag: bool, +} + +impl TypeContext { + pub fn prefix(&self) -> Type { self.prefix } + pub fn needs_drop_flag(&self) -> bool { self.needs_drop_flag } + + fn direct(t: Type) -> TypeContext { + TypeContext { prefix: t, needs_drop_flag: false } + } + fn may_need_drop_flag(t: Type, needs_drop_flag: bool) -> TypeContext { + TypeContext { prefix: t, needs_drop_flag: needs_drop_flag } + } + pub fn to_string(self) -> String { + let TypeContext { prefix, needs_drop_flag } = self; + format!("TypeContext {{ prefix: {}, needs_drop_flag: {} }}", + prefix.to_string(), needs_drop_flag) + } +} + /// Representations. #[derive(Eq, PartialEq, Debug)] pub enum Repr<'tcx> { @@ -125,7 +150,7 @@ pub struct Struct<'tcx> { pub align: u32, pub sized: bool, pub packed: bool, - pub fields: Vec> + pub fields: Vec>, } /// Convenience for `represent_type`. There should probably be more or @@ -681,18 +706,30 @@ fn ensure_enum_fits_in_address_space<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, /// and fill in the actual contents in a second pass to prevent /// unbounded recursion; see also the comments in `trans::type_of`. pub fn type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>) -> Type { - generic_type_of(cx, r, None, false, false) + let c = generic_type_of(cx, r, None, false, false, false); + assert!(!c.needs_drop_flag); + c.prefix } + + // Pass dst=true if the type you are passing is a DST. Yes, we could figure // this out, but if you call this on an unsized type without realising it, you // are going to get the wrong type (it will not include the unsized parts of it). pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>, dst: bool) -> Type { - generic_type_of(cx, r, None, true, dst) + let c = generic_type_of(cx, r, None, true, dst, false); + assert!(!c.needs_drop_flag); + c.prefix +} +pub fn sizing_type_context_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + r: &Repr<'tcx>, dst: bool) -> TypeContext { + generic_type_of(cx, r, None, true, dst, true) } pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>, name: &str) -> Type { - generic_type_of(cx, r, Some(name), false, false) + let c = generic_type_of(cx, r, Some(name), false, false, false); + assert!(!c.needs_drop_flag); + c.prefix } pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>, llty: &mut Type) { @@ -708,20 +745,50 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>, name: Option<&str>, sizing: bool, - dst: bool) -> Type { + dst: bool, + delay_drop_flag: bool) -> TypeContext { + debug!("adt::generic_type_of r: {:?} name: {:?} sizing: {} dst: {} delay_drop_flag: {}", + r, name, sizing, dst, delay_drop_flag); match *r { - CEnum(ity, _, _) => ll_inttype(cx, ity), - RawNullablePointer { nnty, .. } => type_of::sizing_type_of(cx, nnty), - Univariant(ref st, _) | StructWrappedNullablePointer { nonnull: ref st, .. } => { + CEnum(ity, _, _) => TypeContext::direct(ll_inttype(cx, ity)), + RawNullablePointer { nnty, .. } => + TypeContext::direct(type_of::sizing_type_of(cx, nnty)), + StructWrappedNullablePointer { nonnull: ref st, .. } => { + match name { + None => { + TypeContext::direct( + Type::struct_(cx, &struct_llfields(cx, st, sizing, dst), + st.packed)) + } + Some(name) => { + assert_eq!(sizing, false); + TypeContext::direct(Type::named_struct(cx, name)) + } + } + } + Univariant(ref st, dtor_needed) => { + let dtor_needed = dtor_needed != 0; match name { None => { - Type::struct_(cx, &struct_llfields(cx, st, sizing, dst), - st.packed) + let mut fields = struct_llfields(cx, st, sizing, dst); + if delay_drop_flag && dtor_needed { + fields.pop(); + } + TypeContext::may_need_drop_flag( + Type::struct_(cx, &fields, + st.packed), + delay_drop_flag && dtor_needed) + } + Some(name) => { + // Hypothesis: named_struct's can never need a + // drop flag. (... needs validation.) + assert_eq!(sizing, false); + TypeContext::direct(Type::named_struct(cx, name)) } - Some(name) => { assert_eq!(sizing, false); Type::named_struct(cx, name) } } } - General(ity, ref sts, _) => { + General(ity, ref sts, dtor_needed) => { + let dtor_needed = dtor_needed != 0; // We need a representation that has: // * The alignment of the most-aligned field // * The size of the largest variant (rounded up to that alignment) @@ -753,15 +820,25 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, }; assert_eq!(machine::llalign_of_min(cx, fill_ty), align); assert_eq!(align_s % discr_size, 0); - let fields = [discr_ty, - Type::array(&discr_ty, align_s / discr_size - 1), - fill_ty]; + let mut fields: Vec = + [discr_ty, + Type::array(&discr_ty, align_s / discr_size - 1), + fill_ty].iter().cloned().collect(); + if delay_drop_flag && dtor_needed { + fields.pop(); + } match name { - None => Type::struct_(cx, &fields[..], false), + None => { + TypeContext::may_need_drop_flag( + Type::struct_(cx, &fields[..], false), + delay_drop_flag && dtor_needed) + } Some(name) => { let mut llty = Type::named_struct(cx, name); llty.set_struct_body(&fields[..], false); - llty + TypeContext::may_need_drop_flag( + llty, + delay_drop_flag && dtor_needed) } } } diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs index 6019099c05825..18fedda49193c 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -406,8 +406,12 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in t, bcx.val_to_string(info)); if type_is_sized(bcx.tcx(), t) { let sizing_type = sizing_type_of(bcx.ccx(), t); - let size = C_uint(bcx.ccx(), llsize_of_alloc(bcx.ccx(), sizing_type)); - let align = C_uint(bcx.ccx(), align_of(bcx.ccx(), t)); + let size = llsize_of_alloc(bcx.ccx(), sizing_type); + let align = align_of(bcx.ccx(), t); + debug!("size_and_align_of_dst t={} info={} size: {} align: {}", + t, bcx.val_to_string(info), size, align); + let size = C_uint(bcx.ccx(), size); + let align = C_uint(bcx.ccx(), align); return (size, align); } match t.sty { @@ -417,9 +421,14 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in // Don't use type_of::sizing_type_of because that expects t to be sized. assert!(!t.is_simd(bcx.tcx())); let repr = adt::represent_type(ccx, t); - let sizing_type = adt::sizing_type_of(ccx, &*repr, true); - let sized_size = C_uint(ccx, llsize_of_alloc(ccx, sizing_type)); - let sized_align = C_uint(ccx, llalign_of_min(ccx, sizing_type)); + let sizing_type = adt::sizing_type_context_of(ccx, &*repr, true); + debug!("DST {} sizing_type: {}", t, sizing_type.to_string()); + let sized_size = llsize_of_alloc(ccx, sizing_type.prefix()); + let sized_align = llalign_of_min(ccx, sizing_type.prefix()); + debug!("DST {} statically sized prefix size: {} align: {}", + t, sized_size, sized_align); + let sized_size = C_uint(ccx, sized_size); + let sized_align = C_uint(ccx, sized_align); // Recurse to get the size of the dynamically sized field (must be // the last field). @@ -428,16 +437,52 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in let field_ty = last_field.mt.ty; let (unsized_size, unsized_align) = size_and_align_of_dst(bcx, field_ty, info); + let dbloc = DebugLoc::None; + + // FIXME (#26403, #27023): We should be adding padding + // to `sized_size` (to accommodate the `unsized_align` + // required of the unsized field that follows) before + // summing it with `sized_size`. (Note that since #26403 + // is unfixed, we do not yet add the necessary padding + // here. But this is where the add would go.) + // Return the sum of sizes and max of aligns. - let size = Add(bcx, sized_size, unsized_size, DebugLoc::None); + let mut size = Add(bcx, sized_size, unsized_size, dbloc); + + // Issue #27023: If there is a drop flag, *now* we add 1 + // to the size. (We can do this without adding any + // padding because drop flags do not have any alignment + // constraints.) + if sizing_type.needs_drop_flag() { + size = Add(bcx, size, C_uint(bcx.ccx(), 1_u64), dbloc); + } + + // Choose max of two known alignments (combined value must + // be aligned according to more restrictive of the two). let align = Select(bcx, ICmp(bcx, - llvm::IntULT, + llvm::IntUGT, sized_align, unsized_align, - DebugLoc::None), + dbloc), sized_align, unsized_align); + + // Issue #27023: must add any necessary padding to `size` + // (to make it a multiple of `align`) before returning it. + // + // Namely, the returned size should be, in C notation: + // + // `size + ((size & (align-1)) ? align : 0)` + // + // emulated via the semi-standard fast bit trick: + // + // `(size + (align-1)) & !align` + + let addend = Sub(bcx, align, C_uint(bcx.ccx(), 1_u64), dbloc); + let size = And( + bcx, Add(bcx, size, addend, dbloc), Neg(bcx, align, dbloc), dbloc); + (size, align) } ty::TyTrait(..) => { diff --git a/src/librustc_trans/trans/type_.rs b/src/librustc_trans/trans/type_.rs index c88509dac4caa..699115a070986 100644 --- a/src/librustc_trans/trans/type_.rs +++ b/src/librustc_trans/trans/type_.rs @@ -50,6 +50,12 @@ impl Type { self.rf } + pub fn to_string(self: Type) -> String { + llvm::build_string(|s| unsafe { + llvm::LLVMWriteTypeToString(self.to_ref(), s); + }).expect("non-UTF8 type description from LLVM") + } + pub fn void(ccx: &CrateContext) -> Type { ty!(llvm::LLVMVoidTypeInContext(ccx.llcx())) } @@ -315,9 +321,7 @@ impl TypeNames { } pub fn type_to_string(&self, ty: Type) -> String { - llvm::build_string(|s| unsafe { - llvm::LLVMWriteTypeToString(ty.to_ref(), s); - }).expect("non-UTF8 type description from LLVM") + ty.to_string() } pub fn types_to_str(&self, tys: &[Type]) -> String {