mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/2] rust: num: add cv! macro to create values from constant expressions (alt)
@ 2026-08-28 12:03 Gary Guo
  2026-08-28 12:03 ` [PATCH 1/2] rust: build_assert: add utility to require const eval Gary Guo
  2026-08-28 12:03 ` [PATCH 2/2] rust: num: add `cv!` macro to create values from constant expressions Gary Guo
  0 siblings, 2 replies; 5+ messages in thread
From: Gary Guo @ 2026-08-28 12:03 UTC (permalink / raw)
  To: Alexandre Courbot, Eliot Courtney, Yury Norov, Miguel Ojeda,
	Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
	Tamir Duberstein, Onur Özkan
  Cc: linux-kernel, rust-for-linux, Gary Guo

This is an alternative to
https://lore.kernel.org/rust-for-linux/20260828-cv-v1-0-48a180dfc4c8@nvidia.com/
and
https://lore.kernel.org/rust-for-linux/20260827-chid-v8-3-bc74c77d0214@nvidia.com
without const generic expressions limitation.

Please see the detailed description in patch 2.

Signed-off-by: Gary Guo <gary@garyguo.net>
---
Gary Guo (2):
      rust: build_assert: add utility to require const eval
      rust: num: add `cv!` macro to create values from constant expressions

 rust/build_error.rs         |   7 ++
 rust/kernel/build_assert.rs |   9 +-
 rust/kernel/device_id.rs    |   4 +
 rust/kernel/num.rs          | 209 +++++++++++++++++++++++++++++++++++++++++++-
 rust/kernel/num/bounded.rs  |  21 +++++
 rust/kernel/ptr.rs          |  21 ++++-
 rust/macros/const_eval.rs   |  24 +++++
 rust/macros/lib.rs          |  21 +++++
 8 files changed, 313 insertions(+), 3 deletions(-)
---
base-commit: e6664f2b33db9b6811eb4cec109f06cb2b4f458d
change-id: 20260828-cv-acaf3400d16c

Best regards,
--  
Gary Guo <gary@garyguo.net>


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] rust: build_assert: add utility to require const eval
  2026-08-28 12:03 [PATCH 0/2] rust: num: add cv! macro to create values from constant expressions (alt) Gary Guo
@ 2026-08-28 12:03 ` Gary Guo
  2026-08-28 12:03 ` [PATCH 2/2] rust: num: add `cv!` macro to create values from constant expressions Gary Guo
  1 sibling, 0 replies; 5+ messages in thread
From: Gary Guo @ 2026-08-28 12:03 UTC (permalink / raw)
  To: Alexandre Courbot, Eliot Courtney, Yury Norov, Miguel Ojeda,
	Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
	Tamir Duberstein, Onur Özkan
  Cc: linux-kernel, rust-for-linux, Gary Guo

We have a lot of helper const functions which are intended to be used
during const evaluation only and runtime calls should not be generated. Add
a macro to denote this explicitly.

Convert device_id.rs as an example.

Signed-off-by: Gary Guo <gary@garyguo.net>
---
 rust/build_error.rs         |  7 +++++++
 rust/kernel/build_assert.rs |  9 ++++++++-
 rust/kernel/device_id.rs    |  4 ++++
 rust/macros/const_eval.rs   | 24 ++++++++++++++++++++++++
 rust/macros/lib.rs          | 21 +++++++++++++++++++++
 5 files changed, 64 insertions(+), 1 deletion(-)

diff --git a/rust/build_error.rs b/rust/build_error.rs
index fa24eeef9929..b7ef80596f1f 100644
--- a/rust/build_error.rs
+++ b/rust/build_error.rs
@@ -29,3 +29,10 @@
 pub const fn build_error(msg: &'static str) -> ! {
     panic!("{}", msg);
 }
+
+/// Assert that the code is in const evaluation.
+///
+/// Triggers a build error if called at runtime.
+#[inline(never)]
+#[export_name = "rust_const_eval_called_at_runtime"]
+pub const fn assert_in_const_eval() {}
diff --git a/rust/kernel/build_assert.rs b/rust/kernel/build_assert.rs
index c3acb9b68a65..4cab4e1a796f 100644
--- a/rust/kernel/build_assert.rs
+++ b/rust/kernel/build_assert.rs
@@ -66,9 +66,16 @@
     build_assert_macro as build_assert,
     build_error,
     const_assert,
-    static_assert, //
+    static_assert,
+    //
 };
 
+#[doc(inline)]
+pub use build_error::assert_in_const_eval;
+
+#[doc(inline)]
+pub use macros::const_eval;
+
 #[doc(hidden)]
 pub use build_error::build_error as build_error_fn;
 
diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs
index c81fca5b4986..476f44d31b79 100644
--- a/rust/kernel/device_id.rs
+++ b/rust/kernel/device_id.rs
@@ -10,6 +10,8 @@
     mem::MaybeUninit, //
 };
 
+use crate::build_assert::const_eval;
+
 /// Marker trait to indicate a Rust device ID type represents a corresponding C device ID type.
 ///
 /// This is meant to be implemented by buses/subsystems so that they can use [`IdTable`] to
@@ -108,6 +110,7 @@ impl<T: RawDeviceId + RawDeviceIdIndex, U: 'static, const N: usize> IdArray<T, U
     /// Creates a new instance of the array.
     ///
     /// The contents are derived from the given identifiers and context information.
+    #[const_eval]
     pub const fn new(ids: [(T, &'static U); N]) -> Self {
         let mut raw_ids = [const { MaybeUninit::<T::RawType>::uninit() }; N];
 
@@ -144,6 +147,7 @@ impl<T: RawDeviceId, const N: usize> IdArray<T, (), N> {
     ///
     /// The contents are derived from the given identifiers and context information.
     /// If the device implements [`RawDeviceIdIndex`], consider using [`IdArray::new`] instead.
+    #[const_eval]
     pub const fn new_without_index(ids: [T; N]) -> Self {
         // SAFETY: `T` is layout-wise compatible with `T::RawType`, so is the array of them.
         let raw_ids: [MaybeUninit<T::RawType>; N] = unsafe { core::mem::transmute_copy(&ids) };
diff --git a/rust/macros/const_eval.rs b/rust/macros/const_eval.rs
new file mode 100644
index 000000000000..0629f2708319
--- /dev/null
+++ b/rust/macros/const_eval.rs
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+use syn::{
+    parse_quote,
+    ItemFn, //
+};
+
+pub(crate) fn const_eval(mut input: ItemFn) -> TokenStream {
+    // Prevent code generation as the function is for const evaluation only.
+    input.attrs.push(parse_quote!(
+        #[inline(always)]
+    ));
+
+    input.block.stmts.insert(
+        0,
+        parse_quote!(
+            ::kernel::build_assert::assert_in_const_eval();
+        ),
+    );
+
+    input.into_token_stream()
+}
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 24f96feaeb34..3865619139a5 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -15,6 +15,7 @@
 #![cfg_attr(not(CONFIG_RUSTC_HAS_SPAN_FILE), feature(proc_macro_span))]
 
 mod concat_idents;
+mod const_eval;
 mod export;
 mod fmt;
 mod for_lt;
@@ -338,6 +339,26 @@ pub fn concat_idents(input: TokenStream) -> TokenStream {
     concat_idents::concat_idents(parse_macro_input!(input)).into()
 }
 
+/// Mark a function as usable from const evaluation only.
+///
+/// Build will fail if the function is used for runtime code.
+///
+/// # Examples
+///
+/// ```
+/// #[const_eval]
+/// const fn call_for_const_eval_only() {
+///     // This code will be executed only during const eval!
+/// }
+///
+/// const _: () = call_for_const_eval_only();
+/// ```
+#[proc_macro_attribute]
+pub fn const_eval(attr: TokenStream, input: TokenStream) -> TokenStream {
+    parse_macro_input!(attr as syn::parse::Nothing);
+    const_eval::const_eval(parse_macro_input!(input)).into()
+}
+
 /// Paste identifiers together.
 ///
 /// Within the `paste!` macro, identifiers inside `[<` and `>]` are concatenated together to form a

-- 
2.54.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 2/2] rust: num: add `cv!` macro to create values from constant expressions
  2026-08-28 12:03 [PATCH 0/2] rust: num: add cv! macro to create values from constant expressions (alt) Gary Guo
  2026-08-28 12:03 ` [PATCH 1/2] rust: build_assert: add utility to require const eval Gary Guo
@ 2026-08-28 12:03 ` Gary Guo
  2026-08-28 14:18   ` Gary Guo
  2026-08-29  4:05   ` Alexandre Courbot
  1 sibling, 2 replies; 5+ messages in thread
From: Gary Guo @ 2026-08-28 12:03 UTC (permalink / raw)
  To: Alexandre Courbot, Eliot Courtney, Yury Norov, Miguel Ojeda,
	Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
	Tamir Duberstein, Onur Özkan
  Cc: linux-kernel, rust-for-linux, Gary Guo

Currently, constructing a `NonZero` or `Bounded` from a constant is
verbose. The former would require `const { NonZero::new(...).unwrap() }`
and the latter require turbofish. Similarly, the `num::casts` exposes
methods that cast numbers using turbofish syntax, which is unergonomic and
unnecessarily causes the value to flow into the type system, which is very
restrictive without `generic_const_exprs`.

Implement a macro `cv!` (short for constant value) which converts a const
integer to types that implements `FromConst` trait and validate them during
const evaluation.

The usage is of form

    cv!(<expression>)

for inferred type and

    cv!(<expression> => <type>)

for explicit type specification.

As we do not have const trait implementation yet, dark magic is used. The
dark magic is documented in the code, but in essence it defines inherent
`__from_const` impls on types, which can be marked const, and rely on
Rust's method resolution algorithm to pick the correct function. Multiple
helpers are defined to aid type inference to work properly.

As a result, this allows construction of primitive integers, `NonZero`,
`Bounded`, `Alignment` using a single `cv!` macro. This macro does not have
`generic_const_exprs` restrictions (e.g. in a function with `const N: u32`
generic parameter, you may use `cv!(N + 1)`), it supports full type
inference and it has nice error messages in some common error scenario:

    error[E0080]: evaluation panicked: constant is zero
       --> example.rs:22:25
        |
     22 | const X: NonZero<u32> = cv!(0);
        |                         ^^^^^^ evaluation of `X::{constant#0}` failed inside this call

    error[E0277]: `kernel::page::Page` cannot be converted from constant
       --> example.rs:22:17
        |
     22 | const X: Page = cv!(0);
        |                 ^^^^^^ the trait `kernel::num::FromConst` is not implemented for `kernel::page::Page`

Of course, this trick is not full const trait impl. So the following code cannot work properly:

    fn generic<T: FromConst>() -> T {
        cv!(0)
    }

That said, useful error message is still produced in this context.

    error[E0080]: evaluation panicked: `cv!()` cannot be used with generic types yet
       --> example.rs:23:5
        |
     22 |     cv!(0)
        |     ^^^^^^ evaluation of `generic::<u32>::{constant#0}` failed inside this call

Co-developed-by: Eliot Courtney <ecourtney@nvidia.com>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
Signed-off-by: Gary Guo <gary@garyguo.net>
---
I used part of
https://lore.kernel.org/rust-for-linux/20260827-chid-v8-3-bc74c77d0214@nvidia.com
so I added Co-developed-by tags of Eliot. Eliot, please let me know if this
is okay.
---
 rust/kernel/num.rs         | 209 ++++++++++++++++++++++++++++++++++++++++++++-
 rust/kernel/num/bounded.rs |  21 +++++
 rust/kernel/ptr.rs         |  21 ++++-
 3 files changed, 249 insertions(+), 2 deletions(-)

diff --git a/rust/kernel/num.rs b/rust/kernel/num.rs
index dbe848e30efe..a38a86c3fc9f 100644
--- a/rust/kernel/num.rs
+++ b/rust/kernel/num.rs
@@ -2,7 +2,16 @@
 
 //! Additional numerical features for the kernel.
 
-use core::ops;
+use core::{
+    marker::PhantomData,
+    num::NonZero,
+    ops, //
+};
+
+use crate::{
+    build_assert::const_eval,
+    prelude::*, //
+};
 
 pub mod bounded;
 pub mod casts;
@@ -79,3 +88,201 @@ impl Integer for $type {
     i128: Signed,
     isize: Signed
 );
+
+/// Types that can be created from an integer constant expression validated during const evaluation.
+#[diagnostic::on_unimplemented(message = "`{Self}` cannot be converted from constant")]
+pub trait FromConst: Sized {
+    /// Create `Self` from constant `v`, fails the build if `v` is not valid for `Self`.
+    ///
+    /// This function must only be called during const evaluation.
+    ///
+    /// # Note
+    ///
+    /// This function is for documentation purpose only, showing what the trait would look like when
+    /// const trait implementation is available. Use [`cv!`] macro instead of calling this function.
+    #[cfg(doc)]
+    fn from_const(v: i128) -> Self {
+        build_error!("For documentation purpose only");
+    }
+}
+
+// Helper for type inference in the `cv!` macro.
+#[doc(hidden)]
+pub struct FromConstInferHelper<T>(PhantomData<T>);
+
+impl<T> FromConstInferHelper<T> {
+    #[expect(clippy::new_without_default)]
+    #[const_eval]
+    pub const fn new() -> Self
+    where
+        // This is on function and not on the impl block so the function always exist. Otherwise we
+        // can "function exists but trait was not satisfied" error instead of "trait not
+        // implemented" error.
+        T: FromConst,
+    {
+        Self(PhantomData)
+    }
+
+    // A helper to help type inference to let type inference know that the return type of
+    // `__from_const` is exactly `T`.
+    #[const_eval]
+    pub const fn infer(self) -> T {
+        panic!("For type inference only");
+    }
+
+    // Convert to the `FromConstMethod`, so `__from_const` can be called.
+    //
+    // We have to split helpers because once a type implements `Deref`, the type must be known when
+    // calling method on it because Rust needs to walk the deref chain. Thus, `infer` is on the
+    // `FromConstInferHelper` which does not implement `Deref`, while `__from_const` is on
+    // `FromConstMethod` which we can use deref to dispatch.
+    #[const_eval]
+    pub const fn method(self) -> FromConstMethod<T> {
+        FromConstMethod(PhantomData)
+    }
+}
+
+// Helper type that we define `__from_const` inherent method on, so they can be marked as const.
+#[doc(hidden)]
+pub struct FromConstMethod<T>(PhantomData<T>);
+
+impl<T> FromConstMethod<T> {
+    // A fallback method that is selected if no `__from_const` can be found on concrete types. This
+    // is needed to avoid "__from_const" doesn't exist error, and have a proper "trait not
+    // implemented" error instead.
+    //
+    // This is selected after concrete `__from_const` because it takes `&self` as receiver, and not
+    // `self`; auto-ref has lower priority in method resolution, so methods that take
+    // `FromConstMethod<T>` is selected first.
+    //
+    // This method may also be selected without accompanying "trait not implemented" error if the
+    // type implementing `FromConst` is generic (e.g. `cv!(foo => T)`) in a function with `T:
+    // FromConst` bound; so it also produce a proper error message for that scenario.
+    #[const_eval]
+    pub const fn __from_const(&self, _: i128) -> T {
+        panic!("`cv!()` cannot be used with generic types yet");
+    }
+}
+
+// Enable the use of `FromConstMethod<T>` as receiver type on `FromConstMethodWrap<T>`.
+impl<T> ops::Deref for FromConstMethod<T> {
+    type Target = FromConstMethodWrap<T>;
+
+    #[inline(always)]
+    fn deref(&self) -> &Self::Target {
+        build_error!("For receiver only");
+    }
+}
+
+// Helper type that we define inherent method on for foreign types.
+#[doc(hidden)]
+pub struct FromConstMethodWrap<T>(PhantomData<T>);
+
+// Enable the use of `FromConstMethod<T>` as receiver type on `T`.
+impl<T> ops::Deref for FromConstMethodWrap<T> {
+    type Target = T;
+
+    #[inline(always)]
+    fn deref(&self) -> &Self::Target {
+        build_error!("For receiver only");
+    }
+}
+
+macro_rules! impl_from_const_primitive {
+    ($($ty:ty)*) => {$(
+        impl FromConst for $ty {}
+
+        impl FromConstMethodWrap<$ty> {
+            #[const_eval]
+            pub const fn __from_const(self: FromConstMethod<$ty>, v: i128) -> $ty {
+                assert!(
+                     v >= <$ty>::MIN as i128 && v <= <$ty>::MAX as i128,
+                     concat!("constant cannot be represented by `", stringify!($ty), "`"),
+                );
+
+                v as $ty
+            }
+        }
+
+        impl FromConst for NonZero<$ty> {}
+
+        impl FromConstMethodWrap<NonZero<$ty>> {
+            #[const_eval]
+            pub const fn __from_const(
+                self: FromConstMethod<NonZero<$ty>>, v: i128
+            ) -> NonZero<$ty> {
+                assert!(
+                     v >= <$ty>::MIN as i128 && v <= <$ty>::MAX as i128,
+                     concat!("constant cannot be represented by `", stringify!($ty), "`"),
+                );
+
+                match NonZero::new(v as $ty) {
+                    Some(v) => v,
+                    None => panic!("constant is zero"),
+                }
+            }
+        }
+    )*};
+}
+
+impl_from_const_primitive!(
+    u8 u16 u32 u64 usize
+    i8 i16 i32 i64 isize
+);
+
+/// Creates a value from an integer constant expression, with validity checked at build time.
+///
+/// This works for any type that implements [`FromConst`]. If a target type is not specified, it is
+/// inferred from the context.
+///
+/// # Examples
+///
+/// ```
+/// use core::num::NonZero;
+/// use kernel::num::Bounded;
+/// use kernel::num::cv;
+/// use kernel::ptr::Alignment;
+///
+/// let v: NonZero<usize> = cv!(8);
+/// assert_eq!(v.get(), 8);
+///
+/// // Any integer constant expression works, not only literals.
+/// let m: NonZero<usize> = cv!(usize::MAX);
+/// assert_eq!(m.get(), usize::MAX);
+///
+/// let b: Bounded<u32, 4> = cv!(15);
+/// assert_eq!(b.get(), 15);
+///
+/// let a: Alignment = cv!(4096);
+/// assert_eq!(a.as_usize(), 4096);
+///
+/// // Explicit type specification.
+/// assert_eq!(cv!(1 => NonZero<u8>).get(), 1);
+/// ```
+#[macro_export]
+#[doc(hidden)]
+macro_rules! cv {
+    ($v:expr $(=> $ty:ty)?) => { const {
+        #[allow(unused_comparisons, unused_assignments, clippy::as_underscore)]
+        {
+            let v = $v;
+            let r = v as i128;
+            // Pin `back` to `v`'s type so `as _` casts back to the source type.
+            let mut back = v;
+            back = r as _;
+            ::core::assert!(
+                back == v && (v < 0) == (r < 0),
+                "value cannot be losslessly widened to `i128`"
+            );
+
+            let helper = $crate::num::FromConstInferHelper$(::<$ty>)?::new();
+            if false {
+                helper.infer()
+            } else {
+                helper.method().__from_const(r)
+            }
+        }
+    }};
+}
+#[doc(inline)]
+pub use cv;
diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs
index d192610a687d..2da4c5f8e799 100644
--- a/rust/kernel/num/bounded.rs
+++ b/rust/kernel/num/bounded.rs
@@ -13,6 +13,7 @@
 };
 
 use kernel::{
+    build_assert::const_eval,
     num::Integer,
     prelude::*, //
 };
@@ -261,7 +262,27 @@ pub const fn new<const VALUE: $type>() -> Self {
                 // `N` bits.
                 unsafe { Self::__new(VALUE) }
             }
+
+            #[doc(hidden)]
+            #[const_eval]
+            pub const fn __from_const(self: super::FromConstMethod<Self>, v: i128) -> Self {
+                assert!(
+                    v >= <$type>::MIN as i128 && v <= <$type>::MAX as i128,
+                    concat!("constant cannot be represented by `", stringify!($type), "`"),
+                );
+
+                assert!(
+                    fits_within!(v as $type, $type, N),
+                    "constant cannot be represented within given bits",
+                );
+
+                // SAFETY: the asserts above confirmed that `V` can be represented within `N`
+                // bits.
+                unsafe { Self::__new(v as $type) }
+            }
         }
+
+        impl<const N: u32> super::FromConst for Bounded<$type, N> {}
         )*
     };
 }
diff --git a/rust/kernel/ptr.rs b/rust/kernel/ptr.rs
index 82acb531b17b..fec7ae71fe15 100644
--- a/rust/kernel/ptr.rs
+++ b/rust/kernel/ptr.rs
@@ -11,7 +11,10 @@
 };
 use core::num::NonZero;
 
-use crate::const_assert;
+use crate::{
+    build_assert::const_eval, //
+    prelude::*,
+};
 
 /// Type representing an alignment, which is always a power of two.
 ///
@@ -164,8 +167,24 @@ pub const fn mask(self) -> usize {
         // non-zero.
         !(self.as_usize() - 1)
     }
+
+    #[doc(hidden)]
+    #[const_eval]
+    pub const fn __from_const(self: crate::num::FromConstMethod<Self>, v: i128) -> Self {
+        assert!(
+            v >= 0 && v <= usize::MAX as i128,
+            "constant cannot be represented by `usize`"
+        );
+
+        match Alignment::new_checked(v as usize) {
+            Some(v) => v,
+            None => panic!("constant is not power of 2"),
+        }
+    }
 }
 
+impl crate::num::FromConst for Alignment {}
+
 /// Trait for items that can be aligned against an [`Alignment`].
 pub trait Alignable: Sized {
     /// Aligns `self` down to `alignment`.

-- 
2.54.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] rust: num: add `cv!` macro to create values from constant expressions
  2026-08-28 12:03 ` [PATCH 2/2] rust: num: add `cv!` macro to create values from constant expressions Gary Guo
@ 2026-08-28 14:18   ` Gary Guo
  2026-08-29  4:05   ` Alexandre Courbot
  1 sibling, 0 replies; 5+ messages in thread
From: Gary Guo @ 2026-08-28 14:18 UTC (permalink / raw)
  To: Gary Guo, Alexandre Courbot, Eliot Courtney, Yury Norov,
	Miguel Ojeda, Boqun Feng, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
	Daniel Almeida, Tamir Duberstein, Onur Özkan
  Cc: linux-kernel, rust-for-linux

On Fri Aug 28, 2026 at 1:03 PM BST, Gary Guo wrote:
> Currently, constructing a `NonZero` or `Bounded` from a constant is
> verbose. The former would require `const { NonZero::new(...).unwrap() }`
> and the latter require turbofish. Similarly, the `num::casts` exposes
> methods that cast numbers using turbofish syntax, which is unergonomic and
> unnecessarily causes the value to flow into the type system, which is very
> restrictive without `generic_const_exprs`.
>
> Implement a macro `cv!` (short for constant value) which converts a const
> integer to types that implements `FromConst` trait and validate them during
> const evaluation.
>
> The usage is of form
>
>     cv!(<expression>)
>
> for inferred type and
>
>     cv!(<expression> => <type>)
>
> for explicit type specification.
>
> As we do not have const trait implementation yet, dark magic is used. The
> dark magic is documented in the code, but in essence it defines inherent
> `__from_const` impls on types, which can be marked const, and rely on
> Rust's method resolution algorithm to pick the correct function. Multiple
> helpers are defined to aid type inference to work properly.

Here's a generalized version that work for any trait methods (same limitation,
that you cannot use this trick if you have only `T: Trait` instead of a specific
`T`):

// Inference helper.
const fn would_call<T, U, F: FnOnce(T) -> U>(_: T, _: F) -> U {
    todo!()
}

// We can have a single helper type for *all* fake const traits, as long as
// method names don't duplicate.
struct Const<T>(T);

impl<T> core::ops::Deref for Const<T> {
    type Target = T;
    
    #[inline]
    fn deref(&self) -> &T {
        &self.0
    }
}

// Const call!
macro_rules! cc {
    (($e:expr).$ident:ident($($args:tt)*)) => {
        if false {
            // The normal "runtime" call path for type inference.
            would_call($e, |x| x.$ident($($args)*));
        } else {
            // Dispatch via inherent methods. Type already known with the above
            // helper!
            // A drawback here is that `$e` -> `&$e` autoref won't work here, as
            // `Deref` impl is not const.
            Const($e).$ident($($args)*)
        }
    }
}

// Imagine this being an attribute macro.
macro_rules! const_trait {
    (impl const $tr:ident for $ty:ty {
        fn $method:ident(self: $self_ty:ty) $body:block
    }) => {
        impl $tr for $ty {
            fn $method(self: $self_ty) {
                Const(self).$method()
            }
        }
        
        impl $ty {
            pub const fn $method(self: Const<$self_ty>) $body
        }
    }
}

struct MyStruct;

trait MyTrait {
    fn foo(self: Self);
}

const_trait!{
    impl const MyTrait for MyStruct {
        fn foo(self: Self) {}
    }
}

fn test() {
    const {
        let x = MyStruct;
        // MAGIC!
        cc!((x).foo());
    }
}

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] rust: num: add `cv!` macro to create values from constant expressions
  2026-08-28 12:03 ` [PATCH 2/2] rust: num: add `cv!` macro to create values from constant expressions Gary Guo
  2026-08-28 14:18   ` Gary Guo
@ 2026-08-29  4:05   ` Alexandre Courbot
  1 sibling, 0 replies; 5+ messages in thread
From: Alexandre Courbot @ 2026-08-29  4:05 UTC (permalink / raw)
  To: Gary Guo
  Cc: Eliot Courtney, Yury Norov, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
	Onur Özkan, linux-kernel, rust-for-linux

On Fri Aug 28, 2026 at 9:03 PM JST, Gary Guo wrote:
> Currently, constructing a `NonZero` or `Bounded` from a constant is
> verbose. The former would require `const { NonZero::new(...).unwrap() }`
> and the latter require turbofish. Similarly, the `num::casts` exposes
> methods that cast numbers using turbofish syntax, which is unergonomic and
> unnecessarily causes the value to flow into the type system, which is very
> restrictive without `generic_const_exprs`.
>
> Implement a macro `cv!` (short for constant value) which converts a const
> integer to types that implements `FromConst` trait and validate them during
> const evaluation.
>
> The usage is of form
>
>     cv!(<expression>)
>
> for inferred type and
>
>     cv!(<expression> => <type>)
>
> for explicit type specification.
>
> As we do not have const trait implementation yet, dark magic is used. The
> dark magic is documented in the code, but in essence it defines inherent
> `__from_const` impls on types, which can be marked const, and rely on
> Rust's method resolution algorithm to pick the correct function. Multiple
> helpers are defined to aid type inference to work properly.
>
> As a result, this allows construction of primitive integers, `NonZero`,
> `Bounded`, `Alignment` using a single `cv!` macro. This macro does not have
> `generic_const_exprs` restrictions (e.g. in a function with `const N: u32`
> generic parameter, you may use `cv!(N + 1)`), it supports full type
> inference and it has nice error messages in some common error scenario:
>
>     error[E0080]: evaluation panicked: constant is zero
>        --> example.rs:22:25
>         |
>      22 | const X: NonZero<u32> = cv!(0);
>         |                         ^^^^^^ evaluation of `X::{constant#0}` failed inside this call
>
>     error[E0277]: `kernel::page::Page` cannot be converted from constant
>        --> example.rs:22:17
>         |
>      22 | const X: Page = cv!(0);
>         |                 ^^^^^^ the trait `kernel::num::FromConst` is not implemented for `kernel::page::Page`
>
> Of course, this trick is not full const trait impl. So the following code cannot work properly:
>
>     fn generic<T: FromConst>() -> T {
>         cv!(0)
>     }
>
> That said, useful error message is still produced in this context.
>
>     error[E0080]: evaluation panicked: `cv!()` cannot be used with generic types yet
>        --> example.rs:23:5
>         |
>      22 |     cv!(0)
>         |     ^^^^^^ evaluation of `generic::<u32>::{constant#0}` failed inside this call
>
> Co-developed-by: Eliot Courtney <ecourtney@nvidia.com>
> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
> Signed-off-by: Gary Guo <gary@garyguo.net>

That's cool, and I like the inherent generality of it, but OTOH I am not
sure that invoking dark magic is warranted in a world where all current
in-tree users are covered by Eliot's version (with a few hacks, granted,
but these are not visible to callers - and this version also has quite a
few hacks of its own).

I think this is a case where I'd rather live with a small limitation for
a while until the language is capable of covering what we need natively.
Or at least, until the generalized version you mentioned in your reply
becomes kernel infrastructure and we can benefit from it here for free.

Since the public interface of both versions is identical, switching from
one mechanism to the other would be transparent anyway.

Eliot is driving the series so the call is his to make, but I think we
can make something available sooner (and we need it soon) if we start
with his solution.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-08-29  4:05 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-28 12:03 [PATCH 0/2] rust: num: add cv! macro to create values from constant expressions (alt) Gary Guo
2026-08-28 12:03 ` [PATCH 1/2] rust: build_assert: add utility to require const eval Gary Guo
2026-08-28 12:03 ` [PATCH 2/2] rust: num: add `cv!` macro to create values from constant expressions Gary Guo
2026-08-28 14:18   ` Gary Guo
2026-08-29  4:05   ` Alexandre Courbot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®