Rust changes for v6.20 / v7.0

Toolchain and infrastructure:
 
  - Add '__rust_helper' annotation to the C helpers.
 
    This is needed to inline these helpers into Rust code.
 
  - Remove imports available via the prelude, treewide.
 
    This was possible thanks to a new lint in Klint that Gary has
    implemented -- more Klint-related changes, including initial upstream
    support, are coming.
 
  - Deduplicate pin-init flags.
 
 'kernel' crate:
 
  - Add support for calling a function exactly once with the new
    'do_once_lite!' macro (and 'OnceLite' type).
 
    Based on this, add 'pr_*_once!' macros to print only once.
 
  - Add 'impl_flags!' macro for defining common bitflags operations:
 
        impl_flags!(
            /// Represents multiple permissions.
            #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
            pub struct Permissions(u32);
 
            /// Represents a single permission.
            #[derive(Debug, Clone, Copy, PartialEq, Eq)]
            pub enum Permission {
                /// Read permission.
                Read = 1 << 0,
 
                /// Write permission.
                Write = 1 << 1,
 
                /// Execute permission.
                Execute = 1 << 2,
            }
        );
 
        let mut f: Permissions = Permission::Read | Permission::Write;
        assert!(f.contains(Permission::Read));
        assert!(!f.contains(Permission::Execute));
 
        f |= Permission::Execute;
        assert!(f.contains(Permission::Execute));
 
        let f2: Permissions = Permission::Write | Permission::Execute;
        assert!((f ^ f2).contains(Permission::Read));
        assert!(!(f ^ f2).contains(Permission::Write));
 
  - 'bug' module: support 'CONFIG_DEBUG_BUGVERBOSE_DETAILED' in the
    'warn_on!' macro in order to show the evaluated condition alongside
    the file path:
 
         ------------[ cut here ]------------
         WARNING: [val == 1] linux/samples/rust/rust_minimal.rs:27 at ...
         Modules linked in: rust_minimal(+)
 
  - Add safety module with 'unsafe_precondition_assert!' macro, currently
    a wrapper for 'debug_assert!', intended to mark the validation of
    safety preconditions where possible:
 
        /// # Safety
        ///
        /// The caller must ensure that `index` is less than `N`.
        unsafe fn set_unchecked(&mut self, index: usize, value: T) {
            unsafe_precondition_assert!(
                index < N,
                "set_unchecked() requires index ({index}) < N ({N})"
            );
 
            ...
        }
 
  - Add instructions to 'build_assert!' documentation requesting to
    always inline functions when used with function arguments.
 
  - 'ptr' module: replace 'build_assert!' with a 'const' one.
 
  - 'rbtree' module: reduce unsafe blocks on pointer derefs.
 
  - 'transmute' module: implement 'FromBytes' and 'AsBytes' for
    inhabited ZSTs, and use it in Nova.
 
  - More treewide replacements of 'c_str!' with C string literals.
 
 'macros' crate:
 
  - Rewrite most procedural macros ('module!', 'concat_idents!',
    '#[export]', '#[vtable]', '#[kunit_tests]') to use the 'syn' parsing
    library which we introduced last cycle, with better diagnostics.
 
    This also allows to support '#[cfg]' properly in the '#[vtable]'
    macro, to support arbitrary types in 'module!' macro (not just an
    identifier) and to remove several custom parsing helpers we had.
 
  - Use 'quote!' from the recently vendored 'quote' library and remove
    our custom one.
 
    The vendored one also allows us to avoid quoting '"' and '{}' inside
    the template anymore and editors can now highlight it. In addition,
    it improves robustness as it eliminates the need for string quoting
    and escaping.
 
  - Use 'pin_init::zeroed()' to simplify KUnit code.
 
 'pin-init' crate:
 
  - Rewrite all procedural macros ('[pin_]init!', '#[pin_data]',
    '#[pinned_drop]', 'derive([Maybe]Zeroable)') to use the 'syn' parsing
    library which we introduced last cycle, with better diagnostics.
 
  - Implement 'InPlaceWrite' for '&'static mut MaybeUninit<T>'. This
    enables users to use external allocation mechanisms such as
    'static_cell'.
 
  - Support tuple structs in 'derive([Maybe]Zeroable)'.
 
  - Support attributes on fields in '[pin_]init!' (such as
    '#[cfg(...)]').
 
  - Add a '#[default_error(<type>)]' attribute to '[pin_]init!' to
    override the default error (when no '? Error' is specified).
 
  - Support packed structs in '[pin_]init!' with
    '#[disable_initialized_field_access]'.
 
  - Remove 'try_[pin_]init!' in favor of merging their feature
    with '[pin_]init!'. Update the kernel's own 'try_[pin_]init!'
    macros to use the 'default_error' attribute.
 
  - Correct 'T: Sized' bounds to 'T: ?Sized' in the generated
    'PinnedDrop' check by '#[pin_data]'.
 
 Documentation:
 
  - Conclude the Rust experiment.
 
 MAINTAINERS:
 
  - Add "RUST [RUST-ANALYZER]" entry for the rust-analyzer support. Tamir
    and Jesung will take care of it. They have both been active around it
    for a while. The new tree will flow through the Rust one.
 
  - Add Gary as maintainer for "RUST [PIN-INIT]".
 
  - Update Boqun and Tamir emails to their kernel.org accounts.
 
 And a few other cleanups and improvements.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEPjU5OPd5QIZ9jqqOGXyLc2htIW0FAmmIeIwACgkQGXyLc2ht
 IW13gg//ZW9kgIILHRbyLtLA9luNukD75pYigg0sigeKfZq5SIbDVepOtDGIsXId
 fJwYme297u81nf+TWySGPKEwCPFX9NNwrN6JiIKTxxkpfIYNd+cA57QN141xm2y4
 4+cG0Zw7yj2PitTKl5nzjRr4dfdfG+t63zLa29O4D5XqMtKs+yeawMLmYpNJLcfJ
 /RDRt+p/GX84xgP7fFRPi6zG4sD48ZktmLOe2H7st/mQKxjtqXdMVSqKENhdriGm
 h6IdKPs38fxA953+xHE1jO+XygbevnBmWD/E+/7ns5nNajoHU8KKkKMUUyzGqcQK
 0tgnOW5Pn5zdZ2Tf+CYGEjo04aA1rDtAUTtjr74uGiUtJR63E3TcGaRr1sY9Hye2
 tqKC1MVnj+UFzaZw57OJI9s8At6HBz7NXx4ZZ/MrkKp3jaV5BFhz11rWPAF9SM2E
 DqMyIXSrKLCeLr1LocizvrTvNQFGQSdmlbnywnJ2PP679JbJsZz714igxTOvdQBt
 M4AGRl/BnC6ZUJV3+/cdpRfqUAi0TPYxouphQ5ynm0zuIG0VjCYhSWM/w9k3LiZD
 pamQYOVdBCUrIcPyXMyEt6DszZM5qlt+4syCygcYfdr3PklFrTxeqF9mhetEuJRR
 UcOEXAdknHFcPFUnMakAYrL9PBnVyb2INsdsRpOj2GTb/neJKPg=
 =en99
 -----END PGP SIGNATURE-----

Merge tag 'rust-6.20-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/ojeda/linux

Pull rust updates from Miguel Ojeda:
 "Toolchain and infrastructure:

   - Add '__rust_helper' annotation to the C helpers

     This is needed to inline these helpers into Rust code

   - Remove imports available via the prelude, treewide

     This was possible thanks to a new lint in Klint that Gary has
     implemented -- more Klint-related changes, including initial
     upstream support, are coming

   - Deduplicate pin-init flags

  'kernel' crate:

   - Add support for calling a function exactly once with the new
     'do_once_lite!' macro (and 'OnceLite' type)

     Based on this, add 'pr_*_once!' macros to print only once

   - Add 'impl_flags!' macro for defining common bitflags operations:

         impl_flags!(
             /// Represents multiple permissions.
             #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
             pub struct Permissions(u32);

             /// Represents a single permission.
             #[derive(Debug, Clone, Copy, PartialEq, Eq)]
             pub enum Permission {
                 /// Read permission.
                 Read = 1 << 0,

                 /// Write permission.
                 Write = 1 << 1,

                 /// Execute permission.
                 Execute = 1 << 2,
             }
         );

         let mut f: Permissions = Permission::Read | Permission::Write;
         assert!(f.contains(Permission::Read));
         assert!(!f.contains(Permission::Execute));

         f |= Permission::Execute;
         assert!(f.contains(Permission::Execute));

         let f2: Permissions = Permission::Write | Permission::Execute;
         assert!((f ^ f2).contains(Permission::Read));
         assert!(!(f ^ f2).contains(Permission::Write));

   - 'bug' module: support 'CONFIG_DEBUG_BUGVERBOSE_DETAILED' in the
     'warn_on!' macro in order to show the evaluated condition alongside
     the file path:

          ------------[ cut here ]------------
          WARNING: [val == 1] linux/samples/rust/rust_minimal.rs:27 at ...
          Modules linked in: rust_minimal(+)

   - Add safety module with 'unsafe_precondition_assert!' macro,
     currently a wrapper for 'debug_assert!', intended to mark the
     validation of safety preconditions where possible:

         /// # Safety
         ///
         /// The caller must ensure that `index` is less than `N`.
         unsafe fn set_unchecked(&mut self, index: usize, value: T) {
             unsafe_precondition_assert!(
                 index < N,
                 "set_unchecked() requires index ({index}) < N ({N})"
             );

             ...
         }

   - Add instructions to 'build_assert!' documentation requesting to
     always inline functions when used with function arguments

   - 'ptr' module: replace 'build_assert!' with a 'const' one

   - 'rbtree' module: reduce unsafe blocks on pointer derefs

   - 'transmute' module: implement 'FromBytes' and 'AsBytes' for
     inhabited ZSTs, and use it in Nova

   - More treewide replacements of 'c_str!' with C string literals

  'macros' crate:

   - Rewrite most procedural macros ('module!', 'concat_idents!',
     '#[export]', '#[vtable]', '#[kunit_tests]') to use the 'syn'
     parsing library which we introduced last cycle, with better
     diagnostics

     This also allows to support '#[cfg]' properly in the '#[vtable]'
     macro, to support arbitrary types in 'module!' macro (not just an
     identifier) and to remove several custom parsing helpers we had

   - Use 'quote!' from the recently vendored 'quote' library and remove
     our custom one

     The vendored one also allows us to avoid quoting '"' and '{}'
     inside the template anymore and editors can now highlight it. In
     addition, it improves robustness as it eliminates the need for
     string quoting and escaping

   - Use 'pin_init::zeroed()' to simplify KUnit code

  'pin-init' crate:

   - Rewrite all procedural macros ('[pin_]init!', '#[pin_data]',
     '#[pinned_drop]', 'derive([Maybe]Zeroable)') to use the 'syn'
     parsing library which we introduced last cycle, with better
     diagnostics

   - Implement 'InPlaceWrite' for '&'static mut MaybeUninit<T>'. This
     enables users to use external allocation mechanisms such as
     'static_cell'

   - Support tuple structs in 'derive([Maybe]Zeroable)'

   - Support attributes on fields in '[pin_]init!' (such as
     '#[cfg(...)]')

   - Add a '#[default_error(<type>)]' attribute to '[pin_]init!' to
     override the default error (when no '? Error' is specified)

   - Support packed structs in '[pin_]init!' with
     '#[disable_initialized_field_access]'

   - Remove 'try_[pin_]init!' in favor of merging their feature with
     '[pin_]init!'. Update the kernel's own 'try_[pin_]init!' macros to
     use the 'default_error' attribute

   - Correct 'T: Sized' bounds to 'T: ?Sized' in the generated
     'PinnedDrop' check by '#[pin_data]'

  Documentation:

   - Conclude the Rust experiment

  MAINTAINERS:

   - Add "RUST [RUST-ANALYZER]" entry for the rust-analyzer support.
     Tamir and Jesung will take care of it. They have both been active
     around it for a while. The new tree will flow through the Rust one

   - Add Gary as maintainer for "RUST [PIN-INIT]"

   - Update Boqun and Tamir emails to their kernel.org accounts

  And a few other cleanups and improvements"

* tag 'rust-6.20-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/ojeda/linux: (59 commits)
  rust: safety: introduce `unsafe_precondition_assert!` macro
  rust: add `impl_flags!` macro for defining common bitflag operations
  rust: print: Add pr_*_once macros
  rust: bug: Support DEBUG_BUGVERBOSE_DETAILED option
  rust: print: Add support for calling a function exactly once
  rust: kbuild: deduplicate pin-init flags
  gpu: nova-core: remove imports available via prelude
  rust: clk: replace `kernel::c_str!` with C-Strings
  MAINTAINERS: Update my email address to @kernel.org
  rust: macros: support `#[cfg]` properly in `#[vtable]` macro.
  rust: kunit: use `pin_init::zeroed` instead of custom null value
  rust: macros: rearrange `#[doc(hidden)]` in `module!` macro
  rust: macros: allow arbitrary types to be used in `module!` macro
  rust: macros: convert `#[kunit_tests]` macro to use `syn`
  rust: macros: convert `concat_idents!` to use `syn`
  rust: macros: convert `#[export]` to use `syn`
  rust: macros: use `quote!` for `module!` macro
  rust: macros: use `syn` to parse `module!` macro
  rust: macros: convert `#[vtable]` macro to use `syn`
  rust: macros: use `quote!` from vendored crate
  ...
This commit is contained in:
Linus Torvalds 2026-02-10 11:53:01 -08:00
commit a9aabb3b83
67 changed files with 2758 additions and 3428 deletions

View file

@ -152,6 +152,7 @@ Bjorn Andersson <andersson@kernel.org> <bjorn.andersson@sonymobile.com>
Björn Steinbrink <B.Steinbrink@gmx.de>
Björn Töpel <bjorn@kernel.org> <bjorn.topel@gmail.com>
Björn Töpel <bjorn@kernel.org> <bjorn.topel@intel.com>
Boqun Feng <boqun@kernel.org> <boqun.feng@gmail.com>
Boris Brezillon <bbrezillon@kernel.org> <b.brezillon.dev@gmail.com>
Boris Brezillon <bbrezillon@kernel.org> <b.brezillon@overkiz.com>
Boris Brezillon <bbrezillon@kernel.org> <boris.brezillon@bootlin.com>
@ -800,6 +801,7 @@ Sven Eckelmann <sven@narfation.org> <sven@open-mesh.com>
Sven Peter <sven@kernel.org> <sven@svenpeter.dev>
Szymon Wilczek <swilczek.lx@gmail.com> <szymonwilczek@gmx.com>
Takashi YOSHII <takashi.yoshii.zj@renesas.com>
Tamir Duberstein <tamird@kernel.org> <tamird@gmail.com>
Tamizh Chelvam Raja <quic_tamizhr@quicinc.com> <tamizhr@codeaurora.org>
Taniya Das <quic_tdas@quicinc.com> <tdas@codeaurora.org>
Tanzir Hasan <tanzhasanwork@gmail.com> <tanzirh@google.com>

View file

@ -34,7 +34,7 @@ Please refer to ``include/linux/compiler_attributes.h`` for more information.
Rust
----
The kernel has experimental support for the Rust programming language
The kernel has support for the Rust programming language
[rust-language]_ under ``CONFIG_RUST``. It is compiled with ``rustc`` [rustc]_
under ``--edition=2021`` [rust-editions]_. Editions are a way to introduce
small changes to the language that are not backwards compatible.

View file

@ -7,24 +7,6 @@ Documentation related to Rust within the kernel. To start using Rust
in the kernel, please read the quick-start.rst guide.
The Rust experiment
-------------------
The Rust support was merged in v6.1 into mainline in order to help in
determining whether Rust as a language was suitable for the kernel, i.e. worth
the tradeoffs.
Currently, the Rust support is primarily intended for kernel developers and
maintainers interested in the Rust support, so that they can start working on
abstractions and drivers, as well as helping the development of infrastructure
and tools.
If you are an end user, please note that there are currently no in-tree
drivers/modules suitable or intended for production use, and that the Rust
support is still in development/experimental, especially for certain kernel
configurations.
Code documentation
------------------

View file

@ -4113,7 +4113,7 @@ F: drivers/input/touchscreen/atmel_mxt_ts.c
ATOMIC INFRASTRUCTURE
M: Will Deacon <will@kernel.org>
M: Peter Zijlstra <peterz@infradead.org>
M: Boqun Feng <boqun.feng@gmail.com>
M: Boqun Feng <boqun@kernel.org>
R: Mark Rutland <mark.rutland@arm.com>
R: Gary Guo <gary@garyguo.net>
L: linux-kernel@vger.kernel.org
@ -4506,7 +4506,7 @@ F: lib/sbitmap.c
BLOCK LAYER DEVICE DRIVER API [RUST]
M: Andreas Hindborg <a.hindborg@kernel.org>
R: Boqun Feng <boqun.feng@gmail.com>
R: Boqun Feng <boqun@kernel.org>
L: linux-block@vger.kernel.org
L: rust-for-linux@vger.kernel.org
S: Supported
@ -11281,7 +11281,7 @@ F: tools/testing/selftests/timers/
DELAY, SLEEP, TIMEKEEPING, TIMERS [RUST]
M: Andreas Hindborg <a.hindborg@kernel.org>
R: Boqun Feng <boqun.feng@gmail.com>
R: Boqun Feng <boqun@kernel.org>
R: FUJITA Tomonori <fujita.tomonori@gmail.com>
R: Frederic Weisbecker <frederic@kernel.org>
R: Lyude Paul <lyude@redhat.com>
@ -14582,7 +14582,7 @@ M: Alan Stern <stern@rowland.harvard.edu>
M: Andrea Parri <parri.andrea@gmail.com>
M: Will Deacon <will@kernel.org>
M: Peter Zijlstra <peterz@infradead.org>
M: Boqun Feng <boqun.feng@gmail.com>
M: Boqun Feng <boqun@kernel.org>
M: Nicholas Piggin <npiggin@gmail.com>
M: David Howells <dhowells@redhat.com>
M: Jade Alglave <j.alglave@ucl.ac.uk>
@ -14741,7 +14741,7 @@ LOCKING PRIMITIVES
M: Peter Zijlstra <peterz@infradead.org>
M: Ingo Molnar <mingo@redhat.com>
M: Will Deacon <will@kernel.org>
M: Boqun Feng <boqun.feng@gmail.com> (LOCKDEP & RUST)
M: Boqun Feng <boqun@kernel.org> (LOCKDEP & RUST)
R: Waiman Long <longman@redhat.com>
L: linux-kernel@vger.kernel.org
S: Maintained
@ -21948,7 +21948,7 @@ M: Frederic Weisbecker <frederic@kernel.org> (kernel/rcu/tree_nocb.h)
M: Neeraj Upadhyay <neeraj.upadhyay@kernel.org> (kernel/rcu/tasks.h)
M: Joel Fernandes <joelagnelf@nvidia.com>
M: Josh Triplett <josh@joshtriplett.org>
M: Boqun Feng <boqun.feng@gmail.com>
M: Boqun Feng <boqun@kernel.org>
M: Uladzislau Rezki <urezki@gmail.com>
R: Steven Rostedt <rostedt@goodmis.org>
R: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
@ -22399,7 +22399,7 @@ RESTARTABLE SEQUENCES SUPPORT
M: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
M: Peter Zijlstra <peterz@infradead.org>
M: "Paul E. McKenney" <paulmck@kernel.org>
M: Boqun Feng <boqun.feng@gmail.com>
M: Boqun Feng <boqun@kernel.org>
L: linux-kernel@vger.kernel.org
S: Supported
F: include/trace/events/rseq.h
@ -22922,7 +22922,7 @@ F: tools/verification/
RUST
M: Miguel Ojeda <ojeda@kernel.org>
R: Boqun Feng <boqun.feng@gmail.com>
R: Boqun Feng <boqun@kernel.org>
R: Gary Guo <gary@garyguo.net>
R: Björn Roy Baron <bjorn3_gh@protonmail.com>
R: Benno Lossin <lossin@kernel.org>
@ -22968,6 +22968,7 @@ F: rust/kernel/num/
RUST [PIN-INIT]
M: Benno Lossin <lossin@kernel.org>
M: Gary Guo <gary@garyguo.net>
L: rust-for-linux@vger.kernel.org
S: Maintained
W: https://rust-for-linux.com/pin-init
@ -22979,6 +22980,14 @@ F: rust/kernel/init.rs
F: rust/pin-init/
K: \bpin-init\b|pin_init\b|PinInit
RUST [RUST-ANALYZER]
M: Tamir Duberstein <tamird@kernel.org>
R: Jesung Yang <y.j3ms.n@gmail.com>
L: rust-for-linux@vger.kernel.org
S: Maintained
T: git https://github.com/Rust-for-Linux/linux.git rust-analyzer-next
F: scripts/generate_rust_analyzer.py
RXRPC SOCKETS (AF_RXRPC)
M: David Howells <dhowells@redhat.com>
M: Marc Dionne <marc.dionne@auristor.com>
@ -28385,7 +28394,7 @@ F: lib/xarray.c
F: tools/testing/radix-tree
XARRAY API [RUST]
M: Tamir Duberstein <tamird@gmail.com>
M: Tamir Duberstein <tamird@kernel.org>
M: Andreas Hindborg <a.hindborg@kernel.org>
L: rust-for-linux@vger.kernel.org
S: Supported

View file

@ -12,7 +12,6 @@
use core::{
marker::PhantomData,
mem::size_of,
ops::Deref, //
};

View file

@ -1,7 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
use core::mem::size_of_val;
use kernel::{
device,
dma::{
@ -34,11 +32,11 @@ use crate::{
/// that scheme before nova-core becomes stable, which means this module will eventually be
/// removed.
mod elf {
use core::mem::size_of;
use kernel::bindings;
use kernel::str::CStr;
use kernel::transmute::FromBytes;
use kernel::{
bindings,
prelude::*,
transmute::FromBytes, //
};
/// Newtype to provide a [`FromBytes`] implementation.
#[repr(transparent)]

View file

@ -3,8 +3,6 @@
//! Support for firmware binaries designed to run on a RISC-V core. Such firmwares files have a
//! dedicated header.
use core::mem::size_of;
use kernel::{
device,
firmware::Firmware,

View file

@ -142,7 +142,7 @@ impl CommandToGsp for SetRegistry {
}
/// Message type for GSP initialization done notification.
struct GspInitDone {}
struct GspInitDone;
// SAFETY: `GspInitDone` is a zero-sized type with no bytes, therefore it
// trivially has no uninitialized bytes.
@ -151,13 +151,13 @@ unsafe impl FromBytes for GspInitDone {}
impl MessageFromGsp for GspInitDone {
const FUNCTION: MsgFunction = MsgFunction::GspInitDone;
type InitError = Infallible;
type Message = GspInitDone;
type Message = ();
fn read(
_msg: &Self::Message,
_sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
) -> Result<Self, Self::InitError> {
Ok(GspInitDone {})
Ok(GspInitDone)
}
}

View file

@ -2,13 +2,7 @@
//! GSP Sequencer implementation for Pre-hopper GSP boot sequence.
use core::{
array,
mem::{
size_of,
size_of_val, //
},
};
use core::array;
use kernel::{
device,

View file

@ -2,10 +2,7 @@
use core::ops::Deref;
use kernel::{
alloc::KVec,
prelude::*, //
};
use kernel::prelude::*;
/// A buffer abstraction for discontiguous byte slices.
///

View file

@ -117,6 +117,23 @@ syn-flags := \
--extern quote \
$(call cfgs-to-flags,$(syn-cfgs))
pin_init_internal-cfgs := \
kernel
pin_init_internal-flags := \
--extern proc_macro2 \
--extern quote \
--extern syn \
$(call cfgs-to-flags,$(pin_init_internal-cfgs))
pin_init-cfgs := \
kernel
pin_init-flags := \
--extern pin_init_internal \
--extern macros \
$(call cfgs-to-flags,$(pin_init-cfgs))
# `rustdoc` did not save the target modifiers, thus workaround for
# the time being (https://github.com/rust-lang/rust/issues/144521).
rustdoc_modifiers_workaround := $(if $(call rustc-min-version,108800),-Cunsafe-allow-abi-mismatch=fixed-x18)
@ -211,15 +228,15 @@ rustdoc-ffi: $(src)/ffi.rs rustdoc-core FORCE
+$(call if_changed,rustdoc)
rustdoc-pin_init_internal: private rustdoc_host = yes
rustdoc-pin_init_internal: private rustc_target_flags = --cfg kernel \
rustdoc-pin_init_internal: private rustc_target_flags = $(pin_init_internal-flags) \
--extern proc_macro --crate-type proc-macro
rustdoc-pin_init_internal: $(src)/pin-init/internal/src/lib.rs \
rustdoc-clean FORCE
rustdoc-clean rustdoc-proc_macro2 rustdoc-quote rustdoc-syn FORCE
+$(call if_changed,rustdoc)
rustdoc-pin_init: private rustdoc_host = yes
rustdoc-pin_init: private rustc_target_flags = --extern pin_init_internal \
--extern macros --extern alloc --cfg kernel --cfg feature=\"alloc\"
rustdoc-pin_init: private rustc_target_flags = $(pin_init-flags) \
--extern alloc --cfg feature=\"alloc\"
rustdoc-pin_init: $(src)/pin-init/src/lib.rs rustdoc-pin_init_internal \
rustdoc-macros FORCE
+$(call if_changed,rustdoc)
@ -272,14 +289,14 @@ rusttestlib-macros: $(src)/macros/lib.rs \
rusttestlib-proc_macro2 rusttestlib-quote rusttestlib-syn FORCE
+$(call if_changed,rustc_test_library)
rusttestlib-pin_init_internal: private rustc_target_flags = --cfg kernel \
rusttestlib-pin_init_internal: private rustc_target_flags = $(pin_init_internal-flags) \
--extern proc_macro
rusttestlib-pin_init_internal: private rustc_test_library_proc = yes
rusttestlib-pin_init_internal: $(src)/pin-init/internal/src/lib.rs FORCE
rusttestlib-pin_init_internal: $(src)/pin-init/internal/src/lib.rs \
rusttestlib-proc_macro2 rusttestlib-quote rusttestlib-syn FORCE
+$(call if_changed,rustc_test_library)
rusttestlib-pin_init: private rustc_target_flags = --extern pin_init_internal \
--extern macros --cfg kernel
rusttestlib-pin_init: private rustc_target_flags = $(pin_init-flags)
rusttestlib-pin_init: $(src)/pin-init/src/lib.rs rusttestlib-macros \
rusttestlib-pin_init_internal $(obj)/$(libpin_init_internal_name) FORCE
+$(call if_changed,rustc_test_library)
@ -548,8 +565,9 @@ $(obj)/$(libmacros_name): $(src)/macros/lib.rs $(obj)/libproc_macro2.rlib \
$(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE
+$(call if_changed_dep,rustc_procmacro)
$(obj)/$(libpin_init_internal_name): private rustc_target_flags = --cfg kernel
$(obj)/$(libpin_init_internal_name): $(src)/pin-init/internal/src/lib.rs FORCE
$(obj)/$(libpin_init_internal_name): private rustc_target_flags = $(pin_init_internal-flags)
$(obj)/$(libpin_init_internal_name): $(src)/pin-init/internal/src/lib.rs \
$(obj)/libproc_macro2.rlib $(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE
+$(call if_changed_dep,rustc_procmacro)
quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L $@
@ -643,8 +661,7 @@ $(obj)/compiler_builtins.o: $(src)/compiler_builtins.rs $(obj)/core.o FORCE
+$(call if_changed_rule,rustc_library)
$(obj)/pin_init.o: private skip_gendwarfksyms = 1
$(obj)/pin_init.o: private rustc_target_flags = --extern pin_init_internal \
--extern macros --cfg kernel
$(obj)/pin_init.o: private rustc_target_flags = $(pin_init-flags)
$(obj)/pin_init.o: $(src)/pin-init/src/lib.rs $(obj)/compiler_builtins.o \
$(obj)/$(libpin_init_internal_name) $(obj)/$(libmacros_name) FORCE
+$(call if_changed_rule,rustc_library)

View file

@ -2,12 +2,12 @@
#include <linux/bug.h>
__noreturn void rust_helper_BUG(void)
__rust_helper __noreturn void rust_helper_BUG(void)
{
BUG();
}
bool rust_helper_WARN_ON(bool cond)
__rust_helper bool rust_helper_WARN_ON(bool cond)
{
return WARN_ON(cond);
}

View file

@ -2,7 +2,7 @@
#include <linux/errname.h>
const char *rust_helper_errname(int err)
__rust_helper const char *rust_helper_errname(int err)
{
return errname(err);
}

View file

@ -2,17 +2,17 @@
#include <linux/err.h>
__force void *rust_helper_ERR_PTR(long err)
__rust_helper __force void *rust_helper_ERR_PTR(long err)
{
return ERR_PTR(err);
}
bool rust_helper_IS_ERR(__force const void *ptr)
__rust_helper bool rust_helper_IS_ERR(__force const void *ptr)
{
return IS_ERR(ptr);
}
long rust_helper_PTR_ERR(__force const void *ptr)
__rust_helper long rust_helper_PTR_ERR(__force const void *ptr)
{
return PTR_ERR(ptr);
}

View file

@ -2,7 +2,8 @@
#include <linux/maple_tree.h>
void rust_helper_mt_init_flags(struct maple_tree *mt, unsigned int flags)
__rust_helper void rust_helper_mt_init_flags(struct maple_tree *mt,
unsigned int flags)
{
mt_init_flags(mt, flags);
}

View file

@ -3,48 +3,48 @@
#include <linux/mm.h>
#include <linux/sched/mm.h>
void rust_helper_mmgrab(struct mm_struct *mm)
__rust_helper void rust_helper_mmgrab(struct mm_struct *mm)
{
mmgrab(mm);
}
void rust_helper_mmdrop(struct mm_struct *mm)
__rust_helper void rust_helper_mmdrop(struct mm_struct *mm)
{
mmdrop(mm);
}
void rust_helper_mmget(struct mm_struct *mm)
__rust_helper void rust_helper_mmget(struct mm_struct *mm)
{
mmget(mm);
}
bool rust_helper_mmget_not_zero(struct mm_struct *mm)
__rust_helper bool rust_helper_mmget_not_zero(struct mm_struct *mm)
{
return mmget_not_zero(mm);
}
void rust_helper_mmap_read_lock(struct mm_struct *mm)
__rust_helper void rust_helper_mmap_read_lock(struct mm_struct *mm)
{
mmap_read_lock(mm);
}
bool rust_helper_mmap_read_trylock(struct mm_struct *mm)
__rust_helper bool rust_helper_mmap_read_trylock(struct mm_struct *mm)
{
return mmap_read_trylock(mm);
}
void rust_helper_mmap_read_unlock(struct mm_struct *mm)
__rust_helper void rust_helper_mmap_read_unlock(struct mm_struct *mm)
{
mmap_read_unlock(mm);
}
struct vm_area_struct *rust_helper_vma_lookup(struct mm_struct *mm,
unsigned long addr)
__rust_helper struct vm_area_struct *
rust_helper_vma_lookup(struct mm_struct *mm, unsigned long addr)
{
return vma_lookup(mm, addr);
}
void rust_helper_vma_end_read(struct vm_area_struct *vma)
__rust_helper void rust_helper_vma_end_read(struct vm_area_struct *vma)
{
vma_end_read(vma);
}

View file

@ -2,7 +2,7 @@
#include <linux/of.h>
bool rust_helper_is_of_node(const struct fwnode_handle *fwnode)
__rust_helper bool rust_helper_is_of_node(const struct fwnode_handle *fwnode)
{
return is_of_node(fwnode);
}

View file

@ -4,23 +4,24 @@
#include <linux/highmem.h>
#include <linux/mm.h>
struct page *rust_helper_alloc_pages(gfp_t gfp_mask, unsigned int order)
__rust_helper struct page *rust_helper_alloc_pages(gfp_t gfp_mask,
unsigned int order)
{
return alloc_pages(gfp_mask, order);
}
void *rust_helper_kmap_local_page(struct page *page)
__rust_helper void *rust_helper_kmap_local_page(struct page *page)
{
return kmap_local_page(page);
}
void rust_helper_kunmap_local(const void *addr)
__rust_helper void rust_helper_kunmap_local(const void *addr)
{
kunmap_local(addr);
}
#ifndef NODE_NOT_IN_PAGE_FLAGS
int rust_helper_page_to_nid(const struct page *page)
__rust_helper int rust_helper_page_to_nid(const struct page *page)
{
return page_to_nid(page);
}

View file

@ -2,18 +2,19 @@
#include <linux/rbtree.h>
void rust_helper_rb_link_node(struct rb_node *node, struct rb_node *parent,
__rust_helper void rust_helper_rb_link_node(struct rb_node *node,
struct rb_node *parent,
struct rb_node **rb_link)
{
rb_link_node(node, parent, rb_link);
}
struct rb_node *rust_helper_rb_first(const struct rb_root *root)
__rust_helper struct rb_node *rust_helper_rb_first(const struct rb_root *root)
{
return rb_first(root);
}
struct rb_node *rust_helper_rb_last(const struct rb_root *root)
__rust_helper struct rb_node *rust_helper_rb_last(const struct rb_root *root)
{
return rb_last(root);
}

View file

@ -2,14 +2,14 @@
#include <linux/slab.h>
void * __must_check __realloc_size(2)
__rust_helper void *__must_check __realloc_size(2)
rust_helper_krealloc_node_align(const void *objp, size_t new_size, unsigned long align,
gfp_t flags, int node)
{
return krealloc_node_align(objp, new_size, align, flags, node);
}
void * __must_check __realloc_size(2)
__rust_helper void *__must_check __realloc_size(2)
rust_helper_kvrealloc_node_align(const void *p, size_t size, unsigned long align,
gfp_t flags, int node)
{

View file

@ -2,24 +2,26 @@
#include <linux/uaccess.h>
unsigned long rust_helper_copy_from_user(void *to, const void __user *from,
unsigned long n)
__rust_helper unsigned long
rust_helper_copy_from_user(void *to, const void __user *from, unsigned long n)
{
return copy_from_user(to, from, n);
}
unsigned long rust_helper_copy_to_user(void __user *to, const void *from,
unsigned long n)
__rust_helper unsigned long
rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n)
{
return copy_to_user(to, from, n);
}
#ifdef INLINE_COPY_FROM_USER
__rust_helper
unsigned long rust_helper__copy_from_user(void *to, const void __user *from, unsigned long n)
{
return _inline_copy_from_user(to, from, n);
}
__rust_helper
unsigned long rust_helper__copy_to_user(void __user *to, const void *from, unsigned long n)
{
return _inline_copy_to_user(to, from, n);

View file

@ -2,7 +2,7 @@
#include <linux/vmalloc.h>
void * __must_check __realloc_size(2)
__rust_helper void *__must_check __realloc_size(2)
rust_helper_vrealloc_node_align(const void *p, size_t size, unsigned long align,
gfp_t flags, int node)
{

View file

@ -2,8 +2,10 @@
#include <linux/workqueue.h>
void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func,
bool onstack, const char *name,
__rust_helper void rust_helper_init_work_with_key(struct work_struct *work,
work_func_t func,
bool onstack,
const char *name,
struct lock_class_key *key)
{
__init_work(work, onstack);

View file

@ -2,27 +2,27 @@
#include <linux/xarray.h>
int rust_helper_xa_err(void *entry)
__rust_helper int rust_helper_xa_err(void *entry)
{
return xa_err(entry);
}
void rust_helper_xa_init_flags(struct xarray *xa, gfp_t flags)
__rust_helper void rust_helper_xa_init_flags(struct xarray *xa, gfp_t flags)
{
return xa_init_flags(xa, flags);
}
int rust_helper_xa_trylock(struct xarray *xa)
__rust_helper int rust_helper_xa_trylock(struct xarray *xa)
{
return xa_trylock(xa);
}
void rust_helper_xa_lock(struct xarray *xa)
__rust_helper void rust_helper_xa_lock(struct xarray *xa)
{
return xa_lock(xa);
}
void rust_helper_xa_unlock(struct xarray *xa)
__rust_helper void rust_helper_xa_unlock(struct xarray *xa)
{
return xa_unlock(xa);
}

View file

@ -11,9 +11,9 @@
#[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))]
#[cfg(CONFIG_DEBUG_BUGVERBOSE)]
macro_rules! warn_flags {
($flags:expr) => {
($file:expr, $flags:expr) => {
const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags;
const _FILE: &[u8] = file!().as_bytes();
const _FILE: &[u8] = $file.as_bytes();
// Plus one for null-terminator.
static FILE: [u8; _FILE.len() + 1] = {
let mut bytes = [0; _FILE.len() + 1];
@ -50,7 +50,7 @@ macro_rules! warn_flags {
#[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))]
#[cfg(not(CONFIG_DEBUG_BUGVERBOSE))]
macro_rules! warn_flags {
($flags:expr) => {
($file:expr, $flags:expr) => {
const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags;
// SAFETY:
@ -75,7 +75,7 @@ macro_rules! warn_flags {
#[doc(hidden)]
#[cfg(all(CONFIG_BUG, CONFIG_UML))]
macro_rules! warn_flags {
($flags:expr) => {
($file:expr, $flags:expr) => {
// SAFETY: It is always safe to call `warn_slowpath_fmt()`
// with a valid null-terminated string.
unsafe {
@ -93,7 +93,7 @@ macro_rules! warn_flags {
#[doc(hidden)]
#[cfg(all(CONFIG_BUG, any(CONFIG_LOONGARCH, CONFIG_ARM)))]
macro_rules! warn_flags {
($flags:expr) => {
($file:expr, $flags:expr) => {
// SAFETY: It is always safe to call `WARN_ON()`.
unsafe { $crate::bindings::WARN_ON(true) }
};
@ -103,7 +103,7 @@ macro_rules! warn_flags {
#[doc(hidden)]
#[cfg(not(CONFIG_BUG))]
macro_rules! warn_flags {
($flags:expr) => {};
($file:expr, $flags:expr) => {};
}
#[doc(hidden)]
@ -116,10 +116,16 @@ pub const fn bugflag_taint(value: u32) -> u32 {
macro_rules! warn_on {
($cond:expr) => {{
let cond = $cond;
#[cfg(CONFIG_DEBUG_BUGVERBOSE_DETAILED)]
const _COND_STR: &str = concat!("[", stringify!($cond), "] ", file!());
#[cfg(not(CONFIG_DEBUG_BUGVERBOSE_DETAILED))]
const _COND_STR: &str = file!();
if cond {
const WARN_ON_FLAGS: u32 = $crate::bug::bugflag_taint($crate::bindings::TAINT_WARN);
$crate::warn_flags!(WARN_ON_FLAGS);
$crate::warn_flags!(_COND_STR, WARN_ON_FLAGS);
}
cond
}};

View file

@ -61,8 +61,13 @@ macro_rules! build_error {
/// build_assert!(N > 1); // Build-time check
/// assert!(N > 1); // Run-time check
/// }
/// ```
///
/// #[inline]
/// When a condition depends on a function argument, the function must be annotated with
/// `#[inline(always)]`. Without this attribute, the compiler may choose to not inline the
/// function, preventing it from optimizing out the error path.
/// ```
/// #[inline(always)]
/// fn bar(n: usize) {
/// // `static_assert!(n > 1);` is not allowed
/// build_assert!(n > 1); // Build-time check

View file

@ -94,7 +94,7 @@ mod common_clk {
/// # Invariants
///
/// A [`Clk`] instance holds either a pointer to a valid [`struct clk`] created by the C
/// portion of the kernel or a NULL pointer.
/// portion of the kernel or a `NULL` pointer.
///
/// Instances of this type are reference-counted. Calling [`Clk::get`] ensures that the
/// allocation remains valid for the lifetime of the [`Clk`].
@ -104,13 +104,12 @@ mod common_clk {
/// The following example demonstrates how to obtain and configure a clock for a device.
///
/// ```
/// use kernel::c_str;
/// use kernel::clk::{Clk, Hertz};
/// use kernel::device::Device;
/// use kernel::error::Result;
///
/// fn configure_clk(dev: &Device) -> Result {
/// let clk = Clk::get(dev, Some(c_str!("apb_clk")))?;
/// let clk = Clk::get(dev, Some(c"apb_clk"))?;
///
/// clk.prepare_enable()?;
///
@ -272,13 +271,12 @@ mod common_clk {
/// device. The code functions correctly whether or not the clock is available.
///
/// ```
/// use kernel::c_str;
/// use kernel::clk::{OptionalClk, Hertz};
/// use kernel::device::Device;
/// use kernel::error::Result;
///
/// fn configure_clk(dev: &Device) -> Result {
/// let clk = OptionalClk::get(dev, Some(c_str!("apb_clk")))?;
/// let clk = OptionalClk::get(dev, Some(c"apb_clk"))?;
///
/// clk.prepare_enable()?;
///

View file

@ -148,7 +148,7 @@ impl Entry<'_> {
/// # Guarantees
///
/// Due to the type invariant, the value returned from this function will always be an error
/// code, NULL, or a live DebugFS directory. If it is live, it will remain live at least as
/// code, `NULL`, or a live DebugFS directory. If it is live, it will remain live at least as
/// long as this entry lives.
pub(crate) fn as_ptr(&self) -> *mut bindings::dentry {
self.entry

View file

@ -262,7 +262,7 @@ macro_rules! module_i2c_driver {
/// # Example
///
///```
/// # use kernel::{acpi, bindings, c_str, device::Core, i2c, of};
/// # use kernel::{acpi, bindings, device::Core, i2c, of};
///
/// struct MyDriver;
///
@ -271,7 +271,7 @@ macro_rules! module_i2c_driver {
/// MODULE_ACPI_TABLE,
/// <MyDriver as i2c::Driver>::IdInfo,
/// [
/// (acpi::DeviceId::new(c_str!("LNUXBEEF")), ())
/// (acpi::DeviceId::new(c"LNUXBEEF"), ())
/// ]
/// );
///
@ -280,7 +280,7 @@ macro_rules! module_i2c_driver {
/// MODULE_I2C_TABLE,
/// <MyDriver as i2c::Driver>::IdInfo,
/// [
/// (i2c::DeviceId::new(c_str!("rust_driver_i2c")), ())
/// (i2c::DeviceId::new(c"rust_driver_i2c"), ())
/// ]
/// );
///
@ -289,7 +289,7 @@ macro_rules! module_i2c_driver {
/// MODULE_OF_TABLE,
/// <MyDriver as i2c::Driver>::IdInfo,
/// [
/// (of::DeviceId::new(c_str!("test,device")), ())
/// (of::DeviceId::new(c"test,device"), ())
/// ]
/// );
///

272
rust/kernel/impl_flags.rs Normal file
View file

@ -0,0 +1,272 @@
// SPDX-License-Identifier: GPL-2.0
//! Bitflag type generator.
/// Common helper for declaring bitflag and bitmask types.
///
/// This macro takes as input:
/// - A struct declaration representing a bitmask type
/// (e.g., `pub struct Permissions(u32)`).
/// - An enumeration declaration representing individual bit flags
/// (e.g., `pub enum Permission { ... }`).
///
/// And generates:
/// - The struct and enum types with appropriate `#[repr]` attributes.
/// - Implementations of common bitflag operators
/// ([`::core::ops::BitOr`], [`::core::ops::BitAnd`], etc.).
/// - Utility methods such as `.contains()` to check flags.
///
/// # Examples
///
/// ```
/// use kernel::impl_flags;
///
/// impl_flags!(
/// /// Represents multiple permissions.
/// #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
/// pub struct Permissions(u32);
///
/// /// Represents a single permission.
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// pub enum Permission {
/// /// Read permission.
/// Read = 1 << 0,
///
/// /// Write permission.
/// Write = 1 << 1,
///
/// /// Execute permission.
/// Execute = 1 << 2,
/// }
/// );
///
/// // Combine multiple permissions using the bitwise OR (`|`) operator.
/// let mut read_write: Permissions = Permission::Read | Permission::Write;
/// assert!(read_write.contains(Permission::Read));
/// assert!(read_write.contains(Permission::Write));
/// assert!(!read_write.contains(Permission::Execute));
/// assert!(read_write.contains_any(Permission::Read | Permission::Execute));
/// assert!(read_write.contains_all(Permission::Read | Permission::Write));
///
/// // Using the bitwise OR assignment (`|=`) operator.
/// read_write |= Permission::Execute;
/// assert!(read_write.contains(Permission::Execute));
///
/// // Masking a permission with the bitwise AND (`&`) operator.
/// let read_only: Permissions = read_write & Permission::Read;
/// assert!(read_only.contains(Permission::Read));
/// assert!(!read_only.contains(Permission::Write));
///
/// // Toggling permissions with the bitwise XOR (`^`) operator.
/// let toggled: Permissions = read_only ^ Permission::Read;
/// assert!(!toggled.contains(Permission::Read));
///
/// // Inverting permissions with the bitwise NOT (`!`) operator.
/// let negated = !read_only;
/// assert!(negated.contains(Permission::Write));
/// assert!(!negated.contains(Permission::Read));
/// ```
#[macro_export]
macro_rules! impl_flags {
(
$(#[$outer_flags:meta])*
$vis_flags:vis struct $flags:ident($ty:ty);
$(#[$outer_flag:meta])*
$vis_flag:vis enum $flag:ident {
$(
$(#[$inner_flag:meta])*
$name:ident = $value:expr
),+ $( , )?
}
) => {
$(#[$outer_flags])*
#[repr(transparent)]
$vis_flags struct $flags($ty);
$(#[$outer_flag])*
#[repr($ty)]
$vis_flag enum $flag {
$(
$(#[$inner_flag])*
$name = $value
),+
}
impl ::core::convert::From<$flag> for $flags {
#[inline]
fn from(value: $flag) -> Self {
Self(value as $ty)
}
}
impl ::core::convert::From<$flags> for $ty {
#[inline]
fn from(value: $flags) -> Self {
value.0
}
}
impl ::core::ops::BitOr for $flags {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl ::core::ops::BitOrAssign for $flags {
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
*self = *self | rhs;
}
}
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 for $flags {
type Output = Self;
#[inline]
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.0 & rhs.0)
}
}
impl ::core::ops::BitAndAssign for $flags {
#[inline]
fn bitand_assign(&mut self, rhs: Self) {
*self = *self & rhs;
}
}
impl ::core::ops::BitAnd<$flag> for $flags {
type Output = Self;
#[inline]
fn bitand(self, rhs: $flag) -> Self::Output {
self & Self::from(rhs)
}
}
impl ::core::ops::BitAndAssign<$flag> for $flags {
#[inline]
fn bitand_assign(&mut self, rhs: $flag) {
*self = *self & rhs;
}
}
impl ::core::ops::BitXor for $flags {
type Output = Self;
#[inline]
fn bitxor(self, rhs: Self) -> Self::Output {
Self((self.0 ^ rhs.0) & Self::all_bits())
}
}
impl ::core::ops::BitXorAssign for $flags {
#[inline]
fn bitxor_assign(&mut self, rhs: Self) {
*self = *self ^ rhs;
}
}
impl ::core::ops::BitXor<$flag> for $flags {
type Output = Self;
#[inline]
fn bitxor(self, rhs: $flag) -> Self::Output {
self ^ Self::from(rhs)
}
}
impl ::core::ops::BitXorAssign<$flag> for $flags {
#[inline]
fn bitxor_assign(&mut self, rhs: $flag) {
*self = *self ^ rhs;
}
}
impl ::core::ops::Not for $flags {
type Output = Self;
#[inline]
fn not(self) -> Self::Output {
Self((!self.0) & Self::all_bits())
}
}
impl ::core::ops::BitOr for $flag {
type Output = $flags;
#[inline]
fn bitor(self, rhs: Self) -> Self::Output {
$flags(self as $ty | rhs as $ty)
}
}
impl ::core::ops::BitAnd for $flag {
type Output = $flags;
#[inline]
fn bitand(self, rhs: Self) -> Self::Output {
$flags(self as $ty & rhs as $ty)
}
}
impl ::core::ops::BitXor for $flag {
type Output = $flags;
#[inline]
fn bitxor(self, rhs: Self) -> Self::Output {
$flags((self as $ty ^ rhs as $ty) & $flags::all_bits())
}
}
impl ::core::ops::Not for $flag {
type Output = $flags;
#[inline]
fn not(self) -> Self::Output {
$flags((!(self as $ty)) & $flags::all_bits())
}
}
impl $flags {
/// Returns an empty instance where no flags are set.
#[inline]
pub const fn empty() -> Self {
Self(0)
}
/// Returns a mask containing all valid flag bits.
#[inline]
pub const fn all_bits() -> $ty {
0 $( | $value )+
}
/// Checks if a specific flag is set.
#[inline]
pub fn contains(self, flag: $flag) -> bool {
(self.0 & flag as $ty) == flag as $ty
}
/// Checks if at least one of the provided flags is set.
#[inline]
pub fn contains_any(self, flags: $flags) -> bool {
(self.0 & flags.0) != 0
}
/// Checks if all of the provided flags are set.
#[inline]
pub fn contains_all(self, flags: $flags) -> bool {
(self.0 & flags.0) == flags.0
}
}
};
}

View file

@ -219,20 +219,12 @@ pub trait InPlaceInit<T>: Sized {
/// [`Error`]: crate::error::Error
#[macro_export]
macro_rules! try_init {
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
$($fields:tt)*
}) => {
::pin_init::try_init!($(&$this in)? $t $(::<$($generics),*>)? {
$($fields)*
}? $crate::error::Error)
};
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
$($fields:tt)*
}? $err:ty) => {
::pin_init::try_init!($(&$this in)? $t $(::<$($generics),*>)? {
$($fields)*
}? $err)
};
($($args:tt)*) => {
::pin_init::init!(
#[default_error($crate::error::Error)]
$($args)*
)
}
}
/// Construct an in-place, fallible pinned initializer for `struct`s.
@ -279,18 +271,10 @@ macro_rules! try_init {
/// [`Error`]: crate::error::Error
#[macro_export]
macro_rules! try_pin_init {
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
$($fields:tt)*
}) => {
::pin_init::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
$($fields)*
}? $crate::error::Error)
};
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
$($fields:tt)*
}? $err:ty) => {
::pin_init::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
$($fields)*
}? $err)
};
($($args:tt)*) => {
::pin_init::pin_init!(
#[default_error($crate::error::Error)]
$($args)*
)
}
}

View file

@ -9,9 +9,6 @@
use crate::fmt;
use crate::prelude::*;
#[cfg(CONFIG_PRINTK)]
use crate::c_str;
/// Prints a KUnit error-level message.
///
/// Public but hidden since it should only be used from KUnit generated code.
@ -22,7 +19,7 @@ pub fn err(args: fmt::Arguments<'_>) {
#[cfg(CONFIG_PRINTK)]
unsafe {
bindings::_printk(
c_str!("\x013%pA").as_char_ptr(),
c"\x013%pA".as_char_ptr(),
core::ptr::from_ref(&args).cast::<c_void>(),
);
}
@ -38,7 +35,7 @@ pub fn info(args: fmt::Arguments<'_>) {
#[cfg(CONFIG_PRINTK)]
unsafe {
bindings::_printk(
c_str!("\x016%pA").as_char_ptr(),
c"\x016%pA".as_char_ptr(),
core::ptr::from_ref(&args).cast::<c_void>(),
);
}
@ -60,7 +57,7 @@ macro_rules! kunit_assert {
break 'out;
}
static FILE: &'static $crate::str::CStr = $crate::c_str!($file);
static FILE: &'static $crate::str::CStr = $file;
static LINE: i32 = ::core::line!() as i32 - $diff;
static CONDITION: &'static $crate::str::CStr = $crate::c_str!(stringify!($condition));
@ -192,9 +189,6 @@ pub fn is_test_result_ok(t: impl TestResult) -> bool {
}
/// Represents an individual test case.
///
/// The [`kunit_unsafe_test_suite!`] macro expects a NULL-terminated list of valid test cases.
/// Use [`kunit_case_null`] to generate such a delimiter.
#[doc(hidden)]
pub const fn kunit_case(
name: &'static kernel::str::CStr,
@ -215,32 +209,11 @@ pub const fn kunit_case(
}
}
/// Represents the NULL test case delimiter.
///
/// The [`kunit_unsafe_test_suite!`] macro expects a NULL-terminated list of test cases. This
/// function returns such a delimiter.
#[doc(hidden)]
pub const fn kunit_case_null() -> kernel::bindings::kunit_case {
kernel::bindings::kunit_case {
run_case: None,
name: core::ptr::null_mut(),
generate_params: None,
attr: kernel::bindings::kunit_attributes {
speed: kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL,
},
status: kernel::bindings::kunit_status_KUNIT_SUCCESS,
module_name: core::ptr::null_mut(),
log: core::ptr::null_mut(),
param_init: None,
param_exit: None,
}
}
/// Registers a KUnit test suite.
///
/// # Safety
///
/// `test_cases` must be a NULL terminated array of valid test cases,
/// `test_cases` must be a `NULL` terminated array of valid test cases,
/// whose lifetime is at least that of the test suite (i.e., static).
///
/// # Examples
@ -253,8 +226,8 @@ pub const fn kunit_case_null() -> kernel::bindings::kunit_case {
/// }
///
/// static mut KUNIT_TEST_CASES: [kernel::bindings::kunit_case; 2] = [
/// kernel::kunit::kunit_case(kernel::c_str!("name"), test_fn),
/// kernel::kunit::kunit_case_null(),
/// kernel::kunit::kunit_case(c"name", test_fn),
/// pin_init::zeroed(),
/// ];
/// kernel::kunit_unsafe_test_suite!(suite_name, KUNIT_TEST_CASES);
/// ```

View file

@ -100,6 +100,8 @@ pub mod fs;
#[cfg(CONFIG_I2C = "y")]
pub mod i2c;
pub mod id_pool;
#[doc(hidden)]
pub mod impl_flags;
pub mod init;
pub mod io;
pub mod ioctl;
@ -133,6 +135,7 @@ pub mod pwm;
pub mod rbtree;
pub mod regulator;
pub mod revocable;
pub mod safety;
pub mod scatterlist;
pub mod security;
pub mod seq_file;

View file

@ -11,6 +11,11 @@ use crate::{
fmt,
prelude::*,
str::RawFormatter,
sync::atomic::{
Atomic,
AtomicType,
Relaxed, //
},
};
// Called from `vsprintf` with format specifier `%pA`.
@ -423,3 +428,151 @@ macro_rules! pr_cont (
$crate::print_macro!($crate::print::format_strings::CONT, true, $($arg)*)
)
);
/// A lightweight `call_once` primitive.
///
/// This structure provides the Rust equivalent of the kernel's `DO_ONCE_LITE` macro.
/// While it would be possible to implement the feature entirely as a Rust macro,
/// the functionality that can be implemented as regular functions has been
/// extracted and implemented as the `OnceLite` struct for better code maintainability.
pub struct OnceLite(Atomic<State>);
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
enum State {
Incomplete = 0,
Complete = 1,
}
// SAFETY: `State` and `i32` has the same size and alignment, and it's round-trip
// transmutable to `i32`.
unsafe impl AtomicType for State {
type Repr = i32;
}
impl OnceLite {
/// Creates a new [`OnceLite`] in the incomplete state.
#[inline(always)]
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
OnceLite(Atomic::new(State::Incomplete))
}
/// Calls the provided function exactly once.
///
/// There is no other synchronization between two `call_once()`s
/// except that only one will execute `f`, in other words, callers
/// should not use a failed `call_once()` as a proof that another
/// `call_once()` has already finished and the effect is observable
/// to this thread.
pub fn call_once<F>(&self, f: F) -> bool
where
F: FnOnce(),
{
// Avoid expensive cmpxchg if already completed.
// ORDERING: `Relaxed` is used here since no synchronization is required.
let old = self.0.load(Relaxed);
if old == State::Complete {
return false;
}
// ORDERING: `Relaxed` is used here since no synchronization is required.
let old = self.0.xchg(State::Complete, Relaxed);
if old == State::Complete {
return false;
}
f();
true
}
}
/// Run the given function exactly once.
///
/// This is equivalent to the kernel's `DO_ONCE_LITE` macro.
///
/// # Examples
///
/// ```
/// kernel::do_once_lite! {
/// kernel::pr_info!("This will be printed only once\n");
/// };
/// ```
#[macro_export]
macro_rules! do_once_lite {
{ $($e:tt)* } => {{
#[link_section = ".data..once"]
static ONCE: $crate::print::OnceLite = $crate::print::OnceLite::new();
ONCE.call_once(|| { $($e)* });
}};
}
/// Prints an emergency-level message (level 0) only once.
///
/// Equivalent to the kernel's `pr_emerg_once` macro.
#[macro_export]
macro_rules! pr_emerg_once (
($($arg:tt)*) => (
$crate::do_once_lite! { $crate::pr_emerg!($($arg)*) }
)
);
/// Prints an alert-level message (level 1) only once.
///
/// Equivalent to the kernel's `pr_alert_once` macro.
#[macro_export]
macro_rules! pr_alert_once (
($($arg:tt)*) => (
$crate::do_once_lite! { $crate::pr_alert!($($arg)*) }
)
);
/// Prints a critical-level message (level 2) only once.
///
/// Equivalent to the kernel's `pr_crit_once` macro.
#[macro_export]
macro_rules! pr_crit_once (
($($arg:tt)*) => (
$crate::do_once_lite! { $crate::pr_crit!($($arg)*) }
)
);
/// Prints an error-level message (level 3) only once.
///
/// Equivalent to the kernel's `pr_err_once` macro.
#[macro_export]
macro_rules! pr_err_once (
($($arg:tt)*) => (
$crate::do_once_lite! { $crate::pr_err!($($arg)*) }
)
);
/// Prints a warning-level message (level 4) only once.
///
/// Equivalent to the kernel's `pr_warn_once` macro.
#[macro_export]
macro_rules! pr_warn_once (
($($arg:tt)*) => (
$crate::do_once_lite! { $crate::pr_warn!($($arg)*) }
)
);
/// Prints a notice-level message (level 5) only once.
///
/// Equivalent to the kernel's `pr_notice_once` macro.
#[macro_export]
macro_rules! pr_notice_once (
($($arg:tt)*) => (
$crate::do_once_lite! { $crate::pr_notice!($($arg)*) }
)
);
/// Prints an info-level message (level 6) only once.
///
/// Equivalent to the kernel's `pr_info_once` macro.
#[macro_export]
macro_rules! pr_info_once (
($($arg:tt)*) => (
$crate::do_once_lite! { $crate::pr_info!($($arg)*) }
)
);

View file

@ -5,8 +5,6 @@
use core::mem::align_of;
use core::num::NonZero;
use crate::build_assert;
/// Type representing an alignment, which is always a power of two.
///
/// It is used to validate that a given value is a valid alignment, and to perform masking and
@ -40,10 +38,12 @@ impl Alignment {
/// ```
#[inline(always)]
pub const fn new<const ALIGN: usize>() -> Self {
build_assert!(
const {
assert!(
ALIGN.is_power_of_two(),
"Provided alignment is not a power of two."
);
}
// INVARIANT: `align` is a power of two.
// SAFETY: `align` is a power of two, and thus non-zero.

View file

@ -414,14 +414,17 @@ where
// SAFETY: By the type invariant of `Self`, all non-null `rb_node` pointers stored in `self`
// point to the links field of `Node<K, V>` objects.
let this = unsafe { container_of!(node, Node<K, V>, links) };
// SAFETY: `this` is a non-null node so it is valid by the type invariants.
node = match key.cmp(unsafe { &(*this).key }) {
let this_ref = unsafe { &*this };
// SAFETY: `node` is a non-null node so it is valid by the type invariants.
Ordering::Less => unsafe { (*node).rb_left },
// SAFETY: `node` is a non-null node so it is valid by the type invariants.
Ordering::Greater => unsafe { (*node).rb_right },
// SAFETY: `node` is a non-null node so it is valid by the type invariants.
Ordering::Equal => return Some(unsafe { &(*this).value }),
let node_ref = unsafe { &*node };
node = match key.cmp(&this_ref.key) {
Ordering::Less => node_ref.rb_left,
Ordering::Greater => node_ref.rb_right,
Ordering::Equal => return Some(&this_ref.value),
}
}
None
@ -498,10 +501,10 @@ where
let this = unsafe { container_of!(node, Node<K, V>, links) };
// SAFETY: `this` is a non-null node so it is valid by the type invariants.
let this_key = unsafe { &(*this).key };
// SAFETY: `node` is a non-null node so it is valid by the type invariants.
let left_child = unsafe { (*node).rb_left };
// SAFETY: `node` is a non-null node so it is valid by the type invariants.
let right_child = unsafe { (*node).rb_right };
let node_ref = unsafe { &*node };
match key.cmp(this_key) {
Ordering::Equal => {
// SAFETY: `this` is a non-null node so it is valid by the type invariants.
@ -509,7 +512,7 @@ where
break;
}
Ordering::Greater => {
node = right_child;
node = node_ref.rb_right;
}
Ordering::Less => {
let is_better_match = match best_key {
@ -521,7 +524,7 @@ where
// SAFETY: `this` is a non-null node so it is valid by the type invariants.
best_links = Some(unsafe { NonNull::new_unchecked(&mut (*this).links) });
}
node = left_child;
node = node_ref.rb_left;
}
};
}

53
rust/kernel/safety.rs Normal file
View file

@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-2.0
//! Safety related APIs.
/// Checks that a precondition of an unsafe function is followed.
///
/// The check is enabled at runtime if debug assertions (`CONFIG_RUST_DEBUG_ASSERTIONS`)
/// are enabled. Otherwise, this macro is a no-op.
///
/// # Examples
///
/// ```no_run
/// use kernel::unsafe_precondition_assert;
///
/// struct RawBuffer<T: Copy, const N: usize> {
/// data: [T; N],
/// }
///
/// impl<T: Copy, const N: usize> RawBuffer<T, N> {
/// /// # Safety
/// ///
/// /// The caller must ensure that `index` is less than `N`.
/// unsafe fn set_unchecked(&mut self, index: usize, value: T) {
/// unsafe_precondition_assert!(
/// index < N,
/// "RawBuffer::set_unchecked() requires index ({index}) < N ({N})"
/// );
///
/// // SAFETY: By the safety requirements of this function, `index` is valid.
/// unsafe {
/// *self.data.get_unchecked_mut(index) = value;
/// }
/// }
/// }
/// ```
///
/// # Panics
///
/// Panics if the expression is evaluated to [`false`] at runtime.
#[macro_export]
macro_rules! unsafe_precondition_assert {
($cond:expr $(,)?) => {
$crate::unsafe_precondition_assert!(@inner $cond, ::core::stringify!($cond))
};
($cond:expr, $($arg:tt)+) => {
$crate::unsafe_precondition_assert!(@inner $cond, $crate::prelude::fmt!($($arg)+))
};
(@inner $cond:expr, $msg:expr) => {
::core::debug_assert!($cond, "unsafe precondition violated: {}", $msg)
};
}

View file

@ -170,6 +170,10 @@ macro_rules! impl_frombytes {
}
impl_frombytes! {
// SAFETY: Inhabited ZSTs only have one possible bit pattern, and these two have no invariant.
(),
{<T>} core::marker::PhantomData<T>,
// SAFETY: All bit patterns are acceptable values of the types below.
u8, u16, u32, u64, usize,
i8, i16, i32, i64, isize,
@ -230,6 +234,10 @@ macro_rules! impl_asbytes {
}
impl_asbytes! {
// SAFETY: Inhabited ZSTs only have one possible bit pattern, and these two have no invariant.
(),
{<T>} core::marker::PhantomData<T>,
// SAFETY: Instances of the following types have no uninitialized portions.
u8, u16, u32, u64, usize,
i8, i16, i32, i64, isize,

View file

@ -1,23 +1,36 @@
// SPDX-License-Identifier: GPL-2.0
use proc_macro::{token_stream, Ident, TokenStream, TokenTree};
use proc_macro2::{
Ident,
TokenStream,
TokenTree, //
};
use syn::{
parse::{
Parse,
ParseStream, //
},
Result,
Token, //
};
use crate::helpers::expect_punct;
pub(crate) struct Input {
a: Ident,
_comma: Token![,],
b: Ident,
}
fn expect_ident(it: &mut token_stream::IntoIter) -> Ident {
if let Some(TokenTree::Ident(ident)) = it.next() {
ident
} else {
panic!("Expected Ident")
impl Parse for Input {
fn parse(input: ParseStream<'_>) -> Result<Self> {
Ok(Self {
a: input.parse()?,
_comma: input.parse()?,
b: input.parse()?,
})
}
}
pub(crate) fn concat_idents(ts: TokenStream) -> TokenStream {
let mut it = ts.into_iter();
let a = expect_ident(&mut it);
assert_eq!(expect_punct(&mut it), ',');
let b = expect_ident(&mut it);
assert!(it.next().is_none(), "only two idents can be concatenated");
pub(crate) fn concat_idents(Input { a, b, .. }: Input) -> TokenStream {
let res = Ident::new(&format!("{a}{b}"), b.span());
TokenStream::from_iter([TokenTree::Ident(res)])
}

View file

@ -1,19 +1,16 @@
// SPDX-License-Identifier: GPL-2.0
use crate::helpers::function_name;
use proc_macro::TokenStream;
use proc_macro2::TokenStream;
use quote::quote;
/// Please see [`crate::export`] for documentation.
pub(crate) fn export(_attr: TokenStream, ts: TokenStream) -> TokenStream {
let Some(name) = function_name(ts.clone()) else {
return "::core::compile_error!(\"The #[export] attribute must be used on a function.\");"
.parse::<TokenStream>()
.unwrap();
};
pub(crate) fn export(f: syn::ItemFn) -> TokenStream {
let name = &f.sig.ident;
quote! {
// This verifies that the function has the same signature as the declaration generated by
// bindgen. It makes use of the fact that all branches of an if/else must have the same type.
let signature_check = quote!(
// bindgen. It makes use of the fact that all branches of an if/else must have the same
// type.
const _: () = {
if true {
::kernel::bindings::#name
@ -21,9 +18,8 @@ pub(crate) fn export(_attr: TokenStream, ts: TokenStream) -> TokenStream {
#name
};
};
);
let no_mangle = quote!(#[no_mangle]);
TokenStream::from_iter([signature_check, no_mangle, ts])
#[no_mangle]
#f
}
}

View file

@ -1,8 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
use proc_macro::{Ident, TokenStream, TokenTree};
use std::collections::BTreeSet;
use proc_macro2::{Ident, TokenStream, TokenTree};
use quote::quote_spanned;
/// Please see [`crate::fmt`] for documentation.
pub(crate) fn fmt(input: TokenStream) -> TokenStream {
let mut input = input.into_iter();

View file

@ -1,103 +1,43 @@
// SPDX-License-Identifier: GPL-2.0
use proc_macro::{token_stream, Group, Ident, TokenStream, TokenTree};
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{
parse::{
Parse,
ParseStream, //
},
Attribute,
Error,
LitStr,
Result, //
};
pub(crate) fn try_ident(it: &mut token_stream::IntoIter) -> Option<String> {
if let Some(TokenTree::Ident(ident)) = it.next() {
Some(ident.to_string())
} else {
None
/// A string literal that is required to have ASCII value only.
pub(crate) struct AsciiLitStr(LitStr);
impl Parse for AsciiLitStr {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let s: LitStr = input.parse()?;
if !s.value().is_ascii() {
return Err(Error::new_spanned(s, "expected ASCII-only string literal"));
}
Ok(Self(s))
}
}
pub(crate) fn try_sign(it: &mut token_stream::IntoIter) -> Option<char> {
let peek = it.clone().next();
match peek {
Some(TokenTree::Punct(punct)) if punct.as_char() == '-' => {
let _ = it.next();
Some(punct.as_char())
}
_ => None,
impl ToTokens for AsciiLitStr {
fn to_tokens(&self, ts: &mut TokenStream) {
self.0.to_tokens(ts);
}
}
pub(crate) fn try_literal(it: &mut token_stream::IntoIter) -> Option<String> {
if let Some(TokenTree::Literal(literal)) = it.next() {
Some(literal.to_string())
} else {
None
impl AsciiLitStr {
pub(crate) fn value(&self) -> String {
self.0.value()
}
}
pub(crate) fn try_string(it: &mut token_stream::IntoIter) -> Option<String> {
try_literal(it).and_then(|string| {
if string.starts_with('\"') && string.ends_with('\"') {
let content = &string[1..string.len() - 1];
if content.contains('\\') {
panic!("Escape sequences in string literals not yet handled");
}
Some(content.to_string())
} else if string.starts_with("r\"") {
panic!("Raw string literals are not yet handled");
} else {
None
}
})
}
pub(crate) fn expect_ident(it: &mut token_stream::IntoIter) -> String {
try_ident(it).expect("Expected Ident")
}
pub(crate) fn expect_punct(it: &mut token_stream::IntoIter) -> char {
if let TokenTree::Punct(punct) = it.next().expect("Reached end of token stream for Punct") {
punct.as_char()
} else {
panic!("Expected Punct");
}
}
pub(crate) fn expect_string(it: &mut token_stream::IntoIter) -> String {
try_string(it).expect("Expected string")
}
pub(crate) fn expect_string_ascii(it: &mut token_stream::IntoIter) -> String {
let string = try_string(it).expect("Expected string");
assert!(string.is_ascii(), "Expected ASCII string");
string
}
pub(crate) fn expect_group(it: &mut token_stream::IntoIter) -> Group {
if let TokenTree::Group(group) = it.next().expect("Reached end of token stream for Group") {
group
} else {
panic!("Expected Group");
}
}
pub(crate) fn expect_end(it: &mut token_stream::IntoIter) {
if it.next().is_some() {
panic!("Expected end");
}
}
/// Given a function declaration, finds the name of the function.
pub(crate) fn function_name(input: TokenStream) -> Option<Ident> {
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
}
pub(crate) fn file() -> String {
#[cfg(not(CONFIG_RUSTC_HAS_SPAN_FILE))]
{
@ -115,16 +55,7 @@ pub(crate) fn file() -> String {
}
}
/// Parse a token stream of the form `expected_name: "value",` and return the
/// string in the position of "value".
///
/// # Panics
///
/// - On parse error.
pub(crate) fn expect_string_field(it: &mut token_stream::IntoIter, expected_name: &str) -> String {
assert_eq!(expect_ident(it), expected_name);
assert_eq!(expect_punct(it), ':');
let string = expect_string(it);
assert_eq!(expect_punct(it), ',');
string
/// Obtain all `#[cfg]` attributes.
pub(crate) fn gather_cfg_attrs(attr: &[Attribute]) -> impl Iterator<Item = &Attribute> + '_ {
attr.iter().filter(|a| a.path().is_ident("cfg"))
}

View file

@ -4,80 +4,50 @@
//!
//! Copyright (c) 2023 José Expósito <jose.exposito89@gmail.com>
use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
use std::collections::HashMap;
use std::fmt::Write;
use std::ffi::CString;
pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream {
let attr = attr.to_string();
if attr.is_empty() {
panic!("Missing test name in `#[kunit_tests(test_name)]` macro")
}
if attr.len() > 255 {
panic!("The test suite name `{attr}` exceeds the maximum length of 255 bytes")
}
let mut tokens: Vec<_> = ts.into_iter().collect();
// Scan for the `mod` keyword.
tokens
.iter()
.find_map(|token| match token {
TokenTree::Ident(ident) => match ident.to_string().as_str() {
"mod" => Some(true),
_ => None,
},
_ => None,
})
.expect("`#[kunit_tests(test_name)]` attribute should only be applied to modules");
// Retrieve the main body. The main body should be the last token tree.
let body = match tokens.pop() {
Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => group,
_ => panic!("Cannot locate main body of module"),
use proc_macro2::TokenStream;
use quote::{
format_ident,
quote,
ToTokens, //
};
use syn::{
parse_quote,
Error,
Ident,
Item,
ItemMod,
LitCStr,
Result, //
};
// Get the functions set as tests. Search for `[test]` -> `fn`.
let mut body_it = body.stream().into_iter();
let mut tests = Vec::new();
let mut attributes: HashMap<String, TokenStream> = HashMap::new();
while let Some(token) = body_it.next() {
match token {
TokenTree::Punct(ref p) if p.as_char() == '#' => match body_it.next() {
Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket => {
if let Some(TokenTree::Ident(name)) = g.stream().into_iter().next() {
// Collect attributes because we need to find which are tests. We also
// need to copy `cfg` attributes so tests can be conditionally enabled.
attributes
.entry(name.to_string())
.or_default()
.extend([token, TokenTree::Group(g)]);
}
continue;
}
_ => (),
},
TokenTree::Ident(i) if i.to_string() == "fn" && attributes.contains_key("test") => {
if let Some(TokenTree::Ident(test_name)) = body_it.next() {
tests.push((test_name, attributes.remove("cfg").unwrap_or_default()))
}
pub(crate) fn kunit_tests(test_suite: Ident, mut module: ItemMod) -> Result<TokenStream> {
if test_suite.to_string().len() > 255 {
return Err(Error::new_spanned(
test_suite,
"test suite names cannot exceed the maximum length of 255 bytes",
));
}
_ => (),
}
attributes.clear();
}
// We cannot handle modules that defer to another file (e.g. `mod foo;`).
let Some((module_brace, module_items)) = module.content.take() else {
Err(Error::new_spanned(
module,
"`#[kunit_tests(test_name)]` attribute should only be applied to inline modules",
))?
};
// Add `#[cfg(CONFIG_KUNIT="y")]` before the module declaration.
let config_kunit = "#[cfg(CONFIG_KUNIT=\"y\")]".to_owned().parse().unwrap();
tokens.insert(
0,
TokenTree::Group(Group::new(Delimiter::None, config_kunit)),
);
// Make the entire module gated behind `CONFIG_KUNIT`.
module
.attrs
.insert(0, parse_quote!(#[cfg(CONFIG_KUNIT="y")]));
let mut processed_items = Vec::new();
let mut test_cases = Vec::new();
// Generate the test KUnit test suite and a test case for each `#[test]`.
//
// The code generated for the following test module:
//
// ```
@ -102,105 +72,100 @@ pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream {
// unsafe extern "C" fn kunit_rust_wrapper_bar(_test: *mut ::kernel::bindings::kunit) { bar(); }
//
// static mut TEST_CASES: [::kernel::bindings::kunit_case; 3] = [
// ::kernel::kunit::kunit_case(::kernel::c_str!("foo"), kunit_rust_wrapper_foo),
// ::kernel::kunit::kunit_case(::kernel::c_str!("bar"), kunit_rust_wrapper_bar),
// ::kernel::kunit::kunit_case_null(),
// ::kernel::kunit::kunit_case(c"foo", kunit_rust_wrapper_foo),
// ::kernel::kunit::kunit_case(c"bar", kunit_rust_wrapper_bar),
// ::pin_init::zeroed(),
// ];
//
// ::kernel::kunit_unsafe_test_suite!(kunit_test_suit_name, TEST_CASES);
// ```
let mut kunit_macros = "".to_owned();
let mut test_cases = "".to_owned();
let mut assert_macros = "".to_owned();
let path = crate::helpers::file();
let num_tests = tests.len();
for (test, cfg_attr) in tests {
let kunit_wrapper_fn_name = format!("kunit_rust_wrapper_{test}");
// Append any `cfg` attributes the user might have written on their tests so we don't
// attempt to call them when they are `cfg`'d out. An extra `use` is used here to reduce
// the length of the assert message.
let kunit_wrapper = format!(
r#"unsafe extern "C" fn {kunit_wrapper_fn_name}(_test: *mut ::kernel::bindings::kunit)
{{
//
// Non-function items (e.g. imports) are preserved.
for item in module_items {
let Item::Fn(mut f) = item else {
processed_items.push(item);
continue;
};
// TODO: Replace below with `extract_if` when MSRV is bumped above 1.85.
let before_len = f.attrs.len();
f.attrs.retain(|attr| !attr.path().is_ident("test"));
if f.attrs.len() == before_len {
processed_items.push(Item::Fn(f));
continue;
}
let test = f.sig.ident.clone();
// Retrieve `#[cfg]` applied on the function which needs to be present on derived items too.
let cfg_attrs: Vec<_> = f
.attrs
.iter()
.filter(|attr| attr.path().is_ident("cfg"))
.cloned()
.collect();
// Before the test, override usual `assert!` and `assert_eq!` macros with ones that call
// KUnit instead.
let test_str = test.to_string();
let path = CString::new(crate::helpers::file()).expect("file path cannot contain NUL");
processed_items.push(parse_quote! {
#[allow(unused)]
macro_rules! assert {
($cond:expr $(,)?) => {{
kernel::kunit_assert!(#test_str, #path, 0, $cond);
}}
}
});
processed_items.push(parse_quote! {
#[allow(unused)]
macro_rules! assert_eq {
($left:expr, $right:expr $(,)?) => {{
kernel::kunit_assert_eq!(#test_str, #path, 0, $left, $right);
}}
}
});
// Add back the test item.
processed_items.push(Item::Fn(f));
let kunit_wrapper_fn_name = format_ident!("kunit_rust_wrapper_{test}");
let test_cstr = LitCStr::new(
&CString::new(test_str.as_str()).expect("identifier cannot contain NUL"),
test.span(),
);
processed_items.push(parse_quote! {
unsafe extern "C" fn #kunit_wrapper_fn_name(_test: *mut ::kernel::bindings::kunit) {
(*_test).status = ::kernel::bindings::kunit_status_KUNIT_SKIPPED;
{cfg_attr} {{
// Append any `cfg` attributes the user might have written on their tests so we
// don't attempt to call them when they are `cfg`'d out. An extra `use` is used
// here to reduce the length of the assert message.
#(#cfg_attrs)*
{
(*_test).status = ::kernel::bindings::kunit_status_KUNIT_SUCCESS;
use ::kernel::kunit::is_test_result_ok;
assert!(is_test_result_ok({test}()));
}}
}}"#,
);
writeln!(kunit_macros, "{kunit_wrapper}").unwrap();
writeln!(
test_cases,
" ::kernel::kunit::kunit_case(::kernel::c_str!(\"{test}\"), {kunit_wrapper_fn_name}),"
)
.unwrap();
writeln!(
assert_macros,
r#"
/// Overrides the usual [`assert!`] macro with one that calls KUnit instead.
#[allow(unused)]
macro_rules! assert {{
($cond:expr $(,)?) => {{{{
kernel::kunit_assert!("{test}", "{path}", 0, $cond);
}}}}
}}
/// Overrides the usual [`assert_eq!`] macro with one that calls KUnit instead.
#[allow(unused)]
macro_rules! assert_eq {{
($left:expr, $right:expr $(,)?) => {{{{
kernel::kunit_assert_eq!("{test}", "{path}", 0, $left, $right);
}}}}
}}
"#
)
.unwrap();
}
writeln!(kunit_macros).unwrap();
writeln!(
kunit_macros,
"static mut TEST_CASES: [::kernel::bindings::kunit_case; {}] = [\n{test_cases} ::kernel::kunit::kunit_case_null(),\n];",
num_tests + 1
)
.unwrap();
writeln!(
kunit_macros,
"::kernel::kunit_unsafe_test_suite!({attr}, TEST_CASES);"
)
.unwrap();
// Remove the `#[test]` macros.
// We do this at a token level, in order to preserve span information.
let mut new_body = vec![];
let mut body_it = body.stream().into_iter();
while let Some(token) = body_it.next() {
match token {
TokenTree::Punct(ref c) if c.as_char() == '#' => match body_it.next() {
Some(TokenTree::Group(group)) if group.to_string() == "[test]" => (),
Some(next) => {
new_body.extend([token, next]);
}
_ => {
new_body.push(token);
}
},
_ => {
new_body.push(token);
assert!(is_test_result_ok(#test()));
}
}
});
test_cases.push(quote!(
::kernel::kunit::kunit_case(#test_cstr, #kunit_wrapper_fn_name)
));
}
let mut final_body = TokenStream::new();
final_body.extend::<TokenStream>(assert_macros.parse().unwrap());
final_body.extend(new_body);
final_body.extend::<TokenStream>(kunit_macros.parse().unwrap());
let num_tests_plus_1 = test_cases.len() + 1;
processed_items.push(parse_quote! {
static mut TEST_CASES: [::kernel::bindings::kunit_case; #num_tests_plus_1] = [
#(#test_cases,)*
::pin_init::zeroed(),
];
});
processed_items.push(parse_quote! {
::kernel::kunit_unsafe_test_suite!(#test_suite, TEST_CASES);
});
tokens.push(TokenTree::Group(Group::new(Delimiter::Brace, final_body)));
tokens.into_iter().collect()
module.content = Some((module_brace, processed_items));
Ok(module.to_token_stream())
}

View file

@ -11,8 +11,6 @@
// to avoid depending on the full `proc_macro_span` on Rust >= 1.88.0.
#![cfg_attr(not(CONFIG_RUSTC_HAS_SPAN_FILE), feature(proc_macro_span))]
#[macro_use]
mod quote;
mod concat_idents;
mod export;
mod fmt;
@ -24,6 +22,8 @@ mod vtable;
use proc_macro::TokenStream;
use syn::parse_macro_input;
/// Declares a kernel module.
///
/// The `type` argument should be a type which implements the [`Module`]
@ -131,8 +131,10 @@ use proc_macro::TokenStream;
/// - `firmware`: array of ASCII string literals of the firmware files of
/// the kernel module.
#[proc_macro]
pub fn module(ts: TokenStream) -> TokenStream {
module::module(ts)
pub fn module(input: TokenStream) -> TokenStream {
module::module(parse_macro_input!(input))
.unwrap_or_else(|e| e.into_compile_error())
.into()
}
/// Declares or implements a vtable trait.
@ -154,7 +156,7 @@ pub fn module(ts: TokenStream) -> TokenStream {
/// case the default implementation will never be executed. The reason for this
/// is that the functions will be called through function pointers installed in
/// C side vtables. When an optional method is not implemented on a `#[vtable]`
/// trait, a NULL entry is installed in the vtable. Thus the default
/// trait, a `NULL` entry is installed in the vtable. Thus the default
/// implementation is never called. Since these traits are not designed to be
/// used on the Rust side, it should not be possible to call the default
/// implementation. This is done to ensure that we call the vtable methods
@ -206,8 +208,11 @@ pub fn module(ts: TokenStream) -> TokenStream {
///
/// [`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)
pub fn vtable(attr: TokenStream, input: TokenStream) -> TokenStream {
parse_macro_input!(attr as syn::parse::Nothing);
vtable::vtable(parse_macro_input!(input))
.unwrap_or_else(|e| e.into_compile_error())
.into()
}
/// Export a function so that C code can call it via a header file.
@ -229,8 +234,9 @@ pub fn vtable(attr: TokenStream, ts: TokenStream) -> TokenStream {
/// This macro is *not* the same as the C macros `EXPORT_SYMBOL_*`. All Rust symbols are currently
/// automatically exported with `EXPORT_SYMBOL_GPL`.
#[proc_macro_attribute]
pub fn export(attr: TokenStream, ts: TokenStream) -> TokenStream {
export::export(attr, ts)
pub fn export(attr: TokenStream, input: TokenStream) -> TokenStream {
parse_macro_input!(attr as syn::parse::Nothing);
export::export(parse_macro_input!(input)).into()
}
/// Like [`core::format_args!`], but automatically wraps arguments in [`kernel::fmt::Adapter`].
@ -248,7 +254,7 @@ pub fn export(attr: TokenStream, ts: TokenStream) -> TokenStream {
/// [`pr_info!`]: ../kernel/macro.pr_info.html
#[proc_macro]
pub fn fmt(input: TokenStream) -> TokenStream {
fmt::fmt(input)
fmt::fmt(input.into()).into()
}
/// Concatenate two identifiers.
@ -305,8 +311,8 @@ pub fn fmt(input: TokenStream) -> TokenStream {
/// assert_eq!(BR_OK, binder_driver_return_protocol_BR_OK);
/// ```
#[proc_macro]
pub fn concat_idents(ts: TokenStream) -> TokenStream {
concat_idents::concat_idents(ts)
pub fn concat_idents(input: TokenStream) -> TokenStream {
concat_idents::concat_idents(parse_macro_input!(input)).into()
}
/// Paste identifiers together.
@ -444,9 +450,12 @@ pub fn concat_idents(ts: TokenStream) -> TokenStream {
/// [`paste`]: https://docs.rs/paste/
#[proc_macro]
pub fn paste(input: TokenStream) -> TokenStream {
let mut tokens = input.into_iter().collect();
let mut tokens = proc_macro2::TokenStream::from(input).into_iter().collect();
paste::expand(&mut tokens);
tokens.into_iter().collect()
tokens
.into_iter()
.collect::<proc_macro2::TokenStream>()
.into()
}
/// Registers a KUnit test suite and its test cases using a user-space like syntax.
@ -472,6 +481,8 @@ pub fn paste(input: TokenStream) -> TokenStream {
/// }
/// ```
#[proc_macro_attribute]
pub fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream {
kunit::kunit_tests(attr, ts)
pub fn kunit_tests(attr: TokenStream, input: TokenStream) -> TokenStream {
kunit::kunit_tests(parse_macro_input!(attr), parse_macro_input!(input))
.unwrap_or_else(|e| e.into_compile_error())
.into()
}

View file

@ -1,32 +1,42 @@
// SPDX-License-Identifier: GPL-2.0
use std::ffi::CString;
use proc_macro2::{
Literal,
TokenStream, //
};
use quote::{
format_ident,
quote, //
};
use syn::{
braced,
bracketed,
ext::IdentExt,
parse::{
Parse,
ParseStream, //
},
parse_quote,
punctuated::Punctuated,
Error,
Expr,
Ident,
LitStr,
Path,
Result,
Token,
Type, //
};
use crate::helpers::*;
use proc_macro::{token_stream, Delimiter, Literal, TokenStream, TokenTree};
use std::fmt::Write;
fn expect_string_array(it: &mut token_stream::IntoIter) -> Vec<String> {
let group = expect_group(it);
assert_eq!(group.delimiter(), Delimiter::Bracket);
let mut values = Vec::new();
let mut it = group.stream().into_iter();
while let Some(val) = try_string(&mut it) {
assert!(val.is_ascii(), "Expected ASCII string");
values.push(val);
match it.next() {
Some(TokenTree::Punct(punct)) => assert_eq!(punct.as_char(), ','),
None => break,
_ => panic!("Expected ',' or end of array"),
}
}
values
}
struct ModInfoBuilder<'a> {
module: &'a str,
counter: usize,
buffer: String,
param_buffer: String,
ts: TokenStream,
param_ts: TokenStream,
}
impl<'a> ModInfoBuilder<'a> {
@ -34,8 +44,8 @@ impl<'a> ModInfoBuilder<'a> {
ModInfoBuilder {
module,
counter: 0,
buffer: String::new(),
param_buffer: String::new(),
ts: TokenStream::new(),
param_ts: TokenStream::new(),
}
}
@ -52,33 +62,31 @@ impl<'a> ModInfoBuilder<'a> {
// Loadable modules' modinfo strings go as-is.
format!("{field}={content}\0")
};
let buffer = if param {
&mut self.param_buffer
let length = string.len();
let string = Literal::byte_string(string.as_bytes());
let cfg = if builtin {
quote!(#[cfg(not(MODULE))])
} else {
&mut self.buffer
quote!(#[cfg(MODULE)])
};
write!(
buffer,
"
{cfg}
#[doc(hidden)]
#[cfg_attr(not(target_os = \"macos\"), link_section = \".modinfo\")]
#[used(compiler)]
pub static __{module}_{counter}: [u8; {length}] = *{string};
",
cfg = if builtin {
"#[cfg(not(MODULE))]"
} else {
"#[cfg(MODULE)]"
},
let counter = format_ident!(
"__{module}_{counter}",
module = self.module.to_uppercase(),
counter = self.counter,
length = string.len(),
string = Literal::byte_string(string.as_bytes()),
)
.unwrap();
counter = self.counter
);
let item = quote! {
#cfg
#[cfg_attr(not(target_os = "macos"), link_section = ".modinfo")]
#[used(compiler)]
pub static #counter: [u8; #length] = *#string;
};
if param {
self.param_ts.extend(item);
} else {
self.ts.extend(item);
}
self.counter += 1;
}
@ -111,201 +119,160 @@ impl<'a> ModInfoBuilder<'a> {
};
for param in params {
let ops = param_ops_path(&param.ptype);
let param_name_str = param.name.to_string();
let param_type_str = param.ptype.to_string();
let ops = param_ops_path(&param_type_str);
// Note: The spelling of these fields is dictated by the user space
// tool `modinfo`.
self.emit_param("parmtype", &param.name, &param.ptype);
self.emit_param("parm", &param.name, &param.description);
self.emit_param("parmtype", &param_name_str, &param_type_str);
self.emit_param("parm", &param_name_str, &param.description.value());
write!(
self.param_buffer,
"
pub(crate) static {param_name}:
::kernel::module_param::ModuleParamAccess<{param_type}> =
::kernel::module_param::ModuleParamAccess::new({param_default});
let static_name = format_ident!("__{}_{}_struct", self.module, param.name);
let param_name_cstr =
CString::new(param_name_str).expect("name contains NUL-terminator");
let param_name_cstr_with_module =
CString::new(format!("{}.{}", self.module, param.name))
.expect("name contains NUL-terminator");
const _: () = {{
#[link_section = \"__param\"]
#[used]
static __{module_name}_{param_name}_struct:
let param_name = &param.name;
let param_type = &param.ptype;
let param_default = &param.default;
self.param_ts.extend(quote! {
#[allow(non_upper_case_globals)]
pub(crate) static #param_name:
::kernel::module_param::ModuleParamAccess<#param_type> =
::kernel::module_param::ModuleParamAccess::new(#param_default);
const _: () = {
#[allow(non_upper_case_globals)]
#[link_section = "__param"]
#[used(compiler)]
static #static_name:
::kernel::module_param::KernelParam =
::kernel::module_param::KernelParam::new(
::kernel::bindings::kernel_param {{
name: if ::core::cfg!(MODULE) {{
::kernel::c_str!(\"{param_name}\").to_bytes_with_nul()
}} else {{
::kernel::c_str!(\"{module_name}.{param_name}\")
.to_bytes_with_nul()
}}.as_ptr(),
::kernel::bindings::kernel_param {
name: kernel::str::as_char_ptr_in_const_context(
if ::core::cfg!(MODULE) {
#param_name_cstr
} else {
#param_name_cstr_with_module
}
),
// SAFETY: `__this_module` is constructed by the kernel at load
// time and will not be freed until the module is unloaded.
#[cfg(MODULE)]
mod_: unsafe {{
mod_: unsafe {
core::ptr::from_ref(&::kernel::bindings::__this_module)
.cast_mut()
}},
},
#[cfg(not(MODULE))]
mod_: ::core::ptr::null_mut(),
ops: core::ptr::from_ref(&{ops}),
ops: core::ptr::from_ref(&#ops),
perm: 0, // Will not appear in sysfs
level: -1,
flags: 0,
__bindgen_anon_1: ::kernel::bindings::kernel_param__bindgen_ty_1 {{
arg: {param_name}.as_void_ptr()
}},
}}
__bindgen_anon_1: ::kernel::bindings::kernel_param__bindgen_ty_1 {
arg: #param_name.as_void_ptr()
},
}
);
}};
",
module_name = info.name,
param_type = param.ptype,
param_default = param.default,
param_name = param.name,
ops = ops,
)
.unwrap();
};
});
}
}
}
fn param_ops_path(param_type: &str) -> &'static str {
fn param_ops_path(param_type: &str) -> Path {
match param_type {
"i8" => "::kernel::module_param::PARAM_OPS_I8",
"u8" => "::kernel::module_param::PARAM_OPS_U8",
"i16" => "::kernel::module_param::PARAM_OPS_I16",
"u16" => "::kernel::module_param::PARAM_OPS_U16",
"i32" => "::kernel::module_param::PARAM_OPS_I32",
"u32" => "::kernel::module_param::PARAM_OPS_U32",
"i64" => "::kernel::module_param::PARAM_OPS_I64",
"u64" => "::kernel::module_param::PARAM_OPS_U64",
"isize" => "::kernel::module_param::PARAM_OPS_ISIZE",
"usize" => "::kernel::module_param::PARAM_OPS_USIZE",
"i8" => parse_quote!(::kernel::module_param::PARAM_OPS_I8),
"u8" => parse_quote!(::kernel::module_param::PARAM_OPS_U8),
"i16" => parse_quote!(::kernel::module_param::PARAM_OPS_I16),
"u16" => parse_quote!(::kernel::module_param::PARAM_OPS_U16),
"i32" => parse_quote!(::kernel::module_param::PARAM_OPS_I32),
"u32" => parse_quote!(::kernel::module_param::PARAM_OPS_U32),
"i64" => parse_quote!(::kernel::module_param::PARAM_OPS_I64),
"u64" => parse_quote!(::kernel::module_param::PARAM_OPS_U64),
"isize" => parse_quote!(::kernel::module_param::PARAM_OPS_ISIZE),
"usize" => parse_quote!(::kernel::module_param::PARAM_OPS_USIZE),
t => panic!("Unsupported parameter type {}", t),
}
}
fn expect_param_default(param_it: &mut token_stream::IntoIter) -> String {
assert_eq!(expect_ident(param_it), "default");
assert_eq!(expect_punct(param_it), ':');
let sign = try_sign(param_it);
let default = try_literal(param_it).expect("Expected default param value");
assert_eq!(expect_punct(param_it), ',');
let mut value = sign.map(String::from).unwrap_or_default();
value.push_str(&default);
value
}
/// Parse fields that are required to use a specific order.
///
/// As fields must follow a specific order, we *could* just parse fields one by one by peeking.
/// However the error message generated when implementing that way is not very friendly.
///
/// So instead we parse fields in an arbitrary order, but only enforce the ordering after parsing,
/// and if the wrong order is used, the proper order is communicated to the user with error message.
///
/// Usage looks like this:
/// ```ignore
/// parse_ordered_fields! {
/// from input;
///
/// // This will extract "foo: <field>" into a variable named "foo".
/// // The variable will have type `Option<_>`.
/// foo => <expression that parses the field>,
///
/// // If you need the variable name to be different than the key name.
/// // This extracts "baz: <field>" into a variable named "bar".
/// // You might want this if "baz" is a keyword.
/// baz as bar => <expression that parse the field>,
///
/// // You can mark a key as required, and the variable will no longer be `Option`.
/// // foobar will be of type `Expr` instead of `Option<Expr>`.
/// foobar [required] => input.parse::<Expr>()?,
/// }
/// ```
macro_rules! parse_ordered_fields {
(@gen
[$input:expr]
[$([$name:ident; $key:ident; $parser:expr])*]
[$([$req_name:ident; $req_key:ident])*]
) => {
$(let mut $name = None;)*
#[derive(Debug, Default)]
struct ModuleInfo {
type_: String,
license: String,
name: String,
authors: Option<Vec<String>>,
description: Option<String>,
alias: Option<Vec<String>>,
firmware: Option<Vec<String>>,
imports_ns: Option<Vec<String>>,
params: Option<Vec<Parameter>>,
}
const EXPECTED_KEYS: &[&str] = &[$(stringify!($key),)*];
const REQUIRED_KEYS: &[&str] = &[$(stringify!($req_key),)*];
#[derive(Debug)]
struct Parameter {
name: String,
ptype: String,
default: String,
description: String,
}
fn expect_params(it: &mut token_stream::IntoIter) -> Vec<Parameter> {
let params = expect_group(it);
assert_eq!(params.delimiter(), Delimiter::Brace);
let mut it = params.stream().into_iter();
let mut parsed = Vec::new();
loop {
let param_name = match it.next() {
Some(TokenTree::Ident(ident)) => ident.to_string(),
Some(_) => panic!("Expected Ident or end"),
None => break,
};
assert_eq!(expect_punct(&mut it), ':');
let param_type = expect_ident(&mut it);
let group = expect_group(&mut it);
assert_eq!(group.delimiter(), Delimiter::Brace);
assert_eq!(expect_punct(&mut it), ',');
let mut param_it = group.stream().into_iter();
let param_default = expect_param_default(&mut param_it);
let param_description = expect_string_field(&mut param_it, "description");
expect_end(&mut param_it);
parsed.push(Parameter {
name: param_name,
ptype: param_type,
default: param_default,
description: param_description,
})
}
parsed
}
impl ModuleInfo {
fn parse(it: &mut token_stream::IntoIter) -> Self {
let mut info = ModuleInfo::default();
const EXPECTED_KEYS: &[&str] = &[
"type",
"name",
"authors",
"description",
"license",
"alias",
"firmware",
"imports_ns",
"params",
];
const REQUIRED_KEYS: &[&str] = &["type", "name", "license"];
let span = $input.span();
let mut seen_keys = Vec::new();
loop {
let key = match it.next() {
Some(TokenTree::Ident(ident)) => ident.to_string(),
Some(_) => panic!("Expected Ident or end"),
None => break,
};
while !$input.is_empty() {
let key = $input.call(Ident::parse_any)?;
if seen_keys.contains(&key) {
panic!("Duplicated key \"{key}\". Keys can only be specified once.");
Err(Error::new_spanned(
&key,
format!(r#"duplicated key "{key}". Keys can only be specified once."#),
))?
}
assert_eq!(expect_punct(it), ':');
$input.parse::<Token![:]>()?;
match key.as_str() {
"type" => info.type_ = expect_ident(it),
"name" => info.name = expect_string_ascii(it),
"authors" => info.authors = Some(expect_string_array(it)),
"description" => info.description = Some(expect_string(it)),
"license" => info.license = expect_string_ascii(it),
"alias" => info.alias = Some(expect_string_array(it)),
"firmware" => info.firmware = Some(expect_string_array(it)),
"imports_ns" => info.imports_ns = Some(expect_string_array(it)),
"params" => info.params = Some(expect_params(it)),
_ => panic!("Unknown key \"{key}\". Valid keys are: {EXPECTED_KEYS:?}."),
match &*key.to_string() {
$(
stringify!($key) => $name = Some($parser),
)*
_ => {
Err(Error::new_spanned(
&key,
format!(r#"unknown key "{key}". Valid keys are: {EXPECTED_KEYS:?}."#),
))?
}
}
assert_eq!(expect_punct(it), ',');
$input.parse::<Token![,]>()?;
seen_keys.push(key);
}
expect_end(it);
for key in REQUIRED_KEYS {
if !seen_keys.iter().any(|e| e == key) {
panic!("Missing required key \"{key}\".");
Err(Error::new(span, format!(r#"missing required key "{key}""#)))?
}
}
@ -317,43 +284,190 @@ impl ModuleInfo {
}
if seen_keys != ordered_keys {
panic!("Keys are not ordered as expected. Order them like: {ordered_keys:?}.");
Err(Error::new(
span,
format!(r#"keys are not ordered as expected. Order them like: {ordered_keys:?}."#),
))?
}
info
$(let $req_name = $req_name.expect("required field");)*
};
// Handle required fields.
(@gen
[$input:expr] [$($tok:tt)*] [$($req:tt)*]
$key:ident as $name:ident [required] => $parser:expr,
$($rest:tt)*
) => {
parse_ordered_fields!(
@gen [$input] [$($tok)* [$name; $key; $parser]] [$($req)* [$name; $key]] $($rest)*
)
};
(@gen
[$input:expr] [$($tok:tt)*] [$($req:tt)*]
$name:ident [required] => $parser:expr,
$($rest:tt)*
) => {
parse_ordered_fields!(
@gen [$input] [$($tok)* [$name; $name; $parser]] [$($req)* [$name; $name]] $($rest)*
)
};
// Handle optional fields.
(@gen
[$input:expr] [$($tok:tt)*] [$($req:tt)*]
$key:ident as $name:ident => $parser:expr,
$($rest:tt)*
) => {
parse_ordered_fields!(
@gen [$input] [$($tok)* [$name; $key; $parser]] [$($req)*] $($rest)*
)
};
(@gen
[$input:expr] [$($tok:tt)*] [$($req:tt)*]
$name:ident => $parser:expr,
$($rest:tt)*
) => {
parse_ordered_fields!(
@gen [$input] [$($tok)* [$name; $name; $parser]] [$($req)*] $($rest)*
)
};
(from $input:expr; $($tok:tt)*) => {
parse_ordered_fields!(@gen [$input] [] [] $($tok)*)
}
}
pub(crate) fn module(ts: TokenStream) -> TokenStream {
let mut it = ts.into_iter();
struct Parameter {
name: Ident,
ptype: Ident,
default: Expr,
description: LitStr,
}
let info = ModuleInfo::parse(&mut it);
impl Parse for Parameter {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let name = input.parse()?;
input.parse::<Token![:]>()?;
let ptype = input.parse()?;
let fields;
braced!(fields in input);
parse_ordered_fields! {
from fields;
default [required] => fields.parse()?,
description [required] => fields.parse()?,
}
Ok(Self {
name,
ptype,
default,
description,
})
}
}
pub(crate) struct ModuleInfo {
type_: Type,
license: AsciiLitStr,
name: AsciiLitStr,
authors: Option<Punctuated<AsciiLitStr, Token![,]>>,
description: Option<LitStr>,
alias: Option<Punctuated<AsciiLitStr, Token![,]>>,
firmware: Option<Punctuated<AsciiLitStr, Token![,]>>,
imports_ns: Option<Punctuated<AsciiLitStr, Token![,]>>,
params: Option<Punctuated<Parameter, Token![,]>>,
}
impl Parse for ModuleInfo {
fn parse(input: ParseStream<'_>) -> Result<Self> {
parse_ordered_fields!(
from input;
type as type_ [required] => input.parse()?,
name [required] => input.parse()?,
authors => {
let list;
bracketed!(list in input);
Punctuated::parse_terminated(&list)?
},
description => input.parse()?,
license [required] => input.parse()?,
alias => {
let list;
bracketed!(list in input);
Punctuated::parse_terminated(&list)?
},
firmware => {
let list;
bracketed!(list in input);
Punctuated::parse_terminated(&list)?
},
imports_ns => {
let list;
bracketed!(list in input);
Punctuated::parse_terminated(&list)?
},
params => {
let list;
braced!(list in input);
Punctuated::parse_terminated(&list)?
},
);
Ok(ModuleInfo {
type_,
license,
name,
authors,
description,
alias,
firmware,
imports_ns,
params,
})
}
}
pub(crate) fn module(info: ModuleInfo) -> Result<TokenStream> {
let ModuleInfo {
type_,
license,
name,
authors,
description,
alias,
firmware,
imports_ns,
params: _,
} = &info;
// Rust does not allow hyphens in identifiers, use underscore instead.
let ident = info.name.replace('-', "_");
let ident = name.value().replace('-', "_");
let mut modinfo = ModInfoBuilder::new(ident.as_ref());
if let Some(authors) = &info.authors {
if let Some(authors) = authors {
for author in authors {
modinfo.emit("author", author);
modinfo.emit("author", &author.value());
}
}
if let Some(description) = &info.description {
modinfo.emit("description", description);
if let Some(description) = description {
modinfo.emit("description", &description.value());
}
modinfo.emit("license", &info.license);
if let Some(aliases) = &info.alias {
modinfo.emit("license", &license.value());
if let Some(aliases) = alias {
for alias in aliases {
modinfo.emit("alias", alias);
modinfo.emit("alias", &alias.value());
}
}
if let Some(firmware) = &info.firmware {
if let Some(firmware) = firmware {
for fw in firmware {
modinfo.emit("firmware", fw);
modinfo.emit("firmware", &fw.value());
}
}
if let Some(imports) = &info.imports_ns {
if let Some(imports) = imports_ns {
for ns in imports {
modinfo.emit("import_ns", ns);
modinfo.emit("import_ns", &ns.value());
}
}
@ -364,182 +478,181 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
modinfo.emit_params(&info);
format!(
"
let modinfo_ts = modinfo.ts;
let params_ts = modinfo.param_ts;
let ident_init = format_ident!("__{ident}_init");
let ident_exit = format_ident!("__{ident}_exit");
let ident_initcall = format_ident!("__{ident}_initcall");
let initcall_section = ".initcall6.init";
let global_asm = format!(
r#".section "{initcall_section}", "a"
__{ident}_initcall:
.long __{ident}_init - .
.previous
"#
);
let name_cstr = CString::new(name.value()).expect("name contains NUL-terminator");
Ok(quote! {
/// The module name.
///
/// Used by the printing macros, e.g. [`info!`].
const __LOG_PREFIX: &[u8] = b\"{name}\\0\";
const __LOG_PREFIX: &[u8] = #name_cstr.to_bytes_with_nul();
// SAFETY: `__this_module` is constructed by the kernel at load time and will not be
// freed until the module is unloaded.
#[cfg(MODULE)]
static THIS_MODULE: ::kernel::ThisModule = unsafe {{
extern \"C\" {{
static THIS_MODULE: ::kernel::ThisModule = unsafe {
extern "C" {
static __this_module: ::kernel::types::Opaque<::kernel::bindings::module>;
}}
};
::kernel::ThisModule::from_ptr(__this_module.get())
}};
};
#[cfg(not(MODULE))]
static THIS_MODULE: ::kernel::ThisModule = unsafe {{
static THIS_MODULE: ::kernel::ThisModule = unsafe {
::kernel::ThisModule::from_ptr(::core::ptr::null_mut())
}};
};
/// The `LocalModule` type is the type of the module created by `module!`,
/// `module_pci_driver!`, `module_platform_driver!`, etc.
type LocalModule = {type_};
type LocalModule = #type_;
impl ::kernel::ModuleMetadata for {type_} {{
const NAME: &'static ::kernel::str::CStr = c\"{name}\";
}}
impl ::kernel::ModuleMetadata for #type_ {
const NAME: &'static ::kernel::str::CStr = #name_cstr;
}
// Double nested modules, since then nobody can access the public items inside.
mod __module_init {{
mod __module_init {{
use super::super::{type_};
#[doc(hidden)]
mod __module_init {
mod __module_init {
use pin_init::PinInit;
/// The \"Rust loadable module\" mark.
/// The "Rust loadable module" mark.
//
// This may be best done another way later on, e.g. as a new modinfo
// key or a new section. For the moment, keep it simple.
#[cfg(MODULE)]
#[doc(hidden)]
#[used(compiler)]
static __IS_RUST_MODULE: () = ();
static mut __MOD: ::core::mem::MaybeUninit<{type_}> =
static mut __MOD: ::core::mem::MaybeUninit<super::super::LocalModule> =
::core::mem::MaybeUninit::uninit();
// Loadable modules need to export the `{{init,cleanup}}_module` identifiers.
// Loadable modules need to export the `{init,cleanup}_module` identifiers.
/// # Safety
///
/// This function must not be called after module initialization, because it may be
/// freed after that completes.
#[cfg(MODULE)]
#[doc(hidden)]
#[no_mangle]
#[link_section = \".init.text\"]
pub unsafe extern \"C\" fn init_module() -> ::kernel::ffi::c_int {{
#[link_section = ".init.text"]
pub unsafe extern "C" fn init_module() -> ::kernel::ffi::c_int {
// SAFETY: This function is inaccessible to the outside due to the double
// module wrapping it. It is called exactly once by the C side via its
// unique name.
unsafe {{ __init() }}
}}
unsafe { __init() }
}
#[cfg(MODULE)]
#[doc(hidden)]
#[used(compiler)]
#[link_section = \".init.data\"]
static __UNIQUE_ID___addressable_init_module: unsafe extern \"C\" fn() -> i32 = init_module;
#[link_section = ".init.data"]
static __UNIQUE_ID___addressable_init_module: unsafe extern "C" fn() -> i32 =
init_module;
#[cfg(MODULE)]
#[doc(hidden)]
#[no_mangle]
#[link_section = \".exit.text\"]
pub extern \"C\" fn cleanup_module() {{
#[link_section = ".exit.text"]
pub extern "C" fn cleanup_module() {
// SAFETY:
// - This function is inaccessible to the outside due to the double
// module wrapping it. It is called exactly once by the C side via its
// unique name,
// - furthermore it is only called after `init_module` has returned `0`
// (which delegates to `__init`).
unsafe {{ __exit() }}
}}
unsafe { __exit() }
}
#[cfg(MODULE)]
#[doc(hidden)]
#[used(compiler)]
#[link_section = \".exit.data\"]
static __UNIQUE_ID___addressable_cleanup_module: extern \"C\" fn() = cleanup_module;
#[link_section = ".exit.data"]
static __UNIQUE_ID___addressable_cleanup_module: extern "C" fn() = cleanup_module;
// Built-in modules are initialized through an initcall pointer
// and the identifiers need to be unique.
#[cfg(not(MODULE))]
#[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))]
#[doc(hidden)]
#[link_section = \"{initcall_section}\"]
#[link_section = #initcall_section]
#[used(compiler)]
pub static __{ident}_initcall: extern \"C\" fn() ->
::kernel::ffi::c_int = __{ident}_init;
pub static #ident_initcall: extern "C" fn() ->
::kernel::ffi::c_int = #ident_init;
#[cfg(not(MODULE))]
#[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)]
::core::arch::global_asm!(
r#\".section \"{initcall_section}\", \"a\"
__{ident}_initcall:
.long __{ident}_init - .
.previous
\"#
);
::core::arch::global_asm!(#global_asm);
#[cfg(not(MODULE))]
#[doc(hidden)]
#[no_mangle]
pub extern \"C\" fn __{ident}_init() -> ::kernel::ffi::c_int {{
pub extern "C" fn #ident_init() -> ::kernel::ffi::c_int {
// SAFETY: This function is inaccessible to the outside due to the double
// module wrapping it. It is called exactly once by the C side via its
// placement above in the initcall section.
unsafe {{ __init() }}
}}
unsafe { __init() }
}
#[cfg(not(MODULE))]
#[doc(hidden)]
#[no_mangle]
pub extern \"C\" fn __{ident}_exit() {{
pub extern "C" fn #ident_exit() {
// SAFETY:
// - This function is inaccessible to the outside due to the double
// module wrapping it. It is called exactly once by the C side via its
// unique name,
// - furthermore it is only called after `__{ident}_init` has
// - furthermore it is only called after `#ident_init` has
// returned `0` (which delegates to `__init`).
unsafe {{ __exit() }}
}}
unsafe { __exit() }
}
/// # Safety
///
/// This function must only be called once.
unsafe fn __init() -> ::kernel::ffi::c_int {{
let initer =
<{type_} as ::kernel::InPlaceModule>::init(&super::super::THIS_MODULE);
unsafe fn __init() -> ::kernel::ffi::c_int {
let initer = <super::super::LocalModule as ::kernel::InPlaceModule>::init(
&super::super::THIS_MODULE
);
// SAFETY: No data race, since `__MOD` can only be accessed by this module
// and there only `__init` and `__exit` access it. These functions are only
// called once and `__exit` cannot be called before or during `__init`.
match unsafe {{ initer.__pinned_init(__MOD.as_mut_ptr()) }} {{
match unsafe { initer.__pinned_init(__MOD.as_mut_ptr()) } {
Ok(m) => 0,
Err(e) => e.to_errno(),
}}
}}
}
}
/// # Safety
///
/// This function must
/// - only be called once,
/// - be called after `__init` has been called and returned `0`.
unsafe fn __exit() {{
unsafe fn __exit() {
// SAFETY: No data race, since `__MOD` can only be accessed by this module
// and there only `__init` and `__exit` access it. These functions are only
// called once and `__init` was already called.
unsafe {{
unsafe {
// Invokes `drop()` on `__MOD`, which should be used for cleanup.
__MOD.assume_init_drop();
}}
}}
{modinfo}
}}
}}
mod module_parameters {{
{params}
}}
",
type_ = info.type_,
name = info.name,
ident = ident,
modinfo = modinfo.buffer,
params = modinfo.param_buffer,
initcall_section = ".initcall6.init"
)
.parse()
.expect("Error parsing formatted string into token stream.")
}
}
#modinfo_ts
}
}
mod module_parameters {
#params_ts
}
})
}

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
use proc_macro::{Delimiter, Group, Ident, Spacing, Span, TokenTree};
use proc_macro2::{Delimiter, Group, Ident, Spacing, Span, TokenTree};
fn concat_helper(tokens: &[TokenTree]) -> Vec<(String, Span)> {
let mut tokens = tokens.iter();

View file

@ -1,182 +0,0 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
use proc_macro::{TokenStream, TokenTree};
pub(crate) trait ToTokens {
fn to_tokens(&self, tokens: &mut TokenStream);
}
impl<T: ToTokens> ToTokens for Option<T> {
fn to_tokens(&self, tokens: &mut TokenStream) {
if let Some(v) = self {
v.to_tokens(tokens);
}
}
}
impl ToTokens for proc_macro::Group {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend([TokenTree::from(self.clone())]);
}
}
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()]);
}
}
impl ToTokens for TokenStream {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend(self.clone());
}
}
/// Converts tokens into [`proc_macro::TokenStream`] and performs variable interpolations with
/// the given span.
///
/// This is a similar to the
/// [`quote_spanned!`](https://docs.rs/quote/latest/quote/macro.quote_spanned.html) macro from the
/// `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 = ::proc_macro::TokenStream::new();
{
#[allow(unused_variables)]
let span = $span;
quote_spanned!(@proc tokens span $($tt)*);
}
tokens
}};
(@proc $v:ident $span:ident) => {};
(@proc $v:ident $span:ident #$id:ident $($tt:tt)*) => {
$crate::quote::ToTokens::to_tokens(&$id, &mut $v);
quote_spanned!(@proc $v $span $($tt)*);
};
(@proc $v:ident $span:ident #(#$id:ident)* $($tt:tt)*) => {
for token in $id {
$crate::quote::ToTokens::to_tokens(&token, &mut $v);
}
quote_spanned!(@proc $v $span $($tt)*);
};
(@proc $v:ident $span:ident ( $($inner:tt)* ) $($tt:tt)*) => {
#[allow(unused_mut)]
let mut tokens = ::proc_macro::TokenStream::new();
quote_spanned!(@proc tokens $span $($inner)*);
$v.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new(
::proc_macro::Delimiter::Parenthesis,
tokens,
))]);
quote_spanned!(@proc $v $span $($tt)*);
};
(@proc $v:ident $span:ident [ $($inner:tt)* ] $($tt:tt)*) => {
let mut tokens = ::proc_macro::TokenStream::new();
quote_spanned!(@proc tokens $span $($inner)*);
$v.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new(
::proc_macro::Delimiter::Bracket,
tokens,
))]);
quote_spanned!(@proc $v $span $($tt)*);
};
(@proc $v:ident $span:ident { $($inner:tt)* } $($tt:tt)*) => {
let mut tokens = ::proc_macro::TokenStream::new();
quote_spanned!(@proc tokens $span $($inner)*);
$v.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new(
::proc_macro::Delimiter::Brace,
tokens,
))]);
quote_spanned!(@proc $v $span $($tt)*);
};
(@proc $v:ident $span:ident :: $($tt:tt)*) => {
$v.extend([::proc_macro::Spacing::Joint, ::proc_macro::Spacing::Alone].map(|spacing| {
::proc_macro::TokenTree::Punct(::proc_macro::Punct::new(':', spacing))
}));
quote_spanned!(@proc $v $span $($tt)*);
};
(@proc $v:ident $span:ident : $($tt:tt)*) => {
$v.extend([::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.extend([::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.extend([::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.extend([::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.extend([::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.extend([::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.extend([::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.extend([::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.extend([::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.extend([::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.extend([::proc_macro::TokenTree::Ident(
::proc_macro::Ident::new(stringify!($id), $span),
)]);
quote_spanned!(@proc $v $span $($tt)*);
};
}
/// Converts tokens into [`proc_macro::TokenStream`] and performs variable interpolations with
/// mixed site span ([`Span::mixed_site()`]).
///
/// This is a similar to the [`quote!`](https://docs.rs/quote/latest/quote/macro.quote.html) macro
/// from the `quote` crate but provides only just enough functionality needed by the current
/// `macros` crate.
///
/// [`Span::mixed_site()`]: https://doc.rust-lang.org/proc_macro/struct.Span.html#method.mixed_site
macro_rules! quote {
($($tt:tt)*) => {
quote_spanned!(::proc_macro::Span::mixed_site() => $($tt)*)
}
}

View file

@ -1,96 +1,105 @@
// SPDX-License-Identifier: GPL-2.0
use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
use std::collections::HashSet;
use std::fmt::Write;
pub(crate) fn vtable(_attr: TokenStream, ts: TokenStream) -> TokenStream {
let mut tokens: Vec<_> = ts.into_iter().collect();
// Scan for the `trait` or `impl` keyword.
let is_trait = tokens
.iter()
.find_map(|token| match token {
TokenTree::Ident(ident) => match ident.to_string().as_str() {
"trait" => Some(true),
"impl" => Some(false),
_ => None,
},
_ => None,
})
.expect("#[vtable] attribute should only be applied to trait or impl block");
// Retrieve the main body. The main body should be the last token tree.
let body = match tokens.pop() {
Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => group,
_ => panic!("cannot locate main body of trait or impl block"),
use std::{
collections::HashSet,
iter::Extend, //
};
let mut body_it = body.stream().into_iter();
let mut functions = Vec::new();
let mut consts = HashSet::new();
while let Some(token) = body_it.next() {
match token {
TokenTree::Ident(ident) if ident.to_string() == "fn" => {
let fn_name = match body_it.next() {
Some(TokenTree::Ident(ident)) => ident.to_string(),
// Possibly we've encountered a fn pointer type instead.
_ => continue,
use proc_macro2::{
Ident,
TokenStream, //
};
functions.push(fn_name);
}
TokenTree::Ident(ident) if ident.to_string() == "const" => {
let const_name = match body_it.next() {
Some(TokenTree::Ident(ident)) => ident.to_string(),
// Possibly we've encountered an inline const block instead.
_ => continue,
use quote::ToTokens;
use syn::{
parse_quote,
Error,
ImplItem,
Item,
ItemImpl,
ItemTrait,
Result,
TraitItem, //
};
consts.insert(const_name);
}
_ => (),
}
}
let mut const_items;
if is_trait {
const_items = "
fn handle_trait(mut item: ItemTrait) -> Result<ItemTrait> {
let mut gen_items = Vec::new();
gen_items.push(parse_quote! {
/// A marker to prevent implementors from forgetting to use [`#[vtable]`](vtable)
/// attribute when implementing this trait.
const USE_VTABLE_ATTR: ();
"
.to_owned();
});
for item in &item.items {
if let TraitItem::Fn(fn_item) = item {
let name = &fn_item.sig.ident;
let gen_const_name = Ident::new(
&format!("HAS_{}", name.to_string().to_uppercase()),
name.span(),
);
for f in functions {
let gen_const_name = format!("HAS_{}", f.to_uppercase());
// Skip if it's declared already -- this allows user override.
if consts.contains(&gen_const_name) {
continue;
}
// We don't know on the implementation-site whether a method is required or provided
// so we have to generate a const for all methods.
write!(
const_items,
"/// Indicates if the `{f}` method is overridden by the implementor.
const {gen_const_name}: bool = false;",
)
.unwrap();
consts.insert(gen_const_name);
let cfg_attrs = crate::helpers::gather_cfg_attrs(&fn_item.attrs);
let comment =
format!("Indicates if the `{name}` method is overridden by the implementor.");
gen_items.push(parse_quote! {
#(#cfg_attrs)*
#[doc = #comment]
const #gen_const_name: bool = false;
});
}
}
} else {
const_items = "const USE_VTABLE_ATTR: () = ();".to_owned();
for f in functions {
let gen_const_name = format!("HAS_{}", f.to_uppercase());
if consts.contains(&gen_const_name) {
item.items.extend(gen_items);
Ok(item)
}
fn handle_impl(mut item: ItemImpl) -> Result<ItemImpl> {
let mut gen_items = Vec::new();
let mut defined_consts = HashSet::new();
// Iterate over all user-defined constants to gather any possible explicit overrides.
for item in &item.items {
if let ImplItem::Const(const_item) = item {
defined_consts.insert(const_item.ident.clone());
}
}
gen_items.push(parse_quote! {
const USE_VTABLE_ATTR: () = ();
});
for item in &item.items {
if let ImplItem::Fn(fn_item) = item {
let name = &fn_item.sig.ident;
let gen_const_name = Ident::new(
&format!("HAS_{}", name.to_string().to_uppercase()),
name.span(),
);
// Skip if it's declared already -- this allows user override.
if defined_consts.contains(&gen_const_name) {
continue;
}
write!(const_items, "const {gen_const_name}: bool = true;").unwrap();
let cfg_attrs = crate::helpers::gather_cfg_attrs(&fn_item.attrs);
gen_items.push(parse_quote! {
#(#cfg_attrs)*
const #gen_const_name: bool = true;
});
}
}
let new_body = vec![const_items.parse().unwrap(), body.stream()]
.into_iter()
.collect();
tokens.push(TokenTree::Group(Group::new(Delimiter::Brace, new_body)));
tokens.into_iter().collect()
item.items.extend(gen_items);
Ok(item)
}
pub(crate) fn vtable(input: Item) -> Result<TokenStream> {
match input {
Item::Trait(item) => Ok(handle_trait(item)?.into_token_stream()),
Item::Impl(item) => Ok(handle_impl(item)?.into_token_stream()),
_ => Err(Error::new_spanned(
input,
"`#[vtable]` attribute should only be applied to trait or impl block",
))?,
}
}

View file

@ -135,7 +135,7 @@ struct DriverData {
impl DriverData {
fn new() -> impl PinInit<Self, Error> {
try_pin_init!(Self {
pin_init!(Self {
status <- CMutex::new(0),
buffer: Box::init(pin_init::init_zeroed())?,
}? Error)

View file

@ -6,7 +6,6 @@
use core::{
cell::Cell,
convert::Infallible,
marker::PhantomPinned,
pin::Pin,
ptr::{self, NonNull},
@ -31,31 +30,31 @@ pub struct ListHead {
impl ListHead {
#[inline]
pub fn new() -> impl PinInit<Self, Infallible> {
try_pin_init!(&this in Self {
pub fn new() -> impl PinInit<Self> {
pin_init!(&this in Self {
next: unsafe { Link::new_unchecked(this) },
prev: unsafe { Link::new_unchecked(this) },
pin: PhantomPinned,
}? Infallible)
})
}
#[inline]
#[allow(dead_code)]
pub fn insert_next(list: &ListHead) -> impl PinInit<Self, Infallible> + '_ {
try_pin_init!(&this in Self {
pub fn insert_next(list: &ListHead) -> impl PinInit<Self> + '_ {
pin_init!(&this in Self {
prev: list.next.prev().replace(unsafe { Link::new_unchecked(this)}),
next: list.next.replace(unsafe { Link::new_unchecked(this)}),
pin: PhantomPinned,
}? Infallible)
})
}
#[inline]
pub fn insert_prev(list: &ListHead) -> impl PinInit<Self, Infallible> + '_ {
try_pin_init!(&this in Self {
pub fn insert_prev(list: &ListHead) -> impl PinInit<Self> + '_ {
pin_init!(&this in Self {
next: list.prev.next().replace(unsafe { Link::new_unchecked(this)}),
prev: list.prev.replace(unsafe { Link::new_unchecked(this)}),
pin: PhantomPinned,
}? Infallible)
})
}
#[inline]

View file

@ -98,7 +98,7 @@ mod pthread_mtx {
// SAFETY: mutex has been initialized
unsafe { pin_init_from_closure(init) }
}
try_pin_init!(Self {
pin_init!(Self {
data: UnsafeCell::new(data),
raw <- init_raw(),
pin: PhantomPinned,

View file

@ -0,0 +1,30 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
use std::fmt::Display;
use proc_macro2::TokenStream;
use syn::{spanned::Spanned, Error};
pub(crate) struct DiagCtxt(TokenStream);
pub(crate) struct ErrorGuaranteed(());
impl DiagCtxt {
pub(crate) fn error(&mut self, span: impl Spanned, msg: impl Display) -> ErrorGuaranteed {
let error = Error::new(span.span(), msg);
self.0.extend(error.into_compile_error());
ErrorGuaranteed(())
}
pub(crate) fn with(
fun: impl FnOnce(&mut DiagCtxt) -> Result<TokenStream, ErrorGuaranteed>,
) -> TokenStream {
let mut dcx = Self(TokenStream::new());
match fun(&mut dcx) {
Ok(mut stream) => {
stream.extend(dcx.0);
stream
}
Err(ErrorGuaranteed(())) => dcx.0,
}
}
}

View file

@ -1,152 +0,0 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
#[cfg(not(kernel))]
use proc_macro2 as proc_macro;
use proc_macro::{TokenStream, TokenTree};
/// Parsed generics.
///
/// See the field documentation for an explanation what each of the fields represents.
///
/// # Examples
///
/// ```rust,ignore
/// # let input = todo!();
/// let (Generics { decl_generics, impl_generics, ty_generics }, rest) = parse_generics(input);
/// quote! {
/// struct Foo<$($decl_generics)*> {
/// // ...
/// }
///
/// impl<$impl_generics> Foo<$ty_generics> {
/// fn foo() {
/// // ...
/// }
/// }
/// }
/// ```
pub(crate) struct Generics {
/// The generics with bounds and default values (e.g. `T: Clone, const N: usize = 0`).
///
/// Use this on type definitions e.g. `struct Foo<$decl_generics> ...` (or `union`/`enum`).
pub(crate) decl_generics: Vec<TokenTree>,
/// The generics with bounds (e.g. `T: Clone, const N: usize`).
///
/// Use this on `impl` blocks e.g. `impl<$impl_generics> Trait for ...`.
pub(crate) impl_generics: Vec<TokenTree>,
/// The generics without bounds and without default values (e.g. `T, N`).
///
/// Use this when you use the type that is declared with these generics e.g.
/// `Foo<$ty_generics>`.
pub(crate) ty_generics: Vec<TokenTree>,
}
/// Parses the given `TokenStream` into `Generics` and the rest.
///
/// The generics are not present in the rest, but a where clause might remain.
pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec<TokenTree>) {
// The generics with bounds and default values.
let mut decl_generics = vec![];
// `impl_generics`, the declared generics with their bounds.
let mut impl_generics = vec![];
// Only the names of the generics, without any bounds.
let mut ty_generics = vec![];
// Tokens not related to the generics e.g. the `where` token and definition.
let mut rest = vec![];
// The current level of `<`.
let mut nesting = 0;
let mut toks = input.into_iter();
// If we are at the beginning of a generic parameter.
let mut at_start = true;
let mut skip_until_comma = false;
while let Some(tt) = toks.next() {
if nesting == 1 && matches!(&tt, TokenTree::Punct(p) if p.as_char() == '>') {
// Found the end of the generics.
break;
} else if nesting >= 1 {
decl_generics.push(tt.clone());
}
match tt.clone() {
TokenTree::Punct(p) if p.as_char() == '<' => {
if nesting >= 1 && !skip_until_comma {
// This is inside of the generics and part of some bound.
impl_generics.push(tt);
}
nesting += 1;
}
TokenTree::Punct(p) if p.as_char() == '>' => {
// This is a parsing error, so we just end it here.
if nesting == 0 {
break;
} else {
nesting -= 1;
if nesting >= 1 && !skip_until_comma {
// We are still inside of the generics and part of some bound.
impl_generics.push(tt);
}
}
}
TokenTree::Punct(p) if skip_until_comma && p.as_char() == ',' => {
if nesting == 1 {
impl_generics.push(tt.clone());
impl_generics.push(tt);
skip_until_comma = false;
}
}
_ if !skip_until_comma => {
match nesting {
// If we haven't entered the generics yet, we still want to keep these tokens.
0 => rest.push(tt),
1 => {
// Here depending on the token, it might be a generic variable name.
match tt.clone() {
TokenTree::Ident(i) if at_start && i.to_string() == "const" => {
let Some(name) = toks.next() else {
// Parsing error.
break;
};
impl_generics.push(tt);
impl_generics.push(name.clone());
ty_generics.push(name.clone());
decl_generics.push(name);
at_start = false;
}
TokenTree::Ident(_) if at_start => {
impl_generics.push(tt.clone());
ty_generics.push(tt);
at_start = false;
}
TokenTree::Punct(p) if p.as_char() == ',' => {
impl_generics.push(tt.clone());
ty_generics.push(tt);
at_start = true;
}
// Lifetimes begin with `'`.
TokenTree::Punct(p) if p.as_char() == '\'' && at_start => {
impl_generics.push(tt.clone());
ty_generics.push(tt);
}
// Generics can have default values, we skip these.
TokenTree::Punct(p) if p.as_char() == '=' => {
skip_until_comma = true;
}
_ => impl_generics.push(tt),
}
}
_ => impl_generics.push(tt),
}
}
_ => {}
}
}
rest.extend(toks);
(
Generics {
impl_generics,
decl_generics,
ty_generics,
},
rest,
)
}

View file

@ -0,0 +1,548 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use syn::{
braced,
parse::{End, Parse},
parse_quote,
punctuated::Punctuated,
spanned::Spanned,
token, Attribute, Block, Expr, ExprCall, ExprPath, Ident, Path, Token, Type,
};
use crate::diagnostics::{DiagCtxt, ErrorGuaranteed};
pub(crate) struct Initializer {
attrs: Vec<InitializerAttribute>,
this: Option<This>,
path: Path,
brace_token: token::Brace,
fields: Punctuated<InitializerField, Token![,]>,
rest: Option<(Token![..], Expr)>,
error: Option<(Token![?], Type)>,
}
struct This {
_and_token: Token![&],
ident: Ident,
_in_token: Token![in],
}
struct InitializerField {
attrs: Vec<Attribute>,
kind: InitializerKind,
}
enum InitializerKind {
Value {
ident: Ident,
value: Option<(Token![:], Expr)>,
},
Init {
ident: Ident,
_left_arrow_token: Token![<-],
value: Expr,
},
Code {
_underscore_token: Token![_],
_colon_token: Token![:],
block: Block,
},
}
impl InitializerKind {
fn ident(&self) -> Option<&Ident> {
match self {
Self::Value { ident, .. } | Self::Init { ident, .. } => Some(ident),
Self::Code { .. } => None,
}
}
}
enum InitializerAttribute {
DefaultError(DefaultErrorAttribute),
DisableInitializedFieldAccess,
}
struct DefaultErrorAttribute {
ty: Box<Type>,
}
pub(crate) fn expand(
Initializer {
attrs,
this,
path,
brace_token,
fields,
rest,
error,
}: Initializer,
default_error: Option<&'static str>,
pinned: bool,
dcx: &mut DiagCtxt,
) -> Result<TokenStream, ErrorGuaranteed> {
let error = error.map_or_else(
|| {
if let Some(default_error) = attrs.iter().fold(None, |acc, attr| {
if let InitializerAttribute::DefaultError(DefaultErrorAttribute { ty }) = attr {
Some(ty.clone())
} else {
acc
}
}) {
default_error
} else if let Some(default_error) = default_error {
syn::parse_str(default_error).unwrap()
} else {
dcx.error(brace_token.span.close(), "expected `? <type>` after `}`");
parse_quote!(::core::convert::Infallible)
}
},
|(_, err)| Box::new(err),
);
let slot = format_ident!("slot");
let (has_data_trait, data_trait, get_data, init_from_closure) = if pinned {
(
format_ident!("HasPinData"),
format_ident!("PinData"),
format_ident!("__pin_data"),
format_ident!("pin_init_from_closure"),
)
} else {
(
format_ident!("HasInitData"),
format_ident!("InitData"),
format_ident!("__init_data"),
format_ident!("init_from_closure"),
)
};
let init_kind = get_init_kind(rest, dcx);
let zeroable_check = match init_kind {
InitKind::Normal => quote!(),
InitKind::Zeroing => quote! {
// The user specified `..Zeroable::zeroed()` at the end of the list of fields.
// Therefore we check if the struct implements `Zeroable` and then zero the memory.
// This allows us to also remove the check that all fields are present (since we
// already set the memory to zero and that is a valid bit pattern).
fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T)
where T: ::pin_init::Zeroable
{}
// Ensure that the struct is indeed `Zeroable`.
assert_zeroable(#slot);
// SAFETY: The type implements `Zeroable` by the check above.
unsafe { ::core::ptr::write_bytes(#slot, 0, 1) };
},
};
let this = match this {
None => quote!(),
Some(This { ident, .. }) => quote! {
// Create the `this` so it can be referenced by the user inside of the
// expressions creating the individual fields.
let #ident = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };
},
};
// `mixed_site` ensures that the data is not accessible to the user-controlled code.
let data = Ident::new("__data", Span::mixed_site());
let init_fields = init_fields(
&fields,
pinned,
!attrs
.iter()
.any(|attr| matches!(attr, InitializerAttribute::DisableInitializedFieldAccess)),
&data,
&slot,
);
let field_check = make_field_check(&fields, init_kind, &path);
Ok(quote! {{
// We do not want to allow arbitrary returns, so we declare this type as the `Ok` return
// type and shadow it later when we insert the arbitrary user code. That way there will be
// no possibility of returning without `unsafe`.
struct __InitOk;
// Get the data about fields from the supplied type.
// SAFETY: TODO
let #data = unsafe {
use ::pin_init::__internal::#has_data_trait;
// Can't use `<#path as #has_data_trait>::#get_data`, since the user is able to omit
// generics (which need to be present with that syntax).
#path::#get_data()
};
// Ensure that `#data` really is of type `#data` and help with type inference:
let init = ::pin_init::__internal::#data_trait::make_closure::<_, __InitOk, #error>(
#data,
move |slot| {
{
// Shadow the structure so it cannot be used to return early.
struct __InitOk;
#zeroable_check
#this
#init_fields
#field_check
}
Ok(__InitOk)
}
);
let init = move |slot| -> ::core::result::Result<(), #error> {
init(slot).map(|__InitOk| ())
};
// SAFETY: TODO
let init = unsafe { ::pin_init::#init_from_closure::<_, #error>(init) };
init
}})
}
enum InitKind {
Normal,
Zeroing,
}
fn get_init_kind(rest: Option<(Token![..], Expr)>, dcx: &mut DiagCtxt) -> InitKind {
let Some((dotdot, expr)) = rest else {
return InitKind::Normal;
};
match &expr {
Expr::Call(ExprCall { func, args, .. }) if args.is_empty() => match &**func {
Expr::Path(ExprPath {
attrs,
qself: None,
path:
Path {
leading_colon: None,
segments,
},
}) if attrs.is_empty()
&& segments.len() == 2
&& segments[0].ident == "Zeroable"
&& segments[0].arguments.is_none()
&& segments[1].ident == "init_zeroed"
&& segments[1].arguments.is_none() =>
{
return InitKind::Zeroing;
}
_ => {}
},
_ => {}
}
dcx.error(
dotdot.span().join(expr.span()).unwrap_or(expr.span()),
"expected nothing or `..Zeroable::init_zeroed()`.",
);
InitKind::Normal
}
/// Generate the code that initializes the fields of the struct using the initializers in `field`.
fn init_fields(
fields: &Punctuated<InitializerField, Token![,]>,
pinned: bool,
generate_initialized_accessors: bool,
data: &Ident,
slot: &Ident,
) -> TokenStream {
let mut guards = vec![];
let mut guard_attrs = vec![];
let mut res = TokenStream::new();
for InitializerField { attrs, kind } in fields {
let cfgs = {
let mut cfgs = attrs.clone();
cfgs.retain(|attr| attr.path().is_ident("cfg"));
cfgs
};
let init = match kind {
InitializerKind::Value { ident, value } => {
let mut value_ident = ident.clone();
let value_prep = value.as_ref().map(|value| &value.1).map(|value| {
// Setting the span of `value_ident` to `value`'s span improves error messages
// when the type of `value` is wrong.
value_ident.set_span(value.span());
quote!(let #value_ident = #value;)
});
// Again span for better diagnostics
let write = quote_spanned!(ident.span()=> ::core::ptr::write);
let accessor = if pinned {
let project_ident = format_ident!("__project_{ident}");
quote! {
// SAFETY: TODO
unsafe { #data.#project_ident(&mut (*#slot).#ident) }
}
} else {
quote! {
// SAFETY: TODO
unsafe { &mut (*#slot).#ident }
}
};
let accessor = generate_initialized_accessors.then(|| {
quote! {
#(#cfgs)*
#[allow(unused_variables)]
let #ident = #accessor;
}
});
quote! {
#(#attrs)*
{
#value_prep
// SAFETY: TODO
unsafe { #write(::core::ptr::addr_of_mut!((*#slot).#ident), #value_ident) };
}
#accessor
}
}
InitializerKind::Init { ident, value, .. } => {
// Again span for better diagnostics
let init = format_ident!("init", span = value.span());
let (value_init, accessor) = if pinned {
let project_ident = format_ident!("__project_{ident}");
(
quote! {
// SAFETY:
// - `slot` is valid, because we are inside of an initializer closure, we
// return when an error/panic occurs.
// - We also use `#data` to require the correct trait (`Init` or `PinInit`)
// for `#ident`.
unsafe { #data.#ident(::core::ptr::addr_of_mut!((*#slot).#ident), #init)? };
},
quote! {
// SAFETY: TODO
unsafe { #data.#project_ident(&mut (*#slot).#ident) }
},
)
} else {
(
quote! {
// SAFETY: `slot` is valid, because we are inside of an initializer
// closure, we return when an error/panic occurs.
unsafe {
::pin_init::Init::__init(
#init,
::core::ptr::addr_of_mut!((*#slot).#ident),
)?
};
},
quote! {
// SAFETY: TODO
unsafe { &mut (*#slot).#ident }
},
)
};
let accessor = generate_initialized_accessors.then(|| {
quote! {
#(#cfgs)*
#[allow(unused_variables)]
let #ident = #accessor;
}
});
quote! {
#(#attrs)*
{
let #init = #value;
#value_init
}
#accessor
}
}
InitializerKind::Code { block: value, .. } => quote! {
#(#attrs)*
#[allow(unused_braces)]
#value
},
};
res.extend(init);
if let Some(ident) = kind.ident() {
// `mixed_site` ensures that the guard is not accessible to the user-controlled code.
let guard = format_ident!("__{ident}_guard", span = Span::mixed_site());
res.extend(quote! {
#(#cfgs)*
// Create the drop guard:
//
// We rely on macro hygiene to make it impossible for users to access this local
// variable.
// SAFETY: We forget the guard later when initialization has succeeded.
let #guard = unsafe {
::pin_init::__internal::DropGuard::new(
::core::ptr::addr_of_mut!((*slot).#ident)
)
};
});
guards.push(guard);
guard_attrs.push(cfgs);
}
}
quote! {
#res
// If execution reaches this point, all fields have been initialized. Therefore we can now
// dismiss the guards by forgetting them.
#(
#(#guard_attrs)*
::core::mem::forget(#guards);
)*
}
}
/// Generate the check for ensuring that every field has been initialized.
fn make_field_check(
fields: &Punctuated<InitializerField, Token![,]>,
init_kind: InitKind,
path: &Path,
) -> TokenStream {
let field_attrs = fields
.iter()
.filter_map(|f| f.kind.ident().map(|_| &f.attrs));
let field_name = fields.iter().filter_map(|f| f.kind.ident());
match init_kind {
InitKind::Normal => quote! {
// We use unreachable code to ensure that all fields have been mentioned exactly once,
// this struct initializer will still be type-checked and complain with a very natural
// error message if a field is forgotten/mentioned more than once.
#[allow(unreachable_code, clippy::diverging_sub_expression)]
// SAFETY: this code is never executed.
let _ = || unsafe {
::core::ptr::write(slot, #path {
#(
#(#field_attrs)*
#field_name: ::core::panic!(),
)*
})
};
},
InitKind::Zeroing => quote! {
// We use unreachable code to ensure that all fields have been mentioned at most once.
// Since the user specified `..Zeroable::zeroed()` at the end, all missing fields will
// be zeroed. This struct initializer will still be type-checked and complain with a
// very natural error message if a field is mentioned more than once, or doesn't exist.
#[allow(unreachable_code, clippy::diverging_sub_expression, unused_assignments)]
// SAFETY: this code is never executed.
let _ = || unsafe {
::core::ptr::write(slot, #path {
#(
#(#field_attrs)*
#field_name: ::core::panic!(),
)*
..::core::mem::zeroed()
})
};
},
}
}
impl Parse for Initializer {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let this = input.peek(Token![&]).then(|| input.parse()).transpose()?;
let path = input.parse()?;
let content;
let brace_token = braced!(content in input);
let mut fields = Punctuated::new();
loop {
let lh = content.lookahead1();
if lh.peek(End) || lh.peek(Token![..]) {
break;
} else if lh.peek(Ident) || lh.peek(Token![_]) || lh.peek(Token![#]) {
fields.push_value(content.parse()?);
let lh = content.lookahead1();
if lh.peek(End) {
break;
} else if lh.peek(Token![,]) {
fields.push_punct(content.parse()?);
} else {
return Err(lh.error());
}
} else {
return Err(lh.error());
}
}
let rest = content
.peek(Token![..])
.then(|| Ok::<_, syn::Error>((content.parse()?, content.parse()?)))
.transpose()?;
let error = input
.peek(Token![?])
.then(|| Ok::<_, syn::Error>((input.parse()?, input.parse()?)))
.transpose()?;
let attrs = attrs
.into_iter()
.map(|a| {
if a.path().is_ident("default_error") {
a.parse_args::<DefaultErrorAttribute>()
.map(InitializerAttribute::DefaultError)
} else if a.path().is_ident("disable_initialized_field_access") {
a.meta
.require_path_only()
.map(|_| InitializerAttribute::DisableInitializedFieldAccess)
} else {
Err(syn::Error::new_spanned(a, "unknown initializer attribute"))
}
})
.collect::<Result<Vec<_>, _>>()?;
Ok(Self {
attrs,
this,
path,
brace_token,
fields,
rest,
error,
})
}
}
impl Parse for DefaultErrorAttribute {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
Ok(Self { ty: input.parse()? })
}
}
impl Parse for This {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
Ok(Self {
_and_token: input.parse()?,
ident: input.parse()?,
_in_token: input.parse()?,
})
}
}
impl Parse for InitializerField {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
Ok(Self {
attrs,
kind: input.parse()?,
})
}
}
impl Parse for InitializerKind {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let lh = input.lookahead1();
if lh.peek(Token![_]) {
Ok(Self::Code {
_underscore_token: input.parse()?,
_colon_token: input.parse()?,
block: input.parse()?,
})
} else if lh.peek(Ident) {
let ident = input.parse()?;
let lh = input.lookahead1();
if lh.peek(Token![<-]) {
Ok(Self::Init {
ident,
_left_arrow_token: input.parse()?,
value: input.parse()?,
})
} else if lh.peek(Token![:]) {
Ok(Self::Value {
ident,
value: Some((input.parse()?, input.parse()?)),
})
} else if lh.peek(Token![,]) || lh.peek(End) {
Ok(Self::Value { ident, value: None })
} else {
Err(lh.error())
}
} else {
Err(lh.error())
}
}
}

View file

@ -7,48 +7,54 @@
//! `pin-init` proc macros.
#![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))]
// Allow `.into()` to convert
// - `proc_macro2::TokenStream` into `proc_macro::TokenStream` in the user-space version.
// - `proc_macro::TokenStream` into `proc_macro::TokenStream` in the kernel version.
// Clippy warns on this conversion, but it's required by the user-space version.
//
// Remove once we have `proc_macro2` in the kernel.
#![allow(clippy::useless_conversion)]
// Documentation is done in the pin-init crate instead.
#![allow(missing_docs)]
use proc_macro::TokenStream;
use syn::parse_macro_input;
#[cfg(kernel)]
#[path = "../../../macros/quote.rs"]
#[macro_use]
#[cfg_attr(not(kernel), rustfmt::skip)]
mod quote;
#[cfg(not(kernel))]
#[macro_use]
extern crate quote;
use crate::diagnostics::DiagCtxt;
mod helpers;
mod diagnostics;
mod init;
mod pin_data;
mod pinned_drop;
mod zeroable;
#[proc_macro_attribute]
pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream {
pin_data::pin_data(inner.into(), item.into()).into()
pub fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args);
let input = parse_macro_input!(input);
DiagCtxt::with(|dcx| pin_data::pin_data(args, input, dcx)).into()
}
#[proc_macro_attribute]
pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
pinned_drop::pinned_drop(args.into(), input.into()).into()
let args = parse_macro_input!(args);
let input = parse_macro_input!(input);
DiagCtxt::with(|dcx| pinned_drop::pinned_drop(args, input, dcx)).into()
}
#[proc_macro_derive(Zeroable)]
pub fn derive_zeroable(input: TokenStream) -> TokenStream {
zeroable::derive(input.into()).into()
let input = parse_macro_input!(input);
DiagCtxt::with(|dcx| zeroable::derive(input, dcx)).into()
}
#[proc_macro_derive(MaybeZeroable)]
pub fn maybe_derive_zeroable(input: TokenStream) -> TokenStream {
zeroable::maybe_derive(input.into()).into()
let input = parse_macro_input!(input);
DiagCtxt::with(|dcx| zeroable::maybe_derive(input, dcx)).into()
}
#[proc_macro]
pub fn init(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input);
DiagCtxt::with(|dcx| init::expand(input, Some("::core::convert::Infallible"), false, dcx))
.into()
}
#[proc_macro]
pub fn pin_init(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input);
DiagCtxt::with(|dcx| init::expand(input, Some("::core::convert::Infallible"), true, dcx)).into()
}

View file

@ -1,132 +1,513 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
#[cfg(not(kernel))]
use proc_macro2 as proc_macro;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{
parse::{End, Nothing, Parse},
parse_quote, parse_quote_spanned,
spanned::Spanned,
visit_mut::VisitMut,
Field, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause,
};
use crate::helpers::{parse_generics, Generics};
use proc_macro::{Group, Punct, Spacing, TokenStream, TokenTree};
use crate::diagnostics::{DiagCtxt, ErrorGuaranteed};
pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream {
// This proc-macro only does some pre-parsing and then delegates the actual parsing to
// `pin_init::__pin_data!`.
let (
Generics {
impl_generics,
decl_generics,
ty_generics,
},
rest,
) = parse_generics(input);
// The struct definition might contain the `Self` type. Since `__pin_data!` will define a new
// type with the same generics and bounds, this poses a problem, since `Self` will refer to the
// new type as opposed to this struct definition. Therefore we have to replace `Self` with the
// concrete name.
// Errors that occur when replacing `Self` with `struct_name`.
let mut errs = TokenStream::new();
// The name of the struct with ty_generics.
let struct_name = rest
.iter()
.skip_while(|tt| !matches!(tt, TokenTree::Ident(i) if i.to_string() == "struct"))
.nth(1)
.and_then(|tt| match tt {
TokenTree::Ident(_) => {
let tt = tt.clone();
let mut res = vec![tt];
if !ty_generics.is_empty() {
// We add this, so it is maximally compatible with e.g. `Self::CONST` which
// will be replaced by `StructName::<$generics>::CONST`.
res.push(TokenTree::Punct(Punct::new(':', Spacing::Joint)));
res.push(TokenTree::Punct(Punct::new(':', Spacing::Alone)));
res.push(TokenTree::Punct(Punct::new('<', Spacing::Alone)));
res.extend(ty_generics.iter().cloned());
res.push(TokenTree::Punct(Punct::new('>', Spacing::Alone)));
pub(crate) mod kw {
syn::custom_keyword!(PinnedDrop);
}
Some(res)
pub(crate) enum Args {
Nothing(Nothing),
#[allow(dead_code)]
PinnedDrop(kw::PinnedDrop),
}
_ => None,
})
.unwrap_or_else(|| {
// If we did not find the name of the struct then we will use `Self` as the replacement
// and add a compile error to ensure it does not compile.
errs.extend(
"::core::compile_error!(\"Could not locate type name.\");"
.parse::<TokenStream>()
.unwrap(),
);
"Self".parse::<TokenStream>().unwrap().into_iter().collect()
});
let impl_generics = impl_generics
.into_iter()
.flat_map(|tt| replace_self_and_deny_type_defs(&struct_name, tt, &mut errs))
.collect::<Vec<_>>();
let mut rest = rest
.into_iter()
.flat_map(|tt| {
// We ignore top level `struct` tokens, since they would emit a compile error.
if matches!(&tt, TokenTree::Ident(i) if i.to_string() == "struct") {
vec![tt]
impl Parse for Args {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let lh = input.lookahead1();
if lh.peek(End) {
input.parse().map(Self::Nothing)
} else if lh.peek(kw::PinnedDrop) {
input.parse().map(Self::PinnedDrop)
} else {
replace_self_and_deny_type_defs(&struct_name, tt, &mut errs)
Err(lh.error())
}
}
})
.collect::<Vec<_>>();
// This should be the body of the struct `{...}`.
let last = rest.pop();
let mut quoted = quote!(::pin_init::__pin_data! {
parse_input:
@args(#args),
@sig(#(#rest)*),
@impl_generics(#(#impl_generics)*),
@ty_generics(#(#ty_generics)*),
@decl_generics(#(#decl_generics)*),
@body(#last),
});
quoted.extend(errs);
quoted
}
/// Replaces `Self` with `struct_name` and errors on `enum`, `trait`, `struct` `union` and `impl`
/// keywords.
///
/// The error is appended to `errs` to allow normal parsing to continue.
fn replace_self_and_deny_type_defs(
struct_name: &Vec<TokenTree>,
tt: TokenTree,
errs: &mut TokenStream,
) -> Vec<TokenTree> {
match tt {
TokenTree::Ident(ref i)
if i.to_string() == "enum"
|| i.to_string() == "trait"
|| i.to_string() == "struct"
|| i.to_string() == "union"
|| i.to_string() == "impl" =>
{
errs.extend(
pub(crate) fn pin_data(
args: Args,
input: Item,
dcx: &mut DiagCtxt,
) -> Result<TokenStream, ErrorGuaranteed> {
let mut struct_ = match input {
Item::Struct(struct_) => struct_,
Item::Enum(enum_) => {
return Err(dcx.error(
enum_.enum_token,
"`#[pin_data]` only supports structs for now",
));
}
Item::Union(union) => {
return Err(dcx.error(
union.union_token,
"`#[pin_data]` only supports structs for now",
));
}
rest => {
return Err(dcx.error(
rest,
"`#[pin_data]` can only be applied to struct, enum and union definitions",
));
}
};
// The generics might contain the `Self` type. Since this macro will define a new type with the
// same generics and bounds, this poses a problem: `Self` will refer to the new type as opposed
// to this struct definition. Therefore we have to replace `Self` with the concrete name.
let mut replacer = {
let name = &struct_.ident;
let (_, ty_generics, _) = struct_.generics.split_for_impl();
SelfReplacer(parse_quote!(#name #ty_generics))
};
replacer.visit_generics_mut(&mut struct_.generics);
replacer.visit_fields_mut(&mut struct_.fields);
let fields: Vec<(bool, &Field)> = struct_
.fields
.iter_mut()
.map(|field| {
let len = field.attrs.len();
field.attrs.retain(|a| !a.path().is_ident("pin"));
(len != field.attrs.len(), &*field)
})
.collect();
for (pinned, field) in &fields {
if !pinned && is_phantom_pinned(&field.ty) {
dcx.error(
field,
format!(
"::core::compile_error!(\"Cannot use `{i}` inside of struct definition with \
`#[pin_data]`.\");"
)
.parse::<TokenStream>()
.unwrap()
.into_iter()
.map(|mut tok| {
tok.set_span(tt.span());
tok
}),
"The field `{}` of type `PhantomPinned` only has an effect \
if it has the `#[pin]` attribute",
field.ident.as_ref().unwrap(),
),
);
vec![tt]
}
TokenTree::Ident(i) if i.to_string() == "Self" => struct_name.clone(),
TokenTree::Literal(_) | TokenTree::Punct(_) | TokenTree::Ident(_) => vec![tt],
TokenTree::Group(g) => vec![TokenTree::Group(Group::new(
g.delimiter(),
g.stream()
.into_iter()
.flat_map(|tt| replace_self_and_deny_type_defs(struct_name, tt, errs))
.collect(),
))],
}
}
let unpin_impl = generate_unpin_impl(&struct_.ident, &struct_.generics, &fields);
let drop_impl = generate_drop_impl(&struct_.ident, &struct_.generics, args);
let projections =
generate_projections(&struct_.vis, &struct_.ident, &struct_.generics, &fields);
let the_pin_data =
generate_the_pin_data(&struct_.vis, &struct_.ident, &struct_.generics, &fields);
Ok(quote! {
#struct_
#projections
// We put the rest into this const item, because it then will not be accessible to anything
// outside.
const _: () = {
#the_pin_data
#unpin_impl
#drop_impl
};
})
}
fn is_phantom_pinned(ty: &Type) -> bool {
match ty {
Type::Path(TypePath { qself: None, path }) => {
// Cannot possibly refer to `PhantomPinned` (except alias, but that's on the user).
if path.segments.len() > 3 {
return false;
}
// If there is a `::`, then the path needs to be `::core::marker::PhantomPinned` or
// `::std::marker::PhantomPinned`.
if path.leading_colon.is_some() && path.segments.len() != 3 {
return false;
}
let expected: Vec<&[&str]> = vec![&["PhantomPinned"], &["marker"], &["core", "std"]];
for (actual, expected) in path.segments.iter().rev().zip(expected) {
if !actual.arguments.is_empty() || expected.iter().all(|e| actual.ident != e) {
return false;
}
}
true
}
_ => false,
}
}
fn generate_unpin_impl(
ident: &Ident,
generics: &Generics,
fields: &[(bool, &Field)],
) -> TokenStream {
let (_, ty_generics, _) = generics.split_for_impl();
let mut generics_with_pin_lt = generics.clone();
generics_with_pin_lt.params.insert(0, parse_quote!('__pin));
generics_with_pin_lt.make_where_clause();
let (
impl_generics_with_pin_lt,
ty_generics_with_pin_lt,
Some(WhereClause {
where_token,
predicates,
}),
) = generics_with_pin_lt.split_for_impl()
else {
unreachable!()
};
let pinned_fields = fields.iter().filter_map(|(b, f)| b.then_some(f));
quote! {
// This struct will be used for the unpin analysis. It is needed, because only structurally
// pinned fields are relevant whether the struct should implement `Unpin`.
#[allow(dead_code)] // The fields below are never used.
struct __Unpin #generics_with_pin_lt
#where_token
#predicates
{
__phantom_pin: ::core::marker::PhantomData<fn(&'__pin ()) -> &'__pin ()>,
__phantom: ::core::marker::PhantomData<
fn(#ident #ty_generics) -> #ident #ty_generics
>,
#(#pinned_fields),*
}
#[doc(hidden)]
impl #impl_generics_with_pin_lt ::core::marker::Unpin for #ident #ty_generics
#where_token
__Unpin #ty_generics_with_pin_lt: ::core::marker::Unpin,
#predicates
{}
}
}
fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenStream {
let (impl_generics, ty_generics, whr) = generics.split_for_impl();
let has_pinned_drop = matches!(args, Args::PinnedDrop(_));
// We need to disallow normal `Drop` implementation, the exact behavior depends on whether
// `PinnedDrop` was specified in `args`.
if has_pinned_drop {
// When `PinnedDrop` was specified we just implement `Drop` and delegate.
quote! {
impl #impl_generics ::core::ops::Drop for #ident #ty_generics
#whr
{
fn drop(&mut self) {
// SAFETY: Since this is a destructor, `self` will not move after this function
// terminates, since it is inaccessible.
let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) };
// SAFETY: Since this is a drop function, we can create this token to call the
// pinned destructor of this type.
let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() };
::pin_init::PinnedDrop::drop(pinned, token);
}
}
}
} else {
// When no `PinnedDrop` was specified, then we have to prevent implementing drop.
quote! {
// We prevent this by creating a trait that will be implemented for all types implementing
// `Drop`. Additionally we will implement this trait for the struct leading to a conflict,
// if it also implements `Drop`
trait MustNotImplDrop {}
#[expect(drop_bounds)]
impl<T: ::core::ops::Drop + ?::core::marker::Sized> MustNotImplDrop for T {}
impl #impl_generics MustNotImplDrop for #ident #ty_generics
#whr
{}
// We also take care to prevent users from writing a useless `PinnedDrop` implementation.
// They might implement `PinnedDrop` correctly for the struct, but forget to give
// `PinnedDrop` as the parameter to `#[pin_data]`.
#[expect(non_camel_case_types)]
trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}
impl<T: ::pin_init::PinnedDrop + ?::core::marker::Sized>
UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
impl #impl_generics
UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #ident #ty_generics
#whr
{}
}
}
}
fn generate_projections(
vis: &Visibility,
ident: &Ident,
generics: &Generics,
fields: &[(bool, &Field)],
) -> TokenStream {
let (impl_generics, ty_generics, _) = generics.split_for_impl();
let mut generics_with_pin_lt = generics.clone();
generics_with_pin_lt.params.insert(0, parse_quote!('__pin));
let (_, ty_generics_with_pin_lt, whr) = generics_with_pin_lt.split_for_impl();
let projection = format_ident!("{ident}Projection");
let this = format_ident!("this");
let (fields_decl, fields_proj) = collect_tuple(fields.iter().map(
|(
pinned,
Field {
vis,
ident,
ty,
attrs,
..
},
)| {
let mut attrs = attrs.clone();
attrs.retain(|a| !a.path().is_ident("pin"));
let mut no_doc_attrs = attrs.clone();
no_doc_attrs.retain(|a| !a.path().is_ident("doc"));
let ident = ident
.as_ref()
.expect("only structs with named fields are supported");
if *pinned {
(
quote!(
#(#attrs)*
#vis #ident: ::core::pin::Pin<&'__pin mut #ty>,
),
quote!(
#(#no_doc_attrs)*
// SAFETY: this field is structurally pinned.
#ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) },
),
)
} else {
(
quote!(
#(#attrs)*
#vis #ident: &'__pin mut #ty,
),
quote!(
#(#no_doc_attrs)*
#ident: &mut #this.#ident,
),
)
}
},
));
let structurally_pinned_fields_docs = fields
.iter()
.filter_map(|(pinned, field)| pinned.then_some(field))
.map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwrap()));
let not_structurally_pinned_fields_docs = fields
.iter()
.filter_map(|(pinned, field)| (!pinned).then_some(field))
.map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwrap()));
let docs = format!(" Pin-projections of [`{ident}`]");
quote! {
#[doc = #docs]
#[allow(dead_code)]
#[doc(hidden)]
#vis struct #projection #generics_with_pin_lt {
#(#fields_decl)*
___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
}
impl #impl_generics #ident #ty_generics
#whr
{
/// Pin-projects all fields of `Self`.
///
/// These fields are structurally pinned:
#(#[doc = #structurally_pinned_fields_docs])*
///
/// These fields are **not** structurally pinned:
#(#[doc = #not_structurally_pinned_fields_docs])*
#[inline]
#vis fn project<'__pin>(
self: ::core::pin::Pin<&'__pin mut Self>,
) -> #projection #ty_generics_with_pin_lt {
// SAFETY: we only give access to `&mut` for fields not structurally pinned.
let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
#projection {
#(#fields_proj)*
___pin_phantom_data: ::core::marker::PhantomData,
}
}
}
}
}
fn generate_the_pin_data(
vis: &Visibility,
ident: &Ident,
generics: &Generics,
fields: &[(bool, &Field)],
) -> TokenStream {
let (impl_generics, ty_generics, whr) = generics.split_for_impl();
// For every field, we create an initializing projection function according to its projection
// type. If a field is structurally pinned, then it must be initialized via `PinInit`, if it is
// not structurally pinned, then it can be initialized via `Init`.
//
// The functions are `unsafe` to prevent accidentally calling them.
fn handle_field(
Field {
vis,
ident,
ty,
attrs,
..
}: &Field,
struct_ident: &Ident,
pinned: bool,
) -> TokenStream {
let mut attrs = attrs.clone();
attrs.retain(|a| !a.path().is_ident("pin"));
let ident = ident
.as_ref()
.expect("only structs with named fields are supported");
let project_ident = format_ident!("__project_{ident}");
let (init_ty, init_fn, project_ty, project_body, pin_safety) = if pinned {
(
quote!(PinInit),
quote!(__pinned_init),
quote!(::core::pin::Pin<&'__slot mut #ty>),
// SAFETY: this field is structurally pinned.
quote!(unsafe { ::core::pin::Pin::new_unchecked(slot) }),
quote!(
/// - `slot` will not move until it is dropped, i.e. it will be pinned.
),
)
} else {
(
quote!(Init),
quote!(__init),
quote!(&'__slot mut #ty),
quote!(slot),
quote!(),
)
};
let slot_safety = format!(
" `slot` points at the field `{ident}` inside of `{struct_ident}`, which is pinned.",
);
quote! {
/// # Safety
///
/// - `slot` is a valid pointer to uninitialized memory.
/// - the caller does not touch `slot` when `Err` is returned, they are only permitted
/// to deallocate.
#pin_safety
#(#attrs)*
#vis unsafe fn #ident<E>(
self,
slot: *mut #ty,
init: impl ::pin_init::#init_ty<#ty, E>,
) -> ::core::result::Result<(), E> {
// SAFETY: this function has the same safety requirements as the __init function
// called below.
unsafe { ::pin_init::#init_ty::#init_fn(init, slot) }
}
/// # Safety
///
#[doc = #slot_safety]
#(#attrs)*
#vis unsafe fn #project_ident<'__slot>(
self,
slot: &'__slot mut #ty,
) -> #project_ty {
#project_body
}
}
}
let field_accessors = fields
.iter()
.map(|(pinned, field)| handle_field(field, ident, *pinned))
.collect::<TokenStream>();
quote! {
// We declare this struct which will host all of the projection function for our type. It
// will be invariant over all generic parameters which are inherited from the struct.
#[doc(hidden)]
#vis struct __ThePinData #generics
#whr
{
__phantom: ::core::marker::PhantomData<
fn(#ident #ty_generics) -> #ident #ty_generics
>,
}
impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics
#whr
{
fn clone(&self) -> Self { *self }
}
impl #impl_generics ::core::marker::Copy for __ThePinData #ty_generics
#whr
{}
#[allow(dead_code)] // Some functions might never be used and private.
#[expect(clippy::missing_safety_doc)]
impl #impl_generics __ThePinData #ty_generics
#whr
{
#field_accessors
}
// SAFETY: We have added the correct projection functions above to `__ThePinData` and
// we also use the least restrictive generics possible.
unsafe impl #impl_generics ::pin_init::__internal::HasPinData for #ident #ty_generics
#whr
{
type PinData = __ThePinData #ty_generics;
unsafe fn __pin_data() -> Self::PinData {
__ThePinData { __phantom: ::core::marker::PhantomData }
}
}
// SAFETY: TODO
unsafe impl #impl_generics ::pin_init::__internal::PinData for __ThePinData #ty_generics
#whr
{
type Datee = #ident #ty_generics;
}
}
}
struct SelfReplacer(PathSegment);
impl VisitMut for SelfReplacer {
fn visit_path_mut(&mut self, i: &mut syn::Path) {
if i.is_ident("Self") {
let span = i.span();
let seg = &self.0;
*i = parse_quote_spanned!(span=> #seg);
} else {
syn::visit_mut::visit_path_mut(self, i);
}
}
fn visit_path_segment_mut(&mut self, seg: &mut PathSegment) {
if seg.ident == "Self" {
let span = seg.span();
let this = &self.0;
*seg = parse_quote_spanned!(span=> #this);
} else {
syn::visit_mut::visit_path_segment_mut(self, seg);
}
}
fn visit_item_mut(&mut self, _: &mut Item) {
// Do not descend into items, since items reset/change what `Self` refers to.
}
}
// replace with `.collect()` once MSRV is above 1.79
fn collect_tuple<A, B>(iter: impl Iterator<Item = (A, B)>) -> (Vec<A>, Vec<B>) {
let mut res_a = vec![];
let mut res_b = vec![];
for (a, b) in iter {
res_a.push(a);
res_b.push(b);
}
(res_a, res_b)
}

View file

@ -1,51 +1,61 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
#[cfg(not(kernel))]
use proc_macro2 as proc_macro;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse::Nothing, parse_quote, spanned::Spanned, ImplItem, ItemImpl, Token};
use proc_macro::{TokenStream, TokenTree};
use crate::diagnostics::{DiagCtxt, ErrorGuaranteed};
pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> TokenStream {
let mut toks = input.into_iter().collect::<Vec<_>>();
assert!(!toks.is_empty());
// Ensure that we have an `impl` item.
assert!(matches!(&toks[0], TokenTree::Ident(i) if i.to_string() == "impl"));
// Ensure that we are implementing `PinnedDrop`.
let mut nesting: usize = 0;
let mut pinned_drop_idx = None;
for (i, tt) in toks.iter().enumerate() {
match tt {
TokenTree::Punct(p) if p.as_char() == '<' => {
nesting += 1;
pub(crate) fn pinned_drop(
_args: Nothing,
mut input: ItemImpl,
dcx: &mut DiagCtxt,
) -> Result<TokenStream, ErrorGuaranteed> {
if let Some(unsafety) = input.unsafety {
dcx.error(unsafety, "implementing `PinnedDrop` is safe");
}
TokenTree::Punct(p) if p.as_char() == '>' => {
nesting = nesting.checked_sub(1).unwrap();
continue;
input.unsafety = Some(Token![unsafe](input.impl_token.span));
match &mut input.trait_ {
Some((not, path, _for)) => {
if let Some(not) = not {
dcx.error(not, "cannot implement `!PinnedDrop`");
}
_ => {}
for (seg, expected) in path
.segments
.iter()
.rev()
.zip(["PinnedDrop", "pin_init", ""])
{
if expected.is_empty() || seg.ident != expected {
dcx.error(seg, "bad import path for `PinnedDrop`");
}
if i >= 1 && nesting == 0 {
// Found the end of the generics, this should be `PinnedDrop`.
assert!(
matches!(tt, TokenTree::Ident(i) if i.to_string() == "PinnedDrop"),
"expected 'PinnedDrop', found: '{tt:?}'"
if !seg.arguments.is_none() {
dcx.error(&seg.arguments, "unexpected arguments for `PinnedDrop` path");
}
}
*path = parse_quote!(::pin_init::PinnedDrop);
}
None => {
let span = input
.impl_token
.span
.join(input.self_ty.span())
.unwrap_or(input.impl_token.span);
dcx.error(
span,
"expected `impl ... PinnedDrop for ...`, got inherent impl",
);
pinned_drop_idx = Some(i);
break;
}
}
let idx = pinned_drop_idx
.unwrap_or_else(|| panic!("Expected an `impl` block implementing `PinnedDrop`."));
// Fully qualify the `PinnedDrop`, as to avoid any tampering.
toks.splice(idx..idx, quote!(::pin_init::));
// Take the `{}` body and call the declarative macro.
if let Some(TokenTree::Group(last)) = toks.pop() {
let last = last.stream();
quote!(::pin_init::__pinned_drop! {
@impl_sig(#(#toks)*),
@impl_body(#last),
})
} else {
TokenStream::from_iter(toks)
for item in &mut input.items {
if let ImplItem::Fn(fn_item) = item {
if fn_item.sig.ident == "drop" {
fn_item
.sig
.inputs
.push(parse_quote!(_: ::pin_init::__internal::OnlyCallFromDrop));
}
}
}
Ok(quote!(#input))
}

View file

@ -1,101 +1,78 @@
// SPDX-License-Identifier: GPL-2.0
#[cfg(not(kernel))]
use proc_macro2 as proc_macro;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse_quote, Data, DeriveInput, Field, Fields};
use crate::helpers::{parse_generics, Generics};
use proc_macro::{TokenStream, TokenTree};
use crate::{diagnostics::ErrorGuaranteed, DiagCtxt};
pub(crate) fn parse_zeroable_derive_input(
input: TokenStream,
) -> (
Vec<TokenTree>,
Vec<TokenTree>,
Vec<TokenTree>,
Option<TokenTree>,
) {
let (
Generics {
impl_generics,
decl_generics: _,
ty_generics,
},
mut rest,
) = parse_generics(input);
// This should be the body of the struct `{...}`.
let last = rest.pop();
// Now we insert `Zeroable` as a bound for every generic parameter in `impl_generics`.
let mut new_impl_generics = Vec::with_capacity(impl_generics.len());
// Are we inside of a generic where we want to add `Zeroable`?
let mut in_generic = !impl_generics.is_empty();
// Have we already inserted `Zeroable`?
let mut inserted = false;
// Level of `<>` nestings.
let mut nested = 0;
for tt in impl_generics {
match &tt {
// If we find a `,`, then we have finished a generic/constant/lifetime parameter.
TokenTree::Punct(p) if nested == 0 && p.as_char() == ',' => {
if in_generic && !inserted {
new_impl_generics.extend(quote! { : ::pin_init::Zeroable });
pub(crate) fn derive(
input: DeriveInput,
dcx: &mut DiagCtxt,
) -> Result<TokenStream, ErrorGuaranteed> {
let fields = match input.data {
Data::Struct(data_struct) => data_struct.fields,
Data::Union(data_union) => Fields::Named(data_union.fields),
Data::Enum(data_enum) => {
return Err(dcx.error(data_enum.enum_token, "cannot derive `Zeroable` for an enum"));
}
in_generic = true;
inserted = false;
new_impl_generics.push(tt);
};
let name = input.ident;
let mut generics = input.generics;
for param in generics.type_params_mut() {
param.bounds.insert(0, parse_quote!(::pin_init::Zeroable));
}
// If we find `'`, then we are entering a lifetime.
TokenTree::Punct(p) if nested == 0 && p.as_char() == '\'' => {
in_generic = false;
new_impl_generics.push(tt);
let (impl_gen, ty_gen, whr) = generics.split_for_impl();
let field_type = fields.iter().map(|field| &field.ty);
Ok(quote! {
// SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
#[automatically_derived]
unsafe impl #impl_gen ::pin_init::Zeroable for #name #ty_gen
#whr
{}
const _: () = {
fn assert_zeroable<T: ?::core::marker::Sized + ::pin_init::Zeroable>() {}
fn ensure_zeroable #impl_gen ()
#whr
{
#(
assert_zeroable::<#field_type>();
)*
}
TokenTree::Punct(p) if nested == 0 && p.as_char() == ':' => {
new_impl_generics.push(tt);
if in_generic {
new_impl_generics.extend(quote! { ::pin_init::Zeroable + });
inserted = true;
}
}
TokenTree::Punct(p) if p.as_char() == '<' => {
nested += 1;
new_impl_generics.push(tt);
}
TokenTree::Punct(p) if p.as_char() == '>' => {
assert!(nested > 0);
nested -= 1;
new_impl_generics.push(tt);
}
_ => new_impl_generics.push(tt),
}
}
assert_eq!(nested, 0);
if in_generic && !inserted {
new_impl_generics.extend(quote! { : ::pin_init::Zeroable });
}
(rest, new_impl_generics, ty_generics, last)
};
})
}
pub(crate) fn derive(input: TokenStream) -> TokenStream {
let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input);
quote! {
::pin_init::__derive_zeroable!(
parse_input:
@sig(#(#rest)*),
@impl_generics(#(#new_impl_generics)*),
@ty_generics(#(#ty_generics)*),
@body(#last),
);
pub(crate) fn maybe_derive(
input: DeriveInput,
dcx: &mut DiagCtxt,
) -> Result<TokenStream, ErrorGuaranteed> {
let fields = match input.data {
Data::Struct(data_struct) => data_struct.fields,
Data::Union(data_union) => Fields::Named(data_union.fields),
Data::Enum(data_enum) => {
return Err(dcx.error(data_enum.enum_token, "cannot derive `Zeroable` for an enum"));
}
};
let name = input.ident;
let mut generics = input.generics;
for param in generics.type_params_mut() {
param.bounds.insert(0, parse_quote!(::pin_init::Zeroable));
}
pub(crate) fn maybe_derive(input: TokenStream) -> TokenStream {
let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input);
quote! {
::pin_init::__maybe_derive_zeroable!(
parse_input:
@sig(#(#rest)*),
@impl_generics(#(#new_impl_generics)*),
@ty_generics(#(#ty_generics)*),
@body(#last),
);
for Field { ty, .. } in fields {
generics
.make_where_clause()
.predicates
// the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds`
// feature <https://github.com/rust-lang/rust/issues/48214#issuecomment-2557829956>.
.push(parse_quote!(#ty: for<'__dummy> ::pin_init::Zeroable));
}
let (impl_gen, ty_gen, whr) = generics.split_for_impl();
Ok(quote! {
// SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
#[automatically_derived]
unsafe impl #impl_gen ::pin_init::Zeroable for #name #ty_gen
#whr
{}
})
}

View file

@ -146,7 +146,7 @@
//!
//! impl DriverData {
//! fn new() -> impl PinInit<Self, Error> {
//! try_pin_init!(Self {
//! pin_init!(Self {
//! status <- CMutex::new(0),
//! buffer: Box::init(pin_init::init_zeroed())?,
//! }? Error)
@ -290,10 +290,13 @@ use core::{
ptr::{self, NonNull},
};
// This is used by doc-tests -- the proc-macros expand to `::pin_init::...` and without this the
// doc-tests wouldn't have an extern crate named `pin_init`.
#[allow(unused_extern_crates)]
extern crate self as pin_init;
#[doc(hidden)]
pub mod __internal;
#[doc(hidden)]
pub mod macros;
#[cfg(any(feature = "std", feature = "alloc"))]
mod alloc;
@ -528,7 +531,7 @@ macro_rules! stack_pin_init {
/// x: u32,
/// }
///
/// stack_try_pin_init!(let foo: Foo = try_pin_init!(Foo {
/// stack_try_pin_init!(let foo: Foo = pin_init!(Foo {
/// a <- CMutex::new(42),
/// b: Box::try_new(Bar {
/// x: 64,
@ -555,7 +558,7 @@ macro_rules! stack_pin_init {
/// x: u32,
/// }
///
/// stack_try_pin_init!(let foo: Foo =? try_pin_init!(Foo {
/// stack_try_pin_init!(let foo: Foo =? pin_init!(Foo {
/// a <- CMutex::new(42),
/// b: Box::try_new(Bar {
/// x: 64,
@ -584,10 +587,10 @@ macro_rules! stack_try_pin_init {
};
}
/// Construct an in-place, pinned initializer for `struct`s.
/// Construct an in-place, fallible pinned initializer for `struct`s.
///
/// This macro defaults the error to [`Infallible`]. If you need a different error, then use
/// [`try_pin_init!`].
/// The error type defaults to [`Infallible`]; if you need a different one, write `? Error` at the
/// end, after the struct initializer.
///
/// The syntax is almost identical to that of a normal `struct` initializer:
///
@ -776,81 +779,12 @@ macro_rules! stack_try_pin_init {
/// ```
///
/// [`NonNull<Self>`]: core::ptr::NonNull
// For a detailed example of how this macro works, see the module documentation of the hidden
// module `macros` inside of `macros.rs`.
#[macro_export]
macro_rules! pin_init {
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
$($fields:tt)*
}) => {
$crate::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
$($fields)*
}? ::core::convert::Infallible)
};
}
pub use pin_init_internal::pin_init;
/// Construct an in-place, fallible pinned initializer for `struct`s.
/// Construct an in-place, fallible initializer for `struct`s.
///
/// If the initialization can complete without error (or [`Infallible`]), then use [`pin_init!`].
///
/// You can use the `?` operator or use `return Err(err)` inside the initializer to stop
/// initialization and return the error.
///
/// IMPORTANT: if you have `unsafe` code inside of the initializer you have to ensure that when
/// initialization fails, the memory can be safely deallocated without any further modifications.
///
/// The syntax is identical to [`pin_init!`] with the following exception: you must append `? $type`
/// after the `struct` initializer to specify the error type you want to use.
///
/// # Examples
///
/// ```rust
/// # #![feature(allocator_api)]
/// # #[path = "../examples/error.rs"] mod error; use error::Error;
/// use pin_init::{pin_data, try_pin_init, PinInit, InPlaceInit, init_zeroed};
///
/// #[pin_data]
/// struct BigBuf {
/// big: Box<[u8; 1024 * 1024 * 1024]>,
/// small: [u8; 1024 * 1024],
/// ptr: *mut u8,
/// }
///
/// impl BigBuf {
/// fn new() -> impl PinInit<Self, Error> {
/// try_pin_init!(Self {
/// big: Box::init(init_zeroed())?,
/// small: [0; 1024 * 1024],
/// ptr: core::ptr::null_mut(),
/// }? Error)
/// }
/// }
/// # let _ = Box::pin_init(BigBuf::new());
/// ```
// For a detailed example of how this macro works, see the module documentation of the hidden
// module `macros` inside of `macros.rs`.
#[macro_export]
macro_rules! try_pin_init {
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
$($fields:tt)*
}? $err:ty) => {
$crate::__init_internal!(
@this($($this)?),
@typ($t $(::<$($generics),*>)? ),
@fields($($fields)*),
@error($err),
@data(PinData, use_data),
@has_data(HasPinData, __pin_data),
@construct_closure(pin_init_from_closure),
@munch_fields($($fields)*),
)
}
}
/// Construct an in-place initializer for `struct`s.
///
/// This macro defaults the error to [`Infallible`]. If you need a different error, then use
/// [`try_init!`].
/// This macro defaults the error to [`Infallible`]; if you need a different one, write `? Error`
/// at the end, after the struct initializer.
///
/// The syntax is identical to [`pin_init!`] and its safety caveats also apply:
/// - `unsafe` code must guarantee either full initialization or return an error and allow
@ -883,74 +817,7 @@ macro_rules! try_pin_init {
/// }
/// # let _ = Box::init(BigBuf::new());
/// ```
// For a detailed example of how this macro works, see the module documentation of the hidden
// module `macros` inside of `macros.rs`.
#[macro_export]
macro_rules! init {
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
$($fields:tt)*
}) => {
$crate::try_init!($(&$this in)? $t $(::<$($generics),*>)? {
$($fields)*
}? ::core::convert::Infallible)
}
}
/// Construct an in-place fallible initializer for `struct`s.
///
/// If the initialization can complete without error (or [`Infallible`]), then use
/// [`init!`].
///
/// The syntax is identical to [`try_pin_init!`]. You need to specify a custom error
/// via `? $type` after the `struct` initializer.
/// The safety caveats from [`try_pin_init!`] also apply:
/// - `unsafe` code must guarantee either full initialization or return an error and allow
/// deallocation of the memory.
/// - the fields are initialized in the order given in the initializer.
/// - no references to fields are allowed to be created inside of the initializer.
///
/// # Examples
///
/// ```rust
/// # #![feature(allocator_api)]
/// # use core::alloc::AllocError;
/// # use pin_init::InPlaceInit;
/// use pin_init::{try_init, Init, init_zeroed};
///
/// struct BigBuf {
/// big: Box<[u8; 1024 * 1024 * 1024]>,
/// small: [u8; 1024 * 1024],
/// }
///
/// impl BigBuf {
/// fn new() -> impl Init<Self, AllocError> {
/// try_init!(Self {
/// big: Box::init(init_zeroed())?,
/// small: [0; 1024 * 1024],
/// }? AllocError)
/// }
/// }
/// # let _ = Box::init(BigBuf::new());
/// ```
// For a detailed example of how this macro works, see the module documentation of the hidden
// module `macros` inside of `macros.rs`.
#[macro_export]
macro_rules! try_init {
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
$($fields:tt)*
}? $err:ty) => {
$crate::__init_internal!(
@this($($this)?),
@typ($t $(::<$($generics),*>)?),
@fields($($fields)*),
@error($err),
@data(InitData, /*no use_data*/),
@has_data(HasInitData, __init_data),
@construct_closure(init_from_closure),
@munch_fields($($fields)*),
)
}
}
pub use pin_init_internal::init;
/// Asserts that a field on a struct using `#[pin_data]` is marked with `#[pin]` ie. that it is
/// structurally pinned.
@ -1410,14 +1277,14 @@ where
/// fn init_foo() -> impl PinInit<Foo, Error> {
/// pin_init_scope(|| {
/// let bar = lookup_bar()?;
/// Ok(try_pin_init!(Foo { a: bar.a.into(), b: bar.b }? Error))
/// Ok(pin_init!(Foo { a: bar.a.into(), b: bar.b }? Error))
/// })
/// }
/// ```
///
/// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the
/// initializer itself will fail with that error. If it returned `Ok`, then it will run the
/// initializer returned by the [`try_pin_init!`] invocation.
/// initializer returned by the [`pin_init!`] invocation.
pub fn pin_init_scope<T, E, F, I>(make_init: F) -> impl PinInit<T, E>
where
F: FnOnce() -> Result<I, E>,
@ -1453,14 +1320,14 @@ where
/// fn init_foo() -> impl Init<Foo, Error> {
/// init_scope(|| {
/// let bar = lookup_bar()?;
/// Ok(try_init!(Foo { a: bar.a.into(), b: bar.b }? Error))
/// Ok(init!(Foo { a: bar.a.into(), b: bar.b }? Error))
/// })
/// }
/// ```
///
/// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the
/// initializer itself will fail with that error. If it returned `Ok`, then it will run the
/// initializer returned by the [`try_init!`] invocation.
/// initializer returned by the [`init!`] invocation.
pub fn init_scope<T, E, F, I>(make_init: F) -> impl Init<T, E>
where
F: FnOnce() -> Result<I, E>,
@ -1536,6 +1403,33 @@ pub trait InPlaceWrite<T> {
fn write_pin_init<E>(self, init: impl PinInit<T, E>) -> Result<Pin<Self::Initialized>, E>;
}
impl<T> InPlaceWrite<T> for &'static mut MaybeUninit<T> {
type Initialized = &'static mut T;
fn write_init<E>(self, init: impl Init<T, E>) -> Result<Self::Initialized, E> {
let slot = self.as_mut_ptr();
// SAFETY: `slot` is a valid pointer to uninitialized memory.
unsafe { init.__init(slot)? };
// SAFETY: The above call initialized the memory.
unsafe { Ok(self.assume_init_mut()) }
}
fn write_pin_init<E>(self, init: impl PinInit<T, E>) -> Result<Pin<Self::Initialized>, E> {
let slot = self.as_mut_ptr();
// SAFETY: `slot` is a valid pointer to uninitialized memory.
//
// The `'static` borrow guarantees the data will not be
// moved/invalidated until it gets dropped (which is never).
unsafe { init.__pinned_init(slot)? };
// SAFETY: The above call initialized the memory.
Ok(Pin::static_mut(unsafe { self.assume_init_mut() }))
}
}
/// Trait facilitating pinned destruction.
///
/// Use [`pinned_drop`] to implement this trait safely:

File diff suppressed because it is too large Load diff

View file

@ -16,7 +16,6 @@ use kernel::{
};
use core::any::TypeId;
use pin_init::PinInit;
const MODULE_NAME: &CStr = <LocalModule as kernel::ModuleMetadata>::NAME;
const AUXILIARY_NAME: &CStr = c_str!("auxiliary");

View file

@ -4,7 +4,6 @@
use kernel::{
acpi,
c_str,
device::Core,
i2c,
of,
@ -17,21 +16,21 @@ kernel::acpi_device_table! {
ACPI_TABLE,
MODULE_ACPI_TABLE,
<SampleDriver as i2c::Driver>::IdInfo,
[(acpi::DeviceId::new(c_str!("LNUXBEEF")), 0)]
[(acpi::DeviceId::new(c"LNUXBEEF"), 0)]
}
kernel::i2c_device_table! {
I2C_TABLE,
MODULE_I2C_TABLE,
<SampleDriver as i2c::Driver>::IdInfo,
[(i2c::DeviceId::new(c_str!("rust_driver_i2c")), 0)]
[(i2c::DeviceId::new(c"rust_driver_i2c"), 0)]
}
kernel::of_device_table! {
OF_TABLE,
MODULE_OF_TABLE,
<SampleDriver as i2c::Driver>::IdInfo,
[(of::DeviceId::new(c_str!("test,rust_driver_i2c")), 0)]
[(of::DeviceId::new(c"test,rust_driver_i2c"), 0)]
}
impl i2c::Driver for SampleDriver {

View file

@ -69,7 +69,6 @@
use kernel::{
acpi,
c_str,
device,
devres::Devres,
i2c,
@ -90,20 +89,20 @@ kernel::of_device_table!(
OF_TABLE,
MODULE_OF_TABLE,
<SampleDriver as platform::Driver>::IdInfo,
[(of::DeviceId::new(c_str!("test,rust-device")), ())]
[(of::DeviceId::new(c"test,rust-device"), ())]
);
kernel::acpi_device_table!(
ACPI_TABLE,
MODULE_ACPI_TABLE,
<SampleDriver as platform::Driver>::IdInfo,
[(acpi::DeviceId::new(c_str!("LNUXBEEF")), ())]
[(acpi::DeviceId::new(c"LNUXBEEF"), ())]
);
const SAMPLE_I2C_CLIENT_ADDR: u16 = 0x30;
const SAMPLE_I2C_ADAPTER_INDEX: i32 = 0;
const BOARD_INFO: i2c::I2cBoardInfo =
i2c::I2cBoardInfo::new(c_str!("rust_driver_i2c"), SAMPLE_I2C_CLIENT_ADDR);
i2c::I2cBoardInfo::new(c"rust_driver_i2c", SAMPLE_I2C_CLIENT_ADDR);
impl platform::Driver for SampleDriver {
type IdInfo = ();

View file

@ -95,8 +95,6 @@
//! }
//! ```
use core::pin::Pin;
use kernel::{
c_str,
device::Device,

View file

@ -101,7 +101,7 @@ impl Drop for RustPrint {
}
mod trace {
use kernel::ffi::c_int;
use kernel::prelude::*;
kernel::declare_trace! {
/// # Safety

View file

@ -147,7 +147,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
append_crate(
"pin_init_internal",
srctree / "rust" / "pin-init" / "internal" / "src" / "lib.rs",
["std", "proc_macro"],
["std", "proc_macro", "proc_macro2", "quote", "syn"],
cfg=["kernel"],
is_proc_macro=True,
)

View file

@ -174,7 +174,7 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{
macro_rules! assert {{
($cond:expr $(,)?) => {{{{
::kernel::kunit_assert!(
"{kunit_name}", "{real_path}", __DOCTEST_ANCHOR - {line}, $cond
"{kunit_name}", c"{real_path}", __DOCTEST_ANCHOR - {line}, $cond
);
}}}}
}}
@ -184,7 +184,7 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{
macro_rules! assert_eq {{
($left:expr, $right:expr $(,)?) => {{{{
::kernel::kunit_assert_eq!(
"{kunit_name}", "{real_path}", __DOCTEST_ANCHOR - {line}, $left, $right
"{kunit_name}", c"{real_path}", __DOCTEST_ANCHOR - {line}, $left, $right
);
}}}}
}}