[PATCH v6 09/10] rust: macros: validate and qualify conversion helper types
From: Kaiqi Guo
Date: Tue Sep 29 2026 - 10:01:15 EST
Reject generic arguments attached to a primitive helper input.
Previously bool<Undefined> was silently treated as bool because only the
final identifier was validated.
Qualify Result in generated TryFrom signatures and the backing primitive
passed to Bounded::new. Local aliases named Result or u8 must not alter
the implementation after its input was validated as a primitive. Add
compile-pass and compile-fail regressions for each case.
Signed-off-by: Kaiqi Guo <chaoji_xinren@xxxxxxx>
---
rust/macros/convert.rs | 52 ++++++++++++++++++++++++++++++++++++++++--
1 file changed, 50 insertions(+), 2 deletions(-)
diff --git a/rust/macros/convert.rs b/rust/macros/convert.rs
index c23b8819572e..0fda475349ef 100644
--- a/rust/macros/convert.rs
+++ b/rust/macros/convert.rs
@@ -295,7 +295,7 @@ fn impl_try_from(
#[automatically_derived]
impl ::core::convert::TryFrom<#input_ty> for #enum_ident {
type Error = ::kernel::prelude::Error;
- fn try_from(#param: #input_ty) -> Result<#enum_ident, Self::Error> {
+ fn try_from(#param: #input_ty) -> ::core::result::Result<#enum_ident, Self::Error> {
#overflow_assertion
#(#clauses)* {
@@ -492,7 +492,8 @@ impl Bounded {
const QUALIFIED_NAME: &'static str = "::kernel::num::Bounded";
fn emit_new(&self, expr: &Expr) -> TokenStream {
- let Self { base_ty, bits, .. } = self;
+ let base_ty = self.emit_qualified_base_ty();
+ let bits = &self.bits;
let qualified_name: syn::Path = parse_str(Self::QUALIFIED_NAME).expect("valid path");
::quote::quote! {
#qualified_name::<#base_ty, #bits>::new::<{ #expr }>()
@@ -556,6 +557,8 @@ fn validate_type(ty: &Type) -> syn::Result<ValidTy> {
let segment = &path.segments[0];
if segment.ident == Bounded::NAME {
return validate_bounded(segment);
+ } else if !matches!(segment.arguments, PathArguments::None) {
+ return Err(make_err(ty));
} else {
return validate_primitive(&segment.ident);
}
@@ -2043,3 +2046,48 @@ mod incomplete_wide_input_8 {}
/// ```
mod incomplete_wide_input_9 {}
}
+
+mod helper_type_tests {
+ /// ```compile_fail
+ /// use kernel::macros::Into;
+ /// #[derive(Into)]
+ /// #[into(bool<Undefined>)]
+ /// enum Invalid { A, B }
+ /// ```
+ ///
+ /// ```compile_fail
+ /// use kernel::macros::TryFrom;
+ /// #[derive(TryFrom)]
+ /// #[try_from(bool<Undefined>)]
+ /// enum Invalid { A, B }
+ /// ```
+ ///
+ /// ```compile_fail
+ /// use kernel::macros::From;
+ /// #[derive(From)]
+ /// #[from(bool<Undefined>)]
+ /// enum Invalid { A, B }
+ /// ```
+ mod primitive_arguments_are_not_ignored {}
+
+ /// ```
+ /// #![allow(non_camel_case_types, dead_code)]
+ /// use kernel::{macros::{From, Into, TryFrom}, num::Bounded};
+ ///
+ /// type Result = ();
+ /// type u8 = i8;
+ ///
+ /// #[derive(Debug, PartialEq, From, Into, TryFrom)]
+ /// #[from(Bounded<u8, 1>)]
+ /// #[into(Bounded<u8, 1>)]
+ /// #[try_from(u8, Bounded<u8, 2>)]
+ /// enum Switch { Off, On }
+ ///
+ /// let value = Bounded::<core::primitive::u8, 1>::new::<1>();
+ /// assert_eq!(Switch::from(value), Switch::On);
+ /// assert_eq!(Bounded::<core::primitive::u8, 1>::from(Switch::On), value);
+ /// assert_eq!(Switch::try_from(0_u8), Ok(Switch::Off));
+ /// assert_eq!(Switch::try_from(value.extend::<2>()), Ok(Switch::On));
+ /// ```
+ mod names_in_scope_do_not_change_generated_types {}
+}