Skip to main content

simplicity/types/
mod.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Types and Type Inference
4//!
5//! Every Simplicity expression has two types associated with it: a source and
6//! a target. We refer to this pair of types as an "arrow". The types are
7//! inferred from the structure of the program.
8//!
9//! Simplicity types are one of three things
10//!   * A unit type, which has one value
11//!   * A sum of two other types
12//!   * A product of two other types
13//!
14//! During type inference, types are initially "free", meaning that there are
15//! no constraints on what they will eventually be. The program structure then
16//! imposes additional constraints (for example, the `comp` combinator requires
17//! that its left child's target type be the same as its right child's source
18//! type), and by unifying all these constraints, all types can be inferred.
19//!
20//! Type inference is done progressively during construction of Simplicity
21//! expressions. It is completed by the [`Type::finalize`] method, which
22//! recursively completes types by setting any remaining free variables to unit.
23//! If any type constraints are incompatible with each other (e.g. a type is
24//! bound to be both a product and a sum type) then inference fails at that point
25//! by returning an error.
26//!
27//! In addition to completing types [`Type::finalize`], does one additional
28//! check, the "occurs check", to ensures that there are no infinitely-sized
29//! types. Such types occur when a type has itself as a child, are illegal in
30//! Simplicity, and could not be represented by our data structures.
31//!
32
33// In this module, during inference types are characterized by their [`Bound`],
34// which describes the constraints on the type. The bound of a type can be
35// obtained by the [`Type::bound`] method, and is an enum with four variants:
36//
37// * [`Bound::Free`] means that the type has no constraints; it is a free
38//   variable. The type has a name which can be used to identify it in error
39//   messages.
40// * [`Bound::Sum`] and [`Bound::Product`] means that the the type is a sum
41//   (resp. product) of two other types, which are characterized by their
42//   own bounds.
43// * [`Bound::Complete`] means that the type has no free variables at all,
44//   and has an already-computed [`Final`] structure suitable for use in
45//   contexts that require complete types. (Unit types are always complete,
46//   and therefore use this variant rather than getting their own.)
47//
48// During inference, it is possible for a type to be complete, in the sense
49// of having no free variables, without its bound being [`Bound::Complete`].
50// This occurs, for example, if a type is a sum of two incomplete types, then
51// the child types are completed during type inference on an unrelated part
52// of the type hierarchy. The type would then have a [`Bound::Sum`] with two
53// children, both of which are complete.
54//
55// The inference engine makes an effort to notice when this happens and set
56// the bound of complete types to [`Bound::Complete`], but since type inference
57// is inherently non-local this cannot always be done.
58//
59// When the distinction matters, we say a type is "finalized" only if its bound
60// is `Complete` and "complete" if it has no free variables. But the distinction
61// usually does not matter, so we prefer to use the word "complete".
62//
63// There are three main types in this module:
64//   * [`Type`] is the main type representing a Simplicity type, whether it is
65//     complete or not. Its main methods are [`Type::bound`] which returns the
66//     current state of the type and [`Type::bind`] which adds a new constraint
67//     to the type.
68//   * `Final` is a mutex-free structure that can be obtained from a complete
69//     type. It includes the TMR and the complete bound describing the type.
70//   * `Bound` defines the structure of a type: whether it is free, complete,
71//     or a sum or product of other types.
72//
73
74use self::union_bound::{PointerLike, UbElement, WithGhostToken};
75use crate::dag::{DagLike, NoSharing};
76use crate::Tmr;
77
78use std::fmt;
79use std::sync::Arc;
80
81pub mod arrow;
82mod context;
83mod final_data;
84mod incomplete;
85mod precomputed;
86mod union_bound;
87mod variable;
88
89pub use arrow::{Arrow, FinalArrow};
90pub use context::{BoundRef, Context};
91pub use final_data::{CompleteBound, Final, TypeTooLargeError};
92pub use incomplete::Incomplete;
93
94/// Error type for simplicity
95#[non_exhaustive]
96#[derive(Clone, Debug)]
97pub enum Error {
98    /// An attempt to bind a type conflicted with an existing bound on the type
99    Bind {
100        existing_bound: Arc<Incomplete>,
101        new_bound: Arc<Incomplete>,
102        hint: &'static str,
103    },
104    /// Two unequal complete types were attempted to be unified
105    CompleteTypeMismatch {
106        type1: Arc<Final>,
107        type2: Arc<Final>,
108        hint: &'static str,
109    },
110    /// A type is recursive (i.e., occurs within itself), violating the "occurs check"
111    OccursCheck { infinite_bound: Arc<Incomplete> },
112    /// Attempted to combine two nodes which had different type inference
113    /// contexts. This is probably a programming error.
114    InferenceContextMismatch,
115}
116
117impl fmt::Display for Error {
118    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119        match self {
120            Error::Bind {
121                ref existing_bound,
122                ref new_bound,
123                hint,
124            } => {
125                write!(
126                    f,
127                    "failed to apply bound `{}` to existing bound `{}`: {}",
128                    new_bound, existing_bound, hint,
129                )
130            }
131            Error::CompleteTypeMismatch {
132                ref type1,
133                ref type2,
134                hint,
135            } => {
136                write!(
137                    f,
138                    "attempted to unify unequal types `{}` and `{}`: {}",
139                    type1, type2, hint,
140                )
141            }
142            Error::OccursCheck { infinite_bound } => {
143                write!(f, "infinitely-sized type {}", infinite_bound,)
144            }
145            Error::InferenceContextMismatch => {
146                f.write_str("attempted to combine two nodes with different type inference contexts")
147            }
148        }
149    }
150}
151
152impl std::error::Error for Error {}
153
154/// The state of a [`Type`] based on all constraints currently imposed on it.
155#[derive(Clone)]
156enum Bound<'brand> {
157    /// Fully-unconstrained type
158    Free(String),
159    /// Fully-constrained (i.e. complete) type, which has no free variables.
160    Complete(Arc<Final>),
161    /// A sum of two other types
162    Sum(TypeInner<'brand>, TypeInner<'brand>),
163    /// A product of two other types
164    Product(TypeInner<'brand>, TypeInner<'brand>),
165}
166
167impl Bound<'_> {
168    /// Clones the `Bound`.
169    ///
170    /// This is the same as just calling `.clone()` but has a different name to
171    /// emphasize that what's being cloned is (at most) a pair of ref-counted
172    /// pointers.
173    pub fn shallow_clone(&self) -> Self {
174        self.clone()
175    }
176}
177
178/// Source or target type of a Simplicity expression.
179///
180/// Internally this type is essentially just a refcounted pointer; it is
181/// therefore quite cheap to clone, but be aware that cloning will not
182/// actually create a new independent type, just a second pointer to the
183/// first one.
184#[derive(Clone)]
185pub struct Type<'brand> {
186    /// Handle to the type context.
187    ctx: Context<'brand>,
188    /// The actual contents of the type.
189    inner: TypeInner<'brand>,
190}
191
192#[derive(Clone)]
193struct TypeInner<'brand> {
194    /// A set of constraints, which maintained by the union-bound algorithm and
195    /// is progressively tightened as type inference proceeds.
196    bound: UbElement<'brand, BoundRef<'brand>>,
197}
198
199impl TypeInner<'_> {
200    fn shallow_clone(&self) -> Self {
201        self.clone()
202    }
203}
204
205impl<'brand> Type<'brand> {
206    /// Return an unbound type with the given name
207    pub fn free(ctx: &Context<'brand>, name: String) -> Self {
208        Self::wrap_bound(ctx, ctx.alloc_free(name))
209    }
210
211    /// Create the unit type.
212    pub fn unit(ctx: &Context<'brand>) -> Self {
213        Self::wrap_bound(ctx, ctx.alloc_unit())
214    }
215
216    /// Create the type `2^(2^n)` for the given `n`.
217    ///
218    /// The type is precomputed and fast to access.
219    pub fn two_two_n(ctx: &Context<'brand>, n: usize) -> Self {
220        Self::complete(ctx, precomputed::nth_power_of_2(n))
221    }
222
223    /// Create the sum of the given `left` and `right` types.
224    pub fn sum(ctx: &Context<'brand>, left: Self, right: Self) -> Self {
225        Self::wrap_bound(ctx, ctx.alloc_sum(left, right))
226    }
227
228    /// Create the product of the given `left` and `right` types.
229    pub fn product(ctx: &Context<'brand>, left: Self, right: Self) -> Self {
230        Self::wrap_bound(ctx, ctx.alloc_product(left, right))
231    }
232
233    /// Create a complete type.
234    pub fn complete(ctx: &Context<'brand>, final_data: Arc<Final>) -> Self {
235        Self::wrap_bound(ctx, ctx.alloc_complete(final_data))
236    }
237
238    fn wrap_bound(ctx: &Context<'brand>, bound: BoundRef<'brand>) -> Self {
239        Type {
240            ctx: ctx.shallow_clone(),
241            inner: TypeInner {
242                bound: UbElement::new(bound),
243            },
244        }
245    }
246
247    /// Clones the `Type`.
248    ///
249    /// This is the same as just calling `.clone()` but has a different name to
250    /// emphasize that what's being cloned is merely a ref-counted pointer.
251    pub fn shallow_clone(&self) -> Self {
252        self.clone()
253    }
254
255    /// Accessor for the TMR of this type, if it is final
256    pub fn tmr(&self) -> Option<Tmr> {
257        self.final_data().map(|data| data.tmr())
258    }
259
260    /// Accessor for the data of this type, if it is complete
261    pub fn final_data(&self) -> Option<Arc<Final>> {
262        let root = self.ctx.get_root_ref(&self.inner.bound);
263        let bound = self.ctx.get(&root);
264        if let Bound::Complete(ref data) = bound {
265            Some(Arc::clone(data))
266        } else {
267            None
268        }
269    }
270
271    /// Whether this type is known to be final
272    ///
273    /// During type inference this may be false even though the type is, in fact,
274    /// complete, since its children may have been unified to a complete type. To
275    /// ensure a type is complete, call [`Type::finalize`].
276    pub fn is_final(&self) -> bool {
277        self.final_data().is_some()
278    }
279
280    /// Converts a type as-is to an `Incomplete` type for use in an error.
281    pub fn to_incomplete(&self) -> Arc<Incomplete> {
282        let root = self.ctx.get_root_ref(&self.inner.bound);
283        Incomplete::from_bound_ref(&self.ctx, root)
284    }
285
286    /// Attempts to finalize the type. Returns its TMR on success.
287    pub fn finalize(&self) -> Result<Arc<Final>, Error> {
288        let root = self.ctx.get_root_ref(&self.inner.bound);
289        let bound = self.ctx.get(&root);
290        if let Bound::Complete(ref data) = bound {
291            return Ok(Arc::clone(data));
292        }
293
294        // First, do occurs-check to ensure that we have no infinitely sized types.
295        if let Some(infinite_bound) = Incomplete::occurs_check(&self.ctx, root.shallow_clone()) {
296            return Err(Error::OccursCheck { infinite_bound });
297        }
298
299        // Now that we know our types have finite size, we can safely use a
300        // post-order iterator to finalize them.
301        let mut finalized = vec![];
302        for data in (&self.ctx, root).post_order_iter::<NoSharing>() {
303            let bound_get = data.node.0.get(&data.node.1);
304            let final_data = match bound_get {
305                Bound::Free(_) => Final::unit(),
306                Bound::Complete(ref arc) => Arc::clone(arc),
307                Bound::Sum(..) => Final::sum(
308                    Arc::clone(&finalized[data.left_index.unwrap()]),
309                    Arc::clone(&finalized[data.right_index.unwrap()]),
310                ),
311                Bound::Product(..) => Final::product(
312                    Arc::clone(&finalized[data.left_index.unwrap()]),
313                    Arc::clone(&finalized[data.right_index.unwrap()]),
314                ),
315            };
316
317            if !matches!(bound_get, Bound::Complete(..)) {
318                self.ctx
319                    .reassign_non_complete(data.node.1, Bound::Complete(Arc::clone(&final_data)));
320            }
321            finalized.push(final_data);
322        }
323        Ok(finalized.pop().unwrap())
324    }
325
326    /// Return a vector containing the types 2^(2^i) for i from 0 to n-1.
327    pub fn powers_of_two(ctx: &Context<'brand>, n: usize) -> Vec<Self> {
328        let mut ret = Vec::with_capacity(n);
329
330        let unit = Type::unit(ctx);
331        let mut two = Type::sum(ctx, unit.shallow_clone(), unit);
332        for _ in 0..n {
333            ret.push(two.shallow_clone());
334            two = Type::product(ctx, two.shallow_clone(), two);
335        }
336        ret
337    }
338}
339
340const MAX_DISPLAY_DEPTH: usize = 64;
341const MAX_DISPLAY_LENGTH: usize = 10000;
342
343impl fmt::Debug for Type<'_> {
344    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
345        let root = self.ctx.get_root_ref(&self.inner.bound);
346        for data in (&self.ctx, root).verbose_pre_order_iter::<NoSharing>(Some(MAX_DISPLAY_DEPTH)) {
347            if data.index > MAX_DISPLAY_LENGTH {
348                write!(f, "... [truncated type after {} nodes]", MAX_DISPLAY_LENGTH)?;
349                return Ok(());
350            }
351            if data.depth == MAX_DISPLAY_DEPTH {
352                if data.n_children_yielded == 0 {
353                    f.write_str("...")?;
354                }
355                continue;
356            }
357            let bound = data.node.0.get(&data.node.1);
358            match (bound, data.n_children_yielded) {
359                (Bound::Free(ref s), _) => f.write_str(s)?,
360                (Bound::Complete(ref comp), _) => fmt::Debug::fmt(comp, f)?,
361                (Bound::Sum(..), 0) | (Bound::Product(..), 0) => {
362                    if data.index > 0 {
363                        f.write_str("(")?;
364                    }
365                }
366                (Bound::Sum(..), 2) | (Bound::Product(..), 2) => {
367                    if data.index > 0 {
368                        f.write_str(")")?
369                    }
370                }
371                (Bound::Sum(..), _) => f.write_str(" + ")?,
372                (Bound::Product(..), _) => f.write_str(" × ")?,
373            }
374        }
375        Ok(())
376    }
377}
378
379impl fmt::Display for Type<'_> {
380    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
381        let root = self.ctx.get_root_ref(&self.inner.bound);
382        for data in (&self.ctx, root).verbose_pre_order_iter::<NoSharing>(Some(MAX_DISPLAY_DEPTH)) {
383            if data.index > MAX_DISPLAY_LENGTH {
384                write!(f, "... [truncated type after {} nodes]", MAX_DISPLAY_LENGTH)?;
385                return Ok(());
386            }
387            if data.depth == MAX_DISPLAY_DEPTH {
388                if data.n_children_yielded == 0 {
389                    f.write_str("...")?;
390                }
391                continue;
392            }
393            let bound = data.node.0.get(&data.node.1);
394            match (bound, data.n_children_yielded) {
395                (Bound::Free(ref s), _) => f.write_str(s)?,
396                (Bound::Complete(ref comp), _) => fmt::Display::fmt(comp, f)?,
397                (Bound::Sum(..), 0) | (Bound::Product(..), 0) => {
398                    if data.index > 0 {
399                        f.write_str("(")?;
400                    }
401                }
402                (Bound::Sum(..), 2) | (Bound::Product(..), 2) => {
403                    if data.index > 0 {
404                        f.write_str(")")?
405                    }
406                }
407                (Bound::Sum(..), _) => f.write_str(" + ")?,
408                (Bound::Product(..), _) => f.write_str(" × ")?,
409            }
410        }
411        Ok(())
412    }
413}
414
415#[cfg(test)]
416mod tests {
417    use super::*;
418
419    use crate::jet::Core;
420    use crate::node::{ConstructNode, CoreConstructible as _};
421
422    #[test]
423    fn inference_failure() {
424        Context::with_context(|ctx| {
425            // unit: A -> 1
426            let unit = Arc::<ConstructNode>::unit(&ctx); // 1 -> 1
427
428            // Force unit to be 1->1
429            Arc::<ConstructNode>::comp(&unit, &unit).unwrap();
430
431            // take unit: 1 * B -> 1
432            let take_unit = Arc::<ConstructNode>::take(&unit); // 1*1 -> 1
433
434            // Pair will try to unify 1 and 1*B
435            Arc::<ConstructNode>::pair(&unit, &take_unit).unwrap_err();
436            // Trying to do it again should not work.
437            Arc::<ConstructNode>::pair(&unit, &take_unit).unwrap_err();
438        });
439    }
440
441    #[test]
442    fn memory_leak() {
443        Context::with_context(|ctx| {
444            let iden = Arc::<ConstructNode>::iden(&ctx);
445            let drop = Arc::<ConstructNode>::drop_(&iden);
446            let case = Arc::<ConstructNode>::case(&iden, &drop).unwrap();
447
448            let _ = format!("{:?}", case.arrow().source);
449        });
450    }
451
452    #[test]
453    fn check_jet_same_type_ok() {
454        Context::with_context(|ctx| {
455            let _ = Arc::<ConstructNode>::jet(&ctx, &Core::Add32);
456            let _ = Arc::<ConstructNode>::jet(&ctx, &Core::Subtract32);
457        });
458    }
459
460    #[cfg(feature = "elements")]
461    #[test]
462    #[should_panic(expected = "mixed jet types in context")]
463    fn check_jet_different_types_panics() {
464        use crate::jet::Elements;
465
466        Context::with_context(|ctx| {
467            let _ = Arc::<ConstructNode>::jet(&ctx, &Core::Add32);
468            let _ = Arc::<ConstructNode>::jet(&ctx, &Elements::Add32);
469        });
470    }
471}