From nobody Sat Feb 7 08:27:19 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7F6572D876B; Thu, 25 Dec 2025 08:37:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766651875; cv=none; b=WMuoLvlzZEGXNKF+CimYf6zofeMDoKR4N1XCpqwaPelvO+dkObNs8wWaoV3Tth22kD1VKfjTmC0/6ENxRblbXAGIQrGr1suoRp8SQKDJlvXKqAlXREkAXadh9yFadbFmCvCPIcQlE1qdCa/TdK9XouPZSIgceT9dPNTuNJ8xZSc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766651875; c=relaxed/simple; bh=kEPIDLhcAbj/Qu5TKz2vB5GQhjvF6dn/IaVxsb71bY0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=eQy06kRospL+syCO6EPFBrzID4bCpn9vywzo0O9ycElBwGNItqhMaD4U/gYcsGnbMTKJodSxDJraiBC0dmBjemWkJNZ/79dY0RK2KSZ7geuZDRcizFfVJXLUbJhD4OxahY/PzI2PqVcGgpbXcvCWMkfuqNtb9yY6I4zAbxOJSPc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=PlAtwxb8; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="PlAtwxb8" Received: by smtp.kernel.org (Postfix) with ESMTPS id 2C598C116C6; Thu, 25 Dec 2025 08:37:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1766651875; bh=kEPIDLhcAbj/Qu5TKz2vB5GQhjvF6dn/IaVxsb71bY0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=PlAtwxb8mCyZr/HZ0QJ3hgZ/QMfM5vvUNx/Hd/aAh1dL/utfec2zoiUodJ//MzKem Tc390waknzFHqAO64t1l5/t5LJ/Z6FmuVT14GfaZppWoWBVUeSDXUH2wfLDYJcOjk4 TBtu/p56pVkj59lTiZBqJZlJL9YofWMVjIqcIiJIGr9/zZqy17NzMin+PoL43QgznF PnCDNR+xRo5c0OHMtfpyMM28tAfYcJ1/5La7/RyMnkeGG3kfdGlrrIicWuI7kkcPNB ObqW1Pb/telDu+6vjWiIO4QJJd5bjYuBEuv9xq2+i6LlERVXwQesKRSu0MifKweAyr 7CR/UKPhmaNsg== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1EECDE7AD41; Thu, 25 Dec 2025 08:37:55 +0000 (UTC) From: Jesung Yang via B4 Relay Date: Thu, 25 Dec 2025 08:37:47 +0000 Subject: [PATCH v4 1/4] rust: macros: add derive macro for `Into` Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251225-try-from-into-macro-v4-1-4a563d597836@gmail.com> References: <20251225-try-from-into-macro-v4-0-4a563d597836@gmail.com> In-Reply-To: <20251225-try-from-into-macro-v4-0-4a563d597836@gmail.com> To: Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Alexandre Courbot Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org, Jesung Yang X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1766651873; l=22554; i=y.j3ms.n@gmail.com; s=20251225; h=from:subject:message-id; bh=71sFAuNmqDmFx2Wy2tcO+Pz75k5Gz+cj97K17lcBWLY=; b=6+o7BxFUvUB4O0UQev7O+klPOgqok94E3YymJRBhjH98QA1VriXoYbhKdEbH51OeVXywDqYMt p25Un6Ic2qgDOnG8AiTky2MPOBGgYUPRleIT/tC+pWUkvlbzY9exTp3 X-Developer-Key: i=y.j3ms.n@gmail.com; a=ed25519; pk=2yVgO1I+y7kkFSF2Dc/Dckj4L2FgRnvmERHFt4bspbI= X-Endpoint-Received: by B4 Relay for y.j3ms.n@gmail.com/20251225 with auth_id=586 X-Original-From: Jesung Yang Reply-To: y.j3ms.n@gmail.com From: Jesung Yang Introduce a procedural macro `Into` to automatically implement the `Into` trait for unit-only enums. This reduces boilerplate in cases where enum variants need to be interpreted as relevant numeric values. A concrete example can be found in nova-core, where the `register!()` macro requires enum types used within it to be convertible via `u32::from()` [1]. The macro not only supports primitive types such as `bool` or `i8`, but also `Bounded`, a wrapper around integer types limiting the number of bits usable for value representation. This accommodates the shift toward more restrictive register field representations in nova-core where values are constrained to specific bit ranges. Note that the macro actually generates `From for T` implementations, where `E` is an enum identifier and `T` is an arbitrary integer type. This automatically provides the corresponding `Into for E` implementations through the blanket implementation. Link: https://lore.kernel.org/rust-for-linux/20250624132337.2242-1-dakr@ker= nel.org/ [1] Signed-off-by: Jesung Yang --- rust/macros/convert.rs | 467 +++++++++++++++++++++++++++++++++++++++++++++= ++++ rust/macros/lib.rs | 157 +++++++++++++++++ 2 files changed, 624 insertions(+) diff --git a/rust/macros/convert.rs b/rust/macros/convert.rs new file mode 100644 index 0000000000000000000000000000000000000000..3e623cc894bff279482dd9daeaa= 7054937357ec6 --- /dev/null +++ b/rust/macros/convert.rs @@ -0,0 +1,467 @@ +// SPDX-License-Identifier: GPL-2.0 + +use proc_macro2::{ + Span, + TokenStream, // +}; + +use std::fmt; + +use syn::{ + parse::{ + Parse, + ParseStream, // + }, + parse_quote, + parse_str, + punctuated::Punctuated, + spanned::Spanned, + Attribute, + Data, + DeriveInput, + Expr, + Fields, + Ident, + LitInt, + Token, // +}; + +pub(crate) fn derive_into(input: DeriveInput) -> syn::Result { + derive(DeriveTarget::Into, input) +} + +fn derive(target: DeriveTarget, input: DeriveInput) -> syn::Result { + let mut errors: Option =3D None; + let mut combine_error =3D |err| match errors.as_mut() { + Some(errors) =3D> errors.combine(err), + None =3D> errors =3D Some(err), + }; + + let (helper_tys, repr_ty) =3D parse_attrs(target, &input.attrs)?; + for ty in &helper_tys { + if let Err(err) =3D ty.validate() { + combine_error(err); + } + } + + let data_enum =3D match input.data { + Data::Enum(data) =3D> data, + Data::Struct(data) =3D> { + let msg =3D format!( + "expected `enum`, found `struct`; \ + `#[derive({})]` can only be applied to a unit-only enum", + target.get_trait_name() + ); + return Err(syn::Error::new(data.struct_token.span(), msg)); + } + Data::Union(data) =3D> { + let msg =3D format!( + "expected `enum`, found `union`; \ + `#[derive({})]` can only be applied to a unit-only enum", + target.get_trait_name() + ); + return Err(syn::Error::new(data.union_token.span(), msg)); + } + }; + + for variant in &data_enum.variants { + match &variant.fields { + Fields::Named(fields) =3D> { + let msg =3D format!( + "expected unit-like variant, found struct-like variant= ; \ + `#[derive({})]` can only be applied to a unit-only enu= m", + target.get_trait_name() + ); + combine_error(syn::Error::new_spanned(fields, msg)); + } + Fields::Unnamed(fields) =3D> { + let msg =3D format!( + "expected unit-like variant, found tuple-like variant;= \ + `#[derive({})]` can only be applied to a unit-only enu= m", + target.get_trait_name() + ); + combine_error(syn::Error::new_spanned(fields, msg)); + } + _ =3D> (), + } + } + + if let Some(errors) =3D errors { + return Err(errors); + } + + let variants: Vec<_> =3D data_enum + .variants + .into_iter() + .map(|variant| variant.ident) + .collect(); + + Ok(derive_for_enum( + target, + &input.ident, + &variants, + &helper_tys, + &repr_ty, + )) +} + +#[derive(Clone, Copy, Debug)] +enum DeriveTarget { + Into, +} + +impl DeriveTarget { + fn get_trait_name(&self) -> &'static str { + match self { + Self::Into =3D> "Into", + } + } + + fn get_helper_name(&self) -> &'static str { + match self { + Self::Into =3D> "into", + } + } +} + +fn parse_attrs(target: DeriveTarget, attrs: &[Attribute]) -> syn::Result<(= Vec, Ident)> { + let helper =3D target.get_helper_name(); + + let mut repr_ty =3D None; + let mut helper_tys =3D Vec::new(); + for attr in attrs { + if attr.path().is_ident("repr") { + attr.parse_nested_meta(|meta| { + let ident =3D meta.path.get_ident(); + if ident.is_some_and(is_valid_primitive) { + repr_ty =3D ident.cloned(); + } + // Delegate `repr` attribute validation to rustc. + Ok(()) + })?; + } else if attr.path().is_ident(helper) && helper_tys.is_empty() { + let args =3D attr.parse_args_with(Punctuated::::parse_terminated)?; + helper_tys.extend(args); + } + } + + // Note on field-less `repr(C)` enums (quote from [1]): + // + // In C, enums with discriminants that do not all fit into an `int` = or all fit into an + // `unsigned int` are a portability hazard: such enums are only perm= itted since C23, and not + // supported e.g. by MSVC. + // + // Furthermore, Rust interprets the discriminant values of `repr(C)`= enums as expressions of + // type `isize`. This makes it impossible to implement the C23 behav= ior of enums where the + // enum discriminants have no predefined type and instead the enum u= ses a type large enough + // to hold all discriminants. + // + // Therefore, `repr(C)` enums in Rust require that either all discri= minants to fit into a C + // `int` or they all fit into an `unsigned int`. + // + // As such, `isize` is a reasonable representation for `repr(C)` enums= , as it covers the range + // of both `int` and `unsigned int`. + // + // For more information, see: + // - https://github.com/rust-lang/rust/issues/124403 + // - https://github.com/rust-lang/rust/pull/147017 + // - https://github.com/rust-lang/rust/blob/2ca7bcd03b87b52f7055a59b81= 7443b0ac4a530d/compiler/rustc_lint_defs/src/builtin.rs#L5251-L5263 [1] + + // Extract the representation passed by `#[repr(...)]` if present. If = nothing is + // specified, the default is `Rust` representation, which uses `isize`= for its + // discriminant type. + // See: https://doc.rust-lang.org/reference/items/enumerations.html#r-= items.enum.discriminant.repr-rust + let repr_ty =3D repr_ty.unwrap_or_else(|| Ident::new("isize", Span::ca= ll_site())); + Ok((helper_tys, repr_ty)) +} + +fn derive_for_enum( + target: DeriveTarget, + enum_ident: &Ident, + variants: &[Ident], + helper_tys: &[ValidTy], + repr_ty: &Ident, +) -> TokenStream { + let impl_fn =3D match target { + DeriveTarget::Into =3D> impl_into, + }; + + let qualified_repr_ty: syn::Path =3D parse_quote! { ::core::primitive:= :#repr_ty }; + + return if helper_tys.is_empty() { + let ty =3D ValidTy::Primitive(repr_ty.clone()); + let impls =3D + std::iter::once(ty).map(|ty| impl_fn(enum_ident, variants, &qu= alified_repr_ty, &ty)); + ::quote::quote! { #(#impls)* } + } else { + let impls =3D helper_tys + .iter() + .map(|ty| impl_fn(enum_ident, variants, &qualified_repr_ty, ty= )); + ::quote::quote! { #(#impls)* } + }; + + fn impl_into( + enum_ident: &Ident, + variants: &[Ident], + repr_ty: &syn::Path, + input_ty: &ValidTy, + ) -> TokenStream { + let param =3D Ident::new("value", Span::call_site()); + + let overflow_assertion =3D emit_overflow_assert(enum_ident, varian= ts, repr_ty, input_ty); + let cast =3D match input_ty { + ValidTy::Bounded(inner) =3D> { + let base_ty =3D inner.emit_qualified_base_ty(); + let expr =3D parse_quote! { #param as #base_ty }; + // Since the discriminant of `#param`, an enum variant, is= determined + // at compile-time, we can rely on `Bounded::from_expr()`.= It requires + // the provided expression to be verifiable at compile-tim= e to avoid + // triggering a build error. + inner.emit_from_expr(&expr) + } + ValidTy::Primitive(ident) if ident =3D=3D "bool" =3D> { + ::quote::quote! { (#param as #repr_ty) =3D=3D 1 } + } + qualified @ ValidTy::Primitive(_) =3D> ::quote::quote! { #para= m as #qualified }, + }; + + ::quote::quote! { + #[automatically_derived] + impl ::core::convert::From<#enum_ident> for #input_ty { + fn from(#param: #enum_ident) -> #input_ty { + #overflow_assertion + + #cast + } + } + } + } + + fn emit_overflow_assert( + enum_ident: &Ident, + variants: &[Ident], + repr_ty: &syn::Path, + input_ty: &ValidTy, + ) -> TokenStream { + let qualified_i128: syn::Path =3D parse_quote! { ::core::primitive= ::i128 }; + let qualified_u128: syn::Path =3D parse_quote! { ::core::primitive= ::u128 }; + + let input_min =3D input_ty.emit_min(); + let input_max =3D input_ty.emit_max(); + + let variant_fits =3D variants.iter().map(|variant| { + let msg =3D format!( + "enum discriminant overflow: \ + `{enum_ident}::{variant}` does not fit in `{input_ty}`", + ); + ::quote::quote! { + ::core::assert!(fits(#enum_ident::#variant as #repr_ty), #= msg); + } + }); + + ::quote::quote! { + const _: () =3D { + const fn fits(d: #repr_ty) -> ::core::primitive::bool { + // For every integer type, its minimum value always fi= ts in `i128`. + let dst_min =3D #input_min; + // For every integer type, its maximum value always fi= ts in `u128`. + let dst_max =3D #input_max; + + #[allow(unused_comparisons)] + let is_src_signed =3D #repr_ty::MIN < 0; + #[allow(unused_comparisons)] + let is_dst_signed =3D dst_min < 0; + + if is_src_signed && is_dst_signed { + // Casting from a signed value to `i128` does not = overflow since + // `i128` is the largest signed primitive integer = type. + (d as #qualified_i128) >=3D (dst_min as #qualified= _i128) + && (d as #qualified_i128) <=3D (dst_max as #qu= alified_i128) + } else if is_src_signed && !is_dst_signed { + // Casting from a signed value greater than 0 to `= u128` does not + // overflow since `u128::MAX` is greater than `i12= 8::MAX`. + d >=3D 0 && (d as #qualified_u128) <=3D (dst_max a= s #qualified_u128) + } else { + // Casting from an unsigned value to `u128` does n= ot overflow since + // `u128` is the largest unsigned primitive intege= r type. + (d as #qualified_u128) <=3D (dst_max as #qualified= _u128) + } + } + + #(#variant_fits)* + }; + } + } +} + +enum ValidTy { + Bounded(Bounded), + Primitive(Ident), +} + +impl ValidTy { + fn validate(&self) -> syn::Result<()> { + match self { + Self::Bounded(inner) =3D> inner.validate(), + Self::Primitive(ident) =3D> validate_primitive(ident), + } + } + + fn emit_min(&self) -> TokenStream { + match self { + Self::Bounded(inner) =3D> inner.emit_min(), + Self::Primitive(ident) if ident =3D=3D "bool" =3D> { + ::quote::quote! { 0 } + } + qualified @ Self::Primitive(_) =3D> ::quote::quote! { #qualifi= ed::MIN }, + } + } + + fn emit_max(&self) -> TokenStream { + match self { + Self::Bounded(inner) =3D> inner.emit_max(), + Self::Primitive(ident) if ident =3D=3D "bool" =3D> { + ::quote::quote! { 1 } + } + qualified @ Self::Primitive(_) =3D> ::quote::quote! { #qualifi= ed::MAX }, + } + } +} + +impl Parse for ValidTy { + fn parse(input: ParseStream<'_>) -> syn::Result { + if input.peek(Ident) && input.peek2(Token![<]) { + return Ok(ValidTy::Bounded(input.parse()?)); + } + Ok(ValidTy::Primitive(input.parse()?)) + } +} + +impl ::quote::ToTokens for ValidTy { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Bounded(inner) =3D> inner.to_tokens(tokens), + Self::Primitive(ident) =3D> { + let qualified_name: syn::Path =3D parse_quote! { ::core::p= rimitive::#ident }; + qualified_name.to_tokens(tokens) + } + } + } +} + +impl fmt::Display for ValidTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Bounded(inner) =3D> inner.fmt(f), + Self::Primitive(ident) =3D> ident.fmt(f), + } + } +} + +struct Bounded { + name: Ident, + open_angle: Token![<], + base_ty: Ident, + comma: Token![,], + bits: LitInt, + close_angle: Token![>], +} + +impl Bounded { + const NAME: &'static str =3D "Bounded"; + const QUALIFIED_NAME: &'static str =3D "::kernel::num::Bounded"; + + fn validate(&self) -> syn::Result<()> { + let name =3D &self.name; + if name !=3D Self::NAME { + let msg =3D format!("expected `{}`, found {}", Self::NAME, nam= e); + return Err(syn::Error::new(name.span(), msg)); + } + validate_primitive(&self.base_ty)?; + Ok(()) + } + + fn emit_from_expr(&self, expr: &Expr) -> TokenStream { + let Self { base_ty, bits, .. } =3D self; + let qualified_name: syn::Path =3D parse_str(Self::QUALIFIED_NAME).= expect("valid path"); + ::quote::quote! { + #qualified_name::<#base_ty, #bits>::from_expr(#expr) + } + } + + fn emit_qualified_base_ty(&self) -> TokenStream { + let base_ty =3D &self.base_ty; + ::quote::quote! { ::core::primitive::#base_ty } + } + + fn emit_min(&self) -> TokenStream { + let bits =3D &self.bits; + let base_ty =3D self.emit_qualified_base_ty(); + ::quote::quote! { #base_ty::MIN >> (#base_ty::BITS - #bits) } + } + + fn emit_max(&self) -> TokenStream { + let bits =3D &self.bits; + let base_ty =3D self.emit_qualified_base_ty(); + ::quote::quote! { #base_ty::MAX >> (#base_ty::BITS - #bits) } + } +} + +impl Parse for Bounded { + fn parse(input: ParseStream<'_>) -> syn::Result { + Ok(Self { + name: input.parse()?, + open_angle: input.parse()?, + base_ty: input.parse()?, + comma: input.parse()?, + bits: input.parse()?, + close_angle: input.parse()?, + }) + } +} + +impl ::quote::ToTokens for Bounded { + fn to_tokens(&self, tokens: &mut TokenStream) { + let qualified_name: syn::Path =3D parse_str(Self::QUALIFIED_NAME).= expect("valid path"); + qualified_name.to_tokens(tokens); + self.open_angle.to_tokens(tokens); + self.base_ty.to_tokens(tokens); + self.comma.to_tokens(tokens); + self.bits.to_tokens(tokens); + self.close_angle.to_tokens(tokens); + } +} + +impl fmt::Display for Bounded { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}<{}, {}>", Self::NAME, self.base_ty, self.bits) + } +} + +fn validate_primitive(ident: &Ident) -> syn::Result<()> { + if is_valid_primitive(ident) { + return Ok(()); + } + let msg =3D + format!("expected `bool` or primitive integer type (e.g., `u8`, `i= 8`), found {ident}"); + Err(syn::Error::new(ident.span(), msg)) +} + +fn is_valid_primitive(ident: &Ident) -> bool { + matches!( + ident.to_string().as_str(), + "bool" + | "u8" + | "u16" + | "u32" + | "u64" + | "u128" + | "usize" + | "i8" + | "i16" + | "i32" + | "i64" + | "i128" + | "isize" + ) +} diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index b38002151871a33f6b4efea70be2deb6ddad38e2..02528d7212b75d28788f0c33479= edb272fa12e27 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -14,6 +14,7 @@ #[macro_use] mod quote; mod concat_idents; +mod convert; mod export; mod fmt; mod helpers; @@ -23,6 +24,10 @@ mod vtable; =20 use proc_macro::TokenStream; +use syn::{ + parse_macro_input, + DeriveInput, // +}; =20 /// Declares a kernel module. /// @@ -475,3 +480,155 @@ pub fn paste(input: TokenStream) -> TokenStream { pub fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { kunit::kunit_tests(attr, ts) } + +/// A derive macro for providing an implementation of the [`Into`] trait. +/// +/// This macro automatically derives the [`Into`] trait for a given enum b= y generating +/// the relevant [`From`] implementation. Currently, it only supports [uni= t-only enum]s. +/// +/// [unit-only enum]: https://doc.rust-lang.org/reference/items/enumeratio= ns.html#r-items.enum.unit-only +/// +/// # Notes +/// +/// - Unlike its name suggests, the macro actually generates [`From`] impl= ementations +/// which automatically provide corresponding [`Into`] implementations. +/// +/// - The macro uses the `into` custom attribute or `repr` attribute to ge= nerate [`From`] +/// implementations. `into` always takes precedence over `repr`. +/// +/// - The macro generates a compile-time assertion for every variant to en= sure its +/// discriminant value fits within the type being converted into. +/// +/// # Supported types in `#[into(...)]` +/// +/// - [`bool`] +/// - Primitive integer types (e.g., [`i8`], [`u8`]) +/// - [`Bounded`] +/// +/// [`Bounded`]: ../kernel/num/bounded/struct.Bounded.html +/// +/// # Examples +/// +/// ## Without Attributes +/// +/// Since [the default `Rust` representation uses `isize` for the discrimi= nant type][repr-rust], +/// the macro implements `From` for `isize`: +/// +/// [repr-rust]: https://doc.rust-lang.org/reference/items/enumerations.ht= ml#r-items.enum.discriminant.repr-rust +/// +/// ``` +/// use kernel::macros::Into; +/// +/// #[derive(Debug, Default, Into)] +/// enum Foo { +/// #[default] +/// A, +/// B =3D 0x7, +/// } +/// +/// assert_eq!(0_isize, Foo::A.into()); +/// assert_eq!(0x7_isize, Foo::B.into()); +/// ``` +/// +/// ## With `#[repr(T)]` +/// +/// The macro implements `From` for `T`: +/// +/// ``` +/// use kernel::macros::Into; +/// +/// #[derive(Debug, Default, Into)] +/// #[repr(u8)] +/// enum Foo { +/// #[default] +/// A, +/// B =3D 0x7, +/// } +/// +/// assert_eq!(0_u8, Foo::A.into()); +/// assert_eq!(0x7_u8, Foo::B.into()); +/// ``` +/// +/// ## With `#[into(...)]` +/// +/// The macro implements `From` for each `T` specified in `#[into(...= )]`, +/// which always overrides `#[repr(...)]`: +/// +/// ``` +/// use kernel::{ +/// macros::Into, +/// num::Bounded, // +/// }; +/// +/// #[derive(Debug, Default, Into)] +/// #[into(bool, i16, Bounded)] +/// #[repr(u8)] +/// enum Foo { +/// #[default] +/// A, +/// B, +/// } +/// +/// assert_eq!(false, Foo::A.into()); +/// assert_eq!(true, Foo::B.into()); +/// +/// assert_eq!(0_i16, Foo::A.into()); +/// assert_eq!(1_i16, Foo::B.into()); +/// +/// let foo_a: Bounded =3D Foo::A.into(); +/// let foo_b: Bounded =3D Foo::B.into(); +/// assert_eq!(Bounded::::new::<0>(), foo_a); +/// assert_eq!(Bounded::::new::<1>(), foo_b); +/// ``` +/// +/// ## Compile-time Overflow Assertion +/// +/// The following examples do not compile: +/// +/// ```compile_fail +/// # use kernel::macros::Into; +/// #[derive(Into)] +/// #[into(u8)] +/// enum Foo { +/// // `256` is larger than `u8::MAX`. +/// A =3D 256, +/// } +/// ``` +/// +/// ```compile_fail +/// # use kernel::macros::Into; +/// #[derive(Into)] +/// #[into(u8)] +/// enum Foo { +/// // `-1` cannot be represented with `u8`. +/// A =3D -1, +/// } +/// ``` +/// +/// ## Unsupported Cases +/// +/// The following examples do not compile: +/// +/// ```compile_fail +/// # use kernel::macros::Into; +/// // Tuple-like enums or struct-like enums are not allowed. +/// #[derive(Into)] +/// enum Foo { +/// A(u8), +/// B { inner: u8 }, +/// } +/// ``` +/// +/// ```compile_fail +/// # use kernel::macros::Into; +/// // Structs are not allowed. +/// #[derive(Into)] +/// struct Foo(u8); +/// ``` +#[proc_macro_derive(Into, attributes(into))] +pub fn derive_into(input: TokenStream) -> TokenStream { + let input =3D parse_macro_input!(input as DeriveInput); + convert::derive_into(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} --=20 2.47.3 From nobody Sat Feb 7 08:27:19 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9756A2F0C48; Thu, 25 Dec 2025 08:37:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766651875; cv=none; b=XyIJET3AI6DdVV93iBJ2U9iAsZs8U3YpxeWXhdCS1YpLJvgxEvymVnRcsBFufYQo+nKBs5/0QCwk29yeteyn5NKHfN21OYc5MRYNwrgS5giM/EiRrntJrZgRUAGvZILk/ens4cUujDBEo91JU3WL0DfG88L4RRdpzbfm1xfAvfk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766651875; c=relaxed/simple; bh=/uKl1PFp+5/UbX3th8IeCZMAt9/LNq8Ebt1s0AOGn6I=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=EWV+GGHZIB31OYYJJ7wSdWwXZElwKJ+5mZoNBM+Li6uBu/QaQdsZnIVxnueor0iw7D8Re9FkM8bswkYhX9niyHlJ/JGTdJyFJnwREyiuXMkDOoBOvyi30UtylAY4zc6RFj4vcMzmANStnGyy2ZZiDKTRMYdjXu4/1N8KhLFXLE4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=u7K7YT1f; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="u7K7YT1f" Received: by smtp.kernel.org (Postfix) with ESMTPS id 3859AC16AAE; Thu, 25 Dec 2025 08:37:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1766651875; bh=/uKl1PFp+5/UbX3th8IeCZMAt9/LNq8Ebt1s0AOGn6I=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=u7K7YT1fLjoQJ66NL1+awwatHkrbRwEiyN3ve7dozucfeBZAkCXR289dwu91hugkx xWtXMplFYqPiDKRV8D4Pn5hvhDutyUPlG9yAzpxXT3SRk/yiXX+m/DefbRcIwHExsk HbVG4ZvmuWWPIBSdIYhxKvcccSHG6prkyKlY+4FqTvp+7rsfhhQ9cbBvZXLPxPZO7b VIbLS86pqRnrjvopBf6C8zDk9GaETsQHfoDYhZjpq5H27Ilwr98M3hp2inBuO/8RB6 ohPA7taWT9VQ0Si978UhdTjp2crolhkJOVRY2uKES3pbD8KlIOERVcAj/dG23xjnG8 O7St0f7gqBLiw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2D464E784A6; Thu, 25 Dec 2025 08:37:55 +0000 (UTC) From: Jesung Yang via B4 Relay Date: Thu, 25 Dec 2025 08:37:48 +0000 Subject: [PATCH v4 2/4] rust: macros: add derive macro for `TryFrom` Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251225-try-from-into-macro-v4-2-4a563d597836@gmail.com> References: <20251225-try-from-into-macro-v4-0-4a563d597836@gmail.com> In-Reply-To: <20251225-try-from-into-macro-v4-0-4a563d597836@gmail.com> To: Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Alexandre Courbot Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org, Jesung Yang X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1766651873; l=10323; i=y.j3ms.n@gmail.com; s=20251225; h=from:subject:message-id; bh=r7sp35OD69C2rrD2B8/6PQ+AdErp08gK/fQ9WuVo77U=; b=OATMTspRpfvOCzDCQGHDIpegplqChFKkXHkFhcvjfPp9VsBz+K727vqzgsBgkqvenTcUvdCZK Gi1+XU4A7E1DNyIEGYufy+cQPVwsbxxqA+zoXh2RXT9YIszmOqz9ZtF X-Developer-Key: i=y.j3ms.n@gmail.com; a=ed25519; pk=2yVgO1I+y7kkFSF2Dc/Dckj4L2FgRnvmERHFt4bspbI= X-Endpoint-Received: by B4 Relay for y.j3ms.n@gmail.com/20251225 with auth_id=586 X-Original-From: Jesung Yang Reply-To: y.j3ms.n@gmail.com From: Jesung Yang Introduce a procedural macro `TryFrom` to automatically implement the `TryFrom` trait for unit-only enums. This reduces boilerplate in cases where numeric values need to be interpreted as relevant enum variants. This situation often arises when working with low-level data sources. A typical example is the `Chipset` enum in nova-core, where the value read from a GPU register should be mapped to a corresponding variant. The macro not only supports primitive types such as `bool` or `i8`, but also `Bounded`, a wrapper around integer types limiting the number of bits usable for value representation. This accommodates the shift toward more restrictive register field representations in nova-core where values are constrained to specific bit ranges. Signed-off-by: Jesung Yang --- rust/macros/convert.rs | 64 +++++++++++++++++++ rust/macros/lib.rs | 162 +++++++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 226 insertions(+) diff --git a/rust/macros/convert.rs b/rust/macros/convert.rs index 3e623cc894bff279482dd9daeaa7054937357ec6..d49a58e85de16d13ce9a51cafa3= 1940e42c5840f 100644 --- a/rust/macros/convert.rs +++ b/rust/macros/convert.rs @@ -30,6 +30,10 @@ pub(crate) fn derive_into(input: DeriveInput) -> syn::Re= sult { derive(DeriveTarget::Into, input) } =20 +pub(crate) fn derive_try_from(input: DeriveInput) -> syn::Result { + derive(DeriveTarget::TryFrom, input) +} + fn derive(target: DeriveTarget, input: DeriveInput) -> syn::Result { let mut errors: Option =3D None; let mut combine_error =3D |err| match errors.as_mut() { @@ -108,18 +112,21 @@ fn derive(target: DeriveTarget, input: DeriveInput) -= > syn::Result #[derive(Clone, Copy, Debug)] enum DeriveTarget { Into, + TryFrom, } =20 impl DeriveTarget { fn get_trait_name(&self) -> &'static str { match self { Self::Into =3D> "Into", + Self::TryFrom =3D> "TryFrom", } } =20 fn get_helper_name(&self) -> &'static str { match self { Self::Into =3D> "into", + Self::TryFrom =3D> "try_from", } } } @@ -184,6 +191,7 @@ fn derive_for_enum( ) -> TokenStream { let impl_fn =3D match target { DeriveTarget::Into =3D> impl_into, + DeriveTarget::TryFrom =3D> impl_try_from, }; =20 let qualified_repr_ty: syn::Path =3D parse_quote! { ::core::primitive:= :#repr_ty }; @@ -237,6 +245,54 @@ fn from(#param: #enum_ident) -> #input_ty { } } =20 + fn impl_try_from( + enum_ident: &Ident, + variants: &[Ident], + repr_ty: &syn::Path, + input_ty: &ValidTy, + ) -> TokenStream { + let param =3D Ident::new("value", Span::call_site()); + + let overflow_assertion =3D emit_overflow_assert(enum_ident, varian= ts, repr_ty, input_ty); + let emit_cast =3D |variant| { + let variant =3D ::quote::quote! { #enum_ident::#variant }; + match input_ty { + ValidTy::Bounded(inner) =3D> { + let base_ty =3D inner.emit_qualified_base_ty(); + let expr =3D parse_quote! { #variant as #base_ty }; + inner.emit_new(&expr) + } + ValidTy::Primitive(ident) if ident =3D=3D "bool" =3D> { + ::quote::quote! { ((#variant as #repr_ty) =3D=3D 1) } + } + qualified @ ValidTy::Primitive(_) =3D> ::quote::quote! { #= variant as #qualified }, + } + }; + + let clauses =3D variants.iter().map(|variant| { + let cast =3D emit_cast(variant); + ::quote::quote! { + if #param =3D=3D #cast { + ::core::result::Result::Ok(#enum_ident::#variant) + } else + } + }); + + ::quote::quote! { + #[automatically_derived] + impl ::core::convert::TryFrom<#input_ty> for #enum_ident { + type Error =3D ::kernel::prelude::Error; + fn try_from(#param: #input_ty) -> Result<#enum_ident, Self= ::Error> { + #overflow_assertion + + #(#clauses)* { + ::core::result::Result::Err(::kernel::prelude::EIN= VAL) + } + } + } + } + } + fn emit_overflow_assert( enum_ident: &Ident, variants: &[Ident], @@ -389,6 +445,14 @@ fn emit_from_expr(&self, expr: &Expr) -> TokenStream { } } =20 + fn emit_new(&self, expr: &Expr) -> TokenStream { + let Self { base_ty, bits, .. } =3D self; + let qualified_name: syn::Path =3D parse_str(Self::QUALIFIED_NAME).= expect("valid path"); + ::quote::quote! { + #qualified_name::<#base_ty, #bits>::new::<{ #expr }>() + } + } + fn emit_qualified_base_ty(&self) -> TokenStream { let base_ty =3D &self.base_ty; ::quote::quote! { ::core::primitive::#base_ty } diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index 02528d7212b75d28788f0c33479edb272fa12e27..4dc7de0167a53b778562e24cd14= 5cece50555d91 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -632,3 +632,165 @@ pub fn derive_into(input: TokenStream) -> TokenStream= { .unwrap_or_else(syn::Error::into_compile_error) .into() } + +/// A derive macro for generating an implementation of the [`TryFrom`] tra= it. +/// +/// This macro automatically derives the [`TryFrom`] trait for a given enu= m. Currently, +/// it only supports [unit-only enum]s. +/// +/// [unit-only enum]: https://doc.rust-lang.org/reference/items/enumeratio= ns.html#r-items.enum.unit-only +/// +/// # Notes +/// +/// - The macro generates [`TryFrom`] implementations that: +/// - Return `Ok(VARIANT)` when the input corresponds to a variant. +/// - Return `Err(EINVAL)` when the input does not correspond to any var= iant. +/// (where `EINVAL` is from [`kernel::error::code`]). +/// +/// - The macro uses the `try_from` custom attribute or `repr` attribute t= o generate +/// [`TryFrom`] implementations. `try_from` always takes precedence over= `repr`. +/// +/// - The macro generates a compile-time assertion for every variant to en= sure its +/// discriminant value fits within the type being converted from. +/// +/// [`kernel::error::code`]: ../kernel/error/code/index.html +/// +/// # Supported types in `#[try_from(...)]` +/// +/// - [`bool`] +/// - Primitive integer types (e.g., [`i8`], [`u8`]) +/// - [`Bounded`] +/// +/// [`Bounded`]: ../kernel/num/bounded/struct.Bounded.html +/// +/// # Examples +/// +/// ## Without Attributes +/// +/// Since [the default `Rust` representation uses `isize` for the discrimi= nant type][repr-rust], +/// the macro implements `TryFrom`: +/// +/// [repr-rust]: https://doc.rust-lang.org/reference/items/enumerations.ht= ml#r-items.enum.discriminant.repr-rust +/// +/// ```rust +/// # use kernel::prelude::*; +/// use kernel::macros::TryFrom; +/// +/// #[derive(Debug, Default, PartialEq, TryFrom)] +/// enum Foo { +/// #[default] +/// A, +/// B =3D 0x7, +/// } +/// +/// assert_eq!(Err(EINVAL), Foo::try_from(-1_isize)); +/// assert_eq!(Ok(Foo::A), Foo::try_from(0_isize)); +/// assert_eq!(Ok(Foo::B), Foo::try_from(0x7_isize)); +/// assert_eq!(Err(EINVAL), Foo::try_from(0x8_isize)); +/// ``` +/// +/// ## With `#[repr(T)]` +/// +/// The macro implements `TryFrom`: +/// +/// ```rust +/// # use kernel::prelude::*; +/// use kernel::macros::TryFrom; +/// +/// #[derive(Debug, Default, PartialEq, TryFrom)] +/// #[repr(u8)] +/// enum Foo { +/// #[default] +/// A, +/// B =3D 0x7, +/// } +/// +/// assert_eq!(Ok(Foo::A), Foo::try_from(0_u8)); +/// assert_eq!(Ok(Foo::B), Foo::try_from(0x7_u8)); +/// assert_eq!(Err(EINVAL), Foo::try_from(0x8_u8)); +/// ``` +/// +/// ## With `#[try_from(...)]` +/// +/// The macro implements `TryFrom` for each `T` specified in `#[try_fro= m(...)]`, +/// which always overrides `#[repr(...)]`: +/// +/// ```rust +/// # use kernel::prelude::*; +/// use kernel::{ +/// macros::TryFrom, +/// num::Bounded, // +/// }; +/// +/// #[derive(Debug, Default, PartialEq, TryFrom)] +/// #[try_from(bool, i16, Bounded)] +/// #[repr(u8)] +/// enum Foo { +/// #[default] +/// A, +/// B, +/// } +/// +/// assert_eq!(Err(EINVAL), Foo::try_from(-1_i16)); +/// assert_eq!(Ok(Foo::A), Foo::try_from(0_i16)); +/// assert_eq!(Ok(Foo::B), Foo::try_from(1_i16)); +/// assert_eq!(Err(EINVAL), Foo::try_from(2_i16)); +/// +/// assert_eq!(Ok(Foo::A), Foo::try_from(false)); +/// assert_eq!(Ok(Foo::B), Foo::try_from(true)); +/// +/// assert_eq!(Ok(Foo::A), Foo::try_from(Bounded::::new::<0>())); +/// assert_eq!(Ok(Foo::B), Foo::try_from(Bounded::::new::<1>())); +/// ``` +/// +/// ## Compile-time Overflow Assertion +/// +/// The following examples do not compile: +/// +/// ```compile_fail +/// # use kernel::macros::Into; +/// #[derive(Into)] +/// #[into(u8)] +/// enum Foo { +/// // `256` is larger than `u8::MAX`. +/// A =3D 256, +/// } +/// ``` +/// +/// ```compile_fail +/// # use kernel::macros::Into; +/// #[derive(Into)] +/// #[into(u8)] +/// enum Foo { +/// // `-1` cannot be represented with `u8`. +/// A =3D -1, +/// } +/// ``` +/// +/// ## Unsupported Cases +/// +/// The following examples do not compile: +/// +/// ```compile_fail +/// # use kernel::macros::TryFrom; +/// // Tuple-like enums or struct-like enums are not allowed. +/// #[derive(TryFrom)] +/// enum Foo { +/// A(u8), +/// B { inner: u8 }, +/// } +/// ``` +/// +/// ```compile_fail +/// # use kernel::macros::TryFrom; +/// // Structs are not allowed. +/// #[derive(TryFrom)] +/// struct Foo(u8); +/// ``` +#[proc_macro_derive(TryFrom, attributes(try_from))] +pub fn derive_try_from(input: TokenStream) -> TokenStream { + let input =3D parse_macro_input!(input as DeriveInput); + convert::derive_try_from(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} --=20 2.47.3 From nobody Sat Feb 7 08:27:19 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BAA1330ACF1; Thu, 25 Dec 2025 08:37:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766651875; cv=none; b=oVHNOrTG77Z+oKf3RbH+I0RuVD68+yynyE2J6+PSrTPW7onmeZAjpz4pSszjWQ598bT39fEmKqfXpLqBhY6slKXgpLuVnwryLnVf/JLwAcmKgosFay4AxKuJFCeYqr8LIRio+oCXLl7Momk8DDXngwF+wy5L4mdrYUL9daf8CGk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766651875; c=relaxed/simple; bh=eBZWbUMtB9g7AP2jiTel5PiCma6wU3T2xSvKajGCa+g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cB73DDQAJ7OqXYCcH205JgpbXI6Cvn7QbNhszxot+BDbve3UNCP/HbuW1KYb0a6Zu6YgIN9RmFAckFdt0w7DXDOdBjkLshVdxSpYmqsBIfFOKDkTeb4Vec/r2tpBLp7vsZLbjhS3vMcBR80lE3te5HG4cJpv8o4DT6uTQssMzdU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=GO0/MFk+; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="GO0/MFk+" Received: by smtp.kernel.org (Postfix) with ESMTPS id 46372C19423; Thu, 25 Dec 2025 08:37:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1766651875; bh=eBZWbUMtB9g7AP2jiTel5PiCma6wU3T2xSvKajGCa+g=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=GO0/MFk+Zww35ruZgCPHwcBXrPNtjt/EXlCrRRWa+BhlO4aY+LUJta+tEnCaI03vm XD99w2M6W00RsKhZ8OKwQiosMC2l+WUbj4ubSIlOO4YX0f/9YcHo7vQ81+5Mr6hF+Y WdILR0Cydgp+KiWVf8GPPzbQ0YA0+OyoWWR/+jPIbZjvuNP8FFA7g+eksy8cQMgsxm KiDNd/9zrcoSef0qEiBow5URUTVY6dB/0M9Cam8KFrHNp9BTaZlZMcOckLQXLiYelF pnJhfyxLda3OqOE52o9gcb1X+O0Vvb6czYHWjAbHatqHjnUHmVEEvu/RcWVLiJcd6w H+tr8DHTpmN1w== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3D986E7AD45; Thu, 25 Dec 2025 08:37:55 +0000 (UTC) From: Jesung Yang via B4 Relay Date: Thu, 25 Dec 2025 08:37:49 +0000 Subject: [PATCH v4 3/4] rust: macros: add private doctests for `Into` derive macro Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251225-try-from-into-macro-v4-3-4a563d597836@gmail.com> References: <20251225-try-from-into-macro-v4-0-4a563d597836@gmail.com> In-Reply-To: <20251225-try-from-into-macro-v4-0-4a563d597836@gmail.com> To: Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Alexandre Courbot Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org, Jesung Yang X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1766651873; l=12327; i=y.j3ms.n@gmail.com; s=20251225; h=from:subject:message-id; bh=lMOPIc4clzbCypgXQIv3rNSmwd950xQ9+ntiFJzHqx4=; b=T6p9JQKyPNjovnL84Q1S3wtM1tzNhIr3rm4p87AtpW7GOZWsR6H2HEDWIsDNdhCNbb1lFdv9o A+zo9xyyrgPBpL3GHCsJ/uaqKUT0pR7B5uyNv+5w7sIzcFglQrx+AY+ X-Developer-Key: i=y.j3ms.n@gmail.com; a=ed25519; pk=2yVgO1I+y7kkFSF2Dc/Dckj4L2FgRnvmERHFt4bspbI= X-Endpoint-Received: by B4 Relay for y.j3ms.n@gmail.com/20251225 with auth_id=586 X-Original-From: Jesung Yang Reply-To: y.j3ms.n@gmail.com From: Jesung Yang Add internal doctests to verify the `Into` derive macro's logic. This ensures comprehensive testing while keeping the public-facing documentation compact and readable. Signed-off-by: Jesung Yang --- rust/macros/convert.rs | 436 +++++++++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 436 insertions(+) diff --git a/rust/macros/convert.rs b/rust/macros/convert.rs index d49a58e85de16d13ce9a51cafa31940e42c5840f..63b391a47867fde3ed3d4873dac= cfc50b0872314 100644 --- a/rust/macros/convert.rs +++ b/rust/macros/convert.rs @@ -529,3 +529,439 @@ fn is_valid_primitive(ident: &Ident) -> bool { | "isize" ) } + +mod derive_into_tests { + /// ``` + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(u8)] + /// enum Foo { + /// // Works with const expressions. + /// A =3D add(0, 0), + /// B =3D 2_isize.pow(1) - 1, + /// } + /// + /// const fn add(a: isize, b: isize) -> isize { + /// a + b + /// } + /// + /// assert_eq!(0_u8, Foo::A.into()); + /// assert_eq!(1_u8, Foo::B.into()); + /// ``` + mod works_with_const_expr {} + + /// ``` + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(bool)] + /// enum Foo { + /// A, + /// B, + /// } + /// + /// assert_eq!(false, Foo::A.into()); + /// assert_eq!(true, Foo::B.into()); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(bool)] + /// enum Foo { + /// // `-1` cannot be represented with `bool`. + /// A =3D -1, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(bool)] + /// enum Foo { + /// // `2` cannot be represented with `bool`. + /// A =3D 2, + /// } + /// ``` + mod overflow_assert_works_on_bool {} + + /// ``` + /// use kernel::{ + /// macros::Into, + /// num::Bounded, // + /// }; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// enum Foo { + /// A =3D -1 << 6, // The minimum value of `Bounded`. + /// B =3D (1 << 6) - 1, // The maximum value of `Bounded`. + /// } + /// + /// let foo_a: Bounded =3D Foo::A.into(); + /// let foo_b: Bounded =3D Foo::B.into(); + /// assert_eq!(Bounded::::new::<{ -1_i8 << 6 }>(), foo_a); + /// assert_eq!(Bounded::::new::<{ (1_i8 << 6) - 1 }>(), foo_b); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// enum Foo { + /// // `1 << 6` cannot be represented with `Bounded`. + /// A =3D 1 << 6, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// enum Foo { + /// // `(-1 << 6) - 1` cannot be represented with `Bounded`. + /// A =3D (-1 << 6) - 1, + /// } + /// ``` + /// + /// ``` + /// use kernel::{ + /// macros::Into, + /// num::Bounded, // + /// }; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// enum Foo { + /// A =3D -1, // The minimum value of `Bounded`. + /// B, // The maximum value of `Bounded`. + /// } + /// + /// let foo_a: Bounded =3D Foo::A.into(); + /// let foo_b: Bounded =3D Foo::B.into(); + /// assert_eq!(Bounded::::new::<{ -1_i8 }>(), foo_a); + /// assert_eq!(Bounded::::new::<{ 0_i8 } >(), foo_b); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// enum Foo { + /// // `1` cannot be represented with `Bounded`. + /// A =3D 1, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// enum Foo { + /// // `-2` cannot be represented with `Bounded`. + /// A =3D -2, + /// } + /// ``` + /// + /// ``` + /// use kernel::{ + /// macros::Into, + /// num::Bounded, // + /// }; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// #[repr(i64)] + /// enum Foo { + /// A =3D i32::MIN as i64, + /// B =3D i32::MAX as i64, + /// } + /// + /// let foo_a: Bounded =3D Foo::A.into(); + /// let foo_b: Bounded =3D Foo::B.into(); + /// assert_eq!(Bounded::::new::<{ i32::MIN }>(), foo_a); + /// assert_eq!(Bounded::::new::<{ i32::MAX }>(), foo_b); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// #[repr(i64)] + /// enum Foo { + /// // `1 << 31` cannot be represented with `Bounded`. + /// A =3D 1 << 31, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// #[repr(i64)] + /// enum Foo { + /// // `(-1 << 31) - 1` cannot be represented with `Bounded`. + /// A =3D (-1 << 31) - 1, + /// } + /// ``` + mod overflow_assert_works_on_signed_bounded {} + + /// ``` + /// use kernel::{ + /// macros::Into, + /// num::Bounded, // + /// }; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// enum Foo { + /// A, // The minimum value of `Bounded`. + /// B =3D (1 << 7) - 1, // The maximum value of `Bounded`. + /// } + /// + /// let foo_a: Bounded =3D Foo::A.into(); + /// let foo_b: Bounded =3D Foo::B.into(); + /// assert_eq!(Bounded::::new::<{ 0 }>(), foo_a); + /// assert_eq!(Bounded::::new::<{ (1_u8 << 7) - 1 }>(), foo_b); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// enum Foo { + /// // `1 << 7` cannot be represented with `Bounded`. + /// A =3D 1 << 7, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// enum Foo { + /// // `-1` cannot be represented with `Bounded`. + /// A =3D -1, + /// } + /// ``` + /// + /// ``` + /// use kernel::{ + /// macros::Into, + /// num::Bounded, // + /// }; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// enum Foo { + /// A, // The minimum value of `Bounded`. + /// B, // The maximum value of `Bounded`. + /// } + /// + /// let foo_a: Bounded =3D Foo::A.into(); + /// let foo_b: Bounded =3D Foo::B.into(); + /// assert_eq!(Bounded::::new::<{ 0 }>(), foo_a); + /// assert_eq!(Bounded::::new::<{ 1 }>(), foo_b); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// enum Foo { + /// // `2` cannot be represented with `Bounded`. + /// A =3D 2, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// enum Foo { + /// // `-1` cannot be represented with `Bounded`. + /// A =3D -1, + /// } + /// ``` + /// + /// ``` + /// use kernel::{ + /// macros::Into, + /// num::Bounded, // + /// }; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// #[repr(u64)] + /// enum Foo { + /// A =3D u32::MIN as u64, + /// B =3D u32::MAX as u64, + /// } + /// + /// let foo_a: Bounded =3D Foo::A.into(); + /// let foo_b: Bounded =3D Foo::B.into(); + /// assert_eq!(Bounded::::new::<{ u32::MIN }>(), foo_a); + /// assert_eq!(Bounded::::new::<{ u32::MAX }>(), foo_b); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// #[repr(u64)] + /// enum Foo { + /// // `1 << 32` cannot be represented with `Bounded`. + /// A =3D 1 << 32, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(Bounded)] + /// #[repr(u64)] + /// enum Foo { + /// // `-1` cannot be represented with `Bounded`. + /// A =3D -1, + /// } + /// ``` + mod overflow_assert_works_on_unsigned_bounded {} + + /// ``` + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(isize)] + /// #[repr(isize)] + /// enum Foo { + /// A =3D isize::MIN, + /// B =3D isize::MAX, + /// } + /// + /// assert_eq!(isize::MIN, Foo::A.into()); + /// assert_eq!(isize::MAX, Foo::B.into()); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(isize)] + /// #[repr(usize)] + /// enum Foo { + /// A =3D (isize::MAX as usize) + 1 + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(i32)] + /// #[repr(i64)] + /// enum Foo { + /// A =3D (i32::MIN as i64) - 1, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(i32)] + /// #[repr(i64)] + /// enum Foo { + /// A =3D (i32::MAX as i64) + 1, + /// } + /// ``` + mod overflow_assert_works_on_signed_int {} + + /// ``` + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(usize)] + /// #[repr(usize)] + /// enum Foo { + /// A =3D usize::MIN, + /// B =3D usize::MAX, + /// } + /// + /// assert_eq!(usize::MIN, Foo::A.into()); + /// assert_eq!(usize::MAX, Foo::B.into()); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(usize)] + /// #[repr(isize)] + /// enum Foo { + /// A =3D -1, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(u32)] + /// #[repr(i64)] + /// enum Foo { + /// A =3D (u32::MIN as i64) - 1, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(u32)] + /// #[repr(i64)] + /// enum Foo { + /// A =3D (u32::MAX as i64) + 1, + /// } + /// ``` + mod overflow_assert_works_on_unsigned_int {} + + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(Bounded, i8, i16, i32, i64)] + /// #[repr(i8)] + /// enum Foo { + /// // `i8::MAX` cannot be represented with `Bounded`. + /// A =3D i8::MAX, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::Into; + /// + /// #[derive(Into)] + /// #[into(i8, i16, i32, i64, Bounded)] + /// #[repr(i8)] + /// enum Foo { + /// // `i8::MAX` cannot be represented with `Bounded`. + /// A =3D i8::MAX, + /// } + /// ``` + mod any_into_target_overflow_is_rejected {} +} --=20 2.47.3 From nobody Sat Feb 7 08:27:19 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 82CAC2E8E09; Thu, 25 Dec 2025 08:37:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766651875; cv=none; b=Ai1L4irx2zrrNiW8DOWtchP2jrX9CzbV0l065/+GOpnbkFnL/3gUfYUDRWyMOXqfEXDrhsRSDVsrzRupIYMk7uMdIGpJBA5xvWE7sflc57FOCcQr2pSIl6LzNyNtVBmTv3r12xBbrIgf+0c9SDXsvDLvYkXnSYb46Szz5bEoERE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766651875; c=relaxed/simple; bh=0P/a/kON4YrMNmaGk0jVyjatgS2RgSiZ+spVmicQNEY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YC+4W4ralfSy4Et05UZ88nHdctOaGOCHHPnuuC98EOU0osG9W2LSysUOe9Kax1+3D9UkK6aJoyKl8+KynEMLC1gXE5r/EVbky9eiht7sgvYJN/GAyiAbKI47JreRKlMR4p2bdkO4m3MpRSEvTL/GVocrbZ3O3GbZsc1YV0J9r6U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=I9ZjDrET; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="I9ZjDrET" Received: by smtp.kernel.org (Postfix) with ESMTPS id 51A22C19421; Thu, 25 Dec 2025 08:37:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1766651875; bh=0P/a/kON4YrMNmaGk0jVyjatgS2RgSiZ+spVmicQNEY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=I9ZjDrETLHPgoV6Ho0eLw2hSJAoDJrzFA6nV7itGcSWHx8y32zMbkqb8X2axMuqJV 53kYJX6Ayo6reuF3rAp67plUCyz8s1TDQngFn/y2vus/WS4Hz3YDVSdV52PgKYxreA 7XaX7RyVNFeJxBiSZsJApDSODNkbAbSOj9lFpnSl9hI6Zc9N2fI8zmzLOJOE1fn0Cb 5fbta+gpb/PJEnocRrSDn26buaqbIjtSFNJOvjWrdEOZEM4Uc9ThvkQWTU8gc+oB2r 0r1T9GoY5cqUljoLz/XeMTSXIT2T7En2fCWOvBZwwbKqV3T/CgR/EAkCpWs3r5MyYC 0V9TVMVKxVVVA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4C520E7AD42; Thu, 25 Dec 2025 08:37:55 +0000 (UTC) From: Jesung Yang via B4 Relay Date: Thu, 25 Dec 2025 08:37:50 +0000 Subject: [PATCH v4 4/4] rust: macros: add private doctests for `TryFrom` derive macro Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251225-try-from-into-macro-v4-4-4a563d597836@gmail.com> References: <20251225-try-from-into-macro-v4-0-4a563d597836@gmail.com> In-Reply-To: <20251225-try-from-into-macro-v4-0-4a563d597836@gmail.com> To: Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Alexandre Courbot Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org, Jesung Yang X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1766651873; l=19161; i=y.j3ms.n@gmail.com; s=20251225; h=from:subject:message-id; bh=UjKHJTRkCJvJ39+EQxak94ej+XBOzZ1tzrGLObX4UHY=; b=KxzLP9ZgeNRB0iLvA3Ww+OpdQcfsfvWVCXcY8SuhLe9G1fXCaos39ucvQKNtpbQaulS/lXeQi j92KABdo9PPCUP/FBga3TAZe5v9MRi6Z8kJ59yfCSR9FRW/NXTgmeKk X-Developer-Key: i=y.j3ms.n@gmail.com; a=ed25519; pk=2yVgO1I+y7kkFSF2Dc/Dckj4L2FgRnvmERHFt4bspbI= X-Endpoint-Received: by B4 Relay for y.j3ms.n@gmail.com/20251225 with auth_id=586 X-Original-From: Jesung Yang Reply-To: y.j3ms.n@gmail.com From: Jesung Yang Add internal doctests to verify the `TryFrom` derive macro's logic. This ensures comprehensive testing while keeping the public-facing documentation compact and readable. Signed-off-by: Jesung Yang --- rust/macros/convert.rs | 579 +++++++++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 579 insertions(+) diff --git a/rust/macros/convert.rs b/rust/macros/convert.rs index 63b391a47867fde3ed3d4873daccfc50b0872314..87c11a98836fe982932b0c62a4e= e3b5401db2126 100644 --- a/rust/macros/convert.rs +++ b/rust/macros/convert.rs @@ -965,3 +965,582 @@ mod overflow_assert_works_on_unsigned_int {} /// ``` mod any_into_target_overflow_is_rejected {} } + +mod derive_try_from_tests { + /// ``` + /// use kernel::{ + /// macros::{ + /// Into, + /// TryFrom, // + /// }, + /// num::Bounded, + /// prelude::*, // + /// }; + /// + /// #[derive(Debug, Into, PartialEq, TryFrom)] + /// #[into(bool, Bounded, Bounded, i8, i16, i32, i64, i1= 28, isize, u8, u16, u32, u64, u128, usize)] + /// #[try_from(bool, Bounded, Bounded, i8, i16, i32, i64= , i128, isize, u8, u16, u32, u64, u128, usize)] + /// enum Foo { + /// A, + /// B, + /// } + /// + /// assert_eq!(false, Foo::A.into()); + /// assert_eq!(true, Foo::B.into()); + /// assert_eq!(Ok(Foo::A), Foo::try_from(false)); + /// assert_eq!(Ok(Foo::B), Foo::try_from(true)); + /// + /// let foo_a: Bounded =3D Foo::A.into(); + /// let foo_b: Bounded =3D Foo::B.into(); + /// assert_eq!(Bounded::::new::<0>(), foo_a); + /// assert_eq!(Bounded::::new::<1>(), foo_b); + /// assert_eq!(Ok(Foo::A), Foo::try_from(Bounded::::new::<0>())= ); + /// assert_eq!(Ok(Foo::B), Foo::try_from(Bounded::::new::<1>())= ); + /// assert_eq!(Err(EINVAL), Foo::try_from(Bounded::::new::<-1>(= ))); + /// assert_eq!(Err(EINVAL), Foo::try_from(Bounded::::new::<2>()= )); + /// + /// let foo_a: Bounded =3D Foo::A.into(); + /// let foo_b: Bounded =3D Foo::B.into(); + /// assert_eq!(Bounded::::new::<0>(), foo_a); + /// assert_eq!(Bounded::::new::<1>(), foo_b); + /// assert_eq!(Ok(Foo::A), Foo::try_from(Bounded::::new::<0>())= ); + /// assert_eq!(Ok(Foo::B), Foo::try_from(Bounded::::new::<1>())= ); + /// assert_eq!(Err(EINVAL), Foo::try_from(Bounded::::new::<2>()= )); + /// + /// macro_rules! gen_signed_tests { + /// ($($type:ty),*) =3D> { + /// $( + /// assert_eq!(0 as $type, Foo::A.into()); + /// assert_eq!(1 as $type, Foo::B.into()); + /// assert_eq!(Ok(Foo::A), Foo::try_from(0 as $type)); + /// assert_eq!(Ok(Foo::B), Foo::try_from(1 as $type)); + /// assert_eq!(Err(EINVAL), Foo::try_from((0 as $type) - 1= )); + /// assert_eq!(Err(EINVAL), Foo::try_from((1 as $type) + 1= )); + /// )* + /// }; + /// } + /// macro_rules! gen_unsigned_tests { + /// ($($type:ty),*) =3D> { + /// $( + /// assert_eq!(0 as $type, Foo::A.into()); + /// assert_eq!(1 as $type, Foo::B.into()); + /// assert_eq!(Ok(Foo::A), Foo::try_from(0 as $type)); + /// assert_eq!(Ok(Foo::B), Foo::try_from(1 as $type)); + /// assert_eq!(Err(EINVAL), Foo::try_from((1 as $type) + 1= )); + /// )* + /// }; + /// } + /// gen_signed_tests!(i8, i16, i32, i64, i128, isize); + /// gen_unsigned_tests!(u8, u16, u32, u64, u128, usize); + /// ``` + mod works_with_derive_into {} + + /// ``` + /// use kernel::{ + /// macros::TryFrom, + /// prelude::*, // + /// }; + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(u8)] + /// enum Foo { + /// // Works with const expressions. + /// A =3D add(0, 0), + /// B =3D 2_isize.pow(1) - 1, + /// } + /// + /// const fn add(a: isize, b: isize) -> isize { + /// a + b + /// } + /// + /// assert_eq!(Ok(Foo::A), Foo::try_from(0_u8)); + /// assert_eq!(Ok(Foo::B), Foo::try_from(1_u8)); + /// assert_eq!(Err(EINVAL), Foo::try_from(2_u8)); + /// ``` + mod works_with_const_expr {} + + /// ``` + /// use kernel::{ + /// macros::TryFrom, + /// prelude::*, // + /// }; + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(bool)] + /// enum Foo { + /// A, + /// B, + /// } + /// + /// assert_eq!(Ok(Foo::A), Foo::try_from(false)); + /// assert_eq!(Ok(Foo::B), Foo::try_from(true)); + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(bool)] + /// enum Bar { + /// A, + /// } + /// + /// assert_eq!(Ok(Bar::A), Bar::try_from(false)); + /// assert_eq!(Err(EINVAL), Bar::try_from(true)); + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(bool)] + /// enum Baz { + /// A =3D 1, + /// } + /// + /// assert_eq!(Err(EINVAL), Baz::try_from(false)); + /// assert_eq!(Ok(Baz::A), Baz::try_from(true)); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(bool)] + /// enum Foo { + /// // `-1` cannot be represented with `bool`. + /// A =3D -1, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(bool)] + /// enum Foo { + /// // `2` cannot be represented with `bool`. + /// A =3D 2, + /// } + /// ``` + mod overflow_assert_works_on_bool {} + + /// ``` + /// use kernel::{ + /// macros::TryFrom, + /// num::Bounded, + /// prelude::*, // + /// }; + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(Bounded)] + /// enum Foo { + /// A =3D -1 << 6, // The minimum value of `Bounded`. + /// B =3D (1 << 6) - 1, // The maximum value of `Bounded`. + /// } + /// + /// assert_eq!(Ok(Foo::A), Foo::try_from(Bounded::::new::<{ -1_= i8 << 6 }>())); + /// assert_eq!(Ok(Foo::B), Foo::try_from(Bounded::::new::<{ (1_= i8 << 6) - 1 }>())); + /// assert_eq!(Err(EINVAL), Foo::try_from(Bounded::::new::<{ (-= 1_i8 << 6) + 1 }>())); + /// assert_eq!(Err(EINVAL), Foo::try_from(Bounded::::new::<{ (1= _i8 << 6) - 2 }>())); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(Bounded)] + /// enum Foo { + /// // `1 << 6` cannot be represented with `Bounded`. + /// A =3D 1 << 6, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(Bounded)] + /// enum Foo { + /// // `(-1 << 6) - 1` cannot be represented with `Bounded`. + /// A =3D (-1 << 6) - 1, + /// } + /// ``` + /// + /// ``` + /// use kernel::{ + /// macros::TryFrom, + /// num::Bounded, + /// prelude::*, // + /// }; + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(Bounded)] + /// enum Foo { + /// A =3D -1, // The minimum value of `Bounded`. + /// B, // The maximum value of `Bounded`. + /// } + /// + /// assert_eq!(Ok(Foo::A), Foo::try_from(Bounded::::new::<{ -1_= i8 }>())); + /// assert_eq!(Ok(Foo::B), Foo::try_from(Bounded::::new::<{ 0_i= 8 } >())); + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(Bounded)] + /// enum Bar { + /// A =3D -1, // The minimum value of `Bounded`. + /// } + /// + /// assert_eq!(Ok(Bar::A), Bar::try_from(Bounded::::new::<{ -1_= i8 }>())); + /// assert_eq!(Err(EINVAL), Bar::try_from(Bounded::::new::<{ 0_= i8 } >())); + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(Bounded)] + /// enum Baz { + /// A, // The maximum value of `Bounded`. + /// } + /// + /// assert_eq!(Err(EINVAL), Baz::try_from(Bounded::::new::<{ -1= _i8 }>())); + /// assert_eq!(Ok(Baz::A), Baz::try_from(Bounded::::new::<{ 0_i= 8 } >())); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(Bounded)] + /// enum Foo { + /// // `1` cannot be represented with `Bounded`. + /// A =3D 1, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(Bounded)] + /// enum Foo { + /// // `-2` cannot be represented with `Bounded`. + /// A =3D -2, + /// } + /// ``` + /// + /// ``` + /// use kernel::{ + /// macros::TryFrom, + /// num::Bounded, + /// prelude::*, // + /// }; + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(Bounded)] + /// #[repr(i64)] + /// enum Foo { + /// A =3D i32::MIN as i64, + /// B =3D i32::MAX as i64, + /// } + /// + /// assert_eq!(Ok(Foo::A), Foo::try_from(Bounded::::new::<{ i= 32::MIN }>())); + /// assert_eq!(Ok(Foo::B), Foo::try_from(Bounded::::new::<{ i= 32::MAX }>())); + /// assert_eq!(Err(EINVAL), Foo::try_from(Bounded::::new::<{ = i32::MIN + 1 }>())); + /// assert_eq!(Err(EINVAL), Foo::try_from(Bounded::::new::<{ = i32::MAX - 1 }>())); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(Bounded)] + /// #[repr(i64)] + /// enum Foo { + /// // `1 << 31` cannot be represented with `Bounded`. + /// A =3D 1 << 31, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(Bounded)] + /// #[repr(i64)] + /// enum Foo { + /// // `(-1 << 31) - 1` cannot be represented with `Bounded`. + /// A =3D (-1 << 31) - 1, + /// } + /// ``` + mod overflow_assert_works_on_signed_bounded {} + + /// ``` + /// use kernel::{ + /// macros::TryFrom, + /// num::Bounded, + /// prelude::*, // + /// }; + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(Bounded)] + /// enum Foo { + /// A, // The minimum value of `Bounded`. + /// B =3D (1 << 7) - 1, // The maximum value of `Bounded`. + /// } + /// + /// assert_eq!(Ok(Foo::A), Foo::try_from(Bounded::::new::<{ 0 }= >())); + /// assert_eq!(Ok(Foo::B), Foo::try_from(Bounded::::new::<{ (1_= u8 << 7) - 1 }>())); + /// assert_eq!(Err(EINVAL), Foo::try_from(Bounded::::new::<{ 1 = }>())); + /// assert_eq!(Err(EINVAL), Foo::try_from(Bounded::::new::<{ (1= _u8 << 7) - 2 }>())); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(Bounded)] + /// enum Foo { + /// // `1 << 7` cannot be represented with `Bounded`. + /// A =3D 1 << 7, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(Bounded)] + /// enum Foo { + /// // `-1` cannot be represented with `Bounded`. + /// A =3D -1, + /// } + /// ``` + /// + /// ``` + /// use kernel::{ + /// macros::TryFrom, + /// num::Bounded, + /// prelude::*, // + /// }; + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(Bounded)] + /// enum Foo { + /// A, // The minimum value of `Bounded`. + /// B, // The maximum value of `Bounded`. + /// } + /// + /// assert_eq!(Ok(Foo::A), Foo::try_from(Bounded::::new::<{ 0 }= >())); + /// assert_eq!(Ok(Foo::B), Foo::try_from(Bounded::::new::<{ 1 }= >())); + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(Bounded)] + /// enum Bar { + /// A, // The minimum value of `Bounded`. + /// } + /// + /// assert_eq!(Ok(Bar::A), Bar::try_from(Bounded::::new::<{ 0 }= >())); + /// assert_eq!(Err(EINVAL), Bar::try_from(Bounded::::new::<{ 1 = }>())); + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(Bounded)] + /// enum Baz { + /// A =3D 1, // The maximum value of `Bounded`. + /// } + /// + /// assert_eq!(Err(EINVAL), Baz::try_from(Bounded::::new::<{ 0 = }>())); + /// assert_eq!(Ok(Baz::A), Baz::try_from(Bounded::::new::<{ 1 }= >())); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(Bounded)] + /// enum Foo { + /// // `2` cannot be represented with `Bounded`. + /// A =3D 2, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(Bounded)] + /// enum Foo { + /// // `-1` cannot be represented with `Bounded`. + /// A =3D -1, + /// } + /// ``` + /// + /// ``` + /// use kernel::{ + /// macros::TryFrom, + /// num::Bounded, + /// prelude::*, // + /// }; + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(Bounded)] + /// #[repr(u64)] + /// enum Foo { + /// A =3D u32::MIN as u64, + /// B =3D u32::MAX as u64, + /// } + /// + /// assert_eq!(Ok(Foo::A), Foo::try_from(Bounded::::new::<{ u= 32::MIN }>())); + /// assert_eq!(Ok(Foo::B), Foo::try_from(Bounded::::new::<{ u= 32::MAX }>())); + /// assert_eq!(Err(EINVAL), Foo::try_from(Bounded::::new::<{ = u32::MIN + 1 }>())); + /// assert_eq!(Err(EINVAL), Foo::try_from(Bounded::::new::<{ = u32::MAX - 1 }>())); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(Bounded)] + /// #[repr(u64)] + /// enum Foo { + /// // `1 << 32` cannot be represented with `Bounded`. + /// A =3D 1 << 32, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(Bounded)] + /// #[repr(u64)] + /// enum Foo { + /// // `-1` cannot be represented with `Bounded`. + /// A =3D -1, + /// } + /// ``` + mod overflow_assert_works_on_unsigned_bounded {} + + /// ``` + /// use kernel::{ + /// macros::TryFrom, + /// num::Bounded, + /// prelude::*, // + /// }; + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(isize)] + /// #[repr(isize)] + /// enum Foo { + /// A =3D isize::MIN, + /// B =3D isize::MAX, + /// } + /// + /// assert_eq!(Ok(Foo::A), Foo::try_from(isize::MIN)); + /// assert_eq!(Ok(Foo::B), Foo::try_from(isize::MAX)); + /// assert_eq!(Err(EINVAL), Foo::try_from(isize::MIN + 1)); + /// assert_eq!(Err(EINVAL), Foo::try_from(isize::MAX - 1)); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(isize)] + /// #[repr(usize)] + /// enum Foo { + /// A =3D (isize::MAX as usize) + 1 + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(i32)] + /// #[repr(i64)] + /// enum Foo { + /// A =3D (i32::MIN as i64) - 1, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(i32)] + /// #[repr(i64)] + /// enum Foo { + /// A =3D (i32::MAX as i64) + 1, + /// } + /// ``` + mod overflow_assert_works_on_signed_int {} + + /// ``` + /// use kernel::{ + /// macros::TryFrom, + /// num::Bounded, + /// prelude::*, // + /// }; + /// + /// #[derive(Debug, PartialEq, TryFrom)] + /// #[try_from(usize)] + /// #[repr(usize)] + /// enum Foo { + /// A =3D usize::MIN, + /// B =3D usize::MAX, + /// } + /// + /// assert_eq!(Ok(Foo::A), Foo::try_from(usize::MIN)); + /// assert_eq!(Ok(Foo::B), Foo::try_from(usize::MAX)); + /// assert_eq!(Err(EINVAL), Foo::try_from(usize::MIN + 1)); + /// assert_eq!(Err(EINVAL), Foo::try_from(usize::MAX - 1)); + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(usize)] + /// #[repr(isize)] + /// enum Foo { + /// A =3D -1, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(u32)] + /// #[repr(i64)] + /// enum Foo { + /// A =3D (u32::MIN as i64) - 1, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(u32)] + /// #[repr(i64)] + /// enum Foo { + /// A =3D (u32::MAX as i64) + 1, + /// } + /// ``` + mod overflow_assert_works_on_unsigned_int {} + + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(Bounded, i8, i16, i32, i64)] + /// #[repr(i8)] + /// enum Foo { + /// // `i8::MAX` cannot be represented with `Bounded`. + /// A =3D i8::MAX, + /// } + /// ``` + /// + /// ```compile_fail + /// use kernel::macros::TryFrom; + /// + /// #[derive(TryFrom)] + /// #[try_from(i8, i16, i32, i64, Bounded)] + /// #[repr(i8)] + /// enum Foo { + /// // `i8::MAX` cannot be represented with `Bounded`. + /// A =3D i8::MAX, + /// } + /// ``` + mod any_try_from_target_overflow_is_rejected {} +} --=20 2.47.3