Skip to content

Commit cf5f80c

Browse files
committed
Fix having multiple reprs on the same type.
This bug has applied to master for an indefinite period of time and is orthogonal to univariant layout optimization.
1 parent 74f5c61 commit cf5f80c

File tree

3 files changed

+124
-54
lines changed

3 files changed

+124
-54
lines changed

src/librustc/ty/layout.rs

Lines changed: 79 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ impl Integer {
416416
/// signed discriminant range and #[repr] attribute.
417417
/// N.B.: u64 values above i64::MAX will be treated as signed, but
418418
/// that shouldn't affect anything, other than maybe debuginfo.
419-
pub fn repr_discr(tcx: TyCtxt, ty: Ty, hint: attr::ReprAttr, min: i64, max: i64)
419+
fn repr_discr(tcx: TyCtxt, ty: Ty, hints: &[attr::ReprAttr], min: i64, max: i64)
420420
-> (Integer, bool) {
421421
// Theoretically, negative values could be larger in unsigned representation
422422
// than the unsigned representation of the signed minimum. However, if there
@@ -425,33 +425,44 @@ impl Integer {
425425
let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u64, max as u64));
426426
let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
427427

428-
let at_least = match hint {
429-
attr::ReprInt(ity) => {
430-
let discr = Integer::from_attr(&tcx.data_layout, ity);
431-
let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
432-
if discr < fit {
433-
bug!("Integer::repr_discr: `#[repr]` hint too small for \
434-
discriminant range of enum `{}", ty)
428+
let mut min_from_extern = None;
429+
let min_default = I8;
430+
431+
for &r in hints.iter() {
432+
match r {
433+
attr::ReprInt(ity) => {
434+
let discr = Integer::from_attr(&tcx.data_layout, ity);
435+
let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
436+
if discr < fit {
437+
bug!("Integer::repr_discr: `#[repr]` hint too small for \
438+
discriminant range of enum `{}", ty)
439+
}
440+
return (discr, ity.is_signed());
435441
}
436-
return (discr, ity.is_signed());
437-
}
438-
attr::ReprExtern => {
439-
match &tcx.sess.target.target.arch[..] {
440-
// WARNING: the ARM EABI has two variants; the one corresponding
441-
// to `at_least == I32` appears to be used on Linux and NetBSD,
442-
// but some systems may use the variant corresponding to no
443-
// lower bound. However, we don't run on those yet...?
444-
"arm" => I32,
445-
_ => I32,
442+
attr::ReprExtern => {
443+
match &tcx.sess.target.target.arch[..] {
444+
// WARNING: the ARM EABI has two variants; the one corresponding
445+
// to `at_least == I32` appears to be used on Linux and NetBSD,
446+
// but some systems may use the variant corresponding to no
447+
// lower bound. However, we don't run on those yet...?
448+
"arm" => min_from_extern = Some(I32),
449+
_ => min_from_extern = Some(I32),
450+
}
451+
}
452+
attr::ReprAny => {},
453+
attr::ReprPacked => {
454+
bug!("Integer::repr_discr: found #[repr(packed)] on enum `{}", ty);
455+
}
456+
attr::ReprSimd => {
457+
bug!("Integer::repr_discr: found #[repr(simd)] on enum `{}", ty);
446458
}
447459
}
448-
attr::ReprAny => I8,
449-
attr::ReprPacked => {
450-
bug!("Integer::repr_discr: found #[repr(packed)] on enum `{}", ty);
451-
}
452-
attr::ReprSimd => {
453-
bug!("Integer::repr_discr: found #[repr(simd)] on enum `{}", ty);
454-
}
460+
}
461+
462+
let at_least = if let Some(i) = min_from_extern {
463+
i
464+
} else {
465+
min_default
455466
};
456467

457468
// If there are no negative values, we can use the unsigned fit.
@@ -536,10 +547,11 @@ enum StructKind {
536547
}
537548

538549
impl<'a, 'gcx, 'tcx> Struct {
550+
// FIXME(camlorn): reprs need a better representation to deal with multiple reprs on one type.
539551
fn new(dl: &TargetDataLayout, fields: &Vec<&'a Layout>,
540-
repr: attr::ReprAttr, kind: StructKind,
552+
reprs: &[attr::ReprAttr], kind: StructKind,
541553
scapegoat: Ty<'gcx>) -> Result<Struct, LayoutError<'gcx>> {
542-
let packed = repr == attr::ReprPacked;
554+
let packed = reprs.contains(&attr::ReprPacked);
543555
let mut ret = Struct {
544556
align: if packed { dl.i8_align } else { dl.aggregate_align },
545557
packed: packed,
@@ -549,23 +561,37 @@ impl<'a, 'gcx, 'tcx> Struct {
549561
min_size: Size::from_bytes(0),
550562
};
551563

552-
// 1-member and 2-member structs don't optimize.
564+
if fields.len() == 0 {return Ok(ret)};
565+
566+
// Anything with ReprExtern or ReprPacked doesn't optimize.
567+
// Neither do 1-member and 2-member structs.
553568
// In addition, code in trans assume that 2-element structs can become pairs.
554569
// It's easier to just short-circuit here.
555-
let can_optimize_struct = fields.len() > 2;
570+
let mut can_optimize = fields.len() > 2 || StructKind::EnumVariant == kind;
571+
if can_optimize {
572+
// This exhaustive match makes new reprs force the adder to modify this function.
573+
// Otherwise, things can silently break.
574+
// Note the inversion, return true to stop matching.
575+
can_optimize = !reprs.iter().any(|r| {
576+
match *r {
577+
attr::ReprAny => false,
578+
attr::ReprInt(_) => false,
579+
attr::ReprExtern => true,
580+
attr::ReprPacked => true,
581+
attr::ReprSimd => bug!("Simd vectors should be represented as a layout::Vector")
582+
}
583+
});
584+
}
556585

557-
let (optimize, sort_ascending) = match (repr, kind) {
558-
(attr::ReprAny, StructKind::AlwaysSizedUnivariant) => (can_optimize_struct, false),
559-
(attr::ReprAny, StructKind::MaybeUnsizedUnivariant) => (can_optimize_struct, true),
560-
(attr::ReprAny, StructKind::EnumVariant) => {
586+
let (optimize, sort_ascending) = match kind {
587+
StructKind::AlwaysSizedUnivariant => (can_optimize, false),
588+
StructKind::MaybeUnsizedUnivariant => (can_optimize, true),
589+
StructKind::EnumVariant => {
561590
assert!(fields.len() >= 1, "Enum variants must have discriminants.");
562-
(true, fields[0].size(dl).bytes() == 1)
591+
(can_optimize, fields[0].size(dl).bytes() == 1)
563592
}
564-
_ => (false, false)
565593
};
566594

567-
if fields.len() == 0 {return Ok(ret)};
568-
569595
ret.offsets = vec![Size::from_bytes(0); fields.len()];
570596
let mut inverse_memory_index: Vec<u32> = (0..fields.len() as u32).collect();
571597

@@ -590,6 +616,7 @@ impl<'a, 'gcx, 'tcx> Struct {
590616
}
591617
}
592618

619+
// inverse_memory_index holds field indices by increasing memory offset.
593620
// That is, if field 5 has offset 0, the first element of inverse_memory_index is 5.
594621
// We now write field offsets to the corresponding offset slot;
595622
// field 5 with offset 0 puts 0 in offsets[5].
@@ -1021,7 +1048,7 @@ impl<'a, 'gcx, 'tcx> Layout {
10211048

10221049
// The never type.
10231050
ty::TyNever => Univariant {
1024-
variant: Struct::new(dl, &vec![], attr::ReprAny,
1051+
variant: Struct::new(dl, &vec![], &[],
10251052
StructKind::AlwaysSizedUnivariant, ty)?,
10261053
non_zero: false
10271054
},
@@ -1076,12 +1103,12 @@ impl<'a, 'gcx, 'tcx> Layout {
10761103
ty::TyFnDef(..) => {
10771104
Univariant {
10781105
variant: Struct::new(dl, &vec![],
1079-
attr::ReprAny, StructKind::AlwaysSizedUnivariant, ty)?,
1106+
&[], StructKind::AlwaysSizedUnivariant, ty)?,
10801107
non_zero: false
10811108
}
10821109
}
10831110
ty::TyDynamic(_) => {
1084-
let mut unit = Struct::new(dl, &vec![], attr::ReprAny,
1111+
let mut unit = Struct::new(dl, &vec![], &[],
10851112
StructKind::AlwaysSizedUnivariant, ty)?;
10861113
unit.sized = false;
10871114
Univariant { variant: unit, non_zero: false }
@@ -1093,7 +1120,7 @@ impl<'a, 'gcx, 'tcx> Layout {
10931120
let st = Struct::new(dl,
10941121
&tys.map(|ty| ty.layout(infcx))
10951122
.collect::<Result<Vec<_>, _>>()?,
1096-
attr::ReprAny,
1123+
&[],
10971124
StructKind::AlwaysSizedUnivariant, ty)?;
10981125
Univariant { variant: st, non_zero: false }
10991126
}
@@ -1104,7 +1131,7 @@ impl<'a, 'gcx, 'tcx> Layout {
11041131
let st = Struct::new(dl,
11051132
&tys.iter().map(|ty| ty.layout(infcx))
11061133
.collect::<Result<Vec<_>, _>>()?,
1107-
attr::ReprAny, StructKind::AlwaysSizedUnivariant, ty)?;
1134+
&[], StructKind::AlwaysSizedUnivariant, ty)?;
11081135
Univariant { variant: st, non_zero: false }
11091136
}
11101137

@@ -1128,17 +1155,16 @@ impl<'a, 'gcx, 'tcx> Layout {
11281155

11291156
// ADTs.
11301157
ty::TyAdt(def, substs) => {
1131-
let hint = *tcx.lookup_repr_hints(def.did).get(0)
1132-
.unwrap_or(&attr::ReprAny);
1158+
let hints = &tcx.lookup_repr_hints(def.did)[..];
11331159

11341160
if def.variants.is_empty() {
11351161
// Uninhabitable; represent as unit
11361162
// (Typechecking will reject discriminant-sizing attrs.)
1137-
assert_eq!(hint, attr::ReprAny);
1163+
assert_eq!(hints.len(), 0);
11381164

11391165
return success(Univariant {
11401166
variant: Struct::new(dl, &vec![],
1141-
hint, StructKind::AlwaysSizedUnivariant, ty)?,
1167+
&hints[..], StructKind::AlwaysSizedUnivariant, ty)?,
11421168
non_zero: false
11431169
});
11441170
}
@@ -1153,7 +1179,7 @@ impl<'a, 'gcx, 'tcx> Layout {
11531179
if x > max { max = x; }
11541180
}
11551181

1156-
let (discr, signed) = Integer::repr_discr(tcx, ty, hint, min, max);
1182+
let (discr, signed) = Integer::repr_discr(tcx, ty, &hints[..], min, max);
11571183
return success(CEnum {
11581184
discr: discr,
11591185
signed: signed,
@@ -1163,7 +1189,7 @@ impl<'a, 'gcx, 'tcx> Layout {
11631189
});
11641190
}
11651191

1166-
if !def.is_enum() || def.variants.len() == 1 && hint == attr::ReprAny {
1192+
if !def.is_enum() || def.variants.len() == 1 && hints.len() == 0 {
11671193
// Struct, or union, or univariant enum equivalent to a struct.
11681194
// (Typechecking will reject discriminant-sizing attrs.)
11691195

@@ -1190,7 +1216,7 @@ impl<'a, 'gcx, 'tcx> Layout {
11901216
un.extend(dl, fields.iter().map(|&f| Ok(f)), ty)?;
11911217
UntaggedUnion { variants: un }
11921218
} else {
1193-
let st = Struct::new(dl, &fields, hint,
1219+
let st = Struct::new(dl, &fields, &hints[..],
11941220
kind, ty)?;
11951221
let non_zero = Some(def.did) == tcx.lang_items.non_zero();
11961222
Univariant { variant: st, non_zero: non_zero }
@@ -1213,7 +1239,7 @@ impl<'a, 'gcx, 'tcx> Layout {
12131239
v.fields.iter().map(|field| field.ty(tcx, substs)).collect::<Vec<_>>()
12141240
}).collect::<Vec<_>>();
12151241

1216-
if variants.len() == 2 && hint == attr::ReprAny {
1242+
if variants.len() == 2 && hints.len() == 0 {
12171243
// Nullable pointer optimization
12181244
for discr in 0..2 {
12191245
let other_fields = variants[1 - discr].iter().map(|ty| {
@@ -1245,7 +1271,7 @@ impl<'a, 'gcx, 'tcx> Layout {
12451271
let st = Struct::new(dl,
12461272
&variants[discr].iter().map(|ty| ty.layout(infcx))
12471273
.collect::<Result<Vec<_>, _>>()?,
1248-
hint, StructKind::AlwaysSizedUnivariant, ty)?;
1274+
&hints[..], StructKind::AlwaysSizedUnivariant, ty)?;
12491275

12501276
// We have to fix the last element of path here.
12511277
let mut i = *path.last().unwrap();
@@ -1265,7 +1291,7 @@ impl<'a, 'gcx, 'tcx> Layout {
12651291
// The general case.
12661292
let discr_max = (variants.len() - 1) as i64;
12671293
assert!(discr_max >= 0);
1268-
let (min_ity, _) = Integer::repr_discr(tcx, ty, hint, 0, discr_max);
1294+
let (min_ity, _) = Integer::repr_discr(tcx, ty, &hints[..], 0, discr_max);
12691295

12701296
let mut align = dl.aggregate_align;
12711297
let mut size = Size::from_bytes(0);
@@ -1283,7 +1309,7 @@ impl<'a, 'gcx, 'tcx> Layout {
12831309
fields.insert(0, &discr);
12841310
let st = Struct::new(dl,
12851311
&fields,
1286-
hint, StructKind::EnumVariant, ty)?;
1312+
&hints[..], StructKind::EnumVariant, ty)?;
12871313
// Find the first field we can't move later
12881314
// to make room for a larger discriminant.
12891315
// It is important to skip the first field.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
12+
use std::mem::size_of;
13+
14+
// The two enums that follow are designed so that bugs trigger layout optimization.
15+
// Specifically, if either of the following reprs used here is not detected by the compiler,
16+
// then the sizes will be wrong.
17+
18+
#[repr(C, u8)]
19+
enum E1 {
20+
A(u8, u16, u8),
21+
B(u8, u16, u8)
22+
}
23+
24+
#[repr(u8, C)]
25+
enum E2 {
26+
A(u8, u16, u8),
27+
B(u8, u16, u8)
28+
}
29+
30+
// From pr 37429
31+
pub const SIZEOF_QUERY: usize = 21;
32+
33+
#[repr(C,packed)]
34+
pub struct p0f_api_query {
35+
pub magic: u32,
36+
pub addr_type: u8,
37+
pub addr: [u8; 16],
38+
39+
}
40+
pub fn main() {
41+
assert_eq!(size_of::<E1>(), 6);
42+
assert_eq!(size_of::<E2>(), 6);
43+
assert_eq!(size_of::<p0f_api_query>(), 21);
44+
}

src/test/run-pass/type-sizes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ enum e2 {
2626
a(u32), b
2727
}
2828

29-
#[repr(u8)]
29+
#[repr(C, u8)]
3030
enum e3 {
3131
a([u16; 0], u8), b
3232
}

0 commit comments

Comments
 (0)