Skip to content

Commit ef2538b

Browse files
committed
refactor: improve get_element_alignment function for packed arrays
1 parent b37f571 commit ef2538b

File tree

5 files changed

+200
-54
lines changed

5 files changed

+200
-54
lines changed

compiler/rustc_const_eval/src/util/alignment.rs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ where
5454
// Therefore align([T]) == align(T). Length does not affect alignment.
5555

5656
// Try to determine alignment from the type structure
57-
if let Some(element_align) = get_element_alignment(tcx, ty) {
57+
if let Some(element_align) = get_element_alignment(tcx, typing_env, ty) {
5858
element_align > pack
5959
} else {
6060
// If we still can't determine alignment, conservatively assume disaligned
@@ -64,22 +64,36 @@ where
6464
}
6565
}
6666

67-
/// Try to determine the alignment of an array element type
68-
fn get_element_alignment<'tcx>(_tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Align> {
67+
/// Returns the ABI alignment of the *element type* if `ty` is an array/slice,
68+
/// otherwise `None`.
69+
///
70+
/// Soundness note:
71+
/// For any `T`, the ABI alignment of `[T]` (and `[T; N]`) equals that of `T`
72+
/// and does not depend on the length `N`.
73+
/// Proof sketch:
74+
/// (1) From `&[T]` we can obtain `&T` ⇒ align([T]) ≥ align(T).
75+
/// (2) From `&T` we can obtain `&[T; 1]` via `std::array::from_ref`
76+
/// (and thus `&[T]`) ⇒ align(T) ≥ align([T]).
77+
/// Hence `align([T]) == align(T)`.
78+
///
79+
/// Therefore, when `layout_of([T; N])` is unavailable in generic contexts,
80+
/// it is sufficient (and safe) to use `layout_of(T)` for alignment checks.
81+
///
82+
/// Returns:
83+
/// - `Some(align)` if `ty` is `Array(elem, _)` or `Slice(elem)` and
84+
/// `layout_of(elem)` is available;
85+
/// - `None` otherwise (caller should stay conservative).
86+
fn get_element_alignment<'tcx>(
87+
tcx: TyCtxt<'tcx>,
88+
typing_env: ty::TypingEnv<'tcx>,
89+
ty: Ty<'tcx>,
90+
) -> Option<Align> {
6991
match ty.kind() {
70-
ty::Array(element_ty, _) | ty::Slice(element_ty) => {
71-
// Only allow u8 and i8 arrays when layout computation fails
72-
// Other types are conservatively assumed to be misaligned
73-
match element_ty.kind() {
74-
ty::Uint(ty::UintTy::U8) | ty::Int(ty::IntTy::I8) => {
75-
// For u8 and i8, we know their alignment is 1
76-
Some(Align::from_bytes(1).unwrap())
77-
}
78-
_ => {
79-
// For other types, we cannot safely determine alignment
80-
// Conservatively return None to indicate potential misalignment
81-
None
82-
}
92+
ty::Array(elem_ty, _) | ty::Slice(elem_ty) => {
93+
// Try to obtain the element's layout; if we can, use its ABI align.
94+
match tcx.layout_of(typing_env.as_query_input(*elem_ty)) {
95+
Ok(layout) => Some(layout.align.abi),
96+
Err(_) => None, // stay conservative when even the element's layout is unknown
8397
}
8498
}
8599
_ => None,

tests/ui/packed/packed-array-const-generic.rs

Lines changed: 0 additions & 19 deletions
This file was deleted.
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
//! Borrowing from packed arrays with generic length:
2+
//! - allowed if ABI align([T]) == align(T) <= packed alignment
3+
//! - hard error (E0793) otherwise
4+
#![allow(dead_code, unused_variables, unused_mut)]
5+
use std::mem::MaybeUninit;
6+
use std::num::{NonZeroU8, NonZeroU16};
7+
8+
//
9+
// -------- PASS CASES --------
10+
//
11+
12+
mod pass_u8 {
13+
#[repr(C, packed)]
14+
pub struct PascalString<const N: usize> {
15+
len: u8,
16+
buf: [u8; N],
17+
}
18+
19+
pub fn bar<const N: usize>(s: &PascalString<N>) -> &str {
20+
// should NOT trigger E0793
21+
std::str::from_utf8(&s.buf[0..s.len as usize]).unwrap()
22+
}
23+
24+
pub fn run() {
25+
let p = PascalString::<10> { len: 3, buf: *b"abc\0\0\0\0\0\0\0" };
26+
let s = bar(&p);
27+
assert_eq!(s, "abc");
28+
}
29+
}
30+
31+
mod pass_i8 {
32+
#[repr(C, packed)]
33+
pub struct S<const N: usize> {
34+
buf: [i8; N],
35+
}
36+
pub fn run() {
37+
let s = S::<4> { buf: [1, 2, 3, 4] };
38+
let _ok = &s.buf[..]; // no E0793
39+
}
40+
}
41+
42+
mod pass_nonzero_u8 {
43+
use super::*;
44+
#[repr(C, packed)]
45+
pub struct S<const N: usize> {
46+
buf: [NonZeroU8; N],
47+
}
48+
pub fn run() {
49+
let s = S::<3> {
50+
buf: [
51+
NonZeroU8::new(1).unwrap(),
52+
NonZeroU8::new(2).unwrap(),
53+
NonZeroU8::new(3).unwrap(),
54+
],
55+
};
56+
let _ok = &s.buf[..]; // no E0793
57+
}
58+
}
59+
60+
mod pass_maybeuninit_u8 {
61+
use super::*;
62+
#[repr(C, packed)]
63+
pub struct S<const N: usize> {
64+
buf: [MaybeUninit<u8>; N],
65+
}
66+
pub fn run() {
67+
let s = S::<2> { buf: [MaybeUninit::new(1), MaybeUninit::new(2)] };
68+
let _ok = &s.buf[..]; // no E0793
69+
}
70+
}
71+
72+
mod pass_transparent_u8 {
73+
#[repr(transparent)]
74+
pub struct WrapU8(u8);
75+
76+
#[repr(C, packed)]
77+
pub struct S<const N: usize> {
78+
buf: [WrapU8; N],
79+
}
80+
pub fn run() {
81+
let s = S::<2> { buf: [WrapU8(1), WrapU8(2)] };
82+
let _ok = &s.buf[..]; // no E0793
83+
}
84+
}
85+
86+
//
87+
// -------- FAIL CASES (expect E0793) --------
88+
//
89+
90+
mod fail_u16 {
91+
#[repr(C, packed)]
92+
pub struct S<const N: usize> {
93+
buf: [u16; N],
94+
}
95+
pub fn run() {
96+
let s = S::<2> { buf: [1, 2] };
97+
let _err = &s.buf[..];
98+
//~^ ERROR: reference to packed field is unaligned
99+
}
100+
}
101+
102+
mod fail_nonzero_u16 {
103+
use super::*;
104+
#[repr(C, packed)]
105+
pub struct S<const N: usize> {
106+
buf: [NonZeroU16; N],
107+
}
108+
pub fn run() {
109+
let s = S::<1> { buf: [NonZeroU16::new(1).unwrap()] };
110+
let _err = &s.buf[..];
111+
//~^ ERROR: reference to packed field is unaligned
112+
}
113+
}
114+
115+
mod fail_transparent_u16 {
116+
#[repr(transparent)]
117+
pub struct WrapU16(u16);
118+
119+
#[repr(C, packed)]
120+
pub struct S<const N: usize> {
121+
buf: [WrapU16; N],
122+
}
123+
pub fn run() {
124+
let s = S::<1> { buf: [WrapU16(42)] };
125+
let _err = &s.buf[..];
126+
//~^ ERROR: reference to packed field is unaligned
127+
}
128+
}
129+
130+
fn main() {
131+
// Run pass cases (fail cases only check diagnostics)
132+
pass_u8::run();
133+
pass_i8::run();
134+
pass_nonzero_u8::run();
135+
pass_maybeuninit_u8::run();
136+
pass_transparent_u8::run();
137+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error[E0793]: reference to packed field is unaligned
2+
--> $DIR/packed-array-generic-length.rs:97:21
3+
|
4+
LL | let _err = &s.buf[..];
5+
| ^^^^^
6+
|
7+
= note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
8+
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
9+
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
10+
11+
error[E0793]: reference to packed field is unaligned
12+
--> $DIR/packed-array-generic-length.rs:110:21
13+
|
14+
LL | let _err = &s.buf[..];
15+
| ^^^^^
16+
|
17+
= note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
18+
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
19+
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
20+
21+
error[E0793]: reference to packed field is unaligned
22+
--> $DIR/packed-array-generic-length.rs:125:21
23+
|
24+
LL | let _err = &s.buf[..];
25+
| ^^^^^
26+
|
27+
= note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
28+
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
29+
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
30+
31+
error: aborting due to 3 previous errors
32+
33+
For more information about this error, try `rustc --explain E0793`.

tests/ui/packed/packed-array-i8-const-generic.rs

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)