From nobody Sun Oct 5 14:37:52 2025 Received: from mail-pf1-f175.google.com (mail-pf1-f175.google.com [209.85.210.175]) (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 60D91367; Sun, 3 Aug 2025 14:21:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1754230892; cv=none; b=f6mTp4hWw/PjE+x9z/mFOvvSV8xqKIaGcAN7AKC7wzul9wSjowTfQpW3E/LAcXp1S7FauiJtjMUFwQalLCYXJUYihF9HlRw6DrOPOmy1+HXoZ4WzyI45QRCYFJuPqAKwU92f04fmczAh6UUU89+LRkxKOMnwkHO0SxEZhF+Spuc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1754230892; c=relaxed/simple; bh=uOoEHbQIP4eH31so4WafM++NDXrOm/ao6tLqTeppibk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=tTE4kfpAO2beAp/4qYJiWi69fpIPgqP++Tj5DzupLCTLzMGoO/8ggdXMMJIE1Gj3YK5ovBB651PzQzIqCKKEKon6hFMiGP/Xmjb8H85fqqqAp/mSmZK56QFIG/igcW82bGs71o1SIRIBjpT2AfTILlyc5l6sqLM9o4GQlbopYUs= 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=GJI2MLLz; arc=none smtp.client-ip=209.85.210.175 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="GJI2MLLz" Received: by mail-pf1-f175.google.com with SMTP id d2e1a72fcca58-76bc68cc9e4so2934877b3a.2; Sun, 03 Aug 2025 07:21:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1754230890; x=1754835690; 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=114e6dFe36rXGszuWHRDJN4O+b6P/WGKkf7Q+pRcpDg=; b=GJI2MLLzgD0rblgcWCxoargqbnYpeX76nTuM22BH4vuhQ7txULN1vIOiU/2grH35f2 7pfqmGz4Zgu4tXdcZ4mFQKCUoBxTjP/fQDf8lwfK9Z7Lcm53VJXLx+X8LbhsPfJMRO4U 55W0zu8vszpMOzjz9OnFUczJnnlUEDjer0QHT2uRZkauSppc5kts3WdYonc6Gy13eLhu Rmtgje8lnimPqs7Tt4mDL8QU4/vGz2Opr8wpoaulUIiiH+w5V5Ntdzm/MaFvvdFMrGth PoRdp/EFLinDpOpUD1PqADao/fVYYdWaA2FYaeQKrbKdYjgaHflBRIen65O0rK200Qtc m9Lg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1754230890; x=1754835690; 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=114e6dFe36rXGszuWHRDJN4O+b6P/WGKkf7Q+pRcpDg=; b=Rs7LTanJFlTXeRDU/GHzhxDrwMCGopnYqrjmDVt6Lo6cajW6gYDngbLM9m6x57bQNa 2ZAe5kwzKa0w2xf89S29m1+DRq49Ek+7PwK3Q6Zm96lONV5TgFGJSE15lU+JNtC4OcgB uFgilCmkvA5gqOssyDcKWXaPoWjq1/olmCw9VWDFemh8uZyIYt3nmItMHI/BQAzFkZ/h /eOYr735GE/rrfouk5wjiDOBvjn3Upy/q4+cBlLbWowSJ1tafLVK3oo8eY+iOaWUEmml MgF6fy7VHRmyIFZIXDRp8mq0fjZV1Y5aWRRJnpqfsvxVKRnMPO1ASP/nwkJDGiNqWMtV a2CA== X-Forwarded-Encrypted: i=1; AJvYcCXIZzyn8r/yItOAavRtz0LNNeniahx/8m3M1fSo/A5Kz5w/AvspXMNTz92ajeQInA2vxYk5P/Yhb5fhaZp9ig==@vger.kernel.org X-Gm-Message-State: AOJu0Yzi4NRqz4GFhQieI/EMCdlgHRdS2QUwI3o+jdPf122enQk3e180 KpbMMP5Aojn0PbKGO5/rza8VdOVFYPxkOiKCqyva9E/8qrduhVWsIPOREc3DJweClFU= X-Gm-Gg: ASbGncuOk+hEm/q02Y8e8CtAdV1JGLPvHucfQCqPCzD/oR/nCbGAmyEYi9LzQwwIL42 5N+odpryPPteM4PJwJnMvZYcRe83ANvhK/uTRXKp5bKVS3xbqYONxlbAB+DlPriQ3HrqCSWTT/E H5V10YyM7nwKGWkQCCtmJ6Df5n86+wU56eWbAIDrOjL9QjAWmnOe+7zuLPqCvwSdEJY5W5Ootsj t3Mh4lb1fdhJ6+p7y+oGoUxvdP036oFnz4GxdhiH9PZe4FTC53GD7q50CvHpaS/aCfPsSOevqvK s6qweYmSqDJJ9MtQQ8GEsyR9EusA++75js7lUsWroW3g/ZKbv5C1yCrEHPQz0w1GRJnSK+0OhRD C6rBaIa2Naz1QWX9WqorJxc3++fXvTPPdZw== X-Google-Smtp-Source: AGHT+IHKX7auywt4u3zIKgU2fIOKGxq02XNCOJ7Xi4MWV+Fhm3m6UNnqEL+P/ENvgfCJ4sKMDzFRxg== X-Received: by 2002:a05:6a00:1884:b0:76b:49af:eceb with SMTP id d2e1a72fcca58-76bec4d1ef6mr6004641b3a.20.1754230890481; Sun, 03 Aug 2025 07:21:30 -0700 (PDT) Received: from localhost.localdomain ([104.28.217.213]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-76bccfd0279sm8431306b3a.98.2025.08.03.07.21.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 03 Aug 2025 07:21:30 -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 , Alexandre Courbot Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org, Jesung Yang Subject: [PATCH 1/4] rust: macros: extend custom `quote!()` macro Date: Sun, 3 Aug 2025 14:20:51 +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. 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 Tested-by: Alexandre Courbot --- rust/macros/quote.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/rust/macros/quote.rs b/rust/macros/quote.rs index 92cacc4067c9..24764b04a07d 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 { @@ -144,6 +150,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 Sun Oct 5 14:37:52 2025 Received: from mail-pf1-f169.google.com (mail-pf1-f169.google.com [209.85.210.169]) (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 26193367; Sun, 3 Aug 2025 14:21:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1754230898; cv=none; b=BK+MSRNigGFVl/Rg3reHlHKLnyS7YyP1DP0BO/Zaqq/Bn9J4LV835ufsma5tY3XOG8EjgtBrMo3QqMWuAsLVq3VD3M4zG6jpKCvvcmCZDQTZkL/2R6IDePmkMhw8xclObvH46z2QG2dyiDvMJMMc3OJsf3ZBwlJPeuq2L/NtXnw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1754230898; c=relaxed/simple; bh=w26Wr8Qt7YlWCv+uO1QqpfC4cUDgjOmEp19U/1ygDvA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Y38M/aTfrhVVFOX2jOCxbazLEg4+8ljmLGrDndEMeZhdbDz9rb8HHLSe0KmSkTxC+wx0dTtQgbaJeYUm8UNab2VSob8CSU6FNsidbiK1MXn1clla0DKFs2D+2uHqVAVYofjERf4MbvncyNXeRoh3wkd9hIdiPAwxVFTEppT2ZtI= 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=CeiqM/Wz; arc=none smtp.client-ip=209.85.210.169 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="CeiqM/Wz" Received: by mail-pf1-f169.google.com with SMTP id d2e1a72fcca58-76b8d289f73so2387497b3a.1; Sun, 03 Aug 2025 07:21:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1754230896; x=1754835696; 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=WqYDYr0tBuZpHy7d+0t7MzRWJjDlIyRDeWJHNv8j1g0=; b=CeiqM/WzGvQuvvJJNaaD5Hbxpnu9JYPDbxxxbd3eZte/G3VJ5iPeE0X6/LdlqO9s1M H2VPSuJz67+GKjJZ+QKtAaQjkwDRwykh0BfxJEZxQvtXZGgqN8Muhat/svEO72Esawgj RmNeNsZhWjdwHGPwLP8DnvtpfVl0LR6xM6Kip3Acr3vaI6LDSRT1YgBl7oHttopbugMV L5mv/tqEBl505Snw6Isfs/dGhDGNaHpEMNHGXa0Qv+5at2ocGpltTT+65Jq4JzBSYX9m mzB3Dck8b8tD8GXQ2KgQzJlnpSsXdhBC9QkCacMF5RPe7G/VAs+qhJMQ8k0g/Zg5ztrq YsUw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1754230896; x=1754835696; 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=WqYDYr0tBuZpHy7d+0t7MzRWJjDlIyRDeWJHNv8j1g0=; b=BrOdP+mm0qWqwq4fz4H3g1cC8CvusJTSfTHkzYp2S6RoinVb8gWh6K0EXLVuLeby/p K3djHGd1Nu9sR19zTYYsxYlI7rabkmPfozQCifbovQXDCAkO6bBrhEiPMHRbptJVkct5 Pie5t5kZJxpMr29yi3BwKQ4N9FC/IKMXQG+quOPAxt9NdUMwIiwnWZZtuZAfL3M6HaCs CtjXmOR3x1jMUSuO2//motKulMU5rTSod+TiTaWWFKeL+Y8D3iY/YVw1qsfAZkB2widj FWlnUtE5jNc1C16eqmRoHaChwuozq6W1mlCUBMvSf+1GlJeseinj0Vkdde4a/bYuHfXs IDrA== X-Forwarded-Encrypted: i=1; AJvYcCVeC0TkqP7eCiCl9ez3bH1qfx9WpoZRSntkwBWMvcjVBIxC4FpbFI+btX/nxx6oFGPdpg+9RVLri9tE/24E6g==@vger.kernel.org X-Gm-Message-State: AOJu0Yz1f/B5MSrzcToo3soaNx8kuPumIt0K8voC7mKiUQf+C5jOy8Q8 vCazJl5lqabcH9/3SA2XpwgWzp+lN7mx9gWazeuTr+9rbGYWLh3a0CaC X-Gm-Gg: ASbGncvFo8arDWtZl3vf7inlyWp+SO8um0KSF6hlwEBbwoWHyb5Gz66uppefWeHtgC+ jSFrFNVrEwIDS/712739QRY2M3Ao0zLENSSY3APQ1cc6mDOA2G09P9SFH+uS5em+Va5P7xwlu8s jN3b53dKzrrksJ8lAwSemFeQLD+fcl+yY1uzYTwfUNjuCC/zsQkhYSiAsK/oNSeQtrzxdD9blSF 4Yx7Y0cr5NK9iwtC6I4gXMZq3trgyCPBgxESXOrQamse4n5hGad2wRvnMGhY1qhp0A87kxzFUlt AIqkvwPWNkKhTIfv6u5hIPcDNcFPmp53SZIWGrS1QF/MYz3AxBd9q4c2aD9kD0wdjPag7KpEZfa 5ByPxZSa9ReG29Q9wluQcNLMGsmkIHUwaFg== X-Google-Smtp-Source: AGHT+IEhGtCjS9xv0mGVzidB/Y10oRyRYka+R3Bd+zYx80YYBNp2XSW4Qe16PVuCeuUmU165+yjKfQ== X-Received: by 2002:a05:6a00:3993:b0:74c:e253:6c83 with SMTP id d2e1a72fcca58-76bec314a45mr7544823b3a.6.1754230896381; Sun, 03 Aug 2025 07:21:36 -0700 (PDT) Received: from localhost.localdomain ([104.28.217.213]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-76bccfd0279sm8431306b3a.98.2025.08.03.07.21.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 03 Aug 2025 07:21:36 -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 , Alexandre Courbot Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org, Jesung Yang Subject: [PATCH 2/4] rust: macros: prefix variable `span` with underscore Date: Sun, 3 Aug 2025 14:20:52 +0000 Message-Id: <797d4e9577c8cdd219cee879a5cb2f6c25006b32.1754228164.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" 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 Tested-by: Alexandre Courbot --- 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 24764b04a07d..75367a93e0d3 100644 --- a/rust/macros/quote.rs +++ b/rust/macros/quote.rs @@ -57,8 +57,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 Sun Oct 5 14:37:52 2025 Received: from mail-pf1-f178.google.com (mail-pf1-f178.google.com [209.85.210.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 2ABE3156CA; Sun, 3 Aug 2025 14:21:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1754230905; cv=none; b=swkCQ3uKh0p1b2oe3mGL3zNq4dlsHZU8sTwVeT2bL0aNCxodG3Ao1CW063lfHaS7QBu9eFs1LlDKnGO9xLZlUHDOps5mPrWqUJ993J2xzR2wvRkKUGe5NQXdlvYg3DGksPxA4lK5H6DLWRR7jo059Cb9+HUbBlUad6tVxAVEM/w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1754230905; c=relaxed/simple; bh=UW4NgyqkgvDyq/INEfqhRNlZQ1MrMBA5Nk73EGCcPak=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=LUw4tnCdVqYpr+7a1ARAu/z7DQzheafZouwg07YWcyQouLGHOIEKiE6YYMw3poG+5eBdn61c9HupStn58+/i8KEhO7trRatO/srDf7nbc5d0dcMYKoT6pmK/+ax+4elcVsz7SPTJ7OfZ5He8OzVAl2+sG377ZxXX6LwPaSnDQfI= 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=HNLZm1fD; arc=none smtp.client-ip=209.85.210.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="HNLZm1fD" Received: by mail-pf1-f178.google.com with SMTP id d2e1a72fcca58-76bd202ef81so3862438b3a.3; Sun, 03 Aug 2025 07:21:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1754230902; x=1754835702; 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=fXGv88gAYzT87NJ3puOwVetFEtHU40d9kzRdaa9qaB8=; b=HNLZm1fDZl3WVElM9I5hPZizVvh2UUIkWJBk919ZfdQ2ORYB5l35Jd1G7wE5LPI9Q2 b4OPWe+ZyzZlGiUdNsSZU1Mol3lOd3xNpRkGZ+fh/74YqcEq3RZS6aEELN1HCK9jqJ5s Kn3oWUbJZGyqAmuV4Bhdfr99kSqPRCLbKjBkLzduEsS5FwHF+uvvJl7OjU5zj8DvJnaa bDXQOgPji3K3IzjYTN1HPOC5tytnAfv6+ITxt6rFDXji1/gfPXxbQbAEJWcQiMvo56BK HPMwVvCe0AcIHfxAwxKxNAS1LShe0Y2ZO2DmWxg9d/zjplWk1HIzYWH0P7iD4VbKpfp6 KQtQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1754230902; x=1754835702; 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=fXGv88gAYzT87NJ3puOwVetFEtHU40d9kzRdaa9qaB8=; b=BQTbjDsQFVgi4xtd54DbXSs//SGGEmY+5D8SNnDDyvUTqiBn2HbZYZ9SqQZHN8wWUg RxO64KTLUYKmsAG8KqnD8px6SGUMbEwX9lEoqgeIIsy8Auw+QIFI+tl4NJ8d+F1/2NL9 nCwLRdjqc/2lXd67skZMuoTvOehqq5SI9Ao1LQ5CVLyfsHMAk+f4p70IMvYVxdf9M6iv Ej94jaUyAAbF38Q3nG7zHk8+tDZWTYlCJNaOT1WnsC1jZAuFPhkUDlCsyjEa1Ex7Hl/k +/dqm5T89Z/UlT2ANhXOr1g+uIkFb5CAeO5L3RmB2PL3HdHZ6xMnX/Vyy/TGfYD6UPK5 gR2g== X-Forwarded-Encrypted: i=1; AJvYcCXYu0iYbFKlz73MjAn2QbBFtmsDHk/9yNE9zCE2QqRPiilOqBjgQ8IMSpO6oUHnVBnMVExGAFmkvT1c1HJalQ==@vger.kernel.org X-Gm-Message-State: AOJu0YyA8lRiOuZJRGK1Zk2KNFYKS2xlI7CyHiGNuRYGAAxIoCYZYfRt eXCr0gF3AsRSKeVyHP0VVUFe7eM7nHavNR98TQkbZVFctTwa33h6wxvl X-Gm-Gg: ASbGnctpYUgL66zS+SNo/5rCsqANTTxeW4/lIzkYyTYBKzf+UbFZz/nE++JzSfyYAvI zEsIclne0RQ/z7IX4wGRUHQ4prn3t0PQDk+os+n0B2bLTwPPgtFVlF70eBfkJzNQD7sm8tyO89c GWFaiY98k0V9w80r5K6Mwkv1FNGaTotg0Yl5NN95D5k5DysD8B87yK0U+qavImglXURFCv39bx8 BQkgiPOzrxMhCpcKeeJpdp0rcf7IywqO9YBFkUQObiV6DmG/TBqGvvEpKsBaXdiiorjyW1n50mc pez9EpcKSjoMtBwzlF47+tUauhQE0cDJscJxHSGg6dTtLHcMR7lHtcMcwp9XZVu/15R1TRvr00M /K/xatSoTUn7YnelS/2eHnRycv6eS7QrddFPW/XH8VK1A X-Google-Smtp-Source: AGHT+IFDgKyZSfBrsaDU/Nae3URYZoGmkvcLbrkczRPqQQTRhdX+/81NmbjdbpX/nJj0Wo3Lv346RQ== X-Received: by 2002:a05:6a00:1495:b0:749:ad1:ac8a with SMTP id d2e1a72fcca58-76bec48f3e7mr7804931b3a.11.1754230902411; Sun, 03 Aug 2025 07:21:42 -0700 (PDT) Received: from localhost.localdomain ([104.28.217.213]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-76bccfd0279sm8431306b3a.98.2025.08.03.07.21.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 03 Aug 2025 07:21:42 -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 , Alexandre Courbot Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org, Jesung Yang Subject: [PATCH 3/4] rust: macro: add derive macro for `TryFrom` Date: Sun, 3 Aug 2025 14:20:53 +0000 Message-Id: <58f312f85a30d1da0130b10735ddba89244241cb.1754228164.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 `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 arise 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. Since a pending RFC [1] proposes adding the `syn` crate [2] as a dependency, keep the parsing logic minimal. 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 Tested-by: Alexandre Courbot --- rust/macros/convert.rs | 337 +++++++++++++++++++++++++++++++++++++++++ rust/macros/lib.rs | 124 +++++++++++++++ 2 files changed, 461 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..0084bc4308c1 --- /dev/null +++ b/rust/macros/convert.rs @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0 + +use proc_macro::{token_stream, Delimiter, Ident, Span, TokenStream, TokenT= ree}; +use std::iter::Peekable; + +#[derive(Debug)] +struct TypeArgs { + helper: Vec, + repr: Option, +} + +const VALID_TYPES: [&str; 12] =3D [ + "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64",= "i128", "isize", +]; + +pub(crate) fn derive_try_from(input: TokenStream) -> TokenStream { + derive(input) +} + +fn derive(input: TokenStream) -> TokenStream { + let derive_target =3D "TryFrom"; + let derive_helper =3D "try_from"; + + let mut tokens =3D input.into_iter().peekable(); + + let type_args =3D match parse_types(&mut tokens, derive_helper) { + Ok(type_args) =3D> type_args, + Err(errs) =3D> return errs, + }; + + // 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 format!( + "::core::compile_error!(\"`#[derive({derive_target})]` can onl= y \ + 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( + format!( + "::core::compile_error!(\"`#[derive({derive_target})]` \ + does not support enums with generic parameters\");" + ) + .parse::() + .unwrap(), + ); + } + + let Some(variants_group) =3D tokens.find_map(|tt| match tt { + TokenTree::Group(g) if g.delimiter() =3D=3D Delimiter::Brace =3D> = Some(g), + _ =3D> None, + }) else { + unreachable!("Enums have its corresponding body") + }; + + let enum_body_tokens =3D variants_group.stream().into_iter().peekable(= ); + let variants =3D match parse_enum_variants(enum_body_tokens, &enum_ide= nt, derive_target) { + Ok(variants) =3D> variants, + Err(new_errs) =3D> { + errs.extend(new_errs); + return errs; + } + }; + + if !errs.is_empty() { + return errs; + } + + if type_args.helper.is_empty() { + // 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.htm= l#r-items.enum.discriminant.repr-rust + let ty =3D type_args + .repr + .unwrap_or_else(|| Ident::new("isize", Span::mixed_site())); + impl_try_from(&ty, &enum_ident, &variants) + } else { + let impls =3D type_args + .helper + .iter() + .map(|ty| impl_try_from(ty, &enum_ident, &variants)); + quote! { #(#impls)* } + } +} + +fn parse_types( + attrs: &mut Peekable, + helper: &str, +) -> Result { + let mut helper_args =3D vec![]; + let mut repr_arg =3D None; + + // Scan only the attributes. As soon as we see a token that is + // not `#`, we know we have consumed all attributes. + while let Some(TokenTree::Punct(p)) =3D attrs.peek() { + if p.as_char() !=3D '#' { + unreachable!("Outer attributes start with `#`"); + } + attrs.next(); + + // The next token should be a `Group` delimited by brackets. + // (e.g., #[try_from(u8, u16)]) + // ^^^^^^^^^^^^^^^^^^^ + let Some(TokenTree::Group(attr_group)) =3D attrs.next() else { + unreachable!("Outer attributes are surrounded by `[` and `]`"); + }; + + let mut inner =3D attr_group.stream().into_iter(); + + // Extract the attribute identifier. + // (e.g., #[try_from(u8, u16)]) + // ^^^^^^^^ + let attr_name =3D match inner.next() { + Some(TokenTree::Ident(ident)) =3D> ident.to_string(), + _ =3D> unreachable!("Attributes have identifiers"), + }; + + if attr_name =3D=3D helper { + match parse_helper_args(inner, helper) { + Ok(v) =3D> helper_args.extend_from_slice(&v), + Err(errs) =3D> return Err(errs), + } + } else if attr_name =3D=3D "repr" { + repr_arg =3D parse_repr_args(inner); + } + } + + Ok(TypeArgs { + helper: helper_args, + repr: repr_arg, + }) +} + +fn parse_repr_args(mut tt_group: impl Iterator) -> Opt= ion { + // The next token should be a `Group` delimited by parentheses. + // (e.g., #[repr(C, u8)]) + // ^^^^^^^ + let Some(TokenTree::Group(args_group)) =3D tt_group.next() else { + unreachable!("`repr` attribute has at least one argument") + }; + + for arg in args_group.stream() { + if let TokenTree::Ident(type_ident) =3D arg { + if VALID_TYPES.contains(&type_ident.to_string().as_str()) { + return Some(type_ident); + } + } + } + + None +} + +fn parse_helper_args( + mut tt_group: impl Iterator, + helper: &str, +) -> Result, TokenStream> { + let mut errs =3D TokenStream::new(); + let mut args =3D vec![]; + + // The next token should be a `Group`. + // (e.g., #[try_from(u8, u16)]) + // ^^^^^^^^^ + let Some(TokenTree::Group(args_group)) =3D tt_group.next() else { + return Err(format!( + "::core::compile_error!(\"`{helper}` attribute expects at \ + least one integer type argument (e.g., `#[{helper}(u8)]`)\");" + ) + .parse::() + .unwrap()); + }; + + let raw_args =3D args_group.stream(); + if raw_args.is_empty() { + return Err(format!( + "::core::compile_error!(\"`{helper}` attribute expects at \ + least one integer type argument (e.g., `#[{helper}(u8)]`)\");" + ) + .parse::() + .unwrap()); + } + + // Iterate over the attribute argument tokens to collect valid integer + // type identifiers. + let mut raw_args =3D raw_args.into_iter(); + while let Some(tt) =3D raw_args.next() { + let TokenTree::Ident(type_ident) =3D tt else { + errs.extend( + format!( + "::core::compile_error!(\"`{helper}` attribute expects= \ + comma-separated integer type arguments \ + (e.g., `#[{helper}(u8, u16)]`)\");" + ) + .parse::() + .unwrap(), + ); + return Err(errs); + }; + + if VALID_TYPES.contains(&type_ident.to_string().as_str()) { + args.push(type_ident); + } else { + errs.extend( + format!( + "::core::compile_error!(\"`{type_ident}` in `{helper}`= \ + attribute is not an integer type\");" + ) + .parse::() + .unwrap(), + ); + } + + match raw_args.next() { + Some(TokenTree::Punct(p)) if p.as_char() =3D=3D ',' =3D> conti= nue, + None =3D> break, + Some(_) =3D> { + errs.extend( + format!( + "::core::compile_error!(\"`{helper}` attribute exp= ects \ + comma-separated integer type arguments \ + (e.g., `#[{helper}(u8, u16)]`)\");" + ) + .parse::() + .unwrap(), + ); + return Err(errs); + } + } + } + + if !errs.is_empty() { + return Err(errs); + } + + Ok(args) +} + +fn parse_enum_variants( + mut tokens: Peekable, + enum_ident: &Ident, + derive_target: &str, +) -> Result, TokenStream> { + let mut errs =3D TokenStream::new(); + + let mut variants =3D vec![]; + + if tokens.peek().is_none() { + errs.extend( + format!( + "::core::compile_error!(\"`#[derive({derive_target})]` \ + does not support zero-variant enums\");" + ) + .parse::() + .unwrap(), + ); + } + + while let Some(tt) =3D tokens.next() { + // Skip attributes like `#[...]` if present. + if matches!(&tt, TokenTree::Punct(p) if p.as_char() =3D=3D '#') { + tokens.next(); + continue; + } + + let TokenTree::Ident(ident) =3D tt else { + unreachable!("Enum variants have its corresponding identifier"= ); + }; + + // Reject tuple-like or struct-like variants. + if let Some(TokenTree::Group(g)) =3D 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({derive_target})]`= does not \ + support {variant_kind} variant `{enum_ident}::{ident}`= ; \ + only unit variants are allowed\");" + ) + .parse::() + .unwrap(), + ); + } + + // Skip through the comma. + for tt in tokens.by_ref() { + if matches!(tt, TokenTree::Punct(p) if p.as_char() =3D=3D ',')= { + break; + } + } + + variants.push(ident); + } + + if !errs.is_empty() { + return Err(errs); + } + + Ok(variants) +} + +fn impl_try_from(ty: &Ident, enum_ident: &Ident, variants: &[Ident]) -> To= kenStream { + let param =3D Ident::new("value", Span::mixed_site()); + + let clauses =3D variants.iter().map(|variant| { + quote! { + if #param =3D=3D Self::#variant as #ty { + ::core::result::Result::Ok(Self::#variant) + } else + } + }); + + quote! { + #[automatically_derived] + impl ::core::convert::TryFrom<#ty> for #enum_ident { + type Error =3D ::kernel::prelude::Error; + fn try_from(#param: #ty) -> Result { + #(#clauses)* { + ::core::result::Result::Err(::kernel::prelude::EINVAL) + } + } + } + } +} diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index fa847cf3a9b5..569198f188f7 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,126 @@ 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 [`TryFrom`] trait. +/// +/// This macro automatically derives [`TryFrom`] trait for a given enum. C= urrently, +/// 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 +/// +/// The macro generates [`TryFrom`] implementations that: +/// - Convert numeric values to enum variants by matching discriminant val= ues. +/// - Return `Ok(VARIANT)` for valid matches. +/// - Return `Err(EINVAL)` for invalid matches (where `EINVAL` is from +/// [`kernel::error::code`]). +/// +/// The macro uses the `try_from` custom attribute or `repr` attribute to = generate +/// corresponding [`TryFrom`] implementations. `try_from` always takes pre= cedence +/// over `repr`. +/// +/// [`kernel::error::code`]: ../kernel/error/code/index.html +/// +/// # Caveats +/// +/// Ensure that every integer type specified in `#[try_from(...)]` is larg= e enough +/// to cover all enum discriminants. Otherwise, the internal `as` casts ma= y overflow. +/// +/// # Examples +/// +/// ## Without Attributes +/// +/// Since [the default `Rust` representation uses `isize` for the discrimi= nant type][repr-rs], +/// the macro implements `TryFrom`: +/// +/// [repr-rs]: https://doc.rust-lang.org/reference/items/enumerations.html= #r-items.enum.discriminant.repr-rust +/// +/// ```rust +/// use kernel::macros::TryFrom; +/// use kernel::prelude::*; +/// +/// #[derive(Debug, Default, PartialEq, TryFrom)] +/// enum Foo { +/// #[default] +/// A, +/// B =3D 0x17, +/// } +/// +/// assert_eq!(Foo::try_from(0isize), Ok(Foo::A)); +/// assert_eq!(Foo::try_from(0x17isize), Ok(Foo::B)); +/// assert_eq!(Foo::try_from(0x19isize), Err(EINVAL)); +/// ``` +/// +/// ## With `#[repr(T)]` +/// +/// The macro implements `TryFrom`: +/// +/// ```rust +/// use kernel::macros::TryFrom; +/// use kernel::prelude::*; +/// +/// #[derive(Debug, Default, PartialEq, TryFrom)] +/// #[repr(u8)] +/// enum Foo { +/// #[default] +/// A, +/// B =3D 0x17, +/// } +/// +/// assert_eq!(Foo::try_from(0u8), Ok(Foo::A)); +/// assert_eq!(Foo::try_from(0x17u8), Ok(Foo::B)); +/// assert_eq!(Foo::try_from(0x19u8), Err(EINVAL)); +/// ``` +/// +/// ## With `#[try_from(...)]` +/// +/// The macro implements `TryFrom` for each `T` specified in `#[try_fro= m(...)]`, +/// which always overrides `#[repr(...)]`: +/// +/// ```rust +/// use kernel::macros::TryFrom; +/// use kernel::prelude::*; +/// +/// #[derive(Debug, Default, PartialEq, TryFrom)] +/// #[try_from(u8, u16)] +/// #[repr(u8)] +/// enum Foo { +/// #[default] +/// A, +/// B =3D 0x17, +/// } +/// +/// assert_eq!(Foo::try_from(0u16), Ok(Foo::A)); +/// assert_eq!(Foo::try_from(0x17u16), Ok(Foo::B)); +/// assert_eq!(Foo::try_from(0x19u16), Err(EINVAL)); +/// ``` +/// +/// ## Unsupported Cases +/// +/// The following examples do not compile: +/// +/// ```compile_fail +/// # use kernel::macros::TryFrom; +/// // Generic parameters are not allowed. +/// #[derive(TryFrom)] +/// enum Foo { +/// A, +/// } +/// +/// // Tuple-like enums or struct-like enums are not allowed. +/// #[derive(TryFrom)] +/// enum Bar { +/// A(u8), +/// B { inner: u8 }, +/// } +/// +/// // Structs are not allowed. +/// #[derive(TryFrom)] +/// struct Baz(u8); +/// ``` +#[proc_macro_derive(TryFrom, attributes(try_from))] +pub fn derive_try_from(input: TokenStream) -> TokenStream { + convert::derive_try_from(input) +} --=20 2.39.5 From nobody Sun Oct 5 14:37:52 2025 Received: from mail-pf1-f182.google.com (mail-pf1-f182.google.com [209.85.210.182]) (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 2CB9326A0D5; Sun, 3 Aug 2025 14:21:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1754230910; cv=none; b=izrrpNUFFw0YNJbbgpA/kqUT+NmULlSiCgkE2kJPY8bRZlqWUYvikZSkvCmteY0XdCwX9W3rnCJsSt/K5Qeq8cih0bMiNMTH8kv3iPPI0tcqAeuIPG6IiLFTkFthsWT4k92ktFk0aYcC8rYrt4yHscMHy5aWK+Op8JiN9nklHAs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1754230910; c=relaxed/simple; bh=4EgI0v2m+Yvuw7v12aV6d/bqVWoPc0XuNyB+NUvdbwQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=FAS2nj+/2L2akMguku3GHy/LUV6q9oukt0SfizZ7PXJNCi5UxPRwejZEzQXBg2eqni95j2b9quddwOG5TlNkQ3ROs2nWHsvlW443+tbfJCUw0JU8P0iQqL2x2Ouzl0gldvymufPSKf0ls7L8ukTyOC6mgcXIeAVUuh2m5TJUMWQ= 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=Ky499cqD; arc=none smtp.client-ip=209.85.210.182 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="Ky499cqD" Received: by mail-pf1-f182.google.com with SMTP id d2e1a72fcca58-76b36e6b9ddso2899087b3a.1; Sun, 03 Aug 2025 07:21:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1754230908; x=1754835708; 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=APj1c+UB2oHtQRNfZRsuIg6Lw5yFU6MmLLNl9Pq1JRU=; b=Ky499cqDgPO0X11VhOow+d8toFPTLmhO+fNCxSdJYYS10/4zGKhP9lGqwx2qLFTXxN CnvebVaMAR369cmB3ulrP0q+phdPBj044qSf33p5SsinSdq8/eLKpDBe3JrSADcePT8i ldzz9vA5lZK2RH1vmEegQp7Lk/i+J+oX45DySAw6Tfp3dPCWUcpEffM7yHseL1oZX8f5 X1568y1KbNbkkN/YYSafOQaZ1x9xnxBLqg3IqSHdMpWc/4sLhWWiI3Uz2+rIc+A1N/bE ye/1AxNKw5Gt+olwh+4I6lKjXho8GWa7oMeukG2z2GOk9Bn4aVDd1nwS4JrghoRloVGy ImPQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1754230908; x=1754835708; 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=APj1c+UB2oHtQRNfZRsuIg6Lw5yFU6MmLLNl9Pq1JRU=; b=UlN7gpqz3WZl450HZY4rT9nAhl1I4RMIRu1Trzv1v3rtFG1aGAR+wMLSQw5TFbvUu7 ReAb4PwG1Bc/FeiL6P3H5JTCQqOXvK8TSCo1E09TW00VFSx59BZZC638RuB0heKJyMml Uot/NvlvXWWZv0bwyCRLLRxHrZC93Il417zW04U4+hRionFDHjEEu30M1uE50G5jsa4p 79THtN83JQ3W2Hwao8zzMO6iBRwOdi40K2JJJHBlp9/Wrst2hmXw42211F89sTUhkCEZ TzLZ9q8cpWOrDBWXGKPOQKTgw+qKNwypQ+HBflts49apmgp1vGGCE7r6xO/sdqjoULcP sPCw== X-Forwarded-Encrypted: i=1; AJvYcCUpU7kmlamPlnSVvQINbo7eblyY35z9tvJBloRMdTk1VCSW0keHi2fut2iJy2DA8m//mTs/pMTgmqf4eo3dyA==@vger.kernel.org X-Gm-Message-State: AOJu0YzOWITuQXH5OTRaJGaa3mqauLLwpyEOurNEWzHglx5AwrjrY4pq 25eg+VXR3KJ61e2J1GKT//edF3Fb0RbBq0JcjlSpE5WZfSobENFc+0NW X-Gm-Gg: ASbGncsiHiE2J+3M5QLOCtuPWCyFRlpFwAvTUwGe9aKBSUKQ+pHcaS0EaV8w+yGa9kH FYlts4sxPlQOXNY+p639iEsCGhZZTUNqkiVVwQiKp38PjvPl3jyr4Prfuy1HJAt7tRlBnKD2VuB ItaJcehJ8QPUzw5kteyRB46hOTWqoMeau07gnXi+YHVFrAwWmV9koJBBuhqYOPODeWSDSXMix/m qGgUseG6xAxQUAyktJXpUBlSGH2wfsy8cCO8DX/QUWhXFsUZNV1NVKO1KXfyJt0Ux3dz29oJ1eA fuMBdqaZ9XTd6Kg+0jBKxkpkkDyKkYuAkUUmd4N0UV8W+aOnAiEjGwC9x+OdZsR2MD+rU6nh0GG zNWnpZWgCk434jWZCCWOJI4Rxw5FLeOaelQ== X-Google-Smtp-Source: AGHT+IGyyGgtZEcS4DW/mmaf6MBx0aYT/R4DDHKjOPs9I2a1dvSC67wZ0LzCZPaKLpRqjYMvlWpcdQ== X-Received: by 2002:a05:6a20:3c8f:b0:220:9d82:a290 with SMTP id adf61e73a8af0-23df8f94a16mr8795243637.8.1754230908273; Sun, 03 Aug 2025 07:21:48 -0700 (PDT) Received: from localhost.localdomain ([104.28.217.213]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-76bccfd0279sm8431306b3a.98.2025.08.03.07.21.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 03 Aug 2025 07:21:48 -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 , Alexandre Courbot Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org, Jesung Yang Subject: [PATCH 4/4] rust: macro: add derive macro for `Into` Date: Sun, 3 Aug 2025 14:20:54 +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" 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]. 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 Tested-by: Alexandre Courbot --- rust/macros/convert.rs | 36 ++++++++++--- rust/macros/lib.rs | 115 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 6 deletions(-) diff --git a/rust/macros/convert.rs b/rust/macros/convert.rs index 0084bc4308c1..a6ef67ba27c7 100644 --- a/rust/macros/convert.rs +++ b/rust/macros/convert.rs @@ -3,6 +3,12 @@ use proc_macro::{token_stream, Delimiter, Ident, Span, TokenStream, TokenT= ree}; use std::iter::Peekable; =20 +#[derive(Debug)] +enum DeriveTarget { + TryFrom, + Into, +} + #[derive(Debug)] struct TypeArgs { helper: Vec, @@ -13,13 +19,20 @@ struct TypeArgs { "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64",= "i128", "isize", ]; =20 +pub(crate) fn derive_into(input: TokenStream) -> TokenStream { + derive(input, DeriveTarget::Into) +} + pub(crate) fn derive_try_from(input: TokenStream) -> TokenStream { - derive(input) + derive(input, DeriveTarget::TryFrom) } =20 -fn derive(input: TokenStream) -> TokenStream { - let derive_target =3D "TryFrom"; - let derive_helper =3D "try_from"; +fn derive(input: TokenStream, target: DeriveTarget) -> TokenStream { + type ImplFn =3D fn(&Ident, &Ident, &[Ident]) -> TokenStream; + let (derive_target, derive_helper, impl_trait) =3D match target { + DeriveTarget::TryFrom =3D> ("TryFrom", "try_from", impl_try_from a= s ImplFn), + DeriveTarget::Into =3D> ("Into", "into", impl_into as ImplFn), + }; =20 let mut tokens =3D input.into_iter().peekable(); =20 @@ -85,12 +98,12 @@ fn derive(input: TokenStream) -> TokenStream { let ty =3D type_args .repr .unwrap_or_else(|| Ident::new("isize", Span::mixed_site())); - impl_try_from(&ty, &enum_ident, &variants) + impl_trait(&ty, &enum_ident, &variants) } else { let impls =3D type_args .helper .iter() - .map(|ty| impl_try_from(ty, &enum_ident, &variants)); + .map(|ty| impl_trait(ty, &enum_ident, &variants)); quote! { #(#impls)* } } } @@ -335,3 +348,14 @@ fn try_from(#param: #ty) -> Result { } } } + +fn impl_into(ty: &Ident, enum_ident: &Ident, _: &[Ident]) -> TokenStream { + quote! { + #[automatically_derived] + impl ::core::convert::From<#enum_ident> for #ty { + fn from(value: #enum_ident) -> #ty { + value as #ty + } + } + } +} diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index 569198f188f7..374c1bdb696a 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -427,6 +427,121 @@ pub fn kunit_tests(attr: TokenStream, ts: TokenStream= ) -> TokenStream { kunit::kunit_tests(attr, ts) } =20 +/// A derive macro for providing an impl of the [`Into`] trait. +/// +/// This macro automatically derives [`Into`] trait for a given enum by ge= nerating +/// the relevant [`From`] implementation. Currently, it only supports [uni= t-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 +/// +/// Unlike its name suggests, the macro actually generates [`From`] implem= entations +/// which automatically provide corresponding [`Into`] implementations. +/// +/// The macro uses the `into` custom attribute or `repr` attribute to gene= rate [`From`] +/// implementations. `into` always takes precedence over `repr`. +/// +/// # Caveats +/// +/// Ensure that every integer type specified in `#[into(...)]` is large en= ough to cover +/// all enum discriminants. Otherwise, the internal `as` casts may overflo= w. +/// +/// # Examples +/// +/// ## Without Attributes +/// +/// Since [the default `Rust` representation uses `isize` for the discrimi= nant type][repr-rs], +/// the macro implements `From` for `isize`: +/// +/// [repr-rs]: https://doc.rust-lang.org/reference/items/enumerations.html= #r-items.enum.discriminant.repr-rust +/// +/// ```rust +/// use kernel::macros::Into; +/// use kernel::prelude::*; +/// +/// #[derive(Debug, Default, Into)] +/// enum Foo { +/// #[default] +/// A, +/// B =3D 0x17, +/// } +/// +/// assert_eq!(0isize, Foo::A.into()); +/// assert_eq!(0x17isize, Foo::B.into()); +/// ``` +/// +/// ## With `#[repr(T)]` +/// +/// The macro implements `From` for `T`: +/// +/// ```rust +/// use kernel::macros::Into; +/// use kernel::prelude::*; +/// +/// #[derive(Debug, Default, Into)] +/// #[repr(u8)] +/// enum Foo { +/// #[default] +/// A, +/// B =3D 0x17, +/// } +/// +/// assert_eq!(0u8, Foo::A.into()); +/// assert_eq!(0x17u8, Foo::B.into()); +/// ``` +/// +/// ## With `#[into(...)]` +/// +/// The macro implements `From` for each `T` specified in `#[into(...= )]`, +/// which always overrides `#[repr(...)]`: +/// +/// ```rust +/// use kernel::macros::Into; +/// use kernel::prelude::*; +/// +/// #[derive(Debug, Default, Into)] +/// #[into(u8, u16)] +/// #[repr(u8)] +/// enum Foo { +/// #[default] +/// A, +/// B =3D 0x17, +/// } +/// +/// assert_eq!(0u16, Foo::A.into()); +/// assert_eq!(0x17u16, Foo::B.into()); +/// ``` +/// +/// ## Unsupported Cases +/// +/// The following examples do not compile: +/// +/// ```compile_fail +/// # use kernel::macros::Into; +/// // Generic parameters are not allowed. +/// #[derive(Into)] +/// enum Foo { +/// A, +/// } +/// +/// // Tuple-like enums or struct-like enums are not allowed. +/// #[derive(Into)] +/// enum Bar { +/// A(u8), +/// B { inner: u8 }, +/// } +/// +/// // Structs are not allowed. +/// #[derive(Into)] +/// struct Baz(u8); +/// ``` +#[proc_macro_derive(Into, attributes(into))] +pub fn derive_into(input: TokenStream) -> TokenStream { + convert::derive_into(input) +} + /// A derive macro for generating an impl of the [`TryFrom`] trait. /// /// This macro automatically derives [`TryFrom`] trait for a given enum. C= urrently, --=20 2.39.5