diff --git a/crates/rue-compiler/src/compiler/item/type_alias_item.rs b/crates/rue-compiler/src/compiler/item/type_alias_item.rs index 60ffc28..86a9ef2 100644 --- a/crates/rue-compiler/src/compiler/item/type_alias_item.rs +++ b/crates/rue-compiler/src/compiler/item/type_alias_item.rs @@ -1,6 +1,7 @@ use rue_parser::TypeAliasItem; +use rue_typing::{Alias, Type, TypeId}; -use crate::{compiler::Compiler, value::Type, ErrorKind, TypeId}; +use crate::compiler::Compiler; impl Compiler<'_> { /// Define a type for an alias in the current scope, but leave it as unknown for now. @@ -22,22 +23,11 @@ impl Compiler<'_> { .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); // Set the alias type to the resolved type. - *self.db.ty_mut(alias_type_id) = Type::Alias(type_id); - - // A cycle between type aliases has been detected. - // We set it to unknown to prevent stack overflow issues later. - if self.db.is_cyclic(alias_type_id) { - let name = type_alias - .name() - .expect("the name should exist if it's in a cyclic reference"); - - self.db.error( - ErrorKind::RecursiveTypeAlias(name.to_string()), - name.text_range(), - ); - - *self.db.ty_mut(alias_type_id) = Type::Unknown; - } + *self.ty.get_mut(alias_type_id) = Type::Alias(Alias { + original_type_id: None, + type_id, + generic_types: Vec::new(), + }); self.type_definition_stack.pop().unwrap(); } diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index 73a1a4a..0f2f01a 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -3,15 +3,16 @@ use std::collections::HashSet; use num_bigint::BigInt; use num_traits::One; -use crate::{bigint_to_bytes, Enum, StandardTypes, Struct, Type, TypeId, TypeSystem, Variant}; +use crate::{bigint_to_bytes, Enum, Struct, Type, TypeId, TypeSystem, Variant}; pub(crate) fn difference_type( types: &mut TypeSystem, - std: &StandardTypes, lhs: TypeId, rhs: TypeId, visited: &mut HashSet<(TypeId, TypeId)>, ) -> TypeId { + let std = types.std(); + if !visited.insert((lhs, rhs)) { return lhs; } @@ -198,8 +199,8 @@ pub(crate) fn difference_type( let (lhs_first, lhs_rest) = (*lhs_first, *lhs_rest); let (rhs_first, rhs_rest) = (*rhs_first, *rhs_rest); - let first = difference_type(types, std, lhs_first, rhs_first, visited); - let rest = difference_type(types, std, lhs_rest, rhs_rest, visited); + let first = difference_type(types, lhs_first, rhs_first, visited); + let rest = difference_type(types, lhs_rest, rhs_rest, visited); if matches!(types.get(first), Type::Never) || matches!(types.get(first), Type::Never) { std.never @@ -216,7 +217,7 @@ pub(crate) fn difference_type( let mut result = Vec::new(); for item in &items { - let item = difference_type(types, std, *item, rhs, visited); + let item = difference_type(types, *item, rhs, visited); if matches!(types.get(item), Type::Never) { continue; } @@ -238,17 +239,17 @@ pub(crate) fn difference_type( let items = items.clone(); let mut lhs = lhs; for item in items { - lhs = difference_type(types, std, lhs, item, visited); + lhs = difference_type(types, lhs, item, visited); } lhs } - (Type::Alias(alias), _) => difference_type(types, std, alias.type_id, rhs, visited), - (_, Type::Alias(alias)) => difference_type(types, std, lhs, alias.type_id, visited), + (Type::Alias(alias), _) => difference_type(types, alias.type_id, rhs, visited), + (_, Type::Alias(alias)) => difference_type(types, lhs, alias.type_id, visited), (Type::Struct(ty), _) => { let ty = ty.clone(); - let type_id = difference_type(types, std, ty.type_id, rhs, visited); + let type_id = difference_type(types, ty.type_id, rhs, visited); types.alloc(Type::Struct(Struct { original_type_id: Some(ty.original_type_id.unwrap_or(lhs)), @@ -260,7 +261,7 @@ pub(crate) fn difference_type( } (_, Type::Struct(ty)) => { let ty = ty.clone(); - let type_id = difference_type(types, std, lhs, ty.type_id, visited); + let type_id = difference_type(types, lhs, ty.type_id, visited); types.alloc(Type::Struct(Struct { original_type_id: Some(ty.original_type_id.unwrap_or(rhs)), @@ -273,7 +274,7 @@ pub(crate) fn difference_type( (Type::Enum(ty), _) => { let ty = ty.clone(); - let type_id = difference_type(types, std, ty.type_id, rhs, visited); + let type_id = difference_type(types, ty.type_id, rhs, visited); types.alloc(Type::Enum(Enum { original_type_id: Some(ty.original_type_id.unwrap_or(lhs)), @@ -284,7 +285,7 @@ pub(crate) fn difference_type( } (_, Type::Enum(ty)) => { let ty = ty.clone(); - let type_id = difference_type(types, std, lhs, ty.type_id, visited); + let type_id = difference_type(types, lhs, ty.type_id, visited); types.alloc(Type::Enum(Enum { original_type_id: Some(ty.original_type_id.unwrap_or(rhs)), @@ -296,7 +297,7 @@ pub(crate) fn difference_type( (Type::Variant(variant), _) => { let variant = variant.clone(); - let type_id = difference_type(types, std, variant.type_id, rhs, visited); + let type_id = difference_type(types, variant.type_id, rhs, visited); types.alloc(Type::Variant(Variant { original_type_id: Some(variant.original_type_id.unwrap_or(lhs)), @@ -310,7 +311,7 @@ pub(crate) fn difference_type( } (_, Type::Variant(variant)) => { let variant = variant.clone(); - let type_id = difference_type(types, std, lhs, variant.type_id, visited); + let type_id = difference_type(types, lhs, variant.type_id, visited); types.alloc(Type::Variant(Variant { original_type_id: Some(variant.original_type_id.unwrap_or(rhs)), diff --git a/crates/rue-typing/src/semantic_types.rs b/crates/rue-typing/src/semantic_types.rs index 6024b23..bd8b67d 100644 --- a/crates/rue-typing/src/semantic_types.rs +++ b/crates/rue-typing/src/semantic_types.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use indexmap::IndexSet; use num_bigint::BigInt; -use crate::TypeId; +use crate::{Comparison, Type, TypeId, TypeSystem}; /// The kind of ending that a list has. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -86,3 +86,193 @@ pub struct Variant { /// The discriminant value. pub discriminant: BigInt, } + +/// Constructs a structural type consisting of the items in a list. +pub fn construct_items( + db: &mut TypeSystem, + items: impl DoubleEndedIterator, + rest: Rest, +) -> TypeId { + let mut result = db.std().nil; + for (i, item) in items.rev().enumerate() { + if i == 0 { + match rest { + Rest::Spread => { + result = item; + continue; + } + Rest::Optional => { + result = db.alloc(Type::Pair(item, result)); + result = db.alloc(Type::Union(vec![result, db.std().nil])); + continue; + } + Rest::Nil => {} + } + } + result = db.alloc(Type::Pair(item, result)); + } + result +} + +/// Deconstructs a structural type into a list of items and a rest value. +pub fn deconstruct_items( + db: &mut TypeSystem, + type_id: TypeId, + length: usize, + rest: Rest, +) -> Option> { + let mut items = Vec::with_capacity(length); + let mut current = type_id; + + for i in (0..length).rev() { + if i == 0 { + match rest { + Rest::Spread => { + items.push(current); + break; + } + Rest::Optional => { + if db.compare(db.std().nil, current) > Comparison::Assignable { + return None; + } + + let non_nil = db.difference(current, db.std().nil); + let (first, rest) = db.get_pair(non_nil)?; + + if db.compare(rest, db.std().nil) > Comparison::Equal { + return None; + } + + items.push(first); + break; + } + Rest::Nil => { + let (first, rest) = db.get_pair(current)?; + items.push(first); + if db.compare(rest, db.std().nil) > Comparison::Equal { + return None; + } + break; + } + } + } + let (first, rest) = db.get_pair(current)?; + items.push(first); + current = rest; + } + + Some(items) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_construct_int_nil() { + let mut db = TypeSystem::new(); + let std = db.std(); + let type_id = construct_items(&mut db, [std.int].into_iter(), Rest::Nil); + let items = deconstruct_items(&mut db, type_id, 1, Rest::Nil); + assert_eq!(items, Some(vec![std.int])); + } + + #[test] + fn test_construct_int() { + let mut db = TypeSystem::new(); + let std = db.std(); + let type_id = construct_items(&mut db, [std.int].into_iter(), Rest::Spread); + let items = deconstruct_items(&mut db, type_id, 1, Rest::Spread); + assert_eq!(items, Some(vec![std.int])); + } + + #[test] + fn test_construct_int_optional() { + let mut db = TypeSystem::new(); + let std = db.std(); + let type_id = construct_items(&mut db, [std.int].into_iter(), Rest::Optional); + let items = deconstruct_items(&mut db, type_id, 1, Rest::Optional); + assert_eq!(items, Some(vec![std.int])); + } + + #[test] + fn test_construct_empty_nil() { + let mut db = TypeSystem::new(); + let type_id = construct_items(&mut db, [].into_iter(), Rest::Nil); + let items = deconstruct_items(&mut db, type_id, 0, Rest::Nil); + assert_eq!(items, Some(vec![])); + } + + #[test] + fn test_construct_empty() { + let mut db = TypeSystem::new(); + let type_id = construct_items(&mut db, [].into_iter(), Rest::Spread); + let items = deconstruct_items(&mut db, type_id, 0, Rest::Spread); + assert_eq!(items, Some(vec![])); + } + + #[test] + fn test_construct_empty_optional() { + let mut db = TypeSystem::new(); + let type_id = construct_items(&mut db, [].into_iter(), Rest::Optional); + let items = deconstruct_items(&mut db, type_id, 0, Rest::Optional); + assert_eq!(items, Some(vec![])); + } + + #[test] + fn test_construct_int_int_nil() { + let mut db = TypeSystem::new(); + let std = db.std(); + let type_id = construct_items(&mut db, [std.int, std.int].into_iter(), Rest::Nil); + let items = deconstruct_items(&mut db, type_id, 2, Rest::Nil); + assert_eq!(items, Some(vec![std.int, std.int])); + } + + #[test] + fn test_construct_int_int() { + let mut db = TypeSystem::new(); + let std = db.std(); + let type_id = construct_items(&mut db, [std.int, std.int].into_iter(), Rest::Spread); + let items = deconstruct_items(&mut db, type_id, 2, Rest::Spread); + assert_eq!(items, Some(vec![std.int, std.int])); + } + + #[test] + fn test_construct_int_int_optional() { + let mut db = TypeSystem::new(); + let std = db.std(); + let type_id = construct_items(&mut db, [std.int, std.int].into_iter(), Rest::Optional); + let items = deconstruct_items(&mut db, type_id, 2, Rest::Optional); + assert_eq!(items, Some(vec![std.int, std.int])); + } + + #[test] + fn test_construct_bytes32_pair_nil() { + let mut db = TypeSystem::new(); + let std = db.std(); + let pair = db.alloc(Type::Pair(std.bytes32, std.nil)); + let type_id = construct_items(&mut db, [std.bytes32, pair].into_iter(), Rest::Nil); + let items = deconstruct_items(&mut db, type_id, 2, Rest::Nil); + assert_eq!(items, Some(vec![std.bytes32, pair])); + } + + #[test] + fn test_construct_bytes32_pair() { + let mut db = TypeSystem::new(); + let std = db.std(); + let pair = db.alloc(Type::Pair(std.bytes32, std.nil)); + let type_id = construct_items(&mut db, [std.bytes32, pair].into_iter(), Rest::Spread); + let items = deconstruct_items(&mut db, type_id, 2, Rest::Spread); + assert_eq!(items, Some(vec![std.bytes32, pair])); + } + + #[test] + fn test_construct_bytes32_pair_optional() { + let mut db = TypeSystem::new(); + let std = db.std(); + let pair = db.alloc(Type::Pair(std.bytes32, std.nil)); + let type_id = construct_items(&mut db, [std.bytes32, pair].into_iter(), Rest::Optional); + let items = deconstruct_items(&mut db, type_id, 2, Rest::Optional); + assert_eq!(items, Some(vec![std.bytes32, pair])); + } +} diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index 462e51a..b36ea13 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -103,16 +103,16 @@ impl TypeSystem { } } - pub fn pair_first(&self, type_id: TypeId) -> Option { + pub fn get_pair(&self, type_id: TypeId) -> Option<(TypeId, TypeId)> { match self.get(type_id) { - Type::Pair(first, _) => Some(*first), + Type::Pair(first, rest) => Some((*first, *rest)), _ => None, } } - pub fn pair_rest(&self, type_id: TypeId) -> Option { + pub fn get_union(&self, type_id: TypeId) -> Option<&[TypeId]> { match self.get(type_id) { - Type::Pair(_, rest) => Some(*rest), + Type::Union(types) => Some(types), _ => None, } } @@ -178,8 +178,8 @@ impl TypeSystem { check_type(self, lhs, rhs, &mut HashSet::new()).map(simplify_check) } - pub fn difference(&mut self, std: &StandardTypes, lhs: TypeId, rhs: TypeId) -> TypeId { - difference_type(self, std, lhs, rhs, &mut HashSet::new()) + pub fn difference(&mut self, lhs: TypeId, rhs: TypeId) -> TypeId { + difference_type(self, lhs, rhs, &mut HashSet::new()) } pub fn replace(