use core::{
marker::Destruct,
ops::{Index, IndexMut},
};
use super::{BindBorrowType, BindUsage};
use crate::utils::{
binary_heap::{BinaryHeap, BinaryHeapCtx, VecLike},
refcell::RefCell,
Init,
};
#[const_trait]
pub(super) trait SorterCallback {
fn push_bind_order(&mut self, bind_i: usize);
fn report_error(&mut self, e: SorterError<'_>);
fn num_binds(&self) -> usize;
fn bind_users(&self, bind_i: usize) -> &[(BindUsage, BindBorrowType)];
}
pub(super) enum SorterError<'a> {
BindCycle { bind_is: &'a [usize] },
ConflictingIndefiniteBorrow { bind_i: usize },
}
#[derive(Clone, Copy)]
pub(super) struct SorterBindInfo1 {
borrowed_indefinitely: Option<bool>,
first_use_i: Option<usize>,
}
impl Init for SorterBindInfo1 {
const INIT: Self = Self {
borrowed_indefinitely: None,
first_use_i: None,
};
}
#[derive(Clone, Copy)]
pub(super) struct SorterBindInfo2 {
temp_sort: [TopologicalSortVertexInfo; 2],
}
impl Init for SorterBindInfo2 {
const INIT: Self = Self {
temp_sort: Init::INIT,
};
}
#[derive(Clone, Copy)]
pub(super) struct SorterUseInfo {
bind_i: usize,
borrow_type: BindBorrowType,
next_use_i: Option<usize>,
}
#[derive(Clone, Copy)]
pub(super) enum Vertex {
BindInit(usize),
BindDisown(usize),
Executable,
}
pub(super) const fn sort_bindings<Callback, SorterUseInfoList, VertexList>(
cb: &mut Callback,
temp_binds1: &mut [SorterBindInfo1],
temp_binds2: &mut [SorterBindInfo2],
temp_uses: &mut SorterUseInfoList,
temp_vertices: &mut VertexList,
) where
Callback: ~const SorterCallback,
SorterUseInfoList: ~const VecLike<Element = SorterUseInfo>,
VertexList: ~const VecLike<Element = Vertex>,
{
let num_binds = cb.num_binds();
assert!(temp_binds1.len() >= num_binds);
assert!(temp_binds2.len() >= num_binds);
assert!(temp_uses.is_empty());
assert!(temp_vertices.is_empty());
{
let mut bind_i = 0;
while bind_i < num_binds {
let bind_users = cb.bind_users(bind_i);
let mut num_indefinite_shared = 0;
let mut num_indefinite_exclusive = 0;
let mut i = 0;
while i < bind_users.len() {
match bind_users[i] {
(BindUsage::Bind(_), BindBorrowType::Borrow)
| (BindUsage::Bind(_), BindBorrowType::BorrowMut)
| (BindUsage::Bind(_), BindBorrowType::Take)
| (BindUsage::Bind(_), BindBorrowType::TakeRef)
| (BindUsage::Bind(_), BindBorrowType::TakeMut)
| (BindUsage::Executable, BindBorrowType::Take)
| (BindUsage::Executable, BindBorrowType::TakeRef)
| (BindUsage::Executable, BindBorrowType::TakeMut) => {}
(BindUsage::Executable, BindBorrowType::Borrow)
| (BindUsage::Executable, BindBorrowType::BorrowMut) => {
unreachable!()
}
}
match bind_users[i].1 {
BindBorrowType::Borrow | BindBorrowType::BorrowMut => {}
BindBorrowType::TakeRef => {
num_indefinite_shared += 1;
}
BindBorrowType::Take | BindBorrowType::TakeMut => {
num_indefinite_exclusive += 1;
}
}
if let (BindUsage::Bind(user_bind_i), borrow_type) = bind_users[i] {
let use_i = temp_uses.len();
let other_bind_first_use_i = &mut temp_binds1[user_bind_i].first_use_i;
temp_uses.push(SorterUseInfo {
bind_i,
borrow_type,
next_use_i: *other_bind_first_use_i,
});
*other_bind_first_use_i = Some(use_i);
}
i += 1;
}
temp_binds1[bind_i].borrowed_indefinitely =
match (num_indefinite_shared, num_indefinite_exclusive) {
(0, 0) => None,
(_, 0) => Some(false),
(0, 1) => Some(true),
_ => {
cb.report_error(SorterError::ConflictingIndefiniteBorrow { bind_i });
Some(false)
}
};
bind_i += 1;
}
}
impl Vertex {
const fn discriminant(&self) -> usize {
match self {
Vertex::BindInit(_) => 0,
Vertex::BindDisown(_) => 1,
Vertex::Executable => 2,
}
}
const fn lt(&self, rhs: &Vertex) -> bool {
match (*self, *rhs) {
(Vertex::BindInit(lhs_bind_i), Vertex::BindInit(rhs_bind_i)) => {
lhs_bind_i < rhs_bind_i
}
(Vertex::BindDisown(lhs_bind_i), Vertex::BindDisown(rhs_bind_i)) => {
lhs_bind_i < rhs_bind_i
}
(Vertex::Executable, Vertex::Executable) => false,
_ => self.discriminant() < rhs.discriminant(),
}
}
}
struct Graph<'a, Callback> {
temp_binds1: &'a [SorterBindInfo1],
cb: &'a RefCell<&'a mut Callback>,
temp_uses: &'a [SorterUseInfo],
}
impl<'a, Callback> const GraphAccess<Vertex> for Graph<'a, Callback>
where
Callback: ~const SorterCallback,
{
type VertexIter<'b> = VertexIter<'a>
where
Self: 'b;
fn vertices(&self) -> Self::VertexIter<'_> {
VertexIter {
temp_binds1: self.temp_binds1,
st: VertexIterState::BindInit(0),
}
}
type SuccessorIter<'b> = SuccessorIter<'a, Callback>
where
Self: 'b;
fn successors(&self, v: &Vertex) -> Self::SuccessorIter<'_> {
let st = match *v {
Vertex::Executable => SuccessorIterState::End, Vertex::BindInit(bind_i) => SuccessorIterState::BindInitToBorrowingUser(bind_i, 0),
Vertex::BindDisown(bind_i) => {
if self.temp_binds1[bind_i].borrowed_indefinitely.is_some() {
SuccessorIterState::BindDisownToTakingUser(bind_i, 0)
} else {
SuccessorIterState::End }
}
};
SuccessorIter {
temp_binds1: self.temp_binds1,
cb: self.cb,
temp_uses: self.temp_uses,
st,
}
}
}
struct VertexIter<'a> {
temp_binds1: &'a [SorterBindInfo1],
st: VertexIterState,
}
enum VertexIterState {
BindInit(usize),
BindDisown(usize),
Executable,
End,
}
impl const MyIterator for VertexIter<'_> {
type Item = Vertex;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.st {
VertexIterState::BindInit(bind_i) if bind_i >= self.temp_binds1.len() => {
self.st = VertexIterState::Executable;
}
VertexIterState::BindInit(bind_i) => {
self.st = VertexIterState::BindDisown(bind_i);
return Some(Vertex::BindInit(bind_i));
}
VertexIterState::BindDisown(bind_i) => {
self.st = VertexIterState::BindInit(bind_i + 1);
if self.temp_binds1[bind_i].borrowed_indefinitely.is_some() {
return Some(Vertex::BindDisown(bind_i));
}
}
VertexIterState::Executable => {
self.st = VertexIterState::End;
return Some(Vertex::Executable);
}
VertexIterState::End => return None,
}
}
}
}
struct SuccessorIter<'a, Callback> {
temp_binds1: &'a [SorterBindInfo1],
cb: &'a RefCell<&'a mut Callback>,
temp_uses: &'a [SorterUseInfo],
st: SuccessorIterState,
}
enum SuccessorIterState {
BindInitToBorrowingUser(usize, usize),
BindInitToDisown(usize),
BindInitToDependencyDisown(usize, Option<usize>),
BindDisownToTakingUser(usize, usize),
End,
}
impl<Callback> const MyIterator for SuccessorIter<'_, Callback>
where
Callback: ~const SorterCallback,
{
type Item = Vertex;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.st {
SuccessorIterState::BindInitToBorrowingUser(bind_i, bind_user_i) => {
let cb = self.cb.borrow();
let bind_users = cb.bind_users(bind_i);
if let Some(bind_user) = bind_users.get(bind_user_i) {
self.st = SuccessorIterState::BindInitToBorrowingUser(
bind_i,
bind_user_i + 1,
);
if matches!(
bind_user.1,
BindBorrowType::Borrow | BindBorrowType::BorrowMut
) {
return Some(match bind_user.0 {
BindUsage::Executable => Vertex::Executable,
BindUsage::Bind(user_bind_i) => Vertex::BindInit(user_bind_i),
});
}
} else {
self.st = SuccessorIterState::BindInitToDisown(bind_i);
}
}
SuccessorIterState::BindInitToDisown(bind_i) => {
self.st = SuccessorIterState::BindInitToDependencyDisown(bind_i, None);
if self.temp_binds1[bind_i].borrowed_indefinitely.is_some() {
return Some(Vertex::BindDisown(bind_i));
}
}
SuccessorIterState::BindInitToDependencyDisown(bind_i, Some(use_i)) => {
let usage = &self.temp_uses[use_i];
self.st = SuccessorIterState::BindInitToDependencyDisown(
bind_i,
usage.next_use_i,
);
match (
usage.borrow_type,
self.temp_binds1[usage.bind_i].borrowed_indefinitely,
) {
| (BindBorrowType::Borrow | BindBorrowType::BorrowMut, Some(true))
| (BindBorrowType::BorrowMut, Some(false))
=> {
return Some(Vertex::BindDisown(usage.bind_i));
}
_ => {}
}
}
SuccessorIterState::BindInitToDependencyDisown(_, None) => {
self.st = SuccessorIterState::End;
}
SuccessorIterState::BindDisownToTakingUser(bind_i, bind_user_i) => {
let cb = self.cb.borrow();
let bind_users = cb.bind_users(bind_i);
if let Some(bind_user) = bind_users.get(bind_user_i) {
self.st =
SuccessorIterState::BindDisownToTakingUser(bind_i, bind_user_i + 1);
if matches!(
bind_user.1,
BindBorrowType::Take
| BindBorrowType::TakeRef
| BindBorrowType::TakeMut
) {
return Some(match bind_user.0 {
BindUsage::Executable => Vertex::Executable,
BindUsage::Bind(user_bind_i) => Vertex::BindInit(user_bind_i),
});
}
} else {
self.st = SuccessorIterState::End;
}
}
SuccessorIterState::End => return None,
}
}
}
}
struct MyTopologicalSortOutputSink<'a, Callback> {
cb: &'a RefCell<&'a mut Callback>,
}
impl<Callback> const TopologicalSortOutputSink<Vertex> for MyTopologicalSortOutputSink<'_, Callback>
where
Callback: ~const SorterCallback,
{
fn push(&mut self, v: Vertex) {
if let Vertex::BindInit(bind_i) = v {
self.cb.borrow_mut().push_bind_order(bind_i)
}
}
}
struct MyVertexInfoMap<'a> {
executable_info: TopologicalSortVertexInfo,
temp_binds2: &'a mut [SorterBindInfo2],
}
impl<'a> const Index<&'a Vertex> for MyVertexInfoMap<'_> {
type Output = TopologicalSortVertexInfo;
fn index(&self, index: &'a Vertex) -> &Self::Output {
match *index {
Vertex::Executable => &self.executable_info,
Vertex::BindInit(bind_i) => &self.temp_binds2[bind_i].temp_sort[0],
Vertex::BindDisown(bind_i) => &self.temp_binds2[bind_i].temp_sort[1],
}
}
}
impl<'a> const IndexMut<&'a Vertex> for MyVertexInfoMap<'_> {
fn index_mut(&mut self, index: &'a Vertex) -> &mut Self::Output {
match *index {
Vertex::Executable => &mut self.executable_info,
Vertex::BindInit(bind_i) => &mut self.temp_binds2[bind_i].temp_sort[0],
Vertex::BindDisown(bind_i) => &mut self.temp_binds2[bind_i].temp_sort[1],
}
}
}
let cb = RefCell::new(cb);
if topological_sort(
&Graph {
temp_binds1,
cb: &cb,
temp_uses,
},
&mut Vertex::lt,
&mut MyTopologicalSortOutputSink { cb: &cb },
temp_vertices,
&mut MyVertexInfoMap {
executable_info: Init::INIT,
temp_binds2,
},
) {
return;
}
cb.borrow_mut()
.report_error(SorterError::BindCycle { bind_is: &[] });
}
#[const_trait]
trait MyIterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
#[const_trait]
trait GraphAccess<VertexRef> {
type VertexIter<'a>: ~const MyIterator<Item = VertexRef> + ~const Destruct + 'a
where
Self: 'a;
fn vertices(&self) -> Self::VertexIter<'_>;
type SuccessorIter<'a>: ~const MyIterator<Item = VertexRef> + ~const Destruct + 'a
where
Self: 'a;
fn successors(&self, v: &VertexRef) -> Self::SuccessorIter<'_>;
}
#[derive(Clone, Copy)]
struct TopologicalSortVertexInfo {
num_predecessors: usize,
}
impl Init for TopologicalSortVertexInfo {
const INIT: Self = Self {
num_predecessors: 0,
};
}
#[const_trait]
trait TopologicalSortOutputSink<VertexRef> {
fn push(&mut self, v: VertexRef);
}
const fn topological_sort<
'a,
Graph,
VertexRef,
VertexRefLessThan,
ReadyVertexQueue,
VertexInfoMap,
OutputSink,
>(
graph: &'a Graph,
vertex_ord_lt: &mut VertexRefLessThan,
out_vertices: &mut OutputSink,
temp_ready_vertex_queue: &mut ReadyVertexQueue,
temp_vertex_info: &mut VertexInfoMap,
) -> bool
where
Graph: ~const GraphAccess<VertexRef>,
VertexRef: Copy + ~const Destruct,
VertexRefLessThan: ~const FnMut(&VertexRef, &VertexRef) -> bool,
ReadyVertexQueue: ~const VecLike<Element = VertexRef>,
for<'index> VertexInfoMap: ~const Index<&'index VertexRef, Output = TopologicalSortVertexInfo>
+ ~const IndexMut<&'index VertexRef>,
OutputSink: ~const TopologicalSortOutputSink<VertexRef>,
{
assert!(temp_ready_vertex_queue.is_empty());
struct ReadyVertexQueueBinaryHeapCtx<'a, VertexRefLessThan> {
vertex_ord_lt: &'a mut VertexRefLessThan,
}
impl<VertexRefLessThan, VertexRef> const BinaryHeapCtx<VertexRef>
for ReadyVertexQueueBinaryHeapCtx<'_, VertexRefLessThan>
where
VertexRefLessThan: ~const FnMut(&VertexRef, &VertexRef) -> bool,
{
fn lt(&mut self, x: &VertexRef, y: &VertexRef) -> bool {
(self.vertex_ord_lt)(x, y)
}
}
let mut num_vertices_remaining = 0;
{
let mut it_vertices = graph.vertices();
while let Some(v) = it_vertices.next() {
temp_vertex_info[&v] = TopologicalSortVertexInfo {
num_predecessors: 0,
};
num_vertices_remaining += 1;
}
}
{
let mut it_vertices = graph.vertices();
while let Some(v) = it_vertices.next() {
let mut it_successors = graph.successors(&v);
while let Some(successor) = it_successors.next() {
temp_vertex_info[&successor].num_predecessors += 1;
}
}
}
{
let mut it_vertices = graph.vertices();
while let Some(v) = it_vertices.next() {
if temp_vertex_info[&v].num_predecessors == 0 {
temp_ready_vertex_queue
.heap_push(v, ReadyVertexQueueBinaryHeapCtx { vertex_ord_lt });
}
}
}
while let Some(v) =
temp_ready_vertex_queue.heap_pop(ReadyVertexQueueBinaryHeapCtx { vertex_ord_lt })
{
let mut it_successors = graph.successors(&v);
while let Some(successor) = it_successors.next() {
temp_vertex_info[&successor].num_predecessors -= 1;
if temp_vertex_info[&successor].num_predecessors == 0 {
temp_ready_vertex_queue
.heap_push(successor, ReadyVertexQueueBinaryHeapCtx { vertex_ord_lt });
}
}
out_vertices.push(v);
num_vertices_remaining -= 1;
}
num_vertices_remaining == 0
}