[PATCH] rust: impl_flags: add conversions for raw flag representations
From: Filipe Xavier <hidden>
Date: 2026-09-12 16:55:04
Also in:
lkml
Subsystem:
rust, the rest · Maintainers:
Miguel Ojeda, Linus Torvalds
Extend the impl_flags! macro to support conversions between generated flag types and raw C/UAPI integers. Implement TryFrom<Repr> for individual flags (exact variant match) and flag sets (rejecting unknown bits), along with an unsafe from_raw() constructor for flag sets. Additionally, add BitOr and BitOrAssign implementations between the raw representation and flag types. Suggested-by: Daniel Almeida <daniel.almeida@collabora.com> Suggested-by: Andreas Hindborg <a.hindborg@kernel.org> Signed-off-by: Filipe Xavier <redacted> --- rust/kernel/impl_flags.rs | 103 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 91 insertions(+), 12 deletions(-)
diff --git a/rust/kernel/impl_flags.rs b/rust/kernel/impl_flags.rs
index fdf44d5eea9cb907f6d8d209792a1d9b74b55be6..b6cce7318766419efbe18d2915bd8f949c1c7c19 100644
--- a/rust/kernel/impl_flags.rs
+++ b/rust/kernel/impl_flags.rs@@ -14,6 +14,8 @@ /// - The struct and enum types with appropriate `#[repr]` attributes. /// - Implementations of common bitflag operators /// ([`::core::ops::BitOr`], [`::core::ops::BitAnd`], etc.). +/// - Conversions between the Rust-native types and their raw representation. +/// - Validation when converting raw values back into Rust-native types. /// - Utility methods such as `.contains()` to check flags. /// /// # Examples
@@ -68,6 +70,22 @@ /// let negated = !read_only; /// assert!(negated.contains(Permission::Write)); /// assert!(!negated.contains(Permission::Read)); +/// +/// // Convert individual flags and flag sets to their raw representation. +/// let raw: u32 = Permission::Read.into(); +/// assert_eq!(raw, 1); +/// let raw: u32 = read_write.into(); +/// +/// // Raw values can be validated before entering the Rust-native API. +/// assert_eq!(Permission::try_from(1), Ok(Permission::Read)); +/// assert!(Permission::try_from(3).is_err()); +/// assert!(Permissions::try_from(3).is_ok()); +/// +/// // Raw C/UAPI fields can be updated without an intermediate conversion. +/// let mut raw = 0u32; +/// raw |= Permission::Read; +/// raw |= Permission::Write; +/// assert_eq!(raw, 3); /// ``` #[macro_export] macro_rules! impl_flags {
@@ -103,6 +121,13 @@ fn from(value: $flag) -> Self { } } + impl ::core::convert::From<$flag> for $ty { + #[inline] + fn from(value: $flag) -> Self { + value as $ty + } + } + impl ::core::convert::From<$flags> for $ty { #[inline] fn from(value: $flags) -> Self {
@@ -110,32 +135,45 @@ fn from(value: $flags) -> Self { } } - impl ::core::ops::BitOr for $flags { - type Output = Self; + impl ::core::convert::TryFrom<$ty> for $flag { + type Error = ::kernel::error::Error; + #[inline] - fn bitor(self, rhs: Self) -> Self::Output { - Self(self.0 | rhs.0) + fn try_from(value: $ty) -> Result<Self, Self::Error> { + match value { + $( + v if v == ($value as $ty) => Ok($flag::$name), + )+ + _ => Err(::kernel::error::code::EINVAL), + } } } - impl ::core::ops::BitOrAssign for $flags { + impl ::core::convert::TryFrom<$ty> for $flags { + type Error = ::kernel::error::Error; + #[inline] - fn bitor_assign(&mut self, rhs: Self) { - *self = *self | rhs; + fn try_from(value: $ty) -> Result<Self, Self::Error> { + if value & !Self::all_bits() != 0 { + return Err(::kernel::error::code::EINVAL); + } + + // SAFETY: All bits set in `value` are valid flag bits. + Ok(unsafe { Self::from_raw(value) }) } } - impl ::core::ops::BitOr<$flag> for $flags { + impl ::core::ops::BitOr for $flags { type Output = Self; #[inline] - fn bitor(self, rhs: $flag) -> Self::Output { - self | Self::from(rhs) + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) } } - impl ::core::ops::BitOrAssign<$flag> for $flags { + impl ::core::ops::BitOrAssign for $flags { #[inline] - fn bitor_assign(&mut self, rhs: $flag) { + fn bitor_assign(&mut self, rhs: Self) { *self = *self | rhs; } }
@@ -155,6 +193,21 @@ fn bitand_assign(&mut self, rhs: Self) { } } + impl ::core::ops::BitOr<$flag> for $flags { + type Output = Self; + #[inline] + fn bitor(self, rhs: $flag) -> Self::Output { + self | Self::from(rhs) + } + } + + impl ::core::ops::BitOrAssign<$flag> for $flags { + #[inline] + fn bitor_assign(&mut self, rhs: $flag) { + *self = *self | rhs; + } + } + impl ::core::ops::BitAnd<$flag> for $flags { type Output = Self; #[inline]
@@ -240,6 +293,22 @@ fn not(self) -> Self::Output { } } + impl ::core::ops::BitOr<$flag> for $ty { + type Output = Self; + + #[inline] + fn bitor(self, rhs: $flag) -> Self::Output { + self | (rhs as $ty) + } + } + + impl ::core::ops::BitOrAssign<$flag> for $ty { + #[inline] + fn bitor_assign(&mut self, rhs: $flag) { + *self |= rhs as $ty; + } + } + impl $flags { /// Returns an empty instance where no flags are set. #[inline]
@@ -253,6 +322,16 @@ pub const fn all_bits() -> $ty { 0 $( | $value )+ } + /// Creates a flag set from its raw representation without validation. + /// + /// # Safety + /// + /// All bits set in `value` must correspond to valid flags. + #[inline] + pub const unsafe fn from_raw(value: $ty) -> Self { + Self(value) + } + /// Checks if a specific flag is set. #[inline] pub fn contains(self, flag: $flag) -> bool {
--- base-commit: 08df884136f1c1197bab2a27814404fd329d9aac change-id: 20260912-add-from-raw-conversions-4647d890bb77 Best regards, -- Filipe Xavier [off-list ref]