rue_typing/
semantic_types.rs

1use indexmap::{IndexMap, IndexSet};
2use num_bigint::BigInt;
3
4use crate::{Comparison, Type, TypeId, TypeSystem};
5
6/// Allows you to map generic types lazily without having to resolve them immediately.
7/// This prevents stack overflows when resolving generic type definitions that reference themselves.
8/// When reducing a type to its structural form, lazy types should be removed.
9#[derive(Debug, Clone)]
10pub struct Lazy {
11    pub type_id: TypeId,
12    pub substitutions: IndexMap<TypeId, TypeId>,
13}
14
15/// Represents an alias to a type with a set of generic parameters that must be mapped prior to use.
16#[derive(Debug, Clone)]
17pub struct Alias {
18    /// A pointer to the alias from which this was derived.
19    pub original_type_id: TypeId,
20    pub type_id: TypeId,
21    pub generic_types: Vec<TypeId>,
22}
23
24/// Struct types are just wrappers around a structural type that provide field information.
25#[derive(Debug, Clone)]
26pub struct Struct {
27    /// A pointer to the struct from which this was derived.
28    pub original_type_id: TypeId,
29    pub field_names: IndexSet<String>,
30    pub type_id: TypeId,
31    pub nil_terminated: bool,
32    pub generic_types: Vec<TypeId>,
33}
34
35/// Represents something which can be called with arguments and returns a given type.
36#[derive(Debug, Clone)]
37pub struct Callable {
38    /// A pointer to the callable from which this was derived.
39    pub original_type_id: TypeId,
40    pub parameter_names: IndexSet<String>,
41    pub parameters: TypeId,
42    pub return_type: TypeId,
43    pub nil_terminated: bool,
44    pub generic_types: Vec<TypeId>,
45}
46
47/// Represents an enum type which can have multiple variants.
48#[derive(Debug, Clone)]
49pub struct Enum {
50    /// A pointer to the enum from which this was derived.
51    pub original_type_id: TypeId,
52    /// The structural type of the enum.
53    pub type_id: TypeId,
54    /// Whether the enum semantically has fields.
55    pub has_fields: bool,
56    /// This is a map of the original variant names to their type ids.
57    pub variants: IndexMap<String, TypeId>,
58}
59
60/// Represents a variant type which can optionally have fields.
61#[derive(Debug, Clone)]
62pub struct Variant {
63    /// A pointer to the variant from which this was derived.
64    pub original_type_id: TypeId,
65    /// The original enum type to which this variant belongs.
66    pub original_enum_type_id: TypeId,
67    /// The field names of the variant.
68    pub field_names: Option<IndexSet<String>>,
69    /// The structural type of the enum variant.
70    pub type_id: TypeId,
71    /// Whether the chain of cons pairs is nil terminated.
72    pub nil_terminated: bool,
73    /// The generic types of the variant.
74    pub generic_types: Vec<TypeId>,
75    /// The discriminant value.
76    pub discriminant: BigInt,
77}
78
79/// Constructs a structural type consisting of the items in a list.
80pub fn construct_items(
81    db: &mut TypeSystem,
82    items: impl DoubleEndedIterator<Item = TypeId>,
83    nil_terminated: bool,
84) -> TypeId {
85    let mut result = db.std().nil;
86    for (i, item) in items.rev().enumerate() {
87        if i == 0 && !nil_terminated {
88            result = item;
89            continue;
90        }
91        result = db.alloc(Type::Pair(item, result));
92    }
93    result
94}
95
96/// Deconstructs a structural type into a list of items and a rest value.
97pub fn deconstruct_items(
98    db: &mut TypeSystem,
99    type_id: TypeId,
100    length: usize,
101    nil_terminated: bool,
102) -> Option<Vec<TypeId>> {
103    let mut items = Vec::with_capacity(length);
104    let mut current = type_id;
105
106    for i in (0..length).rev() {
107        if i == 0 {
108            if !nil_terminated {
109                items.push(current);
110                break;
111            }
112
113            let (first, rest) = db.get_pair(current)?;
114            items.push(first);
115            if db.compare(rest, db.std().nil) > Comparison::Equal {
116                return None;
117            }
118            break;
119        }
120
121        let (first, rest) = db.get_pair(current)?;
122        items.push(first);
123        current = rest;
124    }
125
126    Some(items)
127}
128
129/// Unwraps a list type into its inner type.
130pub fn unwrap_list(db: &mut TypeSystem, type_id: TypeId) -> Option<TypeId> {
131    if db.compare(db.std().nil, type_id) > Comparison::Assignable {
132        return None;
133    }
134
135    let non_nil = db.difference(type_id, db.std().nil);
136    let (first, rest) = db.get_pair(non_nil)?;
137
138    if db.compare(rest, type_id) > Comparison::Assignable {
139        return None;
140    }
141
142    Some(first)
143}
144
145#[cfg(test)]
146mod tests {
147    use crate::alloc_list;
148
149    use super::*;
150
151    #[test]
152    fn test_construct_int_nil() {
153        let mut db = TypeSystem::new();
154        let std = db.std();
155        let type_id = construct_items(&mut db, [std.int].into_iter(), true);
156        let items = deconstruct_items(&mut db, type_id, 1, true);
157        assert_eq!(items, Some(vec![std.int]));
158    }
159
160    #[test]
161    fn test_construct_int() {
162        let mut db = TypeSystem::new();
163        let std = db.std();
164        let type_id = construct_items(&mut db, [std.int].into_iter(), false);
165        let items = deconstruct_items(&mut db, type_id, 1, false);
166        assert_eq!(items, Some(vec![std.int]));
167    }
168
169    #[test]
170    fn test_construct_empty_nil() {
171        let mut db = TypeSystem::new();
172        let type_id = construct_items(&mut db, [].into_iter(), true);
173        let items = deconstruct_items(&mut db, type_id, 0, true);
174        assert_eq!(items, Some(vec![]));
175    }
176
177    #[test]
178    fn test_construct_empty() {
179        let mut db = TypeSystem::new();
180        let type_id = construct_items(&mut db, [].into_iter(), false);
181        let items = deconstruct_items(&mut db, type_id, 0, false);
182        assert_eq!(items, Some(vec![]));
183    }
184
185    #[test]
186    fn test_construct_int_int_nil() {
187        let mut db = TypeSystem::new();
188        let std = db.std();
189        let type_id = construct_items(&mut db, [std.int, std.int].into_iter(), true);
190        let items = deconstruct_items(&mut db, type_id, 2, true);
191        assert_eq!(items, Some(vec![std.int, std.int]));
192    }
193
194    #[test]
195    fn test_construct_int_int() {
196        let mut db = TypeSystem::new();
197        let std = db.std();
198        let type_id = construct_items(&mut db, [std.int, std.int].into_iter(), false);
199        let items = deconstruct_items(&mut db, type_id, 2, false);
200        assert_eq!(items, Some(vec![std.int, std.int]));
201    }
202
203    #[test]
204    fn test_construct_bytes32_pair_nil() {
205        let mut db = TypeSystem::new();
206        let std = db.std();
207        let pair = db.alloc(Type::Pair(std.bytes32, std.nil));
208        let type_id = construct_items(&mut db, [std.bytes32, pair].into_iter(), true);
209        let items = deconstruct_items(&mut db, type_id, 2, true);
210        assert_eq!(items, Some(vec![std.bytes32, pair]));
211    }
212
213    #[test]
214    fn test_construct_bytes32_pair() {
215        let mut db = TypeSystem::new();
216        let std = db.std();
217        let pair = db.alloc(Type::Pair(std.bytes32, std.nil));
218        let type_id = construct_items(&mut db, [std.bytes32, pair].into_iter(), false);
219        let items = deconstruct_items(&mut db, type_id, 2, false);
220        assert_eq!(items, Some(vec![std.bytes32, pair]));
221    }
222
223    #[test]
224    fn test_unwrap_list() {
225        let mut db = TypeSystem::new();
226        let std = db.std();
227        let list = alloc_list(&mut db, std.public_key);
228        assert_eq!(unwrap_list(&mut db, list), Some(std.public_key));
229    }
230}