Skip to content

Commit 2a01e8d

Browse files
authored
Merge pull request #1485 from LegNeato/packed
Support #[repr(packed(N))] on Rust 1.33+
2 parents 28c0eb4 + 3994a9a commit 2a01e8d

File tree

9 files changed

+222
-21
lines changed

9 files changed

+222
-21
lines changed

CHANGELOG.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,14 @@ Released YYYY/MM/DD
5656

5757
## Changed
5858

59+
- `#pragma pack(n)` is now translated to `#[repr(C, packed(n))]` when targeting Rust 1.33+. [#537][]
60+
61+
[#537]: https://github.com/rust-lang-nursery/rust-bindgen/issues/537
62+
5963
* Bitfield enums now use `#[repr(transparent)]` instead of `#[repr(C)]` when targeting Rust 1.28+. [#1474][]
6064

6165
[#1474]: https://github.com/rust-lang-nursery/rust-bindgen/issues/1474
6266

63-
6467
## Deprecated
6568

6669
* TODO (or remove section if none)

src/codegen/helpers.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use quote::TokenStreamExt;
77

88
pub mod attributes {
99
use proc_macro2::{Ident, Span, TokenStream};
10+
use std::str::FromStr;
1011

1112
pub fn repr(which: &str) -> TokenStream {
1213
let which = Ident::new(which, Span::call_site());
@@ -16,7 +17,7 @@ pub mod attributes {
1617
}
1718

1819
pub fn repr_list(which_ones: &[&str]) -> TokenStream {
19-
let which_ones = which_ones.iter().cloned().map(|one| Ident::new(one, Span::call_site()));
20+
let which_ones = which_ones.iter().cloned().map(|one| TokenStream::from_str(one).expect("repr to be valid"));
2021
quote! {
2122
#[repr( #( #which_ones ),* )]
2223
}

src/codegen/mod.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1673,7 +1673,10 @@ impl CodeGenerator for CompInfo {
16731673
attributes.push(attributes::doc(comment));
16741674
}
16751675
if packed && !is_opaque {
1676-
attributes.push(attributes::repr_list(&["C", "packed"]));
1676+
let n = layout.map_or(1, |l| l.align);
1677+
assert!(ctx.options().rust_features().repr_packed_n || n == 1);
1678+
let packed_repr = if n == 1 { "packed".to_string() } else { format!("packed({})", n) };
1679+
attributes.push(attributes::repr_list(&["C", &packed_repr]));
16771680
} else {
16781681
attributes.push(attributes::repr("C"));
16791682
}

src/features.rs

+6
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ macro_rules! rust_target_base {
102102
=> Stable_1_27 => 1.27;
103103
/// Rust stable 1.28
104104
=> Stable_1_28 => 1.28;
105+
/// Rust stable 1.33
106+
=> Stable_1_33 => 1.33;
105107
/// Nightly rust
106108
=> Nightly => nightly;
107109
);
@@ -190,6 +192,10 @@ rust_feature_def!(
190192
/// repr(transparent) ([PR](https://github.com/rust-lang/rust/pull/51562))
191193
=> repr_transparent;
192194
}
195+
Stable_1_33 {
196+
/// repr(packed(N)) ([PR](https://github.com/rust-lang/rust/pull/57049))
197+
=> repr_packed_n;
198+
}
193199
Nightly {
194200
/// `thiscall` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/42202))
195201
=> thiscall_abi;

src/ir/comp.rs

+13-10
Original file line numberDiff line numberDiff line change
@@ -1649,16 +1649,19 @@ impl IsOpaque for CompInfo {
16491649
return true;
16501650
}
16511651

1652-
// We don't have `#[repr(packed = "N")]` in Rust yet, so the best we can
1653-
// do is make this struct opaque.
1654-
//
1655-
// See https://github.com/rust-lang-nursery/rust-bindgen/issues/537 and
1656-
// https://github.com/rust-lang/rust/issues/33158
1657-
if self.is_packed(ctx, layout) && layout.map_or(false, |l| l.align > 1) {
1658-
warn!("Found a type that is both packed and aligned to greater than \
1659-
1; Rust doesn't have `#[repr(packed = \"N\")]` yet, so we \
1660-
are treating it as opaque");
1661-
return true;
1652+
if !ctx.options().rust_features().repr_packed_n {
1653+
// If we don't have `#[repr(packed(N)]`, the best we can
1654+
// do is make this struct opaque.
1655+
//
1656+
// See https://github.com/rust-lang-nursery/rust-bindgen/issues/537 and
1657+
// https://github.com/rust-lang/rust/issues/33158
1658+
if self.is_packed(ctx, layout) && layout.map_or(false, |l| l.align > 1) {
1659+
warn!("Found a type that is both packed and aligned to greater than \
1660+
1; Rust before version 1.33 doesn't have `#[repr(packed(N))]`, so we \
1661+
are treating it as opaque. You may wish to set bindgen's rust target \
1662+
version to 1.33 or later to enable `#[repr(packed(N))]` support.");
1663+
return true;
1664+
}
16621665
}
16631666

16641667
false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/* automatically generated by rust-bindgen */
2+
3+
#![allow(
4+
dead_code,
5+
non_snake_case,
6+
non_camel_case_types,
7+
non_upper_case_globals
8+
)]
9+
#![cfg(feature = "nightly")]
10+
11+
/// This should not be opaque; we can see the attributes and can pack the
12+
/// struct.
13+
#[repr(C, packed)]
14+
#[derive(Debug, Default, Copy, Clone)]
15+
pub struct AlignedToOne {
16+
pub i: ::std::os::raw::c_int,
17+
}
18+
#[test]
19+
fn bindgen_test_layout_AlignedToOne() {
20+
assert_eq!(
21+
::std::mem::size_of::<AlignedToOne>(),
22+
4usize,
23+
concat!("Size of: ", stringify!(AlignedToOne))
24+
);
25+
assert_eq!(
26+
::std::mem::align_of::<AlignedToOne>(),
27+
1usize,
28+
concat!("Alignment of ", stringify!(AlignedToOne))
29+
);
30+
assert_eq!(
31+
unsafe { &(*(::std::ptr::null::<AlignedToOne>())).i as *const _ as usize },
32+
0usize,
33+
concat!(
34+
"Offset of field: ",
35+
stringify!(AlignedToOne),
36+
"::",
37+
stringify!(i)
38+
)
39+
);
40+
}
41+
/// This should be be packed because Rust 1.33 has `#[repr(packed(N))]`.
42+
#[repr(C, packed(2))]
43+
#[derive(Debug, Default, Copy, Clone)]
44+
pub struct AlignedToTwo {
45+
pub i: ::std::os::raw::c_int,
46+
}
47+
#[test]
48+
fn bindgen_test_layout_AlignedToTwo() {
49+
assert_eq!(
50+
::std::mem::size_of::<AlignedToTwo>(),
51+
4usize,
52+
concat!("Size of: ", stringify!(AlignedToTwo))
53+
);
54+
assert_eq!(
55+
::std::mem::align_of::<AlignedToTwo>(),
56+
2usize,
57+
concat!("Alignment of ", stringify!(AlignedToTwo))
58+
);
59+
assert_eq!(
60+
unsafe { &(*(::std::ptr::null::<AlignedToTwo>())).i as *const _ as usize },
61+
0usize,
62+
concat!(
63+
"Offset of field: ",
64+
stringify!(AlignedToTwo),
65+
"::",
66+
stringify!(i)
67+
)
68+
);
69+
}
70+
/// This should not be opaque because although `libclang` doesn't give us the
71+
/// `#pragma pack(1)`, we can detect that alignment is 1 and add
72+
/// `#[repr(packed)]` to the struct ourselves.
73+
#[repr(C, packed)]
74+
#[derive(Debug, Default, Copy, Clone)]
75+
pub struct PackedToOne {
76+
pub x: ::std::os::raw::c_int,
77+
pub y: ::std::os::raw::c_int,
78+
}
79+
#[test]
80+
fn bindgen_test_layout_PackedToOne() {
81+
assert_eq!(
82+
::std::mem::size_of::<PackedToOne>(),
83+
8usize,
84+
concat!("Size of: ", stringify!(PackedToOne))
85+
);
86+
assert_eq!(
87+
::std::mem::align_of::<PackedToOne>(),
88+
1usize,
89+
concat!("Alignment of ", stringify!(PackedToOne))
90+
);
91+
assert_eq!(
92+
unsafe { &(*(::std::ptr::null::<PackedToOne>())).x as *const _ as usize },
93+
0usize,
94+
concat!(
95+
"Offset of field: ",
96+
stringify!(PackedToOne),
97+
"::",
98+
stringify!(x)
99+
)
100+
);
101+
assert_eq!(
102+
unsafe { &(*(::std::ptr::null::<PackedToOne>())).y as *const _ as usize },
103+
4usize,
104+
concat!(
105+
"Offset of field: ",
106+
stringify!(PackedToOne),
107+
"::",
108+
stringify!(y)
109+
)
110+
);
111+
}
112+
/// This should be be packed because Rust 1.33 has `#[repr(packed(N))]`.
113+
#[repr(C, packed(2))]
114+
#[derive(Debug, Default, Copy, Clone)]
115+
pub struct PackedToTwo {
116+
pub x: ::std::os::raw::c_int,
117+
pub y: ::std::os::raw::c_int,
118+
}
119+
#[test]
120+
fn bindgen_test_layout_PackedToTwo() {
121+
assert_eq!(
122+
::std::mem::size_of::<PackedToTwo>(),
123+
8usize,
124+
concat!("Size of: ", stringify!(PackedToTwo))
125+
);
126+
assert_eq!(
127+
::std::mem::align_of::<PackedToTwo>(),
128+
2usize,
129+
concat!("Alignment of ", stringify!(PackedToTwo))
130+
);
131+
assert_eq!(
132+
unsafe { &(*(::std::ptr::null::<PackedToTwo>())).x as *const _ as usize },
133+
0usize,
134+
concat!(
135+
"Offset of field: ",
136+
stringify!(PackedToTwo),
137+
"::",
138+
stringify!(x)
139+
)
140+
);
141+
assert_eq!(
142+
unsafe { &(*(::std::ptr::null::<PackedToTwo>())).y as *const _ as usize },
143+
4usize,
144+
concat!(
145+
"Offset of field: ",
146+
stringify!(PackedToTwo),
147+
"::",
148+
stringify!(y)
149+
)
150+
);
151+
}

tests/expectations/tests/issue-537.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ fn bindgen_test_layout_AlignedToOne() {
3737
)
3838
);
3939
}
40-
/// This should be opaque because although we can see the attributes, Rust
41-
/// doesn't have `#[repr(packed = "N")]` yet.
40+
/// This should be opaque because although we can see the attributes, Rust before
41+
/// 1.33 doesn't have `#[repr(packed(N))]`.
4242
#[repr(C)]
4343
#[derive(Debug, Default, Copy, Clone)]
4444
pub struct AlignedToTwo {
@@ -100,8 +100,8 @@ fn bindgen_test_layout_PackedToOne() {
100100
);
101101
}
102102
/// In this case, even if we can detect the weird alignment triggered by
103-
/// `#pragma pack(2)`, we can't do anything about it because Rust doesn't have
104-
/// `#[repr(packed = "N")]`. Therefore, we must make it opaque.
103+
/// `#pragma pack(2)`, we can't do anything about it because Rust before 1.33
104+
/// doesn't have `#[repr(packed(N))]`. Therefore, we must make it opaque.
105105
#[repr(C)]
106106
#[derive(Debug, Default, Copy, Clone)]
107107
pub struct PackedToTwo {
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// bindgen-flags: --raw-line '#![cfg(feature = "nightly")]' --rust-target 1.33
2+
3+
/// This should not be opaque; we can see the attributes and can pack the
4+
/// struct.
5+
struct AlignedToOne {
6+
int i;
7+
} __attribute__ ((packed,aligned(1)));
8+
9+
/// This should be be packed because Rust 1.33 has `#[repr(packed(N))]`.
10+
struct AlignedToTwo {
11+
int i;
12+
} __attribute__ ((packed,aligned(2)));
13+
14+
#pragma pack(1)
15+
16+
/// This should not be opaque because although `libclang` doesn't give us the
17+
/// `#pragma pack(1)`, we can detect that alignment is 1 and add
18+
/// `#[repr(packed)]` to the struct ourselves.
19+
struct PackedToOne {
20+
int x;
21+
int y;
22+
};
23+
24+
#pragma pack()
25+
26+
#pragma pack(2)
27+
28+
/// This should be be packed because Rust 1.33 has `#[repr(packed(N))]`.
29+
struct PackedToTwo {
30+
int x;
31+
int y;
32+
};
33+
34+
#pragma pack()

tests/headers/issue-537.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ struct AlignedToOne {
44
int i;
55
} __attribute__ ((packed,aligned(1)));
66

7-
/// This should be opaque because although we can see the attributes, Rust
8-
/// doesn't have `#[repr(packed = "N")]` yet.
7+
/// This should be opaque because although we can see the attributes, Rust before
8+
/// 1.33 doesn't have `#[repr(packed(N))]`.
99
struct AlignedToTwo {
1010
int i;
1111
} __attribute__ ((packed,aligned(2)));
@@ -25,8 +25,8 @@ struct PackedToOne {
2525
#pragma pack(2)
2626

2727
/// In this case, even if we can detect the weird alignment triggered by
28-
/// `#pragma pack(2)`, we can't do anything about it because Rust doesn't have
29-
/// `#[repr(packed = "N")]`. Therefore, we must make it opaque.
28+
/// `#pragma pack(2)`, we can't do anything about it because Rust before 1.33
29+
/// doesn't have `#[repr(packed(N))]`. Therefore, we must make it opaque.
3030
struct PackedToTwo {
3131
int x;
3232
int y;

0 commit comments

Comments
 (0)