Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2229210/?format=api
{ "id": 2229210, "url": "http://patchwork.ozlabs.org/api/1.1/patches/2229210/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-pci/patch/20260427221155.2144848-3-dakr@kernel.org/", "project": { "id": 28, "url": "http://patchwork.ozlabs.org/api/1.1/projects/28/?format=api", "name": "Linux PCI development", "link_name": "linux-pci", "list_id": "linux-pci.vger.kernel.org", "list_email": "linux-pci@vger.kernel.org", "web_url": null, "scm_url": null, "webscm_url": null }, "msgid": "<20260427221155.2144848-3-dakr@kernel.org>", "date": "2026-04-27T22:11:00", "name": "[02/24] rust: types: add `ForLt` trait for higher-ranked lifetime support", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "f527f42fd30f5b658ae050b5ecfc2f2657eaa9eb", "submitter": { "id": 89037, "url": "http://patchwork.ozlabs.org/api/1.1/people/89037/?format=api", "name": "Danilo Krummrich", "email": "dakr@kernel.org" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-pci/patch/20260427221155.2144848-3-dakr@kernel.org/mbox/", "series": [ { "id": 501733, "url": "http://patchwork.ozlabs.org/api/1.1/series/501733/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-pci/list/?series=501733", "date": "2026-04-27T22:10:58", "name": "rust: device: Higher-Ranked Lifetime Types for device drivers", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/501733/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2229210/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2229210/checks/", "tags": {}, "headers": { "Return-Path": "\n <linux-pci+bounces-53270-incoming=patchwork.ozlabs.org@vger.kernel.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-pci@vger.kernel.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256\n header.s=k20201202 header.b=eQAgRJnW;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=172.234.253.10; helo=sea.lore.kernel.org;\n envelope-from=linux-pci+bounces-53270-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)", "smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org\n header.b=\"eQAgRJnW\"", "smtp.subspace.kernel.org;\n arc=none smtp.client-ip=10.30.226.201" ], "Received": [ "from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g4Hrp4HVsz1xrS\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 28 Apr 2026 08:13:30 +1000 (AEST)", "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sea.lore.kernel.org (Postfix) with ESMTP id 7B9063097443\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 27 Apr 2026 22:12:22 +0000 (UTC)", "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 8CA883AB26A;\n\tMon, 27 Apr 2026 22:12:21 +0000 (UTC)", "from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org\n [10.30.226.201])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 0FAA43A7F4B;\n\tMon, 27 Apr 2026 22:12:20 +0000 (UTC)", "by smtp.kernel.org (Postfix) with ESMTPSA id ED1B3C2BCB7;\n\tMon, 27 Apr 2026 22:12:14 +0000 (UTC)" ], "ARC-Seal": "i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1777327941; cv=none;\n b=DpqVAgAC8p95Udu+L1X7+urblH1t167TVxRmWSiBFdwvsiww6opaUIsMHHSHOfJH+bgVpdGEuMHaz3UPMKD6NeaDxHWgQfCbtQHB4znbqYynPkx4Yeif6ApAEEhiQDxgIf9RkxhtAMMpaMlozw6ABJXozqFKrjJUWK1nJb7Dz+Y=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1777327941; c=relaxed/simple;\n\tbh=pZq55eZRbTUZKxw9zFnJXT0p6kpbJ6klMByaUzBGqrM=;\n\th=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version;\n b=OF1Z4bypCXA/TalF3GOwvobnvUoLs2G3XgW/na5MaRhlJa0WYLRiTsdk7Yc4yrC9AoadGpwcl2o2dSzISOX0adV76/x2oj1pMu+ikpswBlsmwCz5BpPhT4yYMQQ0cw8pxagt1duhiqjxHACMcnaXsSS6+UT+g5EMf2eU+3K1KW0=", "ARC-Authentication-Results": "i=1; smtp.subspace.kernel.org;\n dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org\n header.b=eQAgRJnW; arc=none smtp.client-ip=10.30.226.201", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org;\n\ts=k20201202; t=1777327940;\n\tbh=pZq55eZRbTUZKxw9zFnJXT0p6kpbJ6klMByaUzBGqrM=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=eQAgRJnWO09slwMNvi5KoH/ECuE3dX56H3NTQmoRISAJfYPKST2BKY3GQmasQK/+o\n\t tzRIVCG2DjKfdAZWGiNdZwQo5XxG05X4hQ/zsLS1bHhcUsIT5XR1x5ktYAQBxr1EcZ\n\t +Xkgmfv/aaByw/8SIwwTWRCrRyfuZFPuGq+HaU8/N3LVK4/vvT0SauQpeF4lJv11CY\n\t JUZdqTiCxRvxljRH4cDnmC6+40QCyzckAYdfYFGGavK1PK/341c4Ifjqqwhz1BbICq\n\t S1Zw+qc6td7rvxj+9So05Taxbb68hKXQYeigw0ZysZh2zdiEYtoMkNLFGgZOxngig/\n\t AMaqjN8lMppjw==", "From": "Danilo Krummrich <dakr@kernel.org>", "To": "gregkh@linuxfoundation.org,\n\trafael@kernel.org,\n\tacourbot@nvidia.com,\n\taliceryhl@google.com,\n\tdavid.m.ertman@intel.com,\n\tira.weiny@intel.com,\n\tleon@kernel.org,\n\tviresh.kumar@linaro.org,\n\tm.wilczynski@samsung.com,\n\tukleinek@kernel.org,\n\tbhelgaas@google.com,\n\tkwilczynski@kernel.org,\n\tabdiel.janulgue@gmail.com,\n\trobin.murphy@arm.com,\n\tmarkus.probst@posteo.de,\n\tojeda@kernel.org,\n\tboqun@kernel.org,\n\tgary@garyguo.net,\n\tbjorn3_gh@protonmail.com,\n\tlossin@kernel.org,\n\ta.hindborg@kernel.org,\n\ttmgross@umich.edu", "Cc": "driver-core@lists.linux.dev,\n\tlinux-kernel@vger.kernel.org,\n\tnova-gpu@lists.linux.dev,\n\tdri-devel@lists.freedesktop.org,\n\tlinux-pm@vger.kernel.org,\n\tlinux-pwm@vger.kernel.org,\n\tlinux-pci@vger.kernel.org,\n\trust-for-linux@vger.kernel.org", "Subject": "[PATCH 02/24] rust: types: add `ForLt` trait for higher-ranked\n lifetime support", "Date": "Tue, 28 Apr 2026 00:11:00 +0200", "Message-ID": "<20260427221155.2144848-3-dakr@kernel.org>", "X-Mailer": "git-send-email 2.54.0", "In-Reply-To": "<20260427221155.2144848-1-dakr@kernel.org>", "References": "<20260427221155.2144848-1-dakr@kernel.org>", "Precedence": "bulk", "X-Mailing-List": "linux-pci@vger.kernel.org", "List-Id": "<linux-pci.vger.kernel.org>", "List-Subscribe": "<mailto:linux-pci+subscribe@vger.kernel.org>", "List-Unsubscribe": "<mailto:linux-pci+unsubscribe@vger.kernel.org>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit" }, "content": "From: Gary Guo <gary@garyguo.net>\n\nThere are a few cases, e.g. when dealing with data referencing each other,\none might want to write code that are generic over lifetimes. For example,\nif you want take a function that takes `&'a Foo` and gives `Bar<'a>`, you\ncan write:\n\n f: impl for<'a> FnOnce(&'a Foo) -> Bar<'a>,\n\nHowever, it becomes tricky when you want that function to not have a fixed\n`Bar`, but have it be generic again. In this case, one needs something that\nis generic over types that are themselves generic over lifetimes.\n\n`ForLt` provides such support. It provides a trait `ForLt` which describes\na type generic over lifetime. One may use `ForLt::Of<'a>` to get an\ninstance of a type for a specific lifetime.\n\nFor the case of cross referencing, one would almost always want the\nlifetime to be covariant. Therefore this is also made a requirement for the\n`ForLt` trait, so functions with `ForLt` trait bound can assume covariance.\n\nA macro `ForLt!()` is provided to be able to obtain a type that implements\n`ForLt`. For example, `ForLt!(for<'a> Bar<'a>)` would yield a type that\n`<TheType as ForLt>::Of<'a>` is `Bar<'a>`. This also works with lifetime\nelision, e.g. `ForLt!(Bar<'_>)` or for types without lifetime at all, e.g.\n`ForLt!(u32)`.\n\nThe API design draws inspiration from the higher-kinded-types [1] crate,\nhowever different design decision has been taken (e.g. covariance\nrequirement) and the implementation is independent.\n\nLicense headers use \"Apache-2.0 OR MIT\" because I anticipate this to be\nused in pin-init crate too which is licensed as such.\n\nLink: https://docs.rs/higher-kinded-types/ [1]\n\nSigned-off-by: Gary Guo <gary@garyguo.net>\n---\n rust/Makefile | 1 +\n rust/kernel/types.rs | 4 +\n rust/kernel/types/for_lt.rs | 117 +++++++++++++++++\n rust/macros/for_lt.rs | 242 ++++++++++++++++++++++++++++++++++++\n rust/macros/lib.rs | 12 ++\n 5 files changed, 376 insertions(+)\n create mode 100644 rust/kernel/types/for_lt.rs\n create mode 100644 rust/macros/for_lt.rs", "diff": "diff --git a/rust/Makefile b/rust/Makefile\nindex b361bfedfdf0..c5a9a3339416 100644\n--- a/rust/Makefile\n+++ b/rust/Makefile\n@@ -110,6 +110,7 @@ syn-cfgs := \\\n feature=\"parsing\" \\\n feature=\"printing\" \\\n feature=\"proc-macro\" \\\n+ feature=\"visit\" \\\n feature=\"visit-mut\"\n \n syn-flags := \\\ndiff --git a/rust/kernel/types.rs b/rust/kernel/types.rs\nindex 4329d3c2c2e5..3119401dcb9f 100644\n--- a/rust/kernel/types.rs\n+++ b/rust/kernel/types.rs\n@@ -11,6 +11,10 @@\n };\n use pin_init::{PinInit, Wrapper, Zeroable};\n \n+#[doc(hidden)]\n+pub mod for_lt;\n+pub use for_lt::ForLt;\n+\n /// Used to transfer ownership to and from foreign (non-Rust) languages.\n ///\n /// Ownership is transferred from Rust to a foreign language by calling [`Self::into_foreign`] and\ndiff --git a/rust/kernel/types/for_lt.rs b/rust/kernel/types/for_lt.rs\nnew file mode 100644\nindex 000000000000..4983cc761f80\n--- /dev/null\n+++ b/rust/kernel/types/for_lt.rs\n@@ -0,0 +1,117 @@\n+// SPDX-License-Identifier: Apache-2.0 OR MIT\n+\n+//! Provide implementation and test of the `ForLt` trait and macro.\n+//!\n+//! This module is hidden and user should just use `ForLt!` directly.\n+\n+use core::marker::PhantomData;\n+\n+/// Representation of types generic over a lifetime.\n+///\n+/// The type must be covariant over the generic lifetime, i.e. the lifetime parameter\n+/// can be soundly shorterned.\n+///\n+/// The lifetime involved must be covariant.\n+///\n+/// # Macro\n+///\n+/// It is not recommended to implement this trait directly. `ForLt!` macro is provided to obtain a\n+/// type that implements this trait.\n+///\n+/// The full syntax is\n+/// ```\n+/// # use kernel::types::ForLt;\n+/// # fn expect_lt<F: ForLt>() {}\n+/// # struct TypeThatUse<'a>(&'a ());\n+/// # expect_lt::<\n+/// ForLt!(for<'a> TypeThatUse<'a>)\n+/// # >();\n+/// ```\n+/// which gives a type so that `<ForLt!(for<'a> TypeThatUse<'a>) as ForLt>::Of<'b>`\n+/// is `TypeThatUse<'b>`.\n+///\n+/// You may also use a short-hand syntax which works similar to lifetime elision.\n+/// The macro also accepts types that does not involved lifetime at all.\n+/// ```\n+/// # use kernel::types::ForLt;\n+/// # fn expect_lt<F: ForLt>() {}\n+/// # struct TypeThatUse<'a>(&'a ());\n+/// # expect_lt::<\n+/// ForLt!(TypeThatUse<'_>) // Equivalent to `ForLt!(for<'a> TypeThatUse<'a>)`\n+/// # >();\n+/// # expect_lt::<\n+/// ForLt!(&u32) // Equivalent to `ForLt!(for<'a> &'a u32)`\n+/// # >();\n+/// # expect_lt::<\n+/// ForLt!(u32) // Equivalent to `ForLt!(for<'a> u32)`\n+/// # >();\n+/// ```\n+///\n+/// The macro will attempt to prove that the type is indeed covariant over the lifetime supplied.\n+/// When it cannot be syntactically proven, it will emit checks to ask the Rust compiler to prove\n+/// it.\n+/// ```ignore,compile_fail\n+/// # use kernel::types::ForLt;\n+/// # fn expect_lt<F: ForLt>() {}\n+/// # expect_lt::<\n+/// ForLt!(fn(&u32)) // Contravariant, will fail compilation.\n+/// # >();\n+/// ```\n+///\n+/// There is a limitation if the type refer to generic parameters; if the macro cannot prove the\n+/// covariance syntactically, the emitted checks will fail the compilation as it needs to refer to\n+/// the generic parameter but is in a separate item.\n+/// ```\n+/// # use kernel::types::ForLt;\n+/// fn expect_lt<F: ForLt>() {}\n+/// # #[allow(clippy::unnecessary_safety_comment, reason = \"false positive\")]\n+/// fn generic_fn<T: 'static>() {\n+/// // Syntactically proven by the macro\n+/// expect_lt::<ForLt!(&T)>();\n+/// // Syntactically proven by the macro\n+/// expect_lt::<ForLt!(&KBox<T>)>();\n+/// // Cannot be syntactically proven, need to check covariance of `KBox`\n+/// // expect_lt::<ForLt!(&KBox<&T>)>();\n+/// }\n+/// ```\n+///\n+/// # Safety\n+///\n+/// `Self::Of<'a>` must be covariant over the lifetime `'a`.\n+pub unsafe trait ForLt {\n+ /// The type parameterized by the lifetime.\n+ type Of<'a>;\n+\n+ /// Cast a reference to a shorter lifetime.\n+ #[inline(always)]\n+ fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Self::Of<'short> {\n+ // SAFETY: This is sound as this trait guarantees covariance.\n+ unsafe { core::mem::transmute(long) }\n+ }\n+}\n+pub use macros::ForLt;\n+\n+/// This is intended to be an \"unsafe-to-refer-to\" type.\n+///\n+/// Must only be used by the `ForLt!` macro.\n+///\n+/// `T` is the magic `dyn for<'a> WithLt<'a, TypeThatUse<'a>>` generated by macro.\n+///\n+/// `WF` is a type that the macro can use to assert some specific type is well-formed.\n+///\n+/// `N` is to provide the macro a place to emit arbitrary items, in case it needs to prove\n+/// additional properties.\n+#[doc(hidden)]\n+pub struct UnsafeForLtImpl<T: ?Sized, WF, const N: usize>(PhantomData<(WF, T)>);\n+\n+// This is a helper trait for implementation `ForLt` to be able to use HRTB.\n+#[doc(hidden)]\n+pub trait WithLt<'a> {\n+ type Of;\n+}\n+\n+// SAFETY: In `ForLt!` macro, a covariance proof is generated when naming `UnsafeForLtImpl`\n+// and it will fail to evaluate if the type is not covariant.\n+unsafe impl<T: ?Sized + for<'a> WithLt<'a>, WF> ForLt for UnsafeForLtImpl<T, WF, 0> {\n+ type Of<'a> = <T as WithLt<'a>>::Of;\n+}\ndiff --git a/rust/macros/for_lt.rs b/rust/macros/for_lt.rs\nnew file mode 100644\nindex 000000000000..df2027789713\n--- /dev/null\n+++ b/rust/macros/for_lt.rs\n@@ -0,0 +1,242 @@\n+// SPDX-License-Identifier: Apache-2.0 OR MIT\n+\n+use proc_macro2::{\n+ Span,\n+ TokenStream, //\n+};\n+use quote::{\n+ format_ident,\n+ quote, //\n+};\n+use syn::{\n+ parse::{\n+ Parse,\n+ ParseStream, //\n+ },\n+ visit::Visit,\n+ visit_mut::VisitMut,\n+ Lifetime,\n+ Result,\n+ Token,\n+ Type, //\n+};\n+\n+pub(crate) enum HigherRankedType {\n+ Explicit {\n+ _for_token: Token![for],\n+ _lt_token: Token![<],\n+ lifetime: Lifetime,\n+ _gt_token: Token![>],\n+ ty: Type,\n+ },\n+ Implicit {\n+ ty: Type,\n+ },\n+}\n+\n+impl Parse for HigherRankedType {\n+ fn parse(input: ParseStream<'_>) -> Result<Self> {\n+ if input.peek(Token![for]) {\n+ Ok(Self::Explicit {\n+ _for_token: input.parse()?,\n+ _lt_token: input.parse()?,\n+ lifetime: input.parse()?,\n+ _gt_token: input.parse()?,\n+ ty: input.parse()?,\n+ })\n+ } else {\n+ Ok(Self::Implicit { ty: input.parse()? })\n+ }\n+ }\n+}\n+\n+trait TypeExt {\n+ fn expand_elided_lifetime(&self, explicit_lt: &Lifetime) -> Type;\n+ fn replace_lifetime(&self, src: &Lifetime, dst: &Lifetime) -> Type;\n+ fn has_lifetime(&self, lt: &Lifetime) -> bool;\n+}\n+\n+impl TypeExt for Type {\n+ fn expand_elided_lifetime(&self, explicit_lt: &Lifetime) -> Type {\n+ struct ElidedLifetimeExpander<'a>(&'a Lifetime);\n+\n+ impl VisitMut for ElidedLifetimeExpander<'_> {\n+ fn visit_lifetime_mut(&mut self, lifetime: &mut Lifetime) {\n+ // Expand explicit `'_`\n+ if lifetime.ident == \"_\" {\n+ *lifetime = self.0.clone();\n+ }\n+ }\n+\n+ fn visit_type_reference_mut(&mut self, reference: &mut syn::TypeReference) {\n+ syn::visit_mut::visit_type_reference_mut(self, reference);\n+\n+ if reference.lifetime.is_none() {\n+ reference.lifetime = Some(self.0.clone());\n+ }\n+ }\n+ }\n+\n+ let mut ret = self.clone();\n+ ElidedLifetimeExpander(explicit_lt).visit_type_mut(&mut ret);\n+ ret\n+ }\n+\n+ fn replace_lifetime(&self, src: &Lifetime, dst: &Lifetime) -> Type {\n+ struct LifetimeReplacer<'a>(&'a Lifetime, &'a Lifetime);\n+\n+ impl VisitMut for LifetimeReplacer<'_> {\n+ fn visit_lifetime_mut(&mut self, lifetime: &mut Lifetime) {\n+ if lifetime.ident == self.0.ident {\n+ *lifetime = self.1.clone();\n+ }\n+ }\n+ }\n+\n+ let mut ret = self.clone();\n+ LifetimeReplacer(src, dst).visit_type_mut(&mut ret);\n+ ret\n+ }\n+\n+ fn has_lifetime(&self, lt: &Lifetime) -> bool {\n+ struct HasLifetime<'a>(&'a Lifetime, bool);\n+\n+ impl Visit<'_> for HasLifetime<'_> {\n+ fn visit_lifetime(&mut self, lifetime: &Lifetime) {\n+ if lifetime.ident == self.0.ident {\n+ self.1 = true;\n+ }\n+ }\n+ }\n+\n+ let mut visitor = HasLifetime(lt, false);\n+ visitor.visit_type(self);\n+ visitor.1\n+ }\n+}\n+\n+struct Prover<'a>(&'a Lifetime, Vec<&'a Type>);\n+\n+impl<'a> Prover<'a> {\n+ /// Prove that `ty` is covariant over `'lt`.\n+ ///\n+ /// This also needs to prove that it'll be wellformed for any instance of `'lt`.\n+ /// It can be assumed that `ty` will be wellformed if `'lt` is substituted to `'static`.\n+ fn prove(&mut self, ty: &'a Type) {\n+ match ty {\n+ Type::Paren(ty) => self.prove(&ty.elem),\n+ Type::Group(ty) => self.prove(&ty.elem),\n+\n+ // No lifetime involved\n+ Type::Never(_) => {}\n+\n+ // `[T; N]` and `[T]` is covariant over `T`.\n+ Type::Array(ty) => self.prove(&ty.elem),\n+ Type::Slice(ty) => self.prove(&ty.elem),\n+\n+ Type::Tuple(ty) => {\n+ for elem in &ty.elems {\n+ self.prove(elem);\n+ }\n+ }\n+\n+ // `*const T` is covariant over `T`\n+ Type::Ptr(ty) if ty.const_token.is_some() => self.prove(&ty.elem),\n+\n+ // `&T` is covariant over `T` and lifetime.\n+ //\n+ // Note that if we encounter `&'other_lt T`, then we still need to make sure the type\n+ // is wellformed if `T` involves `&'lt`, so we defer to the compiler.\n+ //\n+ // This is to block cases like `ForLt!(for<'a> &'static &'a u32)`, as the presence of\n+ // the type implies `'a: 'static` but this is unsound.\n+ Type::Reference(ty)\n+ if ty.mutability.is_none() && ty.lifetime.as_ref() == Some(self.0) =>\n+ {\n+ self.prove(&ty.elem)\n+ }\n+\n+ // `&[mut] T` is covariant over lifetime.\n+ // In case we have `&[mut] NoLifetime`, we don't need to do additional checks.\n+ Type::Reference(ty) if !ty.elem.has_lifetime(self.0) => (),\n+\n+ // No mention of lifetime at all, no need to perform compiler check.\n+ ty if !ty.has_lifetime(self.0) => (),\n+\n+ // Otherwise, we need to emit checks so that compiler can determine if the types are\n+ // actually covariant.\n+ ty => self.1.push(ty),\n+ }\n+ }\n+}\n+\n+pub(crate) fn for_lt(input: HigherRankedType) -> TokenStream {\n+ let (ty, lifetime) = match input {\n+ HigherRankedType::Explicit { lifetime, ty, .. } => (ty, lifetime),\n+ HigherRankedType::Implicit { ty } => {\n+ // If there's no explicit `for<'a>` binder, inject a synthetic `'__elided` lifetime\n+ // and expand elided sites.\n+ let lifetime = Lifetime {\n+ apostrophe: Span::mixed_site(),\n+ ident: format_ident!(\"__elided\", span = Span::mixed_site()),\n+ };\n+ (ty.expand_elided_lifetime(&lifetime), lifetime)\n+ }\n+ };\n+\n+ let mut prover = Prover(&lifetime, Vec::new());\n+ prover.prove(&ty);\n+\n+ let mut proof = Vec::new();\n+\n+ // Emit proofs for every type that requires additional compiler help in proving covariance.\n+ for (idx, required_proof) in prover.1.into_iter().enumerate() {\n+ // Insert a proof that the type is well-formed.\n+ //\n+ // This is intended to workaround a Rust compiler soundness bug related to HRTB.\n+ // https://github.com/rust-lang/rust/issues/152489\n+ //\n+ // This needs to be a struct instead of fn to avoid the implied WF bounds.\n+ let wf_proof_name = format_ident!(\"ProveWf{idx}\");\n+ proof.push(quote!(\n+ struct #wf_proof_name<#lifetime>(\n+ ::core::marker::PhantomData<&#lifetime ()>, #required_proof\n+ );\n+ ));\n+\n+ // Insert a proof that the type is covariant.\n+ let cov_proof_name = format_ident!(\"prove_covariant_{idx}\");\n+ proof.push(quote!(\n+ fn #cov_proof_name<'__short, '__long: '__short>(\n+ long: #wf_proof_name<'__long>\n+ ) -> #wf_proof_name<'__short> {\n+ long\n+ }\n+ ));\n+ }\n+\n+ // Make sure that the type is wellformed when substituting lifetime with `'static`.\n+ //\n+ // Currently the Rust compiler doesn't check this, see the above ProveWf documentation.\n+ //\n+ // We prefer to use this way of proving WF-ness as it can work when generics are involved.\n+ let ty_static = ty.replace_lifetime(\n+ &lifetime,\n+ &Lifetime {\n+ apostrophe: Span::mixed_site(),\n+ ident: format_ident!(\"static\"),\n+ },\n+ );\n+\n+ quote!(\n+ ::kernel::types::for_lt::UnsafeForLtImpl::<\n+ dyn for<#lifetime> ::kernel::types::for_lt::WithLt<#lifetime, Of = #ty>,\n+ #ty_static,\n+ {\n+ #(#proof)*\n+\n+ 0\n+ }\n+ >\n+ )\n+}\ndiff --git a/rust/macros/lib.rs b/rust/macros/lib.rs\nindex 2cfd59e0f9e7..e5f6f8318112 100644\n--- a/rust/macros/lib.rs\n+++ b/rust/macros/lib.rs\n@@ -17,6 +17,7 @@\n mod concat_idents;\n mod export;\n mod fmt;\n+mod for_lt;\n mod helpers;\n mod kunit;\n mod module;\n@@ -489,3 +490,14 @@ pub fn kunit_tests(attr: TokenStream, input: TokenStream) -> TokenStream {\n .unwrap_or_else(|e| e.into_compile_error())\n .into()\n }\n+\n+/// Obtain a type that implements `ForLt` for the given higher-ranked type.\n+///\n+/// Please refer to the documentation of [`ForLt`] trait.\n+///\n+/// [`ForLt`]: trait.ForLt.html\n+#[proc_macro]\n+#[allow(non_snake_case)] // The macro shares the name with the trait.\n+pub fn ForLt(input: TokenStream) -> TokenStream {\n+ for_lt::for_lt(parse_macro_input!(input)).into()\n+}\n", "prefixes": [ "02/24" ] }