Skip to content

Commit 0902928

Browse files
author
Lukas Markeffsky
committed
[DRAFT] assert field oder for unsizeable struct
1 parent 381b778 commit 0902928

File tree

3 files changed

+121
-16
lines changed

3 files changed

+121
-16
lines changed

compiler/rustc_abi/src/layout.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ pub trait LayoutCalculator {
5151
kind: StructKind,
5252
) -> Option<LayoutS> {
5353
let layout = univariant(self, dl, fields, repr, kind, NicheBias::Start);
54+
55+
if matches!(kind, StructKind::MaybeUnsized) {
56+
// Don't compute an alternate layout for types that can be unsized, because
57+
return layout;
58+
}
59+
5460
// Enums prefer niches close to the beginning or the end of the variants so that other (smaller)
5561
// data-carrying variants can be packed into the space after/before the niche.
5662
// If the default field ordering does not give us a niche at the front then we do a second

compiler/rustc_ty_utils/src/layout.rs

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -425,8 +425,19 @@ fn layout_of_uncached<'tcx>(
425425
));
426426
}
427427

428-
tcx.mk_layout(
429-
cx.layout_of_struct_or_enum(
428+
let always_sized = {
429+
let param_env = tcx.param_env(def.did());
430+
def.is_struct()
431+
&& match def.variants().iter().next().and_then(|x| x.fields.raw.last()) {
432+
Some(last_field) => {
433+
tcx.type_of(last_field.did).subst_identity().is_sized(tcx, param_env)
434+
}
435+
None => false,
436+
}
437+
};
438+
439+
let layout = cx
440+
.layout_of_struct_or_enum(
430441
&def.repr(),
431442
&variants,
432443
def.is_enum(),
@@ -442,21 +453,67 @@ fn layout_of_uncached<'tcx>(
442453
.variants()
443454
.iter_enumerated()
444455
.any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32())),
445-
{
446-
let param_env = tcx.param_env(def.did());
447-
def.is_struct()
448-
&& match def.variants().iter().next().and_then(|x| x.fields.raw.last())
449-
{
450-
Some(last_field) => tcx
451-
.type_of(last_field.did)
452-
.subst_identity()
453-
.is_sized(tcx, param_env),
454-
None => false,
455-
}
456-
},
456+
always_sized,
457457
)
458-
.ok_or(LayoutError::SizeOverflow(ty))?,
459-
)
458+
.ok_or(LayoutError::SizeOverflow(ty))?;
459+
460+
if def.is_struct() && !always_sized {
461+
let mut variants_again = variants.clone();
462+
463+
let mut unit = univariant_uninterned(
464+
cx,
465+
ty,
466+
IndexSlice::empty(),
467+
&ReprOptions::default(),
468+
StructKind::AlwaysSized,
469+
)
470+
.unwrap();
471+
match unit.abi {
472+
Abi::Aggregate { ref mut sized } => *sized = false,
473+
_ => bug!(),
474+
}
475+
let unit_interned = tcx.mk_layout(unit.clone());
476+
477+
if let Some(tail) = variants_again[FIRST_VARIANT].raw.last_mut() && *tail.0.0 != unit {
478+
*tail = unit_interned;
479+
480+
let layout_again = cx.layout_of_struct_or_enum(
481+
&def.repr(),
482+
&variants_again,
483+
def.is_enum(),
484+
def.is_unsafe_cell(),
485+
tcx.layout_scalar_valid_range(def.did()),
486+
|min, max| Integer::repr_discr(tcx, ty, &def.repr(), min, max),
487+
def.is_enum()
488+
.then(|| def.discriminants(tcx).map(|(v, d)| (v, d.val as i128)))
489+
.into_iter()
490+
.flatten(),
491+
def.repr().inhibit_enum_layout_opt()
492+
|| def
493+
.variants()
494+
.iter_enumerated()
495+
.any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32())),
496+
always_sized,
497+
)
498+
.unwrap();
499+
500+
let mut fields1 = layout.fields.clone();
501+
let mut fields2 = layout_again.fields.clone();
502+
503+
if let FieldsShape::Arbitrary { offsets: ref mut offsets1, .. } = fields1
504+
&& let FieldsShape::Arbitrary { offsets: ref mut offsets2, .. } = fields2
505+
&& let Some(last1) = offsets1.raw.last()
506+
&& let Some(last2) = offsets1.raw.last() {
507+
assert!(last1 >= last2);
508+
offsets1.pop();
509+
offsets2.pop();
510+
}
511+
512+
assert_eq!(fields1, fields2);
513+
}
514+
}
515+
516+
tcx.mk_layout(layout)
460517
}
461518

462519
// Types with no meaningful known layout.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// run-pass
2+
3+
#![feature(offset_of)]
4+
#![allow(dead_code)]
5+
6+
use std::mem::offset_of;
7+
8+
#[derive(Clone)]
9+
struct WideptrField<T: ?Sized> {
10+
first: usize,
11+
second: usize,
12+
niche: NicheAtEnd,
13+
tail: T,
14+
}
15+
16+
#[derive(Clone)]
17+
#[repr(C)]
18+
struct NicheAtEnd {
19+
arr: [u8; 7],
20+
b: bool,
21+
}
22+
23+
type Tail = [bool; 8];
24+
25+
fn main() {
26+
let sized = Box::new(WideptrField {
27+
first: 0xdead,
28+
second: 0xbeef,
29+
niche: NicheAtEnd {
30+
arr: [0; 7],
31+
b: false,
32+
},
33+
tail: [false; 8] as Tail,
34+
});
35+
36+
let _unsized_value: Box<WideptrField<dyn Send>> = sized.clone();
37+
38+
assert_eq!(
39+
offset_of!(WideptrField<Tail>, niche),
40+
offset_of!(WideptrField<dyn Send>, niche),
41+
);
42+
}

0 commit comments

Comments
 (0)