From 06e4e20f6ff85b7e129fadfb9bde20572c1224b5 Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Wed, 20 Nov 2024 19:27:15 +0800 Subject: [PATCH] rust: switch to CoercePointee macro, with examples Since Rust 1.84.0 the macro `CoercePointee` has been made public on Nightly, so that it answers the some usability questions. If one wants to equip generic types with the ability to weaken itself and to work with unsized types with dynamic dispatching. This feature is useful such that Rust code are enabled to work with a family of types satisfying the same protocol or Rust traits, while the same safety guarantees are still uphold [1]. Examples of this weakening include those from *[u8; 8]* to *[u8]*, eliding the concrete size of the array; and a concrete type *T* to *dyn Trait* where *T* implements the trait or traits *Trait*. As of date, the exact language features to enable this type weakening is still under stabilization effort. Nevertheless, Alice Ryhl has proposed [2] a very valuable combination of them such that a user can enable this feature via a procedural macro `CoercePointee` without much verbosity and without declaring dependence on the relevant unstable language features. Alice has previously filed a patch [3] to demonstrate the capability of this macro. This patch provides further updates to incorporate recent changes to the proposal in [2] and paves the way for the final stabilization of the feature in the Rust language. A minimal demostration code is added to the *samples/rust/rust_print_main.rs* module. The use of the macro is now gated behind the available Rust version *1.83.0*. The *kernel* crate will still be as functional on the prior Rust toolchains. Link: https://doc.rust-lang.org/stable/nomicon/exotic-sizes.html?highlight=dynamic#dynamically-sized-types-dsts [1] Link: https://rust-lang.github.io/rfcs/3621-derive-smart-pointer.html [2] Link: https://lore.kernel.org/all/20240823-derive-smart-pointer-v1-1-53769cd37239@google.com/ [3] Signed-off-by: Xiangfei Ding --- init/Kconfig | 3 +++ rust/kernel/lib.rs | 7 ++++--- rust/kernel/list/arc.rs | 9 ++++++--- rust/kernel/sync/arc.rs | 15 +++++++++++---- samples/rust/rust_print_main.rs | 15 +++++++++++++++ 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/init/Kconfig b/init/Kconfig index a20e6efd3f0fbd..eb9b7e24e859b0 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1978,6 +1978,9 @@ config RUST If unsure, say N. +config RUST_COERCE_POINTEE + def_bool y if RUSTC_VERSION >= 108300 + config RUSTC_VERSION_TEXT string depends on RUST diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 04dbee70d3e66a..b665a28d1c123a 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -13,11 +13,12 @@ #![no_std] #![feature(arbitrary_self_types)] -#![feature(coerce_unsized)] -#![feature(dispatch_from_dyn)] +#![cfg_attr(CONFIG_RUST_COERCE_POINTEE, feature(derive_coerce_pointee))] +#![cfg_attr(not(CONFIG_RUST_COERCE_POINTEE), feature(coerce_unsized))] +#![cfg_attr(not(CONFIG_RUST_COERCE_POINTEE), feature(dispatch_from_dyn))] +#![cfg_attr(not(CONFIG_RUST_COERCE_POINTEE), feature(unsize))] #![feature(inline_const)] #![feature(lint_reasons)] -#![feature(unsize)] // Ensure conditional compilation based on the kernel configuration works; // otherwise we may silently break things like initcall handling. diff --git a/rust/kernel/list/arc.rs b/rust/kernel/list/arc.rs index 3483d8c232c4f1..f5b3a446eae25c 100644 --- a/rust/kernel/list/arc.rs +++ b/rust/kernel/list/arc.rs @@ -7,7 +7,7 @@ use crate::alloc::{AllocError, Flags}; use crate::prelude::*; use crate::sync::{Arc, ArcBorrow, UniqueArc}; -use core::marker::{PhantomPinned, Unsize}; +use core::marker::PhantomPinned; use core::ops::Deref; use core::pin::Pin; use core::sync::atomic::{AtomicBool, Ordering}; @@ -159,6 +159,7 @@ pub use impl_list_arc_safe; /// /// [`List`]: crate::list::List #[repr(transparent)] +#[cfg_attr(CONFIG_RUST_COERCE_POINTEE, derive(core::marker::CoercePointee))] pub struct ListArc where T: ListArcSafe + ?Sized, @@ -443,18 +444,20 @@ where // This is to allow coercion from `ListArc` to `ListArc` if `T` can be converted to the // dynamically-sized type (DST) `U`. +#[cfg(not(CONFIG_RUST_COERCE_POINTEE))] impl core::ops::CoerceUnsized> for ListArc where - T: ListArcSafe + Unsize + ?Sized, + T: ListArcSafe + core::marker::Unsize + ?Sized, U: ListArcSafe + ?Sized, { } // This is to allow `ListArc` to be dispatched on when `ListArc` can be coerced into // `ListArc`. +#[cfg(not(CONFIG_RUST_COERCE_POINTEE))] impl core::ops::DispatchFromDyn> for ListArc where - T: ListArcSafe + Unsize + ?Sized, + T: ListArcSafe + core::marker::Unsize + ?Sized, U: ListArcSafe + ?Sized, { } diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index fa4509406ee909..bca3d1f1e1b6f8 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -26,7 +26,7 @@ use crate::{ use core::{ alloc::Layout, fmt, - marker::{PhantomData, Unsize}, + marker::PhantomData, mem::{ManuallyDrop, MaybeUninit}, ops::{Deref, DerefMut}, pin::Pin, @@ -125,6 +125,8 @@ mod std_vendor; /// let coerced: Arc = obj; /// # Ok::<(), Error>(()) /// ``` +#[repr(transparent)] +#[cfg_attr(CONFIG_RUST_COERCE_POINTEE, derive(core::marker::CoercePointee))] pub struct Arc { ptr: NonNull>, _p: PhantomData>, @@ -172,10 +174,12 @@ impl ArcInner { // This is to allow coercion from `Arc` to `Arc` if `T` can be converted to the // dynamically-sized type (DST) `U`. -impl, U: ?Sized> core::ops::CoerceUnsized> for Arc {} +#[cfg(not(CONFIG_RUST_COERCE_POINTEE))] +impl, U: ?Sized> core::ops::CoerceUnsized> for Arc {} // This is to allow `Arc` to be dispatched on when `Arc` can be coerced into `Arc`. -impl, U: ?Sized> core::ops::DispatchFromDyn> for Arc {} +#[cfg(not(CONFIG_RUST_COERCE_POINTEE))] +impl, U: ?Sized> core::ops::DispatchFromDyn> for Arc {} // SAFETY: It is safe to send `Arc` to another thread when the underlying `T` is `Sync` because // it effectively means sharing `&T` (which is safe because `T` is `Sync`); additionally, it needs @@ -471,6 +475,8 @@ impl From>> for Arc { /// obj.as_arc_borrow().use_reference(); /// # Ok::<(), Error>(()) /// ``` +#[repr(transparent)] +#[cfg_attr(CONFIG_RUST_COERCE_POINTEE, derive(core::marker::CoercePointee))] pub struct ArcBorrow<'a, T: ?Sized + 'a> { inner: NonNull>, _p: PhantomData<&'a ()>, @@ -478,7 +484,8 @@ pub struct ArcBorrow<'a, T: ?Sized + 'a> { // This is to allow `ArcBorrow` to be dispatched on when `ArcBorrow` can be coerced into // `ArcBorrow`. -impl, U: ?Sized> core::ops::DispatchFromDyn> +#[cfg(not(CONFIG_RUST_COERCE_POINTEE))] +impl, U: ?Sized> core::ops::DispatchFromDyn> for ArcBorrow<'_, T> { } diff --git a/samples/rust/rust_print_main.rs b/samples/rust/rust_print_main.rs index aed90a6feecfa7..87cf5321f49254 100644 --- a/samples/rust/rust_print_main.rs +++ b/samples/rust/rust_print_main.rs @@ -34,6 +34,21 @@ fn arc_print() -> Result { // Uses `dbg` to print, will move `c` (for temporary debugging purposes). dbg!(c); + { + use core::fmt::Display; + fn arc_dyn_print(arc: &Arc) { + pr_info!("Arc says {arc}"); + } + // `Arc` can be used to delegate dynamic dispatch and the following is an example. + // Both `i32` and `&str` implements `Display`. + // This enables us to express a unified behaviour, contract or protocol + // on both `i32` and `&str` into a single `Arc` type `Arc`. + let a_i32_display: Arc = Arc::new(42i32, GFP_KERNEL)?; + let a_str_display: Arc = a.clone(); + arc_dyn_print(&a_i32_display); + arc_dyn_print(&a_str_display); + } + // Pretty-prints the debug formatting with lower-case hexadecimal integers. pr_info!("{:#x?}", a);