fix: Handle multiple #[repr(..)] attrs correctly

This commit is contained in:
Shoyu Vanilla 2025-03-22 18:57:37 +09:00
parent 32fa60f3a6
commit 602b4f37b9
3 changed files with 91 additions and 71 deletions

View file

@ -233,7 +233,12 @@ impl Attrs {
} }
pub fn repr(&self) -> Option<ReprOptions> { pub fn repr(&self) -> Option<ReprOptions> {
self.by_key(&sym::repr).tt_values().find_map(parse_repr_tt) self.by_key(&sym::repr).tt_values().filter_map(parse_repr_tt).fold(None, |acc, repr| {
acc.map_or(Some(repr), |mut acc| {
merge_repr(&mut acc, repr);
Some(acc)
})
})
} }
} }
@ -260,6 +265,19 @@ fn parse_rustc_legacy_const_generics(tt: &crate::tt::TopSubtree) -> Box<[u32]> {
indices.into_boxed_slice() indices.into_boxed_slice()
} }
fn merge_repr(this: &mut ReprOptions, other: ReprOptions) {
let ReprOptions { int, align, pack, flags, field_shuffle_seed: _ } = this;
flags.insert(other.flags);
*align = (*align).max(other.align);
*pack = match (*pack, other.pack) {
(Some(pack), None) | (None, Some(pack)) => Some(pack),
_ => (*pack).min(other.pack),
};
if other.int.is_some() {
*int = other.int;
}
}
fn parse_repr_tt(tt: &crate::tt::TopSubtree) -> Option<ReprOptions> { fn parse_repr_tt(tt: &crate::tt::TopSubtree) -> Option<ReprOptions> {
use crate::builtin_type::{BuiltinInt, BuiltinUint}; use crate::builtin_type::{BuiltinInt, BuiltinUint};
use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
@ -269,83 +287,76 @@ fn parse_repr_tt(tt: &crate::tt::TopSubtree) -> Option<ReprOptions> {
_ => return None, _ => return None,
} }
let mut flags = ReprFlags::empty(); let mut acc = ReprOptions::default();
let mut int = None;
let mut max_align: Option<Align> = None;
let mut min_pack: Option<Align> = None;
let mut tts = tt.iter(); let mut tts = tt.iter();
while let Some(tt) = tts.next() { while let Some(tt) = tts.next() {
if let TtElement::Leaf(tt::Leaf::Ident(ident)) = tt { let TtElement::Leaf(tt::Leaf::Ident(ident)) = tt else {
flags.insert(match &ident.sym { continue;
s if *s == sym::packed => { };
let pack = if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() { let repr = match &ident.sym {
tts.next(); s if *s == sym::packed => {
if let Some(TtElement::Leaf(tt::Leaf::Literal(lit))) = tt_iter.next() { let pack = if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() {
lit.symbol.as_str().parse().unwrap_or_default() tts.next();
} else { if let Some(TtElement::Leaf(tt::Leaf::Literal(lit))) = tt_iter.next() {
0 lit.symbol.as_str().parse().unwrap_or_default()
}
} else { } else {
0 0
}; }
let pack = Align::from_bytes(pack).unwrap_or(Align::ONE); } else {
min_pack = 0
Some(if let Some(min_pack) = min_pack { min_pack.min(pack) } else { pack }); };
ReprFlags::empty() let pack = Some(Align::from_bytes(pack).unwrap_or(Align::ONE));
} ReprOptions { pack, ..Default::default() }
s if *s == sym::align => { }
if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() { s if *s == sym::align => {
tts.next(); let mut align = None;
if let Some(TtElement::Leaf(tt::Leaf::Literal(lit))) = tt_iter.next() { if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() {
if let Ok(align) = lit.symbol.as_str().parse() { tts.next();
let align = Align::from_bytes(align).ok(); if let Some(TtElement::Leaf(tt::Leaf::Literal(lit))) = tt_iter.next() {
max_align = max_align.max(align); if let Ok(a) = lit.symbol.as_str().parse() {
} align = Align::from_bytes(a).ok();
} }
} }
ReprFlags::empty()
} }
s if *s == sym::C => ReprFlags::IS_C, ReprOptions { align, ..Default::default() }
s if *s == sym::transparent => ReprFlags::IS_TRANSPARENT, }
s if *s == sym::simd => ReprFlags::IS_SIMD, s if *s == sym::C => ReprOptions { flags: ReprFlags::IS_C, ..Default::default() },
repr => { s if *s == sym::transparent => {
if let Some(builtin) = BuiltinInt::from_suffix_sym(repr) ReprOptions { flags: ReprFlags::IS_TRANSPARENT, ..Default::default() }
.map(Either::Left) }
.or_else(|| BuiltinUint::from_suffix_sym(repr).map(Either::Right)) s if *s == sym::simd => ReprOptions { flags: ReprFlags::IS_SIMD, ..Default::default() },
{ repr => {
int = Some(match builtin { let mut int = None;
Either::Left(bi) => match bi { if let Some(builtin) = BuiltinInt::from_suffix_sym(repr)
BuiltinInt::Isize => IntegerType::Pointer(true), .map(Either::Left)
BuiltinInt::I8 => IntegerType::Fixed(Integer::I8, true), .or_else(|| BuiltinUint::from_suffix_sym(repr).map(Either::Right))
BuiltinInt::I16 => IntegerType::Fixed(Integer::I16, true), {
BuiltinInt::I32 => IntegerType::Fixed(Integer::I32, true), int = Some(match builtin {
BuiltinInt::I64 => IntegerType::Fixed(Integer::I64, true), Either::Left(bi) => match bi {
BuiltinInt::I128 => IntegerType::Fixed(Integer::I128, true), BuiltinInt::Isize => IntegerType::Pointer(true),
}, BuiltinInt::I8 => IntegerType::Fixed(Integer::I8, true),
Either::Right(bu) => match bu { BuiltinInt::I16 => IntegerType::Fixed(Integer::I16, true),
BuiltinUint::Usize => IntegerType::Pointer(false), BuiltinInt::I32 => IntegerType::Fixed(Integer::I32, true),
BuiltinUint::U8 => IntegerType::Fixed(Integer::I8, false), BuiltinInt::I64 => IntegerType::Fixed(Integer::I64, true),
BuiltinUint::U16 => IntegerType::Fixed(Integer::I16, false), BuiltinInt::I128 => IntegerType::Fixed(Integer::I128, true),
BuiltinUint::U32 => IntegerType::Fixed(Integer::I32, false), },
BuiltinUint::U64 => IntegerType::Fixed(Integer::I64, false), Either::Right(bu) => match bu {
BuiltinUint::U128 => IntegerType::Fixed(Integer::I128, false), BuiltinUint::Usize => IntegerType::Pointer(false),
}, BuiltinUint::U8 => IntegerType::Fixed(Integer::I8, false),
}); BuiltinUint::U16 => IntegerType::Fixed(Integer::I16, false),
} BuiltinUint::U32 => IntegerType::Fixed(Integer::I32, false),
ReprFlags::empty() BuiltinUint::U64 => IntegerType::Fixed(Integer::I64, false),
BuiltinUint::U128 => IntegerType::Fixed(Integer::I128, false),
},
});
} }
}) ReprOptions { int, ..Default::default() }
} }
};
merge_repr(&mut acc, repr);
} }
Some(ReprOptions { Some(acc)
int,
align: max_align,
pack: min_pack,
flags,
field_shuffle_seed: rustc_hashes::Hash64::ZERO,
})
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]

View file

@ -24,9 +24,6 @@ extern crate rustc_hashes;
#[cfg(not(feature = "in-rust-tree"))] #[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_abi as rustc_abi; extern crate ra_ap_rustc_abi as rustc_abi;
#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_hashes as rustc_hashes;
pub mod db; pub mod db;
pub mod attr; pub mod attr;

View file

@ -284,6 +284,18 @@ fn repr_packed() {
check_size_and_align("#[repr(Rust, packed(5))] struct Goal(i32);", "", 4, 1); check_size_and_align("#[repr(Rust, packed(5))] struct Goal(i32);", "", 4, 1);
} }
#[test]
fn multiple_repr_attrs() {
size_and_align!(
#[repr(C)]
#[repr(packed)]
struct Goal {
id: i32,
u: u8,
}
)
}
#[test] #[test]
fn generic() { fn generic() {
size_and_align! { size_and_align! {