Skip to content

Commit 7fefc1c

Browse files
committed
Append missing padding after last field of struct
This patch fixes issue rust-lang#13186. When generating constant expression for enum, it is possible that alignment of expression may be not equal to alignment of type. In that case space after last struct field must be padded to match size of value and size of struct. This commit adds that padding. See detailed explanation in src/test/run-pass/trans-tag-static-padding.rs
1 parent ecc774f commit 7fefc1c

File tree

2 files changed

+104
-12
lines changed

2 files changed

+104
-12
lines changed

src/librustc/middle/trans/adt.rs

+38-12
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,26 @@ pub fn trans_const(ccx: &CrateContext, r: &Repr, discr: Disr,
766766
}
767767
}
768768

769+
/**
770+
* Compute struct field offsets relative to struct begin.
771+
*/
772+
fn compute_struct_field_offsets(ccx: &CrateContext, st: &Struct) -> Vec<u64> {
773+
let mut offsets = vec!();
774+
775+
let mut offset = 0;
776+
for &ty in st.fields.iter() {
777+
let llty = type_of::sizing_type_of(ccx, ty);
778+
if !st.packed {
779+
let type_align = machine::llalign_of_min(ccx, llty) as u64;
780+
offset = roundup(offset, type_align);
781+
}
782+
offsets.push(offset);
783+
offset += machine::llsize_of_alloc(ccx, llty) as u64;
784+
}
785+
assert_eq!(st.fields.len(), offsets.len());
786+
offsets
787+
}
788+
769789
/**
770790
* Building structs is a little complicated, because we might need to
771791
* insert padding if a field's value is less aligned than its type.
@@ -780,26 +800,32 @@ fn build_const_struct(ccx: &CrateContext, st: &Struct, vals: &[ValueRef])
780800
-> Vec<ValueRef> {
781801
assert_eq!(vals.len(), st.fields.len());
782802

803+
let target_offsets = compute_struct_field_offsets(ccx, st);
804+
805+
// offset of current value
783806
let mut offset = 0;
784807
let mut cfields = Vec::new();
785-
for (i, &ty) in st.fields.iter().enumerate() {
786-
let llty = type_of::sizing_type_of(ccx, ty);
787-
let type_align = machine::llalign_of_min(ccx, llty)
788-
/*bad*/as u64;
789-
let val_align = machine::llalign_of_min(ccx, val_ty(vals[i]))
790-
/*bad*/as u64;
791-
let target_offset = roundup(offset, type_align);
792-
offset = roundup(offset, val_align);
808+
for (&val, &target_offset) in vals.iter().zip(target_offsets.iter()) {
809+
if !st.packed {
810+
let val_align = machine::llalign_of_min(ccx, val_ty(val))
811+
/*bad*/as u64;
812+
offset = roundup(offset, val_align);
813+
}
793814
if offset != target_offset {
794815
cfields.push(padding(ccx, target_offset - offset));
795816
offset = target_offset;
796817
}
797-
assert!(!is_undef(vals[i]));
798-
cfields.push(vals[i]);
799-
offset += machine::llsize_of_alloc(ccx, llty) as u64
818+
assert!(!is_undef(val));
819+
cfields.push(val);
820+
offset += machine::llsize_of_alloc(ccx, val_ty(val)) as u64;
821+
}
822+
823+
assert!(offset <= st.size);
824+
if offset != st.size {
825+
cfields.push(padding(ccx, st.size - offset));
800826
}
801827

802-
return cfields;
828+
cfields
803829
}
804830

805831
fn padding(ccx: &CrateContext, size: u64) -> ValueRef {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2014 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+
// Issue #13186
13+
14+
// For simplicity of explanations assuming code is compiled for x86_64
15+
// Linux ABI.
16+
17+
// Size of TestOption<u64> is 16, and alignment of TestOption<u64> is 8.
18+
// Size of u8 is 1, and alignment of u8 is 1.
19+
// So size of Request is 24, and alignment of Request must be 8:
20+
// the maximum alignment of its fields.
21+
// Last 7 bytes of Request struct are not occupied by any fields.
22+
23+
24+
enum TestOption<T> {
25+
TestNone,
26+
TestSome(T),
27+
}
28+
29+
pub struct Request {
30+
foo: TestOption<u64>,
31+
bar: u8,
32+
}
33+
34+
fn default_instance() -> &'static Request {
35+
static instance: Request = Request {
36+
// LLVM does not allow to specify alignment of expressions, thus
37+
// alignment of `foo` in constant is 1, not 8.
38+
foo: TestNone,
39+
bar: 17,
40+
// Space after last field is not occupied by any data, but it is
41+
// reserved to make struct aligned properly. If compiler does
42+
// not insert padding after last field when emitting constant,
43+
// size of struct may be not equal to size of struct, and
44+
// compiler crashes in internal assertion check.
45+
};
46+
&'static instance
47+
}
48+
49+
fn non_default_instance() -> &'static Request {
50+
static instance: Request = Request {
51+
foo: TestSome(0x1020304050607080),
52+
bar: 19,
53+
};
54+
&'static instance
55+
}
56+
57+
pub fn main() {
58+
match default_instance() {
59+
&Request { foo: TestNone, bar: 17 } => {},
60+
_ => fail!(),
61+
};
62+
match non_default_instance() {
63+
&Request { foo: TestSome(0x1020304050607080), bar: 19 } => {},
64+
_ => fail!(),
65+
};
66+
}

0 commit comments

Comments
 (0)