Skip to content

Commit

Permalink
rust: macros: improve #[vtable] documentation
Browse files Browse the repository at this point in the history
Traits marked with `#[vtable]` need to provide default implementations
for optional functions. The C side represents these with `NULL` in the
vtable, so the default functions are never actually called. We do not
want to replicate the default behavior from C in Rust, because that is
not maintainable. Therefore we should use `build_error` in those default
implementations. The error message for that is provided at
`kernel::error::VTABLE_DEFAULT_ERROR`.

Signed-off-by: Benno Lossin <[email protected]>
Reviewed-by: Alice Ryhl <[email protected]>
Reviewed-by: Ariel Miculas <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
  • Loading branch information
Benno Lossin authored and fbq committed Oct 23, 2023
1 parent 6a3f428 commit f347fa1
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 8 deletions.
4 changes: 4 additions & 0 deletions rust/kernel/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,3 +335,7 @@ where
Err(e) => T::from(e.to_errno() as i16),
}
}

/// Error message for calling a default function of a [`#[vtable]`](macros::vtable) trait.
pub const VTABLE_DEFAULT_ERROR: &str =
"This function must not be called, see the #[vtable] documentation.";
32 changes: 24 additions & 8 deletions rust/macros/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,27 +87,41 @@ pub fn module(ts: TokenStream) -> TokenStream {
/// implementation could just return `Error::EINVAL`); Linux typically use C
/// `NULL` pointers to represent these functions.
///
/// This attribute is intended to close the gap. Traits can be declared and
/// implemented with the `#[vtable]` attribute, and a `HAS_*` associated constant
/// will be generated for each method in the trait, indicating if the implementor
/// has overridden a method.
/// This attribute closes that gap. A trait can be annotated with the `#[vtable]` attribute.
/// Implementers of the trait will then also have to annotate the trait with `#[vtable]`. This
/// attribute generates a `HAS_*` associated constant bool for each method in the trait that is set
/// to true if the implementer has overridden the associated method.
///
/// For a function to be optional, it must have a default implementation. But this default
/// implementation will never be executed, since these functions are exclusively called from
/// callbacks from the C side. This is because the vtable will have a `NULL` entry and the C side
/// will execute the default behavior. Since it is not maintainable to replicate the default
/// behavior in Rust, the default implementation should be:
///
/// ```compile_fail
/// # use kernel::error::VTABLE_DEFAULT_ERROR;
/// kernel::build_error(VTABLE_DEFAULT_ERROR)
/// ```
///
/// note that you might need to import [`kernel::error::VTABLE_DEFAULT_ERROR`].
///
/// This attribute is not needed if all methods are required.
/// This macro should not be used when all function are required.
///
/// # Examples
///
/// ```ignore
/// # use kernel::error::VTABLE_DEFAULT_ERROR;
/// use kernel::prelude::*;
///
/// // Declares a `#[vtable]` trait
/// #[vtable]
/// pub trait Operations: Send + Sync + Sized {
/// pub trait Operations {
/// fn foo(&self) -> Result<()> {
/// Err(EINVAL)
/// kernel::build_error(VTABLE_DEFAULT_ERROR)
/// }
///
/// fn bar(&self) -> Result<()> {
/// Err(EINVAL)
/// kernel::build_error(VTABLE_DEFAULT_ERROR)
/// }
/// }
///
Expand All @@ -125,6 +139,8 @@ pub fn module(ts: TokenStream) -> TokenStream {
/// assert_eq!(<Foo as Operations>::HAS_FOO, true);
/// assert_eq!(<Foo as Operations>::HAS_BAR, false);
/// ```
///
/// [`kernel::error::VTABLE_DEFAULT_ERROR`]: ../kernel/error/constant.VTABLE_DEFAULT_ERROR.html
#[proc_macro_attribute]
pub fn vtable(attr: TokenStream, ts: TokenStream) -> TokenStream {
vtable::vtable(attr, ts)
Expand Down

0 comments on commit f347fa1

Please sign in to comment.