Skip to content

Commit d6eb063

Browse files
committed
Fix unsized structs with destructors
The presence of the drop flag caused the offset calculation to be incorrect, leading to the pointer being incorrect. This has been fixed by calculating the offset based on the field index (and not assuming that the field is always the last one). However, I've also stopped the drop flag from being added to the end of unsized structs to begin with. Since it's not actually accessed for unsized structs, and isn't actually where we would say it is, this made more sense.
1 parent a2557d4 commit d6eb063

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

Diff for: src/librustc_trans/trans/adt.rs

+16-6
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,11 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
275275
monomorphize::field_ty(cx.tcx(), substs, field)
276276
}).collect::<Vec<_>>();
277277
let packed = cx.tcx().lookup_packed(def.did);
278-
let dtor = def.dtor_kind().has_drop_flag();
278+
// FIXME(16758) don't add a drop flag to unsized structs, as it
279+
// won't actually be in the location we say it is because it'll be after
280+
// the unsized field. Several other pieces of code assume that the unsized
281+
// field is definitely the last one.
282+
let dtor = def.dtor_kind().has_drop_flag() && type_is_sized(cx.tcx(), t);
279283
if dtor {
280284
ftys.push(cx.tcx().dtor_type());
281285
}
@@ -1105,8 +1109,8 @@ pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
11051109

11061110
pub fn struct_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, st: &Struct<'tcx>, val: MaybeSizedValue,
11071111
ix: usize, needs_cast: bool) -> ValueRef {
1112+
let ccx = bcx.ccx();
11081113
let ptr_val = if needs_cast {
1109-
let ccx = bcx.ccx();
11101114
let fields = st.fields.iter().map(|&ty| {
11111115
type_of::in_memory_type_of(ccx, ty)
11121116
}).collect::<Vec<_>>();
@@ -1147,7 +1151,7 @@ pub fn struct_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, st: &Struct<'tcx>, v
11471151
// We need to get the pointer manually now.
11481152
// We do this by casting to a *i8, then offsetting it by the appropriate amount.
11491153
// We do this instead of, say, simply adjusting the pointer from the result of a GEP
1150-
// because the the field may have an arbitrary alignment in the LLVM representation
1154+
// because the field may have an arbitrary alignment in the LLVM representation
11511155
// anyway.
11521156
//
11531157
// To demonstrate:
@@ -1161,9 +1165,15 @@ pub fn struct_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, st: &Struct<'tcx>, v
11611165

11621166
let meta = val.meta;
11631167

1164-
// st.size is the size of the sized portion of the struct. So the position
1165-
// exactly after it is the offset for unaligned data.
1166-
let unaligned_offset = C_uint(bcx.ccx(), st.size);
1168+
// Calculate the unaligned offset of the the unsized field.
1169+
let mut offset = 0;
1170+
for &ty in &st.fields[0..ix] {
1171+
let llty = type_of::sizing_type_of(ccx, ty);
1172+
let type_align = type_of::align_of(ccx, ty);
1173+
offset = roundup(offset, type_align);
1174+
offset += machine::llsize_of_alloc(ccx, llty);
1175+
}
1176+
let unaligned_offset = C_uint(bcx.ccx(), offset);
11671177

11681178
// Get the alignment of the field
11691179
let (_, align) = glue::size_and_align_of_dst(bcx, fty, meta);

Diff for: src/test/run-pass/dst-field-align.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
struct Foo<T: ?Sized> {
12-
a: u8,
12+
a: u16,
1313
b: T
1414
}
1515

@@ -31,6 +31,11 @@ struct Packed<T: ?Sized> {
3131
b: T
3232
}
3333

34+
struct HasDrop<T: ?Sized> {
35+
ptr: Box<usize>,
36+
data: T
37+
}
38+
3439
fn main() {
3540
// Test that zero-offset works properly
3641
let b : Baz<usize> = Baz { a: 7 };
@@ -68,4 +73,15 @@ fn main() {
6873
let f : &Foo<Bar> = &f;
6974
let &Foo { a: _, b: ref bar } = f;
7075
assert_eq!(bar.get(), 11);
76+
77+
// Make sure that drop flags don't screw things up
78+
79+
let d : HasDrop<Baz<[i32; 4]>> = HasDrop {
80+
ptr: Box::new(0),
81+
data: Baz { a: [1,2,3,4] }
82+
};
83+
assert_eq!([1,2,3,4], d.data.a);
84+
85+
let d : &HasDrop<Baz<[i32]>> = &d;
86+
assert_eq!(&[1,2,3,4], &d.data.a);
7187
}

0 commit comments

Comments
 (0)