Skip to content

Commit 4d7eb65

Browse files
fitzgentmfink
authored andcommitted
Be conservative about deriving Debug/Default with large alignment
When there is large enough alignment that we might generate padding which has more members that `RUST_DERIVE_IN_ARRAY_LIMIT`, we can break our ability to derive traits. This commit solves this issue conservatively: there are cases where we leave a derive on the table, because in order to know that we could add that derive, we would need to compute padding before we determine whether we can derive. Fixes rust-lang#648
1 parent 55149b9 commit 4d7eb65

9 files changed

+113
-9
lines changed

src/ir/analysis/derive_debug.rs

+9
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,15 @@ impl<'ctx, 'gen> MonotoneFramework for CannotDeriveDebug<'ctx, 'gen> {
141141
};
142142
}
143143

144+
if ty.layout(self.ctx).map_or(false, |l| l.align > RUST_DERIVE_IN_ARRAY_LIMIT) {
145+
// We have to be conservative: the struct *could* have enough
146+
// padding that we emit an array that is longer than
147+
// `RUST_DERIVE_IN_ARRAY_LIMIT`. If we moved padding calculations
148+
// into the IR and computed them before this analysis, then we could
149+
// be precise rather than conservative here.
150+
return self.insert(id);
151+
}
152+
144153
match *ty.kind() {
145154
// Handle the simple cases. These can derive debug without further
146155
// information.

src/ir/comp.rs

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use super::dot::DotAttributes;
77
use super::item::{IsOpaque, Item};
88
use super::layout::Layout;
99
use super::traversal::{EdgeKind, Trace, Tracer};
10+
use super::ty::RUST_DERIVE_IN_ARRAY_LIMIT;
1011
use super::template::TemplateParameters;
1112
use clang;
1213
use codegen::struct_layout::{align_to, bytes_from_bits_pow2};
@@ -1435,6 +1436,10 @@ impl<'a> CanDeriveDefault<'a> for CompInfo {
14351436
return true;
14361437
}
14371438

1439+
if layout.map_or(false, |l| l.align > RUST_DERIVE_IN_ARRAY_LIMIT) {
1440+
return false;
1441+
}
1442+
14381443
if self.kind == CompKind::Union {
14391444
if ctx.options().unstable_rust {
14401445
return false;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/* automatically generated by rust-bindgen */
2+
3+
4+
#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
5+
6+
7+
/// We emit a `[u8; 63usize]` padding field for this struct, which cannot derive
8+
/// Debug because 63 is over the hard coded limit. (Yes, this struct doesn't end
9+
/// up with the reight alignment, we're waiting on `#[repr(align="N")]` to land
10+
/// in rustc).
11+
#[repr(C)]
12+
#[derive(Copy)]
13+
pub struct NoDebug {
14+
pub c: ::std::os::raw::c_char,
15+
pub __bindgen_padding_0: [u8; 63usize],
16+
}
17+
#[test]
18+
fn bindgen_test_layout_NoDebug() {
19+
assert_eq!(::std::mem::size_of::<NoDebug>() , 64usize , concat ! (
20+
"Size of: " , stringify ! ( NoDebug ) ));
21+
assert_eq! (unsafe {
22+
& ( * ( 0 as * const NoDebug ) ) . c as * const _ as usize } ,
23+
0usize , concat ! (
24+
"Alignment of field: " , stringify ! ( NoDebug ) , "::" ,
25+
stringify ! ( c ) ));
26+
}
27+
impl Clone for NoDebug {
28+
fn clone(&self) -> Self { *self }
29+
}
30+
impl Default for NoDebug {
31+
fn default() -> Self { unsafe { ::std::mem::zeroed() } }
32+
}
33+
/// This should derive Debug because the padding size is less than the max derive
34+
/// Debug impl for arrays. However, we conservatively don't derive Debug because
35+
/// we determine Debug derive-ability before we compute padding, which happens at
36+
/// codegen. (Again, we expect to get the alignment wrong for similar reasons.)
37+
#[repr(C)]
38+
#[derive(Copy)]
39+
pub struct ShouldDeriveDebugButDoesNot {
40+
pub c: [::std::os::raw::c_char; 32usize],
41+
pub d: ::std::os::raw::c_char,
42+
pub __bindgen_padding_0: [u8; 31usize],
43+
}
44+
#[test]
45+
fn bindgen_test_layout_ShouldDeriveDebugButDoesNot() {
46+
assert_eq!(::std::mem::size_of::<ShouldDeriveDebugButDoesNot>() , 64usize
47+
, concat ! (
48+
"Size of: " , stringify ! ( ShouldDeriveDebugButDoesNot ) ));
49+
assert_eq! (unsafe {
50+
& ( * ( 0 as * const ShouldDeriveDebugButDoesNot ) ) . c as *
51+
const _ as usize } , 0usize , concat ! (
52+
"Alignment of field: " , stringify ! (
53+
ShouldDeriveDebugButDoesNot ) , "::" , stringify ! ( c ) ));
54+
assert_eq! (unsafe {
55+
& ( * ( 0 as * const ShouldDeriveDebugButDoesNot ) ) . d as *
56+
const _ as usize } , 32usize , concat ! (
57+
"Alignment of field: " , stringify ! (
58+
ShouldDeriveDebugButDoesNot ) , "::" , stringify ! ( d ) ));
59+
}
60+
impl Clone for ShouldDeriveDebugButDoesNot {
61+
fn clone(&self) -> Self { *self }
62+
}
63+
impl Default for ShouldDeriveDebugButDoesNot {
64+
fn default() -> Self { unsafe { ::std::mem::zeroed() } }
65+
}

tests/expectations/tests/layout_array.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub type rte_mempool_get_count =
4747
-> ::std::os::raw::c_uint>;
4848
/// Structure defining mempool operations structure
4949
#[repr(C)]
50-
#[derive(Debug, Copy)]
50+
#[derive(Copy)]
5151
pub struct rte_mempool_ops {
5252
/// < Name of mempool ops struct.
5353
pub name: [::std::os::raw::c_char; 32usize],
@@ -134,7 +134,7 @@ impl Clone for rte_spinlock_t {
134134
/// any function pointers stored directly in the mempool struct would not be.
135135
/// This results in us simply having "ops_index" in the mempool struct.
136136
#[repr(C)]
137-
#[derive(Debug, Copy)]
137+
#[derive(Copy)]
138138
pub struct rte_mempool_ops_table {
139139
/// < Spinlock for add/delete.
140140
pub sl: rte_spinlock_t,
@@ -173,7 +173,7 @@ impl Default for rte_mempool_ops_table {
173173
}
174174
/// Structure to hold malloc heap
175175
#[repr(C)]
176-
#[derive(Debug, Copy)]
176+
#[derive(Copy)]
177177
pub struct malloc_heap {
178178
pub lock: rte_spinlock_t,
179179
pub free_head: [malloc_heap__bindgen_ty_1; 13usize],

tests/expectations/tests/layout_array_too_long.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ impl Clone for ip_frag_key {
9696
/// @internal Fragmented packet to reassemble.
9797
/// First two entries in the frags[] array are for the last and first fragments.
9898
#[repr(C)]
99-
#[derive(Debug, Copy)]
99+
#[derive(Copy)]
100100
pub struct ip_frag_pkt {
101101
/// < LRU list
102102
pub lru: ip_frag_pkt__bindgen_ty_1,

tests/expectations/tests/layout_kni_mbuf.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
pub const RTE_CACHE_LINE_MIN_SIZE: ::std::os::raw::c_uint = 64;
88
pub const RTE_CACHE_LINE_SIZE: ::std::os::raw::c_uint = 64;
99
#[repr(C)]
10-
#[derive(Debug, Copy)]
10+
#[derive(Copy)]
1111
pub struct rte_kni_mbuf {
1212
pub buf_addr: *mut ::std::os::raw::c_void,
1313
pub buf_physaddr: u64,

tests/expectations/tests/layout_large_align_field.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ impl Clone for ip_frag_key {
129129
/// @internal Fragmented packet to reassemble.
130130
/// First two entries in the frags[] array are for the last and first fragments.
131131
#[repr(C)]
132-
#[derive(Debug, Copy)]
132+
#[derive(Copy)]
133133
pub struct ip_frag_pkt {
134134
/// < LRU list
135135
pub lru: ip_frag_pkt__bindgen_ty_1,
@@ -258,7 +258,7 @@ impl Default for ip_pkt_list {
258258
}
259259
/// fragmentation table statistics
260260
#[repr(C)]
261-
#[derive(Debug, Default, Copy)]
261+
#[derive(Copy)]
262262
pub struct ip_frag_tbl_stat {
263263
/// < total # of find/insert attempts.
264264
pub find_num: u64,
@@ -312,9 +312,12 @@ fn bindgen_test_layout_ip_frag_tbl_stat() {
312312
impl Clone for ip_frag_tbl_stat {
313313
fn clone(&self) -> Self { *self }
314314
}
315+
impl Default for ip_frag_tbl_stat {
316+
fn default() -> Self { unsafe { ::std::mem::zeroed() } }
317+
}
315318
/// fragmentation table
316319
#[repr(C)]
317-
#[derive(Debug, Copy)]
320+
#[derive(Copy)]
318321
pub struct rte_ip_frag_tbl {
319322
/// < ttl for table entries.
320323
pub max_cycles: u64,

tests/expectations/tests/layout_mbuf.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ impl Clone for rte_atomic16_t {
5858
}
5959
/// The generic rte_mbuf, containing a packet mbuf.
6060
#[repr(C)]
61-
#[derive(Debug, Copy)]
61+
#[derive(Copy)]
6262
pub struct rte_mbuf {
6363
pub cacheline0: MARKER,
6464
/// < Virtual address of segment buffer.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* We emit a `[u8; 63usize]` padding field for this struct, which cannot derive
3+
* Debug because 63 is over the hard coded limit. (Yes, this struct doesn't end
4+
* up with the reight alignment, we're waiting on `#[repr(align="N")]` to land
5+
* in rustc).
6+
*/
7+
struct NoDebug {
8+
char c;
9+
// padding of 63 bytes
10+
} __attribute__((__aligned__(64)));
11+
12+
/**
13+
* This should derive Debug because the padding size is less than the max derive
14+
* Debug impl for arrays. However, we conservatively don't derive Debug because
15+
* we determine Debug derive-ability before we compute padding, which happens at
16+
* codegen. (Again, we expect to get the alignment wrong for similar reasons.)
17+
*/
18+
struct ShouldDeriveDebugButDoesNot {
19+
char c[32];
20+
char d;
21+
// padding of 31 bytes
22+
} __attribute__((__aligned__(64)));

0 commit comments

Comments
 (0)