Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make lifetimes code a bit more generic #404

Merged
merged 10 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions core/src/hir/elision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@
//!
//! [Nomicon]: https://doc.rust-lang.org/nomicon/lifetime-elision.html

use super::{
Lifetime, LifetimeEnv, LoweringContext, MaybeStatic, MethodLifetime, TypeLifetime,
TypeLifetimes,
use super::lifetimes::{
self, BoundedLifetime, LifetimeEnv, MaybeStatic, MethodLifetime, TypeLifetime, TypeLifetimes,
};
use super::LoweringContext;
use crate::ast;
use smallvec::SmallVec;

Expand Down Expand Up @@ -171,7 +171,7 @@ impl ElisionSource {
pub(super) struct BaseLifetimeLowerer<'ast> {
lifetime_env: &'ast ast::LifetimeEnv,
self_lifetimes: Option<TypeLifetimes>,
nodes: SmallVec<[Lifetime; 4]>,
nodes: SmallVec<[BoundedLifetime<lifetimes::Method>; super::lifetimes::INLINE_NUM_LIFETIMES]>,
num_lifetimes: usize,
}

Expand Down Expand Up @@ -253,7 +253,7 @@ impl<'ast> SelfParamLifetimeLowerer<'ast> {
let lifetime = ctx.lower_ident(ast_node.lifetime.name(), "named lifetime");
match (lifetime, &mut hir_nodes) {
(Some(lifetime), Some(hir_nodes)) => {
hir_nodes.push(Lifetime::new(
hir_nodes.push(BoundedLifetime::new(
lifetime,
ast_node
.longer
Expand Down Expand Up @@ -345,7 +345,7 @@ impl<'ast> LifetimeLowerer for ParamLifetimeLowerer<'ast> {

impl<'ast> ReturnLifetimeLowerer<'ast> {
/// Finalize the lifetimes in the method, returning the resulting [`LifetimeEnv`].
pub fn finish(self) -> LifetimeEnv {
pub fn finish(self) -> LifetimeEnv<lifetimes::Method> {
LifetimeEnv::new(self.base.nodes, self.base.num_lifetimes)
}
}
Expand Down
141 changes: 85 additions & 56 deletions core/src/hir/lifetimes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,28 @@

use super::IdentBuf;
use crate::ast;
use core::fmt::{self, Debug};
use core::hash::Hash;
use core::marker::PhantomData;
use smallvec::{smallvec, SmallVec};

/// Convenience const representing the number of lifetimes a [`LifetimeEnv`]
/// can hold inline before needing to dynamically allocate.
const INLINE_NUM_LIFETIMES: usize = 4;
pub(crate) const INLINE_NUM_LIFETIMES: usize = 4;

/// The lifetimes and bounds found on a method or type definition (determined by
/// Kind parameter, which will be one of [`LifetimeKind`])
// TODO(Quinn): This type is going to mainly be recycled from `ast::LifetimeEnv`.
// Not fully sure how that will look like yet, but the ideas of what this will do
// is basically the same.
#[derive(Debug)]
pub struct LifetimeEnv {
/// List of named lifetimes in scope of the method, in the form of an
/// adjacency matrix.
nodes: SmallVec<[Lifetime; INLINE_NUM_LIFETIMES]>,
pub struct LifetimeEnv<Kind> {
/// List of named lifetimes in scope of the method, and their bounds
nodes: SmallVec<[BoundedLifetime<Kind>; INLINE_NUM_LIFETIMES]>,

/// 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 [`MethodLfetimes`] with
/// are in range of the graph. If we make a [`MethodLifetimes`] with
/// `num_lifetimes` entries, then `TypeLifetime`s that convert into
/// `MethodLifetime`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
Expand All @@ -32,18 +36,18 @@ pub struct LifetimeEnv {
/// A lifetime in a [`LifetimeEnv`], which keeps track of which lifetimes it's
/// longer and shorter than.
#[derive(Debug)]
pub(super) struct Lifetime {
pub(super) struct BoundedLifetime<Kind> {
ident: IdentBuf,
longer: SmallVec<[MethodLifetime; 2]>,
shorter: SmallVec<[MethodLifetime; 2]>,
longer: SmallVec<[Lifetime<Kind>; 2]>,
shorter: SmallVec<[Lifetime<Kind>; 2]>,
}

impl Lifetime {
/// Returns a new [`Lifetime`].
impl<Kind> BoundedLifetime<Kind> {
/// Returns a new [`BoundedLifetime`].
pub(super) fn new(
ident: IdentBuf,
longer: SmallVec<[MethodLifetime; 2]>,
shorter: SmallVec<[MethodLifetime; 2]>,
longer: SmallVec<[Lifetime<Kind>; 2]>,
shorter: SmallVec<[Lifetime<Kind>; 2]>,
) -> Self {
Self {
ident,
Expand All @@ -55,17 +59,18 @@ impl Lifetime {

/// Visit subtype lifetimes recursively, keeping track of which have already
/// been visited.
pub struct SubtypeLifetimeVisitor<'lt, F> {
lifetime_env: &'lt LifetimeEnv,
pub struct SubtypeLifetimeVisitor<'lt, Kind, F> {
lifetime_env: &'lt LifetimeEnv<Kind>,
visited: SmallVec<[bool; INLINE_NUM_LIFETIMES]>,
visit_fn: F,
}

impl<'lt, F> SubtypeLifetimeVisitor<'lt, F>
impl<'lt, Kind: LifetimeKind, F> SubtypeLifetimeVisitor<'lt, Kind, F>
where
F: FnMut(MethodLifetime),
F: FnMut(Lifetime<Kind>),
Kind: LifetimeKind,
{
fn new(lifetime_env: &'lt LifetimeEnv, visit_fn: F) -> Self {
fn new(lifetime_env: &'lt LifetimeEnv<Kind>, visit_fn: F) -> Self {
Self {
lifetime_env,
visited: smallvec![false; lifetime_env.nodes.len()],
Expand All @@ -75,7 +80,7 @@ where

/// Visit more sublifetimes. This method tracks which lifetimes have already
/// been visited, and uses this to not visit the same lifetime twice.
pub fn visit_subtypes(&mut self, method_lifetime: MethodLifetime) {
pub fn visit_subtypes(&mut self, method_lifetime: Lifetime<Kind>) {
if let Some(visited @ false) = self.visited.get_mut(method_lifetime.0) {
*visited = true;

Expand Down Expand Up @@ -128,12 +133,47 @@ impl<T> MaybeStatic<T> {
}
}

/// The [`LifetimeKind`] of [`TypeLifetimes`]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[allow(clippy::exhaustive_structs)] // marker type
pub struct Type;
/// The [`LifetimeKind`] of [`MethodLifetimes`]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[allow(clippy::exhaustive_structs)] // marker type
pub struct Method;

/// Abstraction over where lifetimes can occur
pub trait LifetimeKind: Copy + Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord {}

impl LifetimeKind for Type {}
impl LifetimeKind for Method {}

/// A lifetime that exists as part of a type or method signature (determined by
/// Kind parameter, which will be one of [`LifetimeKind`]).
///
/// 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)]
pub struct Lifetime<Kind>(usize, PhantomData<Kind>);

impl<Kind> Debug for Lifetime<Kind> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}Lifetime({})", std::any::type_name::<Kind>(), self.0)
}
}

/// A set of lifetimes found on a type name or method signature (determined by
/// Kind parameter, which will be one of [`LifetimeKind`])
#[derive(Clone, Debug)]
pub struct Lifetimes<Kind> {
indices: SmallVec<[MaybeStatic<Lifetime<Kind>>; 2]>,
}

/// A lifetime that exists as part of a type signature.
///
/// This type can be mapped to a [`MethodLifetime`] by using the
/// [`TypeLifetime::as_method_lifetime`] method.
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct TypeLifetime(usize);
pub type TypeLifetime = Lifetime<Type>;

/// A set of lifetimes that exist as generic arguments on [`StructPath`]s,
/// [`OutStructPath`]s, and [`OpaquePath`]s.
Expand All @@ -145,29 +185,23 @@ pub struct TypeLifetime(usize);
/// [`StructPath`]: super::StructPath
/// [`OutStructPath`]: super::OutStructPath
/// [`OpaquePath`]: super::OpaquePath
#[derive(Clone, Debug)]
pub struct TypeLifetimes {
indices: SmallVec<[MaybeStatic<TypeLifetime>; 2]>,
}
pub type TypeLifetimes = Lifetimes<Type>;

/// A lifetime that exists as part of a method signature, e.g. `'a` or an
/// anonymous lifetime.
///
/// This type is intended to be used as a key into a map to keep track of which
/// borrowed fields depend on which method lifetimes.
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct MethodLifetime(usize);
pub type MethodLifetime = Lifetime<Method>;

/// Map a lifetime in a nested struct to the original lifetime defined
/// in the method that it refers to.
pub struct MethodLifetimes {
indices: SmallVec<[MaybeStatic<MethodLifetime>; 2]>,
}
pub type MethodLifetimes = Lifetimes<Method>;

impl LifetimeEnv {
impl<Kind: LifetimeKind> LifetimeEnv<Kind> {
/// Returns a new [`LifetimeEnv`].
pub(super) fn new(
nodes: SmallVec<[Lifetime; INLINE_NUM_LIFETIMES]>,
nodes: SmallVec<[BoundedLifetime<Kind>; INLINE_NUM_LIFETIMES]>,
num_lifetimes: usize,
) -> Self {
Self {
Expand All @@ -177,35 +211,44 @@ impl LifetimeEnv {
}

/// Returns a fresh [`MethodLifetimes`] corresponding to `self`.
pub fn method_lifetimes(&self) -> MethodLifetimes {
pub fn lifetimes(&self) -> Lifetimes<Kind> {
let indices = (0..self.num_lifetimes)
.map(|index| MaybeStatic::NonStatic(MethodLifetime(index)))
.map(|index| MaybeStatic::NonStatic(Lifetime::new(index)))
.collect();

MethodLifetimes { indices }
Lifetimes { indices }
}

/// Returns a new [`SubtypeLifetimeVisitor`], which can visit all reachable
/// lifetimes
pub fn subtype_lifetimes_visitor<F>(&self, visit_fn: F) -> SubtypeLifetimeVisitor<'_, F>
pub fn subtype_lifetimes_visitor<F>(&self, visit_fn: F) -> SubtypeLifetimeVisitor<'_, Kind, F>
where
F: FnMut(MethodLifetime),
F: FnMut(Lifetime<Kind>),
{
SubtypeLifetimeVisitor::new(self, visit_fn)
}
}

impl<Kind: LifetimeKind> Lifetime<Kind> {
pub(super) fn new(index: usize) -> Self {
Self(index, PhantomData)
}
}

impl<Kind: LifetimeKind> Lifetimes<Kind> {
/// Returns an iterator over the contained [`Lifetime`]s.
pub(super) fn lifetimes(&self) -> impl Iterator<Item = MaybeStatic<Lifetime<Kind>>> + '_ {
self.indices.iter().copied()
}
}

impl TypeLifetime {
/// Returns a [`TypeLifetime`] 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(index)
}

pub(super) fn new(index: usize) -> Self {
Self(index)
Self::new(index)
}

/// Returns a new [`MaybeStatic<MethodLifetime>`] representing `self` in the
Expand Down Expand Up @@ -269,17 +312,3 @@ impl TypeLifetimes {
MethodLifetimes { indices }
}
}

impl MethodLifetime {
/// Returns a new `MethodLifetime` from an index into a `LifetimeEnv`.
pub(super) fn new(index: usize) -> Self {
Self(index)
}
}

impl MethodLifetimes {
/// Returns an iterator over the contained [`MethodLifetime`]s.
pub(super) fn lifetimes(&self) -> impl Iterator<Item = MaybeStatic<MethodLifetime>> + '_ {
self.indices.iter().copied()
}
}
9 changes: 5 additions & 4 deletions core/src/hir/lowering.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use super::lifetimes::{self, LifetimeEnv};
use super::{
AttributeContext, AttributeValidator, Borrow, EnumDef, EnumPath, EnumVariant, IdentBuf,
LifetimeEnv, LifetimeLowerer, LookupId, MaybeOwn, Method, NonOptional, OpaqueDef, OpaquePath,
Optional, OutStructDef, OutStructField, OutStructPath, OutType, Param, ParamLifetimeLowerer,
ParamSelf, PrimitiveType, ReturnLifetimeLowerer, ReturnType, ReturnableStructPath,
LifetimeLowerer, LookupId, MaybeOwn, Method, NonOptional, OpaqueDef, OpaquePath, Optional,
OutStructDef, OutStructField, OutStructPath, OutType, Param, ParamLifetimeLowerer, ParamSelf,
PrimitiveType, ReturnLifetimeLowerer, ReturnType, ReturnableStructPath,
SelfParamLifetimeLowerer, SelfType, Slice, StructDef, StructField, StructPath, SuccessType,
Type,
};
Expand Down Expand Up @@ -816,7 +817,7 @@ impl<'ast, 'errors> LoweringContext<'ast, 'errors> {
takes_writeable: bool,
mut return_ltl: Option<ReturnLifetimeLowerer<'_>>,
in_path: &ast::Path,
) -> Option<(ReturnType, LifetimeEnv)> {
) -> Option<(ReturnType, LifetimeEnv<lifetimes::Method>)> {
let writeable_option = if takes_writeable {
Some(SuccessType::Writeable)
} else {
Expand Down
11 changes: 6 additions & 5 deletions core/src/hir/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ use std::fmt::{self, Write};

use smallvec::SmallVec;

use super::{
paths, Attrs, Docs, Ident, IdentBuf, LifetimeEnv, MaybeStatic, MethodLifetime, MethodLifetimes,
OutType, SelfType, Slice, Type, TypeContext, TypeLifetime, TypeLifetimes,
use super::{paths, Attrs, Docs, Ident, IdentBuf, OutType, SelfType, Slice, Type, TypeContext};

use super::lifetimes::{
self, LifetimeEnv, MaybeStatic, MethodLifetime, MethodLifetimes, TypeLifetime, TypeLifetimes,
};

/// A method exposed to Diplomat.
Expand All @@ -15,7 +16,7 @@ use super::{
pub struct Method {
pub docs: Docs,
pub name: IdentBuf,
pub lifetime_env: LifetimeEnv,
pub lifetime_env: LifetimeEnv<lifetimes::Method>,

pub param_self: Option<ParamSelf>,
pub params: Vec<Param>,
Expand Down Expand Up @@ -168,7 +169,7 @@ impl Method {

/// Returns a fresh [`MethodLifetimes`] corresponding to `self`.
pub fn method_lifetimes(&self) -> MethodLifetimes {
self.lifetime_env.method_lifetimes()
self.lifetime_env.lifetimes()
}

/// Returns a new [`BorrowingFieldVisitor`], which allocates memory to
Expand Down
10 changes: 8 additions & 2 deletions core/src/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
mod attrs;
mod defs;
mod elision;
mod lifetimes;
// We don't reexport this for two reasons.
//
// One is that these are somewhat more niche types and we don't want to clutter the main module too
// much. You only need these if you're dealing with lifetimes, which not all backends may wish to
// do.
//
// Two is that this module contains types named Type and Method which will conflict with others.
pub mod lifetimes;
mod lowering;
mod methods;
mod paths;
Expand All @@ -16,7 +23,6 @@ mod types;
pub use attrs::*;
pub use defs::*;
pub(super) use elision::*;
pub use lifetimes::*;
pub(super) use lowering::*;
pub use methods::*;
pub use paths::*;
Expand Down
3 changes: 2 additions & 1 deletion core/src/hir/paths.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::lifetimes::TypeLifetimes;
use super::{
Borrow, EnumDef, EnumId, Everywhere, OpaqueDef, OpaqueId, OpaqueOwner, OutStructDef,
OutputOnly, ReturnableStructDef, StructDef, TyPosition, TypeContext, TypeLifetimes,
OutputOnly, ReturnableStructDef, StructDef, TyPosition, TypeContext,
};

/// Path to a struct that may appear as an output.
Expand Down
Loading
Loading