2023-10-12 13:22:20

by Benno Lossin

[permalink] [raw]
Subject: [PATCH] rust: macros: improve `#[vtable]` documentation

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]>
---
rust/kernel/error.rs | 4 ++++
rust/macros/lib.rs | 32 ++++++++++++++++++++++++--------
2 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 05fcab6abfe6..1373cde025ef 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -335,3 +335,7 @@ pub(crate) fn from_result<T, F>(f: F) -> T
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.";
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index c42105c2ff96..dab9a1080b82 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -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.
+///
+/// If you want to make a function optional, you must provide 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, you should use the following code:
+///
+/// ```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)
/// }
/// }
///
@@ -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)

base-commit: b2516f7af9d238ebc391bdbdae01ac9528f1109e
--
2.41.0



2023-10-12 13:43:57

by Alice Ryhl

[permalink] [raw]
Subject: Re: [PATCH] rust: macros: improve `#[vtable]` documentation

On Thu, Oct 12, 2023 at 3:22 PM Benno Lossin <[email protected]> wrote:
>
> 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]>

2023-10-12 13:48:56

by Wedson Almeida Filho

[permalink] [raw]
Subject: Re: [PATCH] rust: macros: improve `#[vtable]` documentation

On Thu, 12 Oct 2023 at 10:22, Benno Lossin <[email protected]> wrote:
>
> 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: Wedson Almeida Filho <[email protected]>

> ---
> rust/kernel/error.rs | 4 ++++
> rust/macros/lib.rs | 32 ++++++++++++++++++++++++--------
> 2 files changed, 28 insertions(+), 8 deletions(-)
>
> diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
> index 05fcab6abfe6..1373cde025ef 100644
> --- a/rust/kernel/error.rs
> +++ b/rust/kernel/error.rs
> @@ -335,3 +335,7 @@ pub(crate) fn from_result<T, F>(f: F) -> T
> 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.";
> diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
> index c42105c2ff96..dab9a1080b82 100644
> --- a/rust/macros/lib.rs
> +++ b/rust/macros/lib.rs
> @@ -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.
> +///
> +/// If you want to make a function optional, you must provide a default implementation. But this

We should standardise how we write our documentation. In the `rust`
branch, we avoided using the imperative mood like you have here; the
rationale was that the documentation was describing how things
are/work, so we shouldn't be giving orders to readers (they may be
authors of traits, implementers of some vtable trait, or neither, just
someone learning about things, etc.).

In the paragraph above, you'll find an example: "Implementers of the
trait will then also have to...".

For the specific case above, I'd suggest: 'For a function to be
optional, it must have a default implementation.', or using the
passive voice, 'To make a function optional, a default implementation
must be provided'.

> +/// 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, you should use the following code:
> +///
> +/// ```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)
> /// }
> /// }
> ///
> @@ -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)
>
> base-commit: b2516f7af9d238ebc391bdbdae01ac9528f1109e
> --
> 2.41.0
>
>

2023-10-12 13:52:11

by Benno Lossin

[permalink] [raw]
Subject: Re: [PATCH] rust: macros: improve `#[vtable]` documentation

On 12.10.23 15:48, Wedson Almeida Filho wrote:
> On Thu, 12 Oct 2023 at 10:22, Benno Lossin <[email protected]> wrote:
>> +
>> +/// 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.";
>> diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
>> index c42105c2ff96..dab9a1080b82 100644
>> --- a/rust/macros/lib.rs
>> +++ b/rust/macros/lib.rs
>> @@ -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.
>> +///
>> +/// If you want to make a function optional, you must provide a default implementation. But this
>
> We should standardise how we write our documentation. In the `rust`
> branch, we avoided using the imperative mood like you have here; the
> rationale was that the documentation was describing how things
> are/work, so we shouldn't be giving orders to readers (they may be
> authors of traits, implementers of some vtable trait, or neither, just
> someone learning about things, etc.).
>
> In the paragraph above, you'll find an example: "Implementers of the
> trait will then also have to...".
>
> For the specific case above, I'd suggest: 'For a function to be
> optional, it must have a default implementation.', or using the
> passive voice, 'To make a function optional, a default implementation
> must be provided'.

I agree, I also think we should just fix this now, so I will send a v2.

--
Cheers,
Benno