From: Andreas Hindborg <a.hindborg@kernel.org>
To: "Alice Ryhl" <aliceryhl@google.com>
Cc: "Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Petr Mladek" <pmladek@suse.com>,
"Steven Rostedt" <rostedt@goodmis.org>,
"Andy Shevchenko" <andriy.shevchenko@linux.intel.com>,
"Rasmus Villemoes" <linux@rasmusvillemoes.dk>,
"Sergey Senozhatsky" <senozhatsky@chromium.org>,
"Andrew Morton" <akpm@linux-foundation.org>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <benno.lossin@proton.me>,
"Trevor Gross" <tmgross@umich.edu>,
"Maarten Lankhorst" <maarten.lankhorst@linux.intel.com>,
"Maxime Ripard" <mripard@kernel.org>,
"Thomas Zimmermann" <tzimmermann@suse.de>,
"David Airlie" <airlied@gmail.com>,
"Simona Vetter" <simona@ffwll.ch>,
linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
dri-devel@lists.freedesktop.org
Subject: Re: [PATCH 2/4] rust: add #[export] macro
Date: Fri, 28 Feb 2025 10:24:42 +0100 [thread overview]
Message-ID: <8734fyo205.fsf@kernel.org> (raw)
In-Reply-To: <CAH5fLggu+-Fw-4Z02xS3qSdhJAcSyNXaMn+CQ0XkBvqvgeAbGQ@mail.gmail.com> (Alice Ryhl's message of "Fri, 28 Feb 2025 10:04:11 +0100")
"Alice Ryhl" <aliceryhl@google.com> writes:
> On Fri, Feb 28, 2025 at 9:20 AM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>>
>> "Alice Ryhl" <aliceryhl@google.com> writes:
>>
>> > This macro behaves like #[no_mangle], but also performs an assertion
>> > that the Rust function has the same signature as what is declared in the
>> > C header.
>> >
>> > If the signatures don't match, you will get errors that look like this:
>> >
>> > error[E0308]: `if` and `else` have incompatible types
>> > --> <linux>/rust/kernel/print.rs:22:22
>> > |
>> > 21 | #[export]
>> > | --------- expected because of this
>> > 22 | unsafe extern "C" fn rust_fmt_argument(
>> > | ^^^^^^^^^^^^^^^^^ expected `u8`, found `i8`
>> > |
>> > = note: expected fn item `unsafe extern "C" fn(*mut u8, *mut u8, *mut c_void) -> *mut u8 {bindings::rust_fmt_argument}`
>> > found fn item `unsafe extern "C" fn(*mut i8, *mut i8, *const c_void) -> *mut i8 {print::rust_fmt_argument}`
>> >
>> > Signed-off-by: Alice Ryhl <aliceryhl@google.com>
>> > ---
>> > rust/kernel/prelude.rs | 2 +-
>> > rust/macros/export.rs | 25 +++++++++++++++++++++++++
>> > rust/macros/helpers.rs | 19 ++++++++++++++++++-
>> > rust/macros/lib.rs | 18 ++++++++++++++++++
>> > rust/macros/quote.rs | 21 +++++++++++++++++++--
>> > 5 files changed, 81 insertions(+), 4 deletions(-)
>> >
>> > diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
>> > index dde2e0649790..889102f5a81e 100644
>> > --- a/rust/kernel/prelude.rs
>> > +++ b/rust/kernel/prelude.rs
>> > @@ -17,7 +17,7 @@
>> > pub use crate::alloc::{flags::*, Box, KBox, KVBox, KVVec, KVec, VBox, VVec, Vec};
>> >
>> > #[doc(no_inline)]
>> > -pub use macros::{module, pin_data, pinned_drop, vtable, Zeroable};
>> > +pub use macros::{export, module, pin_data, pinned_drop, vtable, Zeroable};
>> >
>> > pub use super::{build_assert, build_error};
>> >
>> > diff --git a/rust/macros/export.rs b/rust/macros/export.rs
>> > new file mode 100644
>> > index 000000000000..3398e1655124
>> > --- /dev/null
>> > +++ b/rust/macros/export.rs
>> > @@ -0,0 +1,25 @@
>> > +// SPDX-License-Identifier: GPL-2.0
>> > +
>> > +use crate::helpers::function_name;
>> > +use proc_macro::TokenStream;
>> > +
>> > +pub(crate) fn export(_attr: TokenStream, ts: TokenStream) -> TokenStream {
>>
>> This function is documented in macros/lib.rs. Could you insert a
>> docstring with a link to the function that carries the docs?
>
> These functions are not visible in the docs, and no other macro does that.
I do realize that. I don't think it is ever too late to start improving.
Don't you think it would be overall net positive to have
/// Please see [`crate::export`] for documentation.
attached to this function?
>
>> Please describe how the function operates and what mechanics it uses to
>> achieve its goal in a implementation detail comment.
>>
>> > + let Some(name) = function_name(ts.clone()) else {
>> > + return "::core::compile_error!(\"The #[export] attribute must be used on a function.\");"
>> > + .parse::<TokenStream>()
>> > + .unwrap();
>> > + };
>> > +
>> > + let signature_check = quote!(
>> > + const _: () = {
>> > + if true {
>> > + ::kernel::bindings::#name
>> > + } else {
>> > + #name
>> > + };
>> > + };
>> > + );
>> > +
>> > + let no_mangle = "#[no_mangle]".parse::<TokenStream>().unwrap();
>> > + TokenStream::from_iter([signature_check, no_mangle, ts])
>> > +}
>> > diff --git a/rust/macros/helpers.rs b/rust/macros/helpers.rs
>> > index 563dcd2b7ace..3e04f8ecfc74 100644
>> > --- a/rust/macros/helpers.rs
>> > +++ b/rust/macros/helpers.rs
>> > @@ -1,6 +1,6 @@
>> > // SPDX-License-Identifier: GPL-2.0
>> >
>> > -use proc_macro::{token_stream, Group, TokenStream, TokenTree};
>> > +use proc_macro::{token_stream, Group, Ident, TokenStream, TokenTree};
>> >
>> > pub(crate) fn try_ident(it: &mut token_stream::IntoIter) -> Option<String> {
>> > if let Some(TokenTree::Ident(ident)) = it.next() {
>> > @@ -215,3 +215,20 @@ pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec<TokenTree>) {
>> > rest,
>> > )
>> > }
>> > +
>> > +/// Given a function declaration, finds the name of the function.
>> > +pub(crate) fn function_name(input: TokenStream) -> Option<Ident> {
>>
>> It would be great with a few tests for this function.
>
> I don't think we have a mechanism for tests in the macro crate?
Ah, I didn't realize. I'll create an issue for that if it is so.
>
>> > + let mut input = input.into_iter();
>> > + while let Some(token) = input.next() {
>> > + match token {
>> > + TokenTree::Ident(i) if i.to_string() == "fn" => {
>> > + if let Some(TokenTree::Ident(i)) = input.next() {
>> > + return Some(i);
>> > + }
>> > + return None;
>> > + }
>> > + _ => continue,
>> > + }
>> > + }
>> > + None
>> > +}
>> > diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
>> > index d61bc6a56425..3cbf7705c4c1 100644
>> > --- a/rust/macros/lib.rs
>> > +++ b/rust/macros/lib.rs
>> > @@ -9,6 +9,7 @@
>> > #[macro_use]
>> > mod quote;
>> > mod concat_idents;
>> > +mod export;
>> > mod helpers;
>> > mod module;
>> > mod paste;
>> > @@ -174,6 +175,23 @@ pub fn vtable(attr: TokenStream, ts: TokenStream) -> TokenStream {
>> > vtable::vtable(attr, ts)
>> > }
>> >
>> > +/// Export a function so that C code can call it.
>> > +///
>> > +/// This macro has the following effect:
>> > +///
>> > +/// * Disables name mangling for this function.
>> > +/// * Verifies at compile-time that the function signature matches what's in the header file.
>> > +///
>> > +/// This macro requires that the function is mentioned in a C header file, and that the header file
>> > +/// is included in `rust/bindings/bindings_helper.h`.
>> > +///
>> > +/// This macro is *not* the same as the C macro `EXPORT_SYMBOL*`, since all Rust symbols are
>> > +/// currently automatically exported with `EXPORT_SYMBOL_GPL`.
>>
>> Perhaps add the following:
>>
>> This macro is useful when rust code is providing a function symbol whose
>> signature is dictated by a C header file.
>
> I do think this could use more info about when to use it. E.g., you
> don't use it if C calls it via a vtable, but only if C calls it via a
> declaration in a header file. I'll add more info.
>
>> > +#[proc_macro_attribute]
>> > +pub fn export(attr: TokenStream, ts: TokenStream) -> TokenStream {
>> > + export::export(attr, ts)
>> > +}
>> > +
>> > /// Concatenate two identifiers.
>> > ///
>> > /// This is useful in macros that need to declare or reference items with names
>> > diff --git a/rust/macros/quote.rs b/rust/macros/quote.rs
>> > index 33a199e4f176..c18960a91082 100644
>> > --- a/rust/macros/quote.rs
>> > +++ b/rust/macros/quote.rs
>> > @@ -20,6 +20,12 @@ fn to_tokens(&self, tokens: &mut TokenStream) {
>> > }
>> > }
>> >
>> > +impl ToTokens for proc_macro::Ident {
>> > + fn to_tokens(&self, tokens: &mut TokenStream) {
>> > + tokens.extend([TokenTree::from(self.clone())]);
>> > + }
>> > +}
>> > +
>> > impl ToTokens for TokenTree {
>> > fn to_tokens(&self, tokens: &mut TokenStream) {
>> > tokens.extend([self.clone()]);
>> > @@ -40,7 +46,7 @@ fn to_tokens(&self, tokens: &mut TokenStream) {
>> > /// `quote` crate but provides only just enough functionality needed by the current `macros` crate.
>> > macro_rules! quote_spanned {
>> > ($span:expr => $($tt:tt)*) => {{
>> > - let mut tokens;
>> > + let mut tokens: ::std::vec::Vec<::proc_macro::TokenTree>;
>> > #[allow(clippy::vec_init_then_push)]
>> > {
>> > tokens = ::std::vec::Vec::new();
>> > @@ -65,7 +71,8 @@ macro_rules! quote_spanned {
>> > quote_spanned!(@proc $v $span $($tt)*);
>> > };
>> > (@proc $v:ident $span:ident ( $($inner:tt)* ) $($tt:tt)*) => {
>> > - let mut tokens = ::std::vec::Vec::new();
>> > + #[allow(unused_mut)]
>> > + let mut tokens = ::std::vec::Vec::<::proc_macro::TokenTree>::new();
>> > quote_spanned!(@proc tokens $span $($inner)*);
>> > $v.push(::proc_macro::TokenTree::Group(::proc_macro::Group::new(
>> > ::proc_macro::Delimiter::Parenthesis,
>> > @@ -136,6 +143,16 @@ macro_rules! quote_spanned {
>> > ));
>> > quote_spanned!(@proc $v $span $($tt)*);
>> > };
>> > + (@proc $v:ident $span:ident = $($tt:tt)*) => {
>> > + $v.push(::proc_macro::TokenTree::Punct(
>> > + ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Alone)
>> > + ));
>> > + quote_spanned!(@proc $v $span $($tt)*);
>> > + };
>> > + (@proc $v:ident $span:ident _ $($tt:tt)*) => {
>> > + $v.push(::proc_macro::TokenTree::Ident(::proc_macro::Ident::new("_", $span)));
>> > + quote_spanned!(@proc $v $span $($tt)*);
>> > + };
>> > (@proc $v:ident $span:ident $id:ident $($tt:tt)*) => {
>> > $v.push(::proc_macro::TokenTree::Ident(::proc_macro::Ident::new(stringify!($id), $span)));
>> > quote_spanned!(@proc $v $span $($tt)*);
>>
>> The update to `impl ToTokens for TokenTree` should be split out in a
>> separate patch and should carry some explanation of the change.
>
> I think this case is borderline for whether it's necessary to split
> up, but okay.
Thanks!
Best regards,
Andreas Hindborg
next prev parent reply other threads:[~2025-02-28 9:25 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <UXTosjUFv_CKOV-K4oqNGBhMEv64tds9NgXWhHEYdCHuKM2qSPFrpBnTqhFGkcbv5_KXYERykIXhn-sYnEeuUg==@protonmail.internalid>
2025-02-27 17:01 ` [PATCH 0/4] Check Rust signatures at compile time Alice Ryhl
2025-02-27 17:01 ` [PATCH 1/4] rust: fix signature of rust_fmt_argument Alice Ryhl
2025-02-28 8:13 ` Andreas Hindborg
2025-02-28 8:44 ` Alice Ryhl
2025-02-28 9:17 ` Andreas Hindborg
2025-02-27 17:02 ` [PATCH 2/4] rust: add #[export] macro Alice Ryhl
2025-02-28 8:12 ` Andreas Hindborg
2025-02-28 9:04 ` Alice Ryhl
2025-02-28 9:24 ` Andreas Hindborg [this message]
2025-02-27 17:02 ` [PATCH 3/4] print: use new #[export] macro for rust_fmt_argument Alice Ryhl
2025-02-28 8:15 ` Andreas Hindborg
2025-02-27 17:02 ` [PATCH 4/4] panic_qr: use new #[export] macro Alice Ryhl
2025-02-27 21:28 ` Boqun Feng
2025-02-27 23:01 ` Alice Ryhl
2025-02-28 8:19 ` Andreas Hindborg
2025-02-27 19:30 ` [PATCH 0/4] Check Rust signatures at compile time Greg Kroah-Hartman
2025-02-27 23:03 ` Alice Ryhl
2025-02-28 8:47 ` Alice Ryhl
2025-02-28 7:19 ` Andreas Hindborg
2025-02-28 8:46 ` Alice Ryhl
2025-02-28 9:18 ` Andreas Hindborg
2025-02-28 12:27 ` Andy Shevchenko
2025-02-28 12:26 ` Andy Shevchenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=8734fyo205.fsf@kernel.org \
--to=a.hindborg@kernel.org \
--cc=airlied@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=aliceryhl@google.com \
--cc=andriy.shevchenko@linux.intel.com \
--cc=benno.lossin@proton.me \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@rasmusvillemoes.dk \
--cc=maarten.lankhorst@linux.intel.com \
--cc=mripard@kernel.org \
--cc=ojeda@kernel.org \
--cc=pmladek@suse.com \
--cc=rostedt@goodmis.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=senozhatsky@chromium.org \
--cc=simona@ffwll.ch \
--cc=tmgross@umich.edu \
--cc=tzimmermann@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.