blob: 4cf724bebd4bcccd97a0792f19acecc660d5c3a3 [file]
// Copyright 2026 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::collections::HashMap;
use proc_macro::TokenStream;
use proc_macro2::{Literal, Span, TokenStream as TokenStream2};
use quote::{ToTokens, format_ident, quote};
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::spanned::Spanned;
use syn::{
Attribute, Expr, ExprLit, ExprRange, Fields, Ident, ItemStruct, Lit, Pat, PatIdent,
RangeLimits, Stmt, Token, Type, braced, parse_macro_input,
};
#[proc_macro_attribute]
pub fn bitfield_repr(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr: TokenStream2 = attr.into();
let item: TokenStream2 = item.into();
quote! {
#[repr(#attr)]
#[derive(
Debug,
Eq,
PartialEq,
::bitrs::__zerocopy::Immutable,
::bitrs::__zerocopy::IntoBytes,
::bitrs::__zerocopy::TryFromBytes,
)]
#item
}
.into()
}
#[proc_macro]
pub fn layout(item: TokenStream) -> TokenStream {
parse_macro_input!(item as Layout).to_token_stream().into()
}
//
// Parsing of the bitrs type.
//
enum BaseType {
U8,
U16,
U32,
U64,
U128,
}
impl BaseType {
const fn high_bit(&self) -> usize {
match *self {
Self::U8 => (u8::BITS - 1) as usize,
Self::U16 => (u16::BITS - 1) as usize,
Self::U32 => (u32::BITS - 1) as usize,
Self::U64 => (u64::BITS - 1) as usize,
Self::U128 => (u128::BITS - 1) as usize,
}
}
}
struct BaseTypeDef {
def: Type,
ty: BaseType,
}
impl TryFrom<Type> for BaseTypeDef {
type Error = Error;
fn try_from(type_def: Type) -> Result<Self> {
const INVALID_BASE_TYPE: &str = "base type must be an unsigned integral type";
let Type::Path(ref path_ty) = type_def else {
return Err(Error::new_spanned(type_def, INVALID_BASE_TYPE));
};
let path = &path_ty.path;
let ty = if path.is_ident("u8") {
BaseType::U8
} else if path.is_ident("u16") {
BaseType::U16
} else if path.is_ident("u32") {
BaseType::U32
} else if path.is_ident("u64") {
BaseType::U64
} else if path.is_ident("u128") {
BaseType::U128
} else {
return Err(Error::new_spanned(path, INVALID_BASE_TYPE));
};
Ok(Self { def: type_def, ty })
}
}
struct TypeDef {
def: ItemStruct,
base: BaseTypeDef,
}
impl Parse for TypeDef {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let strct: ItemStruct = input.parse()?;
// Check for any redundant derives; all other derives are forwarded.
for attr in &strct.attrs {
if attr.path().is_ident("derive") {
attr.parse_nested_meta(|meta| {
for t in &["Copy", "Clone", "Debug", "Default", "Eq", "PartialEq"] {
if meta.path.is_ident(t) {
return Err(Error::new_spanned(
meta.path,
format!("layout! already derives {t}"),
));
}
}
Ok(())
})?;
continue;
}
}
let base_type = if let Fields::Unnamed(fields) = &strct.fields {
if fields.unnamed.is_empty() {
return Err(Error::new_spanned(&fields.unnamed, "no base type provided"));
}
if fields.unnamed.len() > 1 {
return Err(Error::new_spanned(
&fields.unnamed,
"too many tuple fields; only the base type should be provided",
));
}
BaseTypeDef::try_from(fields.unnamed.first().unwrap().ty.clone())?
} else {
return Err(Error::new_spanned(
&strct.fields,
"bitrs type must be defined as a tuple struct",
));
};
if !strct.generics.params.is_empty() {
return Err(Error::new_spanned(
&strct.generics,
"generic parameters are not supported",
));
}
if let Some(where_clause) = &strct.generics.where_clause {
return Err(Error::new_spanned(where_clause, "generic parameters are not supported"));
}
Ok(Self { def: strct, base: base_type })
}
}
//
// Parsing and binding for an individual bitfield.
//
struct Bitfield {
span: Span,
name: Option<Ident>,
high_bit: usize,
low_bit: usize,
repr: Option<Type>,
doc_attrs: Vec<Attribute>,
unshifted: bool,
default: Option<Box<Expr>>,
}
impl Bitfield {
const fn is_reserved(&self) -> bool {
self.name.is_none()
}
fn display_name(&self) -> String {
match &self.name {
Some(name) => format!("`{name}`"),
None => "reserved".to_string(),
}
}
fn display_kind(&self) -> &'static str {
if self.bit_width() == 1 { "bit" } else { "field" }
}
fn display_range(&self) -> String {
if self.bit_width() == 1 {
format!("{}", self.low_bit)
} else {
format!("[{}:{}]", self.high_bit, self.low_bit)
}
}
const fn bit_width(&self) -> usize {
self.high_bit - self.low_bit + 1
}
fn minimum_width_integral_type(&self) -> TokenStream2 {
match self.bit_width() {
2..=8 => quote! {u8},
9..=16 => quote! {u16},
17..=32 => quote! {u32},
33..=64 => quote! {u64},
65..=128 => quote! {u128},
width => panic!("unexpected integral bit width: {width}"),
}
}
fn getter_and_setter(&self, ty: &TypeDef) -> TokenStream2 {
debug_assert!(!self.is_reserved());
if self.unshifted {
debug_assert!(self.repr.is_none());
}
let doc_attrs = &self.doc_attrs;
let name = self.name.as_ref().unwrap();
let setter_name = format_ident!("set_{}", name);
let type_name = &ty.def.ident;
let base_type = &ty.base.def;
let high_bit = self.high_bit;
let low_bit = self.low_bit;
let shifted = !self.unshifted;
let (get_doc, set_doc) = {
let range = self.display_range();
let qualifier = if shifted { "" } else { "unshifted " };
(
format!("The {qualifier}value of `{type_name}{range}`."),
format!("Sets the {qualifier}value of `{type_name}{range}`."),
)
};
if self.bit_width() == 1 && shifted {
return quote! {
#(#doc_attrs)*
#[doc = #get_doc]
#[inline]
pub const fn #name(&self) -> bool {
::bitrs::get_bit!(self.0, #low_bit)
}
#(#doc_attrs)*
#[doc = #set_doc]
#[inline]
pub const fn #setter_name(&mut self, value: bool) -> &mut Self {
::bitrs::set_bit!(self.0, #low_bit, value);
self
}
};
}
let clamped_type: TokenStream2 = if shifted {
self.minimum_width_integral_type()
} else {
quote! { #base_type }
};
let get_clamped = quote! {
::bitrs::get_field!(
#base_type,
#clamped_type,
#high_bit,
#low_bit,
#shifted,
self.0
)
};
let set_clamped = quote! {
::bitrs::set_field!(
#base_type,
#high_bit,
#low_bit,
#shifted,
self.0,
value
)
};
// TODO: Custom-repr getter/setter can't be `const fn` until
// `zerocopy::TryFromBytes` / `IntoBytes` have const methods.
// Tracking upstream: https://github.com/google/zerocopy/issues/115.
let getter = if let Some(repr) = &self.repr {
let try_name = format_ident!("try_{}", name);
let try_get_doc = format!("Fallible variant of [`Self::{name}`].");
let panic_doc = format!(
"# Panics\n\n\
Panics if the field's bit pattern is not a valid value of \
the custom representation. Use [`Self::{try_name}`] to \
recover from invalid bit patterns."
);
quote! {
#(#doc_attrs)*
#[doc = #get_doc]
///
#[doc = #panic_doc]
#[inline]
pub fn #name(&self) -> #repr
where
#repr: ::bitrs::__zerocopy::TryFromBytes,
{
self.#try_name().unwrap()
}
#[doc = #try_get_doc]
#[inline]
pub fn #try_name(&self)
-> ::core::result::Result<#repr, ::bitrs::InvalidBits<#clamped_type>>
where
#repr: ::bitrs::__zerocopy::TryFromBytes,
{
use ::bitrs::__zerocopy::IntoBytes;
use ::bitrs::__zerocopy::TryFromBytes;
let value = #get_clamped ;
#repr::try_read_from_bytes(value.as_bytes())
.map_err(|_| ::bitrs::InvalidBits(value))
}
}
} else {
quote! {
#(#doc_attrs)*
#[doc = #get_doc]
#[inline]
pub const fn #name(&self) -> #clamped_type {
#get_clamped
}
}
};
let setter = if let Some(repr) = &self.repr {
quote! {
#[doc = #set_doc]
#[inline]
pub fn #setter_name(&mut self, value: #repr) -> &mut Self
where
#repr: ::bitrs::__zerocopy::IntoBytes + ::bitrs::__zerocopy::Immutable
{
use ::bitrs::__zerocopy::IntoBytes;
use ::bitrs::__zerocopy::FromBytes;
const { assert!(::core::mem::size_of::<#repr>() == ::core::mem::size_of::<#clamped_type>()) }
let value = #clamped_type::read_from_bytes(value.as_bytes()).unwrap() as #base_type;
#set_clamped ;
self
}
}
} else {
quote! {
#[doc = #set_doc]
#[inline]
pub const fn #setter_name(&mut self, value: #clamped_type) -> &mut Self {
let value = value as #base_type;
#set_clamped ;
self
}
}
};
quote! {
#getter
#setter
}
}
}
impl Parse for Bitfield {
fn parse(input: ParseStream<'_>) -> Result<Self> {
const INVALID_BITFIELD_DECL_FORM: &str = "bitfield declaration should take one of the following forms:\n\
* `let $name @ $bit (= $default)?;`\n\
* `let $name @ $high..$low (: $repr)? (= $default)?;`\n\
* `let __ @ $bit (= $value)?;`\n\
* `let __ @ $high..$low (= $value)?;`";
let err = |spanned: &dyn ToTokens| Error::new_spanned(spanned, INVALID_BITFIELD_DECL_FORM);
let wildcard_err = |spanned: &dyn ToTokens| {
Error::new_spanned(
spanned,
"use `__` for reserved fields; bare `_` is not a valid \
identifier in this position",
)
};
// `_ @ ...` is not valid Rust grammar (`@` requires an identifier on
// its LHS), so syn would fail with a confusing "expected `;`" pointing
// at `@`. Intercept that sequence here to issue the right error. Look
// past any leading attributes (`#[unshifted]`, doc comments) on a fork
// so the diagnostic still fires when the let is attribute-prefixed.
let fork = input.fork();
let _ = fork.call(Attribute::parse_outer)?;
if fork.peek(Token![let]) && fork.peek2(Token![_]) && fork.peek3(Token![@]) {
let _: Vec<Attribute> = input.call(Attribute::parse_outer)?;
let _: Token![let] = input.parse()?;
let underscore: Token![_] = input.parse()?;
return Err(wildcard_err(&underscore));
}
let stmt = input.parse::<Stmt>()?;
let Stmt::Local(ref local) = stmt else {
return Err(err(&stmt));
};
let mut doc_attrs = Vec::new();
let mut unshifted = false;
for attr in &local.attrs {
if attr.path().is_ident("doc") {
doc_attrs.push(attr.clone());
} else if attr.path().is_ident("unshifted") {
if unshifted {
return Err(Error::new_spanned(attr, "duplicate `#[unshifted]` attribute"));
}
unshifted = true;
} else {
return Err(Error::new_spanned(
attr,
"attributes are not permitted on individual fields",
));
}
}
// syn parks the `: Type` annotation in a `Pat::Type` wrapping the
// inner pattern, so a custom repr surfaces here rather than on the
// local itself.
let (pat_ident, repr): (&PatIdent, Option<Type>) = match &local.pat {
// `let foo @ ...;`
Pat::Ident(binding) => (binding, None),
Pat::Type(typed) => match &*typed.pat {
// `let foo @ ...: Repr;`
Pat::Ident(binding) => (binding, Some((*typed.ty).clone())),
// `let _: T ...;`
Pat::Wild(_) => return Err(wildcard_err(&typed.pat)),
_ => return Err(err(&local.pat)),
},
// `let _ = ...;` or `let _;`
Pat::Wild(_) => return Err(wildcard_err(&local.pat)),
_ => return Err(err(&local.pat)),
};
if let Some(by_ref) = &pat_ident.by_ref {
return Err(Error::new_spanned(
by_ref,
"`ref` is not permitted on bitfield declarations",
));
}
if let Some(mutability) = &pat_ident.mutability {
return Err(Error::new_spanned(
mutability,
"`mut` is not permitted on bitfield declarations",
));
}
let ident_str = pat_ident.ident.to_string();
let name: Option<Ident> = if ident_str == "__" {
None
} else if ident_str.starts_with('_') {
return Err(Error::new_spanned(
&pat_ident.ident,
"leading-underscore identifiers are not permitted on \
bitfields; use `__` for reserved fields, or rename to a \
non-`_`-prefixed identifier",
));
} else {
Some(pat_ident.ident.clone())
};
let Some((_, subpat)) = &pat_ident.subpat else {
return Err(Error::new_spanned(local, "missing `@ BIT_RANGE` in bitfield declaration"));
};
let int_lit_from_expr = |e: &Expr| -> Result<usize> {
match e {
Expr::Lit(ExprLit { lit: Lit::Int(i), .. }) => i.base10_parse(),
_ => Err(err(e)),
}
};
let (high_bit, low_bit) = match &**subpat {
Pat::Lit(ExprLit { lit: Lit::Int(i), .. }) => {
let n: usize = i.base10_parse()?;
(n, n)
}
Pat::Range(range @ ExprRange { start, end, limits, .. }) => {
if let RangeLimits::Closed(eq) = limits {
return Err(Error::new_spanned(
eq,
"bit ranges use the exclusive `..` token, but both \
endpoints are treated as inclusive bit indices; \
write `H..L`, not `H..=L`",
));
}
let (Some(start), Some(end)) = (start, end) else {
return Err(Error::new_spanned(range, "bit range requires both endpoints"));
};
let high = int_lit_from_expr(start)?;
let low = int_lit_from_expr(end)?;
if high < low {
return Err(Error::new_spanned(range, "first high bit, then low"));
}
(high, low)
}
_ => return Err(err(subpat)),
};
let default_or_value = if let Some(ref init) = local.init {
if init.diverge.is_some() {
return Err(err(local));
}
Some(init.expr.clone())
} else {
None
};
if !doc_attrs.is_empty() && name.is_none() {
return Err(Error::new_spanned(
&doc_attrs[0],
"doc comments are not permitted on reserved fields",
));
}
if let Some(repr) = &repr {
if name.is_none() {
return Err(Error::new_spanned(
repr,
"custom representations are not permitted for reserved fields",
));
}
if high_bit == low_bit {
return Err(Error::new_spanned(
repr,
"custom representations are not permitted for bits",
));
}
}
if unshifted && name.is_none() {
return Err(Error::new_spanned(
&local.pat,
"`#[unshifted]` is not permitted on reserved fields",
));
}
if unshifted && repr.is_some() {
return Err(Error::new_spanned(
repr,
"`#[unshifted]` is not permitted on fields with custom representations",
));
}
Ok(Bitfield {
span: stmt.span(),
name,
high_bit,
low_bit,
repr,
doc_attrs,
unshifted,
default: default_or_value,
})
}
}
struct Layout {
ty: TypeDef,
named: Vec<Bitfield>,
reserved: Vec<Bitfield>,
}
impl Layout {
fn constants(&self) -> TokenStream2 {
let base = &self.ty.base.def;
let mut field_constants = Vec::new();
let mut field_metadata = Vec::new();
let mut checks = Vec::new();
let mut default_stmts = Vec::new();
for field in &self.named {
let name_lower = field.name.as_ref().unwrap().to_string();
let name_upper = name_lower.to_uppercase();
let high_bit = field.high_bit;
let low_bit = Literal::usize_unsuffixed(field.low_bit);
let shifted_mask = quote! {::bitrs::shifted_mask!(#base, #high_bit, #low_bit) };
let mask_name = format_ident!("{name_upper}_MASK");
let mask_doc = format!("Unshifted bitmask of `{name_lower}`.");
let shift_name = format_ident!("{name_upper}_SHIFT");
let shift_doc = format!("Bit shift (i.e., the low bit) of `{name_lower}`.");
field_constants.push(quote! {
#[doc = #mask_doc]
pub const #mask_name: #base = (#shifted_mask << #low_bit);
#[doc = #shift_doc]
pub const #shift_name: usize = #low_bit;
});
if let Some(default) = &field.default {
let default_name = format_ident!("{name_upper}_DEFAULT");
let doc = format!("Pre-shifted default value of the `{name_lower}` field.",);
field_constants.push(quote! {
#[doc = #doc]
pub const #default_name: #base = ((#default) as #base) << #low_bit;
});
checks.push(quote! {
const { assert!(((#default) as #base) << #low_bit & !(#shifted_mask << #low_bit) == 0) }
});
default_stmts.push(quote! {
{ v |= Self::#default_name; }
});
}
let high_bit = Literal::usize_unsuffixed(field.high_bit);
let default = if let Some(default) = &field.default {
quote! { #default }
} else {
quote! { 0 }
};
field_metadata.push(quote! {
::bitrs::FieldMetadata::<#base>{
name: #name_lower,
high_bit: #high_bit,
low_bit: #low_bit,
default: #default as #base,
},
});
}
let num_fields = self.named.len();
let mut rsvd1_stmts = Vec::new();
let mut rsvd0_stmts = Vec::new();
for rsvd in &self.reserved {
let rsvd_value = rsvd.default.as_ref().unwrap();
let high_bit = rsvd.high_bit;
let low_bit = Literal::usize_unsuffixed(rsvd.low_bit);
let shifted_mask = quote! {::bitrs::shifted_mask!(#base, #high_bit, #low_bit) };
let name = format_ident!("RSVD_{}_{}", rsvd.high_bit, rsvd.low_bit);
field_constants.push(quote! {
const #name: #base = (#rsvd_value as #base) << #low_bit;
});
checks.push(quote! {
const { assert!((#rsvd_value as #base) << #low_bit & !(#shifted_mask << #low_bit) == 0) }
});
rsvd1_stmts.push(quote! {
{ v |= Self::#name; }
});
rsvd0_stmts.push(quote! {
{ v |= !Self::#name & (#shifted_mask << #low_bit); }
});
}
field_constants.push(quote! {
#[doc(hidden)]
const NUM_FIELDS: usize = #num_fields;
/// Metadata of all named fields in the layout.
pub const FIELDS: [::bitrs::FieldMetadata::<#base>; #num_fields] = [
#(#field_metadata)*
];
});
let check_fn = if checks.is_empty() {
quote! {}
} else {
let checks = checks.into_iter();
quote! {
#[forbid(overflowing_literals)]
const fn check_defaults() -> () {
#(#checks)*
}
}
};
quote! {
/// Mask of all reserved-as-1 bits.
pub const RSVD1_MASK: #base = {
let mut v: #base = 0;
#(#rsvd1_stmts)*
v
};
/// Mask of all reserved-as-0 bits.
pub const RSVD0_MASK: #base = {
let mut v: #base = 0;
#(#rsvd0_stmts)*
v
};
/// The default value of the layout, combining all field
/// defaults and reserved-as values.
pub const DEFAULT: #base = {
let mut v: #base = Self::RSVD1_MASK;
#(#default_stmts)*
v
};
#(#field_constants)*
#check_fn
}
}
fn iter_impl(&self) -> TokenStream2 {
let ty = &self.ty.def.ident;
let base = &self.ty.base.def;
let iter_type = format_ident!("{}Iter", ty);
let vis = &self.ty.def.vis;
quote! {
#[doc(hidden)]
#vis struct #iter_type(#base, usize, usize);
impl ::core::iter::Iterator for #iter_type {
type Item = (&'static ::bitrs::FieldMetadata<#base>, #base);
fn next(&mut self) -> Option<Self::Item> {
if self.1 >= self.2 {
return None;
}
let metadata = &#ty::FIELDS[self.1];
let shifted_mask = (1 << (metadata.high_bit - metadata.low_bit + 1)) - 1;
let value = (self.0 >> metadata.low_bit) & shifted_mask;
self.1 += 1;
Some((metadata, value))
}
}
impl ::core::iter::DoubleEndedIterator for #iter_type {
fn next_back(&mut self) -> Option<Self::Item> {
if self.1 >= self.2 {
return None;
}
self.2 -= 1;
let metadata = &#ty::FIELDS[self.2];
let shifted_mask = (1 << (metadata.high_bit - metadata.low_bit + 1)) - 1;
let value = (self.0 >> metadata.low_bit) & shifted_mask;
Some((metadata, value))
}
}
impl #ty {
/// Returns an iterator over
/// ([metadata][`bitrs::FieldMetadata`], value) pairs for each
/// field.
pub fn iter(&self) -> #iter_type {
#iter_type(self.0, 0, Self::NUM_FIELDS)
}
}
impl ::core::iter::IntoIterator for #ty {
type Item = (&'static ::bitrs::FieldMetadata<#base>, #base);
type IntoIter = #iter_type;
fn into_iter(self) -> Self::IntoIter { #iter_type(self.0, 0, Self::NUM_FIELDS) }
}
impl<'a> ::core::iter::IntoIterator for &'a #ty {
type Item = (&'static ::bitrs::FieldMetadata<#base>, #base);
type IntoIter = #iter_type;
fn into_iter(self) -> Self::IntoIter { #iter_type(self.0, 0, #ty::NUM_FIELDS) }
}
}
}
fn getters_and_setters(&self) -> impl Iterator<Item = TokenStream2> + '_ {
self.named.iter().map(|field| field.getter_and_setter(&self.ty))
}
fn fmt_fn(&self, integral_specifier: &str) -> TokenStream2 {
let ty_str = &self.ty.def.ident.to_string();
let mut custom_repr_fields =
self.named.iter().filter(|field| field.repr.is_some()).peekable();
let where_clause = if custom_repr_fields.peek().is_some() {
let bounds = custom_repr_fields.map(|field| {
let repr = field.repr.as_ref().unwrap();
quote! {#repr: ::core::fmt::Debug,}
});
quote! {
where
#(#bounds)*
}
} else {
quote! {}
};
let fmt_fields = self.named.iter().map(|field| {
let name = &field.name;
let name_str = name.as_ref().unwrap().to_string();
let default_specifier = if field.bit_width() == 1 { "" } else { integral_specifier };
if field.repr.is_some() {
let try_name = format_ident!("try_{}", name.as_ref().unwrap());
let ok_format_string = format!("{{indent}}{name_str}: {{:#?}},{{sep}}");
let ok_format_string = Literal::string(&ok_format_string);
let err_format_string =
format!("{{indent}}{name_str}: InvalidBits({{{default_specifier}}}),{{sep}}");
let err_format_string = Literal::string(&err_format_string);
quote! {
{
match self.#try_name() {
Ok(value) => write!(f, #ok_format_string, value),
Err(invalid) => write!(f, #err_format_string, invalid.0),
}?;
}
}
} else {
let format_string =
format!("{{indent}}{name_str}: {{{default_specifier}}},{{sep}}");
let format_string = Literal::string(&format_string);
quote! {
{ write!(f, #format_string, self.#name())?; }
}
}
});
quote! {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result
#where_clause
{
let (sep, indent) = if f.alternate() {
('\n', " ")
} else {
(' ', "")
};
write!(f, "{} {{{sep}", #ty_str)?;
#(#fmt_fields)*
write!(f, "}}")
}
}
}
fn fmt_impls(&self) -> TokenStream2 {
let ty = &self.ty.def.ident;
let lower_hex_fmt = self.fmt_fn(":#x");
let upper_hex_fmt = self.fmt_fn(":#X");
let binary_fmt = self.fmt_fn(":#b");
let octal_fmt = self.fmt_fn(":#o");
quote! {
impl ::core::fmt::Debug for #ty {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
::core::fmt::LowerHex::fmt(self, f)
}
}
impl ::core::fmt::Binary for #ty {
#binary_fmt
}
impl ::core::fmt::LowerHex for #ty {
#lower_hex_fmt
}
impl ::core::fmt::UpperHex for #ty {
#upper_hex_fmt
}
impl ::core::fmt::Octal for #ty {
#octal_fmt
}
}
}
}
impl Layout {
/// Builds a `Layout` from a pre-parsed type and a flat field list. Runs
/// the within-layout invariants (sort, overlap check, high-bit bound),
/// returning the first violation as an error, then splits fields into
/// named/reserved.
fn from_parts(ty: TypeDef, mut fields: Vec<Bitfield>) -> Result<Self> {
fields.sort_by_key(|field| field.low_bit);
// TODO(https://github.com/rust-lang/rust/issues/54725): For the
// overlap diagnostic, it would be nice to Span::join() the two
// spans, but that's still experimental.
let mut seen: HashMap<String, &Bitfield> = HashMap::new();
let mut prev: Option<&Bitfield> = None;
for field in &fields {
if let Some(prev) = prev
&& prev.high_bit >= field.low_bit
{
return Err(Error::new(
field.span,
format!(
"{} ({} {}) overlaps with {} ({} {})",
field.display_name(),
field.display_kind(),
field.display_range(),
prev.display_name(),
prev.display_kind(),
prev.display_range(),
),
));
}
if let Some(name) = &field.name {
let key = name.to_string();
if let Some(prev_named) = seen.get(&key) {
return Err(Error::new(
field.span,
format!(
"field `{key}` declared twice ({} {} and {} {})",
prev_named.display_kind(),
prev_named.display_range(),
field.display_kind(),
field.display_range(),
),
));
}
seen.insert(key, field);
}
prev = Some(field);
}
let highest_possible = ty.base.ty.high_bit();
if let Some(highest) = fields.last()
&& highest.high_bit > highest_possible
{
return Err(Error::new(
highest.span,
format!(
"high bit {} exceeds the highest possible value \
of {highest_possible}",
highest.high_bit
),
));
}
// Drain in reverse so `named` and `reserved` end up in descending
// bit order (high bit first). The codegen doesn't depend on a
// specific order, but the metadata does, which is downstream of this
// ordering.
let mut layout = Self { ty, named: vec![], reserved: vec![] };
for field in fields.into_iter().rev() {
if field.is_reserved() {
if field.default.is_some() {
layout.reserved.push(field);
}
} else {
layout.named.push(field);
}
}
Ok(layout)
}
}
impl Parse for Layout {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let input = {
let content;
braced!(content in input);
content
};
let ty = input.parse::<TypeDef>()?;
let inner = {
let content;
braced!(content in input);
content
};
let mut fields = Vec::new();
while !inner.is_empty() {
fields.push(inner.parse::<Bitfield>()?);
}
Self::from_parts(ty, fields)
}
}
impl ToTokens for Layout {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let type_def = &self.ty.def;
let type_name = &type_def.ident;
let base = &self.ty.base.def;
let constants = self.constants();
let getters_and_setters = self.getters_and_setters();
let iter_impl = self.iter_impl();
let fmt_impls = self.fmt_impls();
quote! {
#[derive(Copy, Clone, Eq, PartialEq)]
#type_def
impl #type_name {
#constants
/// Creates a new instance with reserved-as-1 bits set and
/// all other bits zeroed (i.e., with a value of
/// [`Self::RSVD1_MASK`]).
pub const fn new() -> Self {
Self(Self::RSVD1_MASK)
}
pub const fn bits(self) -> #base {
self.0
}
#(#getters_and_setters)*
}
impl ::core::default::Default for #type_name {
/// Returns an instance with the default bits set (i.e,. with a
/// value of [`Self::DEFAULT`].
fn default() -> Self {
Self(Self::DEFAULT)
}
}
impl ::core::convert::From<#base> for #type_name {
// `RSVD{0,1}_MASK` may be zero, in which case the following
// mask conditions might be trivially true.
#[allow(clippy::bad_bit_mask)]
fn from(value: #base) -> Self {
debug_assert!(
value & Self::RSVD1_MASK == Self::RSVD1_MASK,
"from(): Invalid base value ({value:#x}) has reserved-as-1 bits ({:#x}) unset",
Self::RSVD1_MASK,
);
debug_assert!(
!value & Self::RSVD0_MASK == Self::RSVD0_MASK,
"from(): Invalid base value ({value:#x}) has reserved-as-0 bits ({:#x}) set",
Self::RSVD0_MASK,
);
Self(value)
}
}
impl ::core::convert::From<#type_name> for #base {
fn from(value: #type_name) -> Self {
value.bits()
}
}
#iter_impl
#fmt_impls
}
.to_tokens(tokens);
}
}