Re: [PATCH v3 10/16] rust: io: register: make register have a typed base
From: Alexandre Courbot
Date: Thu Aug 27 2026 - 09:46:37 EST
On Wed Aug 19, 2026 at 8:09 PM JST, Gary Guo wrote:
> Previously `register!` defined registers can be used on any untyped I/O
> regions. With all users specifying their desired register type now,
> propagate the specified type and restrict I/O access only when type
> matches.
>
> Also, add an `io_project!` example which is enabled by this change.
>
> Signed-off-by: Gary Guo <gary@xxxxxxxxxxx>
> ---
> rust/kernel/io.rs | 13 +++++++++++
> rust/kernel/io/register.rs | 55 ++++++++++++++++++++++++++++++++--------------
> rust/macros/io/register.rs | 34 ++++++++++++++--------------
> 3 files changed, 68 insertions(+), 34 deletions(-)
>
> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
> index 84dd876b3407..4542187d6b91 100644
> --- a/rust/kernel/io.rs
> +++ b/rust/kernel/io.rs
> @@ -1692,21 +1692,34 @@ pub fn project_loc<U, L>(self, location: L) -> <T::Backend as IoBackend>::View<'
> /// The syntax is of form `io_project!(io, proj)` where `io` is an expression to a type that
> /// implements [`Io`] and `proj` is a [projection specification](kernel::ptr::project!).
> ///
> +/// `io_project!` can also project to subview of registers defined with [`register!`] macro.
nit: "to a subview".
<...>
> +/// Helper function for register alias implementation.
> +///
> +/// This is used to enforce base matching. Only called during const eval.
> +#[doc(hidden)]
> +#[inline(always)]
> +pub const fn alias_offset<Base: ?Sized, Alias: Register<Base = Base>>() -> usize {
> + Alias::OFFSET
> +}
> +
> +/// Helper function for register element alias implementation.
> +///
> +/// This is used to enforce base matching and provide bounds checking. Only called during const
> +/// eval.
> +#[doc(hidden)]
> +#[inline(always)]
> +pub const fn element_alias_offset<Base: ?Sized, Alias: RegisterArray<Base = Base>>(
> + idx: usize,
> +) -> usize {
> + build_assert!(idx < Alias::SIZE);
> + Alias::OFFSET + idx * Alias::STRIDE
> +}
You could convert the `build_assert!` (always good to eschew) into a
`const_assert!` if you turn `idx` into a generic parameter:
pub const fn element_alias_offset<
Base: ?Sized,
Alias: RegisterArray<Base = Base>,
const IDX: usize,
>() -> usize {
crate::const_assert!(IDX < Alias::SIZE);
Alias::OFFSET + IDX * Alias::STRIDE
}
You get a better error message, the const parameter is used in a
very controlled environment well within the expressive power of const
generics, and that function is not public interface anyway.
If you don't like it (recent discussions make me think you might not
:)), then let's remove the `build_assert!` and keep the `static_assert`
emitted by the macro until this patch; it's not as elegant as checking
the condition into the same block of code that uses it, but again we are
in a controlled environment and static_assert > const_assert >
build_assert so this may actually be my preferred solution. You can
document the invariant in `element_alias_offset`.
> +
> /// Defines a dedicated type for a register, including getter and setter methods for its fields and
> /// methods to read and write it from an [`Io`](kernel::io::Io) region.
> ///
> diff --git a/rust/macros/io/register.rs b/rust/macros/io/register.rs
> index 2fb48e1be82d..cb02e850b23f 100644
> --- a/rust/macros/io/register.rs
> +++ b/rust/macros/io/register.rs
> @@ -140,21 +140,23 @@ fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
> }
>
> pub(crate) struct RegDef {
> - base: Option<Type>,
> + base: Type,
> regs: Vec<Reg>,
> }
>
> impl Parse for RegDef {
> fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
> - let base = if input.peek(kw::base) {
> - let _: kw::base = input.parse()?;
> - let _: Token![:] = input.parse()?;
> - let base = input.parse()?;
> - let _: Token![;] = input.parse()?;
> - Some(base)
> - } else {
> - None
> - };
> + if !input.peek(kw::base) {
> + Err(input.error(
> + "a base type needs to be specified for `register!` invocation with `base: ty;`",
> + ))?;
> + }
> +
> + let _: kw::base = input.parse()?;
IIUC here you can avoid the peek/parse and just do
let _: kw::base = input.parse().map_err(|e| {
Error::new(
e.span(),
"a base type needs to be specified for `register!` invocation with `base: ty;`",
)
})?;