Skip to content
Open
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
20 changes: 20 additions & 0 deletions src/node/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,26 @@ impl<C, X, W> Inner<C, X, W> {
}

impl<C, X: Disconnectable<C>, W> Inner<Arc<C>, X, W> {
/// Collapse the node information to a `Dag`
pub fn into_dag(self) -> Dag<Arc<C>> {
match self {
Inner::Iden
| Inner::Unit
| Inner::Witness(_)
| Inner::Fail(_)
| Inner::Jet(_)
| Inner::Word(_) => Dag::Nullary,
Inner::InjL(c)
| Inner::InjR(c)
| Inner::Take(c)
| Inner::Drop(c)
| Inner::AssertL(c, _)
| Inner::AssertR(_, c) => Dag::Unary(c),
Inner::Comp(cl, cr) | Inner::Case(cl, cr) | Inner::Pair(cl, cr) => Dag::Binary(cl, cr),
Inner::Disconnect(cl, cr) => cr.disconnect_dag_arc(cl),
}
}

/// Collapse the node information to a `Dag`
pub fn as_dag(&self) -> Dag<&C> {
match self {
Expand Down
28 changes: 28 additions & 0 deletions src/node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,34 @@ impl<N: Marker<Witness = Value>> Node<N> {
}
}

impl<N: Marker> Drop for Node<N> {
fn drop(&mut self) {
// Note: this is basically identical to the drop impl for types::Incomplete.
fn push_children<N: Marker>(
stack: &mut Vec<Arc<Node<N>>>,
inner: Inner<Arc<Node<N>>, N::Disconnect, N::Witness>,
) {
use crate::dag::Dag;
match inner.into_dag() {
Dag::Nullary => {}
Dag::Unary(child) => stack.push(child),
Dag::Binary(left, right) => {
stack.push(left);
stack.push(right);
}
}
}

let mut stack = Vec::new();
push_children(&mut stack, std::mem::replace(&mut self.inner, Inner::Unit));
while let Some(child) = stack.pop() {
if let Some(mut child) = Arc::into_inner(child) {
push_children(&mut stack, std::mem::replace(&mut child.inner, Inner::Unit));
}
}
}
}

#[cfg(test)]
#[cfg(all(feature = "test-utils", feature = "elements"))]
mod tests {
Expand Down
30 changes: 27 additions & 3 deletions src/types/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
//!

use std::any::TypeId;
use std::collections::HashMap;
use std::fmt;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex, MutexGuard};

use ghost_cell::GhostToken;

use crate::dag::{Dag, DagLike};
use crate::dag::{Dag, DagLike, SharingTracker};
use crate::jet::Jet;

use super::{
Expand Down Expand Up @@ -254,7 +255,7 @@ impl<'brand> Context<'brand> {
}
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct BoundRef<'brand> {
phantom: InvariantLifetime<'brand>,
index: usize,
Expand Down Expand Up @@ -285,7 +286,9 @@ impl super::PointerLike for BoundRef<'_> {
}
}

impl<'brand> DagLike for (&'_ Context<'brand>, BoundRef<'brand>) {
pub type CtxAndBoundRef<'ctx, 'brand> = (&'ctx Context<'brand>, BoundRef<'brand>);

impl<'brand> DagLike for CtxAndBoundRef<'_, 'brand> {
type Node = BoundRef<'brand>;
fn data(&self) -> &BoundRef<'brand> {
&self.1
Expand All @@ -303,6 +306,27 @@ impl<'brand> DagLike for (&'_ Context<'brand>, BoundRef<'brand>) {
}
}

#[derive(Clone, Debug, Default)]
pub struct BoundRefSharing<'brand> {
map: HashMap<BoundRef<'brand>, usize>,
}

impl<'brand> SharingTracker<CtxAndBoundRef<'_, 'brand>> for BoundRefSharing<'brand> {
fn record(&mut self, d: &CtxAndBoundRef<'_, 'brand>, index: usize) -> Option<usize> {
use std::collections::hash_map::Entry;
match self.map.entry(d.1.clone()) {
Entry::Occupied(occ) => Some(*occ.get()),
Entry::Vacant(vac) => {
vac.insert(index);
None
}
}
}
fn seen_before(&self, d: &CtxAndBoundRef<'_, 'brand>) -> Option<usize> {
self.map.get(&d.1).copied()
}
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct OccursCheckId<'brand> {
phantom: InvariantLifetime<'brand>,
Expand Down
95 changes: 93 additions & 2 deletions src/types/incomplete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
use crate::dag::{Dag, DagLike, NoSharing};
use crate::types::union_bound::PointerLike;

use super::context::BoundRefSharing;
use super::{Bound, BoundRef, Context};
use super::{MAX_DISPLAY_DEPTH, MAX_DISPLAY_LENGTH};

use std::fmt;
use std::sync::Arc;
Expand All @@ -31,6 +33,59 @@ pub enum Incomplete {
Final(Arc<super::Final>),
}

impl Incomplete {
/// Private helper function for `Incomplete::drop`. See `node::Inner::into_dag` which is
/// similar but doesn't require unsafe code.
fn into_dag(mut self) -> Dag<Arc<Self>> {
use core::{mem, ptr};
let ret = match &mut self {
Incomplete::Sum(ref mut left, ref mut right)
| Incomplete::Product(ref mut left, ref mut right) => {
// Because Rust is stupid, we cannot just move 'left' and 'right' out of 'self'.
// We get the error "cannot move out of type that implements Drop". This message
// dates to before Rust 1.0, before mem::forget was marked safe, and has no
// justification that stands up to any scrutiny. There have been mulitple RFCs to
// remove it but for some reason they have never gone anywhere. See for example
//
// https://internals.rust-lang.org/t/destructuring-droppable-structs/20993/41
//
// Anyway, instead we have to use unsafe code here to do this obviously-safe
// operation in an overcomplicated and hard-to-review way.
unsafe {
// SAFETY we are calling `ptr::read` on valid pointers (they come directly
// from references, which are always valid), and we will mem::forget their old
// locations before any early returns or panics.

let left = ptr::read(left);
let right = ptr::read(right);
Dag::Binary(left, right)
}
}
Incomplete::Cycle => Dag::Nullary,
Incomplete::Free(s) => {
// SAFETY: see above. We are manually dropping `s` here, which we have to do
// by reading it out of &mut self, because Rust is stupid.
unsafe {
ptr::read(s);
}
Dag::Nullary
}
Incomplete::Final(fin) => {
// SAFETY: see above
unsafe {
ptr::read(fin);
}
Dag::Nullary
}
};
// Because `Incomplete::drop` calls this method, we cannot allow `self` to be dropped
// under any circumstances, or else we will infinitely recurse and stack-overflow. (This
// is why we had to do ptr::read in every branch above).
mem::forget(self);
ret
}
}

impl DagLike for &'_ Incomplete {
type Node = Incomplete;
fn data(&self) -> &Incomplete {
Expand All @@ -55,7 +110,18 @@ impl fmt::Debug for Incomplete {
impl fmt::Display for Incomplete {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut skip_next = false;
for data in self.verbose_pre_order_iter::<NoSharing>(None) {
for data in self.verbose_pre_order_iter::<NoSharing>(Some(MAX_DISPLAY_DEPTH)) {
if data.index > MAX_DISPLAY_LENGTH {
write!(f, "... [truncated type after {} nodes]", MAX_DISPLAY_LENGTH)?;
return Ok(());
}
if data.depth == MAX_DISPLAY_DEPTH {
if data.n_children_yielded == 0 {
f.write_str("...")?;
}
continue;
}

if skip_next {
skip_next = false;
continue;
Expand Down Expand Up @@ -92,6 +158,31 @@ impl fmt::Display for Incomplete {
}
}

impl Drop for Incomplete {
fn drop(&mut self) {
// Note: this is basically identical to the drop impl for node::Node.
fn push_children(stack: &mut Vec<Arc<Incomplete>>, inner: Incomplete) {
use crate::dag::Dag;
match inner.into_dag() {
Dag::Nullary => {}
Dag::Unary(child) => stack.push(child),
Dag::Binary(left, right) => {
stack.push(left);
stack.push(right);
}
}
}

let mut stack = Vec::new();
push_children(&mut stack, std::mem::replace(self, Incomplete::Cycle));
while let Some(child) = stack.pop() {
if let Some(mut child) = Arc::into_inner(child) {
push_children(&mut stack, std::mem::replace(&mut child, Incomplete::Cycle));
}
}
}
}

impl Incomplete {
/// Whether this "incomplete bound" is the unit type.
pub fn is_unit(&self) -> bool {
Expand Down Expand Up @@ -168,7 +259,7 @@ impl Incomplete {
// Now that we know our bound has finite size, we can safely use a
// post-order iterator on it.
let mut finalized = vec![];
for data in (ctx, bound_ref).post_order_iter::<NoSharing>() {
for data in (ctx, bound_ref).post_order_iter::<BoundRefSharing<'_>>() {
let bound_get = data.node.0.get(&data.node.1);
let final_data = match bound_get {
Bound::Free(s) => Incomplete::Free(s),
Expand Down
Loading