Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266// SPDX-License-Identifier: Apache-2.0 OR MIT
use proc_macro2::{ Span, TokenStream, //};use quote::{ format_ident, quote, //};use syn::{ parse::{ Parse, ParseStream, // }, visit::Visit, visit_mut::VisitMut, Lifetime, Result, Token, Type, //};
pub(crate) enum HigherRankedType { Explicit { _for_token: Token![for], _lt_token: Token![<], lifetime: Lifetime, _gt_token: Token![>], ty: Type, }, Implicit { ty: Type, },}
impl Parse for HigherRankedType { fn parse(input: ParseStream<'_>) -> Result<Self> { if input.peek(Token![for]) { Ok(Self::Explicit { _for_token: input.parse()?, _lt_token: input.parse()?, lifetime: input.parse()?, _gt_token: input.parse()?, ty: input.parse()?, }) } else { Ok(Self::Implicit { ty: input.parse()? }) } }}
trait TypeExt { fn expand_elided_lifetime(&self, explicit_lt: &Lifetime) -> Type; fn replace_lifetime(&self, src: &Lifetime, dst: &Lifetime) -> Type; fn has_lifetime(&self, lt: &Lifetime) -> bool;}
impl TypeExt for Type { fn expand_elided_lifetime(&self, explicit_lt: &Lifetime) -> Type { struct ElidedLifetimeExpander<'a>(&'a Lifetime);
impl VisitMut for ElidedLifetimeExpander<'_> { fn visit_lifetime_mut(&mut self, lifetime: &mut Lifetime) { // Expand explicit `'_` if lifetime.ident == "_" { *lifetime = self.0.clone(); } }
fn visit_type_reference_mut(&mut self, reference: &mut syn::TypeReference) { syn::visit_mut::visit_type_reference_mut(self, reference);
if reference.lifetime.is_none() { reference.lifetime = Some(self.0.clone()); } } }
let mut ret = self.clone(); ElidedLifetimeExpander(explicit_lt).visit_type_mut(&mut ret); ret }
fn replace_lifetime(&self, src: &Lifetime, dst: &Lifetime) -> Type { struct LifetimeReplacer<'a>(&'a Lifetime, &'a Lifetime);
impl VisitMut for LifetimeReplacer<'_> { fn visit_lifetime_mut(&mut self, lifetime: &mut Lifetime) { if lifetime.ident == self.0.ident { *lifetime = self.1.clone(); } } }
let mut ret = self.clone(); LifetimeReplacer(src, dst).visit_type_mut(&mut ret); ret }
fn has_lifetime(&self, lt: &Lifetime) -> bool { struct HasLifetime<'a>(&'a Lifetime, bool);
impl Visit<'_> for HasLifetime<'_> { fn visit_lifetime(&mut self, lifetime: &Lifetime) { if lifetime.ident == self.0.ident { self.1 = true; } }
// Macro invocations are opaque; conservatively assume they may // reference the lifetime. fn visit_macro(&mut self, _: &syn::Macro) { self.1 = true; } }
let mut visitor = HasLifetime(lt, false); visitor.visit_type(self); visitor.1 }}
struct Prover<'a>(&'a Lifetime, Vec<&'a Type>);
impl<'a> Prover<'a> { /// Prove that `ty` is covariant over `'lt`. /// /// This also needs to prove that it'll be wellformed for any instance of `'lt`. /// It can be assumed that `ty` will be wellformed if `'lt` is substituted to `'static`. fn prove(&mut self, ty: &'a Type) { match ty { Type::Paren(ty) => self.prove(&ty.elem), Type::Group(ty) => self.prove(&ty.elem),
// No lifetime involved Type::Never(_) => {}
// `[T; N]` and `[T]` is covariant over `T`. Type::Array(ty) => self.prove(&ty.elem), Type::Slice(ty) => self.prove(&ty.elem),
Type::Tuple(ty) => { for elem in &ty.elems { self.prove(elem); } }
// `*const T` is covariant over `T` Type::Ptr(ty) if ty.const_token.is_some() => self.prove(&ty.elem),
// `&T` is covariant over `T` and lifetime. // // Note that if we encounter `&'other_lt T`, then we still need to make sure the type // is wellformed if `T` involves `&'lt`, so we defer to the compiler. // // This is to block cases like `CovariantForLt!(for<'a> &'static &'a u32)`, as the // presence of the type implies `'a: 'static` but this is unsound. Type::Reference(ty) if ty.mutability.is_none() && ty.lifetime.as_ref() == Some(self.0) => { self.prove(&ty.elem) }
// `&[mut] T` is covariant over lifetime. // In case we have `&[mut] NoLifetime`, we don't need to do additional checks. Type::Reference(ty) if !ty.elem.has_lifetime(self.0) => (),
// No mention of lifetime at all, no need to perform compiler check. ty if !ty.has_lifetime(self.0) => (),
// Otherwise, we need to emit checks so that compiler can determine if the types are // actually covariant. ty => self.1.push(ty), } }}
/// Shared implementation for both `ForLt!` and `CovariantForLt!`.////// Both macros run the prover and emit `ProveWf` structs to check well-formedness for all lifetime/// instances (workaround for <https://github.com/rust-lang/rust/issues/152489>). `CovariantForLt!`/// additionally emits covariance proof functions and sets `N = 1`.fn for_lt_inner(input: HigherRankedType, prove_covariance: bool) -> TokenStream { let (ty, lifetime) = match input { HigherRankedType::Explicit { lifetime, ty, .. } => (ty, lifetime), HigherRankedType::Implicit { ty } => { // If there's no explicit `for<'a>` binder, inject a synthetic `'__elided` lifetime // and expand elided sites. let lifetime = Lifetime { apostrophe: Span::mixed_site(), ident: format_ident!("__elided", span = Span::mixed_site()), }; (ty.expand_elided_lifetime(&lifetime), lifetime) } };
let mut prover = Prover(&lifetime, Vec::new()); prover.prove(&ty);
let mut proof = Vec::new();
// Emit proofs for every type that requires additional compiler help in proving covariance. for (idx, required_proof) in prover.1.into_iter().enumerate() { // Insert a proof that the type is well-formed. // // This is intended to workaround a Rust compiler soundness bug related to HRTB. // https://github.com/rust-lang/rust/issues/152489 // // This needs to be a struct instead of fn to avoid the implied WF bounds. let wf_proof_name = format_ident!("ProveWf{idx}"); proof.push(quote!( struct #wf_proof_name<#lifetime>( ::core::marker::PhantomData<&#lifetime ()>, #required_proof ); ));
// Insert a proof that the type is covariant. if prove_covariance { let cov_proof_name = format_ident!("prove_covariant_{idx}"); proof.push(quote!( fn #cov_proof_name<'__short, '__long: '__short>( long: #wf_proof_name<'__long> ) -> #wf_proof_name<'__short> { long } )); } }
// Make sure that the type is wellformed when substituting lifetime with `'static`. // // Currently the Rust compiler doesn't check this, see the above `ProveWf` documentation. // // We prefer to use this way of proving WF-ness as it can work when generics are involved. let ty_static = ty.replace_lifetime( &lifetime, &Lifetime { apostrophe: Span::mixed_site(), ident: format_ident!("static"), }, );
let n: usize = prove_covariance.into();
quote!( ::kernel::types::for_lt::UnsafeForLtImpl::< dyn for<#lifetime> ::kernel::types::for_lt::WithLt<#lifetime, Of = #ty>, #ty_static, { #(#proof)*
#n } > )}
pub(crate) fn for_lt(input: HigherRankedType) -> TokenStream { for_lt_inner(input, false)}
pub(crate) fn covariant_for_lt(input: HigherRankedType) -> TokenStream { for_lt_inner(input, true)}