From nobody Wed Oct 8 23:04:36 2025 Received: from mail-pg1-f178.google.com (mail-pg1-f178.google.com [209.85.215.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3E652286D7F; Mon, 23 Jun 2025 15:14:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750691687; cv=none; b=NxgmKJ/iBCyawx2Egb7vG3vDwSmjLOOaQXOLpircDCjln2l19InfGvljXFcTmCtdCh9DJocy3GPHWABUq0rFtn7+H5yAlBr1VNMlACyZElcpsXKdUUh6haBQ9K55gXMDP1XTCWyLhKYtFRe1yo/W/6AaiIWUnwqbxepT0KbUlIw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750691687; c=relaxed/simple; bh=XATRCyZN9h3h1eCMdiJkmGZxqm8MxTsDWzC41eiSOpw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=LsAUZlYcAPWHRCLTVacmdHN8+EbHIzaUeQfs6+eMKWLxc3oKqKgcCR8ynH4qPlG/IaAWkgcmAFeXE3qRnXAzSpcO3TxoR5eGNmZvRUTIbB9LsuIG3QEtdTXcXfg9IDukLDbTxnbytW6/n+G6KCbkds4MxNrzTOhZrSBIUiPdZrY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=LzP6RUw8; arc=none smtp.client-ip=209.85.215.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="LzP6RUw8" Received: by mail-pg1-f178.google.com with SMTP id 41be03b00d2f7-b31d592bbe8so2946027a12.2; Mon, 23 Jun 2025 08:14:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1750691685; x=1751296485; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=4L+SLp5sEjKp/3F3270exGqS9NQW7oUMk/S+cCYt5Es=; b=LzP6RUw8xN8lM8RLg/PWNKgpv1DGYSTcpItELeEQhssAKGXL4uiuSoWrKGVHHTgi06 H2lbuc9vJBFCjVYkSpkG5stgTocsh1FoQ77GIwUJfyjioZfvqyG76Sxu1ORoGwV8sKDd VEzOwr6ZIxeax9eOQDN6J92oj/T70s0Vd0J9nqihsyRdUm95b45C0L/2/YL37O+MNT8p gis+Iq1efdSeJicNo8vgIbya+3kIej9o1QkkqsHIi9nf3IBC2xWB4+nAnA8VNrFIVRyc XAXk2JsYYuJQYd6WI2ga789jTIK0zi82G5G0fjKo2PsEF/0kCNNfmhDUYI+HOX6z9f0g KIDg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750691685; x=1751296485; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=4L+SLp5sEjKp/3F3270exGqS9NQW7oUMk/S+cCYt5Es=; b=K990LMcRgs2YXvTSVRe5RSeh4Udp8thfu66yXcLJj4A4eWiPaWYJfpGr9V1lutK8x9 jgTtLM4BG+7VcOw6mcNsEYh9f3VXDnCCyFZQfngmgrkY6fssJ/rBcaRELC4qBsrkdjzI ZXwGHG7J/ArLGG/be2G9Xhxu1qS3UEaVtu4jsRchzZCFhmClBoOWdZZHBdXIQmGRk2of gYK+UXX30AKZGSsu7d3S0k/1U/93rrCbnnekLZoi+xkwFMJMtOgH7cd8EyFvC1Q6Ki5t BEZq8eAujQ0l2w/UyJYXHBeq8TydukKh9waM11eq4n+3CYx9JANRcYOM9gPv9Dwf2i51 gMYQ== X-Forwarded-Encrypted: i=1; AJvYcCVfASESmFssd/2p2heclco8OIQ5z7lj+yivlr6ydVgbSspC3ckDju3vQlrxu8OUnE1lkD34neysxyhDTR09oQ==@vger.kernel.org X-Gm-Message-State: AOJu0Yx8nxNrteKe1HZOisAHNaC/oetiug2X6Kl9uecPx80JDPdeRB10 RaaEMgsCgnUOxjzjLN4UDJG7p5YGNJkGrFpSYPH7Oh+g5s9GVm44i5mX X-Gm-Gg: ASbGncvPRrOhdo+Pbo4wLr4NYb9b219J/w5/rTJU56vOj1eWToXjbHKB4x7RAWgSrOy PHtwXOhpj0+Y6qJmzYPc6RPQ2Hp+t3CxB1PaiQ5M4ukY+haUQ1u/Onf0GWtpK96+UXcvQ3zocWx AbAZd4BFPkK4Ni5wIltPkJkUOJvSziaOyuYqHM92tQxFwRCIEv0b9pHKLSxwvQyEKu+x2PkP6qO +QjUsOmjExJUJ6+0Tbgyce3qsY/1h7HYHgaBj/EPL2QrGXV48KiUCrIHaAhUSKeiz4r1hWLEr0N QiBJMkfzfrj71/mm1Y9FpUtbk3YAW1+M+k8CrKyYK06nzixzFutGbY9+hWabrzeAZAcFohb1eo5 iHYaTdN0= X-Google-Smtp-Source: AGHT+IHjXjnNaJs4mpv+QbUUX8obW8sAmU/EZar1U85z03oyOBaK0Zh98OMIpjOXdxPItTkxI0ZxFA== X-Received: by 2002:a17:90b:4f46:b0:312:e49b:c972 with SMTP id 98e67ed59e1d1-3159d68a9cbmr18302917a91.15.1750691685273; Mon, 23 Jun 2025 08:14:45 -0700 (PDT) Received: from localhost.localdomain ([112.149.32.52]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3159df71cd6sm8338512a91.4.2025.06.23.08.14.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jun 2025 08:14:44 -0700 (PDT) From: Jesung Yang To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org, Jesung Yang Subject: [PATCH 1/4] rust: introduce `FromPrimitive` trait Date: Mon, 23 Jun 2025 15:14:27 +0000 Message-Id: <702d21d34368b1dcd896343771b00a2303e9a312.1750689857.git.y.j3ms.n@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Introduce a new `FromPrimitive` trait under `kernel::convert` that enables fallible conversion from primitive types to user-defined types. This is useful when numeric values need to be interpreted as structured representations such as enums. These situations often arise when working with low-level data sources, for example when reading values from hardware registers. Signed-off-by: Jesung Yang --- rust/kernel/convert.rs | 154 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 2 files changed, 155 insertions(+) create mode 100644 rust/kernel/convert.rs diff --git a/rust/kernel/convert.rs b/rust/kernel/convert.rs new file mode 100644 index 000000000000..fb01a0e1507a --- /dev/null +++ b/rust/kernel/convert.rs @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Traits for type conversion. + +/// A trait for fallible conversions from primitive types. +/// +/// [`FromPrimitive`] allows converting from various built-in primitive ty= pes +/// (such as integers and `bool`) into a user-defined type, typically an `= enum`. +/// +/// At least [`from_i64`] and [`from_u64`] should be implemented. All othe= r methods +/// have default implementations that convert to `i64` or `u64` using fall= ible casts and +/// delegate to those two core methods. +/// +/// Enums with wide representations such as `#[repr(i128)]` or `#[repr(u12= 8)]` may lose +/// information through narrowing in the default implementations. In such = cases, override +/// [`from_i128`] and [`from_u128`] explicitly. +/// +/// This trait can be used with `#[derive]`. +/// See [`FromPrimitive`](../../macros/derive.FromPrimitive.html) derive m= acro for more +/// information. +/// +/// [`from_i64`]: FromPrimitive::from_i64 +/// [`from_i128`]: FromPrimitive::from_i128 +/// [`from_u64`]: FromPrimitive::from_u64 +/// [`from_u128`]: FromPrimitive::from_u128 +/// +/// # Examples +/// +/// ```rust +/// use kernel::convert::FromPrimitive; +/// +/// #[derive(PartialEq)] +/// enum Foo { +/// A, +/// B =3D 0x17, +/// C =3D -2, +/// } +/// +/// impl FromPrimitive for Foo { +/// fn from_i64(n: i64) -> Option { +/// match n { +/// 0 =3D> Some(Self::A), +/// 0x17 =3D> Some(Self::B), +/// -2 =3D> Some(Self::C), +/// _ =3D> None, +/// } +/// } +/// +/// fn from_u64(n: u64) -> Option { +/// i64::try_from(n).ok().and_then(Self::from_i64) +/// } +/// } +/// +/// assert_eq!(Foo::from_u64(0), Some(Foo::A)); +/// assert_eq!(Foo::from_u64(0x17), Some(Foo::B)); +/// assert_eq!(Foo::from_i64(-2), Some(Foo::C)); +/// assert_eq!(Foo::from_i64(-3), None); +/// ``` +pub trait FromPrimitive: Sized { + /// Attempts to convert a `bool` to `Self`. Returns `Some(Self)` if th= e input + /// corresponds to a known value; otherwise, `None`. + #[inline] + fn from_bool(b: bool) -> Option { + Self::from_u64(u64::from(b)) + } + + /// Attempts to convert an `isize` to `Self`. Returns `Some(Self)` if = the input + /// corresponds to a known value; otherwise, `None`. + #[inline] + fn from_isize(n: isize) -> Option { + i64::try_from(n).ok().and_then(Self::from_i64) + } + + /// Attempts to convert an `i8` to `Self`. Returns `Some(Self)` if the= input + /// corresponds to a known value; otherwise, `None`. + #[inline] + fn from_i8(n: i8) -> Option { + Self::from_i64(i64::from(n)) + } + + /// Attempts to convert an `i16` to `Self`. Returns `Some(Self)` if th= e input + /// corresponds to a known value; otherwise, `None`. + #[inline] + fn from_i16(n: i16) -> Option { + Self::from_i64(i64::from(n)) + } + + /// Attempts to convert an `i32` to `Self`. Returns `Some(Self)` if th= e input + /// corresponds to a known value; otherwise, `None`. + #[inline] + fn from_i32(n: i32) -> Option { + Self::from_i64(i64::from(n)) + } + + /// Attempts to convert an `i64` to `Self`. Returns `Some(Self)` if th= e input + /// corresponds to a known value; otherwise, `None`. + fn from_i64(n: i64) -> Option; + + /// Attempts to convert an `i128` to `Self`. Returns `Some(Self)` if t= he input + /// corresponds to a known value; otherwise, `None`. + /// + /// The default implementation delegates to [`from_i64`](FromPrimitive= ::from_i64) + /// by downcasting from `i128` to `i64`, which may result in informati= on loss. + /// Consider overriding this method if `Self` can represent values out= side the + /// `i64` range. + #[inline] + fn from_i128(n: i128) -> Option { + i64::try_from(n).ok().and_then(Self::from_i64) + } + + /// Attempts to convert a `usize` to `Self`. Returns `Some(Self)` if t= he input + /// corresponds to a known value; otherwise, `None`. + #[inline] + fn from_usize(n: usize) -> Option { + u64::try_from(n).ok().and_then(Self::from_u64) + } + + /// Attempts to convert a `u8` to `Self`. Returns `Some(Self)` if the = input + /// corresponds to a known value; otherwise, `None`. + #[inline] + fn from_u8(n: u8) -> Option { + Self::from_u64(u64::from(n)) + } + + /// Attempts to convert a `u16` to `Self`. Returns `Some(Self)` if the= input + /// corresponds to a known value; otherwise, `None`. + #[inline] + fn from_u16(n: u16) -> Option { + Self::from_u64(u64::from(n)) + } + + /// Attempts to convert a `u32` to `Self`. Returns `Some(Self)` if the= input + /// corresponds to a known value; otherwise, `None`. + #[inline] + fn from_u32(n: u32) -> Option { + Self::from_u64(u64::from(n)) + } + + /// Attempts to convert a `u64` to `Self`. Returns `Some(Self)` if the= input + /// corresponds to a known value; otherwise, `None`. + fn from_u64(n: u64) -> Option; + + /// Attempts to convert a `u128` to `Self`. Returns `Some(Self)` if th= e input + /// corresponds to a known value; otherwise, `None`. + /// + /// The default implementation delegates to [`from_u64`](FromPrimitive= ::from_u64) + /// by downcasting from `u128` to `u64`, which may result in informati= on loss. + /// Consider overriding this method if `Self` can represent values out= side the + /// `u64` range. + #[inline] + fn from_u128(n: u128) -> Option { + u64::try_from(n).ok().and_then(Self::from_u64) + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 6b4774b2b1c3..861c9340d9c2 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -61,6 +61,7 @@ pub mod clk; #[cfg(CONFIG_CONFIGFS_FS)] pub mod configfs; +pub mod convert; pub mod cpu; #[cfg(CONFIG_CPU_FREQ)] pub mod cpufreq; --=20 2.39.5 From nobody Wed Oct 8 23:04:36 2025 Received: from mail-pg1-f179.google.com (mail-pg1-f179.google.com [209.85.215.179]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0060728D8D5; Mon, 23 Jun 2025 15:14:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750691692; cv=none; b=W/1vpNq338Get9o+ZofuWyHAu6ot4g8/5aBl9u0bZXk2j5Uhl3lXMWGFnd7y3jH0Ysr6g29cDNwyLkNrTfi+bEHvyFjxbmlgp46+q4e1yaMVxk1TLSZVDSFpXtHvwag2OrdeLKW29KkPWJbFZ4XeaWllgcHxtwOI8WTzOefT29Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750691692; c=relaxed/simple; bh=9HLDxDHCQ3M24YIM9qCCru7ri37BgrydlZsGUsGK7W8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=KWkS8/uo4Yh076G2KoBM7AZ+zK2q7/y+x1wZahs2bfqbodNbj334EGKZMgBz4aPm/p2i+OS/M+36LvDOkJYnhP7cVAAjJSMVVmY4ISZ7mG9186NJtC0JYKY7yF0oS7TjTJ4XAPlGj95AnQvDVXHsA71B4falUTSeYLN+F3VHWg0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=e2XLvP08; arc=none smtp.client-ip=209.85.215.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="e2XLvP08" Received: by mail-pg1-f179.google.com with SMTP id 41be03b00d2f7-b3220c39cffso988241a12.0; Mon, 23 Jun 2025 08:14:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1750691690; x=1751296490; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=WxVvDAMvkM375HX/zxtZMIQJuoJ7i7/DvaZjzOiyFTo=; b=e2XLvP08j0nShTwdzm7Py4fwUGz6oZg4KmmxXCmpcH2uHDyG6aSHLEdS8yP7ztVKi+ anl/KOVZeNxlPD8VCu/IiFodxcKoNTnLWYlf6US1ZCRGoiUVRsW1fmTwTfzFo20jmRxI HBs7EmlMbnRdB3cwrh99YRODqDn6/1+/PxbNiH+IikRfezLqFQMh1SBwjf/OqGRC0DsV BUPcm98VKf+tqnRMHKNC28Ydwy01Pnh7z0eBnyUROp/hM3qdIL9q/VzYdoy0q3RKc0Hq 203p/9I0IjPV/5xg+HqG5b98USuIQbpKNXpeScQvZSmIdywSXOcZB7pR4FN4o0LfSqzi Mxtg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750691690; x=1751296490; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=WxVvDAMvkM375HX/zxtZMIQJuoJ7i7/DvaZjzOiyFTo=; b=LshM3cH4S1mzrDk4RFKW5c7FHfJ6BocU5m3y6FFMxf6iIAYh6SaxmEZAKkBsqTZc3j s9yHH9+yCGjOeTcuMWIkhlCUZuMUTULB5XKCtpBne7lTa3NYkRWw2bbxw2/akwuIxHgR 3rnuHHn08kw6OicsNMuaqVpbJIumQZ70qaJ60ZYaQAJS4URyfk2apVi1AysYSWaf9/7n nQ1yYCpl4+0PvPhozHsYsREAstPaCy+PnZKioMw385ptDk4avqf39B7FfudQu3cGYAH/ rN/orrbKaySgU7Ie+c1TbY5l7m6pFOn/hzTrCRko1gj0Wl+n5mrB80795hUnriFOedeV mq4Q== X-Forwarded-Encrypted: i=1; AJvYcCVOuCRhXxrMS+4Blqh84CX/Cth//umdMfuw/ObsnYJbHJ6l7Cpj8HT1nnmb1W/d20+fO3wznm6YnaSCEt12kw==@vger.kernel.org X-Gm-Message-State: AOJu0YywKtrjLW3VB730839fs47aGX25H2WrGNeFNRFVhY+2s9mIs+UI 76M4SMiksz/DPb/S+jt5XURF8pFqLwml9W3psB8N0fmeATZgvGWbOXbv X-Gm-Gg: ASbGncupD0SpF3tLt/+7JhNm+/TfwK8CFkZZE80Ck4A+T7goZt1ZnkKJtArMysR3v3y 9vCCoLjRnctc7iWzQI6XePQGpUuCuLkkiLsWyZpnrQE1bhd3ESujAH739eXirWdhnauSyJFGSy5 R3jOgqI3hzAyBfGklklRDaowOKY/zpB7ypy5tlT763C/WzuOuMSmR5aFGaqS7a+4YSPv5UbpeRI bfjjg5ixTrn9+TruIOAPHMEjEEwgLT8CdLme8MoflsVajbr6XC/EGaqDPKTgRAiVm1IoI1L+DKQ vilvTQCsmDuS1MeJpMvfvl8Sa7Z+hY/bsTE78qW5fEZfnb4kZfT9hPDGXgHbJe2z1VotXjmCmIZ AGnBFXOE= X-Google-Smtp-Source: AGHT+IHZ6b3VUpDC1c2zoZYui2q3WO6oIWeoRkRFN9suemWLWo9jP+W3JOYotkgd1zwMLlo2zWW6Rg== X-Received: by 2002:a17:90b:574d:b0:312:e731:5a66 with SMTP id 98e67ed59e1d1-3159d624244mr19128362a91.3.1750691689838; Mon, 23 Jun 2025 08:14:49 -0700 (PDT) Received: from localhost.localdomain ([112.149.32.52]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3159df71cd6sm8338512a91.4.2025.06.23.08.14.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jun 2025 08:14:49 -0700 (PDT) From: Jesung Yang To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org, Jesung Yang Subject: [PATCH 2/4] rust: macros: extend custom `quote!` macro Date: Mon, 23 Jun 2025 15:14:28 +0000 Message-Id: X-Mailer: git-send-email 2.39.5 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Implement the `ToTokens` trait for `&T` where `T` implements `ToTokens`. This allows users to use the `quote!` macro with references directly, avoiding the need to clone values. Implement the `ToTokens` trait for `proc_macro::Literal`. This enables the direct use of literals in the `quote!` macro, which is useful when emitting numeric constants. Extend the `quote_spanned!` macro to support additional punctuation tokens: `->`, `<`, `>`, and `=3D=3D`. This symbols are commonly needed when dealing with functions, generic bounds, and equality comparisons. Signed-off-by: Jesung Yang --- rust/macros/quote.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/rust/macros/quote.rs b/rust/macros/quote.rs index 92cacc4067c9..d05f60f55623 100644 --- a/rust/macros/quote.rs +++ b/rust/macros/quote.rs @@ -7,6 +7,12 @@ pub(crate) trait ToTokens { fn to_tokens(&self, tokens: &mut TokenStream); } =20 +impl ToTokens for &T { + fn to_tokens(&self, tokens: &mut TokenStream) { + (*self).to_tokens(tokens); + } +} + impl ToTokens for Option { fn to_tokens(&self, tokens: &mut TokenStream) { if let Some(v) =3D self { @@ -27,6 +33,12 @@ fn to_tokens(&self, tokens: &mut TokenStream) { } } =20 +impl ToTokens for proc_macro::Literal { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.extend([TokenTree::from(self.clone())]); + } +} + impl ToTokens for TokenTree { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.extend([self.clone()]); @@ -144,6 +156,36 @@ macro_rules! quote_spanned { )); quote_spanned!(@proc $v $span $($tt)*); }; + (@proc $v:ident $span:ident -> $($tt:tt)*) =3D> { + $v.push(::proc_macro::TokenTree::Punct( + ::proc_macro::Punct::new('-', ::proc_macro::Spacing::Joint) + )); + $v.push(::proc_macro::TokenTree::Punct( + ::proc_macro::Punct::new('>', ::proc_macro::Spacing::Alone) + )); + quote_spanned!(@proc $v $span $($tt)*); + }; + (@proc $v:ident $span:ident < $($tt:tt)*) =3D> { + $v.push(::proc_macro::TokenTree::Punct( + ::proc_macro::Punct::new('<', ::proc_macro::Spacing::Alone) + )); + quote_spanned!(@proc $v $span $($tt)*); + }; + (@proc $v:ident $span:ident > $($tt:tt)*) =3D> { + $v.push(::proc_macro::TokenTree::Punct( + ::proc_macro::Punct::new('>', ::proc_macro::Spacing::Alone) + )); + quote_spanned!(@proc $v $span $($tt)*); + }; + (@proc $v:ident $span:ident =3D=3D $($tt:tt)*) =3D> { + $v.push(::proc_macro::TokenTree::Punct( + ::proc_macro::Punct::new('=3D', ::proc_macro::Spacing::Joi= nt) + )); + $v.push(::proc_macro::TokenTree::Punct( + ::proc_macro::Punct::new('=3D', ::proc_macro::Spacing::Alo= ne) + )); + quote_spanned!(@proc $v $span $($tt)*); + }; (@proc $v:ident $span:ident =3D $($tt:tt)*) =3D> { $v.push(::proc_macro::TokenTree::Punct( ::proc_macro::Punct::new('=3D', ::proc_macro::Spacing::Alo= ne) --=20 2.39.5 From nobody Wed Oct 8 23:04:36 2025 Received: from mail-pj1-f54.google.com (mail-pj1-f54.google.com [209.85.216.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E29FC28DB54; Mon, 23 Jun 2025 15:14:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750691696; cv=none; b=rqH6eMdDBCvShphd3ILrGqbQgA2YWXZSUtq2jN2C2BLBfqiSNMGBNZM0bYdyBQQ5WPZHczkDc3YaSvxg7yUJm2ZAZFSoM22Or9jXMOjZF9Q8xYfFp/1ur2c9F3C+JEH3BaGO6LnhbVlU3TPi/3xamfJFiG9nYoQDjpcBfbYg+Og= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750691696; c=relaxed/simple; bh=xAmznbHV3hCSsoZUINUVnkaj02XcHozp+B0zTRtfSKQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=pHA7Xd0Rje048OW2JtLLAYuqbX3vjwW/r8vT4TZf0ugb5C0bLfrWNeBb1nI03RasQ5DlD38XNICrHlfGF3MhRHSNdM1IpadSrSWbDYotfYyyL8+nQHD7SILFx7mPu4q+pDudR8tdyK2Vgl7TH/zvz/VrFkCAtxOt8Ob+Zmt7JqA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GZyj55jS; arc=none smtp.client-ip=209.85.216.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GZyj55jS" Received: by mail-pj1-f54.google.com with SMTP id 98e67ed59e1d1-3134c67a173so4687626a91.1; Mon, 23 Jun 2025 08:14:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1750691694; x=1751296494; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=uqJIIUX1RcMuJD/oxLXivkAND/EflsQl1f/q6RQcXXU=; b=GZyj55jSCKmUo7j4Hnn9So0eW5z/C2/UwVYxYSVu3Co4urlmZai+WpR0phl/Ni3eA7 +P4axveiICiJNZQZWSMsj7Y54auozZrsWmjLaQFd3e6rd5oYatNP/PMrYj2jzFO9/NB3 cSeLWw66pLtIbhr4012CIcHu1GLbh/hSUOafBpUbnJLPTbu22d1DlDgLPn38rigqt9qi Gz+ErnYG8DlP6HjUp3msPDdtn96Q9drkt9Si5ImBQzYZy2fAFZWr3CA8WXthWnJepig8 p831YiECSeE2G1U5XXCMTt31Z8WP3KTi6pI9w0IGOUbNWNmEfyPaGfhPas0cysJHmOiw TyHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750691694; x=1751296494; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=uqJIIUX1RcMuJD/oxLXivkAND/EflsQl1f/q6RQcXXU=; b=r7ZPKjm1HSuCB26qFnFfNtrZipQnUPVSZkDwNNlsY2Il3t+LSvQqyEdIxzwEaMAocu URSwzNhTyXRiZgsx02rPq52iA8Y8t7piGCSicMMcOKIHNBDVtdksXiYcTD4XsT8/4nC/ uar+UqKYNJJPKhg9H6jk9U48raz1Rc+Eh5cgsv4msFkbi6rPNs7IrRMUwyeUroPdpYzJ MJv4VK1ga5xDK1DT2uIyLTsmdrBAc+ICSITkI+UFoFpH6M9Uhp8TU61lUIRmCGwhueLl XzzVBb+JCIi9sDHlVG/gzTyKFBQPHtWQJYNoqv6M5YYuPpFCKAyc1OypWqdFH0FAlzWh 6xAw== X-Forwarded-Encrypted: i=1; AJvYcCXAsxG7OFb8QigSV7X3pcgJKLG7FxL/OvpkaYGgp4eQ1/6N7tPVs5NKvRlNekfbl42Eurm4X7MGVHf0OY6iDw==@vger.kernel.org X-Gm-Message-State: AOJu0YxFFgQf/WzmT2wgLSkhqjhy9n7APP6B9PnHEJ9zTvvYX1LLc7Wd rWz3zRT9M2TwlUUHDGZ2hwNiZV/DNP72OvuCpOOw4r2vjXQMqCF95Zte X-Gm-Gg: ASbGncvfL5oPAtFMqEj1fg3pZ04u2nT1c5qkbLYKuTMZfOiju4TzHRIhkqKCrYJSpD8 omO3Wh1CpsdplYmbQKBbeNj3w6CVBQjJ/nm1fqJEkCSKa78+5S0EuLszokZrx11KGCn/O/YzsQd Obw2Wi07t0vud0lYBB9Kkcqsc4tjVG+/PzojC9CtxMlBMUgRP1eNdAPHQDsKQhqTFh/cYPYapCE LPZZbSK5qUpk8NSRszLxxohrV20Ux8VJFShxeMphXEpxjFV2iRz9CnKdsBqAqAwxUZ9YhNjocH8 lRXqNac9pdr0ONMqQ9z9zdYTDfXhwF4x8wBQ2NzQc8IU9CWU9kNggGRmjQLxrRHvAKW3NUIoBfx FLUut6CA= X-Google-Smtp-Source: AGHT+IGyPrHLryPORGsprOtDMijB7hbNnK12I6gc10XEvKh4YRH0MVHZxU7ZJO4C5upE0HZ9B5wtGg== X-Received: by 2002:a17:90b:1fcc:b0:311:b0ec:135b with SMTP id 98e67ed59e1d1-3159d8d6282mr22412103a91.24.1750691694156; Mon, 23 Jun 2025 08:14:54 -0700 (PDT) Received: from localhost.localdomain ([112.149.32.52]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3159df71cd6sm8338512a91.4.2025.06.23.08.14.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jun 2025 08:14:53 -0700 (PDT) From: Jesung Yang To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org, Jesung Yang Subject: [PATCH 3/4] rust: macros: prefix variable `span` with underscore Date: Mon, 23 Jun 2025 15:14:29 +0000 Message-Id: X-Mailer: git-send-email 2.39.5 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Prefix the variable `span` in `quote_spanned!` macro with an underscore to silence unused variable warnings. The warning occurs when the macro is used without any uninterpolated identifiers. For example: // Triggers a warning: "unused variable: `span`" quote! { #foo } // This is fine quote! { Some(#foo) } There is no good reason to disallow such quoting patterns, so fix the warning instead. Signed-off-by: Jesung Yang --- rust/macros/quote.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/macros/quote.rs b/rust/macros/quote.rs index d05f60f55623..e6c36bd7f925 100644 --- a/rust/macros/quote.rs +++ b/rust/macros/quote.rs @@ -63,8 +63,8 @@ macro_rules! quote_spanned { #[allow(clippy::vec_init_then_push)] { tokens =3D ::std::vec::Vec::new(); - let span =3D $span; - quote_spanned!(@proc tokens span $($tt)*); + let _span =3D $span; + quote_spanned!(@proc tokens _span $($tt)*); } ::proc_macro::TokenStream::from_iter(tokens) }}; --=20 2.39.5 From nobody Wed Oct 8 23:04:36 2025 Received: from mail-pj1-f50.google.com (mail-pj1-f50.google.com [209.85.216.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7015F28851A; Mon, 23 Jun 2025 15:14:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750691701; cv=none; b=F7PlgZhdEKxMk3YRgPMV+gLpbyKgyP6u/d5rqE8Y1aODhzaan+DMP+K3Ifhi2ssCgwAQtJ+Grqm8kytMZoUHoipYbMqr8DZoT0jUMw+zCiBktyVwZcKAgYuOSY6+EQ6fWdkhCjLwNV36EcIqos8O/t5/1m8wgUD67TIB0us4kv4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750691701; c=relaxed/simple; bh=z8eM/QAgo2uBd9U3jxMudtp4sBsBxeD57sJlFKS7yTM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=WxlAHvq4OQyAeSQHoLunCZBPeB0/Ln/TWKZQS8D1Olin7sN0Sx0ijMLBUwzgBWRxU4/vfdEJh40E+dRoEyzpeJVZaZIZwsJRwxGrBmlYPAh9FVHirj9+eyHy0bhSW2vzvNlsWNd9MvY6aW+PuBG9KdYzNveoYayrUR5k67yVZlE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=XbS5fwDH; arc=none smtp.client-ip=209.85.216.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="XbS5fwDH" Received: by mail-pj1-f50.google.com with SMTP id 98e67ed59e1d1-311da0bef4aso4535924a91.3; Mon, 23 Jun 2025 08:14:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1750691699; x=1751296499; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=I/UVNNeZTNbbS6EiV9y0jpLo5pQWewIFfPvGVeBsxd0=; b=XbS5fwDHrhTF8Ybx8P1pz8KND+eih26FBCeC4Stg3sanLdVUZYagruCCYCsKsJbJti uSHXHjBT9YzZzILjMPm8nIlhFFeqamrzKhdCgAyaXsY1HwCzkZ2wY3TaOXR/aI2FOqes NViu4mc5HWXfhuUkK/5ePNKdQbhPrTAEQJucVfMzfLdFrNkRlLJnvIcBR9GrID9GsOBV FS5YId0IxikyKHyyzA/RV+wX3zhNc3SqX1Bg9iLJ+Ffz0z8zh4g7NayQFvT1I8Qyx/gT zYd3op+3RH+ars+fa7XxRN1lFVcdw1cmP8u7PZnqhY4q2PKsK006qARbiwaXDK5QYZvY 633g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750691699; x=1751296499; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=I/UVNNeZTNbbS6EiV9y0jpLo5pQWewIFfPvGVeBsxd0=; b=cPRROrF5l9MlKX8wXrxImkeZc8kFDuUUPg1wR64uDz6wFJEiSDYHiGxlVWofrGujLQ cPJs84pY8sOzp3g5zNXH/W/Xl+Zcv/bAPowz6G55cTsmoDm9FPlxtJhCsZok0C4Og1pc jvnMwZ8BqkrqhKNjip2zWGaMKaUx8NYX3Lz+BX6ehczd+PmiDhV3wOIQe4WPO2fywPiS ulq4ph2TQEtpq9wRFJ4qFV/OTwpHKdmZbesx4ct21jrwTZ60eektj3hFpvcZ0sEhhjI/ UzszPrxKzZfvMmuMzcDYZSZRd9lx40QJ9B+T6zrSIr1ZhWAChOFcVIi0GordooO2yAmw 9ZGA== X-Forwarded-Encrypted: i=1; AJvYcCUOfjzic6kd4EWsRcvy8g6G7McVF4YPE1u5wPK/W/cP5MPTxTXLulZI0F/WLCTeacqlbtLGqqWBcdtKEn+6BQ==@vger.kernel.org X-Gm-Message-State: AOJu0Yx2BDnRKFoidofSwSp7TmvwK5gr39RzT4tDs0FtjCCVDMtupuyz 35GdQizroqIJhVbgeAZaP6r/CL+mfWFM/V9mEBbe+BHz4h4xBCOn7k8x X-Gm-Gg: ASbGncsWToeH3O1EZLm+a8nX2scFCsZNVZYyAgYOojxmMQ4Amq90/IVK2onxYcVGrSj h5dCo7aFBkM6VVnFexZnX+ZNDYkYNnOMgbREDwqD94wGFn1QN37C+t6AuRQ152w+EXJ0NPT0r1w aLnl4hlXaEezuDolBBvKuQ/dbTUsb0w9iGq5o26qRNFBtsAbjFYENUZjgnwJH7xBzfMxtvHoPhc yS4Qabj9VMoD4fqV40ywmkpALUSIJGIuH/LLsoooOZ3BAZZn45ZQPyeSUBORchYfsuYjU5nlxRu +Z9s7Q9wFjNNcCEmp+EvHRzrS0UJcaJqv24IRhNyqBnKAIcv4mZtmYGyR7HA/6UFy1j72BmWRIR HigTHikw= X-Google-Smtp-Source: AGHT+IHdZsLaH4YMTbKkOHbc75FpS8Ud5NNvNyYkbiSebSTVtXIoBSnmzbaE7hnjZltAv6BZVc2UFQ== X-Received: by 2002:a17:90b:1642:b0:313:5d2f:5505 with SMTP id 98e67ed59e1d1-3159d8c143bmr23501595a91.16.1750691698516; Mon, 23 Jun 2025 08:14:58 -0700 (PDT) Received: from localhost.localdomain ([112.149.32.52]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3159df71cd6sm8338512a91.4.2025.06.23.08.14.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jun 2025 08:14:58 -0700 (PDT) From: Jesung Yang To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org, Jesung Yang Subject: [PATCH 4/4] rust: macros: add derive macro for `FromPrimitive` Date: Mon, 23 Jun 2025 15:14:30 +0000 Message-Id: <2010f25bb9dbc5eb2e2e009dfc7c1939c6f2e9cb.1750689857.git.y.j3ms.n@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Introduce a procedural macro `FromPrimitive` to automatically implement the `FromPrimitive` trait for unit-only enums. Motivation =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D This reduces boilerplate in cases where mapping a numeric value to a corresponding enum variant is needed. 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. Design =3D=3D=3D=3D=3D=3D The macro currently rejects generics, zero-variant enums, tuple-like variants, andstruct-like variants at compile time, as the intended use case does not require support for those forms for now. Each method implementation by the macro avoids silent overflows by using per-variant constants and fallible `try_from` conversions, rather than `as` casts. This is important for enums with wide representations such as `#[repr(i128)]`, or enums with negative discriminants. For example, in the case below, `Foo::from_u8(255)` returns `None`, which is expected: \#[derive(Debug, FromPrimitive)] enum Foo { A =3D -1, } // The result of macro expansion (relevant part only). impl FromPrimitive for Foo { fn from_u8(n: u8) -> Option { const A: isize =3D -1; if Ok(n) =3D=3D u8::try_from(A) { ::core::option::Option::Some(Self::A) } else { ::core::option::Option::None } } } assert_eq!(Foo::from_u8(255), None); By contrast, in the following case, `Foo::from_u8(255)` returns `Some(Foo::A)` due to a silent overflow. impl FromPrimitive for Foo { fn from_u8(n: u8) -> Option { if n =3D=3D Self::A as u8 { ::core::option::Option::Some(Self::A) } else { ::core::option::Option::None } } } assert_eq!(Foo::from_u8(255), Some(Foo::A)); Parsing =3D=3D=3D=3D=3D=3D=3D Only minimal token inspection is used for now, since a pending RFC [1] proposes adding the `syn` crate [2] as a dependency. This macro keeps its parsing logic narrow until that discussion is settled. Link: https://lore.kernel.org/rust-for-linux/20250304225536.2033853-1-benno= .lossin@proton.me [1] Link: https://docs.rs/syn/latest/syn [2] Signed-off-by: Jesung Yang --- rust/macros/convert.rs | 217 +++++++++++++++++++++++++++++++++++++++++ rust/macros/lib.rs | 71 ++++++++++++++ 2 files changed, 288 insertions(+) create mode 100644 rust/macros/convert.rs diff --git a/rust/macros/convert.rs b/rust/macros/convert.rs new file mode 100644 index 000000000000..99550eb9f841 --- /dev/null +++ b/rust/macros/convert.rs @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0 + +use proc_macro::{token_stream, Delimiter, Ident, Literal, Span, TokenStrea= m, TokenTree}; +use std::iter::Peekable; + +pub(crate) fn derive(input: TokenStream) -> TokenStream { + let mut tokens =3D input.into_iter().peekable(); + + // Extract the representation passed by `#[repr(...)]` if present. + // If nothing is specified, the default is `Rust` representation, + // which uses `isize` for the discriminant type. + // See: https://doc.rust-lang.org/reference/items/enumerations.html#r-= items.enum.discriminant.repr-rust + let repr_ty_ident =3D + get_repr(&mut tokens).unwrap_or_else(|| Ident::new("isize", Span::= mixed_site())); + + // Skip until the `enum` keyword, including the `enum` itself. + for tt in tokens.by_ref() { + if matches!(tt, TokenTree::Ident(ident) if ident.to_string() =3D= =3D "enum") { + break; + } + } + + let Some(TokenTree::Ident(enum_ident)) =3D tokens.next() else { + return "::core::compile_error!(\"`#[derive(FromPrimitive)]` can on= ly \ + be applied to an enum\");" + .parse::() + .unwrap(); + }; + + let mut errs =3D TokenStream::new(); + + if matches!(tokens.peek(), Some(TokenTree::Punct(p)) if p.as_char() = =3D=3D '<') { + errs.extend( + "::core::compile_error!(\"`#[derive(FromPrimitive)]` \ + does not support enums with generic parameters\");" + .parse::() + .unwrap(), + ); + } + + let variants_group =3D tokens + .find_map(|tt| match tt { + TokenTree::Group(g) if g.delimiter() =3D=3D Delimiter::Brace = =3D> Some(g), + _ =3D> None, + }) + .expect("Missing main body of an enum"); + + let zero =3D Literal::usize_unsuffixed(0); + let one =3D Literal::usize_unsuffixed(1); + let mut const_defs =3D vec![]; + let mut variant_idents =3D vec![]; + let mut variant_tokens =3D variants_group.stream().into_iter().peekabl= e(); + + if variant_tokens.peek().is_none() { + return "::core::compile_error!(\"`#[derive(FromPrimitive)]` does n= ot \ + support zero-variant enums \");" + .parse::() + .unwrap(); + } + + while let Some(tt) =3D variant_tokens.next() { + // Skip attributes like `#[...]` if present. + if matches!(&tt, TokenTree::Punct(p) if p.as_char() =3D=3D '#') { + variant_tokens.next(); + continue; + } + + let TokenTree::Ident(ident) =3D tt else { + unreachable!("Missing enum variant identifier"); + }; + + // Reject tuple-like or struct-like variants. + if let Some(TokenTree::Group(g)) =3D variant_tokens.peek() { + let variant_kind =3D match g.delimiter() { + Delimiter::Brace =3D> "struct-like", + Delimiter::Parenthesis =3D> "tuple-like", + _ =3D> unreachable!("Invalid enum variant syntax"), + }; + errs.extend( + format!( + "::core::compile_error!(\"`#[derive(FromPrimitive)]` d= oes not \ + support {variant_kind} variant `{enum_ident}::{ident}`= ; \ + only unit variants are allowed\");" + ) + .parse::() + .unwrap(), + ); + } + + let const_expr: TokenStream =3D match variant_tokens.next() { + Some(TokenTree::Punct(p)) if p.as_char() =3D=3D '=3D' =3D> { + // Extract the explicit discriminant, which is a constant = expression. + // See: https://doc.rust-lang.org/reference/items/enumerat= ions.html#r-items.enum.discriminant.explicit.intro + variant_tokens + .by_ref() + .take_while(|tt| !matches!(&tt, TokenTree::Punct(p) if= p.as_char() =3D=3D ',')) + .collect() + } + _ =3D> { + // In this case, we have an implicit discriminant. + // Generate constant expression based on the previous iden= tifier. + match variant_idents.last() { + Some(prev) =3D> quote! { #prev + #one }, + None =3D> quote! { #zero }, + } + } + }; + + // These constants, named after each variant identifier, help dete= ct overflows. + const_defs.push(quote! { + #[allow(non_upper_case_globals)] + const #ident: #repr_ty_ident =3D #const_expr; + }); + + variant_idents.push(ident); + } + + if !errs.is_empty() { + return errs; + } + + // Implement `from_*` methods for these types; other types use default= implementations + // that delegate to `from_i64` or `from_u64`. While `isize`, `i128`, `= usize`, `u128` + // also have default implementations, providing explicit ones avoids r= elying on + // `u64::try_from`, which may silently fail (false negative) with `Non= e` if the enum + // is marked with a wide representation like `#[repr(i128)]`. + let type_names =3D ["isize", "i64", "i128", "usize", "u64", "u128"]; + let methods =3D type_names.into_iter().map(|ty| { + impl_method( + &Ident::new(ty, Span::mixed_site()), + &Ident::new(&format!("from_{ty}"), Span::mixed_site()), + &variant_idents, + &const_defs, + ) + }); + + quote! { + #[automatically_derived] + impl FromPrimitive for #enum_ident { + #(#methods)* + } + } +} + +fn get_repr(tokens: &mut Peekable) -> Option { + const PRIM_REPRS: [&str; 12] =3D [ + "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i= 64", "i128", "isize", + ]; + + // Scan only the attributes. As soon as we see a token that is + // not `#`, we know we have consumed all attributes. + while let TokenTree::Punct(p) =3D tokens.peek()? { + if p.as_char() !=3D '#' { + break; + } + tokens.next(); + + // The next token should be a `Group` delimited by brackets. + let TokenTree::Group(attr) =3D tokens.next()? else { + break; + }; + + let mut inner =3D attr.stream().into_iter(); + + // Skip attributes other than `repr`. + if !matches!(inner.next()?, TokenTree::Ident(ident) if ident.to_st= ring() =3D=3D "repr") { + continue; + } + + // Extract arguments passed to `repr`. + let TokenTree::Group(repr_args) =3D inner.next()? else { + break; + }; + + // Look for any specified primitive representation in `#[repr(...)= ]` args. + for arg in repr_args.stream() { + if let TokenTree::Ident(ident) =3D arg { + if PRIM_REPRS.contains(&ident.to_string().as_str()) { + return Some(ident); + } + } + } + } + + None +} + +fn impl_method( + ty: &Ident, + method: &Ident, + variants: &[Ident], + const_defs: &[TokenStream], +) -> TokenStream { + let param =3D Ident::new("n", Span::mixed_site()); + + // Discriminants can only be cast to integers using `as`, which may si= lently + // overflow. To avoid this, we use `try_from` on the defined constants= instead. + // A failed conversion indicates an overflow, which means the value do= esn't + // match the intended discriminant, so we fall through to the next cla= use. + let clauses =3D variants.iter().map(|ident| { + quote! { + if Ok(#param) =3D=3D #ty::try_from(#ident) { + ::core::option::Option::Some(Self::#ident) + } else + } + }); + + quote! { + #[inline] + fn #method(#param: #ty) -> ::core::option::Option { + #(#const_defs)* + #(#clauses)* { + ::core::option::Option::None + } + } + } +} diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index fa847cf3a9b5..fe7a261cc078 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 helpers; mod kunit; @@ -425,3 +426,73 @@ pub fn paste(input: TokenStream) -> TokenStream { pub fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { kunit::kunit_tests(attr, ts) } + +/// A derive macro for generating an impl of the trait [`FromPrimitive`]. +/// +/// This macro automatically derives [`FromPrimitive`] trait for a given e= num. Currently, +/// it only supports [unit-only enum]s without generic parameters. +/// +/// [unit-only enum]: https://doc.rust-lang.org/reference/items/enumeratio= ns.html#r-items.enum.unit-only +/// +/// # Notes +/// +/// When you manually implement [`FromPrimitive`], only `from_i64` and `fr= om_u64` are +/// required since the other `from_*` methods delegate to those two defaul= ts. However, +/// an enum may be marked with `#[repr(i128)]` or `#[repr(u128)]`, which m= eans discriminants +/// outside the 64-bit range would never be matched by the defaults. To av= oid this silent +/// failure, this macro also generates explicit `from_i128` and `from_u128= ` implementations. +/// As a side note, `from_isize` and `from_usize` are also explicitly impl= emented, since +/// converting from 64-bit integers to pointer-sized integers involves `Re= sult`. +/// +/// [`FromPrimitive`]: ../kernel/convert/trait.FromPrimitive.html +/// +/// # Examples +/// +/// You may give each variant an explicit discriminant; the macro uses tho= se values during +/// expansion. +/// +/// ```rust +/// use kernel::convert::FromPrimitive; +/// use kernel::macros::FromPrimitive; +/// +/// #[derive(Default, FromPrimitive)] +/// #[repr(u8)] +/// enum Foo { +/// #[default] +/// A, +/// B =3D 0x17, +/// } +/// +/// assert_eq!(Foo::from_u8(0), Some(Foo::A)); +/// assert_eq!(Foo::from_u8(0x17), Some(Foo::B)); +/// assert_eq!(Foo::from_u8(0x19), None); +/// ``` +/// +/// The following examples do not compile. +/// +/// ```compile_fail +/// # use kernel::convert::FromPrimitive; +/// # use kernel::macros::FromPrimitive; +/// // Generic parameters are not allowed. +/// #[derive(FromPrimitive)] +/// enum Foo { +/// A, +/// } +/// +/// // Tuple-like enums or struct-like enums are not allowed. +/// #[derive(FromPrimitive)] +/// enum Bar { +/// A(u8), +/// B { +/// inner: u8, +/// }, +/// } +/// +/// // Structs are not allowed. +/// #[derive(FromPrimitive)] +/// struct Baz(u8); +/// ``` +#[proc_macro_derive(FromPrimitive)] +pub fn derive_from_primitive(input: TokenStream) -> TokenStream { + convert::derive(input) +} --=20 2.39.5