Skip to content

Commit 75729bd

Browse files
committed
codegen, miri: fix computing the offset of an unsized field in a packed struct
1 parent 0919ad1 commit 75729bd

File tree

5 files changed

+116
-11
lines changed

5 files changed

+116
-11
lines changed

compiler/rustc_codegen_ssa/src/mir/place.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
143143
// Simple cases, which don't need DST adjustment:
144144
// * no metadata available - just log the case
145145
// * known alignment - sized types, `[T]`, `str` or a foreign type
146-
// * packed struct - there is no alignment padding
147146
match field.ty.kind() {
148147
_ if self.llextra.is_none() => {
149148
debug!(
@@ -154,14 +153,6 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
154153
}
155154
_ if field.is_sized() => return simple(),
156155
ty::Slice(..) | ty::Str | ty::Foreign(..) => return simple(),
157-
ty::Adt(def, _) => {
158-
if def.repr().packed() {
159-
// FIXME(eddyb) generalize the adjustment when we
160-
// start supporting packing to larger alignments.
161-
assert_eq!(self.layout.align.abi.bytes(), 1);
162-
return simple();
163-
}
164-
}
165156
_ => {}
166157
}
167158

@@ -186,7 +177,16 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
186177
let unaligned_offset = bx.cx().const_usize(offset.bytes());
187178

188179
// Get the alignment of the field
189-
let (_, unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta);
180+
let (_, mut unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta);
181+
182+
// For packed types, we need to cap alignment.
183+
if let ty::Adt(def, _) = self.layout.ty.kind()
184+
&& let Some(packed) = def.repr().pack
185+
{
186+
let packed = bx.const_usize(packed.bytes());
187+
let cmp = bx.icmp(IntPredicate::IntULT, unsized_align, packed);
188+
unsized_align = bx.select(cmp, unsized_align, packed)
189+
}
190190

191191
// Bump the unaligned offset up to the appropriate alignment
192192
let offset = round_up_const_value_to_alignment(bx, unaligned_offset, unsized_align);

compiler/rustc_const_eval/src/interpret/projection.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,17 @@ where
163163
// With custom DSTS, this *will* execute user-defined code, but the same
164164
// happens at run-time so that's okay.
165165
match self.size_and_align_of(&base_meta, &field_layout)? {
166-
Some((_, align)) => (base_meta, offset.align_to(align)),
166+
Some((_, align)) => {
167+
// For packed types, we need to cap alignment.
168+
let align = if let ty::Adt(def, _) = base.layout().ty.kind()
169+
&& let Some(packed) = def.repr().pack
170+
{
171+
align.min(packed)
172+
} else {
173+
align
174+
};
175+
(base_meta, offset.align_to(align))
176+
}
167177
None => {
168178
// For unsized types with an extern type tail we perform no adjustments.
169179
// NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use std::mem;
2+
3+
#[repr(packed, C)]
4+
struct PackedSized {
5+
f: u8,
6+
d: [u32; 4],
7+
}
8+
9+
#[repr(packed, C)]
10+
struct PackedUnsized {
11+
f: u8,
12+
d: [u32],
13+
}
14+
15+
impl PackedSized {
16+
fn unsize(&self) -> &PackedUnsized {
17+
// We can't unsize via a generic type since then we get the error
18+
// that packed structs with unsized tail don't work if the tail
19+
// might need dropping.
20+
let len = 4usize;
21+
unsafe { mem::transmute((self, len)) }
22+
}
23+
}
24+
25+
fn main() { unsafe {
26+
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
27+
let p = p.unsize() as *const PackedUnsized;
28+
let d = std::ptr::addr_of!((*p).d);
29+
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
30+
} }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// run-pass
2+
use std::mem;
3+
4+
#[repr(packed(4))]
5+
struct Slice([u32]);
6+
7+
#[repr(packed(2), C)]
8+
struct PackedSized {
9+
f: u8,
10+
d: [u32; 4],
11+
}
12+
13+
#[repr(packed(2), C)]
14+
struct PackedUnsized {
15+
f: u8,
16+
d: Slice,
17+
}
18+
19+
impl PackedSized {
20+
fn unsize(&self) -> &PackedUnsized {
21+
// We can't unsize via a generic type since then we get the error
22+
// that packed structs with unsized tail don't work if the tail
23+
// might need dropping.
24+
let len = 4usize;
25+
unsafe { mem::transmute((self, len)) }
26+
}
27+
}
28+
29+
fn main() { unsafe {
30+
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
31+
let p = p.unsize() as *const PackedUnsized;
32+
let d = std::ptr::addr_of!((*p).d);
33+
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
34+
} }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// run-pass
2+
use std::mem;
3+
4+
#[repr(packed, C)]
5+
struct PackedSized {
6+
f: u8,
7+
d: [u32; 4],
8+
}
9+
10+
#[repr(packed, C)]
11+
struct PackedUnsized {
12+
f: u8,
13+
d: [u32],
14+
}
15+
16+
impl PackedSized {
17+
fn unsize(&self) -> &PackedUnsized {
18+
// We can't unsize via a generic type since then we get the error
19+
// that packed structs with unsized tail don't work if the tail
20+
// might need dropping.
21+
let len = 4usize;
22+
unsafe { mem::transmute((self, len)) }
23+
}
24+
}
25+
26+
fn main() { unsafe {
27+
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
28+
let p = p.unsize() as *const PackedUnsized;
29+
let d = std::ptr::addr_of!((*p).d);
30+
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
31+
} }

0 commit comments

Comments
 (0)