Skip to content

Commit

Permalink
types: introduce BoundRef type, use in place of Arc<BoundMutex> in un…
Browse files Browse the repository at this point in the history
…ion-bound

This introduces the BoundRef type, an opaque reference type which
currently just holds an Arc<BoundMutex>, but which in a later commit
will be tied to the type inference context and refer to data within the
context.

It currently preserves the get() and set() methods on BoundRef, which
come from BoundMutex, but these will need to be replaced in a later
commit, since eventually BoundRef by itself will not contain enough
information to update its data.

This is essentially an API-only change though it does move some data
structures around.
  • Loading branch information
apoelstra committed Jul 3, 2024
1 parent 021316c commit 33f58fa
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 31 deletions.
97 changes: 96 additions & 1 deletion src/types/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
use std::fmt;
use std::sync::{Arc, Mutex};

use super::{Bound, Error, Type};
use super::bound_mutex::BoundMutex;
use super::{Bound, Error, Final, Type};

/// Type inference context, or handle to a context.
///
Expand Down Expand Up @@ -55,6 +56,53 @@ impl Context {
}
}

/// Helper function to allocate a bound and return a reference to it.
fn alloc_bound(&self, bound: Bound) -> BoundRef {
BoundRef {
context: Arc::as_ptr(&self.slab),
index: Arc::new(BoundMutex::new(bound)),
}
}

/// Allocate a new free type bound, and return a reference to it.
pub fn alloc_free(&self, name: String) -> BoundRef {
self.alloc_bound(Bound::Free(name))
}

/// Allocate a new unit type bound, and return a reference to it.
pub fn alloc_unit(&self) -> BoundRef {
self.alloc_bound(Bound::Complete(Final::unit()))
}

/// Allocate a new unit type bound, and return a reference to it.
pub fn alloc_complete(&self, data: Arc<Final>) -> BoundRef {
self.alloc_bound(Bound::Complete(data))
}

/// Allocate a new sum-type bound, and return a reference to it.
///
/// # Panics
///
/// Panics if either of the child types are from a different inference context.
pub fn alloc_sum(&self, left: Type, right: Type) -> BoundRef {
left.bound.root().assert_matches_context(self);
right.bound.root().assert_matches_context(self);

self.alloc_bound(Bound::sum(left, right))
}

/// Allocate a new product-type bound, and return a reference to it.
///
/// # Panics
///
/// Panics if either of the child types are from a different inference context.
pub fn alloc_product(&self, left: Type, right: Type) -> BoundRef {
left.bound.root().assert_matches_context(self);
right.bound.root().assert_matches_context(self);

self.alloc_bound(Bound::product(left, right))
}

/// Creates a new handle to the context.
///
/// This handle holds a reference to the underlying context and will keep
Expand Down Expand Up @@ -90,3 +138,50 @@ impl Context {
ty1.unify(ty2, hint)
}
}

#[derive(Debug)]
pub struct BoundRef {
context: *const Mutex<Vec<Bound>>,
// Will become an index into the context in a latter commit, but for
// now we set it to an Arc<BoundMutex> to preserve semantics.
index: Arc<BoundMutex>,
}

impl BoundRef {
pub fn assert_matches_context(&self, ctx: &Context) {
assert_eq!(
self.context,
Arc::as_ptr(&ctx.slab),
"bound was accessed from a type inference context that did not create it",
);
}

pub fn get(&self) -> Arc<Bound> {
self.index.get()
}

pub fn set(&self, new: Arc<Bound>) {
self.index.set(new)
}

pub fn bind(&self, bound: Arc<Bound>, hint: &'static str) -> Result<(), Error> {
self.index.bind(bound, hint)
}
}

impl super::PointerLike for BoundRef {
fn ptr_eq(&self, other: &Self) -> bool {
debug_assert_eq!(
self.context, other.context,
"tried to compare two bounds from different inference contexts"
);
Arc::ptr_eq(&self.index, &other.index)
}

fn shallow_clone(&self) -> Self {
BoundRef {
context: self.context,
index: Arc::clone(&self.index),
}
}
}
53 changes: 23 additions & 30 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
//! or a sum or product of other types.
//!

use self::union_bound::UbElement;
use self::union_bound::{PointerLike, UbElement};
use crate::dag::{Dag, DagLike, NoSharing};
use crate::Tmr;

Expand All @@ -85,7 +85,7 @@ mod precomputed;
mod union_bound;
mod variable;

pub use context::Context;
pub use context::{BoundRef, Context};
pub use final_data::{CompleteBound, Final};

/// Error type for simplicity
Expand Down Expand Up @@ -285,14 +285,6 @@ impl Bound {
self.clone()
}

fn free(name: String) -> Self {
Bound::Free(name)
}

fn unit() -> Self {
Bound::Complete(Final::unit())
}

fn sum(a: Type, b: Type) -> Self {
if let (Some(adata), Some(bdata)) = (a.final_data(), b.final_data()) {
Bound::Complete(Final::sum(adata, bdata))
Expand Down Expand Up @@ -392,18 +384,22 @@ impl DagLike for Arc<Bound> {
pub struct Type {
/// A set of constraints, which maintained by the union-bound algorithm and
/// is progressively tightened as type inference proceeds.
bound: UbElement<Arc<bound_mutex::BoundMutex>>,
bound: UbElement<BoundRef>,
}

impl Type {
/// Return an unbound type with the given name
pub fn free(_: &Context, name: String) -> Self {
Type::from(Bound::free(name))
pub fn free(ctx: &Context, name: String) -> Self {
Type {
bound: UbElement::new(ctx.alloc_free(name)),
}
}

/// Create the unit type.
pub fn unit(_: &Context) -> Self {
Type::from(Bound::unit())
pub fn unit(ctx: &Context) -> Self {
Type {
bound: UbElement::new(ctx.alloc_unit()),
}
}

/// Create the type `2^(2^n)` for the given `n`.
Expand All @@ -414,18 +410,24 @@ impl Type {
}

/// Create the sum of the given `left` and `right` types.
pub fn sum(_: &Context, left: Self, right: Self) -> Self {
Type::from(Bound::sum(left, right))
pub fn sum(ctx: &Context, left: Self, right: Self) -> Self {
Type {
bound: UbElement::new(ctx.alloc_sum(left, right)),
}
}

/// Create the product of the given `left` and `right` types.
pub fn product(_: &Context, left: Self, right: Self) -> Self {
Type::from(Bound::product(left, right))
pub fn product(ctx: &Context, left: Self, right: Self) -> Self {
Type {
bound: UbElement::new(ctx.alloc_product(left, right)),
}
}

/// Create a complete type.
pub fn complete(_: &Context, final_data: Arc<Final>) -> Self {
Type::from(Bound::Complete(final_data))
pub fn complete(ctx: &Context, final_data: Arc<Final>) -> Self {
Type {
bound: UbElement::new(ctx.alloc_complete(final_data)),
}
}

/// Clones the `Type`.
Expand Down Expand Up @@ -580,15 +582,6 @@ impl fmt::Display for Type {
}
}

impl From<Bound> for Type {
/// Promotes a `Bound` to a type defined by that constraint
fn from(bound: Bound) -> Type {
Type {
bound: UbElement::new(Arc::new(bound_mutex::BoundMutex::new(bound))),
}
}
}

impl DagLike for Type {
type Node = Type;
fn data(&self) -> &Type {
Expand Down

0 comments on commit 33f58fa

Please sign in to comment.