Skip to content

Commit

Permalink
lifetime branded bounds docs
Browse files Browse the repository at this point in the history
  • Loading branch information
sarah-quinones committed Sep 30, 2024
1 parent fb78b7c commit e5e3503
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/col/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub trait ColIndex<RowRange>: crate::seal::Seal + Sized {

/// Trait for types that can be converted to a column view.
pub trait AsColRef<E: Entity> {
/// Row dimension of the column.
type R: Shape;

/// Convert to a column view.
Expand Down
32 changes: 31 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1802,57 +1802,77 @@ mod tests {
// #[path = "krylov_schur.rs"]
// mod krylov_schur_prototype;

/// Sealed trait for types that can be created from "unbound" values, as long as their
/// struct preconditions are upheld.
pub trait Unbind<I = usize>: Send + Sync + Copy + core::fmt::Debug + seal::Seal {
/// Create new value.
/// # Safety
/// Safety invariants must be upheld.
unsafe fn new_unbound(idx: I) -> Self;

/// Returns the unbound value, unconstrained by safety invariants.
fn unbound(self) -> I;
}

/// Type that can be used to index into a range.
pub type Idx<N, I = usize> = <N as ShapeIdx>::Idx<I>;
/// Type that can be used to partition a range.
pub type IdxInc<N, I = usize> = <N as ShapeIdx>::IdxInc<I>;

/// Base trait for [`Shape`].
pub trait ShapeIdx {
/// Type that can be used to index into a range.
type Idx<I: Index>: Unbind<I> + Ord + Eq;
/// Type that can be used to partition a range.
type IdxInc<I: Index>: Unbind<I> + Ord + Eq + From<Idx<Self, I>>;
}

/// Matrix dimension.
pub trait Shape:
Unbind
+ Ord
+ ShapeIdx<Idx<usize>: Ord + Eq + PartialOrd<Self>, IdxInc<usize>: Ord + Eq + PartialOrd<Self>>
{
const COMPTIME: Option<usize> = None;
/// Whether the types involved have any safety invariants.
const IS_BOUND: bool = true;

/// Bind the current value using a invariant lifetime guard.
#[inline]
fn bind<'n>(self, guard: generativity::Guard<'n>) -> utils::bound::Dim<'n> {
utils::bound::Dim::new(self.unbound(), guard)
}

/// Cast a slice of bound values to unbound values.
#[inline]
fn cast_idx_slice<I: Index>(slice: &[Idx<Self, I>]) -> &[I] {
unsafe { core::slice::from_raw_parts(slice.as_ptr() as _, slice.len()) }
}

/// Cast a slice of bound values to unbound values.
#[inline]
fn cast_idx_inc_slice<I: Index>(slice: &[IdxInc<Self, I>]) -> &[I] {
unsafe { core::slice::from_raw_parts(slice.as_ptr() as _, slice.len()) }
}

/// Returns the index `0`, which is always valid.
#[inline(always)]
fn start() -> IdxInc<Self> {
unsafe { IdxInc::<Self>::new_unbound(0) }
}

/// Returns the incremented value, as an inclusive index.
#[inline(always)]
fn next(idx: Idx<Self>) -> IdxInc<Self> {
unsafe { IdxInc::<Self>::new_unbound(idx.unbound() + 1) }
}

/// Returns the last value, equal to the dimension.
#[inline(always)]
fn end(self) -> IdxInc<Self> {
unsafe { IdxInc::<Self>::new_unbound(self.unbound()) }
}

/// Checks if the index is valid, returning `Some(_)` in that case.
#[inline(always)]
fn idx(self, idx: usize) -> Option<Idx<Self>> {
if idx < self.unbound() {
Expand All @@ -1862,6 +1882,7 @@ pub trait Shape:
}
}

/// Checks if the index is valid, returning `Some(_)` in that case.
#[inline(always)]
fn idx_inc(self, idx: usize) -> Option<IdxInc<Self>> {
if idx <= self.unbound() {
Expand All @@ -1871,30 +1892,39 @@ pub trait Shape:
}
}

/// Checks if the index is valid, and panics otherwise.
#[inline(always)]
fn checked_idx(self, idx: usize) -> Idx<Self> {
equator::assert!(idx < self.unbound());
unsafe { Idx::<Self>::new_unbound(idx) }
}

/// Checks if the index is valid, and panics otherwise.
#[inline(always)]
fn checked_idx_inc(self, idx: usize) -> IdxInc<Self> {
equator::assert!(idx <= self.unbound());
unsafe { IdxInc::<Self>::new_unbound(idx) }
}

/// Assumes the index is valid.
/// # Safety
/// The index must be valid.
#[inline(always)]
unsafe fn unchecked_idx(self, idx: usize) -> Idx<Self> {
equator::debug_assert!(idx < self.unbound());
unsafe { Idx::<Self>::new_unbound(idx) }
}

/// Assumes the index is valid.
/// # Safety
/// The index must be valid.
#[inline(always)]
unsafe fn unchecked_idx_inc(self, idx: usize) -> IdxInc<Self> {
equator::debug_assert!(idx <= self.unbound());
unsafe { IdxInc::<Self>::new_unbound(idx) }
}

/// Returns an iterator over the indices between `from` and `to`.
#[inline(always)]
fn indices(
from: IdxInc<Self>,
Expand Down
2 changes: 2 additions & 0 deletions src/mat/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ pub trait MatIndex<RowRange, ColRange>: crate::seal::Seal + Sized {
/// [`MatRef`], and [`MatMut`], but not for types like [`Col`], [`Row`], or
/// their families. For a more general trait, see [`As2D`].
pub trait AsMatRef<E: Entity> {
/// Row dimension of the matrix.
type R: Shape;
/// Column dimension of the matrix.
type C: Shape;

/// Convert to a matrix view.
Expand Down
1 change: 1 addition & 0 deletions src/row/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub trait RowIndex<ColRange>: crate::seal::Seal + Sized {

/// Trait for types that can be converted to a row view.
pub trait AsRowRef<E: Entity> {
/// Column dimension of the row.
type C: Shape;

/// Convert to a row view.
Expand Down
47 changes: 47 additions & 0 deletions src/utils/bound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,28 @@ use crate::{Index, Shape, ShapeIdx, Unbind};

type Invariant<'a> = fn(&'a ()) -> &'a ();

/// Splits a range into two segments.
#[derive(Copy, Clone)]
pub struct Partition<'head, 'tail, 'n> {
/// Size of the first half.
pub head: Dim<'head>,
/// Size of the second half.
pub tail: Dim<'tail>,
__marker: PhantomData<Invariant<'n>>,
}

impl<'head, 'tail, 'n> Partition<'head, 'tail, 'n> {
/// Returns the midpoint of the partition.
#[inline]
pub const fn midpoint(&self) -> IdxInc<'n> {
unsafe { IdxInc::new_unbound(self.head.unbound) }
}
}

/// Lifetime branded length
/// # Safety
/// The type's safety invariant is that all instances of this type with the same lifetime
/// correspond to the same length.
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct Dim<'n> {
Expand Down Expand Up @@ -49,13 +57,21 @@ impl Ord for Dim<'_> {
}
}

/// Lifetime branded index.
/// # Safety
/// The type's safety invariant is that all instances of this type are valid indices for
/// [`Dim<'n>`].
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Idx<'n, I: Index = usize> {
unbound: I,
__marker: PhantomData<Invariant<'n>>,
}

/// Lifetime branded partition index.
/// # Safety
/// The type's safety invariant is that all instances of this type are valid partition places for
/// [`Dim<'n>`].
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct IdxInc<'n, I: Index = usize> {
Expand Down Expand Up @@ -122,6 +138,9 @@ impl<'n, I: Index> PartialOrd<Dim<'n>> for IdxInc<'n, I> {
}

impl<'n> Dim<'n> {
/// Create new branded value with an arbitrary brand.
/// # Safety
/// See struct safety invariant.
#[inline(always)]
pub const unsafe fn new_unbound(dim: usize) -> Self {
Self {
Expand All @@ -130,6 +149,7 @@ impl<'n> Dim<'n> {
}
}

/// Create new branded value with a unique brand.
#[inline(always)]
pub fn new(dim: usize, guard: Guard<'n>) -> Self {
_ = guard;
Expand All @@ -139,11 +159,13 @@ impl<'n> Dim<'n> {
}
}

/// Returns the unconstrained value.
#[inline(always)]
pub const fn unbound(self) -> usize {
self.unbound
}

/// Partitions `self` into two segments as specifiedd by the midpoint.
#[inline]
pub const fn partition<'head, 'tail>(
self,
Expand All @@ -161,13 +183,17 @@ impl<'n> Dim<'n> {
}
}

/// Returns an iterator over the indices between `0` and `self`.
#[inline]
pub fn indices(self) -> impl Clone + ExactSizeIterator + DoubleEndedIterator<Item = Idx<'n>> {
(0..self.unbound).map(|i| unsafe { Idx::new_unbound(i) })
}
}

impl<'n, I: Index> Idx<'n, I> {
/// Create new branded value with an arbitrary brand.
/// # Safety
/// See struct safety invariant.
#[inline(always)]
pub const unsafe fn new_unbound(idx: I) -> Self {
Self {
Expand All @@ -176,6 +202,9 @@ impl<'n, I: Index> Idx<'n, I> {
}
}

/// Create new branded value with the same brand as `dim`.
/// # Safety
/// The behavior is undefined unless `idx < dim`.
#[inline(always)]
pub unsafe fn new_unchecked(idx: I, dim: Dim<'n>) -> Self {
equator::debug_assert!(idx.zx() < dim.unbound);
Expand All @@ -186,6 +215,9 @@ impl<'n, I: Index> Idx<'n, I> {
}
}

/// Create new branded value with the same brand as `dim`.
/// # Panics
/// Panics unless `idx < dim`.
#[inline(always)]
pub fn new_checked(idx: I, dim: Dim<'n>) -> Self {
equator::assert!(idx.zx() < dim.unbound);
Expand All @@ -196,11 +228,13 @@ impl<'n, I: Index> Idx<'n, I> {
}
}

/// Returns the unconstrained value.
#[inline(always)]
pub const fn unbound(self) -> I {
self.unbound
}

/// Zero-extends the internal value into a `usize`.
#[inline(always)]
pub fn zx(self) -> Idx<'n, usize> {
Idx {
Expand All @@ -210,13 +244,17 @@ impl<'n, I: Index> Idx<'n, I> {
}
}
impl<'n> Idx<'n> {
/// Returns the incremented value.
#[inline(always)]
pub const fn next(self) -> IdxInc<'n> {
unsafe { IdxInc::new_unbound(self.unbound + 1) }
}
}

impl<'n, I: Index> IdxInc<'n, I> {
/// Create new branded value with an arbitrary brand.
/// # Safety
/// See struct safety invariant.
#[inline(always)]
pub const unsafe fn new_unbound(idx: I) -> Self {
Self {
Expand All @@ -225,6 +263,9 @@ impl<'n, I: Index> IdxInc<'n, I> {
}
}

/// Create new branded value with the same brand as `dim`.
/// # Safety
/// The behavior is undefined unless `idx <= dim`.
#[inline(always)]
pub unsafe fn new_unchecked(idx: I, dim: Dim<'n>) -> Self {
equator::debug_assert!(idx.zx() <= dim.unbound);
Expand All @@ -235,6 +276,9 @@ impl<'n, I: Index> IdxInc<'n, I> {
}
}

/// Create new branded value with the same brand as `dim`.
/// # Panics
/// Panics unless `idx <= dim`.
#[inline(always)]
pub fn new_checked(idx: I, dim: Dim<'n>) -> Self {
equator::assert!(idx.zx() <= dim.unbound);
Expand All @@ -245,11 +289,13 @@ impl<'n, I: Index> IdxInc<'n, I> {
}
}

/// Returns the unconstrained value.
#[inline(always)]
pub const fn unbound(self) -> I {
self.unbound
}

/// Zero-extends the internal value into a `usize`.
#[inline(always)]
pub fn zx(self) -> IdxInc<'n, usize> {
IdxInc {
Expand All @@ -260,6 +306,7 @@ impl<'n, I: Index> IdxInc<'n, I> {
}

impl<'n> IdxInc<'n> {
/// Returns an iterator over the indices between `self` and `to`.
#[inline]
pub fn to(
self,
Expand Down
1 change: 1 addition & 0 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ pub mod thread;
/// Vector type for [entities](crate::Entity).
pub mod vec;

/// Lifetime-branded bound index and dimension types.
pub mod bound;

0 comments on commit e5e3503

Please sign in to comment.