mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:24:47 +01:00
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:
commit
a9aabb3b83
67 changed files with 2758 additions and 3428 deletions
2
.mailmap
2
.mailmap
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
------------------
|
||||
|
||||
|
|
|
|||
27
MAINTAINERS
27
MAINTAINERS
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
mem::size_of,
|
||||
ops::Deref, //
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@
|
|||
|
||||
use core::ops::Deref;
|
||||
|
||||
use kernel::{
|
||||
alloc::KVec,
|
||||
prelude::*, //
|
||||
};
|
||||
use kernel::prelude::*;
|
||||
|
||||
/// A buffer abstraction for discontiguous byte slices.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,18 +2,19 @@
|
|||
|
||||
#include <linux/rbtree.h>
|
||||
|
||||
void rust_helper_rb_link_node(struct rb_node *node, struct rb_node *parent,
|
||||
struct rb_node **rb_link)
|
||||
__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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
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)
|
||||
__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);
|
||||
work->data = (atomic_long_t)WORK_DATA_INIT();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()?;
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
272
rust/kernel/impl_flags.rs
Normal 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
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -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)*
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
/// ```
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)*) }
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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!(
|
||||
ALIGN.is_power_of_two(),
|
||||
"Provided alignment is not a power of two."
|
||||
);
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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 }) {
|
||||
// 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 this_ref = unsafe { &*this };
|
||||
|
||||
// SAFETY: `node` is a non-null node so it is valid by the type invariants.
|
||||
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
53
rust/kernel/safety.rs
Normal 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)
|
||||
};
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
// 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!(
|
||||
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.
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
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())
|
||||
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"));
|
||||
}
|
||||
_ => None,
|
||||
Ok(Self(s))
|
||||
}
|
||||
}
|
||||
|
||||
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 ToTokens for AsciiLitStr {
|
||||
fn to_tokens(&self, ts: &mut TokenStream) {
|
||||
self.0.to_tokens(ts);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
impl AsciiLitStr {
|
||||
pub(crate) fn value(&self) -> String {
|
||||
self.0.value()
|
||||
}
|
||||
}
|
||||
|
||||
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"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{
|
||||
format_ident,
|
||||
quote,
|
||||
ToTokens, //
|
||||
};
|
||||
use syn::{
|
||||
parse_quote,
|
||||
Error,
|
||||
Ident,
|
||||
Item,
|
||||
ItemMod,
|
||||
LitCStr,
|
||||
Result, //
|
||||
};
|
||||
|
||||
if attr.is_empty() {
|
||||
panic!("Missing test name in `#[kunit_tests(test_name)]` macro")
|
||||
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",
|
||||
));
|
||||
}
|
||||
|
||||
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"),
|
||||
// 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",
|
||||
))?
|
||||
};
|
||||
|
||||
// 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()))
|
||||
}
|
||||
}
|
||||
// Make the entire module gated behind `CONFIG_KUNIT`.
|
||||
module
|
||||
.attrs
|
||||
.insert(0, parse_quote!(#[cfg(CONFIG_KUNIT="y")]));
|
||||
|
||||
_ => (),
|
||||
}
|
||||
attributes.clear();
|
||||
}
|
||||
|
||||
// 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)),
|
||||
);
|
||||
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]);
|
||||
assert!(is_test_result_ok(#test()));
|
||||
}
|
||||
_ => {
|
||||
new_body.push(token);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
new_body.push(token);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(¶m.ptype);
|
||||
let param_name_str = param.name.to_string();
|
||||
let param_type_str = param.ptype.to_string();
|
||||
|
||||
let ops = param_ops_path(¶m_type_str);
|
||||
|
||||
// Note: The spelling of these fields is dictated by the user space
|
||||
// tool `modinfo`.
|
||||
self.emit_param("parmtype", ¶m.name, ¶m.ptype);
|
||||
self.emit_param("parm", ¶m.name, ¶m.description);
|
||||
self.emit_param("parmtype", ¶m_name_str, ¶m_type_str);
|
||||
self.emit_param("parm", ¶m_name_str, ¶m.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 = ¶m.name;
|
||||
let param_type = ¶m.ptype;
|
||||
let param_default = ¶m.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!(
|
||||
"
|
||||
/// The module name.
|
||||
///
|
||||
/// Used by the printing macros, e.g. [`info!`].
|
||||
const __LOG_PREFIX: &[u8] = b\"{name}\\0\";
|
||||
let modinfo_ts = modinfo.ts;
|
||||
let params_ts = modinfo.param_ts;
|
||||
|
||||
// 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::types::Opaque<::kernel::bindings::module>;
|
||||
}}
|
||||
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";
|
||||
|
||||
::kernel::ThisModule::from_ptr(__this_module.get())
|
||||
}};
|
||||
#[cfg(not(MODULE))]
|
||||
static THIS_MODULE: ::kernel::ThisModule = unsafe {{
|
||||
::kernel::ThisModule::from_ptr(::core::ptr::null_mut())
|
||||
}};
|
||||
let global_asm = format!(
|
||||
r#".section "{initcall_section}", "a"
|
||||
__{ident}_initcall:
|
||||
.long __{ident}_init - .
|
||||
.previous
|
||||
"#
|
||||
);
|
||||
|
||||
/// The `LocalModule` type is the type of the module created by `module!`,
|
||||
/// `module_pci_driver!`, `module_platform_driver!`, etc.
|
||||
type LocalModule = {type_};
|
||||
let name_cstr = CString::new(name.value()).expect("name contains NUL-terminator");
|
||||
|
||||
impl ::kernel::ModuleMetadata for {type_} {{
|
||||
const NAME: &'static ::kernel::str::CStr = c\"{name}\";
|
||||
}}
|
||||
Ok(quote! {
|
||||
/// The module name.
|
||||
///
|
||||
/// Used by the printing macros, e.g. [`info!`].
|
||||
const __LOG_PREFIX: &[u8] = #name_cstr.to_bytes_with_nul();
|
||||
|
||||
// Double nested modules, since then nobody can access the public items inside.
|
||||
mod __module_init {{
|
||||
mod __module_init {{
|
||||
use super::super::{type_};
|
||||
use pin_init::PinInit;
|
||||
// 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::types::Opaque<::kernel::bindings::module>;
|
||||
};
|
||||
|
||||
/// 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: () = ();
|
||||
::kernel::ThisModule::from_ptr(__this_module.get())
|
||||
};
|
||||
|
||||
static mut __MOD: ::core::mem::MaybeUninit<{type_}> =
|
||||
::core::mem::MaybeUninit::uninit();
|
||||
#[cfg(not(MODULE))]
|
||||
static THIS_MODULE: ::kernel::ThisModule = unsafe {
|
||||
::kernel::ThisModule::from_ptr(::core::ptr::null_mut())
|
||||
};
|
||||
|
||||
// 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 {{
|
||||
// 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() }}
|
||||
}}
|
||||
/// The `LocalModule` type is the type of the module created by `module!`,
|
||||
/// `module_pci_driver!`, `module_platform_driver!`, etc.
|
||||
type LocalModule = #type_;
|
||||
|
||||
#[cfg(MODULE)]
|
||||
#[doc(hidden)]
|
||||
#[used(compiler)]
|
||||
#[link_section = \".init.data\"]
|
||||
static __UNIQUE_ID___addressable_init_module: unsafe extern \"C\" fn() -> i32 = init_module;
|
||||
impl ::kernel::ModuleMetadata for #type_ {
|
||||
const NAME: &'static ::kernel::str::CStr = #name_cstr;
|
||||
}
|
||||
|
||||
#[cfg(MODULE)]
|
||||
#[doc(hidden)]
|
||||
#[no_mangle]
|
||||
#[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() }}
|
||||
}}
|
||||
// Double nested modules, since then nobody can access the public items inside.
|
||||
#[doc(hidden)]
|
||||
mod __module_init {
|
||||
mod __module_init {
|
||||
use pin_init::PinInit;
|
||||
|
||||
#[cfg(MODULE)]
|
||||
#[doc(hidden)]
|
||||
#[used(compiler)]
|
||||
#[link_section = \".exit.data\"]
|
||||
static __UNIQUE_ID___addressable_cleanup_module: extern \"C\" fn() = cleanup_module;
|
||||
/// 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)]
|
||||
#[used(compiler)]
|
||||
static __IS_RUST_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}\"]
|
||||
#[used(compiler)]
|
||||
pub static __{ident}_initcall: extern \"C\" fn() ->
|
||||
::kernel::ffi::c_int = __{ident}_init;
|
||||
static mut __MOD: ::core::mem::MaybeUninit<super::super::LocalModule> =
|
||||
::core::mem::MaybeUninit::uninit();
|
||||
|
||||
#[cfg(not(MODULE))]
|
||||
#[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)]
|
||||
::core::arch::global_asm!(
|
||||
r#\".section \"{initcall_section}\", \"a\"
|
||||
__{ident}_initcall:
|
||||
.long __{ident}_init - .
|
||||
.previous
|
||||
\"#
|
||||
// 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)]
|
||||
#[no_mangle]
|
||||
#[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() }
|
||||
}
|
||||
|
||||
#[cfg(MODULE)]
|
||||
#[used(compiler)]
|
||||
#[link_section = ".init.data"]
|
||||
static __UNIQUE_ID___addressable_init_module: unsafe extern "C" fn() -> i32 =
|
||||
init_module;
|
||||
|
||||
#[cfg(MODULE)]
|
||||
#[no_mangle]
|
||||
#[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() }
|
||||
}
|
||||
|
||||
#[cfg(MODULE)]
|
||||
#[used(compiler)]
|
||||
#[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))]
|
||||
#[link_section = #initcall_section]
|
||||
#[used(compiler)]
|
||||
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!(#global_asm);
|
||||
|
||||
#[cfg(not(MODULE))]
|
||||
#[no_mangle]
|
||||
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() }
|
||||
}
|
||||
|
||||
#[cfg(not(MODULE))]
|
||||
#[no_mangle]
|
||||
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
|
||||
// returned `0` (which delegates to `__init`).
|
||||
unsafe { __exit() }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function must only be called once.
|
||||
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()) } {
|
||||
Ok(m) => 0,
|
||||
Err(e) => e.to_errno(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(MODULE))]
|
||||
#[doc(hidden)]
|
||||
#[no_mangle]
|
||||
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() }}
|
||||
}}
|
||||
/// # Safety
|
||||
///
|
||||
/// This function must
|
||||
/// - only be called once,
|
||||
/// - be called after `__init` has been called and returned `0`.
|
||||
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 {
|
||||
// Invokes `drop()` on `__MOD`, which should be used for cleanup.
|
||||
__MOD.assume_init_drop();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(MODULE))]
|
||||
#[doc(hidden)]
|
||||
#[no_mangle]
|
||||
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
|
||||
// returned `0` (which delegates to `__init`).
|
||||
unsafe {{ __exit() }}
|
||||
}}
|
||||
#modinfo_ts
|
||||
}
|
||||
}
|
||||
|
||||
/// # 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);
|
||||
// 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()) }} {{
|
||||
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() {{
|
||||
// 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 {{
|
||||
// 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.")
|
||||
mod module_parameters {
|
||||
#params_ts
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)*)
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
iter::Extend, //
|
||||
};
|
||||
|
||||
pub(crate) fn vtable(_attr: TokenStream, ts: TokenStream) -> TokenStream {
|
||||
let mut tokens: Vec<_> = ts.into_iter().collect();
|
||||
use proc_macro2::{
|
||||
Ident,
|
||||
TokenStream, //
|
||||
};
|
||||
use quote::ToTokens;
|
||||
use syn::{
|
||||
parse_quote,
|
||||
Error,
|
||||
ImplItem,
|
||||
Item,
|
||||
ItemImpl,
|
||||
ItemTrait,
|
||||
Result,
|
||||
TraitItem, //
|
||||
};
|
||||
|
||||
// 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");
|
||||
fn handle_trait(mut item: ItemTrait) -> Result<ItemTrait> {
|
||||
let mut gen_items = Vec::new();
|
||||
|
||||
// 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"),
|
||||
};
|
||||
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: ();
|
||||
});
|
||||
|
||||
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,
|
||||
};
|
||||
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,
|
||||
};
|
||||
consts.insert(const_name);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
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(),
|
||||
);
|
||||
|
||||
let mut const_items;
|
||||
if is_trait {
|
||||
const_items = "
|
||||
/// A marker to prevent implementors from forgetting to use [`#[vtable]`](vtable)
|
||||
/// attribute when implementing this trait.
|
||||
const USE_VTABLE_ATTR: ();
|
||||
"
|
||||
.to_owned();
|
||||
|
||||
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);
|
||||
}
|
||||
} 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) {
|
||||
continue;
|
||||
}
|
||||
write!(const_items, "const {gen_const_name}: bool = true;").unwrap();
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
let cfg_attrs = crate::helpers::gather_cfg_attrs(&fn_item.attrs);
|
||||
gen_items.push(parse_quote! {
|
||||
#(#cfg_attrs)*
|
||||
const #gen_const_name: bool = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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",
|
||||
))?,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -98,11 +98,11 @@ mod pthread_mtx {
|
|||
// SAFETY: mutex has been initialized
|
||||
unsafe { pin_init_from_closure(init) }
|
||||
}
|
||||
try_pin_init!(Self {
|
||||
data: UnsafeCell::new(data),
|
||||
raw <- init_raw(),
|
||||
pin: PhantomPinned,
|
||||
}? Error)
|
||||
pin_init!(Self {
|
||||
data: UnsafeCell::new(data),
|
||||
raw <- init_raw(),
|
||||
pin: PhantomPinned,
|
||||
}? Error)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
|
|||
30
rust/pin-init/internal/src/diagnostics.rs
Normal file
30
rust/pin-init/internal/src/diagnostics.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
548
rust/pin-init/internal/src/init.rs
Normal file
548
rust/pin-init/internal/src/init.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
Some(res)
|
||||
}
|
||||
_ => 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]
|
||||
} else {
|
||||
replace_self_and_deny_type_defs(&struct_name, tt, &mut errs)
|
||||
}
|
||||
})
|
||||
.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
|
||||
pub(crate) mod kw {
|
||||
syn::custom_keyword!(PinnedDrop);
|
||||
}
|
||||
|
||||
/// 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(
|
||||
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
|
||||
}),
|
||||
);
|
||||
vec![tt]
|
||||
pub(crate) enum Args {
|
||||
Nothing(Nothing),
|
||||
#[allow(dead_code)]
|
||||
PinnedDrop(kw::PinnedDrop),
|
||||
}
|
||||
|
||||
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 {
|
||||
Err(lh.error())
|
||||
}
|
||||
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(),
|
||||
))],
|
||||
}
|
||||
}
|
||||
|
||||
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!(
|
||||
"The field `{}` of type `PhantomPinned` only has an effect \
|
||||
if it has the `#[pin]` attribute",
|
||||
field.ident.as_ref().unwrap(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
input.unsafety = Some(Token);
|
||||
match &mut input.trait_ {
|
||||
Some((not, path, _for)) => {
|
||||
if let Some(not) = not {
|
||||
dcx.error(not, "cannot implement `!PinnedDrop`");
|
||||
}
|
||||
TokenTree::Punct(p) if p.as_char() == '>' => {
|
||||
nesting = nesting.checked_sub(1).unwrap();
|
||||
continue;
|
||||
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 !seg.arguments.is_none() {
|
||||
dcx.error(&seg.arguments, "unexpected arguments for `PinnedDrop` path");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
*path = parse_quote!(::pin_init::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:?}'"
|
||||
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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
}
|
||||
in_generic = true;
|
||||
inserted = false;
|
||||
new_impl_generics.push(tt);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
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),
|
||||
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"));
|
||||
}
|
||||
};
|
||||
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));
|
||||
}
|
||||
assert_eq!(nested, 0);
|
||||
if in_generic && !inserted {
|
||||
new_impl_generics.extend(quote! { : ::pin_init::Zeroable });
|
||||
}
|
||||
(rest, new_impl_generics, ty_generics, last)
|
||||
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>();
|
||||
)*
|
||||
}
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
{}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 = ();
|
||||
|
|
|
|||
|
|
@ -95,8 +95,6 @@
|
|||
//! }
|
||||
//! ```
|
||||
|
||||
use core::pin::Pin;
|
||||
|
||||
use kernel::{
|
||||
c_str,
|
||||
device::Device,
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ impl Drop for RustPrint {
|
|||
}
|
||||
|
||||
mod trace {
|
||||
use kernel::ffi::c_int;
|
||||
use kernel::prelude::*;
|
||||
|
||||
kernel::declare_trace! {
|
||||
/// # Safety
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
}}}}
|
||||
}}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue