Skip to content

Commit 79cee92

Browse files
committed
types: drop BoundMutex and instead use references into the type context slab
This completes the transition to using type contexts to keep track of (and allocate/mass-deallocate) type bounds :). There are three major improvements in this changeset: * We no longer leak memory when infinite type bounds are constructed. * It is no longer possible to create distinct programs where the variables are mixed up. (Ok, you can do this still, but you have to explicitly use the same type context for both programs, which is an obvious bug.) * Unification and binding happen atomically, so if you are doing type inference across multiple threads, crosstalk won't happen between them.
1 parent 3d7bd65 commit 79cee92

File tree

2 files changed

+39
-65
lines changed

2 files changed

+39
-65
lines changed

src/types/context.rs

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use std::sync::{Arc, Mutex, MutexGuard};
1919

2020
use crate::dag::{Dag, DagLike};
2121

22-
use super::bound_mutex::BoundMutex;
2322
use super::{Bound, CompleteBound, Error, Final, Type};
2423

2524
/// Type inference context, or handle to a context.
@@ -60,9 +59,13 @@ impl Context {
6059

6160
/// Helper function to allocate a bound and return a reference to it.
6261
fn alloc_bound(&self, bound: Bound) -> BoundRef {
62+
let mut lock = self.lock();
63+
lock.slab.push(bound);
64+
let index = lock.slab.len() - 1;
65+
6366
BoundRef {
6467
context: Arc::as_ptr(&self.slab),
65-
index: Arc::new(BoundMutex::new(bound)),
68+
index,
6669
}
6770
}
6871

@@ -132,7 +135,8 @@ impl Context {
132135
/// Panics if passed a `BoundRef` that was not allocated by this context.
133136
pub fn get(&self, bound: &BoundRef) -> Bound {
134137
bound.assert_matches_context(self);
135-
bound.index.get().shallow_clone()
138+
let lock = self.lock();
139+
lock.slab[bound.index].shallow_clone()
136140
}
137141

138142
/// Reassigns a bound to a different bound.
@@ -147,8 +151,8 @@ impl Context {
147151
///
148152
/// Also panics if passed a `BoundRef` that was not allocated by this context.
149153
pub fn reassign_non_complete(&self, bound: BoundRef, new: Bound) {
150-
bound.assert_matches_context(self);
151-
bound.index.set(new)
154+
let mut lock = self.lock();
155+
lock.reassign_non_complete(bound, new);
152156
}
153157

154158
/// Binds the type to a given bound. If this fails, attach the provided
@@ -157,15 +161,15 @@ impl Context {
157161
/// Fails if the type has an existing incompatible bound.
158162
pub fn bind(&self, existing: &Type, new: Bound, hint: &'static str) -> Result<(), Error> {
159163
let existing_root = existing.bound.root();
160-
let lock = self.lock();
164+
let mut lock = self.lock();
161165
lock.bind(existing_root, new, hint)
162166
}
163167

164168
/// Unify the type with another one.
165169
///
166170
/// Fails if the bounds on the two types are incompatible
167171
pub fn unify(&self, ty1: &Type, ty2: &Type, hint: &'static str) -> Result<(), Error> {
168-
let lock = self.lock();
172+
let mut lock = self.lock();
169173
lock.unify(ty1, ty2, hint)
170174
}
171175

@@ -180,9 +184,7 @@ impl Context {
180184
#[derive(Debug, Clone)]
181185
pub struct BoundRef {
182186
context: *const Mutex<Vec<Bound>>,
183-
// Will become an index into the context in a latter commit, but for
184-
// now we set it to an Arc<BoundMutex> to preserve semantics.
185-
index: Arc<BoundMutex>,
187+
index: usize,
186188
}
187189

188190
impl BoundRef {
@@ -200,7 +202,7 @@ impl BoundRef {
200202
pub fn occurs_check_id(&self) -> OccursCheckId {
201203
OccursCheckId {
202204
context: self.context,
203-
index: Arc::as_ptr(&self.index),
205+
index: self.index,
204206
}
205207
}
206208
}
@@ -211,13 +213,13 @@ impl super::PointerLike for BoundRef {
211213
self.context, other.context,
212214
"tried to compare two bounds from different inference contexts"
213215
);
214-
Arc::ptr_eq(&self.index, &other.index)
216+
self.index == other.index
215217
}
216218

217219
fn shallow_clone(&self) -> Self {
218220
BoundRef {
219221
context: self.context,
220-
index: Arc::clone(&self.index),
222+
index: self.index,
221223
}
222224
}
223225
}
@@ -243,7 +245,7 @@ pub struct OccursCheckId {
243245
context: *const Mutex<Vec<Bound>>,
244246
// Will become an index into the context in a latter commit, but for
245247
// now we set it to an Arc<BoundMutex> to preserve semantics.
246-
index: *const BoundMutex,
248+
index: usize,
247249
}
248250

249251
/// Structure representing an inference context with its slab allocator mutex locked.
@@ -255,17 +257,25 @@ struct LockedContext<'ctx> {
255257
}
256258

257259
impl<'ctx> LockedContext<'ctx> {
260+
fn reassign_non_complete(&mut self, bound: BoundRef, new: Bound) {
261+
assert!(
262+
!matches!(self.slab[bound.index], Bound::Complete(..)),
263+
"tried to modify finalized type",
264+
);
265+
self.slab[bound.index] = new;
266+
}
267+
258268
/// Unify the type with another one.
259269
///
260270
/// Fails if the bounds on the two types are incompatible
261-
fn unify(&self, existing: &Type, other: &Type, hint: &'static str) -> Result<(), Error> {
271+
fn unify(&mut self, existing: &Type, other: &Type, hint: &'static str) -> Result<(), Error> {
262272
existing.bound.unify(&other.bound, |x_bound, y_bound| {
263-
self.bind(x_bound, y_bound.index.get(), hint)
273+
self.bind(x_bound, self.slab[y_bound.index].shallow_clone(), hint)
264274
})
265275
}
266276

267-
fn bind(&self, existing: BoundRef, new: Bound, hint: &'static str) -> Result<(), Error> {
268-
let existing_bound = existing.index.get();
277+
fn bind(&mut self, existing: BoundRef, new: Bound, hint: &'static str) -> Result<(), Error> {
278+
let existing_bound = self.slab[existing.index].shallow_clone();
269279
let bind_error = || Error::Bind {
270280
existing_bound: existing_bound.shallow_clone(),
271281
new_bound: new.shallow_clone(),
@@ -278,7 +288,7 @@ impl<'ctx> LockedContext<'ctx> {
278288
// Free types are simply dropped and replaced by the new bound
279289
(Bound::Free(_), _) => {
280290
// Free means non-finalized, so set() is ok.
281-
existing.index.set(new);
291+
self.reassign_non_complete(existing, new);
282292
Ok(())
283293
}
284294
// Binding complete->complete shouldn't ever happen, but if so, we just
@@ -320,14 +330,17 @@ impl<'ctx> LockedContext<'ctx> {
320330
//
321331
// It also gives the user access to more information about the type,
322332
// prior to finalization.
323-
if let (Some(data1), Some(data2)) = (y1.final_data(), y2.final_data()) {
324-
existing
325-
.index
326-
.set(Bound::Complete(if let Bound::Sum(..) = existing_bound {
327-
Final::sum(data1, data2)
333+
let y1_bound = &self.slab[y1.bound.root().index];
334+
let y2_bound = &self.slab[y2.bound.root().index];
335+
if let (Bound::Complete(data1), Bound::Complete(data2)) = (y1_bound, y2_bound) {
336+
self.reassign_non_complete(
337+
existing,
338+
Bound::Complete(if let Bound::Sum(..) = existing_bound {
339+
Final::sum(Arc::clone(data1), Arc::clone(data2))
328340
} else {
329-
Final::product(data1, data2)
330-
}));
341+
Final::product(Arc::clone(data1), Arc::clone(data2))
342+
}),
343+
);
331344
}
332345
Ok(())
333346
}

src/types/mod.rs

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -148,45 +148,6 @@ impl fmt::Display for Error {
148148

149149
impl std::error::Error for Error {}
150150

151-
mod bound_mutex {
152-
use super::Bound;
153-
use std::fmt;
154-
use std::sync::Mutex;
155-
156-
/// Source or target type of a Simplicity expression
157-
pub struct BoundMutex {
158-
/// The type's status according to the union-bound algorithm.
159-
inner: Mutex<Bound>,
160-
}
161-
162-
impl fmt::Debug for BoundMutex {
163-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164-
self.get().fmt(f)
165-
}
166-
}
167-
168-
impl BoundMutex {
169-
pub fn new(bound: Bound) -> Self {
170-
BoundMutex {
171-
inner: Mutex::new(bound),
172-
}
173-
}
174-
175-
pub fn get(&self) -> Bound {
176-
self.inner.lock().unwrap().shallow_clone()
177-
}
178-
179-
pub fn set(&self, new: Bound) {
180-
let mut lock = self.inner.lock().unwrap();
181-
assert!(
182-
!matches!(*lock, Bound::Complete(..)),
183-
"tried to modify finalized type",
184-
);
185-
*lock = new;
186-
}
187-
}
188-
}
189-
190151
/// The state of a [`Type`] based on all constraints currently imposed on it.
191152
#[derive(Clone, Debug)]
192153
pub enum Bound {

0 commit comments

Comments
 (0)