//! Lifetime information for types. #![allow(dead_code)]
usesuper::IdentBuf; usecrate::ast; use core::fmt::Debug; use core::hash::Hash;
use smallvec::{smallvec, SmallVec}; use std::borrow::{Borrow, Cow};
/// Convenience const representing the number of lifetimes a [`LifetimeEnv`] /// can hold inline before needing to dynamically allocate. pub(crate) const INLINE_NUM_LIFETIMES: usize = 4;
/// The lifetimes and bounds found on a method or type definition #[derive(Debug)] pubstruct LifetimeEnv { /// List of named lifetimes in scope of the method, and their bounds
nodes: SmallVec<[BoundedLifetime; INLINE_NUM_LIFETIMES]>,
/// Only relevant for method LifetimeEnvs (otherwise this is nodes.len()) /// /// The number of named _and_ anonymous lifetimes in the method. /// We store the sum since it represents the upper bound on what indices /// are in range of the graph. If we make a [`Lifetimes`] with /// `num_lifetimes` entries, then `Lifetime`s that convert into /// `Lifetime`s will fall into this range, and we'll know that it's /// a named lifetime if it's < `nodes.len()`, or that it's an anonymous /// lifetime if it's < `num_lifetimes`. Otherwise, we'd have to make a /// distinction in `Lifetime` about which context it's in.
num_lifetimes: usize,
}
impl LifetimeEnv { /// Format a lifetime indexing this env for use in code pubfn fmt_lifetime(&self, lt: impl Borrow<Lifetime>) -> Cow<str> { // we use Borrow here so that this can be used in templates where there's autoborrowing let lt = *lt.borrow(); iflet Some(lt) = self.nodes.get(lt.0) {
Cow::from(lt.ident.as_str())
} elseif lt.0 < self.num_lifetimes {
format!("anon_{}", lt.0 - self.nodes.len()).into()
} else {
panic!("Found out of range lifetime: Got {lt:?} for env with {} nodes and {} total lifetimes", self.nodes.len(), self.num_lifetimes);
}
}
/// Get an iterator of all lifetimes that this must live as long as (including itself) /// with the first lifetime always being returned first
pubfn all_shorter_lifetimes(
&self,
lt: impl Borrow<Lifetime>,
) -> impl Iterator<Item = Lifetime> + '_ { // we use Borrow here so that this can be used in templates where there's autoborrowing let lt = *lt.borrow(); // longer = true, since we are looking for lifetimes this is longer than
LifetimeTransitivityIterator::new(self, lt.0, false)
}
/// Same as all_shorter_lifetimes but the other way pubfn all_longer_lifetimes(
&self,
lt: impl Borrow<Lifetime>,
) -> impl Iterator<Item = Lifetime> + '_ { // we use Borrow here so that this can be used in templates where there's autoborrowing let lt = *lt.borrow();
LifetimeTransitivityIterator::new(self, lt.0, true)
}
// List all named and unnamed lifetimes pubfn num_lifetimes(&self) -> usize { self.num_lifetimes
}
/// Get the bounds for a named lifetime (none for unnamed lifetimes) pub(super) fn get_bounds(&self, named_lifetime: Lifetime) -> Option<&BoundedLifetime> { self.nodes.get(named_lifetime.0)
}
/// Returns a fresh [`Lifetimes`] corresponding to `self`. pubfn lifetimes(&self) -> Lifetimes { let indices = (0..self.num_lifetimes)
.map(|index| MaybeStatic::NonStatic(Lifetime::new(index)))
.collect();
Lifetimes { indices }
}
/// Returns a new [`SubtypeLifetimeVisitor`], which can visit all reachable /// lifetimes pubfn subtype_lifetimes_visitor<F>(&self, visit_fn: F) -> SubtypeLifetimeVisitor<'_, F> where
F: FnMut(Lifetime),
{
SubtypeLifetimeVisitor::new(self, visit_fn)
}
}
/// A lifetime in a [`LifetimeEnv`], which keeps track of which lifetimes it's /// longer and shorter than. /// /// Invariant: for a BoundedLifetime found inside a LifetimeEnv, all short/long connections /// should be bidirectional. #[derive(Debug)] pub(super) struct BoundedLifetime { pub(super) ident: IdentBuf, /// Lifetimes longer than this (not transitive) /// /// These are the inverse graph edges compared to `shorter` pub(super) longer: SmallVec<[Lifetime; 2]>, /// Lifetimes this is shorter than (not transitive) /// /// These match `'a: 'b + 'c` bounds pub(super) shorter: SmallVec<[Lifetime; 2]>,
}
/// Visit more sublifetimes. This method tracks which lifetimes have already /// been visited, and uses this to not visit the same lifetime twice. pubfn visit_subtypes(&mutself, method_lifetime: Lifetime) { iflet Some(visited @ false) = self.visited.get_mut(method_lifetime.0) {
*visited = true;
(self.visit_fn)(method_lifetime);
for longer inself.lifetime_env.nodes[method_lifetime.0].longer.iter() { self.visit_subtypes(*longer)
}
} else {
debug_assert!(
method_lifetime.0 > self.lifetime_env.num_lifetimes, "method lifetime has an internal index that's not in range of the lifetime env"
);
}
}
}
/// Wrapper type for `Lifetime` and `Lifetime`, indicating that it may /// be the `'static` lifetime. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] #[allow(clippy::exhaustive_enums)] // this will only ever have two variants pubenum MaybeStatic<T> { Static,
NonStatic(T),
}
impl<T> MaybeStatic<T> { /// Maps the lifetime, if it's not the `'static` lifetime, to another /// non-static lifetime. pub(super) fn map_nonstatic<F, R>(self, f: F) -> MaybeStatic<R> where
F: FnOnce(T) -> R,
{ matchself {
MaybeStatic::Static => MaybeStatic::Static,
MaybeStatic::NonStatic(lifetime) => MaybeStatic::NonStatic(f(lifetime)),
}
}
/// Maps the lifetime, if it's not the `'static` lifetime, to a potentially /// static lifetime. pub(super) fn flat_map_nonstatic<R, F>(self, f: F) -> MaybeStatic<R> where
F: FnOnce(T) -> MaybeStatic<R>,
{ matchself {
MaybeStatic::Static => MaybeStatic::Static,
MaybeStatic::NonStatic(lifetime) => f(lifetime),
}
}
}
/// A lifetime that exists as part of a type name, struct signature, or method signature. /// /// This index only makes sense in the context of a surrounding type or method; since /// this is essentially an index into that type/method's lifetime list. #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)] pubstruct Lifetime(usize);
/// A set of lifetimes found on a type name, struct signature, or method signature #[derive(Clone, Debug)] pubstruct Lifetimes {
indices: SmallVec<[MaybeStatic<Lifetime>; 2]>,
}
impl Lifetime { /// Returns a [`Lifetime`] from its AST counterparts. pub(super) fn from_ast(named: &ast::NamedLifetime, lifetime_env: &ast::LifetimeEnv) -> Self { let index = lifetime_env
.id(named)
.unwrap_or_else(|| panic!("lifetime `{named}` not found in lifetime env")); Self::new(index)
}
/// Returns a new [`MaybeStatic<Lifetime>`] representing `self` in the /// scope of the method that it appears in. /// /// For example, if we have some `Foo<'a>` type with a field `&'a Bar`, then /// we can call this on the `'a` on the field. If `Foo` was `Foo<'static>` /// in the method, then this will return `MaybeStatic::Static`. But if it /// was `Foo<'b>`, then this will return `MaybeStatic::NonStatic` containing /// the `Lifetime` corresponding to `'b`. pubfn as_method_lifetime(self, method_lifetimes: &Lifetimes) -> MaybeStatic<Lifetime> {
method_lifetimes.indices[self.0]
}
}
/// Append an additional lifetime. Used to tack on anon lifetimes pub(super) fn append_lifetime(&mutself, lifetime: MaybeStatic<Lifetime>) { self.indices.push(lifetime)
}
/// Returns a new [`Lifetimes`] representing the lifetimes in the scope /// of the method this type appears in. /// /// # Examples /// /// ```rust /// # struct Alice<'a>(&'a ()); /// # struct Bob<'b>(&'b ()); /// struct Foo<'a, 'b> { /// alice: Alice<'a>, /// bob: Bob<'b>, /// } /// /// fn bar<'x, 'y>(arg: Foo<'x, 'y>) {} /// ``` /// Here, `Foo` will have a [`Lifetimes`] containing `['a, 'b]`, /// and `bar` will have a [`Lifetimes`] containing `{'x: 'x, 'y: 'y}`. /// When we enter the scope of `Foo` as a type, we use this method to combine /// the two to get a new [`Lifetimes`] representing the mapping from /// lifetimes in `Foo`'s scope to lifetimes in `bar`s scope: `{'a: 'x, 'b: 'y}`. /// /// This tells us that `arg.alice` has lifetime `'x` in the method, and /// that `arg.bob` has lifetime `'y`. pubfn as_method_lifetimes(&self, method_lifetimes: &Lifetimes) -> Lifetimes { let indices = self
.indices
.iter()
.map(|maybe_static_lt| {
maybe_static_lt.flat_map_nonstatic(|lt| lt.as_method_lifetime(method_lifetimes))
})
.collect();
/// Convenience type for linking the lifetimes found at a type *use* site (e.g. `&'c Foo<'a, 'b>`) /// with the lifetimes found at its *def* site (e.g. `struct Foo<'x, 'y>`). /// /// Construct this by calling `.linked_lifetimes()` on a StructPath or OpaquePath pubstruct LinkedLifetimes<'def, 'tcx> {
env: &'tcx LifetimeEnv,
self_lt: Option<MaybeStatic<Lifetime>>,
lifetimes: &'def Lifetimes,
}
impl<'def, 'tcx> LinkedLifetimes<'def, 'tcx> { pub(crate) fn new(
env: &'tcx LifetimeEnv,
self_lt: Option<MaybeStatic<Lifetime>>,
lifetimes: &'def Lifetimes,
) -> Self {
debug_assert_eq!(
lifetimes.lifetimes().len(),
env.all_lifetimes().len(), "Should only link lifetimes between a type and its def"
); Self {
env,
self_lt,
lifetimes,
}
}
/// Takes a lifetime at the def site and produces one at the use site pubfn def_to_use(&self, def_lt: Lifetime) -> MaybeStatic<Lifetime> {
*self
.lifetimes
.as_slice()
.get(def_lt.0)
.expect("All def site lifetimes must be used!")
}
/// The lifetime env at the def site. Def lifetimes should be resolved /// against this. pubfn def_env(&self) -> &'tcx LifetimeEnv { self.env
}
/// Link lifetimes from the use site to lifetimes from the def site, only including /// lifetimes found at the def site. /// /// This will *not* include the self-lifetime, i.e. for an opaque use site `&'c Foo<'a, 'b>` /// this will not include `'c` (but you can obtain it from [`Self::self_lifetime()`])) /// /// The return iterator returns pairs of (use_lt, def_lt), in order. /// /// This behaves identically to [`Self::lifetimes_all()`] for `LinkedLifetimes` constructed /// from anything other than a borrowing opaque. pubfn lifetimes_def_only(
&self,
) -> impl Iterator<Item = (MaybeStatic<Lifetime>, Lifetime)> + '_ { self.lifetimes.lifetimes().zip(self.env.all_lifetimes())
}
/// If there is a self-lifetime (e.g. `'c` on `&'c Foo<'a, 'b>`), return it. This lifetime /// isn't found at the def site. pubfn self_lifetime(&self) -> Option<MaybeStatic<Lifetime>> { self.self_lt
}
/// Link lifetimes from the use site to lifetimes from the def site, including self lifetimes. /// /// This returns Options since self-lifetimes do not map to anything at the def site. /// /// The return iterator returns pairs of (use_lt, def_lt), in order, with the first entry potentially being /// the self lifetime (which has a def_lt of None). /// /// This behaves identically to [`Self::lifetimes_all()`] for `LinkedLifetimes` constructed /// from anything other than a borrowing opaque. pubfn lifetimes_all(
&self,
) -> impl Iterator<Item = (MaybeStatic<Lifetime>, Option<Lifetime>)> + '_ { self.self_lt.iter().map(|i| (*i, None)).chain( self.lifetimes
.lifetimes()
.zip(self.env.all_lifetimes().map(Some)),
)
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.21 Sekunden
(vorverarbeitet am 2026-06-23)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.