From b342dd2549500dbd155ee3eab23308f9bbcc035f Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 18 Jul 2024 15:40:36 -0400 Subject: [PATCH 001/100] Typing crate and workspace dependencies --- Cargo.lock | 4 ++++ Cargo.toml | 26 ++++++++++++++++++++++++++ crates/rue-cli/Cargo.toml | 12 ++++++------ crates/rue-clvm/Cargo.toml | 12 ++++++------ crates/rue-compiler/Cargo.toml | 22 +++++++++++----------- crates/rue-lsp/Cargo.toml | 10 +++++----- crates/rue-parser/Cargo.toml | 10 +++++----- crates/rue-tests/Cargo.toml | 22 +++++++++++----------- crates/rue-typing/Cargo.toml | 17 +++++++++++++++++ crates/rue-typing/src/lib.rs | 1 + crates/rue-typing/src/ty.rs | 2 ++ 11 files changed, 94 insertions(+), 44 deletions(-) create mode 100644 crates/rue-typing/Cargo.toml create mode 100644 crates/rue-typing/src/lib.rs create mode 100644 crates/rue-typing/src/ty.rs diff --git a/Cargo.lock b/Cargo.lock index 4968a86..1297c97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1455,6 +1455,10 @@ dependencies = [ "walkdir", ] +[[package]] +name = "rue-typing" +version = "0.1.1" + [[package]] name = "rustc-demangle" version = "0.1.23" diff --git a/Cargo.toml b/Cargo.toml index a17e72f..ccde803 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,3 +34,29 @@ module_name_repetitions = "allow" multiple_crate_versions = "allow" must_use_candidate = "allow" too_many_lines = "allow" + +[workspace.dependencies] +rue-parser = { path = "./crates/rue-parser", version = "0.1.0" } +rue-compiler = { path = "./crates/rue-compiler", version = "0.1.0" } +rue-clvm = { path = "./crates/rue-clvm", version = "0.1.0" } +rue-lexer = { path = "./crates/rue-lexer", version = "0.1.0" } +clvmr_old = { version = "0.3.2", package = "clvmr" } +clvmr = "0.6.1" +clap = "4.5.4" +hex = "0.4.3" +clvm_tools_rs = "0.1.41" +thiserror = "1.0.61" +num-bigint = "0.4.6" +num-traits = "0.2.19" +num-derive = "0.4.2" +id-arena = "2.2.1" +indexmap = "2.2.6" +rowan = "0.15.15" +log = "0.4.21" +indoc = "2.0.5" +tokio = "1.37.0" +tower-lsp = "0.20.0" +clvm-utils = "0.6.0" +toml = "0.8.12" +serde = "1.0.197" +walkdir = "2.5.0" diff --git a/crates/rue-cli/Cargo.toml b/crates/rue-cli/Cargo.toml index 952c6ac..9bc0913 100644 --- a/crates/rue-cli/Cargo.toml +++ b/crates/rue-cli/Cargo.toml @@ -15,12 +15,12 @@ categories = { workspace = true } workspace = true [dependencies] -clap = { version = "4.5.4", features = ["derive"] } -rue-parser = { path = "../../crates/rue-parser", version = "0.1.0" } -rue-compiler = { path = "../../crates/rue-compiler", version = "0.1.0" } -rue-clvm = { path = "../../crates/rue-clvm", version = "0.1.0" } -clvmr = "0.6.1" -hex = "0.4.3" +clap = { workspace = true, features = ["derive"] } +rue-parser = { workspace = true } +rue-compiler = { workspace = true } +rue-clvm = { workspace = true } +clvmr = { workspace = true } +hex = { workspace = true } [[bin]] name = "rue" diff --git a/crates/rue-clvm/Cargo.toml b/crates/rue-clvm/Cargo.toml index 5e2f733..4bb4bed 100644 --- a/crates/rue-clvm/Cargo.toml +++ b/crates/rue-clvm/Cargo.toml @@ -15,9 +15,9 @@ categories = { workspace = true } workspace = true [dependencies] -clvmr = "0.6.1" -clvmr_old = { version = "0.3.2", package = "clvmr" } -clvm_tools_rs = "0.1.41" -thiserror = "1.0.61" -num-bigint = "0.4.6" -num-traits = "0.2.19" +clvmr = { workspace = true } +clvmr_old = { workspace = true } +clvm_tools_rs = { workspace = true } +thiserror = { workspace = true } +num-bigint = { workspace = true } +num-traits = { workspace = true } diff --git a/crates/rue-compiler/Cargo.toml b/crates/rue-compiler/Cargo.toml index 40c2574..51afcef 100644 --- a/crates/rue-compiler/Cargo.toml +++ b/crates/rue-compiler/Cargo.toml @@ -15,14 +15,14 @@ categories = { workspace = true } workspace = true [dependencies] -rue-parser = { path = "../rue-parser", version = "0.1.0" } -rue-clvm = { path = "../rue-clvm", version = "0.1.0" } -clvmr = "0.6.1" -id-arena = "2.2.1" -indexmap = "2.2.6" -rowan = "0.15.15" -num-traits = "0.2.18" -num-bigint = "0.4.4" -log = "0.4.21" -hex = "0.4.3" -indoc = "2.0.5" +rue-parser = { workspace = true } +rue-clvm = { workspace = true } +clvmr = { workspace = true } +id-arena = { workspace = true } +indexmap = { workspace = true } +rowan = { workspace = true } +num-traits = { workspace = true } +num-bigint = { workspace = true } +log = { workspace = true } +hex = { workspace = true } +indoc = { workspace = true } diff --git a/crates/rue-lsp/Cargo.toml b/crates/rue-lsp/Cargo.toml index 0440666..c45472a 100644 --- a/crates/rue-lsp/Cargo.toml +++ b/crates/rue-lsp/Cargo.toml @@ -15,8 +15,8 @@ categories = { workspace = true } workspace = true [dependencies] -rue-parser = { path = "../rue-parser", version = "0.1.0" } -rue-compiler = { path = "../rue-compiler", version = "0.1.0" } -tokio = { version = "1.37.0", features = ["full"] } -tower-lsp = "0.20.0" -clvmr = "0.6.1" +rue-parser = { workspace = true } +rue-compiler = { workspace = true } +tokio = { workspace = true, features = ["full"] } +tower-lsp = { workspace = true } +clvmr = { workspace = true } diff --git a/crates/rue-parser/Cargo.toml b/crates/rue-parser/Cargo.toml index 4fc88b0..f1ae2bf 100644 --- a/crates/rue-parser/Cargo.toml +++ b/crates/rue-parser/Cargo.toml @@ -15,8 +15,8 @@ categories = { workspace = true } workspace = true [dependencies] -indexmap = "2.2.6" -num-derive = "0.4.2" -num-traits = "0.2.18" -rowan = "0.15.15" -rue-lexer = { path = "../rue-lexer", version = "0.1.0" } +rue-lexer = { workspace = true } +indexmap = { workspace = true } +num-derive = { workspace = true } +num-traits = { workspace = true } +rowan = { workspace = true } diff --git a/crates/rue-tests/Cargo.toml b/crates/rue-tests/Cargo.toml index 114fea5..46627d5 100644 --- a/crates/rue-tests/Cargo.toml +++ b/crates/rue-tests/Cargo.toml @@ -16,14 +16,14 @@ categories = { workspace = true } workspace = true [dependencies] -rue-parser = { path = "../rue-parser", version = "0.1.0" } -rue-compiler = { path = "../rue-compiler", version = "0.1.0" } -rue-clvm = { path = "../rue-clvm", version = "0.1.0" } -clvm-utils = "0.6.0" -clvmr = "0.6.1" -hex = "0.4.3" -toml = "0.8.12" -serde = { version = "1.0.197", features = ["derive"] } -clap = { version = "4.5.4", features = ["derive"] } -walkdir = "2.5.0" -indexmap = { version = "2.2.6", features = ["serde"] } +rue-parser = { workspace = true } +rue-compiler = { workspace = true } +rue-clvm = { workspace = true } +clvm-utils = { workspace = true } +clvmr = { workspace = true } +hex = { workspace = true } +toml = { workspace = true } +walkdir = { workspace = true } +serde = { workspace = true, features = ["derive"] } +clap = { workspace = true, features = ["derive"] } +indexmap = { workspace = true, features = ["serde"] } diff --git a/crates/rue-typing/Cargo.toml b/crates/rue-typing/Cargo.toml new file mode 100644 index 0000000..0ff467c --- /dev/null +++ b/crates/rue-typing/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "rue-typing" +version = "0.1.1" +edition = "2021" +license = "Apache-2.0" +description = "The type system used by the Rue compiler." +authors = ["Brandon Haggstrom "] +homepage = "https://github.com/rigidity/rue" +repository = "https://github.com/rigidity/rue" +readme = { workspace = true } +keywords = { workspace = true } +categories = { workspace = true } + +[lints] +workspace = true + +[dependencies] diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs new file mode 100644 index 0000000..6ce7e9a --- /dev/null +++ b/crates/rue-typing/src/lib.rs @@ -0,0 +1 @@ +mod ty; diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs new file mode 100644 index 0000000..64f70a7 --- /dev/null +++ b/crates/rue-typing/src/ty.rs @@ -0,0 +1,2 @@ +#[derive(Debug, Clone)] +pub enum Type {} From e314575db51ededc0b6609ccc0c2461f1ceb7d67 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 18 Jul 2024 20:59:47 -0400 Subject: [PATCH 002/100] Prototype new type system --- Cargo.lock | 3 + Cargo.toml | 1 + crates/rue-typing/Cargo.toml | 1 + crates/rue-typing/src/check.rs | 15 ++++ crates/rue-typing/src/comparison.rs | 8 ++ crates/rue-typing/src/lib.rs | 10 +++ crates/rue-typing/src/standard_types.rs | 40 +++++++++ crates/rue-typing/src/ty.rs | 111 +++++++++++++++++++++++- crates/rue-typing/src/type_system.rs | 40 +++++++++ 9 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 crates/rue-typing/src/check.rs create mode 100644 crates/rue-typing/src/comparison.rs create mode 100644 crates/rue-typing/src/standard_types.rs create mode 100644 crates/rue-typing/src/type_system.rs diff --git a/Cargo.lock b/Cargo.lock index 1297c97..99d0173 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1458,6 +1458,9 @@ dependencies = [ [[package]] name = "rue-typing" version = "0.1.1" +dependencies = [ + "id-arena", +] [[package]] name = "rustc-demangle" diff --git a/Cargo.toml b/Cargo.toml index ccde803..0b25a8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ module_name_repetitions = "allow" multiple_crate_versions = "allow" must_use_candidate = "allow" too_many_lines = "allow" +match_same_arms = "allow" [workspace.dependencies] rue-parser = { path = "./crates/rue-parser", version = "0.1.0" } diff --git a/crates/rue-typing/Cargo.toml b/crates/rue-typing/Cargo.toml index 0ff467c..3df9ec1 100644 --- a/crates/rue-typing/Cargo.toml +++ b/crates/rue-typing/Cargo.toml @@ -15,3 +15,4 @@ categories = { workspace = true } workspace = true [dependencies] +id-arena = { workspace = true } diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs new file mode 100644 index 0000000..4d0c145 --- /dev/null +++ b/crates/rue-typing/src/check.rs @@ -0,0 +1,15 @@ +#[derive(Debug, Clone)] +pub enum Check { + IsPair, + IsAtom, + IsBool, + IsNil, + Length(usize), + Not(Box), + And(Vec), + Or(Vec), +} + +pub fn check_type() -> Check { + todo!() +} diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs new file mode 100644 index 0000000..bc9b47f --- /dev/null +++ b/crates/rue-typing/src/comparison.rs @@ -0,0 +1,8 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Comparison { + Equal, + Assignable, + Castable, + Superset, + Incompatible, +} diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index 6ce7e9a..a27f4d2 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -1 +1,11 @@ +mod check; +mod comparison; +mod standard_types; mod ty; +mod type_system; + +pub use check::*; +pub use comparison::*; +pub use standard_types::*; +pub use ty::*; +pub use type_system::*; diff --git a/crates/rue-typing/src/standard_types.rs b/crates/rue-typing/src/standard_types.rs new file mode 100644 index 0000000..cf11d9d --- /dev/null +++ b/crates/rue-typing/src/standard_types.rs @@ -0,0 +1,40 @@ +use crate::{Type, TypeId, TypeSystem}; + +#[derive(Debug, Clone, Copy)] +pub struct StandardTypes { + pub unknown: TypeId, + pub any: TypeId, + pub bytes: TypeId, + pub bytes32: TypeId, + pub public_key: TypeId, + pub int: TypeId, + pub bool: TypeId, + pub nil: TypeId, +} + +impl StandardTypes { + pub fn alloc(type_system: &mut TypeSystem) -> Self { + let unknown = type_system.alloc(Type::Unknown); + let bytes = type_system.alloc(Type::Bytes); + let bytes32 = type_system.alloc(Type::Bytes32); + let public_key = type_system.alloc(Type::PublicKey); + let int = type_system.alloc(Type::Int); + let bool = type_system.alloc(Type::Bool); + let nil = type_system.alloc(Type::Nil); + + let any = type_system.alloc(Type::Unknown); + let pair = type_system.alloc(Type::Pair(any, any)); + *type_system.get_mut(any) = Type::Union(vec![bytes, pair]); + + Self { + unknown, + any, + bytes, + bytes32, + public_key, + int, + bool, + nil, + } + } +} diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs index 64f70a7..d9e61e3 100644 --- a/crates/rue-typing/src/ty.rs +++ b/crates/rue-typing/src/ty.rs @@ -1,2 +1,111 @@ +use std::{ + cmp::{max, min}, + collections::HashSet, + hash::BuildHasher, +}; + +use crate::{Comparison, TypeId, TypeSystem}; + #[derive(Debug, Clone)] -pub enum Type {} +pub enum Type { + Unknown, + Bytes, + Bytes32, + PublicKey, + Int, + Bool, + Nil, + Pair(TypeId, TypeId), + Union(Vec), + Ref(TypeId), +} + +pub(crate) fn compare_type( + types: &TypeSystem, + lhs: TypeId, + rhs: TypeId, + visited: &mut HashSet<(TypeId, TypeId), S>, +) -> Comparison +where + S: BuildHasher, +{ + if !visited.insert((lhs, rhs)) { + return Comparison::Assignable; + } + + let comparison = match (types.get(lhs), types.get(rhs)) { + (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), + + (Type::Unknown, _) | (_, Type::Unknown) => Comparison::Assignable, + + (Type::Union(lhs), _) => { + let mut result = Comparison::Assignable; + for lhs in lhs { + let comparison = compare_type(types, *lhs, rhs, visited); + result = max(result, comparison); + } + result + } + + (_, Type::Union(rhs)) => { + let mut result = Comparison::Assignable; + for rhs in rhs { + let comparison = compare_type(types, lhs, *rhs, visited); + result = min(result, comparison); + } + result + } + + (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { + let first = compare_type(types, *lhs_first, *rhs_first, visited); + let rest = compare_type(types, *lhs_rest, *rhs_rest, visited); + max(first, rest) + } + (Type::Pair(..), _) | (_, Type::Pair(..)) => Comparison::Incompatible, + + (Type::Bytes, Type::Bytes) => Comparison::Equal, + (Type::Bytes32, Type::Bytes32) => Comparison::Equal, + (Type::PublicKey, Type::PublicKey) => Comparison::Equal, + (Type::Int, Type::Int) => Comparison::Equal, + (Type::Bool, Type::Bool) => Comparison::Equal, + (Type::Nil, Type::Nil) => Comparison::Equal, + + (Type::Bytes, Type::Bytes32) => Comparison::Superset, + (Type::Bytes, Type::PublicKey) => Comparison::Superset, + (Type::Bytes, Type::Bool) => Comparison::Superset, + (Type::Bytes, Type::Nil) => Comparison::Superset, + (Type::Int, Type::Bytes32) => Comparison::Superset, + (Type::Int, Type::PublicKey) => Comparison::Superset, + (Type::Int, Type::Bool) => Comparison::Superset, + (Type::Int, Type::Nil) => Comparison::Superset, + + (Type::Bytes32, Type::Bytes) => Comparison::Assignable, + (Type::Nil, Type::Bytes) => Comparison::Assignable, + + (Type::Bytes, Type::Int) => Comparison::Castable, + (Type::Bytes32, Type::Int) => Comparison::Castable, + (Type::PublicKey, Type::Bytes) => Comparison::Castable, + (Type::PublicKey, Type::Int) => Comparison::Castable, + (Type::Int, Type::Bytes) => Comparison::Castable, + (Type::Nil, Type::Bool) => Comparison::Castable, + (Type::Nil, Type::Int) => Comparison::Castable, + (Type::Bool, Type::Bytes) => Comparison::Castable, + (Type::Bool, Type::Int) => Comparison::Castable, + + (Type::Bytes32, Type::PublicKey) => Comparison::Incompatible, + (Type::Bytes32, Type::Bool) => Comparison::Incompatible, + (Type::Bytes32, Type::Nil) => Comparison::Incompatible, + (Type::PublicKey, Type::Bytes32) => Comparison::Incompatible, + (Type::PublicKey, Type::Bool) => Comparison::Incompatible, + (Type::PublicKey, Type::Nil) => Comparison::Incompatible, + (Type::Bool, Type::Bytes32) => Comparison::Incompatible, + (Type::Bool, Type::PublicKey) => Comparison::Incompatible, + (Type::Bool, Type::Nil) => Comparison::Incompatible, + (Type::Nil, Type::Bytes32) => Comparison::Incompatible, + (Type::Nil, Type::PublicKey) => Comparison::Incompatible, + }; + + visited.remove(&(lhs, rhs)); + + comparison +} diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs new file mode 100644 index 0000000..247cf0a --- /dev/null +++ b/crates/rue-typing/src/type_system.rs @@ -0,0 +1,40 @@ +use std::collections::HashSet; + +use id_arena::{Arena, Id}; + +use crate::{compare_type, Comparison, Type}; + +pub type TypeId = Id; + +#[derive(Debug, Default, Clone)] +pub struct TypeSystem { + arena: Arena, +} + +impl TypeSystem { + pub fn new() -> Self { + Self::default() + } + + pub fn alloc(&mut self, ty: Type) -> TypeId { + self.arena.alloc(ty) + } + + pub fn get(&self, id: TypeId) -> &Type { + match &self.arena[id] { + Type::Ref(id) => self.get(*id), + ty => ty, + } + } + + pub fn get_mut(&mut self, id: TypeId) -> &mut Type { + match &self.arena[id] { + Type::Ref(id) => self.get_mut(*id), + _ => &mut self.arena[id], + } + } + + pub fn compare(&self, lhs: TypeId, rhs: TypeId) -> Comparison { + compare_type(self, lhs, rhs, &mut HashSet::new()) + } +} From 6f24cf41d10024da14a96a5b859ec96f94dd3430 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 18 Jul 2024 22:52:46 -0400 Subject: [PATCH 003/100] Prototype new check system --- Cargo.lock | 1 + crates/rue-typing/Cargo.toml | 1 + crates/rue-typing/src/check.rs | 207 ++++++++++++++++++++++++++++++++- 3 files changed, 206 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 99d0173..d8b051b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1460,6 +1460,7 @@ name = "rue-typing" version = "0.1.1" dependencies = [ "id-arena", + "thiserror", ] [[package]] diff --git a/crates/rue-typing/Cargo.toml b/crates/rue-typing/Cargo.toml index 3df9ec1..aad8ab9 100644 --- a/crates/rue-typing/Cargo.toml +++ b/crates/rue-typing/Cargo.toml @@ -16,3 +16,4 @@ workspace = true [dependencies] id-arena = { workspace = true } +thiserror = { workspace = true } diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 4d0c145..c30cd1d 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -1,15 +1,216 @@ +use std::{collections::HashSet, hash::BuildHasher}; + +use crate::{Type, TypeId, TypeSystem}; + +#[derive(Debug, Clone, Copy)] +pub enum CheckError { + Recursive(TypeId, TypeId), + Impossible(TypeId, TypeId), +} + #[derive(Debug, Clone)] pub enum Check { + None, IsPair, IsAtom, IsBool, IsNil, Length(usize), - Not(Box), And(Vec), Or(Vec), + If(Box, Box, Box), + Pair(Box, Box), +} + +/// Returns [`None`] for recursive checks. +pub fn check_type( + types: &mut TypeSystem, + lhs: TypeId, + rhs: TypeId, + visited: &mut HashSet<(TypeId, TypeId), S>, +) -> Result +where + S: BuildHasher, +{ + if !visited.insert((lhs, rhs)) { + return Err(CheckError::Recursive(lhs, rhs)); + } + + let check = match (types.get(lhs), types.get(rhs)) { + (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), + + (Type::Unknown, _) | (_, Type::Unknown) => Check::None, + + (Type::Bytes, Type::Bytes) => Check::None, + (Type::Bytes32, Type::Bytes32) => Check::None, + (Type::PublicKey, Type::PublicKey) => Check::None, + (Type::Int, Type::Int) => Check::None, + (Type::Bool, Type::Bool) => Check::None, + (Type::Nil, Type::Nil) => Check::None, + + (Type::Bytes32, Type::Bytes) => Check::None, + (Type::PublicKey, Type::Bytes) => Check::None, + (Type::Int, Type::Bytes) => Check::None, + (Type::Bool, Type::Bytes) => Check::None, + (Type::Nil, Type::Bytes) => Check::None, + + (Type::Bytes32, Type::Int) => Check::None, + (Type::PublicKey, Type::Int) => Check::None, + (Type::Bytes, Type::Int) => Check::None, + (Type::Bool, Type::Int) => Check::None, + (Type::Nil, Type::Int) => Check::None, + + (Type::Nil, Type::Bool) => Check::None, + + (Type::Bytes, Type::Bool) => Check::IsBool, + (Type::Bytes, Type::Nil) => Check::IsNil, + (Type::Bytes, Type::PublicKey) => Check::Length(48), + (Type::Bytes, Type::Bytes32) => Check::Length(32), + + (Type::Int, Type::Bool) => Check::IsBool, + (Type::Int, Type::Nil) => Check::IsNil, + (Type::Int, Type::PublicKey) => Check::Length(48), + (Type::Int, Type::Bytes32) => Check::Length(32), + + (Type::Bool, Type::Nil) => Check::IsNil, + + (_, Type::Union(items)) => Check::Or( + items + .clone() + .into_iter() + .map(|item| check_type(types, lhs, item, visited)) + .collect::>()?, + ), + + (Type::Union(items), _) => { + let mut atom_count = 0; + let mut bool_count = 0; + let mut nil_count = 0; + let mut bytes32_count = 0; + let mut public_key_count = 0; + let mut pairs = Vec::new(); + + for item in items { + match types.get(*item) { + Type::Ref(..) => unreachable!(), + Type::Union(..) => unreachable!(), + Type::Unknown => {} + Type::Bytes | Type::Int => { + atom_count += 1; + } + Type::Bytes32 => { + atom_count += 1; + bytes32_count += 1; + } + Type::PublicKey => { + atom_count += 1; + public_key_count += 1; + } + Type::Bool => { + atom_count += 1; + bool_count += 1; + } + Type::Nil => { + atom_count += 1; + nil_count += 1; + bool_count += 1; + } + Type::Pair(first, rest) => { + pairs.push((*first, *rest)); + } + } + } + + let always_atom = atom_count == items.len(); + let always_pair = pairs.len() == items.len(); + let always_bool = bool_count == items.len(); + let always_nil = nil_count == items.len(); + let always_bytes32 = bytes32_count == items.len(); + let always_public_key = public_key_count == items.len(); + + match types.get(rhs) { + Type::Unknown => Check::None, + Type::Ref(..) => unreachable!(), + Type::Union(..) => unreachable!(), + Type::Bytes if always_atom => Check::None, + Type::Int if always_atom => Check::None, + Type::Bool if always_bool => Check::None, + Type::Nil if always_nil => Check::None, + Type::Bytes32 if always_bytes32 => Check::None, + Type::PublicKey if always_public_key => Check::None, + Type::Bytes32 if always_atom => Check::Length(32), + Type::PublicKey if always_atom => Check::Length(48), + Type::Bool if always_atom => Check::IsBool, + Type::Nil if always_atom => Check::IsNil, + Type::Bytes => Check::IsAtom, + Type::Int => Check::IsAtom, + Type::Bytes32 => Check::And(vec![Check::IsAtom, Check::Length(32)]), + Type::PublicKey => Check::And(vec![Check::IsAtom, Check::Length(48)]), + Type::Bool => Check::And(vec![Check::IsAtom, Check::IsBool]), + Type::Nil => Check::And(vec![Check::IsAtom, Check::IsNil]), + Type::Pair(..) if always_atom => return Err(CheckError::Impossible(lhs, rhs)), + Type::Pair(first, rest) => { + let (first, rest) = (*first, *rest); + + let first_items = + types.alloc(Type::Union(pairs.iter().map(|(first, _)| *first).collect())); + + let rest_items = + types.alloc(Type::Union(pairs.iter().map(|(_, rest)| *rest).collect())); + + let first = check_type(types, first_items, first, visited)?; + let rest = check_type(types, rest_items, rest, visited)?; + + let pair_check = Check::Pair(Box::new(first), Box::new(rest)); + + if always_pair { + pair_check + } else { + Check::And(vec![Check::IsPair, pair_check]) + } + } + } + } + + (Type::PublicKey, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bytes32, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Nil, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Nil, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::PublicKey, Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bytes32, Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bool, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bool, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::PublicKey, Type::Bool) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bytes32, Type::Bool) => return Err(CheckError::Impossible(lhs, rhs)), + + (Type::Bytes, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bytes32, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::PublicKey, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Int, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bool, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Nil, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + + (Type::Pair(..), Type::Bytes) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Pair(..), Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Pair(..), Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Pair(..), Type::Int) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Pair(..), Type::Bool) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Pair(..), Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), + + (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { + let (lhs_first, lhs_rest) = (*lhs_first, *lhs_rest); + let (rhs_first, rhs_rest) = (*rhs_first, *rhs_rest); + let first = check_type(types, lhs_first, rhs_first, visited)?; + let rest = check_type(types, lhs_rest, rhs_rest, visited)?; + Check::Pair(Box::new(first), Box::new(rest)) + } + }; + + visited.remove(&(lhs, rhs)); + + Ok(check) } -pub fn check_type() -> Check { - todo!() +pub fn simplify_check(check: Check) -> Check { + check } From 8067d601dbc67d04c52856f1a32e8b370f8c37a8 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 18 Jul 2024 23:06:22 -0400 Subject: [PATCH 004/100] Add Display for Check --- crates/rue-typing/src/check.rs | 120 +++++++++++++++++++++++++++++++-- 1 file changed, 116 insertions(+), 4 deletions(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index c30cd1d..fd8f694 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, hash::BuildHasher}; +use std::{collections::HashSet, fmt, hash::BuildHasher}; use crate::{Type, TypeId, TypeSystem}; @@ -8,7 +8,7 @@ pub enum CheckError { Impossible(TypeId, TypeId), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum Check { None, IsPair, @@ -22,6 +22,18 @@ pub enum Check { Pair(Box, Box), } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum CheckPath { + First, + Rest, +} + +impl fmt::Display for Check { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt_check(self, f, &mut Vec::new()) + } +} + /// Returns [`None`] for recursive checks. pub fn check_type( types: &mut TypeSystem, @@ -211,6 +223,106 @@ where Ok(check) } -pub fn simplify_check(check: Check) -> Check { - check +fn fmt_val(f: &mut fmt::Formatter<'_>, path: &[CheckPath]) -> fmt::Result { + for path in path.iter().rev() { + match path { + CheckPath::First => write!(f, "(f ")?, + CheckPath::Rest => write!(f, "(r ")?, + } + } + write!(f, "val")?; + for _ in 0..path.len() { + write!(f, ")")?; + } + Ok(()) +} + +fn fmt_check(check: &Check, f: &mut fmt::Formatter<'_>, path: &mut Vec) -> fmt::Result { + match check { + Check::None => write!(f, "()"), + Check::IsPair => { + write!(f, "(l ")?; + fmt_val(f, path)?; + write!(f, ")") + } + Check::IsAtom => { + write!(f, "(not (l ")?; + fmt_val(f, path)?; + write!(f, "))") + } + Check::IsBool => { + write!(f, "(any (= ")?; + fmt_val(f, path)?; + write!(f, " ()) (= ")?; + fmt_val(f, path)?; + write!(f, " 1))") + } + Check::IsNil => { + write!(f, "(= ")?; + fmt_val(f, path)?; + write!(f, " ())") + } + Check::Length(len) => { + write!(f, "(= (stlren ")?; + fmt_val(f, path)?; + write!(f, ") {len})") + } + Check::And(checks) => { + write!(f, "(and")?; + for (i, check) in checks.iter().enumerate() { + if i > 0 { + write!(f, " ")?; + } + fmt_check(check, f, path)?; + } + write!(f, ")") + } + Check::Or(checks) => { + write!(f, "(or")?; + for (i, check) in checks.iter().enumerate() { + if i > 0 { + write!(f, " ")?; + } + fmt_check(check, f, path)?; + } + write!(f, ")") + } + Check::If(cond, then, else_) => { + write!(f, "(if ")?; + fmt_check(cond, f, path)?; + write!(f, " ")?; + fmt_check(then, f, path)?; + write!(f, " ")?; + fmt_check(else_, f, path)?; + write!(f, ")") + } + Check::Pair(first, rest) => { + let has_first = first.as_ref() != &Check::None; + let has_rest = rest.as_ref() != &Check::None; + + if has_first && has_rest { + write!(f, "(all ")?; + path.push(CheckPath::First); + fmt_check(first, f, path)?; + path.pop().unwrap(); + write!(f, " ")?; + path.push(CheckPath::Rest); + fmt_check(rest, f, path)?; + path.pop().unwrap(); + write!(f, ")") + } else if has_first { + path.push(CheckPath::First); + fmt_check(first, f, path)?; + path.pop().unwrap(); + Ok(()) + } else if has_rest { + path.push(CheckPath::Rest); + fmt_check(rest, f, path)?; + path.pop().unwrap(); + Ok(()) + } else { + write!(f, "()") + } + } + } } From 49b010450201eeff1bdfdf5b9b7734176a14742e Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 18 Jul 2024 23:56:26 -0400 Subject: [PATCH 005/100] Tests passing --- Cargo.lock | 5 +- Cargo.toml | 1 + crates/rue-typing/Cargo.toml | 3 + crates/rue-typing/src/check.rs | 147 ++++++++++++++++++++++----- crates/rue-typing/src/type_system.rs | 6 +- 5 files changed, 133 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d8b051b..c6a52a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1321,9 +1321,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -1460,6 +1460,7 @@ name = "rue-typing" version = "0.1.1" dependencies = [ "id-arena", + "regex", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index 0b25a8c..eba5193 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,3 +61,4 @@ clvm-utils = "0.6.0" toml = "0.8.12" serde = "1.0.197" walkdir = "2.5.0" +regex = "1.10.5" diff --git a/crates/rue-typing/Cargo.toml b/crates/rue-typing/Cargo.toml index aad8ab9..762955c 100644 --- a/crates/rue-typing/Cargo.toml +++ b/crates/rue-typing/Cargo.toml @@ -17,3 +17,6 @@ workspace = true [dependencies] id-arena = { workspace = true } thiserror = { workspace = true } + +[dev-dependencies] +regex = { workspace = true } diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index fd8f694..6b25b11 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -1,4 +1,8 @@ -use std::{collections::HashSet, fmt, hash::BuildHasher}; +use std::{ + collections::{HashSet, VecDeque}, + fmt, + hash::BuildHasher, +}; use crate::{Type, TypeId, TypeSystem}; @@ -35,7 +39,7 @@ impl fmt::Display for Check { } /// Returns [`None`] for recursive checks. -pub fn check_type( +pub(crate) fn check_type( types: &mut TypeSystem, lhs: TypeId, rhs: TypeId, @@ -86,13 +90,13 @@ where (Type::Bool, Type::Nil) => Check::IsNil, - (_, Type::Union(items)) => Check::Or( - items - .clone() - .into_iter() - .map(|item| check_type(types, lhs, item, visited)) - .collect::>()?, - ), + (_, Type::Union(items)) => { + let mut result = Vec::new(); + for item in items.clone() { + result.push(check_type(types, lhs, item, visited)?); + } + Check::Or(result) + } (Type::Union(items), _) => { let mut atom_count = 0; @@ -102,10 +106,18 @@ where let mut public_key_count = 0; let mut pairs = Vec::new(); - for item in items { - match types.get(*item) { + let mut items: VecDeque<_> = items.clone().into(); + let mut length = 0; + + while !items.is_empty() { + let item = items.remove(0).unwrap(); + length += 1; + + match types.get(item) { Type::Ref(..) => unreachable!(), - Type::Union(..) => unreachable!(), + Type::Union(child_items) => { + items.extend(child_items); + } Type::Unknown => {} Type::Bytes | Type::Int => { atom_count += 1; @@ -133,12 +145,12 @@ where } } - let always_atom = atom_count == items.len(); - let always_pair = pairs.len() == items.len(); - let always_bool = bool_count == items.len(); - let always_nil = nil_count == items.len(); - let always_bytes32 = bytes32_count == items.len(); - let always_public_key = public_key_count == items.len(); + let always_atom = atom_count == length; + let always_pair = pairs.len() == length; + let always_bool = bool_count == length; + let always_nil = nil_count == length; + let always_bytes32 = bytes32_count == length; + let always_public_key = public_key_count == length; match types.get(rhs) { Type::Unknown => Check::None, @@ -164,6 +176,11 @@ where Type::Pair(first, rest) => { let (first, rest) = (*first, *rest); + // Prevent infinite recursion. + // TODO: This is a bit of a hack. + check_type(types, lhs, first, visited)?; + check_type(types, lhs, rest, visited)?; + let first_items = types.alloc(Type::Union(pairs.iter().map(|(first, _)| *first).collect())); @@ -269,20 +286,16 @@ fn fmt_check(check: &Check, f: &mut fmt::Formatter<'_>, path: &mut Vec { write!(f, "(and")?; - for (i, check) in checks.iter().enumerate() { - if i > 0 { - write!(f, " ")?; - } + for check in checks { + write!(f, " ")?; fmt_check(check, f, path)?; } write!(f, ")") } Check::Or(checks) => { write!(f, "(or")?; - for (i, check) in checks.iter().enumerate() { - if i > 0 { - write!(f, " ")?; - } + for check in checks { + write!(f, " ")?; fmt_check(check, f, path)?; } write!(f, ")") @@ -326,3 +339,85 @@ fn fmt_check(check: &Check, f: &mut fmt::Formatter<'_>, path: &mut Vec String { + Regex::new(r"\s+") + .unwrap() + .replace_all(text.trim(), " ") + .replace(" )", ")") + } + + #[test] + fn check_any_bytes32() { + let mut db = TypeSystem::new(); + let types = StandardTypes::alloc(&mut db); + assert_eq!( + format!("{}", db.check(types.any, types.bytes32).unwrap()), + "(and (not (l val)) (= (stlren val) 32))" + ); + } + + #[test] + fn check_any_int() { + let mut db = TypeSystem::new(); + let types = StandardTypes::alloc(&mut db); + assert_eq!( + format!("{}", db.check(types.any, types.int).unwrap()), + "(not (l val))" + ); + } + + #[test] + fn check_any_any() { + let mut db = TypeSystem::new(); + let types = StandardTypes::alloc(&mut db); + assert!(matches!( + db.check(types.any, types.any).unwrap_err(), + CheckError::Recursive(..) + )); + } + + #[test] + fn check_any_pair_bytes32_pair_int_nil() { + let mut db = TypeSystem::new(); + let types = StandardTypes::alloc(&mut db); + + let int_nil_pair = db.alloc(Type::Pair(types.int, types.nil)); + let ty = db.alloc(Type::Pair(types.bytes32, int_nil_pair)); + + assert_eq!( + format!("{}", db.check(types.any, ty).unwrap()), + trim( + " + (and + (l val) + (all + (and + (not (l (f val))) + (= (stlren (f val)) 32) + ) + (and + (l (r val)) + (all + (not (l (f (r val)))) + (and + (not (l (r (r val)))) + (= (r (r val)) ()) + ) + ) + ) + ) + ) + " + ) + ); + } +} diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index 247cf0a..a4715ec 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; use id_arena::{Arena, Id}; -use crate::{compare_type, Comparison, Type}; +use crate::{check_type, compare_type, Check, CheckError, Comparison, Type}; pub type TypeId = Id; @@ -37,4 +37,8 @@ impl TypeSystem { pub fn compare(&self, lhs: TypeId, rhs: TypeId) -> Comparison { compare_type(self, lhs, rhs, &mut HashSet::new()) } + + pub fn check(&mut self, lhs: TypeId, rhs: TypeId) -> Result { + check_type(self, lhs, rhs, &mut HashSet::new()) + } } From b3477903be9c52d2e94bd8815b1bd8cb7c250aa7 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 19 Jul 2024 00:10:30 -0400 Subject: [PATCH 006/100] Refactor to prevent recursion --- crates/rue-typing/src/check.rs | 215 ++++++++++++++------------- crates/rue-typing/src/type_system.rs | 2 +- 2 files changed, 113 insertions(+), 104 deletions(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 6b25b11..01e1703 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -40,7 +40,7 @@ impl fmt::Display for Check { /// Returns [`None`] for recursive checks. pub(crate) fn check_type( - types: &mut TypeSystem, + types: &TypeSystem, lhs: TypeId, rhs: TypeId, visited: &mut HashSet<(TypeId, TypeId), S>, @@ -98,108 +98,7 @@ where Check::Or(result) } - (Type::Union(items), _) => { - let mut atom_count = 0; - let mut bool_count = 0; - let mut nil_count = 0; - let mut bytes32_count = 0; - let mut public_key_count = 0; - let mut pairs = Vec::new(); - - let mut items: VecDeque<_> = items.clone().into(); - let mut length = 0; - - while !items.is_empty() { - let item = items.remove(0).unwrap(); - length += 1; - - match types.get(item) { - Type::Ref(..) => unreachable!(), - Type::Union(child_items) => { - items.extend(child_items); - } - Type::Unknown => {} - Type::Bytes | Type::Int => { - atom_count += 1; - } - Type::Bytes32 => { - atom_count += 1; - bytes32_count += 1; - } - Type::PublicKey => { - atom_count += 1; - public_key_count += 1; - } - Type::Bool => { - atom_count += 1; - bool_count += 1; - } - Type::Nil => { - atom_count += 1; - nil_count += 1; - bool_count += 1; - } - Type::Pair(first, rest) => { - pairs.push((*first, *rest)); - } - } - } - - let always_atom = atom_count == length; - let always_pair = pairs.len() == length; - let always_bool = bool_count == length; - let always_nil = nil_count == length; - let always_bytes32 = bytes32_count == length; - let always_public_key = public_key_count == length; - - match types.get(rhs) { - Type::Unknown => Check::None, - Type::Ref(..) => unreachable!(), - Type::Union(..) => unreachable!(), - Type::Bytes if always_atom => Check::None, - Type::Int if always_atom => Check::None, - Type::Bool if always_bool => Check::None, - Type::Nil if always_nil => Check::None, - Type::Bytes32 if always_bytes32 => Check::None, - Type::PublicKey if always_public_key => Check::None, - Type::Bytes32 if always_atom => Check::Length(32), - Type::PublicKey if always_atom => Check::Length(48), - Type::Bool if always_atom => Check::IsBool, - Type::Nil if always_atom => Check::IsNil, - Type::Bytes => Check::IsAtom, - Type::Int => Check::IsAtom, - Type::Bytes32 => Check::And(vec![Check::IsAtom, Check::Length(32)]), - Type::PublicKey => Check::And(vec![Check::IsAtom, Check::Length(48)]), - Type::Bool => Check::And(vec![Check::IsAtom, Check::IsBool]), - Type::Nil => Check::And(vec![Check::IsAtom, Check::IsNil]), - Type::Pair(..) if always_atom => return Err(CheckError::Impossible(lhs, rhs)), - Type::Pair(first, rest) => { - let (first, rest) = (*first, *rest); - - // Prevent infinite recursion. - // TODO: This is a bit of a hack. - check_type(types, lhs, first, visited)?; - check_type(types, lhs, rest, visited)?; - - let first_items = - types.alloc(Type::Union(pairs.iter().map(|(first, _)| *first).collect())); - - let rest_items = - types.alloc(Type::Union(pairs.iter().map(|(_, rest)| *rest).collect())); - - let first = check_type(types, first_items, first, visited)?; - let rest = check_type(types, rest_items, rest, visited)?; - - let pair_check = Check::Pair(Box::new(first), Box::new(rest)); - - if always_pair { - pair_check - } else { - Check::And(vec![Check::IsPair, pair_check]) - } - } - } - } + (Type::Union(items), _) => check_union_against_rhs(types, lhs, items, rhs, visited)?, (Type::PublicKey, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Bytes32, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), @@ -240,6 +139,116 @@ where Ok(check) } +fn check_union_against_rhs( + types: &TypeSystem, + original_type_id: TypeId, + items: &[TypeId], + rhs: TypeId, + visited: &mut HashSet<(TypeId, TypeId), S>, +) -> Result +where + S: BuildHasher, +{ + let mut atom_count = 0; + let mut bool_count = 0; + let mut nil_count = 0; + let mut bytes32_count = 0; + let mut public_key_count = 0; + let mut pairs = Vec::new(); + + let mut items: VecDeque<_> = items.iter().copied().collect::>(); + let mut length = 0; + + while !items.is_empty() { + let item = items.remove(0).unwrap(); + length += 1; + + if !visited.insert((item, rhs)) { + return Err(CheckError::Recursive(item, rhs)); + } + + match types.get(item) { + Type::Ref(..) => unreachable!(), + Type::Union(child_items) => { + items.extend(child_items); + } + Type::Unknown => {} + Type::Bytes | Type::Int => { + atom_count += 1; + } + Type::Bytes32 => { + atom_count += 1; + bytes32_count += 1; + } + Type::PublicKey => { + atom_count += 1; + public_key_count += 1; + } + Type::Bool => { + atom_count += 1; + bool_count += 1; + } + Type::Nil => { + atom_count += 1; + nil_count += 1; + bool_count += 1; + } + Type::Pair(first, rest) => { + pairs.push((*first, *rest)); + } + } + } + + let always_atom = atom_count == length; + let always_pair = pairs.len() == length; + let always_bool = bool_count == length; + let always_nil = nil_count == length; + let always_bytes32 = bytes32_count == length; + let always_public_key = public_key_count == length; + + Ok(match types.get(rhs) { + Type::Unknown => Check::None, + Type::Ref(..) => unreachable!(), + Type::Union(..) => unreachable!(), + Type::Bytes if always_atom => Check::None, + Type::Int if always_atom => Check::None, + Type::Bool if always_bool => Check::None, + Type::Nil if always_nil => Check::None, + Type::Bytes32 if always_bytes32 => Check::None, + Type::PublicKey if always_public_key => Check::None, + Type::Bytes32 if always_atom => Check::Length(32), + Type::PublicKey if always_atom => Check::Length(48), + Type::Bool if always_atom => Check::IsBool, + Type::Nil if always_atom => Check::IsNil, + Type::Bytes => Check::IsAtom, + Type::Int => Check::IsAtom, + Type::Bytes32 => Check::And(vec![Check::IsAtom, Check::Length(32)]), + Type::PublicKey => Check::And(vec![Check::IsAtom, Check::Length(48)]), + Type::Bool => Check::And(vec![Check::IsAtom, Check::IsBool]), + Type::Nil => Check::And(vec![Check::IsAtom, Check::IsNil]), + Type::Pair(..) if always_atom => return Err(CheckError::Impossible(original_type_id, rhs)), + Type::Pair(first, rest) => { + let (first, rest) = (*first, *rest); + + let first_items: Vec<_> = pairs.iter().map(|(first, _)| *first).collect(); + let rest_items: Vec<_> = pairs.iter().map(|(_, rest)| *rest).collect(); + + let first = + check_union_against_rhs(types, original_type_id, &first_items, first, visited)?; + let rest = + check_union_against_rhs(types, original_type_id, &rest_items, rest, visited)?; + + let pair_check = Check::Pair(Box::new(first), Box::new(rest)); + + if always_pair { + pair_check + } else { + Check::And(vec![Check::IsPair, pair_check]) + } + } + }) +} + fn fmt_val(f: &mut fmt::Formatter<'_>, path: &[CheckPath]) -> fmt::Result { for path in path.iter().rev() { match path { diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index a4715ec..ef5c324 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -38,7 +38,7 @@ impl TypeSystem { compare_type(self, lhs, rhs, &mut HashSet::new()) } - pub fn check(&mut self, lhs: TypeId, rhs: TypeId) -> Result { + pub fn check(&self, lhs: TypeId, rhs: TypeId) -> Result { check_type(self, lhs, rhs, &mut HashSet::new()) } } From 7fd15071689c01bc38cc3d69d7a77f6690cb009b Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 19 Jul 2024 01:24:04 -0400 Subject: [PATCH 007/100] More tests --- Cargo.lock | 1 - Cargo.toml | 1 - crates/rue-typing/Cargo.toml | 3 -- crates/rue-typing/src/check.rs | 93 ++++++++++++++++++++-------------- main.sym | 1 + 5 files changed, 57 insertions(+), 42 deletions(-) create mode 100644 main.sym diff --git a/Cargo.lock b/Cargo.lock index c6a52a5..b8e086a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1460,7 +1460,6 @@ name = "rue-typing" version = "0.1.1" dependencies = [ "id-arena", - "regex", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index eba5193..0b25a8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,4 +61,3 @@ clvm-utils = "0.6.0" toml = "0.8.12" serde = "1.0.197" walkdir = "2.5.0" -regex = "1.10.5" diff --git a/crates/rue-typing/Cargo.toml b/crates/rue-typing/Cargo.toml index 762955c..aad8ab9 100644 --- a/crates/rue-typing/Cargo.toml +++ b/crates/rue-typing/Cargo.toml @@ -17,6 +17,3 @@ workspace = true [dependencies] id-arena = { workspace = true } thiserror = { workspace = true } - -[dev-dependencies] -regex = { workspace = true } diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 01e1703..8323f74 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -197,6 +197,8 @@ where pairs.push((*first, *rest)); } } + + visited.remove(&(item, rhs)); } let always_atom = atom_count == length; @@ -265,7 +267,7 @@ fn fmt_val(f: &mut fmt::Formatter<'_>, path: &[CheckPath]) -> fmt::Result { fn fmt_check(check: &Check, f: &mut fmt::Formatter<'_>, path: &mut Vec) -> fmt::Result { match check { - Check::None => write!(f, "()"), + Check::None => write!(f, "1"), Check::IsPair => { write!(f, "(l ")?; fmt_val(f, path)?; @@ -289,7 +291,7 @@ fn fmt_check(check: &Check, f: &mut fmt::Formatter<'_>, path: &mut Vec { - write!(f, "(= (stlren ")?; + write!(f, "(= (strlen ")?; fmt_val(f, path)?; write!(f, ") {len})") } @@ -343,7 +345,7 @@ fn fmt_check(check: &Check, f: &mut fmt::Formatter<'_>, path: &mut Vec, path: &mut Vec String { - Regex::new(r"\s+") - .unwrap() - .replace_all(text.trim(), " ") - .replace(" )", ")") - } - #[test] fn check_any_bytes32() { let mut db = TypeSystem::new(); let types = StandardTypes::alloc(&mut db); assert_eq!( format!("{}", db.check(types.any, types.bytes32).unwrap()), - "(and (not (l val)) (= (stlren val) 32))" + "(and (not (l val)) (= (strlen val) 32))" ); } @@ -394,6 +387,22 @@ mod tests { )); } + #[test] + fn check_optional_int() { + let mut db = TypeSystem::new(); + let types = StandardTypes::alloc(&mut db); + let pair = db.alloc(Type::Pair(types.int, types.int)); + let optional_pair = db.alloc(Type::Union(vec![pair, types.nil])); + assert_eq!( + format!("{}", db.check(optional_pair, types.nil).unwrap()), + "(and (not (l val)) (= val ()))" + ); + assert_eq!( + format!("{}", db.check(optional_pair, pair).unwrap()), + "(and (l val) 1)" + ); + } + #[test] fn check_any_pair_bytes32_pair_int_nil() { let mut db = TypeSystem::new(); @@ -402,31 +411,41 @@ mod tests { let int_nil_pair = db.alloc(Type::Pair(types.int, types.nil)); let ty = db.alloc(Type::Pair(types.bytes32, int_nil_pair)); + assert_eq!(format!("{}", db.check(types.any, ty).unwrap()), "(and (l val) (all (and (not (l (f val))) (= (strlen (f val)) 32)) (and (l (r val)) (all (not (l (f (r val)))) (and (not (l (r (r val)))) (= (r (r val)) ()))))))"); + } + + #[test] + fn check_complex_type_unions() { + let mut db = TypeSystem::new(); + let types = StandardTypes::alloc(&mut db); + + let int_nil_pair = db.alloc(Type::Pair(types.int, types.nil)); + let bytes32_pair = db.alloc(Type::Pair(types.bytes32, types.bytes32)); + let complex_pair = db.alloc(Type::Pair(int_nil_pair, bytes32_pair)); + + let lhs = db.alloc(Type::Union(vec![ + types.bytes32, + types.public_key, + types.nil, + complex_pair, + int_nil_pair, + bytes32_pair, + types.bool, + ])); + + let rhs = db.alloc(Type::Union(vec![ + types.bytes32, + bytes32_pair, + types.nil, + complex_pair, + ])); + assert_eq!( - format!("{}", db.check(types.any, ty).unwrap()), - trim( - " - (and - (l val) - (all - (and - (not (l (f val))) - (= (stlren (f val)) 32) - ) - (and - (l (r val)) - (all - (not (l (f (r val)))) - (and - (not (l (r (r val)))) - (= (r (r val)) ()) - ) - ) - ) - ) - ) - " - ) + format!("{}", db.check(lhs, rhs).unwrap()), + "(or (and (not (l val)) (= (strlen val) 32)) (and (l val) (all (and (not (l (f val))) (= (strlen (f val)) 32)) (and (not (l (r val))) (= (strlen (r val)) 32)))) (and (not (l val)) (= val ())) (and (l val) (all (and (l (f val)) 1) (and (l (r val)) 1))))" + ); + assert_eq!(format!("{}", db.check(types.any, rhs).unwrap()), + "(or (and (not (l val)) (= (strlen val) 32)) (and (l val) (all (and (not (l (f val))) (= (strlen (f val)) 32)) (and (not (l (r val))) (= (strlen (r val)) 32)))) (and (not (l val)) (= val ())) (and (l val) (all (and (l (f val)) (all (not (l (f (f val)))) (and (not (l (r (f val)))) (= (r (f val)) ())))) (and (l (r val)) (all (and (not (l (f (r val)))) (= (strlen (f (r val))) 32)) (and (not (l (r (r val)))) (= (strlen (r (r val))) 32)))))))" ); } } diff --git a/main.sym b/main.sym new file mode 100644 index 0000000..521825a --- /dev/null +++ b/main.sym @@ -0,0 +1 @@ +{"678511f57ffc5dcff6aed60184835c789556756f551ff7960ea272208db78842":"and_","2dfe28ca44551d031d5eb2a43fa9e7fa2d0d6c82d1c3355daaccf50e0309e3e0":"or_","6a01676bfb72acb4c2fdfbf3e7b177a6679619f79c8d4f951729b2ac4bc62e85":"and","0373418bd7bf23dbc1e74d65f7a9ddfadfd16bb253bab741a5c880e30cde4566":"or"} \ No newline at end of file From 0fb848d16fb1e67d143706af32d431cb57c86df0 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 19 Jul 2024 01:24:33 -0400 Subject: [PATCH 008/100] Reformat --- crates/rue-typing/src/check.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 8323f74..6a08c62 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -411,7 +411,10 @@ mod tests { let int_nil_pair = db.alloc(Type::Pair(types.int, types.nil)); let ty = db.alloc(Type::Pair(types.bytes32, int_nil_pair)); - assert_eq!(format!("{}", db.check(types.any, ty).unwrap()), "(and (l val) (all (and (not (l (f val))) (= (strlen (f val)) 32)) (and (l (r val)) (all (not (l (f (r val)))) (and (not (l (r (r val)))) (= (r (r val)) ()))))))"); + assert_eq!( + format!("{}", db.check(types.any, ty).unwrap()), + "(and (l val) (all (and (not (l (f val))) (= (strlen (f val)) 32)) (and (l (r val)) (all (not (l (f (r val)))) (and (not (l (r (r val)))) (= (r (r val)) ()))))))" + ); } #[test] From 00af3201000a6c729b720725a66e26401db5b887 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 19 Jul 2024 01:25:11 -0400 Subject: [PATCH 009/100] unpossible :) --- crates/rue-typing/src/check.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 6a08c62..5751018 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -357,6 +357,16 @@ mod tests { use super::*; + #[test] + fn check_incompatible() { + let mut db = TypeSystem::new(); + let types = StandardTypes::alloc(&mut db); + assert!(matches!( + db.check(types.bytes32, types.public_key).unwrap_err(), + CheckError::Impossible(..) + )); + } + #[test] fn check_any_bytes32() { let mut db = TypeSystem::new(); From d39d550c93add62687d2786ec55800bf4a2cbd01 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 19 Jul 2024 15:49:21 -0400 Subject: [PATCH 010/100] Difference and testing --- crates/rue-typing/src/check.rs | 93 ++++++++++- crates/rue-typing/src/standard_types.rs | 3 + crates/rue-typing/src/ty.rs | 206 ++++++++++++++++++++++-- crates/rue-typing/src/type_system.rs | 8 +- 4 files changed, 296 insertions(+), 14 deletions(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 5751018..ecc6770 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -57,6 +57,10 @@ where (Type::Unknown, _) | (_, Type::Unknown) => Check::None, + // TODO: Are these correct? + (Type::Never, _) => return Err(CheckError::Impossible(lhs, rhs)), + (_, Type::Never) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bytes, Type::Bytes) => Check::None, (Type::Bytes32, Type::Bytes32) => Check::None, (Type::PublicKey, Type::PublicKey) => Check::None, @@ -173,6 +177,10 @@ where items.extend(child_items); } Type::Unknown => {} + // TODO: Is this correct? + Type::Never => { + length -= 1; + } Type::Bytes | Type::Int => { atom_count += 1; } @@ -210,6 +218,8 @@ where Ok(match types.get(rhs) { Type::Unknown => Check::None, + // TODO: Is this correct? + Type::Never => return Err(CheckError::Impossible(original_type_id, rhs)), Type::Ref(..) => unreachable!(), Type::Union(..) => unreachable!(), Type::Bytes if always_atom => Check::None, @@ -353,7 +363,7 @@ fn fmt_check(check: &Check, f: &mut fmt::Formatter<'_>, path: &mut Vec Self { let unknown = type_system.alloc(Type::Unknown); + let never = type_system.alloc(Type::Never); let bytes = type_system.alloc(Type::Bytes); let bytes32 = type_system.alloc(Type::Bytes32); let public_key = type_system.alloc(Type::PublicKey); @@ -28,6 +30,7 @@ impl StandardTypes { Self { unknown, + never, any, bytes, bytes32, diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs index d9e61e3..07dc084 100644 --- a/crates/rue-typing/src/ty.rs +++ b/crates/rue-typing/src/ty.rs @@ -4,11 +4,12 @@ use std::{ hash::BuildHasher, }; -use crate::{Comparison, TypeId, TypeSystem}; +use crate::{Comparison, StandardTypes, TypeId, TypeSystem}; #[derive(Debug, Clone)] pub enum Type { Unknown, + Never, Bytes, Bytes32, PublicKey, @@ -38,24 +39,68 @@ where (Type::Unknown, _) | (_, Type::Unknown) => Comparison::Assignable, - (Type::Union(lhs), _) => { + // TODO: Is this correct? + (Type::Never, _) => Comparison::Assignable, + (_, Type::Never) => Comparison::Superset, + + (Type::Union(items), _) => { + let items = items.clone(); let mut result = Comparison::Assignable; - for lhs in lhs { - let comparison = compare_type(types, *lhs, rhs, visited); - result = max(result, comparison); + let mut incompatible_count = 0; + + let length = items.len(); + + for item in items { + let cmp = compare_type(types, item, rhs, visited); + result = max(result, cmp); + if cmp == Comparison::Incompatible { + incompatible_count += 1; + } + } + + if incompatible_count == length { + Comparison::Incompatible + } else { + min(result, Comparison::Superset) } - result } - (_, Type::Union(rhs)) => { - let mut result = Comparison::Assignable; - for rhs in rhs { - let comparison = compare_type(types, lhs, *rhs, visited); - result = min(result, comparison); + (_, Type::Union(items)) => { + let items = items.clone(); + let mut result = Comparison::Incompatible; + + for item in items { + let cmp = compare_type(types, lhs, item, visited); + result = min(result, cmp); } - result + + max(result, Comparison::Assignable) } + // (Type::Union(lhs), _) => { + // let mut results = Vec::new(); + // for lhs in lhs { + // let comparison = compare_type(types, *lhs, rhs, visited); + // results.push(comparison); + // } + // if results.iter().all(|cmp| cmp == &Comparison::Incompatible) { + // Comparison::Incompatible + // } else { + // max( + // min(*results.iter().max().unwrap(), Comparison::Superset), + // Comparison::Assignable, + // ) + // } + // } + + // (_, Type::Union(rhs)) => { + // let mut result = Comparison::Assignable; + // for rhs in rhs { + // let comparison = compare_type(types, lhs, *rhs, visited); + // result = min(result, comparison); + // } + // max(result, Comparison::Assignable) + // } (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { let first = compare_type(types, *lhs_first, *rhs_first, visited); let rest = compare_type(types, *lhs_rest, *rhs_rest, visited); @@ -109,3 +154,140 @@ where comparison } + +pub fn difference_type( + types: &mut TypeSystem, + std: &StandardTypes, + lhs: TypeId, + rhs: TypeId, + visited: &mut HashSet<(TypeId, TypeId), S>, +) -> TypeId +where + S: BuildHasher, +{ + if !visited.insert((lhs, rhs)) { + return lhs; + } + + let result = match (types.get(lhs), types.get(rhs)) { + (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), + + (Type::Unknown, _) | (_, Type::Unknown) => lhs, + + (Type::Never, _) => std.never, + (_, Type::Never) => lhs, + + (Type::Bytes, Type::Bytes) => std.never, + (Type::Bytes32, Type::Bytes32) => std.never, + (Type::PublicKey, Type::PublicKey) => std.never, + (Type::Int, Type::Int) => std.never, + (Type::Bool, Type::Bool) => std.never, + (Type::Nil, Type::Nil) => std.never, + + (Type::Int, Type::Bytes32) => lhs, + (Type::Int, Type::PublicKey) => lhs, + (Type::Int, Type::Bytes) => std.never, + (Type::Int, Type::Bool) => lhs, + (Type::Int, Type::Nil) => lhs, + + (Type::Bytes, Type::Bytes32) => lhs, + (Type::Bytes, Type::PublicKey) => lhs, + (Type::Bytes, Type::Int) => std.never, + (Type::Bytes, Type::Bool) => lhs, + (Type::Bytes, Type::Nil) => lhs, + + (Type::Bytes32, Type::PublicKey) => lhs, + (Type::Bytes32, Type::Bytes) => std.never, + (Type::Bytes32, Type::Int) => std.never, + (Type::Bytes32, Type::Bool) => lhs, + (Type::Bytes32, Type::Nil) => lhs, + + (Type::PublicKey, Type::Bytes32) => lhs, + (Type::PublicKey, Type::Bytes) => std.never, + (Type::PublicKey, Type::Int) => std.never, + (Type::PublicKey, Type::Bool) => lhs, + (Type::PublicKey, Type::Nil) => lhs, + + (Type::Bool, Type::Bytes32) => lhs, + (Type::Bool, Type::PublicKey) => lhs, + (Type::Bool, Type::Bytes) => std.never, + (Type::Bool, Type::Int) => std.never, + (Type::Bool, Type::Nil) => lhs, + + (Type::Nil, Type::Bytes32) => lhs, + (Type::Nil, Type::PublicKey) => lhs, + (Type::Nil, Type::Bytes) => std.never, + (Type::Nil, Type::Int) => std.never, + (Type::Nil, Type::Bool) => std.never, + + (Type::Bytes, Type::Pair(..)) => lhs, + (Type::Bytes32, Type::Pair(..)) => lhs, + (Type::PublicKey, Type::Pair(..)) => lhs, + (Type::Int, Type::Pair(..)) => lhs, + (Type::Bool, Type::Pair(..)) => lhs, + (Type::Nil, Type::Pair(..)) => lhs, + + (Type::Pair(..), Type::Bytes) => lhs, + (Type::Pair(..), Type::Bytes32) => lhs, + (Type::Pair(..), Type::PublicKey) => lhs, + (Type::Pair(..), Type::Int) => lhs, + (Type::Pair(..), Type::Bool) => lhs, + (Type::Pair(..), Type::Nil) => lhs, + + (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { + 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); + + if matches!( + (types.get(first), types.get(first)), + (Type::Never, Type::Never) + ) { + std.never + } else if first == lhs_first && rest == lhs_rest { + lhs + } else { + types.alloc(Type::Pair(first, rest)) + } + } + + (Type::Union(items), _) => { + let items = items.clone(); + + let mut result = Vec::new(); + + for item in &items { + let item = difference_type(types, std, *item, rhs, visited); + if matches!(types.get(item), Type::Never) { + continue; + } + result.push(item); + } + + if result.is_empty() { + std.never + } else if result.len() == 1 { + result[0] + } else if result == items { + lhs + } else { + types.alloc(Type::Union(result)) + } + } + + (_, Type::Union(items)) => { + let items = items.clone(); + let mut lhs = lhs; + for item in items { + lhs = difference_type(types, std, lhs, item, visited); + } + lhs + } + }; + + visited.remove(&(lhs, rhs)); + + result +} diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index ef5c324..80647d5 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -2,7 +2,9 @@ use std::collections::HashSet; use id_arena::{Arena, Id}; -use crate::{check_type, compare_type, Check, CheckError, Comparison, Type}; +use crate::{ + check_type, compare_type, difference_type, Check, CheckError, Comparison, StandardTypes, Type, +}; pub type TypeId = Id; @@ -41,4 +43,8 @@ impl TypeSystem { pub fn check(&self, lhs: TypeId, rhs: TypeId) -> Result { check_type(self, lhs, rhs, &mut HashSet::new()) } + + pub fn difference(&mut self, std: &StandardTypes, lhs: TypeId, rhs: TypeId) -> TypeId { + difference_type(self, std, lhs, rhs, &mut HashSet::new()) + } } From f759737591976a826ad5ada3e7682e1f0603ae10 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 19 Jul 2024 16:30:19 -0400 Subject: [PATCH 011/100] Optimize checks --- crates/rue-typing/src/check.rs | 167 ++++++++++++++++++++++++++- crates/rue-typing/src/type_system.rs | 5 +- 2 files changed, 168 insertions(+), 4 deletions(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index ecc6770..8d520a0 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -261,6 +261,169 @@ where }) } +pub(crate) fn simplify_check(check: Check) -> Check { + match check { + Check::None => Check::None, + Check::IsAtom => Check::IsAtom, + Check::IsPair => Check::IsPair, + Check::IsBool => Check::IsBool, + Check::IsNil => Check::IsNil, + Check::Length(len) => { + if len == 0 { + Check::IsNil + } else { + Check::Length(len) + } + } + Check::And(items) => { + let mut result = Vec::new(); + + let mut is_atom = false; + let mut is_pair = false; + let mut is_bool = false; + let mut is_nil = false; + let mut length = false; + + let mut items: VecDeque<_> = items.into(); + + while !items.is_empty() { + let item = simplify_check(items.pop_front().unwrap()); + + match item { + Check::None => continue, + Check::IsAtom => { + if is_atom { + continue; + } + is_atom = true; + } + Check::IsPair => { + if is_pair { + continue; + } + is_pair = true; + } + Check::IsBool => { + if is_bool { + continue; + } + is_bool = true; + } + Check::IsNil => { + if is_nil { + continue; + } + is_nil = true; + } + Check::Length(..) => { + if length { + continue; + } + length = false; + } + Check::And(children) => { + items.extend(children); + continue; + } + _ => {} + } + + result.push(item); + } + + if result.is_empty() { + Check::None + } else if result.len() == 1 { + result.remove(0) + } else { + Check::And(result) + } + } + Check::Or(items) => { + let mut result = Vec::new(); + let mut atoms: Vec = Vec::new(); + let mut pairs: Vec = Vec::new(); + + let mut items: VecDeque<_> = items.into(); + + while !items.is_empty() { + let item = simplify_check(items.pop_front().unwrap()); + + match item { + Check::And(children) => { + match children + .iter() + .find(|child| matches!(child, Check::IsAtom | Check::IsPair)) + { + Some(Check::IsAtom) => { + atoms.push(Check::And( + children + .into_iter() + .filter(|child| *child != Check::IsAtom) + .collect(), + )); + continue; + } + Some(Check::IsPair) => { + pairs.push(Check::And( + children + .into_iter() + .filter(|child| *child != Check::IsPair) + .collect(), + )); + continue; + } + _ => {} + } + } + Check::Or(children) => { + items.extend(children); + continue; + } + _ => {} + } + } + + if !atoms.is_empty() && !pairs.is_empty() { + if atoms.len() > pairs.len() { + result.push(Check::If( + Box::new(Check::IsAtom), + Box::new(Check::Or(atoms)), + Box::new(Check::Or(pairs)), + )); + } else { + result.push(Check::If( + Box::new(Check::IsPair), + Box::new(Check::Or(pairs)), + Box::new(Check::Or(atoms)), + )); + } + } else if atoms.is_empty() { + result.push(Check::And(vec![Check::IsPair, Check::Or(pairs)])); + } else if pairs.is_empty() { + result.push(Check::And(vec![Check::IsAtom, Check::Or(atoms)])); + } + + if result.len() == 1 { + result.remove(0) + } else { + Check::Or(result) + } + } + Check::If(cond, then, else_) => { + let cond = simplify_check(*cond); + let then = simplify_check(*then); + let else_ = simplify_check(*else_); + Check::If(Box::new(cond), Box::new(then), Box::new(else_)) + } + Check::Pair(first, rest) => { + let first = simplify_check(*first); + let rest = simplify_check(*rest); + Check::Pair(Box::new(first), Box::new(rest)) + } + } +} + fn fmt_val(f: &mut fmt::Formatter<'_>, path: &[CheckPath]) -> fmt::Result { for path in path.iter().rev() { match path { @@ -532,7 +695,7 @@ mod tests { assert_eq!( format!("{}", db.check(lhs, rhs).unwrap()), - "(or (and (not (l val)) (= (strlen val) 32)) (and (l val) (all (and (not (l (f val))) (= (strlen (f val)) 32)) (and (not (l (r val))) (= (strlen (r val)) 32)))) (and (not (l val)) (= val ())) (and (l val) (all (and (l (f val)) 1) (and (l (r val)) 1))))" + "(if (l val) (or (and (all (and (not (l (f val))) (= (strlen (f val)) 32)) (and (not (l (r val))) (= (strlen (r val)) 32)))) (and (all (and (l (f val)) 1) (and (l (r val)) 1)))) (or (and (= (strlen val) 32)) (and (= val ()))))" ); assert_eq!(db.compare(lhs, rhs), Comparison::Superset); @@ -544,7 +707,7 @@ mod tests { ); assert_eq!(format!("{}", db.check(types.any, rhs).unwrap()), - "(or (and (not (l val)) (= (strlen val) 32)) (and (l val) (all (and (not (l (f val))) (= (strlen (f val)) 32)) (and (not (l (r val))) (= (strlen (r val)) 32)))) (and (not (l val)) (= val ())) (and (l val) (all (and (l (f val)) (all (not (l (f (f val)))) (and (not (l (r (f val)))) (= (r (f val)) ())))) (and (l (r val)) (all (and (not (l (f (r val)))) (= (strlen (f (r val))) 32)) (and (not (l (r (r val)))) (= (strlen (r (r val))) 32)))))))" + "(if (l val) (or (and (all (and (not (l (f val))) (= (strlen (f val)) 32)) (and (not (l (r val))) (= (strlen (r val)) 32)))) (and (all (and (l (f val)) (all (not (l (f (f val)))) (and (not (l (r (f val)))) (= (r (f val)) ())))) (and (l (r val)) (all (and (not (l (f (r val)))) (= (strlen (f (r val))) 32)) (and (not (l (r (r val)))) (= (strlen (r (r val))) 32))))))) (or (and (= (strlen val) 32)) (and (= val ()))))" ); assert_eq!(db.compare(types.any, rhs), Comparison::Superset); diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index 80647d5..98ef7ed 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -3,7 +3,8 @@ use std::collections::HashSet; use id_arena::{Arena, Id}; use crate::{ - check_type, compare_type, difference_type, Check, CheckError, Comparison, StandardTypes, Type, + check_type, compare_type, difference_type, simplify_check, Check, CheckError, Comparison, + StandardTypes, Type, }; pub type TypeId = Id; @@ -41,7 +42,7 @@ impl TypeSystem { } pub fn check(&self, lhs: TypeId, rhs: TypeId) -> Result { - check_type(self, lhs, rhs, &mut HashSet::new()) + check_type(self, lhs, rhs, &mut HashSet::new()).map(simplify_check) } pub fn difference(&mut self, std: &StandardTypes, lhs: TypeId, rhs: TypeId) -> TypeId { From 3648b5c3ee26d69e5d069140a8e5ea6994632812 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 19 Jul 2024 16:38:00 -0400 Subject: [PATCH 012/100] Remove comments --- crates/rue-typing/src/check.rs | 5 +---- crates/rue-typing/src/ty.rs | 25 ------------------------- 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 8d520a0..2af7efd 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -57,8 +57,7 @@ where (Type::Unknown, _) | (_, Type::Unknown) => Check::None, - // TODO: Are these correct? - (Type::Never, _) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Never, _) => Check::None, (_, Type::Never) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Bytes, Type::Bytes) => Check::None, @@ -177,7 +176,6 @@ where items.extend(child_items); } Type::Unknown => {} - // TODO: Is this correct? Type::Never => { length -= 1; } @@ -218,7 +216,6 @@ where Ok(match types.get(rhs) { Type::Unknown => Check::None, - // TODO: Is this correct? Type::Never => return Err(CheckError::Impossible(original_type_id, rhs)), Type::Ref(..) => unreachable!(), Type::Union(..) => unreachable!(), diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs index 07dc084..a8461b6 100644 --- a/crates/rue-typing/src/ty.rs +++ b/crates/rue-typing/src/ty.rs @@ -39,7 +39,6 @@ where (Type::Unknown, _) | (_, Type::Unknown) => Comparison::Assignable, - // TODO: Is this correct? (Type::Never, _) => Comparison::Assignable, (_, Type::Never) => Comparison::Superset, @@ -77,30 +76,6 @@ where max(result, Comparison::Assignable) } - // (Type::Union(lhs), _) => { - // let mut results = Vec::new(); - // for lhs in lhs { - // let comparison = compare_type(types, *lhs, rhs, visited); - // results.push(comparison); - // } - // if results.iter().all(|cmp| cmp == &Comparison::Incompatible) { - // Comparison::Incompatible - // } else { - // max( - // min(*results.iter().max().unwrap(), Comparison::Superset), - // Comparison::Assignable, - // ) - // } - // } - - // (_, Type::Union(rhs)) => { - // let mut result = Comparison::Assignable; - // for rhs in rhs { - // let comparison = compare_type(types, lhs, *rhs, visited); - // result = min(result, comparison); - // } - // max(result, Comparison::Assignable) - // } (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { let first = compare_type(types, *lhs_first, *rhs_first, visited); let rest = compare_type(types, *lhs_rest, *rhs_rest, visited); From 89402fca635cc5d734bdc2687a80f81a187bca20 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 19 Jul 2024 16:39:00 -0400 Subject: [PATCH 013/100] Type path --- crates/rue-typing/src/check.rs | 24 +++++++++--------------- crates/rue-typing/src/lib.rs | 2 ++ crates/rue-typing/src/type_path.rs | 5 +++++ 3 files changed, 16 insertions(+), 15 deletions(-) create mode 100644 crates/rue-typing/src/type_path.rs diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 2af7efd..30bf99f 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -4,7 +4,7 @@ use std::{ hash::BuildHasher, }; -use crate::{Type, TypeId, TypeSystem}; +use crate::{Type, TypeId, TypePath, TypeSystem}; #[derive(Debug, Clone, Copy)] pub enum CheckError { @@ -26,12 +26,6 @@ pub enum Check { Pair(Box, Box), } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum CheckPath { - First, - Rest, -} - impl fmt::Display for Check { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt_check(self, f, &mut Vec::new()) @@ -421,11 +415,11 @@ pub(crate) fn simplify_check(check: Check) -> Check { } } -fn fmt_val(f: &mut fmt::Formatter<'_>, path: &[CheckPath]) -> fmt::Result { +fn fmt_val(f: &mut fmt::Formatter<'_>, path: &[TypePath]) -> fmt::Result { for path in path.iter().rev() { match path { - CheckPath::First => write!(f, "(f ")?, - CheckPath::Rest => write!(f, "(r ")?, + TypePath::First => write!(f, "(f ")?, + TypePath::Rest => write!(f, "(r ")?, } } write!(f, "val")?; @@ -435,7 +429,7 @@ fn fmt_val(f: &mut fmt::Formatter<'_>, path: &[CheckPath]) -> fmt::Result { Ok(()) } -fn fmt_check(check: &Check, f: &mut fmt::Formatter<'_>, path: &mut Vec) -> fmt::Result { +fn fmt_check(check: &Check, f: &mut fmt::Formatter<'_>, path: &mut Vec) -> fmt::Result { match check { Check::None => write!(f, "1"), Check::IsPair => { @@ -496,21 +490,21 @@ fn fmt_check(check: &Check, f: &mut fmt::Formatter<'_>, path: &mut Vec Date: Fri, 19 Jul 2024 22:48:15 -0400 Subject: [PATCH 014/100] Generics maybe --- crates/rue-typing/src/check.rs | 9 +++- crates/rue-typing/src/ty.rs | 76 +++++++++++++++++++++++----- crates/rue-typing/src/type_system.rs | 36 +++++++++++-- 3 files changed, 102 insertions(+), 19 deletions(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 30bf99f..ef0d6df 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -49,7 +49,12 @@ where let check = match (types.get(lhs), types.get(rhs)) { (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), - (Type::Unknown, _) | (_, Type::Unknown) => Check::None, + // TODO: Implement generic type checking? + (Type::Generic, _) => Check::None, + (_, Type::Generic) => Check::None, + + (Type::Unknown, _) => Check::None, + (_, Type::Unknown) => Check::None, (Type::Never, _) => Check::None, (_, Type::Never) => return Err(CheckError::Impossible(lhs, rhs)), @@ -169,6 +174,7 @@ where Type::Union(child_items) => { items.extend(child_items); } + Type::Generic => return Err(CheckError::Impossible(item, rhs)), Type::Unknown => {} Type::Never => { length -= 1; @@ -210,6 +216,7 @@ where Ok(match types.get(rhs) { Type::Unknown => Check::None, + Type::Generic => return Err(CheckError::Impossible(original_type_id, rhs)), Type::Never => return Err(CheckError::Impossible(original_type_id, rhs)), Type::Ref(..) => unreachable!(), Type::Union(..) => unreachable!(), diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs index a8461b6..407967e 100644 --- a/crates/rue-typing/src/ty.rs +++ b/crates/rue-typing/src/ty.rs @@ -1,14 +1,15 @@ use std::{ cmp::{max, min}, - collections::HashSet, + collections::{HashMap, HashSet}, hash::BuildHasher, }; -use crate::{Comparison, StandardTypes, TypeId, TypeSystem}; +use crate::{Comparison, StandardTypes, TypeId, TypePath, TypeSystem}; #[derive(Debug, Clone)] pub enum Type { Unknown, + Generic, Never, Bytes, Bytes32, @@ -21,22 +22,44 @@ pub enum Type { Ref(TypeId), } -pub(crate) fn compare_type( +pub(crate) struct ComparisonContext<'a> { + pub visited: HashSet<(TypeId, TypeId)>, + pub generic_type_stack: &'a mut Vec>, + pub infer_generics: bool, +} + +pub(crate) fn compare_type( types: &TypeSystem, lhs: TypeId, rhs: TypeId, - visited: &mut HashSet<(TypeId, TypeId), S>, -) -> Comparison -where - S: BuildHasher, -{ - if !visited.insert((lhs, rhs)) { + ctx: &mut ComparisonContext<'_>, +) -> Comparison { + if !ctx.visited.insert((lhs, rhs)) { return Comparison::Assignable; } let comparison = match (types.get(lhs), types.get(rhs)) { (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), + (_, Type::Generic) => { + let mut type_id = None; + + for generics in ctx.generic_type_stack.iter().rev() { + if let Some(&generic) = generics.get(&rhs) { + type_id = Some(generic); + } + } + + if let Some(type_id) = type_id { + compare_type(types, lhs, type_id, ctx) + } else if ctx.infer_generics { + ctx.generic_type_stack.last_mut().unwrap().insert(rhs, lhs); + Comparison::Assignable + } else { + Comparison::Incompatible + } + } + (Type::Unknown, _) | (_, Type::Unknown) => Comparison::Assignable, (Type::Never, _) => Comparison::Assignable, @@ -50,7 +73,7 @@ where let length = items.len(); for item in items { - let cmp = compare_type(types, item, rhs, visited); + let cmp = compare_type(types, item, rhs, ctx); result = max(result, cmp); if cmp == Comparison::Incompatible { incompatible_count += 1; @@ -69,7 +92,7 @@ where let mut result = Comparison::Incompatible; for item in items { - let cmp = compare_type(types, lhs, item, visited); + let cmp = compare_type(types, lhs, item, ctx); result = min(result, cmp); } @@ -77,8 +100,8 @@ where } (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { - let first = compare_type(types, *lhs_first, *rhs_first, visited); - let rest = compare_type(types, *lhs_rest, *rhs_rest, visited); + let first = compare_type(types, *lhs_first, *rhs_first, ctx); + let rest = compare_type(types, *lhs_rest, *rhs_rest, ctx); max(first, rest) } (Type::Pair(..), _) | (_, Type::Pair(..)) => Comparison::Incompatible, @@ -123,9 +146,11 @@ where (Type::Bool, Type::Nil) => Comparison::Incompatible, (Type::Nil, Type::Bytes32) => Comparison::Incompatible, (Type::Nil, Type::PublicKey) => Comparison::Incompatible, + + (Type::Generic, _) => Comparison::Incompatible, }; - visited.remove(&(lhs, rhs)); + ctx.visited.remove(&(lhs, rhs)); comparison } @@ -147,6 +172,10 @@ where let result = match (types.get(lhs), types.get(rhs)) { (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), + // TODO: Not sure how to implement this yet. + (Type::Generic, _) => lhs, + (_, Type::Generic) => lhs, + (Type::Unknown, _) | (_, Type::Unknown) => lhs, (Type::Never, _) => std.never, @@ -266,3 +295,22 @@ where result } + +pub(crate) fn replace_type( + types: &mut TypeSystem, + type_id: TypeId, + replace_type_id: TypeId, + path: &[TypePath], +) -> TypeId { + if path.is_empty() { + return replace_type_id; + } + + match types.get(type_id) { + Type::Pair(first, rest) => match path[0] { + TypePath::First => replace_type(types, *first, replace_type_id, &path[1..]), + TypePath::Rest => replace_type(types, *rest, replace_type_id, &path[1..]), + }, + _ => type_id, + } +} diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index 98ef7ed..b2cb323 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -1,10 +1,10 @@ -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use id_arena::{Arena, Id}; use crate::{ - check_type, compare_type, difference_type, simplify_check, Check, CheckError, Comparison, - StandardTypes, Type, + check_type, compare_type, difference_type, replace_type, simplify_check, Check, CheckError, + Comparison, ComparisonContext, StandardTypes, Type, TypePath, }; pub type TypeId = Id; @@ -38,7 +38,26 @@ impl TypeSystem { } pub fn compare(&self, lhs: TypeId, rhs: TypeId) -> Comparison { - compare_type(self, lhs, rhs, &mut HashSet::new()) + self.compare_with_generics(lhs, rhs, &mut Vec::new(), false) + } + + pub fn compare_with_generics( + &self, + lhs: TypeId, + rhs: TypeId, + generic_type_stack: &mut Vec>, + infer_generics: bool, + ) -> Comparison { + compare_type( + self, + lhs, + rhs, + &mut ComparisonContext { + visited: HashSet::new(), + generic_type_stack, + infer_generics, + }, + ) } pub fn check(&self, lhs: TypeId, rhs: TypeId) -> Result { @@ -48,4 +67,13 @@ impl TypeSystem { pub fn difference(&mut self, std: &StandardTypes, lhs: TypeId, rhs: TypeId) -> TypeId { difference_type(self, std, lhs, rhs, &mut HashSet::new()) } + + pub fn replace( + &mut self, + type_id: TypeId, + replace_type_id: TypeId, + path: &[TypePath], + ) -> TypeId { + replace_type(self, type_id, replace_type_id, path) + } } From 01b60bb197411a2a9d3c4eb4185d5a38899e5561 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 19 Jul 2024 23:35:41 -0400 Subject: [PATCH 015/100] Allegedly add aliases --- crates/rue-typing/src/check.rs | 11 +- crates/rue-typing/src/lib.rs | 2 + crates/rue-typing/src/semantic_types.rs | 19 ++++ crates/rue-typing/src/ty.rs | 142 ++++++++++++++++++++---- crates/rue-typing/src/type_system.rs | 26 ++++- 5 files changed, 171 insertions(+), 29 deletions(-) create mode 100644 crates/rue-typing/src/semantic_types.rs diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index ef0d6df..9e6bba6 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -48,6 +48,8 @@ where let check = match (types.get(lhs), types.get(rhs)) { (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), + (Type::Lazy(..), _) | (_, Type::Lazy(..)) => unreachable!(), + (Type::Alias(..), _) | (_, Type::Alias(..)) => unreachable!(), // TODO: Implement generic type checking? (Type::Generic, _) => Check::None, @@ -171,6 +173,9 @@ where match types.get(item) { Type::Ref(..) => unreachable!(), + Type::Lazy(..) => unreachable!(), + Type::Alias(..) => unreachable!(), + Type::Union(child_items) => { items.extend(child_items); } @@ -215,11 +220,13 @@ where let always_public_key = public_key_count == length; Ok(match types.get(rhs) { + Type::Ref(..) => unreachable!(), + Type::Lazy(..) => unreachable!(), + Type::Alias(..) => unreachable!(), + Type::Union(..) => unreachable!(), Type::Unknown => Check::None, Type::Generic => return Err(CheckError::Impossible(original_type_id, rhs)), Type::Never => return Err(CheckError::Impossible(original_type_id, rhs)), - Type::Ref(..) => unreachable!(), - Type::Union(..) => unreachable!(), Type::Bytes if always_atom => Check::None, Type::Int if always_atom => Check::None, Type::Bool if always_bool => Check::None, diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index dbb7b1f..59f5f56 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -1,5 +1,6 @@ mod check; mod comparison; +mod semantic_types; mod standard_types; mod ty; mod type_path; @@ -7,6 +8,7 @@ mod type_system; pub use check::*; pub use comparison::*; +pub use semantic_types::*; pub use standard_types::*; pub use ty::*; pub use type_path::*; diff --git a/crates/rue-typing/src/semantic_types.rs b/crates/rue-typing/src/semantic_types.rs new file mode 100644 index 0000000..6b0da44 --- /dev/null +++ b/crates/rue-typing/src/semantic_types.rs @@ -0,0 +1,19 @@ +use std::collections::HashMap; + +use crate::TypeId; + +/// Allows you to map generic types lazily without having to resolve them immediately. +/// This prevents stack overflows when resolving generic type definitions that reference themselves. +/// When reducing a type to its structural form, lazy types should be removed. +#[derive(Debug, Clone)] +pub struct Lazy { + pub type_id: TypeId, + pub substitutions: HashMap, +} + +/// Represents an alias to a type with a set of generic parameters that must be mapped prior to use. +#[derive(Debug, Clone)] +pub struct Alias { + pub type_id: TypeId, + pub generic_types: Vec, +} diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs index 407967e..603c2ec 100644 --- a/crates/rue-typing/src/ty.rs +++ b/crates/rue-typing/src/ty.rs @@ -4,7 +4,7 @@ use std::{ hash::BuildHasher, }; -use crate::{Comparison, StandardTypes, TypeId, TypePath, TypeSystem}; +use crate::{Alias, Comparison, Lazy, StandardTypes, TypeId, TypePath, TypeSystem}; #[derive(Debug, Clone)] pub enum Type { @@ -20,12 +20,15 @@ pub enum Type { Pair(TypeId, TypeId), Union(Vec), Ref(TypeId), + Lazy(Lazy), + Alias(Alias), } pub(crate) struct ComparisonContext<'a> { pub visited: HashSet<(TypeId, TypeId)>, - pub generic_type_stack: &'a mut Vec>, - pub infer_generics: bool, + pub substitution_stack: &'a mut Vec>, + pub initial_substitution_length: usize, + pub generic_stack_frame: Option, } pub(crate) fn compare_type( @@ -42,24 +45,44 @@ pub(crate) fn compare_type( (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), (_, Type::Generic) => { - let mut type_id = None; + let mut found = None; - for generics in ctx.generic_type_stack.iter().rev() { - if let Some(&generic) = generics.get(&rhs) { - type_id = Some(generic); + for substititons in ctx.substitution_stack.iter().rev() { + if let Some(&substititon) = substititons.get(&rhs) { + found = Some(substititon); } } - if let Some(type_id) = type_id { - compare_type(types, lhs, type_id, ctx) - } else if ctx.infer_generics { - ctx.generic_type_stack.last_mut().unwrap().insert(rhs, lhs); + if let Some(found) = found { + compare_type(types, lhs, found, ctx) + } else if let Some(generic_stack_frame) = ctx.generic_stack_frame { + ctx.substitution_stack[generic_stack_frame].insert(rhs, lhs); Comparison::Assignable } else { Comparison::Incompatible } } + (Type::Generic, _) => { + let mut found = None; + + for (i, substititons) in ctx.substitution_stack.iter().enumerate().rev() { + if i < ctx.initial_substitution_length { + break; + } + + if let Some(&substititon) = substititons.get(&lhs) { + found = Some(substititon); + } + } + + if let Some(found) = found { + compare_type(types, found, rhs, ctx) + } else { + Comparison::Incompatible + } + } + (Type::Unknown, _) | (_, Type::Unknown) => Comparison::Assignable, (Type::Never, _) => Comparison::Assignable, @@ -147,7 +170,22 @@ pub(crate) fn compare_type( (Type::Nil, Type::Bytes32) => Comparison::Incompatible, (Type::Nil, Type::PublicKey) => Comparison::Incompatible, - (Type::Generic, _) => Comparison::Incompatible, + (Type::Lazy(lazy), _) => { + ctx.substitution_stack.push(lazy.substitutions.clone()); + let result = compare_type(types, lazy.type_id, rhs, ctx); + ctx.substitution_stack.pop().unwrap(); + result + } + + (_, Type::Lazy(lazy)) => { + ctx.substitution_stack.push(lazy.substitutions.clone()); + let result = compare_type(types, lhs, lazy.type_id, ctx); + ctx.substitution_stack.pop().unwrap(); + result + } + + (Type::Alias(alias), _) => compare_type(types, alias.type_id, rhs, ctx), + (_, Type::Alias(alias)) => compare_type(types, lhs, alias.type_id, ctx), }; ctx.visited.remove(&(lhs, rhs)); @@ -155,7 +193,7 @@ pub(crate) fn compare_type( comparison } -pub fn difference_type( +pub(crate) fn difference_type( types: &mut TypeSystem, std: &StandardTypes, lhs: TypeId, @@ -171,13 +209,10 @@ where let result = match (types.get(lhs), types.get(rhs)) { (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), + (Type::Lazy(..), _) | (_, Type::Lazy(..)) => unreachable!(), - // TODO: Not sure how to implement this yet. - (Type::Generic, _) => lhs, - (_, Type::Generic) => lhs, - + (Type::Generic, _) | (_, Type::Generic) => lhs, (Type::Unknown, _) | (_, Type::Unknown) => lhs, - (Type::Never, _) => std.never, (_, Type::Never) => lhs, @@ -245,10 +280,7 @@ where let first = difference_type(types, std, lhs_first, rhs_first, visited); let rest = difference_type(types, std, lhs_rest, rhs_rest, visited); - if matches!( - (types.get(first), types.get(first)), - (Type::Never, Type::Never) - ) { + if matches!(types.get(first), Type::Never) || matches!(types.get(first), Type::Never) { std.never } else if first == lhs_first && rest == lhs_rest { lhs @@ -289,6 +321,9 @@ where } 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), }; visited.remove(&(lhs, rhs)); @@ -314,3 +349,66 @@ pub(crate) fn replace_type( _ => type_id, } } + +pub(crate) fn structural_type( + types: &mut TypeSystem, + type_id: TypeId, + substitutions: &mut HashMap, + visited: &mut HashSet, +) -> TypeId { + if let Some(new_type_id) = substitutions.get(&type_id) { + return *new_type_id; + } + + if !visited.insert(type_id) { + return type_id; + } + + let result = match types.get(type_id) { + Type::Ref(..) => unreachable!(), + Type::Unknown => type_id, + Type::Generic => type_id, + Type::Never => type_id, + Type::Bytes => type_id, + Type::Bytes32 => type_id, + Type::PublicKey => type_id, + Type::Int => type_id, + Type::Bool => type_id, + Type::Nil => type_id, + Type::Pair(first, rest) => { + let (first, rest) = (*first, *rest); + + let new_first = structural_type(types, first, substitutions, visited); + let new_rest = structural_type(types, rest, substitutions, visited); + + if new_first == first && new_rest == rest { + type_id + } else { + types.alloc(Type::Pair(new_first, new_rest)) + } + } + Type::Union(items) => { + let items = items.clone(); + let mut result = Vec::new(); + + for item in &items { + result.push(structural_type(types, *item, substitutions, visited)); + } + + if result == items { + type_id + } else { + types.alloc(Type::Union(result)) + } + } + Type::Lazy(lazy) => { + substitutions.extend(lazy.substitutions.clone()); + structural_type(types, lazy.type_id, substitutions, visited) + } + Type::Alias(alias) => alias.type_id, + }; + + visited.remove(&type_id); + + result +} diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index b2cb323..04df2cb 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -3,8 +3,8 @@ use std::collections::{HashMap, HashSet}; use id_arena::{Arena, Id}; use crate::{ - check_type, compare_type, difference_type, replace_type, simplify_check, Check, CheckError, - Comparison, ComparisonContext, StandardTypes, Type, TypePath, + check_type, compare_type, difference_type, replace_type, simplify_check, structural_type, + Check, CheckError, Comparison, ComparisonContext, StandardTypes, Type, TypePath, }; pub type TypeId = Id; @@ -45,21 +45,37 @@ impl TypeSystem { &self, lhs: TypeId, rhs: TypeId, - generic_type_stack: &mut Vec>, + substitution_stack: &mut Vec>, infer_generics: bool, ) -> Comparison { + let generic_stack_frame = if infer_generics { + Some(substitution_stack.len() - 1) + } else { + None + }; + let initial_substitution_length = substitution_stack.len(); + compare_type( self, lhs, rhs, &mut ComparisonContext { visited: HashSet::new(), - generic_type_stack, - infer_generics, + substitution_stack, + initial_substitution_length, + generic_stack_frame, }, ) } + pub fn structure( + &mut self, + type_id: TypeId, + mut substitutions: HashMap, + ) -> TypeId { + structural_type(self, type_id, &mut substitutions, &mut HashSet::new()) + } + pub fn check(&self, lhs: TypeId, rhs: TypeId) -> Result { check_type(self, lhs, rhs, &mut HashSet::new()).map(simplify_check) } From b844f7a1e01a6f9da1d241853e16cf727fe10c97 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 19 Jul 2024 23:36:52 -0400 Subject: [PATCH 016/100] Remove build hasher --- crates/rue-typing/src/ty.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs index 603c2ec..ad51be5 100644 --- a/crates/rue-typing/src/ty.rs +++ b/crates/rue-typing/src/ty.rs @@ -1,7 +1,6 @@ use std::{ cmp::{max, min}, collections::{HashMap, HashSet}, - hash::BuildHasher, }; use crate::{Alias, Comparison, Lazy, StandardTypes, TypeId, TypePath, TypeSystem}; @@ -193,16 +192,13 @@ pub(crate) fn compare_type( comparison } -pub(crate) fn difference_type( +pub(crate) fn difference_type( types: &mut TypeSystem, std: &StandardTypes, lhs: TypeId, rhs: TypeId, - visited: &mut HashSet<(TypeId, TypeId), S>, -) -> TypeId -where - S: BuildHasher, -{ + visited: &mut HashSet<(TypeId, TypeId)>, +) -> TypeId { if !visited.insert((lhs, rhs)) { return lhs; } From dc15630fd8bec429c371e946545acac70dda101f Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 19 Jul 2024 23:40:57 -0400 Subject: [PATCH 017/100] Factor --- crates/rue-typing/src/comparison.rs | 166 +++++++++++++++ crates/rue-typing/src/difference.rs | 138 +++++++++++++ crates/rue-typing/src/lib.rs | 3 + crates/rue-typing/src/ty.rs | 304 +--------------------------- 4 files changed, 309 insertions(+), 302 deletions(-) create mode 100644 crates/rue-typing/src/difference.rs diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index bc9b47f..c54133f 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -1,3 +1,7 @@ +use std::cmp::{max, min}; + +use crate::{ComparisonContext, Type, TypeId, TypeSystem}; + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Comparison { Equal, @@ -6,3 +10,165 @@ pub enum Comparison { Superset, Incompatible, } + +pub(crate) fn compare_type( + types: &TypeSystem, + lhs: TypeId, + rhs: TypeId, + ctx: &mut ComparisonContext<'_>, +) -> Comparison { + if !ctx.visited.insert((lhs, rhs)) { + return Comparison::Assignable; + } + + let comparison = match (types.get(lhs), types.get(rhs)) { + (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), + + (_, Type::Generic) => { + let mut found = None; + + for substititons in ctx.substitution_stack.iter().rev() { + if let Some(&substititon) = substititons.get(&rhs) { + found = Some(substititon); + } + } + + if let Some(found) = found { + compare_type(types, lhs, found, ctx) + } else if let Some(generic_stack_frame) = ctx.generic_stack_frame { + ctx.substitution_stack[generic_stack_frame].insert(rhs, lhs); + Comparison::Assignable + } else { + Comparison::Incompatible + } + } + + (Type::Generic, _) => { + let mut found = None; + + for (i, substititons) in ctx.substitution_stack.iter().enumerate().rev() { + if i < ctx.initial_substitution_length { + break; + } + + if let Some(&substititon) = substititons.get(&lhs) { + found = Some(substititon); + } + } + + if let Some(found) = found { + compare_type(types, found, rhs, ctx) + } else { + Comparison::Incompatible + } + } + + (Type::Unknown, _) | (_, Type::Unknown) => Comparison::Assignable, + + (Type::Never, _) => Comparison::Assignable, + (_, Type::Never) => Comparison::Superset, + + (Type::Union(items), _) => { + let items = items.clone(); + let mut result = Comparison::Assignable; + let mut incompatible_count = 0; + + let length = items.len(); + + for item in items { + let cmp = compare_type(types, item, rhs, ctx); + result = max(result, cmp); + if cmp == Comparison::Incompatible { + incompatible_count += 1; + } + } + + if incompatible_count == length { + Comparison::Incompatible + } else { + min(result, Comparison::Superset) + } + } + + (_, Type::Union(items)) => { + let items = items.clone(); + let mut result = Comparison::Incompatible; + + for item in items { + let cmp = compare_type(types, lhs, item, ctx); + result = min(result, cmp); + } + + max(result, Comparison::Assignable) + } + + (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { + let first = compare_type(types, *lhs_first, *rhs_first, ctx); + let rest = compare_type(types, *lhs_rest, *rhs_rest, ctx); + max(first, rest) + } + (Type::Pair(..), _) | (_, Type::Pair(..)) => Comparison::Incompatible, + + (Type::Bytes, Type::Bytes) => Comparison::Equal, + (Type::Bytes32, Type::Bytes32) => Comparison::Equal, + (Type::PublicKey, Type::PublicKey) => Comparison::Equal, + (Type::Int, Type::Int) => Comparison::Equal, + (Type::Bool, Type::Bool) => Comparison::Equal, + (Type::Nil, Type::Nil) => Comparison::Equal, + + (Type::Bytes, Type::Bytes32) => Comparison::Superset, + (Type::Bytes, Type::PublicKey) => Comparison::Superset, + (Type::Bytes, Type::Bool) => Comparison::Superset, + (Type::Bytes, Type::Nil) => Comparison::Superset, + (Type::Int, Type::Bytes32) => Comparison::Superset, + (Type::Int, Type::PublicKey) => Comparison::Superset, + (Type::Int, Type::Bool) => Comparison::Superset, + (Type::Int, Type::Nil) => Comparison::Superset, + + (Type::Bytes32, Type::Bytes) => Comparison::Assignable, + (Type::Nil, Type::Bytes) => Comparison::Assignable, + + (Type::Bytes, Type::Int) => Comparison::Castable, + (Type::Bytes32, Type::Int) => Comparison::Castable, + (Type::PublicKey, Type::Bytes) => Comparison::Castable, + (Type::PublicKey, Type::Int) => Comparison::Castable, + (Type::Int, Type::Bytes) => Comparison::Castable, + (Type::Nil, Type::Bool) => Comparison::Castable, + (Type::Nil, Type::Int) => Comparison::Castable, + (Type::Bool, Type::Bytes) => Comparison::Castable, + (Type::Bool, Type::Int) => Comparison::Castable, + + (Type::Bytes32, Type::PublicKey) => Comparison::Incompatible, + (Type::Bytes32, Type::Bool) => Comparison::Incompatible, + (Type::Bytes32, Type::Nil) => Comparison::Incompatible, + (Type::PublicKey, Type::Bytes32) => Comparison::Incompatible, + (Type::PublicKey, Type::Bool) => Comparison::Incompatible, + (Type::PublicKey, Type::Nil) => Comparison::Incompatible, + (Type::Bool, Type::Bytes32) => Comparison::Incompatible, + (Type::Bool, Type::PublicKey) => Comparison::Incompatible, + (Type::Bool, Type::Nil) => Comparison::Incompatible, + (Type::Nil, Type::Bytes32) => Comparison::Incompatible, + (Type::Nil, Type::PublicKey) => Comparison::Incompatible, + + (Type::Lazy(lazy), _) => { + ctx.substitution_stack.push(lazy.substitutions.clone()); + let result = compare_type(types, lazy.type_id, rhs, ctx); + ctx.substitution_stack.pop().unwrap(); + result + } + + (_, Type::Lazy(lazy)) => { + ctx.substitution_stack.push(lazy.substitutions.clone()); + let result = compare_type(types, lhs, lazy.type_id, ctx); + ctx.substitution_stack.pop().unwrap(); + result + } + + (Type::Alias(alias), _) => compare_type(types, alias.type_id, rhs, ctx), + (_, Type::Alias(alias)) => compare_type(types, lhs, alias.type_id, ctx), + }; + + ctx.visited.remove(&(lhs, rhs)); + + comparison +} diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs new file mode 100644 index 0000000..ccc9b4e --- /dev/null +++ b/crates/rue-typing/src/difference.rs @@ -0,0 +1,138 @@ +use std::collections::HashSet; + +use crate::{StandardTypes, Type, TypeId, TypeSystem}; + +pub(crate) fn difference_type( + types: &mut TypeSystem, + std: &StandardTypes, + lhs: TypeId, + rhs: TypeId, + visited: &mut HashSet<(TypeId, TypeId)>, +) -> TypeId { + if !visited.insert((lhs, rhs)) { + return lhs; + } + + let result = match (types.get(lhs), types.get(rhs)) { + (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), + (Type::Lazy(..), _) | (_, Type::Lazy(..)) => unreachable!(), + + (Type::Generic, _) | (_, Type::Generic) => lhs, + (Type::Unknown, _) | (_, Type::Unknown) => lhs, + (Type::Never, _) => std.never, + (_, Type::Never) => lhs, + + (Type::Bytes, Type::Bytes) => std.never, + (Type::Bytes32, Type::Bytes32) => std.never, + (Type::PublicKey, Type::PublicKey) => std.never, + (Type::Int, Type::Int) => std.never, + (Type::Bool, Type::Bool) => std.never, + (Type::Nil, Type::Nil) => std.never, + + (Type::Int, Type::Bytes32) => lhs, + (Type::Int, Type::PublicKey) => lhs, + (Type::Int, Type::Bytes) => std.never, + (Type::Int, Type::Bool) => lhs, + (Type::Int, Type::Nil) => lhs, + + (Type::Bytes, Type::Bytes32) => lhs, + (Type::Bytes, Type::PublicKey) => lhs, + (Type::Bytes, Type::Int) => std.never, + (Type::Bytes, Type::Bool) => lhs, + (Type::Bytes, Type::Nil) => lhs, + + (Type::Bytes32, Type::PublicKey) => lhs, + (Type::Bytes32, Type::Bytes) => std.never, + (Type::Bytes32, Type::Int) => std.never, + (Type::Bytes32, Type::Bool) => lhs, + (Type::Bytes32, Type::Nil) => lhs, + + (Type::PublicKey, Type::Bytes32) => lhs, + (Type::PublicKey, Type::Bytes) => std.never, + (Type::PublicKey, Type::Int) => std.never, + (Type::PublicKey, Type::Bool) => lhs, + (Type::PublicKey, Type::Nil) => lhs, + + (Type::Bool, Type::Bytes32) => lhs, + (Type::Bool, Type::PublicKey) => lhs, + (Type::Bool, Type::Bytes) => std.never, + (Type::Bool, Type::Int) => std.never, + (Type::Bool, Type::Nil) => lhs, + + (Type::Nil, Type::Bytes32) => lhs, + (Type::Nil, Type::PublicKey) => lhs, + (Type::Nil, Type::Bytes) => std.never, + (Type::Nil, Type::Int) => std.never, + (Type::Nil, Type::Bool) => std.never, + + (Type::Bytes, Type::Pair(..)) => lhs, + (Type::Bytes32, Type::Pair(..)) => lhs, + (Type::PublicKey, Type::Pair(..)) => lhs, + (Type::Int, Type::Pair(..)) => lhs, + (Type::Bool, Type::Pair(..)) => lhs, + (Type::Nil, Type::Pair(..)) => lhs, + + (Type::Pair(..), Type::Bytes) => lhs, + (Type::Pair(..), Type::Bytes32) => lhs, + (Type::Pair(..), Type::PublicKey) => lhs, + (Type::Pair(..), Type::Int) => lhs, + (Type::Pair(..), Type::Bool) => lhs, + (Type::Pair(..), Type::Nil) => lhs, + + (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { + 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); + + if matches!(types.get(first), Type::Never) || matches!(types.get(first), Type::Never) { + std.never + } else if first == lhs_first && rest == lhs_rest { + lhs + } else { + types.alloc(Type::Pair(first, rest)) + } + } + + (Type::Union(items), _) => { + let items = items.clone(); + + let mut result = Vec::new(); + + for item in &items { + let item = difference_type(types, std, *item, rhs, visited); + if matches!(types.get(item), Type::Never) { + continue; + } + result.push(item); + } + + if result.is_empty() { + std.never + } else if result.len() == 1 { + result[0] + } else if result == items { + lhs + } else { + types.alloc(Type::Union(result)) + } + } + + (_, Type::Union(items)) => { + let items = items.clone(); + let mut lhs = lhs; + for item in items { + lhs = difference_type(types, std, 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), + }; + + visited.remove(&(lhs, rhs)); + + result +} diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index 59f5f56..ea74ab2 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -1,5 +1,6 @@ mod check; mod comparison; +mod difference; mod semantic_types; mod standard_types; mod ty; @@ -13,3 +14,5 @@ pub use standard_types::*; pub use ty::*; pub use type_path::*; pub use type_system::*; + +pub(crate) use difference::difference_type; diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs index ad51be5..6e8b52c 100644 --- a/crates/rue-typing/src/ty.rs +++ b/crates/rue-typing/src/ty.rs @@ -1,9 +1,6 @@ -use std::{ - cmp::{max, min}, - collections::{HashMap, HashSet}, -}; +use std::collections::{HashMap, HashSet}; -use crate::{Alias, Comparison, Lazy, StandardTypes, TypeId, TypePath, TypeSystem}; +use crate::{Alias, Lazy, TypeId, TypePath, TypeSystem}; #[derive(Debug, Clone)] pub enum Type { @@ -30,303 +27,6 @@ pub(crate) struct ComparisonContext<'a> { pub generic_stack_frame: Option, } -pub(crate) fn compare_type( - types: &TypeSystem, - lhs: TypeId, - rhs: TypeId, - ctx: &mut ComparisonContext<'_>, -) -> Comparison { - if !ctx.visited.insert((lhs, rhs)) { - return Comparison::Assignable; - } - - let comparison = match (types.get(lhs), types.get(rhs)) { - (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), - - (_, Type::Generic) => { - let mut found = None; - - for substititons in ctx.substitution_stack.iter().rev() { - if let Some(&substititon) = substititons.get(&rhs) { - found = Some(substititon); - } - } - - if let Some(found) = found { - compare_type(types, lhs, found, ctx) - } else if let Some(generic_stack_frame) = ctx.generic_stack_frame { - ctx.substitution_stack[generic_stack_frame].insert(rhs, lhs); - Comparison::Assignable - } else { - Comparison::Incompatible - } - } - - (Type::Generic, _) => { - let mut found = None; - - for (i, substititons) in ctx.substitution_stack.iter().enumerate().rev() { - if i < ctx.initial_substitution_length { - break; - } - - if let Some(&substititon) = substititons.get(&lhs) { - found = Some(substititon); - } - } - - if let Some(found) = found { - compare_type(types, found, rhs, ctx) - } else { - Comparison::Incompatible - } - } - - (Type::Unknown, _) | (_, Type::Unknown) => Comparison::Assignable, - - (Type::Never, _) => Comparison::Assignable, - (_, Type::Never) => Comparison::Superset, - - (Type::Union(items), _) => { - let items = items.clone(); - let mut result = Comparison::Assignable; - let mut incompatible_count = 0; - - let length = items.len(); - - for item in items { - let cmp = compare_type(types, item, rhs, ctx); - result = max(result, cmp); - if cmp == Comparison::Incompatible { - incompatible_count += 1; - } - } - - if incompatible_count == length { - Comparison::Incompatible - } else { - min(result, Comparison::Superset) - } - } - - (_, Type::Union(items)) => { - let items = items.clone(); - let mut result = Comparison::Incompatible; - - for item in items { - let cmp = compare_type(types, lhs, item, ctx); - result = min(result, cmp); - } - - max(result, Comparison::Assignable) - } - - (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { - let first = compare_type(types, *lhs_first, *rhs_first, ctx); - let rest = compare_type(types, *lhs_rest, *rhs_rest, ctx); - max(first, rest) - } - (Type::Pair(..), _) | (_, Type::Pair(..)) => Comparison::Incompatible, - - (Type::Bytes, Type::Bytes) => Comparison::Equal, - (Type::Bytes32, Type::Bytes32) => Comparison::Equal, - (Type::PublicKey, Type::PublicKey) => Comparison::Equal, - (Type::Int, Type::Int) => Comparison::Equal, - (Type::Bool, Type::Bool) => Comparison::Equal, - (Type::Nil, Type::Nil) => Comparison::Equal, - - (Type::Bytes, Type::Bytes32) => Comparison::Superset, - (Type::Bytes, Type::PublicKey) => Comparison::Superset, - (Type::Bytes, Type::Bool) => Comparison::Superset, - (Type::Bytes, Type::Nil) => Comparison::Superset, - (Type::Int, Type::Bytes32) => Comparison::Superset, - (Type::Int, Type::PublicKey) => Comparison::Superset, - (Type::Int, Type::Bool) => Comparison::Superset, - (Type::Int, Type::Nil) => Comparison::Superset, - - (Type::Bytes32, Type::Bytes) => Comparison::Assignable, - (Type::Nil, Type::Bytes) => Comparison::Assignable, - - (Type::Bytes, Type::Int) => Comparison::Castable, - (Type::Bytes32, Type::Int) => Comparison::Castable, - (Type::PublicKey, Type::Bytes) => Comparison::Castable, - (Type::PublicKey, Type::Int) => Comparison::Castable, - (Type::Int, Type::Bytes) => Comparison::Castable, - (Type::Nil, Type::Bool) => Comparison::Castable, - (Type::Nil, Type::Int) => Comparison::Castable, - (Type::Bool, Type::Bytes) => Comparison::Castable, - (Type::Bool, Type::Int) => Comparison::Castable, - - (Type::Bytes32, Type::PublicKey) => Comparison::Incompatible, - (Type::Bytes32, Type::Bool) => Comparison::Incompatible, - (Type::Bytes32, Type::Nil) => Comparison::Incompatible, - (Type::PublicKey, Type::Bytes32) => Comparison::Incompatible, - (Type::PublicKey, Type::Bool) => Comparison::Incompatible, - (Type::PublicKey, Type::Nil) => Comparison::Incompatible, - (Type::Bool, Type::Bytes32) => Comparison::Incompatible, - (Type::Bool, Type::PublicKey) => Comparison::Incompatible, - (Type::Bool, Type::Nil) => Comparison::Incompatible, - (Type::Nil, Type::Bytes32) => Comparison::Incompatible, - (Type::Nil, Type::PublicKey) => Comparison::Incompatible, - - (Type::Lazy(lazy), _) => { - ctx.substitution_stack.push(lazy.substitutions.clone()); - let result = compare_type(types, lazy.type_id, rhs, ctx); - ctx.substitution_stack.pop().unwrap(); - result - } - - (_, Type::Lazy(lazy)) => { - ctx.substitution_stack.push(lazy.substitutions.clone()); - let result = compare_type(types, lhs, lazy.type_id, ctx); - ctx.substitution_stack.pop().unwrap(); - result - } - - (Type::Alias(alias), _) => compare_type(types, alias.type_id, rhs, ctx), - (_, Type::Alias(alias)) => compare_type(types, lhs, alias.type_id, ctx), - }; - - ctx.visited.remove(&(lhs, rhs)); - - comparison -} - -pub(crate) fn difference_type( - types: &mut TypeSystem, - std: &StandardTypes, - lhs: TypeId, - rhs: TypeId, - visited: &mut HashSet<(TypeId, TypeId)>, -) -> TypeId { - if !visited.insert((lhs, rhs)) { - return lhs; - } - - let result = match (types.get(lhs), types.get(rhs)) { - (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), - (Type::Lazy(..), _) | (_, Type::Lazy(..)) => unreachable!(), - - (Type::Generic, _) | (_, Type::Generic) => lhs, - (Type::Unknown, _) | (_, Type::Unknown) => lhs, - (Type::Never, _) => std.never, - (_, Type::Never) => lhs, - - (Type::Bytes, Type::Bytes) => std.never, - (Type::Bytes32, Type::Bytes32) => std.never, - (Type::PublicKey, Type::PublicKey) => std.never, - (Type::Int, Type::Int) => std.never, - (Type::Bool, Type::Bool) => std.never, - (Type::Nil, Type::Nil) => std.never, - - (Type::Int, Type::Bytes32) => lhs, - (Type::Int, Type::PublicKey) => lhs, - (Type::Int, Type::Bytes) => std.never, - (Type::Int, Type::Bool) => lhs, - (Type::Int, Type::Nil) => lhs, - - (Type::Bytes, Type::Bytes32) => lhs, - (Type::Bytes, Type::PublicKey) => lhs, - (Type::Bytes, Type::Int) => std.never, - (Type::Bytes, Type::Bool) => lhs, - (Type::Bytes, Type::Nil) => lhs, - - (Type::Bytes32, Type::PublicKey) => lhs, - (Type::Bytes32, Type::Bytes) => std.never, - (Type::Bytes32, Type::Int) => std.never, - (Type::Bytes32, Type::Bool) => lhs, - (Type::Bytes32, Type::Nil) => lhs, - - (Type::PublicKey, Type::Bytes32) => lhs, - (Type::PublicKey, Type::Bytes) => std.never, - (Type::PublicKey, Type::Int) => std.never, - (Type::PublicKey, Type::Bool) => lhs, - (Type::PublicKey, Type::Nil) => lhs, - - (Type::Bool, Type::Bytes32) => lhs, - (Type::Bool, Type::PublicKey) => lhs, - (Type::Bool, Type::Bytes) => std.never, - (Type::Bool, Type::Int) => std.never, - (Type::Bool, Type::Nil) => lhs, - - (Type::Nil, Type::Bytes32) => lhs, - (Type::Nil, Type::PublicKey) => lhs, - (Type::Nil, Type::Bytes) => std.never, - (Type::Nil, Type::Int) => std.never, - (Type::Nil, Type::Bool) => std.never, - - (Type::Bytes, Type::Pair(..)) => lhs, - (Type::Bytes32, Type::Pair(..)) => lhs, - (Type::PublicKey, Type::Pair(..)) => lhs, - (Type::Int, Type::Pair(..)) => lhs, - (Type::Bool, Type::Pair(..)) => lhs, - (Type::Nil, Type::Pair(..)) => lhs, - - (Type::Pair(..), Type::Bytes) => lhs, - (Type::Pair(..), Type::Bytes32) => lhs, - (Type::Pair(..), Type::PublicKey) => lhs, - (Type::Pair(..), Type::Int) => lhs, - (Type::Pair(..), Type::Bool) => lhs, - (Type::Pair(..), Type::Nil) => lhs, - - (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { - 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); - - if matches!(types.get(first), Type::Never) || matches!(types.get(first), Type::Never) { - std.never - } else if first == lhs_first && rest == lhs_rest { - lhs - } else { - types.alloc(Type::Pair(first, rest)) - } - } - - (Type::Union(items), _) => { - let items = items.clone(); - - let mut result = Vec::new(); - - for item in &items { - let item = difference_type(types, std, *item, rhs, visited); - if matches!(types.get(item), Type::Never) { - continue; - } - result.push(item); - } - - if result.is_empty() { - std.never - } else if result.len() == 1 { - result[0] - } else if result == items { - lhs - } else { - types.alloc(Type::Union(result)) - } - } - - (_, Type::Union(items)) => { - let items = items.clone(); - let mut lhs = lhs; - for item in items { - lhs = difference_type(types, std, 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), - }; - - visited.remove(&(lhs, rhs)); - - result -} - pub(crate) fn replace_type( types: &mut TypeSystem, type_id: TypeId, From 0f9ff29d9766d021700afcbedb3746851077964b Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 19 Jul 2024 23:57:39 -0400 Subject: [PATCH 018/100] Add stringification --- crates/rue-typing/src/comparison.rs | 14 ++++- crates/rue-typing/src/lib.rs | 2 + crates/rue-typing/src/stringify.rs | 91 ++++++++++++++++++++++++++++ crates/rue-typing/src/ty.rs | 56 +++++++++++++---- crates/rue-typing/src/type_system.rs | 29 ++++++++- 5 files changed, 174 insertions(+), 18 deletions(-) create mode 100644 crates/rue-typing/src/stringify.rs diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index c54133f..ccef885 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -1,6 +1,9 @@ -use std::cmp::{max, min}; +use std::{ + cmp::{max, min}, + collections::{HashMap, HashSet}, +}; -use crate::{ComparisonContext, Type, TypeId, TypeSystem}; +use crate::{Type, TypeId, TypeSystem}; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Comparison { @@ -11,6 +14,13 @@ pub enum Comparison { Incompatible, } +pub(crate) struct ComparisonContext<'a> { + pub visited: HashSet<(TypeId, TypeId)>, + pub substitution_stack: &'a mut Vec>, + pub initial_substitution_length: usize, + pub generic_stack_frame: Option, +} + pub(crate) fn compare_type( types: &TypeSystem, lhs: TypeId, diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index ea74ab2..add1c13 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -3,6 +3,7 @@ mod comparison; mod difference; mod semantic_types; mod standard_types; +mod stringify; mod ty; mod type_path; mod type_system; @@ -16,3 +17,4 @@ pub use type_path::*; pub use type_system::*; pub(crate) use difference::difference_type; +pub(crate) use stringify::stringify_type; diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs new file mode 100644 index 0000000..5f4ac74 --- /dev/null +++ b/crates/rue-typing/src/stringify.rs @@ -0,0 +1,91 @@ +use std::collections::{HashMap, HashSet}; + +use crate::{Type, TypeId, TypeSystem}; + +pub(crate) fn stringify_type( + types: &TypeSystem, + type_id: TypeId, + names: &HashMap, + visited: &mut HashSet, +) -> String { + if let Some(name) = names.get(&type_id) { + return name.clone(); + } + + if !visited.insert(type_id) { + return "{recursive}".to_string(); + } + + let result = match types.get(type_id) { + Type::Ref(..) => unreachable!(), + Type::Unknown => "{unknown}".to_string(), + Type::Generic => "{generic}".to_string(), + Type::Never => "Never".to_string(), + Type::Bytes => "Bytes".to_string(), + Type::Bytes32 => "Bytes32".to_string(), + Type::PublicKey => "PublicKey".to_string(), + Type::Int => "Int".to_string(), + Type::Bool => "Bool".to_string(), + Type::Nil => "Nil".to_string(), + Type::Pair(first, rest) => { + let first = stringify_type(types, *first, names, visited); + let rest = stringify_type(types, *rest, names, visited); + format!("({first}, {rest})") + } + Type::Union(items) => { + let mut result = String::new(); + + for (index, item) in items.iter().enumerate() { + if index > 0 { + result.push_str(" | "); + } + result.push_str(&stringify_type(types, *item, names, visited)); + } + + result + } + Type::Lazy(lazy) => stringify_type(types, lazy.type_id, names, visited), + Type::Alias(alias) => stringify_type(types, alias.type_id, names, visited), + }; + + visited.remove(&type_id); + + result +} + +#[cfg(test)] +mod tests { + use crate::StandardTypes; + + use super::*; + + #[test] + fn stringify_atoms() { + let mut db = TypeSystem::new(); + let types = StandardTypes::alloc(&mut db); + + assert_eq!(db.stringify(types.unknown), "{unknown}"); + assert_eq!(db.stringify(types.never), "Never"); + assert_eq!(db.stringify(types.bytes), "Bytes"); + assert_eq!(db.stringify(types.bytes32), "Bytes32"); + assert_eq!(db.stringify(types.public_key), "PublicKey"); + assert_eq!(db.stringify(types.int), "Int"); + assert_eq!(db.stringify(types.bool), "Bool"); + assert_eq!(db.stringify(types.nil), "Nil"); + assert_eq!( + db.stringify(types.any), + "Bytes | ({recursive}, {recursive})" + ); + } + + #[test] + fn stringify_named() { + let mut db = TypeSystem::new(); + let types = StandardTypes::alloc(&mut db); + + let mut names = HashMap::new(); + names.insert(types.any, "Any".to_string()); + + assert_eq!(db.stringify_named(types.any, &names), "Any"); + } +} diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs index 6e8b52c..33bbb4c 100644 --- a/crates/rue-typing/src/ty.rs +++ b/crates/rue-typing/src/ty.rs @@ -20,13 +20,6 @@ pub enum Type { Alias(Alias), } -pub(crate) struct ComparisonContext<'a> { - pub visited: HashSet<(TypeId, TypeId)>, - pub substitution_stack: &'a mut Vec>, - pub initial_substitution_length: usize, - pub generic_stack_frame: Option, -} - pub(crate) fn replace_type( types: &mut TypeSystem, type_id: TypeId, @@ -46,10 +39,11 @@ pub(crate) fn replace_type( } } -pub(crate) fn structural_type( +pub(crate) fn substitute_type( types: &mut TypeSystem, type_id: TypeId, substitutions: &mut HashMap, + preserve_semantics: bool, visited: &mut HashSet, ) -> TypeId { if let Some(new_type_id) = substitutions.get(&type_id) { @@ -74,8 +68,9 @@ pub(crate) fn structural_type( Type::Pair(first, rest) => { let (first, rest) = (*first, *rest); - let new_first = structural_type(types, first, substitutions, visited); - let new_rest = structural_type(types, rest, substitutions, visited); + let new_first = + substitute_type(types, first, substitutions, preserve_semantics, visited); + let new_rest = substitute_type(types, rest, substitutions, preserve_semantics, visited); if new_first == first && new_rest == rest { type_id @@ -88,7 +83,13 @@ pub(crate) fn structural_type( let mut result = Vec::new(); for item in &items { - result.push(structural_type(types, *item, substitutions, visited)); + result.push(substitute_type( + types, + *item, + substitutions, + preserve_semantics, + visited, + )); } if result == items { @@ -99,9 +100,38 @@ pub(crate) fn structural_type( } Type::Lazy(lazy) => { substitutions.extend(lazy.substitutions.clone()); - structural_type(types, lazy.type_id, substitutions, visited) + substitute_type( + types, + lazy.type_id, + substitutions, + preserve_semantics, + visited, + ) + } + Type::Alias(alias) => { + let alias = alias.clone(); + + let new_type_id = substitute_type( + types, + alias.type_id, + substitutions, + preserve_semantics, + visited, + ); + + if preserve_semantics { + if new_type_id == alias.type_id { + type_id + } else { + types.alloc(Type::Alias(Alias { + type_id: new_type_id, + generic_types: alias.generic_types, + })) + } + } else { + new_type_id + } } - Type::Alias(alias) => alias.type_id, }; visited.remove(&type_id); diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index 04df2cb..3e8bd1b 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -3,8 +3,9 @@ use std::collections::{HashMap, HashSet}; use id_arena::{Arena, Id}; use crate::{ - check_type, compare_type, difference_type, replace_type, simplify_check, structural_type, - Check, CheckError, Comparison, ComparisonContext, StandardTypes, Type, TypePath, + check_type, compare_type, difference_type, replace_type, simplify_check, stringify_type, + substitute_type, Check, CheckError, Comparison, ComparisonContext, StandardTypes, Type, + TypePath, }; pub type TypeId = Id; @@ -37,6 +38,14 @@ impl TypeSystem { } } + pub fn stringify_named(&self, type_id: TypeId, names: &HashMap) -> String { + stringify_type(self, type_id, names, &mut HashSet::new()) + } + + pub fn stringify(&self, type_id: TypeId) -> String { + self.stringify_named(type_id, &HashMap::new()) + } + pub fn compare(&self, lhs: TypeId, rhs: TypeId) -> Comparison { self.compare_with_generics(lhs, rhs, &mut Vec::new(), false) } @@ -68,12 +77,26 @@ impl TypeSystem { ) } + pub fn substitute( + &mut self, + type_id: TypeId, + mut substitutions: HashMap, + ) -> TypeId { + substitute_type(self, type_id, &mut substitutions, true, &mut HashSet::new()) + } + pub fn structure( &mut self, type_id: TypeId, mut substitutions: HashMap, ) -> TypeId { - structural_type(self, type_id, &mut substitutions, &mut HashSet::new()) + substitute_type( + self, + type_id, + &mut substitutions, + false, + &mut HashSet::new(), + ) } pub fn check(&self, lhs: TypeId, rhs: TypeId) -> Result { From 042cc837fada26405d0db5f14f68b9ad6a4033ae Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sat, 20 Jul 2024 00:33:17 -0400 Subject: [PATCH 019/100] WIP --- Cargo.lock | 5 +- Cargo.toml | 1 + crates/rue-typing/Cargo.toml | 3 + crates/rue-typing/src/check.rs | 322 +++++++++++++++++++-------------- 4 files changed, 196 insertions(+), 135 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8e086a..dab46a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,9 +76,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arbitrary" @@ -1459,6 +1459,7 @@ dependencies = [ name = "rue-typing" version = "0.1.1" dependencies = [ + "anyhow", "id-arena", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index 0b25a8c..d0df60e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,3 +61,4 @@ clvm-utils = "0.6.0" toml = "0.8.12" serde = "1.0.197" walkdir = "2.5.0" +anyhow = "1.0.86" diff --git a/crates/rue-typing/Cargo.toml b/crates/rue-typing/Cargo.toml index aad8ab9..74b2a97 100644 --- a/crates/rue-typing/Cargo.toml +++ b/crates/rue-typing/Cargo.toml @@ -17,3 +17,6 @@ workspace = true [dependencies] id-arena = { workspace = true } thiserror = { workspace = true } + +[dev-dependencies] +anyhow = { workspace = true } diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 9e6bba6..0b224b9 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -4,11 +4,15 @@ use std::{ hash::BuildHasher, }; +use thiserror::Error; + use crate::{Type, TypeId, TypePath, TypeSystem}; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Error, Clone, Copy)] pub enum CheckError { + #[error("recursive check")] Recursive(TypeId, TypeId), + #[error("impossible check")] Impossible(TypeId, TypeId), } @@ -153,6 +157,24 @@ fn check_union_against_rhs( where S: BuildHasher, { + if let Type::Union(union) = types.get(rhs) { + let rhs_items = union.clone(); + let mut result = Vec::new(); + for rhs_item in rhs_items { + if !visited.insert((original_type_id, rhs_item)) { + return Err(CheckError::Recursive(original_type_id, rhs_item)); + } + result.push(check_union_against_rhs( + types, + original_type_id, + items, + rhs_item, + visited, + )?); + } + return Ok(Check::Or(result)); + } + let mut atom_count = 0; let mut bool_count = 0; let mut nil_count = 0; @@ -531,193 +553,227 @@ fn fmt_check(check: &Check, f: &mut fmt::Formatter<'_>, path: &mut Vec #[cfg(test)] mod tests { - use crate::{Comparison, StandardTypes}; + use crate::StandardTypes; use super::*; - #[test] - fn check_incompatible() { + fn setup() -> (TypeSystem, StandardTypes) { let mut db = TypeSystem::new(); let types = StandardTypes::alloc(&mut db); + (db, types) + } + fn check_str(db: &TypeSystem, lhs: TypeId, rhs: TypeId, expected: &str) { + assert_eq!(format!("{}", db.check(lhs, rhs).unwrap()), expected); + } + + fn check_recursive(db: &TypeSystem, lhs: TypeId, rhs: TypeId) { + assert!(matches!(db.check(lhs, rhs), Err(CheckError::Recursive(..)))); + } + + fn check_impossible(db: &TypeSystem, lhs: TypeId, rhs: TypeId) { assert!(matches!( - db.check(types.bytes32, types.public_key).unwrap_err(), - CheckError::Impossible(..) + db.check(lhs, rhs), + Err(CheckError::Impossible(..)) )); + } - assert_eq!( - db.compare(types.bytes32, types.public_key), - Comparison::Incompatible - ); + fn alloc_list(db: &mut TypeSystem, types: &StandardTypes, item_type_id: TypeId) -> TypeId { + let list = db.alloc(Type::Unknown); + let pair = db.alloc(Type::Pair(item_type_id, list)); + *db.get_mut(list) = Type::Union(vec![pair, types.nil]); + list + } - let difference = db.difference(&types, types.bytes32, types.public_key); - assert_eq!(db.compare(difference, types.bytes32), Comparison::Equal); + #[test] + fn check_any_bytes() { + let (db, types) = setup(); + check_str(&db, types.any, types.bytes, "(not (l val))"); } #[test] fn check_any_bytes32() { - let mut db = TypeSystem::new(); - let types = StandardTypes::alloc(&mut db); - - assert_eq!( - format!("{}", db.check(types.any, types.bytes32).unwrap()), - "(and (not (l val)) (= (strlen val) 32))" + let (db, types) = setup(); + check_str( + &db, + types.any, + types.bytes32, + "(and (not (l val)) (= (strlen val) 32))", ); - - assert_eq!(db.compare(types.any, types.bytes32), Comparison::Superset); - - let difference = db.difference(&types, types.any, types.bytes32); - assert_eq!(db.compare(difference, types.any), Comparison::Assignable); } #[test] - fn check_any_int() { - let mut db = TypeSystem::new(); - let types = StandardTypes::alloc(&mut db); - - assert_eq!( - format!("{}", db.check(types.any, types.int).unwrap()), - "(not (l val))" + fn check_any_public_key() { + let (db, types) = setup(); + check_str( + &db, + types.any, + types.public_key, + "(and (not (l val)) (= (strlen val) 48))", ); - - assert_eq!(db.compare(types.any, types.int), Comparison::Superset); - - let difference = db.difference(&types, types.any, types.int); - assert_eq!(db.compare(difference, types.any), Comparison::Assignable); } #[test] - fn check_any_any() { - let mut db = TypeSystem::new(); - let types = StandardTypes::alloc(&mut db); - - assert!(matches!( - db.check(types.any, types.any).unwrap_err(), - CheckError::Recursive(..) - )); - - assert_eq!(db.compare(types.any, types.any), Comparison::Assignable); - - let difference = db.difference(&types, types.any, types.any); - assert_eq!(db.compare(difference, types.any), Comparison::Assignable); + fn check_any_int() { + let (db, types) = setup(); + check_str(&db, types.any, types.int, "(not (l val))"); } #[test] - fn check_optional_int() { - let mut db = TypeSystem::new(); - let types = StandardTypes::alloc(&mut db); - let pair = db.alloc(Type::Pair(types.int, types.int)); - let optional_pair = db.alloc(Type::Union(vec![pair, types.nil])); - - assert_eq!( - format!("{}", db.check(optional_pair, types.nil).unwrap()), - "(and (not (l val)) (= val ()))" + fn check_any_bool() { + let (db, types) = setup(); + check_str( + &db, + types.any, + types.bool, + "(and (not (l val)) (any (= val ()) (= val 1)))", ); + } - assert_eq!(db.compare(optional_pair, types.nil), Comparison::Superset); - - let difference = db.difference(&types, optional_pair, types.nil); - assert_eq!(db.compare(difference, pair), Comparison::Equal); + #[test] + fn check_any_nil() { + let (db, types) = setup(); + check_str(&db, types.any, types.nil, "(and (not (l val)) (= val ()))"); + } - assert_eq!( - format!("{}", db.check(optional_pair, pair).unwrap()), - "(and (l val) 1)" - ); + #[test] + fn check_bytes_bytes() { + let (db, types) = setup(); + check_str(&db, types.bytes, types.bytes, "1"); + } - assert_eq!(db.compare(optional_pair, pair), Comparison::Superset); + #[test] + fn check_bytes32_bytes32() { + let (db, types) = setup(); + check_str(&db, types.bytes32, types.bytes32, "1"); + } - let difference = db.difference(&types, optional_pair, pair); - assert_eq!(db.compare(difference, types.nil), Comparison::Equal); + #[test] + fn check_public_key_public_key() { + let (db, types) = setup(); + check_str(&db, types.public_key, types.public_key, "1"); } #[test] - fn check_simple_union() { - let mut db = TypeSystem::new(); - let types = StandardTypes::alloc(&mut db); - let union = db.alloc(Type::Union(vec![types.bytes32, types.public_key])); + fn check_int_int() { + let (db, types) = setup(); + check_str(&db, types.int, types.int, "1"); + } - assert_eq!( - format!("{}", db.check(union, types.public_key).unwrap()), - "(= (strlen val) 48)" - ); + #[test] + fn check_bool_bool() { + let (db, types) = setup(); + check_str(&db, types.bool, types.bool, "1"); + } - assert_eq!(db.compare(union, types.public_key), Comparison::Superset); + #[test] + fn check_nil_nil() { + let (db, types) = setup(); + check_str(&db, types.nil, types.nil, "1"); + } - let difference = db.difference(&types, union, types.public_key); - assert_eq!(db.compare(difference, types.bytes32), Comparison::Equal); + #[test] + fn check_bytes_bytes32() { + let (db, types) = setup(); + check_str(&db, types.bytes, types.bytes32, "(= (strlen val) 32)"); } #[test] - fn check_any_pair_bytes32_pair_int_nil() { - let mut db = TypeSystem::new(); - let types = StandardTypes::alloc(&mut db); + fn check_bytes32_bytes() { + let (db, types) = setup(); + check_str(&db, types.bytes32, types.bytes, "1"); + } - let int_nil_pair = db.alloc(Type::Pair(types.int, types.nil)); - let ty = db.alloc(Type::Pair(types.bytes32, int_nil_pair)); + #[test] + fn check_bytes_public_key() { + let (db, types) = setup(); + check_str(&db, types.bytes, types.public_key, "(= (strlen val) 48)"); + } - assert_eq!( - format!("{}", db.check(types.any, ty).unwrap()), - "(and (l val) (all (and (not (l (f val))) (= (strlen (f val)) 32)) (and (l (r val)) (all (not (l (f (r val)))) (and (not (l (r (r val)))) (= (r (r val)) ()))))))" - ); + #[test] + fn check_public_key_bytes() { + let (db, types) = setup(); + check_str(&db, types.public_key, types.bytes, "1"); + } - assert_eq!(db.compare(types.any, ty), Comparison::Superset); + #[test] + fn check_bytes_nil() { + let (db, types) = setup(); + check_str(&db, types.bytes, types.nil, "(= val ())"); + } - let difference = db.difference(&types, types.any, ty); - assert_eq!(db.compare(difference, types.any), Comparison::Assignable); + #[test] + fn check_nil_bytes() { + let (db, types) = setup(); + check_str(&db, types.nil, types.bytes, "1"); } #[test] - fn check_complex_type_unions() { - let mut db = TypeSystem::new(); - let types = StandardTypes::alloc(&mut db); + fn check_bytes_bool() { + let (db, types) = setup(); + check_str(&db, types.bytes, types.bool, "(any (= val ()) (= val 1))"); + } - let int_nil_pair = db.alloc(Type::Pair(types.int, types.nil)); - let bytes32_pair = db.alloc(Type::Pair(types.bytes32, types.bytes32)); - let complex_pair = db.alloc(Type::Pair(int_nil_pair, bytes32_pair)); + #[test] + fn check_bool_bytes() { + let (db, types) = setup(); + check_str(&db, types.bool, types.bytes, "1"); + } - let lhs = db.alloc(Type::Union(vec![ - types.bytes32, - types.public_key, - types.nil, - complex_pair, - int_nil_pair, - bytes32_pair, - types.bool, - ])); + #[test] + fn check_bytes32_public_key() { + let (db, types) = setup(); + check_impossible(&db, types.bytes32, types.public_key); + } - let rhs = db.alloc(Type::Union(vec![ - types.bytes32, - bytes32_pair, - types.nil, - complex_pair, - ])); + #[test] + fn check_bytes_int() { + let (db, types) = setup(); + check_str(&db, types.bytes, types.int, "1"); + } - let expected_diff = db.alloc(Type::Union(vec![ - int_nil_pair, - types.public_key, - types.bool, - ])); + #[test] + fn check_int_bytes() { + let (db, types) = setup(); + check_str(&db, types.int, types.bytes, "1"); + } - assert_eq!( - format!("{}", db.check(lhs, rhs).unwrap()), - "(if (l val) (or (and (all (and (not (l (f val))) (= (strlen (f val)) 32)) (and (not (l (r val))) (= (strlen (r val)) 32)))) (and (all (and (l (f val)) 1) (and (l (r val)) 1)))) (or (and (= (strlen val) 32)) (and (= val ()))))" - ); + #[test] + fn check_bool_nil() { + let (db, types) = setup(); + check_str(&db, types.bool, types.nil, "(= val ())"); + } - assert_eq!(db.compare(lhs, rhs), Comparison::Superset); + #[test] + fn check_nil_bool() { + let (db, types) = setup(); + check_str(&db, types.nil, types.bool, "1"); + } - let difference = db.difference(&types, lhs, rhs); - assert_eq!( - db.compare(difference, expected_diff), - Comparison::Assignable - ); + #[test] + fn check_any_any() { + let (db, types) = setup(); + check_recursive(&db, types.any, types.any); + } - assert_eq!(format!("{}", db.check(types.any, rhs).unwrap()), - "(if (l val) (or (and (all (and (not (l (f val))) (= (strlen (f val)) 32)) (and (not (l (r val))) (= (strlen (r val)) 32)))) (and (all (and (l (f val)) (all (not (l (f (f val)))) (and (not (l (r (f val)))) (= (r (f val)) ())))) (and (l (r val)) (all (and (not (l (f (r val)))) (= (strlen (f (r val))) 32)) (and (not (l (r (r val)))) (= (strlen (r (r val))) 32))))))) (or (and (= (strlen val) 32)) (and (= val ()))))" - ); + #[test] + fn check_bytes_any() { + let (db, types) = setup(); + check_recursive(&db, types.any, types.any); + } - assert_eq!(db.compare(types.any, rhs), Comparison::Superset); + #[test] + fn check_list_nil() { + let (mut db, types) = setup(); + let list = alloc_list(&mut db, &types, types.bytes); + check_str(&db, list, types.nil, "(and (not (l val)) (= val ()))"); + } - let difference = db.difference(&types, types.any, rhs); - assert_eq!(db.compare(difference, types.any), Comparison::Assignable); + #[test] + fn check_list_pair() { + let (mut db, types) = setup(); + let list = alloc_list(&mut db, &types, types.bytes); + let pair = db.alloc(Type::Pair(types.bytes, list)); + check_str(&db, list, pair, ""); } } From 55c09c3d1f1fe1657633a487e915af373cbe0758 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 16:01:15 -0400 Subject: [PATCH 020/100] Tests passing --- crates/rue-typing/src/check.rs | 296 +++++++++++++++++++-------- crates/rue-typing/src/type_system.rs | 2 +- 2 files changed, 206 insertions(+), 92 deletions(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 0b224b9..922d880 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -6,7 +6,7 @@ use std::{ use thiserror::Error; -use crate::{Type, TypeId, TypePath, TypeSystem}; +use crate::{Comparison, Type, TypeId, TypePath, TypeSystem}; #[derive(Debug, Error, Clone, Copy)] pub enum CheckError { @@ -38,7 +38,7 @@ impl fmt::Display for Check { /// Returns [`None`] for recursive checks. pub(crate) fn check_type( - types: &TypeSystem, + types: &mut TypeSystem, lhs: TypeId, rhs: TypeId, visited: &mut HashSet<(TypeId, TypeId), S>, @@ -47,6 +47,9 @@ where S: BuildHasher, { if !visited.insert((lhs, rhs)) { + if types.compare(lhs, rhs) <= Comparison::Castable { + return Ok(Check::None); + } return Err(CheckError::Recursive(lhs, rhs)); } @@ -106,7 +109,10 @@ where Check::Or(result) } - (Type::Union(items), _) => check_union_against_rhs(types, lhs, items, rhs, visited)?, + (Type::Union(items), _) => { + let items = items.clone(); + check_union_against_rhs(types, lhs, &items, rhs, visited)? + } (Type::PublicKey, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Bytes32, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), @@ -148,7 +154,7 @@ where } fn check_union_against_rhs( - types: &TypeSystem, + types: &mut TypeSystem, original_type_id: TypeId, items: &[TypeId], rhs: TypeId, @@ -157,6 +163,12 @@ fn check_union_against_rhs( where S: BuildHasher, { + let union = types.alloc(Type::Union(items.to_vec())); + + if types.compare(union, rhs) <= Comparison::Castable { + return Ok(Check::None); + } + if let Type::Union(union) = types.get(rhs) { let rhs_items = union.clone(); let mut result = Vec::new(); @@ -373,62 +385,137 @@ pub(crate) fn simplify_check(check: Check) -> Check { let mut items: VecDeque<_> = items.into(); + let mut is_atom = false; + let mut is_pair = false; + while !items.is_empty() { let item = simplify_check(items.pop_front().unwrap()); + let mut and = Vec::new(); + match item { + Check::None => return Check::None, Check::And(children) => { match children .iter() .find(|child| matches!(child, Check::IsAtom | Check::IsPair)) { Some(Check::IsAtom) => { - atoms.push(Check::And( - children - .into_iter() - .filter(|child| *child != Check::IsAtom) - .collect(), - )); - continue; + let mut children: Vec = children + .into_iter() + .filter(|child| *child != Check::IsAtom) + .collect(); + + if children.is_empty() { + result.push(Check::IsAtom); + } else if children.len() == 1 { + atoms.push(children.remove(0)); + } else { + atoms.push(Check::And(children)); + } } Some(Check::IsPair) => { - pairs.push(Check::And( - children - .into_iter() - .filter(|child| *child != Check::IsPair) - .collect(), - )); - continue; + let mut children: Vec = children + .into_iter() + .filter(|child| *child != Check::IsPair) + .collect(); + + if children.is_empty() { + result.push(Check::IsPair); + } else if children.len() == 1 { + pairs.push(children.remove(0)); + } else { + pairs.push(Check::And(children)); + } + } + _ => { + and.extend(children); } - _ => {} } } Check::Or(children) => { items.extend(children); - continue; } - _ => {} + item => { + and.push(item); + } + } + + let mut new_and = Vec::new(); + + for and_item in and { + match and_item { + Check::IsAtom => { + if is_atom { + continue; + } else if is_pair { + return Check::None; + } + new_and.push(Check::IsAtom); + is_atom = true; + } + Check::IsPair => { + if is_pair { + continue; + } else if is_atom { + return Check::None; + } + new_and.push(Check::IsPair); + is_pair = true; + } + item => { + new_and.push(item); + } + } + } + + if new_and.is_empty() { + continue; + } + + if new_and.len() == 1 { + result.push(new_and.remove(0)); + } else { + result.push(Check::And(new_and)); } } - if !atoms.is_empty() && !pairs.is_empty() { - if atoms.len() > pairs.len() { + let prefer_atoms = atoms.len() > pairs.len(); + + let atoms = if atoms.is_empty() { + Check::None + } else if atoms.len() == 1 { + atoms.remove(0) + } else { + Check::Or(atoms) + }; + + let pairs = if pairs.is_empty() { + Check::None + } else if pairs.len() == 1 { + pairs.remove(0) + } else { + Check::Or(pairs) + }; + + if atoms != Check::None && pairs != Check::None { + if prefer_atoms { result.push(Check::If( Box::new(Check::IsAtom), - Box::new(Check::Or(atoms)), - Box::new(Check::Or(pairs)), + Box::new(atoms), + Box::new(pairs), )); } else { result.push(Check::If( Box::new(Check::IsPair), - Box::new(Check::Or(pairs)), - Box::new(Check::Or(atoms)), + Box::new(pairs), + Box::new(atoms), )); } - } else if atoms.is_empty() { - result.push(Check::And(vec![Check::IsPair, Check::Or(pairs)])); - } else if pairs.is_empty() { - result.push(Check::And(vec![Check::IsAtom, Check::Or(atoms)])); + } else if atoms == Check::None && pairs != Check::None { + result.push(Check::And(vec![Check::IsPair, pairs])); + } else if pairs == Check::None && atoms != Check::None { + result.push(Check::And(vec![Check::IsAtom, atoms])); } if result.len() == 1 { @@ -446,7 +533,12 @@ pub(crate) fn simplify_check(check: Check) -> Check { Check::Pair(first, rest) => { let first = simplify_check(*first); let rest = simplify_check(*rest); - Check::Pair(Box::new(first), Box::new(rest)) + + if first == Check::None && rest == Check::None { + Check::None + } else { + Check::Pair(Box::new(first), Box::new(rest)) + } } } } @@ -563,15 +655,15 @@ mod tests { (db, types) } - fn check_str(db: &TypeSystem, lhs: TypeId, rhs: TypeId, expected: &str) { + fn check_str(db: &mut TypeSystem, lhs: TypeId, rhs: TypeId, expected: &str) { assert_eq!(format!("{}", db.check(lhs, rhs).unwrap()), expected); } - fn check_recursive(db: &TypeSystem, lhs: TypeId, rhs: TypeId) { + fn check_recursive(db: &mut TypeSystem, lhs: TypeId, rhs: TypeId) { assert!(matches!(db.check(lhs, rhs), Err(CheckError::Recursive(..)))); } - fn check_impossible(db: &TypeSystem, lhs: TypeId, rhs: TypeId) { + fn check_impossible(db: &mut TypeSystem, lhs: TypeId, rhs: TypeId) { assert!(matches!( db.check(lhs, rhs), Err(CheckError::Impossible(..)) @@ -587,15 +679,15 @@ mod tests { #[test] fn check_any_bytes() { - let (db, types) = setup(); - check_str(&db, types.any, types.bytes, "(not (l val))"); + let (mut db, types) = setup(); + check_str(&mut db, types.any, types.bytes, "(not (l val))"); } #[test] fn check_any_bytes32() { - let (db, types) = setup(); + let (mut db, types) = setup(); check_str( - &db, + &mut db, types.any, types.bytes32, "(and (not (l val)) (= (strlen val) 32))", @@ -604,9 +696,9 @@ mod tests { #[test] fn check_any_public_key() { - let (db, types) = setup(); + let (mut db, types) = setup(); check_str( - &db, + &mut db, types.any, types.public_key, "(and (not (l val)) (= (strlen val) 48))", @@ -615,15 +707,15 @@ mod tests { #[test] fn check_any_int() { - let (db, types) = setup(); - check_str(&db, types.any, types.int, "(not (l val))"); + let (mut db, types) = setup(); + check_str(&mut db, types.any, types.int, "(not (l val))"); } #[test] fn check_any_bool() { - let (db, types) = setup(); + let (mut db, types) = setup(); check_str( - &db, + &mut db, types.any, types.bool, "(and (not (l val)) (any (= val ()) (= val 1)))", @@ -632,141 +724,156 @@ mod tests { #[test] fn check_any_nil() { - let (db, types) = setup(); - check_str(&db, types.any, types.nil, "(and (not (l val)) (= val ()))"); + let (mut db, types) = setup(); + check_str( + &mut db, + types.any, + types.nil, + "(and (not (l val)) (= val ()))", + ); } #[test] fn check_bytes_bytes() { - let (db, types) = setup(); - check_str(&db, types.bytes, types.bytes, "1"); + let (mut db, types) = setup(); + check_str(&mut db, types.bytes, types.bytes, "1"); } #[test] fn check_bytes32_bytes32() { - let (db, types) = setup(); - check_str(&db, types.bytes32, types.bytes32, "1"); + let (mut db, types) = setup(); + check_str(&mut db, types.bytes32, types.bytes32, "1"); } #[test] fn check_public_key_public_key() { - let (db, types) = setup(); - check_str(&db, types.public_key, types.public_key, "1"); + let (mut db, types) = setup(); + check_str(&mut db, types.public_key, types.public_key, "1"); } #[test] fn check_int_int() { - let (db, types) = setup(); - check_str(&db, types.int, types.int, "1"); + let (mut db, types) = setup(); + check_str(&mut db, types.int, types.int, "1"); } #[test] fn check_bool_bool() { - let (db, types) = setup(); - check_str(&db, types.bool, types.bool, "1"); + let (mut db, types) = setup(); + check_str(&mut db, types.bool, types.bool, "1"); } #[test] fn check_nil_nil() { - let (db, types) = setup(); - check_str(&db, types.nil, types.nil, "1"); + let (mut db, types) = setup(); + check_str(&mut db, types.nil, types.nil, "1"); } #[test] fn check_bytes_bytes32() { - let (db, types) = setup(); - check_str(&db, types.bytes, types.bytes32, "(= (strlen val) 32)"); + let (mut db, types) = setup(); + check_str(&mut db, types.bytes, types.bytes32, "(= (strlen val) 32)"); } #[test] fn check_bytes32_bytes() { - let (db, types) = setup(); - check_str(&db, types.bytes32, types.bytes, "1"); + let (mut db, types) = setup(); + check_str(&mut db, types.bytes32, types.bytes, "1"); } #[test] fn check_bytes_public_key() { - let (db, types) = setup(); - check_str(&db, types.bytes, types.public_key, "(= (strlen val) 48)"); + let (mut db, types) = setup(); + check_str( + &mut db, + types.bytes, + types.public_key, + "(= (strlen val) 48)", + ); } #[test] fn check_public_key_bytes() { - let (db, types) = setup(); - check_str(&db, types.public_key, types.bytes, "1"); + let (mut db, types) = setup(); + check_str(&mut db, types.public_key, types.bytes, "1"); } #[test] fn check_bytes_nil() { - let (db, types) = setup(); - check_str(&db, types.bytes, types.nil, "(= val ())"); + let (mut db, types) = setup(); + check_str(&mut db, types.bytes, types.nil, "(= val ())"); } #[test] fn check_nil_bytes() { - let (db, types) = setup(); - check_str(&db, types.nil, types.bytes, "1"); + let (mut db, types) = setup(); + check_str(&mut db, types.nil, types.bytes, "1"); } #[test] fn check_bytes_bool() { - let (db, types) = setup(); - check_str(&db, types.bytes, types.bool, "(any (= val ()) (= val 1))"); + let (mut db, types) = setup(); + check_str( + &mut db, + types.bytes, + types.bool, + "(any (= val ()) (= val 1))", + ); } #[test] fn check_bool_bytes() { - let (db, types) = setup(); - check_str(&db, types.bool, types.bytes, "1"); + let (mut db, types) = setup(); + check_str(&mut db, types.bool, types.bytes, "1"); } #[test] fn check_bytes32_public_key() { - let (db, types) = setup(); - check_impossible(&db, types.bytes32, types.public_key); + let (mut db, types) = setup(); + check_impossible(&mut db, types.bytes32, types.public_key); } #[test] fn check_bytes_int() { - let (db, types) = setup(); - check_str(&db, types.bytes, types.int, "1"); + let (mut db, types) = setup(); + check_str(&mut db, types.bytes, types.int, "1"); } #[test] fn check_int_bytes() { - let (db, types) = setup(); - check_str(&db, types.int, types.bytes, "1"); + let (mut db, types) = setup(); + check_str(&mut db, types.int, types.bytes, "1"); } #[test] fn check_bool_nil() { - let (db, types) = setup(); - check_str(&db, types.bool, types.nil, "(= val ())"); + let (mut db, types) = setup(); + check_str(&mut db, types.bool, types.nil, "(= val ())"); } #[test] fn check_nil_bool() { - let (db, types) = setup(); - check_str(&db, types.nil, types.bool, "1"); + let (mut db, types) = setup(); + check_str(&mut db, types.nil, types.bool, "1"); } #[test] fn check_any_any() { - let (db, types) = setup(); - check_recursive(&db, types.any, types.any); + let (mut db, types) = setup(); + check_str(&mut db, types.any, types.any, "1"); } #[test] fn check_bytes_any() { - let (db, types) = setup(); - check_recursive(&db, types.any, types.any); + let (mut db, types) = setup(); + check_str(&mut db, types.any, types.any, "1"); } #[test] fn check_list_nil() { let (mut db, types) = setup(); let list = alloc_list(&mut db, &types, types.bytes); - check_str(&db, list, types.nil, "(and (not (l val)) (= val ()))"); + check_str(&mut db, list, types.nil, "(and (not (l val)) (= val ()))"); } #[test] @@ -774,6 +881,13 @@ mod tests { let (mut db, types) = setup(); let list = alloc_list(&mut db, &types, types.bytes); let pair = db.alloc(Type::Pair(types.bytes, list)); - check_str(&db, list, pair, ""); + check_str(&mut db, list, pair, "(l val)"); + } + + #[test] + fn check_any_list() { + let (mut db, types) = setup(); + let list = alloc_list(&mut db, &types, types.bytes); + check_recursive(&mut db, types.any, list); } } diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index 3e8bd1b..4de1bb3 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -99,7 +99,7 @@ impl TypeSystem { ) } - pub fn check(&self, lhs: TypeId, rhs: TypeId) -> Result { + pub fn check(&mut self, lhs: TypeId, rhs: TypeId) -> Result { check_type(self, lhs, rhs, &mut HashSet::new()).map(simplify_check) } From bdb0200f0521927a8c20eb21b7114e92b6fa1f8f Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 16:12:38 -0400 Subject: [PATCH 021/100] Factor out simplify_check --- crates/rue-typing/src/check.rs | 244 +---------------------- crates/rue-typing/src/lib.rs | 2 + crates/rue-typing/src/simplify_check.rs | 246 ++++++++++++++++++++++++ 3 files changed, 249 insertions(+), 243 deletions(-) create mode 100644 crates/rue-typing/src/simplify_check.rs diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 922d880..9502740 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -12,6 +12,7 @@ use crate::{Comparison, Type, TypeId, TypePath, TypeSystem}; pub enum CheckError { #[error("recursive check")] Recursive(TypeId, TypeId), + #[error("impossible check")] Impossible(TypeId, TypeId), } @@ -300,249 +301,6 @@ where }) } -pub(crate) fn simplify_check(check: Check) -> Check { - match check { - Check::None => Check::None, - Check::IsAtom => Check::IsAtom, - Check::IsPair => Check::IsPair, - Check::IsBool => Check::IsBool, - Check::IsNil => Check::IsNil, - Check::Length(len) => { - if len == 0 { - Check::IsNil - } else { - Check::Length(len) - } - } - Check::And(items) => { - let mut result = Vec::new(); - - let mut is_atom = false; - let mut is_pair = false; - let mut is_bool = false; - let mut is_nil = false; - let mut length = false; - - let mut items: VecDeque<_> = items.into(); - - while !items.is_empty() { - let item = simplify_check(items.pop_front().unwrap()); - - match item { - Check::None => continue, - Check::IsAtom => { - if is_atom { - continue; - } - is_atom = true; - } - Check::IsPair => { - if is_pair { - continue; - } - is_pair = true; - } - Check::IsBool => { - if is_bool { - continue; - } - is_bool = true; - } - Check::IsNil => { - if is_nil { - continue; - } - is_nil = true; - } - Check::Length(..) => { - if length { - continue; - } - length = false; - } - Check::And(children) => { - items.extend(children); - continue; - } - _ => {} - } - - result.push(item); - } - - if result.is_empty() { - Check::None - } else if result.len() == 1 { - result.remove(0) - } else { - Check::And(result) - } - } - Check::Or(items) => { - let mut result = Vec::new(); - let mut atoms: Vec = Vec::new(); - let mut pairs: Vec = Vec::new(); - - let mut items: VecDeque<_> = items.into(); - - let mut is_atom = false; - let mut is_pair = false; - - while !items.is_empty() { - let item = simplify_check(items.pop_front().unwrap()); - - let mut and = Vec::new(); - - match item { - Check::None => return Check::None, - Check::And(children) => { - match children - .iter() - .find(|child| matches!(child, Check::IsAtom | Check::IsPair)) - { - Some(Check::IsAtom) => { - let mut children: Vec = children - .into_iter() - .filter(|child| *child != Check::IsAtom) - .collect(); - - if children.is_empty() { - result.push(Check::IsAtom); - } else if children.len() == 1 { - atoms.push(children.remove(0)); - } else { - atoms.push(Check::And(children)); - } - } - Some(Check::IsPair) => { - let mut children: Vec = children - .into_iter() - .filter(|child| *child != Check::IsPair) - .collect(); - - if children.is_empty() { - result.push(Check::IsPair); - } else if children.len() == 1 { - pairs.push(children.remove(0)); - } else { - pairs.push(Check::And(children)); - } - } - _ => { - and.extend(children); - } - } - } - Check::Or(children) => { - items.extend(children); - } - item => { - and.push(item); - } - } - - let mut new_and = Vec::new(); - - for and_item in and { - match and_item { - Check::IsAtom => { - if is_atom { - continue; - } else if is_pair { - return Check::None; - } - new_and.push(Check::IsAtom); - is_atom = true; - } - Check::IsPair => { - if is_pair { - continue; - } else if is_atom { - return Check::None; - } - new_and.push(Check::IsPair); - is_pair = true; - } - item => { - new_and.push(item); - } - } - } - - if new_and.is_empty() { - continue; - } - - if new_and.len() == 1 { - result.push(new_and.remove(0)); - } else { - result.push(Check::And(new_and)); - } - } - - let prefer_atoms = atoms.len() > pairs.len(); - - let atoms = if atoms.is_empty() { - Check::None - } else if atoms.len() == 1 { - atoms.remove(0) - } else { - Check::Or(atoms) - }; - - let pairs = if pairs.is_empty() { - Check::None - } else if pairs.len() == 1 { - pairs.remove(0) - } else { - Check::Or(pairs) - }; - - if atoms != Check::None && pairs != Check::None { - if prefer_atoms { - result.push(Check::If( - Box::new(Check::IsAtom), - Box::new(atoms), - Box::new(pairs), - )); - } else { - result.push(Check::If( - Box::new(Check::IsPair), - Box::new(pairs), - Box::new(atoms), - )); - } - } else if atoms == Check::None && pairs != Check::None { - result.push(Check::And(vec![Check::IsPair, pairs])); - } else if pairs == Check::None && atoms != Check::None { - result.push(Check::And(vec![Check::IsAtom, atoms])); - } - - if result.len() == 1 { - result.remove(0) - } else { - Check::Or(result) - } - } - Check::If(cond, then, else_) => { - let cond = simplify_check(*cond); - let then = simplify_check(*then); - let else_ = simplify_check(*else_); - Check::If(Box::new(cond), Box::new(then), Box::new(else_)) - } - Check::Pair(first, rest) => { - let first = simplify_check(*first); - let rest = simplify_check(*rest); - - if first == Check::None && rest == Check::None { - Check::None - } else { - Check::Pair(Box::new(first), Box::new(rest)) - } - } - } -} - fn fmt_val(f: &mut fmt::Formatter<'_>, path: &[TypePath]) -> fmt::Result { for path in path.iter().rev() { match path { diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index add1c13..dce3e70 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -2,6 +2,7 @@ mod check; mod comparison; mod difference; mod semantic_types; +mod simplify_check; mod standard_types; mod stringify; mod ty; @@ -17,4 +18,5 @@ pub use type_path::*; pub use type_system::*; pub(crate) use difference::difference_type; +pub(crate) use simplify_check::simplify_check; pub(crate) use stringify::stringify_type; diff --git a/crates/rue-typing/src/simplify_check.rs b/crates/rue-typing/src/simplify_check.rs new file mode 100644 index 0000000..a4fa17b --- /dev/null +++ b/crates/rue-typing/src/simplify_check.rs @@ -0,0 +1,246 @@ +use std::collections::VecDeque; + +use crate::Check; + +pub(crate) fn simplify_check(check: Check) -> Check { + match check { + Check::None => Check::None, + Check::IsAtom => Check::IsAtom, + Check::IsPair => Check::IsPair, + Check::IsBool => Check::IsBool, + Check::IsNil => Check::IsNil, + Check::Length(len) => { + if len == 0 { + Check::IsNil + } else { + Check::Length(len) + } + } + Check::And(items) => { + let mut result = Vec::new(); + + let mut is_atom = false; + let mut is_pair = false; + let mut is_bool = false; + let mut is_nil = false; + let mut length = false; + + let mut items: VecDeque<_> = items.into(); + + while !items.is_empty() { + let item = simplify_check(items.pop_front().unwrap()); + + match item { + Check::None => continue, + Check::IsAtom => { + if is_atom { + continue; + } + is_atom = true; + } + Check::IsPair => { + if is_pair { + continue; + } + is_pair = true; + } + Check::IsBool => { + if is_bool { + continue; + } + is_bool = true; + } + Check::IsNil => { + if is_nil { + continue; + } + is_nil = true; + } + Check::Length(..) => { + if length { + continue; + } + length = false; + } + Check::And(children) => { + items.extend(children); + continue; + } + _ => {} + } + + result.push(item); + } + + if result.is_empty() { + Check::None + } else if result.len() == 1 { + result.remove(0) + } else { + Check::And(result) + } + } + Check::Or(items) => { + let mut result = Vec::new(); + let mut atoms: Vec = Vec::new(); + let mut pairs: Vec = Vec::new(); + + let mut items: VecDeque<_> = items.into(); + + let mut is_atom = false; + let mut is_pair = false; + + while !items.is_empty() { + let item = simplify_check(items.pop_front().unwrap()); + + let mut and = Vec::new(); + + match item { + Check::None => return Check::None, + Check::And(children) => { + match children + .iter() + .find(|child| matches!(child, Check::IsAtom | Check::IsPair)) + { + Some(Check::IsAtom) => { + let mut children: Vec = children + .into_iter() + .filter(|child| *child != Check::IsAtom) + .collect(); + + if children.is_empty() { + result.push(Check::IsAtom); + } else if children.len() == 1 { + atoms.push(children.remove(0)); + } else { + atoms.push(Check::And(children)); + } + } + Some(Check::IsPair) => { + let mut children: Vec = children + .into_iter() + .filter(|child| *child != Check::IsPair) + .collect(); + + if children.is_empty() { + result.push(Check::IsPair); + } else if children.len() == 1 { + pairs.push(children.remove(0)); + } else { + pairs.push(Check::And(children)); + } + } + _ => { + and.extend(children); + } + } + } + Check::Or(children) => { + items.extend(children); + } + item => { + and.push(item); + } + } + + let mut new_and = Vec::new(); + + for and_item in and { + match and_item { + Check::IsAtom => { + if is_atom { + continue; + } else if is_pair { + return Check::None; + } + new_and.push(Check::IsAtom); + is_atom = true; + } + Check::IsPair => { + if is_pair { + continue; + } else if is_atom { + return Check::None; + } + new_and.push(Check::IsPair); + is_pair = true; + } + item => { + new_and.push(item); + } + } + } + + if new_and.is_empty() { + continue; + } + + if new_and.len() == 1 { + result.push(new_and.remove(0)); + } else { + result.push(Check::And(new_and)); + } + } + + let prefer_atoms = atoms.len() > pairs.len(); + + let atoms = if atoms.is_empty() { + Check::None + } else if atoms.len() == 1 { + atoms.remove(0) + } else { + Check::Or(atoms) + }; + + let pairs = if pairs.is_empty() { + Check::None + } else if pairs.len() == 1 { + pairs.remove(0) + } else { + Check::Or(pairs) + }; + + if atoms != Check::None && pairs != Check::None { + if prefer_atoms { + result.push(Check::If( + Box::new(Check::IsAtom), + Box::new(atoms), + Box::new(pairs), + )); + } else { + result.push(Check::If( + Box::new(Check::IsPair), + Box::new(pairs), + Box::new(atoms), + )); + } + } else if atoms == Check::None && pairs != Check::None { + result.push(Check::And(vec![Check::IsPair, pairs])); + } else if pairs == Check::None && atoms != Check::None { + result.push(Check::And(vec![Check::IsAtom, atoms])); + } + + if result.len() == 1 { + result.remove(0) + } else { + Check::Or(result) + } + } + Check::If(cond, then, else_) => { + let cond = simplify_check(*cond); + let then = simplify_check(*then); + let else_ = simplify_check(*else_); + Check::If(Box::new(cond), Box::new(then), Box::new(else_)) + } + Check::Pair(first, rest) => { + let first = simplify_check(*first); + let rest = simplify_check(*rest); + + if first == Check::None && rest == Check::None { + Check::None + } else { + Check::Pair(Box::new(first), Box::new(rest)) + } + } + } +} From 4a69c4bd4d68f016566c55ff20908df75d590967 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 19:20:17 -0400 Subject: [PATCH 022/100] Factor part 1 --- crates/rue-typing/src/check.rs | 13 +- crates/rue-typing/src/check/check_error.rs | 12 + crates/rue-typing/src/check/simplify_check.rs | 307 ++++++++++++++++++ crates/rue-typing/src/lib.rs | 2 - crates/rue-typing/src/simplify_check.rs | 246 -------------- 5 files changed, 323 insertions(+), 257 deletions(-) create mode 100644 crates/rue-typing/src/check/check_error.rs create mode 100644 crates/rue-typing/src/check/simplify_check.rs delete mode 100644 crates/rue-typing/src/simplify_check.rs diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 9502740..a3fb2ac 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -4,18 +4,13 @@ use std::{ hash::BuildHasher, }; -use thiserror::Error; - use crate::{Comparison, Type, TypeId, TypePath, TypeSystem}; -#[derive(Debug, Error, Clone, Copy)] -pub enum CheckError { - #[error("recursive check")] - Recursive(TypeId, TypeId), +mod check_error; +mod simplify_check; - #[error("impossible check")] - Impossible(TypeId, TypeId), -} +pub use check_error::*; +pub(crate) use simplify_check::*; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Check { diff --git a/crates/rue-typing/src/check/check_error.rs b/crates/rue-typing/src/check/check_error.rs new file mode 100644 index 0000000..660baf9 --- /dev/null +++ b/crates/rue-typing/src/check/check_error.rs @@ -0,0 +1,12 @@ +use thiserror::Error; + +use crate::TypeId; + +#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)] +pub enum CheckError { + #[error("recursive check")] + Recursive(TypeId, TypeId), + + #[error("impossible check")] + Impossible(TypeId, TypeId), +} diff --git a/crates/rue-typing/src/check/simplify_check.rs b/crates/rue-typing/src/check/simplify_check.rs new file mode 100644 index 0000000..e15eb31 --- /dev/null +++ b/crates/rue-typing/src/check/simplify_check.rs @@ -0,0 +1,307 @@ +use std::collections::VecDeque; + +use crate::Check; + +pub(crate) fn simplify_check(check: Check) -> Check { + match check { + Check::None => Check::None, + Check::IsAtom => Check::IsAtom, + Check::IsPair => Check::IsPair, + Check::IsBool => Check::IsBool, + Check::IsNil => Check::IsNil, + Check::Length(len) => { + if len == 0 { + Check::IsNil + } else { + Check::Length(len) + } + } + Check::And(items) => simplify_and_deep(items), + Check::Or(items) => simplify_or_deep(items), + Check::If(cond, then, else_) => { + let cond = simplify_check(*cond); + let then = simplify_check(*then); + let else_ = simplify_check(*else_); + Check::If(Box::new(cond), Box::new(then), Box::new(else_)) + } + Check::Pair(first, rest) => { + let first = simplify_check(*first); + let rest = simplify_check(*rest); + + if first == Check::None && rest == Check::None { + Check::None + } else { + Check::Pair(Box::new(first), Box::new(rest)) + } + } + } +} + +fn simplify_or_deep(items: Vec) -> Check { + let mut result = Vec::new(); + let mut atoms: Vec = Vec::new(); + let mut pairs: Vec = Vec::new(); + + let mut items: VecDeque<_> = items.into(); + + let mut is_atom = false; + let mut is_pair = false; + + while !items.is_empty() { + let item = simplify_check(items.pop_front().unwrap()); + + let mut and = Vec::new(); + + match item { + Check::None => return Check::None, + Check::And(children) => { + match children + .iter() + .find(|child| matches!(child, Check::IsAtom | Check::IsPair)) + { + Some(Check::IsAtom) => { + let mut children: Vec = children + .into_iter() + .filter(|child| *child != Check::IsAtom) + .collect(); + + if children.is_empty() { + result.push(Check::IsAtom); + } else if children.len() == 1 { + atoms.push(children.remove(0)); + } else { + atoms.push(Check::And(children)); + } + } + Some(Check::IsPair) => { + let mut children: Vec = children + .into_iter() + .filter(|child| *child != Check::IsPair) + .collect(); + + if children.is_empty() { + result.push(Check::IsPair); + } else if children.len() == 1 { + pairs.push(children.remove(0)); + } else { + pairs.push(Check::And(children)); + } + } + _ => { + and.extend(children); + } + } + } + Check::Or(children) => { + items.extend(children); + } + item => { + and.push(item); + } + } + + let mut new_and = Vec::new(); + + for and_item in and { + match and_item { + Check::IsAtom => { + if is_atom { + continue; + } else if is_pair { + return Check::None; + } + new_and.push(Check::IsAtom); + is_atom = true; + } + Check::IsPair => { + if is_pair { + continue; + } else if is_atom { + return Check::None; + } + new_and.push(Check::IsPair); + is_pair = true; + } + item => { + new_and.push(item); + } + } + } + + if new_and.is_empty() { + continue; + } + + if new_and.len() == 1 { + result.push(new_and.remove(0)); + } else { + result.push(Check::And(new_and)); + } + } + + let prefer_atoms = atoms.len() > pairs.len(); + + let atoms = if atoms.is_empty() { + Check::None + } else if atoms.len() == 1 { + atoms.remove(0) + } else { + Check::Or(atoms) + }; + + let pairs = if pairs.is_empty() { + Check::None + } else if pairs.len() == 1 { + pairs.remove(0) + } else { + Check::Or(pairs) + }; + + if atoms != Check::None && pairs != Check::None { + if prefer_atoms { + result.push(Check::If( + Box::new(Check::IsAtom), + Box::new(atoms), + Box::new(pairs), + )); + } else { + result.push(Check::If( + Box::new(Check::IsPair), + Box::new(pairs), + Box::new(atoms), + )); + } + } else if atoms == Check::None && pairs != Check::None { + result.push(Check::And(vec![Check::IsPair, pairs])); + } else if pairs == Check::None && atoms != Check::None { + result.push(Check::And(vec![Check::IsAtom, atoms])); + } + + if result.len() == 1 { + result.remove(0) + } else { + Check::Or(result) + } +} + +fn simplify_and_deep(items: Vec) -> Check { + let mut items = VecDeque::from(items); + + let iter = std::iter::from_fn(|| { + while let Some(item) = items.pop_front() { + match simplify_check(item) { + Check::And(children) => items.extend(children), + item => return Some(item), + } + } + None + }); + + simplify_and_shallow(iter) +} + +fn simplify_and_shallow(items: impl IntoIterator) -> Check { + let mut result = Vec::new(); + let mut is_atom = false; + let mut is_pair = false; + let mut is_bool = false; + let mut is_nil = false; + let mut length = false; + + for item in items { + match item { + Check::None => continue, + Check::IsAtom if is_atom => continue, + Check::IsAtom => is_atom = true, + Check::IsPair if is_pair => continue, + Check::IsPair => is_pair = true, + Check::IsBool if is_bool => continue, + Check::IsBool => is_bool = true, + Check::IsNil if is_nil => continue, + Check::IsNil => is_nil = true, + Check::Length(..) if length => continue, + Check::Length(..) => length = true, + _ => {} + } + result.push(item); + } + + construct_and(result) +} + +fn construct_and(mut items: Vec) -> Check { + if items.is_empty() { + Check::None + } else if items.len() == 1 { + items.remove(0) + } else { + Check::And(items) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn simplify_and_none() { + assert_eq!(simplify_and_shallow(vec![Check::None]), Check::None); + } + + #[test] + fn simplify_none_and_none() { + assert_eq!( + simplify_and_shallow(vec![Check::None, Check::None]), + Check::None + ); + } + + #[test] + fn simplify_check_and_none() { + assert_eq!( + simplify_and_shallow(vec![Check::None, Check::IsAtom]), + Check::IsAtom + ); + assert_eq!( + simplify_and_shallow(vec![Check::IsAtom, Check::None]), + Check::IsAtom + ); + } + + #[test] + fn simplify_and_one_check() { + assert_eq!(simplify_and_shallow(vec![Check::IsAtom]), Check::IsAtom); + } + + #[test] + fn simplify_atom_and_atom() { + assert_eq!( + simplify_and_shallow(vec![Check::IsAtom, Check::IsAtom]), + Check::IsAtom + ); + } + + #[test] + fn simplify_atom_and_pair() { + assert_eq!( + simplify_and_shallow(vec![Check::IsAtom, Check::IsPair]), + Check::And(vec![Check::IsAtom, Check::IsPair]) + ); + } + + #[test] + fn simplify_pair_and_pair() { + assert_eq!( + simplify_and_shallow(vec![Check::IsPair, Check::IsPair]), + Check::IsPair + ); + } + + #[test] + fn simplify_pair_and_atom() { + assert_eq!( + simplify_and_shallow(vec![Check::IsPair, Check::IsAtom]), + Check::And(vec![Check::IsPair, Check::IsAtom]) + ); + } +} diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index dce3e70..add1c13 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -2,7 +2,6 @@ mod check; mod comparison; mod difference; mod semantic_types; -mod simplify_check; mod standard_types; mod stringify; mod ty; @@ -18,5 +17,4 @@ pub use type_path::*; pub use type_system::*; pub(crate) use difference::difference_type; -pub(crate) use simplify_check::simplify_check; pub(crate) use stringify::stringify_type; diff --git a/crates/rue-typing/src/simplify_check.rs b/crates/rue-typing/src/simplify_check.rs deleted file mode 100644 index a4fa17b..0000000 --- a/crates/rue-typing/src/simplify_check.rs +++ /dev/null @@ -1,246 +0,0 @@ -use std::collections::VecDeque; - -use crate::Check; - -pub(crate) fn simplify_check(check: Check) -> Check { - match check { - Check::None => Check::None, - Check::IsAtom => Check::IsAtom, - Check::IsPair => Check::IsPair, - Check::IsBool => Check::IsBool, - Check::IsNil => Check::IsNil, - Check::Length(len) => { - if len == 0 { - Check::IsNil - } else { - Check::Length(len) - } - } - Check::And(items) => { - let mut result = Vec::new(); - - let mut is_atom = false; - let mut is_pair = false; - let mut is_bool = false; - let mut is_nil = false; - let mut length = false; - - let mut items: VecDeque<_> = items.into(); - - while !items.is_empty() { - let item = simplify_check(items.pop_front().unwrap()); - - match item { - Check::None => continue, - Check::IsAtom => { - if is_atom { - continue; - } - is_atom = true; - } - Check::IsPair => { - if is_pair { - continue; - } - is_pair = true; - } - Check::IsBool => { - if is_bool { - continue; - } - is_bool = true; - } - Check::IsNil => { - if is_nil { - continue; - } - is_nil = true; - } - Check::Length(..) => { - if length { - continue; - } - length = false; - } - Check::And(children) => { - items.extend(children); - continue; - } - _ => {} - } - - result.push(item); - } - - if result.is_empty() { - Check::None - } else if result.len() == 1 { - result.remove(0) - } else { - Check::And(result) - } - } - Check::Or(items) => { - let mut result = Vec::new(); - let mut atoms: Vec = Vec::new(); - let mut pairs: Vec = Vec::new(); - - let mut items: VecDeque<_> = items.into(); - - let mut is_atom = false; - let mut is_pair = false; - - while !items.is_empty() { - let item = simplify_check(items.pop_front().unwrap()); - - let mut and = Vec::new(); - - match item { - Check::None => return Check::None, - Check::And(children) => { - match children - .iter() - .find(|child| matches!(child, Check::IsAtom | Check::IsPair)) - { - Some(Check::IsAtom) => { - let mut children: Vec = children - .into_iter() - .filter(|child| *child != Check::IsAtom) - .collect(); - - if children.is_empty() { - result.push(Check::IsAtom); - } else if children.len() == 1 { - atoms.push(children.remove(0)); - } else { - atoms.push(Check::And(children)); - } - } - Some(Check::IsPair) => { - let mut children: Vec = children - .into_iter() - .filter(|child| *child != Check::IsPair) - .collect(); - - if children.is_empty() { - result.push(Check::IsPair); - } else if children.len() == 1 { - pairs.push(children.remove(0)); - } else { - pairs.push(Check::And(children)); - } - } - _ => { - and.extend(children); - } - } - } - Check::Or(children) => { - items.extend(children); - } - item => { - and.push(item); - } - } - - let mut new_and = Vec::new(); - - for and_item in and { - match and_item { - Check::IsAtom => { - if is_atom { - continue; - } else if is_pair { - return Check::None; - } - new_and.push(Check::IsAtom); - is_atom = true; - } - Check::IsPair => { - if is_pair { - continue; - } else if is_atom { - return Check::None; - } - new_and.push(Check::IsPair); - is_pair = true; - } - item => { - new_and.push(item); - } - } - } - - if new_and.is_empty() { - continue; - } - - if new_and.len() == 1 { - result.push(new_and.remove(0)); - } else { - result.push(Check::And(new_and)); - } - } - - let prefer_atoms = atoms.len() > pairs.len(); - - let atoms = if atoms.is_empty() { - Check::None - } else if atoms.len() == 1 { - atoms.remove(0) - } else { - Check::Or(atoms) - }; - - let pairs = if pairs.is_empty() { - Check::None - } else if pairs.len() == 1 { - pairs.remove(0) - } else { - Check::Or(pairs) - }; - - if atoms != Check::None && pairs != Check::None { - if prefer_atoms { - result.push(Check::If( - Box::new(Check::IsAtom), - Box::new(atoms), - Box::new(pairs), - )); - } else { - result.push(Check::If( - Box::new(Check::IsPair), - Box::new(pairs), - Box::new(atoms), - )); - } - } else if atoms == Check::None && pairs != Check::None { - result.push(Check::And(vec![Check::IsPair, pairs])); - } else if pairs == Check::None && atoms != Check::None { - result.push(Check::And(vec![Check::IsAtom, atoms])); - } - - if result.len() == 1 { - result.remove(0) - } else { - Check::Or(result) - } - } - Check::If(cond, then, else_) => { - let cond = simplify_check(*cond); - let then = simplify_check(*then); - let else_ = simplify_check(*else_); - Check::If(Box::new(cond), Box::new(then), Box::new(else_)) - } - Check::Pair(first, rest) => { - let first = simplify_check(*first); - let rest = simplify_check(*rest); - - if first == Check::None && rest == Check::None { - Check::None - } else { - Check::Pair(Box::new(first), Box::new(rest)) - } - } - } -} From d95d738fbdb2447619d54c029839379a8c58992b Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 19:22:15 -0400 Subject: [PATCH 023/100] Multiple files --- crates/rue-typing/src/check.rs | 5 + crates/rue-typing/src/check/simplify_and.rs | 58 +++++ crates/rue-typing/src/check/simplify_check.rs | 208 +----------------- crates/rue-typing/src/check/simplify_or.rs | 150 +++++++++++++ 4 files changed, 216 insertions(+), 205 deletions(-) create mode 100644 crates/rue-typing/src/check/simplify_and.rs create mode 100644 crates/rue-typing/src/check/simplify_or.rs diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index a3fb2ac..9130463 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -7,10 +7,15 @@ use std::{ use crate::{Comparison, Type, TypeId, TypePath, TypeSystem}; mod check_error; +mod simplify_and; mod simplify_check; +mod simplify_or; pub use check_error::*; + +pub(crate) use simplify_and::*; pub(crate) use simplify_check::*; +pub(crate) use simplify_or::*; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Check { diff --git a/crates/rue-typing/src/check/simplify_and.rs b/crates/rue-typing/src/check/simplify_and.rs new file mode 100644 index 0000000..e4d70fe --- /dev/null +++ b/crates/rue-typing/src/check/simplify_and.rs @@ -0,0 +1,58 @@ +use std::collections::VecDeque; + +use super::{simplify_check, Check}; + +pub(crate) fn simplify_and_deep(items: Vec) -> Check { + let mut items = VecDeque::from(items); + + let iter = std::iter::from_fn(|| { + while let Some(item) = items.pop_front() { + match simplify_check(item) { + Check::And(children) => items.extend(children), + item => return Some(item), + } + } + None + }); + + simplify_and_shallow(iter) +} + +pub(crate) fn simplify_and_shallow(items: impl IntoIterator) -> Check { + let mut result = Vec::new(); + let mut is_atom = false; + let mut is_pair = false; + let mut is_bool = false; + let mut is_nil = false; + let mut length = false; + + for item in items { + match item { + Check::None => continue, + Check::IsAtom if is_atom => continue, + Check::IsAtom => is_atom = true, + Check::IsPair if is_pair => continue, + Check::IsPair => is_pair = true, + Check::IsBool if is_bool => continue, + Check::IsBool => is_bool = true, + Check::IsNil if is_nil => continue, + Check::IsNil => is_nil = true, + Check::Length(..) if length => continue, + Check::Length(..) => length = true, + _ => {} + } + result.push(item); + } + + construct_and(result) +} + +fn construct_and(mut items: Vec) -> Check { + if items.is_empty() { + Check::None + } else if items.len() == 1 { + items.remove(0) + } else { + Check::And(items) + } +} diff --git a/crates/rue-typing/src/check/simplify_check.rs b/crates/rue-typing/src/check/simplify_check.rs index e15eb31..c6c5a61 100644 --- a/crates/rue-typing/src/check/simplify_check.rs +++ b/crates/rue-typing/src/check/simplify_check.rs @@ -1,6 +1,4 @@ -use std::collections::VecDeque; - -use crate::Check; +use super::{simplify_and_deep, simplify_or_deep, Check}; pub(crate) fn simplify_check(check: Check) -> Check { match check { @@ -37,210 +35,10 @@ pub(crate) fn simplify_check(check: Check) -> Check { } } -fn simplify_or_deep(items: Vec) -> Check { - let mut result = Vec::new(); - let mut atoms: Vec = Vec::new(); - let mut pairs: Vec = Vec::new(); - - let mut items: VecDeque<_> = items.into(); - - let mut is_atom = false; - let mut is_pair = false; - - while !items.is_empty() { - let item = simplify_check(items.pop_front().unwrap()); - - let mut and = Vec::new(); - - match item { - Check::None => return Check::None, - Check::And(children) => { - match children - .iter() - .find(|child| matches!(child, Check::IsAtom | Check::IsPair)) - { - Some(Check::IsAtom) => { - let mut children: Vec = children - .into_iter() - .filter(|child| *child != Check::IsAtom) - .collect(); - - if children.is_empty() { - result.push(Check::IsAtom); - } else if children.len() == 1 { - atoms.push(children.remove(0)); - } else { - atoms.push(Check::And(children)); - } - } - Some(Check::IsPair) => { - let mut children: Vec = children - .into_iter() - .filter(|child| *child != Check::IsPair) - .collect(); - - if children.is_empty() { - result.push(Check::IsPair); - } else if children.len() == 1 { - pairs.push(children.remove(0)); - } else { - pairs.push(Check::And(children)); - } - } - _ => { - and.extend(children); - } - } - } - Check::Or(children) => { - items.extend(children); - } - item => { - and.push(item); - } - } - - let mut new_and = Vec::new(); - - for and_item in and { - match and_item { - Check::IsAtom => { - if is_atom { - continue; - } else if is_pair { - return Check::None; - } - new_and.push(Check::IsAtom); - is_atom = true; - } - Check::IsPair => { - if is_pair { - continue; - } else if is_atom { - return Check::None; - } - new_and.push(Check::IsPair); - is_pair = true; - } - item => { - new_and.push(item); - } - } - } - - if new_and.is_empty() { - continue; - } - - if new_and.len() == 1 { - result.push(new_and.remove(0)); - } else { - result.push(Check::And(new_and)); - } - } - - let prefer_atoms = atoms.len() > pairs.len(); - - let atoms = if atoms.is_empty() { - Check::None - } else if atoms.len() == 1 { - atoms.remove(0) - } else { - Check::Or(atoms) - }; - - let pairs = if pairs.is_empty() { - Check::None - } else if pairs.len() == 1 { - pairs.remove(0) - } else { - Check::Or(pairs) - }; - - if atoms != Check::None && pairs != Check::None { - if prefer_atoms { - result.push(Check::If( - Box::new(Check::IsAtom), - Box::new(atoms), - Box::new(pairs), - )); - } else { - result.push(Check::If( - Box::new(Check::IsPair), - Box::new(pairs), - Box::new(atoms), - )); - } - } else if atoms == Check::None && pairs != Check::None { - result.push(Check::And(vec![Check::IsPair, pairs])); - } else if pairs == Check::None && atoms != Check::None { - result.push(Check::And(vec![Check::IsAtom, atoms])); - } - - if result.len() == 1 { - result.remove(0) - } else { - Check::Or(result) - } -} - -fn simplify_and_deep(items: Vec) -> Check { - let mut items = VecDeque::from(items); - - let iter = std::iter::from_fn(|| { - while let Some(item) = items.pop_front() { - match simplify_check(item) { - Check::And(children) => items.extend(children), - item => return Some(item), - } - } - None - }); - - simplify_and_shallow(iter) -} - -fn simplify_and_shallow(items: impl IntoIterator) -> Check { - let mut result = Vec::new(); - let mut is_atom = false; - let mut is_pair = false; - let mut is_bool = false; - let mut is_nil = false; - let mut length = false; - - for item in items { - match item { - Check::None => continue, - Check::IsAtom if is_atom => continue, - Check::IsAtom => is_atom = true, - Check::IsPair if is_pair => continue, - Check::IsPair => is_pair = true, - Check::IsBool if is_bool => continue, - Check::IsBool => is_bool = true, - Check::IsNil if is_nil => continue, - Check::IsNil => is_nil = true, - Check::Length(..) if length => continue, - Check::Length(..) => length = true, - _ => {} - } - result.push(item); - } - - construct_and(result) -} - -fn construct_and(mut items: Vec) -> Check { - if items.is_empty() { - Check::None - } else if items.len() == 1 { - items.remove(0) - } else { - Check::And(items) - } -} - #[cfg(test)] mod tests { + use crate::simplify_and_shallow; + use super::*; #[test] diff --git a/crates/rue-typing/src/check/simplify_or.rs b/crates/rue-typing/src/check/simplify_or.rs new file mode 100644 index 0000000..605568d --- /dev/null +++ b/crates/rue-typing/src/check/simplify_or.rs @@ -0,0 +1,150 @@ +use std::collections::VecDeque; + +use super::{simplify_check, Check}; + +pub(crate) fn simplify_or_deep(items: Vec) -> Check { + let mut result = Vec::new(); + let mut atoms: Vec = Vec::new(); + let mut pairs: Vec = Vec::new(); + + let mut items: VecDeque<_> = items.into(); + + let mut is_atom = false; + let mut is_pair = false; + + while !items.is_empty() { + let item = simplify_check(items.pop_front().unwrap()); + + let mut and = Vec::new(); + + match item { + Check::None => return Check::None, + Check::And(children) => { + match children + .iter() + .find(|child| matches!(child, Check::IsAtom | Check::IsPair)) + { + Some(Check::IsAtom) => { + let mut children: Vec = children + .into_iter() + .filter(|child| *child != Check::IsAtom) + .collect(); + + if children.is_empty() { + result.push(Check::IsAtom); + } else if children.len() == 1 { + atoms.push(children.remove(0)); + } else { + atoms.push(Check::And(children)); + } + } + Some(Check::IsPair) => { + let mut children: Vec = children + .into_iter() + .filter(|child| *child != Check::IsPair) + .collect(); + + if children.is_empty() { + result.push(Check::IsPair); + } else if children.len() == 1 { + pairs.push(children.remove(0)); + } else { + pairs.push(Check::And(children)); + } + } + _ => { + and.extend(children); + } + } + } + Check::Or(children) => { + items.extend(children); + } + item => { + and.push(item); + } + } + + let mut new_and = Vec::new(); + + for and_item in and { + match and_item { + Check::IsAtom => { + if is_atom { + continue; + } else if is_pair { + return Check::None; + } + new_and.push(Check::IsAtom); + is_atom = true; + } + Check::IsPair => { + if is_pair { + continue; + } else if is_atom { + return Check::None; + } + new_and.push(Check::IsPair); + is_pair = true; + } + item => { + new_and.push(item); + } + } + } + + if new_and.is_empty() { + continue; + } + + if new_and.len() == 1 { + result.push(new_and.remove(0)); + } else { + result.push(Check::And(new_and)); + } + } + + let prefer_atoms = atoms.len() > pairs.len(); + + let atoms = if atoms.is_empty() { + Check::None + } else if atoms.len() == 1 { + atoms.remove(0) + } else { + Check::Or(atoms) + }; + + let pairs = if pairs.is_empty() { + Check::None + } else if pairs.len() == 1 { + pairs.remove(0) + } else { + Check::Or(pairs) + }; + + if atoms != Check::None && pairs != Check::None { + if prefer_atoms { + result.push(Check::If( + Box::new(Check::IsAtom), + Box::new(atoms), + Box::new(pairs), + )); + } else { + result.push(Check::If( + Box::new(Check::IsPair), + Box::new(pairs), + Box::new(atoms), + )); + } + } else if atoms == Check::None && pairs != Check::None { + result.push(Check::And(vec![Check::IsPair, pairs])); + } else if pairs == Check::None && atoms != Check::None { + result.push(Check::And(vec![Check::IsAtom, atoms])); + } + + if result.len() == 1 { + result.remove(0) + } else { + Check::Or(result) + } +} From 4e24d7f8b7d5aa71e3f14b5300fbf6a898d07379 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 19:27:07 -0400 Subject: [PATCH 024/100] Factor out shape checks --- crates/rue-typing/src/check/simplify_or.rs | 65 ++++++++++++---------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/crates/rue-typing/src/check/simplify_or.rs b/crates/rue-typing/src/check/simplify_or.rs index 605568d..2318b30 100644 --- a/crates/rue-typing/src/check/simplify_or.rs +++ b/crates/rue-typing/src/check/simplify_or.rs @@ -6,9 +6,7 @@ pub(crate) fn simplify_or_deep(items: Vec) -> Check { let mut result = Vec::new(); let mut atoms: Vec = Vec::new(); let mut pairs: Vec = Vec::new(); - let mut items: VecDeque<_> = items.into(); - let mut is_atom = false; let mut is_pair = false; @@ -104,47 +102,58 @@ pub(crate) fn simplify_or_deep(items: Vec) -> Check { } } - let prefer_atoms = atoms.len() > pairs.len(); + if let Some(check) = construct_shape_checks(atoms, pairs) { + result.push(check); + } + + construct_or(result) +} + +fn construct_shape_checks( + mut atom_checks: Vec, + mut pair_checks: Vec, +) -> Option { + let prefer_atoms = atom_checks.len() > pair_checks.len(); - let atoms = if atoms.is_empty() { + let atoms = if atom_checks.is_empty() { Check::None - } else if atoms.len() == 1 { - atoms.remove(0) + } else if atom_checks.len() == 1 { + atom_checks.remove(0) } else { - Check::Or(atoms) + Check::Or(atom_checks) }; - let pairs = if pairs.is_empty() { + let pairs = if pair_checks.is_empty() { Check::None - } else if pairs.len() == 1 { - pairs.remove(0) + } else if pair_checks.len() == 1 { + pair_checks.remove(0) } else { - Check::Or(pairs) + Check::Or(pair_checks) }; - if atoms != Check::None && pairs != Check::None { + let check = if atoms != Check::None && pairs != Check::None { if prefer_atoms { - result.push(Check::If( - Box::new(Check::IsAtom), - Box::new(atoms), - Box::new(pairs), - )); + Check::If(Box::new(Check::IsAtom), Box::new(atoms), Box::new(pairs)) } else { - result.push(Check::If( - Box::new(Check::IsPair), - Box::new(pairs), - Box::new(atoms), - )); + Check::If(Box::new(Check::IsPair), Box::new(pairs), Box::new(atoms)) } } else if atoms == Check::None && pairs != Check::None { - result.push(Check::And(vec![Check::IsPair, pairs])); + Check::And(vec![Check::IsPair, pairs]) } else if pairs == Check::None && atoms != Check::None { - result.push(Check::And(vec![Check::IsAtom, atoms])); - } + Check::And(vec![Check::IsAtom, atoms]) + } else { + return None; + }; + + Some(check) +} - if result.len() == 1 { - result.remove(0) +fn construct_or(mut items: Vec) -> Check { + if items.is_empty() { + unreachable!() + } else if items.len() == 1 { + items.remove(0) } else { - Check::Or(result) + Check::Or(items) } } From d32e072a0503f34acf66c9aad02fb58b6c491d16 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 19:28:28 -0400 Subject: [PATCH 025/100] Move simplify and tests --- crates/rue-typing/src/check/simplify_and.rs | 67 ++++++++++++++++++ crates/rue-typing/src/check/simplify_check.rs | 69 ------------------- 2 files changed, 67 insertions(+), 69 deletions(-) diff --git a/crates/rue-typing/src/check/simplify_and.rs b/crates/rue-typing/src/check/simplify_and.rs index e4d70fe..4d0eb32 100644 --- a/crates/rue-typing/src/check/simplify_and.rs +++ b/crates/rue-typing/src/check/simplify_and.rs @@ -56,3 +56,70 @@ fn construct_and(mut items: Vec) -> Check { Check::And(items) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn simplify_and_none() { + assert_eq!(simplify_and_shallow(vec![Check::None]), Check::None); + } + + #[test] + fn simplify_none_and_none() { + assert_eq!( + simplify_and_shallow(vec![Check::None, Check::None]), + Check::None + ); + } + + #[test] + fn simplify_check_and_none() { + assert_eq!( + simplify_and_shallow(vec![Check::None, Check::IsAtom]), + Check::IsAtom + ); + assert_eq!( + simplify_and_shallow(vec![Check::IsAtom, Check::None]), + Check::IsAtom + ); + } + + #[test] + fn simplify_and_one_check() { + assert_eq!(simplify_and_shallow(vec![Check::IsAtom]), Check::IsAtom); + } + + #[test] + fn simplify_atom_and_atom() { + assert_eq!( + simplify_and_shallow(vec![Check::IsAtom, Check::IsAtom]), + Check::IsAtom + ); + } + + #[test] + fn simplify_atom_and_pair() { + assert_eq!( + simplify_and_shallow(vec![Check::IsAtom, Check::IsPair]), + Check::And(vec![Check::IsAtom, Check::IsPair]) + ); + } + + #[test] + fn simplify_pair_and_pair() { + assert_eq!( + simplify_and_shallow(vec![Check::IsPair, Check::IsPair]), + Check::IsPair + ); + } + + #[test] + fn simplify_pair_and_atom() { + assert_eq!( + simplify_and_shallow(vec![Check::IsPair, Check::IsAtom]), + Check::And(vec![Check::IsPair, Check::IsAtom]) + ); + } +} diff --git a/crates/rue-typing/src/check/simplify_check.rs b/crates/rue-typing/src/check/simplify_check.rs index c6c5a61..4f9e314 100644 --- a/crates/rue-typing/src/check/simplify_check.rs +++ b/crates/rue-typing/src/check/simplify_check.rs @@ -34,72 +34,3 @@ pub(crate) fn simplify_check(check: Check) -> Check { } } } - -#[cfg(test)] -mod tests { - use crate::simplify_and_shallow; - - use super::*; - - #[test] - fn simplify_and_none() { - assert_eq!(simplify_and_shallow(vec![Check::None]), Check::None); - } - - #[test] - fn simplify_none_and_none() { - assert_eq!( - simplify_and_shallow(vec![Check::None, Check::None]), - Check::None - ); - } - - #[test] - fn simplify_check_and_none() { - assert_eq!( - simplify_and_shallow(vec![Check::None, Check::IsAtom]), - Check::IsAtom - ); - assert_eq!( - simplify_and_shallow(vec![Check::IsAtom, Check::None]), - Check::IsAtom - ); - } - - #[test] - fn simplify_and_one_check() { - assert_eq!(simplify_and_shallow(vec![Check::IsAtom]), Check::IsAtom); - } - - #[test] - fn simplify_atom_and_atom() { - assert_eq!( - simplify_and_shallow(vec![Check::IsAtom, Check::IsAtom]), - Check::IsAtom - ); - } - - #[test] - fn simplify_atom_and_pair() { - assert_eq!( - simplify_and_shallow(vec![Check::IsAtom, Check::IsPair]), - Check::And(vec![Check::IsAtom, Check::IsPair]) - ); - } - - #[test] - fn simplify_pair_and_pair() { - assert_eq!( - simplify_and_shallow(vec![Check::IsPair, Check::IsPair]), - Check::IsPair - ); - } - - #[test] - fn simplify_pair_and_atom() { - assert_eq!( - simplify_and_shallow(vec![Check::IsPair, Check::IsAtom]), - Check::And(vec![Check::IsPair, Check::IsAtom]) - ); - } -} From 0ff446ef5ef50e0f8fb720f70b12d218b9a39266 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 19:31:09 -0400 Subject: [PATCH 026/100] More tests --- crates/rue-typing/src/check/simplify_and.rs | 50 ++++++++++++++------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/crates/rue-typing/src/check/simplify_and.rs b/crates/rue-typing/src/check/simplify_and.rs index 4d0eb32..ca761ec 100644 --- a/crates/rue-typing/src/check/simplify_and.rs +++ b/crates/rue-typing/src/check/simplify_and.rs @@ -62,64 +62,80 @@ mod tests { use super::*; #[test] - fn simplify_and_none() { - assert_eq!(simplify_and_shallow(vec![Check::None]), Check::None); + fn test_simplify_and_none() { + assert_eq!(simplify_and_shallow([Check::None]), Check::None); } #[test] - fn simplify_none_and_none() { + fn test_simplify_none_and_none() { assert_eq!( - simplify_and_shallow(vec![Check::None, Check::None]), + simplify_and_shallow([Check::None, Check::None]), Check::None ); } #[test] - fn simplify_check_and_none() { + fn test_simplify_check_and_none() { assert_eq!( - simplify_and_shallow(vec![Check::None, Check::IsAtom]), + simplify_and_shallow([Check::None, Check::IsAtom]), Check::IsAtom ); assert_eq!( - simplify_and_shallow(vec![Check::IsAtom, Check::None]), + simplify_and_shallow([Check::IsAtom, Check::None]), Check::IsAtom ); } #[test] - fn simplify_and_one_check() { - assert_eq!(simplify_and_shallow(vec![Check::IsAtom]), Check::IsAtom); + fn test_simplify_and_one_check() { + assert_eq!(simplify_and_shallow([Check::IsAtom]), Check::IsAtom); } #[test] - fn simplify_atom_and_atom() { + fn test_simplify_atom_and_atom() { assert_eq!( - simplify_and_shallow(vec![Check::IsAtom, Check::IsAtom]), + simplify_and_shallow([Check::IsAtom, Check::IsAtom]), Check::IsAtom ); } #[test] - fn simplify_atom_and_pair() { + fn test_simplify_atom_and_pair() { assert_eq!( - simplify_and_shallow(vec![Check::IsAtom, Check::IsPair]), + simplify_and_shallow([Check::IsAtom, Check::IsPair]), Check::And(vec![Check::IsAtom, Check::IsPair]) ); } #[test] - fn simplify_pair_and_pair() { + fn test_simplify_pair_and_pair() { assert_eq!( - simplify_and_shallow(vec![Check::IsPair, Check::IsPair]), + simplify_and_shallow([Check::IsPair, Check::IsPair]), Check::IsPair ); } #[test] - fn simplify_pair_and_atom() { + fn test_simplify_pair_and_atom() { assert_eq!( - simplify_and_shallow(vec![Check::IsPair, Check::IsAtom]), + simplify_and_shallow([Check::IsPair, Check::IsAtom]), Check::And(vec![Check::IsPair, Check::IsAtom]) ); } + + #[test] + fn test_simplify_and_shallow() { + assert_eq!( + simplify_and_shallow([Check::And(vec![Check::None, Check::None])]), + Check::And(vec![Check::None, Check::None]) + ); + } + + #[test] + fn test_simplify_and_deep() { + assert_eq!( + simplify_and_deep(vec![Check::And(vec![Check::None, Check::None])]), + Check::None + ); + } } From aa8a642cb7b551dcbd3fa8cdfc71230ba173267d Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 20:51:09 -0400 Subject: [PATCH 027/100] Refactor or --- crates/rue-typing/src/check/simplify_and.rs | 10 +- crates/rue-typing/src/check/simplify_or.rs | 320 ++++++++++++-------- 2 files changed, 205 insertions(+), 125 deletions(-) diff --git a/crates/rue-typing/src/check/simplify_and.rs b/crates/rue-typing/src/check/simplify_and.rs index ca761ec..e22a680 100644 --- a/crates/rue-typing/src/check/simplify_and.rs +++ b/crates/rue-typing/src/check/simplify_and.rs @@ -47,7 +47,7 @@ pub(crate) fn simplify_and_shallow(items: impl IntoIterator) -> Ch construct_and(result) } -fn construct_and(mut items: Vec) -> Check { +pub(crate) fn construct_and(mut items: Vec) -> Check { if items.is_empty() { Check::None } else if items.len() == 1 { @@ -77,11 +77,15 @@ mod tests { #[test] fn test_simplify_check_and_none() { assert_eq!( - simplify_and_shallow([Check::None, Check::IsAtom]), + simplify_and_shallow([Check::IsAtom, Check::None]), Check::IsAtom ); + } + + #[test] + fn test_simplify_none_and_check() { assert_eq!( - simplify_and_shallow([Check::IsAtom, Check::None]), + simplify_and_shallow([Check::None, Check::IsAtom]), Check::IsAtom ); } diff --git a/crates/rue-typing/src/check/simplify_or.rs b/crates/rue-typing/src/check/simplify_or.rs index 2318b30..a045116 100644 --- a/crates/rue-typing/src/check/simplify_or.rs +++ b/crates/rue-typing/src/check/simplify_or.rs @@ -1,151 +1,140 @@ use std::collections::VecDeque; -use super::{simplify_check, Check}; +use super::{construct_and, simplify_check, Check}; + +enum ShapeCheck { + None, + Any, + Or(Check), +} pub(crate) fn simplify_or_deep(items: Vec) -> Check { + let mut items = VecDeque::from(items); + + let iter = std::iter::from_fn(|| { + while let Some(item) = items.pop_front() { + match simplify_check(item) { + Check::Or(children) => items.extend(children), + item => return Some(item), + } + } + None + }); + + simplify_or_shallow(iter) +} + +pub(crate) fn simplify_or_shallow(items: impl IntoIterator) -> Check { let mut result = Vec::new(); - let mut atoms: Vec = Vec::new(); - let mut pairs: Vec = Vec::new(); - let mut items: VecDeque<_> = items.into(); - let mut is_atom = false; - let mut is_pair = false; - while !items.is_empty() { - let item = simplify_check(items.pop_front().unwrap()); + let mut atom_checks: Vec = Vec::new(); + let mut pair_checks: Vec = Vec::new(); - let mut and = Vec::new(); + let mut any_atom = false; + let mut any_pair = false; + for item in items { match item { Check::None => return Check::None, - Check::And(children) => { - match children - .iter() - .find(|child| matches!(child, Check::IsAtom | Check::IsPair)) - { - Some(Check::IsAtom) => { - let mut children: Vec = children - .into_iter() - .filter(|child| *child != Check::IsAtom) - .collect(); - - if children.is_empty() { - result.push(Check::IsAtom); - } else if children.len() == 1 { - atoms.push(children.remove(0)); - } else { - atoms.push(Check::And(children)); - } - } - Some(Check::IsPair) => { - let mut children: Vec = children - .into_iter() - .filter(|child| *child != Check::IsPair) - .collect(); - - if children.is_empty() { - result.push(Check::IsPair); - } else if children.len() == 1 { - pairs.push(children.remove(0)); - } else { - pairs.push(Check::And(children)); - } - } - _ => { - and.extend(children); - } - } - } - Check::Or(children) => { - items.extend(children); + Check::IsAtom if any_atom => continue, + Check::IsPair if any_pair => continue, + Check::IsAtom => { + any_atom = true; + continue; } - item => { - and.push(item); + Check::IsPair => { + any_pair = true; + continue; } - } - - let mut new_and = Vec::new(); - - for and_item in and { - match and_item { - Check::IsAtom => { - if is_atom { - continue; - } else if is_pair { - return Check::None; - } - new_and.push(Check::IsAtom); - is_atom = true; - } - Check::IsPair => { - if is_pair { - continue; - } else if is_atom { - return Check::None; - } - new_and.push(Check::IsPair); - is_pair = true; - } - item => { - new_and.push(item); + Check::And(children) => { + let (shape, checks) = extract_shape_check(children); + match shape { + Some(Check::IsAtom) => atom_checks.push(checks), + Some(Check::IsPair) => pair_checks.push(checks), + _ => result.push(checks), } + continue; } - } - - if new_and.is_empty() { - continue; - } - - if new_and.len() == 1 { - result.push(new_and.remove(0)); - } else { - result.push(Check::And(new_and)); + item => result.push(item), } } - if let Some(check) = construct_shape_checks(atoms, pairs) { - result.push(check); - } + let prefer_atom = atom_checks.len() > pair_checks.len(); - construct_or(result) -} - -fn construct_shape_checks( - mut atom_checks: Vec, - mut pair_checks: Vec, -) -> Option { - let prefer_atoms = atom_checks.len() > pair_checks.len(); - - let atoms = if atom_checks.is_empty() { - Check::None - } else if atom_checks.len() == 1 { - atom_checks.remove(0) + let atom_check = if any_atom { + ShapeCheck::Any + } else if atom_checks.is_empty() { + ShapeCheck::None } else { - Check::Or(atom_checks) + ShapeCheck::Or(construct_or(atom_checks)) }; - let pairs = if pair_checks.is_empty() { - Check::None - } else if pair_checks.len() == 1 { - pair_checks.remove(0) + let pair_check = if any_pair { + ShapeCheck::Any + } else if pair_checks.is_empty() { + ShapeCheck::None } else { - Check::Or(pair_checks) + ShapeCheck::Or(construct_or(pair_checks)) }; - let check = if atoms != Check::None && pairs != Check::None { - if prefer_atoms { - Check::If(Box::new(Check::IsAtom), Box::new(atoms), Box::new(pairs)) - } else { - Check::If(Box::new(Check::IsPair), Box::new(pairs), Box::new(atoms)) + match (atom_check, pair_check) { + (ShapeCheck::None, ShapeCheck::None) => {} + (ShapeCheck::Any, ShapeCheck::Any) => { + return Check::None; } - } else if atoms == Check::None && pairs != Check::None { - Check::And(vec![Check::IsPair, pairs]) - } else if pairs == Check::None && atoms != Check::None { - Check::And(vec![Check::IsAtom, atoms]) - } else { - return None; - }; + (ShapeCheck::Any, ShapeCheck::None) => { + result.push(Check::IsAtom); + } + (ShapeCheck::None, ShapeCheck::Any) => { + result.push(Check::IsPair); + } + (ShapeCheck::Or(atom_check), ShapeCheck::None) => { + result.push(construct_and(vec![Check::IsAtom, atom_check])); + } + (ShapeCheck::None, ShapeCheck::Or(pair_check)) => { + result.push(construct_and(vec![Check::IsPair, pair_check])); + } + (ShapeCheck::Or(atom_check), ShapeCheck::Any) => { + result.push(Check::IsPair); + result.push(atom_check); + } + (ShapeCheck::Any, ShapeCheck::Or(pair_check)) => { + result.push(Check::IsAtom); + result.push(pair_check); + } + (ShapeCheck::Or(atom_check), ShapeCheck::Or(pair_check)) => { + if prefer_atom { + result.push(Check::If( + Box::new(Check::IsAtom), + Box::new(atom_check), + Box::new(pair_check), + )); + } else { + result.push(Check::If( + Box::new(Check::IsPair), + Box::new(pair_check), + Box::new(atom_check), + )); + } + } + } - Some(check) + construct_or(result) +} + +fn extract_shape_check(items: Vec) -> (Option, Check) { + let mut result = Vec::new(); + let mut shape = None; + + for item in items { + match item { + Check::IsAtom => shape = Some(Check::IsAtom), + Check::IsPair => shape = Some(Check::IsPair), + _ => result.push(item), + } + } + + (shape, construct_and(result)) } fn construct_or(mut items: Vec) -> Check { @@ -157,3 +146,90 @@ fn construct_or(mut items: Vec) -> Check { Check::Or(items) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simplify_or_none() { + assert_eq!(simplify_or_shallow([Check::None]), Check::None); + } + + #[test] + fn test_simplify_none_or_none() { + assert_eq!(simplify_or_shallow([Check::None, Check::None]), Check::None); + } + + #[test] + fn test_simplify_check_or_none() { + assert_eq!( + simplify_or_shallow([Check::IsAtom, Check::None]), + Check::None + ); + } + + #[test] + fn test_simplify_none_or_check() { + assert_eq!( + simplify_or_shallow([Check::None, Check::IsAtom]), + Check::None + ); + } + + #[test] + fn test_simplify_or_one_check() { + assert_eq!(simplify_or_shallow([Check::IsAtom]), Check::IsAtom); + } + + #[test] + fn test_simplify_or_two_checks() { + assert_eq!( + simplify_or_shallow([Check::IsPair, Check::IsNil]), + Check::Or(vec![Check::IsNil, Check::IsPair]) + ); + } + + #[test] + fn test_simplify_atom_or_pair() { + assert_eq!( + simplify_or_shallow([Check::IsAtom, Check::IsPair]), + Check::None + ); + } + + #[test] + fn test_simplify_pair_or_atom() { + assert_eq!( + simplify_or_shallow([Check::IsPair, Check::IsAtom]), + Check::None + ); + } + + #[test] + fn test_simplify_atom_or_atom() { + assert_eq!( + simplify_or_shallow([Check::IsAtom, Check::IsAtom]), + Check::IsAtom + ); + } + + #[test] + fn test_simplify_pair_or_pair() { + assert_eq!( + simplify_or_shallow([Check::IsPair, Check::IsPair]), + Check::IsPair + ); + } + + #[test] + fn test_simplify_compound_atom_or_pair() { + assert_eq!( + simplify_or_shallow([ + Check::And(vec![Check::IsAtom, Check::Length(32)]), + Check::IsPair + ]), + Check::Or(vec![Check::IsPair, Check::Length(32)]) + ); + } +} From 7a03c5eda77df3206f716a24fd6ebbcb17bdb6c0 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 21:49:07 -0400 Subject: [PATCH 028/100] Bug fix --- crates/rue-typing/src/check.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 9130463..ee5f0f5 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -253,6 +253,10 @@ where let always_nil = nil_count == length; let always_bytes32 = bytes32_count == length; let always_public_key = public_key_count == length; + let atom_always_nil = nil_count == atom_count; + let atom_always_bool = bool_count == atom_count; + let atom_always_bytes32 = bytes32_count == atom_count; + let atom_always_public_key = public_key_count == atom_count; Ok(match types.get(rhs) { Type::Ref(..) => unreachable!(), @@ -274,6 +278,10 @@ where Type::Nil if always_atom => Check::IsNil, Type::Bytes => Check::IsAtom, Type::Int => Check::IsAtom, + Type::Bytes32 if atom_always_bytes32 => Check::IsAtom, + Type::PublicKey if atom_always_public_key => Check::IsAtom, + Type::Bool if atom_always_bool => Check::IsAtom, + Type::Nil if atom_always_nil => Check::IsAtom, Type::Bytes32 => Check::And(vec![Check::IsAtom, Check::Length(32)]), Type::PublicKey => Check::And(vec![Check::IsAtom, Check::Length(48)]), Type::Bool => Check::And(vec![Check::IsAtom, Check::IsBool]), @@ -631,7 +639,7 @@ mod tests { fn check_list_nil() { let (mut db, types) = setup(); let list = alloc_list(&mut db, &types, types.bytes); - check_str(&mut db, list, types.nil, "(and (not (l val)) (= val ()))"); + check_str(&mut db, list, types.nil, "(not (l val))"); } #[test] From 45410d18959264b0d07416799d4f441a2f4d7816 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 21:50:44 -0400 Subject: [PATCH 029/100] Simplify --- crates/rue-typing/src/check.rs | 45 ++++++++++++++-------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index ee5f0f5..6dd5522 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -247,17 +247,6 @@ where visited.remove(&(item, rhs)); } - let always_atom = atom_count == length; - let always_pair = pairs.len() == length; - let always_bool = bool_count == length; - let always_nil = nil_count == length; - let always_bytes32 = bytes32_count == length; - let always_public_key = public_key_count == length; - let atom_always_nil = nil_count == atom_count; - let atom_always_bool = bool_count == atom_count; - let atom_always_bytes32 = bytes32_count == atom_count; - let atom_always_public_key = public_key_count == atom_count; - Ok(match types.get(rhs) { Type::Ref(..) => unreachable!(), Type::Lazy(..) => unreachable!(), @@ -266,27 +255,29 @@ where Type::Unknown => Check::None, Type::Generic => return Err(CheckError::Impossible(original_type_id, rhs)), Type::Never => return Err(CheckError::Impossible(original_type_id, rhs)), - Type::Bytes if always_atom => Check::None, - Type::Int if always_atom => Check::None, - Type::Bool if always_bool => Check::None, - Type::Nil if always_nil => Check::None, - Type::Bytes32 if always_bytes32 => Check::None, - Type::PublicKey if always_public_key => Check::None, - Type::Bytes32 if always_atom => Check::Length(32), - Type::PublicKey if always_atom => Check::Length(48), - Type::Bool if always_atom => Check::IsBool, - Type::Nil if always_atom => Check::IsNil, + Type::Bytes if atom_count == length => Check::None, + Type::Int if atom_count == length => Check::None, + Type::Bool if bool_count == length => Check::None, + Type::Nil if nil_count == length => Check::None, + Type::Bytes32 if bytes32_count == length => Check::None, + Type::PublicKey if public_key_count == length => Check::None, + Type::Bytes32 if atom_count == length => Check::Length(32), + Type::PublicKey if atom_count == length => Check::Length(48), + Type::Bool if atom_count == length => Check::IsBool, + Type::Nil if atom_count == length => Check::IsNil, Type::Bytes => Check::IsAtom, Type::Int => Check::IsAtom, - Type::Bytes32 if atom_always_bytes32 => Check::IsAtom, - Type::PublicKey if atom_always_public_key => Check::IsAtom, - Type::Bool if atom_always_bool => Check::IsAtom, - Type::Nil if atom_always_nil => Check::IsAtom, + Type::Bytes32 if bytes32_count == atom_count => Check::IsAtom, + Type::PublicKey if public_key_count == atom_count => Check::IsAtom, + Type::Bool if bool_count == atom_count => Check::IsAtom, + Type::Nil if nil_count == atom_count => Check::IsAtom, Type::Bytes32 => Check::And(vec![Check::IsAtom, Check::Length(32)]), Type::PublicKey => Check::And(vec![Check::IsAtom, Check::Length(48)]), Type::Bool => Check::And(vec![Check::IsAtom, Check::IsBool]), Type::Nil => Check::And(vec![Check::IsAtom, Check::IsNil]), - Type::Pair(..) if always_atom => return Err(CheckError::Impossible(original_type_id, rhs)), + Type::Pair(..) if atom_count == length => { + return Err(CheckError::Impossible(original_type_id, rhs)) + } Type::Pair(first, rest) => { let (first, rest) = (*first, *rest); @@ -300,7 +291,7 @@ where let pair_check = Check::Pair(Box::new(first), Box::new(rest)); - if always_pair { + if pairs.len() == length { pair_check } else { Check::And(vec![Check::IsPair, pair_check]) From 82574287d6ee1b2efef4e3dc8d89580e5166a9e0 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 21:52:19 -0400 Subject: [PATCH 030/100] Move stringify check out --- crates/rue-typing/src/check.rs | 106 +---------------- .../rue-typing/src/check/stringify_check.rs | 109 ++++++++++++++++++ 2 files changed, 113 insertions(+), 102 deletions(-) create mode 100644 crates/rue-typing/src/check/stringify_check.rs diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 6dd5522..2fd10b3 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -4,18 +4,20 @@ use std::{ hash::BuildHasher, }; -use crate::{Comparison, Type, TypeId, TypePath, TypeSystem}; +use crate::{Comparison, Type, TypeId, TypeSystem}; mod check_error; mod simplify_and; mod simplify_check; mod simplify_or; +mod stringify_check; pub use check_error::*; pub(crate) use simplify_and::*; pub(crate) use simplify_check::*; pub(crate) use simplify_or::*; +pub(crate) use stringify_check::*; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Check { @@ -33,7 +35,7 @@ pub enum Check { impl fmt::Display for Check { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt_check(self, f, &mut Vec::new()) + stringify_check(self, f, &mut Vec::new()) } } @@ -300,106 +302,6 @@ where }) } -fn fmt_val(f: &mut fmt::Formatter<'_>, path: &[TypePath]) -> fmt::Result { - for path in path.iter().rev() { - match path { - TypePath::First => write!(f, "(f ")?, - TypePath::Rest => write!(f, "(r ")?, - } - } - write!(f, "val")?; - for _ in 0..path.len() { - write!(f, ")")?; - } - Ok(()) -} - -fn fmt_check(check: &Check, f: &mut fmt::Formatter<'_>, path: &mut Vec) -> fmt::Result { - match check { - Check::None => write!(f, "1"), - Check::IsPair => { - write!(f, "(l ")?; - fmt_val(f, path)?; - write!(f, ")") - } - Check::IsAtom => { - write!(f, "(not (l ")?; - fmt_val(f, path)?; - write!(f, "))") - } - Check::IsBool => { - write!(f, "(any (= ")?; - fmt_val(f, path)?; - write!(f, " ()) (= ")?; - fmt_val(f, path)?; - write!(f, " 1))") - } - Check::IsNil => { - write!(f, "(= ")?; - fmt_val(f, path)?; - write!(f, " ())") - } - Check::Length(len) => { - write!(f, "(= (strlen ")?; - fmt_val(f, path)?; - write!(f, ") {len})") - } - Check::And(checks) => { - write!(f, "(and")?; - for check in checks { - write!(f, " ")?; - fmt_check(check, f, path)?; - } - write!(f, ")") - } - Check::Or(checks) => { - write!(f, "(or")?; - for check in checks { - write!(f, " ")?; - fmt_check(check, f, path)?; - } - write!(f, ")") - } - Check::If(cond, then, else_) => { - write!(f, "(if ")?; - fmt_check(cond, f, path)?; - write!(f, " ")?; - fmt_check(then, f, path)?; - write!(f, " ")?; - fmt_check(else_, f, path)?; - write!(f, ")") - } - Check::Pair(first, rest) => { - let has_first = first.as_ref() != &Check::None; - let has_rest = rest.as_ref() != &Check::None; - - if has_first && has_rest { - write!(f, "(all ")?; - path.push(TypePath::First); - fmt_check(first, f, path)?; - path.pop().unwrap(); - write!(f, " ")?; - path.push(TypePath::Rest); - fmt_check(rest, f, path)?; - path.pop().unwrap(); - write!(f, ")") - } else if has_first { - path.push(TypePath::First); - fmt_check(first, f, path)?; - path.pop().unwrap(); - Ok(()) - } else if has_rest { - path.push(TypePath::Rest); - fmt_check(rest, f, path)?; - path.pop().unwrap(); - Ok(()) - } else { - write!(f, "1") - } - } - } -} - #[cfg(test)] mod tests { use crate::StandardTypes; diff --git a/crates/rue-typing/src/check/stringify_check.rs b/crates/rue-typing/src/check/stringify_check.rs new file mode 100644 index 0000000..93936a5 --- /dev/null +++ b/crates/rue-typing/src/check/stringify_check.rs @@ -0,0 +1,109 @@ +use std::fmt; + +use crate::TypePath; + +use super::Check; + +fn stringify_value(f: &mut fmt::Formatter<'_>, path: &[TypePath]) -> fmt::Result { + for path in path.iter().rev() { + match path { + TypePath::First => write!(f, "(f ")?, + TypePath::Rest => write!(f, "(r ")?, + } + } + write!(f, "val")?; + for _ in 0..path.len() { + write!(f, ")")?; + } + Ok(()) +} + +pub(crate) fn stringify_check( + check: &Check, + f: &mut fmt::Formatter<'_>, + path: &mut Vec, +) -> fmt::Result { + match check { + Check::None => write!(f, "1"), + Check::IsPair => { + write!(f, "(l ")?; + stringify_value(f, path)?; + write!(f, ")") + } + Check::IsAtom => { + write!(f, "(not (l ")?; + stringify_value(f, path)?; + write!(f, "))") + } + Check::IsBool => { + write!(f, "(any (= ")?; + stringify_value(f, path)?; + write!(f, " ()) (= ")?; + stringify_value(f, path)?; + write!(f, " 1))") + } + Check::IsNil => { + write!(f, "(= ")?; + stringify_value(f, path)?; + write!(f, " ())") + } + Check::Length(len) => { + write!(f, "(= (strlen ")?; + stringify_value(f, path)?; + write!(f, ") {len})") + } + Check::And(checks) => { + write!(f, "(and")?; + for check in checks { + write!(f, " ")?; + stringify_check(check, f, path)?; + } + write!(f, ")") + } + Check::Or(checks) => { + write!(f, "(or")?; + for check in checks { + write!(f, " ")?; + stringify_check(check, f, path)?; + } + write!(f, ")") + } + Check::If(cond, then, else_) => { + write!(f, "(if ")?; + stringify_check(cond, f, path)?; + write!(f, " ")?; + stringify_check(then, f, path)?; + write!(f, " ")?; + stringify_check(else_, f, path)?; + write!(f, ")") + } + Check::Pair(first, rest) => { + let has_first = first.as_ref() != &Check::None; + let has_rest = rest.as_ref() != &Check::None; + + if has_first && has_rest { + write!(f, "(all ")?; + path.push(TypePath::First); + stringify_check(first, f, path)?; + path.pop().unwrap(); + write!(f, " ")?; + path.push(TypePath::Rest); + stringify_check(rest, f, path)?; + path.pop().unwrap(); + write!(f, ")") + } else if has_first { + path.push(TypePath::First); + stringify_check(first, f, path)?; + path.pop().unwrap(); + Ok(()) + } else if has_rest { + path.push(TypePath::Rest); + stringify_check(rest, f, path)?; + path.pop().unwrap(); + Ok(()) + } else { + write!(f, "1") + } + } + } +} From 56f253ecb243a3c7c35610c9595187ccadd8731a Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 22:09:43 -0400 Subject: [PATCH 031/100] Add comparison tests --- crates/rue-typing/src/check.rs | 36 +++--- crates/rue-typing/src/comparison.rs | 163 ++++++++++++++++++++++++ crates/rue-typing/src/difference.rs | 16 +++ crates/rue-typing/src/lib.rs | 7 + crates/rue-typing/src/standard_types.rs | 5 +- crates/rue-typing/src/stringify.rs | 6 +- crates/rue-typing/src/test_tools.rs | 14 ++ crates/rue-typing/src/ty.rs | 2 + 8 files changed, 229 insertions(+), 20 deletions(-) create mode 100644 crates/rue-typing/src/test_tools.rs diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 2fd10b3..6274a5f 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -71,6 +71,7 @@ where (Type::Never, _) => Check::None, (_, Type::Never) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Atom, Type::Atom) => Check::None, (Type::Bytes, Type::Bytes) => Check::None, (Type::Bytes32, Type::Bytes32) => Check::None, (Type::PublicKey, Type::PublicKey) => Check::None, @@ -78,12 +79,21 @@ where (Type::Bool, Type::Bool) => Check::None, (Type::Nil, Type::Nil) => Check::None, + (Type::Bytes32, Type::Atom) => Check::None, + (Type::PublicKey, Type::Atom) => Check::None, + (Type::Int, Type::Atom) => Check::None, + (Type::Bytes, Type::Atom) => Check::None, + (Type::Bool, Type::Atom) => Check::None, + (Type::Nil, Type::Atom) => Check::None, + + (Type::Atom, Type::Bytes) => Check::None, (Type::Bytes32, Type::Bytes) => Check::None, (Type::PublicKey, Type::Bytes) => Check::None, (Type::Int, Type::Bytes) => Check::None, (Type::Bool, Type::Bytes) => Check::None, (Type::Nil, Type::Bytes) => Check::None, + (Type::Atom, Type::Int) => Check::None, (Type::Bytes32, Type::Int) => Check::None, (Type::PublicKey, Type::Int) => Check::None, (Type::Bytes, Type::Int) => Check::None, @@ -92,6 +102,11 @@ where (Type::Nil, Type::Bool) => Check::None, + (Type::Atom, Type::Bool) => Check::IsBool, + (Type::Atom, Type::Nil) => Check::IsNil, + (Type::Atom, Type::PublicKey) => Check::Length(48), + (Type::Atom, Type::Bytes32) => Check::Length(32), + (Type::Bytes, Type::Bool) => Check::IsBool, (Type::Bytes, Type::Nil) => Check::IsNil, (Type::Bytes, Type::PublicKey) => Check::Length(48), @@ -128,6 +143,7 @@ where (Type::PublicKey, Type::Bool) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Bytes32, Type::Bool) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Atom, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Bytes, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Bytes32, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), (Type::PublicKey, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), @@ -135,6 +151,7 @@ where (Type::Bool, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Nil, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Pair(..), Type::Atom) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Pair(..), Type::Bytes) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Pair(..), Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Pair(..), Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), @@ -221,7 +238,7 @@ where Type::Never => { length -= 1; } - Type::Bytes | Type::Int => { + Type::Atom | Type::Bytes | Type::Int => { atom_count += 1; } Type::Bytes32 => { @@ -257,6 +274,7 @@ where Type::Unknown => Check::None, Type::Generic => return Err(CheckError::Impossible(original_type_id, rhs)), Type::Never => return Err(CheckError::Impossible(original_type_id, rhs)), + Type::Atom if atom_count == length => Check::None, Type::Bytes if atom_count == length => Check::None, Type::Int if atom_count == length => Check::None, Type::Bool if bool_count == length => Check::None, @@ -267,6 +285,7 @@ where Type::PublicKey if atom_count == length => Check::Length(48), Type::Bool if atom_count == length => Check::IsBool, Type::Nil if atom_count == length => Check::IsNil, + Type::Atom => Check::IsAtom, Type::Bytes => Check::IsAtom, Type::Int => Check::IsAtom, Type::Bytes32 if bytes32_count == atom_count => Check::IsAtom, @@ -304,16 +323,10 @@ where #[cfg(test)] mod tests { - use crate::StandardTypes; + use crate::{alloc_list, setup}; use super::*; - fn setup() -> (TypeSystem, StandardTypes) { - let mut db = TypeSystem::new(); - let types = StandardTypes::alloc(&mut db); - (db, types) - } - fn check_str(db: &mut TypeSystem, lhs: TypeId, rhs: TypeId, expected: &str) { assert_eq!(format!("{}", db.check(lhs, rhs).unwrap()), expected); } @@ -329,13 +342,6 @@ mod tests { )); } - fn alloc_list(db: &mut TypeSystem, types: &StandardTypes, item_type_id: TypeId) -> TypeId { - let list = db.alloc(Type::Unknown); - let pair = db.alloc(Type::Pair(item_type_id, list)); - *db.get_mut(list) = Type::Union(vec![pair, types.nil]); - list - } - #[test] fn check_any_bytes() { let (mut db, types) = setup(); diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index ccef885..2ad02b9 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -27,6 +27,10 @@ pub(crate) fn compare_type( rhs: TypeId, ctx: &mut ComparisonContext<'_>, ) -> Comparison { + if lhs == rhs { + return Comparison::Equal; + } + if !ctx.visited.insert((lhs, rhs)) { return Comparison::Assignable; } @@ -119,6 +123,7 @@ pub(crate) fn compare_type( } (Type::Pair(..), _) | (_, Type::Pair(..)) => Comparison::Incompatible, + (Type::Atom, Type::Atom) => Comparison::Equal, (Type::Bytes, Type::Bytes) => Comparison::Equal, (Type::Bytes32, Type::Bytes32) => Comparison::Equal, (Type::PublicKey, Type::PublicKey) => Comparison::Equal, @@ -126,6 +131,10 @@ pub(crate) fn compare_type( (Type::Bool, Type::Bool) => Comparison::Equal, (Type::Nil, Type::Nil) => Comparison::Equal, + (Type::Atom, Type::Bytes32) => Comparison::Superset, + (Type::Atom, Type::PublicKey) => Comparison::Superset, + (Type::Atom, Type::Bool) => Comparison::Superset, + (Type::Atom, Type::Nil) => Comparison::Superset, (Type::Bytes, Type::Bytes32) => Comparison::Superset, (Type::Bytes, Type::PublicKey) => Comparison::Superset, (Type::Bytes, Type::Bool) => Comparison::Superset, @@ -135,9 +144,18 @@ pub(crate) fn compare_type( (Type::Int, Type::Bool) => Comparison::Superset, (Type::Int, Type::Nil) => Comparison::Superset, + (Type::Bytes32, Type::Atom) => Comparison::Assignable, + (Type::PublicKey, Type::Atom) => Comparison::Assignable, + (Type::Bool, Type::Atom) => Comparison::Assignable, + (Type::Nil, Type::Atom) => Comparison::Assignable, + (Type::Bytes, Type::Atom) => Comparison::Assignable, + (Type::Int, Type::Atom) => Comparison::Assignable, + (Type::Bytes32, Type::Bytes) => Comparison::Assignable, (Type::Nil, Type::Bytes) => Comparison::Assignable, + (Type::Atom, Type::Int) => Comparison::Castable, + (Type::Atom, Type::Bytes) => Comparison::Castable, (Type::Bytes, Type::Int) => Comparison::Castable, (Type::Bytes32, Type::Int) => Comparison::Castable, (Type::PublicKey, Type::Bytes) => Comparison::Castable, @@ -182,3 +200,148 @@ pub(crate) fn compare_type( comparison } + +#[cfg(test)] +mod tests { + use crate::{alloc_list, setup}; + + use super::*; + + #[test] + fn test_compare_int_int() { + let (db, types) = setup(); + assert_eq!(db.compare(types.int, types.int), Comparison::Equal); + } + + #[test] + fn test_compare_int_bytes() { + let (db, types) = setup(); + assert_eq!(db.compare(types.int, types.bytes), Comparison::Castable); + } + + #[test] + fn test_compare_bytes_int() { + let (db, types) = setup(); + assert_eq!(db.compare(types.bytes, types.int), Comparison::Castable); + } + + #[test] + fn test_compare_bytes_bytes32() { + let (db, types) = setup(); + assert_eq!(db.compare(types.bytes, types.bytes32), Comparison::Superset); + } + + #[test] + fn test_compare_bytes32_bytes() { + let (db, types) = setup(); + assert_eq!( + db.compare(types.bytes32, types.bytes), + Comparison::Assignable + ); + } + + #[test] + fn test_compare_bytes_public_key() { + let (db, types) = setup(); + assert_eq!( + db.compare(types.bytes, types.public_key), + Comparison::Superset + ); + } + + #[test] + fn test_compare_public_key_bytes() { + let (db, types) = setup(); + assert_eq!( + db.compare(types.public_key, types.bytes), + Comparison::Castable + ); + } + + #[test] + fn test_compare_bytes32_public_key() { + let (db, types) = setup(); + assert_eq!( + db.compare(types.bytes32, types.public_key), + Comparison::Incompatible + ); + } + + #[test] + fn test_compare_public_key_bytes32() { + let (db, types) = setup(); + assert_eq!( + db.compare(types.public_key, types.bytes32), + Comparison::Incompatible + ); + } + + #[test] + fn test_compare_bytes_any() { + let (db, types) = setup(); + assert_eq!(db.compare(types.bytes, types.any), Comparison::Assignable); + } + + #[test] + fn test_compare_any_bytes() { + let (db, types) = setup(); + assert_eq!(db.compare(types.any, types.bytes), Comparison::Superset); + } + + #[test] + fn test_compare_bytes32_any() { + let (db, types) = setup(); + assert_eq!(db.compare(types.bytes32, types.any), Comparison::Assignable); + } + + #[test] + fn test_compare_any_bytes32() { + let (db, types) = setup(); + assert_eq!(db.compare(types.any, types.bytes32), Comparison::Superset); + } + + #[test] + fn test_compare_list_any() { + let (mut db, types) = setup(); + let list = alloc_list(&mut db, &types, types.int); + assert_eq!(db.compare(list, types.any), Comparison::Assignable); + } + + #[test] + fn test_compare_pair_any() { + let (mut db, types) = setup(); + let pair = db.alloc(Type::Pair(types.int, types.public_key)); + assert_eq!(db.compare(pair, types.any), Comparison::Assignable); + } + + #[test] + fn test_compare_int_any() { + let (db, types) = setup(); + assert_eq!(db.compare(types.int, types.any), Comparison::Assignable); + } + + #[test] + fn test_compare_public_key_any() { + let (db, types) = setup(); + assert_eq!( + db.compare(types.public_key, types.any), + Comparison::Assignable + ); + } + + #[test] + fn test_compare_complex_any() { + let (mut db, types) = setup(); + let pair_inner_inner = db.alloc(Type::Pair(types.any, types.nil)); + let pair_inner = db.alloc(Type::Pair(pair_inner_inner, pair_inner_inner)); + let pair = db.alloc(Type::Pair(types.int, pair_inner)); + let list = alloc_list(&mut db, &types, pair); + assert_eq!(db.compare(list, types.any), Comparison::Assignable); + } + + #[test] + fn test_compare_any_any() { + let (db, types) = setup(); + assert_eq!(db.compare(types.any, types.any), Comparison::Equal); + } +} diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index ccc9b4e..ce0006c 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -22,6 +22,7 @@ pub(crate) fn difference_type( (Type::Never, _) => std.never, (_, Type::Never) => lhs, + (Type::Atom, Type::Atom) => std.never, (Type::Bytes, Type::Bytes) => std.never, (Type::Bytes32, Type::Bytes32) => std.never, (Type::PublicKey, Type::PublicKey) => std.never, @@ -32,39 +33,53 @@ pub(crate) fn difference_type( (Type::Int, Type::Bytes32) => lhs, (Type::Int, Type::PublicKey) => lhs, (Type::Int, Type::Bytes) => std.never, + (Type::Int, Type::Atom) => std.never, (Type::Int, Type::Bool) => lhs, (Type::Int, Type::Nil) => lhs, (Type::Bytes, Type::Bytes32) => lhs, (Type::Bytes, Type::PublicKey) => lhs, (Type::Bytes, Type::Int) => std.never, + (Type::Bytes, Type::Atom) => std.never, (Type::Bytes, Type::Bool) => lhs, (Type::Bytes, Type::Nil) => lhs, + (Type::Atom, Type::Bytes32) => lhs, + (Type::Atom, Type::PublicKey) => lhs, + (Type::Atom, Type::Int) => std.never, + (Type::Atom, Type::Bytes) => std.never, + (Type::Atom, Type::Bool) => lhs, + (Type::Atom, Type::Nil) => lhs, + (Type::Bytes32, Type::PublicKey) => lhs, (Type::Bytes32, Type::Bytes) => std.never, (Type::Bytes32, Type::Int) => std.never, + (Type::Bytes32, Type::Atom) => std.never, (Type::Bytes32, Type::Bool) => lhs, (Type::Bytes32, Type::Nil) => lhs, (Type::PublicKey, Type::Bytes32) => lhs, (Type::PublicKey, Type::Bytes) => std.never, (Type::PublicKey, Type::Int) => std.never, + (Type::PublicKey, Type::Atom) => std.never, (Type::PublicKey, Type::Bool) => lhs, (Type::PublicKey, Type::Nil) => lhs, (Type::Bool, Type::Bytes32) => lhs, (Type::Bool, Type::PublicKey) => lhs, (Type::Bool, Type::Bytes) => std.never, + (Type::Bool, Type::Atom) => std.never, (Type::Bool, Type::Int) => std.never, (Type::Bool, Type::Nil) => lhs, (Type::Nil, Type::Bytes32) => lhs, (Type::Nil, Type::PublicKey) => lhs, (Type::Nil, Type::Bytes) => std.never, + (Type::Nil, Type::Atom) => std.never, (Type::Nil, Type::Int) => std.never, (Type::Nil, Type::Bool) => std.never, + (Type::Atom, Type::Pair(..)) => lhs, (Type::Bytes, Type::Pair(..)) => lhs, (Type::Bytes32, Type::Pair(..)) => lhs, (Type::PublicKey, Type::Pair(..)) => lhs, @@ -72,6 +87,7 @@ pub(crate) fn difference_type( (Type::Bool, Type::Pair(..)) => lhs, (Type::Nil, Type::Pair(..)) => lhs, + (Type::Pair(..), Type::Atom) => lhs, (Type::Pair(..), Type::Bytes) => lhs, (Type::Pair(..), Type::Bytes32) => lhs, (Type::Pair(..), Type::PublicKey) => lhs, diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index add1c13..0642641 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -4,6 +4,7 @@ mod difference; mod semantic_types; mod standard_types; mod stringify; + mod ty; mod type_path; mod type_system; @@ -18,3 +19,9 @@ pub use type_system::*; pub(crate) use difference::difference_type; pub(crate) use stringify::stringify_type; + +#[cfg(test)] +mod test_tools; + +#[cfg(test)] +pub(crate) use test_tools::*; diff --git a/crates/rue-typing/src/standard_types.rs b/crates/rue-typing/src/standard_types.rs index b8cf605..d292c4c 100644 --- a/crates/rue-typing/src/standard_types.rs +++ b/crates/rue-typing/src/standard_types.rs @@ -5,6 +5,7 @@ pub struct StandardTypes { pub unknown: TypeId, pub never: TypeId, pub any: TypeId, + pub atom: TypeId, pub bytes: TypeId, pub bytes32: TypeId, pub public_key: TypeId, @@ -17,6 +18,7 @@ impl StandardTypes { pub fn alloc(type_system: &mut TypeSystem) -> Self { let unknown = type_system.alloc(Type::Unknown); let never = type_system.alloc(Type::Never); + let atom = type_system.alloc(Type::Atom); let bytes = type_system.alloc(Type::Bytes); let bytes32 = type_system.alloc(Type::Bytes32); let public_key = type_system.alloc(Type::PublicKey); @@ -26,12 +28,13 @@ impl StandardTypes { let any = type_system.alloc(Type::Unknown); let pair = type_system.alloc(Type::Pair(any, any)); - *type_system.get_mut(any) = Type::Union(vec![bytes, pair]); + *type_system.get_mut(any) = Type::Union(vec![atom, pair]); Self { unknown, never, any, + atom, bytes, bytes32, public_key, diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs index 5f4ac74..ffd7404 100644 --- a/crates/rue-typing/src/stringify.rs +++ b/crates/rue-typing/src/stringify.rs @@ -21,6 +21,7 @@ pub(crate) fn stringify_type( Type::Unknown => "{unknown}".to_string(), Type::Generic => "{generic}".to_string(), Type::Never => "Never".to_string(), + Type::Atom => "Atom".to_string(), Type::Bytes => "Bytes".to_string(), Type::Bytes32 => "Bytes32".to_string(), Type::PublicKey => "PublicKey".to_string(), @@ -72,10 +73,7 @@ mod tests { assert_eq!(db.stringify(types.int), "Int"); assert_eq!(db.stringify(types.bool), "Bool"); assert_eq!(db.stringify(types.nil), "Nil"); - assert_eq!( - db.stringify(types.any), - "Bytes | ({recursive}, {recursive})" - ); + assert_eq!(db.stringify(types.any), "Atom | ({recursive}, {recursive})"); } #[test] diff --git a/crates/rue-typing/src/test_tools.rs b/crates/rue-typing/src/test_tools.rs new file mode 100644 index 0000000..512c50e --- /dev/null +++ b/crates/rue-typing/src/test_tools.rs @@ -0,0 +1,14 @@ +use crate::{StandardTypes, Type, TypeId, TypeSystem}; + +pub fn setup() -> (TypeSystem, StandardTypes) { + let mut db = TypeSystem::new(); + let types = StandardTypes::alloc(&mut db); + (db, types) +} + +pub fn alloc_list(db: &mut TypeSystem, types: &StandardTypes, item_type_id: TypeId) -> TypeId { + let list = db.alloc(Type::Unknown); + let pair = db.alloc(Type::Pair(item_type_id, list)); + *db.get_mut(list) = Type::Union(vec![pair, types.nil]); + list +} diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs index 33bbb4c..74d630f 100644 --- a/crates/rue-typing/src/ty.rs +++ b/crates/rue-typing/src/ty.rs @@ -7,6 +7,7 @@ pub enum Type { Unknown, Generic, Never, + Atom, Bytes, Bytes32, PublicKey, @@ -59,6 +60,7 @@ pub(crate) fn substitute_type( Type::Unknown => type_id, Type::Generic => type_id, Type::Never => type_id, + Type::Atom => type_id, Type::Bytes => type_id, Type::Bytes32 => type_id, Type::PublicKey => type_id, From 157a82ba64a3e5fb339990ce22a2dcb35815a4d1 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 22:11:19 -0400 Subject: [PATCH 032/100] Add pair tests --- crates/rue-typing/src/comparison.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index 2ad02b9..e0ab011 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -344,4 +344,28 @@ mod tests { let (db, types) = setup(); assert_eq!(db.compare(types.any, types.any), Comparison::Equal); } + + #[test] + fn test_compare_incompatible_pair() { + let (mut db, types) = setup(); + let lhs = db.alloc(Type::Pair(types.int, types.public_key)); + let rhs = db.alloc(Type::Pair(types.bytes, types.nil)); + assert_eq!(db.compare(lhs, rhs), Comparison::Incompatible); + } + + #[test] + fn test_compare_castable_pair() { + let (mut db, types) = setup(); + let lhs = db.alloc(Type::Pair(types.int, types.public_key)); + let rhs = db.alloc(Type::Pair(types.bytes, types.bytes)); + assert_eq!(db.compare(lhs, rhs), Comparison::Castable); + } + + #[test] + fn test_compare_assignable_pair() { + let (mut db, types) = setup(); + let lhs = db.alloc(Type::Pair(types.int, types.public_key)); + let rhs = db.alloc(Type::Pair(types.atom, types.atom)); + assert_eq!(db.compare(lhs, rhs), Comparison::Assignable); + } } From e7e434e884f70eb7bf3659b47aed55fae42114e8 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 22:15:13 -0400 Subject: [PATCH 033/100] Generic inference test --- crates/rue-typing/src/comparison.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index e0ab011..4da6aa1 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -368,4 +368,32 @@ mod tests { let rhs = db.alloc(Type::Pair(types.atom, types.atom)); assert_eq!(db.compare(lhs, rhs), Comparison::Assignable); } + + #[test] + fn test_generic_inference() { + let (mut db, types) = setup(); + + let generic = db.alloc(Type::Generic); + + let mut stack = vec![HashMap::new()]; + + assert_eq!( + db.compare_with_generics(types.int, generic, &mut stack, true), + Comparison::Assignable + ); + + assert_eq!(stack.len(), 1); + assert_eq!(stack[0].get(&generic), Some(&types.int)); + + for infer in [true, false] { + assert_eq!( + db.compare_with_generics(types.bytes, generic, &mut stack, infer), + Comparison::Castable + ); + assert_eq!( + db.compare_with_generics(types.any, generic, &mut stack, infer), + Comparison::Superset + ); + } + } } From 4d042d840d76cd64eca6933215a566c09810be02 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 22:17:04 -0400 Subject: [PATCH 034/100] Refactor other functions --- crates/rue-typing/src/lib.rs | 4 + crates/rue-typing/src/replace_type.rs | 20 ++++ crates/rue-typing/src/substitute_type.rs | 104 +++++++++++++++++++ crates/rue-typing/src/ty.rs | 124 +---------------------- 4 files changed, 129 insertions(+), 123 deletions(-) create mode 100644 crates/rue-typing/src/replace_type.rs create mode 100644 crates/rue-typing/src/substitute_type.rs diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index 0642641..8633460 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -1,9 +1,11 @@ mod check; mod comparison; mod difference; +mod replace_type; mod semantic_types; mod standard_types; mod stringify; +mod substitute_type; mod ty; mod type_path; @@ -18,7 +20,9 @@ pub use type_path::*; pub use type_system::*; pub(crate) use difference::difference_type; +pub(crate) use replace_type::replace_type; pub(crate) use stringify::stringify_type; +pub(crate) use substitute_type::substitute_type; #[cfg(test)] mod test_tools; diff --git a/crates/rue-typing/src/replace_type.rs b/crates/rue-typing/src/replace_type.rs new file mode 100644 index 0000000..806a519 --- /dev/null +++ b/crates/rue-typing/src/replace_type.rs @@ -0,0 +1,20 @@ +use crate::{Type, TypeId, TypePath, TypeSystem}; + +pub(crate) fn replace_type( + types: &mut TypeSystem, + type_id: TypeId, + replace_type_id: TypeId, + path: &[TypePath], +) -> TypeId { + if path.is_empty() { + return replace_type_id; + } + + match types.get(type_id) { + Type::Pair(first, rest) => match path[0] { + TypePath::First => replace_type(types, *first, replace_type_id, &path[1..]), + TypePath::Rest => replace_type(types, *rest, replace_type_id, &path[1..]), + }, + _ => type_id, + } +} diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs new file mode 100644 index 0000000..e11c3db --- /dev/null +++ b/crates/rue-typing/src/substitute_type.rs @@ -0,0 +1,104 @@ +use std::collections::{HashMap, HashSet}; + +use crate::{Alias, Type, TypeId, TypeSystem}; + +pub(crate) fn substitute_type( + types: &mut TypeSystem, + type_id: TypeId, + substitutions: &mut HashMap, + preserve_semantics: bool, + visited: &mut HashSet, +) -> TypeId { + if let Some(new_type_id) = substitutions.get(&type_id) { + return *new_type_id; + } + + if !visited.insert(type_id) { + return type_id; + } + + let result = match types.get(type_id) { + Type::Ref(..) => unreachable!(), + Type::Unknown => type_id, + Type::Generic => type_id, + Type::Never => type_id, + Type::Atom => type_id, + Type::Bytes => type_id, + Type::Bytes32 => type_id, + Type::PublicKey => type_id, + Type::Int => type_id, + Type::Bool => type_id, + Type::Nil => type_id, + Type::Pair(first, rest) => { + let (first, rest) = (*first, *rest); + + let new_first = + substitute_type(types, first, substitutions, preserve_semantics, visited); + let new_rest = substitute_type(types, rest, substitutions, preserve_semantics, visited); + + if new_first == first && new_rest == rest { + type_id + } else { + types.alloc(Type::Pair(new_first, new_rest)) + } + } + Type::Union(items) => { + let items = items.clone(); + let mut result = Vec::new(); + + for item in &items { + result.push(substitute_type( + types, + *item, + substitutions, + preserve_semantics, + visited, + )); + } + + if result == items { + type_id + } else { + types.alloc(Type::Union(result)) + } + } + Type::Lazy(lazy) => { + substitutions.extend(lazy.substitutions.clone()); + substitute_type( + types, + lazy.type_id, + substitutions, + preserve_semantics, + visited, + ) + } + Type::Alias(alias) => { + let alias = alias.clone(); + + let new_type_id = substitute_type( + types, + alias.type_id, + substitutions, + preserve_semantics, + visited, + ); + + if preserve_semantics { + if new_type_id == alias.type_id { + type_id + } else { + types.alloc(Type::Alias(Alias { + type_id: new_type_id, + generic_types: alias.generic_types, + })) + } + } else { + new_type_id + } + } + }; + + visited.remove(&type_id); + + result +} diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs index 74d630f..03fba06 100644 --- a/crates/rue-typing/src/ty.rs +++ b/crates/rue-typing/src/ty.rs @@ -1,6 +1,4 @@ -use std::collections::{HashMap, HashSet}; - -use crate::{Alias, Lazy, TypeId, TypePath, TypeSystem}; +use crate::{Alias, Lazy, TypeId}; #[derive(Debug, Clone)] pub enum Type { @@ -20,123 +18,3 @@ pub enum Type { Lazy(Lazy), Alias(Alias), } - -pub(crate) fn replace_type( - types: &mut TypeSystem, - type_id: TypeId, - replace_type_id: TypeId, - path: &[TypePath], -) -> TypeId { - if path.is_empty() { - return replace_type_id; - } - - match types.get(type_id) { - Type::Pair(first, rest) => match path[0] { - TypePath::First => replace_type(types, *first, replace_type_id, &path[1..]), - TypePath::Rest => replace_type(types, *rest, replace_type_id, &path[1..]), - }, - _ => type_id, - } -} - -pub(crate) fn substitute_type( - types: &mut TypeSystem, - type_id: TypeId, - substitutions: &mut HashMap, - preserve_semantics: bool, - visited: &mut HashSet, -) -> TypeId { - if let Some(new_type_id) = substitutions.get(&type_id) { - return *new_type_id; - } - - if !visited.insert(type_id) { - return type_id; - } - - let result = match types.get(type_id) { - Type::Ref(..) => unreachable!(), - Type::Unknown => type_id, - Type::Generic => type_id, - Type::Never => type_id, - Type::Atom => type_id, - Type::Bytes => type_id, - Type::Bytes32 => type_id, - Type::PublicKey => type_id, - Type::Int => type_id, - Type::Bool => type_id, - Type::Nil => type_id, - Type::Pair(first, rest) => { - let (first, rest) = (*first, *rest); - - let new_first = - substitute_type(types, first, substitutions, preserve_semantics, visited); - let new_rest = substitute_type(types, rest, substitutions, preserve_semantics, visited); - - if new_first == first && new_rest == rest { - type_id - } else { - types.alloc(Type::Pair(new_first, new_rest)) - } - } - Type::Union(items) => { - let items = items.clone(); - let mut result = Vec::new(); - - for item in &items { - result.push(substitute_type( - types, - *item, - substitutions, - preserve_semantics, - visited, - )); - } - - if result == items { - type_id - } else { - types.alloc(Type::Union(result)) - } - } - Type::Lazy(lazy) => { - substitutions.extend(lazy.substitutions.clone()); - substitute_type( - types, - lazy.type_id, - substitutions, - preserve_semantics, - visited, - ) - } - Type::Alias(alias) => { - let alias = alias.clone(); - - let new_type_id = substitute_type( - types, - alias.type_id, - substitutions, - preserve_semantics, - visited, - ); - - if preserve_semantics { - if new_type_id == alias.type_id { - type_id - } else { - types.alloc(Type::Alias(Alias { - type_id: new_type_id, - generic_types: alias.generic_types, - })) - } - } else { - new_type_id - } - } - }; - - visited.remove(&type_id); - - result -} From f9ec679bfaf18530c12480b0fbd0c8c7f043079e Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 22:17:46 -0400 Subject: [PATCH 035/100] Remove main.sym --- main.sym | 1 - 1 file changed, 1 deletion(-) delete mode 100644 main.sym diff --git a/main.sym b/main.sym deleted file mode 100644 index 521825a..0000000 --- a/main.sym +++ /dev/null @@ -1 +0,0 @@ -{"678511f57ffc5dcff6aed60184835c789556756f551ff7960ea272208db78842":"and_","2dfe28ca44551d031d5eb2a43fa9e7fa2d0d6c82d1c3355daaccf50e0309e3e0":"or_","6a01676bfb72acb4c2fdfbf3e7b177a6679619f79c8d4f951729b2ac4bc62e85":"and","0373418bd7bf23dbc1e74d65f7a9ddfadfd16bb253bab741a5c880e30cde4566":"or"} \ No newline at end of file From 2336abecece72b8872751a5621c29f1a2e0e48c8 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 23:08:26 -0400 Subject: [PATCH 036/100] Split f and r --- crates/rue-typing/src/check.rs | 21 +++++++--- crates/rue-typing/src/check/simplify_check.rs | 14 +++++-- .../rue-typing/src/check/stringify_check.rs | 38 ++++++------------- 3 files changed, 37 insertions(+), 36 deletions(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 6274a5f..0d9e17f 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -30,7 +30,8 @@ pub enum Check { And(Vec), Or(Vec), If(Box, Box, Box), - Pair(Box, Box), + First(Box), + Rest(Box), } impl fmt::Display for Check { @@ -164,7 +165,10 @@ where let (rhs_first, rhs_rest) = (*rhs_first, *rhs_rest); let first = check_type(types, lhs_first, rhs_first, visited)?; let rest = check_type(types, lhs_rest, rhs_rest, visited)?; - Check::Pair(Box::new(first), Box::new(rest)) + Check::And(vec![ + Check::First(Box::new(first)), + Check::Rest(Box::new(rest)), + ]) } }; @@ -310,12 +314,17 @@ where let rest = check_union_against_rhs(types, original_type_id, &rest_items, rest, visited)?; - let pair_check = Check::Pair(Box::new(first), Box::new(rest)); - if pairs.len() == length { - pair_check + Check::And(vec![ + Check::First(Box::new(first)), + Check::Rest(Box::new(rest)), + ]) } else { - Check::And(vec![Check::IsPair, pair_check]) + Check::And(vec![ + Check::IsPair, + Check::First(Box::new(first)), + Check::Rest(Box::new(rest)), + ]) } } }) diff --git a/crates/rue-typing/src/check/simplify_check.rs b/crates/rue-typing/src/check/simplify_check.rs index 4f9e314..1f92583 100644 --- a/crates/rue-typing/src/check/simplify_check.rs +++ b/crates/rue-typing/src/check/simplify_check.rs @@ -22,14 +22,22 @@ pub(crate) fn simplify_check(check: Check) -> Check { let else_ = simplify_check(*else_); Check::If(Box::new(cond), Box::new(then), Box::new(else_)) } - Check::Pair(first, rest) => { + Check::First(first) => { let first = simplify_check(*first); + + if first == Check::None { + Check::None + } else { + Check::First(Box::new(first)) + } + } + Check::Rest(rest) => { let rest = simplify_check(*rest); - if first == Check::None && rest == Check::None { + if rest == Check::None { Check::None } else { - Check::Pair(Box::new(first), Box::new(rest)) + Check::Rest(Box::new(rest)) } } } diff --git a/crates/rue-typing/src/check/stringify_check.rs b/crates/rue-typing/src/check/stringify_check.rs index 93936a5..963957e 100644 --- a/crates/rue-typing/src/check/stringify_check.rs +++ b/crates/rue-typing/src/check/stringify_check.rs @@ -77,33 +77,17 @@ pub(crate) fn stringify_check( stringify_check(else_, f, path)?; write!(f, ")") } - Check::Pair(first, rest) => { - let has_first = first.as_ref() != &Check::None; - let has_rest = rest.as_ref() != &Check::None; - - if has_first && has_rest { - write!(f, "(all ")?; - path.push(TypePath::First); - stringify_check(first, f, path)?; - path.pop().unwrap(); - write!(f, " ")?; - path.push(TypePath::Rest); - stringify_check(rest, f, path)?; - path.pop().unwrap(); - write!(f, ")") - } else if has_first { - path.push(TypePath::First); - stringify_check(first, f, path)?; - path.pop().unwrap(); - Ok(()) - } else if has_rest { - path.push(TypePath::Rest); - stringify_check(rest, f, path)?; - path.pop().unwrap(); - Ok(()) - } else { - write!(f, "1") - } + Check::First(first) => { + path.push(TypePath::First); + stringify_check(first, f, path)?; + path.pop().unwrap(); + Ok(()) + } + Check::Rest(rest) => { + path.push(TypePath::Rest); + stringify_check(rest, f, path)?; + path.pop().unwrap(); + Ok(()) } } } From 665852f69c6eb1fb470a8be9fbf6ca310c954cec Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 23:19:37 -0400 Subject: [PATCH 037/100] Check any point --- crates/rue-typing/src/check.rs | 8 +++ crates/rue-typing/src/check/simplify_check.rs | 50 ++++++++++++------- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 0d9e17f..048638d 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -564,4 +564,12 @@ mod tests { let list = alloc_list(&mut db, &types, types.bytes); check_recursive(&mut db, types.any, list); } + + #[test] + fn check_any_point() { + let (mut db, types) = setup(); + let point_end = db.alloc(Type::Pair(types.int, types.nil)); + let point = db.alloc(Type::Pair(types.int, point_end)); + check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) ()))"); + } } diff --git a/crates/rue-typing/src/check/simplify_check.rs b/crates/rue-typing/src/check/simplify_check.rs index 1f92583..5d2d24b 100644 --- a/crates/rue-typing/src/check/simplify_check.rs +++ b/crates/rue-typing/src/check/simplify_check.rs @@ -22,23 +22,37 @@ pub(crate) fn simplify_check(check: Check) -> Check { let else_ = simplify_check(*else_); Check::If(Box::new(cond), Box::new(then), Box::new(else_)) } - Check::First(first) => { - let first = simplify_check(*first); - - if first == Check::None { - Check::None - } else { - Check::First(Box::new(first)) - } - } - Check::Rest(rest) => { - let rest = simplify_check(*rest); - - if rest == Check::None { - Check::None - } else { - Check::Rest(Box::new(rest)) - } - } + Check::First(first) => match simplify_check(*first) { + Check::None => Check::None, + Check::And(items) => Check::And( + items + .into_iter() + .map(|item| Check::First(Box::new(item))) + .collect(), + ), + Check::Or(items) => Check::Or( + items + .into_iter() + .map(|item| Check::First(Box::new(item))) + .collect(), + ), + first => Check::First(Box::new(first)), + }, + Check::Rest(rest) => match simplify_check(*rest) { + Check::None => Check::None, + Check::And(items) => Check::And( + items + .into_iter() + .map(|item| Check::Rest(Box::new(item))) + .collect(), + ), + Check::Or(items) => Check::Or( + items + .into_iter() + .map(|item| Check::Rest(Box::new(item))) + .collect(), + ), + rest => Check::Rest(Box::new(rest)), + }, } } From ce3cca319d407dfe1b30e2f1494ad88cc56e7253 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 22 Jul 2024 23:40:44 -0400 Subject: [PATCH 038/100] Structs --- Cargo.lock | 1 + crates/rue-typing/Cargo.toml | 1 + crates/rue-typing/src/check.rs | 32 +++++++++++--- crates/rue-typing/src/comparison.rs | 9 ++++ crates/rue-typing/src/difference.rs | 27 +++++++++++- crates/rue-typing/src/semantic_types.rs | 13 ++++++ crates/rue-typing/src/stringify.rs | 3 +- crates/rue-typing/src/substitute_type.rs | 28 +++++++++++++ crates/rue-typing/src/test_tools.rs | 53 +++++++++++++++++++++++- crates/rue-typing/src/ty.rs | 3 +- 10 files changed, 161 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dab46a3..e99dada 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1461,6 +1461,7 @@ version = "0.1.1" dependencies = [ "anyhow", "id-arena", + "indexmap", "thiserror", ] diff --git a/crates/rue-typing/Cargo.toml b/crates/rue-typing/Cargo.toml index 74b2a97..237ae0e 100644 --- a/crates/rue-typing/Cargo.toml +++ b/crates/rue-typing/Cargo.toml @@ -17,6 +17,7 @@ workspace = true [dependencies] id-arena = { workspace = true } thiserror = { workspace = true } +indexmap = { workspace = true } [dev-dependencies] anyhow = { workspace = true } diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 048638d..9ab1db0 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -61,6 +61,7 @@ where (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), (Type::Lazy(..), _) | (_, Type::Lazy(..)) => unreachable!(), (Type::Alias(..), _) | (_, Type::Alias(..)) => unreachable!(), + (Type::Struct(..), _) | (_, Type::Struct(..)) => unreachable!(), // TODO: Implement generic type checking? (Type::Generic, _) => Check::None, @@ -233,10 +234,7 @@ where Type::Ref(..) => unreachable!(), Type::Lazy(..) => unreachable!(), Type::Alias(..) => unreachable!(), - - Type::Union(child_items) => { - items.extend(child_items); - } + Type::Struct(..) => unreachable!(), Type::Generic => return Err(CheckError::Impossible(item, rhs)), Type::Unknown => {} Type::Never => { @@ -265,6 +263,9 @@ where Type::Pair(first, rest) => { pairs.push((*first, *rest)); } + Type::Union(child_items) => { + items.extend(child_items); + } } visited.remove(&(item, rhs)); @@ -275,6 +276,7 @@ where Type::Lazy(..) => unreachable!(), Type::Alias(..) => unreachable!(), Type::Union(..) => unreachable!(), + Type::Struct(..) => unreachable!(), Type::Unknown => Check::None, Type::Generic => return Err(CheckError::Impossible(original_type_id, rhs)), Type::Never => return Err(CheckError::Impossible(original_type_id, rhs)), @@ -332,7 +334,11 @@ where #[cfg(test)] mod tests { - use crate::{alloc_list, setup}; + use std::collections::HashMap; + + use indexmap::indexmap; + + use crate::{alloc_list, alloc_struct, setup}; use super::*; @@ -572,4 +578,20 @@ mod tests { let point = db.alloc(Type::Pair(types.int, point_end)); check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) ()))"); } + + #[test] + fn check_any_point_struct() { + let (mut db, types) = setup(); + let point_struct = alloc_struct( + &mut db, + &types, + &indexmap! { + "x".to_string() => types.int, + "y".to_string() => types.int, + }, + true, + ); + let point = db.structure(point_struct, HashMap::new()); + check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) ()))"); + } } diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index 4da6aa1..c7cdca1 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -194,6 +194,15 @@ pub(crate) fn compare_type( (Type::Alias(alias), _) => compare_type(types, alias.type_id, rhs, ctx), (_, Type::Alias(alias)) => compare_type(types, lhs, alias.type_id, ctx), + + (Type::Struct(lhs), _) => max( + compare_type(types, lhs.type_id, rhs, ctx), + Comparison::Castable, + ), + (_, Type::Struct(rhs)) => max( + compare_type(types, lhs, rhs.type_id, ctx), + Comparison::Castable, + ), }; ctx.visited.remove(&(lhs, rhs)); diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index ce0006c..85e7ddb 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -1,6 +1,6 @@ use std::collections::HashSet; -use crate::{StandardTypes, Type, TypeId, TypeSystem}; +use crate::{StandardTypes, Struct, Type, TypeId, TypeSystem}; pub(crate) fn difference_type( types: &mut TypeSystem, @@ -146,6 +146,31 @@ pub(crate) fn difference_type( (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::Struct(ty), _) => { + let ty = ty.clone(); + let type_id = difference_type(types, std, ty.type_id, rhs, visited); + + types.alloc(Type::Struct(Struct { + original_type_id: Some(ty.original_type_id.unwrap_or(lhs)), + type_id, + field_names: ty.field_names, + nil_terminated: ty.nil_terminated, + generic_types: ty.generic_types, + })) + } + (_, Type::Struct(ty)) => { + let ty = ty.clone(); + let type_id = difference_type(types, std, lhs, ty.type_id, visited); + + types.alloc(Type::Struct(Struct { + original_type_id: Some(ty.original_type_id.unwrap_or(rhs)), + type_id, + field_names: ty.field_names, + nil_terminated: ty.nil_terminated, + generic_types: ty.generic_types, + })) + } }; visited.remove(&(lhs, rhs)); diff --git a/crates/rue-typing/src/semantic_types.rs b/crates/rue-typing/src/semantic_types.rs index 6b0da44..2d04c43 100644 --- a/crates/rue-typing/src/semantic_types.rs +++ b/crates/rue-typing/src/semantic_types.rs @@ -1,5 +1,7 @@ use std::collections::HashMap; +use indexmap::IndexSet; + use crate::TypeId; /// Allows you to map generic types lazily without having to resolve them immediately. @@ -14,6 +16,17 @@ pub struct Lazy { /// Represents an alias to a type with a set of generic parameters that must be mapped prior to use. #[derive(Debug, Clone)] pub struct Alias { + pub original_type_id: Option, + pub type_id: TypeId, + pub generic_types: Vec, +} + +/// Struct types are just wrappers around a structural type that provide field information. +#[derive(Debug, Clone)] +pub struct Struct { + pub original_type_id: Option, pub type_id: TypeId, + pub field_names: IndexSet, + pub nil_terminated: bool, pub generic_types: Vec, } diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs index ffd7404..4bd3e0b 100644 --- a/crates/rue-typing/src/stringify.rs +++ b/crates/rue-typing/src/stringify.rs @@ -1,6 +1,6 @@ use std::collections::{HashMap, HashSet}; -use crate::{Type, TypeId, TypeSystem}; +use crate::{Struct, Type, TypeId, TypeSystem}; pub(crate) fn stringify_type( types: &TypeSystem, @@ -47,6 +47,7 @@ pub(crate) fn stringify_type( } Type::Lazy(lazy) => stringify_type(types, lazy.type_id, names, visited), Type::Alias(alias) => stringify_type(types, alias.type_id, names, visited), + Type::Struct(Struct { type_id, .. }) => stringify_type(types, *type_id, names, visited), }; visited.remove(&type_id); diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index e11c3db..0842b1e 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -88,6 +88,7 @@ pub(crate) fn substitute_type( type_id } else { types.alloc(Type::Alias(Alias { + original_type_id: Some(alias.original_type_id.unwrap_or(type_id)), type_id: new_type_id, generic_types: alias.generic_types, })) @@ -96,6 +97,33 @@ pub(crate) fn substitute_type( new_type_id } } + Type::Struct(ty) => { + let ty = ty.clone(); + + let new_type_id = substitute_type( + types, + ty.type_id, + substitutions, + preserve_semantics, + visited, + ); + + if preserve_semantics { + if new_type_id == ty.type_id { + type_id + } else { + types.alloc(Type::Struct(crate::Struct { + original_type_id: Some(ty.original_type_id.unwrap_or(type_id)), + type_id: new_type_id, + field_names: ty.field_names, + nil_terminated: ty.nil_terminated, + generic_types: ty.generic_types, + })) + } + } else { + new_type_id + } + } }; visited.remove(&type_id); diff --git a/crates/rue-typing/src/test_tools.rs b/crates/rue-typing/src/test_tools.rs index 512c50e..3520c60 100644 --- a/crates/rue-typing/src/test_tools.rs +++ b/crates/rue-typing/src/test_tools.rs @@ -1,4 +1,6 @@ -use crate::{StandardTypes, Type, TypeId, TypeSystem}; +use indexmap::IndexMap; + +use crate::{StandardTypes, Struct, Type, TypeId, TypeSystem}; pub fn setup() -> (TypeSystem, StandardTypes) { let mut db = TypeSystem::new(); @@ -12,3 +14,52 @@ pub fn alloc_list(db: &mut TypeSystem, types: &StandardTypes, item_type_id: Type *db.get_mut(list) = Type::Union(vec![pair, types.nil]); list } + +pub fn alloc_struct( + db: &mut TypeSystem, + types: &StandardTypes, + fields: &IndexMap, + nil_terminated: bool, +) -> TypeId { + let structure = if nil_terminated { + alloc_list_of(db, types, fields.values().copied()) + } else { + alloc_tuple_of(db, types, fields.values().copied()) + }; + + db.alloc(Type::Struct(Struct { + original_type_id: None, + type_id: structure, + field_names: fields.keys().cloned().collect(), + nil_terminated, + generic_types: Vec::new(), + })) +} + +pub fn alloc_list_of( + db: &mut TypeSystem, + types: &StandardTypes, + items: impl DoubleEndedIterator, +) -> TypeId { + let mut tuple = types.nil; + for item in items.rev() { + tuple = db.alloc(Type::Pair(item, tuple)); + } + tuple +} + +pub fn alloc_tuple_of( + db: &mut TypeSystem, + types: &StandardTypes, + items: impl DoubleEndedIterator, +) -> TypeId { + let mut tuple = types.nil; + for (i, item) in items.rev().enumerate() { + if i == 0 { + tuple = item; + continue; + } + tuple = db.alloc(Type::Pair(item, tuple)); + } + tuple +} diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs index 03fba06..dc68748 100644 --- a/crates/rue-typing/src/ty.rs +++ b/crates/rue-typing/src/ty.rs @@ -1,4 +1,4 @@ -use crate::{Alias, Lazy, TypeId}; +use crate::{Alias, Lazy, Struct, TypeId}; #[derive(Debug, Clone)] pub enum Type { @@ -17,4 +17,5 @@ pub enum Type { Ref(TypeId), Lazy(Lazy), Alias(Alias), + Struct(Struct), } From a963569a44bffb093e583d3214ae4975b7234b76 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 00:22:41 -0400 Subject: [PATCH 039/100] Callable --- crates/rue-typing/src/check.rs | 111 ++++++++++++++++------- crates/rue-typing/src/comparison.rs | 102 +++++++++++++-------- crates/rue-typing/src/difference.rs | 7 +- crates/rue-typing/src/lib.rs | 2 +- crates/rue-typing/src/semantic_types.rs | 26 +++++- crates/rue-typing/src/standard_types.rs | 33 +------ crates/rue-typing/src/stringify.rs | 77 ++++++++++++++-- crates/rue-typing/src/substitute_type.rs | 87 ++++++++++++------ crates/rue-typing/src/test_tools.rs | 49 +++++++--- crates/rue-typing/src/ty.rs | 3 +- crates/rue-typing/src/type_system.rs | 58 +++++++++--- 11 files changed, 383 insertions(+), 172 deletions(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 9ab1db0..ea87de2 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -62,6 +62,7 @@ where (Type::Lazy(..), _) | (_, Type::Lazy(..)) => unreachable!(), (Type::Alias(..), _) | (_, Type::Alias(..)) => unreachable!(), (Type::Struct(..), _) | (_, Type::Struct(..)) => unreachable!(), + (Type::Callable(..), _) | (_, Type::Callable(..)) => unreachable!(), // TODO: Implement generic type checking? (Type::Generic, _) => Check::None, @@ -235,6 +236,7 @@ where Type::Lazy(..) => unreachable!(), Type::Alias(..) => unreachable!(), Type::Struct(..) => unreachable!(), + Type::Callable(..) => unreachable!(), Type::Generic => return Err(CheckError::Impossible(item, rhs)), Type::Unknown => {} Type::Never => { @@ -277,6 +279,7 @@ where Type::Alias(..) => unreachable!(), Type::Union(..) => unreachable!(), Type::Struct(..) => unreachable!(), + Type::Callable(..) => unreachable!(), Type::Unknown => Check::None, Type::Generic => return Err(CheckError::Impossible(original_type_id, rhs)), Type::Never => return Err(CheckError::Impossible(original_type_id, rhs)), @@ -338,7 +341,7 @@ mod tests { use indexmap::indexmap; - use crate::{alloc_list, alloc_struct, setup}; + use crate::{alloc_list, alloc_struct, Rest, Semantics}; use super::*; @@ -359,13 +362,15 @@ mod tests { #[test] fn check_any_bytes() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.any, types.bytes, "(not (l val))"); } #[test] fn check_any_bytes32() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str( &mut db, types.any, @@ -376,7 +381,8 @@ mod tests { #[test] fn check_any_public_key() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str( &mut db, types.any, @@ -387,13 +393,15 @@ mod tests { #[test] fn check_any_int() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.any, types.int, "(not (l val))"); } #[test] fn check_any_bool() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str( &mut db, types.any, @@ -404,7 +412,8 @@ mod tests { #[test] fn check_any_nil() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str( &mut db, types.any, @@ -415,55 +424,64 @@ mod tests { #[test] fn check_bytes_bytes() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.bytes, types.bytes, "1"); } #[test] fn check_bytes32_bytes32() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.bytes32, types.bytes32, "1"); } #[test] fn check_public_key_public_key() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.public_key, types.public_key, "1"); } #[test] fn check_int_int() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.int, types.int, "1"); } #[test] fn check_bool_bool() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.bool, types.bool, "1"); } #[test] fn check_nil_nil() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.nil, types.nil, "1"); } #[test] fn check_bytes_bytes32() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.bytes, types.bytes32, "(= (strlen val) 32)"); } #[test] fn check_bytes32_bytes() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.bytes32, types.bytes, "1"); } #[test] fn check_bytes_public_key() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str( &mut db, types.bytes, @@ -474,25 +492,29 @@ mod tests { #[test] fn check_public_key_bytes() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.public_key, types.bytes, "1"); } #[test] fn check_bytes_nil() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.bytes, types.nil, "(= val ())"); } #[test] fn check_nil_bytes() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.nil, types.bytes, "1"); } #[test] fn check_bytes_bool() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str( &mut db, types.bytes, @@ -503,62 +525,72 @@ mod tests { #[test] fn check_bool_bytes() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.bool, types.bytes, "1"); } #[test] fn check_bytes32_public_key() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_impossible(&mut db, types.bytes32, types.public_key); } #[test] fn check_bytes_int() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.bytes, types.int, "1"); } #[test] fn check_int_bytes() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.int, types.bytes, "1"); } #[test] fn check_bool_nil() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.bool, types.nil, "(= val ())"); } #[test] fn check_nil_bool() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.nil, types.bool, "1"); } #[test] fn check_any_any() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.any, types.any, "1"); } #[test] fn check_bytes_any() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); check_str(&mut db, types.any, types.any, "1"); } #[test] fn check_list_nil() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); let list = alloc_list(&mut db, &types, types.bytes); check_str(&mut db, list, types.nil, "(not (l val))"); } #[test] fn check_list_pair() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); let list = alloc_list(&mut db, &types, types.bytes); let pair = db.alloc(Type::Pair(types.bytes, list)); check_str(&mut db, list, pair, "(l val)"); @@ -566,14 +598,16 @@ mod tests { #[test] fn check_any_list() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); let list = alloc_list(&mut db, &types, types.bytes); check_recursive(&mut db, types.any, list); } #[test] fn check_any_point() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); let point_end = db.alloc(Type::Pair(types.int, types.nil)); let point = db.alloc(Type::Pair(types.int, point_end)); check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) ()))"); @@ -581,7 +615,8 @@ mod tests { #[test] fn check_any_point_struct() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); let point_struct = alloc_struct( &mut db, &types, @@ -589,9 +624,15 @@ mod tests { "x".to_string() => types.int, "y".to_string() => types.int, }, - true, + Rest::Nil, + ); + let point = db.substitute( + point_struct, + HashMap::new(), + Semantics::StructuralOnly { + callable: types.never, + }, ); - let point = db.structure(point_struct, HashMap::new()); check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) ()))"); } } diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index c7cdca1..c73fd3e 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -22,7 +22,7 @@ pub(crate) struct ComparisonContext<'a> { } pub(crate) fn compare_type( - types: &TypeSystem, + db: &TypeSystem, lhs: TypeId, rhs: TypeId, ctx: &mut ComparisonContext<'_>, @@ -35,7 +35,7 @@ pub(crate) fn compare_type( return Comparison::Assignable; } - let comparison = match (types.get(lhs), types.get(rhs)) { + let comparison = match (db.get(lhs), db.get(rhs)) { (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), (_, Type::Generic) => { @@ -48,7 +48,7 @@ pub(crate) fn compare_type( } if let Some(found) = found { - compare_type(types, lhs, found, ctx) + compare_type(db, lhs, found, ctx) } else if let Some(generic_stack_frame) = ctx.generic_stack_frame { ctx.substitution_stack[generic_stack_frame].insert(rhs, lhs); Comparison::Assignable @@ -71,7 +71,7 @@ pub(crate) fn compare_type( } if let Some(found) = found { - compare_type(types, found, rhs, ctx) + compare_type(db, found, rhs, ctx) } else { Comparison::Incompatible } @@ -90,7 +90,7 @@ pub(crate) fn compare_type( let length = items.len(); for item in items { - let cmp = compare_type(types, item, rhs, ctx); + let cmp = compare_type(db, item, rhs, ctx); result = max(result, cmp); if cmp == Comparison::Incompatible { incompatible_count += 1; @@ -109,7 +109,7 @@ pub(crate) fn compare_type( let mut result = Comparison::Incompatible; for item in items { - let cmp = compare_type(types, lhs, item, ctx); + let cmp = compare_type(db, lhs, item, ctx); result = min(result, cmp); } @@ -117,8 +117,8 @@ pub(crate) fn compare_type( } (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { - let first = compare_type(types, *lhs_first, *rhs_first, ctx); - let rest = compare_type(types, *lhs_rest, *rhs_rest, ctx); + let first = compare_type(db, *lhs_first, *rhs_first, ctx); + let rest = compare_type(db, *lhs_rest, *rhs_rest, ctx); max(first, rest) } (Type::Pair(..), _) | (_, Type::Pair(..)) => Comparison::Incompatible, @@ -180,29 +180,32 @@ pub(crate) fn compare_type( (Type::Lazy(lazy), _) => { ctx.substitution_stack.push(lazy.substitutions.clone()); - let result = compare_type(types, lazy.type_id, rhs, ctx); + let result = compare_type(db, lazy.type_id, rhs, ctx); ctx.substitution_stack.pop().unwrap(); result } (_, Type::Lazy(lazy)) => { ctx.substitution_stack.push(lazy.substitutions.clone()); - let result = compare_type(types, lhs, lazy.type_id, ctx); + let result = compare_type(db, lhs, lazy.type_id, ctx); ctx.substitution_stack.pop().unwrap(); result } - (Type::Alias(alias), _) => compare_type(types, alias.type_id, rhs, ctx), - (_, Type::Alias(alias)) => compare_type(types, lhs, alias.type_id, ctx), + (Type::Alias(alias), _) => compare_type(db, alias.type_id, rhs, ctx), + (_, Type::Alias(alias)) => compare_type(db, lhs, alias.type_id, ctx), (Type::Struct(lhs), _) => max( - compare_type(types, lhs.type_id, rhs, ctx), + compare_type(db, lhs.type_id, rhs, ctx), Comparison::Castable, ), (_, Type::Struct(rhs)) => max( - compare_type(types, lhs, rhs.type_id, ctx), + compare_type(db, lhs, rhs.type_id, ctx), Comparison::Castable, ), + + (Type::Callable(..), _) => compare_type(db, lhs, db.standard_types().any, ctx), + (_, Type::Callable(..)) => Comparison::Incompatible, }; ctx.visited.remove(&(lhs, rhs)); @@ -212,37 +215,42 @@ pub(crate) fn compare_type( #[cfg(test)] mod tests { - use crate::{alloc_list, setup}; + use crate::alloc_list; use super::*; #[test] fn test_compare_int_int() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!(db.compare(types.int, types.int), Comparison::Equal); } #[test] fn test_compare_int_bytes() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!(db.compare(types.int, types.bytes), Comparison::Castable); } #[test] fn test_compare_bytes_int() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!(db.compare(types.bytes, types.int), Comparison::Castable); } #[test] fn test_compare_bytes_bytes32() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!(db.compare(types.bytes, types.bytes32), Comparison::Superset); } #[test] fn test_compare_bytes32_bytes() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!( db.compare(types.bytes32, types.bytes), Comparison::Assignable @@ -251,7 +259,8 @@ mod tests { #[test] fn test_compare_bytes_public_key() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!( db.compare(types.bytes, types.public_key), Comparison::Superset @@ -260,7 +269,8 @@ mod tests { #[test] fn test_compare_public_key_bytes() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!( db.compare(types.public_key, types.bytes), Comparison::Castable @@ -269,7 +279,8 @@ mod tests { #[test] fn test_compare_bytes32_public_key() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!( db.compare(types.bytes32, types.public_key), Comparison::Incompatible @@ -278,7 +289,8 @@ mod tests { #[test] fn test_compare_public_key_bytes32() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!( db.compare(types.public_key, types.bytes32), Comparison::Incompatible @@ -287,51 +299,59 @@ mod tests { #[test] fn test_compare_bytes_any() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!(db.compare(types.bytes, types.any), Comparison::Assignable); } #[test] fn test_compare_any_bytes() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!(db.compare(types.any, types.bytes), Comparison::Superset); } #[test] fn test_compare_bytes32_any() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!(db.compare(types.bytes32, types.any), Comparison::Assignable); } #[test] fn test_compare_any_bytes32() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!(db.compare(types.any, types.bytes32), Comparison::Superset); } #[test] fn test_compare_list_any() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); let list = alloc_list(&mut db, &types, types.int); assert_eq!(db.compare(list, types.any), Comparison::Assignable); } #[test] fn test_compare_pair_any() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); let pair = db.alloc(Type::Pair(types.int, types.public_key)); assert_eq!(db.compare(pair, types.any), Comparison::Assignable); } #[test] fn test_compare_int_any() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!(db.compare(types.int, types.any), Comparison::Assignable); } #[test] fn test_compare_public_key_any() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!( db.compare(types.public_key, types.any), Comparison::Assignable @@ -340,7 +360,8 @@ mod tests { #[test] fn test_compare_complex_any() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); let pair_inner_inner = db.alloc(Type::Pair(types.any, types.nil)); let pair_inner = db.alloc(Type::Pair(pair_inner_inner, pair_inner_inner)); let pair = db.alloc(Type::Pair(types.int, pair_inner)); @@ -350,13 +371,15 @@ mod tests { #[test] fn test_compare_any_any() { - let (db, types) = setup(); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!(db.compare(types.any, types.any), Comparison::Equal); } #[test] fn test_compare_incompatible_pair() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); let lhs = db.alloc(Type::Pair(types.int, types.public_key)); let rhs = db.alloc(Type::Pair(types.bytes, types.nil)); assert_eq!(db.compare(lhs, rhs), Comparison::Incompatible); @@ -364,7 +387,8 @@ mod tests { #[test] fn test_compare_castable_pair() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); let lhs = db.alloc(Type::Pair(types.int, types.public_key)); let rhs = db.alloc(Type::Pair(types.bytes, types.bytes)); assert_eq!(db.compare(lhs, rhs), Comparison::Castable); @@ -372,7 +396,8 @@ mod tests { #[test] fn test_compare_assignable_pair() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); let lhs = db.alloc(Type::Pair(types.int, types.public_key)); let rhs = db.alloc(Type::Pair(types.atom, types.atom)); assert_eq!(db.compare(lhs, rhs), Comparison::Assignable); @@ -380,7 +405,8 @@ mod tests { #[test] fn test_generic_inference() { - let (mut db, types) = setup(); + let mut db = TypeSystem::new(); + let types = db.standard_types(); let generic = db.alloc(Type::Generic); diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index 85e7ddb..9930075 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -19,6 +19,7 @@ pub(crate) fn difference_type( (Type::Generic, _) | (_, Type::Generic) => lhs, (Type::Unknown, _) | (_, Type::Unknown) => lhs, + (Type::Never, _) => std.never, (_, Type::Never) => lhs, @@ -155,7 +156,7 @@ pub(crate) fn difference_type( original_type_id: Some(ty.original_type_id.unwrap_or(lhs)), type_id, field_names: ty.field_names, - nil_terminated: ty.nil_terminated, + rest: ty.rest, generic_types: ty.generic_types, })) } @@ -167,10 +168,12 @@ pub(crate) fn difference_type( original_type_id: Some(ty.original_type_id.unwrap_or(rhs)), type_id, field_names: ty.field_names, - nil_terminated: ty.nil_terminated, + rest: ty.rest, generic_types: ty.generic_types, })) } + (Type::Callable(..), _) => lhs, + (_, Type::Callable(..)) => lhs, }; visited.remove(&(lhs, rhs)); diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index 8633460..88d5e93 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -22,7 +22,7 @@ pub use type_system::*; pub(crate) use difference::difference_type; pub(crate) use replace_type::replace_type; pub(crate) use stringify::stringify_type; -pub(crate) use substitute_type::substitute_type; +pub(crate) use substitute_type::{substitute_type, Semantics}; #[cfg(test)] mod test_tools; diff --git a/crates/rue-typing/src/semantic_types.rs b/crates/rue-typing/src/semantic_types.rs index 2d04c43..2ed9522 100644 --- a/crates/rue-typing/src/semantic_types.rs +++ b/crates/rue-typing/src/semantic_types.rs @@ -1,9 +1,21 @@ use std::collections::HashMap; -use indexmap::IndexSet; +use indexmap::{IndexMap, IndexSet}; use crate::TypeId; +/// The kind of ending that a list has. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Rest { + /// This means that there is no special terminator for the list. + /// The last element is the rest value. + Spread, + /// This means that the list is nil-terminated. + Nil, + /// This means that the list is nil-terminated, but may contain an additional optional value. + Optional, +} + /// Allows you to map generic types lazily without having to resolve them immediately. /// This prevents stack overflows when resolving generic type definitions that reference themselves. /// When reducing a type to its structural form, lazy types should be removed. @@ -27,6 +39,16 @@ pub struct Struct { pub original_type_id: Option, pub type_id: TypeId, pub field_names: IndexSet, - pub nil_terminated: bool, + pub rest: Rest, + pub generic_types: Vec, +} + +/// Represents something which can be called with arguments and returns a given type. +#[derive(Debug, Clone)] +pub struct Callable { + pub original_type_id: Option, + pub parameters: IndexMap, + pub return_type: TypeId, + pub rest: Rest, pub generic_types: Vec, } diff --git a/crates/rue-typing/src/standard_types.rs b/crates/rue-typing/src/standard_types.rs index d292c4c..9d853d9 100644 --- a/crates/rue-typing/src/standard_types.rs +++ b/crates/rue-typing/src/standard_types.rs @@ -1,4 +1,4 @@ -use crate::{Type, TypeId, TypeSystem}; +use crate::TypeId; #[derive(Debug, Clone, Copy)] pub struct StandardTypes { @@ -13,34 +13,3 @@ pub struct StandardTypes { pub bool: TypeId, pub nil: TypeId, } - -impl StandardTypes { - pub fn alloc(type_system: &mut TypeSystem) -> Self { - let unknown = type_system.alloc(Type::Unknown); - let never = type_system.alloc(Type::Never); - let atom = type_system.alloc(Type::Atom); - let bytes = type_system.alloc(Type::Bytes); - let bytes32 = type_system.alloc(Type::Bytes32); - let public_key = type_system.alloc(Type::PublicKey); - let int = type_system.alloc(Type::Int); - let bool = type_system.alloc(Type::Bool); - let nil = type_system.alloc(Type::Nil); - - let any = type_system.alloc(Type::Unknown); - let pair = type_system.alloc(Type::Pair(any, any)); - *type_system.get_mut(any) = Type::Union(vec![atom, pair]); - - Self { - unknown, - never, - any, - atom, - bytes, - bytes32, - public_key, - int, - bool, - nil, - } - } -} diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs index 4bd3e0b..ae75cf6 100644 --- a/crates/rue-typing/src/stringify.rs +++ b/crates/rue-typing/src/stringify.rs @@ -1,6 +1,6 @@ use std::collections::{HashMap, HashSet}; -use crate::{Struct, Type, TypeId, TypeSystem}; +use crate::{Callable, Rest, Struct, Type, TypeId, TypeSystem}; pub(crate) fn stringify_type( types: &TypeSystem, @@ -48,6 +48,48 @@ pub(crate) fn stringify_type( Type::Lazy(lazy) => stringify_type(types, lazy.type_id, names, visited), Type::Alias(alias) => stringify_type(types, alias.type_id, names, visited), Type::Struct(Struct { type_id, .. }) => stringify_type(types, *type_id, names, visited), + Type::Callable(Callable { + parameters, + return_type, + rest, + .. + }) => { + let mut result = "fun(".to_string(); + + for (index, (parameter_name, parameter_type)) in + parameters.clone().into_iter().enumerate() + { + if index > 0 { + result.push_str(", "); + } + + if index == parameters.len() - 1 { + match rest { + Rest::Spread => { + result.push_str("..."); + result.push_str(¶meter_name); + } + Rest::Nil => { + result.push_str(¶meter_name); + } + Rest::Optional => { + result.push_str(¶meter_name); + result.push('?'); + } + } + } else { + result.push_str(¶meter_name); + } + + result.push_str(": "); + result.push_str(&stringify_type(types, parameter_type, names, visited)); + } + + result.push_str(") -> "); + result.push_str(&stringify_type(types, *return_type, names, visited)); + + result + } }; visited.remove(&type_id); @@ -57,14 +99,16 @@ pub(crate) fn stringify_type( #[cfg(test)] mod tests { - use crate::StandardTypes; + use indexmap::indexmap; + + use crate::alloc_callable; use super::*; #[test] fn stringify_atoms() { - let mut db = TypeSystem::new(); - let types = StandardTypes::alloc(&mut db); + let db = TypeSystem::new(); + let types = db.standard_types(); assert_eq!(db.stringify(types.unknown), "{unknown}"); assert_eq!(db.stringify(types.never), "Never"); @@ -79,12 +123,33 @@ mod tests { #[test] fn stringify_named() { - let mut db = TypeSystem::new(); - let types = StandardTypes::alloc(&mut db); + let db = TypeSystem::new(); + let types = db.standard_types(); let mut names = HashMap::new(); names.insert(types.any, "Any".to_string()); assert_eq!(db.stringify_named(types.any, &names), "Any"); } + + #[test] + fn test_stringify_callable() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + + let callable = alloc_callable( + &mut db, + indexmap! { + "a".to_string() => types.int, + "b".to_string() => types.bytes, + }, + types.bool, + Rest::Nil, + ); + + assert_eq!( + db.stringify_named(callable, &HashMap::new()), + "fun(a: Int, b: Bytes) -> Bool" + ); + } } diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index 0842b1e..d4b2d8f 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -1,12 +1,20 @@ use std::collections::{HashMap, HashSet}; -use crate::{Alias, Type, TypeId, TypeSystem}; +use indexmap::IndexMap; + +use crate::{Alias, Callable, Type, TypeId, TypeSystem}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Semantics { + Preserve, + StructuralOnly { callable: TypeId }, +} pub(crate) fn substitute_type( types: &mut TypeSystem, type_id: TypeId, substitutions: &mut HashMap, - preserve_semantics: bool, + semantics: Semantics, visited: &mut HashSet, ) -> TypeId { if let Some(new_type_id) = substitutions.get(&type_id) { @@ -32,9 +40,8 @@ pub(crate) fn substitute_type( Type::Pair(first, rest) => { let (first, rest) = (*first, *rest); - let new_first = - substitute_type(types, first, substitutions, preserve_semantics, visited); - let new_rest = substitute_type(types, rest, substitutions, preserve_semantics, visited); + let new_first = substitute_type(types, first, substitutions, semantics, visited); + let new_rest = substitute_type(types, rest, substitutions, semantics, visited); if new_first == first && new_rest == rest { type_id @@ -51,7 +58,7 @@ pub(crate) fn substitute_type( types, *item, substitutions, - preserve_semantics, + semantics, visited, )); } @@ -64,26 +71,15 @@ pub(crate) fn substitute_type( } Type::Lazy(lazy) => { substitutions.extend(lazy.substitutions.clone()); - substitute_type( - types, - lazy.type_id, - substitutions, - preserve_semantics, - visited, - ) + substitute_type(types, lazy.type_id, substitutions, semantics, visited) } Type::Alias(alias) => { let alias = alias.clone(); - let new_type_id = substitute_type( - types, - alias.type_id, - substitutions, - preserve_semantics, - visited, - ); + let new_type_id = + substitute_type(types, alias.type_id, substitutions, semantics, visited); - if preserve_semantics { + if semantics == Semantics::Preserve { if new_type_id == alias.type_id { type_id } else { @@ -100,15 +96,9 @@ pub(crate) fn substitute_type( Type::Struct(ty) => { let ty = ty.clone(); - let new_type_id = substitute_type( - types, - ty.type_id, - substitutions, - preserve_semantics, - visited, - ); + let new_type_id = substitute_type(types, ty.type_id, substitutions, semantics, visited); - if preserve_semantics { + if semantics == Semantics::Preserve { if new_type_id == ty.type_id { type_id } else { @@ -116,7 +106,7 @@ pub(crate) fn substitute_type( original_type_id: Some(ty.original_type_id.unwrap_or(type_id)), type_id: new_type_id, field_names: ty.field_names, - nil_terminated: ty.nil_terminated, + rest: ty.rest, generic_types: ty.generic_types, })) } @@ -124,6 +114,43 @@ pub(crate) fn substitute_type( new_type_id } } + Type::Callable(callable) => { + let callable = callable.clone(); + + let new_return_type = substitute_type( + types, + callable.return_type, + substitutions, + semantics, + visited, + ); + + let mut new_parameters = IndexMap::new(); + for (name, parameter) in &callable.parameters { + let new_parameter = + substitute_type(types, *parameter, substitutions, semantics, visited); + new_parameters.insert(name.clone(), new_parameter); + } + + match semantics { + Semantics::Preserve => { + if new_return_type == callable.return_type + && new_parameters == callable.parameters + { + type_id + } else { + types.alloc(Type::Callable(Callable { + original_type_id: Some(callable.original_type_id.unwrap_or(type_id)), + parameters: new_parameters, + return_type: new_return_type, + rest: callable.rest, + generic_types: callable.generic_types, + })) + } + } + Semantics::StructuralOnly { callable } => callable, + } + } }; visited.remove(&type_id); diff --git a/crates/rue-typing/src/test_tools.rs b/crates/rue-typing/src/test_tools.rs index 3520c60..07e3f90 100644 --- a/crates/rue-typing/src/test_tools.rs +++ b/crates/rue-typing/src/test_tools.rs @@ -1,12 +1,6 @@ use indexmap::IndexMap; -use crate::{StandardTypes, Struct, Type, TypeId, TypeSystem}; - -pub fn setup() -> (TypeSystem, StandardTypes) { - let mut db = TypeSystem::new(); - let types = StandardTypes::alloc(&mut db); - (db, types) -} +use crate::{Callable, Rest, StandardTypes, Struct, Type, TypeId, TypeSystem}; pub fn alloc_list(db: &mut TypeSystem, types: &StandardTypes, item_type_id: TypeId) -> TypeId { let list = db.alloc(Type::Unknown); @@ -15,23 +9,52 @@ pub fn alloc_list(db: &mut TypeSystem, types: &StandardTypes, item_type_id: Type list } +pub fn alloc_callable( + db: &mut TypeSystem, + parameters: IndexMap, + return_type: TypeId, + rest: Rest, +) -> TypeId { + db.alloc(Type::Callable(Callable { + original_type_id: None, + parameters, + return_type, + rest, + generic_types: Vec::new(), + })) +} + pub fn alloc_struct( db: &mut TypeSystem, types: &StandardTypes, fields: &IndexMap, - nil_terminated: bool, + rest: Rest, ) -> TypeId { - let structure = if nil_terminated { - alloc_list_of(db, types, fields.values().copied()) - } else { - alloc_tuple_of(db, types, fields.values().copied()) + let structure = match rest { + Rest::Nil => alloc_list_of(db, types, fields.values().copied()), + Rest::Spread => alloc_tuple_of(db, types, fields.values().copied()), + Rest::Optional => { + let fields: Vec = fields + .values() + .copied() + .enumerate() + .map(|(i, field)| { + if i == fields.len() - 1 { + db.alloc(Type::Union(vec![field, types.nil])) + } else { + field + } + }) + .collect(); + alloc_tuple_of(db, types, fields.into_iter()) + } }; db.alloc(Type::Struct(Struct { original_type_id: None, type_id: structure, field_names: fields.keys().cloned().collect(), - nil_terminated, + rest, generic_types: Vec::new(), })) } diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs index dc68748..cfce009 100644 --- a/crates/rue-typing/src/ty.rs +++ b/crates/rue-typing/src/ty.rs @@ -1,4 +1,4 @@ -use crate::{Alias, Lazy, Struct, TypeId}; +use crate::{Alias, Callable, Lazy, Struct, TypeId}; #[derive(Debug, Clone)] pub enum Type { @@ -18,4 +18,5 @@ pub enum Type { Lazy(Lazy), Alias(Alias), Struct(Struct), + Callable(Callable), } diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index 4de1bb3..7d71333 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -4,15 +4,52 @@ use id_arena::{Arena, Id}; use crate::{ check_type, compare_type, difference_type, replace_type, simplify_check, stringify_type, - substitute_type, Check, CheckError, Comparison, ComparisonContext, StandardTypes, Type, - TypePath, + substitute_type, Check, CheckError, Comparison, ComparisonContext, Semantics, StandardTypes, + Type, TypePath, }; pub type TypeId = Id; -#[derive(Debug, Default, Clone)] +#[derive(Debug, Clone)] pub struct TypeSystem { arena: Arena, + types: StandardTypes, +} + +impl Default for TypeSystem { + fn default() -> Self { + let mut arena = Arena::new(); + + let unknown = arena.alloc(Type::Unknown); + let never = arena.alloc(Type::Never); + let atom = arena.alloc(Type::Atom); + let bytes = arena.alloc(Type::Bytes); + let bytes32 = arena.alloc(Type::Bytes32); + let public_key = arena.alloc(Type::PublicKey); + let int = arena.alloc(Type::Int); + let bool = arena.alloc(Type::Bool); + let nil = arena.alloc(Type::Nil); + + let any = arena.alloc(Type::Unknown); + let pair = arena.alloc(Type::Pair(any, any)); + arena[any] = Type::Union(vec![atom, pair]); + + Self { + arena, + types: StandardTypes { + unknown, + never, + any, + atom, + bytes, + bytes32, + public_key, + int, + bool, + nil, + }, + } + } } impl TypeSystem { @@ -20,6 +57,10 @@ impl TypeSystem { Self::default() } + pub fn standard_types(&self) -> StandardTypes { + self.types + } + pub fn alloc(&mut self, ty: Type) -> TypeId { self.arena.alloc(ty) } @@ -81,20 +122,13 @@ impl TypeSystem { &mut self, type_id: TypeId, mut substitutions: HashMap, - ) -> TypeId { - substitute_type(self, type_id, &mut substitutions, true, &mut HashSet::new()) - } - - pub fn structure( - &mut self, - type_id: TypeId, - mut substitutions: HashMap, + semantics: Semantics, ) -> TypeId { substitute_type( self, type_id, &mut substitutions, - false, + semantics, &mut HashSet::new(), ) } From 7217c4efa8c2ca58b9f5ff2c15988c7f326343d9 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 00:38:16 -0400 Subject: [PATCH 040/100] Callable 2 --- crates/rue-typing/src/check.rs | 7 ++-- crates/rue-typing/src/comparison.rs | 9 +++- crates/rue-typing/src/semantic_types.rs | 7 ++-- crates/rue-typing/src/stringify.rs | 41 +++---------------- crates/rue-typing/src/substitute_type.rs | 16 ++++---- crates/rue-typing/src/test_tools.rs | 52 +++++++++++++++--------- 6 files changed, 60 insertions(+), 72 deletions(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index ea87de2..bf1ea2a 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -583,7 +583,7 @@ mod tests { fn check_list_nil() { let mut db = TypeSystem::new(); let types = db.standard_types(); - let list = alloc_list(&mut db, &types, types.bytes); + let list = alloc_list(&mut db, types.bytes); check_str(&mut db, list, types.nil, "(not (l val))"); } @@ -591,7 +591,7 @@ mod tests { fn check_list_pair() { let mut db = TypeSystem::new(); let types = db.standard_types(); - let list = alloc_list(&mut db, &types, types.bytes); + let list = alloc_list(&mut db, types.bytes); let pair = db.alloc(Type::Pair(types.bytes, list)); check_str(&mut db, list, pair, "(l val)"); } @@ -600,7 +600,7 @@ mod tests { fn check_any_list() { let mut db = TypeSystem::new(); let types = db.standard_types(); - let list = alloc_list(&mut db, &types, types.bytes); + let list = alloc_list(&mut db, types.bytes); check_recursive(&mut db, types.any, list); } @@ -619,7 +619,6 @@ mod tests { let types = db.standard_types(); let point_struct = alloc_struct( &mut db, - &types, &indexmap! { "x".to_string() => types.int, "y".to_string() => types.int, diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index c73fd3e..8de3a84 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -204,6 +204,11 @@ pub(crate) fn compare_type( Comparison::Castable, ), + (Type::Callable(lhs), Type::Callable(rhs)) => max( + compare_type(db, lhs.parameters, rhs.parameters, ctx), + compare_type(db, lhs.return_type, rhs.return_type, ctx), + ), + (Type::Callable(..), _) => compare_type(db, lhs, db.standard_types().any, ctx), (_, Type::Callable(..)) => Comparison::Incompatible, }; @@ -329,7 +334,7 @@ mod tests { fn test_compare_list_any() { let mut db = TypeSystem::new(); let types = db.standard_types(); - let list = alloc_list(&mut db, &types, types.int); + let list = alloc_list(&mut db, types.int); assert_eq!(db.compare(list, types.any), Comparison::Assignable); } @@ -365,7 +370,7 @@ mod tests { let pair_inner_inner = db.alloc(Type::Pair(types.any, types.nil)); let pair_inner = db.alloc(Type::Pair(pair_inner_inner, pair_inner_inner)); let pair = db.alloc(Type::Pair(types.int, pair_inner)); - let list = alloc_list(&mut db, &types, pair); + let list = alloc_list(&mut db, pair); assert_eq!(db.compare(list, types.any), Comparison::Assignable); } diff --git a/crates/rue-typing/src/semantic_types.rs b/crates/rue-typing/src/semantic_types.rs index 2ed9522..b2c9971 100644 --- a/crates/rue-typing/src/semantic_types.rs +++ b/crates/rue-typing/src/semantic_types.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use indexmap::{IndexMap, IndexSet}; +use indexmap::IndexSet; use crate::TypeId; @@ -37,8 +37,8 @@ pub struct Alias { #[derive(Debug, Clone)] pub struct Struct { pub original_type_id: Option, - pub type_id: TypeId, pub field_names: IndexSet, + pub type_id: TypeId, pub rest: Rest, pub generic_types: Vec, } @@ -47,7 +47,8 @@ pub struct Struct { #[derive(Debug, Clone)] pub struct Callable { pub original_type_id: Option, - pub parameters: IndexMap, + pub parameter_names: IndexSet, + pub parameters: TypeId, pub return_type: TypeId, pub rest: Rest, pub generic_types: Vec, diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs index ae75cf6..7c678e1 100644 --- a/crates/rue-typing/src/stringify.rs +++ b/crates/rue-typing/src/stringify.rs @@ -1,6 +1,6 @@ use std::collections::{HashMap, HashSet}; -use crate::{Callable, Rest, Struct, Type, TypeId, TypeSystem}; +use crate::{Callable, Struct, Type, TypeId, TypeSystem}; pub(crate) fn stringify_type( types: &TypeSystem, @@ -51,43 +51,12 @@ pub(crate) fn stringify_type( Type::Callable(Callable { parameters, return_type, - rest, .. }) => { let mut result = "fun(".to_string(); - - for (index, (parameter_name, parameter_type)) in - parameters.clone().into_iter().enumerate() - { - if index > 0 { - result.push_str(", "); - } - - if index == parameters.len() - 1 { - match rest { - Rest::Spread => { - result.push_str("..."); - result.push_str(¶meter_name); - } - Rest::Nil => { - result.push_str(¶meter_name); - } - Rest::Optional => { - result.push_str(¶meter_name); - result.push('?'); - } - } - } else { - result.push_str(¶meter_name); - } - - result.push_str(": "); - result.push_str(&stringify_type(types, parameter_type, names, visited)); - } - + result.push_str(&stringify_type(types, *parameters, names, visited)); result.push_str(") -> "); result.push_str(&stringify_type(types, *return_type, names, visited)); - result } }; @@ -101,7 +70,7 @@ pub(crate) fn stringify_type( mod tests { use indexmap::indexmap; - use crate::alloc_callable; + use crate::{alloc_callable, Rest}; use super::*; @@ -139,7 +108,7 @@ mod tests { let callable = alloc_callable( &mut db, - indexmap! { + &indexmap! { "a".to_string() => types.int, "b".to_string() => types.bytes, }, @@ -149,7 +118,7 @@ mod tests { assert_eq!( db.stringify_named(callable, &HashMap::new()), - "fun(a: Int, b: Bytes) -> Bool" + "fun((Int, (Bytes, Nil))) -> Bool" ); } } diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index d4b2d8f..4739669 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -1,7 +1,5 @@ use std::collections::{HashMap, HashSet}; -use indexmap::IndexMap; - use crate::{Alias, Callable, Type, TypeId, TypeSystem}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -125,12 +123,13 @@ pub(crate) fn substitute_type( visited, ); - let mut new_parameters = IndexMap::new(); - for (name, parameter) in &callable.parameters { - let new_parameter = - substitute_type(types, *parameter, substitutions, semantics, visited); - new_parameters.insert(name.clone(), new_parameter); - } + let new_parameters = substitute_type( + types, + callable.parameters, + substitutions, + semantics, + visited, + ); match semantics { Semantics::Preserve => { @@ -141,6 +140,7 @@ pub(crate) fn substitute_type( } else { types.alloc(Type::Callable(Callable { original_type_id: Some(callable.original_type_id.unwrap_or(type_id)), + parameter_names: callable.parameter_names, parameters: new_parameters, return_type: new_return_type, rest: callable.rest, diff --git a/crates/rue-typing/src/test_tools.rs b/crates/rue-typing/src/test_tools.rs index 07e3f90..d0446a3 100644 --- a/crates/rue-typing/src/test_tools.rs +++ b/crates/rue-typing/src/test_tools.rs @@ -1,38 +1,54 @@ use indexmap::IndexMap; -use crate::{Callable, Rest, StandardTypes, Struct, Type, TypeId, TypeSystem}; +use crate::{Callable, Rest, Struct, Type, TypeId, TypeSystem}; -pub fn alloc_list(db: &mut TypeSystem, types: &StandardTypes, item_type_id: TypeId) -> TypeId { +pub fn alloc_list(db: &mut TypeSystem, item_type_id: TypeId) -> TypeId { let list = db.alloc(Type::Unknown); let pair = db.alloc(Type::Pair(item_type_id, list)); - *db.get_mut(list) = Type::Union(vec![pair, types.nil]); + *db.get_mut(list) = Type::Union(vec![pair, db.standard_types().nil]); list } pub fn alloc_callable( db: &mut TypeSystem, - parameters: IndexMap, + parameters: &IndexMap, return_type: TypeId, rest: Rest, ) -> TypeId { + let structure = match rest { + Rest::Nil => alloc_list_of(db, parameters.values().copied()), + Rest::Spread => alloc_tuple_of(db, parameters.values().copied()), + Rest::Optional => { + let parameters: Vec = parameters + .values() + .copied() + .enumerate() + .map(|(i, field)| { + if i == parameters.len() - 1 { + db.alloc(Type::Union(vec![field, db.standard_types().nil])) + } else { + field + } + }) + .collect(); + alloc_tuple_of(db, parameters.into_iter()) + } + }; + db.alloc(Type::Callable(Callable { original_type_id: None, - parameters, + parameter_names: parameters.keys().cloned().collect(), + parameters: structure, return_type, rest, generic_types: Vec::new(), })) } -pub fn alloc_struct( - db: &mut TypeSystem, - types: &StandardTypes, - fields: &IndexMap, - rest: Rest, -) -> TypeId { +pub fn alloc_struct(db: &mut TypeSystem, fields: &IndexMap, rest: Rest) -> TypeId { let structure = match rest { - Rest::Nil => alloc_list_of(db, types, fields.values().copied()), - Rest::Spread => alloc_tuple_of(db, types, fields.values().copied()), + Rest::Nil => alloc_list_of(db, fields.values().copied()), + Rest::Spread => alloc_tuple_of(db, fields.values().copied()), Rest::Optional => { let fields: Vec = fields .values() @@ -40,13 +56,13 @@ pub fn alloc_struct( .enumerate() .map(|(i, field)| { if i == fields.len() - 1 { - db.alloc(Type::Union(vec![field, types.nil])) + db.alloc(Type::Union(vec![field, db.standard_types().nil])) } else { field } }) .collect(); - alloc_tuple_of(db, types, fields.into_iter()) + alloc_tuple_of(db, fields.into_iter()) } }; @@ -61,10 +77,9 @@ pub fn alloc_struct( pub fn alloc_list_of( db: &mut TypeSystem, - types: &StandardTypes, items: impl DoubleEndedIterator, ) -> TypeId { - let mut tuple = types.nil; + let mut tuple = db.standard_types().nil; for item in items.rev() { tuple = db.alloc(Type::Pair(item, tuple)); } @@ -73,10 +88,9 @@ pub fn alloc_list_of( pub fn alloc_tuple_of( db: &mut TypeSystem, - types: &StandardTypes, items: impl DoubleEndedIterator, ) -> TypeId { - let mut tuple = types.nil; + let mut tuple = db.standard_types().nil; for (i, item) in items.rev().enumerate() { if i == 0 { tuple = item; From 86f2dc9489263fe911110e140bb982dfffb30465 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 00:39:46 -0400 Subject: [PATCH 041/100] Move check_type --- crates/rue-typing/src/check.rs | 606 +--------------------- crates/rue-typing/src/check/check_type.rs | 604 +++++++++++++++++++++ 2 files changed, 607 insertions(+), 603 deletions(-) create mode 100644 crates/rue-typing/src/check/check_type.rs diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index bf1ea2a..db8c73d 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -1,12 +1,7 @@ -use std::{ - collections::{HashSet, VecDeque}, - fmt, - hash::BuildHasher, -}; - -use crate::{Comparison, Type, TypeId, TypeSystem}; +use std::fmt; mod check_error; +mod check_type; mod simplify_and; mod simplify_check; mod simplify_or; @@ -14,6 +9,7 @@ mod stringify_check; pub use check_error::*; +pub(crate) use check_type::*; pub(crate) use simplify_and::*; pub(crate) use simplify_check::*; pub(crate) use simplify_or::*; @@ -39,599 +35,3 @@ impl fmt::Display for Check { stringify_check(self, f, &mut Vec::new()) } } - -/// Returns [`None`] for recursive checks. -pub(crate) fn check_type( - types: &mut TypeSystem, - lhs: TypeId, - rhs: TypeId, - visited: &mut HashSet<(TypeId, TypeId), S>, -) -> Result -where - S: BuildHasher, -{ - if !visited.insert((lhs, rhs)) { - if types.compare(lhs, rhs) <= Comparison::Castable { - return Ok(Check::None); - } - return Err(CheckError::Recursive(lhs, rhs)); - } - - let check = match (types.get(lhs), types.get(rhs)) { - (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), - (Type::Lazy(..), _) | (_, Type::Lazy(..)) => unreachable!(), - (Type::Alias(..), _) | (_, Type::Alias(..)) => unreachable!(), - (Type::Struct(..), _) | (_, Type::Struct(..)) => unreachable!(), - (Type::Callable(..), _) | (_, Type::Callable(..)) => unreachable!(), - - // TODO: Implement generic type checking? - (Type::Generic, _) => Check::None, - (_, Type::Generic) => Check::None, - - (Type::Unknown, _) => Check::None, - (_, Type::Unknown) => Check::None, - - (Type::Never, _) => Check::None, - (_, Type::Never) => return Err(CheckError::Impossible(lhs, rhs)), - - (Type::Atom, Type::Atom) => Check::None, - (Type::Bytes, Type::Bytes) => Check::None, - (Type::Bytes32, Type::Bytes32) => Check::None, - (Type::PublicKey, Type::PublicKey) => Check::None, - (Type::Int, Type::Int) => Check::None, - (Type::Bool, Type::Bool) => Check::None, - (Type::Nil, Type::Nil) => Check::None, - - (Type::Bytes32, Type::Atom) => Check::None, - (Type::PublicKey, Type::Atom) => Check::None, - (Type::Int, Type::Atom) => Check::None, - (Type::Bytes, Type::Atom) => Check::None, - (Type::Bool, Type::Atom) => Check::None, - (Type::Nil, Type::Atom) => Check::None, - - (Type::Atom, Type::Bytes) => Check::None, - (Type::Bytes32, Type::Bytes) => Check::None, - (Type::PublicKey, Type::Bytes) => Check::None, - (Type::Int, Type::Bytes) => Check::None, - (Type::Bool, Type::Bytes) => Check::None, - (Type::Nil, Type::Bytes) => Check::None, - - (Type::Atom, Type::Int) => Check::None, - (Type::Bytes32, Type::Int) => Check::None, - (Type::PublicKey, Type::Int) => Check::None, - (Type::Bytes, Type::Int) => Check::None, - (Type::Bool, Type::Int) => Check::None, - (Type::Nil, Type::Int) => Check::None, - - (Type::Nil, Type::Bool) => Check::None, - - (Type::Atom, Type::Bool) => Check::IsBool, - (Type::Atom, Type::Nil) => Check::IsNil, - (Type::Atom, Type::PublicKey) => Check::Length(48), - (Type::Atom, Type::Bytes32) => Check::Length(32), - - (Type::Bytes, Type::Bool) => Check::IsBool, - (Type::Bytes, Type::Nil) => Check::IsNil, - (Type::Bytes, Type::PublicKey) => Check::Length(48), - (Type::Bytes, Type::Bytes32) => Check::Length(32), - - (Type::Int, Type::Bool) => Check::IsBool, - (Type::Int, Type::Nil) => Check::IsNil, - (Type::Int, Type::PublicKey) => Check::Length(48), - (Type::Int, Type::Bytes32) => Check::Length(32), - - (Type::Bool, Type::Nil) => Check::IsNil, - - (_, Type::Union(items)) => { - let mut result = Vec::new(); - for item in items.clone() { - result.push(check_type(types, lhs, item, visited)?); - } - Check::Or(result) - } - - (Type::Union(items), _) => { - let items = items.clone(); - check_union_against_rhs(types, lhs, &items, rhs, visited)? - } - - (Type::PublicKey, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bytes32, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Nil, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Nil, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::PublicKey, Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bytes32, Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bool, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bool, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::PublicKey, Type::Bool) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bytes32, Type::Bool) => return Err(CheckError::Impossible(lhs, rhs)), - - (Type::Atom, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bytes, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bytes32, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::PublicKey, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Int, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bool, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Nil, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - - (Type::Pair(..), Type::Atom) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Pair(..), Type::Bytes) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Pair(..), Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Pair(..), Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Pair(..), Type::Int) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Pair(..), Type::Bool) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Pair(..), Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), - - (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { - let (lhs_first, lhs_rest) = (*lhs_first, *lhs_rest); - let (rhs_first, rhs_rest) = (*rhs_first, *rhs_rest); - let first = check_type(types, lhs_first, rhs_first, visited)?; - let rest = check_type(types, lhs_rest, rhs_rest, visited)?; - Check::And(vec![ - Check::First(Box::new(first)), - Check::Rest(Box::new(rest)), - ]) - } - }; - - visited.remove(&(lhs, rhs)); - - Ok(check) -} - -fn check_union_against_rhs( - types: &mut TypeSystem, - original_type_id: TypeId, - items: &[TypeId], - rhs: TypeId, - visited: &mut HashSet<(TypeId, TypeId), S>, -) -> Result -where - S: BuildHasher, -{ - let union = types.alloc(Type::Union(items.to_vec())); - - if types.compare(union, rhs) <= Comparison::Castable { - return Ok(Check::None); - } - - if let Type::Union(union) = types.get(rhs) { - let rhs_items = union.clone(); - let mut result = Vec::new(); - for rhs_item in rhs_items { - if !visited.insert((original_type_id, rhs_item)) { - return Err(CheckError::Recursive(original_type_id, rhs_item)); - } - result.push(check_union_against_rhs( - types, - original_type_id, - items, - rhs_item, - visited, - )?); - } - return Ok(Check::Or(result)); - } - - let mut atom_count = 0; - let mut bool_count = 0; - let mut nil_count = 0; - let mut bytes32_count = 0; - let mut public_key_count = 0; - let mut pairs = Vec::new(); - - let mut items: VecDeque<_> = items.iter().copied().collect::>(); - let mut length = 0; - - while !items.is_empty() { - let item = items.remove(0).unwrap(); - length += 1; - - if !visited.insert((item, rhs)) { - return Err(CheckError::Recursive(item, rhs)); - } - - match types.get(item) { - Type::Ref(..) => unreachable!(), - Type::Lazy(..) => unreachable!(), - Type::Alias(..) => unreachable!(), - Type::Struct(..) => unreachable!(), - Type::Callable(..) => unreachable!(), - Type::Generic => return Err(CheckError::Impossible(item, rhs)), - Type::Unknown => {} - Type::Never => { - length -= 1; - } - Type::Atom | Type::Bytes | Type::Int => { - atom_count += 1; - } - Type::Bytes32 => { - atom_count += 1; - bytes32_count += 1; - } - Type::PublicKey => { - atom_count += 1; - public_key_count += 1; - } - Type::Bool => { - atom_count += 1; - bool_count += 1; - } - Type::Nil => { - atom_count += 1; - nil_count += 1; - bool_count += 1; - } - Type::Pair(first, rest) => { - pairs.push((*first, *rest)); - } - Type::Union(child_items) => { - items.extend(child_items); - } - } - - visited.remove(&(item, rhs)); - } - - Ok(match types.get(rhs) { - Type::Ref(..) => unreachable!(), - Type::Lazy(..) => unreachable!(), - Type::Alias(..) => unreachable!(), - Type::Union(..) => unreachable!(), - Type::Struct(..) => unreachable!(), - Type::Callable(..) => unreachable!(), - Type::Unknown => Check::None, - Type::Generic => return Err(CheckError::Impossible(original_type_id, rhs)), - Type::Never => return Err(CheckError::Impossible(original_type_id, rhs)), - Type::Atom if atom_count == length => Check::None, - Type::Bytes if atom_count == length => Check::None, - Type::Int if atom_count == length => Check::None, - Type::Bool if bool_count == length => Check::None, - Type::Nil if nil_count == length => Check::None, - Type::Bytes32 if bytes32_count == length => Check::None, - Type::PublicKey if public_key_count == length => Check::None, - Type::Bytes32 if atom_count == length => Check::Length(32), - Type::PublicKey if atom_count == length => Check::Length(48), - Type::Bool if atom_count == length => Check::IsBool, - Type::Nil if atom_count == length => Check::IsNil, - Type::Atom => Check::IsAtom, - Type::Bytes => Check::IsAtom, - Type::Int => Check::IsAtom, - Type::Bytes32 if bytes32_count == atom_count => Check::IsAtom, - Type::PublicKey if public_key_count == atom_count => Check::IsAtom, - Type::Bool if bool_count == atom_count => Check::IsAtom, - Type::Nil if nil_count == atom_count => Check::IsAtom, - Type::Bytes32 => Check::And(vec![Check::IsAtom, Check::Length(32)]), - Type::PublicKey => Check::And(vec![Check::IsAtom, Check::Length(48)]), - Type::Bool => Check::And(vec![Check::IsAtom, Check::IsBool]), - Type::Nil => Check::And(vec![Check::IsAtom, Check::IsNil]), - Type::Pair(..) if atom_count == length => { - return Err(CheckError::Impossible(original_type_id, rhs)) - } - Type::Pair(first, rest) => { - let (first, rest) = (*first, *rest); - - let first_items: Vec<_> = pairs.iter().map(|(first, _)| *first).collect(); - let rest_items: Vec<_> = pairs.iter().map(|(_, rest)| *rest).collect(); - - let first = - check_union_against_rhs(types, original_type_id, &first_items, first, visited)?; - let rest = - check_union_against_rhs(types, original_type_id, &rest_items, rest, visited)?; - - if pairs.len() == length { - Check::And(vec![ - Check::First(Box::new(first)), - Check::Rest(Box::new(rest)), - ]) - } else { - Check::And(vec![ - Check::IsPair, - Check::First(Box::new(first)), - Check::Rest(Box::new(rest)), - ]) - } - } - }) -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use indexmap::indexmap; - - use crate::{alloc_list, alloc_struct, Rest, Semantics}; - - use super::*; - - fn check_str(db: &mut TypeSystem, lhs: TypeId, rhs: TypeId, expected: &str) { - assert_eq!(format!("{}", db.check(lhs, rhs).unwrap()), expected); - } - - fn check_recursive(db: &mut TypeSystem, lhs: TypeId, rhs: TypeId) { - assert!(matches!(db.check(lhs, rhs), Err(CheckError::Recursive(..)))); - } - - fn check_impossible(db: &mut TypeSystem, lhs: TypeId, rhs: TypeId) { - assert!(matches!( - db.check(lhs, rhs), - Err(CheckError::Impossible(..)) - )); - } - - #[test] - fn check_any_bytes() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.any, types.bytes, "(not (l val))"); - } - - #[test] - fn check_any_bytes32() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str( - &mut db, - types.any, - types.bytes32, - "(and (not (l val)) (= (strlen val) 32))", - ); - } - - #[test] - fn check_any_public_key() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str( - &mut db, - types.any, - types.public_key, - "(and (not (l val)) (= (strlen val) 48))", - ); - } - - #[test] - fn check_any_int() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.any, types.int, "(not (l val))"); - } - - #[test] - fn check_any_bool() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str( - &mut db, - types.any, - types.bool, - "(and (not (l val)) (any (= val ()) (= val 1)))", - ); - } - - #[test] - fn check_any_nil() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str( - &mut db, - types.any, - types.nil, - "(and (not (l val)) (= val ()))", - ); - } - - #[test] - fn check_bytes_bytes() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.bytes, types.bytes, "1"); - } - - #[test] - fn check_bytes32_bytes32() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.bytes32, types.bytes32, "1"); - } - - #[test] - fn check_public_key_public_key() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.public_key, types.public_key, "1"); - } - - #[test] - fn check_int_int() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.int, types.int, "1"); - } - - #[test] - fn check_bool_bool() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.bool, types.bool, "1"); - } - - #[test] - fn check_nil_nil() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.nil, types.nil, "1"); - } - - #[test] - fn check_bytes_bytes32() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.bytes, types.bytes32, "(= (strlen val) 32)"); - } - - #[test] - fn check_bytes32_bytes() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.bytes32, types.bytes, "1"); - } - - #[test] - fn check_bytes_public_key() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str( - &mut db, - types.bytes, - types.public_key, - "(= (strlen val) 48)", - ); - } - - #[test] - fn check_public_key_bytes() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.public_key, types.bytes, "1"); - } - - #[test] - fn check_bytes_nil() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.bytes, types.nil, "(= val ())"); - } - - #[test] - fn check_nil_bytes() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.nil, types.bytes, "1"); - } - - #[test] - fn check_bytes_bool() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str( - &mut db, - types.bytes, - types.bool, - "(any (= val ()) (= val 1))", - ); - } - - #[test] - fn check_bool_bytes() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.bool, types.bytes, "1"); - } - - #[test] - fn check_bytes32_public_key() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_impossible(&mut db, types.bytes32, types.public_key); - } - - #[test] - fn check_bytes_int() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.bytes, types.int, "1"); - } - - #[test] - fn check_int_bytes() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.int, types.bytes, "1"); - } - - #[test] - fn check_bool_nil() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.bool, types.nil, "(= val ())"); - } - - #[test] - fn check_nil_bool() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.nil, types.bool, "1"); - } - - #[test] - fn check_any_any() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.any, types.any, "1"); - } - - #[test] - fn check_bytes_any() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - check_str(&mut db, types.any, types.any, "1"); - } - - #[test] - fn check_list_nil() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - let list = alloc_list(&mut db, types.bytes); - check_str(&mut db, list, types.nil, "(not (l val))"); - } - - #[test] - fn check_list_pair() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - let list = alloc_list(&mut db, types.bytes); - let pair = db.alloc(Type::Pair(types.bytes, list)); - check_str(&mut db, list, pair, "(l val)"); - } - - #[test] - fn check_any_list() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - let list = alloc_list(&mut db, types.bytes); - check_recursive(&mut db, types.any, list); - } - - #[test] - fn check_any_point() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - let point_end = db.alloc(Type::Pair(types.int, types.nil)); - let point = db.alloc(Type::Pair(types.int, point_end)); - check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) ()))"); - } - - #[test] - fn check_any_point_struct() { - let mut db = TypeSystem::new(); - let types = db.standard_types(); - let point_struct = alloc_struct( - &mut db, - &indexmap! { - "x".to_string() => types.int, - "y".to_string() => types.int, - }, - Rest::Nil, - ); - let point = db.substitute( - point_struct, - HashMap::new(), - Semantics::StructuralOnly { - callable: types.never, - }, - ); - check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) ()))"); - } -} diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs new file mode 100644 index 0000000..6167834 --- /dev/null +++ b/crates/rue-typing/src/check/check_type.rs @@ -0,0 +1,604 @@ +use std::{ + collections::{HashSet, VecDeque}, + hash::BuildHasher, +}; + +use crate::{Comparison, Type, TypeId, TypeSystem}; + +use super::{Check, CheckError}; + +/// Returns [`None`] for recursive checks. +pub(crate) fn check_type( + types: &mut TypeSystem, + lhs: TypeId, + rhs: TypeId, + visited: &mut HashSet<(TypeId, TypeId), S>, +) -> Result +where + S: BuildHasher, +{ + if !visited.insert((lhs, rhs)) { + if types.compare(lhs, rhs) <= Comparison::Castable { + return Ok(Check::None); + } + return Err(CheckError::Recursive(lhs, rhs)); + } + + let check = match (types.get(lhs), types.get(rhs)) { + (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), + (Type::Lazy(..), _) | (_, Type::Lazy(..)) => unreachable!(), + (Type::Alias(..), _) | (_, Type::Alias(..)) => unreachable!(), + (Type::Struct(..), _) | (_, Type::Struct(..)) => unreachable!(), + (Type::Callable(..), _) | (_, Type::Callable(..)) => unreachable!(), + + // TODO: Implement generic type checking? + (Type::Generic, _) => Check::None, + (_, Type::Generic) => Check::None, + + (Type::Unknown, _) => Check::None, + (_, Type::Unknown) => Check::None, + + (Type::Never, _) => Check::None, + (_, Type::Never) => return Err(CheckError::Impossible(lhs, rhs)), + + (Type::Atom, Type::Atom) => Check::None, + (Type::Bytes, Type::Bytes) => Check::None, + (Type::Bytes32, Type::Bytes32) => Check::None, + (Type::PublicKey, Type::PublicKey) => Check::None, + (Type::Int, Type::Int) => Check::None, + (Type::Bool, Type::Bool) => Check::None, + (Type::Nil, Type::Nil) => Check::None, + + (Type::Bytes32, Type::Atom) => Check::None, + (Type::PublicKey, Type::Atom) => Check::None, + (Type::Int, Type::Atom) => Check::None, + (Type::Bytes, Type::Atom) => Check::None, + (Type::Bool, Type::Atom) => Check::None, + (Type::Nil, Type::Atom) => Check::None, + + (Type::Atom, Type::Bytes) => Check::None, + (Type::Bytes32, Type::Bytes) => Check::None, + (Type::PublicKey, Type::Bytes) => Check::None, + (Type::Int, Type::Bytes) => Check::None, + (Type::Bool, Type::Bytes) => Check::None, + (Type::Nil, Type::Bytes) => Check::None, + + (Type::Atom, Type::Int) => Check::None, + (Type::Bytes32, Type::Int) => Check::None, + (Type::PublicKey, Type::Int) => Check::None, + (Type::Bytes, Type::Int) => Check::None, + (Type::Bool, Type::Int) => Check::None, + (Type::Nil, Type::Int) => Check::None, + + (Type::Nil, Type::Bool) => Check::None, + + (Type::Atom, Type::Bool) => Check::IsBool, + (Type::Atom, Type::Nil) => Check::IsNil, + (Type::Atom, Type::PublicKey) => Check::Length(48), + (Type::Atom, Type::Bytes32) => Check::Length(32), + + (Type::Bytes, Type::Bool) => Check::IsBool, + (Type::Bytes, Type::Nil) => Check::IsNil, + (Type::Bytes, Type::PublicKey) => Check::Length(48), + (Type::Bytes, Type::Bytes32) => Check::Length(32), + + (Type::Int, Type::Bool) => Check::IsBool, + (Type::Int, Type::Nil) => Check::IsNil, + (Type::Int, Type::PublicKey) => Check::Length(48), + (Type::Int, Type::Bytes32) => Check::Length(32), + + (Type::Bool, Type::Nil) => Check::IsNil, + + (_, Type::Union(items)) => { + let mut result = Vec::new(); + for item in items.clone() { + result.push(check_type(types, lhs, item, visited)?); + } + Check::Or(result) + } + + (Type::Union(items), _) => { + let items = items.clone(); + check_union_against_rhs(types, lhs, &items, rhs, visited)? + } + + (Type::PublicKey, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bytes32, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Nil, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Nil, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::PublicKey, Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bytes32, Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bool, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bool, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::PublicKey, Type::Bool) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bytes32, Type::Bool) => return Err(CheckError::Impossible(lhs, rhs)), + + (Type::Atom, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bytes, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bytes32, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::PublicKey, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Int, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bool, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Nil, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + + (Type::Pair(..), Type::Atom) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Pair(..), Type::Bytes) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Pair(..), Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Pair(..), Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Pair(..), Type::Int) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Pair(..), Type::Bool) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Pair(..), Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), + + (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { + let (lhs_first, lhs_rest) = (*lhs_first, *lhs_rest); + let (rhs_first, rhs_rest) = (*rhs_first, *rhs_rest); + let first = check_type(types, lhs_first, rhs_first, visited)?; + let rest = check_type(types, lhs_rest, rhs_rest, visited)?; + Check::And(vec![ + Check::First(Box::new(first)), + Check::Rest(Box::new(rest)), + ]) + } + }; + + visited.remove(&(lhs, rhs)); + + Ok(check) +} + +fn check_union_against_rhs( + types: &mut TypeSystem, + original_type_id: TypeId, + items: &[TypeId], + rhs: TypeId, + visited: &mut HashSet<(TypeId, TypeId), S>, +) -> Result +where + S: BuildHasher, +{ + let union = types.alloc(Type::Union(items.to_vec())); + + if types.compare(union, rhs) <= Comparison::Castable { + return Ok(Check::None); + } + + if let Type::Union(union) = types.get(rhs) { + let rhs_items = union.clone(); + let mut result = Vec::new(); + for rhs_item in rhs_items { + if !visited.insert((original_type_id, rhs_item)) { + return Err(CheckError::Recursive(original_type_id, rhs_item)); + } + result.push(check_union_against_rhs( + types, + original_type_id, + items, + rhs_item, + visited, + )?); + } + return Ok(Check::Or(result)); + } + + let mut atom_count = 0; + let mut bool_count = 0; + let mut nil_count = 0; + let mut bytes32_count = 0; + let mut public_key_count = 0; + let mut pairs = Vec::new(); + + let mut items: VecDeque<_> = items.iter().copied().collect::>(); + let mut length = 0; + + while !items.is_empty() { + let item = items.remove(0).unwrap(); + length += 1; + + if !visited.insert((item, rhs)) { + return Err(CheckError::Recursive(item, rhs)); + } + + match types.get(item) { + Type::Ref(..) => unreachable!(), + Type::Lazy(..) => unreachable!(), + Type::Alias(..) => unreachable!(), + Type::Struct(..) => unreachable!(), + Type::Callable(..) => unreachable!(), + Type::Generic => return Err(CheckError::Impossible(item, rhs)), + Type::Unknown => {} + Type::Never => { + length -= 1; + } + Type::Atom | Type::Bytes | Type::Int => { + atom_count += 1; + } + Type::Bytes32 => { + atom_count += 1; + bytes32_count += 1; + } + Type::PublicKey => { + atom_count += 1; + public_key_count += 1; + } + Type::Bool => { + atom_count += 1; + bool_count += 1; + } + Type::Nil => { + atom_count += 1; + nil_count += 1; + bool_count += 1; + } + Type::Pair(first, rest) => { + pairs.push((*first, *rest)); + } + Type::Union(child_items) => { + items.extend(child_items); + } + } + + visited.remove(&(item, rhs)); + } + + Ok(match types.get(rhs) { + Type::Ref(..) => unreachable!(), + Type::Lazy(..) => unreachable!(), + Type::Alias(..) => unreachable!(), + Type::Union(..) => unreachable!(), + Type::Struct(..) => unreachable!(), + Type::Callable(..) => unreachable!(), + Type::Unknown => Check::None, + Type::Generic => return Err(CheckError::Impossible(original_type_id, rhs)), + Type::Never => return Err(CheckError::Impossible(original_type_id, rhs)), + Type::Atom if atom_count == length => Check::None, + Type::Bytes if atom_count == length => Check::None, + Type::Int if atom_count == length => Check::None, + Type::Bool if bool_count == length => Check::None, + Type::Nil if nil_count == length => Check::None, + Type::Bytes32 if bytes32_count == length => Check::None, + Type::PublicKey if public_key_count == length => Check::None, + Type::Bytes32 if atom_count == length => Check::Length(32), + Type::PublicKey if atom_count == length => Check::Length(48), + Type::Bool if atom_count == length => Check::IsBool, + Type::Nil if atom_count == length => Check::IsNil, + Type::Atom => Check::IsAtom, + Type::Bytes => Check::IsAtom, + Type::Int => Check::IsAtom, + Type::Bytes32 if bytes32_count == atom_count => Check::IsAtom, + Type::PublicKey if public_key_count == atom_count => Check::IsAtom, + Type::Bool if bool_count == atom_count => Check::IsAtom, + Type::Nil if nil_count == atom_count => Check::IsAtom, + Type::Bytes32 => Check::And(vec![Check::IsAtom, Check::Length(32)]), + Type::PublicKey => Check::And(vec![Check::IsAtom, Check::Length(48)]), + Type::Bool => Check::And(vec![Check::IsAtom, Check::IsBool]), + Type::Nil => Check::And(vec![Check::IsAtom, Check::IsNil]), + Type::Pair(..) if atom_count == length => { + return Err(CheckError::Impossible(original_type_id, rhs)) + } + Type::Pair(first, rest) => { + let (first, rest) = (*first, *rest); + + let first_items: Vec<_> = pairs.iter().map(|(first, _)| *first).collect(); + let rest_items: Vec<_> = pairs.iter().map(|(_, rest)| *rest).collect(); + + let first = + check_union_against_rhs(types, original_type_id, &first_items, first, visited)?; + let rest = + check_union_against_rhs(types, original_type_id, &rest_items, rest, visited)?; + + if pairs.len() == length { + Check::And(vec![ + Check::First(Box::new(first)), + Check::Rest(Box::new(rest)), + ]) + } else { + Check::And(vec![ + Check::IsPair, + Check::First(Box::new(first)), + Check::Rest(Box::new(rest)), + ]) + } + } + }) +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use indexmap::indexmap; + + use crate::{alloc_list, alloc_struct, Rest, Semantics}; + + use super::*; + + fn check_str(db: &mut TypeSystem, lhs: TypeId, rhs: TypeId, expected: &str) { + assert_eq!(format!("{}", db.check(lhs, rhs).unwrap()), expected); + } + + fn check_recursive(db: &mut TypeSystem, lhs: TypeId, rhs: TypeId) { + assert!(matches!(db.check(lhs, rhs), Err(CheckError::Recursive(..)))); + } + + fn check_impossible(db: &mut TypeSystem, lhs: TypeId, rhs: TypeId) { + assert!(matches!( + db.check(lhs, rhs), + Err(CheckError::Impossible(..)) + )); + } + + #[test] + fn check_any_bytes() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.any, types.bytes, "(not (l val))"); + } + + #[test] + fn check_any_bytes32() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str( + &mut db, + types.any, + types.bytes32, + "(and (not (l val)) (= (strlen val) 32))", + ); + } + + #[test] + fn check_any_public_key() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str( + &mut db, + types.any, + types.public_key, + "(and (not (l val)) (= (strlen val) 48))", + ); + } + + #[test] + fn check_any_int() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.any, types.int, "(not (l val))"); + } + + #[test] + fn check_any_bool() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str( + &mut db, + types.any, + types.bool, + "(and (not (l val)) (any (= val ()) (= val 1)))", + ); + } + + #[test] + fn check_any_nil() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str( + &mut db, + types.any, + types.nil, + "(and (not (l val)) (= val ()))", + ); + } + + #[test] + fn check_bytes_bytes() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.bytes, types.bytes, "1"); + } + + #[test] + fn check_bytes32_bytes32() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.bytes32, types.bytes32, "1"); + } + + #[test] + fn check_public_key_public_key() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.public_key, types.public_key, "1"); + } + + #[test] + fn check_int_int() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.int, types.int, "1"); + } + + #[test] + fn check_bool_bool() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.bool, types.bool, "1"); + } + + #[test] + fn check_nil_nil() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.nil, types.nil, "1"); + } + + #[test] + fn check_bytes_bytes32() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.bytes, types.bytes32, "(= (strlen val) 32)"); + } + + #[test] + fn check_bytes32_bytes() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.bytes32, types.bytes, "1"); + } + + #[test] + fn check_bytes_public_key() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str( + &mut db, + types.bytes, + types.public_key, + "(= (strlen val) 48)", + ); + } + + #[test] + fn check_public_key_bytes() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.public_key, types.bytes, "1"); + } + + #[test] + fn check_bytes_nil() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.bytes, types.nil, "(= val ())"); + } + + #[test] + fn check_nil_bytes() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.nil, types.bytes, "1"); + } + + #[test] + fn check_bytes_bool() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str( + &mut db, + types.bytes, + types.bool, + "(any (= val ()) (= val 1))", + ); + } + + #[test] + fn check_bool_bytes() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.bool, types.bytes, "1"); + } + + #[test] + fn check_bytes32_public_key() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_impossible(&mut db, types.bytes32, types.public_key); + } + + #[test] + fn check_bytes_int() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.bytes, types.int, "1"); + } + + #[test] + fn check_int_bytes() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.int, types.bytes, "1"); + } + + #[test] + fn check_bool_nil() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.bool, types.nil, "(= val ())"); + } + + #[test] + fn check_nil_bool() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.nil, types.bool, "1"); + } + + #[test] + fn check_any_any() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.any, types.any, "1"); + } + + #[test] + fn check_bytes_any() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + check_str(&mut db, types.any, types.any, "1"); + } + + #[test] + fn check_list_nil() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + let list = alloc_list(&mut db, types.bytes); + check_str(&mut db, list, types.nil, "(not (l val))"); + } + + #[test] + fn check_list_pair() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + let list = alloc_list(&mut db, types.bytes); + let pair = db.alloc(Type::Pair(types.bytes, list)); + check_str(&mut db, list, pair, "(l val)"); + } + + #[test] + fn check_any_list() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + let list = alloc_list(&mut db, types.bytes); + check_recursive(&mut db, types.any, list); + } + + #[test] + fn check_any_point() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + let point_end = db.alloc(Type::Pair(types.int, types.nil)); + let point = db.alloc(Type::Pair(types.int, point_end)); + check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) ()))"); + } + + #[test] + fn check_any_point_struct() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + let point_struct = alloc_struct( + &mut db, + &indexmap! { + "x".to_string() => types.int, + "y".to_string() => types.int, + }, + Rest::Nil, + ); + let point = db.substitute( + point_struct, + HashMap::new(), + Semantics::StructuralOnly { + callable: types.never, + }, + ); + check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) ()))"); + } +} From cdae35337447bb58789c2773cec2cd42ed840ac4 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 00:46:00 -0400 Subject: [PATCH 042/100] Value check --- Cargo.lock | 1 + crates/rue-typing/Cargo.toml | 1 + crates/rue-typing/src/check.rs | 3 +- crates/rue-typing/src/check/check_type.rs | 28 ++++++++++--------- crates/rue-typing/src/check/simplify_and.rs | 6 ++-- crates/rue-typing/src/check/simplify_check.rs | 6 ++-- crates/rue-typing/src/check/simplify_or.rs | 6 ++-- .../rue-typing/src/check/stringify_check.rs | 10 ++++--- 8 files changed, 36 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e99dada..091ee3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1462,6 +1462,7 @@ dependencies = [ "anyhow", "id-arena", "indexmap", + "num-bigint", "thiserror", ] diff --git a/crates/rue-typing/Cargo.toml b/crates/rue-typing/Cargo.toml index 237ae0e..3743b57 100644 --- a/crates/rue-typing/Cargo.toml +++ b/crates/rue-typing/Cargo.toml @@ -18,6 +18,7 @@ workspace = true id-arena = { workspace = true } thiserror = { workspace = true } indexmap = { workspace = true } +num-bigint = { workspace = true } [dev-dependencies] anyhow = { workspace = true } diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index db8c73d..b111ae6 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -10,6 +10,7 @@ mod stringify_check; pub use check_error::*; pub(crate) use check_type::*; +use num_bigint::BigInt; pub(crate) use simplify_and::*; pub(crate) use simplify_check::*; pub(crate) use simplify_or::*; @@ -21,7 +22,7 @@ pub enum Check { IsPair, IsAtom, IsBool, - IsNil, + Value(BigInt), Length(usize), And(Vec), Or(Vec), diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs index 6167834..0d8e457 100644 --- a/crates/rue-typing/src/check/check_type.rs +++ b/crates/rue-typing/src/check/check_type.rs @@ -3,6 +3,8 @@ use std::{ hash::BuildHasher, }; +use num_bigint::BigInt; + use crate::{Comparison, Type, TypeId, TypeSystem}; use super::{Check, CheckError}; @@ -73,21 +75,21 @@ where (Type::Nil, Type::Bool) => Check::None, (Type::Atom, Type::Bool) => Check::IsBool, - (Type::Atom, Type::Nil) => Check::IsNil, + (Type::Atom, Type::Nil) => Check::Value(BigInt::ZERO), (Type::Atom, Type::PublicKey) => Check::Length(48), (Type::Atom, Type::Bytes32) => Check::Length(32), (Type::Bytes, Type::Bool) => Check::IsBool, - (Type::Bytes, Type::Nil) => Check::IsNil, + (Type::Bytes, Type::Nil) => Check::Value(BigInt::ZERO), (Type::Bytes, Type::PublicKey) => Check::Length(48), (Type::Bytes, Type::Bytes32) => Check::Length(32), (Type::Int, Type::Bool) => Check::IsBool, - (Type::Int, Type::Nil) => Check::IsNil, + (Type::Int, Type::Nil) => Check::Value(BigInt::ZERO), (Type::Int, Type::PublicKey) => Check::Length(48), (Type::Int, Type::Bytes32) => Check::Length(32), - (Type::Bool, Type::Nil) => Check::IsNil, + (Type::Bool, Type::Nil) => Check::Value(BigInt::ZERO), (_, Type::Union(items)) => { let mut result = Vec::new(); @@ -260,7 +262,7 @@ where Type::Bytes32 if atom_count == length => Check::Length(32), Type::PublicKey if atom_count == length => Check::Length(48), Type::Bool if atom_count == length => Check::IsBool, - Type::Nil if atom_count == length => Check::IsNil, + Type::Nil if atom_count == length => Check::Value(BigInt::ZERO), Type::Atom => Check::IsAtom, Type::Bytes => Check::IsAtom, Type::Int => Check::IsAtom, @@ -271,7 +273,7 @@ where Type::Bytes32 => Check::And(vec![Check::IsAtom, Check::Length(32)]), Type::PublicKey => Check::And(vec![Check::IsAtom, Check::Length(48)]), Type::Bool => Check::And(vec![Check::IsAtom, Check::IsBool]), - Type::Nil => Check::And(vec![Check::IsAtom, Check::IsNil]), + Type::Nil => Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]), Type::Pair(..) if atom_count == length => { return Err(CheckError::Impossible(original_type_id, rhs)) } @@ -373,7 +375,7 @@ mod tests { &mut db, types.any, types.bool, - "(and (not (l val)) (any (= val ()) (= val 1)))", + "(and (not (l val)) (any (= val 0) (= val 1)))", ); } @@ -385,7 +387,7 @@ mod tests { &mut db, types.any, types.nil, - "(and (not (l val)) (= val ()))", + "(and (not (l val)) (= val 0))", ); } @@ -468,7 +470,7 @@ mod tests { fn check_bytes_nil() { let mut db = TypeSystem::new(); let types = db.standard_types(); - check_str(&mut db, types.bytes, types.nil, "(= val ())"); + check_str(&mut db, types.bytes, types.nil, "(= val 0)"); } #[test] @@ -486,7 +488,7 @@ mod tests { &mut db, types.bytes, types.bool, - "(any (= val ()) (= val 1))", + "(any (= val 0) (= val 1))", ); } @@ -522,7 +524,7 @@ mod tests { fn check_bool_nil() { let mut db = TypeSystem::new(); let types = db.standard_types(); - check_str(&mut db, types.bool, types.nil, "(= val ())"); + check_str(&mut db, types.bool, types.nil, "(= val 0)"); } #[test] @@ -577,7 +579,7 @@ mod tests { let types = db.standard_types(); let point_end = db.alloc(Type::Pair(types.int, types.nil)); let point = db.alloc(Type::Pair(types.int, point_end)); - check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) ()))"); + check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) 0))"); } #[test] @@ -599,6 +601,6 @@ mod tests { callable: types.never, }, ); - check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) ()))"); + check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) 0))"); } } diff --git a/crates/rue-typing/src/check/simplify_and.rs b/crates/rue-typing/src/check/simplify_and.rs index e22a680..3f002ce 100644 --- a/crates/rue-typing/src/check/simplify_and.rs +++ b/crates/rue-typing/src/check/simplify_and.rs @@ -23,7 +23,7 @@ pub(crate) fn simplify_and_shallow(items: impl IntoIterator) -> Ch let mut is_atom = false; let mut is_pair = false; let mut is_bool = false; - let mut is_nil = false; + let mut value = false; let mut length = false; for item in items { @@ -35,8 +35,8 @@ pub(crate) fn simplify_and_shallow(items: impl IntoIterator) -> Ch Check::IsPair => is_pair = true, Check::IsBool if is_bool => continue, Check::IsBool => is_bool = true, - Check::IsNil if is_nil => continue, - Check::IsNil => is_nil = true, + Check::Value(..) if value => continue, + Check::Value(..) => value = true, Check::Length(..) if length => continue, Check::Length(..) => length = true, _ => {} diff --git a/crates/rue-typing/src/check/simplify_check.rs b/crates/rue-typing/src/check/simplify_check.rs index 5d2d24b..ab4924f 100644 --- a/crates/rue-typing/src/check/simplify_check.rs +++ b/crates/rue-typing/src/check/simplify_check.rs @@ -1,3 +1,5 @@ +use num_bigint::BigInt; + use super::{simplify_and_deep, simplify_or_deep, Check}; pub(crate) fn simplify_check(check: Check) -> Check { @@ -6,10 +8,10 @@ pub(crate) fn simplify_check(check: Check) -> Check { Check::IsAtom => Check::IsAtom, Check::IsPair => Check::IsPair, Check::IsBool => Check::IsBool, - Check::IsNil => Check::IsNil, + Check::Value(value) => Check::Value(value), Check::Length(len) => { if len == 0 { - Check::IsNil + Check::Value(BigInt::ZERO) } else { Check::Length(len) } diff --git a/crates/rue-typing/src/check/simplify_or.rs b/crates/rue-typing/src/check/simplify_or.rs index a045116..cd5f394 100644 --- a/crates/rue-typing/src/check/simplify_or.rs +++ b/crates/rue-typing/src/check/simplify_or.rs @@ -149,6 +149,8 @@ fn construct_or(mut items: Vec) -> Check { #[cfg(test)] mod tests { + use num_bigint::BigInt; + use super::*; #[test] @@ -185,8 +187,8 @@ mod tests { #[test] fn test_simplify_or_two_checks() { assert_eq!( - simplify_or_shallow([Check::IsPair, Check::IsNil]), - Check::Or(vec![Check::IsNil, Check::IsPair]) + simplify_or_shallow([Check::IsPair, Check::Value(BigInt::ZERO)]), + Check::Or(vec![Check::Value(BigInt::ZERO), Check::IsPair]) ); } diff --git a/crates/rue-typing/src/check/stringify_check.rs b/crates/rue-typing/src/check/stringify_check.rs index 963957e..682c803 100644 --- a/crates/rue-typing/src/check/stringify_check.rs +++ b/crates/rue-typing/src/check/stringify_check.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::fmt::{self, Display}; use crate::TypePath; @@ -38,14 +38,16 @@ pub(crate) fn stringify_check( Check::IsBool => { write!(f, "(any (= ")?; stringify_value(f, path)?; - write!(f, " ()) (= ")?; + write!(f, " 0) (= ")?; stringify_value(f, path)?; write!(f, " 1))") } - Check::IsNil => { + Check::Value(value) => { write!(f, "(= ")?; stringify_value(f, path)?; - write!(f, " ())") + write!(f, " ")?; + value.fmt(f)?; + write!(f, ")") } Check::Length(len) => { write!(f, "(= (strlen ")?; From cb3a7d08a88ea296192aada5d93865a6106e8031 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 00:46:39 -0400 Subject: [PATCH 043/100] Organize import --- crates/rue-typing/src/check.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index b111ae6..e6b465e 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -10,12 +10,13 @@ mod stringify_check; pub use check_error::*; pub(crate) use check_type::*; -use num_bigint::BigInt; pub(crate) use simplify_and::*; pub(crate) use simplify_check::*; pub(crate) use simplify_or::*; pub(crate) use stringify_check::*; +use num_bigint::BigInt; + #[derive(Debug, Clone, PartialEq, Eq)] pub enum Check { None, From 24758e228bab17623e41fd3310b1c98c3c71a1d1 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 00:50:31 -0400 Subject: [PATCH 044/100] Rename tests --- crates/rue-typing/src/check/check_type.rs | 64 +++++++++++------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs index 0d8e457..266cec2 100644 --- a/crates/rue-typing/src/check/check_type.rs +++ b/crates/rue-typing/src/check/check_type.rs @@ -330,14 +330,14 @@ mod tests { } #[test] - fn check_any_bytes() { + fn test_check_any_bytes() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.any, types.bytes, "(not (l val))"); } #[test] - fn check_any_bytes32() { + fn test_check_any_bytes32() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str( @@ -349,7 +349,7 @@ mod tests { } #[test] - fn check_any_public_key() { + fn test_check_any_public_key() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str( @@ -361,14 +361,14 @@ mod tests { } #[test] - fn check_any_int() { + fn test_check_any_int() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.any, types.int, "(not (l val))"); } #[test] - fn check_any_bool() { + fn test_check_any_bool() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str( @@ -380,7 +380,7 @@ mod tests { } #[test] - fn check_any_nil() { + fn test_check_any_nil() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str( @@ -392,63 +392,63 @@ mod tests { } #[test] - fn check_bytes_bytes() { + fn test_check_bytes_bytes() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.bytes, types.bytes, "1"); } #[test] - fn check_bytes32_bytes32() { + fn test_check_bytes32_bytes32() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.bytes32, types.bytes32, "1"); } #[test] - fn check_public_key_public_key() { + fn test_check_public_key_public_key() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.public_key, types.public_key, "1"); } #[test] - fn check_int_int() { + fn test_check_int_int() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.int, types.int, "1"); } #[test] - fn check_bool_bool() { + fn test_check_bool_bool() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.bool, types.bool, "1"); } #[test] - fn check_nil_nil() { + fn test_check_nil_nil() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.nil, types.nil, "1"); } #[test] - fn check_bytes_bytes32() { + fn test_check_bytes_bytes32() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.bytes, types.bytes32, "(= (strlen val) 32)"); } #[test] - fn check_bytes32_bytes() { + fn test_check_bytes32_bytes() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.bytes32, types.bytes, "1"); } #[test] - fn check_bytes_public_key() { + fn test_check_bytes_public_key() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str( @@ -460,28 +460,28 @@ mod tests { } #[test] - fn check_public_key_bytes() { + fn test_check_public_key_bytes() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.public_key, types.bytes, "1"); } #[test] - fn check_bytes_nil() { + fn test_check_bytes_nil() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.bytes, types.nil, "(= val 0)"); } #[test] - fn check_nil_bytes() { + fn test_check_nil_bytes() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.nil, types.bytes, "1"); } #[test] - fn check_bytes_bool() { + fn test_check_bytes_bool() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str( @@ -493,63 +493,63 @@ mod tests { } #[test] - fn check_bool_bytes() { + fn test_check_bool_bytes() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.bool, types.bytes, "1"); } #[test] - fn check_bytes32_public_key() { + fn test_check_bytes32_public_key() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_impossible(&mut db, types.bytes32, types.public_key); } #[test] - fn check_bytes_int() { + fn test_check_bytes_int() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.bytes, types.int, "1"); } #[test] - fn check_int_bytes() { + fn test_check_int_bytes() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.int, types.bytes, "1"); } #[test] - fn check_bool_nil() { + fn test_check_bool_nil() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.bool, types.nil, "(= val 0)"); } #[test] - fn check_nil_bool() { + fn test_check_nil_bool() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.nil, types.bool, "1"); } #[test] - fn check_any_any() { + fn test_check_any_any() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.any, types.any, "1"); } #[test] - fn check_bytes_any() { + fn test_check_bytes_any() { let mut db = TypeSystem::new(); let types = db.standard_types(); check_str(&mut db, types.any, types.any, "1"); } #[test] - fn check_list_nil() { + fn test_check_list_nil() { let mut db = TypeSystem::new(); let types = db.standard_types(); let list = alloc_list(&mut db, types.bytes); @@ -557,7 +557,7 @@ mod tests { } #[test] - fn check_list_pair() { + fn test_check_list_pair() { let mut db = TypeSystem::new(); let types = db.standard_types(); let list = alloc_list(&mut db, types.bytes); @@ -566,7 +566,7 @@ mod tests { } #[test] - fn check_any_list() { + fn test_check_any_list() { let mut db = TypeSystem::new(); let types = db.standard_types(); let list = alloc_list(&mut db, types.bytes); @@ -574,7 +574,7 @@ mod tests { } #[test] - fn check_any_point() { + fn test_check_any_point() { let mut db = TypeSystem::new(); let types = db.standard_types(); let point_end = db.alloc(Type::Pair(types.int, types.nil)); @@ -583,7 +583,7 @@ mod tests { } #[test] - fn check_any_point_struct() { + fn test_check_any_point_struct() { let mut db = TypeSystem::new(); let types = db.standard_types(); let point_struct = alloc_struct( From 430d6390e2b3deaa3ffde231aa82e63a2c07c16b Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 00:51:42 -0400 Subject: [PATCH 045/100] Move --- .../rue-typing/src/check/stringify_check.rs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/rue-typing/src/check/stringify_check.rs b/crates/rue-typing/src/check/stringify_check.rs index 682c803..e747732 100644 --- a/crates/rue-typing/src/check/stringify_check.rs +++ b/crates/rue-typing/src/check/stringify_check.rs @@ -4,20 +4,6 @@ use crate::TypePath; use super::Check; -fn stringify_value(f: &mut fmt::Formatter<'_>, path: &[TypePath]) -> fmt::Result { - for path in path.iter().rev() { - match path { - TypePath::First => write!(f, "(f ")?, - TypePath::Rest => write!(f, "(r ")?, - } - } - write!(f, "val")?; - for _ in 0..path.len() { - write!(f, ")")?; - } - Ok(()) -} - pub(crate) fn stringify_check( check: &Check, f: &mut fmt::Formatter<'_>, @@ -93,3 +79,17 @@ pub(crate) fn stringify_check( } } } + +fn stringify_value(f: &mut fmt::Formatter<'_>, path: &[TypePath]) -> fmt::Result { + for path in path.iter().rev() { + match path { + TypePath::First => write!(f, "(f ")?, + TypePath::Rest => write!(f, "(r ")?, + } + } + write!(f, "val")?; + for _ in 0..path.len() { + write!(f, ")")?; + } + Ok(()) +} From 2f251ae747974bc7e98c95c3b63e58da84dce642 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 00:55:06 -0400 Subject: [PATCH 046/100] Fix callable --- crates/rue-typing/src/replace_type.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/crates/rue-typing/src/replace_type.rs b/crates/rue-typing/src/replace_type.rs index 806a519..cfb978a 100644 --- a/crates/rue-typing/src/replace_type.rs +++ b/crates/rue-typing/src/replace_type.rs @@ -1,4 +1,4 @@ -use crate::{Type, TypeId, TypePath, TypeSystem}; +use crate::{Alias, Struct, Type, TypeId, TypePath, TypeSystem}; pub(crate) fn replace_type( types: &mut TypeSystem, @@ -15,6 +15,26 @@ pub(crate) fn replace_type( TypePath::First => replace_type(types, *first, replace_type_id, &path[1..]), TypePath::Rest => replace_type(types, *rest, replace_type_id, &path[1..]), }, + Type::Alias(alias) => { + let alias = alias.clone(); + let new_type_id = replace_type(types, alias.type_id, replace_type_id, path); + types.alloc(Type::Alias(Alias { + original_type_id: Some(alias.original_type_id.unwrap_or(type_id)), + type_id: new_type_id, + generic_types: alias.generic_types, + })) + } + Type::Struct(ty) => { + let ty = ty.clone(); + let new_type_id = replace_type(types, ty.type_id, replace_type_id, path); + types.alloc(Type::Struct(Struct { + original_type_id: Some(ty.original_type_id.unwrap_or(type_id)), + type_id: new_type_id, + field_names: ty.field_names, + rest: ty.rest, + generic_types: ty.generic_types, + })) + } _ => type_id, } } From 4ad0e11b96369a75b4c9869ed9b989349c4ce05a Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 01:18:03 -0400 Subject: [PATCH 047/100] Temp --- Cargo.lock | 2 + crates/rue-typing/Cargo.toml | 2 + crates/rue-typing/src/bigint.rs | 9 ++ crates/rue-typing/src/comparison.rs | 118 +++++++++++++++++++++++- crates/rue-typing/src/difference.rs | 29 +++++- crates/rue-typing/src/lib.rs | 2 + crates/rue-typing/src/semantic_types.rs | 26 ++++++ crates/rue-typing/src/standard_types.rs | 2 + crates/rue-typing/src/ty.rs | 6 +- crates/rue-typing/src/type_system.rs | 4 + 10 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 crates/rue-typing/src/bigint.rs diff --git a/Cargo.lock b/Cargo.lock index 091ee3b..831c0fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1460,9 +1460,11 @@ name = "rue-typing" version = "0.1.1" dependencies = [ "anyhow", + "clvmr 0.6.1", "id-arena", "indexmap", "num-bigint", + "num-traits", "thiserror", ] diff --git a/crates/rue-typing/Cargo.toml b/crates/rue-typing/Cargo.toml index 3743b57..10602ee 100644 --- a/crates/rue-typing/Cargo.toml +++ b/crates/rue-typing/Cargo.toml @@ -19,6 +19,8 @@ id-arena = { workspace = true } thiserror = { workspace = true } indexmap = { workspace = true } num-bigint = { workspace = true } +num-traits = { workspace = true } +clvmr = { workspace = true } [dev-dependencies] anyhow = { workspace = true } diff --git a/crates/rue-typing/src/bigint.rs b/crates/rue-typing/src/bigint.rs new file mode 100644 index 0000000..55ed663 --- /dev/null +++ b/crates/rue-typing/src/bigint.rs @@ -0,0 +1,9 @@ +use clvmr::Allocator; +use num_bigint::BigInt; + +pub fn bigint_to_bytes(value: BigInt) -> Vec { + let mut allocator = Allocator::new(); + let ptr = allocator.new_number(value).unwrap(); + let atom = allocator.atom(ptr); + atom.as_ref().to_vec() +} diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index 8de3a84..b71305f 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -3,7 +3,10 @@ use std::{ collections::{HashMap, HashSet}, }; -use crate::{Type, TypeId, TypeSystem}; +use num_bigint::BigInt; +use num_traits::One; + +use crate::{bigint_to_bytes, Type, TypeId, TypeSystem}; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Comparison { @@ -123,6 +126,8 @@ pub(crate) fn compare_type( } (Type::Pair(..), _) | (_, Type::Pair(..)) => Comparison::Incompatible, + (Type::True, Type::True) => Comparison::Equal, + (Type::False, Type::False) => Comparison::Equal, (Type::Atom, Type::Atom) => Comparison::Equal, (Type::Bytes, Type::Bytes) => Comparison::Equal, (Type::Bytes32, Type::Bytes32) => Comparison::Equal, @@ -178,6 +183,117 @@ pub(crate) fn compare_type( (Type::Nil, Type::Bytes32) => Comparison::Incompatible, (Type::Nil, Type::PublicKey) => Comparison::Incompatible, + (Type::True, Type::False) => Comparison::Incompatible, + (Type::False, Type::True) => Comparison::Incompatible, + + (Type::True, Type::Bool) => Comparison::Assignable, + (Type::False, Type::Bool) => Comparison::Assignable, + (Type::True, Type::Bytes) => Comparison::Castable, + (Type::False, Type::Bytes) => Comparison::Castable, + (Type::True, Type::Int) => Comparison::Castable, + (Type::False, Type::Int) => Comparison::Castable, + (Type::True, Type::Atom) => Comparison::Assignable, + (Type::False, Type::Atom) => Comparison::Assignable, + (Type::True, Type::Nil) => Comparison::Incompatible, + (Type::False, Type::Nil) => Comparison::Castable, + (Type::True, Type::Bytes32) => Comparison::Incompatible, + (Type::False, Type::Bytes32) => Comparison::Incompatible, + (Type::True, Type::PublicKey) => Comparison::Incompatible, + (Type::False, Type::PublicKey) => Comparison::Incompatible, + + (Type::Bool, Type::True) => Comparison::Superset, + (Type::Bool, Type::False) => Comparison::Superset, + (Type::Bytes, Type::True) => Comparison::Superset, + (Type::Bytes, Type::False) => Comparison::Superset, + (Type::Int, Type::True) => Comparison::Superset, + (Type::Int, Type::False) => Comparison::Superset, + (Type::Atom, Type::True) => Comparison::Superset, + (Type::Atom, Type::False) => Comparison::Superset, + (Type::Nil, Type::True) => Comparison::Incompatible, + (Type::Nil, Type::False) => Comparison::Castable, + (Type::Bytes32, Type::True) => Comparison::Incompatible, + (Type::Bytes32, Type::False) => Comparison::Incompatible, + (Type::PublicKey, Type::True) => Comparison::Incompatible, + (Type::PublicKey, Type::False) => Comparison::Incompatible, + + (Type::Value(..), Type::Atom) => Comparison::Assignable, + (Type::Value(..), Type::Bytes) => Comparison::Castable, + (Type::Value(..), Type::Int) => Comparison::Assignable, + (Type::Value(value), Type::Bytes32) => { + if bigint_to_bytes(value.clone()).len() == 32 { + Comparison::Castable + } else { + Comparison::Incompatible + } + } + (Type::Value(value), Type::PublicKey) => { + if bigint_to_bytes(value.clone()).len() == 48 { + Comparison::Castable + } else { + Comparison::Incompatible + } + } + (Type::Value(value), Type::Nil) => { + if value == &BigInt::ZERO { + Comparison::Castable + } else { + Comparison::Incompatible + } + } + (Type::Value(value), Type::True) => { + if value == &BigInt::one() { + Comparison::Castable + } else { + Comparison::Incompatible + } + } + (Type::Value(value), Type::Bool) => { + if value == &BigInt::ZERO || value == &BigInt::one() { + Comparison::Castable + } else { + Comparison::Incompatible + } + } + + (Type::Atom, Type::Value(..)) => Comparison::Superset, + (Type::Bytes, Type::Value(..)) => Comparison::Superset, + (Type::Int, Type::Value(..)) => Comparison::Superset, + (Type::Bytes32, Type::Value(value)) => { + if bigint_to_bytes(value.clone()).len() == 32 { + Comparison::Superset + } else { + Comparison::Incompatible + } + } + (Type::PublicKey, Type::Value(value)) => { + if bigint_to_bytes(value.clone()).len() == 48 { + Comparison::Superset + } else { + Comparison::Incompatible + } + } + (Type::Nil, Type::Value(value)) => { + if value == &BigInt::ZERO { + Comparison::Castable + } else { + Comparison::Incompatible + } + } + (Type::Bool, Type::Value(value)) => { + if value == &BigInt::ZERO || value == &BigInt::one() { + Comparison::Castable + } else { + Comparison::Incompatible + } + } + (Type::Value(lhs), Type::Value(rhs)) => { + if lhs == rhs { + Comparison::Equal + } else { + Comparison::Incompatible + } + } + (Type::Lazy(lazy), _) => { ctx.substitution_stack.push(lazy.substitutions.clone()); let result = compare_type(db, lazy.type_id, rhs, ctx); diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index 9930075..4f0ec57 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -1,5 +1,8 @@ use std::collections::HashSet; +use num_bigint::BigInt; +use num_traits::One; + use crate::{StandardTypes, Struct, Type, TypeId, TypeSystem}; pub(crate) fn difference_type( @@ -71,7 +74,7 @@ pub(crate) fn difference_type( (Type::Bool, Type::Bytes) => std.never, (Type::Bool, Type::Atom) => std.never, (Type::Bool, Type::Int) => std.never, - (Type::Bool, Type::Nil) => lhs, + (Type::Bool, Type::Nil) => types.alloc(Type::Value(BigInt::one())), (Type::Nil, Type::Bytes32) => lhs, (Type::Nil, Type::PublicKey) => lhs, @@ -80,6 +83,30 @@ pub(crate) fn difference_type( (Type::Nil, Type::Int) => std.never, (Type::Nil, Type::Bool) => std.never, + (Type::Nil, Type::Value(value)) => { + if value == &BigInt::ZERO { + std.never + } else { + lhs + } + } + (Type::Bool, Type::Value(value)) => { + if value == &BigInt::ZERO { + types.standard_types().true_bool + } else if value == &BigInt::one() { + types.standard_types().false_bool + } else { + lhs + } + } + (Type::Value(lhs_value), Type::Value(rhs_value)) => { + if lhs_value == rhs_value { + std.never + } else { + lhs + } + } + (Type::Atom, Type::Pair(..)) => lhs, (Type::Bytes, Type::Pair(..)) => lhs, (Type::Bytes32, Type::Pair(..)) => lhs, diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index 88d5e93..3682658 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -1,3 +1,4 @@ +mod bigint; mod check; mod comparison; mod difference; @@ -11,6 +12,7 @@ mod ty; mod type_path; mod type_system; +pub use bigint::*; pub use check::*; pub use comparison::*; pub use semantic_types::*; diff --git a/crates/rue-typing/src/semantic_types.rs b/crates/rue-typing/src/semantic_types.rs index b2c9971..e389105 100644 --- a/crates/rue-typing/src/semantic_types.rs +++ b/crates/rue-typing/src/semantic_types.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use indexmap::IndexSet; +use num_bigint::BigInt; use crate::TypeId; @@ -53,3 +54,28 @@ pub struct Callable { pub rest: Rest, pub generic_types: Vec, } + +/// Represents an enum type which can have multiple variants. +#[derive(Debug, Clone)] +pub struct Enum { + pub original_variants: Vec, + pub has_fields: bool, +} + +/// Represents a variant type which can optionally have fields. +#[derive(Debug, Clone)] +pub struct Variant { + pub original_type_id: Option, + pub enum_type: TypeId, + pub field_names: IndexSet, + pub fields: Option, + pub generic_types: Vec, + pub discriminant: BigInt, +} + +/// Field information for a variant type. +#[derive(Debug, Clone)] +pub struct VariantFields { + pub fields: TypeId, + pub rest: Rest, +} diff --git a/crates/rue-typing/src/standard_types.rs b/crates/rue-typing/src/standard_types.rs index 9d853d9..e61ccff 100644 --- a/crates/rue-typing/src/standard_types.rs +++ b/crates/rue-typing/src/standard_types.rs @@ -11,5 +11,7 @@ pub struct StandardTypes { pub public_key: TypeId, pub int: TypeId, pub bool: TypeId, + pub true_bool: TypeId, + pub false_bool: TypeId, pub nil: TypeId, } diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs index cfce009..6bd2d60 100644 --- a/crates/rue-typing/src/ty.rs +++ b/crates/rue-typing/src/ty.rs @@ -1,3 +1,5 @@ +use num_bigint::BigInt; + use crate::{Alias, Callable, Lazy, Struct, TypeId}; #[derive(Debug, Clone)] @@ -10,8 +12,10 @@ pub enum Type { Bytes32, PublicKey, Int, - Bool, + True, + False, Nil, + Value(BigInt), Pair(TypeId, TypeId), Union(Vec), Ref(TypeId), diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index 7d71333..12eba0b 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -28,6 +28,8 @@ impl Default for TypeSystem { let public_key = arena.alloc(Type::PublicKey); let int = arena.alloc(Type::Int); let bool = arena.alloc(Type::Bool); + let true_bool = arena.alloc(Type::True); + let false_bool = arena.alloc(Type::False); let nil = arena.alloc(Type::Nil); let any = arena.alloc(Type::Unknown); @@ -46,6 +48,8 @@ impl Default for TypeSystem { public_key, int, bool, + true_bool, + false_bool, nil, }, } From e4004d5ab38920e1205b771304a94a5bed5d3e85 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 01:44:35 -0400 Subject: [PATCH 048/100] Temp 2 --- crates/rue-typing/src/check.rs | 1 - crates/rue-typing/src/check/check_type.rs | 232 ++++++++++++++---- crates/rue-typing/src/check/simplify_and.rs | 3 - crates/rue-typing/src/check/simplify_check.rs | 1 - .../rue-typing/src/check/stringify_check.rs | 7 - crates/rue-typing/src/comparison.rs | 32 +-- crates/rue-typing/src/difference.rs | 113 +++++++-- crates/rue-typing/src/semantic_types.rs | 2 +- crates/rue-typing/src/stringify.rs | 12 +- crates/rue-typing/src/substitute_type.rs | 4 +- crates/rue-typing/src/type_system.rs | 27 +- 11 files changed, 316 insertions(+), 118 deletions(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index e6b465e..0073913 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -22,7 +22,6 @@ pub enum Check { None, IsPair, IsAtom, - IsBool, Value(BigInt), Length(usize), And(Vec), diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs index 266cec2..e393107 100644 --- a/crates/rue-typing/src/check/check_type.rs +++ b/crates/rue-typing/src/check/check_type.rs @@ -1,11 +1,12 @@ use std::{ - collections::{HashSet, VecDeque}, + collections::{HashMap, HashSet, VecDeque}, hash::BuildHasher, }; use num_bigint::BigInt; +use num_traits::One; -use crate::{Comparison, Type, TypeId, TypeSystem}; +use crate::{bigint_to_bytes, Comparison, Type, TypeId, TypeSystem}; use super::{Check, CheckError}; @@ -48,88 +49,180 @@ where (Type::Bytes32, Type::Bytes32) => Check::None, (Type::PublicKey, Type::PublicKey) => Check::None, (Type::Int, Type::Int) => Check::None, - (Type::Bool, Type::Bool) => Check::None, (Type::Nil, Type::Nil) => Check::None, + (Type::True, Type::True) => Check::None, + (Type::False, Type::False) => Check::None, (Type::Bytes32, Type::Atom) => Check::None, (Type::PublicKey, Type::Atom) => Check::None, (Type::Int, Type::Atom) => Check::None, (Type::Bytes, Type::Atom) => Check::None, - (Type::Bool, Type::Atom) => Check::None, (Type::Nil, Type::Atom) => Check::None, (Type::Atom, Type::Bytes) => Check::None, (Type::Bytes32, Type::Bytes) => Check::None, (Type::PublicKey, Type::Bytes) => Check::None, (Type::Int, Type::Bytes) => Check::None, - (Type::Bool, Type::Bytes) => Check::None, (Type::Nil, Type::Bytes) => Check::None, (Type::Atom, Type::Int) => Check::None, (Type::Bytes32, Type::Int) => Check::None, (Type::PublicKey, Type::Int) => Check::None, (Type::Bytes, Type::Int) => Check::None, - (Type::Bool, Type::Int) => Check::None, (Type::Nil, Type::Int) => Check::None, - (Type::Nil, Type::Bool) => Check::None, - - (Type::Atom, Type::Bool) => Check::IsBool, (Type::Atom, Type::Nil) => Check::Value(BigInt::ZERO), (Type::Atom, Type::PublicKey) => Check::Length(48), (Type::Atom, Type::Bytes32) => Check::Length(32), - (Type::Bytes, Type::Bool) => Check::IsBool, (Type::Bytes, Type::Nil) => Check::Value(BigInt::ZERO), (Type::Bytes, Type::PublicKey) => Check::Length(48), (Type::Bytes, Type::Bytes32) => Check::Length(32), - (Type::Int, Type::Bool) => Check::IsBool, (Type::Int, Type::Nil) => Check::Value(BigInt::ZERO), (Type::Int, Type::PublicKey) => Check::Length(48), (Type::Int, Type::Bytes32) => Check::Length(32), - (Type::Bool, Type::Nil) => Check::Value(BigInt::ZERO), - - (_, Type::Union(items)) => { - let mut result = Vec::new(); - for item in items.clone() { - result.push(check_type(types, lhs, item, visited)?); - } - Check::Or(result) - } - - (Type::Union(items), _) => { - let items = items.clone(); - check_union_against_rhs(types, lhs, &items, rhs, visited)? - } - (Type::PublicKey, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Bytes32, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Nil, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Nil, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), (Type::PublicKey, Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Bytes32, Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bool, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bool, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::PublicKey, Type::Bool) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bytes32, Type::Bool) => return Err(CheckError::Impossible(lhs, rhs)), + + (Type::True, Type::Atom) => Check::None, + (Type::False, Type::Atom) => Check::None, + (Type::True, Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::False, Type::Nil) => Check::None, + (Type::True, Type::False) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::False, Type::True) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::True, Type::Bytes) => Check::None, + (Type::False, Type::Bytes) => Check::None, + (Type::True, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::False, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::True, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::False, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::True, Type::Int) => Check::None, + (Type::False, Type::Int) => Check::None, + + (Type::Atom, Type::True) => Check::Value(BigInt::one()), + (Type::Atom, Type::False) => Check::Value(BigInt::ZERO), + (Type::Bytes, Type::True) => Check::Value(BigInt::one()), + (Type::Bytes, Type::False) => Check::Value(BigInt::ZERO), + (Type::Int, Type::True) => Check::Value(BigInt::one()), + (Type::Int, Type::False) => Check::Value(BigInt::ZERO), + (Type::Bytes32, Type::True) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Bytes32, Type::False) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::PublicKey, Type::True) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::PublicKey, Type::False) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Nil, Type::True) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Nil, Type::False) => Check::None, + + (Type::Value(..), Type::Atom) => Check::None, + (Type::Value(..), Type::Bytes) => Check::None, + (Type::Value(..), Type::Int) => Check::None, + (Type::Value(value), Type::Bytes32) => { + if bigint_to_bytes(value.clone()).len() == 32 { + Check::None + } else { + return Err(CheckError::Impossible(lhs, rhs)); + } + } + (Type::Value(value), Type::PublicKey) => { + if bigint_to_bytes(value.clone()).len() == 48 { + Check::None + } else { + return Err(CheckError::Impossible(lhs, rhs)); + } + } + (Type::Value(value), Type::Nil) => { + if value == &BigInt::ZERO { + Check::None + } else { + return Err(CheckError::Impossible(lhs, rhs)); + } + } + (Type::Value(value), Type::True) => { + if value == &BigInt::one() { + Check::None + } else { + return Err(CheckError::Impossible(lhs, rhs)); + } + } + (Type::Value(value), Type::False) => { + if value == &BigInt::ZERO { + Check::None + } else { + return Err(CheckError::Impossible(lhs, rhs)); + } + } + + (Type::Atom, Type::Value(value)) => Check::Value(value.clone()), + (Type::Bytes, Type::Value(value)) => Check::Value(value.clone()), + (Type::Int, Type::Value(value)) => Check::Value(value.clone()), + (Type::Bytes32, Type::Value(value)) => { + if bigint_to_bytes(value.clone()).len() == 32 { + Check::Value(value.clone()) + } else { + return Err(CheckError::Impossible(lhs, rhs)); + } + } + (Type::PublicKey, Type::Value(value)) => { + if bigint_to_bytes(value.clone()).len() == 48 { + Check::Value(value.clone()) + } else { + return Err(CheckError::Impossible(lhs, rhs)); + } + } + (Type::Nil, Type::Value(value)) => { + if value == &BigInt::ZERO { + Check::None + } else { + return Err(CheckError::Impossible(lhs, rhs)); + } + } + (Type::True, Type::Value(value)) => { + if value == &BigInt::one() { + Check::None + } else { + return Err(CheckError::Impossible(lhs, rhs)); + } + } + (Type::False, Type::Value(value)) => { + if value == &BigInt::ZERO { + Check::None + } else { + return Err(CheckError::Impossible(lhs, rhs)); + } + } + + (Type::Value(lhs_value), Type::Value(rhs_value)) => { + if lhs_value == rhs_value { + Check::None + } else { + return Err(CheckError::Impossible(lhs, rhs)); + } + } (Type::Atom, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Bytes, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Bytes32, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), (Type::PublicKey, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Int, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bool, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Nil, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::True, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::False, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Value(..), Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Pair(..), Type::Atom) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Pair(..), Type::Bytes) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Pair(..), Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Pair(..), Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Pair(..), Type::Int) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Pair(..), Type::Bool) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Pair(..), Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Pair(..), Type::True) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Pair(..), Type::False) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Pair(..), Type::Value(..)) => return Err(CheckError::Impossible(lhs, rhs)), (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { let (lhs_first, lhs_rest) = (*lhs_first, *lhs_rest); @@ -141,6 +234,19 @@ where Check::Rest(Box::new(rest)), ]) } + + (_, Type::Union(items)) => { + let mut result = Vec::new(); + for item in items.clone() { + result.push(check_type(types, lhs, item, visited)?); + } + Check::Or(result) + } + + (Type::Union(items), _) => { + let items = items.clone(); + check_union_against_rhs(types, lhs, &items, rhs, visited)? + } }; visited.remove(&(lhs, rhs)); @@ -183,11 +289,10 @@ where } let mut atom_count = 0; - let mut bool_count = 0; - let mut nil_count = 0; let mut bytes32_count = 0; let mut public_key_count = 0; let mut pairs = Vec::new(); + let mut values = HashMap::new(); let mut items: VecDeque<_> = items.iter().copied().collect::>(); let mut length = 0; @@ -222,14 +327,21 @@ where atom_count += 1; public_key_count += 1; } - Type::Bool => { + Type::Nil => { atom_count += 1; - bool_count += 1; + *values.entry(BigInt::ZERO).or_insert(0) += 1; } - Type::Nil => { + Type::True => { + atom_count += 1; + *values.entry(BigInt::one()).or_insert(0) += 1; + } + Type::False => { + atom_count += 1; + *values.entry(BigInt::ZERO).or_insert(0) += 1; + } + Type::Value(value) => { atom_count += 1; - nil_count += 1; - bool_count += 1; + *values.entry(value.clone()).or_insert(0) += 1; } Type::Pair(first, rest) => { pairs.push((*first, *rest)); @@ -253,27 +365,41 @@ where Type::Generic => return Err(CheckError::Impossible(original_type_id, rhs)), Type::Never => return Err(CheckError::Impossible(original_type_id, rhs)), Type::Atom if atom_count == length => Check::None, + Type::Atom => Check::IsAtom, Type::Bytes if atom_count == length => Check::None, + Type::Bytes => Check::IsAtom, Type::Int if atom_count == length => Check::None, - Type::Bool if bool_count == length => Check::None, - Type::Nil if nil_count == length => Check::None, + Type::Int => Check::IsAtom, + Type::Nil if values.get(&BigInt::ZERO).copied().unwrap_or(0) == length => Check::None, + Type::Nil if values.get(&BigInt::ZERO).copied().unwrap_or(0) == atom_count => Check::IsAtom, + Type::Nil if atom_count == length => Check::Value(BigInt::ZERO), + Type::Nil => Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]), + Type::False if values.get(&BigInt::ZERO).copied().unwrap_or(0) == length => Check::None, + Type::False if values.get(&BigInt::ZERO).copied().unwrap_or(0) == atom_count => { + Check::IsAtom + } + Type::False if atom_count == length => Check::Value(BigInt::ZERO), + Type::False => Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]), + Type::True if values.get(&BigInt::one()).copied().unwrap_or(0) == length => Check::None, + Type::True if values.get(&BigInt::one()).copied().unwrap_or(0) == atom_count => { + Check::IsAtom + } + Type::True if atom_count == length => Check::Value(BigInt::one()), + Type::True => Check::And(vec![Check::IsAtom, Check::Value(BigInt::one())]), + Type::Value(value) if values.get(value).copied().unwrap_or(0) == length => Check::None, + Type::Value(value) if values.get(value).copied().unwrap_or(0) == atom_count => { + Check::IsAtom + } + Type::Value(value) if atom_count == length => Check::Value(value.clone()), + Type::Value(value) => Check::And(vec![Check::IsAtom, Check::Value(value.clone())]), Type::Bytes32 if bytes32_count == length => Check::None, - Type::PublicKey if public_key_count == length => Check::None, Type::Bytes32 if atom_count == length => Check::Length(32), - Type::PublicKey if atom_count == length => Check::Length(48), - Type::Bool if atom_count == length => Check::IsBool, - Type::Nil if atom_count == length => Check::Value(BigInt::ZERO), - Type::Atom => Check::IsAtom, - Type::Bytes => Check::IsAtom, - Type::Int => Check::IsAtom, Type::Bytes32 if bytes32_count == atom_count => Check::IsAtom, - Type::PublicKey if public_key_count == atom_count => Check::IsAtom, - Type::Bool if bool_count == atom_count => Check::IsAtom, - Type::Nil if nil_count == atom_count => Check::IsAtom, Type::Bytes32 => Check::And(vec![Check::IsAtom, Check::Length(32)]), + Type::PublicKey if public_key_count == length => Check::None, + Type::PublicKey if atom_count == length => Check::Length(48), + Type::PublicKey if public_key_count == atom_count => Check::IsAtom, Type::PublicKey => Check::And(vec![Check::IsAtom, Check::Length(48)]), - Type::Bool => Check::And(vec![Check::IsAtom, Check::IsBool]), - Type::Nil => Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]), Type::Pair(..) if atom_count == length => { return Err(CheckError::Impossible(original_type_id, rhs)) } diff --git a/crates/rue-typing/src/check/simplify_and.rs b/crates/rue-typing/src/check/simplify_and.rs index 3f002ce..7feb0f4 100644 --- a/crates/rue-typing/src/check/simplify_and.rs +++ b/crates/rue-typing/src/check/simplify_and.rs @@ -22,7 +22,6 @@ pub(crate) fn simplify_and_shallow(items: impl IntoIterator) -> Ch let mut result = Vec::new(); let mut is_atom = false; let mut is_pair = false; - let mut is_bool = false; let mut value = false; let mut length = false; @@ -33,8 +32,6 @@ pub(crate) fn simplify_and_shallow(items: impl IntoIterator) -> Ch Check::IsAtom => is_atom = true, Check::IsPair if is_pair => continue, Check::IsPair => is_pair = true, - Check::IsBool if is_bool => continue, - Check::IsBool => is_bool = true, Check::Value(..) if value => continue, Check::Value(..) => value = true, Check::Length(..) if length => continue, diff --git a/crates/rue-typing/src/check/simplify_check.rs b/crates/rue-typing/src/check/simplify_check.rs index ab4924f..290185a 100644 --- a/crates/rue-typing/src/check/simplify_check.rs +++ b/crates/rue-typing/src/check/simplify_check.rs @@ -7,7 +7,6 @@ pub(crate) fn simplify_check(check: Check) -> Check { Check::None => Check::None, Check::IsAtom => Check::IsAtom, Check::IsPair => Check::IsPair, - Check::IsBool => Check::IsBool, Check::Value(value) => Check::Value(value), Check::Length(len) => { if len == 0 { diff --git a/crates/rue-typing/src/check/stringify_check.rs b/crates/rue-typing/src/check/stringify_check.rs index e747732..f4338ab 100644 --- a/crates/rue-typing/src/check/stringify_check.rs +++ b/crates/rue-typing/src/check/stringify_check.rs @@ -21,13 +21,6 @@ pub(crate) fn stringify_check( stringify_value(f, path)?; write!(f, "))") } - Check::IsBool => { - write!(f, "(any (= ")?; - stringify_value(f, path)?; - write!(f, " 0) (= ")?; - stringify_value(f, path)?; - write!(f, " 1))") - } Check::Value(value) => { write!(f, "(= ")?; stringify_value(f, path)?; diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index b71305f..9cb6f21 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -133,25 +133,20 @@ pub(crate) fn compare_type( (Type::Bytes32, Type::Bytes32) => Comparison::Equal, (Type::PublicKey, Type::PublicKey) => Comparison::Equal, (Type::Int, Type::Int) => Comparison::Equal, - (Type::Bool, Type::Bool) => Comparison::Equal, (Type::Nil, Type::Nil) => Comparison::Equal, (Type::Atom, Type::Bytes32) => Comparison::Superset, (Type::Atom, Type::PublicKey) => Comparison::Superset, - (Type::Atom, Type::Bool) => Comparison::Superset, (Type::Atom, Type::Nil) => Comparison::Superset, (Type::Bytes, Type::Bytes32) => Comparison::Superset, (Type::Bytes, Type::PublicKey) => Comparison::Superset, - (Type::Bytes, Type::Bool) => Comparison::Superset, (Type::Bytes, Type::Nil) => Comparison::Superset, (Type::Int, Type::Bytes32) => Comparison::Superset, (Type::Int, Type::PublicKey) => Comparison::Superset, - (Type::Int, Type::Bool) => Comparison::Superset, (Type::Int, Type::Nil) => Comparison::Superset, (Type::Bytes32, Type::Atom) => Comparison::Assignable, (Type::PublicKey, Type::Atom) => Comparison::Assignable, - (Type::Bool, Type::Atom) => Comparison::Assignable, (Type::Nil, Type::Atom) => Comparison::Assignable, (Type::Bytes, Type::Atom) => Comparison::Assignable, (Type::Int, Type::Atom) => Comparison::Assignable, @@ -166,28 +161,18 @@ pub(crate) fn compare_type( (Type::PublicKey, Type::Bytes) => Comparison::Castable, (Type::PublicKey, Type::Int) => Comparison::Castable, (Type::Int, Type::Bytes) => Comparison::Castable, - (Type::Nil, Type::Bool) => Comparison::Castable, (Type::Nil, Type::Int) => Comparison::Castable, - (Type::Bool, Type::Bytes) => Comparison::Castable, - (Type::Bool, Type::Int) => Comparison::Castable, (Type::Bytes32, Type::PublicKey) => Comparison::Incompatible, - (Type::Bytes32, Type::Bool) => Comparison::Incompatible, (Type::Bytes32, Type::Nil) => Comparison::Incompatible, (Type::PublicKey, Type::Bytes32) => Comparison::Incompatible, - (Type::PublicKey, Type::Bool) => Comparison::Incompatible, (Type::PublicKey, Type::Nil) => Comparison::Incompatible, - (Type::Bool, Type::Bytes32) => Comparison::Incompatible, - (Type::Bool, Type::PublicKey) => Comparison::Incompatible, - (Type::Bool, Type::Nil) => Comparison::Incompatible, (Type::Nil, Type::Bytes32) => Comparison::Incompatible, (Type::Nil, Type::PublicKey) => Comparison::Incompatible, (Type::True, Type::False) => Comparison::Incompatible, (Type::False, Type::True) => Comparison::Incompatible, - (Type::True, Type::Bool) => Comparison::Assignable, - (Type::False, Type::Bool) => Comparison::Assignable, (Type::True, Type::Bytes) => Comparison::Castable, (Type::False, Type::Bytes) => Comparison::Castable, (Type::True, Type::Int) => Comparison::Castable, @@ -201,8 +186,6 @@ pub(crate) fn compare_type( (Type::True, Type::PublicKey) => Comparison::Incompatible, (Type::False, Type::PublicKey) => Comparison::Incompatible, - (Type::Bool, Type::True) => Comparison::Superset, - (Type::Bool, Type::False) => Comparison::Superset, (Type::Bytes, Type::True) => Comparison::Superset, (Type::Bytes, Type::False) => Comparison::Superset, (Type::Int, Type::True) => Comparison::Superset, @@ -247,8 +230,8 @@ pub(crate) fn compare_type( Comparison::Incompatible } } - (Type::Value(value), Type::Bool) => { - if value == &BigInt::ZERO || value == &BigInt::one() { + (Type::Value(value), Type::False) => { + if value == &BigInt::ZERO { Comparison::Castable } else { Comparison::Incompatible @@ -279,8 +262,15 @@ pub(crate) fn compare_type( Comparison::Incompatible } } - (Type::Bool, Type::Value(value)) => { - if value == &BigInt::ZERO || value == &BigInt::one() { + (Type::True, Type::Value(value)) => { + if value == &BigInt::one() { + Comparison::Castable + } else { + Comparison::Incompatible + } + } + (Type::False, Type::Value(value)) => { + if value == &BigInt::ZERO { Comparison::Castable } else { Comparison::Incompatible diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index 4f0ec57..b23014e 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use num_bigint::BigInt; use num_traits::One; -use crate::{StandardTypes, Struct, Type, TypeId, TypeSystem}; +use crate::{bigint_to_bytes, StandardTypes, Struct, Type, TypeId, TypeSystem}; pub(crate) fn difference_type( types: &mut TypeSystem, @@ -31,57 +31,78 @@ pub(crate) fn difference_type( (Type::Bytes32, Type::Bytes32) => std.never, (Type::PublicKey, Type::PublicKey) => std.never, (Type::Int, Type::Int) => std.never, - (Type::Bool, Type::Bool) => std.never, (Type::Nil, Type::Nil) => std.never, + (Type::True, Type::True) => std.never, + (Type::False, Type::False) => std.never, (Type::Int, Type::Bytes32) => lhs, (Type::Int, Type::PublicKey) => lhs, (Type::Int, Type::Bytes) => std.never, (Type::Int, Type::Atom) => std.never, - (Type::Int, Type::Bool) => lhs, (Type::Int, Type::Nil) => lhs, + (Type::Int, Type::True) => lhs, + (Type::Int, Type::False) => lhs, + (Type::Int, Type::Value(..)) => lhs, (Type::Bytes, Type::Bytes32) => lhs, (Type::Bytes, Type::PublicKey) => lhs, (Type::Bytes, Type::Int) => std.never, (Type::Bytes, Type::Atom) => std.never, - (Type::Bytes, Type::Bool) => lhs, (Type::Bytes, Type::Nil) => lhs, + (Type::Bytes, Type::True) => lhs, + (Type::Bytes, Type::False) => lhs, + (Type::Bytes, Type::Value(..)) => lhs, (Type::Atom, Type::Bytes32) => lhs, (Type::Atom, Type::PublicKey) => lhs, (Type::Atom, Type::Int) => std.never, (Type::Atom, Type::Bytes) => std.never, - (Type::Atom, Type::Bool) => lhs, (Type::Atom, Type::Nil) => lhs, + (Type::Atom, Type::True) => lhs, + (Type::Atom, Type::False) => lhs, + (Type::Atom, Type::Value(..)) => lhs, (Type::Bytes32, Type::PublicKey) => lhs, (Type::Bytes32, Type::Bytes) => std.never, (Type::Bytes32, Type::Int) => std.never, (Type::Bytes32, Type::Atom) => std.never, - (Type::Bytes32, Type::Bool) => lhs, (Type::Bytes32, Type::Nil) => lhs, + (Type::Bytes32, Type::True) => lhs, + (Type::Bytes32, Type::False) => lhs, + (Type::Bytes32, Type::Value(..)) => lhs, (Type::PublicKey, Type::Bytes32) => lhs, (Type::PublicKey, Type::Bytes) => std.never, (Type::PublicKey, Type::Int) => std.never, (Type::PublicKey, Type::Atom) => std.never, - (Type::PublicKey, Type::Bool) => lhs, (Type::PublicKey, Type::Nil) => lhs, - - (Type::Bool, Type::Bytes32) => lhs, - (Type::Bool, Type::PublicKey) => lhs, - (Type::Bool, Type::Bytes) => std.never, - (Type::Bool, Type::Atom) => std.never, - (Type::Bool, Type::Int) => std.never, - (Type::Bool, Type::Nil) => types.alloc(Type::Value(BigInt::one())), + (Type::PublicKey, Type::True) => lhs, + (Type::PublicKey, Type::False) => lhs, + (Type::PublicKey, Type::Value(..)) => lhs, (Type::Nil, Type::Bytes32) => lhs, (Type::Nil, Type::PublicKey) => lhs, (Type::Nil, Type::Bytes) => std.never, (Type::Nil, Type::Atom) => std.never, (Type::Nil, Type::Int) => std.never, - (Type::Nil, Type::Bool) => std.never, + (Type::Nil, Type::True) => lhs, + (Type::Nil, Type::False) => std.never, + + (Type::True, Type::Atom) => std.never, + (Type::True, Type::Bytes) => std.never, + (Type::True, Type::Bytes32) => lhs, + (Type::True, Type::PublicKey) => lhs, + (Type::True, Type::Int) => std.never, + (Type::True, Type::Nil) => lhs, + (Type::True, Type::False) => lhs, + + (Type::False, Type::Atom) => std.never, + (Type::False, Type::Bytes) => std.never, + (Type::False, Type::Bytes32) => lhs, + (Type::False, Type::PublicKey) => lhs, + (Type::False, Type::Int) => std.never, + (Type::False, Type::Nil) => lhs, + (Type::False, Type::True) => lhs, (Type::Nil, Type::Value(value)) => { if value == &BigInt::ZERO { @@ -90,15 +111,61 @@ pub(crate) fn difference_type( lhs } } - (Type::Bool, Type::Value(value)) => { + (Type::False, Type::Value(value)) => { if value == &BigInt::ZERO { - types.standard_types().true_bool - } else if value == &BigInt::one() { - types.standard_types().false_bool + std.never + } else { + lhs + } + } + (Type::True, Type::Value(value)) => { + if value == &BigInt::one() { + std.never + } else { + lhs + } + } + + (Type::Value(..), Type::Atom) => std.never, + (Type::Value(..), Type::Bytes) => std.never, + (Type::Value(..), Type::Int) => std.never, + + (Type::Value(value), Type::Bytes32) => { + if bigint_to_bytes(value.clone()).len() == 32 { + std.never } else { lhs } } + (Type::Value(value), Type::PublicKey) => { + if bigint_to_bytes(value.clone()).len() == 48 { + std.never + } else { + lhs + } + } + (Type::Value(value), Type::True) => { + if value == &BigInt::one() { + std.never + } else { + lhs + } + } + (Type::Value(value), Type::False) => { + if value == &BigInt::ZERO { + std.never + } else { + lhs + } + } + (Type::Value(value), Type::Nil) => { + if value == &BigInt::ZERO { + std.never + } else { + lhs + } + } + (Type::Value(lhs_value), Type::Value(rhs_value)) => { if lhs_value == rhs_value { std.never @@ -112,16 +179,20 @@ pub(crate) fn difference_type( (Type::Bytes32, Type::Pair(..)) => lhs, (Type::PublicKey, Type::Pair(..)) => lhs, (Type::Int, Type::Pair(..)) => lhs, - (Type::Bool, Type::Pair(..)) => lhs, (Type::Nil, Type::Pair(..)) => lhs, + (Type::True, Type::Pair(..)) => lhs, + (Type::False, Type::Pair(..)) => lhs, + (Type::Value(..), Type::Pair(..)) => lhs, (Type::Pair(..), Type::Atom) => lhs, (Type::Pair(..), Type::Bytes) => lhs, (Type::Pair(..), Type::Bytes32) => lhs, (Type::Pair(..), Type::PublicKey) => lhs, (Type::Pair(..), Type::Int) => lhs, - (Type::Pair(..), Type::Bool) => lhs, (Type::Pair(..), Type::Nil) => lhs, + (Type::Pair(..), Type::True) => lhs, + (Type::Pair(..), Type::False) => lhs, + (Type::Pair(..), Type::Value(..)) => lhs, (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { let (lhs_first, lhs_rest) = (*lhs_first, *lhs_rest); diff --git a/crates/rue-typing/src/semantic_types.rs b/crates/rue-typing/src/semantic_types.rs index e389105..2d3a3bb 100644 --- a/crates/rue-typing/src/semantic_types.rs +++ b/crates/rue-typing/src/semantic_types.rs @@ -74,7 +74,7 @@ pub struct Variant { } /// Field information for a variant type. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct VariantFields { pub fields: TypeId, pub rest: Rest, diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs index 7c678e1..a289c7b 100644 --- a/crates/rue-typing/src/stringify.rs +++ b/crates/rue-typing/src/stringify.rs @@ -26,8 +26,10 @@ pub(crate) fn stringify_type( Type::Bytes32 => "Bytes32".to_string(), Type::PublicKey => "PublicKey".to_string(), Type::Int => "Int".to_string(), - Type::Bool => "Bool".to_string(), + Type::True => "True".to_string(), + Type::False => "False".to_string(), Type::Nil => "Nil".to_string(), + Type::Value(value) => format!("{value}"), Type::Pair(first, rest) => { let first = stringify_type(types, *first, names, visited); let rest = stringify_type(types, *rest, names, visited); @@ -87,7 +89,7 @@ mod tests { assert_eq!(db.stringify(types.int), "Int"); assert_eq!(db.stringify(types.bool), "Bool"); assert_eq!(db.stringify(types.nil), "Nil"); - assert_eq!(db.stringify(types.any), "Atom | ({recursive}, {recursive})"); + assert_eq!(db.stringify(types.any), "Any"); } #[test] @@ -96,9 +98,9 @@ mod tests { let types = db.standard_types(); let mut names = HashMap::new(); - names.insert(types.any, "Any".to_string()); + names.insert(types.any, "CustomAny".to_string()); - assert_eq!(db.stringify_named(types.any, &names), "Any"); + assert_eq!(db.stringify_named(types.any, names), "CustomAny"); } #[test] @@ -117,7 +119,7 @@ mod tests { ); assert_eq!( - db.stringify_named(callable, &HashMap::new()), + db.stringify_named(callable, HashMap::new()), "fun((Int, (Bytes, Nil))) -> Bool" ); } diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index 4739669..e6ab0d4 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -33,8 +33,10 @@ pub(crate) fn substitute_type( Type::Bytes32 => type_id, Type::PublicKey => type_id, Type::Int => type_id, - Type::Bool => type_id, Type::Nil => type_id, + Type::True => type_id, + Type::False => type_id, + Type::Value(..) => type_id, Type::Pair(first, rest) => { let (first, rest) = (*first, *rest); diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index 12eba0b..d94d9b0 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -14,6 +14,7 @@ pub type TypeId = Id; pub struct TypeSystem { arena: Arena, types: StandardTypes, + names: HashMap, } impl Default for TypeSystem { @@ -27,7 +28,6 @@ impl Default for TypeSystem { let bytes32 = arena.alloc(Type::Bytes32); let public_key = arena.alloc(Type::PublicKey); let int = arena.alloc(Type::Int); - let bool = arena.alloc(Type::Bool); let true_bool = arena.alloc(Type::True); let false_bool = arena.alloc(Type::False); let nil = arena.alloc(Type::Nil); @@ -36,6 +36,21 @@ impl Default for TypeSystem { let pair = arena.alloc(Type::Pair(any, any)); arena[any] = Type::Union(vec![atom, pair]); + let bool = arena.alloc(Type::Union(vec![false_bool, true_bool])); + + let mut names = HashMap::new(); + names.insert(never, "Never".to_string()); + names.insert(atom, "Atom".to_string()); + names.insert(bytes, "Bytes".to_string()); + names.insert(bytes32, "Bytes32".to_string()); + names.insert(public_key, "PublicKey".to_string()); + names.insert(int, "Int".to_string()); + names.insert(bool, "Bool".to_string()); + names.insert(true_bool, "true".to_string()); + names.insert(false_bool, "false".to_string()); + names.insert(nil, "Nil".to_string()); + names.insert(any, "Any".to_string()); + Self { arena, types: StandardTypes { @@ -52,6 +67,7 @@ impl Default for TypeSystem { false_bool, nil, }, + names, } } } @@ -83,12 +99,15 @@ impl TypeSystem { } } - pub fn stringify_named(&self, type_id: TypeId, names: &HashMap) -> String { - stringify_type(self, type_id, names, &mut HashSet::new()) + pub fn stringify_named(&self, type_id: TypeId, mut names: HashMap) -> String { + for (id, name) in &self.names { + names.entry(*id).or_insert_with(|| name.clone()); + } + stringify_type(self, type_id, &names, &mut HashSet::new()) } pub fn stringify(&self, type_id: TypeId) -> String { - self.stringify_named(type_id, &HashMap::new()) + self.stringify_named(type_id, HashMap::new()) } pub fn compare(&self, lhs: TypeId, rhs: TypeId) -> Comparison { From d189bf15c1c8a3a411a0d3a99b8a5d728bf5fecf Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 01:50:17 -0400 Subject: [PATCH 049/100] Temp --- crates/rue-typing/src/check/check_type.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs index e393107..8fb5662 100644 --- a/crates/rue-typing/src/check/check_type.rs +++ b/crates/rue-typing/src/check/check_type.rs @@ -501,7 +501,7 @@ mod tests { &mut db, types.any, types.bool, - "(and (not (l val)) (any (= val 0) (= val 1)))", + "(and (not (l val)) (or (= val 0) (= val 1)))", ); } @@ -610,12 +610,7 @@ mod tests { fn test_check_bytes_bool() { let mut db = TypeSystem::new(); let types = db.standard_types(); - check_str( - &mut db, - types.bytes, - types.bool, - "(any (= val 0) (= val 1))", - ); + check_str(&mut db, types.bytes, types.bool, "(or (= val 0) (= val 1))"); } #[test] From 606426a83e9d5d02b87a4adc859d2b1cec7974d2 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 02:04:22 -0400 Subject: [PATCH 050/100] Refactor attributes --- crates/rue-typing/src/check.rs | 2 + crates/rue-typing/src/check/attributes.rs | 140 +++++++++++++++++++++ crates/rue-typing/src/check/check_type.rs | 147 +++++----------------- 3 files changed, 176 insertions(+), 113 deletions(-) create mode 100644 crates/rue-typing/src/check/attributes.rs diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 0073913..61f05ea 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -1,5 +1,6 @@ use std::fmt; +mod attributes; mod check_error; mod check_type; mod simplify_and; @@ -9,6 +10,7 @@ mod stringify_check; pub use check_error::*; +pub(crate) use attributes::*; pub(crate) use check_type::*; pub(crate) use simplify_and::*; pub(crate) use simplify_check::*; diff --git a/crates/rue-typing/src/check/attributes.rs b/crates/rue-typing/src/check/attributes.rs new file mode 100644 index 0000000..407ad3f --- /dev/null +++ b/crates/rue-typing/src/check/attributes.rs @@ -0,0 +1,140 @@ +use std::collections::{HashMap, HashSet, VecDeque}; + +use num_bigint::BigInt; +use num_traits::One; + +use crate::{Type, TypeId, TypeSystem}; + +use super::CheckError; + +pub(crate) struct Attributes { + pub atom_count: usize, + pub bytes32_count: usize, + pub public_key_count: usize, + pub pairs: Vec<(TypeId, TypeId)>, + pub values: HashMap, + pub length: usize, +} + +impl Attributes { + pub fn all_atoms(&self) -> bool { + self.atom_count == self.length + } + + pub fn all_bytes32(&self) -> bool { + self.bytes32_count == self.length + } + + pub fn all_public_key(&self) -> bool { + self.public_key_count == self.length + } + + pub fn all_pairs(&self) -> bool { + self.pairs.len() == self.length + } + + pub fn all_value(&self, value: &BigInt) -> bool { + self.values.get(value).copied().unwrap_or(0) == self.length + } + + pub fn atoms_are_bytes32(&self) -> bool { + self.bytes32_count == self.atom_count + } + + pub fn atoms_are_public_key(&self) -> bool { + self.public_key_count == self.atom_count + } + + pub fn atoms_are_value(&self, value: &BigInt) -> bool { + self.values.get(value).copied().unwrap_or(0) == self.atom_count + } +} + +pub(crate) fn union_attributes( + db: &TypeSystem, + items: &[TypeId], + is_lhs: bool, + other_type_id: TypeId, + visited: &mut HashSet<(TypeId, TypeId)>, +) -> Result { + let mut atom_count = 0; + let mut bytes32_count = 0; + let mut public_key_count = 0; + let mut pairs = Vec::new(); + let mut values = HashMap::new(); + + let mut items: VecDeque<_> = items.iter().copied().collect(); + let mut length = 0; + + while !items.is_empty() { + let item = items.remove(0).unwrap(); + length += 1; + + let key = if is_lhs { + (item, other_type_id) + } else { + (other_type_id, item) + }; + + if !visited.insert(key) { + return Err(CheckError::Recursive(key.0, key.1)); + } + + match db.get(item) { + Type::Ref(..) => unreachable!(), + Type::Lazy(..) => unreachable!(), + Type::Alias(..) => unreachable!(), + Type::Struct(..) => unreachable!(), + Type::Callable(..) => unreachable!(), + Type::Generic => return Err(CheckError::Impossible(key.0, key.1)), + Type::Unknown => {} + Type::Never => { + length -= 1; + } + Type::Atom | Type::Bytes | Type::Int => { + atom_count += 1; + } + Type::Bytes32 => { + atom_count += 1; + bytes32_count += 1; + } + Type::PublicKey => { + atom_count += 1; + public_key_count += 1; + } + Type::Nil => { + atom_count += 1; + *values.entry(BigInt::ZERO).or_insert(0) += 1; + } + Type::True => { + atom_count += 1; + *values.entry(BigInt::one()).or_insert(0) += 1; + } + Type::False => { + atom_count += 1; + *values.entry(BigInt::ZERO).or_insert(0) += 1; + } + Type::Value(value) => { + atom_count += 1; + *values.entry(value.clone()).or_insert(0) += 1; + } + Type::Pair(first, rest) => { + pairs.push((*first, *rest)); + } + Type::Union(child_items) => { + items.extend(child_items); + } + } + + visited.remove(&key); + } + + Ok(Attributes { + atom_count, + bytes32_count, + public_key_count, + pairs, + values, + length, + }) +} diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs index 8fb5662..fa7b0d4 100644 --- a/crates/rue-typing/src/check/check_type.rs +++ b/crates/rue-typing/src/check/check_type.rs @@ -1,25 +1,19 @@ -use std::{ - collections::{HashMap, HashSet, VecDeque}, - hash::BuildHasher, -}; +use std::collections::HashSet; use num_bigint::BigInt; use num_traits::One; use crate::{bigint_to_bytes, Comparison, Type, TypeId, TypeSystem}; -use super::{Check, CheckError}; +use super::{union_attributes, Check, CheckError}; /// Returns [`None`] for recursive checks. -pub(crate) fn check_type( +pub(crate) fn check_type( types: &mut TypeSystem, lhs: TypeId, rhs: TypeId, - visited: &mut HashSet<(TypeId, TypeId), S>, -) -> Result -where - S: BuildHasher, -{ + visited: &mut HashSet<(TypeId, TypeId)>, +) -> Result { if !visited.insert((lhs, rhs)) { if types.compare(lhs, rhs) <= Comparison::Castable { return Ok(Check::None); @@ -254,16 +248,13 @@ where Ok(check) } -fn check_union_against_rhs( +fn check_union_against_rhs( types: &mut TypeSystem, original_type_id: TypeId, items: &[TypeId], rhs: TypeId, - visited: &mut HashSet<(TypeId, TypeId), S>, -) -> Result -where - S: BuildHasher, -{ + visited: &mut HashSet<(TypeId, TypeId)>, +) -> Result { let union = types.alloc(Type::Union(items.to_vec())); if types.compare(union, rhs) <= Comparison::Castable { @@ -288,71 +279,7 @@ where return Ok(Check::Or(result)); } - let mut atom_count = 0; - let mut bytes32_count = 0; - let mut public_key_count = 0; - let mut pairs = Vec::new(); - let mut values = HashMap::new(); - - let mut items: VecDeque<_> = items.iter().copied().collect::>(); - let mut length = 0; - - while !items.is_empty() { - let item = items.remove(0).unwrap(); - length += 1; - - if !visited.insert((item, rhs)) { - return Err(CheckError::Recursive(item, rhs)); - } - - match types.get(item) { - Type::Ref(..) => unreachable!(), - Type::Lazy(..) => unreachable!(), - Type::Alias(..) => unreachable!(), - Type::Struct(..) => unreachable!(), - Type::Callable(..) => unreachable!(), - Type::Generic => return Err(CheckError::Impossible(item, rhs)), - Type::Unknown => {} - Type::Never => { - length -= 1; - } - Type::Atom | Type::Bytes | Type::Int => { - atom_count += 1; - } - Type::Bytes32 => { - atom_count += 1; - bytes32_count += 1; - } - Type::PublicKey => { - atom_count += 1; - public_key_count += 1; - } - Type::Nil => { - atom_count += 1; - *values.entry(BigInt::ZERO).or_insert(0) += 1; - } - Type::True => { - atom_count += 1; - *values.entry(BigInt::one()).or_insert(0) += 1; - } - Type::False => { - atom_count += 1; - *values.entry(BigInt::ZERO).or_insert(0) += 1; - } - Type::Value(value) => { - atom_count += 1; - *values.entry(value.clone()).or_insert(0) += 1; - } - Type::Pair(first, rest) => { - pairs.push((*first, *rest)); - } - Type::Union(child_items) => { - items.extend(child_items); - } - } - - visited.remove(&(item, rhs)); - } + let attrs = union_attributes(types, items, true, rhs, visited)?; Ok(match types.get(rhs) { Type::Ref(..) => unreachable!(), @@ -364,57 +291,51 @@ where Type::Unknown => Check::None, Type::Generic => return Err(CheckError::Impossible(original_type_id, rhs)), Type::Never => return Err(CheckError::Impossible(original_type_id, rhs)), - Type::Atom if atom_count == length => Check::None, + Type::Atom if attrs.all_atoms() => Check::None, Type::Atom => Check::IsAtom, - Type::Bytes if atom_count == length => Check::None, + Type::Bytes if attrs.all_atoms() => Check::None, Type::Bytes => Check::IsAtom, - Type::Int if atom_count == length => Check::None, + Type::Int if attrs.all_atoms() => Check::None, Type::Int => Check::IsAtom, - Type::Nil if values.get(&BigInt::ZERO).copied().unwrap_or(0) == length => Check::None, - Type::Nil if values.get(&BigInt::ZERO).copied().unwrap_or(0) == atom_count => Check::IsAtom, - Type::Nil if atom_count == length => Check::Value(BigInt::ZERO), + Type::Nil if attrs.all_value(&BigInt::ZERO) => Check::None, + Type::Nil if attrs.atoms_are_value(&BigInt::ZERO) => Check::IsAtom, + Type::Nil if attrs.all_atoms() => Check::Value(BigInt::ZERO), Type::Nil => Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]), - Type::False if values.get(&BigInt::ZERO).copied().unwrap_or(0) == length => Check::None, - Type::False if values.get(&BigInt::ZERO).copied().unwrap_or(0) == atom_count => { - Check::IsAtom - } - Type::False if atom_count == length => Check::Value(BigInt::ZERO), + Type::False if attrs.all_value(&BigInt::ZERO) => Check::None, + Type::False if attrs.atoms_are_value(&BigInt::ZERO) => Check::IsAtom, + Type::False if attrs.all_atoms() => Check::Value(BigInt::ZERO), Type::False => Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]), - Type::True if values.get(&BigInt::one()).copied().unwrap_or(0) == length => Check::None, - Type::True if values.get(&BigInt::one()).copied().unwrap_or(0) == atom_count => { - Check::IsAtom - } - Type::True if atom_count == length => Check::Value(BigInt::one()), + Type::True if attrs.all_value(&BigInt::one()) => Check::None, + Type::True if attrs.atoms_are_value(&BigInt::ZERO) => Check::IsAtom, + Type::True if attrs.all_atoms() => Check::Value(BigInt::one()), Type::True => Check::And(vec![Check::IsAtom, Check::Value(BigInt::one())]), - Type::Value(value) if values.get(value).copied().unwrap_or(0) == length => Check::None, - Type::Value(value) if values.get(value).copied().unwrap_or(0) == atom_count => { - Check::IsAtom - } - Type::Value(value) if atom_count == length => Check::Value(value.clone()), + Type::Value(value) if attrs.all_value(value) => Check::None, + Type::Value(value) if attrs.atoms_are_value(value) => Check::IsAtom, + Type::Value(value) if attrs.all_atoms() => Check::Value(value.clone()), Type::Value(value) => Check::And(vec![Check::IsAtom, Check::Value(value.clone())]), - Type::Bytes32 if bytes32_count == length => Check::None, - Type::Bytes32 if atom_count == length => Check::Length(32), - Type::Bytes32 if bytes32_count == atom_count => Check::IsAtom, + Type::Bytes32 if attrs.all_bytes32() => Check::None, + Type::Bytes32 if attrs.all_atoms() => Check::Length(32), + Type::Bytes32 if attrs.atoms_are_bytes32() => Check::IsAtom, Type::Bytes32 => Check::And(vec![Check::IsAtom, Check::Length(32)]), - Type::PublicKey if public_key_count == length => Check::None, - Type::PublicKey if atom_count == length => Check::Length(48), - Type::PublicKey if public_key_count == atom_count => Check::IsAtom, + Type::PublicKey if attrs.all_public_key() => Check::None, + Type::PublicKey if attrs.all_atoms() => Check::Length(48), + Type::PublicKey if attrs.atoms_are_public_key() => Check::IsAtom, Type::PublicKey => Check::And(vec![Check::IsAtom, Check::Length(48)]), - Type::Pair(..) if atom_count == length => { + Type::Pair(..) if attrs.all_atoms() => { return Err(CheckError::Impossible(original_type_id, rhs)) } Type::Pair(first, rest) => { let (first, rest) = (*first, *rest); - let first_items: Vec<_> = pairs.iter().map(|(first, _)| *first).collect(); - let rest_items: Vec<_> = pairs.iter().map(|(_, rest)| *rest).collect(); + let first_items: Vec<_> = attrs.pairs.iter().map(|(first, _)| *first).collect(); + let rest_items: Vec<_> = attrs.pairs.iter().map(|(_, rest)| *rest).collect(); let first = check_union_against_rhs(types, original_type_id, &first_items, first, visited)?; let rest = check_union_against_rhs(types, original_type_id, &rest_items, rest, visited)?; - if pairs.len() == length { + if attrs.all_pairs() { Check::And(vec![ Check::First(Box::new(first)), Check::Rest(Box::new(rest)), From c833a003cd871d640e1ac86a077363c775fa55f4 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 02:19:28 -0400 Subject: [PATCH 051/100] Refactor and remove impossible --- crates/rue-typing/src/check.rs | 3 +- crates/rue-typing/src/check/attributes.rs | 4 +- crates/rue-typing/src/check/check_error.rs | 3 - crates/rue-typing/src/check/check_type.rs | 261 +++++++++--------- crates/rue-typing/src/check/simplify_and.rs | 22 +- crates/rue-typing/src/check/simplify_check.rs | 7 +- crates/rue-typing/src/check/simplify_or.rs | 20 +- .../rue-typing/src/check/stringify_check.rs | 3 +- crates/rue-typing/src/type_system.rs | 4 +- 9 files changed, 160 insertions(+), 167 deletions(-) diff --git a/crates/rue-typing/src/check.rs b/crates/rue-typing/src/check.rs index 61f05ea..a993f7b 100644 --- a/crates/rue-typing/src/check.rs +++ b/crates/rue-typing/src/check.rs @@ -21,7 +21,8 @@ use num_bigint::BigInt; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Check { - None, + True, + False, IsPair, IsAtom, Value(BigInt), diff --git a/crates/rue-typing/src/check/attributes.rs b/crates/rue-typing/src/check/attributes.rs index 407ad3f..a1bda49 100644 --- a/crates/rue-typing/src/check/attributes.rs +++ b/crates/rue-typing/src/check/attributes.rs @@ -86,7 +86,9 @@ pub(crate) fn union_attributes( Type::Alias(..) => unreachable!(), Type::Struct(..) => unreachable!(), Type::Callable(..) => unreachable!(), - Type::Generic => return Err(CheckError::Impossible(key.0, key.1)), + Type::Generic => { + length -= 1; + } Type::Unknown => {} Type::Never => { length -= 1; diff --git a/crates/rue-typing/src/check/check_error.rs b/crates/rue-typing/src/check/check_error.rs index 660baf9..c1b9963 100644 --- a/crates/rue-typing/src/check/check_error.rs +++ b/crates/rue-typing/src/check/check_error.rs @@ -6,7 +6,4 @@ use crate::TypeId; pub enum CheckError { #[error("recursive check")] Recursive(TypeId, TypeId), - - #[error("impossible check")] - Impossible(TypeId, TypeId), } diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs index fa7b0d4..bf8043f 100644 --- a/crates/rue-typing/src/check/check_type.rs +++ b/crates/rue-typing/src/check/check_type.rs @@ -16,7 +16,7 @@ pub(crate) fn check_type( ) -> Result { if !visited.insert((lhs, rhs)) { if types.compare(lhs, rhs) <= Comparison::Castable { - return Ok(Check::None); + return Ok(Check::True); } return Err(CheckError::Recursive(lhs, rhs)); } @@ -29,41 +29,41 @@ pub(crate) fn check_type( (Type::Callable(..), _) | (_, Type::Callable(..)) => unreachable!(), // TODO: Implement generic type checking? - (Type::Generic, _) => Check::None, - (_, Type::Generic) => Check::None, - - (Type::Unknown, _) => Check::None, - (_, Type::Unknown) => Check::None, - - (Type::Never, _) => Check::None, - (_, Type::Never) => return Err(CheckError::Impossible(lhs, rhs)), - - (Type::Atom, Type::Atom) => Check::None, - (Type::Bytes, Type::Bytes) => Check::None, - (Type::Bytes32, Type::Bytes32) => Check::None, - (Type::PublicKey, Type::PublicKey) => Check::None, - (Type::Int, Type::Int) => Check::None, - (Type::Nil, Type::Nil) => Check::None, - (Type::True, Type::True) => Check::None, - (Type::False, Type::False) => Check::None, - - (Type::Bytes32, Type::Atom) => Check::None, - (Type::PublicKey, Type::Atom) => Check::None, - (Type::Int, Type::Atom) => Check::None, - (Type::Bytes, Type::Atom) => Check::None, - (Type::Nil, Type::Atom) => Check::None, - - (Type::Atom, Type::Bytes) => Check::None, - (Type::Bytes32, Type::Bytes) => Check::None, - (Type::PublicKey, Type::Bytes) => Check::None, - (Type::Int, Type::Bytes) => Check::None, - (Type::Nil, Type::Bytes) => Check::None, - - (Type::Atom, Type::Int) => Check::None, - (Type::Bytes32, Type::Int) => Check::None, - (Type::PublicKey, Type::Int) => Check::None, - (Type::Bytes, Type::Int) => Check::None, - (Type::Nil, Type::Int) => Check::None, + (Type::Generic, _) => Check::True, + (_, Type::Generic) => Check::True, + + (Type::Unknown, _) => Check::True, + (_, Type::Unknown) => Check::True, + + (Type::Never, _) => Check::True, + (_, Type::Never) => Check::False, + + (Type::Atom, Type::Atom) => Check::True, + (Type::Bytes, Type::Bytes) => Check::True, + (Type::Bytes32, Type::Bytes32) => Check::True, + (Type::PublicKey, Type::PublicKey) => Check::True, + (Type::Int, Type::Int) => Check::True, + (Type::Nil, Type::Nil) => Check::True, + (Type::True, Type::True) => Check::True, + (Type::False, Type::False) => Check::True, + + (Type::Bytes32, Type::Atom) => Check::True, + (Type::PublicKey, Type::Atom) => Check::True, + (Type::Int, Type::Atom) => Check::True, + (Type::Bytes, Type::Atom) => Check::True, + (Type::Nil, Type::Atom) => Check::True, + + (Type::Atom, Type::Bytes) => Check::True, + (Type::Bytes32, Type::Bytes) => Check::True, + (Type::PublicKey, Type::Bytes) => Check::True, + (Type::Int, Type::Bytes) => Check::True, + (Type::Nil, Type::Bytes) => Check::True, + + (Type::Atom, Type::Int) => Check::True, + (Type::Bytes32, Type::Int) => Check::True, + (Type::PublicKey, Type::Int) => Check::True, + (Type::Bytes, Type::Int) => Check::True, + (Type::Nil, Type::Int) => Check::True, (Type::Atom, Type::Nil) => Check::Value(BigInt::ZERO), (Type::Atom, Type::PublicKey) => Check::Length(48), @@ -77,27 +77,27 @@ pub(crate) fn check_type( (Type::Int, Type::PublicKey) => Check::Length(48), (Type::Int, Type::Bytes32) => Check::Length(32), - (Type::PublicKey, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bytes32, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Nil, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Nil, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::PublicKey, Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bytes32, Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), - - (Type::True, Type::Atom) => Check::None, - (Type::False, Type::Atom) => Check::None, - (Type::True, Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::False, Type::Nil) => Check::None, - (Type::True, Type::False) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::False, Type::True) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::True, Type::Bytes) => Check::None, - (Type::False, Type::Bytes) => Check::None, - (Type::True, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::False, Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::True, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::False, Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::True, Type::Int) => Check::None, - (Type::False, Type::Int) => Check::None, + (Type::PublicKey, Type::Bytes32) => Check::False, + (Type::Bytes32, Type::PublicKey) => Check::False, + (Type::Nil, Type::PublicKey) => Check::False, + (Type::Nil, Type::Bytes32) => Check::False, + (Type::PublicKey, Type::Nil) => Check::False, + (Type::Bytes32, Type::Nil) => Check::False, + + (Type::True, Type::Atom) => Check::True, + (Type::False, Type::Atom) => Check::True, + (Type::True, Type::Nil) => Check::False, + (Type::False, Type::Nil) => Check::True, + (Type::True, Type::False) => Check::False, + (Type::False, Type::True) => Check::False, + (Type::True, Type::Bytes) => Check::True, + (Type::False, Type::Bytes) => Check::True, + (Type::True, Type::Bytes32) => Check::False, + (Type::False, Type::Bytes32) => Check::False, + (Type::True, Type::PublicKey) => Check::False, + (Type::False, Type::PublicKey) => Check::False, + (Type::True, Type::Int) => Check::True, + (Type::False, Type::Int) => Check::True, (Type::Atom, Type::True) => Check::Value(BigInt::one()), (Type::Atom, Type::False) => Check::Value(BigInt::ZERO), @@ -105,49 +105,49 @@ pub(crate) fn check_type( (Type::Bytes, Type::False) => Check::Value(BigInt::ZERO), (Type::Int, Type::True) => Check::Value(BigInt::one()), (Type::Int, Type::False) => Check::Value(BigInt::ZERO), - (Type::Bytes32, Type::True) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bytes32, Type::False) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::PublicKey, Type::True) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::PublicKey, Type::False) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Nil, Type::True) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Nil, Type::False) => Check::None, - - (Type::Value(..), Type::Atom) => Check::None, - (Type::Value(..), Type::Bytes) => Check::None, - (Type::Value(..), Type::Int) => Check::None, + (Type::Bytes32, Type::True) => Check::False, + (Type::Bytes32, Type::False) => Check::False, + (Type::PublicKey, Type::True) => Check::False, + (Type::PublicKey, Type::False) => Check::False, + (Type::Nil, Type::True) => Check::False, + (Type::Nil, Type::False) => Check::True, + + (Type::Value(..), Type::Atom) => Check::True, + (Type::Value(..), Type::Bytes) => Check::True, + (Type::Value(..), Type::Int) => Check::True, (Type::Value(value), Type::Bytes32) => { if bigint_to_bytes(value.clone()).len() == 32 { - Check::None + Check::True } else { - return Err(CheckError::Impossible(lhs, rhs)); + Check::False } } (Type::Value(value), Type::PublicKey) => { if bigint_to_bytes(value.clone()).len() == 48 { - Check::None + Check::True } else { - return Err(CheckError::Impossible(lhs, rhs)); + Check::False } } (Type::Value(value), Type::Nil) => { if value == &BigInt::ZERO { - Check::None + Check::True } else { - return Err(CheckError::Impossible(lhs, rhs)); + Check::False } } (Type::Value(value), Type::True) => { if value == &BigInt::one() { - Check::None + Check::True } else { - return Err(CheckError::Impossible(lhs, rhs)); + Check::False } } (Type::Value(value), Type::False) => { if value == &BigInt::ZERO { - Check::None + Check::True } else { - return Err(CheckError::Impossible(lhs, rhs)); + Check::False } } @@ -158,65 +158,65 @@ pub(crate) fn check_type( if bigint_to_bytes(value.clone()).len() == 32 { Check::Value(value.clone()) } else { - return Err(CheckError::Impossible(lhs, rhs)); + Check::False } } (Type::PublicKey, Type::Value(value)) => { if bigint_to_bytes(value.clone()).len() == 48 { Check::Value(value.clone()) } else { - return Err(CheckError::Impossible(lhs, rhs)); + Check::False } } (Type::Nil, Type::Value(value)) => { if value == &BigInt::ZERO { - Check::None + Check::True } else { - return Err(CheckError::Impossible(lhs, rhs)); + Check::False } } (Type::True, Type::Value(value)) => { if value == &BigInt::one() { - Check::None + Check::True } else { - return Err(CheckError::Impossible(lhs, rhs)); + Check::False } } (Type::False, Type::Value(value)) => { if value == &BigInt::ZERO { - Check::None + Check::True } else { - return Err(CheckError::Impossible(lhs, rhs)); + Check::False } } (Type::Value(lhs_value), Type::Value(rhs_value)) => { if lhs_value == rhs_value { - Check::None + Check::True } else { - return Err(CheckError::Impossible(lhs, rhs)); + Check::False } } - (Type::Atom, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bytes, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Bytes32, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::PublicKey, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Int, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Nil, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::True, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::False, Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Value(..), Type::Pair(..)) => return Err(CheckError::Impossible(lhs, rhs)), - - (Type::Pair(..), Type::Atom) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Pair(..), Type::Bytes) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Pair(..), Type::Bytes32) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Pair(..), Type::PublicKey) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Pair(..), Type::Int) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Pair(..), Type::Nil) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Pair(..), Type::True) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Pair(..), Type::False) => return Err(CheckError::Impossible(lhs, rhs)), - (Type::Pair(..), Type::Value(..)) => return Err(CheckError::Impossible(lhs, rhs)), + (Type::Atom, Type::Pair(..)) => Check::False, + (Type::Bytes, Type::Pair(..)) => Check::False, + (Type::Bytes32, Type::Pair(..)) => Check::False, + (Type::PublicKey, Type::Pair(..)) => Check::False, + (Type::Int, Type::Pair(..)) => Check::False, + (Type::Nil, Type::Pair(..)) => Check::False, + (Type::True, Type::Pair(..)) => Check::False, + (Type::False, Type::Pair(..)) => Check::False, + (Type::Value(..), Type::Pair(..)) => Check::False, + + (Type::Pair(..), Type::Atom) => Check::False, + (Type::Pair(..), Type::Bytes) => Check::False, + (Type::Pair(..), Type::Bytes32) => Check::False, + (Type::Pair(..), Type::PublicKey) => Check::False, + (Type::Pair(..), Type::Int) => Check::False, + (Type::Pair(..), Type::Nil) => Check::False, + (Type::Pair(..), Type::True) => Check::False, + (Type::Pair(..), Type::False) => Check::False, + (Type::Pair(..), Type::Value(..)) => Check::False, (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { let (lhs_first, lhs_rest) = (*lhs_first, *lhs_rest); @@ -229,6 +229,11 @@ pub(crate) fn check_type( ]) } + (Type::Union(items), _) => { + let items = items.clone(); + check_union_against_rhs(types, lhs, &items, rhs, visited)? + } + (_, Type::Union(items)) => { let mut result = Vec::new(); for item in items.clone() { @@ -236,11 +241,6 @@ pub(crate) fn check_type( } Check::Or(result) } - - (Type::Union(items), _) => { - let items = items.clone(); - check_union_against_rhs(types, lhs, &items, rhs, visited)? - } }; visited.remove(&(lhs, rhs)); @@ -258,7 +258,7 @@ fn check_union_against_rhs( let union = types.alloc(Type::Union(items.to_vec())); if types.compare(union, rhs) <= Comparison::Castable { - return Ok(Check::None); + return Ok(Check::True); } if let Type::Union(union) = types.get(rhs) { @@ -288,42 +288,40 @@ fn check_union_against_rhs( Type::Union(..) => unreachable!(), Type::Struct(..) => unreachable!(), Type::Callable(..) => unreachable!(), - Type::Unknown => Check::None, - Type::Generic => return Err(CheckError::Impossible(original_type_id, rhs)), - Type::Never => return Err(CheckError::Impossible(original_type_id, rhs)), - Type::Atom if attrs.all_atoms() => Check::None, + Type::Unknown => Check::True, + Type::Generic => Check::False, + Type::Never => Check::False, + Type::Atom if attrs.all_atoms() => Check::True, Type::Atom => Check::IsAtom, - Type::Bytes if attrs.all_atoms() => Check::None, + Type::Bytes if attrs.all_atoms() => Check::True, Type::Bytes => Check::IsAtom, - Type::Int if attrs.all_atoms() => Check::None, + Type::Int if attrs.all_atoms() => Check::True, Type::Int => Check::IsAtom, - Type::Nil if attrs.all_value(&BigInt::ZERO) => Check::None, + Type::Nil if attrs.all_value(&BigInt::ZERO) => Check::True, Type::Nil if attrs.atoms_are_value(&BigInt::ZERO) => Check::IsAtom, Type::Nil if attrs.all_atoms() => Check::Value(BigInt::ZERO), Type::Nil => Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]), - Type::False if attrs.all_value(&BigInt::ZERO) => Check::None, + Type::False if attrs.all_value(&BigInt::ZERO) => Check::True, Type::False if attrs.atoms_are_value(&BigInt::ZERO) => Check::IsAtom, Type::False if attrs.all_atoms() => Check::Value(BigInt::ZERO), Type::False => Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]), - Type::True if attrs.all_value(&BigInt::one()) => Check::None, + Type::True if attrs.all_value(&BigInt::one()) => Check::True, Type::True if attrs.atoms_are_value(&BigInt::ZERO) => Check::IsAtom, Type::True if attrs.all_atoms() => Check::Value(BigInt::one()), Type::True => Check::And(vec![Check::IsAtom, Check::Value(BigInt::one())]), - Type::Value(value) if attrs.all_value(value) => Check::None, + Type::Value(value) if attrs.all_value(value) => Check::True, Type::Value(value) if attrs.atoms_are_value(value) => Check::IsAtom, Type::Value(value) if attrs.all_atoms() => Check::Value(value.clone()), Type::Value(value) => Check::And(vec![Check::IsAtom, Check::Value(value.clone())]), - Type::Bytes32 if attrs.all_bytes32() => Check::None, + Type::Bytes32 if attrs.all_bytes32() => Check::True, Type::Bytes32 if attrs.all_atoms() => Check::Length(32), Type::Bytes32 if attrs.atoms_are_bytes32() => Check::IsAtom, Type::Bytes32 => Check::And(vec![Check::IsAtom, Check::Length(32)]), - Type::PublicKey if attrs.all_public_key() => Check::None, + Type::PublicKey if attrs.all_public_key() => Check::True, Type::PublicKey if attrs.all_atoms() => Check::Length(48), Type::PublicKey if attrs.atoms_are_public_key() => Check::IsAtom, Type::PublicKey => Check::And(vec![Check::IsAtom, Check::Length(48)]), - Type::Pair(..) if attrs.all_atoms() => { - return Err(CheckError::Impossible(original_type_id, rhs)) - } + Type::Pair(..) if attrs.all_atoms() => Check::False, Type::Pair(first, rest) => { let (first, rest) = (*first, *rest); @@ -369,13 +367,6 @@ mod tests { assert!(matches!(db.check(lhs, rhs), Err(CheckError::Recursive(..)))); } - fn check_impossible(db: &mut TypeSystem, lhs: TypeId, rhs: TypeId) { - assert!(matches!( - db.check(lhs, rhs), - Err(CheckError::Impossible(..)) - )); - } - #[test] fn test_check_any_bytes() { let mut db = TypeSystem::new(); @@ -545,7 +536,7 @@ mod tests { fn test_check_bytes32_public_key() { let mut db = TypeSystem::new(); let types = db.standard_types(); - check_impossible(&mut db, types.bytes32, types.public_key); + check_str(&mut db, types.bytes32, types.public_key, "0"); } #[test] diff --git a/crates/rue-typing/src/check/simplify_and.rs b/crates/rue-typing/src/check/simplify_and.rs index 7feb0f4..ae29557 100644 --- a/crates/rue-typing/src/check/simplify_and.rs +++ b/crates/rue-typing/src/check/simplify_and.rs @@ -27,7 +27,7 @@ pub(crate) fn simplify_and_shallow(items: impl IntoIterator) -> Ch for item in items { match item { - Check::None => continue, + Check::True => continue, Check::IsAtom if is_atom => continue, Check::IsAtom => is_atom = true, Check::IsPair if is_pair => continue, @@ -46,7 +46,7 @@ pub(crate) fn simplify_and_shallow(items: impl IntoIterator) -> Ch pub(crate) fn construct_and(mut items: Vec) -> Check { if items.is_empty() { - Check::None + Check::True } else if items.len() == 1 { items.remove(0) } else { @@ -60,21 +60,21 @@ mod tests { #[test] fn test_simplify_and_none() { - assert_eq!(simplify_and_shallow([Check::None]), Check::None); + assert_eq!(simplify_and_shallow([Check::True]), Check::True); } #[test] fn test_simplify_none_and_none() { assert_eq!( - simplify_and_shallow([Check::None, Check::None]), - Check::None + simplify_and_shallow([Check::True, Check::True]), + Check::True ); } #[test] fn test_simplify_check_and_none() { assert_eq!( - simplify_and_shallow([Check::IsAtom, Check::None]), + simplify_and_shallow([Check::IsAtom, Check::True]), Check::IsAtom ); } @@ -82,7 +82,7 @@ mod tests { #[test] fn test_simplify_none_and_check() { assert_eq!( - simplify_and_shallow([Check::None, Check::IsAtom]), + simplify_and_shallow([Check::True, Check::IsAtom]), Check::IsAtom ); } @@ -127,16 +127,16 @@ mod tests { #[test] fn test_simplify_and_shallow() { assert_eq!( - simplify_and_shallow([Check::And(vec![Check::None, Check::None])]), - Check::And(vec![Check::None, Check::None]) + simplify_and_shallow([Check::And(vec![Check::True, Check::True])]), + Check::And(vec![Check::True, Check::True]) ); } #[test] fn test_simplify_and_deep() { assert_eq!( - simplify_and_deep(vec![Check::And(vec![Check::None, Check::None])]), - Check::None + simplify_and_deep(vec![Check::And(vec![Check::True, Check::True])]), + Check::True ); } } diff --git a/crates/rue-typing/src/check/simplify_check.rs b/crates/rue-typing/src/check/simplify_check.rs index 290185a..9ad9cdc 100644 --- a/crates/rue-typing/src/check/simplify_check.rs +++ b/crates/rue-typing/src/check/simplify_check.rs @@ -4,7 +4,8 @@ use super::{simplify_and_deep, simplify_or_deep, Check}; pub(crate) fn simplify_check(check: Check) -> Check { match check { - Check::None => Check::None, + Check::True => Check::True, + Check::False => Check::False, Check::IsAtom => Check::IsAtom, Check::IsPair => Check::IsPair, Check::Value(value) => Check::Value(value), @@ -24,7 +25,7 @@ pub(crate) fn simplify_check(check: Check) -> Check { Check::If(Box::new(cond), Box::new(then), Box::new(else_)) } Check::First(first) => match simplify_check(*first) { - Check::None => Check::None, + Check::True => Check::True, Check::And(items) => Check::And( items .into_iter() @@ -40,7 +41,7 @@ pub(crate) fn simplify_check(check: Check) -> Check { first => Check::First(Box::new(first)), }, Check::Rest(rest) => match simplify_check(*rest) { - Check::None => Check::None, + Check::True => Check::True, Check::And(items) => Check::And( items .into_iter() diff --git a/crates/rue-typing/src/check/simplify_or.rs b/crates/rue-typing/src/check/simplify_or.rs index cd5f394..2b0b054 100644 --- a/crates/rue-typing/src/check/simplify_or.rs +++ b/crates/rue-typing/src/check/simplify_or.rs @@ -35,7 +35,7 @@ pub(crate) fn simplify_or_shallow(items: impl IntoIterator) -> Che for item in items { match item { - Check::None => return Check::None, + Check::True => return Check::True, Check::IsAtom if any_atom => continue, Check::IsPair if any_pair => continue, Check::IsAtom => { @@ -80,7 +80,7 @@ pub(crate) fn simplify_or_shallow(items: impl IntoIterator) -> Che match (atom_check, pair_check) { (ShapeCheck::None, ShapeCheck::None) => {} (ShapeCheck::Any, ShapeCheck::Any) => { - return Check::None; + return Check::True; } (ShapeCheck::Any, ShapeCheck::None) => { result.push(Check::IsAtom); @@ -155,27 +155,27 @@ mod tests { #[test] fn test_simplify_or_none() { - assert_eq!(simplify_or_shallow([Check::None]), Check::None); + assert_eq!(simplify_or_shallow([Check::True]), Check::True); } #[test] fn test_simplify_none_or_none() { - assert_eq!(simplify_or_shallow([Check::None, Check::None]), Check::None); + assert_eq!(simplify_or_shallow([Check::True, Check::True]), Check::True); } #[test] fn test_simplify_check_or_none() { assert_eq!( - simplify_or_shallow([Check::IsAtom, Check::None]), - Check::None + simplify_or_shallow([Check::IsAtom, Check::True]), + Check::True ); } #[test] fn test_simplify_none_or_check() { assert_eq!( - simplify_or_shallow([Check::None, Check::IsAtom]), - Check::None + simplify_or_shallow([Check::True, Check::IsAtom]), + Check::True ); } @@ -196,7 +196,7 @@ mod tests { fn test_simplify_atom_or_pair() { assert_eq!( simplify_or_shallow([Check::IsAtom, Check::IsPair]), - Check::None + Check::True ); } @@ -204,7 +204,7 @@ mod tests { fn test_simplify_pair_or_atom() { assert_eq!( simplify_or_shallow([Check::IsPair, Check::IsAtom]), - Check::None + Check::True ); } diff --git a/crates/rue-typing/src/check/stringify_check.rs b/crates/rue-typing/src/check/stringify_check.rs index f4338ab..e627ad9 100644 --- a/crates/rue-typing/src/check/stringify_check.rs +++ b/crates/rue-typing/src/check/stringify_check.rs @@ -10,7 +10,8 @@ pub(crate) fn stringify_check( path: &mut Vec, ) -> fmt::Result { match check { - Check::None => write!(f, "1"), + Check::True => write!(f, "1"), + Check::False => write!(f, "0"), Check::IsPair => { write!(f, "(l ")?; stringify_value(f, path)?; diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index d94d9b0..299933b 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -46,8 +46,8 @@ impl Default for TypeSystem { names.insert(public_key, "PublicKey".to_string()); names.insert(int, "Int".to_string()); names.insert(bool, "Bool".to_string()); - names.insert(true_bool, "true".to_string()); - names.insert(false_bool, "false".to_string()); + names.insert(true_bool, "True".to_string()); + names.insert(false_bool, "False".to_string()); names.insert(nil, "Nil".to_string()); names.insert(any, "Any".to_string()); From 41bd46ba0b50b77595ca988bdef18530f43b0145 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 11:28:42 -0400 Subject: [PATCH 052/100] Add enums and variants --- crates/rue-typing/src/check/attributes.rs | 2 + crates/rue-typing/src/check/check_type.rs | 53 +++++++++++++++++++++++ crates/rue-typing/src/comparison.rs | 22 ++++++++++ crates/rue-typing/src/difference.rs | 51 +++++++++++++++++++++- crates/rue-typing/src/semantic_types.rs | 26 ++++++----- crates/rue-typing/src/stringify.rs | 4 +- crates/rue-typing/src/substitute_type.rs | 45 ++++++++++++++++++- crates/rue-typing/src/ty.rs | 4 +- 8 files changed, 190 insertions(+), 17 deletions(-) diff --git a/crates/rue-typing/src/check/attributes.rs b/crates/rue-typing/src/check/attributes.rs index a1bda49..9f87244 100644 --- a/crates/rue-typing/src/check/attributes.rs +++ b/crates/rue-typing/src/check/attributes.rs @@ -86,6 +86,8 @@ pub(crate) fn union_attributes( Type::Alias(..) => unreachable!(), Type::Struct(..) => unreachable!(), Type::Callable(..) => unreachable!(), + Type::Enum(..) => unreachable!(), + Type::Variant(..) => unreachable!(), Type::Generic => { length -= 1; } diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs index bf8043f..1a38063 100644 --- a/crates/rue-typing/src/check/check_type.rs +++ b/crates/rue-typing/src/check/check_type.rs @@ -27,6 +27,8 @@ pub(crate) fn check_type( (Type::Alias(..), _) | (_, Type::Alias(..)) => unreachable!(), (Type::Struct(..), _) | (_, Type::Struct(..)) => unreachable!(), (Type::Callable(..), _) | (_, Type::Callable(..)) => unreachable!(), + (Type::Enum(..), _) | (_, Type::Enum(..)) => unreachable!(), + (Type::Variant(..), _) | (_, Type::Variant(..)) => unreachable!(), // TODO: Implement generic type checking? (Type::Generic, _) => Check::True, @@ -288,6 +290,8 @@ fn check_union_against_rhs( Type::Union(..) => unreachable!(), Type::Struct(..) => unreachable!(), Type::Callable(..) => unreachable!(), + Type::Enum(..) => unreachable!(), + Type::Variant(..) => unreachable!(), Type::Unknown => Check::True, Type::Generic => Check::False, Type::Never => Check::False, @@ -636,4 +640,53 @@ mod tests { ); check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) 0))"); } + + #[test] + fn test_check_condition_agg_sig_me() { + let mut db = TypeSystem::new(); + let types = db.standard_types(); + + let opcode = db.alloc(Type::Value(BigInt::from(49))); + let agg_sig_unsafe = alloc_struct( + &mut db, + &indexmap! { + "opcode".to_string() => opcode, + "public_key".to_string() => types.public_key, + "message".to_string() => types.bytes, + }, + Rest::Nil, + ); + + let opcode = db.alloc(Type::Value(BigInt::from(50))); + let agg_sig_me = alloc_struct( + &mut db, + &indexmap! { + "opcode".to_string() => opcode, + "public_key".to_string() => types.public_key, + "message".to_string() => types.bytes, + }, + Rest::Nil, + ); + + let condition = db.alloc(Type::Union(vec![agg_sig_unsafe, agg_sig_me])); + + let lhs = db.substitute( + condition, + HashMap::new(), + Semantics::StructuralOnly { + callable: types.any, + }, + ); + + let rhs = db.substitute( + agg_sig_me, + HashMap::new(), + Semantics::StructuralOnly { + callable: types.never, + }, + ); + + check_str(&mut db, lhs, rhs, "(= (f val) 50)"); + check_str(&mut db, types.any, rhs, "(and (l val) (not (l (f val))) (= (f val) 50) (l (r val)) (not (l (f (r val)))) (= (strlen (f (r val))) 48) (l (r (r val))) (not (l (f (r (r val))))) (not (l (r (r (r val))))) (= (r (r (r val))) 0))"); + } } diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index 9cb6f21..dd26d04 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -310,6 +310,28 @@ pub(crate) fn compare_type( Comparison::Castable, ), + (Type::Variant(variant), Type::Enum(ty)) => { + let comparison = compare_type(db, variant.type_id, ty.type_id, ctx); + + if variant.enum_type == rhs { + max(comparison, Comparison::Assignable) + } else { + max(comparison, Comparison::Castable) + } + } + + (Type::Enum(ty), _) => max(compare_type(db, ty.type_id, rhs, ctx), Comparison::Castable), + (_, Type::Enum(ty)) => max(compare_type(db, lhs, ty.type_id, ctx), Comparison::Castable), + + (Type::Variant(lhs), _) => max( + compare_type(db, lhs.type_id, rhs, ctx), + Comparison::Castable, + ), + (_, Type::Variant(rhs)) => max( + compare_type(db, lhs, rhs.type_id, ctx), + Comparison::Castable, + ), + (Type::Callable(lhs), Type::Callable(rhs)) => max( compare_type(db, lhs.parameters, rhs.parameters, ctx), compare_type(db, lhs.return_type, rhs.return_type, ctx), diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index b23014e..ec1932e 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use num_bigint::BigInt; use num_traits::One; -use crate::{bigint_to_bytes, StandardTypes, Struct, Type, TypeId, TypeSystem}; +use crate::{bigint_to_bytes, Enum, StandardTypes, Struct, Type, TypeId, TypeSystem, Variant}; pub(crate) fn difference_type( types: &mut TypeSystem, @@ -270,6 +270,55 @@ pub(crate) fn difference_type( generic_types: ty.generic_types, })) } + + (Type::Enum(ty), _) => { + let ty = *ty; + let type_id = difference_type(types, std, ty.type_id, rhs, visited); + + types.alloc(Type::Enum(Enum { + original_type_id: Some(ty.original_type_id.unwrap_or(lhs)), + type_id, + has_fields: ty.has_fields, + })) + } + (_, Type::Enum(ty)) => { + let ty = *ty; + let type_id = difference_type(types, std, lhs, ty.type_id, visited); + + types.alloc(Type::Enum(Enum { + original_type_id: Some(ty.original_type_id.unwrap_or(rhs)), + type_id, + has_fields: ty.has_fields, + })) + } + + (Type::Variant(variant), _) => { + let variant = variant.clone(); + let type_id = difference_type(types, std, variant.type_id, rhs, visited); + + types.alloc(Type::Variant(Variant { + original_type_id: Some(variant.original_type_id.unwrap_or(lhs)), + enum_type: variant.enum_type, + field_names: variant.field_names, + type_id, + rest: variant.rest, + generic_types: variant.generic_types, + })) + } + (_, Type::Variant(variant)) => { + let variant = variant.clone(); + let type_id = difference_type(types, std, lhs, variant.type_id, visited); + + types.alloc(Type::Variant(Variant { + original_type_id: Some(variant.original_type_id.unwrap_or(rhs)), + enum_type: variant.enum_type, + field_names: variant.field_names, + type_id, + rest: variant.rest, + generic_types: variant.generic_types, + })) + } + (Type::Callable(..), _) => lhs, (_, Type::Callable(..)) => lhs, }; diff --git a/crates/rue-typing/src/semantic_types.rs b/crates/rue-typing/src/semantic_types.rs index 2d3a3bb..7f42435 100644 --- a/crates/rue-typing/src/semantic_types.rs +++ b/crates/rue-typing/src/semantic_types.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use indexmap::IndexSet; -use num_bigint::BigInt; use crate::TypeId; @@ -56,26 +55,29 @@ pub struct Callable { } /// Represents an enum type which can have multiple variants. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct Enum { - pub original_variants: Vec, + /// A pointer to the enum from which this was derived, if any. + pub original_type_id: Option, + /// The structural type of the enum. + pub type_id: TypeId, + /// Whether the enum semantically has fields. pub has_fields: bool, } /// Represents a variant type which can optionally have fields. #[derive(Debug, Clone)] pub struct Variant { + /// A pointer to the variant from which this was derived, if any. pub original_type_id: Option, + /// The enum type to which this variant belongs. pub enum_type: TypeId, + /// The field names of the variant. pub field_names: IndexSet, - pub fields: Option, - pub generic_types: Vec, - pub discriminant: BigInt, -} - -/// Field information for a variant type. -#[derive(Debug, Clone, Copy)] -pub struct VariantFields { - pub fields: TypeId, + /// The structural type of the enum variant. + pub type_id: TypeId, + /// The rest kind of the variant. pub rest: Rest, + /// The generic types of the variant. + pub generic_types: Vec, } diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs index a289c7b..7066fdd 100644 --- a/crates/rue-typing/src/stringify.rs +++ b/crates/rue-typing/src/stringify.rs @@ -1,6 +1,6 @@ use std::collections::{HashMap, HashSet}; -use crate::{Callable, Struct, Type, TypeId, TypeSystem}; +use crate::{Callable, Enum, Struct, Type, TypeId, TypeSystem, Variant}; pub(crate) fn stringify_type( types: &TypeSystem, @@ -50,6 +50,8 @@ pub(crate) fn stringify_type( Type::Lazy(lazy) => stringify_type(types, lazy.type_id, names, visited), Type::Alias(alias) => stringify_type(types, alias.type_id, names, visited), Type::Struct(Struct { type_id, .. }) => stringify_type(types, *type_id, names, visited), + Type::Variant(Variant { type_id, .. }) => stringify_type(types, *type_id, names, visited), + Type::Enum(Enum { type_id, .. }) => stringify_type(types, *type_id, names, visited), Type::Callable(Callable { parameters, return_type, diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index e6ab0d4..972ef5e 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -1,6 +1,6 @@ use std::collections::{HashMap, HashSet}; -use crate::{Alias, Callable, Type, TypeId, TypeSystem}; +use crate::{Alias, Callable, Enum, Struct, Type, TypeId, TypeSystem, Variant}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Semantics { @@ -102,7 +102,7 @@ pub(crate) fn substitute_type( if new_type_id == ty.type_id { type_id } else { - types.alloc(Type::Struct(crate::Struct { + types.alloc(Type::Struct(Struct { original_type_id: Some(ty.original_type_id.unwrap_or(type_id)), type_id: new_type_id, field_names: ty.field_names, @@ -114,6 +114,47 @@ pub(crate) fn substitute_type( new_type_id } } + Type::Variant(ty) => { + let ty = ty.clone(); + + let new_type_id = substitute_type(types, ty.type_id, substitutions, semantics, visited); + + if semantics == Semantics::Preserve { + if new_type_id == ty.type_id { + type_id + } else { + types.alloc(Type::Variant(Variant { + original_type_id: Some(ty.original_type_id.unwrap_or(type_id)), + enum_type: ty.enum_type, + type_id: new_type_id, + field_names: ty.field_names, + rest: ty.rest, + generic_types: ty.generic_types, + })) + } + } else { + new_type_id + } + } + Type::Enum(ty) => { + let ty = *ty; + + let new_type_id = substitute_type(types, ty.type_id, substitutions, semantics, visited); + + if semantics == Semantics::Preserve { + if new_type_id == ty.type_id { + type_id + } else { + types.alloc(Type::Enum(Enum { + original_type_id: Some(ty.original_type_id.unwrap_or(type_id)), + type_id: new_type_id, + has_fields: ty.has_fields, + })) + } + } else { + new_type_id + } + } Type::Callable(callable) => { let callable = callable.clone(); diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs index 6bd2d60..74899b4 100644 --- a/crates/rue-typing/src/ty.rs +++ b/crates/rue-typing/src/ty.rs @@ -1,6 +1,6 @@ use num_bigint::BigInt; -use crate::{Alias, Callable, Lazy, Struct, TypeId}; +use crate::{Alias, Callable, Enum, Lazy, Struct, TypeId, Variant}; #[derive(Debug, Clone)] pub enum Type { @@ -23,4 +23,6 @@ pub enum Type { Alias(Alias), Struct(Struct), Callable(Callable), + Enum(Enum), + Variant(Variant), } From 0591b69c49e1aac3617ef48216533e4acaf8be35 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 11:30:23 -0400 Subject: [PATCH 053/100] Dependency --- Cargo.lock | 1 + Cargo.toml | 9 +++++---- crates/rue-compiler/Cargo.toml | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 831c0fc..df43a2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1410,6 +1410,7 @@ dependencies = [ "rowan", "rue-clvm", "rue-parser", + "rue-typing", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d0df60e..b6da478 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,10 +37,11 @@ too_many_lines = "allow" match_same_arms = "allow" [workspace.dependencies] -rue-parser = { path = "./crates/rue-parser", version = "0.1.0" } -rue-compiler = { path = "./crates/rue-compiler", version = "0.1.0" } -rue-clvm = { path = "./crates/rue-clvm", version = "0.1.0" } -rue-lexer = { path = "./crates/rue-lexer", version = "0.1.0" } +rue-parser = { path = "./crates/rue-parser", version = "0.1.1" } +rue-compiler = { path = "./crates/rue-compiler", version = "0.1.1" } +rue-typing = { path = "./crates/rue-typing", version = "0.1.1" } +rue-clvm = { path = "./crates/rue-clvm", version = "0.1.1" } +rue-lexer = { path = "./crates/rue-lexer", version = "0.1.1" } clvmr_old = { version = "0.3.2", package = "clvmr" } clvmr = "0.6.1" clap = "4.5.4" diff --git a/crates/rue-compiler/Cargo.toml b/crates/rue-compiler/Cargo.toml index 51afcef..bbdb618 100644 --- a/crates/rue-compiler/Cargo.toml +++ b/crates/rue-compiler/Cargo.toml @@ -17,6 +17,7 @@ workspace = true [dependencies] rue-parser = { workspace = true } rue-clvm = { workspace = true } +rue-typing = { workspace = true } clvmr = { workspace = true } id-arena = { workspace = true } indexmap = { workspace = true } From 0aab1c015b165121597f7c279a5e09e25908011c Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 11:33:53 -0400 Subject: [PATCH 054/100] Temp 1 --- crates/rue-compiler/src/compiler.rs | 93 +- crates/rue-compiler/src/database.rs | 23 +- crates/rue-compiler/src/database/ids.rs | 8 +- .../rue-compiler/src/database/type_system.rs | 841 ------------------ crates/rue-compiler/src/dependency_graph.rs | 2 +- crates/rue-compiler/src/lowerer.rs | 2 +- crates/rue-compiler/src/scope.rs | 3 +- crates/rue-compiler/src/symbol.rs | 7 +- crates/rue-compiler/src/value.rs | 6 +- crates/rue-compiler/src/value/ty.rs | 69 -- 10 files changed, 17 insertions(+), 1037 deletions(-) delete mode 100644 crates/rue-compiler/src/database/type_system.rs delete mode 100644 crates/rue-compiler/src/value/ty.rs diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index ad7cc33..ead431d 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -5,13 +5,14 @@ use std::collections::HashMap; pub(crate) use builtins::Builtins; use indexmap::IndexSet; use rowan::TextRange; +use rue_typing::TypeId; use symbol_table::SymbolTable; use crate::{ - database::{Database, HirId, ScopeId, SymbolId, TypeId}, + database::{Database, HirId, ScopeId, SymbolId}, hir::{Hir, Op}, scope::Scope, - value::{GuardPath, Mutation, PairType, Type, TypeOverride, Value}, + value::{GuardPath, Mutation, TypeOverride, Value}, ErrorKind, }; @@ -25,8 +26,6 @@ mod stmt; mod symbol_table; mod ty; -#[cfg(test)] -pub use builtins::*; pub use context::*; /// Responsible for lowering the AST into the HIR. @@ -134,91 +133,7 @@ impl<'a> Compiler<'a> { stack.insert(ty); - let name = match self.db.ty(ty) { - Type::Unknown => "{unknown}".to_string(), - Type::Generic => "{generic}".to_string(), - Type::Nil => "Nil".to_string(), - Type::Any => "Any".to_string(), - Type::Int => "Int".to_string(), - Type::Bool => "Bool".to_string(), - Type::Bytes => "Bytes".to_string(), - Type::Bytes32 => "Bytes32".to_string(), - Type::PublicKey => "PublicKey".to_string(), - Type::List(items) => { - let inner = self.type_name_visitor(*items, stack); - format!("{inner}[]") - } - Type::Pair(PairType { first, rest }) => { - let first = self.type_name_visitor(*first, stack); - let rest = self.type_name_visitor(*rest, stack); - format!("({first}, {rest})") - } - Type::Struct(struct_type) => { - if struct_type.original_type_id == ty { - let fields: Vec = struct_type - .fields - .iter() - .map(|(name, ty)| { - format!("{}: {}", name, self.type_name_visitor(*ty, stack)) - }) - .collect(); - - format!("{{{}}}", fields.join(", ")) - } else { - self.type_name_visitor(struct_type.original_type_id, stack) - } - } - Type::Enum { .. } => "".to_string(), - Type::EnumVariant(enum_variant) => { - let enum_name = self.type_name_visitor(enum_variant.enum_type, stack); - - let fields: Option> = enum_variant.fields.as_ref().map(|fields| { - fields - .iter() - .map(|(name, ty)| { - format!("{}: {}", name, self.type_name_visitor(*ty, stack)) - }) - .collect() - }); - - let variant_name = match self.db.ty(enum_variant.enum_type) { - Type::Enum(enum_type) => enum_type - .variants - .iter() - .find(|item| *item.1 == enum_variant.original_type_id) - .expect("enum type is missing variant") - .0 - .clone(), - _ => unreachable!(), - }; - - if let Some(fields) = fields { - format!("{enum_name}::{variant_name} {{{}}}", fields.join(", ")) - } else { - format!("{enum_name}::{variant_name}") - } - } - Type::Function(function_type) => { - let params: Vec = function_type - .param_types - .iter() - .map(|&ty| self.type_name_visitor(ty, stack)) - .collect(); - - let ret = self.type_name_visitor(function_type.return_type, stack); - - format!("fun({}) -> {}", params.join(", "), ret) - } - Type::Alias(..) => unreachable!(), - Type::Nullable(ty) => { - let inner = self.type_name_visitor(*ty, stack); - format!("{inner}?") - } - Type::Optional(ty) => { - let inner = self.type_name_visitor(*ty, stack); - format!("optional {inner}") - } - }; + let name = match self.db.ty(ty) {}; stack.pop().unwrap(); diff --git a/crates/rue-compiler/src/database.rs b/crates/rue-compiler/src/database.rs index 6c2f9e4..eef5a0e 100644 --- a/crates/rue-compiler/src/database.rs +++ b/crates/rue-compiler/src/database.rs @@ -5,10 +5,10 @@ use rue_parser::SyntaxToken; mod comparison; mod ids; -mod type_system; pub use comparison::*; pub use ids::*; +use rue_typing::TypeId; use crate::{ environment::Environment, @@ -17,7 +17,6 @@ use crate::{ mir::Mir, scope::Scope, symbol::Symbol, - value::Type, Diagnostic, DiagnosticKind, ErrorKind, WarningKind, }; @@ -26,7 +25,6 @@ pub struct Database { diagnostics: Vec, scopes: Arena, symbols: Arena, - types: Arena, hir: Arena, mir: Arena, lir: Arena, @@ -49,10 +47,6 @@ impl Database { SymbolId(self.symbols.alloc(symbol)) } - pub(crate) fn alloc_type(&mut self, ty: Type) -> TypeId { - TypeId(self.types.alloc(ty)) - } - pub(crate) fn alloc_hir(&mut self, hir: Hir) -> HirId { HirId(self.hir.alloc(hir)) } @@ -77,17 +71,6 @@ impl Database { &self.symbols[id.0] } - pub fn ty_raw(&self, id: TypeId) -> &Type { - &self.types[id.0] - } - - pub fn ty(&self, mut id: TypeId) -> &Type { - while let Type::Alias(alias) = self.ty_raw(id) { - id = *alias; - } - self.ty_raw(id) - } - pub fn hir(&self, id: HirId) -> &Hir { &self.hir[id.0] } @@ -104,10 +87,6 @@ impl Database { &self.environments[id.0] } - pub(crate) fn ty_mut(&mut self, id: TypeId) -> &mut Type { - &mut self.types[id.0] - } - pub(crate) fn env_mut(&mut self, id: EnvironmentId) -> &mut Environment { &mut self.environments[id.0] } diff --git a/crates/rue-compiler/src/database/ids.rs b/crates/rue-compiler/src/database/ids.rs index 00cffdd..489eab6 100644 --- a/crates/rue-compiler/src/database/ids.rs +++ b/crates/rue-compiler/src/database/ids.rs @@ -1,9 +1,6 @@ use id_arena::Id; -use crate::{ - environment::Environment, hir::Hir, lir::Lir, mir::Mir, scope::Scope, symbol::Symbol, - value::Type, -}; +use crate::{environment::Environment, hir::Hir, lir::Lir, mir::Mir, scope::Scope, symbol::Symbol}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct SymbolId(pub(super) Id); @@ -11,9 +8,6 @@ pub struct SymbolId(pub(super) Id); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ScopeId(pub(super) Id); -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct TypeId(pub(super) Id); - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct HirId(pub(super) Id); diff --git a/crates/rue-compiler/src/database/type_system.rs b/crates/rue-compiler/src/database/type_system.rs deleted file mode 100644 index 555d8aa..0000000 --- a/crates/rue-compiler/src/database/type_system.rs +++ /dev/null @@ -1,841 +0,0 @@ -use std::collections::{HashMap, HashSet}; - -use crate::{ - value::{EnumType, EnumVariantType, FunctionType, PairType, StructType, Type}, - Comparison, Database, TypeId, -}; - -#[derive(Debug)] -struct ComparisonContext<'a> { - visited: HashSet<(TypeId, TypeId)>, - generic_type_stack: &'a mut Vec>, - infer_generics: bool, -} - -impl<'a> ComparisonContext<'a> { - fn new(generic_type_stack: &'a mut Vec>, infer_generics: bool) -> Self { - Self { - visited: HashSet::new(), - generic_type_stack, - infer_generics, - } - } -} - -impl Database { - pub fn substitute_type( - &mut self, - type_id: TypeId, - substitutions: &HashMap, - ) -> TypeId { - self.substitute_type_visitor(type_id, substitutions, &mut HashSet::new()) - } - - pub fn compare_type(&self, lhs: TypeId, rhs: TypeId) -> Comparison { - self.compare_type_visitor( - lhs, - rhs, - &mut ComparisonContext::new(&mut Vec::new(), false), - ) - } - - pub fn compare_type_with_generics( - &self, - lhs: TypeId, - rhs: TypeId, - generic_type_stack: &mut Vec>, - ) -> Comparison { - self.compare_type_visitor( - lhs, - rhs, - &mut ComparisonContext::new(generic_type_stack, true), - ) - } - - pub fn is_cyclic(&self, type_id: TypeId) -> bool { - self.is_cyclic_visitor(type_id, &mut HashSet::new()) - } - - pub fn first_type(&self, type_id: TypeId) -> Option { - let Type::Pair(pair) = self.ty(type_id) else { - return None; - }; - Some(pair.first) - } - - pub fn rest_type(&self, type_id: TypeId) -> Option { - let Type::Pair(pair) = self.ty(type_id) else { - return None; - }; - Some(pair.rest) - } - - pub fn non_nullable(&mut self, ty: TypeId) -> TypeId { - match self.ty(ty) { - Type::Nullable(inner) => self.non_nullable(*inner), - _ => ty, - } - } - - pub fn non_undefined(&mut self, ty: TypeId) -> TypeId { - match self.ty(ty) { - Type::Optional(inner) => self.non_undefined(*inner), - _ => ty, - } - } - - pub fn unwrap_list(&mut self, ty: TypeId) -> Option { - match self.ty(ty) { - Type::List(inner) => Some(*inner), - _ => None, - } - } - - pub fn substitute_type_visitor( - &mut self, - type_id: TypeId, - substitutions: &HashMap, - visited: &mut HashSet, - ) -> TypeId { - if let Some(type_id) = substitutions.get(&type_id).copied() { - return type_id; - } - - if !visited.insert(type_id) { - return type_id; - } - - match self.ty(type_id).clone() { - Type::Alias(..) => unreachable!(), - Type::Pair(pair) => { - let new_pair = PairType { - first: self.substitute_type_visitor(pair.first, substitutions, visited), - rest: self.substitute_type_visitor(pair.rest, substitutions, visited), - }; - - if new_pair == pair { - type_id - } else { - self.alloc_type(Type::Pair(new_pair)) - } - } - Type::Enum(enum_type) => { - let new_enum = EnumType { - has_fields: enum_type.has_fields, - variants: enum_type - .variants - .iter() - .map(|(k, v)| { - ( - k.clone(), - self.substitute_type_visitor(*v, substitutions, visited), - ) - }) - .collect(), - }; - - if new_enum == enum_type { - type_id - } else { - self.alloc_type(Type::Enum(new_enum)) - } - } - Type::EnumVariant(enum_variant) => { - let new_variant = EnumVariantType { - enum_type: enum_variant.enum_type, - original_type_id: enum_variant.original_type_id, - fields: enum_variant.fields.as_ref().map(|fields| { - fields - .iter() - .map(|(k, v)| { - ( - k.clone(), - self.substitute_type_visitor(*v, substitutions, visited), - ) - }) - .collect() - }), - rest: enum_variant.rest, - discriminant: enum_variant.discriminant, - }; - - if new_variant == enum_variant { - type_id - } else { - self.alloc_type(Type::EnumVariant(new_variant)) - } - } - Type::List(inner) => { - let new_inner = self.substitute_type_visitor(inner, substitutions, visited); - - if new_inner == inner { - type_id - } else { - self.alloc_type(Type::List(new_inner)) - } - } - Type::Struct(struct_type) => { - let new_struct = StructType { - original_type_id: struct_type.original_type_id, - fields: struct_type - .fields - .iter() - .map(|(k, v)| { - ( - k.clone(), - self.substitute_type_visitor(*v, substitutions, visited), - ) - }) - .collect(), - rest: struct_type.rest, - }; - - if new_struct == struct_type { - type_id - } else { - self.alloc_type(Type::Struct(new_struct)) - } - } - Type::Function(function) => { - let new_function = FunctionType { - param_types: function - .param_types - .iter() - .map(|ty| self.substitute_type_visitor(*ty, substitutions, visited)) - .collect(), - rest: function.rest, - return_type: self.substitute_type_visitor( - function.return_type, - substitutions, - visited, - ), - generic_types: function - .generic_types - .iter() - .map(|ty| self.substitute_type_visitor(*ty, substitutions, visited)) - .collect(), - }; - - if new_function == function { - type_id - } else { - self.alloc_type(Type::Function(new_function)) - } - } - Type::Nullable(inner) => { - let new_inner = self.substitute_type_visitor(inner, substitutions, visited); - - if new_inner == inner { - type_id - } else { - self.alloc_type(Type::Nullable(inner)) - } - } - Type::Optional(inner) => { - let new_inner = self.substitute_type_visitor(inner, substitutions, visited); - - if new_inner == inner { - type_id - } else { - self.alloc_type(Type::Optional(inner)) - } - } - Type::Unknown - | Type::Generic - | Type::Any - | Type::Bool - | Type::Bytes - | Type::Bytes32 - | Type::Int - | Type::Nil - | Type::PublicKey => type_id, - } - } - - #[allow(clippy::match_same_arms, clippy::too_many_lines)] - fn compare_type_visitor( - &self, - lhs: TypeId, - rhs: TypeId, - ctx: &mut ComparisonContext<'_>, - ) -> Comparison { - let key = (lhs, rhs); - if lhs == rhs || !ctx.visited.insert(key) { - return Comparison::Equal; - } - - let comparison = match (self.ty(lhs), self.ty(rhs)) { - // Aliases are already resolved at this point. - (Type::Alias(..), _) | (_, Type::Alias(..)) => unreachable!(), - - // We need to infer `Generic` types, and return `Unrelated` if incompatible. - (_, Type::Generic) => { - let mut found = None; - - for generics in ctx.generic_type_stack.iter().rev() { - if let Some(&generic) = generics.get(&rhs) { - found = Some(generic); - } - } - - if let Some(found) = found { - self.compare_type_visitor(lhs, found, ctx) - } else if ctx.infer_generics { - ctx.generic_type_stack.last_mut().unwrap().insert(rhs, lhs); - Comparison::Assignable - } else { - Comparison::Unrelated - } - } - - // We should have already given a diagnostic for `Unknown`. - // So we will go ahead and pretend they are equal to everything. - (Type::Unknown, _) | (_, Type::Unknown) => Comparison::Equal, - - // Possibly undefined is a special type. - (Type::Optional(..), _) | (_, Type::Optional(..)) => Comparison::Unrelated, - - // These are of course equal atomic types. - (Type::Any, Type::Any) => Comparison::Equal, - (Type::Int, Type::Int) => Comparison::Equal, - (Type::Bool, Type::Bool) => Comparison::Equal, - (Type::Bytes, Type::Bytes) => Comparison::Equal, - (Type::Bytes32, Type::Bytes32) => Comparison::Equal, - (Type::PublicKey, Type::PublicKey) => Comparison::Equal, - (Type::Nil, Type::Nil) => Comparison::Equal, - - // We should treat `Bytes32` as a subtype of `Bytes` and therefore equal. - // `PublicKey` however, can have different meaning, so is not equal. - // `Bytes` is also not equal to `Bytes32` since it's unsized. - (Type::Bytes32, Type::Bytes) => Comparison::Equal, - - // You can cast `Any` to anything, but it's not implicit. - // So many languages make `Any` implicit and it makes it hard to debug. - (Type::Any, _) => Comparison::Castable, - - // However, anything can be assigned to `Any` implicitly. - (_, Type::Any) => Comparison::Assignable, - - // `Generic` types are unrelated to everything else except their specific instance. - // The only exception is the `Any` type above. - (Type::Generic, _) => Comparison::Unrelated, - - // You have to explicitly convert between other atom types. - // This is because the compiled output can change depending on the type. - (Type::Int, Type::Bytes) => Comparison::Castable, - (Type::Bool, Type::Bytes) => Comparison::Castable, - (Type::Bool, Type::Int) => Comparison::Castable, - (Type::Nil, Type::Int) => Comparison::Castable, - (Type::Nil, Type::Bool) => Comparison::Castable, - (Type::PublicKey, Type::Bytes) => Comparison::Castable, - (Type::PublicKey, Type::Int) => Comparison::Castable, - (Type::Bytes, Type::Int) => Comparison::Castable, - (Type::Bytes32, Type::Int) => Comparison::Castable, - - // Let's allow assigning `Nil` to `Bytes` for ease of use. - // The only alternative without needing a cast is empty strings. - (Type::Nil, Type::Bytes) => Comparison::Assignable, - - // Size changing conversions are not possible without a type guard. - (Type::Bytes, Type::Bytes32) => Comparison::Superset, - (Type::Bytes, Type::PublicKey) => Comparison::Superset, - (Type::Bytes, Type::Nil) => Comparison::Superset, - (Type::Bytes, Type::Bool) => Comparison::Superset, - (Type::Int, Type::Bytes32) => Comparison::Superset, - (Type::Int, Type::PublicKey) => Comparison::Superset, - (Type::Int, Type::Nil) => Comparison::Superset, - (Type::Int, Type::Bool) => Comparison::Superset, - (Type::Bool, Type::PublicKey) => Comparison::Unrelated, - (Type::Bool, Type::Bytes32) => Comparison::Unrelated, - (Type::Bool, Type::Nil) => Comparison::Unrelated, - (Type::Nil, Type::Bytes32) => Comparison::Unrelated, - (Type::Nil, Type::PublicKey) => Comparison::Unrelated, - (Type::PublicKey, Type::Bytes32) => Comparison::Unrelated, - (Type::PublicKey, Type::Bool) => Comparison::Unrelated, - (Type::PublicKey, Type::Nil) => Comparison::Unrelated, - (Type::Bytes32, Type::PublicKey) => Comparison::Unrelated, - (Type::Bytes32, Type::Bool) => Comparison::Unrelated, - (Type::Bytes32, Type::Nil) => Comparison::Unrelated, - - // These are the variants of the `Nullable` type. - (Type::Nil, Type::Nullable(..)) => Comparison::Assignable, - (Type::Nullable(lhs), Type::Nullable(rhs)) => { - self.compare_type_visitor(*lhs, *rhs, ctx) - } - (_, Type::Nullable(inner)) => self.compare_type_visitor(lhs, *inner, ctx), - (Type::Nullable(inner), Type::Bytes) => { - Comparison::Castable & self.compare_type_visitor(*inner, rhs, ctx) - } - - // TODO: Unions would make this more generalized and useful. - // I should add unions back. - (Type::Nullable(_inner), _) => Comparison::Unrelated, - - // Compare both sides of a `Pair`. - (Type::Pair(lhs), Type::Pair(rhs)) => { - let first = self.compare_type_visitor(lhs.first, rhs.first, ctx); - let rest = self.compare_type_visitor(lhs.rest, rhs.rest, ctx); - first & rest - } - - // A `Pair` is a valid `List` if its first type is the same as the list's inner type - // and the rest is also a valid `List` of the same type. - // However, it's not considered equal but rather assignable. - (Type::Pair(pair), Type::List(inner)) => { - let inner = self.compare_type_visitor(pair.first, *inner, ctx); - let rest = self.compare_type_visitor(pair.rest, rhs, ctx); - Comparison::Assignable & inner & rest - } - - // A `List` is not a valid pair since `Nil` is also a valid list. - // It's a `Superset` only if the opposite comparison is not `Unrelated`. - (Type::List(..), Type::Pair(..)) => { - Comparison::Superset & self.compare_type_visitor(rhs, lhs, ctx) - } - - // Nothing else can be assigned to or from a `Pair`. - (Type::Pair(..), _) | (_, Type::Pair(..)) => Comparison::Unrelated, - - // A `List` just compares with the inner type of another `List`. - (Type::List(lhs), Type::List(rhs)) => self.compare_type_visitor(*lhs, *rhs, ctx), - - // `Nil` is a valid list. - (Type::Nil, Type::List(..)) => Comparison::Assignable, - (Type::List(..), Type::Nil) => Comparison::Superset, - - // `List` is not compatible with atoms. - (Type::Bytes, Type::List(..)) => Comparison::Unrelated, - (Type::Bytes32, Type::List(..)) => Comparison::Unrelated, - (Type::PublicKey, Type::List(..)) => Comparison::Unrelated, - (Type::Int, Type::List(..)) => Comparison::Unrelated, - (Type::Bool, Type::List(..)) => Comparison::Unrelated, - (Type::List(..), Type::Bytes) => Comparison::Unrelated, - (Type::List(..), Type::Bytes32) => Comparison::Unrelated, - (Type::List(..), Type::PublicKey) => Comparison::Unrelated, - (Type::List(..), Type::Int) => Comparison::Unrelated, - (Type::List(..), Type::Bool) => Comparison::Unrelated, - - // A `Struct` is castable to others only if the fields are identical. - (Type::Struct(lhs), Type::Struct(rhs)) => { - if lhs.fields.len() == rhs.fields.len() { - let mut result = Comparison::Castable; - for i in 0..lhs.fields.len() { - result &= self.compare_type_visitor(lhs.fields[i], rhs.fields[i], ctx); - } - result - } else { - Comparison::Unrelated - } - } - (Type::Struct(..), _) | (_, Type::Struct(..)) => Comparison::Unrelated, - - // Enum variants are assignable only to their respective enum. - (Type::EnumVariant(variant_type), Type::Enum(..)) => { - if variant_type.enum_type == rhs { - Comparison::Assignable - } else { - Comparison::Unrelated - } - } - - // But not the other way around. - (Type::Enum(..), Type::EnumVariant(variant_type)) => { - if variant_type.enum_type == lhs { - Comparison::Superset - } else { - Comparison::Unrelated - } - } - - // You can cast numeric enums to `Int`. - (Type::EnumVariant(variant_type), Type::Bytes | Type::Int) => { - self.compare_type_visitor(variant_type.enum_type, rhs, ctx) - } - - (Type::Enum(enum_type), Type::Bytes | Type::Int) => { - if enum_type.has_fields { - Comparison::Unrelated - } else { - Comparison::Castable - } - } - - // Enums and their variants are not assignable to anything else. - (Type::Enum(..), _) | (_, Type::Enum(..)) => Comparison::Unrelated, - (Type::EnumVariant(..), _) | (_, Type::EnumVariant(..)) => Comparison::Unrelated, - - // The parameters and return types of functions are checked. - // This can be made more flexible later. - (Type::Function(lhs), Type::Function(rhs)) => { - if lhs.param_types.len() != rhs.param_types.len() || lhs.rest != rhs.rest { - Comparison::Unrelated - } else { - let mut result = - self.compare_type_visitor(lhs.return_type, rhs.return_type, ctx); - - for i in 0..lhs.param_types.len() { - result &= - self.compare_type_visitor(lhs.param_types[i], rhs.param_types[i], ctx); - } - - result - } - } - - // There is likely nothing that should relate to a function. - (Type::Function(..), _) | (_, Type::Function(..)) => Comparison::Unrelated, - }; - - ctx.visited.remove(&key); - comparison - } - - fn is_cyclic_visitor(&self, ty: TypeId, visited_aliases: &mut HashSet) -> bool { - match self.ty_raw(ty).clone() { - Type::Pair(pair) => { - self.is_cyclic_visitor(pair.first, visited_aliases) - || self.is_cyclic_visitor(pair.rest, visited_aliases) - } - Type::Alias(alias) => { - if !visited_aliases.insert(alias) { - return true; - } - self.is_cyclic_visitor(alias, visited_aliases) - } - Type::List(..) - | Type::Struct(..) - | Type::Enum(..) - | Type::EnumVariant(..) - | Type::Function(..) - | Type::Unknown - | Type::Generic - | Type::Nil - | Type::Any - | Type::Int - | Type::Bool - | Type::Bytes - | Type::Bytes32 - | Type::PublicKey => false, - Type::Nullable(ty) | Type::Optional(ty) => self.is_cyclic_visitor(ty, visited_aliases), - } - } -} - -#[cfg(test)] -mod tests { - use indexmap::IndexMap; - - use crate::{ - compiler::{builtins, Builtins}, - value::{EnumType, EnumVariantType, FunctionType, PairType, Rest, StructType}, - }; - - use super::*; - - fn setup() -> (Database, Builtins) { - let mut db = Database::new(); - let ty = builtins(&mut db); - (db, ty) - } - - fn fields(items: &[TypeId]) -> IndexMap { - let mut fields = IndexMap::new(); - for (i, item) in items.iter().enumerate() { - fields.insert(format!("field_{i}"), *item); - } - fields - } - - #[test] - fn test_substitution() { - let (mut db, ty) = setup(); - - let a = db.alloc_type(Type::Generic); - let b = db.alloc_type(Type::Generic); - - let a_list = db.alloc_type(Type::List(a)); - - let function = db.alloc_type(Type::Function(FunctionType { - param_types: vec![a_list], - rest: Rest::Nil, - return_type: b, - generic_types: vec![a, b], - })); - - let int_list = db.alloc_type(Type::List(ty.int)); - - let substitutions = [(a, ty.int), (b, ty.bool)].into_iter().collect(); - let substituted = db.substitute_type(function, &substitutions); - - let expected = db.alloc_type(Type::Function(FunctionType { - param_types: vec![int_list], - rest: Rest::Nil, - return_type: ty.bool, - generic_types: vec![a, b], - })); - - assert_eq!(db.compare_type(substituted, expected), Comparison::Equal); - } - - #[test] - fn test_alias_resolution() { - let (mut db, ty) = setup(); - - let int_alias = db.alloc_type(Type::Alias(ty.int)); - assert_eq!(db.compare_type(int_alias, ty.int), Comparison::Equal); - assert_eq!(db.compare_type(ty.int, int_alias), Comparison::Equal); - assert_eq!(db.compare_type(int_alias, int_alias), Comparison::Equal); - - let double_alias = db.alloc_type(Type::Alias(int_alias)); - assert_eq!(db.compare_type(double_alias, ty.int), Comparison::Equal); - assert_eq!(db.compare_type(ty.int, double_alias), Comparison::Equal); - assert_eq!( - db.compare_type(double_alias, double_alias), - Comparison::Equal - ); - assert_eq!(db.compare_type(double_alias, int_alias), Comparison::Equal); - assert_eq!(db.compare_type(int_alias, double_alias), Comparison::Equal); - } - - #[test] - fn test_generic_type() { - let (mut db, ty) = setup(); - let a = db.alloc_type(Type::Generic); - let b = db.alloc_type(Type::Generic); - assert_eq!(db.compare_type(a, b), Comparison::Unrelated); - assert_eq!(db.compare_type(a, ty.int), Comparison::Unrelated); - assert_eq!(db.compare_type(ty.int, a), Comparison::Unrelated); - assert_eq!(db.compare_type(a, a), Comparison::Equal); - } - - #[test] - fn test_any_type() { - let (db, ty) = setup(); - assert_eq!(db.compare_type(ty.any, ty.int), Comparison::Castable); - assert_eq!(db.compare_type(ty.any, ty.any), Comparison::Equal); - assert_eq!(db.compare_type(ty.int, ty.any), Comparison::Assignable); - } - - #[test] - fn test_unknown_type() { - let (db, ty) = setup(); - assert_eq!(db.compare_type(ty.any, ty.unknown), Comparison::Equal); - assert_eq!(db.compare_type(ty.int, ty.unknown), Comparison::Equal); - assert_eq!(db.compare_type(ty.unknown, ty.int), Comparison::Equal); - assert_eq!(db.compare_type(ty.unknown, ty.any), Comparison::Equal); - } - - #[test] - fn test_atom_types() { - let (db, ty) = setup(); - assert_eq!(db.compare_type(ty.bytes, ty.bytes32), Comparison::Superset); - assert_eq!(db.compare_type(ty.bytes32, ty.bytes), Comparison::Equal); - assert_eq!( - db.compare_type(ty.bytes, ty.public_key), - Comparison::Superset - ); - assert_eq!( - db.compare_type(ty.public_key, ty.bytes), - Comparison::Castable - ); - assert_eq!(db.compare_type(ty.bytes, ty.int), Comparison::Castable); - assert_eq!(db.compare_type(ty.int, ty.bytes), Comparison::Castable); - assert_eq!(db.compare_type(ty.int, ty.int), Comparison::Equal); - } - - #[test] - fn test_nil_type() { - let (mut db, ty) = setup(); - - let list = db.alloc_type(Type::List(ty.int)); - assert_eq!(db.compare_type(ty.nil, ty.int), Comparison::Castable); - assert_eq!(db.compare_type(ty.nil, ty.bool), Comparison::Castable); - assert_eq!(db.compare_type(ty.nil, ty.bytes), Comparison::Assignable); - assert_eq!(db.compare_type(ty.nil, ty.bytes32), Comparison::Unrelated); - assert_eq!( - db.compare_type(ty.nil, ty.public_key), - Comparison::Unrelated - ); - assert_eq!(db.compare_type(ty.nil, list), Comparison::Assignable); - } - - #[test] - fn test_pair_type() { - let (mut db, ty) = setup(); - - let int_pair = db.alloc_type(Type::Pair(PairType { - first: ty.int, - rest: ty.int, - })); - assert_eq!(db.compare_type(int_pair, int_pair), Comparison::Equal); - - let bytes_pair = db.alloc_type(Type::Pair(PairType { - first: ty.bytes, - rest: ty.bytes, - })); - assert_eq!(db.compare_type(int_pair, bytes_pair), Comparison::Castable); - assert_eq!(db.compare_type(bytes_pair, int_pair), Comparison::Castable); - - let bytes32_pair = db.alloc_type(Type::Pair(PairType { - first: ty.bytes32, - rest: ty.bytes32, - })); - assert_eq!( - db.compare_type(bytes_pair, bytes32_pair), - Comparison::Superset - ); - assert_eq!(db.compare_type(bytes32_pair, bytes_pair), Comparison::Equal); - - let bytes32_bytes = db.alloc_type(Type::Pair(PairType { - first: ty.bytes32, - rest: ty.bytes, - })); - assert_eq!( - db.compare_type(bytes_pair, bytes32_bytes), - Comparison::Superset - ); - assert_eq!( - db.compare_type(bytes32_bytes, bytes_pair), - Comparison::Equal - ); - } - - #[test] - fn test_list_types() { - let (mut db, ty) = setup(); - - let int_list = db.alloc_type(Type::List(ty.int)); - assert_eq!(db.compare_type(int_list, int_list), Comparison::Equal); - - let pair_list = db.alloc_type(Type::Pair(PairType { - first: ty.int, - rest: int_list, - })); - assert_eq!(db.compare_type(pair_list, int_list), Comparison::Assignable); - assert_eq!(db.compare_type(int_list, pair_list), Comparison::Superset); - - let pair_nil = db.alloc_type(Type::Pair(PairType { - first: ty.int, - rest: ty.nil, - })); - assert_eq!(db.compare_type(pair_nil, int_list), Comparison::Assignable); - - let pair_unrelated_first = db.alloc_type(Type::Pair(PairType { - first: pair_list, - rest: ty.nil, - })); - assert_eq!( - db.compare_type(pair_unrelated_first, int_list), - Comparison::Unrelated - ); - - let pair_unrelated_rest = db.alloc_type(Type::Pair(PairType { - first: ty.int, - rest: ty.int, - })); - assert_eq!( - db.compare_type(pair_unrelated_rest, int_list), - Comparison::Unrelated - ); - } - - #[test] - fn test_struct_types() { - let (mut db, ty) = setup(); - - let two_ints = db.alloc_type(Type::Unknown); - *db.ty_mut(two_ints) = Type::Struct(StructType { - original_type_id: two_ints, - fields: fields(&[ty.int, ty.int]), - rest: Rest::Nil, - }); - assert_eq!(db.compare_type(two_ints, two_ints), Comparison::Equal); - - let one_int = db.alloc_type(Type::Unknown); - *db.ty_mut(one_int) = Type::Struct(StructType { - original_type_id: one_int, - fields: fields(&[ty.int]), - rest: Rest::Nil, - }); - assert_eq!(db.compare_type(one_int, two_ints), Comparison::Unrelated); - - let empty_struct = db.alloc_type(Type::Unknown); - *db.ty_mut(empty_struct) = Type::Struct(StructType { - original_type_id: empty_struct, - fields: fields(&[]), - rest: Rest::Nil, - }); - assert_eq!( - db.compare_type(empty_struct, empty_struct), - Comparison::Equal - ); - } - - #[test] - fn test_enum_types() { - let (mut db, ty) = setup(); - - let enum_type = db.alloc_type(Type::Unknown); - - let variant = db.alloc_type(Type::Unknown); - *db.ty_mut(variant) = Type::EnumVariant(EnumVariantType { - enum_type, - original_type_id: variant, - fields: Some(fields(&[ty.int])), - discriminant: ty.unknown_hir, - rest: Rest::Nil, - }); - - *db.ty_mut(enum_type) = Type::Enum(EnumType { - has_fields: true, - variants: fields(&[variant]), - }); - - assert_eq!(db.compare_type(enum_type, variant), Comparison::Superset); - assert_eq!(db.compare_type(variant, enum_type), Comparison::Assignable); - } - - #[test] - fn test_function_types() { - let (mut db, ty) = setup(); - - let int_to_bool = db.alloc_type(Type::Function(FunctionType { - param_types: vec![ty.int], - rest: Rest::Nil, - return_type: ty.bool, - generic_types: Vec::new(), - })); - assert_eq!(db.compare_type(int_to_bool, int_to_bool), Comparison::Equal); - - let int_to_int = db.alloc_type(Type::Function(FunctionType { - param_types: vec![ty.int], - rest: Rest::Nil, - return_type: ty.int, - generic_types: Vec::new(), - })); - assert_eq!( - db.compare_type(int_to_bool, int_to_int), - Comparison::Castable - ); - assert_eq!( - db.compare_type(int_to_int, int_to_bool), - Comparison::Superset - ); - - let int_list = db.alloc_type(Type::List(ty.int)); - let int_list_to_bool = db.alloc_type(Type::Function(FunctionType { - param_types: vec![int_list], - rest: Rest::Nil, - return_type: ty.bool, - generic_types: Vec::new(), - })); - assert_eq!( - db.compare_type(int_to_bool, int_list_to_bool), - Comparison::Unrelated - ); - assert_eq!( - db.compare_type(int_list_to_bool, int_to_bool), - Comparison::Unrelated - ); - } -} diff --git a/crates/rue-compiler/src/dependency_graph.rs b/crates/rue-compiler/src/dependency_graph.rs index 5727086..a74963f 100644 --- a/crates/rue-compiler/src/dependency_graph.rs +++ b/crates/rue-compiler/src/dependency_graph.rs @@ -1,11 +1,11 @@ use indexmap::{IndexMap, IndexSet}; use rowan::TextRange; +use rue_typing::Rest; use crate::{ environment::Environment, hir::Hir, symbol::{Function, Module, Symbol}, - value::Rest, Database, EnvironmentId, ErrorKind, HirId, ScopeId, SymbolId, }; diff --git a/crates/rue-compiler/src/lowerer.rs b/crates/rue-compiler/src/lowerer.rs index e81155d..38b4dec 100644 --- a/crates/rue-compiler/src/lowerer.rs +++ b/crates/rue-compiler/src/lowerer.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use indexmap::IndexSet; +use rue_typing::Rest; use crate::{ dependency_graph::DependencyGraph, @@ -8,7 +9,6 @@ use crate::{ hir::Hir, mir::Mir, symbol::{Function, Symbol}, - value::Rest, Database, EnvironmentId, HirId, MirId, ScopeId, SymbolId, }; diff --git a/crates/rue-compiler/src/scope.rs b/crates/rue-compiler/src/scope.rs index 328bb3a..f9d9a35 100644 --- a/crates/rue-compiler/src/scope.rs +++ b/crates/rue-compiler/src/scope.rs @@ -1,6 +1,7 @@ use indexmap::IndexMap; +use rue_typing::TypeId; -use crate::{database::TypeId, SymbolId}; +use crate::SymbolId; #[derive(Debug, Default)] pub struct Scope { diff --git a/crates/rue-compiler/src/symbol.rs b/crates/rue-compiler/src/symbol.rs index 9491aae..e8c4d16 100644 --- a/crates/rue-compiler/src/symbol.rs +++ b/crates/rue-compiler/src/symbol.rs @@ -1,8 +1,9 @@ use indexmap::IndexSet; +use rue_typing::{Callable, TypeId}; use crate::{ - database::{HirId, ScopeId, TypeId}, - value::{FunctionType, Value}, + database::{HirId, ScopeId}, + value::Value, SymbolId, }; @@ -45,7 +46,7 @@ impl Symbol { pub struct Function { pub scope_id: ScopeId, pub hir_id: HirId, - pub ty: FunctionType, + pub ty: Callable, } #[derive(Debug, Clone)] diff --git a/crates/rue-compiler/src/value.rs b/crates/rue-compiler/src/value.rs index 64db5be..5560356 100644 --- a/crates/rue-compiler/src/value.rs +++ b/crates/rue-compiler/src/value.rs @@ -2,13 +2,13 @@ use std::collections::HashMap; mod guard; mod guard_path; -mod ty; pub use guard::*; pub use guard_path::*; -pub use ty::*; -use crate::{HirId, TypeId}; +use rue_typing::TypeId; + +use crate::HirId; #[derive(Debug, Clone)] pub struct Value { diff --git a/crates/rue-compiler/src/value/ty.rs b/crates/rue-compiler/src/value/ty.rs deleted file mode 100644 index e724aed..0000000 --- a/crates/rue-compiler/src/value/ty.rs +++ /dev/null @@ -1,69 +0,0 @@ -use indexmap::IndexMap; - -use crate::database::{HirId, TypeId}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Type { - Unknown, - Generic, - Nil, - Any, - Int, - Bool, - Bytes, - Bytes32, - PublicKey, - Pair(PairType), - List(TypeId), - Struct(StructType), - Enum(EnumType), - EnumVariant(EnumVariantType), - Function(FunctionType), - Alias(TypeId), - Nullable(TypeId), - Optional(TypeId), -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct PairType { - pub first: TypeId, - pub rest: TypeId, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct StructType { - pub original_type_id: TypeId, - pub fields: IndexMap, - pub rest: Rest, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct EnumType { - pub has_fields: bool, - pub variants: IndexMap, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct EnumVariantType { - pub enum_type: TypeId, - pub original_type_id: TypeId, - pub fields: Option>, - pub rest: Rest, - pub discriminant: HirId, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct FunctionType { - pub generic_types: Vec, - pub param_types: Vec, - pub rest: Rest, - pub return_type: TypeId, -} - -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -pub enum Rest { - #[default] - Nil, - Spread, - Optional, -} From b6102174e39763c773f92e4613e64359f67955cf Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 11:47:24 -0400 Subject: [PATCH 055/100] Temp 2 --- crates/rue-compiler/src/compiler.rs | 52 ++++--- crates/rue-compiler/src/compiler/builtins.rs | 135 +++++++++---------- crates/rue-compiler/src/compiler/ty.rs | 3 +- crates/rue-compiler/src/value/guard.rs | 2 +- crates/rue-typing/src/check/check_type.rs | 66 ++++----- crates/rue-typing/src/comparison.rs | 48 +++---- crates/rue-typing/src/stringify.rs | 6 +- crates/rue-typing/src/test_tools.rs | 10 +- crates/rue-typing/src/type_system.rs | 2 +- 9 files changed, 157 insertions(+), 167 deletions(-) diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index ead431d..32ad1c1 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -3,9 +3,9 @@ use std::collections::HashMap; pub(crate) use builtins::Builtins; -use indexmap::IndexSet; + use rowan::TextRange; -use rue_typing::TypeId; +use rue_typing::{Comparison, TypeId, TypeSystem}; use symbol_table::SymbolTable; use crate::{ @@ -34,6 +34,9 @@ pub struct Compiler<'a> { // The database is mutable because we need to allocate new symbols and types. db: &'a mut Database, + // The type system is responsible for type checking and type inference. + ty: TypeSystem, + // The scope stack is used to keep track of the current scope. scope_stack: Vec, @@ -67,6 +70,7 @@ impl<'a> Compiler<'a> { pub fn new(db: &'a mut Database, builtins: Builtins) -> Self { Self { db, + ty: TypeSystem::new(), scope_stack: vec![builtins.scope_id], symbol_stack: Vec::new(), type_definition_stack: Vec::new(), @@ -116,39 +120,29 @@ impl<'a> Compiler<'a> { None } - fn type_name(&self, ty: TypeId) -> String { - self.type_name_visitor(ty, &mut IndexSet::new()) - } + fn type_name(&self, type_id: TypeId) -> String { + let mut names = HashMap::new(); - fn type_name_visitor(&self, ty: TypeId, stack: &mut IndexSet) -> String { - for &scope_id in self.scope_stack.iter().rev() { - if let Some(name) = self.db.scope(scope_id).type_name(ty) { - return name.to_string(); + for &scope_id in self.scope_stack.iter() { + for type_id in self.db.scope(scope_id).local_types() { + if let Some(name) = self.db.scope(scope_id).type_name(type_id) { + names.insert(type_id, name.to_string()); + } } } - if stack.contains(&ty) { - return "".to_string(); - } - - stack.insert(ty); - - let name = match self.db.ty(ty) {}; - - stack.pop().unwrap(); - - name + self.ty.stringify_named(type_id, names) } fn type_check(&mut self, from: TypeId, to: TypeId, range: TextRange) { let comparison = if self.allow_generic_inference_stack.last().copied().unwrap() { - self.db - .compare_type_with_generics(from, to, &mut self.generic_type_stack) + self.ty + .compare_with_generics(from, to, &mut self.generic_type_stack, true) } else { - self.db.compare_type(from, to) + self.ty.compare(from, to) }; - if !comparison.is_assignable() { + if comparison > Comparison::Assignable { self.db.error( ErrorKind::TypeMismatch(self.type_name(from), self.type_name(to)), range, @@ -158,13 +152,13 @@ impl<'a> Compiler<'a> { fn cast_check(&mut self, from: TypeId, to: TypeId, range: TextRange) { let comparison = if self.allow_generic_inference_stack.last().copied().unwrap() { - self.db - .compare_type_with_generics(from, to, &mut self.generic_type_stack) + self.ty + .compare_with_generics(from, to, &mut self.generic_type_stack, true) } else { - self.db.compare_type(from, to) + self.ty.compare(from, to) }; - if !comparison.is_castable() { + if comparison > Comparison::Castable { self.db.error( ErrorKind::CastMismatch(self.type_name(from), self.type_name(to)), range, @@ -173,7 +167,7 @@ impl<'a> Compiler<'a> { } fn unknown(&self) -> Value { - Value::new(self.builtins.unknown_hir, self.builtins.unknown) + Value::new(self.builtins.unknown, self.ty.std().unknown) } fn symbol_type(&self, guard_path: &GuardPath) -> Option { diff --git a/crates/rue-compiler/src/compiler/builtins.rs b/crates/rue-compiler/src/compiler/builtins.rs index 46d908b..ea8d2f6 100644 --- a/crates/rue-compiler/src/compiler/builtins.rs +++ b/crates/rue-compiler/src/compiler/builtins.rs @@ -1,69 +1,47 @@ +use indexmap::indexset; use rowan::TextRange; +use rue_typing::{Callable, Rest, Type, TypeSystem}; use crate::{ hir::{BinOp, Hir, Op}, scope::Scope, symbol::{Function, Symbol}, - value::{FunctionType, PairType, Rest, Type}, - Database, HirId, ScopeId, SymbolId, TypeId, + Database, HirId, ScopeId, SymbolId, }; /// These are the built-in types and most commonly used HIR nodes. pub struct Builtins { pub scope_id: ScopeId, - pub any: TypeId, - pub int: TypeId, - pub bool: TypeId, - pub bytes: TypeId, - pub bytes32: TypeId, - pub public_key: TypeId, - pub nil: TypeId, - pub nil_hir: HirId, - pub unknown: TypeId, - pub unknown_hir: HirId, + pub nil: HirId, + pub unknown: HirId, } /// Defines intrinsics that cannot be implemented in Rue. -pub fn builtins(db: &mut Database) -> Builtins { +pub fn builtins(db: &mut Database, ty: &mut TypeSystem) -> Builtins { let mut scope = Scope::default(); - let int = db.alloc_type(Type::Int); - let bool = db.alloc_type(Type::Bool); - let bytes = db.alloc_type(Type::Bytes); - let bytes32 = db.alloc_type(Type::Bytes32); - let public_key = db.alloc_type(Type::PublicKey); - let any = db.alloc_type(Type::Any); - let nil = db.alloc_type(Type::Nil); - let nil_hir = db.alloc_hir(Hir::Atom(Vec::new())); - let unknown = db.alloc_type(Type::Unknown); - let unknown_hir = db.alloc_hir(Hir::Unknown); - - scope.define_type("Nil".to_string(), nil); - scope.define_type("Int".to_string(), int); - scope.define_type("Bool".to_string(), bool); - scope.define_type("Bytes".to_string(), bytes); - scope.define_type("Bytes32".to_string(), bytes32); - scope.define_type("PublicKey".to_string(), public_key); - scope.define_type("Any".to_string(), any); + let std = ty.standard_types(); + let nil = db.alloc_hir(Hir::Atom(Vec::new())); + let unknown = db.alloc_hir(Hir::Unknown); + + scope.define_type("Nil".to_string(), std.nil); + scope.define_type("Int".to_string(), std.int); + scope.define_type("Bool".to_string(), std.bool); + scope.define_type("Bytes".to_string(), std.bytes); + scope.define_type("Bytes32".to_string(), std.bytes32); + scope.define_type("PublicKey".to_string(), std.public_key); + scope.define_type("Any".to_string(), std.any); let builtins = Builtins { scope_id: db.alloc_scope(scope), - any, - int, - bool, - bytes, - bytes32, - public_key, nil, - nil_hir, unknown, - unknown_hir, }; - let sha256 = sha256(db, &builtins); - let pubkey_for_exp = pubkey_for_exp(db, &builtins); - let divmod = divmod(db, &builtins); - let substr = substr(db, &builtins); + let sha256 = sha256(db, ty); + let pubkey_for_exp = pubkey_for_exp(db, ty); + let divmod = divmod(db, ty); + let substr = substr(db, ty); db.scope_mut(builtins.scope_id) .define_symbol("sha256".to_string(), sha256); @@ -80,10 +58,12 @@ pub fn builtins(db: &mut Database) -> Builtins { builtins } -fn sha256(db: &mut Database, builtins: &Builtins) -> SymbolId { +fn sha256(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { + let std = ty.standard_types(); + let mut scope = Scope::default(); - let param = db.alloc_symbol(Symbol::Parameter(builtins.bytes)); + let param = db.alloc_symbol(Symbol::Parameter(std.bytes)); scope.define_symbol("bytes".to_string(), param); let param_ref = db.alloc_hir(Hir::Reference(param, TextRange::default())); let hir_id = db.alloc_hir(Hir::Op(Op::Sha256, param_ref)); @@ -92,18 +72,22 @@ fn sha256(db: &mut Database, builtins: &Builtins) -> SymbolId { db.alloc_symbol(Symbol::InlineFunction(Function { scope_id, hir_id, - ty: FunctionType { - param_types: vec![builtins.bytes], + ty: Callable { + original_type_id: None, + parameter_names: indexset!["bytes".to_string()], + parameters: ty.alloc(Type::Pair(std.bytes, std.nil)), rest: Rest::Nil, - return_type: builtins.bytes32, + return_type: std.bytes32, generic_types: Vec::new(), }, })) } -fn pubkey_for_exp(db: &mut Database, builtins: &Builtins) -> SymbolId { +fn pubkey_for_exp(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { + let std = ty.standard_types(); + let mut scope = Scope::default(); - let param = db.alloc_symbol(Symbol::Parameter(builtins.bytes32)); + let param = db.alloc_symbol(Symbol::Parameter(std.bytes32)); scope.define_symbol("exponent".to_string(), param); let param_ref = db.alloc_hir(Hir::Reference(param, TextRange::default())); let hir_id = db.alloc_hir(Hir::Op(Op::PubkeyForExp, param_ref)); @@ -112,19 +96,24 @@ fn pubkey_for_exp(db: &mut Database, builtins: &Builtins) -> SymbolId { db.alloc_symbol(Symbol::InlineFunction(Function { scope_id, hir_id, - ty: FunctionType { - param_types: vec![builtins.bytes32], + ty: Callable { + original_type_id: None, + parameter_names: indexset!["exponent".to_string()], + parameters: ty.alloc(Type::Pair(std.bytes32, std.nil)), rest: Rest::Nil, - return_type: builtins.public_key, + return_type: std.public_key, generic_types: Vec::new(), }, })) } -fn divmod(db: &mut Database, builtins: &Builtins) -> SymbolId { +fn divmod(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { + let std = ty.standard_types(); + let mut scope = Scope::default(); - let lhs = db.alloc_symbol(Symbol::Parameter(builtins.int)); - let rhs = db.alloc_symbol(Symbol::Parameter(builtins.int)); + + let lhs = db.alloc_symbol(Symbol::Parameter(std.int)); + let rhs = db.alloc_symbol(Symbol::Parameter(std.int)); scope.define_symbol("lhs".to_string(), lhs); scope.define_symbol("rhs".to_string(), rhs); let lhs_ref = db.alloc_hir(Hir::Reference(lhs, TextRange::default())); @@ -132,16 +121,15 @@ fn divmod(db: &mut Database, builtins: &Builtins) -> SymbolId { let hir_id = db.alloc_hir(Hir::BinaryOp(BinOp::DivMod, lhs_ref, rhs_ref)); let scope_id = db.alloc_scope(scope); - let int_pair = db.alloc_type(Type::Pair(PairType { - first: builtins.int, - rest: builtins.int, - })); + let int_pair = ty.alloc(Type::Pair(std.int, std.int)); db.alloc_symbol(Symbol::InlineFunction(Function { scope_id, hir_id, - ty: FunctionType { - param_types: vec![builtins.int, builtins.int], + ty: Callable { + original_type_id: None, + parameter_names: indexset!["lhs".to_string(), "rhs".to_string()], + parameters: ty.alloc(Type::Pair(int_pair, std.nil)), rest: Rest::Nil, return_type: int_pair, generic_types: Vec::new(), @@ -149,11 +137,14 @@ fn divmod(db: &mut Database, builtins: &Builtins) -> SymbolId { })) } -fn substr(db: &mut Database, builtins: &Builtins) -> SymbolId { +fn substr(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { + let std = ty.standard_types(); + let mut scope = Scope::default(); - let value = db.alloc_symbol(Symbol::Parameter(builtins.bytes)); - let start = db.alloc_symbol(Symbol::Parameter(builtins.int)); - let end = db.alloc_symbol(Symbol::Parameter(builtins.int)); + + let value = db.alloc_symbol(Symbol::Parameter(std.bytes)); + let start = db.alloc_symbol(Symbol::Parameter(std.int)); + let end = db.alloc_symbol(Symbol::Parameter(std.int)); scope.define_symbol("value".to_string(), value); scope.define_symbol("start".to_string(), start); scope.define_symbol("end".to_string(), end); @@ -163,13 +154,19 @@ fn substr(db: &mut Database, builtins: &Builtins) -> SymbolId { let hir_id = db.alloc_hir(Hir::Substr(value_ref, start_ref, end_ref)); let scope_id = db.alloc_scope(scope); + let end = ty.alloc(Type::Pair(std.int, std.nil)); + let int = ty.alloc(Type::Pair(std.int, end)); + let parameters = ty.alloc(Type::Pair(std.bytes, int)); + db.alloc_symbol(Symbol::InlineFunction(Function { scope_id, hir_id, - ty: FunctionType { - param_types: vec![builtins.bytes, builtins.int, builtins.int], + ty: Callable { + original_type_id: None, + parameter_names: indexset!["value".to_string(), "start".to_string(), "end".to_string()], + parameters, rest: Rest::Nil, - return_type: builtins.bytes, + return_type: std.bytes, generic_types: Vec::new(), }, })) diff --git a/crates/rue-compiler/src/compiler/ty.rs b/crates/rue-compiler/src/compiler/ty.rs index adcf983..8714a2c 100644 --- a/crates/rue-compiler/src/compiler/ty.rs +++ b/crates/rue-compiler/src/compiler/ty.rs @@ -1,6 +1,5 @@ use rue_parser::{AstNode, Type}; - -use crate::TypeId; +use rue_typing::TypeId; use super::Compiler; diff --git a/crates/rue-compiler/src/value/guard.rs b/crates/rue-compiler/src/value/guard.rs index 351a083..302b82f 100644 --- a/crates/rue-compiler/src/value/guard.rs +++ b/crates/rue-compiler/src/value/guard.rs @@ -1,6 +1,6 @@ use std::ops::Not; -use crate::TypeId; +use rue_typing::TypeId; #[derive(Debug, Clone, Copy)] pub struct Guard { diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs index 1a38063..2a26ce0 100644 --- a/crates/rue-typing/src/check/check_type.rs +++ b/crates/rue-typing/src/check/check_type.rs @@ -374,14 +374,14 @@ mod tests { #[test] fn test_check_any_bytes() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.any, types.bytes, "(not (l val))"); } #[test] fn test_check_any_bytes32() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str( &mut db, types.any, @@ -393,7 +393,7 @@ mod tests { #[test] fn test_check_any_public_key() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str( &mut db, types.any, @@ -405,14 +405,14 @@ mod tests { #[test] fn test_check_any_int() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.any, types.int, "(not (l val))"); } #[test] fn test_check_any_bool() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str( &mut db, types.any, @@ -424,7 +424,7 @@ mod tests { #[test] fn test_check_any_nil() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str( &mut db, types.any, @@ -436,63 +436,63 @@ mod tests { #[test] fn test_check_bytes_bytes() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.bytes, types.bytes, "1"); } #[test] fn test_check_bytes32_bytes32() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.bytes32, types.bytes32, "1"); } #[test] fn test_check_public_key_public_key() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.public_key, types.public_key, "1"); } #[test] fn test_check_int_int() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.int, types.int, "1"); } #[test] fn test_check_bool_bool() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.bool, types.bool, "1"); } #[test] fn test_check_nil_nil() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.nil, types.nil, "1"); } #[test] fn test_check_bytes_bytes32() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.bytes, types.bytes32, "(= (strlen val) 32)"); } #[test] fn test_check_bytes32_bytes() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.bytes32, types.bytes, "1"); } #[test] fn test_check_bytes_public_key() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str( &mut db, types.bytes, @@ -504,91 +504,91 @@ mod tests { #[test] fn test_check_public_key_bytes() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.public_key, types.bytes, "1"); } #[test] fn test_check_bytes_nil() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.bytes, types.nil, "(= val 0)"); } #[test] fn test_check_nil_bytes() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.nil, types.bytes, "1"); } #[test] fn test_check_bytes_bool() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.bytes, types.bool, "(or (= val 0) (= val 1))"); } #[test] fn test_check_bool_bytes() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.bool, types.bytes, "1"); } #[test] fn test_check_bytes32_public_key() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.bytes32, types.public_key, "0"); } #[test] fn test_check_bytes_int() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.bytes, types.int, "1"); } #[test] fn test_check_int_bytes() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.int, types.bytes, "1"); } #[test] fn test_check_bool_nil() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.bool, types.nil, "(= val 0)"); } #[test] fn test_check_nil_bool() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.nil, types.bool, "1"); } #[test] fn test_check_any_any() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.any, types.any, "1"); } #[test] fn test_check_bytes_any() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); check_str(&mut db, types.any, types.any, "1"); } #[test] fn test_check_list_nil() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); let list = alloc_list(&mut db, types.bytes); check_str(&mut db, list, types.nil, "(not (l val))"); } @@ -596,7 +596,7 @@ mod tests { #[test] fn test_check_list_pair() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); let list = alloc_list(&mut db, types.bytes); let pair = db.alloc(Type::Pair(types.bytes, list)); check_str(&mut db, list, pair, "(l val)"); @@ -605,7 +605,7 @@ mod tests { #[test] fn test_check_any_list() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); let list = alloc_list(&mut db, types.bytes); check_recursive(&mut db, types.any, list); } @@ -613,7 +613,7 @@ mod tests { #[test] fn test_check_any_point() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); let point_end = db.alloc(Type::Pair(types.int, types.nil)); let point = db.alloc(Type::Pair(types.int, point_end)); check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) 0))"); @@ -622,7 +622,7 @@ mod tests { #[test] fn test_check_any_point_struct() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); let point_struct = alloc_struct( &mut db, &indexmap! { @@ -644,7 +644,7 @@ mod tests { #[test] fn test_check_condition_agg_sig_me() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); let opcode = db.alloc(Type::Value(BigInt::from(49))); let agg_sig_unsafe = alloc_struct( diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index dd26d04..d25dc12 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -337,7 +337,7 @@ pub(crate) fn compare_type( compare_type(db, lhs.return_type, rhs.return_type, ctx), ), - (Type::Callable(..), _) => compare_type(db, lhs, db.standard_types().any, ctx), + (Type::Callable(..), _) => compare_type(db, lhs, db.std().any, ctx), (_, Type::Callable(..)) => Comparison::Incompatible, }; @@ -355,35 +355,35 @@ mod tests { #[test] fn test_compare_int_int() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!(db.compare(types.int, types.int), Comparison::Equal); } #[test] fn test_compare_int_bytes() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!(db.compare(types.int, types.bytes), Comparison::Castable); } #[test] fn test_compare_bytes_int() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!(db.compare(types.bytes, types.int), Comparison::Castable); } #[test] fn test_compare_bytes_bytes32() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!(db.compare(types.bytes, types.bytes32), Comparison::Superset); } #[test] fn test_compare_bytes32_bytes() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!( db.compare(types.bytes32, types.bytes), Comparison::Assignable @@ -393,7 +393,7 @@ mod tests { #[test] fn test_compare_bytes_public_key() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!( db.compare(types.bytes, types.public_key), Comparison::Superset @@ -403,7 +403,7 @@ mod tests { #[test] fn test_compare_public_key_bytes() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!( db.compare(types.public_key, types.bytes), Comparison::Castable @@ -413,7 +413,7 @@ mod tests { #[test] fn test_compare_bytes32_public_key() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!( db.compare(types.bytes32, types.public_key), Comparison::Incompatible @@ -423,7 +423,7 @@ mod tests { #[test] fn test_compare_public_key_bytes32() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!( db.compare(types.public_key, types.bytes32), Comparison::Incompatible @@ -433,35 +433,35 @@ mod tests { #[test] fn test_compare_bytes_any() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!(db.compare(types.bytes, types.any), Comparison::Assignable); } #[test] fn test_compare_any_bytes() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!(db.compare(types.any, types.bytes), Comparison::Superset); } #[test] fn test_compare_bytes32_any() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!(db.compare(types.bytes32, types.any), Comparison::Assignable); } #[test] fn test_compare_any_bytes32() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!(db.compare(types.any, types.bytes32), Comparison::Superset); } #[test] fn test_compare_list_any() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); let list = alloc_list(&mut db, types.int); assert_eq!(db.compare(list, types.any), Comparison::Assignable); } @@ -469,7 +469,7 @@ mod tests { #[test] fn test_compare_pair_any() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); let pair = db.alloc(Type::Pair(types.int, types.public_key)); assert_eq!(db.compare(pair, types.any), Comparison::Assignable); } @@ -477,14 +477,14 @@ mod tests { #[test] fn test_compare_int_any() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!(db.compare(types.int, types.any), Comparison::Assignable); } #[test] fn test_compare_public_key_any() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!( db.compare(types.public_key, types.any), Comparison::Assignable @@ -494,7 +494,7 @@ mod tests { #[test] fn test_compare_complex_any() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); let pair_inner_inner = db.alloc(Type::Pair(types.any, types.nil)); let pair_inner = db.alloc(Type::Pair(pair_inner_inner, pair_inner_inner)); let pair = db.alloc(Type::Pair(types.int, pair_inner)); @@ -505,14 +505,14 @@ mod tests { #[test] fn test_compare_any_any() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!(db.compare(types.any, types.any), Comparison::Equal); } #[test] fn test_compare_incompatible_pair() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); let lhs = db.alloc(Type::Pair(types.int, types.public_key)); let rhs = db.alloc(Type::Pair(types.bytes, types.nil)); assert_eq!(db.compare(lhs, rhs), Comparison::Incompatible); @@ -521,7 +521,7 @@ mod tests { #[test] fn test_compare_castable_pair() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); let lhs = db.alloc(Type::Pair(types.int, types.public_key)); let rhs = db.alloc(Type::Pair(types.bytes, types.bytes)); assert_eq!(db.compare(lhs, rhs), Comparison::Castable); @@ -530,7 +530,7 @@ mod tests { #[test] fn test_compare_assignable_pair() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); let lhs = db.alloc(Type::Pair(types.int, types.public_key)); let rhs = db.alloc(Type::Pair(types.atom, types.atom)); assert_eq!(db.compare(lhs, rhs), Comparison::Assignable); @@ -539,7 +539,7 @@ mod tests { #[test] fn test_generic_inference() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); let generic = db.alloc(Type::Generic); diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs index 7066fdd..f3429dd 100644 --- a/crates/rue-typing/src/stringify.rs +++ b/crates/rue-typing/src/stringify.rs @@ -81,7 +81,7 @@ mod tests { #[test] fn stringify_atoms() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); assert_eq!(db.stringify(types.unknown), "{unknown}"); assert_eq!(db.stringify(types.never), "Never"); @@ -97,7 +97,7 @@ mod tests { #[test] fn stringify_named() { let db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); let mut names = HashMap::new(); names.insert(types.any, "CustomAny".to_string()); @@ -108,7 +108,7 @@ mod tests { #[test] fn test_stringify_callable() { let mut db = TypeSystem::new(); - let types = db.standard_types(); + let types = db.std(); let callable = alloc_callable( &mut db, diff --git a/crates/rue-typing/src/test_tools.rs b/crates/rue-typing/src/test_tools.rs index d0446a3..98e3d0b 100644 --- a/crates/rue-typing/src/test_tools.rs +++ b/crates/rue-typing/src/test_tools.rs @@ -5,7 +5,7 @@ use crate::{Callable, Rest, Struct, Type, TypeId, TypeSystem}; pub fn alloc_list(db: &mut TypeSystem, item_type_id: TypeId) -> TypeId { let list = db.alloc(Type::Unknown); let pair = db.alloc(Type::Pair(item_type_id, list)); - *db.get_mut(list) = Type::Union(vec![pair, db.standard_types().nil]); + *db.get_mut(list) = Type::Union(vec![pair, db.std().nil]); list } @@ -25,7 +25,7 @@ pub fn alloc_callable( .enumerate() .map(|(i, field)| { if i == parameters.len() - 1 { - db.alloc(Type::Union(vec![field, db.standard_types().nil])) + db.alloc(Type::Union(vec![field, db.std().nil])) } else { field } @@ -56,7 +56,7 @@ pub fn alloc_struct(db: &mut TypeSystem, fields: &IndexMap, rest .enumerate() .map(|(i, field)| { if i == fields.len() - 1 { - db.alloc(Type::Union(vec![field, db.standard_types().nil])) + db.alloc(Type::Union(vec![field, db.std().nil])) } else { field } @@ -79,7 +79,7 @@ pub fn alloc_list_of( db: &mut TypeSystem, items: impl DoubleEndedIterator, ) -> TypeId { - let mut tuple = db.standard_types().nil; + let mut tuple = db.std().nil; for item in items.rev() { tuple = db.alloc(Type::Pair(item, tuple)); } @@ -90,7 +90,7 @@ pub fn alloc_tuple_of( db: &mut TypeSystem, items: impl DoubleEndedIterator, ) -> TypeId { - let mut tuple = db.standard_types().nil; + let mut tuple = db.std().nil; for (i, item) in items.rev().enumerate() { if i == 0 { tuple = item; diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index 299933b..f3465a1 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -77,7 +77,7 @@ impl TypeSystem { Self::default() } - pub fn standard_types(&self) -> StandardTypes { + pub fn std(&self) -> StandardTypes { self.types } From 9f0ccd9d1b2dcddc0779ec03f493ad7e136f24fa Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 11:54:29 -0400 Subject: [PATCH 056/100] Temp 3 --- crates/rue-compiler/src/compiler.rs | 6 +- crates/rue-compiler/src/compiler/block.rs | 15 ++--- crates/rue-compiler/src/compiler/builtins.rs | 57 ++++++++----------- crates/rue-compiler/src/compiler/context.rs | 13 +++-- crates/rue-compiler/src/compiler/expr.rs | 3 +- crates/rue-compiler/src/compiler/item.rs | 5 +- crates/rue-compiler/src/compiler/path.rs | 9 +-- .../rue-compiler/src/compiler/symbol_table.rs | 11 ++-- crates/rue-typing/src/difference.rs | 6 +- crates/rue-typing/src/semantic_types.rs | 4 +- crates/rue-typing/src/substitute_type.rs | 3 +- crates/rue-typing/src/type_system.rs | 4 ++ 12 files changed, 70 insertions(+), 66 deletions(-) diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index 32ad1c1..068d78b 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -35,7 +35,7 @@ pub struct Compiler<'a> { db: &'a mut Database, // The type system is responsible for type checking and type inference. - ty: TypeSystem, + ty: &'a mut TypeSystem, // The scope stack is used to keep track of the current scope. scope_stack: Vec, @@ -67,10 +67,10 @@ pub struct Compiler<'a> { } impl<'a> Compiler<'a> { - pub fn new(db: &'a mut Database, builtins: Builtins) -> Self { + pub fn new(db: &'a mut Database, ty: &'a mut TypeSystem, builtins: Builtins) -> Self { Self { db, - ty: TypeSystem::new(), + ty, scope_stack: vec![builtins.scope_id], symbol_stack: Vec::new(), type_definition_stack: Vec::new(), diff --git a/crates/rue-compiler/src/compiler/block.rs b/crates/rue-compiler/src/compiler/block.rs index 83d103d..4c6f6af 100644 --- a/crates/rue-compiler/src/compiler/block.rs +++ b/crates/rue-compiler/src/compiler/block.rs @@ -1,9 +1,10 @@ use rue_parser::{AstNode, Block, Stmt}; +use rue_typing::TypeId; use crate::{ hir::{Hir, Op}, value::Value, - ErrorKind, TypeId, + ErrorKind, }; use super::{stmt::Statement, Compiler}; @@ -62,7 +63,7 @@ impl Compiler<'_> { // Make sure that the return value matches the expected type. self.type_check( value.type_id, - expected_type.unwrap_or(self.builtins.unknown), + expected_type.unwrap_or(self.ty.std().unknown), return_stmt.syntax().text_range(), ); @@ -83,19 +84,19 @@ impl Compiler<'_> { terminator = BlockTerminator::Raise; is_terminated = true; - statements.push(Statement::Return(Value::new(hir_id, self.builtins.unknown))); + statements.push(Statement::Return(Value::new(hir_id, self.ty.std().unknown))); } Stmt::AssertStmt(assert_stmt) => { // Compile the condition expression. let condition = assert_stmt .expr() - .map(|condition| self.compile_expr(&condition, Some(self.builtins.bool))) + .map(|condition| self.compile_expr(&condition, Some(self.ty.std().bool))) .unwrap_or_else(|| self.unknown()); // Make sure that the condition is a boolean. self.type_check( condition.type_id, - self.builtins.bool, + self.ty.std().bool, assert_stmt.syntax().text_range(), ); @@ -115,13 +116,13 @@ impl Compiler<'_> { // Compile the expression. let expr = assume_stmt .expr() - .map(|expr| self.compile_expr(&expr, Some(self.builtins.bool))) + .map(|expr| self.compile_expr(&expr, Some(self.ty.std().bool))) .unwrap_or_else(|| self.unknown()); // Make sure that the condition is a boolean. self.type_check( expr.type_id, - self.builtins.bool, + self.ty.std().bool, assume_stmt.syntax().text_range(), ); diff --git a/crates/rue-compiler/src/compiler/builtins.rs b/crates/rue-compiler/src/compiler/builtins.rs index ea8d2f6..3e3621f 100644 --- a/crates/rue-compiler/src/compiler/builtins.rs +++ b/crates/rue-compiler/src/compiler/builtins.rs @@ -20,17 +20,16 @@ pub struct Builtins { pub fn builtins(db: &mut Database, ty: &mut TypeSystem) -> Builtins { let mut scope = Scope::default(); - let std = ty.standard_types(); let nil = db.alloc_hir(Hir::Atom(Vec::new())); let unknown = db.alloc_hir(Hir::Unknown); - scope.define_type("Nil".to_string(), std.nil); - scope.define_type("Int".to_string(), std.int); - scope.define_type("Bool".to_string(), std.bool); - scope.define_type("Bytes".to_string(), std.bytes); - scope.define_type("Bytes32".to_string(), std.bytes32); - scope.define_type("PublicKey".to_string(), std.public_key); - scope.define_type("Any".to_string(), std.any); + scope.define_type("Nil".to_string(), ty.std().nil); + scope.define_type("Int".to_string(), ty.std().int); + scope.define_type("Bool".to_string(), ty.std().bool); + scope.define_type("Bytes".to_string(), ty.std().bytes); + scope.define_type("Bytes32".to_string(), ty.std().bytes32); + scope.define_type("PublicKey".to_string(), ty.std().public_key); + scope.define_type("Any".to_string(), ty.std().any); let builtins = Builtins { scope_id: db.alloc_scope(scope), @@ -59,11 +58,9 @@ pub fn builtins(db: &mut Database, ty: &mut TypeSystem) -> Builtins { } fn sha256(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { - let std = ty.standard_types(); - let mut scope = Scope::default(); - let param = db.alloc_symbol(Symbol::Parameter(std.bytes)); + let param = db.alloc_symbol(Symbol::Parameter(ty.std().bytes)); scope.define_symbol("bytes".to_string(), param); let param_ref = db.alloc_hir(Hir::Reference(param, TextRange::default())); let hir_id = db.alloc_hir(Hir::Op(Op::Sha256, param_ref)); @@ -75,19 +72,17 @@ fn sha256(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { ty: Callable { original_type_id: None, parameter_names: indexset!["bytes".to_string()], - parameters: ty.alloc(Type::Pair(std.bytes, std.nil)), + parameters: ty.alloc(Type::Pair(ty.std().bytes, ty.std().nil)), rest: Rest::Nil, - return_type: std.bytes32, + return_type: ty.std().bytes32, generic_types: Vec::new(), }, })) } fn pubkey_for_exp(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { - let std = ty.standard_types(); - let mut scope = Scope::default(); - let param = db.alloc_symbol(Symbol::Parameter(std.bytes32)); + let param = db.alloc_symbol(Symbol::Parameter(ty.std().bytes32)); scope.define_symbol("exponent".to_string(), param); let param_ref = db.alloc_hir(Hir::Reference(param, TextRange::default())); let hir_id = db.alloc_hir(Hir::Op(Op::PubkeyForExp, param_ref)); @@ -99,21 +94,19 @@ fn pubkey_for_exp(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { ty: Callable { original_type_id: None, parameter_names: indexset!["exponent".to_string()], - parameters: ty.alloc(Type::Pair(std.bytes32, std.nil)), + parameters: ty.alloc(Type::Pair(ty.std().bytes32, ty.std().nil)), rest: Rest::Nil, - return_type: std.public_key, + return_type: ty.std().public_key, generic_types: Vec::new(), }, })) } fn divmod(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { - let std = ty.standard_types(); - let mut scope = Scope::default(); - let lhs = db.alloc_symbol(Symbol::Parameter(std.int)); - let rhs = db.alloc_symbol(Symbol::Parameter(std.int)); + let lhs = db.alloc_symbol(Symbol::Parameter(ty.std().int)); + let rhs = db.alloc_symbol(Symbol::Parameter(ty.std().int)); scope.define_symbol("lhs".to_string(), lhs); scope.define_symbol("rhs".to_string(), rhs); let lhs_ref = db.alloc_hir(Hir::Reference(lhs, TextRange::default())); @@ -121,7 +114,7 @@ fn divmod(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { let hir_id = db.alloc_hir(Hir::BinaryOp(BinOp::DivMod, lhs_ref, rhs_ref)); let scope_id = db.alloc_scope(scope); - let int_pair = ty.alloc(Type::Pair(std.int, std.int)); + let int_pair = ty.alloc(Type::Pair(ty.std().int, ty.std().int)); db.alloc_symbol(Symbol::InlineFunction(Function { scope_id, @@ -129,7 +122,7 @@ fn divmod(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { ty: Callable { original_type_id: None, parameter_names: indexset!["lhs".to_string(), "rhs".to_string()], - parameters: ty.alloc(Type::Pair(int_pair, std.nil)), + parameters: ty.alloc(Type::Pair(int_pair, ty.std().nil)), rest: Rest::Nil, return_type: int_pair, generic_types: Vec::new(), @@ -138,13 +131,11 @@ fn divmod(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { } fn substr(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { - let std = ty.standard_types(); - let mut scope = Scope::default(); - let value = db.alloc_symbol(Symbol::Parameter(std.bytes)); - let start = db.alloc_symbol(Symbol::Parameter(std.int)); - let end = db.alloc_symbol(Symbol::Parameter(std.int)); + let value = db.alloc_symbol(Symbol::Parameter(ty.std().bytes)); + let start = db.alloc_symbol(Symbol::Parameter(ty.std().int)); + let end = db.alloc_symbol(Symbol::Parameter(ty.std().int)); scope.define_symbol("value".to_string(), value); scope.define_symbol("start".to_string(), start); scope.define_symbol("end".to_string(), end); @@ -154,9 +145,9 @@ fn substr(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { let hir_id = db.alloc_hir(Hir::Substr(value_ref, start_ref, end_ref)); let scope_id = db.alloc_scope(scope); - let end = ty.alloc(Type::Pair(std.int, std.nil)); - let int = ty.alloc(Type::Pair(std.int, end)); - let parameters = ty.alloc(Type::Pair(std.bytes, int)); + let end = ty.alloc(Type::Pair(ty.std().int, ty.std().nil)); + let int = ty.alloc(Type::Pair(ty.std().int, end)); + let parameters = ty.alloc(Type::Pair(ty.std().bytes, int)); db.alloc_symbol(Symbol::InlineFunction(Function { scope_id, @@ -166,7 +157,7 @@ fn substr(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { parameter_names: indexset!["value".to_string(), "start".to_string(), "end".to_string()], parameters, rest: Rest::Nil, - return_type: std.bytes, + return_type: ty.std().bytes, generic_types: Vec::new(), }, })) diff --git a/crates/rue-compiler/src/compiler/context.rs b/crates/rue-compiler/src/compiler/context.rs index d81e92c..ca63001 100644 --- a/crates/rue-compiler/src/compiler/context.rs +++ b/crates/rue-compiler/src/compiler/context.rs @@ -3,6 +3,7 @@ use std::collections::HashSet; use clvmr::{Allocator, NodePtr}; use indexmap::IndexMap; use rue_parser::{parse, Root}; +use rue_typing::{Type, TypeSystem}; use crate::{ codegen::Codegen, @@ -11,7 +12,6 @@ use crate::{ optimizer::Optimizer, scope::Scope, symbol::{Module, Symbol}, - value::Type, Database, SymbolId, }; @@ -22,9 +22,9 @@ pub struct CompilerContext<'a> { roots: IndexMap, } -pub fn setup_compiler(db: &mut Database) -> CompilerContext<'_> { - let builtins = builtins(db); - let compiler = Compiler::new(db, builtins); +pub fn setup_compiler<'a>(db: &'a mut Database, ty: &'a mut TypeSystem) -> CompilerContext<'a> { + let builtins = builtins(db, ty); + let compiler = Compiler::new(db, ty, builtins); CompilerContext { compiler, roots: IndexMap::new(), @@ -88,6 +88,7 @@ pub fn compile_modules(mut ctx: CompilerContext<'_>) -> SymbolTable { pub fn build_graph( db: &mut Database, + ty: &TypeSystem, symbol_table: &SymbolTable, main_module_id: SymbolId, library_module_ids: &[SymbolId], @@ -104,7 +105,7 @@ pub fn build_graph( } for type_id in ignored_types.clone() { - let Type::Enum(enum_type) = db.ty(type_id) else { + let Type::Enum(enum_type) = ty.get(type_id) else { continue; }; ignored_types.extend(enum_type.variants.values()); @@ -122,7 +123,7 @@ pub fn build_graph( }; let graph = DependencyGraph::build(db, &module); - symbol_table.calculate_unused(db, &graph, &ignored_symbols, &ignored_types); + symbol_table.calculate_unused(db, ty, &graph, &ignored_symbols, &ignored_types); graph } diff --git a/crates/rue-compiler/src/compiler/expr.rs b/crates/rue-compiler/src/compiler/expr.rs index 7ab0bd7..18a7646 100644 --- a/crates/rue-compiler/src/compiler/expr.rs +++ b/crates/rue-compiler/src/compiler/expr.rs @@ -1,6 +1,7 @@ use rue_parser::{AstNode, Expr}; +use rue_typing::TypeId; -use crate::{value::Value, TypeId}; +use crate::value::Value; use super::Compiler; diff --git a/crates/rue-compiler/src/compiler/item.rs b/crates/rue-compiler/src/compiler/item.rs index 19e5345..8540bdc 100644 --- a/crates/rue-compiler/src/compiler/item.rs +++ b/crates/rue-compiler/src/compiler/item.rs @@ -1,8 +1,9 @@ use std::collections::HashSet; use rue_parser::Item; +use rue_typing::{Type, TypeId}; -use crate::{symbol::Symbol, value::Type, ErrorKind, SymbolId, TypeId}; +use crate::{symbol::Symbol, ErrorKind, SymbolId}; use super::Compiler; @@ -164,7 +165,7 @@ impl Compiler<'_> { .local_types() .into_iter() .filter_map(|type_id| { - if let Type::Enum(..) = self.db.ty(type_id).clone() { + if let Type::Enum(..) = self.ty.get(type_id).clone() { Some(self.scope().type_name(type_id).unwrap().to_string()) } else { None diff --git a/crates/rue-compiler/src/compiler/path.rs b/crates/rue-compiler/src/compiler/path.rs index de98a7a..602668c 100644 --- a/crates/rue-compiler/src/compiler/path.rs +++ b/crates/rue-compiler/src/compiler/path.rs @@ -1,6 +1,7 @@ use rue_parser::SyntaxToken; +use rue_typing::{Type, TypeId}; -use crate::{symbol::Symbol, value::Type, ErrorKind, SymbolId, TypeId}; +use crate::{symbol::Symbol, ErrorKind, SymbolId}; use super::Compiler; @@ -34,7 +35,7 @@ impl Compiler<'_> { } } - if let Type::Enum(..) = self.db.ty(type_id) { + if let Type::Enum(..) = self.ty.get(type_id) { if !last { self.type_reference(type_id); return Some(PathItem::Type(type_id)); @@ -78,7 +79,7 @@ impl Compiler<'_> { ) -> Option { match item { PathItem::Type(type_id) => { - let Type::Enum(enum_type) = self.db.ty(type_id) else { + let Type::Enum(enum_type) = self.ty.get(type_id) else { self.db.error( ErrorKind::InvalidTypePath(self.type_name(type_id)), name.text_range(), @@ -127,7 +128,7 @@ impl Compiler<'_> { } } - if let Type::Enum(..) = self.db.ty(type_id) { + if let Type::Enum(..) = self.ty.get(type_id) { if !last { self.type_reference(type_id); return Some(PathItem::Type(type_id)); diff --git a/crates/rue-compiler/src/compiler/symbol_table.rs b/crates/rue-compiler/src/compiler/symbol_table.rs index eecaddc..d1cc991 100644 --- a/crates/rue-compiler/src/compiler/symbol_table.rs +++ b/crates/rue-compiler/src/compiler/symbol_table.rs @@ -1,11 +1,9 @@ use std::collections::HashSet; use indexmap::{IndexMap, IndexSet}; +use rue_typing::{Type, TypeId, TypeSystem}; -use crate::{ - dependency_graph::DependencyGraph, symbol::Symbol, value::Type, Database, SymbolId, TypeId, - WarningKind, -}; +use crate::{dependency_graph::DependencyGraph, symbol::Symbol, Database, SymbolId, WarningKind}; #[derive(Debug, Default)] pub struct SymbolTable { @@ -44,6 +42,7 @@ impl SymbolTable { pub fn calculate_unused( &self, db: &mut Database, + ty: &TypeSystem, dependency_graph: &DependencyGraph, ignored_symbols: &HashSet, ignored_types: &HashSet, @@ -126,12 +125,12 @@ impl SymbolTable { continue; } let token = db.type_token(*type_id).unwrap(); - let kind = match db.ty_raw(*type_id) { + let kind = match ty.get_raw(*type_id) { Type::Generic => WarningKind::UnusedGenericType(token.to_string()), Type::Alias(..) => WarningKind::UnusedTypeAlias(token.to_string()), Type::Struct(..) => WarningKind::UnusedStruct(token.to_string()), Type::Enum(..) => WarningKind::UnusedEnum(token.to_string()), - Type::EnumVariant(..) => WarningKind::UnusedEnumVariant(token.to_string()), + Type::Variant(..) => WarningKind::UnusedEnumVariant(token.to_string()), _ => continue, }; db.warning(kind, token.text_range()); diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index ec1932e..5ec59bd 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -272,23 +272,25 @@ pub(crate) fn difference_type( } (Type::Enum(ty), _) => { - let ty = *ty; + let ty = ty.clone(); let type_id = difference_type(types, std, ty.type_id, rhs, visited); types.alloc(Type::Enum(Enum { original_type_id: Some(ty.original_type_id.unwrap_or(lhs)), type_id, has_fields: ty.has_fields, + variants: ty.variants, })) } (_, Type::Enum(ty)) => { - let ty = *ty; + let ty = ty.clone(); let type_id = difference_type(types, std, lhs, ty.type_id, visited); types.alloc(Type::Enum(Enum { original_type_id: Some(ty.original_type_id.unwrap_or(rhs)), type_id, has_fields: ty.has_fields, + variants: ty.variants, })) } diff --git a/crates/rue-typing/src/semantic_types.rs b/crates/rue-typing/src/semantic_types.rs index 7f42435..1903041 100644 --- a/crates/rue-typing/src/semantic_types.rs +++ b/crates/rue-typing/src/semantic_types.rs @@ -55,7 +55,7 @@ pub struct Callable { } /// Represents an enum type which can have multiple variants. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub struct Enum { /// A pointer to the enum from which this was derived, if any. pub original_type_id: Option, @@ -63,6 +63,8 @@ pub struct Enum { pub type_id: TypeId, /// Whether the enum semantically has fields. pub has_fields: bool, + /// This is a map of the original variant names to their type ids. + pub variants: HashMap, } /// Represents a variant type which can optionally have fields. diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index 972ef5e..286dc4a 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -137,7 +137,7 @@ pub(crate) fn substitute_type( } } Type::Enum(ty) => { - let ty = *ty; + let ty = ty.clone(); let new_type_id = substitute_type(types, ty.type_id, substitutions, semantics, visited); @@ -149,6 +149,7 @@ pub(crate) fn substitute_type( original_type_id: Some(ty.original_type_id.unwrap_or(type_id)), type_id: new_type_id, has_fields: ty.has_fields, + variants: ty.variants, })) } } else { diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index f3465a1..6b86033 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -85,6 +85,10 @@ impl TypeSystem { self.arena.alloc(ty) } + pub fn get_raw(&self, id: TypeId) -> &Type { + &self.arena[id] + } + pub fn get(&self, id: TypeId) -> &Type { match &self.arena[id] { Type::Ref(id) => self.get(*id), From 633fe23882c16c024f871061c68c6bf70f03f8b8 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 11:56:20 -0400 Subject: [PATCH 057/100] Temp 4 --- crates/rue-compiler/src/compiler/expr.rs | 2 - .../src/compiler/expr/exists_expr.rs | 38 ------------------- crates/rue-compiler/src/compiler/ty.rs | 2 - .../src/compiler/ty/nullable_type.rs | 21 ---------- crates/rue-parser/src/ast.rs | 24 +----------- crates/rue-parser/src/grammar.rs | 8 ---- crates/rue-parser/src/syntax_kind.rs | 5 --- 7 files changed, 1 insertion(+), 99 deletions(-) delete mode 100644 crates/rue-compiler/src/compiler/expr/exists_expr.rs delete mode 100644 crates/rue-compiler/src/compiler/ty/nullable_type.rs diff --git a/crates/rue-compiler/src/compiler/expr.rs b/crates/rue-compiler/src/compiler/expr.rs index 18a7646..16933fd 100644 --- a/crates/rue-compiler/src/compiler/expr.rs +++ b/crates/rue-compiler/src/compiler/expr.rs @@ -8,7 +8,6 @@ use super::Compiler; mod binary_expr; mod block_expr; mod cast_expr; -mod exists_expr; mod field_access_expr; mod function_call_expr; mod group_expr; @@ -49,7 +48,6 @@ impl Compiler<'_> { Expr::FunctionCallExpr(call) => self.compile_function_call_expr(call), Expr::FieldAccessExpr(field_access) => self.compile_field_access_expr(field_access), Expr::IndexAccessExpr(index_access) => self.compile_index_access_expr(index_access), - Expr::ExistsExpr(exists) => self.compile_exists_expr(exists), }; self.is_callee = false; diff --git a/crates/rue-compiler/src/compiler/expr/exists_expr.rs b/crates/rue-compiler/src/compiler/expr/exists_expr.rs deleted file mode 100644 index 153a317..0000000 --- a/crates/rue-compiler/src/compiler/expr/exists_expr.rs +++ /dev/null @@ -1,38 +0,0 @@ -use rue_parser::{AstNode, ExistsExpr}; - -use crate::{ - compiler::Compiler, - hir::{Hir, Op}, - value::{Guard, Mutation, Type, TypeOverride, Value}, - ErrorKind, -}; - -impl Compiler<'_> { - pub fn compile_exists_expr(&mut self, exists: &ExistsExpr) -> Value { - let Some(value) = exists.expr().map(|expr| self.compile_expr(&expr, None)) else { - return self.unknown(); - }; - - let Type::Optional(inner) = self.db.ty(value.type_id).clone() else { - self.db.error( - ErrorKind::InvalidExistanceCheck(self.type_name(value.type_id)), - exists.syntax().text_range(), - ); - return self.unknown(); - }; - - let exists = self.db.alloc_hir(Hir::Op(Op::Listp, value.hir_id)); - let mut new_value = Value::new(exists, self.builtins.bool); - - if let Some(guard_path) = value.guard_path { - let mut unwrap = TypeOverride::new(inner); - unwrap.mutation = Mutation::UnwrapOptional; - new_value.guards.insert( - guard_path, - Guard::new(unwrap, TypeOverride::new(value.type_id)), - ); - } - - new_value - } -} diff --git a/crates/rue-compiler/src/compiler/ty.rs b/crates/rue-compiler/src/compiler/ty.rs index 8714a2c..499314a 100644 --- a/crates/rue-compiler/src/compiler/ty.rs +++ b/crates/rue-compiler/src/compiler/ty.rs @@ -5,7 +5,6 @@ use super::Compiler; mod function_type; mod list_type; -mod nullable_type; mod pair_type; mod path_type; @@ -18,7 +17,6 @@ impl Compiler<'_> { Type::ListType(list) => self.compile_list_type(&list), Type::FunctionType(function) => self.compile_function_type(&function), Type::PairType(tuple) => self.compile_pair_type(&tuple), - Type::NullableType(optional) => self.compile_nullable_type(&optional), } } } diff --git a/crates/rue-compiler/src/compiler/ty/nullable_type.rs b/crates/rue-compiler/src/compiler/ty/nullable_type.rs deleted file mode 100644 index 16d25fb..0000000 --- a/crates/rue-compiler/src/compiler/ty/nullable_type.rs +++ /dev/null @@ -1,21 +0,0 @@ -use rue_parser::{AstNode, NullableType}; - -use crate::{compiler::Compiler, value::Type, TypeId, WarningKind}; - -impl Compiler<'_> { - pub fn compile_nullable_type(&mut self, optional: &NullableType) -> TypeId { - let ty = optional - .ty() - .map_or(self.builtins.unknown, |ty| self.compile_type(ty)); - - if let Type::Nullable(inner) = self.db.ty_raw(ty).clone() { - self.db.warning( - WarningKind::RedundantNullableType(self.type_name(ty)), - optional.syntax().text_range(), - ); - return inner; - } - - self.db.alloc_type(Type::Nullable(ty)) - } -} diff --git a/crates/rue-parser/src/ast.rs b/crates/rue-parser/src/ast.rs index d5a274c..1d8d006 100644 --- a/crates/rue-parser/src/ast.rs +++ b/crates/rue-parser/src/ast.rs @@ -98,7 +98,6 @@ ast_enum!( FunctionCallExpr, FieldAccessExpr, IndexAccessExpr, - ExistsExpr ); ast_node!(PathExpr); ast_node!(InitializerExpr); @@ -117,26 +116,17 @@ ast_node!(FunctionCallExpr); ast_node!(FunctionCallArg); ast_node!(FieldAccessExpr); ast_node!(IndexAccessExpr); -ast_node!(ExistsExpr); ast_node!(LambdaExpr); ast_node!(LambdaParam); -ast_enum!( - Type, - PathType, - ListType, - PairType, - FunctionType, - NullableType -); +ast_enum!(Type, PathType, ListType, PairType, FunctionType); ast_node!(PathType); ast_node!(ListType); ast_node!(ListTypeItem); ast_node!(PairType); ast_node!(FunctionType); ast_node!(FunctionTypeParam); -ast_node!(NullableType); ast_enum!(Stmt, LetStmt, IfStmt, ReturnStmt, RaiseStmt, AssertStmt, AssumeStmt); ast_node!(LetStmt); @@ -835,12 +825,6 @@ impl IndexAccessExpr { } } -impl ExistsExpr { - pub fn expr(&self) -> Option { - self.syntax().children().find_map(Expr::cast) - } -} - impl PathType { pub fn idents(&self) -> Vec { self.syntax() @@ -920,12 +904,6 @@ impl FunctionTypeParam { } } -impl NullableType { - pub fn ty(&self) -> Option { - self.syntax().children().find_map(Type::cast) - } -} - impl GenericTypes { pub fn idents(&self) -> Vec { self.syntax() diff --git a/crates/rue-parser/src/grammar.rs b/crates/rue-parser/src/grammar.rs index 98b16aa..df7e4f6 100644 --- a/crates/rue-parser/src/grammar.rs +++ b/crates/rue-parser/src/grammar.rs @@ -413,10 +413,6 @@ fn expr_binding_power(p: &mut Parser<'_>, minimum_binding_power: u8, allow_initi } p.expect(SyntaxKind::CloseParen); p.finish(); - } else if p.at(SyntaxKind::Question) { - p.start_at(checkpoint, SyntaxKind::ExistsExpr); - p.bump(); - p.finish(); } else if p.at(SyntaxKind::Dot) { p.start_at(checkpoint, SyntaxKind::FieldAccessExpr); p.bump(); @@ -603,10 +599,6 @@ fn ty(p: &mut Parser<'_>) { p.bump(); p.expect(SyntaxKind::CloseBracket); p.finish(); - } else if p.at(SyntaxKind::Question) { - p.start_at(checkpoint, SyntaxKind::NullableType); - p.bump(); - p.finish(); } else { break; } diff --git a/crates/rue-parser/src/syntax_kind.rs b/crates/rue-parser/src/syntax_kind.rs index e18f240..2a70514 100644 --- a/crates/rue-parser/src/syntax_kind.rs +++ b/crates/rue-parser/src/syntax_kind.rs @@ -113,14 +113,12 @@ pub enum SyntaxKind { FunctionCallArg, FieldAccessExpr, IndexAccessExpr, - ExistsExpr, PathType, ListType, ListTypeItem, PairType, FunctionType, FunctionTypeParam, - NullableType, GenericTypes, } @@ -236,15 +234,12 @@ impl fmt::Display for SyntaxKind { Self::FunctionCallArg => "function call argument", Self::FieldAccessExpr => "field access expression", Self::IndexAccessExpr => "index access expression", - Self::ExistsExpr => "exists expression", Self::PathType => "path type", Self::ListType => "list type", Self::ListTypeItem => "list type item", Self::PairType => "pair type", Self::FunctionType => "function type", Self::FunctionTypeParam => "function type parameter", - Self::NullableType => "nullable type", - Self::GenericTypes => "generic types", } ) From ae039bbaad6d6292f9626b0994b8edd959f39324 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 12:12:52 -0400 Subject: [PATCH 058/100] Temp 5 --- .../src/compiler/expr/binary_expr.rs | 160 +++++++++--------- .../src/compiler/expr/cast_expr.rs | 2 +- .../src/compiler/expr/field_access_expr.rs | 6 +- .../src/compiler/expr/function_call_expr.rs | 2 +- .../src/compiler/expr/guard_expr.rs | 22 +-- .../rue-compiler/src/compiler/expr/if_expr.rs | 6 +- .../src/compiler/expr/initializer_expr.rs | 2 +- .../src/compiler/expr/lambda_expr.rs | 8 +- .../src/compiler/expr/list_expr.rs | 4 +- .../src/compiler/expr/literal_expr.rs | 14 +- .../src/compiler/expr/pair_expr.rs | 6 +- .../src/compiler/expr/path_expr.rs | 2 +- .../src/compiler/expr/prefix_expr.rs | 12 +- .../src/compiler/item/const_item.rs | 2 +- .../src/compiler/item/enum_item.rs | 4 +- .../src/compiler/item/function_item.rs | 8 +- .../src/compiler/item/struct_item.rs | 4 +- .../src/compiler/item/type_alias_item.rs | 4 +- .../rue-compiler/src/compiler/stmt/if_stmt.rs | 6 +- crates/rue-compiler/src/compiler/ty.rs | 2 - .../src/compiler/ty/function_type.rs | 43 ++--- .../rue-compiler/src/compiler/ty/list_type.rs | 14 -- .../rue-compiler/src/compiler/ty/pair_type.rs | 17 +- .../rue-compiler/src/compiler/ty/path_type.rs | 9 +- crates/rue-compiler/src/lib.rs | 7 +- crates/rue-parser/src/ast.rs | 23 +-- crates/rue-parser/src/grammar.rs | 13 -- crates/rue-parser/src/syntax_kind.rs | 4 - 28 files changed, 177 insertions(+), 229 deletions(-) delete mode 100644 crates/rue-compiler/src/compiler/ty/list_type.rs diff --git a/crates/rue-compiler/src/compiler/expr/binary_expr.rs b/crates/rue-compiler/src/compiler/expr/binary_expr.rs index 4c81442..dcfb415 100644 --- a/crates/rue-compiler/src/compiler/expr/binary_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/binary_expr.rs @@ -60,7 +60,7 @@ impl Compiler<'_> { if self .db - .compare_type(lhs.type_id, self.builtins.public_key) + .compare_type(lhs.type_id, self.ty.std().public_key) .is_equal() { return self.add_public_key(lhs.hir_id, rhs, text_range); @@ -68,77 +68,77 @@ impl Compiler<'_> { if self .db - .compare_type(lhs.type_id, self.builtins.bytes) + .compare_type(lhs.type_id, self.ty.std().bytes) .is_equal() { return self.add_bytes(lhs.hir_id, rhs, text_range); } let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().int))) .unwrap_or_else(|| self.unknown()); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - self.binary_op(BinOp::Add, lhs.hir_id, rhs.hir_id, self.builtins.int) + self.type_check(lhs.type_id, self.ty.std().int, text_range); + self.type_check(rhs.type_id, self.ty.std().int, text_range); + self.binary_op(BinOp::Add, lhs.hir_id, rhs.hir_id, self.ty.std().int) } fn add_public_key(&mut self, lhs: HirId, rhs: Option<&Expr>, text_range: TextRange) -> Value { let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.public_key))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().public_key))) .unwrap_or_else(|| self.unknown()); - self.type_check(rhs.type_id, self.builtins.public_key, text_range); - self.binary_op(BinOp::PointAdd, lhs, rhs.hir_id, self.builtins.public_key) + self.type_check(rhs.type_id, self.ty.std().public_key, text_range); + self.binary_op(BinOp::PointAdd, lhs, rhs.hir_id, self.ty.std().public_key) } fn add_bytes(&mut self, lhs: HirId, rhs: Option<&Expr>, text_range: TextRange) -> Value { let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.bytes))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().bytes))) .unwrap_or_else(|| self.unknown()); - self.type_check(rhs.type_id, self.builtins.bytes, text_range); - self.binary_op(BinOp::Concat, lhs, rhs.hir_id, self.builtins.bytes) + self.type_check(rhs.type_id, self.ty.std().bytes, text_range); + self.binary_op(BinOp::Concat, lhs, rhs.hir_id, self.ty.std().bytes) } fn op_subtract(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().int))) .unwrap_or_else(|| self.unknown()); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - self.binary_op(BinOp::Subtract, lhs.hir_id, rhs.hir_id, self.builtins.int) + self.type_check(lhs.type_id, self.ty.std().int, text_range); + self.type_check(rhs.type_id, self.ty.std().int, text_range); + self.binary_op(BinOp::Subtract, lhs.hir_id, rhs.hir_id, self.ty.std().int) } fn op_multiply(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().int))) .unwrap_or_else(|| self.unknown()); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - self.binary_op(BinOp::Multiply, lhs.hir_id, rhs.hir_id, self.builtins.int) + self.type_check(lhs.type_id, self.ty.std().int, text_range); + self.type_check(rhs.type_id, self.ty.std().int, text_range); + self.binary_op(BinOp::Multiply, lhs.hir_id, rhs.hir_id, self.ty.std().int) } fn op_divide(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().int))) .unwrap_or_else(|| self.unknown()); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - self.binary_op(BinOp::Divide, lhs.hir_id, rhs.hir_id, self.builtins.int) + self.type_check(lhs.type_id, self.ty.std().int, text_range); + self.type_check(rhs.type_id, self.ty.std().int, text_range); + self.binary_op(BinOp::Divide, lhs.hir_id, rhs.hir_id, self.ty.std().int) } fn op_remainder(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().int))) .unwrap_or_else(|| self.unknown()); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - self.binary_op(BinOp::Remainder, lhs.hir_id, rhs.hir_id, self.builtins.int) + self.type_check(lhs.type_id, self.ty.std().int, text_range); + self.type_check(rhs.type_id, self.ty.std().int, text_range); + self.binary_op(BinOp::Remainder, lhs.hir_id, rhs.hir_id, self.ty.std().int) } fn op_equals(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { @@ -153,13 +153,13 @@ impl Compiler<'_> { .map(|rhs| self.compile_expr(rhs, Some(lhs.type_id))) .unwrap_or_else(|| self.unknown()); - let mut value = self.binary_op(BinOp::Equals, lhs.hir_id, rhs.hir_id, self.builtins.bool); + let mut value = self.binary_op(BinOp::Equals, lhs.hir_id, rhs.hir_id, self.ty.std().bool); let mut is_atom = true; if !self .db - .compare_type(lhs.type_id, self.builtins.bytes) + .compare_type(lhs.type_id, self.ty.std().bytes) .is_castable() { self.db.error( @@ -171,7 +171,7 @@ impl Compiler<'_> { if !self .db - .compare_type(rhs.type_id, self.builtins.bytes) + .compare_type(rhs.type_id, self.ty.std().bytes) .is_castable() { self.db.error( @@ -183,11 +183,11 @@ impl Compiler<'_> { if self .db - .compare_type(lhs.type_id, self.builtins.nil) + .compare_type(lhs.type_id, self.ty.std().nil) .is_equal() { if let Some(guard_path) = rhs.guard_path { - let then_type = self.builtins.nil; + let then_type = self.ty.std().nil; let else_type = self.db.non_nullable(rhs.type_id); value.guards.insert( guard_path, @@ -198,11 +198,11 @@ impl Compiler<'_> { if self .db - .compare_type(rhs.type_id, self.builtins.nil) + .compare_type(rhs.type_id, self.ty.std().nil) .is_equal() { if let Some(guard_path) = lhs.guard_path.clone() { - let then_type = self.builtins.nil; + let then_type = self.ty.std().nil; let else_type = self.db.non_nullable(lhs.type_id); value.guards.insert( guard_path, @@ -223,7 +223,7 @@ impl Compiler<'_> { let mut value = Value::new( self.db.alloc_hir(Hir::Op(Op::Not, comparison.hir_id)), - self.builtins.bool, + self.ty.std().bool, ); for (symbol_id, guard) in comparison.guards { @@ -242,7 +242,7 @@ impl Compiler<'_> { ) -> Value { if self .db - .compare_type(lhs.type_id, self.builtins.bytes) + .compare_type(lhs.type_id, self.ty.std().bytes) .is_assignable() { let op = match op { @@ -254,11 +254,11 @@ impl Compiler<'_> { }; let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.bytes))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().bytes))) .unwrap_or_else(|| self.unknown()); - self.type_check(rhs.type_id, self.builtins.bytes, text_range); - return self.binary_op(op, lhs.hir_id, rhs.hir_id, self.builtins.bool); + self.type_check(rhs.type_id, self.ty.std().bytes, text_range); + return self.binary_op(op, lhs.hir_id, rhs.hir_id, self.ty.std().bool); } let op = match op { @@ -270,31 +270,31 @@ impl Compiler<'_> { }; let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().int))) .unwrap_or_else(|| self.unknown()); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - self.binary_op(op, lhs.hir_id, rhs.hir_id, self.builtins.bool) + self.type_check(lhs.type_id, self.ty.std().int, text_range); + self.type_check(rhs.type_id, self.ty.std().int, text_range); + self.binary_op(op, lhs.hir_id, rhs.hir_id, self.ty.std().bool) } fn op_and(&mut self, lhs: Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { self.type_guard_stack.push(lhs.then_guards()); let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.bool))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().bool))) .unwrap_or_else(|| self.unknown()); self.type_guard_stack.pop().unwrap(); - self.type_check(lhs.type_id, self.builtins.bool, text_range); - self.type_check(rhs.type_id, self.builtins.bool, text_range); + self.type_check(lhs.type_id, self.ty.std().bool, text_range); + self.type_check(rhs.type_id, self.ty.std().bool, text_range); let mut value = self.binary_op( BinOp::LogicalAnd, lhs.hir_id, rhs.hir_id, - self.builtins.bool, + self.ty.std().bool, ); value.guards.extend(lhs.guards); value.guards.extend(rhs.guards); @@ -305,74 +305,74 @@ impl Compiler<'_> { self.type_guard_stack.push(lhs.then_guards()); let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.bool))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().bool))) .unwrap_or_else(|| self.unknown()); self.type_guard_stack.pop().unwrap(); - self.type_check(lhs.type_id, self.builtins.bool, text_range); - self.type_check(rhs.type_id, self.builtins.bool, text_range); - self.binary_op(BinOp::LogicalOr, lhs.hir_id, rhs.hir_id, self.builtins.bool) + self.type_check(lhs.type_id, self.ty.std().bool, text_range); + self.type_check(rhs.type_id, self.ty.std().bool, text_range); + self.binary_op(BinOp::LogicalOr, lhs.hir_id, rhs.hir_id, self.ty.std().bool) } fn op_bitwise_and(&mut self, lhs: Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { if self .db - .compare_type(lhs.type_id, self.builtins.bool) + .compare_type(lhs.type_id, self.ty.std().bool) .is_assignable() { let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.bool))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().bool))) .unwrap_or_else(|| self.unknown()); - self.type_check(rhs.type_id, self.builtins.bool, text_range); + self.type_check(rhs.type_id, self.ty.std().bool, text_range); - let mut value = self.binary_op(BinOp::All, lhs.hir_id, rhs.hir_id, self.builtins.bool); + let mut value = self.binary_op(BinOp::All, lhs.hir_id, rhs.hir_id, self.ty.std().bool); value.guards.extend(lhs.guards); value.guards.extend(rhs.guards); return value; } let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().int))) .unwrap_or_else(|| self.unknown()); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - self.binary_op(BinOp::BitwiseAnd, lhs.hir_id, rhs.hir_id, self.builtins.int) + self.type_check(lhs.type_id, self.ty.std().int, text_range); + self.type_check(rhs.type_id, self.ty.std().int, text_range); + self.binary_op(BinOp::BitwiseAnd, lhs.hir_id, rhs.hir_id, self.ty.std().int) } fn op_bitwise_or(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { if self .db - .compare_type(lhs.type_id, self.builtins.bool) + .compare_type(lhs.type_id, self.ty.std().bool) .is_assignable() { let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.bool))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().bool))) .unwrap_or_else(|| self.unknown()); - self.type_check(rhs.type_id, self.builtins.bool, text_range); - return self.binary_op(BinOp::Any, lhs.hir_id, rhs.hir_id, self.builtins.bool); + self.type_check(rhs.type_id, self.ty.std().bool, text_range); + return self.binary_op(BinOp::Any, lhs.hir_id, rhs.hir_id, self.ty.std().bool); } let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().int))) .unwrap_or_else(|| self.unknown()); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - self.binary_op(BinOp::BitwiseOr, lhs.hir_id, rhs.hir_id, self.builtins.int) + self.type_check(lhs.type_id, self.ty.std().int, text_range); + self.type_check(rhs.type_id, self.ty.std().int, text_range); + self.binary_op(BinOp::BitwiseOr, lhs.hir_id, rhs.hir_id, self.ty.std().int) } fn op_bitwise_xor(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().int))) .unwrap_or_else(|| self.unknown()); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); - self.binary_op(BinOp::BitwiseXor, lhs.hir_id, rhs.hir_id, self.builtins.int) + self.type_check(lhs.type_id, self.ty.std().int, text_range); + self.type_check(rhs.type_id, self.ty.std().int, text_range); + self.binary_op(BinOp::BitwiseXor, lhs.hir_id, rhs.hir_id, self.ty.std().int) } fn op_left_arith_shift( @@ -382,16 +382,16 @@ impl Compiler<'_> { text_range: TextRange, ) -> Value { let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().int))) .unwrap_or_else(|| self.unknown()); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); + self.type_check(lhs.type_id, self.ty.std().int, text_range); + self.type_check(rhs.type_id, self.ty.std().int, text_range); self.binary_op( BinOp::LeftArithShift, lhs.hir_id, rhs.hir_id, - self.builtins.int, + self.ty.std().int, ) } @@ -402,16 +402,16 @@ impl Compiler<'_> { text_range: TextRange, ) -> Value { let rhs = rhs - .map(|rhs| self.compile_expr(rhs, Some(self.builtins.int))) + .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().int))) .unwrap_or_else(|| self.unknown()); - self.type_check(lhs.type_id, self.builtins.int, text_range); - self.type_check(rhs.type_id, self.builtins.int, text_range); + self.type_check(lhs.type_id, self.ty.std().int, text_range); + self.type_check(rhs.type_id, self.ty.std().int, text_range); self.binary_op( BinOp::RightArithShift, lhs.hir_id, rhs.hir_id, - self.builtins.int, + self.ty.std().int, ) } } diff --git a/crates/rue-compiler/src/compiler/expr/cast_expr.rs b/crates/rue-compiler/src/compiler/expr/cast_expr.rs index 225ac8a..c4a4b73 100644 --- a/crates/rue-compiler/src/compiler/expr/cast_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/cast_expr.rs @@ -7,7 +7,7 @@ impl Compiler<'_> { // It's fine to default to unknown, since the cast check will succeed anyways. let type_id = cast .ty() - .map_or(self.builtins.unknown, |ty| self.compile_type(ty)); + .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); // Let's used the cast type as the expected type. let Some(expr) = cast diff --git a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs index d76d6a4..0e642a2 100644 --- a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs @@ -29,7 +29,7 @@ impl Compiler<'_> { let mut type_id = field_type; if index == struct_type.fields.len() - 1 && struct_type.rest == Rest::Optional { - type_id = self.db.alloc_type(Type::Optional(type_id)); + type_id = self.ty.alloc(Type::Optional(type_id)); } Value::new( @@ -56,7 +56,7 @@ impl Compiler<'_> { let mut type_id = field_type; if index == fields.len() - 1 && variant_type.rest == Rest::Optional { - type_id = self.db.alloc_type(Type::Optional(type_id)); + type_id = self.ty.alloc(Type::Optional(type_id)); } let fields_hir_id = self.db.alloc_hir(Hir::Op(Op::Rest, old_value.hir_id)); @@ -99,7 +99,7 @@ impl Compiler<'_> { }, Type::Bytes | Type::Bytes32 if field_name.text() == "length" => Value::new( self.db.alloc_hir(Hir::Op(Op::Strlen, old_value.hir_id)), - self.builtins.int, + self.ty.std().int, ), _ => { self.db.error( diff --git a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs index 3650762..e53eb4a 100644 --- a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs @@ -123,7 +123,7 @@ impl Compiler<'_> { // Calculate the return type. let mut type_id = - function_type.map_or(self.builtins.unknown, |expected| expected.return_type); + function_type.map_or(self.ty.std().unknown, |expected| expected.return_type); if !generic_types.is_empty() { type_id = self.db.substitute_type(type_id, &generic_types); diff --git a/crates/rue-compiler/src/compiler/expr/guard_expr.rs b/crates/rue-compiler/src/compiler/expr/guard_expr.rs index c08a01e..8909371 100644 --- a/crates/rue-compiler/src/compiler/expr/guard_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/guard_expr.rs @@ -23,7 +23,7 @@ impl Compiler<'_> { let ty = guard .ty() - .map_or(self.builtins.unknown, |ty| self.compile_type(ty)); + .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); let Some((guard, hir_id)) = self.guard_into(expr.type_id, ty, expr.hir_id, guard.syntax().text_range()) @@ -31,7 +31,7 @@ impl Compiler<'_> { return Value::new(self.builtins.unknown_hir, ty); }; - let mut value = Value::new(hir_id, self.builtins.bool); + let mut value = Value::new(hir_id, self.ty.std().bool); if let Some(guard_path) = expr.guard_path { value.guards.insert(guard_path, guard); @@ -53,18 +53,18 @@ impl Compiler<'_> { text_range, ); return Some(( - Guard::new(TypeOverride::new(to), TypeOverride::new(self.builtins.bool)), + Guard::new(TypeOverride::new(to), TypeOverride::new(self.ty.std().bool)), hir_id, )); } match (self.db.ty(from).clone(), self.db.ty(to).clone()) { (Type::Any, Type::Pair(PairType { first, rest })) => { - if !self.db.compare_type(first, self.builtins.any).is_equal() { + if !self.db.compare_type(first, self.ty.std().any).is_equal() { self.db.error(ErrorKind::NonAnyPairTypeGuard, text_range); } - if !self.db.compare_type(rest, self.builtins.any).is_equal() { + if !self.db.compare_type(rest, self.ty.std().any).is_equal() { self.db.error(ErrorKind::NonAnyPairTypeGuard, text_range); } @@ -72,15 +72,15 @@ impl Compiler<'_> { Some(( Guard::new( TypeOverride::new(to), - TypeOverride::new(self.builtins.bytes), + TypeOverride::new(self.ty.std().bytes), ), hir_id, )) } (Type::Any, Type::Bytes) => { - let pair_type = self.db.alloc_type(Type::Pair(PairType { - first: self.builtins.any, - rest: self.builtins.any, + let pair_type = self.ty.alloc(Type::Pair(PairType { + first: self.ty.std().any, + rest: self.ty.std().any, })); let is_cons = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)); let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, is_cons)); @@ -100,12 +100,12 @@ impl Compiler<'_> { let hir_id = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)); Some(( - Guard::new(TypeOverride::new(to), TypeOverride::new(self.builtins.nil)), + Guard::new(TypeOverride::new(to), TypeOverride::new(self.ty.std().nil)), hir_id, )) } (Type::List(inner), Type::Nil) => { - let pair_type = self.db.alloc_type(Type::Pair(PairType { + let pair_type = self.ty.alloc(Type::Pair(PairType { first: inner, rest: from, })); diff --git a/crates/rue-compiler/src/compiler/expr/if_expr.rs b/crates/rue-compiler/src/compiler/expr/if_expr.rs index c70e6dc..c60531c 100644 --- a/crates/rue-compiler/src/compiler/expr/if_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/if_expr.rs @@ -6,7 +6,7 @@ impl Compiler<'_> { pub fn compile_if_expr(&mut self, if_expr: &IfExpr, expected_type: Option) -> Value { let condition = if_expr .condition() - .map(|condition| self.compile_expr(&condition, Some(self.builtins.bool))); + .map(|condition| self.compile_expr(&condition, Some(self.ty.std().bool))); if let Some(condition) = condition.as_ref() { self.type_guard_stack.push(condition.then_guards()); @@ -38,7 +38,7 @@ impl Compiler<'_> { if let Some(condition_type) = condition.as_ref().map(|condition| condition.type_id) { self.type_check( condition_type, - self.builtins.bool, + self.ty.std().bool, if_expr.condition().unwrap().syntax().text_range(), ); } @@ -54,7 +54,7 @@ impl Compiler<'_> { let ty = then_block .as_ref() .or(else_block.as_ref()) - .map_or(self.builtins.unknown, |block| block.type_id); + .map_or(self.ty.std().unknown, |block| block.type_id); let value = condition.and_then(|condition| { then_block.and_then(|then_block| { diff --git a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs index bbb3759..1c44e33 100644 --- a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs @@ -100,7 +100,7 @@ impl Compiler<'_> { // Check the type of the field initializer. self.type_check( value.type_id, - expected_type.unwrap_or(self.builtins.unknown), + expected_type.unwrap_or(self.ty.std().unknown), field.syntax().text_range(), ); diff --git a/crates/rue-compiler/src/compiler/expr/lambda_expr.rs b/crates/rue-compiler/src/compiler/expr/lambda_expr.rs index d969a6a..b70c329 100644 --- a/crates/rue-compiler/src/compiler/expr/lambda_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/lambda_expr.rs @@ -43,7 +43,7 @@ impl Compiler<'_> { .unwrap_or_default() { // Create the generic type id. - let type_id = self.db.alloc_type(Type::Generic); + let type_id = self.ty.alloc(Type::Generic); // Check for duplicate generic types. if self.scope().ty(generic_type.text()).is_some() { @@ -79,7 +79,7 @@ impl Compiler<'_> { .unwrap_or_else(|| { self.db .error(ErrorKind::CannotInferType, param.syntax().text_range()); - self.builtins.unknown + self.ty.std().unknown }); // Substitute generic types in the parameter type. @@ -91,7 +91,7 @@ impl Compiler<'_> { let param_type_id = if param.optional().is_some() { // If the parameter is optional, wrap the type in a possibly undefined type. // This prevents referencing the parameter until it's checked for undefined. - self.db.alloc_type(Type::Optional(type_id)) + self.ty.alloc(Type::Optional(type_id)) } else { type_id }; @@ -166,7 +166,7 @@ impl Compiler<'_> { Value::new( self.db .alloc_hir(Hir::Reference(symbol_id, lambda_expr.syntax().text_range())), - self.db.alloc_type(Type::Function(ty)), + self.ty.alloc(Type::Function(ty)), ) } } diff --git a/crates/rue-compiler/src/compiler/expr/list_expr.rs b/crates/rue-compiler/src/compiler/expr/list_expr.rs index 6ec6063..47dd366 100644 --- a/crates/rue-compiler/src/compiler/expr/list_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/list_expr.rs @@ -52,7 +52,7 @@ impl Compiler<'_> { _ => None, }; } else { - list_type = Some(self.db.alloc_type(Type::List(output.type_id))); + list_type = Some(self.ty.alloc(Type::List(output.type_id))); item_type = Some(output.type_id); } } @@ -82,7 +82,7 @@ impl Compiler<'_> { Value::new( hir_id, self.db - .alloc_type(Type::List(item_type.unwrap_or(self.builtins.unknown))), + .alloc_type(Type::List(item_type.unwrap_or(self.ty.std().unknown))), ) } } diff --git a/crates/rue-compiler/src/compiler/expr/literal_expr.rs b/crates/rue-compiler/src/compiler/expr/literal_expr.rs index bacacde..8872395 100644 --- a/crates/rue-compiler/src/compiler/expr/literal_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/literal_expr.rs @@ -23,11 +23,11 @@ impl Compiler<'_> { fn compile_bool_literal(&mut self, value: bool) -> Value { let atom = if value { vec![1] } else { vec![] }; - Value::new(self.db.alloc_hir(Hir::Atom(atom)), self.builtins.bool) + Value::new(self.db.alloc_hir(Hir::Atom(atom)), self.ty.std().bool) } fn compile_nil_literal(&mut self) -> Value { - Value::new(self.db.alloc_hir(Hir::Atom(Vec::new())), self.builtins.nil) + Value::new(self.db.alloc_hir(Hir::Atom(Vec::new())), self.ty.std().nil) } fn compile_int_literal(&mut self, int: &SyntaxToken) -> Value { @@ -45,7 +45,7 @@ impl Compiler<'_> { }); // Extract the atom representation of the number. - Value::new(self.db.alloc_hir(Hir::Atom(atom)), self.builtins.int) + Value::new(self.db.alloc_hir(Hir::Atom(atom)), self.ty.std().int) } fn compile_hex_literal(&mut self, hex: &SyntaxToken) -> Value { @@ -67,15 +67,15 @@ impl Compiler<'_> { if bytes_len == 32 { // We'll assume this is a `Bytes32` since it's the correct length. // This makes putting hashes in the code more convenient. - self.builtins.bytes32 + self.ty.std().bytes32 } else if bytes_len == 48 { // We'll assume this is a `PublicKey` since it's the correct length. // It's unlikely to intend the type being `Bytes`, but you can cast if needed. - self.builtins.public_key + self.ty.std().public_key } else { // Everything else is just `Bytes`. // Leading zeros are not removed, so `0x00` is different than `0`. - self.builtins.bytes + self.ty.std().bytes }, ) } @@ -89,7 +89,7 @@ impl Compiler<'_> { Value::new( self.db .alloc_hir(Hir::Atom(text.replace(quote, "").as_bytes().to_vec())), - self.builtins.bytes, + self.ty.std().bytes, ) } diff --git a/crates/rue-compiler/src/compiler/expr/pair_expr.rs b/crates/rue-compiler/src/compiler/expr/pair_expr.rs index ad60ba3..360f714 100644 --- a/crates/rue-compiler/src/compiler/expr/pair_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/pair_expr.rs @@ -21,7 +21,7 @@ impl Compiler<'_> { let value = self.compile_expr(&expr, first); self.type_check( value.type_id, - first.unwrap_or(self.builtins.unknown), + first.unwrap_or(self.ty.std().unknown), expr.syntax().text_range(), ); value @@ -36,7 +36,7 @@ impl Compiler<'_> { let value = self.compile_expr(&expr, rest); self.type_check( value.type_id, - rest.unwrap_or(self.builtins.unknown), + rest.unwrap_or(self.ty.std().unknown), expr.syntax().text_range(), ); value @@ -44,7 +44,7 @@ impl Compiler<'_> { .unwrap_or_else(|| self.unknown()); let hir_id = self.db.alloc_hir(Hir::Pair(first.hir_id, rest.hir_id)); - let type_id = self.db.alloc_type(Type::Pair(PairType { + let type_id = self.ty.alloc(Type::Pair(PairType { first: first.type_id, rest: rest.type_id, })); diff --git a/crates/rue-compiler/src/compiler/expr/path_expr.rs b/crates/rue-compiler/src/compiler/expr/path_expr.rs index 0631c36..765914d 100644 --- a/crates/rue-compiler/src/compiler/expr/path_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/path_expr.rs @@ -84,7 +84,7 @@ impl Compiler<'_> { let mut value = match self.db.symbol(symbol_id).clone() { Symbol::Unknown | Symbol::Module(..) => unreachable!(), Symbol::Function(Function { ty, .. }) | Symbol::InlineFunction(Function { ty, .. }) => { - let type_id = self.db.alloc_type(Type::Function(ty.clone())); + let type_id = self.ty.alloc(Type::Function(ty.clone())); Value::new(reference, override_type_id.unwrap_or(type_id)) } Symbol::Parameter(type_id) => { diff --git a/crates/rue-compiler/src/compiler/expr/prefix_expr.rs b/crates/rue-compiler/src/compiler/expr/prefix_expr.rs index 3369395..4aaedb0 100644 --- a/crates/rue-compiler/src/compiler/expr/prefix_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/prefix_expr.rs @@ -11,9 +11,9 @@ impl Compiler<'_> { // Determine the expected type based on the prefix operator. let expected_type = match prefix_expr.op() { Some(PrefixOp::BitwiseNot | PrefixOp::Positive | PrefixOp::Negative) => { - Some(self.builtins.int) + Some(self.ty.std().int) } - Some(PrefixOp::Not) => Some(self.builtins.bool), + Some(PrefixOp::Not) => Some(self.ty.std().bool), None => None, }; @@ -32,7 +32,7 @@ impl Compiler<'_> { // Check the type of the expression. self.type_check( expr.type_id, - expected_type.unwrap_or(self.builtins.unknown), + expected_type.unwrap_or(self.ty.std().unknown), prefix_expr .expr() .map_or(prefix_expr.syntax().text_range(), |ast| { @@ -45,7 +45,7 @@ impl Compiler<'_> { // Negate the expression and its type guards. let mut value = Value::new( self.db.alloc_hir(Hir::Op(Op::Not, expr.hir_id)), - self.builtins.bool, + self.ty.std().bool, ); for (symbol_id, guard) in expr.guards { @@ -61,7 +61,7 @@ impl Compiler<'_> { self.builtins.nil_hir, expr.hir_id, )), - self.builtins.int, + self.ty.std().int, ), PrefixOp::Positive => { // Return the expression as is. @@ -71,7 +71,7 @@ impl Compiler<'_> { // Negate the expression and its type guards. Value::new( self.db.alloc_hir(Hir::Op(Op::BitwiseNot, expr.hir_id)), - self.builtins.int, + self.ty.std().int, ) } } diff --git a/crates/rue-compiler/src/compiler/item/const_item.rs b/crates/rue-compiler/src/compiler/item/const_item.rs index 33197f4..ab2ec3e 100644 --- a/crates/rue-compiler/src/compiler/item/const_item.rs +++ b/crates/rue-compiler/src/compiler/item/const_item.rs @@ -11,7 +11,7 @@ impl Compiler<'_> { let type_id = const_item .ty() - .map_or(self.builtins.unknown, |ty| self.compile_type(ty)); + .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); let hir_id = self.db.alloc_hir(Hir::Unknown); diff --git a/crates/rue-compiler/src/compiler/item/enum_item.rs b/crates/rue-compiler/src/compiler/item/enum_item.rs index e3f6edb..37216d5 100644 --- a/crates/rue-compiler/src/compiler/item/enum_item.rs +++ b/crates/rue-compiler/src/compiler/item/enum_item.rs @@ -41,7 +41,7 @@ impl Compiler<'_> { // Allocate a new type for the variant. // It has to be `Unknown` for now, since field types may not be declared yet. - let type_id = self.db.alloc_type(Type::Unknown); + let type_id = self.ty.alloc(Type::Unknown); // Add the variant to the enum and define the token for the variant. variants.insert(name.to_string(), type_id); @@ -49,7 +49,7 @@ impl Compiler<'_> { } // Allocate a new type for the enum. - let type_id = self.db.alloc_type(Type::Enum(EnumType { + let type_id = self.ty.alloc(Type::Enum(EnumType { has_fields, variants, })); diff --git a/crates/rue-compiler/src/compiler/item/function_item.rs b/crates/rue-compiler/src/compiler/item/function_item.rs index f6cd53c..7648e6e 100644 --- a/crates/rue-compiler/src/compiler/item/function_item.rs +++ b/crates/rue-compiler/src/compiler/item/function_item.rs @@ -28,7 +28,7 @@ impl Compiler<'_> { .unwrap_or_default() { // Create the generic type id. - let type_id = self.db.alloc_type(Type::Generic); + let type_id = self.ty.alloc(Type::Generic); // Check for duplicate generic types. if self.scope().ty(generic_type.text()).is_some() { @@ -65,7 +65,7 @@ impl Compiler<'_> { // Otherwise, it's a parser error. let type_id = param .ty() - .map_or(self.builtins.unknown, |ty| self.compile_type(ty)); + .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); // Add the parameter type to the list and update the parameter symbol. param_types.push(type_id); @@ -73,7 +73,7 @@ impl Compiler<'_> { *self.db.symbol_mut(symbol_id) = Symbol::Parameter(if param.optional().is_some() { // If the parameter is optional, wrap the type in a possibly undefined type. // This prevents referencing the parameter until it's checked for undefined. - self.db.alloc_type(Type::Optional(type_id)) + self.ty.alloc(Type::Optional(type_id)) } else { // Otherwise, just use the type. type_id @@ -125,7 +125,7 @@ impl Compiler<'_> { // Otherwise, it's a parser error. let return_type = function_item .return_type() - .map_or(self.builtins.unknown, |ty| self.compile_type(ty)); + .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); self.scope_stack.pop().unwrap(); diff --git a/crates/rue-compiler/src/compiler/item/struct_item.rs b/crates/rue-compiler/src/compiler/item/struct_item.rs index 3c18103..c6bdfda 100644 --- a/crates/rue-compiler/src/compiler/item/struct_item.rs +++ b/crates/rue-compiler/src/compiler/item/struct_item.rs @@ -10,7 +10,7 @@ use crate::{ impl Compiler<'_> { /// Define a type for a struct in the current scope, but leave it as unknown for now. pub fn declare_struct_item(&mut self, struct_item: &StructItem) -> TypeId { - let type_id = self.db.alloc_type(Type::Unknown); + let type_id = self.ty.alloc(Type::Unknown); if let Some(name) = struct_item.name() { self.scope_mut().define_type(name.to_string(), type_id); self.db.insert_type_token(type_id, name); @@ -43,7 +43,7 @@ impl Compiler<'_> { for (i, field) in fields.into_iter().enumerate() { let type_id = field .ty() - .map_or(self.builtins.unknown, |ty| self.compile_type(ty)); + .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); // Check if it's a spread or optional parameter. let last = i + 1 == len; 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 c37d70e..60ffc28 100644 --- a/crates/rue-compiler/src/compiler/item/type_alias_item.rs +++ b/crates/rue-compiler/src/compiler/item/type_alias_item.rs @@ -5,7 +5,7 @@ use crate::{compiler::Compiler, value::Type, ErrorKind, TypeId}; impl Compiler<'_> { /// Define a type for an alias in the current scope, but leave it as unknown for now. pub fn declare_type_alias_item(&mut self, type_alias: &TypeAliasItem) -> TypeId { - let type_id = self.db.alloc_type(Type::Unknown); + let type_id = self.ty.alloc(Type::Unknown); if let Some(name) = type_alias.name() { self.scope_mut().define_type(name.to_string(), type_id); self.db.insert_type_token(type_id, name); @@ -19,7 +19,7 @@ impl Compiler<'_> { let type_id = type_alias .ty() - .map_or(self.builtins.unknown, |ty| self.compile_type(ty)); + .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); diff --git a/crates/rue-compiler/src/compiler/stmt/if_stmt.rs b/crates/rue-compiler/src/compiler/stmt/if_stmt.rs index 0495bc4..c58c5ac 100644 --- a/crates/rue-compiler/src/compiler/stmt/if_stmt.rs +++ b/crates/rue-compiler/src/compiler/stmt/if_stmt.rs @@ -19,13 +19,13 @@ impl Compiler<'_> { // Compile the condition expression. let condition = if_stmt .condition() - .map(|condition| self.compile_expr(&condition, Some(self.builtins.bool))) + .map(|condition| self.compile_expr(&condition, Some(self.ty.std().bool))) .unwrap_or_else(|| self.unknown()); // Check that the condition is a boolean. self.type_check( condition.type_id, - self.builtins.bool, + self.ty.std().bool, if_stmt.syntax().text_range(), ); @@ -61,7 +61,7 @@ impl Compiler<'_> { // Check that the output matches the expected type. self.type_check( then_block.type_id, - expected_type.unwrap_or(self.builtins.unknown), + expected_type.unwrap_or(self.ty.std().unknown), if_stmt.syntax().text_range(), ); diff --git a/crates/rue-compiler/src/compiler/ty.rs b/crates/rue-compiler/src/compiler/ty.rs index 499314a..c056f3c 100644 --- a/crates/rue-compiler/src/compiler/ty.rs +++ b/crates/rue-compiler/src/compiler/ty.rs @@ -4,7 +4,6 @@ use rue_typing::TypeId; use super::Compiler; mod function_type; -mod list_type; mod pair_type; mod path_type; @@ -14,7 +13,6 @@ impl Compiler<'_> { Type::PathType(path) => { self.compile_path_type(&path.idents(), path.syntax().text_range()) } - Type::ListType(list) => self.compile_list_type(&list), Type::FunctionType(function) => self.compile_function_type(&function), Type::PairType(tuple) => self.compile_pair_type(&tuple), } diff --git a/crates/rue-compiler/src/compiler/ty/function_type.rs b/crates/rue-compiler/src/compiler/ty/function_type.rs index 6ed034d..2d42814 100644 --- a/crates/rue-compiler/src/compiler/ty/function_type.rs +++ b/crates/rue-compiler/src/compiler/ty/function_type.rs @@ -1,17 +1,13 @@ -use std::collections::HashSet; - +use indexmap::IndexSet; use rue_parser::{AstNode, FunctionType as Ast}; +use rue_typing::{Callable, Rest, Type, TypeId}; -use crate::{ - compiler::Compiler, - value::{FunctionType, Rest, Type}, - ErrorKind, TypeId, -}; +use crate::{compiler::Compiler, ErrorKind}; impl Compiler<'_> { pub fn compile_function_type(&mut self, function: &Ast) -> TypeId { - let mut param_types = Vec::new(); - let mut type_names = HashSet::new(); + let mut parameters = Vec::new(); + let mut parameter_names = IndexSet::new(); let mut rest = Rest::Nil; let params = function.params(); @@ -21,23 +17,26 @@ impl Compiler<'_> { // We don't actually use the names yet, // but go ahead and check for duplicates. // TODO: Use the name in the actual type? - if let Some(name) = param.name() { - if !type_names.insert(name.to_string()) { - self.db.error( - ErrorKind::DuplicateSymbol(name.to_string()), - name.text_range(), - ); - } + let name = param + .name() + .map(|token| token.to_string()) + .unwrap_or(format!("#{i}")); + + if !parameter_names.insert(name.to_string()) { + self.db.error( + ErrorKind::DuplicateSymbol(name.to_string()), + param.name().unwrap().text_range(), + ); } // Compile the type of the parameter, if present. // Otherwise, it's a parser error. let type_id = param .ty() - .map_or(self.builtins.unknown, |ty| self.compile_type(ty)); + .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); // Add the parameter type to the list. - param_types.push(type_id); + parameters.push(type_id); // Check if it's a spread or optional parameter. let last = i + 1 == len; @@ -70,12 +69,14 @@ impl Compiler<'_> { // Otherwise, it's a parser error. let return_type = function .return_type() - .map_or(self.builtins.unknown, |ty| self.compile_type(ty)); + .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); // Allocate a new type for the function. // TODO: Support generic types. - self.db.alloc_type(Type::Function(FunctionType { - param_types, + self.ty.alloc(Type::Callable(Callable { + original_type_id: None, + parameter_names, + parameters, rest, return_type, generic_types: Vec::new(), diff --git a/crates/rue-compiler/src/compiler/ty/list_type.rs b/crates/rue-compiler/src/compiler/ty/list_type.rs deleted file mode 100644 index e2cd9f0..0000000 --- a/crates/rue-compiler/src/compiler/ty/list_type.rs +++ /dev/null @@ -1,14 +0,0 @@ -use rue_parser::ListType; - -use crate::{compiler::Compiler, value::Type, TypeId}; - -impl Compiler<'_> { - pub fn compile_list_type(&mut self, list: &ListType) -> TypeId { - let Some(inner) = list.ty() else { - return self.builtins.unknown; - }; - - let item_type = self.compile_type(inner); - self.db.alloc_type(Type::List(item_type)) - } -} diff --git a/crates/rue-compiler/src/compiler/ty/pair_type.rs b/crates/rue-compiler/src/compiler/ty/pair_type.rs index 97c0f1b..84fb306 100644 --- a/crates/rue-compiler/src/compiler/ty/pair_type.rs +++ b/crates/rue-compiler/src/compiler/ty/pair_type.rs @@ -1,21 +1,18 @@ -use rue_parser::PairType as Ast; +use rue_parser::PairType; +use rue_typing::{Type, TypeId}; -use crate::{ - compiler::Compiler, - value::{PairType, Type}, - TypeId, -}; +use crate::compiler::Compiler; impl Compiler<'_> { - pub fn compile_pair_type(&mut self, pair_type: &Ast) -> TypeId { + pub fn compile_pair_type(&mut self, pair_type: &PairType) -> TypeId { let first = pair_type .first() - .map_or(self.builtins.unknown, |ty| self.compile_type(ty)); + .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); let rest = pair_type .rest() - .map_or(self.builtins.unknown, |ty| self.compile_type(ty)); + .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); - self.db.alloc_type(Type::Pair(PairType { first, rest })) + self.ty.alloc(Type::Pair(first, rest)) } } diff --git a/crates/rue-compiler/src/compiler/ty/path_type.rs b/crates/rue-compiler/src/compiler/ty/path_type.rs index 4886596..8f62735 100644 --- a/crates/rue-compiler/src/compiler/ty/path_type.rs +++ b/crates/rue-compiler/src/compiler/ty/path_type.rs @@ -1,19 +1,20 @@ use rowan::TextRange; use rue_parser::SyntaxToken; +use rue_typing::TypeId; use crate::{ compiler::{ path::{PathItem, PathKind}, Compiler, }, - ErrorKind, TypeId, + ErrorKind, }; impl Compiler<'_> { pub fn compile_path_type(&mut self, idents: &[SyntaxToken], text_range: TextRange) -> TypeId { let Some(mut item) = self.resolve_base_path(&idents[0], PathKind::Type, idents.len() == 1) else { - return self.builtins.unknown; + return self.ty.std().unknown; }; let mut last_ident = idents[0].to_string(); @@ -22,7 +23,7 @@ impl Compiler<'_> { let Some(next_item) = self.resolve_next_path(item, name, PathKind::Type, i == idents.len() - 1) else { - return self.builtins.unknown; + return self.ty.std().unknown; }; last_ident = name.to_string(); item = next_item; @@ -33,7 +34,7 @@ impl Compiler<'_> { PathItem::Symbol(..) => { self.db .error(ErrorKind::ExpectedTypePath(last_ident), text_range); - self.builtins.unknown + self.ty.std().unknown } } } diff --git a/crates/rue-compiler/src/lib.rs b/crates/rue-compiler/src/lib.rs index 2d8a7fd..80a352b 100644 --- a/crates/rue-compiler/src/lib.rs +++ b/crates/rue-compiler/src/lib.rs @@ -22,6 +22,7 @@ use rue_parser::Root; pub use database::*; pub use error::*; +use rue_typing::TypeSystem; #[derive(Debug)] pub struct Output { @@ -30,8 +31,9 @@ pub struct Output { } pub fn compile(allocator: &mut Allocator, root: &Root, mut should_codegen: bool) -> Output { - let mut db = Database::default(); - let mut ctx = setup_compiler(&mut db); + let mut db = Database::new(); + let mut ty = TypeSystem::new(); + let mut ctx = setup_compiler(&mut db, &mut ty); let stdlib = load_standard_library(&mut ctx); let main_module_id = load_module(&mut ctx, root); @@ -40,6 +42,7 @@ pub fn compile(allocator: &mut Allocator, root: &Root, mut should_codegen: bool) let main = try_export_main(&mut db, main_module_id); let graph = build_graph( &mut db, + &ty, &symbol_table, main_module_id, &[main_module_id, stdlib], diff --git a/crates/rue-parser/src/ast.rs b/crates/rue-parser/src/ast.rs index 1d8d006..c1c6cdc 100644 --- a/crates/rue-parser/src/ast.rs +++ b/crates/rue-parser/src/ast.rs @@ -120,10 +120,8 @@ ast_node!(IndexAccessExpr); ast_node!(LambdaExpr); ast_node!(LambdaParam); -ast_enum!(Type, PathType, ListType, PairType, FunctionType); +ast_enum!(Type, PathType, PairType, FunctionType); ast_node!(PathType); -ast_node!(ListType); -ast_node!(ListTypeItem); ast_node!(PairType); ast_node!(FunctionType); ast_node!(FunctionTypeParam); @@ -835,25 +833,6 @@ impl PathType { } } -impl ListType { - pub fn ty(&self) -> Option { - self.syntax().children().find_map(Type::cast) - } -} - -impl ListTypeItem { - pub fn spread(&self) -> Option { - self.syntax() - .children_with_tokens() - .filter_map(SyntaxElement::into_token) - .find(|token| token.kind() == SyntaxKind::Spread) - } - - pub fn ty(&self) -> Option { - self.syntax().children().find_map(Type::cast) - } -} - impl PairType { pub fn first(&self) -> Option { self.syntax().children().find_map(Type::cast) diff --git a/crates/rue-parser/src/grammar.rs b/crates/rue-parser/src/grammar.rs index df7e4f6..b30ffd2 100644 --- a/crates/rue-parser/src/grammar.rs +++ b/crates/rue-parser/src/grammar.rs @@ -562,8 +562,6 @@ fn lambda_param(p: &mut Parser<'_>) { const TYPE_RECOVERY_SET: &[SyntaxKind] = &[SyntaxKind::OpenBrace, SyntaxKind::CloseBrace]; fn ty(p: &mut Parser<'_>) { - let checkpoint = p.checkpoint(); - if p.at(SyntaxKind::Ident) { path_type(p); } else if p.at(SyntaxKind::Fun) { @@ -592,17 +590,6 @@ fn ty(p: &mut Parser<'_>) { } else { return p.error(TYPE_RECOVERY_SET); } - - loop { - if p.at(SyntaxKind::OpenBracket) { - p.start_at(checkpoint, SyntaxKind::ListType); - p.bump(); - p.expect(SyntaxKind::CloseBracket); - p.finish(); - } else { - break; - } - } } fn path_type(p: &mut Parser<'_>) { diff --git a/crates/rue-parser/src/syntax_kind.rs b/crates/rue-parser/src/syntax_kind.rs index 2a70514..7dc5608 100644 --- a/crates/rue-parser/src/syntax_kind.rs +++ b/crates/rue-parser/src/syntax_kind.rs @@ -114,8 +114,6 @@ pub enum SyntaxKind { FieldAccessExpr, IndexAccessExpr, PathType, - ListType, - ListTypeItem, PairType, FunctionType, FunctionTypeParam, @@ -235,8 +233,6 @@ impl fmt::Display for SyntaxKind { Self::FieldAccessExpr => "field access expression", Self::IndexAccessExpr => "index access expression", Self::PathType => "path type", - Self::ListType => "list type", - Self::ListTypeItem => "list type item", Self::PairType => "pair type", Self::FunctionType => "function type", Self::FunctionTypeParam => "function type parameter", From 563cd4f6709acbe535d7c9b97abc49ccac548e9b Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 12:18:36 -0400 Subject: [PATCH 059/100] Temp 6 --- .../src/compiler/expr/block_expr.rs | 3 +- .../src/compiler/expr/pair_expr.rs | 17 +++------- .../src/compiler/expr/path_expr.rs | 17 ++++++---- .../src/compiler/expr/prefix_expr.rs | 2 +- crates/rue-typing/src/difference.rs | 2 ++ crates/rue-typing/src/semantic_types.rs | 5 ++- crates/rue-typing/src/substitute_type.rs | 1 + crates/rue-typing/src/type_system.rs | 32 +++++++++++++------ 8 files changed, 48 insertions(+), 31 deletions(-) diff --git a/crates/rue-compiler/src/compiler/expr/block_expr.rs b/crates/rue-compiler/src/compiler/expr/block_expr.rs index 8d63073..88eb2c8 100644 --- a/crates/rue-compiler/src/compiler/expr/block_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/block_expr.rs @@ -1,10 +1,11 @@ use rue_parser::{AstNode, Block}; +use rue_typing::TypeId; use crate::{ compiler::{block::BlockTerminator, Compiler}, scope::Scope, value::Value, - ErrorKind, TypeId, + ErrorKind, }; impl Compiler<'_> { diff --git a/crates/rue-compiler/src/compiler/expr/pair_expr.rs b/crates/rue-compiler/src/compiler/expr/pair_expr.rs index 360f714..bb710b4 100644 --- a/crates/rue-compiler/src/compiler/expr/pair_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/pair_expr.rs @@ -1,17 +1,13 @@ use rue_parser::{AstNode, PairExpr}; +use rue_typing::{Type, TypeId}; -use crate::{ - compiler::Compiler, - hir::Hir, - value::{PairType, Type, Value}, - TypeId, -}; +use crate::{compiler::Compiler, hir::Hir, value::Value}; impl Compiler<'_> { pub fn compile_pair_expr(&mut self, pair: &PairExpr, expected_type: Option) -> Value { // Extract the first and rest type out of the expected type. - let first = expected_type.and_then(|type_id| self.db.first_type(type_id)); - let rest = expected_type.and_then(|type_id| self.db.rest_type(type_id)); + let first = expected_type.and_then(|type_id| self.ty.pair_first(type_id)); + let rest = expected_type.and_then(|type_id| self.ty.pair_rest(type_id)); // Compile the first expression, if present. // It's a parser error if not, so it's fine to return unknown. @@ -44,10 +40,7 @@ impl Compiler<'_> { .unwrap_or_else(|| self.unknown()); let hir_id = self.db.alloc_hir(Hir::Pair(first.hir_id, rest.hir_id)); - let type_id = self.ty.alloc(Type::Pair(PairType { - first: first.type_id, - rest: rest.type_id, - })); + let type_id = self.ty.alloc(Type::Pair(first.type_id, rest.type_id)); // We throw away type guards by creating this new value. // They shouldn't be relevant since the type is not `Bool`. diff --git a/crates/rue-compiler/src/compiler/expr/path_expr.rs b/crates/rue-compiler/src/compiler/expr/path_expr.rs index 765914d..c01ce00 100644 --- a/crates/rue-compiler/src/compiler/expr/path_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/path_expr.rs @@ -1,5 +1,6 @@ use rowan::TextRange; use rue_parser::SyntaxToken; +use rue_typing::{bigint_to_bytes, Type}; use crate::{ compiler::{ @@ -8,7 +9,7 @@ use crate::{ }, hir::Hir, symbol::{Function, Symbol}, - value::{GuardPath, Type, Value}, + value::{GuardPath, Value}, ErrorKind, }; @@ -35,22 +36,24 @@ impl Compiler<'_> { let symbol_id = match item { PathItem::Symbol(symbol_id) => symbol_id, PathItem::Type(type_id) => { - if let Type::EnumVariant(variant_type) = self.db.ty(type_id).clone() { - if variant_type.fields.is_some() { + if let Type::Variant(variant) = self.ty.get(type_id).clone() { + if variant.field_names.is_some() { self.db.error( ErrorKind::InvalidEnumVariantReference(self.type_name(type_id)), text_range, ); } - let Type::Enum(enum_type) = self.db.ty(variant_type.enum_type) else { + let Type::Enum(enum_type) = self.ty.get(variant.enum_type) else { unreachable!(); }; - let mut hir_id = variant_type.discriminant; + let mut hir_id = self + .db + .alloc_hir(Hir::Atom(bigint_to_bytes(variant.discriminant))); if enum_type.has_fields { - hir_id = self.db.alloc_hir(Hir::Pair(hir_id, self.builtins.nil_hir)); + hir_id = self.db.alloc_hir(Hir::Pair(hir_id, self.builtins.nil)); } return Value::new(hir_id, type_id); @@ -84,7 +87,7 @@ impl Compiler<'_> { let mut value = match self.db.symbol(symbol_id).clone() { Symbol::Unknown | Symbol::Module(..) => unreachable!(), Symbol::Function(Function { ty, .. }) | Symbol::InlineFunction(Function { ty, .. }) => { - let type_id = self.ty.alloc(Type::Function(ty.clone())); + let type_id = self.ty.alloc(Type::Callable(ty.clone())); Value::new(reference, override_type_id.unwrap_or(type_id)) } Symbol::Parameter(type_id) => { diff --git a/crates/rue-compiler/src/compiler/expr/prefix_expr.rs b/crates/rue-compiler/src/compiler/expr/prefix_expr.rs index 4aaedb0..c4e8a49 100644 --- a/crates/rue-compiler/src/compiler/expr/prefix_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/prefix_expr.rs @@ -58,7 +58,7 @@ impl Compiler<'_> { // Subtract the expression from nil. self.db.alloc_hir(Hir::BinaryOp( BinOp::Subtract, - self.builtins.nil_hir, + self.builtins.nil, expr.hir_id, )), self.ty.std().int, diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index 5ec59bd..73a1a4a 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -305,6 +305,7 @@ pub(crate) fn difference_type( type_id, rest: variant.rest, generic_types: variant.generic_types, + discriminant: variant.discriminant, })) } (_, Type::Variant(variant)) => { @@ -318,6 +319,7 @@ pub(crate) fn difference_type( type_id, rest: variant.rest, generic_types: variant.generic_types, + discriminant: variant.discriminant, })) } diff --git a/crates/rue-typing/src/semantic_types.rs b/crates/rue-typing/src/semantic_types.rs index 1903041..6024b23 100644 --- a/crates/rue-typing/src/semantic_types.rs +++ b/crates/rue-typing/src/semantic_types.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use indexmap::IndexSet; +use num_bigint::BigInt; use crate::TypeId; @@ -75,11 +76,13 @@ pub struct Variant { /// The enum type to which this variant belongs. pub enum_type: TypeId, /// The field names of the variant. - pub field_names: IndexSet, + pub field_names: Option>, /// The structural type of the enum variant. pub type_id: TypeId, /// The rest kind of the variant. pub rest: Rest, /// The generic types of the variant. pub generic_types: Vec, + /// The discriminant value. + pub discriminant: BigInt, } diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index 286dc4a..2febc42 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -130,6 +130,7 @@ pub(crate) fn substitute_type( field_names: ty.field_names, rest: ty.rest, generic_types: ty.generic_types, + discriminant: ty.discriminant, })) } } else { diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index 6b86033..462e51a 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -85,21 +85,35 @@ impl TypeSystem { self.arena.alloc(ty) } - pub fn get_raw(&self, id: TypeId) -> &Type { - &self.arena[id] + pub fn get_raw(&self, type_id: TypeId) -> &Type { + &self.arena[type_id] } - pub fn get(&self, id: TypeId) -> &Type { - match &self.arena[id] { - Type::Ref(id) => self.get(*id), + pub fn get(&self, type_id: TypeId) -> &Type { + match &self.arena[type_id] { + Type::Ref(type_id) => self.get(*type_id), ty => ty, } } - pub fn get_mut(&mut self, id: TypeId) -> &mut Type { - match &self.arena[id] { - Type::Ref(id) => self.get_mut(*id), - _ => &mut self.arena[id], + pub fn get_mut(&mut self, type_id: TypeId) -> &mut Type { + match &self.arena[type_id] { + Type::Ref(type_id) => self.get_mut(*type_id), + _ => &mut self.arena[type_id], + } + } + + pub fn pair_first(&self, type_id: TypeId) -> Option { + match self.get(type_id) { + Type::Pair(first, _) => Some(*first), + _ => None, + } + } + + pub fn pair_rest(&self, type_id: TypeId) -> Option { + match self.get(type_id) { + Type::Pair(_, rest) => Some(*rest), + _ => None, } } From 7313c52e89d4fd687cc0444b9de8813f0c2cb2c3 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 12:30:03 -0400 Subject: [PATCH 060/100] Temp 7 --- crates/rue-compiler/src/compiler/expr.rs | 2 - .../rue-compiler/src/compiler/expr/if_expr.rs | 5 ++- .../src/compiler/expr/index_access_expr.rs | 38 ------------------- .../src/compiler/expr/list_expr.rs | 2 +- crates/rue-parser/src/ast.rs | 15 -------- crates/rue-parser/src/grammar.rs | 6 --- crates/rue-parser/src/syntax_kind.rs | 2 - 7 files changed, 4 insertions(+), 66 deletions(-) delete mode 100644 crates/rue-compiler/src/compiler/expr/index_access_expr.rs diff --git a/crates/rue-compiler/src/compiler/expr.rs b/crates/rue-compiler/src/compiler/expr.rs index 16933fd..ff4b311 100644 --- a/crates/rue-compiler/src/compiler/expr.rs +++ b/crates/rue-compiler/src/compiler/expr.rs @@ -13,7 +13,6 @@ mod function_call_expr; mod group_expr; mod guard_expr; mod if_expr; -mod index_access_expr; mod initializer_expr; mod lambda_expr; mod list_expr; @@ -47,7 +46,6 @@ impl Compiler<'_> { Expr::IfExpr(if_expr) => self.compile_if_expr(if_expr, expected_type), Expr::FunctionCallExpr(call) => self.compile_function_call_expr(call), Expr::FieldAccessExpr(field_access) => self.compile_field_access_expr(field_access), - Expr::IndexAccessExpr(index_access) => self.compile_index_access_expr(index_access), }; self.is_callee = false; diff --git a/crates/rue-compiler/src/compiler/expr/if_expr.rs b/crates/rue-compiler/src/compiler/expr/if_expr.rs index c60531c..9c226b4 100644 --- a/crates/rue-compiler/src/compiler/expr/if_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/if_expr.rs @@ -1,6 +1,7 @@ use rue_parser::{AstNode, IfExpr}; +use rue_typing::TypeId; -use crate::{compiler::Compiler, hir::Hir, value::Value, TypeId}; +use crate::{compiler::Compiler, hir::Hir, value::Value}; impl Compiler<'_> { pub fn compile_if_expr(&mut self, if_expr: &IfExpr, expected_type: Option) -> Value { @@ -68,6 +69,6 @@ impl Compiler<'_> { }) }); - Value::new(value.unwrap_or(self.builtins.unknown_hir), ty) + Value::new(value.unwrap_or(self.builtins.unknown), ty) } } diff --git a/crates/rue-compiler/src/compiler/expr/index_access_expr.rs b/crates/rue-compiler/src/compiler/expr/index_access_expr.rs deleted file mode 100644 index 2b04cc5..0000000 --- a/crates/rue-compiler/src/compiler/expr/index_access_expr.rs +++ /dev/null @@ -1,38 +0,0 @@ -use rue_parser::{AstNode, IndexAccessExpr}; - -use crate::{ - compiler::Compiler, - value::{Type, Value}, - ErrorKind, -}; - -impl Compiler<'_> { - pub fn compile_index_access_expr(&mut self, index_access: &IndexAccessExpr) -> Value { - let Some(value) = index_access - .expr() - .map(|expr| self.compile_expr(&expr, None)) - else { - return self.unknown(); - }; - - let Some(index_token) = index_access.index() else { - return self.unknown(); - }; - - let index = index_token - .text() - .replace('_', "") - .parse() - .expect("failed to parse integer literal"); - - let Type::List(item_type) = self.db.ty(value.type_id).clone() else { - self.db.error( - ErrorKind::InvalidIndexAccess(self.type_name(value.type_id)), - index_access.expr().unwrap().syntax().text_range(), - ); - return self.unknown(); - }; - - Value::new(self.compile_index(value.hir_id, index, false), item_type) - } -} diff --git a/crates/rue-compiler/src/compiler/expr/list_expr.rs b/crates/rue-compiler/src/compiler/expr/list_expr.rs index 47dd366..fc6e7fc 100644 --- a/crates/rue-compiler/src/compiler/expr/list_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/list_expr.rs @@ -69,7 +69,7 @@ impl Compiler<'_> { items.push(output.hir_id); } - let mut hir_id = self.builtins.nil_hir; + let mut hir_id = self.builtins.nil; for (i, item) in items.into_iter().rev().enumerate() { if i == 0 && !nil_terminated { diff --git a/crates/rue-parser/src/ast.rs b/crates/rue-parser/src/ast.rs index c1c6cdc..615420c 100644 --- a/crates/rue-parser/src/ast.rs +++ b/crates/rue-parser/src/ast.rs @@ -97,7 +97,6 @@ ast_enum!( IfExpr, FunctionCallExpr, FieldAccessExpr, - IndexAccessExpr, ); ast_node!(PathExpr); ast_node!(InitializerExpr); @@ -115,7 +114,6 @@ ast_node!(IfExpr); ast_node!(FunctionCallExpr); ast_node!(FunctionCallArg); ast_node!(FieldAccessExpr); -ast_node!(IndexAccessExpr); ast_node!(LambdaExpr); ast_node!(LambdaParam); @@ -810,19 +808,6 @@ impl FieldAccessExpr { } } -impl IndexAccessExpr { - pub fn expr(&self) -> Option { - self.syntax().children().find_map(Expr::cast) - } - - pub fn index(&self) -> Option { - self.syntax() - .children_with_tokens() - .filter_map(SyntaxElement::into_token) - .find(|token| token.kind() == SyntaxKind::Int) - } -} - impl PathType { pub fn idents(&self) -> Vec { self.syntax() diff --git a/crates/rue-parser/src/grammar.rs b/crates/rue-parser/src/grammar.rs index b30ffd2..9216db2 100644 --- a/crates/rue-parser/src/grammar.rs +++ b/crates/rue-parser/src/grammar.rs @@ -418,12 +418,6 @@ fn expr_binding_power(p: &mut Parser<'_>, minimum_binding_power: u8, allow_initi p.bump(); p.expect(SyntaxKind::Ident); p.finish(); - } else if p.at(SyntaxKind::OpenBracket) { - p.start_at(checkpoint, SyntaxKind::IndexAccessExpr); - p.bump(); - p.expect(SyntaxKind::Int); - p.expect(SyntaxKind::CloseBracket); - p.finish(); } else if p.at(SyntaxKind::As) { p.start_at(checkpoint, SyntaxKind::CastExpr); p.bump(); diff --git a/crates/rue-parser/src/syntax_kind.rs b/crates/rue-parser/src/syntax_kind.rs index 7dc5608..1ddde8c 100644 --- a/crates/rue-parser/src/syntax_kind.rs +++ b/crates/rue-parser/src/syntax_kind.rs @@ -112,7 +112,6 @@ pub enum SyntaxKind { FunctionCallExpr, FunctionCallArg, FieldAccessExpr, - IndexAccessExpr, PathType, PairType, FunctionType, @@ -231,7 +230,6 @@ impl fmt::Display for SyntaxKind { Self::FunctionCallExpr => "function call expression", Self::FunctionCallArg => "function call argument", Self::FieldAccessExpr => "field access expression", - Self::IndexAccessExpr => "index access expression", Self::PathType => "path type", Self::PairType => "pair type", Self::FunctionType => "function type", From 8bd10aea098ad11f857d95bdea409ade2ac46c2a Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 14:45:56 -0400 Subject: [PATCH 061/100] Add construction and deconstruction --- .../src/compiler/item/type_alias_item.rs | 24 +-- crates/rue-typing/src/difference.rs | 29 +-- crates/rue-typing/src/semantic_types.rs | 192 +++++++++++++++++- crates/rue-typing/src/type_system.rs | 12 +- 4 files changed, 219 insertions(+), 38 deletions(-) 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( From 564e3970bd6e8fc3a3a38df3a5c33c1c74cd51b5 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 15:11:02 -0400 Subject: [PATCH 062/100] Temp 8 --- .../src/compiler/expr/field_access_expr.rs | 49 +++++++++----- .../src/compiler/expr/group_expr.rs | 3 +- .../src/compiler/expr/list_expr.rs | 8 +-- .../src/compiler/expr/literal_expr.rs | 21 +----- .../src/compiler/expr/pair_expr.rs | 4 +- .../src/compiler/item/enum_item.rs | 66 ++++++++++--------- .../src/compiler/item/function_item.rs | 19 ++++-- .../src/compiler/item/struct_item.rs | 23 ++++--- .../rue-compiler/src/compiler/stmt/if_stmt.rs | 3 +- .../src/compiler/ty/function_type.rs | 7 +- crates/rue-typing/src/semantic_types.rs | 11 ++-- 11 files changed, 113 insertions(+), 101 deletions(-) diff --git a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs index 0e642a2..c766217 100644 --- a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs @@ -1,9 +1,10 @@ use rue_parser::FieldAccessExpr; +use rue_typing::{deconstruct_items, Rest, Type}; use crate::{ compiler::Compiler, hir::{Hir, Op}, - value::{GuardPathItem, PairType, Rest, Type, Value}, + value::{GuardPathItem, Value}, ErrorKind, }; @@ -21,22 +22,24 @@ impl Compiler<'_> { return self.unknown(); }; - let mut new_value = match self.db.ty(old_value.type_id).clone() { - Type::Struct(struct_type) => { - if let Some((index, _, &field_type)) = - struct_type.fields.get_full(field_name.text()) - { - let mut type_id = field_type; + let mut new_value = match self.ty.get(old_value.type_id).clone() { + Type::Struct(ty) => { + let fields = deconstruct_items(self.ty, ty.type_id, ty.field_names.len(), ty.rest) + .expect("invalid struct type"); - if index == struct_type.fields.len() - 1 && struct_type.rest == Rest::Optional { - type_id = self.ty.alloc(Type::Optional(type_id)); + if let Some(index) = ty.field_names.get_index_of(field_name.text()) { + let type_id = fields[index]; + + if index == ty.field_names.len() - 1 && ty.rest == Rest::Optional { + todo!(); + // TODO: type_id = self.ty.alloc(Type::Optional(type_id)); } Value::new( self.compile_index( old_value.hir_id, index, - index == struct_type.fields.len() - 1 && struct_type.rest != Rest::Nil, + index == ty.field_names.len() - 1 && ty.rest != Rest::Nil, ), type_id, ) @@ -49,14 +52,24 @@ impl Compiler<'_> { return self.unknown(); } } - Type::EnumVariant(variant_type) => { - let fields = variant_type.fields.unwrap_or_default(); + Type::Variant(variant) => { + let field_names = variant.field_names.clone().unwrap_or_default(); + + let fields = variant + .field_names + .as_ref() + .map(|field_names| { + deconstruct_items(self.ty, variant.type_id, field_names.len(), variant.rest) + .expect("invalid struct type") + }) + .unwrap_or_default(); - if let Some((index, _, &field_type)) = fields.get_full(field_name.text()) { - let mut type_id = field_type; + if let Some(index) = field_names.get_index_of(field_name.text()) { + let type_id = fields[index]; - if index == fields.len() - 1 && variant_type.rest == Rest::Optional { - type_id = self.ty.alloc(Type::Optional(type_id)); + if index == fields.len() - 1 && variant.rest == Rest::Optional { + // TODO: type_id = self.ty.alloc(Type::Optional(type_id)); + todo!() } let fields_hir_id = self.db.alloc_hir(Hir::Op(Op::Rest, old_value.hir_id)); @@ -65,7 +78,7 @@ impl Compiler<'_> { self.compile_index( fields_hir_id, index, - index == fields.len() - 1 && variant_type.rest != Rest::Nil, + index == fields.len() - 1 && variant.rest != Rest::Nil, ), type_id, ) @@ -78,7 +91,7 @@ impl Compiler<'_> { return self.unknown(); } } - Type::Pair(PairType { first, rest }) => match field_name.text() { + Type::Pair(first, rest) => match field_name.text() { "first" => Value::new( self.db.alloc_hir(Hir::Op(Op::First, old_value.hir_id)), first, diff --git a/crates/rue-compiler/src/compiler/expr/group_expr.rs b/crates/rue-compiler/src/compiler/expr/group_expr.rs index a176058..e8245de 100644 --- a/crates/rue-compiler/src/compiler/expr/group_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/group_expr.rs @@ -1,6 +1,7 @@ use rue_parser::GroupExpr; +use rue_typing::TypeId; -use crate::{compiler::Compiler, value::Value, TypeId}; +use crate::{compiler::Compiler, value::Value}; impl Compiler<'_> { pub fn compile_group_expr( diff --git a/crates/rue-compiler/src/compiler/expr/list_expr.rs b/crates/rue-compiler/src/compiler/expr/list_expr.rs index fc6e7fc..b34b94a 100644 --- a/crates/rue-compiler/src/compiler/expr/list_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/list_expr.rs @@ -1,11 +1,7 @@ use rue_parser::{AstNode, ListExpr}; +use rue_typing::{Type, TypeId}; -use crate::{ - compiler::Compiler, - hir::Hir, - value::{Type, Value}, - ErrorKind, TypeId, -}; +use crate::{compiler::Compiler, hir::Hir, value::Value, ErrorKind}; impl Compiler<'_> { pub fn compile_list_expr( diff --git a/crates/rue-compiler/src/compiler/expr/literal_expr.rs b/crates/rue-compiler/src/compiler/expr/literal_expr.rs index 8872395..05dfefa 100644 --- a/crates/rue-compiler/src/compiler/expr/literal_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/literal_expr.rs @@ -1,8 +1,7 @@ -use clvmr::Allocator; -use num_bigint::BigInt; use rue_parser::{LiteralExpr, SyntaxKind, SyntaxToken}; +use rue_typing::bigint_to_bytes; -use crate::{compiler::Compiler, hir::Hir, value::Value, ErrorKind}; +use crate::{compiler::Compiler, hir::Hir, value::Value}; impl Compiler<'_> { pub fn compile_literal_expr(&mut self, literal: &LiteralExpr) -> Value { @@ -39,10 +38,7 @@ impl Compiler<'_> { .parse() .expect("failed to parse integer literal"); - let atom = Self::bigint_to_bytes(bigint).unwrap_or_else(|| { - self.db.error(ErrorKind::IntegerTooLarge, int.text_range()); - Vec::new() - }); + let atom = bigint_to_bytes(bigint); // Extract the atom representation of the number. Value::new(self.db.alloc_hir(Hir::Atom(atom)), self.ty.std().int) @@ -92,15 +88,4 @@ impl Compiler<'_> { self.ty.std().bytes, ) } - - pub fn bigint_to_bytes(bigint: BigInt) -> Option> { - // Create a CLVM allocator. - let mut allocator = Allocator::new(); - - // Try to allocate the number. - let ptr = allocator.new_number(bigint).ok()?; - - // Extract the atom representation of the number. - Some(allocator.atom(ptr).as_ref().to_vec()) - } } diff --git a/crates/rue-compiler/src/compiler/expr/pair_expr.rs b/crates/rue-compiler/src/compiler/expr/pair_expr.rs index bb710b4..901319a 100644 --- a/crates/rue-compiler/src/compiler/expr/pair_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/pair_expr.rs @@ -6,8 +6,8 @@ use crate::{compiler::Compiler, hir::Hir, value::Value}; impl Compiler<'_> { pub fn compile_pair_expr(&mut self, pair: &PairExpr, expected_type: Option) -> Value { // Extract the first and rest type out of the expected type. - let first = expected_type.and_then(|type_id| self.ty.pair_first(type_id)); - let rest = expected_type.and_then(|type_id| self.ty.pair_rest(type_id)); + let first = expected_type.and_then(|type_id| Some(self.ty.get_pair(type_id)?.0)); + let rest = expected_type.and_then(|type_id| Some(self.ty.get_pair(type_id)?.1)); // Compile the first expression, if present. // It's a parser error if not, so it's fine to return unknown. diff --git a/crates/rue-compiler/src/compiler/item/enum_item.rs b/crates/rue-compiler/src/compiler/item/enum_item.rs index 37216d5..aa2b579 100644 --- a/crates/rue-compiler/src/compiler/item/enum_item.rs +++ b/crates/rue-compiler/src/compiler/item/enum_item.rs @@ -4,13 +4,9 @@ use indexmap::IndexMap; use num_bigint::BigInt; use num_traits::Zero; use rue_parser::EnumItem; +use rue_typing::{construct_items, Enum, Type, TypeId, Variant}; -use crate::{ - compiler::Compiler, - hir::Hir, - value::{EnumType, EnumVariantType, Type}, - ErrorKind, TypeId, -}; +use crate::{compiler::Compiler, ErrorKind}; impl Compiler<'_> { pub fn declare_enum_item(&mut self, enum_item: &EnumItem) -> TypeId { @@ -41,30 +37,36 @@ impl Compiler<'_> { // Allocate a new type for the variant. // It has to be `Unknown` for now, since field types may not be declared yet. - let type_id = self.ty.alloc(Type::Unknown); + let variant_type_id = self.ty.alloc(Type::Unknown); // Add the variant to the enum and define the token for the variant. - variants.insert(name.to_string(), type_id); - self.db.insert_type_token(type_id, name); + variants.insert(name.to_string(), variant_type_id); + self.db.insert_type_token(variant_type_id, name); } // Allocate a new type for the enum. - let type_id = self.ty.alloc(Type::Enum(EnumType { + let enum_structure = self + .ty + .alloc(Type::Union(variants.values().copied().collect())); + + let enum_type_id = self.ty.alloc(Type::Enum(Enum { + original_type_id: None, + type_id: enum_structure, has_fields, variants, })); // Add the enum to the scope and define the token for the enum. if let Some(name) = enum_item.name() { - self.scope_mut().define_type(name.to_string(), type_id); - self.db.insert_type_token(type_id, name); + self.scope_mut().define_type(name.to_string(), enum_type_id); + self.db.insert_type_token(enum_type_id, name); } - type_id + enum_type_id } pub fn compile_enum_item(&mut self, enum_item: &EnumItem, enum_type_id: TypeId) { - let Type::Enum(enum_type) = self.db.ty(enum_type_id).clone() else { + let Type::Enum(enum_type) = self.ty.get(enum_type_id).clone() else { unreachable!(); }; @@ -132,29 +134,33 @@ impl Compiler<'_> { BigInt::zero() }; - let atom = Self::bigint_to_bytes(discriminant).unwrap_or_else(|| { - self.db.error( - ErrorKind::EnumDiscriminantTooLarge, - variant - .discriminant() - .map_or(name.text_range(), |token| token.text_range()), - ); - Vec::new() - }); - - let discriminant = self.db.alloc_hir(Hir::Atom(atom)); - // Update the variant to use the real `EnumVariant` type. - *self.db.ty_mut(variant_type_id) = Type::EnumVariant(EnumVariantType { + let discriminant_type = self.ty.alloc(Type::Value(discriminant.clone())); + + let type_id = if enum_type.has_fields { + construct_items( + self.ty, + [discriminant_type] + .into_iter() + .chain(fields.values().copied()), + rest, + ) + } else { + discriminant_type + }; + + *self.ty.get_mut(variant_type_id) = Type::Variant(Variant { + original_type_id: None, enum_type: enum_type_id, - original_type_id: variant_type_id, - fields: if variant.fields().is_some() { - Some(fields) + field_names: if variant.fields().is_some() { + Some(fields.keys().cloned().collect()) } else { None }, + type_id, rest, discriminant, + generic_types: Vec::new(), }); self.type_definition_stack.pop().unwrap(); diff --git a/crates/rue-compiler/src/compiler/item/function_item.rs b/crates/rue-compiler/src/compiler/item/function_item.rs index 7648e6e..aa03ab8 100644 --- a/crates/rue-compiler/src/compiler/item/function_item.rs +++ b/crates/rue-compiler/src/compiler/item/function_item.rs @@ -1,11 +1,11 @@ use rue_parser::{AstNode, FunctionItem}; +use rue_typing::{construct_items, Callable, Rest, Type}; use crate::{ compiler::Compiler, hir::Hir, scope::Scope, symbol::{Function, Symbol}, - value::{FunctionType, Rest, Type}, ErrorKind, SymbolId, }; @@ -49,6 +49,7 @@ impl Compiler<'_> { } let mut param_types = Vec::new(); + let mut param_names = Vec::new(); let mut rest = Rest::Nil; let params = function_item.params(); @@ -73,7 +74,8 @@ impl Compiler<'_> { *self.db.symbol_mut(symbol_id) = Symbol::Parameter(if param.optional().is_some() { // If the parameter is optional, wrap the type in a possibly undefined type. // This prevents referencing the parameter until it's checked for undefined. - self.ty.alloc(Type::Optional(type_id)) + // TODO: self.ty.alloc(Type::Optional(type_id)) + todo!() } else { // Otherwise, just use the type. type_id @@ -88,8 +90,11 @@ impl Compiler<'_> { ); } + param_names.push(name.to_string()); self.scope_mut().define_symbol(name.to_string(), symbol_id); self.db.insert_symbol_token(symbol_id, name); + } else { + param_names.push(format!("#{}", i)); } // Check if it's a spread or optional parameter. @@ -133,11 +138,13 @@ impl Compiler<'_> { let hir_id = self.db.alloc_hir(Hir::Unknown); // Create the function's type. - let ty = FunctionType { - generic_types, - param_types, - rest, + let ty = Callable { + original_type_id: None, + parameter_names: param_names.into_iter().collect(), + parameters: construct_items(self.ty, param_types.into_iter(), rest), return_type, + rest, + generic_types, }; // Update the symbol with the function. diff --git a/crates/rue-compiler/src/compiler/item/struct_item.rs b/crates/rue-compiler/src/compiler/item/struct_item.rs index c6bdfda..bcca263 100644 --- a/crates/rue-compiler/src/compiler/item/struct_item.rs +++ b/crates/rue-compiler/src/compiler/item/struct_item.rs @@ -1,11 +1,8 @@ use indexmap::IndexMap; use rue_parser::{AstNode, StructField, StructItem}; +use rue_typing::{construct_items, Rest, Struct, Type, TypeId}; -use crate::{ - compiler::Compiler, - value::{Rest, StructType, Type}, - ErrorKind, TypeId, -}; +use crate::{compiler::Compiler, ErrorKind}; impl Compiler<'_> { /// Define a type for a struct in the current scope, but leave it as unknown for now. @@ -19,14 +16,20 @@ impl Compiler<'_> { } /// Compile and resolve a struct type. - pub fn compile_struct_item(&mut self, struct_item: &StructItem, type_id: TypeId) { - self.type_definition_stack.push(type_id); + pub fn compile_struct_item(&mut self, struct_item: &StructItem, struct_type_id: TypeId) { + self.type_definition_stack.push(struct_type_id); + let (fields, rest) = self.compile_struct_fields(struct_item.fields()); - *self.db.ty_mut(type_id) = Type::Struct(StructType { - original_type_id: type_id, - fields, + let type_id = construct_items(self.ty, fields.values().copied(), rest); + + *self.ty.get_mut(struct_type_id) = Type::Struct(Struct { + original_type_id: None, + field_names: fields.keys().cloned().collect(), + type_id, rest, + generic_types: Vec::new(), }); + self.type_definition_stack.pop().unwrap(); } diff --git a/crates/rue-compiler/src/compiler/stmt/if_stmt.rs b/crates/rue-compiler/src/compiler/stmt/if_stmt.rs index c58c5ac..d1af5c8 100644 --- a/crates/rue-compiler/src/compiler/stmt/if_stmt.rs +++ b/crates/rue-compiler/src/compiler/stmt/if_stmt.rs @@ -1,12 +1,13 @@ use std::collections::HashMap; use rue_parser::{AstNode, IfStmt}; +use rue_typing::TypeId; use crate::{ compiler::{block::BlockTerminator, Compiler}, scope::Scope, value::{GuardPath, TypeOverride}, - ErrorKind, HirId, TypeId, + ErrorKind, HirId, }; impl Compiler<'_> { diff --git a/crates/rue-compiler/src/compiler/ty/function_type.rs b/crates/rue-compiler/src/compiler/ty/function_type.rs index 2d42814..47dbfbb 100644 --- a/crates/rue-compiler/src/compiler/ty/function_type.rs +++ b/crates/rue-compiler/src/compiler/ty/function_type.rs @@ -1,6 +1,6 @@ use indexmap::IndexSet; use rue_parser::{AstNode, FunctionType as Ast}; -use rue_typing::{Callable, Rest, Type, TypeId}; +use rue_typing::{construct_items, Callable, Rest, Type, TypeId}; use crate::{compiler::Compiler, ErrorKind}; @@ -14,9 +14,6 @@ impl Compiler<'_> { let len = params.len(); for (i, param) in params.into_iter().enumerate() { - // We don't actually use the names yet, - // but go ahead and check for duplicates. - // TODO: Use the name in the actual type? let name = param .name() .map(|token| token.to_string()) @@ -71,6 +68,8 @@ impl Compiler<'_> { .return_type() .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); + let parameters = construct_items(self.ty, parameters.into_iter(), rest); + // Allocate a new type for the function. // TODO: Support generic types. self.ty.alloc(Type::Callable(Callable { diff --git a/crates/rue-typing/src/semantic_types.rs b/crates/rue-typing/src/semantic_types.rs index bd8b67d..ab9d5cd 100644 --- a/crates/rue-typing/src/semantic_types.rs +++ b/crates/rue-typing/src/semantic_types.rs @@ -1,18 +1,19 @@ use std::collections::HashMap; -use indexmap::IndexSet; +use indexmap::{IndexMap, IndexSet}; use num_bigint::BigInt; use crate::{Comparison, Type, TypeId, TypeSystem}; /// The kind of ending that a list has. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub enum Rest { + /// This means that the list is nil-terminated. + #[default] + Nil, /// This means that there is no special terminator for the list. /// The last element is the rest value. Spread, - /// This means that the list is nil-terminated. - Nil, /// This means that the list is nil-terminated, but may contain an additional optional value. Optional, } @@ -65,7 +66,7 @@ pub struct Enum { /// Whether the enum semantically has fields. pub has_fields: bool, /// This is a map of the original variant names to their type ids. - pub variants: HashMap, + pub variants: IndexMap, } /// Represents a variant type which can optionally have fields. From 15b8c4ff0c3963320037befd139f9155bd31d248 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 15:19:57 -0400 Subject: [PATCH 063/100] Temp 9 --- .../src/compiler/expr/function_call_expr.rs | 80 ++++++++++--------- crates/rue-typing/src/lib.rs | 2 +- crates/rue-typing/src/semantic_types.rs | 26 ++++++ 3 files changed, 71 insertions(+), 37 deletions(-) diff --git a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs index e53eb4a..b39f06f 100644 --- a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs @@ -2,13 +2,9 @@ use std::collections::HashMap; use rowan::TextRange; use rue_parser::{AstNode, FunctionCallExpr}; +use rue_typing::{deconstruct_items, unwrap_list, Callable, Rest, Semantics, Type, TypeId}; -use crate::{ - compiler::Compiler, - hir::Hir, - value::{FunctionType, Rest, Type, Value}, - ErrorKind, -}; +use crate::{compiler::Compiler, hir::Hir, value::Value, ErrorKind}; impl Compiler<'_> { pub fn compile_function_call_expr(&mut self, call: &FunctionCallExpr) -> Value { @@ -21,11 +17,16 @@ impl Compiler<'_> { let function_type = callee .as_ref() - .and_then(|callee| match self.db.ty(callee.type_id).clone() { - Type::Function(function_type) => Some(function_type), + .and_then(|callee| match self.ty.get(callee.type_id).clone() { + Type::Callable(function_type) => Some(function_type), _ => None, }); + let parameter_types = function_type.as_ref().map(|ty| { + deconstruct_items(self.ty, ty.parameters, ty.parameter_names.len(), ty.rest) + .expect("invalid function type") + }); + // Make sure the callee is callable, if present. if let Some(callee) = callee.as_ref() { if function_type.is_none() { @@ -51,16 +52,23 @@ impl Compiler<'_> { .unwrap_or(false); if let Some(function_type) = &function_type { - self.check_argument_length(function_type, len, call.syntax().text_range()); + self.check_argument_length( + function_type, + parameter_types.clone().unwrap(), + len, + call.syntax().text_range(), + ); } for (i, arg) in call_args.iter().enumerate() { // Determine the expected type. let expected_type = function_type.as_ref().and_then(|ty| { - if i < ty.param_types.len() { - Some(ty.param_types[i]) + let parameter_types = parameter_types.as_ref().unwrap(); + + if i < parameter_types.len() { + Some(parameter_types[i]) } else if ty.rest == Rest::Spread { - self.db.unwrap_list(*ty.param_types.last().unwrap()) + unwrap_list(self.ty, *parameter_types.last().unwrap()) } else { None } @@ -90,29 +98,31 @@ impl Compiler<'_> { continue; }; + let parameter_types = parameter_types.as_ref().unwrap(); + if last && spread { if function.rest != Rest::Spread { self.db.error( ErrorKind::UnsupportedFunctionSpread, call_args[i].syntax().text_range(), ); - } else if i >= function.param_types.len() - 1 { - let expected_type = *function.param_types.last().unwrap(); + } else if i >= parameter_types.len() - 1 { + let expected_type = *parameter_types.last().unwrap(); self.type_check(type_id, expected_type, call_args[i].syntax().text_range()); } - } else if function.rest == Rest::Spread && i >= function.param_types.len() - 1 { + } else if function.rest == Rest::Spread && i >= parameter_types.len() - 1 { if let Some(inner_list_type) = - self.db.unwrap_list(*function.param_types.last().unwrap()) + unwrap_list(self.ty, *parameter_types.last().unwrap()) { self.type_check(type_id, inner_list_type, call_args[i].syntax().text_range()); - } else if i == function.param_types.len() - 1 && !spread { + } else if i == parameter_types.len() - 1 && !spread { self.db.error( ErrorKind::RequiredFunctionSpread, call_args[i].syntax().text_range(), ); } - } else if i < function.param_types.len() { - let param_type = function.param_types[i]; + } else if i < parameter_types.len() { + let param_type = parameter_types[i]; self.type_check(type_id, param_type, call_args[i].syntax().text_range()); } } @@ -126,13 +136,15 @@ impl Compiler<'_> { function_type.map_or(self.ty.std().unknown, |expected| expected.return_type); if !generic_types.is_empty() { - type_id = self.db.substitute_type(type_id, &generic_types); + type_id = self + .ty + .substitute(type_id, generic_types, Semantics::Preserve); } // Build the HIR for the function call. let hir_id = self.db.alloc_hir(Hir::FunctionCall( - callee.map_or(self.builtins.unknown_hir, |callee| callee.hir_id), + callee.map_or(self.builtins.unknown, |callee| callee.hir_id), args.iter().map(|arg| arg.hir_id).collect(), spread, )); @@ -142,43 +154,39 @@ impl Compiler<'_> { fn check_argument_length( &mut self, - function: &FunctionType, + function: &Callable, + parameter_types: Vec, length: usize, text_range: TextRange, ) { match function.rest { Rest::Nil => { - if length != function.param_types.len() { + if length != parameter_types.len() { self.db.error( - ErrorKind::ArgumentMismatch(length, function.param_types.len()), + ErrorKind::ArgumentMismatch(length, parameter_types.len()), text_range, ); } } Rest::Optional => { - if length != function.param_types.len() && length != function.param_types.len() - 1 - { + if length != parameter_types.len() && length != parameter_types.len() - 1 { self.db.error( - ErrorKind::ArgumentMismatchOptional(length, function.param_types.len()), + ErrorKind::ArgumentMismatchOptional(length, parameter_types.len()), text_range, ); } } Rest::Spread => { - if self - .db - .unwrap_list(*function.param_types.last().unwrap()) - .is_some() - { - if length < function.param_types.len() - 1 { + if unwrap_list(self.ty, *parameter_types.last().unwrap()).is_some() { + if length < parameter_types.len() - 1 { self.db.error( - ErrorKind::ArgumentMismatchSpread(length, function.param_types.len()), + ErrorKind::ArgumentMismatchSpread(length, parameter_types.len()), text_range, ); } - } else if length != function.param_types.len() { + } else if length != parameter_types.len() { self.db.error( - ErrorKind::ArgumentMismatch(length, function.param_types.len()), + ErrorKind::ArgumentMismatch(length, parameter_types.len()), text_range, ); } diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index 3682658..413fb64 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -17,6 +17,7 @@ pub use check::*; pub use comparison::*; pub use semantic_types::*; pub use standard_types::*; +pub use substitute_type::*; pub use ty::*; pub use type_path::*; pub use type_system::*; @@ -24,7 +25,6 @@ pub use type_system::*; pub(crate) use difference::difference_type; pub(crate) use replace_type::replace_type; pub(crate) use stringify::stringify_type; -pub(crate) use substitute_type::{substitute_type, Semantics}; #[cfg(test)] mod test_tools; diff --git a/crates/rue-typing/src/semantic_types.rs b/crates/rue-typing/src/semantic_types.rs index ab9d5cd..3884aec 100644 --- a/crates/rue-typing/src/semantic_types.rs +++ b/crates/rue-typing/src/semantic_types.rs @@ -165,8 +165,26 @@ pub fn deconstruct_items( Some(items) } +/// Unwraps a list type into its inner type. +pub fn unwrap_list(db: &mut TypeSystem, type_id: TypeId) -> Option { + if db.compare(db.std().nil, type_id) > Comparison::Assignable { + return None; + } + + let non_nil = db.difference(type_id, db.std().nil); + let (first, rest) = db.get_pair(non_nil)?; + + if db.compare(rest, type_id) > Comparison::Assignable { + return None; + } + + Some(first) +} + #[cfg(test)] mod tests { + use crate::alloc_list; + use super::*; #[test] @@ -276,4 +294,12 @@ mod tests { let items = deconstruct_items(&mut db, type_id, 2, Rest::Optional); assert_eq!(items, Some(vec![std.bytes32, pair])); } + + #[test] + fn test_unwrap_list() { + let mut db = TypeSystem::new(); + let std = db.std(); + let list = alloc_list(&mut db, std.public_key); + assert_eq!(unwrap_list(&mut db, list), Some(std.public_key)); + } } From f10a4c8548fbb96fd29b59461051ff118fb1a705 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 15:27:36 -0400 Subject: [PATCH 064/100] TEMP FINISH (NOT FINISHED) --- .../src/compiler/expr/binary_expr.rs | 67 +-- .../src/compiler/expr/guard_expr.rs | 426 +++++++++--------- .../src/compiler/expr/initializer_expr.rs | 53 ++- .../src/compiler/expr/lambda_expr.rs | 321 +++++++------ .../src/compiler/expr/list_expr.rs | 28 +- 5 files changed, 430 insertions(+), 465 deletions(-) diff --git a/crates/rue-compiler/src/compiler/expr/binary_expr.rs b/crates/rue-compiler/src/compiler/expr/binary_expr.rs index dcfb415..430bd1e 100644 --- a/crates/rue-compiler/src/compiler/expr/binary_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/binary_expr.rs @@ -1,11 +1,12 @@ use rowan::TextRange; use rue_parser::{AstNode, BinaryExpr, BinaryOp, Expr}; +use rue_typing::{Comparison, Type, TypeId}; use crate::{ compiler::Compiler, hir::{BinOp, Hir, Op}, - value::{Guard, Type, TypeOverride, Value}, - ErrorKind, HirId, TypeId, + value::{Guard, TypeOverride, Value}, + ErrorKind, HirId, }; impl Compiler<'_> { @@ -51,26 +52,18 @@ impl Compiler<'_> { } fn op_add(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { - if matches!(self.db.ty(lhs.type_id), Type::Unknown) { + if matches!(self.ty.get(lhs.type_id), Type::Unknown) { if let Some(rhs) = rhs { self.compile_expr(rhs, None); } return self.unknown(); } - if self - .db - .compare_type(lhs.type_id, self.ty.std().public_key) - .is_equal() - { + if self.ty.compare(lhs.type_id, self.ty.std().public_key) == Comparison::Equal { return self.add_public_key(lhs.hir_id, rhs, text_range); } - if self - .db - .compare_type(lhs.type_id, self.ty.std().bytes) - .is_equal() - { + if self.ty.compare(lhs.type_id, self.ty.std().bytes) == Comparison::Equal { return self.add_bytes(lhs.hir_id, rhs, text_range); } @@ -142,7 +135,7 @@ impl Compiler<'_> { } fn op_equals(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { - if matches!(self.db.ty(lhs.type_id), Type::Unknown) { + if matches!(self.ty.get(lhs.type_id), Type::Unknown) { if let Some(rhs) = rhs { self.compile_expr(rhs, None); } @@ -157,11 +150,7 @@ impl Compiler<'_> { let mut is_atom = true; - if !self - .db - .compare_type(lhs.type_id, self.ty.std().bytes) - .is_castable() - { + if self.ty.compare(lhs.type_id, self.ty.std().bytes) > Comparison::Castable { self.db.error( ErrorKind::NonAtomEquality(self.type_name(lhs.type_id)), text_range, @@ -169,11 +158,7 @@ impl Compiler<'_> { is_atom = false; } - if !self - .db - .compare_type(rhs.type_id, self.ty.std().bytes) - .is_castable() - { + if self.ty.compare(rhs.type_id, self.ty.std().bytes) > Comparison::Castable { self.db.error( ErrorKind::NonAtomEquality(self.type_name(rhs.type_id)), text_range, @@ -181,14 +166,10 @@ impl Compiler<'_> { is_atom = false; } - if self - .db - .compare_type(lhs.type_id, self.ty.std().nil) - .is_equal() - { + if self.ty.compare(lhs.type_id, self.ty.std().nil) == Comparison::Equal { if let Some(guard_path) = rhs.guard_path { let then_type = self.ty.std().nil; - let else_type = self.db.non_nullable(rhs.type_id); + let else_type = self.ty.difference(rhs.type_id, self.ty.std().nil); value.guards.insert( guard_path, Guard::new(TypeOverride::new(then_type), TypeOverride::new(else_type)), @@ -196,14 +177,10 @@ impl Compiler<'_> { } } - if self - .db - .compare_type(rhs.type_id, self.ty.std().nil) - .is_equal() - { + if self.ty.compare(rhs.type_id, self.ty.std().nil) == Comparison::Equal { if let Some(guard_path) = lhs.guard_path.clone() { let then_type = self.ty.std().nil; - let else_type = self.db.non_nullable(lhs.type_id); + let else_type = self.ty.difference(lhs.type_id, self.ty.std().nil); value.guards.insert( guard_path, Guard::new(TypeOverride::new(then_type), TypeOverride::new(else_type)), @@ -240,11 +217,7 @@ impl Compiler<'_> { op: BinaryOp, text_range: TextRange, ) -> Value { - if self - .db - .compare_type(lhs.type_id, self.ty.std().bytes) - .is_assignable() - { + if self.ty.compare(lhs.type_id, self.ty.std().bytes) <= Comparison::Assignable { let op = match op { BinaryOp::GreaterThan => BinOp::GreaterThanBytes, BinaryOp::LessThan => BinOp::LessThanBytes, @@ -316,11 +289,7 @@ impl Compiler<'_> { } fn op_bitwise_and(&mut self, lhs: Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { - if self - .db - .compare_type(lhs.type_id, self.ty.std().bool) - .is_assignable() - { + if self.ty.compare(lhs.type_id, self.ty.std().bool) <= Comparison::Assignable { let rhs = rhs .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().bool))) .unwrap_or_else(|| self.unknown()); @@ -343,11 +312,7 @@ impl Compiler<'_> { } fn op_bitwise_or(&mut self, lhs: &Value, rhs: Option<&Expr>, text_range: TextRange) -> Value { - if self - .db - .compare_type(lhs.type_id, self.ty.std().bool) - .is_assignable() - { + if self.ty.compare(lhs.type_id, self.ty.std().bool) <= Comparison::Assignable { let rhs = rhs .map(|rhs| self.compile_expr(rhs, Some(self.ty.std().bool))) .unwrap_or_else(|| self.unknown()); diff --git a/crates/rue-compiler/src/compiler/expr/guard_expr.rs b/crates/rue-compiler/src/compiler/expr/guard_expr.rs index 8909371..54e8369 100644 --- a/crates/rue-compiler/src/compiler/expr/guard_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/guard_expr.rs @@ -1,224 +1,220 @@ -use rowan::TextRange; -use rue_parser::{AstNode, GuardExpr}; +use rue_parser::GuardExpr; +use rue_typing::TypeId; -use crate::{ - compiler::Compiler, - hir::{BinOp, Hir, Op}, - value::{Guard, PairType, Type, TypeOverride, Value}, - Comparison, ErrorKind, HirId, TypeId, WarningKind, -}; +use crate::{compiler::Compiler, value::Value}; impl Compiler<'_> { pub fn compile_guard_expr( &mut self, - guard: &GuardExpr, - expected_type: Option, + _guard: &GuardExpr, + _expected_type: Option, ) -> Value { - let Some(expr) = guard - .expr() - .map(|expr| self.compile_expr(&expr, expected_type)) - else { - return self.unknown(); - }; - - let ty = guard - .ty() - .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); - - let Some((guard, hir_id)) = - self.guard_into(expr.type_id, ty, expr.hir_id, guard.syntax().text_range()) - else { - return Value::new(self.builtins.unknown_hir, ty); - }; - - let mut value = Value::new(hir_id, self.ty.std().bool); - - if let Some(guard_path) = expr.guard_path { - value.guards.insert(guard_path, guard); - } - - value + todo!() + // let Some(expr) = guard + // .expr() + // .map(|expr| self.compile_expr(&expr, expected_type)) + // else { + // return self.unknown(); + // }; + + // let ty = guard + // .ty() + // .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); + + // let Some((guard, hir_id)) = + // self.guard_into(expr.type_id, ty, expr.hir_id, guard.syntax().text_range()) + // else { + // return Value::new(self.builtins.unknown_hir, ty); + // }; + + // let mut value = Value::new(hir_id, self.ty.std().bool); + + // if let Some(guard_path) = expr.guard_path { + // value.guards.insert(guard_path, guard); + // } + + // value } - fn guard_into( - &mut self, - from: TypeId, - to: TypeId, - hir_id: HirId, - text_range: TextRange, - ) -> Option<(Guard, HirId)> { - if self.db.compare_type(from, to) <= Comparison::Assignable { - self.db.warning( - WarningKind::RedundantTypeCheck(self.type_name(from)), - text_range, - ); - return Some(( - Guard::new(TypeOverride::new(to), TypeOverride::new(self.ty.std().bool)), - hir_id, - )); - } - - match (self.db.ty(from).clone(), self.db.ty(to).clone()) { - (Type::Any, Type::Pair(PairType { first, rest })) => { - if !self.db.compare_type(first, self.ty.std().any).is_equal() { - self.db.error(ErrorKind::NonAnyPairTypeGuard, text_range); - } - - if !self.db.compare_type(rest, self.ty.std().any).is_equal() { - self.db.error(ErrorKind::NonAnyPairTypeGuard, text_range); - } - - let hir_id = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)); - Some(( - Guard::new( - TypeOverride::new(to), - TypeOverride::new(self.ty.std().bytes), - ), - hir_id, - )) - } - (Type::Any, Type::Bytes) => { - let pair_type = self.ty.alloc(Type::Pair(PairType { - first: self.ty.std().any, - rest: self.ty.std().any, - })); - let is_cons = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)); - let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, is_cons)); - Some(( - Guard::new(TypeOverride::new(to), TypeOverride::new(pair_type)), - hir_id, - )) - } - (Type::List(inner), Type::Pair(PairType { first, rest })) => { - if !self.db.compare_type(first, inner).is_equal() { - self.db.error(ErrorKind::NonListPairTypeGuard, text_range); - } - - if !self.db.compare_type(rest, from).is_equal() { - self.db.error(ErrorKind::NonListPairTypeGuard, text_range); - } - - let hir_id = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)); - Some(( - Guard::new(TypeOverride::new(to), TypeOverride::new(self.ty.std().nil)), - hir_id, - )) - } - (Type::List(inner), Type::Nil) => { - let pair_type = self.ty.alloc(Type::Pair(PairType { - first: inner, - rest: from, - })); - let is_cons = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)); - let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, is_cons)); - Some(( - Guard::new(TypeOverride::new(to), TypeOverride::new(pair_type)), - hir_id, - )) - } - (Type::Bytes, Type::Bytes32) => { - let strlen = self.db.alloc_hir(Hir::Op(Op::Strlen, hir_id)); - let length = self.db.alloc_hir(Hir::Atom(vec![32])); - let hir_id = self - .db - .alloc_hir(Hir::BinaryOp(BinOp::Equals, strlen, length)); - Some(( - Guard::new(TypeOverride::new(to), TypeOverride::new(from)), - hir_id, - )) - } - (Type::Bytes, Type::PublicKey) => { - let strlen = self.db.alloc_hir(Hir::Op(Op::Strlen, hir_id)); - let length = self.db.alloc_hir(Hir::Atom(vec![48])); - let hir_id = self - .db - .alloc_hir(Hir::BinaryOp(BinOp::Equals, strlen, length)); - Some(( - Guard::new(TypeOverride::new(to), TypeOverride::new(from)), - hir_id, - )) - } - (Type::Enum(enum_type), Type::EnumVariant(variant_type)) => { - if variant_type.enum_type != from { - self.db.error( - ErrorKind::UnsupportedTypeGuard(self.type_name(from), self.type_name(to)), - text_range, - ); - return None; - } - - let hir_id = if enum_type.has_fields { - let first = self.db.alloc_hir(Hir::Op(Op::First, hir_id)); - self.db.alloc_hir(Hir::BinaryOp( - BinOp::Equals, - first, - variant_type.discriminant, - )) - } else { - self.db.alloc_hir(Hir::BinaryOp( - BinOp::Equals, - hir_id, - variant_type.discriminant, - )) - }; - - Some(( - Guard::new(TypeOverride::new(to), TypeOverride::new(from)), - hir_id, - )) - } - (Type::Int, Type::EnumVariant(variant_type)) => { - let Type::Enum(enum_type) = self.db.ty(variant_type.enum_type).clone() else { - self.db.error( - ErrorKind::UnsupportedTypeGuard(self.type_name(from), self.type_name(to)), - text_range, - ); - return None; - }; - - if enum_type.has_fields { - self.db.error( - ErrorKind::UnsupportedTypeGuard(self.type_name(from), self.type_name(to)), - text_range, - ); - return None; - } - - let hir_id = self.db.alloc_hir(Hir::BinaryOp( - BinOp::Equals, - hir_id, - variant_type.discriminant, - )); - - Some(( - Guard::new(TypeOverride::new(to), TypeOverride::new(from)), - hir_id, - )) - } - (Type::Nullable(inner), Type::Nil) => { - let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, hir_id)); - - Some(( - Guard::new(TypeOverride::new(to), TypeOverride::new(inner)), - hir_id, - )) - } - (Type::Nullable(inner), _) if self.db.compare_type(to, inner).is_equal() => { - let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, hir_id)); - let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, hir_id)); - - Some(( - Guard::new(TypeOverride::new(to), TypeOverride::new(inner)), - hir_id, - )) - } - _ => { - self.db.error( - ErrorKind::UnsupportedTypeGuard(self.type_name(from), self.type_name(to)), - text_range, - ); - None - } - } - } + // fn guard_into( + // &mut self, + // from: TypeId, + // to: TypeId, + // hir_id: HirId, + // text_range: TextRange, + // ) -> Option<(Guard, HirId)> { + // if self.db.compare_type(from, to) <= Comparison::Assignable { + // self.db.warning( + // WarningKind::RedundantTypeCheck(self.type_name(from)), + // text_range, + // ); + // return Some(( + // Guard::new(TypeOverride::new(to), TypeOverride::new(self.ty.std().bool)), + // hir_id, + // )); + // } + + // match (self.db.ty(from).clone(), self.db.ty(to).clone()) { + // (Type::Any, Type::Pair(PairType { first, rest })) => { + // if !self.db.compare_type(first, self.ty.std().any).is_equal() { + // self.db.error(ErrorKind::NonAnyPairTypeGuard, text_range); + // } + + // if !self.db.compare_type(rest, self.ty.std().any).is_equal() { + // self.db.error(ErrorKind::NonAnyPairTypeGuard, text_range); + // } + + // let hir_id = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)); + // Some(( + // Guard::new( + // TypeOverride::new(to), + // TypeOverride::new(self.ty.std().bytes), + // ), + // hir_id, + // )) + // } + // (Type::Any, Type::Bytes) => { + // let pair_type = self.ty.alloc(Type::Pair(PairType { + // first: self.ty.std().any, + // rest: self.ty.std().any, + // })); + // let is_cons = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)); + // let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, is_cons)); + // Some(( + // Guard::new(TypeOverride::new(to), TypeOverride::new(pair_type)), + // hir_id, + // )) + // } + // (Type::List(inner), Type::Pair(PairType { first, rest })) => { + // if !self.db.compare_type(first, inner).is_equal() { + // self.db.error(ErrorKind::NonListPairTypeGuard, text_range); + // } + + // if !self.db.compare_type(rest, from).is_equal() { + // self.db.error(ErrorKind::NonListPairTypeGuard, text_range); + // } + + // let hir_id = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)); + // Some(( + // Guard::new(TypeOverride::new(to), TypeOverride::new(self.ty.std().nil)), + // hir_id, + // )) + // } + // (Type::List(inner), Type::Nil) => { + // let pair_type = self.ty.alloc(Type::Pair(PairType { + // first: inner, + // rest: from, + // })); + // let is_cons = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)); + // let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, is_cons)); + // Some(( + // Guard::new(TypeOverride::new(to), TypeOverride::new(pair_type)), + // hir_id, + // )) + // } + // (Type::Bytes, Type::Bytes32) => { + // let strlen = self.db.alloc_hir(Hir::Op(Op::Strlen, hir_id)); + // let length = self.db.alloc_hir(Hir::Atom(vec![32])); + // let hir_id = self + // .db + // .alloc_hir(Hir::BinaryOp(BinOp::Equals, strlen, length)); + // Some(( + // Guard::new(TypeOverride::new(to), TypeOverride::new(from)), + // hir_id, + // )) + // } + // (Type::Bytes, Type::PublicKey) => { + // let strlen = self.db.alloc_hir(Hir::Op(Op::Strlen, hir_id)); + // let length = self.db.alloc_hir(Hir::Atom(vec![48])); + // let hir_id = self + // .db + // .alloc_hir(Hir::BinaryOp(BinOp::Equals, strlen, length)); + // Some(( + // Guard::new(TypeOverride::new(to), TypeOverride::new(from)), + // hir_id, + // )) + // } + // (Type::Enum(enum_type), Type::EnumVariant(variant_type)) => { + // if variant_type.enum_type != from { + // self.db.error( + // ErrorKind::UnsupportedTypeGuard(self.type_name(from), self.type_name(to)), + // text_range, + // ); + // return None; + // } + + // let hir_id = if enum_type.has_fields { + // let first = self.db.alloc_hir(Hir::Op(Op::First, hir_id)); + // self.db.alloc_hir(Hir::BinaryOp( + // BinOp::Equals, + // first, + // variant_type.discriminant, + // )) + // } else { + // self.db.alloc_hir(Hir::BinaryOp( + // BinOp::Equals, + // hir_id, + // variant_type.discriminant, + // )) + // }; + + // Some(( + // Guard::new(TypeOverride::new(to), TypeOverride::new(from)), + // hir_id, + // )) + // } + // (Type::Int, Type::EnumVariant(variant_type)) => { + // let Type::Enum(enum_type) = self.db.ty(variant_type.enum_type).clone() else { + // self.db.error( + // ErrorKind::UnsupportedTypeGuard(self.type_name(from), self.type_name(to)), + // text_range, + // ); + // return None; + // }; + + // if enum_type.has_fields { + // self.db.error( + // ErrorKind::UnsupportedTypeGuard(self.type_name(from), self.type_name(to)), + // text_range, + // ); + // return None; + // } + + // let hir_id = self.db.alloc_hir(Hir::BinaryOp( + // BinOp::Equals, + // hir_id, + // variant_type.discriminant, + // )); + + // Some(( + // Guard::new(TypeOverride::new(to), TypeOverride::new(from)), + // hir_id, + // )) + // } + // (Type::Nullable(inner), Type::Nil) => { + // let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, hir_id)); + + // Some(( + // Guard::new(TypeOverride::new(to), TypeOverride::new(inner)), + // hir_id, + // )) + // } + // (Type::Nullable(inner), _) if self.db.compare_type(to, inner).is_equal() => { + // let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, hir_id)); + // let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, hir_id)); + + // Some(( + // Guard::new(TypeOverride::new(to), TypeOverride::new(inner)), + // hir_id, + // )) + // } + // _ => { + // self.db.error( + // ErrorKind::UnsupportedTypeGuard(self.type_name(from), self.type_name(to)), + // text_range, + // ); + // None + // } + // } + // } } diff --git a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs index 1c44e33..c621eff 100644 --- a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs @@ -3,13 +3,9 @@ use std::collections::HashMap; use indexmap::IndexMap; use rowan::TextRange; use rue_parser::{AstNode, InitializerExpr, InitializerField}; +use rue_typing::{bigint_to_bytes, deconstruct_items, Rest, Type, TypeId}; -use crate::{ - compiler::Compiler, - hir::Hir, - value::{Rest, Type, Value}, - ErrorKind, HirId, TypeId, -}; +use crate::{compiler::Compiler, hir::Hir, value::Value, ErrorKind, HirId}; impl Compiler<'_> { pub fn compile_initializer_expr(&mut self, initializer: &InitializerExpr) -> Value { @@ -17,10 +13,18 @@ impl Compiler<'_> { .path() .map(|path| self.compile_path_type(&path.idents(), path.syntax().text_range())); - match ty.map(|ty| self.db.ty(ty)).cloned() { + match ty.map(|ty| self.ty.get(ty)).cloned() { Some(Type::Struct(struct_type)) => { + let fields = deconstruct_items( + self.ty, + struct_type.type_id, + struct_type.field_names.len(), + struct_type.rest, + ) + .expect("invalid variant type"); + let hir_id = self.compile_initializer_fields( - &struct_type.fields, + &struct_type.field_names.into_iter().zip(fields).collect(), struct_type.rest, initializer.fields(), initializer.syntax().text_range(), @@ -31,18 +35,28 @@ impl Compiler<'_> { None => self.unknown(), } } - Some(Type::EnumVariant(enum_variant)) => { - if let Some(fields) = enum_variant.fields { + Some(Type::Variant(enum_variant)) => { + if let Some(field_names) = enum_variant.field_names { + let fields = deconstruct_items( + self.ty, + enum_variant.type_id, + field_names.len(), + enum_variant.rest, + ) + .expect("invalid variant type"); + let fields_hir_id = self.compile_initializer_fields( - &fields, + &field_names.into_iter().zip(fields).collect(), enum_variant.rest, initializer.fields(), initializer.syntax().text_range(), ); - let hir_id = self + let discriminant = self .db - .alloc_hir(Hir::Pair(enum_variant.discriminant, fields_hir_id)); + .alloc_hir(Hir::Atom(bigint_to_bytes(enum_variant.discriminant))); + + let hir_id = self.db.alloc_hir(Hir::Pair(discriminant, fields_hir_id)); match ty { Some(struct_type) => Value::new(hir_id, struct_type), @@ -75,7 +89,7 @@ impl Compiler<'_> { text_range: TextRange, ) -> HirId { let mut specified_fields = HashMap::new(); - let mut optional = false; + let optional = false; for field in initializer_fields { let Some(name) = field.name() else { @@ -84,7 +98,7 @@ impl Compiler<'_> { let expected_type = struct_fields.get(name.text()).copied(); - let mut value = field + let value = field .expr() .map(|expr| self.compile_expr(&expr, expected_type)) .unwrap_or(self.unknown()); @@ -93,8 +107,9 @@ impl Compiler<'_> { if rest == Rest::Optional && struct_fields.get_index_of(name.text()) == Some(struct_fields.len() - 1) { - optional |= matches!(self.db.ty(value.type_id), Type::Optional(..)); - value.type_id = self.db.non_undefined(value.type_id); + // TODO: optional |= matches!(self.db.ty(value.type_id), Type::Optional(..)); + // value.type_id = self.db.non_undefined(value.type_id); + todo!() } // Check the type of the field initializer. @@ -140,7 +155,7 @@ impl Compiler<'_> { ); } - let mut hir_id = self.builtins.nil_hir; + let mut hir_id = self.builtins.nil; // Construct a nil-terminated list from the arguments. for (i, field) in struct_fields.keys().rev().enumerate() { @@ -150,7 +165,7 @@ impl Compiler<'_> { continue; } - let field = value.unwrap_or(self.builtins.unknown_hir); + let field = value.unwrap_or(self.builtins.unknown); if i == 0 && (rest == Rest::Spread || (rest == Rest::Optional && optional)) { hir_id = field; diff --git a/crates/rue-compiler/src/compiler/expr/lambda_expr.rs b/crates/rue-compiler/src/compiler/expr/lambda_expr.rs index b70c329..7ce7208 100644 --- a/crates/rue-compiler/src/compiler/expr/lambda_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/lambda_expr.rs @@ -1,172 +1,165 @@ -use std::collections::HashMap; +use rue_parser::LambdaExpr; +use rue_typing::TypeId; -use rue_parser::{AstNode, LambdaExpr}; - -use crate::{ - compiler::Compiler, - hir::Hir, - scope::Scope, - symbol::{Function, Symbol}, - value::{FunctionType, Rest, Type, Value}, - ErrorKind, TypeId, -}; +use crate::{compiler::Compiler, value::Value}; impl Compiler<'_> { pub fn compile_lambda_expr( &mut self, - lambda_expr: &LambdaExpr, - expected_type: Option, + _lambda_expr: &LambdaExpr, + _expected_type: Option, ) -> Value { - // Precompute the substitutions based on generic types. - let mut substitutions = HashMap::new(); - - for generics in &self.generic_type_stack { - substitutions.extend(generics); - } - - // Determine the expected type of the lambda expression. - let expected = expected_type.and_then(|ty| match self.db.ty(ty) { - Type::Function(function) => Some(function.clone()), - _ => None, - }); - - // Add the scope so you can track generic types. - let scope_id = self.db.alloc_scope(Scope::default()); - self.scope_stack.push(scope_id); - - let mut generic_types = Vec::new(); - - // Add the generic types to the scope. - for generic_type in lambda_expr - .generic_types() - .map(|generics| generics.idents()) - .unwrap_or_default() - { - // Create the generic type id. - let type_id = self.ty.alloc(Type::Generic); - - // Check for duplicate generic types. - if self.scope().ty(generic_type.text()).is_some() { - self.db.error( - ErrorKind::DuplicateType(generic_type.text().to_string()), - generic_type.text_range(), - ); - } - - // Add the generic type to the scope and define the token for the generic type. - self.scope_mut() - .define_type(generic_type.to_string(), type_id); - - self.db.insert_type_token(type_id, generic_type); - - // Add the generic type to the list so it can be added to the function type. - generic_types.push(type_id); - } - - let mut param_types = Vec::new(); - let mut rest = Rest::Nil; - - let len = lambda_expr.params().len(); - - for (i, param) in lambda_expr.params().into_iter().enumerate() { - // Determine the expected type of the parameter. - let type_id = param - .ty() - .map(|ty| self.compile_type(ty)) - .or(expected - .as_ref() - .and_then(|expected| expected.param_types.get(i).copied())) - .unwrap_or_else(|| { - self.db - .error(ErrorKind::CannotInferType, param.syntax().text_range()); - self.ty.std().unknown - }); - - // Substitute generic types in the parameter type. - let type_id = self.db.substitute_type(type_id, &substitutions); - - param_types.push(type_id); - - if let Some(name) = param.name() { - let param_type_id = if param.optional().is_some() { - // If the parameter is optional, wrap the type in a possibly undefined type. - // This prevents referencing the parameter until it's checked for undefined. - self.ty.alloc(Type::Optional(type_id)) - } else { - type_id - }; - - let symbol_id = self.db.alloc_symbol(Symbol::Parameter(param_type_id)); - self.scope_mut().define_symbol(name.to_string(), symbol_id); - self.db.insert_symbol_token(symbol_id, name); - }; - - let last = i + 1 == len; - let spread = param.spread().is_some(); - let optional = param.optional().is_some(); - - if spread && optional { - self.db.error( - ErrorKind::OptionalParameterSpread, - param.syntax().text_range(), - ); - } else if spread && !last { - self.db.error( - ErrorKind::InvalidSpreadParameter, - param.syntax().text_range(), - ); - } else if optional && !last { - self.db.error( - ErrorKind::InvalidOptionalParameter, - param.syntax().text_range(), - ); - } else if spread { - rest = Rest::Spread; - } else if optional { - rest = Rest::Optional; - } - } - - let Some(body) = lambda_expr.body() else { - return self.unknown(); - }; - - let expected_return_type = lambda_expr - .ty() - .map(|ty| self.compile_type(ty)) - .or(expected.map(|expected| expected.return_type)); - - self.allow_generic_inference_stack.push(false); - let body = self.compile_expr(&body, expected_return_type); - self.allow_generic_inference_stack.pop().unwrap(); - - let return_type = expected_return_type.unwrap_or(body.type_id); - - self.scope_stack.pop().unwrap(); - - self.type_check( - body.type_id, - return_type, - lambda_expr.body().unwrap().syntax().text_range(), - ); - - let ty = FunctionType { - param_types: param_types.clone(), - rest, - return_type, - generic_types: Vec::new(), - }; - - let symbol_id = self.db.alloc_symbol(Symbol::Function(Function { - scope_id, - hir_id: body.hir_id, - ty: ty.clone(), - })); - - Value::new( - self.db - .alloc_hir(Hir::Reference(symbol_id, lambda_expr.syntax().text_range())), - self.ty.alloc(Type::Function(ty)), - ) + todo!() + // // Precompute the substitutions based on generic types. + // let mut substitutions = HashMap::new(); + + // for generics in &self.generic_type_stack { + // substitutions.extend(generics); + // } + + // // Determine the expected type of the lambda expression. + // let expected = expected_type.and_then(|ty| match self.db.ty(ty) { + // Type::Function(function) => Some(function.clone()), + // _ => None, + // }); + + // // Add the scope so you can track generic types. + // let scope_id = self.db.alloc_scope(Scope::default()); + // self.scope_stack.push(scope_id); + + // let mut generic_types = Vec::new(); + + // // Add the generic types to the scope. + // for generic_type in lambda_expr + // .generic_types() + // .map(|generics| generics.idents()) + // .unwrap_or_default() + // { + // // Create the generic type id. + // let type_id = self.ty.alloc(Type::Generic); + + // // Check for duplicate generic types. + // if self.scope().ty(generic_type.text()).is_some() { + // self.db.error( + // ErrorKind::DuplicateType(generic_type.text().to_string()), + // generic_type.text_range(), + // ); + // } + + // // Add the generic type to the scope and define the token for the generic type. + // self.scope_mut() + // .define_type(generic_type.to_string(), type_id); + + // self.db.insert_type_token(type_id, generic_type); + + // // Add the generic type to the list so it can be added to the function type. + // generic_types.push(type_id); + // } + + // let mut param_types = Vec::new(); + // let mut rest = Rest::Nil; + + // let len = lambda_expr.params().len(); + + // for (i, param) in lambda_expr.params().into_iter().enumerate() { + // // Determine the expected type of the parameter. + // let type_id = param + // .ty() + // .map(|ty| self.compile_type(ty)) + // .or(expected + // .as_ref() + // .and_then(|expected| expected.param_types.get(i).copied())) + // .unwrap_or_else(|| { + // self.db + // .error(ErrorKind::CannotInferType, param.syntax().text_range()); + // self.ty.std().unknown + // }); + + // // Substitute generic types in the parameter type. + // let type_id = self.db.substitute_type(type_id, &substitutions); + + // param_types.push(type_id); + + // if let Some(name) = param.name() { + // let param_type_id = if param.optional().is_some() { + // // If the parameter is optional, wrap the type in a possibly undefined type. + // // This prevents referencing the parameter until it's checked for undefined. + // self.ty.alloc(Type::Optional(type_id)) + // } else { + // type_id + // }; + + // let symbol_id = self.db.alloc_symbol(Symbol::Parameter(param_type_id)); + // self.scope_mut().define_symbol(name.to_string(), symbol_id); + // self.db.insert_symbol_token(symbol_id, name); + // }; + + // let last = i + 1 == len; + // let spread = param.spread().is_some(); + // let optional = param.optional().is_some(); + + // if spread && optional { + // self.db.error( + // ErrorKind::OptionalParameterSpread, + // param.syntax().text_range(), + // ); + // } else if spread && !last { + // self.db.error( + // ErrorKind::InvalidSpreadParameter, + // param.syntax().text_range(), + // ); + // } else if optional && !last { + // self.db.error( + // ErrorKind::InvalidOptionalParameter, + // param.syntax().text_range(), + // ); + // } else if spread { + // rest = Rest::Spread; + // } else if optional { + // rest = Rest::Optional; + // } + // } + + // let Some(body) = lambda_expr.body() else { + // return self.unknown(); + // }; + + // let expected_return_type = lambda_expr + // .ty() + // .map(|ty| self.compile_type(ty)) + // .or(expected.map(|expected| expected.return_type)); + + // self.allow_generic_inference_stack.push(false); + // let body = self.compile_expr(&body, expected_return_type); + // self.allow_generic_inference_stack.pop().unwrap(); + + // let return_type = expected_return_type.unwrap_or(body.type_id); + + // self.scope_stack.pop().unwrap(); + + // self.type_check( + // body.type_id, + // return_type, + // lambda_expr.body().unwrap().syntax().text_range(), + // ); + + // let ty = FunctionType { + // param_types: param_types.clone(), + // rest, + // return_type, + // generic_types: Vec::new(), + // }; + + // let symbol_id = self.db.alloc_symbol(Symbol::Function(Function { + // scope_id, + // hir_id: body.hir_id, + // ty: ty.clone(), + // })); + + // Value::new( + // self.db + // .alloc_hir(Hir::Reference(symbol_id, lambda_expr.syntax().text_range())), + // self.ty.alloc(Type::Function(ty)), + // ) } } diff --git a/crates/rue-compiler/src/compiler/expr/list_expr.rs b/crates/rue-compiler/src/compiler/expr/list_expr.rs index b34b94a..edb348e 100644 --- a/crates/rue-compiler/src/compiler/expr/list_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/list_expr.rs @@ -1,5 +1,5 @@ use rue_parser::{AstNode, ListExpr}; -use rue_typing::{Type, TypeId}; +use rue_typing::{unwrap_list, TypeId}; use crate::{compiler::Compiler, hir::Hir, value::Value, ErrorKind}; @@ -13,10 +13,7 @@ impl Compiler<'_> { let mut nil_terminated = true; let mut list_type = expected_expr_type; - let mut item_type = expected_expr_type.and_then(|ty| match self.db.ty(ty) { - Type::List(ty) => Some(*ty), - _ => None, - }); + let mut item_type = expected_expr_type.and_then(|ty| unwrap_list(self.ty, ty)); let len = list_expr.items().len(); @@ -43,13 +40,11 @@ impl Compiler<'_> { if i == 0 && item_type.is_none() { if item.spread().is_some() { list_type = Some(output.type_id); - item_type = match self.db.ty(output.type_id) { - Type::List(ty) => Some(*ty), - _ => None, - }; + item_type = unwrap_list(self.ty, output.type_id); } else { - list_type = Some(self.ty.alloc(Type::List(output.type_id))); - item_type = Some(output.type_id); + // TODO: list_type = Some(self.ty.alloc(Type::List(output.type_id))); + // item_type = Some(output.type_id); + todo!() } } @@ -75,10 +70,11 @@ impl Compiler<'_> { } } - Value::new( - hir_id, - self.db - .alloc_type(Type::List(item_type.unwrap_or(self.ty.std().unknown))), - ) + // TODO: Value::new( + // hir_id, + // self.db + // .alloc_type(Type::List(item_type.unwrap_or(self.ty.std().unknown))), + // ) + todo!() } } From 070730e7b2a31124562ef4083ce60e07bc1e18f2 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 16:09:55 -0400 Subject: [PATCH 065/100] More worky --- crates/rue-cli/src/main.rs | 9 +- .../src/compiler/expr/guard_expr.rs | 350 +++++++----------- crates/rue-compiler/src/error.rs | 12 +- crates/rue-compiler/src/lib.rs | 24 +- crates/rue-typing/src/comparison.rs | 131 ++++--- 5 files changed, 245 insertions(+), 281 deletions(-) diff --git a/crates/rue-cli/src/main.rs b/crates/rue-cli/src/main.rs index a64d8cc..ec53d73 100644 --- a/crates/rue-cli/src/main.rs +++ b/crates/rue-cli/src/main.rs @@ -5,7 +5,7 @@ use std::fs; use clap::Parser; use clvmr::{serde::node_to_bytes, Allocator, NodePtr}; use rue_clvm::{parse_clvm, run_clvm, stringify_clvm}; -use rue_compiler::{compile, Diagnostic, DiagnosticKind}; +use rue_compiler::{compile, compile_raw, Diagnostic, DiagnosticKind}; use rue_parser::{line_col, parse, LineCol}; /// CLI tools for working with the Rue compiler. @@ -49,7 +49,12 @@ fn build(file: String, should_compile: bool, run: &Option>) { } let mut allocator = Allocator::new(); - let output = compile(&mut allocator, &ast, should_compile && errors.is_empty()); + let output = compile_raw( + &mut allocator, + &ast, + should_compile && errors.is_empty(), + false, + ); if print_diagnostics(&source, &output.diagnostics) { return; diff --git a/crates/rue-compiler/src/compiler/expr/guard_expr.rs b/crates/rue-compiler/src/compiler/expr/guard_expr.rs index 54e8369..52dfece 100644 --- a/crates/rue-compiler/src/compiler/expr/guard_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/guard_expr.rs @@ -1,220 +1,148 @@ -use rue_parser::GuardExpr; -use rue_typing::TypeId; +use std::collections::HashMap; -use crate::{compiler::Compiler, value::Value}; +use rue_parser::{AstNode, GuardExpr}; +use rue_typing::{bigint_to_bytes, Check, Semantics, TypeId}; + +use crate::{ + compiler::Compiler, + hir::{BinOp, Hir, Op}, + value::{Guard, TypeOverride, Value}, + ErrorKind, HirId, +}; impl Compiler<'_> { pub fn compile_guard_expr( &mut self, - _guard: &GuardExpr, - _expected_type: Option, + guard: &GuardExpr, + expected_type: Option, ) -> Value { - todo!() - // let Some(expr) = guard - // .expr() - // .map(|expr| self.compile_expr(&expr, expected_type)) - // else { - // return self.unknown(); - // }; - - // let ty = guard - // .ty() - // .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); - - // let Some((guard, hir_id)) = - // self.guard_into(expr.type_id, ty, expr.hir_id, guard.syntax().text_range()) - // else { - // return Value::new(self.builtins.unknown_hir, ty); - // }; - - // let mut value = Value::new(hir_id, self.ty.std().bool); - - // if let Some(guard_path) = expr.guard_path { - // value.guards.insert(guard_path, guard); - // } - - // value + let Some(expr) = guard + .expr() + .map(|expr| self.compile_expr(&expr, expected_type)) + else { + return self.unknown(); + }; + + let rhs = guard + .ty() + .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); + + let lhs = self.ty.substitute( + expr.type_id, + HashMap::new(), + Semantics::StructuralOnly { + callable: self.ty.std().any, + }, + ); + + let rhs = self.ty.substitute( + rhs, + HashMap::new(), + Semantics::StructuralOnly { + callable: self.ty.std().never, + }, + ); + + let Ok(check) = self.ty.check(lhs, rhs) else { + self.db.error( + ErrorKind::RecursiveTypeCheck(self.type_name(expr.type_id), self.type_name(rhs)), + guard.syntax().text_range(), + ); + return self.unknown(); + }; + + let hir_id = self.check_hir(expr.hir_id, check); + + let mut value = Value::new(hir_id, self.ty.std().bool); + + if let Some(guard_path) = expr.guard_path { + let difference = self.ty.difference(expr.type_id, rhs); + + value.guards.insert( + guard_path, + Guard::new(TypeOverride::new(rhs), TypeOverride::new(difference)), + ); + } + + value } - // fn guard_into( - // &mut self, - // from: TypeId, - // to: TypeId, - // hir_id: HirId, - // text_range: TextRange, - // ) -> Option<(Guard, HirId)> { - // if self.db.compare_type(from, to) <= Comparison::Assignable { - // self.db.warning( - // WarningKind::RedundantTypeCheck(self.type_name(from)), - // text_range, - // ); - // return Some(( - // Guard::new(TypeOverride::new(to), TypeOverride::new(self.ty.std().bool)), - // hir_id, - // )); - // } - - // match (self.db.ty(from).clone(), self.db.ty(to).clone()) { - // (Type::Any, Type::Pair(PairType { first, rest })) => { - // if !self.db.compare_type(first, self.ty.std().any).is_equal() { - // self.db.error(ErrorKind::NonAnyPairTypeGuard, text_range); - // } - - // if !self.db.compare_type(rest, self.ty.std().any).is_equal() { - // self.db.error(ErrorKind::NonAnyPairTypeGuard, text_range); - // } - - // let hir_id = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)); - // Some(( - // Guard::new( - // TypeOverride::new(to), - // TypeOverride::new(self.ty.std().bytes), - // ), - // hir_id, - // )) - // } - // (Type::Any, Type::Bytes) => { - // let pair_type = self.ty.alloc(Type::Pair(PairType { - // first: self.ty.std().any, - // rest: self.ty.std().any, - // })); - // let is_cons = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)); - // let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, is_cons)); - // Some(( - // Guard::new(TypeOverride::new(to), TypeOverride::new(pair_type)), - // hir_id, - // )) - // } - // (Type::List(inner), Type::Pair(PairType { first, rest })) => { - // if !self.db.compare_type(first, inner).is_equal() { - // self.db.error(ErrorKind::NonListPairTypeGuard, text_range); - // } - - // if !self.db.compare_type(rest, from).is_equal() { - // self.db.error(ErrorKind::NonListPairTypeGuard, text_range); - // } - - // let hir_id = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)); - // Some(( - // Guard::new(TypeOverride::new(to), TypeOverride::new(self.ty.std().nil)), - // hir_id, - // )) - // } - // (Type::List(inner), Type::Nil) => { - // let pair_type = self.ty.alloc(Type::Pair(PairType { - // first: inner, - // rest: from, - // })); - // let is_cons = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)); - // let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, is_cons)); - // Some(( - // Guard::new(TypeOverride::new(to), TypeOverride::new(pair_type)), - // hir_id, - // )) - // } - // (Type::Bytes, Type::Bytes32) => { - // let strlen = self.db.alloc_hir(Hir::Op(Op::Strlen, hir_id)); - // let length = self.db.alloc_hir(Hir::Atom(vec![32])); - // let hir_id = self - // .db - // .alloc_hir(Hir::BinaryOp(BinOp::Equals, strlen, length)); - // Some(( - // Guard::new(TypeOverride::new(to), TypeOverride::new(from)), - // hir_id, - // )) - // } - // (Type::Bytes, Type::PublicKey) => { - // let strlen = self.db.alloc_hir(Hir::Op(Op::Strlen, hir_id)); - // let length = self.db.alloc_hir(Hir::Atom(vec![48])); - // let hir_id = self - // .db - // .alloc_hir(Hir::BinaryOp(BinOp::Equals, strlen, length)); - // Some(( - // Guard::new(TypeOverride::new(to), TypeOverride::new(from)), - // hir_id, - // )) - // } - // (Type::Enum(enum_type), Type::EnumVariant(variant_type)) => { - // if variant_type.enum_type != from { - // self.db.error( - // ErrorKind::UnsupportedTypeGuard(self.type_name(from), self.type_name(to)), - // text_range, - // ); - // return None; - // } - - // let hir_id = if enum_type.has_fields { - // let first = self.db.alloc_hir(Hir::Op(Op::First, hir_id)); - // self.db.alloc_hir(Hir::BinaryOp( - // BinOp::Equals, - // first, - // variant_type.discriminant, - // )) - // } else { - // self.db.alloc_hir(Hir::BinaryOp( - // BinOp::Equals, - // hir_id, - // variant_type.discriminant, - // )) - // }; - - // Some(( - // Guard::new(TypeOverride::new(to), TypeOverride::new(from)), - // hir_id, - // )) - // } - // (Type::Int, Type::EnumVariant(variant_type)) => { - // let Type::Enum(enum_type) = self.db.ty(variant_type.enum_type).clone() else { - // self.db.error( - // ErrorKind::UnsupportedTypeGuard(self.type_name(from), self.type_name(to)), - // text_range, - // ); - // return None; - // }; - - // if enum_type.has_fields { - // self.db.error( - // ErrorKind::UnsupportedTypeGuard(self.type_name(from), self.type_name(to)), - // text_range, - // ); - // return None; - // } - - // let hir_id = self.db.alloc_hir(Hir::BinaryOp( - // BinOp::Equals, - // hir_id, - // variant_type.discriminant, - // )); - - // Some(( - // Guard::new(TypeOverride::new(to), TypeOverride::new(from)), - // hir_id, - // )) - // } - // (Type::Nullable(inner), Type::Nil) => { - // let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, hir_id)); - - // Some(( - // Guard::new(TypeOverride::new(to), TypeOverride::new(inner)), - // hir_id, - // )) - // } - // (Type::Nullable(inner), _) if self.db.compare_type(to, inner).is_equal() => { - // let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, hir_id)); - // let hir_id = self.db.alloc_hir(Hir::Op(Op::Not, hir_id)); - - // Some(( - // Guard::new(TypeOverride::new(to), TypeOverride::new(inner)), - // hir_id, - // )) - // } - // _ => { - // self.db.error( - // ErrorKind::UnsupportedTypeGuard(self.type_name(from), self.type_name(to)), - // text_range, - // ); - // None - // } - // } - // } + fn check_hir(&mut self, hir_id: HirId, check: Check) -> HirId { + match check { + Check::True => self.db.alloc_hir(Hir::Atom(vec![1])), + Check::False => self.db.alloc_hir(Hir::Atom(Vec::new())), + Check::IsAtom => { + let listp = self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)); + self.db.alloc_hir(Hir::Op(Op::Not, listp)) + } + Check::IsPair => self.db.alloc_hir(Hir::Op(Op::Listp, hir_id)), + Check::Value(value) => { + let value = self.db.alloc_hir(Hir::Atom(bigint_to_bytes(value))); + self.db + .alloc_hir(Hir::BinaryOp(BinOp::Equals, hir_id, value)) + } + Check::Length(length) => { + let strlen = self.db.alloc_hir(Hir::Op(Op::Strlen, hir_id)); + let length = self.db.alloc_hir(Hir::Atom(bigint_to_bytes(length.into()))); + self.db + .alloc_hir(Hir::BinaryOp(BinOp::Equals, strlen, length)) + } + Check::If(cond, a, b) => { + let cond = self.check_hir(hir_id, *cond); + let a = self.check_hir(hir_id, *a); + let b = self.check_hir(hir_id, *b); + self.db.alloc_hir(Hir::If(cond, a, b)) + } + Check::And(mut items) => { + if items.is_empty() { + self.db.alloc_hir(Hir::Atom(vec![1])) + } else if items.len() == 1 { + self.check_hir(hir_id, items.remove(0)) + } else { + let a = self.check_hir(hir_id, items.pop().unwrap()); + let b = self.check_hir(hir_id, items.pop().unwrap()); + + let mut result = self.db.alloc_hir(Hir::BinaryOp(BinOp::LogicalAnd, b, a)); + + while let Some(item) = items.pop() { + let next = self.check_hir(hir_id, item); + result = self + .db + .alloc_hir(Hir::BinaryOp(BinOp::LogicalAnd, next, result)); + } + + result + } + } + Check::Or(mut items) => { + if items.is_empty() { + unreachable!() + } else if items.len() == 1 { + self.check_hir(hir_id, items.remove(1)) + } else { + let a = self.check_hir(hir_id, items.pop().unwrap()); + let b = self.check_hir(hir_id, items.pop().unwrap()); + + let mut result = self.db.alloc_hir(Hir::BinaryOp(BinOp::LogicalOr, b, a)); + + while let Some(item) = items.pop() { + let next = self.check_hir(hir_id, item); + result = self + .db + .alloc_hir(Hir::BinaryOp(BinOp::LogicalOr, next, result)); + } + + result + } + } + Check::First(check) => { + let first = self.db.alloc_hir(Hir::Op(Op::First, hir_id)); + self.check_hir(first, *check) + } + Check::Rest(check) => { + let rest = self.db.alloc_hir(Hir::Op(Op::Rest, hir_id)); + self.check_hir(rest, *check) + } + } + } } diff --git a/crates/rue-compiler/src/error.rs b/crates/rue-compiler/src/error.rs index 96fc2fa..0e2756e 100644 --- a/crates/rue-compiler/src/error.rs +++ b/crates/rue-compiler/src/error.rs @@ -145,10 +145,8 @@ pub enum ErrorKind { ExpectedSymbolPath(String), // Type guards. - UnsupportedTypeGuard(String, String), - NonAnyPairTypeGuard, - NonListPairTypeGuard, - InvalidExistanceCheck(String), + ImpossibleTypeCheck(String, String), + RecursiveTypeCheck(String, String), // Blocks. ImplicitReturnInIf, @@ -296,10 +294,8 @@ impl fmt::Display for ErrorKind { Self::ExpectedSymbolPath(name) => format!("Expected symbol, but found type `{name}` instead."), // Type guards. - Self::UnsupportedTypeGuard(from, to) => format!("Cannot check type `{from}` against `{to}`."), - Self::NonAnyPairTypeGuard => "Cannot check `Any` against pair types other than `(Any, Any)`.".to_string(), - Self::NonListPairTypeGuard => "Cannot check `T[]` against pair types other than `(T, T[])`.".to_string(), - Self::InvalidExistanceCheck(ty) => format!("Cannot check existence of value with type `{ty}`, since it can't be undefined."), + Self::ImpossibleTypeCheck(from, to) => format!("Cannot check type `{from}` against `{to}`."), + Self::RecursiveTypeCheck(from, to) => format!("Checking type `{from}` against `{to}` would result in infinite recursion at runtime."), // Blocks. Self::ImplicitReturnInIf => formatdoc!(" diff --git a/crates/rue-compiler/src/lib.rs b/crates/rue-compiler/src/lib.rs index 80a352b..4f09cd9 100644 --- a/crates/rue-compiler/src/lib.rs +++ b/crates/rue-compiler/src/lib.rs @@ -30,12 +30,26 @@ pub struct Output { pub node_ptr: NodePtr, } -pub fn compile(allocator: &mut Allocator, root: &Root, mut should_codegen: bool) -> Output { +pub fn compile(allocator: &mut Allocator, root: &Root, should_codegen: bool) -> Output { + compile_raw(allocator, root, should_codegen, true) +} + +pub fn compile_raw( + allocator: &mut Allocator, + root: &Root, + mut should_codegen: bool, + should_stdlib: bool, +) -> Output { let mut db = Database::new(); let mut ty = TypeSystem::new(); let mut ctx = setup_compiler(&mut db, &mut ty); - let stdlib = load_standard_library(&mut ctx); + let stdlib = if should_stdlib { + Some(load_standard_library(&mut ctx)) + } else { + None + }; + let main_module_id = load_module(&mut ctx, root); let symbol_table = compile_modules(ctx); @@ -45,7 +59,11 @@ pub fn compile(allocator: &mut Allocator, root: &Root, mut should_codegen: bool) &ty, &symbol_table, main_module_id, - &[main_module_id, stdlib], + &if let Some(stdlib) = stdlib { + [main_module_id, stdlib].to_vec() + } else { + [main_module_id].to_vec() + }, ); should_codegen &= !db.diagnostics().iter().any(Diagnostic::is_error); diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index d25dc12..a6a3486 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -41,6 +41,62 @@ pub(crate) fn compare_type( let comparison = match (db.get(lhs), db.get(rhs)) { (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), + (Type::Lazy(lazy), _) => { + ctx.substitution_stack.push(lazy.substitutions.clone()); + let result = compare_type(db, lazy.type_id, rhs, ctx); + ctx.substitution_stack.pop().unwrap(); + result + } + + (_, Type::Lazy(lazy)) => { + ctx.substitution_stack.push(lazy.substitutions.clone()); + let result = compare_type(db, lhs, lazy.type_id, ctx); + ctx.substitution_stack.pop().unwrap(); + result + } + + (Type::Alias(alias), _) => compare_type(db, alias.type_id, rhs, ctx), + (_, Type::Alias(alias)) => compare_type(db, lhs, alias.type_id, ctx), + + (Type::Struct(lhs), _) => max( + compare_type(db, lhs.type_id, rhs, ctx), + Comparison::Castable, + ), + (_, Type::Struct(rhs)) => max( + compare_type(db, lhs, rhs.type_id, ctx), + Comparison::Castable, + ), + + (Type::Variant(variant), Type::Enum(ty)) => { + let comparison = compare_type(db, variant.type_id, ty.type_id, ctx); + + if variant.enum_type == rhs { + max(comparison, Comparison::Assignable) + } else { + max(comparison, Comparison::Castable) + } + } + + (Type::Enum(ty), _) => max(compare_type(db, ty.type_id, rhs, ctx), Comparison::Castable), + (_, Type::Enum(ty)) => max(compare_type(db, lhs, ty.type_id, ctx), Comparison::Castable), + + (Type::Variant(lhs), _) => max( + compare_type(db, lhs.type_id, rhs, ctx), + Comparison::Castable, + ), + (_, Type::Variant(rhs)) => max( + compare_type(db, lhs, rhs.type_id, ctx), + Comparison::Castable, + ), + + (Type::Callable(lhs), Type::Callable(rhs)) => max( + compare_type(db, lhs.parameters, rhs.parameters, ctx), + compare_type(db, lhs.return_type, rhs.return_type, ctx), + ), + + (Type::Callable(..), _) => compare_type(db, lhs, db.std().any, ctx), + (_, Type::Callable(..)) => Comparison::Incompatible, + (_, Type::Generic) => { let mut found = None; @@ -283,62 +339,6 @@ pub(crate) fn compare_type( Comparison::Incompatible } } - - (Type::Lazy(lazy), _) => { - ctx.substitution_stack.push(lazy.substitutions.clone()); - let result = compare_type(db, lazy.type_id, rhs, ctx); - ctx.substitution_stack.pop().unwrap(); - result - } - - (_, Type::Lazy(lazy)) => { - ctx.substitution_stack.push(lazy.substitutions.clone()); - let result = compare_type(db, lhs, lazy.type_id, ctx); - ctx.substitution_stack.pop().unwrap(); - result - } - - (Type::Alias(alias), _) => compare_type(db, alias.type_id, rhs, ctx), - (_, Type::Alias(alias)) => compare_type(db, lhs, alias.type_id, ctx), - - (Type::Struct(lhs), _) => max( - compare_type(db, lhs.type_id, rhs, ctx), - Comparison::Castable, - ), - (_, Type::Struct(rhs)) => max( - compare_type(db, lhs, rhs.type_id, ctx), - Comparison::Castable, - ), - - (Type::Variant(variant), Type::Enum(ty)) => { - let comparison = compare_type(db, variant.type_id, ty.type_id, ctx); - - if variant.enum_type == rhs { - max(comparison, Comparison::Assignable) - } else { - max(comparison, Comparison::Castable) - } - } - - (Type::Enum(ty), _) => max(compare_type(db, ty.type_id, rhs, ctx), Comparison::Castable), - (_, Type::Enum(ty)) => max(compare_type(db, lhs, ty.type_id, ctx), Comparison::Castable), - - (Type::Variant(lhs), _) => max( - compare_type(db, lhs.type_id, rhs, ctx), - Comparison::Castable, - ), - (_, Type::Variant(rhs)) => max( - compare_type(db, lhs, rhs.type_id, ctx), - Comparison::Castable, - ), - - (Type::Callable(lhs), Type::Callable(rhs)) => max( - compare_type(db, lhs.parameters, rhs.parameters, ctx), - compare_type(db, lhs.return_type, rhs.return_type, ctx), - ), - - (Type::Callable(..), _) => compare_type(db, lhs, db.std().any, ctx), - (_, Type::Callable(..)) => Comparison::Incompatible, }; ctx.visited.remove(&(lhs, rhs)); @@ -348,7 +348,9 @@ pub(crate) fn compare_type( #[cfg(test)] mod tests { - use crate::alloc_list; + use indexmap::indexmap; + + use crate::{alloc_list, alloc_struct, Rest}; use super::*; @@ -502,6 +504,21 @@ mod tests { assert_eq!(db.compare(list, types.any), Comparison::Assignable); } + #[test] + fn test_point_struct_any() { + let mut db = TypeSystem::new(); + let types = db.std(); + let point = alloc_struct( + &mut db, + &indexmap! { + "x".to_string() => types.int, + "y".to_string() => types.int, + }, + Rest::Nil, + ); + assert_eq!(db.compare(point, types.any), Comparison::Castable); + } + #[test] fn test_compare_any_any() { let db = TypeSystem::new(); From 34fe9d04bfa61b5d83b7a807b3dd05872730eaaf Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 19:03:40 -0400 Subject: [PATCH 066/100] Add another test --- crates/rue-typing/src/check/check_type.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs index 2a26ce0..34041dc 100644 --- a/crates/rue-typing/src/check/check_type.rs +++ b/crates/rue-typing/src/check/check_type.rs @@ -689,4 +689,25 @@ mod tests { check_str(&mut db, lhs, rhs, "(= (f val) 50)"); check_str(&mut db, types.any, rhs, "(and (l val) (not (l (f val))) (= (f val) 50) (l (r val)) (not (l (f (r val)))) (= (strlen (f (r val))) 48) (l (r (r val))) (not (l (f (r (r val))))) (not (l (r (r (r val))))) (= (r (r (r val))) 0))"); } + + #[test] + fn test_check_three_int_int_list() { + let mut db = TypeSystem::new(); + let types = db.std(); + let inner = db.alloc(Type::Pair(types.int, types.int)); + let pair = db.alloc(Type::Pair(types.int, inner)); + let list = alloc_list(&mut db, types.int); + let union = db.alloc(Type::Union(vec![pair, list])); + + check_str(&mut db, union, union, "1"); + check_str(&mut db, union, types.int, "(not (l val))"); + check_str(&mut db, union, types.nil, "(not (l val))"); + check_recursive(&mut db, union, list); + check_str( + &mut db, + union, + pair, + "(and (l val) (l (r val)) (not (l (r (r val)))))", + ); + } } From 4beecbac9986182dad8ef6e0a916c1816e715d04 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 23 Jul 2024 21:20:31 -0400 Subject: [PATCH 067/100] Temp --- .../src/compiler/expr/function_call_expr.rs | 4 +- .../src/compiler/expr/guard_expr.rs | 19 ++- .../src/compiler/item/function_item.rs | 19 +-- crates/rue-compiler/src/compiler/ty.rs | 2 + .../src/compiler/ty/union_type.rs | 14 ++ crates/rue-compiler/src/error.rs | 158 +++++++++--------- crates/rue-compiler/stdlib.rue | 12 +- crates/rue-lsp/src/main.rs | 4 +- crates/rue-parser/src/ast.rs | 23 ++- crates/rue-parser/src/grammar.rs | 30 +++- crates/rue-parser/src/syntax_kind.rs | 8 +- 11 files changed, 177 insertions(+), 116 deletions(-) create mode 100644 crates/rue-compiler/src/compiler/ty/union_type.rs diff --git a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs index b39f06f..843c45b 100644 --- a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs @@ -54,7 +54,7 @@ impl Compiler<'_> { if let Some(function_type) = &function_type { self.check_argument_length( function_type, - parameter_types.clone().unwrap(), + parameter_types.as_ref().unwrap(), len, call.syntax().text_range(), ); @@ -155,7 +155,7 @@ impl Compiler<'_> { fn check_argument_length( &mut self, function: &Callable, - parameter_types: Vec, + parameter_types: &[TypeId], length: usize, text_range: TextRange, ) { diff --git a/crates/rue-compiler/src/compiler/expr/guard_expr.rs b/crates/rue-compiler/src/compiler/expr/guard_expr.rs index 52dfece..fcadd07 100644 --- a/crates/rue-compiler/src/compiler/expr/guard_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/guard_expr.rs @@ -7,7 +7,7 @@ use crate::{ compiler::Compiler, hir::{BinOp, Hir, Op}, value::{Guard, TypeOverride, Value}, - ErrorKind, HirId, + ErrorKind, HirId, WarningKind, }; impl Compiler<'_> { @@ -51,6 +51,23 @@ impl Compiler<'_> { return self.unknown(); }; + match check { + Check::True => { + self.db.warning( + WarningKind::UnnecessaryTypeCheck(self.type_name(lhs), self.type_name(rhs)), + guard.syntax().text_range(), + ); + } + Check::False => { + self.db.error( + ErrorKind::ImpossibleTypeCheck(self.type_name(lhs), self.type_name(rhs)), + guard.syntax().text_range(), + ); + return self.unknown(); + } + _ => {} + } + let hir_id = self.check_hir(expr.hir_id, check); let mut value = Value::new(hir_id, self.ty.std().bool); diff --git a/crates/rue-compiler/src/compiler/item/function_item.rs b/crates/rue-compiler/src/compiler/item/function_item.rs index aa03ab8..21e0f99 100644 --- a/crates/rue-compiler/src/compiler/item/function_item.rs +++ b/crates/rue-compiler/src/compiler/item/function_item.rs @@ -22,27 +22,26 @@ impl Compiler<'_> { let mut generic_types = Vec::new(); // Add the generic types to the scope. - for generic_type in function_item - .generic_types() - .map(|generics| generics.idents()) + for name in function_item + .generic_params() + .map(|generics| generics.names()) .unwrap_or_default() { // Create the generic type id. let type_id = self.ty.alloc(Type::Generic); // Check for duplicate generic types. - if self.scope().ty(generic_type.text()).is_some() { + if self.scope().ty(name.text()).is_some() { self.db.error( - ErrorKind::DuplicateType(generic_type.text().to_string()), - generic_type.text_range(), + ErrorKind::DuplicateType(name.text().to_string()), + name.text_range(), ); } // Add the generic type to the scope and define the token for the generic type. - self.scope_mut() - .define_type(generic_type.to_string(), type_id); + self.scope_mut().define_type(name.to_string(), type_id); - self.db.insert_type_token(type_id, generic_type); + self.db.insert_type_token(type_id, name); // Add the generic type to the list so it can be added to the function type. generic_types.push(type_id); @@ -94,7 +93,7 @@ impl Compiler<'_> { self.scope_mut().define_symbol(name.to_string(), symbol_id); self.db.insert_symbol_token(symbol_id, name); } else { - param_names.push(format!("#{}", i)); + param_names.push(format!("#{i}")); } // Check if it's a spread or optional parameter. diff --git a/crates/rue-compiler/src/compiler/ty.rs b/crates/rue-compiler/src/compiler/ty.rs index c056f3c..0d0e121 100644 --- a/crates/rue-compiler/src/compiler/ty.rs +++ b/crates/rue-compiler/src/compiler/ty.rs @@ -6,6 +6,7 @@ use super::Compiler; mod function_type; mod pair_type; mod path_type; +mod union_type; impl Compiler<'_> { pub fn compile_type(&mut self, ty: Type) -> TypeId { @@ -15,6 +16,7 @@ impl Compiler<'_> { } Type::FunctionType(function) => self.compile_function_type(&function), Type::PairType(tuple) => self.compile_pair_type(&tuple), + Type::UnionType(union) => self.compile_union_type(&union), } } } diff --git a/crates/rue-compiler/src/compiler/ty/union_type.rs b/crates/rue-compiler/src/compiler/ty/union_type.rs new file mode 100644 index 0000000..57bd268 --- /dev/null +++ b/crates/rue-compiler/src/compiler/ty/union_type.rs @@ -0,0 +1,14 @@ +use rue_parser::UnionType; +use rue_typing::{Type, TypeId}; + +use crate::compiler::Compiler; + +impl Compiler<'_> { + pub fn compile_union_type(&mut self, union: &UnionType) -> TypeId { + let mut types = Vec::new(); + for ty in union.types() { + types.push(self.compile_type(ty)); + } + self.ty.alloc(Type::Union(types)) + } +} diff --git a/crates/rue-compiler/src/error.rs b/crates/rue-compiler/src/error.rs index 0e2756e..a9ab741 100644 --- a/crates/rue-compiler/src/error.rs +++ b/crates/rue-compiler/src/error.rs @@ -49,33 +49,7 @@ pub enum WarningKind { UnusedEnumVariant(String), UnusedStruct(String), UnusedTypeAlias(String), - RedundantNullableType(String), - RedundantTypeCheck(String), -} - -impl fmt::Display for WarningKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let message = match self { - Self::UnusedFunction(name) => format!("Unused function `{name}`."), - Self::UnusedInlineFunction(name) => format!("Unused inline function `{name}`."), - Self::UnusedParameter(name) => format!("Unused parameter `{name}`."), - Self::UnusedConst(name) => format!("Unused constant `{name}`."), - Self::UnusedInlineConst(name) => format!("Unused inline constant `{name}`."), - Self::UnusedLet(name) => format!("Unused let binding `{name}`."), - Self::UnusedGenericType(name) => format!("Unused generic type `{name}`."), - Self::UnusedEnum(name) => format!("Unused enum `{name}`."), - Self::UnusedEnumVariant(name) => format!("Unused enum variant `{name}`."), - Self::UnusedStruct(name) => format!("Unused struct `{name}`."), - Self::UnusedTypeAlias(name) => format!("Unused type alias `{name}`."), - Self::RedundantNullableType(ty) => { - format!("This has no effect, since `{ty}` is already a nullable type.") - } - Self::RedundantTypeCheck(ty) => format!( - "It's redundant to guard against `{ty}`, since the value already has that type." - ), - }; - write!(f, "{}", message.trim()) - } + UnnecessaryTypeCheck(String, String), } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -163,28 +137,50 @@ pub enum ErrorKind { RecursiveInlineFunctionCall, } +impl fmt::Display for WarningKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let message = match self { + Self::UnusedFunction(name) => format!("Unused function `{name}`"), + Self::UnusedInlineFunction(name) => format!("Unused inline function `{name}`"), + Self::UnusedParameter(name) => format!("Unused parameter `{name}`"), + Self::UnusedConst(name) => format!("Unused constant `{name}`"), + Self::UnusedInlineConst(name) => format!("Unused inline constant `{name}`"), + Self::UnusedLet(name) => format!("Unused let binding `{name}`"), + Self::UnusedGenericType(name) => format!("Unused generic type `{name}`"), + Self::UnusedEnum(name) => format!("Unused enum `{name}`"), + Self::UnusedEnumVariant(name) => format!("Unused enum variant `{name}`"), + Self::UnusedStruct(name) => format!("Unused struct `{name}`"), + Self::UnusedTypeAlias(name) => format!("Unused type alias `{name}`"), + Self::UnnecessaryTypeCheck(from, to) => { + format!("Checking `{from}` against `{to}` has no effect") + } + }; + write!(f, "{}", message.trim()) + } +} + impl fmt::Display for ErrorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let message = match self { // Duplicate definitions. - Self::DuplicateType(name) => format!("There is already a type named `{name}` in this scope."), - Self::DuplicateSymbol(name) => format!("There is already a symbol named `{name}` in this scope."), + Self::DuplicateType(name) => format!("There is already a type named `{name}` in this scope"), + Self::DuplicateSymbol(name) => format!("There is already a symbol named `{name}` in this scope"), Self::ModuleNameTakenByEnum(name) => formatdoc!(" There is already an enum type named `{name}` in this scope. \ - This isn't allowed to prevent ambiguity when referencing items. + This isn't allowed to prevent ambiguity when referencing items "), Self::EnumNameTakenByModule(name) => formatdoc!(" There is already a module named `{name}` in this scope. \ - This isn't allowed to prevent ambiguity when referencing items. + This isn't allowed to prevent ambiguity when referencing items "), // Invalid symbol references. - Self::UnknownSymbol(name) => format!("Reference to unknown symbol `{name}`."), - Self::UnknownType(name) => format!("Reference to unknown type `{name}`."), + Self::UnknownSymbol(name) => format!("Reference to unknown symbol `{name}`"), + Self::UnknownType(name) => format!("Reference to unknown type `{name}`"), Self::InlineFunctionReference(name) => formatdoc!(" Cannot reference inline function `{name}`, since it is not a value. \ Inline functions must be resolved at compile time. \ - Try calling the function instead. + Try calling the function instead "), Self::ModuleReference(name) => formatdoc!(" Cannot reference module `{name}`, since it is not a value. \ @@ -196,124 +192,124 @@ impl fmt::Display for ErrorKind { Cycle detected when resolving type alias `{name}`. \ Type aliases cannot reference themselves. "), - Self::TypeMismatch(found, expected) => format!("Expected type `{expected}`, but found `{found}`."), - Self::CastMismatch(found, expected) => format!("Cannot cast type `{found}` to `{expected}`."), - Self::CannotInferType => "Lambda parameter type could not be inferred.".to_string(), + Self::TypeMismatch(found, expected) => format!("Expected type `{expected}`, but found `{found}`"), + Self::CastMismatch(found, expected) => format!("Cannot cast type `{found}` to `{expected}`"), + Self::CannotInferType => "Lambda parameter type could not be inferred".to_string(), // Function calls. - Self::UncallableType(ty) => format!("Expression with type `{ty}` cannot be called, since it is not a function."), + Self::UncallableType(ty) => format!("Expression with type `{ty}` cannot be called, since it is not a function"), Self::ArgumentMismatch(found, expected) => { format!( - "Expected {expected} argument{}, but found {found}.", + "Expected {expected} argument{}, but found {found}", if *expected == 1 { "" } else { "s" } ) } Self::ArgumentMismatchSpread (found, expected) => { format!( - "Expected at least {expected} argument{}, but found {found}.", + "Expected at least {expected} argument{}, but found {found}", if *expected == 1 { "" } else { "s" } ) } Self::ArgumentMismatchOptional (found, expected)=> { - format!("Expected either {} or {expected} arguments, but found {found}.", expected - 1) + format!("Expected either {} or {expected} arguments, but found {found}", expected - 1) } // Field initialization. Self::UninitializableType(ty) => formatdoc!(" Cannot initializable type `{ty}`. \ - Only structs and enum variants with fields can be initialized. + Only structs and enum variants with fields can be initialized "), Self::InvalidEnumVariantReference(name) => formatdoc!(" Cannot reference enum variant `{name}`. \ Enum variants with fields cannot be referenced directly. \ - Consider initializing the enum variant instead. + Consider initializing the enum variant instead "), Self::InvalidEnumVariantInitializer(name) => formatdoc!(" Cannot initialize enum variant `{name}`. \ Enum variants without fields cannot be initialized. \ - Consider referencing the enum variant directly. + Consider referencing the enum variant directly "), - Self::DuplicateInitializerField(name) => format!("Duplicate field `{name}` specified in initializer."), - Self::UnknownInitializerField(name) => format!("Unknown field `{name}` specified in initializer."), - Self::MissingInitializerFields(fields) => format!("Missing fields in initializer: {}.", join_names(fields)), + Self::DuplicateInitializerField(name) => format!("Duplicate field `{name}` specified in initializer"), + Self::UnknownInitializerField(name) => format!("Unknown field `{name}` specified in initializer"), + Self::MissingInitializerFields(fields) => format!("Missing fields in initializer: {}", join_names(fields)), // Field access. - Self::UnknownField(name) => format!("Cannot reference unknown field `{name}`."), - Self::InvalidFieldAccess(field, ty) => format!("Cannot reference field `{field}` of type `{ty}`."), - Self::InvalidIndexAccess(ty) => format!("Cannot index into type `{ty}`."), + Self::UnknownField(name) => format!("Cannot reference unknown field `{name}`"), + Self::InvalidFieldAccess(field, ty) => format!("Cannot reference field `{field}` of type `{ty}`"), + Self::InvalidIndexAccess(ty) => format!("Cannot index into type `{ty}`"), // Spread and optional. Self::InvalidSpreadItem => formatdoc!(" The spread operator can only be used on the last item in a list. \ This is because it requires recursion at runtime to concatenate lists together. \ - By only allowing it on the last item by default, this additional complexity and runtime cost is avoided. + By only allowing it on the last item by default, this additional complexity and runtime cost is avoided "), Self::InvalidSpreadArgument => formatdoc!(" The spread operator can only be used on the last argument in a function call. \ This is because it requires recursion at runtime to concatenate lists together. \ - By only allowing it on the last item by default, this additional complexity and runtime cost is avoided. + By only allowing it on the last item by default, this additional complexity and runtime cost is avoided "), Self::InvalidSpreadParameter => formatdoc!(" The spread operator can only be used on the last parameter in a function. \ - Otherwise, it would be ambiguous where the parameter should start and end. + Otherwise, it would be ambiguous where the parameter should start and end "), Self::InvalidSpreadField => formatdoc!(" The spread operator can only be used on the last field. \ - Otherwise, it would be ambiguous where the field should start and end. + Otherwise, it would be ambiguous where the field should start and end "), Self::InvalidOptionalParameter => formatdoc!(" Only the last parameter in a function can be optional. \ - Otherwise, it would be ambiguous which optional parameter was specified. + Otherwise, it would be ambiguous which optional parameter was specified "), Self::InvalidOptionalField => formatdoc!(" Only the last field can be optional. \ - Otherwise, it would be ambiguous which optional field was specified. + Otherwise, it would be ambiguous which optional field was specified "), - Self::OptionalParameterSpread => "The spread operator cannot be used on optional parameters.".to_string(), - Self::OptionalFieldSpread => "The spread operator cannot be used on optional fields.".to_string(), - Self::UnsupportedFunctionSpread => "This function does not support the spread operator on its last argument.".to_string(), - Self::RequiredFunctionSpread => "This function requires the spread operator on its last argument.".to_string(), + Self::OptionalParameterSpread => "The spread operator cannot be used on optional parameters".to_string(), + Self::OptionalFieldSpread => "The spread operator cannot be used on optional fields".to_string(), + Self::UnsupportedFunctionSpread => "This function does not support the spread operator on its last argument".to_string(), + Self::RequiredFunctionSpread => "This function requires the spread operator on its last argument".to_string(), // Enum variant definitions. - Self::DuplicateEnumVariant(name) => format!("Duplicate enum variant `{name}` specified."), - Self::DuplicateEnumDiscriminant(discriminant) => format!("Duplicate enum discriminant `{discriminant}` specified."), - Self::EnumDiscriminantTooLarge => "Enum discriminant is too large to allocate in CLVM.".to_string(), + Self::DuplicateEnumVariant(name) => format!("Duplicate enum variant `{name}` specified"), + Self::DuplicateEnumDiscriminant(discriminant) => format!("Duplicate enum discriminant `{discriminant}` specified"), + Self::EnumDiscriminantTooLarge => "Enum discriminant is too large to allocate in CLVM".to_string(), // Paths. - Self::UnknownEnumVariantPath(name) => format!("Unknown enum variant `{name}`."), - Self::UnknownModulePath(name) => format!("Could not resolve `{name}` in module."), - Self::PrivateSymbol(name) => format!("Cannot access private symbol `{name}` in module."), - Self::PrivateType(name) => format!("Cannot access private type `{name}` in module."), - Self::InvalidTypePath(ty) => format!("Cannot path into type `{ty}`."), + Self::UnknownEnumVariantPath(name) => format!("Unknown enum variant `{name}`"), + Self::UnknownModulePath(name) => format!("Could not resolve `{name}` in module"), + Self::PrivateSymbol(name) => format!("Cannot access private symbol `{name}` in module"), + Self::PrivateType(name) => format!("Cannot access private type `{name}` in module"), + Self::InvalidTypePath(ty) => format!("Cannot path into type `{ty}`"), Self::InvalidSymbolPath(name) => if let Some(name) = name { - format!("Cannot path into symbol `{name}`.") + format!("Cannot path into symbol `{name}`") } else { "Cannot path into symbol.".to_string() }, - Self::ExpectedTypePath(name) => format!("Expected type, but found symbol `{name}` instead."), - Self::ExpectedSymbolPath(name) => format!("Expected symbol, but found type `{name}` instead."), + Self::ExpectedTypePath(name) => format!("Expected type, but found symbol `{name}` instead"), + Self::ExpectedSymbolPath(name) => format!("Expected symbol, but found type `{name}` instead"), // Type guards. - Self::ImpossibleTypeCheck(from, to) => format!("Cannot check type `{from}` against `{to}`."), - Self::RecursiveTypeCheck(from, to) => format!("Checking type `{from}` against `{to}` would result in infinite recursion at runtime."), + Self::ImpossibleTypeCheck(from, to) => format!("Cannot check type `{from}` against `{to}`"), + Self::RecursiveTypeCheck(from, to) => format!("Checking type `{from}` against `{to}` would result in infinite recursion at runtime"), // Blocks. Self::ImplicitReturnInIf => formatdoc!(" Implicit returns are not allowed in if statements. \ Either use an explicit return statement at the end of the block, \ - or raise an error. + or raise an error "), - Self::ExplicitReturnInExpr => "Explicit return is not allowed within expressions.".to_string(), - Self::EmptyBlock => "Blocks must either return an expression or raise an error.".to_string(), + Self::ExplicitReturnInExpr => "Explicit return is not allowed within expressions".to_string(), + Self::EmptyBlock => "Blocks must either return an expression or raise an error".to_string(), // Atoms. - Self::NonAtomEquality(ty) => format!("Cannot check equality on non-atom type `{ty}`."), - Self::IntegerTooLarge => "Integer literal is too large to allocate in CLVM.".to_string(), + Self::NonAtomEquality(ty) => format!("Cannot check equality on non-atom type `{ty}`"), + Self::IntegerTooLarge => "Integer literal is too large to allocate in CLVM".to_string(), // Recursive constants. - Self::RecursiveConstantReference => "Cannot recursively reference constant.".to_string(), - Self::RecursiveInlineConstantReference => "Cannot recursively reference inline constant.".to_string(), - Self::RecursiveInlineFunctionCall => "Cannot recursively call inline function.".to_string(), + Self::RecursiveConstantReference => "Cannot recursively reference constant".to_string(), + Self::RecursiveInlineConstantReference => "Cannot recursively reference inline constant".to_string(), + Self::RecursiveInlineFunctionCall => "Cannot recursively call inline function".to_string(), }; write!(f, "{}", message.trim()) } diff --git a/crates/rue-compiler/stdlib.rue b/crates/rue-compiler/stdlib.rue index 27b8540..29c186a 100644 --- a/crates/rue-compiler/stdlib.rue +++ b/crates/rue-compiler/stdlib.rue @@ -37,7 +37,7 @@ export enum Condition { CreateCoin = 51 { puzzle_hash: Bytes32, amount: Int, - memos?: Bytes[], + memos?: List, }, ReserveFee = 52 { amount: Int, @@ -167,7 +167,7 @@ inline fun update_hash_with_parameter( sha256(CONS_PREIMAGE_PREFIX + two_item_list_hash(quote_hash(parameter_hash), environment_hash)) } -fun curried_params_hash(parameters: Bytes32[]) -> Bytes32 { +fun curried_params_hash(parameters: List) -> Bytes32 { if parameters is Nil { return ONE_TREE_HASH; } @@ -176,7 +176,7 @@ fun curried_params_hash(parameters: Bytes32[]) -> Bytes32 { export inline fun curry_tree_hash( mod_hash: Bytes32, - ...parameters: Bytes32[] + ...parameters: List ) -> Bytes32 { apply_hash(mod_hash, curried_params_hash(parameters)) } @@ -191,14 +191,14 @@ export fun calculate_coin_id( sha256(parent_coin_id + puzzle_hash + amount as Bytes) } -export fun map(list: T[], fn: fun(item: T) -> U) -> U[] { +export fun map(list: List, fn: fun(item: T) -> U) -> List { if list is Nil { return nil; } [fn(list.first), ...map(list.rest, fn)] } -export fun filter(list: T[], fn: fun(item: T) -> Bool) -> T[] { +export fun filter(list: List, fn: fun(item: T) -> Bool) -> List { if list is Nil { return nil; } @@ -208,7 +208,7 @@ export fun filter(list: T[], fn: fun(item: T) -> Bool) -> T[] { filter(list.rest, fn) } -export fun fold(list: T[], initial: U, fn: fun(acc: U, item: T) -> U) -> U { +export fun fold(list: List, initial: U, fn: fun(acc: U, item: T) -> U) -> U { if list is Nil { return initial; } diff --git a/crates/rue-lsp/src/main.rs b/crates/rue-lsp/src/main.rs index 9390cb8..290c297 100644 --- a/crates/rue-lsp/src/main.rs +++ b/crates/rue-lsp/src/main.rs @@ -1,5 +1,5 @@ use clvmr::Allocator; -use rue_compiler::{compile, DiagnosticKind}; +use rue_compiler::{compile, compile_raw, DiagnosticKind}; use rue_parser::{line_col, parse, LineCol}; use tower_lsp::jsonrpc::Result; use tower_lsp::lsp_types::{ @@ -60,7 +60,7 @@ impl LanguageServer for Backend { /// This is a hack to get around a Rust compiler error. #[allow(clippy::needless_pass_by_value)] fn analyze_owned(root: rue_parser::Root) -> Vec { - compile(&mut Allocator::new(), &root, false).diagnostics + compile_raw(&mut Allocator::new(), &root, false, false).diagnostics } impl Backend { diff --git a/crates/rue-parser/src/ast.rs b/crates/rue-parser/src/ast.rs index 615420c..b2474c4 100644 --- a/crates/rue-parser/src/ast.rs +++ b/crates/rue-parser/src/ast.rs @@ -118,11 +118,12 @@ ast_node!(FieldAccessExpr); ast_node!(LambdaExpr); ast_node!(LambdaParam); -ast_enum!(Type, PathType, PairType, FunctionType); +ast_enum!(Type, PathType, PairType, FunctionType, UnionType); ast_node!(PathType); ast_node!(PairType); ast_node!(FunctionType); ast_node!(FunctionTypeParam); +ast_node!(UnionType); ast_enum!(Stmt, LetStmt, IfStmt, ReturnStmt, RaiseStmt, AssertStmt, AssumeStmt); ast_node!(LetStmt); @@ -132,7 +133,7 @@ ast_node!(RaiseStmt); ast_node!(AssertStmt); ast_node!(AssumeStmt); -ast_node!(GenericTypes); +ast_node!(GenericParams); impl Root { pub fn items(&self) -> Vec { @@ -182,8 +183,8 @@ impl FunctionItem { .find(|token| token.kind() == SyntaxKind::Ident) } - pub fn generic_types(&self) -> Option { - self.syntax().children().find_map(GenericTypes::cast) + pub fn generic_params(&self) -> Option { + self.syntax().children().find_map(GenericParams::cast) } pub fn params(&self) -> Vec { @@ -708,8 +709,8 @@ impl PairExpr { } impl LambdaExpr { - pub fn generic_types(&self) -> Option { - self.syntax().children().find_map(GenericTypes::cast) + pub fn generic_params(&self) -> Option { + self.syntax().children().find_map(GenericParams::cast) } pub fn params(&self) -> Vec { @@ -868,8 +869,14 @@ impl FunctionTypeParam { } } -impl GenericTypes { - pub fn idents(&self) -> Vec { +impl UnionType { + pub fn types(&self) -> Vec { + self.syntax().children().filter_map(Type::cast).collect() + } +} + +impl GenericParams { + pub fn names(&self) -> Vec { self.syntax() .children_with_tokens() .filter_map(SyntaxElement::into_token) diff --git a/crates/rue-parser/src/grammar.rs b/crates/rue-parser/src/grammar.rs index 9216db2..cecfa2f 100644 --- a/crates/rue-parser/src/grammar.rs +++ b/crates/rue-parser/src/grammar.rs @@ -67,7 +67,7 @@ fn function_item(p: &mut Parser<'_>, cp: Checkpoint) { p.expect(SyntaxKind::Fun); p.expect(SyntaxKind::Ident); if p.at(SyntaxKind::LessThan) { - generic_types(p); + generic_params(p); } function_params(p); p.expect(SyntaxKind::Arrow); @@ -524,7 +524,7 @@ fn lambda_expr(p: &mut Parser<'_>) { p.start(SyntaxKind::LambdaExpr); p.expect(SyntaxKind::Fun); if p.at(SyntaxKind::LessThan) { - generic_types(p); + generic_params(p); } p.expect(SyntaxKind::OpenParen); while !p.at(SyntaxKind::CloseParen) { @@ -556,6 +556,8 @@ fn lambda_param(p: &mut Parser<'_>) { const TYPE_RECOVERY_SET: &[SyntaxKind] = &[SyntaxKind::OpenBrace, SyntaxKind::CloseBrace]; fn ty(p: &mut Parser<'_>) { + let cp = p.checkpoint(); + if p.at(SyntaxKind::Ident) { path_type(p); } else if p.at(SyntaxKind::Fun) { @@ -584,6 +586,13 @@ fn ty(p: &mut Parser<'_>) { } else { return p.error(TYPE_RECOVERY_SET); } + + while p.at(SyntaxKind::BitwiseOr) { + p.start_at(cp, SyntaxKind::UnionType); + p.bump(); + ty(p); + p.finish(); + } } fn path_type(p: &mut Parser<'_>) { @@ -605,8 +614,8 @@ fn function_type_param(p: &mut Parser<'_>) { p.finish(); } -fn generic_types(p: &mut Parser<'_>) { - p.start(SyntaxKind::GenericTypes); +fn generic_params(p: &mut Parser<'_>) { + p.start(SyntaxKind::GenericParams); p.expect(SyntaxKind::LessThan); while !p.at(SyntaxKind::GreaterThan) { p.expect(SyntaxKind::Ident); @@ -617,3 +626,16 @@ fn generic_types(p: &mut Parser<'_>) { p.expect(SyntaxKind::GreaterThan); p.finish(); } + +fn generic_args(p: &mut Parser<'_>) { + p.start(SyntaxKind::GenericArgs); + p.expect(SyntaxKind::LessThan); + while !p.at(SyntaxKind::GreaterThan) { + ty(p); + if !p.try_eat(SyntaxKind::Comma) { + break; + } + } + p.expect(SyntaxKind::GreaterThan); + p.finish(); +} diff --git a/crates/rue-parser/src/syntax_kind.rs b/crates/rue-parser/src/syntax_kind.rs index 1ddde8c..1b00bed 100644 --- a/crates/rue-parser/src/syntax_kind.rs +++ b/crates/rue-parser/src/syntax_kind.rs @@ -116,7 +116,9 @@ pub enum SyntaxKind { PairType, FunctionType, FunctionTypeParam, - GenericTypes, + UnionType, + GenericArgs, + GenericParams, } impl fmt::Display for SyntaxKind { @@ -234,7 +236,9 @@ impl fmt::Display for SyntaxKind { Self::PairType => "pair type", Self::FunctionType => "function type", Self::FunctionTypeParam => "function type parameter", - Self::GenericTypes => "generic types", + Self::UnionType => "union type", + Self::GenericArgs => "generic args", + Self::GenericParams => "generic params", } ) } From 3d660ba309a6238a0a2b7145d451ec96a537e87a Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 24 Jul 2024 00:05:09 -0400 Subject: [PATCH 068/100] Temp --- crates/rue-cli/src/main.rs | 18 ++- crates/rue-compiler/src/compiler.rs | 2 +- crates/rue-compiler/src/compiler/builtins.rs | 1 + crates/rue-compiler/src/compiler/expr.rs | 2 +- .../src/compiler/expr/binary_expr.rs | 2 +- .../src/compiler/expr/field_access_expr.rs | 1 + .../src/compiler/expr/function_call_expr.rs | 1 - .../src/compiler/expr/initializer_expr.rs | 2 +- .../src/compiler/expr/list_expr.rs | 16 ++- .../src/compiler/expr/path_expr.rs | 33 +++-- crates/rue-compiler/src/compiler/item.rs | 13 +- .../src/compiler/item/type_alias_item.rs | 79 ++++++++++-- crates/rue-compiler/src/compiler/path.rs | 118 ++++++++++++++---- crates/rue-compiler/src/compiler/ty.rs | 2 +- .../rue-compiler/src/compiler/ty/path_type.rs | 28 ++--- crates/rue-compiler/src/error.rs | 8 ++ crates/rue-compiler/stdlib.rue | 4 +- crates/rue-lsp/src/main.rs | 4 +- crates/rue-parser/src/ast.rs | 39 ++++-- crates/rue-parser/src/grammar.rs | 22 +++- crates/rue-parser/src/syntax_kind.rs | 2 + crates/rue-typing/src/comparison.rs | 17 +++ crates/rue-typing/src/standard_types.rs | 2 + crates/rue-typing/src/substitute_type.rs | 61 ++++----- crates/rue-typing/src/type_system.rs | 42 +++++-- 25 files changed, 371 insertions(+), 148 deletions(-) diff --git a/crates/rue-cli/src/main.rs b/crates/rue-cli/src/main.rs index ec53d73..c32db5c 100644 --- a/crates/rue-cli/src/main.rs +++ b/crates/rue-cli/src/main.rs @@ -5,7 +5,7 @@ use std::fs; use clap::Parser; use clvmr::{serde::node_to_bytes, Allocator, NodePtr}; use rue_clvm::{parse_clvm, run_clvm, stringify_clvm}; -use rue_compiler::{compile, compile_raw, Diagnostic, DiagnosticKind}; +use rue_compiler::{compile_raw, Diagnostic, DiagnosticKind}; use rue_parser::{line_col, parse, LineCol}; /// CLI tools for working with the Rue compiler. @@ -20,23 +20,31 @@ enum Command { /// A list of parameters to run the compiled program with. #[clap(long, short = 'r')] run: Option>, + + /// Whether to exclude the standard library. + #[clap(long, short = 'n')] + no_std: bool, }, /// Check a Rue source file for errors. Check { /// The source file to check. file: String, + + /// Whether to exclude the standard library. + #[clap(long, short = 'n')] + no_std: bool, }, } fn main() { match Command::parse() { - Command::Build { file, run } => build(file, true, &run), - Command::Check { file } => build(file, false, &None), + Command::Build { file, run, no_std } => build(file, true, &run, no_std), + Command::Check { file, no_std } => build(file, false, &None, no_std), } } -fn build(file: String, should_compile: bool, run: &Option>) { +fn build(file: String, should_compile: bool, run: &Option>, no_std: bool) { let source = fs::read_to_string(file).expect("could not read source file"); let (ast, errors) = parse(&source); @@ -53,7 +61,7 @@ fn build(file: String, should_compile: bool, run: &Option>) { &mut allocator, &ast, should_compile && errors.is_empty(), - false, + !no_std, ); if print_diagnostics(&source, &output.diagnostics) { diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index 068d78b..42a1773 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -123,7 +123,7 @@ impl<'a> Compiler<'a> { fn type_name(&self, type_id: TypeId) -> String { let mut names = HashMap::new(); - for &scope_id in self.scope_stack.iter() { + for &scope_id in &self.scope_stack { for type_id in self.db.scope(scope_id).local_types() { if let Some(name) = self.db.scope(scope_id).type_name(type_id) { names.insert(type_id, name.to_string()); diff --git a/crates/rue-compiler/src/compiler/builtins.rs b/crates/rue-compiler/src/compiler/builtins.rs index 3e3621f..3c0f78e 100644 --- a/crates/rue-compiler/src/compiler/builtins.rs +++ b/crates/rue-compiler/src/compiler/builtins.rs @@ -30,6 +30,7 @@ pub fn builtins(db: &mut Database, ty: &mut TypeSystem) -> Builtins { scope.define_type("Bytes32".to_string(), ty.std().bytes32); scope.define_type("PublicKey".to_string(), ty.std().public_key); scope.define_type("Any".to_string(), ty.std().any); + scope.define_type("List".to_string(), ty.std().unmapped_list); let builtins = Builtins { scope_id: db.alloc_scope(scope), diff --git a/crates/rue-compiler/src/compiler/expr.rs b/crates/rue-compiler/src/compiler/expr.rs index ff4b311..2ade03d 100644 --- a/crates/rue-compiler/src/compiler/expr.rs +++ b/crates/rue-compiler/src/compiler/expr.rs @@ -30,7 +30,7 @@ impl Compiler<'_> { let value = match expr { Expr::PathExpr(path) => { - self.compile_path_expr(&path.idents(), path.syntax().text_range()) + self.compile_path_expr(&path.items(), path.syntax().text_range()) } Expr::InitializerExpr(initializer) => self.compile_initializer_expr(initializer), Expr::LiteralExpr(literal) => self.compile_literal_expr(literal), diff --git a/crates/rue-compiler/src/compiler/expr/binary_expr.rs b/crates/rue-compiler/src/compiler/expr/binary_expr.rs index 430bd1e..ddca43c 100644 --- a/crates/rue-compiler/src/compiler/expr/binary_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/binary_expr.rs @@ -63,7 +63,7 @@ impl Compiler<'_> { return self.add_public_key(lhs.hir_id, rhs, text_range); } - if self.ty.compare(lhs.type_id, self.ty.std().bytes) == Comparison::Equal { + if self.ty.compare(lhs.type_id, self.ty.std().bytes) <= Comparison::Assignable { return self.add_bytes(lhs.hir_id, rhs, text_range); } diff --git a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs index c766217..7b64fdc 100644 --- a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs @@ -23,6 +23,7 @@ impl Compiler<'_> { }; let mut new_value = match self.ty.get(old_value.type_id).clone() { + Type::Unknown => return self.unknown(), Type::Struct(ty) => { let fields = deconstruct_items(self.ty, ty.type_id, ty.field_names.len(), ty.rest) .expect("invalid struct type"); diff --git a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs index 843c45b..e16b7cf 100644 --- a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs @@ -142,7 +142,6 @@ impl Compiler<'_> { } // Build the HIR for the function call. - let hir_id = self.db.alloc_hir(Hir::FunctionCall( callee.map_or(self.builtins.unknown, |callee| callee.hir_id), args.iter().map(|arg| arg.hir_id).collect(), diff --git a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs index c621eff..d4077ff 100644 --- a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs @@ -11,7 +11,7 @@ impl Compiler<'_> { pub fn compile_initializer_expr(&mut self, initializer: &InitializerExpr) -> Value { let ty = initializer .path() - .map(|path| self.compile_path_type(&path.idents(), path.syntax().text_range())); + .map(|path| self.compile_path_type(&path.items(), path.syntax().text_range())); match ty.map(|ty| self.ty.get(ty)).cloned() { Some(Type::Struct(struct_type)) => { diff --git a/crates/rue-compiler/src/compiler/expr/list_expr.rs b/crates/rue-compiler/src/compiler/expr/list_expr.rs index edb348e..9e904fb 100644 --- a/crates/rue-compiler/src/compiler/expr/list_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/list_expr.rs @@ -42,9 +42,8 @@ impl Compiler<'_> { list_type = Some(output.type_id); item_type = unwrap_list(self.ty, output.type_id); } else { - // TODO: list_type = Some(self.ty.alloc(Type::List(output.type_id))); - // item_type = Some(output.type_id); - todo!() + list_type = Some(self.ty.alloc_list(output.type_id)); + item_type = Some(output.type_id); } } @@ -70,11 +69,10 @@ impl Compiler<'_> { } } - // TODO: Value::new( - // hir_id, - // self.db - // .alloc_type(Type::List(item_type.unwrap_or(self.ty.std().unknown))), - // ) - todo!() + Value::new( + hir_id, + self.ty + .alloc_list(item_type.unwrap_or(self.ty.std().unknown)), + ) } } diff --git a/crates/rue-compiler/src/compiler/expr/path_expr.rs b/crates/rue-compiler/src/compiler/expr/path_expr.rs index c01ce00..227efb8 100644 --- a/crates/rue-compiler/src/compiler/expr/path_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/path_expr.rs @@ -1,10 +1,10 @@ use rowan::TextRange; -use rue_parser::SyntaxToken; +use rue_parser::PathItem; use rue_typing::{bigint_to_bytes, Type}; use crate::{ compiler::{ - path::{PathItem, PathKind}, + path::{Path, PathKind}, Compiler, }, hir::Hir, @@ -14,28 +14,27 @@ use crate::{ }; impl Compiler<'_> { - pub fn compile_path_expr(&mut self, idents: &[SyntaxToken], text_range: TextRange) -> Value { - let Some(mut item) = - self.resolve_base_path(&idents[0], PathKind::Symbol, idents.len() == 1) + pub fn compile_path_expr(&mut self, items: &[PathItem], text_range: TextRange) -> Value { + let Some(mut path) = self.resolve_base_path(&items[0], PathKind::Symbol, items.len() == 1) else { return self.unknown(); }; - let mut last_ident = idents[0].to_string(); + let mut last_name = items[0].name().unwrap().to_string(); - for (i, name) in idents.iter().enumerate().skip(1) { - let Some(next_item) = - self.resolve_next_path(item, name, PathKind::Symbol, i == idents.len() - 1) + for (i, item) in items.iter().enumerate().skip(1) { + let Some(next_path) = + self.resolve_next_path(path, item, PathKind::Symbol, i == items.len() - 1) else { return self.unknown(); }; - last_ident = name.to_string(); - item = next_item; + last_name = item.name().unwrap().to_string(); + path = next_path; } - let symbol_id = match item { - PathItem::Symbol(symbol_id) => symbol_id, - PathItem::Type(type_id) => { + let symbol_id = match path { + Path::Symbol(symbol_id) => symbol_id, + Path::Type(type_id) => { if let Type::Variant(variant) = self.ty.get(type_id).clone() { if variant.field_names.is_some() { self.db.error( @@ -59,20 +58,20 @@ impl Compiler<'_> { return Value::new(hir_id, type_id); } self.db - .error(ErrorKind::ExpectedSymbolPath(last_ident), text_range); + .error(ErrorKind::ExpectedSymbolPath(last_name), text_range); return self.unknown(); } }; if matches!(self.db.symbol(symbol_id), Symbol::Module(..)) { self.db - .error(ErrorKind::ModuleReference(last_ident), text_range); + .error(ErrorKind::ModuleReference(last_name), text_range); return self.unknown(); } if !self.is_callee && matches!(self.db.symbol(symbol_id), Symbol::InlineFunction(..)) { self.db - .error(ErrorKind::InlineFunctionReference(last_ident), text_range); + .error(ErrorKind::InlineFunctionReference(last_name), text_range); return self.unknown(); } diff --git a/crates/rue-compiler/src/compiler/item.rs b/crates/rue-compiler/src/compiler/item.rs index 8540bdc..33ef470 100644 --- a/crates/rue-compiler/src/compiler/item.rs +++ b/crates/rue-compiler/src/compiler/item.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use rue_parser::Item; use rue_typing::{Type, TypeId}; -use crate::{symbol::Symbol, ErrorKind, SymbolId}; +use crate::{symbol::Symbol, ErrorKind, ScopeId, SymbolId}; use super::Compiler; @@ -18,6 +18,7 @@ mod type_alias_item; pub struct Declarations { pub type_ids: Vec, pub symbol_ids: Vec, + pub scope_ids: Vec, pub exported_types: Vec, pub exported_symbols: Vec, } @@ -30,12 +31,17 @@ impl Compiler<'_> { let mut type_ids = Vec::new(); let mut symbol_ids = Vec::new(); + let mut scope_ids = Vec::new(); let mut exported_types = Vec::new(); let mut exported_symbols = Vec::new(); for item in items { match item { - Item::TypeAliasItem(ty) => type_ids.push(self.declare_type_alias_item(ty)), + Item::TypeAliasItem(ty) => { + let (type_id, scope_id) = self.declare_type_alias_item(ty); + type_ids.push(type_id); + scope_ids.push(scope_id); + } Item::StructItem(struct_item) => { type_ids.push(self.declare_struct_item(struct_item)); } @@ -72,6 +78,7 @@ impl Compiler<'_> { Declarations { type_ids, symbol_ids, + scope_ids, exported_types, exported_symbols, } @@ -85,7 +92,7 @@ impl Compiler<'_> { Item::TypeAliasItem(ty) => { let type_id = declarations.type_ids.remove(0); self.type_definition_stack.push(type_id); - self.compile_type_alias_item(ty, type_id); + self.compile_type_alias_item(ty, type_id, declarations.scope_ids.remove(0)); self.type_definition_stack.pop().unwrap(); } Item::StructItem(struct_item) => { 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 86a9ef2..5c20419 100644 --- a/crates/rue-compiler/src/compiler/item/type_alias_item.rs +++ b/crates/rue-compiler/src/compiler/item/type_alias_item.rs @@ -1,33 +1,92 @@ use rue_parser::TypeAliasItem; use rue_typing::{Alias, Type, TypeId}; -use crate::compiler::Compiler; +use crate::{compiler::Compiler, scope::Scope, ErrorKind, ScopeId}; impl Compiler<'_> { /// Define a type for an alias in the current scope, but leave it as unknown for now. - pub fn declare_type_alias_item(&mut self, type_alias: &TypeAliasItem) -> TypeId { - let type_id = self.ty.alloc(Type::Unknown); + pub fn declare_type_alias_item(&mut self, type_alias: &TypeAliasItem) -> (TypeId, ScopeId) { + // Add the scope so you can track generic types. + let scope_id = self.db.alloc_scope(Scope::default()); + self.scope_stack.push(scope_id); + + let mut generic_types = Vec::new(); + + // Add the generic types to the scope. + for name in type_alias + .generic_params() + .map(|generics| generics.names()) + .unwrap_or_default() + { + // Create the generic type id. + let type_id = self.ty.alloc(Type::Generic); + + // Check for duplicate generic types. + if self.scope().ty(name.text()).is_some() { + self.db.error( + ErrorKind::DuplicateType(name.text().to_string()), + name.text_range(), + ); + } + + // Add the generic type to the scope and define the token for the generic type. + self.scope_mut().define_type(name.to_string(), type_id); + + self.db.insert_type_token(type_id, name); + + // Add the generic type to the list so it can be added to the function type. + generic_types.push(type_id); + } + + self.scope_stack.pop().unwrap(); + + // Create the alias type. + let ref_type_id = self.ty.alloc(Type::Ref(self.ty.std().unknown)); + + let type_id = self.ty.alloc(Type::Alias(Alias { + original_type_id: None, + type_id: ref_type_id, + generic_types, + })); + if let Some(name) = type_alias.name() { self.scope_mut().define_type(name.to_string(), type_id); self.db.insert_type_token(type_id, name); } - type_id + + (type_id, scope_id) } /// Compile and resolve the type that the alias points to. - pub fn compile_type_alias_item(&mut self, type_alias: &TypeAliasItem, alias_type_id: TypeId) { + pub fn compile_type_alias_item( + &mut self, + type_alias: &TypeAliasItem, + alias_type_id: TypeId, + scope_id: ScopeId, + ) { self.type_definition_stack.push(alias_type_id); + // Add the scope so you can use generic types. + self.scope_stack.push(scope_id); let type_id = type_alias .ty() .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); + self.scope_stack.pop().unwrap(); // Set the alias type to the resolved type. - *self.ty.get_mut(alias_type_id) = Type::Alias(Alias { - original_type_id: None, - type_id, - generic_types: Vec::new(), - }); + let Type::Alias(Alias { + type_id: ref_type_id, + .. + }) = self.ty.get(alias_type_id).clone() + else { + unreachable!(); + }; + + let Type::Ref(reference) = self.ty.get_raw_mut(ref_type_id) else { + unreachable!(); + }; + + *reference = type_id; self.type_definition_stack.pop().unwrap(); } diff --git a/crates/rue-compiler/src/compiler/path.rs b/crates/rue-compiler/src/compiler/path.rs index 602668c..cb1fe57 100644 --- a/crates/rue-compiler/src/compiler/path.rs +++ b/crates/rue-compiler/src/compiler/path.rs @@ -1,12 +1,15 @@ -use rue_parser::SyntaxToken; -use rue_typing::{Type, TypeId}; +use std::collections::HashMap; + +use rowan::TextRange; +use rue_parser::{AstNode, GenericArgs, PathItem}; +use rue_typing::{Lazy, Type, TypeId}; use crate::{symbol::Symbol, ErrorKind, SymbolId}; use super::Compiler; #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PathItem { +pub enum Path { Symbol(SymbolId), Type(TypeId), } @@ -20,10 +23,12 @@ pub enum PathKind { impl Compiler<'_> { pub fn resolve_base_path( &mut self, - name: &SyntaxToken, + item: &PathItem, path_kind: PathKind, last: bool, - ) -> Option { + ) -> Option { + let name = item.name()?; + for &scope_id in self.scope_stack.iter().rev() { let type_id = self.db.scope(scope_id).ty(name.text()); let symbol_id = self.db.scope(scope_id).symbol(name.text()); @@ -31,31 +36,39 @@ impl Compiler<'_> { if let (Some(type_id), Some(symbol_id)) = (type_id, symbol_id) { if let Symbol::Module(..) = self.db.symbol(symbol_id) { if !last { - return Some(PathItem::Symbol(symbol_id)); + return Some(Path::Symbol(symbol_id)); } } if let Type::Enum(..) = self.ty.get(type_id) { if !last { self.type_reference(type_id); - return Some(PathItem::Type(type_id)); + return Some(Path::Type(type_id)); } } match path_kind { PathKind::Type => { self.type_reference(type_id); - return Some(PathItem::Type(type_id)); + return Some(Path::Type(self.handle_generics( + type_id, + item.generic_args(), + item.syntax().text_range(), + )?)); } PathKind::Symbol => { - return Some(PathItem::Symbol(symbol_id)); + return Some(Path::Symbol(symbol_id)); } } } else if let Some(type_id) = type_id { self.type_reference(type_id); - return Some(PathItem::Type(type_id)); + return Some(Path::Type(self.handle_generics( + type_id, + item.generic_args(), + item.syntax().text_range(), + )?)); } else if let Some(symbol_id) = symbol_id { - return Some(PathItem::Symbol(symbol_id)); + return Some(Path::Symbol(symbol_id)); } } @@ -72,13 +85,15 @@ impl Compiler<'_> { pub fn resolve_next_path( &mut self, - item: PathItem, - name: &SyntaxToken, + path: Path, + item: &PathItem, path_kind: PathKind, last: bool, - ) -> Option { - match item { - PathItem::Type(type_id) => { + ) -> Option { + let name = item.name()?; + + match path { + Path::Type(type_id) => { let Type::Enum(enum_type) = self.ty.get(type_id) else { self.db.error( ErrorKind::InvalidTypePath(self.type_name(type_id)), @@ -96,9 +111,9 @@ impl Compiler<'_> { }; self.type_reference(variant_type); - Some(PathItem::Type(variant_type)) + Some(Path::Type(variant_type)) } - PathItem::Symbol(module_id) => { + Path::Symbol(module_id) => { let Symbol::Module(module) = self.db.symbol(module_id) else { self.db.error( ErrorKind::InvalidSymbolPath(self.symbol_name(module_id)), @@ -124,29 +139,37 @@ impl Compiler<'_> { if let (Some(type_id), Some(symbol_id)) = (type_id, symbol_id) { if let Symbol::Module(..) = self.db.symbol(symbol_id) { if !last { - return Some(PathItem::Symbol(symbol_id)); + return Some(Path::Symbol(symbol_id)); } } if let Type::Enum(..) = self.ty.get(type_id) { if !last { self.type_reference(type_id); - return Some(PathItem::Type(type_id)); + return Some(Path::Type(type_id)); } } match path_kind { PathKind::Type => { self.type_reference(type_id); - Some(PathItem::Type(type_id)) + Some(Path::Type(self.handle_generics( + type_id, + item.generic_args(), + item.syntax().text_range(), + )?)) } - PathKind::Symbol => Some(PathItem::Symbol(symbol_id)), + PathKind::Symbol => Some(Path::Symbol(symbol_id)), } } else if let Some(type_id) = type_id { self.type_reference(type_id); - Some(PathItem::Type(type_id)) + Some(Path::Type(self.handle_generics( + type_id, + item.generic_args(), + item.syntax().text_range(), + )?)) } else if let Some(symbol_id) = symbol_id { - Some(PathItem::Symbol(symbol_id)) + Some(Path::Symbol(symbol_id)) } else if private_type { self.db.error( ErrorKind::PrivateType(name.text().to_string()), @@ -169,4 +192,51 @@ impl Compiler<'_> { } } } + + fn handle_generics( + &mut self, + type_id: TypeId, + generic_args: Option, + text_range: TextRange, + ) -> Option { + let Type::Alias(alias) = self.ty.get(type_id) else { + if generic_args.is_some() { + self.db.error(ErrorKind::UnexpectedGenericArgs, text_range); + return None; + } + return Some(type_id); + }; + + if generic_args.is_some() && alias.generic_types.is_empty() { + self.db.error(ErrorKind::UnexpectedGenericArgs, text_range); + None + } else if generic_args.is_none() && !alias.generic_types.is_empty() { + self.db.error(ErrorKind::ExpectedGenericArgs, text_range); + None + } else if let Some(generic_args) = generic_args { + let generic_args = generic_args.types(); + + if generic_args.len() != alias.generic_types.len() { + self.db.error( + ErrorKind::GenericArgsMismatch(generic_args.len(), alias.generic_types.len()), + text_range, + ); + return None; + } + + let mut lazy = Lazy { + type_id, + substitutions: HashMap::new(), + }; + + for (generic_type, arg) in alias.generic_types.clone().into_iter().zip(generic_args) { + let arg = self.compile_type(arg); + lazy.substitutions.insert(generic_type, arg); + } + + Some(self.ty.alloc(Type::Lazy(lazy))) + } else { + Some(type_id) + } + } } diff --git a/crates/rue-compiler/src/compiler/ty.rs b/crates/rue-compiler/src/compiler/ty.rs index 0d0e121..4516f0d 100644 --- a/crates/rue-compiler/src/compiler/ty.rs +++ b/crates/rue-compiler/src/compiler/ty.rs @@ -12,7 +12,7 @@ impl Compiler<'_> { pub fn compile_type(&mut self, ty: Type) -> TypeId { match ty { Type::PathType(path) => { - self.compile_path_type(&path.idents(), path.syntax().text_range()) + self.compile_path_type(&path.items(), path.syntax().text_range()) } Type::FunctionType(function) => self.compile_function_type(&function), Type::PairType(tuple) => self.compile_pair_type(&tuple), diff --git a/crates/rue-compiler/src/compiler/ty/path_type.rs b/crates/rue-compiler/src/compiler/ty/path_type.rs index 8f62735..43b9f9e 100644 --- a/crates/rue-compiler/src/compiler/ty/path_type.rs +++ b/crates/rue-compiler/src/compiler/ty/path_type.rs @@ -1,39 +1,39 @@ use rowan::TextRange; -use rue_parser::SyntaxToken; +use rue_parser::PathItem; use rue_typing::TypeId; use crate::{ compiler::{ - path::{PathItem, PathKind}, + path::{Path, PathKind}, Compiler, }, ErrorKind, }; impl Compiler<'_> { - pub fn compile_path_type(&mut self, idents: &[SyntaxToken], text_range: TextRange) -> TypeId { - let Some(mut item) = self.resolve_base_path(&idents[0], PathKind::Type, idents.len() == 1) + pub fn compile_path_type(&mut self, items: &[PathItem], text_range: TextRange) -> TypeId { + let Some(mut path) = self.resolve_base_path(&items[0], PathKind::Type, items.len() == 1) else { return self.ty.std().unknown; }; - let mut last_ident = idents[0].to_string(); + let mut last_name = items[0].name().unwrap().to_string(); - for (i, name) in idents.iter().enumerate().skip(1) { - let Some(next_item) = - self.resolve_next_path(item, name, PathKind::Type, i == idents.len() - 1) + for (i, item) in items.iter().enumerate().skip(1) { + let Some(next_path) = + self.resolve_next_path(path, item, PathKind::Type, i == items.len() - 1) else { return self.ty.std().unknown; }; - last_ident = name.to_string(); - item = next_item; + last_name = item.name().unwrap().to_string(); + path = next_path; } - match item { - PathItem::Type(type_id) => type_id, - PathItem::Symbol(..) => { + match path { + Path::Type(type_id) => type_id, + Path::Symbol(..) => { self.db - .error(ErrorKind::ExpectedTypePath(last_ident), text_range); + .error(ErrorKind::ExpectedTypePath(last_name), text_range); self.ty.std().unknown } } diff --git a/crates/rue-compiler/src/error.rs b/crates/rue-compiler/src/error.rs index a9ab741..c7b013d 100644 --- a/crates/rue-compiler/src/error.rs +++ b/crates/rue-compiler/src/error.rs @@ -117,6 +117,9 @@ pub enum ErrorKind { InvalidSymbolPath(Option), ExpectedTypePath(String), ExpectedSymbolPath(String), + UnexpectedGenericArgs, + ExpectedGenericArgs, + GenericArgsMismatch(usize, usize), // Type guards. ImpossibleTypeCheck(String, String), @@ -288,6 +291,11 @@ impl fmt::Display for ErrorKind { }, Self::ExpectedTypePath(name) => format!("Expected type, but found symbol `{name}` instead"), Self::ExpectedSymbolPath(name) => format!("Expected symbol, but found type `{name}` instead"), + Self::UnexpectedGenericArgs => "Unexpected generic arguments".to_string(), + Self::ExpectedGenericArgs => "Expected generic arguments".to_string(), + Self::GenericArgsMismatch(found, expected) => { + format!("Expected {expected} generic argument{}, but found {found}", if *expected == 1 { "" } else { "s" }) + } // Type guards. Self::ImpossibleTypeCheck(from, to) => format!("Cannot check type `{from}` against `{to}`"), diff --git a/crates/rue-compiler/stdlib.rue b/crates/rue-compiler/stdlib.rue index 29c186a..e791d09 100644 --- a/crates/rue-compiler/stdlib.rue +++ b/crates/rue-compiler/stdlib.rue @@ -109,8 +109,8 @@ export enum Condition { }, } -export fun concat(a: T[], b: T[]) -> T[] { - if a is (T, T[]) { +export fun concat(a: List, b: List) -> List { + if a is (T, List) { return [a.first, ...concat(a.rest, b)]; } b diff --git a/crates/rue-lsp/src/main.rs b/crates/rue-lsp/src/main.rs index 290c297..9390cb8 100644 --- a/crates/rue-lsp/src/main.rs +++ b/crates/rue-lsp/src/main.rs @@ -1,5 +1,5 @@ use clvmr::Allocator; -use rue_compiler::{compile, compile_raw, DiagnosticKind}; +use rue_compiler::{compile, DiagnosticKind}; use rue_parser::{line_col, parse, LineCol}; use tower_lsp::jsonrpc::Result; use tower_lsp::lsp_types::{ @@ -60,7 +60,7 @@ impl LanguageServer for Backend { /// This is a hack to get around a Rust compiler error. #[allow(clippy::needless_pass_by_value)] fn analyze_owned(root: rue_parser::Root) -> Vec { - compile_raw(&mut Allocator::new(), &root, false, false).diagnostics + compile(&mut Allocator::new(), &root, false).diagnostics } impl Backend { diff --git a/crates/rue-parser/src/ast.rs b/crates/rue-parser/src/ast.rs index b2474c4..5c4095a 100644 --- a/crates/rue-parser/src/ast.rs +++ b/crates/rue-parser/src/ast.rs @@ -133,7 +133,9 @@ ast_node!(RaiseStmt); ast_node!(AssertStmt); ast_node!(AssumeStmt); +ast_node!(GenericArgs); ast_node!(GenericParams); +ast_node!(PathItem); impl Root { pub fn items(&self) -> Vec { @@ -252,6 +254,10 @@ impl TypeAliasItem { .find(|token| token.kind() == SyntaxKind::Ident) } + pub fn generic_params(&self) -> Option { + self.syntax().children().find_map(GenericParams::cast) + } + pub fn ty(&self) -> Option { self.syntax().children().find_map(Type::cast) } @@ -499,11 +505,10 @@ impl AssumeStmt { } impl PathExpr { - pub fn idents(&self) -> Vec { + pub fn items(&self) -> Vec { self.syntax() - .children_with_tokens() - .filter_map(SyntaxElement::into_token) - .filter(|token| token.kind() == SyntaxKind::Ident) + .children() + .filter_map(PathItem::cast) .collect() } } @@ -810,11 +815,10 @@ impl FieldAccessExpr { } impl PathType { - pub fn idents(&self) -> Vec { + pub fn items(&self) -> Vec { self.syntax() - .children_with_tokens() - .filter_map(SyntaxElement::into_token) - .filter(|token| token.kind() == SyntaxKind::Ident) + .children() + .filter_map(PathItem::cast) .collect() } } @@ -884,3 +888,22 @@ impl GenericParams { .collect() } } + +impl GenericArgs { + pub fn types(&self) -> Vec { + self.syntax().children().filter_map(Type::cast).collect() + } +} + +impl PathItem { + pub fn name(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(SyntaxElement::into_token) + .find(|token| token.kind() == SyntaxKind::Ident) + } + + pub fn generic_args(&self) -> Option { + self.syntax().children().find_map(GenericArgs::cast) + } +} diff --git a/crates/rue-parser/src/grammar.rs b/crates/rue-parser/src/grammar.rs index cecfa2f..becf3af 100644 --- a/crates/rue-parser/src/grammar.rs +++ b/crates/rue-parser/src/grammar.rs @@ -101,6 +101,9 @@ fn type_alias_item(p: &mut Parser<'_>, cp: Checkpoint) { p.start_at(cp, SyntaxKind::TypeAliasItem); p.expect(SyntaxKind::Type); p.expect(SyntaxKind::Ident); + if p.at(SyntaxKind::LessThan) { + generic_params(p); + } p.expect(SyntaxKind::Assign); ty(p); p.expect(SyntaxKind::Semicolon); @@ -490,9 +493,13 @@ fn expr_binding_power(p: &mut Parser<'_>, minimum_binding_power: u8, allow_initi fn path_expr(p: &mut Parser<'_>) { p.start(SyntaxKind::PathExpr); + p.start(SyntaxKind::PathItem); p.expect(SyntaxKind::Ident); + p.finish(); while p.try_eat(SyntaxKind::PathSeparator) { + p.start(SyntaxKind::PathItem); p.expect(SyntaxKind::Ident); + p.finish(); } p.finish(); } @@ -597,9 +604,18 @@ fn ty(p: &mut Parser<'_>) { fn path_type(p: &mut Parser<'_>) { p.start(SyntaxKind::PathType); - p.expect(SyntaxKind::Ident); + path_type_item(p); while p.try_eat(SyntaxKind::PathSeparator) { - p.expect(SyntaxKind::Ident); + path_type_item(p); + } + p.finish(); +} + +fn path_type_item(p: &mut Parser<'_>) { + p.start(SyntaxKind::PathItem); + p.expect(SyntaxKind::Ident); + if p.at(SyntaxKind::LessThan) { + generic_args(p); } p.finish(); } @@ -630,6 +646,8 @@ fn generic_params(p: &mut Parser<'_>) { fn generic_args(p: &mut Parser<'_>) { p.start(SyntaxKind::GenericArgs); p.expect(SyntaxKind::LessThan); + ty(p); + p.try_eat(SyntaxKind::Comma); while !p.at(SyntaxKind::GreaterThan) { ty(p); if !p.try_eat(SyntaxKind::Comma) { diff --git a/crates/rue-parser/src/syntax_kind.rs b/crates/rue-parser/src/syntax_kind.rs index 1b00bed..8b74432 100644 --- a/crates/rue-parser/src/syntax_kind.rs +++ b/crates/rue-parser/src/syntax_kind.rs @@ -119,6 +119,7 @@ pub enum SyntaxKind { UnionType, GenericArgs, GenericParams, + PathItem, } impl fmt::Display for SyntaxKind { @@ -239,6 +240,7 @@ impl fmt::Display for SyntaxKind { Self::UnionType => "union type", Self::GenericArgs => "generic args", Self::GenericParams => "generic params", + Self::PathItem => "path item", } ) } diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index a6a3486..c08118d 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -553,6 +553,23 @@ mod tests { assert_eq!(db.compare(lhs, rhs), Comparison::Assignable); } + #[test] + fn test_compare_nil_list() { + let mut db = TypeSystem::new(); + let types = db.std(); + let list = alloc_list(&mut db, types.int); + assert_eq!(db.compare(types.nil, list), Comparison::Assignable); + } + + #[test] + fn test_compare_pair_list() { + let mut db = TypeSystem::new(); + let types = db.std(); + let pair = db.alloc(Type::Pair(types.int, types.nil)); + let list = alloc_list(&mut db, types.int); + assert_eq!(db.compare(pair, list), Comparison::Assignable); + } + #[test] fn test_generic_inference() { let mut db = TypeSystem::new(); diff --git a/crates/rue-typing/src/standard_types.rs b/crates/rue-typing/src/standard_types.rs index e61ccff..6d3b5b3 100644 --- a/crates/rue-typing/src/standard_types.rs +++ b/crates/rue-typing/src/standard_types.rs @@ -5,6 +5,8 @@ pub struct StandardTypes { pub unknown: TypeId, pub never: TypeId, pub any: TypeId, + pub unmapped_list: TypeId, + pub generic_list_item: TypeId, pub atom: TypeId, pub bytes: TypeId, pub bytes32: TypeId, diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index 2febc42..c0a139e 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use crate::{Alias, Callable, Enum, Struct, Type, TypeId, TypeSystem, Variant}; @@ -13,15 +13,13 @@ pub(crate) fn substitute_type( type_id: TypeId, substitutions: &mut HashMap, semantics: Semantics, - visited: &mut HashSet, ) -> TypeId { if let Some(new_type_id) = substitutions.get(&type_id) { return *new_type_id; } - if !visited.insert(type_id) { - return type_id; - } + let placeholder = types.alloc(Type::Unknown); + substitutions.insert(type_id, placeholder); let result = match types.get(type_id) { Type::Ref(..) => unreachable!(), @@ -40,8 +38,8 @@ pub(crate) fn substitute_type( Type::Pair(first, rest) => { let (first, rest) = (*first, *rest); - let new_first = substitute_type(types, first, substitutions, semantics, visited); - let new_rest = substitute_type(types, rest, substitutions, semantics, visited); + let new_first = substitute_type(types, first, substitutions, semantics); + let new_rest = substitute_type(types, rest, substitutions, semantics); if new_first == first && new_rest == rest { type_id @@ -54,13 +52,7 @@ pub(crate) fn substitute_type( let mut result = Vec::new(); for item in &items { - result.push(substitute_type( - types, - *item, - substitutions, - semantics, - visited, - )); + result.push(substitute_type(types, *item, substitutions, semantics)); } if result == items { @@ -70,14 +62,21 @@ pub(crate) fn substitute_type( } } Type::Lazy(lazy) => { + let mut old_substitutions = HashMap::new(); + for (key, val) in lazy.substitutions.clone() { + if let Some(old) = substitutions.insert(key, val) { + old_substitutions.insert(key, old); + } + } substitutions.extend(lazy.substitutions.clone()); - substitute_type(types, lazy.type_id, substitutions, semantics, visited) + let result = substitute_type(types, lazy.type_id, substitutions, semantics); + substitutions.extend(old_substitutions); + result } Type::Alias(alias) => { let alias = alias.clone(); - let new_type_id = - substitute_type(types, alias.type_id, substitutions, semantics, visited); + let new_type_id = substitute_type(types, alias.type_id, substitutions, semantics); if semantics == Semantics::Preserve { if new_type_id == alias.type_id { @@ -96,7 +95,7 @@ pub(crate) fn substitute_type( Type::Struct(ty) => { let ty = ty.clone(); - let new_type_id = substitute_type(types, ty.type_id, substitutions, semantics, visited); + let new_type_id = substitute_type(types, ty.type_id, substitutions, semantics); if semantics == Semantics::Preserve { if new_type_id == ty.type_id { @@ -117,7 +116,7 @@ pub(crate) fn substitute_type( Type::Variant(ty) => { let ty = ty.clone(); - let new_type_id = substitute_type(types, ty.type_id, substitutions, semantics, visited); + let new_type_id = substitute_type(types, ty.type_id, substitutions, semantics); if semantics == Semantics::Preserve { if new_type_id == ty.type_id { @@ -140,7 +139,7 @@ pub(crate) fn substitute_type( Type::Enum(ty) => { let ty = ty.clone(); - let new_type_id = substitute_type(types, ty.type_id, substitutions, semantics, visited); + let new_type_id = substitute_type(types, ty.type_id, substitutions, semantics); if semantics == Semantics::Preserve { if new_type_id == ty.type_id { @@ -160,21 +159,11 @@ pub(crate) fn substitute_type( Type::Callable(callable) => { let callable = callable.clone(); - let new_return_type = substitute_type( - types, - callable.return_type, - substitutions, - semantics, - visited, - ); - - let new_parameters = substitute_type( - types, - callable.parameters, - substitutions, - semantics, - visited, - ); + let new_return_type = + substitute_type(types, callable.return_type, substitutions, semantics); + + let new_parameters = + substitute_type(types, callable.parameters, substitutions, semantics); match semantics { Semantics::Preserve => { @@ -198,7 +187,7 @@ pub(crate) fn substitute_type( } }; - visited.remove(&type_id); + *types.get_mut(placeholder) = Type::Ref(result); result } diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index b36ea13..260ab98 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -4,8 +4,8 @@ use id_arena::{Arena, Id}; use crate::{ check_type, compare_type, difference_type, replace_type, simplify_check, stringify_type, - substitute_type, Check, CheckError, Comparison, ComparisonContext, Semantics, StandardTypes, - Type, TypePath, + substitute_type, Alias, Check, CheckError, Comparison, ComparisonContext, Lazy, Semantics, + StandardTypes, Type, TypePath, }; pub type TypeId = Id; @@ -32,11 +32,21 @@ impl Default for TypeSystem { let false_bool = arena.alloc(Type::False); let nil = arena.alloc(Type::Nil); + let bool = arena.alloc(Type::Union(vec![false_bool, true_bool])); + let any = arena.alloc(Type::Unknown); let pair = arena.alloc(Type::Pair(any, any)); arena[any] = Type::Union(vec![atom, pair]); - let bool = arena.alloc(Type::Union(vec![false_bool, true_bool])); + let generic_list_item = arena.alloc(Type::Generic); + let inner = arena.alloc(Type::Unknown); + let unmapped_list = arena.alloc(Type::Alias(Alias { + original_type_id: None, + type_id: inner, + generic_types: vec![generic_list_item], + })); + let pair = arena.alloc(Type::Pair(generic_list_item, unmapped_list)); + arena[inner] = Type::Union(vec![pair, nil]); let mut names = HashMap::new(); names.insert(never, "Never".to_string()); @@ -50,6 +60,7 @@ impl Default for TypeSystem { names.insert(false_bool, "False".to_string()); names.insert(nil, "Nil".to_string()); names.insert(any, "Any".to_string()); + names.insert(unmapped_list, "List".to_string()); Self { arena, @@ -57,6 +68,8 @@ impl Default for TypeSystem { unknown, never, any, + unmapped_list, + generic_list_item, atom, bytes, bytes32, @@ -89,6 +102,10 @@ impl TypeSystem { &self.arena[type_id] } + pub fn get_raw_mut(&mut self, type_id: TypeId) -> &mut Type { + &mut self.arena[type_id] + } + pub fn get(&self, type_id: TypeId) -> &Type { match &self.arena[type_id] { Type::Ref(type_id) => self.get(*type_id), @@ -117,6 +134,15 @@ impl TypeSystem { } } + pub fn alloc_list(&mut self, type_id: TypeId) -> TypeId { + let mut substitutions = HashMap::new(); + substitutions.insert(self.types.generic_list_item, type_id); + self.alloc(Type::Lazy(Lazy { + type_id: self.types.unmapped_list, + substitutions, + })) + } + pub fn stringify_named(&self, type_id: TypeId, mut names: HashMap) -> String { for (id, name) in &self.names { names.entry(*id).or_insert_with(|| name.clone()); @@ -165,13 +191,7 @@ impl TypeSystem { mut substitutions: HashMap, semantics: Semantics, ) -> TypeId { - substitute_type( - self, - type_id, - &mut substitutions, - semantics, - &mut HashSet::new(), - ) + substitute_type(self, type_id, &mut substitutions, semantics) } pub fn check(&mut self, lhs: TypeId, rhs: TypeId) -> Result { @@ -179,6 +199,8 @@ impl TypeSystem { } pub fn difference(&mut self, lhs: TypeId, rhs: TypeId) -> TypeId { + let lhs = self.substitute(lhs, HashMap::new(), Semantics::Preserve); + let rhs = self.substitute(rhs, HashMap::new(), Semantics::Preserve); difference_type(self, lhs, rhs, &mut HashSet::new()) } From 88699be7f72ba9b3437fcb95f074be81c61d4876 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 25 Jul 2024 02:03:24 -0400 Subject: [PATCH 069/100] Any type --- crates/rue-typing/src/check/attributes.rs | 3 +- crates/rue-typing/src/check/check_type.rs | 54 +++++++++++-------- crates/rue-typing/src/comparison.rs | 64 +++++++++++++++-------- crates/rue-typing/src/difference.rs | 38 +++++++------- crates/rue-typing/src/standard_types.rs | 1 - crates/rue-typing/src/stringify.rs | 2 +- crates/rue-typing/src/substitute_type.rs | 2 +- crates/rue-typing/src/ty.rs | 2 +- crates/rue-typing/src/type_system.rs | 11 +--- 9 files changed, 100 insertions(+), 77 deletions(-) diff --git a/crates/rue-typing/src/check/attributes.rs b/crates/rue-typing/src/check/attributes.rs index 9f87244..410922a 100644 --- a/crates/rue-typing/src/check/attributes.rs +++ b/crates/rue-typing/src/check/attributes.rs @@ -92,10 +92,11 @@ pub(crate) fn union_attributes( length -= 1; } Type::Unknown => {} + Type::Any => {} Type::Never => { length -= 1; } - Type::Atom | Type::Bytes | Type::Int => { + Type::Bytes | Type::Int => { atom_count += 1; } Type::Bytes32 => { diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs index 34041dc..8b45f43 100644 --- a/crates/rue-typing/src/check/check_type.rs +++ b/crates/rue-typing/src/check/check_type.rs @@ -40,7 +40,7 @@ pub(crate) fn check_type( (Type::Never, _) => Check::True, (_, Type::Never) => Check::False, - (Type::Atom, Type::Atom) => Check::True, + (Type::Any, Type::Any) => Check::True, (Type::Bytes, Type::Bytes) => Check::True, (Type::Bytes32, Type::Bytes32) => Check::True, (Type::PublicKey, Type::PublicKey) => Check::True, @@ -49,28 +49,47 @@ pub(crate) fn check_type( (Type::True, Type::True) => Check::True, (Type::False, Type::False) => Check::True, - (Type::Bytes32, Type::Atom) => Check::True, - (Type::PublicKey, Type::Atom) => Check::True, - (Type::Int, Type::Atom) => Check::True, - (Type::Bytes, Type::Atom) => Check::True, - (Type::Nil, Type::Atom) => Check::True, + (Type::Bytes32, Type::Any) => Check::True, + (Type::PublicKey, Type::Any) => Check::True, + (Type::Int, Type::Any) => Check::True, + (Type::Bytes, Type::Any) => Check::True, + (Type::Nil, Type::Any) => Check::True, + (Type::True, Type::Any) => Check::True, + (Type::False, Type::Any) => Check::True, + (Type::Value(..), Type::Any) => Check::True, + (Type::Pair(..), Type::Any) => Check::True, + + (Type::Any, Type::Bytes) => Check::IsAtom, + (Type::Any, Type::Int) => Check::IsAtom, + (Type::Any, Type::Bytes32) => Check::And(vec![Check::IsAtom, Check::Length(32)]), + (Type::Any, Type::PublicKey) => Check::And(vec![Check::IsAtom, Check::Length(48)]), + (Type::Any, Type::Nil) => Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]), + (Type::Any, Type::True) => Check::And(vec![Check::IsAtom, Check::Value(BigInt::one())]), + (Type::Any, Type::False) => Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]), + (Type::Any, Type::Value(value)) => { + Check::And(vec![Check::IsAtom, Check::Value(value.clone())]) + } + (Type::Any, Type::Pair(first, rest)) => { + let (first, rest) = (*first, *rest); + let first = check_type(types, types.std().any, first, visited)?; + let rest = check_type(types, types.std().any, rest, visited)?; + Check::And(vec![ + Check::IsPair, + Check::First(Box::new(first)), + Check::Rest(Box::new(rest)), + ]) + } - (Type::Atom, Type::Bytes) => Check::True, (Type::Bytes32, Type::Bytes) => Check::True, (Type::PublicKey, Type::Bytes) => Check::True, (Type::Int, Type::Bytes) => Check::True, (Type::Nil, Type::Bytes) => Check::True, - (Type::Atom, Type::Int) => Check::True, (Type::Bytes32, Type::Int) => Check::True, (Type::PublicKey, Type::Int) => Check::True, (Type::Bytes, Type::Int) => Check::True, (Type::Nil, Type::Int) => Check::True, - (Type::Atom, Type::Nil) => Check::Value(BigInt::ZERO), - (Type::Atom, Type::PublicKey) => Check::Length(48), - (Type::Atom, Type::Bytes32) => Check::Length(32), - (Type::Bytes, Type::Nil) => Check::Value(BigInt::ZERO), (Type::Bytes, Type::PublicKey) => Check::Length(48), (Type::Bytes, Type::Bytes32) => Check::Length(32), @@ -86,8 +105,6 @@ pub(crate) fn check_type( (Type::PublicKey, Type::Nil) => Check::False, (Type::Bytes32, Type::Nil) => Check::False, - (Type::True, Type::Atom) => Check::True, - (Type::False, Type::Atom) => Check::True, (Type::True, Type::Nil) => Check::False, (Type::False, Type::Nil) => Check::True, (Type::True, Type::False) => Check::False, @@ -101,8 +118,6 @@ pub(crate) fn check_type( (Type::True, Type::Int) => Check::True, (Type::False, Type::Int) => Check::True, - (Type::Atom, Type::True) => Check::Value(BigInt::one()), - (Type::Atom, Type::False) => Check::Value(BigInt::ZERO), (Type::Bytes, Type::True) => Check::Value(BigInt::one()), (Type::Bytes, Type::False) => Check::Value(BigInt::ZERO), (Type::Int, Type::True) => Check::Value(BigInt::one()), @@ -114,7 +129,6 @@ pub(crate) fn check_type( (Type::Nil, Type::True) => Check::False, (Type::Nil, Type::False) => Check::True, - (Type::Value(..), Type::Atom) => Check::True, (Type::Value(..), Type::Bytes) => Check::True, (Type::Value(..), Type::Int) => Check::True, (Type::Value(value), Type::Bytes32) => { @@ -153,7 +167,6 @@ pub(crate) fn check_type( } } - (Type::Atom, Type::Value(value)) => Check::Value(value.clone()), (Type::Bytes, Type::Value(value)) => Check::Value(value.clone()), (Type::Int, Type::Value(value)) => Check::Value(value.clone()), (Type::Bytes32, Type::Value(value)) => { @@ -200,7 +213,6 @@ pub(crate) fn check_type( } } - (Type::Atom, Type::Pair(..)) => Check::False, (Type::Bytes, Type::Pair(..)) => Check::False, (Type::Bytes32, Type::Pair(..)) => Check::False, (Type::PublicKey, Type::Pair(..)) => Check::False, @@ -210,7 +222,6 @@ pub(crate) fn check_type( (Type::False, Type::Pair(..)) => Check::False, (Type::Value(..), Type::Pair(..)) => Check::False, - (Type::Pair(..), Type::Atom) => Check::False, (Type::Pair(..), Type::Bytes) => Check::False, (Type::Pair(..), Type::Bytes32) => Check::False, (Type::Pair(..), Type::PublicKey) => Check::False, @@ -295,8 +306,7 @@ fn check_union_against_rhs( Type::Unknown => Check::True, Type::Generic => Check::False, Type::Never => Check::False, - Type::Atom if attrs.all_atoms() => Check::True, - Type::Atom => Check::IsAtom, + Type::Any => Check::True, Type::Bytes if attrs.all_atoms() => Check::True, Type::Bytes => Check::IsAtom, Type::Int if attrs.all_atoms() => Check::True, diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index c08118d..1b09598 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -180,20 +180,54 @@ pub(crate) fn compare_type( let rest = compare_type(db, *lhs_rest, *rhs_rest, ctx); max(first, rest) } - (Type::Pair(..), _) | (_, Type::Pair(..)) => Comparison::Incompatible, - (Type::True, Type::True) => Comparison::Equal, - (Type::False, Type::False) => Comparison::Equal, - (Type::Atom, Type::Atom) => Comparison::Equal, + (Type::Pair(..), Type::Any) => Comparison::Assignable, + (Type::Pair(..), Type::Bytes) => Comparison::Incompatible, + (Type::Pair(..), Type::Bytes32) => Comparison::Incompatible, + (Type::Pair(..), Type::PublicKey) => Comparison::Incompatible, + (Type::Pair(..), Type::Int) => Comparison::Incompatible, + (Type::Pair(..), Type::Nil) => Comparison::Incompatible, + (Type::Pair(..), Type::True) => Comparison::Incompatible, + (Type::Pair(..), Type::False) => Comparison::Incompatible, + (Type::Pair(..), Type::Value(..)) => Comparison::Incompatible, + + (Type::Any, Type::Pair(..)) => Comparison::Superset, + (Type::Bytes, Type::Pair(..)) => Comparison::Incompatible, + (Type::Bytes32, Type::Pair(..)) => Comparison::Incompatible, + (Type::PublicKey, Type::Pair(..)) => Comparison::Incompatible, + (Type::Int, Type::Pair(..)) => Comparison::Incompatible, + (Type::Nil, Type::Pair(..)) => Comparison::Incompatible, + (Type::True, Type::Pair(..)) => Comparison::Incompatible, + (Type::False, Type::Pair(..)) => Comparison::Incompatible, + (Type::Value(..), Type::Pair(..)) => Comparison::Incompatible, + + (Type::Any, Type::Any) => Comparison::Equal, (Type::Bytes, Type::Bytes) => Comparison::Equal, (Type::Bytes32, Type::Bytes32) => Comparison::Equal, (Type::PublicKey, Type::PublicKey) => Comparison::Equal, (Type::Int, Type::Int) => Comparison::Equal, (Type::Nil, Type::Nil) => Comparison::Equal, + (Type::True, Type::True) => Comparison::Equal, + (Type::False, Type::False) => Comparison::Equal, + + (Type::Bytes32, Type::Any) => Comparison::Assignable, + (Type::PublicKey, Type::Any) => Comparison::Assignable, + (Type::Nil, Type::Any) => Comparison::Assignable, + (Type::Bytes, Type::Any) => Comparison::Assignable, + (Type::Int, Type::Any) => Comparison::Assignable, + (Type::True, Type::Any) => Comparison::Assignable, + (Type::False, Type::Any) => Comparison::Assignable, + (Type::Value(..), Type::Any) => Comparison::Assignable, + + (Type::Any, Type::Bytes) => Comparison::Superset, + (Type::Any, Type::Int) => Comparison::Superset, + (Type::Any, Type::Bytes32) => Comparison::Superset, + (Type::Any, Type::PublicKey) => Comparison::Superset, + (Type::Any, Type::Nil) => Comparison::Superset, + (Type::Any, Type::True) => Comparison::Superset, + (Type::Any, Type::False) => Comparison::Superset, + (Type::Any, Type::Value(..)) => Comparison::Superset, - (Type::Atom, Type::Bytes32) => Comparison::Superset, - (Type::Atom, Type::PublicKey) => Comparison::Superset, - (Type::Atom, Type::Nil) => Comparison::Superset, (Type::Bytes, Type::Bytes32) => Comparison::Superset, (Type::Bytes, Type::PublicKey) => Comparison::Superset, (Type::Bytes, Type::Nil) => Comparison::Superset, @@ -201,17 +235,9 @@ pub(crate) fn compare_type( (Type::Int, Type::PublicKey) => Comparison::Superset, (Type::Int, Type::Nil) => Comparison::Superset, - (Type::Bytes32, Type::Atom) => Comparison::Assignable, - (Type::PublicKey, Type::Atom) => Comparison::Assignable, - (Type::Nil, Type::Atom) => Comparison::Assignable, - (Type::Bytes, Type::Atom) => Comparison::Assignable, - (Type::Int, Type::Atom) => Comparison::Assignable, - (Type::Bytes32, Type::Bytes) => Comparison::Assignable, (Type::Nil, Type::Bytes) => Comparison::Assignable, - (Type::Atom, Type::Int) => Comparison::Castable, - (Type::Atom, Type::Bytes) => Comparison::Castable, (Type::Bytes, Type::Int) => Comparison::Castable, (Type::Bytes32, Type::Int) => Comparison::Castable, (Type::PublicKey, Type::Bytes) => Comparison::Castable, @@ -233,8 +259,6 @@ pub(crate) fn compare_type( (Type::False, Type::Bytes) => Comparison::Castable, (Type::True, Type::Int) => Comparison::Castable, (Type::False, Type::Int) => Comparison::Castable, - (Type::True, Type::Atom) => Comparison::Assignable, - (Type::False, Type::Atom) => Comparison::Assignable, (Type::True, Type::Nil) => Comparison::Incompatible, (Type::False, Type::Nil) => Comparison::Castable, (Type::True, Type::Bytes32) => Comparison::Incompatible, @@ -246,8 +270,6 @@ pub(crate) fn compare_type( (Type::Bytes, Type::False) => Comparison::Superset, (Type::Int, Type::True) => Comparison::Superset, (Type::Int, Type::False) => Comparison::Superset, - (Type::Atom, Type::True) => Comparison::Superset, - (Type::Atom, Type::False) => Comparison::Superset, (Type::Nil, Type::True) => Comparison::Incompatible, (Type::Nil, Type::False) => Comparison::Castable, (Type::Bytes32, Type::True) => Comparison::Incompatible, @@ -255,7 +277,6 @@ pub(crate) fn compare_type( (Type::PublicKey, Type::True) => Comparison::Incompatible, (Type::PublicKey, Type::False) => Comparison::Incompatible, - (Type::Value(..), Type::Atom) => Comparison::Assignable, (Type::Value(..), Type::Bytes) => Comparison::Castable, (Type::Value(..), Type::Int) => Comparison::Assignable, (Type::Value(value), Type::Bytes32) => { @@ -294,7 +315,6 @@ pub(crate) fn compare_type( } } - (Type::Atom, Type::Value(..)) => Comparison::Superset, (Type::Bytes, Type::Value(..)) => Comparison::Superset, (Type::Int, Type::Value(..)) => Comparison::Superset, (Type::Bytes32, Type::Value(value)) => { @@ -549,7 +569,7 @@ mod tests { let mut db = TypeSystem::new(); let types = db.std(); let lhs = db.alloc(Type::Pair(types.int, types.public_key)); - let rhs = db.alloc(Type::Pair(types.atom, types.atom)); + let rhs = db.alloc(Type::Pair(types.any, types.any)); assert_eq!(db.compare(lhs, rhs), Comparison::Assignable); } diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index 0f2f01a..b8a3aa5 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -27,7 +27,7 @@ pub(crate) fn difference_type( (Type::Never, _) => std.never, (_, Type::Never) => lhs, - (Type::Atom, Type::Atom) => std.never, + (Type::Any, Type::Any) => std.never, (Type::Bytes, Type::Bytes) => std.never, (Type::Bytes32, Type::Bytes32) => std.never, (Type::PublicKey, Type::PublicKey) => std.never, @@ -39,7 +39,7 @@ pub(crate) fn difference_type( (Type::Int, Type::Bytes32) => lhs, (Type::Int, Type::PublicKey) => lhs, (Type::Int, Type::Bytes) => std.never, - (Type::Int, Type::Atom) => std.never, + (Type::Int, Type::Any) => std.never, (Type::Int, Type::Nil) => lhs, (Type::Int, Type::True) => lhs, (Type::Int, Type::False) => lhs, @@ -48,25 +48,25 @@ pub(crate) fn difference_type( (Type::Bytes, Type::Bytes32) => lhs, (Type::Bytes, Type::PublicKey) => lhs, (Type::Bytes, Type::Int) => std.never, - (Type::Bytes, Type::Atom) => std.never, + (Type::Bytes, Type::Any) => std.never, (Type::Bytes, Type::Nil) => lhs, (Type::Bytes, Type::True) => lhs, (Type::Bytes, Type::False) => lhs, (Type::Bytes, Type::Value(..)) => lhs, - (Type::Atom, Type::Bytes32) => lhs, - (Type::Atom, Type::PublicKey) => lhs, - (Type::Atom, Type::Int) => std.never, - (Type::Atom, Type::Bytes) => std.never, - (Type::Atom, Type::Nil) => lhs, - (Type::Atom, Type::True) => lhs, - (Type::Atom, Type::False) => lhs, - (Type::Atom, Type::Value(..)) => lhs, + (Type::Any, Type::Bytes32) => lhs, + (Type::Any, Type::PublicKey) => lhs, + (Type::Any, Type::Int) => lhs, + (Type::Any, Type::Bytes) => lhs, + (Type::Any, Type::Nil) => lhs, + (Type::Any, Type::True) => lhs, + (Type::Any, Type::False) => lhs, + (Type::Any, Type::Value(..)) => lhs, (Type::Bytes32, Type::PublicKey) => lhs, (Type::Bytes32, Type::Bytes) => std.never, (Type::Bytes32, Type::Int) => std.never, - (Type::Bytes32, Type::Atom) => std.never, + (Type::Bytes32, Type::Any) => std.never, (Type::Bytes32, Type::Nil) => lhs, (Type::Bytes32, Type::True) => lhs, (Type::Bytes32, Type::False) => lhs, @@ -75,7 +75,7 @@ pub(crate) fn difference_type( (Type::PublicKey, Type::Bytes32) => lhs, (Type::PublicKey, Type::Bytes) => std.never, (Type::PublicKey, Type::Int) => std.never, - (Type::PublicKey, Type::Atom) => std.never, + (Type::PublicKey, Type::Any) => std.never, (Type::PublicKey, Type::Nil) => lhs, (Type::PublicKey, Type::True) => lhs, (Type::PublicKey, Type::False) => lhs, @@ -84,12 +84,12 @@ pub(crate) fn difference_type( (Type::Nil, Type::Bytes32) => lhs, (Type::Nil, Type::PublicKey) => lhs, (Type::Nil, Type::Bytes) => std.never, - (Type::Nil, Type::Atom) => std.never, + (Type::Nil, Type::Any) => std.never, (Type::Nil, Type::Int) => std.never, (Type::Nil, Type::True) => lhs, (Type::Nil, Type::False) => std.never, - (Type::True, Type::Atom) => std.never, + (Type::True, Type::Any) => std.never, (Type::True, Type::Bytes) => std.never, (Type::True, Type::Bytes32) => lhs, (Type::True, Type::PublicKey) => lhs, @@ -97,7 +97,7 @@ pub(crate) fn difference_type( (Type::True, Type::Nil) => lhs, (Type::True, Type::False) => lhs, - (Type::False, Type::Atom) => std.never, + (Type::False, Type::Any) => std.never, (Type::False, Type::Bytes) => std.never, (Type::False, Type::Bytes32) => lhs, (Type::False, Type::PublicKey) => lhs, @@ -127,7 +127,7 @@ pub(crate) fn difference_type( } } - (Type::Value(..), Type::Atom) => std.never, + (Type::Value(..), Type::Any) => std.never, (Type::Value(..), Type::Bytes) => std.never, (Type::Value(..), Type::Int) => std.never, @@ -175,7 +175,7 @@ pub(crate) fn difference_type( } } - (Type::Atom, Type::Pair(..)) => lhs, + (Type::Any, Type::Pair(..)) => lhs, (Type::Bytes, Type::Pair(..)) => lhs, (Type::Bytes32, Type::Pair(..)) => lhs, (Type::PublicKey, Type::Pair(..)) => lhs, @@ -185,7 +185,7 @@ pub(crate) fn difference_type( (Type::False, Type::Pair(..)) => lhs, (Type::Value(..), Type::Pair(..)) => lhs, - (Type::Pair(..), Type::Atom) => lhs, + (Type::Pair(..), Type::Any) => std.never, (Type::Pair(..), Type::Bytes) => lhs, (Type::Pair(..), Type::Bytes32) => lhs, (Type::Pair(..), Type::PublicKey) => lhs, diff --git a/crates/rue-typing/src/standard_types.rs b/crates/rue-typing/src/standard_types.rs index 6d3b5b3..41c069f 100644 --- a/crates/rue-typing/src/standard_types.rs +++ b/crates/rue-typing/src/standard_types.rs @@ -7,7 +7,6 @@ pub struct StandardTypes { pub any: TypeId, pub unmapped_list: TypeId, pub generic_list_item: TypeId, - pub atom: TypeId, pub bytes: TypeId, pub bytes32: TypeId, pub public_key: TypeId, diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs index f3429dd..052c5b6 100644 --- a/crates/rue-typing/src/stringify.rs +++ b/crates/rue-typing/src/stringify.rs @@ -21,7 +21,7 @@ pub(crate) fn stringify_type( Type::Unknown => "{unknown}".to_string(), Type::Generic => "{generic}".to_string(), Type::Never => "Never".to_string(), - Type::Atom => "Atom".to_string(), + Type::Any => "Any".to_string(), Type::Bytes => "Bytes".to_string(), Type::Bytes32 => "Bytes32".to_string(), Type::PublicKey => "PublicKey".to_string(), diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index c0a139e..01118e9 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -26,7 +26,7 @@ pub(crate) fn substitute_type( Type::Unknown => type_id, Type::Generic => type_id, Type::Never => type_id, - Type::Atom => type_id, + Type::Any => type_id, Type::Bytes => type_id, Type::Bytes32 => type_id, Type::PublicKey => type_id, diff --git a/crates/rue-typing/src/ty.rs b/crates/rue-typing/src/ty.rs index 74899b4..bbcd043 100644 --- a/crates/rue-typing/src/ty.rs +++ b/crates/rue-typing/src/ty.rs @@ -7,7 +7,7 @@ pub enum Type { Unknown, Generic, Never, - Atom, + Any, Bytes, Bytes32, PublicKey, diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index 260ab98..b4fa0cf 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -23,7 +23,7 @@ impl Default for TypeSystem { let unknown = arena.alloc(Type::Unknown); let never = arena.alloc(Type::Never); - let atom = arena.alloc(Type::Atom); + let any = arena.alloc(Type::Any); let bytes = arena.alloc(Type::Bytes); let bytes32 = arena.alloc(Type::Bytes32); let public_key = arena.alloc(Type::PublicKey); @@ -31,13 +31,8 @@ impl Default for TypeSystem { let true_bool = arena.alloc(Type::True); let false_bool = arena.alloc(Type::False); let nil = arena.alloc(Type::Nil); - let bool = arena.alloc(Type::Union(vec![false_bool, true_bool])); - let any = arena.alloc(Type::Unknown); - let pair = arena.alloc(Type::Pair(any, any)); - arena[any] = Type::Union(vec![atom, pair]); - let generic_list_item = arena.alloc(Type::Generic); let inner = arena.alloc(Type::Unknown); let unmapped_list = arena.alloc(Type::Alias(Alias { @@ -50,7 +45,7 @@ impl Default for TypeSystem { let mut names = HashMap::new(); names.insert(never, "Never".to_string()); - names.insert(atom, "Atom".to_string()); + names.insert(any, "Any".to_string()); names.insert(bytes, "Bytes".to_string()); names.insert(bytes32, "Bytes32".to_string()); names.insert(public_key, "PublicKey".to_string()); @@ -59,7 +54,6 @@ impl Default for TypeSystem { names.insert(true_bool, "True".to_string()); names.insert(false_bool, "False".to_string()); names.insert(nil, "Nil".to_string()); - names.insert(any, "Any".to_string()); names.insert(unmapped_list, "List".to_string()); Self { @@ -70,7 +64,6 @@ impl Default for TypeSystem { any, unmapped_list, generic_list_item, - atom, bytes, bytes32, public_key, From dd9dd8cffda11e34fcfa23c85dd3b5608badf094 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 25 Jul 2024 13:23:06 -0400 Subject: [PATCH 070/100] Refactor comparison --- Cargo.toml | 1 - crates/rue-typing/src/comparison.rs | 489 +++++++++++++---------- crates/rue-typing/src/difference.rs | 16 +- crates/rue-typing/src/replace_type.rs | 4 +- crates/rue-typing/src/semantic_types.rs | 21 +- crates/rue-typing/src/substitute_type.rs | 12 +- crates/rue-typing/src/test_tools.rs | 20 +- crates/rue-typing/src/type_system.rs | 7 +- 8 files changed, 315 insertions(+), 255 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b6da478..08247ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,6 @@ module_name_repetitions = "allow" multiple_crate_versions = "allow" must_use_candidate = "allow" too_many_lines = "allow" -match_same_arms = "allow" [workspace.dependencies] rue-parser = { path = "./crates/rue-parser", version = "0.1.1" } diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index 1b09598..5d787a0 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -41,13 +41,231 @@ pub(crate) fn compare_type( let comparison = match (db.get(lhs), db.get(rhs)) { (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), + // These types are identical. + (Type::Unknown, Type::Unknown) + | (Type::Never, Type::Never) + | (Type::Any, Type::Any) + | (Type::Bytes, Type::Bytes) + | (Type::Bytes32, Type::Bytes32) + | (Type::PublicKey, Type::PublicKey) + | (Type::Int, Type::Int) + | (Type::Nil, Type::Nil) + | (Type::True, Type::True) + | (Type::False, Type::False) => Comparison::Equal, + + // These are assignable since the structure and semantics match. + ( + Type::Pair(..) + | Type::Bytes + | Type::Bytes32 + | Type::PublicKey + | Type::Int + | Type::Nil + | Type::True + | Type::False + | Type::Value(..), + Type::Any, + ) + | ( + Type::Unknown | Type::Never, + Type::Any + | Type::Bytes + | Type::Bytes32 + | Type::PublicKey + | Type::Int + | Type::Nil + | Type::True + | Type::False + | Type::Value(..) + | Type::Pair(..) + | Type::Callable(..), + ) + | ( + Type::Never + | Type::Any + | Type::Bytes + | Type::Bytes32 + | Type::PublicKey + | Type::Int + | Type::Nil + | Type::True + | Type::False + | Type::Value(..) + | Type::Pair(..) + | Type::Callable(..), + Type::Unknown, + ) + | (Type::Unknown, Type::Never) + | (Type::Value(..), Type::Int) + | (Type::Bytes32 | Type::Nil, Type::Bytes) => Comparison::Assignable, + + // These are castable since the structure matches but the semantics differ. + ( + Type::Bytes | Type::Bytes32 | Type::PublicKey | Type::Nil | Type::True | Type::False, + Type::Int, + ) + | (Type::PublicKey | Type::Int | Type::True | Type::False | Type::Value(..), Type::Bytes) + | (Type::False, Type::Nil) + | (Type::Nil, Type::False) => Comparison::Castable, + + // These are a superset since the right hand side is castable to the left hand side. + ( + Type::Any, + Type::Bytes + | Type::Bytes32 + | Type::PublicKey + | Type::Int + | Type::Nil + | Type::True + | Type::False + | Type::Value(..) + | Type::Pair(..), + ) + | ( + Type::Bytes | Type::Int, + Type::Bytes32 + | Type::PublicKey + | Type::Nil + | Type::True + | Type::False + | Type::Value(..), + ) + | ( + Type::Any + | Type::Bytes + | Type::Bytes32 + | Type::PublicKey + | Type::Int + | Type::Nil + | Type::True + | Type::False + | Type::Value(..) + | Type::Pair(..) + | Type::Callable(..), + Type::Never, + ) => Comparison::Superset, + + // These are incompatible since the structure differs. + ( + Type::Pair(..), + Type::Bytes + | Type::Bytes32 + | Type::PublicKey + | Type::Int + | Type::Nil + | Type::True + | Type::False + | Type::Value(..), + ) + | ( + Type::Bytes + | Type::Bytes32 + | Type::PublicKey + | Type::Int + | Type::Nil + | Type::True + | Type::False + | Type::Value(..), + Type::Pair(..), + ) + | (Type::Bytes32, Type::PublicKey | Type::Nil | Type::True | Type::False) + | (Type::PublicKey, Type::Bytes32 | Type::Nil | Type::True | Type::False) + | (Type::Nil, Type::Bytes32 | Type::PublicKey | Type::True) + | (Type::True, Type::False | Type::Nil) + | (Type::False, Type::True) + | (Type::True | Type::False, Type::Bytes32 | Type::PublicKey) + | ( + Type::Any + | Type::Bytes + | Type::Bytes32 + | Type::PublicKey + | Type::Int + | Type::Nil + | Type::True + | Type::False + | Type::Value(..) + | Type::Pair(..), + Type::Callable(..), + ) => Comparison::Incompatible, + + // Value is a subtype of Int, so it's castable to Bytes32 if it's 32 bytes long. + (Type::Value(value), Type::Bytes32) => { + if bigint_to_bytes(value.clone()).len() == 32 { + Comparison::Castable + } else { + Comparison::Incompatible + } + } + + // Value is a subtype of Int, so it's castable to PublicKey if it's 48 bytes long. + (Type::Value(value), Type::PublicKey) => { + if bigint_to_bytes(value.clone()).len() == 48 { + Comparison::Castable + } else { + Comparison::Incompatible + } + } + + // Bytes32 is a superset of Value only if the value is 32 bytes long. + (Type::Bytes32, Type::Value(value)) => { + if bigint_to_bytes(value.clone()).len() == 32 { + Comparison::Superset + } else { + Comparison::Incompatible + } + } + + // PublicKey is a superset of Value only if the value is 48 bytes long. + (Type::PublicKey, Type::Value(value)) => { + if bigint_to_bytes(value.clone()).len() == 48 { + Comparison::Superset + } else { + Comparison::Incompatible + } + } + + // Nil and False are supersets of Value only if the value is zero. + (Type::Nil | Type::False, Type::Value(value)) + | (Type::Value(value), Type::Nil | Type::False) => { + if value == &BigInt::ZERO { + Comparison::Castable + } else { + Comparison::Incompatible + } + } + + // True is a superset of Value only if the value is one. + (Type::True, Type::Value(value)) | (Type::Value(value), Type::True) => { + if value == &BigInt::one() { + Comparison::Castable + } else { + Comparison::Incompatible + } + } + + // Value is equal to other instances of Value only if the values are equal. + (Type::Value(lhs), Type::Value(rhs)) => { + if lhs == rhs { + Comparison::Equal + } else { + Comparison::Incompatible + } + } + + // A comparison of pairs is done by using whichever comparison is the most restrictive. + (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { + let first = compare_type(db, *lhs_first, *rhs_first, ctx); + let rest = compare_type(db, *lhs_rest, *rhs_rest, ctx); + max(first, rest) + } + + // We need to push substititons onto the stack in order to accurately compare them. (Type::Lazy(lazy), _) => { ctx.substitution_stack.push(lazy.substitutions.clone()); let result = compare_type(db, lazy.type_id, rhs, ctx); ctx.substitution_stack.pop().unwrap(); result } - (_, Type::Lazy(lazy)) => { ctx.substitution_stack.push(lazy.substitutions.clone()); let result = compare_type(db, lhs, lazy.type_id, ctx); @@ -55,9 +273,14 @@ pub(crate) fn compare_type( result } + // Resolve the alias to the type that it's pointing to. (Type::Alias(alias), _) => compare_type(db, alias.type_id, rhs, ctx), (_, Type::Alias(alias)) => compare_type(db, lhs, alias.type_id, ctx), + // Structs are at best castable to other types, since they have different semantics. + (Type::Struct(lhs), Type::Struct(rhs)) if lhs.original_type_id == rhs.original_type_id => { + compare_type(db, lhs.type_id, rhs.type_id, ctx) + } (Type::Struct(lhs), _) => max( compare_type(db, lhs.type_id, rhs, ctx), Comparison::Castable, @@ -67,19 +290,30 @@ pub(crate) fn compare_type( Comparison::Castable, ), + // Variants can be assigned to enums if the structure is assignable and it's the same enum. (Type::Variant(variant), Type::Enum(ty)) => { let comparison = compare_type(db, variant.type_id, ty.type_id, ctx); - if variant.enum_type == rhs { + if variant.original_enum_type_id == ty.original_type_id { max(comparison, Comparison::Assignable) } else { max(comparison, Comparison::Castable) } } + // Enums can be assigned if the structure is assignable and it's the same enum. + (Type::Enum(lhs), Type::Enum(rhs)) if lhs.original_type_id == rhs.original_type_id => { + compare_type(db, lhs.type_id, rhs.type_id, ctx) + } (Type::Enum(ty), _) => max(compare_type(db, ty.type_id, rhs, ctx), Comparison::Castable), (_, Type::Enum(ty)) => max(compare_type(db, lhs, ty.type_id, ctx), Comparison::Castable), + // Variants can be assigned if the structure is assignable and it's the same variant. + (Type::Variant(lhs), Type::Variant(rhs)) + if lhs.original_type_id == rhs.original_type_id => + { + compare_type(db, lhs.type_id, rhs.type_id, ctx) + } (Type::Variant(lhs), _) => max( compare_type(db, lhs.type_id, rhs, ctx), Comparison::Castable, @@ -89,58 +323,15 @@ pub(crate) fn compare_type( Comparison::Castable, ), + // Functions can be assigned to other functions if the parameters and return type are assignable. + // They're treated like Never on the right hand side and Any on the left hand side. (Type::Callable(lhs), Type::Callable(rhs)) => max( compare_type(db, lhs.parameters, rhs.parameters, ctx), compare_type(db, lhs.return_type, rhs.return_type, ctx), ), - (Type::Callable(..), _) => compare_type(db, lhs, db.std().any, ctx), - (_, Type::Callable(..)) => Comparison::Incompatible, - - (_, Type::Generic) => { - let mut found = None; - - for substititons in ctx.substitution_stack.iter().rev() { - if let Some(&substititon) = substititons.get(&rhs) { - found = Some(substititon); - } - } - - if let Some(found) = found { - compare_type(db, lhs, found, ctx) - } else if let Some(generic_stack_frame) = ctx.generic_stack_frame { - ctx.substitution_stack[generic_stack_frame].insert(rhs, lhs); - Comparison::Assignable - } else { - Comparison::Incompatible - } - } - - (Type::Generic, _) => { - let mut found = None; - - for (i, substititons) in ctx.substitution_stack.iter().enumerate().rev() { - if i < ctx.initial_substitution_length { - break; - } - - if let Some(&substititon) = substititons.get(&lhs) { - found = Some(substititon); - } - } - - if let Some(found) = found { - compare_type(db, found, rhs, ctx) - } else { - Comparison::Incompatible - } - } - - (Type::Unknown, _) | (_, Type::Unknown) => Comparison::Assignable, - - (Type::Never, _) => Comparison::Assignable, - (_, Type::Never) => Comparison::Superset, + // Unions can be assigned to anything so long as each of the items in the union are also. (Type::Union(items), _) => { let items = items.clone(); let mut result = Comparison::Assignable; @@ -163,6 +354,7 @@ pub(crate) fn compare_type( } } + // Anything can be assigned to a union so long as it's assignable to at least one of the items. (_, Type::Union(items)) => { let items = items.clone(); let mut result = Comparison::Incompatible; @@ -175,186 +367,43 @@ pub(crate) fn compare_type( max(result, Comparison::Assignable) } - (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { - let first = compare_type(db, *lhs_first, *rhs_first, ctx); - let rest = compare_type(db, *lhs_rest, *rhs_rest, ctx); - max(first, rest) - } + // Generics are resolved by looking up the substitution in the stack. + // If we're infering, we'll push the substitution onto the proper generic stack frame. + (_, Type::Generic) => { + let mut found = None; - (Type::Pair(..), Type::Any) => Comparison::Assignable, - (Type::Pair(..), Type::Bytes) => Comparison::Incompatible, - (Type::Pair(..), Type::Bytes32) => Comparison::Incompatible, - (Type::Pair(..), Type::PublicKey) => Comparison::Incompatible, - (Type::Pair(..), Type::Int) => Comparison::Incompatible, - (Type::Pair(..), Type::Nil) => Comparison::Incompatible, - (Type::Pair(..), Type::True) => Comparison::Incompatible, - (Type::Pair(..), Type::False) => Comparison::Incompatible, - (Type::Pair(..), Type::Value(..)) => Comparison::Incompatible, - - (Type::Any, Type::Pair(..)) => Comparison::Superset, - (Type::Bytes, Type::Pair(..)) => Comparison::Incompatible, - (Type::Bytes32, Type::Pair(..)) => Comparison::Incompatible, - (Type::PublicKey, Type::Pair(..)) => Comparison::Incompatible, - (Type::Int, Type::Pair(..)) => Comparison::Incompatible, - (Type::Nil, Type::Pair(..)) => Comparison::Incompatible, - (Type::True, Type::Pair(..)) => Comparison::Incompatible, - (Type::False, Type::Pair(..)) => Comparison::Incompatible, - (Type::Value(..), Type::Pair(..)) => Comparison::Incompatible, - - (Type::Any, Type::Any) => Comparison::Equal, - (Type::Bytes, Type::Bytes) => Comparison::Equal, - (Type::Bytes32, Type::Bytes32) => Comparison::Equal, - (Type::PublicKey, Type::PublicKey) => Comparison::Equal, - (Type::Int, Type::Int) => Comparison::Equal, - (Type::Nil, Type::Nil) => Comparison::Equal, - (Type::True, Type::True) => Comparison::Equal, - (Type::False, Type::False) => Comparison::Equal, - - (Type::Bytes32, Type::Any) => Comparison::Assignable, - (Type::PublicKey, Type::Any) => Comparison::Assignable, - (Type::Nil, Type::Any) => Comparison::Assignable, - (Type::Bytes, Type::Any) => Comparison::Assignable, - (Type::Int, Type::Any) => Comparison::Assignable, - (Type::True, Type::Any) => Comparison::Assignable, - (Type::False, Type::Any) => Comparison::Assignable, - (Type::Value(..), Type::Any) => Comparison::Assignable, - - (Type::Any, Type::Bytes) => Comparison::Superset, - (Type::Any, Type::Int) => Comparison::Superset, - (Type::Any, Type::Bytes32) => Comparison::Superset, - (Type::Any, Type::PublicKey) => Comparison::Superset, - (Type::Any, Type::Nil) => Comparison::Superset, - (Type::Any, Type::True) => Comparison::Superset, - (Type::Any, Type::False) => Comparison::Superset, - (Type::Any, Type::Value(..)) => Comparison::Superset, - - (Type::Bytes, Type::Bytes32) => Comparison::Superset, - (Type::Bytes, Type::PublicKey) => Comparison::Superset, - (Type::Bytes, Type::Nil) => Comparison::Superset, - (Type::Int, Type::Bytes32) => Comparison::Superset, - (Type::Int, Type::PublicKey) => Comparison::Superset, - (Type::Int, Type::Nil) => Comparison::Superset, - - (Type::Bytes32, Type::Bytes) => Comparison::Assignable, - (Type::Nil, Type::Bytes) => Comparison::Assignable, - - (Type::Bytes, Type::Int) => Comparison::Castable, - (Type::Bytes32, Type::Int) => Comparison::Castable, - (Type::PublicKey, Type::Bytes) => Comparison::Castable, - (Type::PublicKey, Type::Int) => Comparison::Castable, - (Type::Int, Type::Bytes) => Comparison::Castable, - (Type::Nil, Type::Int) => Comparison::Castable, - - (Type::Bytes32, Type::PublicKey) => Comparison::Incompatible, - (Type::Bytes32, Type::Nil) => Comparison::Incompatible, - (Type::PublicKey, Type::Bytes32) => Comparison::Incompatible, - (Type::PublicKey, Type::Nil) => Comparison::Incompatible, - (Type::Nil, Type::Bytes32) => Comparison::Incompatible, - (Type::Nil, Type::PublicKey) => Comparison::Incompatible, - - (Type::True, Type::False) => Comparison::Incompatible, - (Type::False, Type::True) => Comparison::Incompatible, - - (Type::True, Type::Bytes) => Comparison::Castable, - (Type::False, Type::Bytes) => Comparison::Castable, - (Type::True, Type::Int) => Comparison::Castable, - (Type::False, Type::Int) => Comparison::Castable, - (Type::True, Type::Nil) => Comparison::Incompatible, - (Type::False, Type::Nil) => Comparison::Castable, - (Type::True, Type::Bytes32) => Comparison::Incompatible, - (Type::False, Type::Bytes32) => Comparison::Incompatible, - (Type::True, Type::PublicKey) => Comparison::Incompatible, - (Type::False, Type::PublicKey) => Comparison::Incompatible, - - (Type::Bytes, Type::True) => Comparison::Superset, - (Type::Bytes, Type::False) => Comparison::Superset, - (Type::Int, Type::True) => Comparison::Superset, - (Type::Int, Type::False) => Comparison::Superset, - (Type::Nil, Type::True) => Comparison::Incompatible, - (Type::Nil, Type::False) => Comparison::Castable, - (Type::Bytes32, Type::True) => Comparison::Incompatible, - (Type::Bytes32, Type::False) => Comparison::Incompatible, - (Type::PublicKey, Type::True) => Comparison::Incompatible, - (Type::PublicKey, Type::False) => Comparison::Incompatible, - - (Type::Value(..), Type::Bytes) => Comparison::Castable, - (Type::Value(..), Type::Int) => Comparison::Assignable, - (Type::Value(value), Type::Bytes32) => { - if bigint_to_bytes(value.clone()).len() == 32 { - Comparison::Castable - } else { - Comparison::Incompatible - } - } - (Type::Value(value), Type::PublicKey) => { - if bigint_to_bytes(value.clone()).len() == 48 { - Comparison::Castable - } else { - Comparison::Incompatible - } - } - (Type::Value(value), Type::Nil) => { - if value == &BigInt::ZERO { - Comparison::Castable - } else { - Comparison::Incompatible - } - } - (Type::Value(value), Type::True) => { - if value == &BigInt::one() { - Comparison::Castable - } else { - Comparison::Incompatible - } - } - (Type::Value(value), Type::False) => { - if value == &BigInt::ZERO { - Comparison::Castable - } else { - Comparison::Incompatible + for substititons in ctx.substitution_stack.iter().rev() { + if let Some(&substititon) = substititons.get(&rhs) { + found = Some(substititon); + } } - } - (Type::Bytes, Type::Value(..)) => Comparison::Superset, - (Type::Int, Type::Value(..)) => Comparison::Superset, - (Type::Bytes32, Type::Value(value)) => { - if bigint_to_bytes(value.clone()).len() == 32 { - Comparison::Superset - } else { - Comparison::Incompatible - } - } - (Type::PublicKey, Type::Value(value)) => { - if bigint_to_bytes(value.clone()).len() == 48 { - Comparison::Superset - } else { - Comparison::Incompatible - } - } - (Type::Nil, Type::Value(value)) => { - if value == &BigInt::ZERO { - Comparison::Castable - } else { - Comparison::Incompatible - } - } - (Type::True, Type::Value(value)) => { - if value == &BigInt::one() { - Comparison::Castable + if let Some(found) = found { + compare_type(db, lhs, found, ctx) + } else if let Some(generic_stack_frame) = ctx.generic_stack_frame { + ctx.substitution_stack[generic_stack_frame].insert(rhs, lhs); + Comparison::Assignable } else { Comparison::Incompatible } } - (Type::False, Type::Value(value)) => { - if value == &BigInt::ZERO { - Comparison::Castable - } else { - Comparison::Incompatible + + // Generics are resolved by looking up the substitution in the stack. + (Type::Generic, _) => { + let mut found = None; + + for (i, substititons) in ctx.substitution_stack.iter().enumerate().rev() { + if i < ctx.initial_substitution_length { + break; + } + + if let Some(&substititon) = substititons.get(&lhs) { + found = Some(substititon); + } } - } - (Type::Value(lhs), Type::Value(rhs)) => { - if lhs == rhs { - Comparison::Equal + + if let Some(found) = found { + compare_type(db, found, rhs, ctx) } else { Comparison::Incompatible } diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index b8a3aa5..832d659 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -252,7 +252,7 @@ pub(crate) fn difference_type( 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)), + original_type_id: ty.original_type_id, type_id, field_names: ty.field_names, rest: ty.rest, @@ -264,7 +264,7 @@ pub(crate) fn difference_type( 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)), + original_type_id: ty.original_type_id, type_id, field_names: ty.field_names, rest: ty.rest, @@ -277,7 +277,7 @@ pub(crate) fn difference_type( 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)), + original_type_id: ty.original_type_id, type_id, has_fields: ty.has_fields, variants: ty.variants, @@ -288,7 +288,7 @@ pub(crate) fn difference_type( 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)), + original_type_id: ty.original_type_id, type_id, has_fields: ty.has_fields, variants: ty.variants, @@ -300,8 +300,8 @@ pub(crate) fn difference_type( 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)), - enum_type: variant.enum_type, + original_type_id: variant.original_type_id, + original_enum_type_id: variant.original_enum_type_id, field_names: variant.field_names, type_id, rest: variant.rest, @@ -314,8 +314,8 @@ pub(crate) fn difference_type( 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)), - enum_type: variant.enum_type, + original_type_id: variant.original_type_id, + original_enum_type_id: variant.original_enum_type_id, field_names: variant.field_names, type_id, rest: variant.rest, diff --git a/crates/rue-typing/src/replace_type.rs b/crates/rue-typing/src/replace_type.rs index cfb978a..a138614 100644 --- a/crates/rue-typing/src/replace_type.rs +++ b/crates/rue-typing/src/replace_type.rs @@ -19,7 +19,7 @@ pub(crate) fn replace_type( let alias = alias.clone(); let new_type_id = replace_type(types, alias.type_id, replace_type_id, path); types.alloc(Type::Alias(Alias { - original_type_id: Some(alias.original_type_id.unwrap_or(type_id)), + original_type_id: alias.original_type_id, type_id: new_type_id, generic_types: alias.generic_types, })) @@ -28,7 +28,7 @@ pub(crate) fn replace_type( let ty = ty.clone(); let new_type_id = replace_type(types, ty.type_id, replace_type_id, path); types.alloc(Type::Struct(Struct { - original_type_id: Some(ty.original_type_id.unwrap_or(type_id)), + original_type_id: ty.original_type_id, type_id: new_type_id, field_names: ty.field_names, rest: ty.rest, diff --git a/crates/rue-typing/src/semantic_types.rs b/crates/rue-typing/src/semantic_types.rs index 3884aec..931dc3a 100644 --- a/crates/rue-typing/src/semantic_types.rs +++ b/crates/rue-typing/src/semantic_types.rs @@ -30,7 +30,8 @@ pub struct Lazy { /// Represents an alias to a type with a set of generic parameters that must be mapped prior to use. #[derive(Debug, Clone)] pub struct Alias { - pub original_type_id: Option, + /// A pointer to the alias from which this was derived. + pub original_type_id: TypeId, pub type_id: TypeId, pub generic_types: Vec, } @@ -38,7 +39,8 @@ pub struct Alias { /// Struct types are just wrappers around a structural type that provide field information. #[derive(Debug, Clone)] pub struct Struct { - pub original_type_id: Option, + /// A pointer to the struct from which this was derived. + pub original_type_id: TypeId, pub field_names: IndexSet, pub type_id: TypeId, pub rest: Rest, @@ -48,7 +50,8 @@ pub struct Struct { /// Represents something which can be called with arguments and returns a given type. #[derive(Debug, Clone)] pub struct Callable { - pub original_type_id: Option, + /// A pointer to the callable from which this was derived. + pub original_type_id: TypeId, pub parameter_names: IndexSet, pub parameters: TypeId, pub return_type: TypeId, @@ -59,8 +62,8 @@ pub struct Callable { /// Represents an enum type which can have multiple variants. #[derive(Debug, Clone)] pub struct Enum { - /// A pointer to the enum from which this was derived, if any. - pub original_type_id: Option, + /// A pointer to the enum from which this was derived. + pub original_type_id: TypeId, /// The structural type of the enum. pub type_id: TypeId, /// Whether the enum semantically has fields. @@ -72,10 +75,10 @@ pub struct Enum { /// Represents a variant type which can optionally have fields. #[derive(Debug, Clone)] pub struct Variant { - /// A pointer to the variant from which this was derived, if any. - pub original_type_id: Option, - /// The enum type to which this variant belongs. - pub enum_type: TypeId, + /// A pointer to the variant from which this was derived. + pub original_type_id: TypeId, + /// The original enum type to which this variant belongs. + pub original_enum_type_id: TypeId, /// The field names of the variant. pub field_names: Option>, /// The structural type of the enum variant. diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index 01118e9..0e9d5e4 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -83,7 +83,7 @@ pub(crate) fn substitute_type( type_id } else { types.alloc(Type::Alias(Alias { - original_type_id: Some(alias.original_type_id.unwrap_or(type_id)), + original_type_id: alias.original_type_id, type_id: new_type_id, generic_types: alias.generic_types, })) @@ -102,7 +102,7 @@ pub(crate) fn substitute_type( type_id } else { types.alloc(Type::Struct(Struct { - original_type_id: Some(ty.original_type_id.unwrap_or(type_id)), + original_type_id: ty.original_type_id, type_id: new_type_id, field_names: ty.field_names, rest: ty.rest, @@ -123,8 +123,8 @@ pub(crate) fn substitute_type( type_id } else { types.alloc(Type::Variant(Variant { - original_type_id: Some(ty.original_type_id.unwrap_or(type_id)), - enum_type: ty.enum_type, + original_type_id: ty.original_type_id, + original_enum_type_id: ty.original_enum_type_id, type_id: new_type_id, field_names: ty.field_names, rest: ty.rest, @@ -146,7 +146,7 @@ pub(crate) fn substitute_type( type_id } else { types.alloc(Type::Enum(Enum { - original_type_id: Some(ty.original_type_id.unwrap_or(type_id)), + original_type_id: ty.original_type_id, type_id: new_type_id, has_fields: ty.has_fields, variants: ty.variants, @@ -173,7 +173,7 @@ pub(crate) fn substitute_type( type_id } else { types.alloc(Type::Callable(Callable { - original_type_id: Some(callable.original_type_id.unwrap_or(type_id)), + original_type_id: callable.original_type_id, parameter_names: callable.parameter_names, parameters: new_parameters, return_type: new_return_type, diff --git a/crates/rue-typing/src/test_tools.rs b/crates/rue-typing/src/test_tools.rs index 98e3d0b..a9bd806 100644 --- a/crates/rue-typing/src/test_tools.rs +++ b/crates/rue-typing/src/test_tools.rs @@ -35,14 +35,18 @@ pub fn alloc_callable( } }; - db.alloc(Type::Callable(Callable { - original_type_id: None, + let type_id = db.alloc(Type::Unknown); + + *db.get_mut(type_id) = Type::Callable(Callable { + original_type_id: type_id, parameter_names: parameters.keys().cloned().collect(), parameters: structure, return_type, rest, generic_types: Vec::new(), - })) + }); + + type_id } pub fn alloc_struct(db: &mut TypeSystem, fields: &IndexMap, rest: Rest) -> TypeId { @@ -66,13 +70,17 @@ pub fn alloc_struct(db: &mut TypeSystem, fields: &IndexMap, rest } }; - db.alloc(Type::Struct(Struct { - original_type_id: None, + let type_id = db.alloc(Type::Unknown); + + *db.get_mut(type_id) = Type::Struct(Struct { + original_type_id: type_id, type_id: structure, field_names: fields.keys().cloned().collect(), rest, generic_types: Vec::new(), - })) + }); + + type_id } pub fn alloc_list_of( diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index b4fa0cf..f22a501 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -35,11 +35,12 @@ impl Default for TypeSystem { let generic_list_item = arena.alloc(Type::Generic); let inner = arena.alloc(Type::Unknown); - let unmapped_list = arena.alloc(Type::Alias(Alias { - original_type_id: None, + let unmapped_list = arena.alloc(Type::Unknown); + arena[unmapped_list] = Type::Alias(Alias { + original_type_id: unmapped_list, type_id: inner, generic_types: vec![generic_list_item], - })); + }); let pair = arena.alloc(Type::Pair(generic_list_item, unmapped_list)); arena[inner] = Type::Union(vec![pair, nil]); From 8e11053e5d5cc96a2d96bc7434e5fce37e77a77a Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 25 Jul 2024 13:26:02 -0400 Subject: [PATCH 071/100] Bug fix --- crates/rue-typing/src/comparison.rs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index 5d787a0..d1fb179 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -54,22 +54,10 @@ pub(crate) fn compare_type( | (Type::False, Type::False) => Comparison::Equal, // These are assignable since the structure and semantics match. - ( - Type::Pair(..) - | Type::Bytes - | Type::Bytes32 - | Type::PublicKey - | Type::Int - | Type::Nil - | Type::True - | Type::False - | Type::Value(..), - Type::Any, - ) + (_, Type::Any) | ( Type::Unknown | Type::Never, - Type::Any - | Type::Bytes + Type::Bytes | Type::Bytes32 | Type::PublicKey | Type::Int @@ -585,7 +573,7 @@ mod tests { }, Rest::Nil, ); - assert_eq!(db.compare(point, types.any), Comparison::Castable); + assert_eq!(db.compare(point, types.any), Comparison::Assignable); } #[test] From 05e9d23cb279e0ae7a1a5c844e8d0662f13c453a Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 25 Jul 2024 14:47:47 -0400 Subject: [PATCH 072/100] More bug fixes --- crates/rue-typing/src/comparison.rs | 66 +++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index d1fb179..76db4a2 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -323,22 +323,22 @@ pub(crate) fn compare_type( (Type::Union(items), _) => { let items = items.clone(); let mut result = Comparison::Assignable; - let mut incompatible_count = 0; - let length = items.len(); + let mut any_castable = false; for item in items { let cmp = compare_type(db, item, rhs, ctx); result = max(result, cmp); - if cmp == Comparison::Incompatible { - incompatible_count += 1; + + if compare_type(db, rhs, item, ctx) <= Comparison::Castable { + any_castable = true; } } - if incompatible_count == length { - Comparison::Incompatible + if result == Comparison::Incompatible && any_castable { + Comparison::Superset } else { - min(result, Comparison::Superset) + result } } @@ -346,13 +346,22 @@ pub(crate) fn compare_type( (_, Type::Union(items)) => { let items = items.clone(); let mut result = Comparison::Incompatible; + let mut any_incompatible = false; for item in items { let cmp = compare_type(db, lhs, item, ctx); result = min(result, cmp); + + if cmp == Comparison::Incompatible { + any_incompatible = true; + } } - max(result, Comparison::Assignable) + if any_incompatible && result == Comparison::Superset { + Comparison::Incompatible + } else { + max(result, Comparison::Assignable) + } } // Generics are resolved by looking up the substitution in the stack. @@ -628,7 +637,7 @@ mod tests { } #[test] - fn test_generic_inference() { + fn test_compare_generic_inference() { let mut db = TypeSystem::new(); let types = db.std(); @@ -655,4 +664,43 @@ mod tests { ); } } + + #[test] + fn test_compare_union_to_rhs_incompatible() { + let mut db = TypeSystem::new(); + let types = db.std(); + + let pair = db.alloc(Type::Pair(types.int, types.public_key)); + let union = db.alloc(Type::Union(vec![types.bytes32, pair, types.nil])); + assert_eq!(db.compare(union, types.bytes), Comparison::Incompatible); + } + + #[test] + fn test_compare_union_to_rhs_superset() { + let mut db = TypeSystem::new(); + let types = db.std(); + + let pair = db.alloc(Type::Pair(types.int, types.public_key)); + let union = db.alloc(Type::Union(vec![types.bytes, pair])); + assert_eq!(db.compare(union, types.bytes), Comparison::Superset); + } + + #[test] + fn test_compare_union_to_rhs_assignable() { + let mut db = TypeSystem::new(); + let types = db.std(); + + let union = db.alloc(Type::Union(vec![types.bytes32, types.nil])); + assert_eq!(db.compare(union, types.bytes), Comparison::Assignable); + } + + #[test] + fn test_compare_lhs_to_union_incompatible() { + let mut db = TypeSystem::new(); + let types = db.std(); + + let pair = db.alloc(Type::Pair(types.int, types.public_key)); + let union = db.alloc(Type::Union(vec![types.bytes32, pair, types.nil])); + assert_eq!(db.compare(types.bytes, union), Comparison::Incompatible); + } } From 968111ee82c81f9341f55e19435ab7bf00b59520 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 25 Jul 2024 14:52:14 -0400 Subject: [PATCH 073/100] Add tests for structs --- crates/rue-typing/src/comparison.rs | 107 +++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index 76db4a2..ac307fd 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -416,7 +416,7 @@ pub(crate) fn compare_type( mod tests { use indexmap::indexmap; - use crate::{alloc_list, alloc_struct, Rest}; + use crate::{alloc_list, alloc_struct, alloc_tuple_of, Rest, Struct}; use super::*; @@ -703,4 +703,109 @@ mod tests { let union = db.alloc(Type::Union(vec![types.bytes32, pair, types.nil])); assert_eq!(db.compare(types.bytes, union), Comparison::Incompatible); } + + #[test] + fn test_compare_same_derivative_struct() { + let mut db = TypeSystem::new(); + let types = db.std(); + + let struct_type = alloc_struct( + &mut db, + &indexmap! { + "x".to_string() => types.int, + "y".to_string() => types.int, + }, + Rest::Nil, + ); + + let Type::Struct(original) = db.get(struct_type) else { + unreachable!(); + }; + + let derivative_struct_type = db.alloc(Type::Struct(Struct { + original_type_id: struct_type, + type_id: original.type_id, + field_names: original.field_names.clone(), + rest: original.rest, + generic_types: original.generic_types.clone(), + })); + + assert_eq!( + db.compare(derivative_struct_type, struct_type), + Comparison::Equal + ); + + assert_eq!( + db.compare(struct_type, derivative_struct_type), + Comparison::Equal + ); + } + + #[test] + fn test_compare_different_derivative_struct() { + let mut db = TypeSystem::new(); + let types = db.std(); + + let struct_type = alloc_struct( + &mut db, + &indexmap! { + "x".to_string() => types.int, + "y".to_string() => types.int, + }, + Rest::Nil, + ); + + let Type::Struct(original) = db.get(struct_type).clone() else { + unreachable!(); + }; + + let new_inner = alloc_tuple_of(&mut db, [types.int, types.bytes, types.nil].into_iter()); + + let derivative_struct_type = db.alloc(Type::Struct(Struct { + original_type_id: struct_type, + type_id: new_inner, + field_names: original.field_names, + rest: original.rest, + generic_types: original.generic_types, + })); + + assert_eq!( + db.compare(derivative_struct_type, struct_type), + Comparison::Castable + ); + + assert_eq!( + db.compare(struct_type, derivative_struct_type), + Comparison::Castable + ); + } + + #[test] + fn test_compare_different_struct() { + let mut db = TypeSystem::new(); + let types = db.std(); + + let struct_type = alloc_struct( + &mut db, + &indexmap! { + "x".to_string() => types.int, + "y".to_string() => types.int, + }, + Rest::Nil, + ); + + let other_struct_type = alloc_struct( + &mut db, + &indexmap! { + "x".to_string() => types.int, + "y".to_string() => types.int, + }, + Rest::Nil, + ); + + assert_eq!( + db.compare(struct_type, other_struct_type), + Comparison::Castable + ); + } } From 517b1c3c1f66cf613de6b9d8493c441dc2fd08bc Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 25 Jul 2024 16:45:01 -0400 Subject: [PATCH 074/100] Overhaul --- crates/rue-compiler/src/compiler/builtins.rs | 84 +++-- crates/rue-compiler/src/compiler/context.rs | 6 +- .../src/compiler/expr/function_call_expr.rs | 6 +- .../src/compiler/expr/guard_expr.rs | 32 +- .../src/compiler/expr/path_expr.rs | 10 +- .../src/compiler/item/enum_item.rs | 12 +- .../src/compiler/item/function_item.rs | 42 ++- .../src/compiler/item/struct_item.rs | 2 +- .../src/compiler/item/type_alias_item.rs | 8 +- .../src/compiler/ty/function_type.rs | 10 +- crates/rue-compiler/src/dependency_graph.rs | 2 +- crates/rue-compiler/src/lowerer.rs | 2 +- crates/rue-compiler/src/symbol.rs | 7 +- crates/rue-typing/src/check/attributes.rs | 29 +- crates/rue-typing/src/check/check_type.rs | 308 ++++++------------ crates/rue-typing/src/comparison.rs | 17 +- crates/rue-typing/src/difference.rs | 280 ++++++---------- crates/rue-typing/src/lib.rs | 2 +- crates/rue-typing/src/replace_type.rs | 25 +- crates/rue-typing/src/stringify.rs | 5 +- crates/rue-typing/src/substitute_type.rs | 180 +++++----- crates/rue-typing/src/type_system.rs | 28 +- 22 files changed, 473 insertions(+), 624 deletions(-) diff --git a/crates/rue-compiler/src/compiler/builtins.rs b/crates/rue-compiler/src/compiler/builtins.rs index 3c0f78e..84d71c8 100644 --- a/crates/rue-compiler/src/compiler/builtins.rs +++ b/crates/rue-compiler/src/compiler/builtins.rs @@ -67,17 +67,22 @@ fn sha256(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { let hir_id = db.alloc_hir(Hir::Op(Op::Sha256, param_ref)); let scope_id = db.alloc_scope(scope); + let type_id = ty.alloc(Type::Unknown); + + *ty.get_mut(type_id) = Type::Callable(Callable { + original_type_id: type_id, + parameter_names: indexset!["bytes".to_string()], + parameters: ty.alloc(Type::Pair(ty.std().bytes, ty.std().nil)), + rest: Rest::Nil, + return_type: ty.std().bytes32, + generic_types: Vec::new(), + }); + db.alloc_symbol(Symbol::InlineFunction(Function { scope_id, hir_id, - ty: Callable { - original_type_id: None, - parameter_names: indexset!["bytes".to_string()], - parameters: ty.alloc(Type::Pair(ty.std().bytes, ty.std().nil)), - rest: Rest::Nil, - return_type: ty.std().bytes32, - generic_types: Vec::new(), - }, + type_id, + rest: Rest::Nil, })) } @@ -89,17 +94,22 @@ fn pubkey_for_exp(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { let hir_id = db.alloc_hir(Hir::Op(Op::PubkeyForExp, param_ref)); let scope_id = db.alloc_scope(scope); + let type_id = ty.alloc(Type::Unknown); + + *ty.get_mut(type_id) = Type::Callable(Callable { + original_type_id: type_id, + parameter_names: indexset!["exponent".to_string()], + parameters: ty.alloc(Type::Pair(ty.std().bytes32, ty.std().nil)), + rest: Rest::Nil, + return_type: ty.std().public_key, + generic_types: Vec::new(), + }); + db.alloc_symbol(Symbol::InlineFunction(Function { scope_id, hir_id, - ty: Callable { - original_type_id: None, - parameter_names: indexset!["exponent".to_string()], - parameters: ty.alloc(Type::Pair(ty.std().bytes32, ty.std().nil)), - rest: Rest::Nil, - return_type: ty.std().public_key, - generic_types: Vec::new(), - }, + type_id, + rest: Rest::Nil, })) } @@ -117,17 +127,22 @@ fn divmod(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { let int_pair = ty.alloc(Type::Pair(ty.std().int, ty.std().int)); + let type_id = ty.alloc(Type::Unknown); + + *ty.get_mut(type_id) = Type::Callable(Callable { + original_type_id: type_id, + parameter_names: indexset!["lhs".to_string(), "rhs".to_string()], + parameters: ty.alloc(Type::Pair(int_pair, ty.std().nil)), + rest: Rest::Nil, + return_type: int_pair, + generic_types: Vec::new(), + }); + db.alloc_symbol(Symbol::InlineFunction(Function { scope_id, hir_id, - ty: Callable { - original_type_id: None, - parameter_names: indexset!["lhs".to_string(), "rhs".to_string()], - parameters: ty.alloc(Type::Pair(int_pair, ty.std().nil)), - rest: Rest::Nil, - return_type: int_pair, - generic_types: Vec::new(), - }, + type_id, + rest: Rest::Nil, })) } @@ -150,16 +165,21 @@ fn substr(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { let int = ty.alloc(Type::Pair(ty.std().int, end)); let parameters = ty.alloc(Type::Pair(ty.std().bytes, int)); + let type_id = ty.alloc(Type::Unknown); + + *ty.get_mut(type_id) = Type::Callable(Callable { + original_type_id: type_id, + parameter_names: indexset!["value".to_string(), "start".to_string(), "end".to_string()], + parameters, + rest: Rest::Nil, + return_type: ty.std().bytes, + generic_types: Vec::new(), + }); + db.alloc_symbol(Symbol::InlineFunction(Function { scope_id, hir_id, - ty: Callable { - original_type_id: None, - parameter_names: indexset!["value".to_string(), "start".to_string(), "end".to_string()], - parameters, - rest: Rest::Nil, - return_type: ty.std().bytes, - generic_types: Vec::new(), - }, + type_id, + rest: Rest::Nil, })) } diff --git a/crates/rue-compiler/src/compiler/context.rs b/crates/rue-compiler/src/compiler/context.rs index ca63001..e530c7f 100644 --- a/crates/rue-compiler/src/compiler/context.rs +++ b/crates/rue-compiler/src/compiler/context.rs @@ -115,7 +115,11 @@ pub fn build_graph( let Symbol::Function(function) = db.symbol_mut(symbol_id).clone() else { continue; }; - ignored_types.extend(function.ty.generic_types.iter().copied()); + ignored_types.extend( + ty.get_callable(function.type_id) + .map(|callable| callable.generic_types.clone()) + .unwrap_or_default(), + ); } let Symbol::Module(module) = db.symbol_mut(main_module_id).clone() else { diff --git a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs index e16b7cf..39c2a8c 100644 --- a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use rowan::TextRange; use rue_parser::{AstNode, FunctionCallExpr}; -use rue_typing::{deconstruct_items, unwrap_list, Callable, Rest, Semantics, Type, TypeId}; +use rue_typing::{deconstruct_items, unwrap_list, Callable, Rest, Type, TypeId}; use crate::{compiler::Compiler, hir::Hir, value::Value, ErrorKind}; @@ -136,9 +136,7 @@ impl Compiler<'_> { function_type.map_or(self.ty.std().unknown, |expected| expected.return_type); if !generic_types.is_empty() { - type_id = self - .ty - .substitute(type_id, generic_types, Semantics::Preserve); + type_id = self.ty.substitute(type_id, generic_types); } // Build the HIR for the function call. diff --git a/crates/rue-compiler/src/compiler/expr/guard_expr.rs b/crates/rue-compiler/src/compiler/expr/guard_expr.rs index fcadd07..6e3a66a 100644 --- a/crates/rue-compiler/src/compiler/expr/guard_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/guard_expr.rs @@ -1,7 +1,5 @@ -use std::collections::HashMap; - use rue_parser::{AstNode, GuardExpr}; -use rue_typing::{bigint_to_bytes, Check, Semantics, TypeId}; +use rue_typing::{bigint_to_bytes, Check, TypeId}; use crate::{ compiler::Compiler, @@ -27,23 +25,7 @@ impl Compiler<'_> { .ty() .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); - let lhs = self.ty.substitute( - expr.type_id, - HashMap::new(), - Semantics::StructuralOnly { - callable: self.ty.std().any, - }, - ); - - let rhs = self.ty.substitute( - rhs, - HashMap::new(), - Semantics::StructuralOnly { - callable: self.ty.std().never, - }, - ); - - let Ok(check) = self.ty.check(lhs, rhs) else { + let Ok(check) = self.ty.check(expr.type_id, rhs) else { self.db.error( ErrorKind::RecursiveTypeCheck(self.type_name(expr.type_id), self.type_name(rhs)), guard.syntax().text_range(), @@ -54,13 +36,19 @@ impl Compiler<'_> { match check { Check::True => { self.db.warning( - WarningKind::UnnecessaryTypeCheck(self.type_name(lhs), self.type_name(rhs)), + WarningKind::UnnecessaryTypeCheck( + self.type_name(expr.type_id), + self.type_name(rhs), + ), guard.syntax().text_range(), ); } Check::False => { self.db.error( - ErrorKind::ImpossibleTypeCheck(self.type_name(lhs), self.type_name(rhs)), + ErrorKind::ImpossibleTypeCheck( + self.type_name(expr.type_id), + self.type_name(rhs), + ), guard.syntax().text_range(), ); return self.unknown(); diff --git a/crates/rue-compiler/src/compiler/expr/path_expr.rs b/crates/rue-compiler/src/compiler/expr/path_expr.rs index 227efb8..0c19033 100644 --- a/crates/rue-compiler/src/compiler/expr/path_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/path_expr.rs @@ -43,7 +43,7 @@ impl Compiler<'_> { ); } - let Type::Enum(enum_type) = self.ty.get(variant.enum_type) else { + let Type::Enum(enum_type) = self.ty.get(variant.original_enum_type_id) else { unreachable!(); }; @@ -85,11 +85,9 @@ impl Compiler<'_> { let mut value = match self.db.symbol(symbol_id).clone() { Symbol::Unknown | Symbol::Module(..) => unreachable!(), - Symbol::Function(Function { ty, .. }) | Symbol::InlineFunction(Function { ty, .. }) => { - let type_id = self.ty.alloc(Type::Callable(ty.clone())); - Value::new(reference, override_type_id.unwrap_or(type_id)) - } - Symbol::Parameter(type_id) => { + Symbol::Function(Function { type_id, .. }) + | Symbol::InlineFunction(Function { type_id, .. }) + | Symbol::Parameter(type_id) => { Value::new(reference, override_type_id.unwrap_or(type_id)) } Symbol::Let(mut value) | Symbol::Const(mut value) | Symbol::InlineConst(mut value) => { diff --git a/crates/rue-compiler/src/compiler/item/enum_item.rs b/crates/rue-compiler/src/compiler/item/enum_item.rs index aa2b579..db802f5 100644 --- a/crates/rue-compiler/src/compiler/item/enum_item.rs +++ b/crates/rue-compiler/src/compiler/item/enum_item.rs @@ -49,12 +49,14 @@ impl Compiler<'_> { .ty .alloc(Type::Union(variants.values().copied().collect())); - let enum_type_id = self.ty.alloc(Type::Enum(Enum { - original_type_id: None, + let enum_type_id = self.ty.alloc(Type::Unknown); + + *self.ty.get_mut(enum_type_id) = Type::Enum(Enum { + original_type_id: enum_type_id, type_id: enum_structure, has_fields, variants, - })); + }); // Add the enum to the scope and define the token for the enum. if let Some(name) = enum_item.name() { @@ -150,8 +152,8 @@ impl Compiler<'_> { }; *self.ty.get_mut(variant_type_id) = Type::Variant(Variant { - original_type_id: None, - enum_type: enum_type_id, + original_type_id: variant_type_id, + original_enum_type_id: enum_type_id, field_names: if variant.fields().is_some() { Some(fields.keys().cloned().collect()) } else { diff --git a/crates/rue-compiler/src/compiler/item/function_item.rs b/crates/rue-compiler/src/compiler/item/function_item.rs index 21e0f99..6d2c543 100644 --- a/crates/rue-compiler/src/compiler/item/function_item.rs +++ b/crates/rue-compiler/src/compiler/item/function_item.rs @@ -137,27 +137,31 @@ impl Compiler<'_> { let hir_id = self.db.alloc_hir(Hir::Unknown); // Create the function's type. - let ty = Callable { - original_type_id: None, + let type_id = self.ty.alloc(Type::Unknown); + + *self.ty.get_mut(type_id) = Type::Callable(Callable { + original_type_id: type_id, parameter_names: param_names.into_iter().collect(), parameters: construct_items(self.ty, param_types.into_iter(), rest), return_type, rest, generic_types, - }; + }); // Update the symbol with the function. if function_item.inline().is_some() { *self.db.symbol_mut(symbol_id) = Symbol::InlineFunction(Function { scope_id, hir_id, - ty, + type_id, + rest, }); } else { *self.db.symbol_mut(symbol_id) = Symbol::Function(Function { scope_id, hir_id, - ty, + type_id, + rest, }); } @@ -179,26 +183,36 @@ impl Compiler<'_> { }; // Get the function's scope and type. - let (Symbol::Function(Function { scope_id, ty, .. }) - | Symbol::InlineFunction(Function { scope_id, ty, .. })) = - self.db.symbol(symbol_id).clone() + let (Symbol::Function(Function { + scope_id, type_id, .. + }) + | Symbol::InlineFunction(Function { + scope_id, type_id, .. + })) = self.db.symbol(symbol_id).clone() else { unreachable!(); }; + let return_type = self + .ty + .get_callable(type_id) + .map(|callable| callable.return_type); + // We don't care about explicit returns in this context. self.scope_stack.push(scope_id); self.allow_generic_inference_stack.push(false); - let value = self.compile_block(&body, Some(ty.return_type)).value; + let value = self.compile_block(&body, return_type).value; self.allow_generic_inference_stack.pop().unwrap(); self.scope_stack.pop().unwrap(); // Ensure that the body is assignable to the return type. - self.type_check( - value.type_id, - ty.return_type, - function.body().unwrap().syntax().text_range(), - ); + if let Some(return_type) = return_type { + self.type_check( + value.type_id, + return_type, + function.body().unwrap().syntax().text_range(), + ); + } // Update the function's HIR with the body's HIR, for code generation purposes. let (Symbol::Function(Function { hir_id, .. }) diff --git a/crates/rue-compiler/src/compiler/item/struct_item.rs b/crates/rue-compiler/src/compiler/item/struct_item.rs index bcca263..d0264c3 100644 --- a/crates/rue-compiler/src/compiler/item/struct_item.rs +++ b/crates/rue-compiler/src/compiler/item/struct_item.rs @@ -23,7 +23,7 @@ impl Compiler<'_> { let type_id = construct_items(self.ty, fields.values().copied(), rest); *self.ty.get_mut(struct_type_id) = Type::Struct(Struct { - original_type_id: None, + original_type_id: struct_type_id, field_names: fields.keys().cloned().collect(), type_id, rest, 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 5c20419..4b98aaf 100644 --- a/crates/rue-compiler/src/compiler/item/type_alias_item.rs +++ b/crates/rue-compiler/src/compiler/item/type_alias_item.rs @@ -43,11 +43,13 @@ impl Compiler<'_> { // Create the alias type. let ref_type_id = self.ty.alloc(Type::Ref(self.ty.std().unknown)); - let type_id = self.ty.alloc(Type::Alias(Alias { - original_type_id: None, + let type_id = self.ty.alloc(Type::Unknown); + + *self.ty.get_mut(type_id) = Type::Alias(Alias { + original_type_id: type_id, type_id: ref_type_id, generic_types, - })); + }); if let Some(name) = type_alias.name() { self.scope_mut().define_type(name.to_string(), type_id); diff --git a/crates/rue-compiler/src/compiler/ty/function_type.rs b/crates/rue-compiler/src/compiler/ty/function_type.rs index 47dbfbb..a135a61 100644 --- a/crates/rue-compiler/src/compiler/ty/function_type.rs +++ b/crates/rue-compiler/src/compiler/ty/function_type.rs @@ -72,13 +72,17 @@ impl Compiler<'_> { // Allocate a new type for the function. // TODO: Support generic types. - self.ty.alloc(Type::Callable(Callable { - original_type_id: None, + let type_id = self.ty.alloc(Type::Unknown); + + *self.ty.get_mut(type_id) = Type::Callable(Callable { + original_type_id: type_id, parameter_names, parameters, rest, return_type, generic_types: Vec::new(), - })) + }); + + type_id } } diff --git a/crates/rue-compiler/src/dependency_graph.rs b/crates/rue-compiler/src/dependency_graph.rs index a74963f..4290809 100644 --- a/crates/rue-compiler/src/dependency_graph.rs +++ b/crates/rue-compiler/src/dependency_graph.rs @@ -136,7 +136,7 @@ impl<'a> GraphBuilder<'a> { let environment_id = self.db.alloc_env(Environment::function( parameters, - function.ty.rest != Rest::Nil, + function.rest != Rest::Nil, )); self.graph diff --git a/crates/rue-compiler/src/lowerer.rs b/crates/rue-compiler/src/lowerer.rs index 38b4dec..4f45055 100644 --- a/crates/rue-compiler/src/lowerer.rs +++ b/crates/rue-compiler/src/lowerer.rs @@ -322,7 +322,7 @@ impl<'a> Lowerer<'a> { let mut param_map = HashMap::new(); for (i, &symbol_id) in params.iter().enumerate() { - if i + 1 != params.len() || function.ty.rest != Rest::Spread { + if i + 1 != params.len() || function.rest != Rest::Spread { let mir_id = self.lower_hir(env_id, args[i]); param_map.insert(symbol_id, mir_id); continue; diff --git a/crates/rue-compiler/src/symbol.rs b/crates/rue-compiler/src/symbol.rs index e8c4d16..2111abe 100644 --- a/crates/rue-compiler/src/symbol.rs +++ b/crates/rue-compiler/src/symbol.rs @@ -1,5 +1,5 @@ use indexmap::IndexSet; -use rue_typing::{Callable, TypeId}; +use rue_typing::{Rest, TypeId}; use crate::{ database::{HirId, ScopeId}, @@ -42,11 +42,12 @@ impl Symbol { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct Function { pub scope_id: ScopeId, pub hir_id: HirId, - pub ty: Callable, + pub type_id: TypeId, + pub rest: Rest, } #[derive(Debug, Clone)] diff --git a/crates/rue-typing/src/check/attributes.rs b/crates/rue-typing/src/check/attributes.rs index 410922a..efb79a3 100644 --- a/crates/rue-typing/src/check/attributes.rs +++ b/crates/rue-typing/src/check/attributes.rs @@ -80,22 +80,17 @@ pub(crate) fn union_attributes( return Err(CheckError::Recursive(key.0, key.1)); } - match db.get(item) { - Type::Ref(..) => unreachable!(), - Type::Lazy(..) => unreachable!(), - Type::Alias(..) => unreachable!(), - Type::Struct(..) => unreachable!(), - Type::Callable(..) => unreachable!(), - Type::Enum(..) => unreachable!(), - Type::Variant(..) => unreachable!(), - Type::Generic => { - length -= 1; - } - Type::Unknown => {} - Type::Any => {} - Type::Never => { + match db.get_recursive(item) { + Type::Ref(..) + | Type::Lazy(..) + | Type::Alias(..) + | Type::Struct(..) + | Type::Enum(..) + | Type::Variant(..) => unreachable!(), + Type::Generic | Type::Never | Type::Callable(..) => { length -= 1; } + Type::Any | Type::Unknown => {} Type::Bytes | Type::Int => { atom_count += 1; } @@ -107,7 +102,7 @@ pub(crate) fn union_attributes( atom_count += 1; public_key_count += 1; } - Type::Nil => { + Type::Nil | Type::False => { atom_count += 1; *values.entry(BigInt::ZERO).or_insert(0) += 1; } @@ -115,10 +110,6 @@ pub(crate) fn union_attributes( atom_count += 1; *values.entry(BigInt::one()).or_insert(0) += 1; } - Type::False => { - atom_count += 1; - *values.entry(BigInt::ZERO).or_insert(0) += 1; - } Type::Value(value) => { atom_count += 1; *values.entry(value.clone()).or_insert(0) += 1; diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs index 8b45f43..4688320 100644 --- a/crates/rue-typing/src/check/check_type.rs +++ b/crates/rue-typing/src/check/check_type.rs @@ -21,55 +21,53 @@ pub(crate) fn check_type( return Err(CheckError::Recursive(lhs, rhs)); } - let check = match (types.get(lhs), types.get(rhs)) { - (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), - (Type::Lazy(..), _) | (_, Type::Lazy(..)) => unreachable!(), - (Type::Alias(..), _) | (_, Type::Alias(..)) => unreachable!(), - (Type::Struct(..), _) | (_, Type::Struct(..)) => unreachable!(), - (Type::Callable(..), _) | (_, Type::Callable(..)) => unreachable!(), - (Type::Enum(..), _) | (_, Type::Enum(..)) => unreachable!(), - (Type::Variant(..), _) | (_, Type::Variant(..)) => unreachable!(), - - // TODO: Implement generic type checking? - (Type::Generic, _) => Check::True, - (_, Type::Generic) => Check::True, - - (Type::Unknown, _) => Check::True, - (_, Type::Unknown) => Check::True, - - (Type::Never, _) => Check::True, - (_, Type::Never) => Check::False, - - (Type::Any, Type::Any) => Check::True, - (Type::Bytes, Type::Bytes) => Check::True, - (Type::Bytes32, Type::Bytes32) => Check::True, - (Type::PublicKey, Type::PublicKey) => Check::True, - (Type::Int, Type::Int) => Check::True, - (Type::Nil, Type::Nil) => Check::True, - (Type::True, Type::True) => Check::True, - (Type::False, Type::False) => Check::True, - - (Type::Bytes32, Type::Any) => Check::True, - (Type::PublicKey, Type::Any) => Check::True, - (Type::Int, Type::Any) => Check::True, - (Type::Bytes, Type::Any) => Check::True, - (Type::Nil, Type::Any) => Check::True, - (Type::True, Type::Any) => Check::True, - (Type::False, Type::Any) => Check::True, - (Type::Value(..), Type::Any) => Check::True, - (Type::Pair(..), Type::Any) => Check::True, - - (Type::Any, Type::Bytes) => Check::IsAtom, - (Type::Any, Type::Int) => Check::IsAtom, - (Type::Any, Type::Bytes32) => Check::And(vec![Check::IsAtom, Check::Length(32)]), - (Type::Any, Type::PublicKey) => Check::And(vec![Check::IsAtom, Check::Length(48)]), - (Type::Any, Type::Nil) => Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]), - (Type::Any, Type::True) => Check::And(vec![Check::IsAtom, Check::Value(BigInt::one())]), - (Type::Any, Type::False) => Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]), - (Type::Any, Type::Value(value)) => { + let check = match (types.get_recursive(lhs), types.get_recursive(rhs)) { + ( + Type::Ref(..) + | Type::Lazy(..) + | Type::Alias(..) + | Type::Struct(..) + | Type::Enum(..) + | Type::Variant(..), + _, + ) + | ( + _, + Type::Ref(..) + | Type::Lazy(..) + | Type::Alias(..) + | Type::Struct(..) + | Type::Enum(..) + | Type::Variant(..), + ) => { + unreachable!() + } + + (_, Type::Any | Type::Unknown) + | (Type::Never | Type::Unknown, _) + | (Type::Bytes | Type::Int | Type::Value(..), Type::Bytes | Type::Int) + | (Type::Bytes32, Type::Bytes | Type::Int | Type::Bytes32) + | (Type::PublicKey, Type::Bytes | Type::Int | Type::PublicKey) + | (Type::Nil | Type::False, Type::Bytes | Type::Int | Type::Nil | Type::False) + | (Type::True, Type::Bytes | Type::Int | Type::True) => Check::True, + + (Type::Any | Type::Generic | Type::Callable(..), Type::Bytes | Type::Int) => Check::IsAtom, + (Type::Any | Type::Generic | Type::Callable(..), Type::Bytes32) => { + Check::And(vec![Check::IsAtom, Check::Length(32)]) + } + (Type::Any | Type::Generic | Type::Callable(..), Type::PublicKey) => { + Check::And(vec![Check::IsAtom, Check::Length(48)]) + } + (Type::Any | Type::Generic | Type::Callable(..), Type::False | Type::Nil) => { + Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]) + } + (Type::Any | Type::Generic | Type::Callable(..), Type::True) => { + Check::And(vec![Check::IsAtom, Check::Value(BigInt::one())]) + } + (Type::Any | Type::Generic | Type::Callable(..), Type::Value(value)) => { Check::And(vec![Check::IsAtom, Check::Value(value.clone())]) } - (Type::Any, Type::Pair(first, rest)) => { + (Type::Any | Type::Generic | Type::Callable(..), Type::Pair(first, rest)) => { let (first, rest) = (*first, *rest); let first = check_type(types, types.std().any, first, visited)?; let rest = check_type(types, types.std().any, rest, visited)?; @@ -80,57 +78,39 @@ pub(crate) fn check_type( ]) } - (Type::Bytes32, Type::Bytes) => Check::True, - (Type::PublicKey, Type::Bytes) => Check::True, - (Type::Int, Type::Bytes) => Check::True, - (Type::Nil, Type::Bytes) => Check::True, - - (Type::Bytes32, Type::Int) => Check::True, - (Type::PublicKey, Type::Int) => Check::True, - (Type::Bytes, Type::Int) => Check::True, - (Type::Nil, Type::Int) => Check::True, - - (Type::Bytes, Type::Nil) => Check::Value(BigInt::ZERO), - (Type::Bytes, Type::PublicKey) => Check::Length(48), - (Type::Bytes, Type::Bytes32) => Check::Length(32), - - (Type::Int, Type::Nil) => Check::Value(BigInt::ZERO), - (Type::Int, Type::PublicKey) => Check::Length(48), - (Type::Int, Type::Bytes32) => Check::Length(32), - - (Type::PublicKey, Type::Bytes32) => Check::False, - (Type::Bytes32, Type::PublicKey) => Check::False, - (Type::Nil, Type::PublicKey) => Check::False, - (Type::Nil, Type::Bytes32) => Check::False, - (Type::PublicKey, Type::Nil) => Check::False, - (Type::Bytes32, Type::Nil) => Check::False, - - (Type::True, Type::Nil) => Check::False, - (Type::False, Type::Nil) => Check::True, - (Type::True, Type::False) => Check::False, - (Type::False, Type::True) => Check::False, - (Type::True, Type::Bytes) => Check::True, - (Type::False, Type::Bytes) => Check::True, - (Type::True, Type::Bytes32) => Check::False, - (Type::False, Type::Bytes32) => Check::False, - (Type::True, Type::PublicKey) => Check::False, - (Type::False, Type::PublicKey) => Check::False, - (Type::True, Type::Int) => Check::True, - (Type::False, Type::Int) => Check::True, - - (Type::Bytes, Type::True) => Check::Value(BigInt::one()), - (Type::Bytes, Type::False) => Check::Value(BigInt::ZERO), - (Type::Int, Type::True) => Check::Value(BigInt::one()), - (Type::Int, Type::False) => Check::Value(BigInt::ZERO), - (Type::Bytes32, Type::True) => Check::False, - (Type::Bytes32, Type::False) => Check::False, - (Type::PublicKey, Type::True) => Check::False, - (Type::PublicKey, Type::False) => Check::False, - (Type::Nil, Type::True) => Check::False, - (Type::Nil, Type::False) => Check::True, - - (Type::Value(..), Type::Bytes) => Check::True, - (Type::Value(..), Type::Int) => Check::True, + (Type::Bytes | Type::Int, Type::Nil | Type::False) => Check::Value(BigInt::ZERO), + (Type::Bytes | Type::Int, Type::True) => Check::Value(BigInt::one()), + (Type::Bytes | Type::Int, Type::PublicKey) => Check::Length(48), + (Type::Bytes | Type::Int, Type::Bytes32) => Check::Length(32), + + (_, Type::Never | Type::Generic | Type::Callable(..)) + | (Type::PublicKey, Type::Bytes32 | Type::Nil | Type::True | Type::False) + | (Type::Bytes32, Type::PublicKey | Type::Nil | Type::True | Type::False) + | (Type::Nil | Type::False, Type::PublicKey | Type::Bytes32 | Type::True) + | (Type::True, Type::PublicKey | Type::Bytes32 | Type::Nil | Type::False) + | ( + Type::Bytes + | Type::Bytes32 + | Type::PublicKey + | Type::Int + | Type::Nil + | Type::True + | Type::False + | Type::Value(..), + Type::Pair(..), + ) + | ( + Type::Pair(..), + Type::Bytes + | Type::Bytes32 + | Type::PublicKey + | Type::Int + | Type::Nil + | Type::True + | Type::False + | Type::Value(..), + ) => Check::False, + (Type::Value(value), Type::Bytes32) => { if bigint_to_bytes(value.clone()).len() == 32 { Check::True @@ -138,6 +118,7 @@ pub(crate) fn check_type( Check::False } } + (Type::Value(value), Type::PublicKey) => { if bigint_to_bytes(value.clone()).len() == 48 { Check::True @@ -145,30 +126,26 @@ pub(crate) fn check_type( Check::False } } - (Type::Value(value), Type::Nil) => { + + (Type::Value(value), Type::Nil | Type::False) + | (Type::Nil | Type::False, Type::Value(value)) => { if value == &BigInt::ZERO { Check::True } else { Check::False } } - (Type::Value(value), Type::True) => { + + (Type::Value(value), Type::True) | (Type::True, Type::Value(value)) => { if value == &BigInt::one() { Check::True } else { Check::False } } - (Type::Value(value), Type::False) => { - if value == &BigInt::ZERO { - Check::True - } else { - Check::False - } - } - (Type::Bytes, Type::Value(value)) => Check::Value(value.clone()), - (Type::Int, Type::Value(value)) => Check::Value(value.clone()), + (Type::Bytes | Type::Int, Type::Value(value)) => Check::Value(value.clone()), + (Type::Bytes32, Type::Value(value)) => { if bigint_to_bytes(value.clone()).len() == 32 { Check::Value(value.clone()) @@ -183,27 +160,6 @@ pub(crate) fn check_type( Check::False } } - (Type::Nil, Type::Value(value)) => { - if value == &BigInt::ZERO { - Check::True - } else { - Check::False - } - } - (Type::True, Type::Value(value)) => { - if value == &BigInt::one() { - Check::True - } else { - Check::False - } - } - (Type::False, Type::Value(value)) => { - if value == &BigInt::ZERO { - Check::True - } else { - Check::False - } - } (Type::Value(lhs_value), Type::Value(rhs_value)) => { if lhs_value == rhs_value { @@ -213,24 +169,6 @@ pub(crate) fn check_type( } } - (Type::Bytes, Type::Pair(..)) => Check::False, - (Type::Bytes32, Type::Pair(..)) => Check::False, - (Type::PublicKey, Type::Pair(..)) => Check::False, - (Type::Int, Type::Pair(..)) => Check::False, - (Type::Nil, Type::Pair(..)) => Check::False, - (Type::True, Type::Pair(..)) => Check::False, - (Type::False, Type::Pair(..)) => Check::False, - (Type::Value(..), Type::Pair(..)) => Check::False, - - (Type::Pair(..), Type::Bytes) => Check::False, - (Type::Pair(..), Type::Bytes32) => Check::False, - (Type::Pair(..), Type::PublicKey) => Check::False, - (Type::Pair(..), Type::Int) => Check::False, - (Type::Pair(..), Type::Nil) => Check::False, - (Type::Pair(..), Type::True) => Check::False, - (Type::Pair(..), Type::False) => Check::False, - (Type::Pair(..), Type::Value(..)) => Check::False, - (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { let (lhs_first, lhs_rest) = (*lhs_first, *lhs_rest); let (rhs_first, rhs_rest) = (*rhs_first, *rhs_rest); @@ -294,31 +232,22 @@ fn check_union_against_rhs( let attrs = union_attributes(types, items, true, rhs, visited)?; - Ok(match types.get(rhs) { - Type::Ref(..) => unreachable!(), - Type::Lazy(..) => unreachable!(), - Type::Alias(..) => unreachable!(), - Type::Union(..) => unreachable!(), - Type::Struct(..) => unreachable!(), - Type::Callable(..) => unreachable!(), - Type::Enum(..) => unreachable!(), - Type::Variant(..) => unreachable!(), - Type::Unknown => Check::True, - Type::Generic => Check::False, - Type::Never => Check::False, - Type::Any => Check::True, - Type::Bytes if attrs.all_atoms() => Check::True, - Type::Bytes => Check::IsAtom, - Type::Int if attrs.all_atoms() => Check::True, - Type::Int => Check::IsAtom, - Type::Nil if attrs.all_value(&BigInt::ZERO) => Check::True, - Type::Nil if attrs.atoms_are_value(&BigInt::ZERO) => Check::IsAtom, - Type::Nil if attrs.all_atoms() => Check::Value(BigInt::ZERO), - Type::Nil => Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]), - Type::False if attrs.all_value(&BigInt::ZERO) => Check::True, - Type::False if attrs.atoms_are_value(&BigInt::ZERO) => Check::IsAtom, - Type::False if attrs.all_atoms() => Check::Value(BigInt::ZERO), - Type::False => Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]), + Ok(match types.get_recursive(rhs) { + Type::Ref(..) + | Type::Lazy(..) + | Type::Union(..) + | Type::Alias(..) + | Type::Struct(..) + | Type::Enum(..) + | Type::Variant(..) => unreachable!(), + Type::Unknown | Type::Any => Check::True, + Type::Never | Type::Generic | Type::Callable(..) => Check::False, + Type::Bytes | Type::Int if attrs.all_atoms() => Check::True, + Type::Bytes | Type::Int => Check::IsAtom, + Type::Nil | Type::False if attrs.all_value(&BigInt::ZERO) => Check::True, + Type::Nil | Type::False if attrs.atoms_are_value(&BigInt::ZERO) => Check::IsAtom, + Type::Nil | Type::False if attrs.all_atoms() => Check::Value(BigInt::ZERO), + Type::Nil | Type::False => Check::And(vec![Check::IsAtom, Check::Value(BigInt::ZERO)]), Type::True if attrs.all_value(&BigInt::one()) => Check::True, Type::True if attrs.atoms_are_value(&BigInt::ZERO) => Check::IsAtom, Type::True if attrs.all_atoms() => Check::Value(BigInt::one()), @@ -365,11 +294,9 @@ fn check_union_against_rhs( #[cfg(test)] mod tests { - use std::collections::HashMap; - use indexmap::indexmap; - use crate::{alloc_list, alloc_struct, Rest, Semantics}; + use crate::{alloc_list, alloc_struct, Rest}; use super::*; @@ -633,7 +560,7 @@ mod tests { fn test_check_any_point_struct() { let mut db = TypeSystem::new(); let types = db.std(); - let point_struct = alloc_struct( + let point = alloc_struct( &mut db, &indexmap! { "x".to_string() => types.int, @@ -641,13 +568,6 @@ mod tests { }, Rest::Nil, ); - let point = db.substitute( - point_struct, - HashMap::new(), - Semantics::StructuralOnly { - callable: types.never, - }, - ); check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) 0))"); } @@ -680,24 +600,8 @@ mod tests { let condition = db.alloc(Type::Union(vec![agg_sig_unsafe, agg_sig_me])); - let lhs = db.substitute( - condition, - HashMap::new(), - Semantics::StructuralOnly { - callable: types.any, - }, - ); - - let rhs = db.substitute( - agg_sig_me, - HashMap::new(), - Semantics::StructuralOnly { - callable: types.never, - }, - ); - - check_str(&mut db, lhs, rhs, "(= (f val) 50)"); - check_str(&mut db, types.any, rhs, "(and (l val) (not (l (f val))) (= (f val) 50) (l (r val)) (not (l (f (r val)))) (= (strlen (f (r val))) 48) (l (r (r val))) (not (l (f (r (r val))))) (not (l (r (r (r val))))) (= (r (r (r val))) 0))"); + check_str(&mut db, condition, agg_sig_me, "(= (f val) 50)"); + check_str(&mut db, types.any, agg_sig_me, "(and (l val) (not (l (f val))) (= (f val) 50) (l (r val)) (not (l (f (r val)))) (= (strlen (f (r val))) 48) (l (r (r val))) (not (l (f (r (r val))))) (not (l (r (r (r val))))) (= (r (r (r val))) 0))"); } #[test] diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index ac307fd..430ed83 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -54,7 +54,7 @@ pub(crate) fn compare_type( | (Type::False, Type::False) => Comparison::Equal, // These are assignable since the structure and semantics match. - (_, Type::Any) + (_, Type::Any | Type::Unknown) | ( Type::Unknown | Type::Never, Type::Bytes @@ -68,21 +68,6 @@ pub(crate) fn compare_type( | Type::Pair(..) | Type::Callable(..), ) - | ( - Type::Never - | Type::Any - | Type::Bytes - | Type::Bytes32 - | Type::PublicKey - | Type::Int - | Type::Nil - | Type::True - | Type::False - | Type::Value(..) - | Type::Pair(..) - | Type::Callable(..), - Type::Unknown, - ) | (Type::Unknown, Type::Never) | (Type::Value(..), Type::Int) | (Type::Bytes32 | Type::Nil, Type::Bytes) => Comparison::Assignable, diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index 832d659..231c958 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -18,108 +18,84 @@ pub(crate) fn difference_type( } let result = match (types.get(lhs), types.get(rhs)) { - (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), - (Type::Lazy(..), _) | (_, Type::Lazy(..)) => unreachable!(), - - (Type::Generic, _) | (_, Type::Generic) => lhs, - (Type::Unknown, _) | (_, Type::Unknown) => lhs, - - (Type::Never, _) => std.never, - (_, Type::Never) => lhs, - - (Type::Any, Type::Any) => std.never, - (Type::Bytes, Type::Bytes) => std.never, - (Type::Bytes32, Type::Bytes32) => std.never, - (Type::PublicKey, Type::PublicKey) => std.never, - (Type::Int, Type::Int) => std.never, - (Type::Nil, Type::Nil) => std.never, - (Type::True, Type::True) => std.never, - (Type::False, Type::False) => std.never, - - (Type::Int, Type::Bytes32) => lhs, - (Type::Int, Type::PublicKey) => lhs, - (Type::Int, Type::Bytes) => std.never, - (Type::Int, Type::Any) => std.never, - (Type::Int, Type::Nil) => lhs, - (Type::Int, Type::True) => lhs, - (Type::Int, Type::False) => lhs, - (Type::Int, Type::Value(..)) => lhs, - - (Type::Bytes, Type::Bytes32) => lhs, - (Type::Bytes, Type::PublicKey) => lhs, - (Type::Bytes, Type::Int) => std.never, - (Type::Bytes, Type::Any) => std.never, - (Type::Bytes, Type::Nil) => lhs, - (Type::Bytes, Type::True) => lhs, - (Type::Bytes, Type::False) => lhs, - (Type::Bytes, Type::Value(..)) => lhs, - - (Type::Any, Type::Bytes32) => lhs, - (Type::Any, Type::PublicKey) => lhs, - (Type::Any, Type::Int) => lhs, - (Type::Any, Type::Bytes) => lhs, - (Type::Any, Type::Nil) => lhs, - (Type::Any, Type::True) => lhs, - (Type::Any, Type::False) => lhs, - (Type::Any, Type::Value(..)) => lhs, - - (Type::Bytes32, Type::PublicKey) => lhs, - (Type::Bytes32, Type::Bytes) => std.never, - (Type::Bytes32, Type::Int) => std.never, - (Type::Bytes32, Type::Any) => std.never, - (Type::Bytes32, Type::Nil) => lhs, - (Type::Bytes32, Type::True) => lhs, - (Type::Bytes32, Type::False) => lhs, - (Type::Bytes32, Type::Value(..)) => lhs, - - (Type::PublicKey, Type::Bytes32) => lhs, - (Type::PublicKey, Type::Bytes) => std.never, - (Type::PublicKey, Type::Int) => std.never, - (Type::PublicKey, Type::Any) => std.never, - (Type::PublicKey, Type::Nil) => lhs, - (Type::PublicKey, Type::True) => lhs, - (Type::PublicKey, Type::False) => lhs, - (Type::PublicKey, Type::Value(..)) => lhs, - - (Type::Nil, Type::Bytes32) => lhs, - (Type::Nil, Type::PublicKey) => lhs, - (Type::Nil, Type::Bytes) => std.never, - (Type::Nil, Type::Any) => std.never, - (Type::Nil, Type::Int) => std.never, - (Type::Nil, Type::True) => lhs, - (Type::Nil, Type::False) => std.never, - - (Type::True, Type::Any) => std.never, - (Type::True, Type::Bytes) => std.never, - (Type::True, Type::Bytes32) => lhs, - (Type::True, Type::PublicKey) => lhs, - (Type::True, Type::Int) => std.never, - (Type::True, Type::Nil) => lhs, - (Type::True, Type::False) => lhs, - - (Type::False, Type::Any) => std.never, - (Type::False, Type::Bytes) => std.never, - (Type::False, Type::Bytes32) => lhs, - (Type::False, Type::PublicKey) => lhs, - (Type::False, Type::Int) => std.never, - (Type::False, Type::Nil) => lhs, - (Type::False, Type::True) => lhs, - - (Type::Nil, Type::Value(value)) => { + (Type::Ref(..) | Type::Lazy(..), _) | (_, Type::Ref(..) | Type::Lazy(..)) => unreachable!(), + + // If you subtract a supertype or equal type, there are no other possible types. + (Type::Never, _) + | (_, Type::Any | Type::Unknown | Type::Generic | Type::Callable(..)) + | (Type::Bytes | Type::Int | Type::Value(..), Type::Bytes | Type::Int) + | (Type::Bytes32, Type::Bytes32 | Type::Bytes | Type::Int) + | (Type::PublicKey, Type::PublicKey | Type::Bytes | Type::Int) + | (Type::Nil | Type::False, Type::Nil | Type::Bytes | Type::Int | Type::False) + | (Type::True, Type::True | Type::Bytes | Type::Int) => std.never, + + // If you subtract something which is a subtype, the result is the same as the original type. + (_, Type::Never) + | ( + Type::Any + | Type::Int + | Type::Bytes + | Type::Unknown + | Type::Generic + | Type::Callable(..), + Type::Bytes32 + | Type::PublicKey + | Type::Nil + | Type::True + | Type::False + | Type::Value(..), + ) + | ( + Type::Unknown | Type::Generic | Type::Callable(..), + Type::Int | Type::Bytes | Type::Pair(..), + ) + | ( + Type::Bytes32, + Type::PublicKey | Type::Nil | Type::True | Type::False | Type::Value(..), + ) + | ( + Type::PublicKey, + Type::Bytes32 | Type::Nil | Type::True | Type::False | Type::Value(..), + ) + | (Type::Nil | Type::False, Type::Bytes32 | Type::PublicKey | Type::True) + | (Type::True, Type::Bytes32 | Type::PublicKey | Type::Nil | Type::False) + | ( + Type::Pair(..), + Type::Bytes + | Type::Bytes32 + | Type::PublicKey + | Type::Int + | Type::Nil + | Type::True + | Type::False + | Type::Value(..), + ) + | ( + Type::Bytes + | Type::Bytes32 + | Type::PublicKey + | Type::Int + | Type::Nil + | Type::True + | Type::False + | Type::Value(..), + Type::Pair(..), + ) => lhs, + + // Any is defined as either Bytes or (Any, Any). + (Type::Any, Type::Pair(..)) => std.bytes, + (Type::Any, Type::Bytes | Type::Int) => types.alloc(Type::Pair(std.any, std.any)), + + (Type::Nil | Type::False, Type::Value(value)) + | (Type::Value(value), Type::Nil | Type::False) => { if value == &BigInt::ZERO { std.never } else { lhs } } - (Type::False, Type::Value(value)) => { - if value == &BigInt::ZERO { - std.never - } else { - lhs - } - } - (Type::True, Type::Value(value)) => { + + (Type::True, Type::Value(value)) | (Type::Value(value), Type::True) => { if value == &BigInt::one() { std.never } else { @@ -127,10 +103,6 @@ pub(crate) fn difference_type( } } - (Type::Value(..), Type::Any) => std.never, - (Type::Value(..), Type::Bytes) => std.never, - (Type::Value(..), Type::Int) => std.never, - (Type::Value(value), Type::Bytes32) => { if bigint_to_bytes(value.clone()).len() == 32 { std.never @@ -145,27 +117,6 @@ pub(crate) fn difference_type( lhs } } - (Type::Value(value), Type::True) => { - if value == &BigInt::one() { - std.never - } else { - lhs - } - } - (Type::Value(value), Type::False) => { - if value == &BigInt::ZERO { - std.never - } else { - lhs - } - } - (Type::Value(value), Type::Nil) => { - if value == &BigInt::ZERO { - std.never - } else { - lhs - } - } (Type::Value(lhs_value), Type::Value(rhs_value)) => { if lhs_value == rhs_value { @@ -175,26 +126,6 @@ pub(crate) fn difference_type( } } - (Type::Any, Type::Pair(..)) => lhs, - (Type::Bytes, Type::Pair(..)) => lhs, - (Type::Bytes32, Type::Pair(..)) => lhs, - (Type::PublicKey, Type::Pair(..)) => lhs, - (Type::Int, Type::Pair(..)) => lhs, - (Type::Nil, Type::Pair(..)) => lhs, - (Type::True, Type::Pair(..)) => lhs, - (Type::False, Type::Pair(..)) => lhs, - (Type::Value(..), Type::Pair(..)) => lhs, - - (Type::Pair(..), Type::Any) => std.never, - (Type::Pair(..), Type::Bytes) => lhs, - (Type::Pair(..), Type::Bytes32) => lhs, - (Type::Pair(..), Type::PublicKey) => lhs, - (Type::Pair(..), Type::Int) => lhs, - (Type::Pair(..), Type::Nil) => lhs, - (Type::Pair(..), Type::True) => lhs, - (Type::Pair(..), Type::False) => lhs, - (Type::Pair(..), Type::Value(..)) => lhs, - (Type::Pair(lhs_first, lhs_rest), Type::Pair(rhs_first, rhs_rest)) => { let (lhs_first, lhs_rest) = (*lhs_first, *lhs_rest); let (rhs_first, rhs_rest) = (*rhs_first, *rhs_rest); @@ -211,39 +142,6 @@ pub(crate) fn difference_type( } } - (Type::Union(items), _) => { - let items = items.clone(); - - let mut result = Vec::new(); - - for item in &items { - let item = difference_type(types, *item, rhs, visited); - if matches!(types.get(item), Type::Never) { - continue; - } - result.push(item); - } - - if result.is_empty() { - std.never - } else if result.len() == 1 { - result[0] - } else if result == items { - lhs - } else { - types.alloc(Type::Union(result)) - } - } - - (_, Type::Union(items)) => { - let items = items.clone(); - let mut lhs = lhs; - for item in items { - lhs = difference_type(types, lhs, item, visited); - } - lhs - } - (Type::Alias(alias), _) => difference_type(types, alias.type_id, rhs, visited), (_, Type::Alias(alias)) => difference_type(types, lhs, alias.type_id, visited), @@ -324,8 +222,38 @@ pub(crate) fn difference_type( })) } - (Type::Callable(..), _) => lhs, - (_, Type::Callable(..)) => lhs, + (Type::Union(items), _) => { + let items = items.clone(); + + let mut result = Vec::new(); + + for item in &items { + let item = difference_type(types, *item, rhs, visited); + if matches!(types.get(item), Type::Never) { + continue; + } + result.push(item); + } + + if result.is_empty() { + std.never + } else if result.len() == 1 { + result[0] + } else if result == items { + lhs + } else { + types.alloc(Type::Union(result)) + } + } + + (_, Type::Union(items)) => { + let items = items.clone(); + let mut lhs = lhs; + for item in items { + lhs = difference_type(types, lhs, item, visited); + } + lhs + } }; visited.remove(&(lhs, rhs)); diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index 413fb64..e8a06d3 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -17,7 +17,6 @@ pub use check::*; pub use comparison::*; pub use semantic_types::*; pub use standard_types::*; -pub use substitute_type::*; pub use ty::*; pub use type_path::*; pub use type_system::*; @@ -25,6 +24,7 @@ pub use type_system::*; pub(crate) use difference::difference_type; pub(crate) use replace_type::replace_type; pub(crate) use stringify::stringify_type; +pub(crate) use substitute_type::substitute_type; #[cfg(test)] mod test_tools; diff --git a/crates/rue-typing/src/replace_type.rs b/crates/rue-typing/src/replace_type.rs index a138614..b23b1b1 100644 --- a/crates/rue-typing/src/replace_type.rs +++ b/crates/rue-typing/src/replace_type.rs @@ -1,4 +1,4 @@ -use crate::{Alias, Struct, Type, TypeId, TypePath, TypeSystem}; +use crate::{Alias, Enum, Struct, Type, TypeId, TypePath, TypeSystem, Variant}; pub(crate) fn replace_type( types: &mut TypeSystem, @@ -29,10 +29,33 @@ pub(crate) fn replace_type( let new_type_id = replace_type(types, ty.type_id, replace_type_id, path); types.alloc(Type::Struct(Struct { original_type_id: ty.original_type_id, + field_names: ty.field_names, type_id: new_type_id, + rest: ty.rest, + generic_types: ty.generic_types, + })) + } + Type::Variant(ty) => { + let ty = ty.clone(); + let new_type_id = replace_type(types, ty.type_id, replace_type_id, path); + types.alloc(Type::Variant(Variant { + original_type_id: ty.original_type_id, + original_enum_type_id: ty.original_enum_type_id, field_names: ty.field_names, + type_id: new_type_id, rest: ty.rest, generic_types: ty.generic_types, + discriminant: ty.discriminant, + })) + } + Type::Enum(ty) => { + let ty = ty.clone(); + let new_type_id = replace_type(types, ty.type_id, replace_type_id, path); + types.alloc(Type::Enum(Enum { + original_type_id: ty.original_type_id, + type_id: new_type_id, + has_fields: ty.has_fields, + variants: ty.variants, })) } _ => type_id, diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs index 052c5b6..f31d73f 100644 --- a/crates/rue-typing/src/stringify.rs +++ b/crates/rue-typing/src/stringify.rs @@ -49,8 +49,9 @@ pub(crate) fn stringify_type( } Type::Lazy(lazy) => stringify_type(types, lazy.type_id, names, visited), Type::Alias(alias) => stringify_type(types, alias.type_id, names, visited), - Type::Struct(Struct { type_id, .. }) => stringify_type(types, *type_id, names, visited), - Type::Variant(Variant { type_id, .. }) => stringify_type(types, *type_id, names, visited), + Type::Struct(Struct { type_id, .. }) | Type::Variant(Variant { type_id, .. }) => { + stringify_type(types, *type_id, names, visited) + } Type::Enum(Enum { type_id, .. }) => stringify_type(types, *type_id, names, visited), Type::Callable(Callable { parameters, diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index 0e9d5e4..e704f3a 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -2,17 +2,10 @@ use std::collections::HashMap; use crate::{Alias, Callable, Enum, Struct, Type, TypeId, TypeSystem, Variant}; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Semantics { - Preserve, - StructuralOnly { callable: TypeId }, -} - pub(crate) fn substitute_type( types: &mut TypeSystem, type_id: TypeId, substitutions: &mut HashMap, - semantics: Semantics, ) -> TypeId { if let Some(new_type_id) = substitutions.get(&type_id) { return *new_type_id; @@ -23,23 +16,23 @@ pub(crate) fn substitute_type( let result = match types.get(type_id) { Type::Ref(..) => unreachable!(), - Type::Unknown => type_id, - Type::Generic => type_id, - Type::Never => type_id, - Type::Any => type_id, - Type::Bytes => type_id, - Type::Bytes32 => type_id, - Type::PublicKey => type_id, - Type::Int => type_id, - Type::Nil => type_id, - Type::True => type_id, - Type::False => type_id, - Type::Value(..) => type_id, + Type::Unknown + | Type::Generic + | Type::Never + | Type::Any + | Type::Bytes + | Type::Bytes32 + | Type::PublicKey + | Type::Int + | Type::Nil + | Type::True + | Type::False + | Type::Value(..) => type_id, Type::Pair(first, rest) => { let (first, rest) = (*first, *rest); - let new_first = substitute_type(types, first, substitutions, semantics); - let new_rest = substitute_type(types, rest, substitutions, semantics); + let new_first = substitute_type(types, first, substitutions); + let new_rest = substitute_type(types, rest, substitutions); if new_first == first && new_rest == rest { type_id @@ -52,7 +45,7 @@ pub(crate) fn substitute_type( let mut result = Vec::new(); for item in &items { - result.push(substitute_type(types, *item, substitutions, semantics)); + result.push(substitute_type(types, *item, substitutions)); } if result == items { @@ -69,120 +62,95 @@ pub(crate) fn substitute_type( } } substitutions.extend(lazy.substitutions.clone()); - let result = substitute_type(types, lazy.type_id, substitutions, semantics); + let result = substitute_type(types, lazy.type_id, substitutions); substitutions.extend(old_substitutions); result } Type::Alias(alias) => { let alias = alias.clone(); - let new_type_id = substitute_type(types, alias.type_id, substitutions, semantics); - - if semantics == Semantics::Preserve { - if new_type_id == alias.type_id { - type_id - } else { - types.alloc(Type::Alias(Alias { - original_type_id: alias.original_type_id, - type_id: new_type_id, - generic_types: alias.generic_types, - })) - } + let new_type_id = substitute_type(types, alias.type_id, substitutions); + + if new_type_id == alias.type_id { + type_id } else { - new_type_id + types.alloc(Type::Alias(Alias { + original_type_id: alias.original_type_id, + type_id: new_type_id, + generic_types: alias.generic_types, + })) } } Type::Struct(ty) => { let ty = ty.clone(); - let new_type_id = substitute_type(types, ty.type_id, substitutions, semantics); - - if semantics == Semantics::Preserve { - if new_type_id == ty.type_id { - type_id - } else { - types.alloc(Type::Struct(Struct { - original_type_id: ty.original_type_id, - type_id: new_type_id, - field_names: ty.field_names, - rest: ty.rest, - generic_types: ty.generic_types, - })) - } + let new_type_id = substitute_type(types, ty.type_id, substitutions); + + if new_type_id == ty.type_id { + type_id } else { - new_type_id + types.alloc(Type::Struct(Struct { + original_type_id: ty.original_type_id, + type_id: new_type_id, + field_names: ty.field_names, + rest: ty.rest, + generic_types: ty.generic_types, + })) } } Type::Variant(ty) => { let ty = ty.clone(); - let new_type_id = substitute_type(types, ty.type_id, substitutions, semantics); - - if semantics == Semantics::Preserve { - if new_type_id == ty.type_id { - type_id - } else { - types.alloc(Type::Variant(Variant { - original_type_id: ty.original_type_id, - original_enum_type_id: ty.original_enum_type_id, - type_id: new_type_id, - field_names: ty.field_names, - rest: ty.rest, - generic_types: ty.generic_types, - discriminant: ty.discriminant, - })) - } + let new_type_id = substitute_type(types, ty.type_id, substitutions); + + if new_type_id == ty.type_id { + type_id } else { - new_type_id + types.alloc(Type::Variant(Variant { + original_type_id: ty.original_type_id, + original_enum_type_id: ty.original_enum_type_id, + type_id: new_type_id, + field_names: ty.field_names, + rest: ty.rest, + generic_types: ty.generic_types, + discriminant: ty.discriminant, + })) } } Type::Enum(ty) => { let ty = ty.clone(); - let new_type_id = substitute_type(types, ty.type_id, substitutions, semantics); - - if semantics == Semantics::Preserve { - if new_type_id == ty.type_id { - type_id - } else { - types.alloc(Type::Enum(Enum { - original_type_id: ty.original_type_id, - type_id: new_type_id, - has_fields: ty.has_fields, - variants: ty.variants, - })) - } + let new_type_id = substitute_type(types, ty.type_id, substitutions); + + if new_type_id == ty.type_id { + type_id } else { - new_type_id + types.alloc(Type::Enum(Enum { + original_type_id: ty.original_type_id, + type_id: new_type_id, + has_fields: ty.has_fields, + variants: ty.variants, + })) } } Type::Callable(callable) => { let callable = callable.clone(); - let new_return_type = - substitute_type(types, callable.return_type, substitutions, semantics); - - let new_parameters = - substitute_type(types, callable.parameters, substitutions, semantics); - - match semantics { - Semantics::Preserve => { - if new_return_type == callable.return_type - && new_parameters == callable.parameters - { - type_id - } else { - types.alloc(Type::Callable(Callable { - original_type_id: callable.original_type_id, - parameter_names: callable.parameter_names, - parameters: new_parameters, - return_type: new_return_type, - rest: callable.rest, - generic_types: callable.generic_types, - })) - } - } - Semantics::StructuralOnly { callable } => callable, + let new_return_type = substitute_type(types, callable.return_type, substitutions); + + let new_parameters = substitute_type(types, callable.parameters, substitutions); + + if new_return_type == callable.return_type && new_parameters == callable.parameters { + type_id + } else { + types.alloc(Type::Callable(Callable { + original_type_id: callable.original_type_id, + parameter_names: callable.parameter_names, + parameters: new_parameters, + return_type: new_return_type, + rest: callable.rest, + generic_types: callable.generic_types, + })) } } }; diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index f22a501..c2a6317 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -4,7 +4,7 @@ use id_arena::{Arena, Id}; use crate::{ check_type, compare_type, difference_type, replace_type, simplify_check, stringify_type, - substitute_type, Alias, Check, CheckError, Comparison, ComparisonContext, Lazy, Semantics, + substitute_type, Alias, Callable, Check, CheckError, Comparison, ComparisonContext, Lazy, StandardTypes, Type, TypePath, }; @@ -100,6 +100,16 @@ impl TypeSystem { &mut self.arena[type_id] } + pub fn get_recursive(&self, type_id: TypeId) -> &Type { + match self.get(type_id) { + Type::Alias(ty) => self.get_recursive(ty.type_id), + Type::Struct(ty) => self.get_recursive(ty.type_id), + Type::Enum(ty) => self.get_recursive(ty.type_id), + Type::Variant(ty) => self.get_recursive(ty.type_id), + ty => ty, + } + } + pub fn get(&self, type_id: TypeId) -> &Type { match &self.arena[type_id] { Type::Ref(type_id) => self.get(*type_id), @@ -128,6 +138,13 @@ impl TypeSystem { } } + pub fn get_callable(&self, type_id: TypeId) -> Option<&Callable> { + match self.get(type_id) { + Type::Callable(callable) => Some(callable), + _ => None, + } + } + pub fn alloc_list(&mut self, type_id: TypeId) -> TypeId { let mut substitutions = HashMap::new(); substitutions.insert(self.types.generic_list_item, type_id); @@ -183,18 +200,19 @@ impl TypeSystem { &mut self, type_id: TypeId, mut substitutions: HashMap, - semantics: Semantics, ) -> TypeId { - substitute_type(self, type_id, &mut substitutions, semantics) + substitute_type(self, type_id, &mut substitutions) } pub fn check(&mut self, lhs: TypeId, rhs: TypeId) -> Result { + let lhs = self.substitute(lhs, HashMap::new()); + let rhs = self.substitute(rhs, HashMap::new()); check_type(self, lhs, rhs, &mut HashSet::new()).map(simplify_check) } pub fn difference(&mut self, lhs: TypeId, rhs: TypeId) -> TypeId { - let lhs = self.substitute(lhs, HashMap::new(), Semantics::Preserve); - let rhs = self.substitute(rhs, HashMap::new(), Semantics::Preserve); + let lhs = self.substitute(lhs, HashMap::new()); + let rhs = self.substitute(rhs, HashMap::new()); difference_type(self, lhs, rhs, &mut HashSet::new()) } From 0fb17d4aaa7ceb31f9c64a748c8ef9b16db01c35 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 25 Jul 2024 17:55:25 -0400 Subject: [PATCH 075/100] Fix --- .../src/compiler/expr/lambda_expr.rs | 339 ++++++++++-------- crates/rue-compiler/src/compiler/path.rs | 5 +- crates/rue-typing/src/comparison.rs | 6 +- crates/rue-typing/src/semantic_types.rs | 4 +- crates/rue-typing/src/stringify.rs | 15 +- crates/rue-typing/src/type_system.rs | 3 +- 6 files changed, 205 insertions(+), 167 deletions(-) diff --git a/crates/rue-compiler/src/compiler/expr/lambda_expr.rs b/crates/rue-compiler/src/compiler/expr/lambda_expr.rs index 7ce7208..1faa437 100644 --- a/crates/rue-compiler/src/compiler/expr/lambda_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/lambda_expr.rs @@ -1,165 +1,190 @@ -use rue_parser::LambdaExpr; -use rue_typing::TypeId; +use std::collections::HashMap; -use crate::{compiler::Compiler, value::Value}; +use indexmap::IndexSet; +use rue_parser::{AstNode, LambdaExpr}; +use rue_typing::{construct_items, deconstruct_items, Callable, Rest, Type, TypeId}; + +use crate::{ + compiler::Compiler, + hir::Hir, + scope::Scope, + symbol::{Function, Symbol}, + value::Value, + ErrorKind, +}; impl Compiler<'_> { pub fn compile_lambda_expr( &mut self, - _lambda_expr: &LambdaExpr, - _expected_type: Option, + lambda_expr: &LambdaExpr, + expected_type: Option, ) -> Value { - todo!() - // // Precompute the substitutions based on generic types. - // let mut substitutions = HashMap::new(); - - // for generics in &self.generic_type_stack { - // substitutions.extend(generics); - // } - - // // Determine the expected type of the lambda expression. - // let expected = expected_type.and_then(|ty| match self.db.ty(ty) { - // Type::Function(function) => Some(function.clone()), - // _ => None, - // }); - - // // Add the scope so you can track generic types. - // let scope_id = self.db.alloc_scope(Scope::default()); - // self.scope_stack.push(scope_id); - - // let mut generic_types = Vec::new(); - - // // Add the generic types to the scope. - // for generic_type in lambda_expr - // .generic_types() - // .map(|generics| generics.idents()) - // .unwrap_or_default() - // { - // // Create the generic type id. - // let type_id = self.ty.alloc(Type::Generic); - - // // Check for duplicate generic types. - // if self.scope().ty(generic_type.text()).is_some() { - // self.db.error( - // ErrorKind::DuplicateType(generic_type.text().to_string()), - // generic_type.text_range(), - // ); - // } - - // // Add the generic type to the scope and define the token for the generic type. - // self.scope_mut() - // .define_type(generic_type.to_string(), type_id); - - // self.db.insert_type_token(type_id, generic_type); - - // // Add the generic type to the list so it can be added to the function type. - // generic_types.push(type_id); - // } - - // let mut param_types = Vec::new(); - // let mut rest = Rest::Nil; - - // let len = lambda_expr.params().len(); - - // for (i, param) in lambda_expr.params().into_iter().enumerate() { - // // Determine the expected type of the parameter. - // let type_id = param - // .ty() - // .map(|ty| self.compile_type(ty)) - // .or(expected - // .as_ref() - // .and_then(|expected| expected.param_types.get(i).copied())) - // .unwrap_or_else(|| { - // self.db - // .error(ErrorKind::CannotInferType, param.syntax().text_range()); - // self.ty.std().unknown - // }); - - // // Substitute generic types in the parameter type. - // let type_id = self.db.substitute_type(type_id, &substitutions); - - // param_types.push(type_id); - - // if let Some(name) = param.name() { - // let param_type_id = if param.optional().is_some() { - // // If the parameter is optional, wrap the type in a possibly undefined type. - // // This prevents referencing the parameter until it's checked for undefined. - // self.ty.alloc(Type::Optional(type_id)) - // } else { - // type_id - // }; - - // let symbol_id = self.db.alloc_symbol(Symbol::Parameter(param_type_id)); - // self.scope_mut().define_symbol(name.to_string(), symbol_id); - // self.db.insert_symbol_token(symbol_id, name); - // }; - - // let last = i + 1 == len; - // let spread = param.spread().is_some(); - // let optional = param.optional().is_some(); - - // if spread && optional { - // self.db.error( - // ErrorKind::OptionalParameterSpread, - // param.syntax().text_range(), - // ); - // } else if spread && !last { - // self.db.error( - // ErrorKind::InvalidSpreadParameter, - // param.syntax().text_range(), - // ); - // } else if optional && !last { - // self.db.error( - // ErrorKind::InvalidOptionalParameter, - // param.syntax().text_range(), - // ); - // } else if spread { - // rest = Rest::Spread; - // } else if optional { - // rest = Rest::Optional; - // } - // } - - // let Some(body) = lambda_expr.body() else { - // return self.unknown(); - // }; - - // let expected_return_type = lambda_expr - // .ty() - // .map(|ty| self.compile_type(ty)) - // .or(expected.map(|expected| expected.return_type)); - - // self.allow_generic_inference_stack.push(false); - // let body = self.compile_expr(&body, expected_return_type); - // self.allow_generic_inference_stack.pop().unwrap(); - - // let return_type = expected_return_type.unwrap_or(body.type_id); - - // self.scope_stack.pop().unwrap(); - - // self.type_check( - // body.type_id, - // return_type, - // lambda_expr.body().unwrap().syntax().text_range(), - // ); - - // let ty = FunctionType { - // param_types: param_types.clone(), - // rest, - // return_type, - // generic_types: Vec::new(), - // }; - - // let symbol_id = self.db.alloc_symbol(Symbol::Function(Function { - // scope_id, - // hir_id: body.hir_id, - // ty: ty.clone(), - // })); - - // Value::new( - // self.db - // .alloc_hir(Hir::Reference(symbol_id, lambda_expr.syntax().text_range())), - // self.ty.alloc(Type::Function(ty)), - // ) + // Precompute the substitutions based on generic types. + let mut substitutions = HashMap::new(); + + for generics in &self.generic_type_stack { + substitutions.extend(generics); + } + + // Determine the expected type of the lambda expression. + let expected = expected_type.and_then(|ty| self.ty.get_callable(ty).cloned()); + let expected_params = expected.as_ref().and_then(|callable| { + deconstruct_items( + self.ty, + callable.parameters, + callable.parameter_names.len(), + callable.rest, + ) + }); + + // Add the scope so you can track generic types. + let scope_id = self.db.alloc_scope(Scope::default()); + self.scope_stack.push(scope_id); + + let mut generic_types = Vec::new(); + + // Add the generic types to the scope. + for generic_type in lambda_expr + .generic_params() + .map(|generics| generics.names()) + .unwrap_or_default() + { + // Create the generic type id. + let type_id = self.ty.alloc(Type::Generic); + + // Check for duplicate generic types. + if self.scope().ty(generic_type.text()).is_some() { + self.db.error( + ErrorKind::DuplicateType(generic_type.text().to_string()), + generic_type.text_range(), + ); + } + + // Add the generic type to the scope and define the token for the generic type. + self.scope_mut() + .define_type(generic_type.to_string(), type_id); + + self.db.insert_type_token(type_id, generic_type); + + // Add the generic type to the list so it can be added to the function type. + generic_types.push(type_id); + } + + let mut param_types = Vec::new(); + let mut param_names = IndexSet::new(); + let mut rest = Rest::Nil; + + let len = lambda_expr.params().len(); + + for (i, param) in lambda_expr.params().into_iter().enumerate() { + // Determine the expected type of the parameter. + let type_id = param + .ty() + .map(|ty| self.compile_type(ty)) + .or(expected_params + .as_ref() + .and_then(|expected| expected.get(i).copied())) + .unwrap_or_else(|| { + self.db + .error(ErrorKind::CannotInferType, param.syntax().text_range()); + self.ty.std().unknown + }); + + // Substitute generic types in the parameter type. + let type_id = self.ty.substitute(type_id, substitutions.clone()); + + param_types.push(type_id); + + if let Some(name) = param.name() { + let param_type_id = if param.optional().is_some() { + // If the parameter is optional, wrap the type in a possibly undefined type. + // This prevents referencing the parameter until it's checked for undefined. + // TODO: self.ty.alloc(Type::Optional(type_id)) + todo!() + } else { + type_id + }; + + let symbol_id = self.db.alloc_symbol(Symbol::Parameter(param_type_id)); + self.scope_mut().define_symbol(name.to_string(), symbol_id); + param_names.insert(name.to_string()); + self.db.insert_symbol_token(symbol_id, name); + } else { + param_names.insert(format!("#{i}")); + }; + + let last = i + 1 == len; + let spread = param.spread().is_some(); + let optional = param.optional().is_some(); + + if spread && optional { + self.db.error( + ErrorKind::OptionalParameterSpread, + param.syntax().text_range(), + ); + } else if spread && !last { + self.db.error( + ErrorKind::InvalidSpreadParameter, + param.syntax().text_range(), + ); + } else if optional && !last { + self.db.error( + ErrorKind::InvalidOptionalParameter, + param.syntax().text_range(), + ); + } else if spread { + rest = Rest::Spread; + } else if optional { + rest = Rest::Optional; + } + } + + let Some(body) = lambda_expr.body() else { + return self.unknown(); + }; + + let expected_return_type = lambda_expr + .ty() + .map(|ty| self.compile_type(ty)) + .or(expected.map(|expected| expected.return_type)); + + self.allow_generic_inference_stack.push(false); + let body = self.compile_expr(&body, expected_return_type); + self.allow_generic_inference_stack.pop().unwrap(); + + let return_type = expected_return_type.unwrap_or(body.type_id); + + self.scope_stack.pop().unwrap(); + + self.type_check( + body.type_id, + return_type, + lambda_expr.body().unwrap().syntax().text_range(), + ); + + let type_id = self.ty.alloc(Type::Unknown); + let parameters = construct_items(self.ty, param_types.into_iter(), rest); + + *self.ty.get_mut(type_id) = Type::Callable(Callable { + original_type_id: type_id, + parameter_names: param_names, + parameters, + rest, + return_type, + generic_types: Vec::new(), + }); + + let symbol_id = self.db.alloc_symbol(Symbol::Function(Function { + scope_id, + hir_id: body.hir_id, + type_id, + rest, + })); + + Value::new( + self.db + .alloc_hir(Hir::Reference(symbol_id, lambda_expr.syntax().text_range())), + type_id, + ) } } diff --git a/crates/rue-compiler/src/compiler/path.rs b/crates/rue-compiler/src/compiler/path.rs index cb1fe57..bc0c4d8 100644 --- a/crates/rue-compiler/src/compiler/path.rs +++ b/crates/rue-compiler/src/compiler/path.rs @@ -1,5 +1,4 @@ -use std::collections::HashMap; - +use indexmap::IndexMap; use rowan::TextRange; use rue_parser::{AstNode, GenericArgs, PathItem}; use rue_typing::{Lazy, Type, TypeId}; @@ -226,7 +225,7 @@ impl Compiler<'_> { let mut lazy = Lazy { type_id, - substitutions: HashMap::new(), + substitutions: IndexMap::new(), }; for (generic_type, arg) in alias.generic_types.clone().into_iter().zip(generic_args) { diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index 430ed83..6f0ae51 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -234,13 +234,15 @@ pub(crate) fn compare_type( // We need to push substititons onto the stack in order to accurately compare them. (Type::Lazy(lazy), _) => { - ctx.substitution_stack.push(lazy.substitutions.clone()); + ctx.substitution_stack + .push(lazy.substitutions.clone().into_iter().collect()); let result = compare_type(db, lazy.type_id, rhs, ctx); ctx.substitution_stack.pop().unwrap(); result } (_, Type::Lazy(lazy)) => { - ctx.substitution_stack.push(lazy.substitutions.clone()); + ctx.substitution_stack + .push(lazy.substitutions.clone().into_iter().collect()); let result = compare_type(db, lhs, lazy.type_id, ctx); ctx.substitution_stack.pop().unwrap(); result diff --git a/crates/rue-typing/src/semantic_types.rs b/crates/rue-typing/src/semantic_types.rs index 931dc3a..60a2f56 100644 --- a/crates/rue-typing/src/semantic_types.rs +++ b/crates/rue-typing/src/semantic_types.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use indexmap::{IndexMap, IndexSet}; use num_bigint::BigInt; @@ -24,7 +22,7 @@ pub enum Rest { #[derive(Debug, Clone)] pub struct Lazy { pub type_id: TypeId, - pub substitutions: HashMap, + pub substitutions: IndexMap, } /// Represents an alias to a type with a set of generic parameters that must be mapped prior to use. diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs index f31d73f..e95c07c 100644 --- a/crates/rue-typing/src/stringify.rs +++ b/crates/rue-typing/src/stringify.rs @@ -47,7 +47,20 @@ pub(crate) fn stringify_type( result } - Type::Lazy(lazy) => stringify_type(types, lazy.type_id, names, visited), + Type::Lazy(lazy) => { + let name = stringify_type(types, lazy.type_id, names, visited); + let mut generics = "<".to_string(); + + for (index, (_, generic)) in lazy.substitutions.iter().enumerate() { + if index > 0 { + generics.push_str(", "); + } + generics.push_str(&stringify_type(types, *generic, names, visited)); + } + + generics.push('>'); + name + &generics + } Type::Alias(alias) => stringify_type(types, alias.type_id, names, visited), Type::Struct(Struct { type_id, .. }) | Type::Variant(Variant { type_id, .. }) => { stringify_type(types, *type_id, names, visited) diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index c2a6317..f9ea655 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -1,6 +1,7 @@ use std::collections::{HashMap, HashSet}; use id_arena::{Arena, Id}; +use indexmap::IndexMap; use crate::{ check_type, compare_type, difference_type, replace_type, simplify_check, stringify_type, @@ -146,7 +147,7 @@ impl TypeSystem { } pub fn alloc_list(&mut self, type_id: TypeId) -> TypeId { - let mut substitutions = HashMap::new(); + let mut substitutions = IndexMap::new(); substitutions.insert(self.types.generic_list_item, type_id); self.alloc(Type::Lazy(Lazy { type_id: self.types.unmapped_list, From 1a2c3e1996e33ac36fad35abd909abe97bac5ddb Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 25 Jul 2024 18:02:57 -0400 Subject: [PATCH 076/100] Fixes 1 --- crates/rue-compiler/src/compiler/expr/function_call_expr.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs index 39c2a8c..bdb738c 100644 --- a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs @@ -135,6 +135,10 @@ impl Compiler<'_> { let mut type_id = function_type.map_or(self.ty.std().unknown, |expected| expected.return_type); + for (key, val) in &generic_types { + println!("{} = {}", self.type_name(*key), self.type_name(*val)); + } + if !generic_types.is_empty() { type_id = self.ty.substitute(type_id, generic_types); } From cf97d88fa9b1c61dbf845a4763ffbaa27cfd4493 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 25 Jul 2024 21:08:29 -0400 Subject: [PATCH 077/100] Fixes --- crates/rue-compiler/src/compiler.rs | 18 +++--- .../src/compiler/expr/binary_expr.rs | 4 +- .../src/compiler/expr/field_access_expr.rs | 4 +- .../src/compiler/expr/function_call_expr.rs | 6 +- .../src/compiler/expr/guard_expr.rs | 13 ++-- .../src/compiler/expr/initializer_expr.rs | 6 +- .../src/compiler/expr/path_expr.rs | 2 +- crates/rue-compiler/src/compiler/path.rs | 2 +- crates/rue-typing/src/stringify.rs | 61 ++++++++++++------- crates/rue-typing/src/substitute_type.rs | 14 +++-- crates/rue-typing/src/type_system.rs | 13 ++-- 11 files changed, 86 insertions(+), 57 deletions(-) diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index 42a1773..e54c85c 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -120,18 +120,20 @@ impl<'a> Compiler<'a> { None } - fn type_name(&self, type_id: TypeId) -> String { + fn type_name(&self, type_id: TypeId, debug: bool) -> String { let mut names = HashMap::new(); - for &scope_id in &self.scope_stack { - for type_id in self.db.scope(scope_id).local_types() { - if let Some(name) = self.db.scope(scope_id).type_name(type_id) { - names.insert(type_id, name.to_string()); + if !debug { + for &scope_id in &self.scope_stack { + for type_id in self.db.scope(scope_id).local_types() { + if let Some(name) = self.db.scope(scope_id).type_name(type_id) { + names.insert(type_id, name.to_string()); + } } } } - self.ty.stringify_named(type_id, names) + self.ty.stringify_named(type_id, names, debug) } fn type_check(&mut self, from: TypeId, to: TypeId, range: TextRange) { @@ -144,7 +146,7 @@ impl<'a> Compiler<'a> { if comparison > Comparison::Assignable { self.db.error( - ErrorKind::TypeMismatch(self.type_name(from), self.type_name(to)), + ErrorKind::TypeMismatch(self.type_name(from, false), self.type_name(to, false)), range, ); } @@ -160,7 +162,7 @@ impl<'a> Compiler<'a> { if comparison > Comparison::Castable { self.db.error( - ErrorKind::CastMismatch(self.type_name(from), self.type_name(to)), + ErrorKind::CastMismatch(self.type_name(from, false), self.type_name(to, false)), range, ); } diff --git a/crates/rue-compiler/src/compiler/expr/binary_expr.rs b/crates/rue-compiler/src/compiler/expr/binary_expr.rs index ddca43c..3e5ceaa 100644 --- a/crates/rue-compiler/src/compiler/expr/binary_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/binary_expr.rs @@ -152,7 +152,7 @@ impl Compiler<'_> { if self.ty.compare(lhs.type_id, self.ty.std().bytes) > Comparison::Castable { self.db.error( - ErrorKind::NonAtomEquality(self.type_name(lhs.type_id)), + ErrorKind::NonAtomEquality(self.type_name(lhs.type_id, false)), text_range, ); is_atom = false; @@ -160,7 +160,7 @@ impl Compiler<'_> { if self.ty.compare(rhs.type_id, self.ty.std().bytes) > Comparison::Castable { self.db.error( - ErrorKind::NonAtomEquality(self.type_name(rhs.type_id)), + ErrorKind::NonAtomEquality(self.type_name(rhs.type_id, false)), text_range, ); is_atom = false; diff --git a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs index 7b64fdc..f7db906 100644 --- a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs @@ -104,7 +104,7 @@ impl Compiler<'_> { self.db.error( ErrorKind::InvalidFieldAccess( field_name.to_string(), - self.type_name(old_value.type_id), + self.type_name(old_value.type_id, false), ), field_name.text_range(), ); @@ -119,7 +119,7 @@ impl Compiler<'_> { self.db.error( ErrorKind::InvalidFieldAccess( field_name.to_string(), - self.type_name(old_value.type_id), + self.type_name(old_value.type_id, false), ), field_name.text_range(), ); diff --git a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs index bdb738c..c9fdf40 100644 --- a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs @@ -31,7 +31,7 @@ impl Compiler<'_> { if let Some(callee) = callee.as_ref() { if function_type.is_none() { self.db.error( - ErrorKind::UncallableType(self.type_name(callee.type_id)), + ErrorKind::UncallableType(self.type_name(callee.type_id, false)), call.callee().unwrap().syntax().text_range(), ); } @@ -135,10 +135,6 @@ impl Compiler<'_> { let mut type_id = function_type.map_or(self.ty.std().unknown, |expected| expected.return_type); - for (key, val) in &generic_types { - println!("{} = {}", self.type_name(*key), self.type_name(*val)); - } - if !generic_types.is_empty() { type_id = self.ty.substitute(type_id, generic_types); } diff --git a/crates/rue-compiler/src/compiler/expr/guard_expr.rs b/crates/rue-compiler/src/compiler/expr/guard_expr.rs index 6e3a66a..b24ef05 100644 --- a/crates/rue-compiler/src/compiler/expr/guard_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/guard_expr.rs @@ -27,7 +27,10 @@ impl Compiler<'_> { let Ok(check) = self.ty.check(expr.type_id, rhs) else { self.db.error( - ErrorKind::RecursiveTypeCheck(self.type_name(expr.type_id), self.type_name(rhs)), + ErrorKind::RecursiveTypeCheck( + self.type_name(expr.type_id, false), + self.type_name(rhs, false), + ), guard.syntax().text_range(), ); return self.unknown(); @@ -37,8 +40,8 @@ impl Compiler<'_> { Check::True => { self.db.warning( WarningKind::UnnecessaryTypeCheck( - self.type_name(expr.type_id), - self.type_name(rhs), + self.type_name(expr.type_id, false), + self.type_name(rhs, false), ), guard.syntax().text_range(), ); @@ -46,8 +49,8 @@ impl Compiler<'_> { Check::False => { self.db.error( ErrorKind::ImpossibleTypeCheck( - self.type_name(expr.type_id), - self.type_name(rhs), + self.type_name(expr.type_id, false), + self.type_name(rhs, false), ), guard.syntax().text_range(), ); diff --git a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs index d4077ff..fc779fe 100644 --- a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs @@ -64,7 +64,9 @@ impl Compiler<'_> { } } else { self.db.error( - ErrorKind::InvalidEnumVariantInitializer(self.type_name(ty.unwrap())), + ErrorKind::InvalidEnumVariantInitializer( + self.type_name(ty.unwrap(), false), + ), initializer.path().unwrap().syntax().text_range(), ); self.unknown() @@ -72,7 +74,7 @@ impl Compiler<'_> { } Some(_) => { self.db.error( - ErrorKind::UninitializableType(self.type_name(ty.unwrap())), + ErrorKind::UninitializableType(self.type_name(ty.unwrap(), false)), initializer.path().unwrap().syntax().text_range(), ); self.unknown() diff --git a/crates/rue-compiler/src/compiler/expr/path_expr.rs b/crates/rue-compiler/src/compiler/expr/path_expr.rs index 0c19033..3d73718 100644 --- a/crates/rue-compiler/src/compiler/expr/path_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/path_expr.rs @@ -38,7 +38,7 @@ impl Compiler<'_> { if let Type::Variant(variant) = self.ty.get(type_id).clone() { if variant.field_names.is_some() { self.db.error( - ErrorKind::InvalidEnumVariantReference(self.type_name(type_id)), + ErrorKind::InvalidEnumVariantReference(self.type_name(type_id, false)), text_range, ); } diff --git a/crates/rue-compiler/src/compiler/path.rs b/crates/rue-compiler/src/compiler/path.rs index bc0c4d8..36600bb 100644 --- a/crates/rue-compiler/src/compiler/path.rs +++ b/crates/rue-compiler/src/compiler/path.rs @@ -95,7 +95,7 @@ impl Compiler<'_> { Path::Type(type_id) => { let Type::Enum(enum_type) = self.ty.get(type_id) else { self.db.error( - ErrorKind::InvalidTypePath(self.type_name(type_id)), + ErrorKind::InvalidTypePath(self.type_name(type_id, false)), name.text_range(), ); return None; diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs index e95c07c..5027044 100644 --- a/crates/rue-typing/src/stringify.rs +++ b/crates/rue-typing/src/stringify.rs @@ -6,6 +6,7 @@ pub(crate) fn stringify_type( types: &TypeSystem, type_id: TypeId, names: &HashMap, + debug: bool, visited: &mut HashSet, ) -> String { if let Some(name) = names.get(&type_id) { @@ -13,13 +14,27 @@ pub(crate) fn stringify_type( } if !visited.insert(type_id) { - return "{recursive}".to_string(); + return format!( + "{{recursive{}}}", + if debug { + format!("{type_id:?}") + } else { + String::new() + } + ); } let result = match types.get(type_id) { Type::Ref(..) => unreachable!(), Type::Unknown => "{unknown}".to_string(), - Type::Generic => "{generic}".to_string(), + Type::Generic => format!( + "{{generic{}}}", + if debug { + format!("{type_id:?}") + } else { + String::new() + } + ), Type::Never => "Never".to_string(), Type::Any => "Any".to_string(), Type::Bytes => "Bytes".to_string(), @@ -31,8 +46,8 @@ pub(crate) fn stringify_type( Type::Nil => "Nil".to_string(), Type::Value(value) => format!("{value}"), Type::Pair(first, rest) => { - let first = stringify_type(types, *first, names, visited); - let rest = stringify_type(types, *rest, names, visited); + let first = stringify_type(types, *first, names, debug, visited); + let rest = stringify_type(types, *rest, names, debug, visited); format!("({first}, {rest})") } Type::Union(items) => { @@ -42,39 +57,39 @@ pub(crate) fn stringify_type( if index > 0 { result.push_str(" | "); } - result.push_str(&stringify_type(types, *item, names, visited)); + result.push_str(&stringify_type(types, *item, names, debug, visited)); } result } Type::Lazy(lazy) => { - let name = stringify_type(types, lazy.type_id, names, visited); + let name = stringify_type(types, lazy.type_id, names, debug, visited); let mut generics = "<".to_string(); for (index, (_, generic)) in lazy.substitutions.iter().enumerate() { if index > 0 { generics.push_str(", "); } - generics.push_str(&stringify_type(types, *generic, names, visited)); + generics.push_str(&stringify_type(types, *generic, names, debug, visited)); } generics.push('>'); name + &generics } - Type::Alias(alias) => stringify_type(types, alias.type_id, names, visited), + Type::Alias(alias) => stringify_type(types, alias.type_id, names, debug, visited), Type::Struct(Struct { type_id, .. }) | Type::Variant(Variant { type_id, .. }) => { - stringify_type(types, *type_id, names, visited) + stringify_type(types, *type_id, names, debug, visited) } - Type::Enum(Enum { type_id, .. }) => stringify_type(types, *type_id, names, visited), + Type::Enum(Enum { type_id, .. }) => stringify_type(types, *type_id, names, debug, visited), Type::Callable(Callable { parameters, return_type, .. }) => { let mut result = "fun(".to_string(); - result.push_str(&stringify_type(types, *parameters, names, visited)); + result.push_str(&stringify_type(types, *parameters, names, debug, visited)); result.push_str(") -> "); - result.push_str(&stringify_type(types, *return_type, names, visited)); + result.push_str(&stringify_type(types, *return_type, names, debug, visited)); result } }; @@ -97,15 +112,15 @@ mod tests { let db = TypeSystem::new(); let types = db.std(); - assert_eq!(db.stringify(types.unknown), "{unknown}"); - assert_eq!(db.stringify(types.never), "Never"); - assert_eq!(db.stringify(types.bytes), "Bytes"); - assert_eq!(db.stringify(types.bytes32), "Bytes32"); - assert_eq!(db.stringify(types.public_key), "PublicKey"); - assert_eq!(db.stringify(types.int), "Int"); - assert_eq!(db.stringify(types.bool), "Bool"); - assert_eq!(db.stringify(types.nil), "Nil"); - assert_eq!(db.stringify(types.any), "Any"); + assert_eq!(db.stringify(types.unknown, false), "{unknown}"); + assert_eq!(db.stringify(types.never, false), "Never"); + assert_eq!(db.stringify(types.bytes, false), "Bytes"); + assert_eq!(db.stringify(types.bytes32, false), "Bytes32"); + assert_eq!(db.stringify(types.public_key, false), "PublicKey"); + assert_eq!(db.stringify(types.int, false), "Int"); + assert_eq!(db.stringify(types.bool, false), "Bool"); + assert_eq!(db.stringify(types.nil, false), "Nil"); + assert_eq!(db.stringify(types.any, false), "Any"); } #[test] @@ -116,7 +131,7 @@ mod tests { let mut names = HashMap::new(); names.insert(types.any, "CustomAny".to_string()); - assert_eq!(db.stringify_named(types.any, names), "CustomAny"); + assert_eq!(db.stringify_named(types.any, names, false), "CustomAny"); } #[test] @@ -135,7 +150,7 @@ mod tests { ); assert_eq!( - db.stringify_named(callable, HashMap::new()), + db.stringify_named(callable, HashMap::new(), false), "fun((Int, (Bytes, Nil))) -> Bool" ); } diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index e704f3a..e84b02d 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -4,11 +4,18 @@ use crate::{Alias, Callable, Enum, Struct, Type, TypeId, TypeSystem, Variant}; pub(crate) fn substitute_type( types: &mut TypeSystem, - type_id: TypeId, + mut type_id: TypeId, substitutions: &mut HashMap, ) -> TypeId { - if let Some(new_type_id) = substitutions.get(&type_id) { - return *new_type_id; + let mut substituted = false; + + while let Some(new_type_id) = substitutions.get(&type_id) { + type_id = *new_type_id; + substituted = true; + } + + if substituted { + return type_id; } let placeholder = types.alloc(Type::Unknown); @@ -61,7 +68,6 @@ pub(crate) fn substitute_type( old_substitutions.insert(key, old); } } - substitutions.extend(lazy.substitutions.clone()); let result = substitute_type(types, lazy.type_id, substitutions); substitutions.extend(old_substitutions); result diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index f9ea655..d5b500c 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -155,15 +155,20 @@ impl TypeSystem { })) } - pub fn stringify_named(&self, type_id: TypeId, mut names: HashMap) -> String { + pub fn stringify_named( + &self, + type_id: TypeId, + mut names: HashMap, + debug: bool, + ) -> String { for (id, name) in &self.names { names.entry(*id).or_insert_with(|| name.clone()); } - stringify_type(self, type_id, &names, &mut HashSet::new()) + stringify_type(self, type_id, &names, debug, &mut HashSet::new()) } - pub fn stringify(&self, type_id: TypeId) -> String { - self.stringify_named(type_id, HashMap::new()) + pub fn stringify(&self, type_id: TypeId, debug: bool) -> String { + self.stringify_named(type_id, HashMap::new(), debug) } pub fn compare(&self, lhs: TypeId, rhs: TypeId) -> Comparison { From 135f45940bc3ac8ebd44ede4402cfafb7d222362 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 26 Jul 2024 11:29:19 -0400 Subject: [PATCH 078/100] Fixes --- crates/rue-typing/src/check/check_type.rs | 2 +- crates/rue-typing/src/substitute_type.rs | 33 +++++++++-------------- crates/rue-typing/src/test_tools.rs | 14 +++++----- crates/rue-typing/src/type_system.rs | 4 +-- 4 files changed, 24 insertions(+), 29 deletions(-) diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs index 4688320..7ae2869 100644 --- a/crates/rue-typing/src/check/check_type.rs +++ b/crates/rue-typing/src/check/check_type.rs @@ -212,7 +212,7 @@ fn check_union_against_rhs( return Ok(Check::True); } - if let Type::Union(union) = types.get(rhs) { + if let Type::Union(union) = types.get_recursive(rhs) { let rhs_items = union.clone(); let mut result = Vec::new(); for rhs_item in rhs_items { diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index e84b02d..aa31dca 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -4,22 +4,20 @@ use crate::{Alias, Callable, Enum, Struct, Type, TypeId, TypeSystem, Variant}; pub(crate) fn substitute_type( types: &mut TypeSystem, - mut type_id: TypeId, - substitutions: &mut HashMap, + type_id: TypeId, + substitutions: &mut Vec>, ) -> TypeId { - let mut substituted = false; - - while let Some(new_type_id) = substitutions.get(&type_id) { - type_id = *new_type_id; - substituted = true; - } - - if substituted { - return type_id; + for frame in substitutions.iter().rev() { + if let Some(new_type_id) = frame.get(&type_id) { + return *new_type_id; + } } let placeholder = types.alloc(Type::Unknown); - substitutions.insert(type_id, placeholder); + substitutions + .last_mut() + .unwrap() + .insert(type_id, placeholder); let result = match types.get(type_id) { Type::Ref(..) => unreachable!(), @@ -62,15 +60,10 @@ pub(crate) fn substitute_type( } } Type::Lazy(lazy) => { - let mut old_substitutions = HashMap::new(); - for (key, val) in lazy.substitutions.clone() { - if let Some(old) = substitutions.insert(key, val) { - old_substitutions.insert(key, old); - } - } + substitutions.push(lazy.substitutions.clone().into_iter().collect()); let result = substitute_type(types, lazy.type_id, substitutions); - substitutions.extend(old_substitutions); - result + substitutions.pop().unwrap(); + substitute_type(types, result, substitutions) } Type::Alias(alias) => { let alias = alias.clone(); diff --git a/crates/rue-typing/src/test_tools.rs b/crates/rue-typing/src/test_tools.rs index a9bd806..005de1b 100644 --- a/crates/rue-typing/src/test_tools.rs +++ b/crates/rue-typing/src/test_tools.rs @@ -1,12 +1,14 @@ -use indexmap::IndexMap; +use indexmap::{indexmap, IndexMap}; -use crate::{Callable, Rest, Struct, Type, TypeId, TypeSystem}; +use crate::{Callable, Lazy, Rest, Struct, Type, TypeId, TypeSystem}; pub fn alloc_list(db: &mut TypeSystem, item_type_id: TypeId) -> TypeId { - let list = db.alloc(Type::Unknown); - let pair = db.alloc(Type::Pair(item_type_id, list)); - *db.get_mut(list) = Type::Union(vec![pair, db.std().nil]); - list + db.alloc(Type::Lazy(Lazy { + type_id: db.std().unmapped_list, + substitutions: indexmap! { + db.std().generic_list_item => item_type_id, + }, + })) } pub fn alloc_callable( diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index d5b500c..cdeca05 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -205,9 +205,9 @@ impl TypeSystem { pub fn substitute( &mut self, type_id: TypeId, - mut substitutions: HashMap, + substitutions: HashMap, ) -> TypeId { - substitute_type(self, type_id, &mut substitutions) + substitute_type(self, type_id, &mut vec![substitutions]) } pub fn check(&mut self, lhs: TypeId, rhs: TypeId) -> Result { From 57c0f69cbfdc47d7ff17021e1e8c1b6da0116dee Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 26 Jul 2024 11:48:23 -0400 Subject: [PATCH 079/100] Temp fix, I need more tests --- crates/rue-typing/src/check/check_type.rs | 12 ++++++++++++ crates/rue-typing/src/comparison.rs | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs index 7ae2869..1f6509d 100644 --- a/crates/rue-typing/src/check/check_type.rs +++ b/crates/rue-typing/src/check/check_type.rs @@ -265,6 +265,9 @@ fn check_union_against_rhs( Type::PublicKey if attrs.atoms_are_public_key() => Check::IsAtom, Type::PublicKey => Check::And(vec![Check::IsAtom, Check::Length(48)]), Type::Pair(..) if attrs.all_atoms() => Check::False, + Type::Pair(..) if attrs.pairs.len() == 1 && attrs.atom_count == attrs.length - 1 => { + Check::IsPair + } Type::Pair(first, rest) => { let (first, rest) = (*first, *rest); @@ -539,6 +542,15 @@ mod tests { check_str(&mut db, list, pair, "(l val)"); } + #[test] + fn test_check_list_pair_generic() { + let mut db = TypeSystem::new(); + let generic = db.alloc(Type::Generic); + let list = alloc_list(&mut db, generic); + let pair = db.alloc(Type::Pair(generic, list)); + check_str(&mut db, list, pair, "(l val)"); + } + #[test] fn test_check_any_list() { let mut db = TypeSystem::new(); diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index 6f0ae51..4023c11 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -795,4 +795,25 @@ mod tests { Comparison::Castable ); } + + #[test] + fn test_compare_generic_equal() { + let mut db = TypeSystem::new(); + let types = db.std(); + let generic = db.alloc(Type::Generic); + assert_eq!(db.compare(types.int, generic), Comparison::Incompatible); + assert_eq!(db.compare(generic, generic), Comparison::Equal); + } + + #[test] + fn test_compare_generic_list_assignable() { + let mut db = TypeSystem::new(); + let types = db.std(); + let generic = db.alloc(Type::Generic); + let list = alloc_list(&mut db, generic); + let pair = db.alloc(Type::Pair(generic, list)); + assert_eq!(db.compare(types.nil, list), Comparison::Assignable); + assert_eq!(db.compare(list, list), Comparison::Equal); + assert_eq!(db.compare(pair, list), Comparison::Assignable); + } } From ad7a304231b9824b89c6dcb18313fce84ad331a6 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 26 Jul 2024 11:58:59 -0400 Subject: [PATCH 080/100] Remove panics --- crates/rue-compiler/src/compiler/builtins.rs | 5 ++- .../src/compiler/expr/field_access_expr.rs | 31 +++++++++++++------ .../src/compiler/expr/initializer_expr.rs | 25 ++++++++++----- .../src/compiler/expr/lambda_expr.rs | 2 +- .../src/compiler/item/function_item.rs | 2 +- 5 files changed, 45 insertions(+), 20 deletions(-) diff --git a/crates/rue-compiler/src/compiler/builtins.rs b/crates/rue-compiler/src/compiler/builtins.rs index 84d71c8..b476ae8 100644 --- a/crates/rue-compiler/src/compiler/builtins.rs +++ b/crates/rue-compiler/src/compiler/builtins.rs @@ -129,10 +129,13 @@ fn divmod(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { let type_id = ty.alloc(Type::Unknown); + let parameters = ty.alloc(Type::Pair(ty.std().int, ty.std().nil)); + let parameters = ty.alloc(Type::Pair(ty.std().int, parameters)); + *ty.get_mut(type_id) = Type::Callable(Callable { original_type_id: type_id, parameter_names: indexset!["lhs".to_string(), "rhs".to_string()], - parameters: ty.alloc(Type::Pair(int_pair, ty.std().nil)), + parameters, rest: Rest::Nil, return_type: int_pair, generic_types: Vec::new(), diff --git a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs index f7db906..f4658d3 100644 --- a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs @@ -32,7 +32,6 @@ impl Compiler<'_> { let type_id = fields[index]; if index == ty.field_names.len() - 1 && ty.rest == Rest::Optional { - todo!(); // TODO: type_id = self.ty.alloc(Type::Optional(type_id)); } @@ -56,14 +55,28 @@ impl Compiler<'_> { Type::Variant(variant) => { let field_names = variant.field_names.clone().unwrap_or_default(); - let fields = variant - .field_names - .as_ref() - .map(|field_names| { - deconstruct_items(self.ty, variant.type_id, field_names.len(), variant.rest) - .expect("invalid struct type") - }) - .unwrap_or_default(); + let Type::Enum(ty) = self.ty.get(variant.original_enum_type_id) else { + unreachable!(); + }; + + let fields = if ty.has_fields { + let type_id = self + .ty + .get_pair(variant.type_id) + .expect("expected a pair") + .1; + + variant + .field_names + .as_ref() + .map(|field_names| { + deconstruct_items(self.ty, type_id, field_names.len(), variant.rest) + .expect("invalid struct type") + }) + .unwrap_or_default() + } else { + Vec::new() + }; if let Some(index) = field_names.get_index_of(field_name.text()) { let type_id = fields[index]; diff --git a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs index fc779fe..7e1a938 100644 --- a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs @@ -37,13 +37,23 @@ impl Compiler<'_> { } Some(Type::Variant(enum_variant)) => { if let Some(field_names) = enum_variant.field_names { - let fields = deconstruct_items( - self.ty, - enum_variant.type_id, - field_names.len(), - enum_variant.rest, - ) - .expect("invalid variant type"); + let Type::Enum(enum_type) = self.ty.get(enum_variant.original_enum_type_id) + else { + unreachable!(); + }; + + let fields = if enum_type.has_fields { + let type_id = self + .ty + .get_pair(enum_variant.type_id) + .expect("expected a pair") + .1; + + deconstruct_items(self.ty, type_id, field_names.len(), enum_variant.rest) + .expect("invalid struct type") + } else { + Vec::new() + }; let fields_hir_id = self.compile_initializer_fields( &field_names.into_iter().zip(fields).collect(), @@ -111,7 +121,6 @@ impl Compiler<'_> { { // TODO: optional |= matches!(self.db.ty(value.type_id), Type::Optional(..)); // value.type_id = self.db.non_undefined(value.type_id); - todo!() } // Check the type of the field initializer. diff --git a/crates/rue-compiler/src/compiler/expr/lambda_expr.rs b/crates/rue-compiler/src/compiler/expr/lambda_expr.rs index 1faa437..bc21893 100644 --- a/crates/rue-compiler/src/compiler/expr/lambda_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/lambda_expr.rs @@ -100,7 +100,7 @@ impl Compiler<'_> { // If the parameter is optional, wrap the type in a possibly undefined type. // This prevents referencing the parameter until it's checked for undefined. // TODO: self.ty.alloc(Type::Optional(type_id)) - todo!() + type_id } else { type_id }; diff --git a/crates/rue-compiler/src/compiler/item/function_item.rs b/crates/rue-compiler/src/compiler/item/function_item.rs index 6d2c543..44be22e 100644 --- a/crates/rue-compiler/src/compiler/item/function_item.rs +++ b/crates/rue-compiler/src/compiler/item/function_item.rs @@ -74,7 +74,7 @@ impl Compiler<'_> { // If the parameter is optional, wrap the type in a possibly undefined type. // This prevents referencing the parameter until it's checked for undefined. // TODO: self.ty.alloc(Type::Optional(type_id)) - todo!() + type_id } else { // Otherwise, just use the type. type_id From 4baf36efee9fb5a0f1327be8efbda2dca1f71db4 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 26 Jul 2024 13:03:53 -0400 Subject: [PATCH 081/100] Fix bug --- crates/rue-compiler/src/compiler.rs | 18 ++- .../src/compiler/expr/binary_expr.rs | 4 +- .../src/compiler/expr/field_access_expr.rs | 4 +- .../src/compiler/expr/function_call_expr.rs | 2 +- .../src/compiler/expr/guard_expr.rs | 13 +- .../src/compiler/expr/initializer_expr.rs | 6 +- .../src/compiler/expr/path_expr.rs | 2 +- crates/rue-compiler/src/compiler/path.rs | 2 +- crates/rue-typing/src/comparison.rs | 111 +++++++++------- crates/rue-typing/src/debug_type.rs | 123 ++++++++++++++++++ crates/rue-typing/src/lib.rs | 3 +- crates/rue-typing/src/stringify.rs | 61 ++++----- crates/rue-typing/src/type_system.rs | 23 ++-- 13 files changed, 246 insertions(+), 126 deletions(-) create mode 100644 crates/rue-typing/src/debug_type.rs diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index e54c85c..42a1773 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -120,20 +120,18 @@ impl<'a> Compiler<'a> { None } - fn type_name(&self, type_id: TypeId, debug: bool) -> String { + fn type_name(&self, type_id: TypeId) -> String { let mut names = HashMap::new(); - if !debug { - for &scope_id in &self.scope_stack { - for type_id in self.db.scope(scope_id).local_types() { - if let Some(name) = self.db.scope(scope_id).type_name(type_id) { - names.insert(type_id, name.to_string()); - } + for &scope_id in &self.scope_stack { + for type_id in self.db.scope(scope_id).local_types() { + if let Some(name) = self.db.scope(scope_id).type_name(type_id) { + names.insert(type_id, name.to_string()); } } } - self.ty.stringify_named(type_id, names, debug) + self.ty.stringify_named(type_id, names) } fn type_check(&mut self, from: TypeId, to: TypeId, range: TextRange) { @@ -146,7 +144,7 @@ impl<'a> Compiler<'a> { if comparison > Comparison::Assignable { self.db.error( - ErrorKind::TypeMismatch(self.type_name(from, false), self.type_name(to, false)), + ErrorKind::TypeMismatch(self.type_name(from), self.type_name(to)), range, ); } @@ -162,7 +160,7 @@ impl<'a> Compiler<'a> { if comparison > Comparison::Castable { self.db.error( - ErrorKind::CastMismatch(self.type_name(from, false), self.type_name(to, false)), + ErrorKind::CastMismatch(self.type_name(from), self.type_name(to)), range, ); } diff --git a/crates/rue-compiler/src/compiler/expr/binary_expr.rs b/crates/rue-compiler/src/compiler/expr/binary_expr.rs index 3e5ceaa..ddca43c 100644 --- a/crates/rue-compiler/src/compiler/expr/binary_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/binary_expr.rs @@ -152,7 +152,7 @@ impl Compiler<'_> { if self.ty.compare(lhs.type_id, self.ty.std().bytes) > Comparison::Castable { self.db.error( - ErrorKind::NonAtomEquality(self.type_name(lhs.type_id, false)), + ErrorKind::NonAtomEquality(self.type_name(lhs.type_id)), text_range, ); is_atom = false; @@ -160,7 +160,7 @@ impl Compiler<'_> { if self.ty.compare(rhs.type_id, self.ty.std().bytes) > Comparison::Castable { self.db.error( - ErrorKind::NonAtomEquality(self.type_name(rhs.type_id, false)), + ErrorKind::NonAtomEquality(self.type_name(rhs.type_id)), text_range, ); is_atom = false; diff --git a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs index f4658d3..a3cc50a 100644 --- a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs @@ -117,7 +117,7 @@ impl Compiler<'_> { self.db.error( ErrorKind::InvalidFieldAccess( field_name.to_string(), - self.type_name(old_value.type_id, false), + self.type_name(old_value.type_id), ), field_name.text_range(), ); @@ -132,7 +132,7 @@ impl Compiler<'_> { self.db.error( ErrorKind::InvalidFieldAccess( field_name.to_string(), - self.type_name(old_value.type_id, false), + self.type_name(old_value.type_id), ), field_name.text_range(), ); diff --git a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs index c9fdf40..39c2a8c 100644 --- a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs @@ -31,7 +31,7 @@ impl Compiler<'_> { if let Some(callee) = callee.as_ref() { if function_type.is_none() { self.db.error( - ErrorKind::UncallableType(self.type_name(callee.type_id, false)), + ErrorKind::UncallableType(self.type_name(callee.type_id)), call.callee().unwrap().syntax().text_range(), ); } diff --git a/crates/rue-compiler/src/compiler/expr/guard_expr.rs b/crates/rue-compiler/src/compiler/expr/guard_expr.rs index b24ef05..6e3a66a 100644 --- a/crates/rue-compiler/src/compiler/expr/guard_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/guard_expr.rs @@ -27,10 +27,7 @@ impl Compiler<'_> { let Ok(check) = self.ty.check(expr.type_id, rhs) else { self.db.error( - ErrorKind::RecursiveTypeCheck( - self.type_name(expr.type_id, false), - self.type_name(rhs, false), - ), + ErrorKind::RecursiveTypeCheck(self.type_name(expr.type_id), self.type_name(rhs)), guard.syntax().text_range(), ); return self.unknown(); @@ -40,8 +37,8 @@ impl Compiler<'_> { Check::True => { self.db.warning( WarningKind::UnnecessaryTypeCheck( - self.type_name(expr.type_id, false), - self.type_name(rhs, false), + self.type_name(expr.type_id), + self.type_name(rhs), ), guard.syntax().text_range(), ); @@ -49,8 +46,8 @@ impl Compiler<'_> { Check::False => { self.db.error( ErrorKind::ImpossibleTypeCheck( - self.type_name(expr.type_id, false), - self.type_name(rhs, false), + self.type_name(expr.type_id), + self.type_name(rhs), ), guard.syntax().text_range(), ); diff --git a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs index 7e1a938..2bcfbfc 100644 --- a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs @@ -74,9 +74,7 @@ impl Compiler<'_> { } } else { self.db.error( - ErrorKind::InvalidEnumVariantInitializer( - self.type_name(ty.unwrap(), false), - ), + ErrorKind::InvalidEnumVariantInitializer(self.type_name(ty.unwrap())), initializer.path().unwrap().syntax().text_range(), ); self.unknown() @@ -84,7 +82,7 @@ impl Compiler<'_> { } Some(_) => { self.db.error( - ErrorKind::UninitializableType(self.type_name(ty.unwrap(), false)), + ErrorKind::UninitializableType(self.type_name(ty.unwrap())), initializer.path().unwrap().syntax().text_range(), ); self.unknown() diff --git a/crates/rue-compiler/src/compiler/expr/path_expr.rs b/crates/rue-compiler/src/compiler/expr/path_expr.rs index 3d73718..0c19033 100644 --- a/crates/rue-compiler/src/compiler/expr/path_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/path_expr.rs @@ -38,7 +38,7 @@ impl Compiler<'_> { if let Type::Variant(variant) = self.ty.get(type_id).clone() { if variant.field_names.is_some() { self.db.error( - ErrorKind::InvalidEnumVariantReference(self.type_name(type_id, false)), + ErrorKind::InvalidEnumVariantReference(self.type_name(type_id)), text_range, ); } diff --git a/crates/rue-compiler/src/compiler/path.rs b/crates/rue-compiler/src/compiler/path.rs index 36600bb..bc0c4d8 100644 --- a/crates/rue-compiler/src/compiler/path.rs +++ b/crates/rue-compiler/src/compiler/path.rs @@ -95,7 +95,7 @@ impl Compiler<'_> { Path::Type(type_id) => { let Type::Enum(enum_type) = self.ty.get(type_id) else { self.db.error( - ErrorKind::InvalidTypePath(self.type_name(type_id, false)), + ErrorKind::InvalidTypePath(self.type_name(type_id)), name.text_range(), ); return None; diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index 4023c11..07ba65b 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -232,6 +232,51 @@ pub(crate) fn compare_type( max(first, rest) } + // Unions can be assigned to anything so long as each of the items in the union are also. + (Type::Union(items), _) => { + let items = items.clone(); + let mut result = Comparison::Assignable; + + let mut any_castable = false; + + for item in items { + let cmp = compare_type(db, item, rhs, ctx); + result = max(result, cmp); + + if compare_type(db, rhs, item, ctx) <= Comparison::Castable { + any_castable = true; + } + } + + if result == Comparison::Incompatible && any_castable { + Comparison::Superset + } else { + result + } + } + + // Anything can be assigned to a union so long as it's assignable to at least one of the items. + (_, Type::Union(items)) => { + let items = items.clone(); + let mut result = Comparison::Incompatible; + let mut any_incompatible = false; + + for item in &items { + let cmp = compare_type(db, lhs, *item, ctx); + result = min(result, cmp); + + if cmp == Comparison::Incompatible { + any_incompatible = true; + } + } + + if any_incompatible && result == Comparison::Superset { + Comparison::Incompatible + } else { + max(result, Comparison::Assignable) + } + } + // We need to push substititons onto the stack in order to accurately compare them. (Type::Lazy(lazy), _) => { ctx.substitution_stack @@ -267,7 +312,7 @@ pub(crate) fn compare_type( // Variants can be assigned to enums if the structure is assignable and it's the same enum. (Type::Variant(variant), Type::Enum(ty)) => { - let comparison = compare_type(db, variant.type_id, ty.type_id, ctx); + let comparison = compare_type(db, lhs, ty.type_id, ctx); if variant.original_enum_type_id == ty.original_type_id { max(comparison, Comparison::Assignable) @@ -306,51 +351,6 @@ pub(crate) fn compare_type( ), (Type::Callable(..), _) => compare_type(db, lhs, db.std().any, ctx), - // Unions can be assigned to anything so long as each of the items in the union are also. - (Type::Union(items), _) => { - let items = items.clone(); - let mut result = Comparison::Assignable; - - let mut any_castable = false; - - for item in items { - let cmp = compare_type(db, item, rhs, ctx); - result = max(result, cmp); - - if compare_type(db, rhs, item, ctx) <= Comparison::Castable { - any_castable = true; - } - } - - if result == Comparison::Incompatible && any_castable { - Comparison::Superset - } else { - result - } - } - - // Anything can be assigned to a union so long as it's assignable to at least one of the items. - (_, Type::Union(items)) => { - let items = items.clone(); - let mut result = Comparison::Incompatible; - let mut any_incompatible = false; - - for item in items { - let cmp = compare_type(db, lhs, item, ctx); - result = min(result, cmp); - - if cmp == Comparison::Incompatible { - any_incompatible = true; - } - } - - if any_incompatible && result == Comparison::Superset { - Comparison::Incompatible - } else { - max(result, Comparison::Assignable) - } - } - // Generics are resolved by looking up the substitution in the stack. // If we're infering, we'll push the substitution onto the proper generic stack frame. (_, Type::Generic) => { @@ -816,4 +816,23 @@ mod tests { assert_eq!(db.compare(list, list), Comparison::Equal); assert_eq!(db.compare(pair, list), Comparison::Assignable); } + + #[test] + fn test_compare_pair_union() { + let mut db = TypeSystem::new(); + let types = db.std(); + + let pair_enum = db.alloc(Type::Pair(types.int, types.nil)); + let pair_enum = db.alloc(Type::Pair(types.int, pair_enum)); + let zero = db.alloc(Type::Value(BigInt::ZERO)); + let pair_enum = db.alloc(Type::Pair(zero, pair_enum)); + + let int_enum = db.alloc(Type::Pair(types.int, types.nil)); + let one = db.alloc(Type::Value(BigInt::one())); + let int_enum = db.alloc(Type::Pair(one, int_enum)); + + let union = db.alloc(Type::Union(vec![pair_enum, int_enum])); + + assert_eq!(db.compare(pair_enum, union), Comparison::Assignable); + } } diff --git a/crates/rue-typing/src/debug_type.rs b/crates/rue-typing/src/debug_type.rs new file mode 100644 index 0000000..20ef142 --- /dev/null +++ b/crates/rue-typing/src/debug_type.rs @@ -0,0 +1,123 @@ +use std::collections::HashSet; + +use crate::{Type, TypeId, TypeSystem}; + +pub(crate) fn debug_type( + ty: &TypeSystem, + prefix: &str, + type_id: TypeId, + indent: usize, + visited: &mut HashSet, +) -> String { + let mut result = String::new(); + + for _ in 0..indent { + result.push_str(" "); + } + + result.push_str(prefix); + result.push_str(&format!("({}) ", type_id.index())); + + if !visited.insert(type_id) { + result.push_str("..."); + return result; + } + + match ty.get_raw(type_id) { + Type::Unknown => result.push_str("Unknown"), + Type::Any => result.push_str("Any"), + Type::Never => result.push_str("Never"), + Type::Bytes => result.push_str("Bytes"), + Type::Bytes32 => result.push_str("Bytes32"), + Type::PublicKey => result.push_str("PublicKey"), + Type::Int => result.push_str("Int"), + Type::Nil => result.push_str("Nil"), + Type::True => result.push_str("True"), + Type::False => result.push_str("False"), + Type::Generic => result.push_str("Generic"), + Type::Value(value) => result.push_str(&format!("Literal {value}")), + Type::Pair(first, rest) => { + let first = debug_type(ty, "First", *first, indent + 1, visited); + let rest = debug_type(ty, "Rest", *rest, indent + 1, visited); + result.push_str(&format!("Pair\n{first}\n{rest}")); + } + Type::Union(types) => { + result.push_str("Union"); + for type_id in types { + let type_str = debug_type(ty, "", *type_id, indent + 1, visited); + result.push_str(&format!("\n{type_str}")); + } + } + Type::Ref(inner) => { + let inner = debug_type(ty, "", *inner, indent + 1, visited); + result.push_str(&format!("Ref\n{inner}")); + } + Type::Alias(alias) => { + result.push_str(&format!("Alias {}", alias.original_type_id.index())); + if !alias.generic_types.is_empty() { + generics(&mut result, &alias.generic_types); + } + let inner = debug_type(ty, "", alias.type_id, indent + 1, visited); + result.push_str(&format!("\n{inner}")); + } + Type::Lazy(lazy) => { + result.push_str("Lazy"); + if !lazy.substitutions.is_empty() { + result.push_str(" <"); + for (i, (from, to)) in lazy.substitutions.iter().enumerate() { + if i != 0 { + result.push_str(", "); + } + result.push_str(&format!("{} = {}", from.index(), to.index())); + } + result.push('>'); + } + let inner = debug_type(ty, "", lazy.type_id, indent + 1, visited); + result.push_str(&format!("\n{inner}")); + } + Type::Struct(struct_type) => { + result.push_str("Struct"); + if !struct_type.generic_types.is_empty() { + generics(&mut result, &struct_type.generic_types); + } + let inner = debug_type(ty, "", struct_type.type_id, indent + 1, visited); + result.push_str(&format!("\n{inner}")); + } + Type::Enum(enum_type) => { + result.push_str("Enum"); + let inner = debug_type(ty, "", enum_type.type_id, indent + 1, visited); + result.push_str(&format!("\n{inner}")); + } + Type::Variant(variant) => { + result.push_str("Variant"); + let inner = debug_type(ty, "", variant.type_id, indent + 1, visited); + result.push_str(&format!("\n{inner}")); + } + Type::Callable(callable) => { + result.push_str("Callable"); + if !callable.generic_types.is_empty() { + generics(&mut result, &callable.generic_types); + } + let inner = debug_type(ty, "Parameters", callable.parameters, indent + 1, visited); + result.push_str(&format!("\n{inner}")); + let inner = debug_type(ty, "Return", callable.return_type, indent + 1, visited); + result.push_str(&format!("\n{inner}")); + } + } + + visited.remove(&type_id); + result +} + +fn generics(result: &mut String, generics: &[TypeId]) { + if !generics.is_empty() { + result.push_str(" <"); + for (i, type_id) in generics.iter().enumerate() { + if i != 0 { + result.push_str(", "); + } + result.push_str(&format!("{}", type_id.index())); + } + result.push('>'); + } +} diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index e8a06d3..fad806c 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -1,13 +1,13 @@ mod bigint; mod check; mod comparison; +mod debug_type; mod difference; mod replace_type; mod semantic_types; mod standard_types; mod stringify; mod substitute_type; - mod ty; mod type_path; mod type_system; @@ -21,6 +21,7 @@ pub use ty::*; pub use type_path::*; pub use type_system::*; +pub(crate) use debug_type::debug_type; pub(crate) use difference::difference_type; pub(crate) use replace_type::replace_type; pub(crate) use stringify::stringify_type; diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs index 5027044..e95c07c 100644 --- a/crates/rue-typing/src/stringify.rs +++ b/crates/rue-typing/src/stringify.rs @@ -6,7 +6,6 @@ pub(crate) fn stringify_type( types: &TypeSystem, type_id: TypeId, names: &HashMap, - debug: bool, visited: &mut HashSet, ) -> String { if let Some(name) = names.get(&type_id) { @@ -14,27 +13,13 @@ pub(crate) fn stringify_type( } if !visited.insert(type_id) { - return format!( - "{{recursive{}}}", - if debug { - format!("{type_id:?}") - } else { - String::new() - } - ); + return "{recursive}".to_string(); } let result = match types.get(type_id) { Type::Ref(..) => unreachable!(), Type::Unknown => "{unknown}".to_string(), - Type::Generic => format!( - "{{generic{}}}", - if debug { - format!("{type_id:?}") - } else { - String::new() - } - ), + Type::Generic => "{generic}".to_string(), Type::Never => "Never".to_string(), Type::Any => "Any".to_string(), Type::Bytes => "Bytes".to_string(), @@ -46,8 +31,8 @@ pub(crate) fn stringify_type( Type::Nil => "Nil".to_string(), Type::Value(value) => format!("{value}"), Type::Pair(first, rest) => { - let first = stringify_type(types, *first, names, debug, visited); - let rest = stringify_type(types, *rest, names, debug, visited); + let first = stringify_type(types, *first, names, visited); + let rest = stringify_type(types, *rest, names, visited); format!("({first}, {rest})") } Type::Union(items) => { @@ -57,39 +42,39 @@ pub(crate) fn stringify_type( if index > 0 { result.push_str(" | "); } - result.push_str(&stringify_type(types, *item, names, debug, visited)); + result.push_str(&stringify_type(types, *item, names, visited)); } result } Type::Lazy(lazy) => { - let name = stringify_type(types, lazy.type_id, names, debug, visited); + let name = stringify_type(types, lazy.type_id, names, visited); let mut generics = "<".to_string(); for (index, (_, generic)) in lazy.substitutions.iter().enumerate() { if index > 0 { generics.push_str(", "); } - generics.push_str(&stringify_type(types, *generic, names, debug, visited)); + generics.push_str(&stringify_type(types, *generic, names, visited)); } generics.push('>'); name + &generics } - Type::Alias(alias) => stringify_type(types, alias.type_id, names, debug, visited), + Type::Alias(alias) => stringify_type(types, alias.type_id, names, visited), Type::Struct(Struct { type_id, .. }) | Type::Variant(Variant { type_id, .. }) => { - stringify_type(types, *type_id, names, debug, visited) + stringify_type(types, *type_id, names, visited) } - Type::Enum(Enum { type_id, .. }) => stringify_type(types, *type_id, names, debug, visited), + Type::Enum(Enum { type_id, .. }) => stringify_type(types, *type_id, names, visited), Type::Callable(Callable { parameters, return_type, .. }) => { let mut result = "fun(".to_string(); - result.push_str(&stringify_type(types, *parameters, names, debug, visited)); + result.push_str(&stringify_type(types, *parameters, names, visited)); result.push_str(") -> "); - result.push_str(&stringify_type(types, *return_type, names, debug, visited)); + result.push_str(&stringify_type(types, *return_type, names, visited)); result } }; @@ -112,15 +97,15 @@ mod tests { let db = TypeSystem::new(); let types = db.std(); - assert_eq!(db.stringify(types.unknown, false), "{unknown}"); - assert_eq!(db.stringify(types.never, false), "Never"); - assert_eq!(db.stringify(types.bytes, false), "Bytes"); - assert_eq!(db.stringify(types.bytes32, false), "Bytes32"); - assert_eq!(db.stringify(types.public_key, false), "PublicKey"); - assert_eq!(db.stringify(types.int, false), "Int"); - assert_eq!(db.stringify(types.bool, false), "Bool"); - assert_eq!(db.stringify(types.nil, false), "Nil"); - assert_eq!(db.stringify(types.any, false), "Any"); + assert_eq!(db.stringify(types.unknown), "{unknown}"); + assert_eq!(db.stringify(types.never), "Never"); + assert_eq!(db.stringify(types.bytes), "Bytes"); + assert_eq!(db.stringify(types.bytes32), "Bytes32"); + assert_eq!(db.stringify(types.public_key), "PublicKey"); + assert_eq!(db.stringify(types.int), "Int"); + assert_eq!(db.stringify(types.bool), "Bool"); + assert_eq!(db.stringify(types.nil), "Nil"); + assert_eq!(db.stringify(types.any), "Any"); } #[test] @@ -131,7 +116,7 @@ mod tests { let mut names = HashMap::new(); names.insert(types.any, "CustomAny".to_string()); - assert_eq!(db.stringify_named(types.any, names, false), "CustomAny"); + assert_eq!(db.stringify_named(types.any, names), "CustomAny"); } #[test] @@ -150,7 +135,7 @@ mod tests { ); assert_eq!( - db.stringify_named(callable, HashMap::new(), false), + db.stringify_named(callable, HashMap::new()), "fun((Int, (Bytes, Nil))) -> Bool" ); } diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index cdeca05..a29b1c7 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -4,9 +4,9 @@ use id_arena::{Arena, Id}; use indexmap::IndexMap; use crate::{ - check_type, compare_type, difference_type, replace_type, simplify_check, stringify_type, - substitute_type, Alias, Callable, Check, CheckError, Comparison, ComparisonContext, Lazy, - StandardTypes, Type, TypePath, + check_type, compare_type, debug_type, difference_type, replace_type, simplify_check, + stringify_type, substitute_type, Alias, Callable, Check, CheckError, Comparison, + ComparisonContext, Lazy, StandardTypes, Type, TypePath, }; pub type TypeId = Id; @@ -155,20 +155,19 @@ impl TypeSystem { })) } - pub fn stringify_named( - &self, - type_id: TypeId, - mut names: HashMap, - debug: bool, - ) -> String { + pub fn stringify_named(&self, type_id: TypeId, mut names: HashMap) -> String { for (id, name) in &self.names { names.entry(*id).or_insert_with(|| name.clone()); } - stringify_type(self, type_id, &names, debug, &mut HashSet::new()) + stringify_type(self, type_id, &names, &mut HashSet::new()) + } + + pub fn stringify(&self, type_id: TypeId) -> String { + self.stringify_named(type_id, HashMap::new()) } - pub fn stringify(&self, type_id: TypeId, debug: bool) -> String { - self.stringify_named(type_id, HashMap::new(), debug) + pub fn debug(&self, type_id: TypeId) -> String { + debug_type(self, "", type_id, 0, &mut HashSet::new()) } pub fn compare(&self, lhs: TypeId, rhs: TypeId) -> Comparison { From 9f599577bcdb8ed1890f676d0b7e723f1f43fe69 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 26 Jul 2024 14:59:53 -0400 Subject: [PATCH 082/100] Next --- crates/rue-typing/src/comparison.rs | 14 ++++++++++ crates/rue-typing/src/difference.rs | 42 +++-------------------------- tests/enum/enum_type_guard.rue | 2 +- 3 files changed, 19 insertions(+), 39 deletions(-) diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index 07ba65b..cbee658 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -262,6 +262,10 @@ pub(crate) fn compare_type( let mut any_incompatible = false; for item in &items { + if matches!(db.get_recursive(*item), Type::Never) { + continue; + } + let cmp = compare_type(db, lhs, *item, ctx); result = min(result, cmp); @@ -321,6 +325,16 @@ pub(crate) fn compare_type( } } + (Type::Enum(ty), Type::Variant(variant)) => { + let comparison = compare_type(db, ty.type_id, rhs, ctx); + + if variant.original_enum_type_id == ty.original_type_id { + max(comparison, Comparison::Assignable) + } else { + max(comparison, Comparison::Castable) + } + } + // Enums can be assigned if the structure is assignable and it's the same enum. (Type::Enum(lhs), Type::Enum(rhs)) if lhs.original_type_id == rhs.original_type_id => { compare_type(db, lhs.type_id, rhs.type_id, ctx) diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index 231c958..25dd5ec 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -157,18 +157,7 @@ pub(crate) fn difference_type( generic_types: ty.generic_types, })) } - (_, Type::Struct(ty)) => { - let ty = ty.clone(); - let type_id = difference_type(types, lhs, ty.type_id, visited); - - types.alloc(Type::Struct(Struct { - original_type_id: ty.original_type_id, - type_id, - field_names: ty.field_names, - rest: ty.rest, - generic_types: ty.generic_types, - })) - } + (_, Type::Struct(ty)) => difference_type(types, lhs, ty.type_id, visited), (Type::Enum(ty), _) => { let ty = ty.clone(); @@ -181,17 +170,7 @@ pub(crate) fn difference_type( variants: ty.variants, })) } - (_, Type::Enum(ty)) => { - let ty = ty.clone(); - let type_id = difference_type(types, lhs, ty.type_id, visited); - - types.alloc(Type::Enum(Enum { - original_type_id: ty.original_type_id, - type_id, - has_fields: ty.has_fields, - variants: ty.variants, - })) - } + (_, Type::Enum(ty)) => difference_type(types, lhs, ty.type_id, visited), (Type::Variant(variant), _) => { let variant = variant.clone(); @@ -207,20 +186,7 @@ pub(crate) fn difference_type( discriminant: variant.discriminant, })) } - (_, Type::Variant(variant)) => { - let variant = variant.clone(); - let type_id = difference_type(types, lhs, variant.type_id, visited); - - types.alloc(Type::Variant(Variant { - original_type_id: variant.original_type_id, - original_enum_type_id: variant.original_enum_type_id, - field_names: variant.field_names, - type_id, - rest: variant.rest, - generic_types: variant.generic_types, - discriminant: variant.discriminant, - })) - } + (_, Type::Variant(variant)) => difference_type(types, lhs, variant.type_id, visited), (Type::Union(items), _) => { let items = items.clone(); @@ -229,7 +195,7 @@ pub(crate) fn difference_type( for item in &items { let item = difference_type(types, *item, rhs, visited); - if matches!(types.get(item), Type::Never) { + if matches!(types.get_recursive(item), Type::Never) { continue; } result.push(item); diff --git a/tests/enum/enum_type_guard.rue b/tests/enum/enum_type_guard.rue index ff3c3cd..44f4461 100644 --- a/tests/enum/enum_type_guard.rue +++ b/tests/enum/enum_type_guard.rue @@ -15,7 +15,7 @@ fun main() -> Int { raise "Unreachable"; } - assert color is Color::Red; let red: Color::Red = color; + red as Int } From 84a4a49622359ea6112b7f38799d89b2003a24e6 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 26 Jul 2024 23:34:09 -0400 Subject: [PATCH 083/100] More progress --- .../src/compiler/expr/field_access_expr.rs | 1 - examples/cat.rue | 32 +++++++++---------- examples/multisig.rue | 14 ++++---- examples/p2_conditions.rue | 2 +- examples/p2_delegated_or_hidden.rue | 6 ++-- examples/p2_fusion.rue | 2 +- examples/royalty_split.rue | 12 +++---- examples/singleton.rue | 10 +++--- tests/function/external_function.rue | 2 +- tests/function/function_call.rue | 4 +-- tests/function/function_rest_param.rue | 6 ++-- 11 files changed, 45 insertions(+), 46 deletions(-) diff --git a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs index a3cc50a..84cea4c 100644 --- a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs @@ -83,7 +83,6 @@ impl Compiler<'_> { if index == fields.len() - 1 && variant.rest == Rest::Optional { // TODO: type_id = self.ty.alloc(Type::Optional(type_id)); - todo!() } let fields_hir_id = self.db.alloc_hir(Hir::Op(Op::Rest, old_value.hir_id)); diff --git a/examples/cat.rue b/examples/cat.rue index b414691..2598bea 100644 --- a/examples/cat.rue +++ b/examples/cat.rue @@ -29,17 +29,17 @@ struct Truths { type Tail = fun( truths: Truths, parent_is_cat: Bool, - lineage_proof: LineageProof?, + lineage_proof: LineageProof | Nil, extra_delta: Int, - conditions: Condition[], - tail_solution: Any[], -) -> Condition[]; + conditions: List, + tail_solution: List, +) -> List; // Information about the TAIL reveal. // This is revealed with `RunTailCondition`. struct TailInfo { tail_puzzle: Tail, - tail_solution: Any[], + tail_solution: List, } // A custom condition `(51 () -113 tail_puzzle tail_solution)`. @@ -49,7 +49,7 @@ struct RunTailCondition { puzzle_hash: Nil, amount: Int, tail_puzzle: Tail, - tail_solution: Any[], + tail_solution: List, } // Information about the current coin. @@ -90,15 +90,15 @@ inline fun cat_puzzle_hash(cat_info: CatInfo, inner_puzzle_hash: Bytes32) -> Byt fun main( mod_hash: Bytes32, asset_id: Bytes32, - inner_puzzle: fun(...solution: Any) -> Condition[], + inner_puzzle: fun(...solution: Any) -> List, inner_solution: Any, - lineage_proof: LineageProof?, + lineage_proof: LineageProof | Nil, prev_coin_id: Bytes32, my_coin: Coin, next_coin_proof: CoinProof, prev_subtotal: Int, extra_delta: Int, -) -> Condition[] { +) -> List { // For simplicity, we'll pack these values into a struct. let cat_info = CatInfo { mod_hash: mod_hash, @@ -144,12 +144,12 @@ fun main( // Prepend the ring conditions to the morphed conditions. // This ensures that the previous and next CATs are linked. // When they form a ring like this, you can be sure the supply isn't changed. - let conditions: Condition[] = [ + let conditions: List = [ Condition::CreateCoinAnnouncement { - message: RING_MORPH_BYTE + tree_hash([prev_coin_id, prev_subtotal] as Any[]), + message: RING_MORPH_BYTE + tree_hash([prev_coin_id, prev_subtotal] as List), }, Condition::AssertCoinAnnouncement { - announcement_id: sha256(next_coin_id + RING_MORPH_BYTE + tree_hash([my_coin_id, subtotal] as Any[])), + announcement_id: sha256(next_coin_id + RING_MORPH_BYTE + tree_hash([my_coin_id, subtotal] as List)), }, ...morph.conditions, ]; @@ -191,20 +191,20 @@ fun main( struct Morph { // The morphed conditions. - conditions: Condition[], + conditions: List, // The total amount of coins created. sum: Int, // Information about the TAIL, revealed in the conditions. - tail_info: TailInfo?, + tail_info: TailInfo | Nil, } // Morph all of the conditions and extract the TAIL info. fun morph_conditions( - conditions: Condition[], + conditions: List, cat_info: CatInfo, - tail_info: TailInfo?, + tail_info: TailInfo | Nil, ) -> Morph { // If there are no conditions, return an empty morph. if conditions is Nil { diff --git a/examples/multisig.rue b/examples/multisig.rue index 9bba1dc..4205d82 100644 --- a/examples/multisig.rue +++ b/examples/multisig.rue @@ -1,23 +1,23 @@ // This puzzle has not been audited or tested, and is for example purposes only. fun main( - public_keys: PublicKey[], + public_keys: List, required: Int, - indices: Int[], - conditions: Condition[], -) -> Condition[] { + indices: List, + conditions: List, +) -> List { let message = tree_hash(conditions); let agg_sigs = check_signatures(public_keys, required, indices, 0, message); concat(agg_sigs, conditions) } fun check_signatures( - public_keys: PublicKey[], + public_keys: List, required: Int, - indices: Int[], + indices: List, pos: Int, message: Bytes, -) -> Condition[] { +) -> List { if required == 0 { return nil; } diff --git a/examples/p2_conditions.rue b/examples/p2_conditions.rue index f017e95..1f5dd95 100644 --- a/examples/p2_conditions.rue +++ b/examples/p2_conditions.rue @@ -1,6 +1,6 @@ // This puzzle has not been audited or tested, and is for example purposes only. -fun main(public_key: PublicKey, conditions: Condition[]) -> Condition[] { +fun main(public_key: PublicKey, conditions: List) -> List { let agg_sig = Condition::AggSigMe { public_key: public_key, message: tree_hash(conditions), diff --git a/examples/p2_delegated_or_hidden.rue b/examples/p2_delegated_or_hidden.rue index a4f66e4..188add2 100644 --- a/examples/p2_delegated_or_hidden.rue +++ b/examples/p2_delegated_or_hidden.rue @@ -2,10 +2,10 @@ fun main( synthetic_pk: PublicKey, - original_pk: PublicKey?, - delegated_puzzle: fun(...solution: Any) -> Condition[], + original_pk: PublicKey | Nil, + delegated_puzzle: fun(...solution: Any) -> List, delegated_solution: Any -) -> Condition[] { +) -> List { let conditions = delegated_puzzle(...delegated_solution); let delegated_puzzle_hash = tree_hash(delegated_puzzle); diff --git a/examples/p2_fusion.rue b/examples/p2_fusion.rue index dbf19d0..b01324e 100644 --- a/examples/p2_fusion.rue +++ b/examples/p2_fusion.rue @@ -20,7 +20,7 @@ fun main( my_inner_puzzle_hash: Bytes32, my_amount: Int, p2_puzzle_hash: Bytes32, -) -> Condition[] { +) -> List { // The NFT singleton has the same mod hash and launcher puzzle hash as the fusion singleton. let nft_singleton = SingletonInfo { mod_hash: fusion_singleton.mod_hash, diff --git a/examples/royalty_split.rue b/examples/royalty_split.rue index 6078916..a6922f8 100644 --- a/examples/royalty_split.rue +++ b/examples/royalty_split.rue @@ -5,7 +5,7 @@ struct Payout { share: Int, } -fun main(payouts: Payout[], total_shares: Int, my_amount: Int) -> Condition[] { +fun main(payouts: List, total_shares: Int, my_amount: Int) -> List { let announcement = Condition::CreateCoinAnnouncement { message: '$' }; let assert_amount = Condition::AssertMyAmount { amount: my_amount }; @@ -14,13 +14,13 @@ fun main(payouts: Payout[], total_shares: Int, my_amount: Int) -> Condition[] { } fun calculate_amount_and_split( - payouts: Payout[], + payouts: List, total_amount: Int, total_shares: Int, shares_sum: Int, remaining_amount: Int, -) -> Condition[] { - if payouts is (Payout, Payout[]) { +) -> List { + if payouts is (Payout, List) { let amount = get_amount(payouts.first, total_amount, total_shares); return split_amount_and_create_coins(payouts, amount, total_amount, total_shares, shares_sum, remaining_amount); } @@ -29,13 +29,13 @@ fun calculate_amount_and_split( } fun split_amount_and_create_coins( - payouts: (Payout, Payout[]), + payouts: (Payout, List), this_amount: Int, total_amount: Int, total_shares: Int, shares_sum: Int, remaining_amount: Int, -) -> Condition[] { +) -> List { let payout = payouts.first; let create_coin = Condition::CreateCoin { puzzle_hash: payout.puzzle_hash, diff --git a/examples/singleton.rue b/examples/singleton.rue index 8112a28..7cff2b7 100644 --- a/examples/singleton.rue +++ b/examples/singleton.rue @@ -8,7 +8,7 @@ struct Singleton { struct LineageProof { parent_parent_coin_info: Bytes32, - parent_inner_puzzle_hash: Bytes32?, + parent_inner_puzzle_hash: Bytes32 | Nil, parent_amount: Int, } @@ -18,11 +18,11 @@ fun singleton_puzzle_hash(singleton: Singleton, inner_puzzle_hash: Bytes32) -> B fun main( singleton: Singleton, - inner_puzzle: fun(...solution: Any) -> Condition[], + inner_puzzle: fun(...solution: Any) -> List, lineage_proof: LineageProof, my_amount: Int, inner_solution: Any, -) -> Condition[] { +) -> List { // Ensure that the amount is odd. assert my_amount & 1 == 1; @@ -55,9 +55,9 @@ fun main( fun morph_conditions( singleton: Singleton, - conditions: Condition[], + conditions: List, found_singleton_output: Bool, -) -> Condition[] { +) -> List { if conditions is Nil { // We must have a singleton output. assert found_singleton_output; diff --git a/tests/function/external_function.rue b/tests/function/external_function.rue index 97e6602..665c977 100644 --- a/tests/function/external_function.rue +++ b/tests/function/external_function.rue @@ -1,3 +1,3 @@ -fun main(conditions: fun(...solution: Any) -> Condition[]) -> Condition[] { +fun main(conditions: fun(...solution: Any) -> List) -> List { conditions(...nil) } diff --git a/tests/function/function_call.rue b/tests/function/function_call.rue index 0655ef1..4996b81 100644 --- a/tests/function/function_call.rue +++ b/tests/function/function_call.rue @@ -68,7 +68,7 @@ fun two_optional(_a: Int, _b?: Int) -> Bool { true } -fun one_spread_list(..._a: Int[]) -> Bool { +fun one_spread_list(..._a: List) -> Bool { true } @@ -76,7 +76,7 @@ fun one_spread_raw(..._a: Int) -> Bool { true } -fun two_spread_list(_a: Int, ..._b: Int[]) -> Bool { +fun two_spread_list(_a: Int, ..._b: List) -> Bool { true } diff --git a/tests/function/function_rest_param.rue b/tests/function/function_rest_param.rue index 667d63c..caadd06 100644 --- a/tests/function/function_rest_param.rue +++ b/tests/function/function_rest_param.rue @@ -2,15 +2,15 @@ fun main() -> Int { sum(...range_inclusive(1, 10)) } -fun range_inclusive(start: Int, end: Int) -> Int[] { +fun range_inclusive(start: Int, end: Int) -> List { if start > end { return nil; } [start, ...range_inclusive(start + 1, end)] } -fun sum(...nums: Int[]) -> Int { - if nums is (Int, Int[]) { +fun sum(...nums: List) -> Int { + if nums is (Int, List) { nums.first + sum(...nums.rest) } else { 0 From db755064a56091d469e224e7708c5b094696c49f Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 26 Jul 2024 23:59:00 -0400 Subject: [PATCH 084/100] Remove optional --- crates/rue-compiler/src/compiler/builtins.rs | 18 +-- .../src/compiler/expr/field_access_expr.rs | 28 ++-- .../src/compiler/expr/function_call_expr.rs | 64 ++++---- .../src/compiler/expr/initializer_expr.rs | 44 ++---- .../src/compiler/expr/lambda_expr.rs | 51 ++---- .../src/compiler/item/enum_item.rs | 6 +- .../src/compiler/item/function_item.rs | 50 ++---- .../src/compiler/item/struct_item.rs | 34 ++-- .../src/compiler/ty/function_type.rs | 36 ++--- crates/rue-compiler/src/dependency_graph.rs | 8 +- crates/rue-compiler/src/error.rs | 22 +-- crates/rue-compiler/src/lowerer.rs | 3 +- crates/rue-compiler/src/symbol.rs | 4 +- crates/rue-compiler/stdlib.rue | 6 +- crates/rue-parser/src/ast.rs | 28 ---- crates/rue-parser/src/grammar.rs | 4 - crates/rue-typing/src/check/check_type.rs | 8 +- crates/rue-typing/src/comparison.rs | 16 +- crates/rue-typing/src/difference.rs | 4 +- crates/rue-typing/src/replace_type.rs | 4 +- crates/rue-typing/src/semantic_types.rs | 146 +++++------------- crates/rue-typing/src/stringify.rs | 4 +- crates/rue-typing/src/substitute_type.rs | 6 +- crates/rue-typing/src/test_tools.rs | 58 ++----- tests/block/block_let_function.rue | 2 +- tests/function/function_call.rue | 25 --- tests/function/function_optional_param.rue | 11 -- tests/function/lambda_optional_param.rue | 10 -- tests/struct/struct_inner_optional.rue | 23 --- tests/struct/struct_optional.rue | 19 --- tests/struct/struct_single_optional.rue | 11 -- 31 files changed, 208 insertions(+), 545 deletions(-) delete mode 100644 tests/function/function_optional_param.rue delete mode 100644 tests/function/lambda_optional_param.rue delete mode 100644 tests/struct/struct_inner_optional.rue delete mode 100644 tests/struct/struct_optional.rue delete mode 100644 tests/struct/struct_single_optional.rue diff --git a/crates/rue-compiler/src/compiler/builtins.rs b/crates/rue-compiler/src/compiler/builtins.rs index b476ae8..0283c07 100644 --- a/crates/rue-compiler/src/compiler/builtins.rs +++ b/crates/rue-compiler/src/compiler/builtins.rs @@ -1,6 +1,6 @@ use indexmap::indexset; use rowan::TextRange; -use rue_typing::{Callable, Rest, Type, TypeSystem}; +use rue_typing::{Callable, Type, TypeSystem}; use crate::{ hir::{BinOp, Hir, Op}, @@ -73,7 +73,7 @@ fn sha256(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { original_type_id: type_id, parameter_names: indexset!["bytes".to_string()], parameters: ty.alloc(Type::Pair(ty.std().bytes, ty.std().nil)), - rest: Rest::Nil, + nil_terminated: true, return_type: ty.std().bytes32, generic_types: Vec::new(), }); @@ -82,7 +82,7 @@ fn sha256(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { scope_id, hir_id, type_id, - rest: Rest::Nil, + nil_terminated: true, })) } @@ -100,7 +100,7 @@ fn pubkey_for_exp(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { original_type_id: type_id, parameter_names: indexset!["exponent".to_string()], parameters: ty.alloc(Type::Pair(ty.std().bytes32, ty.std().nil)), - rest: Rest::Nil, + nil_terminated: true, return_type: ty.std().public_key, generic_types: Vec::new(), }); @@ -109,7 +109,7 @@ fn pubkey_for_exp(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { scope_id, hir_id, type_id, - rest: Rest::Nil, + nil_terminated: true, })) } @@ -136,7 +136,7 @@ fn divmod(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { original_type_id: type_id, parameter_names: indexset!["lhs".to_string(), "rhs".to_string()], parameters, - rest: Rest::Nil, + nil_terminated: true, return_type: int_pair, generic_types: Vec::new(), }); @@ -145,7 +145,7 @@ fn divmod(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { scope_id, hir_id, type_id, - rest: Rest::Nil, + nil_terminated: true, })) } @@ -174,7 +174,7 @@ fn substr(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { original_type_id: type_id, parameter_names: indexset!["value".to_string(), "start".to_string(), "end".to_string()], parameters, - rest: Rest::Nil, + nil_terminated: true, return_type: ty.std().bytes, generic_types: Vec::new(), }); @@ -183,6 +183,6 @@ fn substr(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { scope_id, hir_id, type_id, - rest: Rest::Nil, + nil_terminated: true, })) } diff --git a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs index 84cea4c..9d51ef8 100644 --- a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs @@ -1,5 +1,5 @@ use rue_parser::FieldAccessExpr; -use rue_typing::{deconstruct_items, Rest, Type}; +use rue_typing::{deconstruct_items, Type}; use crate::{ compiler::Compiler, @@ -25,21 +25,18 @@ impl Compiler<'_> { let mut new_value = match self.ty.get(old_value.type_id).clone() { Type::Unknown => return self.unknown(), Type::Struct(ty) => { - let fields = deconstruct_items(self.ty, ty.type_id, ty.field_names.len(), ty.rest) - .expect("invalid struct type"); + let fields = + deconstruct_items(self.ty, ty.type_id, ty.field_names.len(), ty.nil_terminated) + .expect("invalid struct type"); if let Some(index) = ty.field_names.get_index_of(field_name.text()) { let type_id = fields[index]; - if index == ty.field_names.len() - 1 && ty.rest == Rest::Optional { - // TODO: type_id = self.ty.alloc(Type::Optional(type_id)); - } - Value::new( self.compile_index( old_value.hir_id, index, - index == ty.field_names.len() - 1 && ty.rest != Rest::Nil, + index == ty.field_names.len() - 1 && !ty.nil_terminated, ), type_id, ) @@ -70,8 +67,13 @@ impl Compiler<'_> { .field_names .as_ref() .map(|field_names| { - deconstruct_items(self.ty, type_id, field_names.len(), variant.rest) - .expect("invalid struct type") + deconstruct_items( + self.ty, + type_id, + field_names.len(), + variant.nil_terminated, + ) + .expect("invalid struct type") }) .unwrap_or_default() } else { @@ -81,17 +83,13 @@ impl Compiler<'_> { if let Some(index) = field_names.get_index_of(field_name.text()) { let type_id = fields[index]; - if index == fields.len() - 1 && variant.rest == Rest::Optional { - // TODO: type_id = self.ty.alloc(Type::Optional(type_id)); - } - let fields_hir_id = self.db.alloc_hir(Hir::Op(Op::Rest, old_value.hir_id)); Value::new( self.compile_index( fields_hir_id, index, - index == fields.len() - 1 && variant.rest != Rest::Nil, + index == fields.len() - 1 && !variant.nil_terminated, ), type_id, ) diff --git a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs index 39c2a8c..f0eff1e 100644 --- a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use rowan::TextRange; use rue_parser::{AstNode, FunctionCallExpr}; -use rue_typing::{deconstruct_items, unwrap_list, Callable, Rest, Type, TypeId}; +use rue_typing::{deconstruct_items, unwrap_list, Callable, Type, TypeId}; use crate::{compiler::Compiler, hir::Hir, value::Value, ErrorKind}; @@ -23,8 +23,13 @@ impl Compiler<'_> { }); let parameter_types = function_type.as_ref().map(|ty| { - deconstruct_items(self.ty, ty.parameters, ty.parameter_names.len(), ty.rest) - .expect("invalid function type") + deconstruct_items( + self.ty, + ty.parameters, + ty.parameter_names.len(), + ty.nil_terminated, + ) + .expect("invalid function type") }); // Make sure the callee is callable, if present. @@ -67,7 +72,7 @@ impl Compiler<'_> { if i < parameter_types.len() { Some(parameter_types[i]) - } else if ty.rest == Rest::Spread { + } else if !ty.nil_terminated { unwrap_list(self.ty, *parameter_types.last().unwrap()) } else { None @@ -101,7 +106,7 @@ impl Compiler<'_> { let parameter_types = parameter_types.as_ref().unwrap(); if last && spread { - if function.rest != Rest::Spread { + if function.nil_terminated { self.db.error( ErrorKind::UnsupportedFunctionSpread, call_args[i].syntax().text_range(), @@ -110,7 +115,7 @@ impl Compiler<'_> { let expected_type = *parameter_types.last().unwrap(); self.type_check(type_id, expected_type, call_args[i].syntax().text_range()); } - } else if function.rest == Rest::Spread && i >= parameter_types.len() - 1 { + } else if !function.nil_terminated && i >= parameter_types.len() - 1 { if let Some(inner_list_type) = unwrap_list(self.ty, *parameter_types.last().unwrap()) { @@ -156,38 +161,25 @@ impl Compiler<'_> { length: usize, text_range: TextRange, ) { - match function.rest { - Rest::Nil => { - if length != parameter_types.len() { - self.db.error( - ErrorKind::ArgumentMismatch(length, parameter_types.len()), - text_range, - ); - } - } - Rest::Optional => { - if length != parameter_types.len() && length != parameter_types.len() - 1 { - self.db.error( - ErrorKind::ArgumentMismatchOptional(length, parameter_types.len()), - text_range, - ); - } + if function.nil_terminated { + if length != parameter_types.len() { + self.db.error( + ErrorKind::ArgumentMismatch(length, parameter_types.len()), + text_range, + ); } - Rest::Spread => { - if unwrap_list(self.ty, *parameter_types.last().unwrap()).is_some() { - if length < parameter_types.len() - 1 { - self.db.error( - ErrorKind::ArgumentMismatchSpread(length, parameter_types.len()), - text_range, - ); - } - } else if length != parameter_types.len() { - self.db.error( - ErrorKind::ArgumentMismatch(length, parameter_types.len()), - text_range, - ); - } + } else if unwrap_list(self.ty, *parameter_types.last().unwrap()).is_some() { + if length < parameter_types.len() - 1 { + self.db.error( + ErrorKind::ArgumentMismatchSpread(length, parameter_types.len()), + text_range, + ); } + } else if length != parameter_types.len() { + self.db.error( + ErrorKind::ArgumentMismatch(length, parameter_types.len()), + text_range, + ); } } } diff --git a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs index 2bcfbfc..d927334 100644 --- a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use indexmap::IndexMap; use rowan::TextRange; use rue_parser::{AstNode, InitializerExpr, InitializerField}; -use rue_typing::{bigint_to_bytes, deconstruct_items, Rest, Type, TypeId}; +use rue_typing::{bigint_to_bytes, deconstruct_items, Type, TypeId}; use crate::{compiler::Compiler, hir::Hir, value::Value, ErrorKind, HirId}; @@ -19,13 +19,13 @@ impl Compiler<'_> { self.ty, struct_type.type_id, struct_type.field_names.len(), - struct_type.rest, + struct_type.nil_terminated, ) .expect("invalid variant type"); let hir_id = self.compile_initializer_fields( &struct_type.field_names.into_iter().zip(fields).collect(), - struct_type.rest, + struct_type.nil_terminated, initializer.fields(), initializer.syntax().text_range(), ); @@ -49,15 +49,20 @@ impl Compiler<'_> { .expect("expected a pair") .1; - deconstruct_items(self.ty, type_id, field_names.len(), enum_variant.rest) - .expect("invalid struct type") + deconstruct_items( + self.ty, + type_id, + field_names.len(), + enum_variant.nil_terminated, + ) + .expect("invalid struct type") } else { Vec::new() }; let fields_hir_id = self.compile_initializer_fields( &field_names.into_iter().zip(fields).collect(), - enum_variant.rest, + enum_variant.nil_terminated, initializer.fields(), initializer.syntax().text_range(), ); @@ -94,12 +99,11 @@ impl Compiler<'_> { fn compile_initializer_fields( &mut self, struct_fields: &IndexMap, - rest: Rest, + nil_terminated: bool, initializer_fields: Vec, text_range: TextRange, ) -> HirId { let mut specified_fields = HashMap::new(); - let optional = false; for field in initializer_fields { let Some(name) = field.name() else { @@ -113,14 +117,6 @@ impl Compiler<'_> { .map(|expr| self.compile_expr(&expr, expected_type)) .unwrap_or(self.unknown()); - // Resolve optional fields. - if rest == Rest::Optional - && struct_fields.get_index_of(name.text()) == Some(struct_fields.len() - 1) - { - // TODO: optional |= matches!(self.db.ty(value.type_id), Type::Optional(..)); - // value.type_id = self.db.non_undefined(value.type_id); - } - // Check the type of the field initializer. self.type_check( value.type_id, @@ -147,14 +143,8 @@ impl Compiler<'_> { // Check for any missing fields and report them. let missing_fields: Vec = struct_fields .keys() - .enumerate() - .filter(|&(i, name)| { - if rest == Rest::Optional && i == struct_fields.len() - 1 { - return false; - } - !specified_fields.contains_key(name) - }) - .map(|(_, name)| name.to_string()) + .filter(|name| !specified_fields.contains_key(*name)) + .cloned() .collect(); if !missing_fields.is_empty() { @@ -170,13 +160,9 @@ impl Compiler<'_> { for (i, field) in struct_fields.keys().rev().enumerate() { let value = specified_fields.get(field).copied(); - if i == 0 && rest == Rest::Optional && value.is_none() { - continue; - } - let field = value.unwrap_or(self.builtins.unknown); - if i == 0 && (rest == Rest::Spread || (rest == Rest::Optional && optional)) { + if i == 0 && !nil_terminated { hir_id = field; } else { hir_id = self.db.alloc_hir(Hir::Pair(field, hir_id)); diff --git a/crates/rue-compiler/src/compiler/expr/lambda_expr.rs b/crates/rue-compiler/src/compiler/expr/lambda_expr.rs index bc21893..c0f9105 100644 --- a/crates/rue-compiler/src/compiler/expr/lambda_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/lambda_expr.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use indexmap::IndexSet; use rue_parser::{AstNode, LambdaExpr}; -use rue_typing::{construct_items, deconstruct_items, Callable, Rest, Type, TypeId}; +use rue_typing::{construct_items, deconstruct_items, Callable, Type, TypeId}; use crate::{ compiler::Compiler, @@ -33,7 +33,7 @@ impl Compiler<'_> { self.ty, callable.parameters, callable.parameter_names.len(), - callable.rest, + callable.nil_terminated, ) }); @@ -72,7 +72,7 @@ impl Compiler<'_> { let mut param_types = Vec::new(); let mut param_names = IndexSet::new(); - let mut rest = Rest::Nil; + let mut nil_terminated = true; let len = lambda_expr.params().len(); @@ -96,16 +96,7 @@ impl Compiler<'_> { param_types.push(type_id); if let Some(name) = param.name() { - let param_type_id = if param.optional().is_some() { - // If the parameter is optional, wrap the type in a possibly undefined type. - // This prevents referencing the parameter until it's checked for undefined. - // TODO: self.ty.alloc(Type::Optional(type_id)) - type_id - } else { - type_id - }; - - let symbol_id = self.db.alloc_symbol(Symbol::Parameter(param_type_id)); + let symbol_id = self.db.alloc_symbol(Symbol::Parameter(type_id)); self.scope_mut().define_symbol(name.to_string(), symbol_id); param_names.insert(name.to_string()); self.db.insert_symbol_token(symbol_id, name); @@ -115,27 +106,15 @@ impl Compiler<'_> { let last = i + 1 == len; let spread = param.spread().is_some(); - let optional = param.optional().is_some(); - if spread && optional { - self.db.error( - ErrorKind::OptionalParameterSpread, - param.syntax().text_range(), - ); - } else if spread && !last { - self.db.error( - ErrorKind::InvalidSpreadParameter, - param.syntax().text_range(), - ); - } else if optional && !last { - self.db.error( - ErrorKind::InvalidOptionalParameter, - param.syntax().text_range(), - ); - } else if spread { - rest = Rest::Spread; - } else if optional { - rest = Rest::Optional; + if spread { + if !last { + self.db.error( + ErrorKind::InvalidSpreadParameter, + param.syntax().text_range(), + ); + } + nil_terminated = false; } } @@ -163,13 +142,13 @@ impl Compiler<'_> { ); let type_id = self.ty.alloc(Type::Unknown); - let parameters = construct_items(self.ty, param_types.into_iter(), rest); + let parameters = construct_items(self.ty, param_types.into_iter(), nil_terminated); *self.ty.get_mut(type_id) = Type::Callable(Callable { original_type_id: type_id, parameter_names: param_names, parameters, - rest, + nil_terminated, return_type, generic_types: Vec::new(), }); @@ -178,7 +157,7 @@ impl Compiler<'_> { scope_id, hir_id: body.hir_id, type_id, - rest, + nil_terminated, })); Value::new( diff --git a/crates/rue-compiler/src/compiler/item/enum_item.rs b/crates/rue-compiler/src/compiler/item/enum_item.rs index db802f5..80391c9 100644 --- a/crates/rue-compiler/src/compiler/item/enum_item.rs +++ b/crates/rue-compiler/src/compiler/item/enum_item.rs @@ -99,7 +99,7 @@ impl Compiler<'_> { self.type_definition_stack.push(variant_type_id); // Compile the fields of the variant. - let (fields, rest) = variant + let (fields, nil_terminated) = variant .fields() .map(|ast| self.compile_struct_fields(ast.fields())) .unwrap_or_default(); @@ -145,7 +145,7 @@ impl Compiler<'_> { [discriminant_type] .into_iter() .chain(fields.values().copied()), - rest, + nil_terminated, ) } else { discriminant_type @@ -160,7 +160,7 @@ impl Compiler<'_> { None }, type_id, - rest, + nil_terminated, discriminant, generic_types: Vec::new(), }); diff --git a/crates/rue-compiler/src/compiler/item/function_item.rs b/crates/rue-compiler/src/compiler/item/function_item.rs index 44be22e..cdbbfc9 100644 --- a/crates/rue-compiler/src/compiler/item/function_item.rs +++ b/crates/rue-compiler/src/compiler/item/function_item.rs @@ -1,5 +1,5 @@ use rue_parser::{AstNode, FunctionItem}; -use rue_typing::{construct_items, Callable, Rest, Type}; +use rue_typing::{construct_items, Callable, Type}; use crate::{ compiler::Compiler, @@ -49,7 +49,7 @@ impl Compiler<'_> { let mut param_types = Vec::new(); let mut param_names = Vec::new(); - let mut rest = Rest::Nil; + let mut nil_terminated = true; let params = function_item.params(); let len = params.len(); @@ -70,15 +70,7 @@ impl Compiler<'_> { // Add the parameter type to the list and update the parameter symbol. param_types.push(type_id); - *self.db.symbol_mut(symbol_id) = Symbol::Parameter(if param.optional().is_some() { - // If the parameter is optional, wrap the type in a possibly undefined type. - // This prevents referencing the parameter until it's checked for undefined. - // TODO: self.ty.alloc(Type::Optional(type_id)) - type_id - } else { - // Otherwise, just use the type. - type_id - }); + *self.db.symbol_mut(symbol_id) = Symbol::Parameter(type_id); // Add the parameter to the scope and define the token for the parameter. if let Some(name) = param.name() { @@ -99,27 +91,15 @@ impl Compiler<'_> { // Check if it's a spread or optional parameter. let last = i + 1 == len; let spread = param.spread().is_some(); - let optional = param.optional().is_some(); - if spread && optional { - self.db.error( - ErrorKind::OptionalParameterSpread, - param.syntax().text_range(), - ); - } else if spread && !last { - self.db.error( - ErrorKind::InvalidSpreadParameter, - param.syntax().text_range(), - ); - } else if optional && !last { - self.db.error( - ErrorKind::InvalidOptionalParameter, - param.syntax().text_range(), - ); - } else if spread { - rest = Rest::Spread; - } else if optional { - rest = Rest::Optional; + if spread { + if !last { + self.db.error( + ErrorKind::InvalidSpreadParameter, + param.syntax().text_range(), + ); + } + nil_terminated = false; } self.symbol_stack.pop().unwrap(); @@ -142,9 +122,9 @@ impl Compiler<'_> { *self.ty.get_mut(type_id) = Type::Callable(Callable { original_type_id: type_id, parameter_names: param_names.into_iter().collect(), - parameters: construct_items(self.ty, param_types.into_iter(), rest), + parameters: construct_items(self.ty, param_types.into_iter(), nil_terminated), return_type, - rest, + nil_terminated, generic_types, }); @@ -154,14 +134,14 @@ impl Compiler<'_> { scope_id, hir_id, type_id, - rest, + nil_terminated, }); } else { *self.db.symbol_mut(symbol_id) = Symbol::Function(Function { scope_id, hir_id, type_id, - rest, + nil_terminated, }); } diff --git a/crates/rue-compiler/src/compiler/item/struct_item.rs b/crates/rue-compiler/src/compiler/item/struct_item.rs index d0264c3..3cb6ad2 100644 --- a/crates/rue-compiler/src/compiler/item/struct_item.rs +++ b/crates/rue-compiler/src/compiler/item/struct_item.rs @@ -1,6 +1,6 @@ use indexmap::IndexMap; use rue_parser::{AstNode, StructField, StructItem}; -use rue_typing::{construct_items, Rest, Struct, Type, TypeId}; +use rue_typing::{construct_items, Struct, Type, TypeId}; use crate::{compiler::Compiler, ErrorKind}; @@ -19,14 +19,14 @@ impl Compiler<'_> { pub fn compile_struct_item(&mut self, struct_item: &StructItem, struct_type_id: TypeId) { self.type_definition_stack.push(struct_type_id); - let (fields, rest) = self.compile_struct_fields(struct_item.fields()); - let type_id = construct_items(self.ty, fields.values().copied(), rest); + let (fields, nil_terminated) = self.compile_struct_fields(struct_item.fields()); + let type_id = construct_items(self.ty, fields.values().copied(), nil_terminated); *self.ty.get_mut(struct_type_id) = Type::Struct(Struct { original_type_id: struct_type_id, field_names: fields.keys().cloned().collect(), type_id, - rest, + nil_terminated, generic_types: Vec::new(), }); @@ -37,9 +37,9 @@ impl Compiler<'_> { pub fn compile_struct_fields( &mut self, fields: Vec, - ) -> (IndexMap, Rest) { + ) -> (IndexMap, bool) { let mut named_fields = IndexMap::new(); - let mut rest = Rest::Nil; + let mut nil_terminated = true; let len = fields.len(); @@ -51,21 +51,13 @@ impl Compiler<'_> { // Check if it's a spread or optional parameter. let last = i + 1 == len; let spread = field.spread().is_some(); - let optional = field.optional().is_some(); - if spread && optional { - self.db - .error(ErrorKind::OptionalFieldSpread, field.syntax().text_range()); - } else if spread && !last { - self.db - .error(ErrorKind::InvalidSpreadField, field.syntax().text_range()); - } else if optional && !last { - self.db - .error(ErrorKind::InvalidOptionalField, field.syntax().text_range()); - } else if spread { - rest = Rest::Spread; - } else if optional { - rest = Rest::Optional; + if spread { + if !last { + self.db + .error(ErrorKind::InvalidSpreadField, field.syntax().text_range()); + } + nil_terminated = false; } if let Some(name) = field.name() { @@ -73,6 +65,6 @@ impl Compiler<'_> { }; } - (named_fields, rest) + (named_fields, nil_terminated) } } diff --git a/crates/rue-compiler/src/compiler/ty/function_type.rs b/crates/rue-compiler/src/compiler/ty/function_type.rs index a135a61..260b09a 100644 --- a/crates/rue-compiler/src/compiler/ty/function_type.rs +++ b/crates/rue-compiler/src/compiler/ty/function_type.rs @@ -1,6 +1,6 @@ use indexmap::IndexSet; use rue_parser::{AstNode, FunctionType as Ast}; -use rue_typing::{construct_items, Callable, Rest, Type, TypeId}; +use rue_typing::{construct_items, Callable, Type, TypeId}; use crate::{compiler::Compiler, ErrorKind}; @@ -8,7 +8,7 @@ impl Compiler<'_> { pub fn compile_function_type(&mut self, function: &Ast) -> TypeId { let mut parameters = Vec::new(); let mut parameter_names = IndexSet::new(); - let mut rest = Rest::Nil; + let mut nil_terminated = true; let params = function.params(); let len = params.len(); @@ -38,27 +38,15 @@ impl Compiler<'_> { // Check if it's a spread or optional parameter. let last = i + 1 == len; let spread = param.spread().is_some(); - let optional = param.optional().is_some(); - if spread && optional { - self.db.error( - ErrorKind::OptionalParameterSpread, - param.syntax().text_range(), - ); - } else if spread && !last { - self.db.error( - ErrorKind::InvalidSpreadParameter, - param.syntax().text_range(), - ); - } else if optional && !last { - self.db.error( - ErrorKind::InvalidOptionalParameter, - param.syntax().text_range(), - ); - } else if spread { - rest = Rest::Spread; - } else if optional { - rest = Rest::Optional; + if spread { + if !last { + self.db.error( + ErrorKind::InvalidSpreadParameter, + param.syntax().text_range(), + ); + } + nil_terminated = false; } } @@ -68,7 +56,7 @@ impl Compiler<'_> { .return_type() .map_or(self.ty.std().unknown, |ty| self.compile_type(ty)); - let parameters = construct_items(self.ty, parameters.into_iter(), rest); + let parameters = construct_items(self.ty, parameters.into_iter(), nil_terminated); // Allocate a new type for the function. // TODO: Support generic types. @@ -78,7 +66,7 @@ impl Compiler<'_> { original_type_id: type_id, parameter_names, parameters, - rest, + nil_terminated, return_type, generic_types: Vec::new(), }); diff --git a/crates/rue-compiler/src/dependency_graph.rs b/crates/rue-compiler/src/dependency_graph.rs index 4290809..c95b73e 100644 --- a/crates/rue-compiler/src/dependency_graph.rs +++ b/crates/rue-compiler/src/dependency_graph.rs @@ -1,6 +1,5 @@ use indexmap::{IndexMap, IndexSet}; use rowan::TextRange; -use rue_typing::Rest; use crate::{ environment::Environment, @@ -134,10 +133,9 @@ impl<'a> GraphBuilder<'a> { .filter(|&symbol_id| matches!(self.db.symbol(symbol_id), Symbol::Parameter(_))) .collect(); - let environment_id = self.db.alloc_env(Environment::function( - parameters, - function.rest != Rest::Nil, - )); + let environment_id = self + .db + .alloc_env(Environment::function(parameters, !function.nil_terminated)); self.graph .environments diff --git a/crates/rue-compiler/src/error.rs b/crates/rue-compiler/src/error.rs index c7b013d..7b6f7a6 100644 --- a/crates/rue-compiler/src/error.rs +++ b/crates/rue-compiler/src/error.rs @@ -76,7 +76,6 @@ pub enum ErrorKind { UncallableType(String), ArgumentMismatch(usize, usize), ArgumentMismatchSpread(usize, usize), - ArgumentMismatchOptional(usize, usize), // Field initialization. UninitializableType(String), @@ -91,15 +90,11 @@ pub enum ErrorKind { InvalidFieldAccess(String, String), InvalidIndexAccess(String), - // Spread and optional. + // Spread syntax. InvalidSpreadItem, InvalidSpreadArgument, InvalidSpreadParameter, InvalidSpreadField, - InvalidOptionalParameter, - InvalidOptionalField, - OptionalParameterSpread, - OptionalFieldSpread, UnsupportedFunctionSpread, RequiredFunctionSpread, @@ -213,9 +208,6 @@ impl fmt::Display for ErrorKind { if *expected == 1 { "" } else { "s" } ) } - Self::ArgumentMismatchOptional (found, expected)=> { - format!("Expected either {} or {expected} arguments, but found {found}", expected - 1) - } // Field initialization. Self::UninitializableType(ty) => formatdoc!(" @@ -241,7 +233,7 @@ impl fmt::Display for ErrorKind { Self::InvalidFieldAccess(field, ty) => format!("Cannot reference field `{field}` of type `{ty}`"), Self::InvalidIndexAccess(ty) => format!("Cannot index into type `{ty}`"), - // Spread and optional. + // Spread syntax. Self::InvalidSpreadItem => formatdoc!(" The spread operator can only be used on the last item in a list. \ This is because it requires recursion at runtime to concatenate lists together. \ @@ -260,16 +252,6 @@ impl fmt::Display for ErrorKind { The spread operator can only be used on the last field. \ Otherwise, it would be ambiguous where the field should start and end "), - Self::InvalidOptionalParameter => formatdoc!(" - Only the last parameter in a function can be optional. \ - Otherwise, it would be ambiguous which optional parameter was specified - "), - Self::InvalidOptionalField => formatdoc!(" - Only the last field can be optional. \ - Otherwise, it would be ambiguous which optional field was specified - "), - Self::OptionalParameterSpread => "The spread operator cannot be used on optional parameters".to_string(), - Self::OptionalFieldSpread => "The spread operator cannot be used on optional fields".to_string(), Self::UnsupportedFunctionSpread => "This function does not support the spread operator on its last argument".to_string(), Self::RequiredFunctionSpread => "This function requires the spread operator on its last argument".to_string(), diff --git a/crates/rue-compiler/src/lowerer.rs b/crates/rue-compiler/src/lowerer.rs index 4f45055..9ceaa4f 100644 --- a/crates/rue-compiler/src/lowerer.rs +++ b/crates/rue-compiler/src/lowerer.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use indexmap::IndexSet; -use rue_typing::Rest; use crate::{ dependency_graph::DependencyGraph, @@ -322,7 +321,7 @@ impl<'a> Lowerer<'a> { let mut param_map = HashMap::new(); for (i, &symbol_id) in params.iter().enumerate() { - if i + 1 != params.len() || function.rest != Rest::Spread { + if i + 1 != params.len() || function.nil_terminated { let mir_id = self.lower_hir(env_id, args[i]); param_map.insert(symbol_id, mir_id); continue; diff --git a/crates/rue-compiler/src/symbol.rs b/crates/rue-compiler/src/symbol.rs index 2111abe..b61f816 100644 --- a/crates/rue-compiler/src/symbol.rs +++ b/crates/rue-compiler/src/symbol.rs @@ -1,5 +1,5 @@ use indexmap::IndexSet; -use rue_typing::{Rest, TypeId}; +use rue_typing::TypeId; use crate::{ database::{HirId, ScopeId}, @@ -47,7 +47,7 @@ pub struct Function { pub scope_id: ScopeId, pub hir_id: HirId, pub type_id: TypeId, - pub rest: Rest, + pub nil_terminated: bool, } #[derive(Debug, Clone)] diff --git a/crates/rue-compiler/stdlib.rue b/crates/rue-compiler/stdlib.rue index e791d09..d5a327d 100644 --- a/crates/rue-compiler/stdlib.rue +++ b/crates/rue-compiler/stdlib.rue @@ -1,6 +1,6 @@ export enum Condition { Remark = 1 { - value?: Any, + ...value: (Any, Nil) | Nil, }, AggSigParent = 43 { public_key: PublicKey, @@ -37,7 +37,7 @@ export enum Condition { CreateCoin = 51 { puzzle_hash: Bytes32, amount: Int, - memos?: List, + ...memos: (List, Nil) | Nil, }, ReserveFee = 52 { amount: Int, @@ -105,7 +105,7 @@ export enum Condition { }, Softfork = 90 { cost: Int, - value?: Any, + ...value: (Any, Nil) | Nil, }, } diff --git a/crates/rue-parser/src/ast.rs b/crates/rue-parser/src/ast.rs index 5c4095a..d32e9da 100644 --- a/crates/rue-parser/src/ast.rs +++ b/crates/rue-parser/src/ast.rs @@ -227,13 +227,6 @@ impl FunctionParam { .find(|token| token.kind() == SyntaxKind::Spread) } - pub fn optional(&self) -> Option { - self.syntax() - .children_with_tokens() - .filter_map(SyntaxElement::into_token) - .find(|token| token.kind() == SyntaxKind::Question) - } - pub fn name(&self) -> Option { self.syntax() .children_with_tokens() @@ -301,13 +294,6 @@ impl StructField { .find(|token| token.kind() == SyntaxKind::Spread) } - pub fn optional(&self) -> Option { - self.syntax() - .children_with_tokens() - .filter_map(SyntaxElement::into_token) - .find(|token| token.kind() == SyntaxKind::Question) - } - pub fn name(&self) -> Option { self.syntax() .children_with_tokens() @@ -749,13 +735,6 @@ impl LambdaParam { .find(|token| token.kind() == SyntaxKind::Ident) } - pub fn optional(&self) -> Option { - self.syntax() - .children_with_tokens() - .filter_map(SyntaxElement::into_token) - .find(|token| token.kind() == SyntaxKind::Question) - } - pub fn ty(&self) -> Option { self.syntax().children().find_map(Type::cast) } @@ -854,13 +833,6 @@ impl FunctionTypeParam { .find(|token| token.kind() == SyntaxKind::Ident) } - pub fn optional(&self) -> Option { - self.syntax() - .children_with_tokens() - .filter_map(SyntaxElement::into_token) - .find(|token| token.kind() == SyntaxKind::Question) - } - pub fn spread(&self) -> Option { self.syntax() .children_with_tokens() diff --git a/crates/rue-parser/src/grammar.rs b/crates/rue-parser/src/grammar.rs index becf3af..59a625e 100644 --- a/crates/rue-parser/src/grammar.rs +++ b/crates/rue-parser/src/grammar.rs @@ -91,7 +91,6 @@ fn function_param(p: &mut Parser<'_>) { p.start(SyntaxKind::FunctionParam); p.try_eat(SyntaxKind::Spread); p.expect(SyntaxKind::Ident); - p.try_eat(SyntaxKind::Question); p.expect(SyntaxKind::Colon); ty(p); p.finish(); @@ -129,7 +128,6 @@ fn struct_field(p: &mut Parser<'_>) { p.start(SyntaxKind::StructField); p.try_eat(SyntaxKind::Spread); p.expect(SyntaxKind::Ident); - p.try_eat(SyntaxKind::Question); p.expect(SyntaxKind::Colon); ty(p); p.finish(); @@ -553,7 +551,6 @@ fn lambda_param(p: &mut Parser<'_>) { p.start(SyntaxKind::LambdaParam); p.try_eat(SyntaxKind::Spread); p.expect(SyntaxKind::Ident); - p.try_eat(SyntaxKind::Question); if p.try_eat(SyntaxKind::Colon) { ty(p); } @@ -624,7 +621,6 @@ fn function_type_param(p: &mut Parser<'_>) { p.start(SyntaxKind::FunctionTypeParam); p.try_eat(SyntaxKind::Spread); p.expect(SyntaxKind::Ident); - p.try_eat(SyntaxKind::Question); p.expect(SyntaxKind::Colon); ty(p); p.finish(); diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs index 1f6509d..ed877ee 100644 --- a/crates/rue-typing/src/check/check_type.rs +++ b/crates/rue-typing/src/check/check_type.rs @@ -299,7 +299,7 @@ fn check_union_against_rhs( mod tests { use indexmap::indexmap; - use crate::{alloc_list, alloc_struct, Rest}; + use crate::{alloc_list, alloc_struct}; use super::*; @@ -578,7 +578,7 @@ mod tests { "x".to_string() => types.int, "y".to_string() => types.int, }, - Rest::Nil, + true, ); check_str(&mut db, types.any, point, "(and (l val) (not (l (f val))) (l (r val)) (not (l (f (r val)))) (not (l (r (r val)))) (= (r (r val)) 0))"); } @@ -596,7 +596,7 @@ mod tests { "public_key".to_string() => types.public_key, "message".to_string() => types.bytes, }, - Rest::Nil, + true, ); let opcode = db.alloc(Type::Value(BigInt::from(50))); @@ -607,7 +607,7 @@ mod tests { "public_key".to_string() => types.public_key, "message".to_string() => types.bytes, }, - Rest::Nil, + true, ); let condition = db.alloc(Type::Union(vec![agg_sig_unsafe, agg_sig_me])); diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index cbee658..7a8758e 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -417,7 +417,7 @@ pub(crate) fn compare_type( mod tests { use indexmap::indexmap; - use crate::{alloc_list, alloc_struct, alloc_tuple_of, Rest, Struct}; + use crate::{alloc_list, alloc_struct, alloc_tuple_of, Struct}; use super::*; @@ -581,7 +581,7 @@ mod tests { "x".to_string() => types.int, "y".to_string() => types.int, }, - Rest::Nil, + true, ); assert_eq!(db.compare(point, types.any), Comparison::Assignable); } @@ -716,7 +716,7 @@ mod tests { "x".to_string() => types.int, "y".to_string() => types.int, }, - Rest::Nil, + true, ); let Type::Struct(original) = db.get(struct_type) else { @@ -727,7 +727,7 @@ mod tests { original_type_id: struct_type, type_id: original.type_id, field_names: original.field_names.clone(), - rest: original.rest, + nil_terminated: original.nil_terminated, generic_types: original.generic_types.clone(), })); @@ -753,7 +753,7 @@ mod tests { "x".to_string() => types.int, "y".to_string() => types.int, }, - Rest::Nil, + true, ); let Type::Struct(original) = db.get(struct_type).clone() else { @@ -766,7 +766,7 @@ mod tests { original_type_id: struct_type, type_id: new_inner, field_names: original.field_names, - rest: original.rest, + nil_terminated: original.nil_terminated, generic_types: original.generic_types, })); @@ -792,7 +792,7 @@ mod tests { "x".to_string() => types.int, "y".to_string() => types.int, }, - Rest::Nil, + true, ); let other_struct_type = alloc_struct( @@ -801,7 +801,7 @@ mod tests { "x".to_string() => types.int, "y".to_string() => types.int, }, - Rest::Nil, + true, ); assert_eq!( diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index 25dd5ec..cd82e9a 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -153,7 +153,7 @@ pub(crate) fn difference_type( original_type_id: ty.original_type_id, type_id, field_names: ty.field_names, - rest: ty.rest, + nil_terminated: ty.nil_terminated, generic_types: ty.generic_types, })) } @@ -181,7 +181,7 @@ pub(crate) fn difference_type( original_enum_type_id: variant.original_enum_type_id, field_names: variant.field_names, type_id, - rest: variant.rest, + nil_terminated: variant.nil_terminated, generic_types: variant.generic_types, discriminant: variant.discriminant, })) diff --git a/crates/rue-typing/src/replace_type.rs b/crates/rue-typing/src/replace_type.rs index b23b1b1..54565d4 100644 --- a/crates/rue-typing/src/replace_type.rs +++ b/crates/rue-typing/src/replace_type.rs @@ -31,7 +31,7 @@ pub(crate) fn replace_type( original_type_id: ty.original_type_id, field_names: ty.field_names, type_id: new_type_id, - rest: ty.rest, + nil_terminated: ty.nil_terminated, generic_types: ty.generic_types, })) } @@ -43,7 +43,7 @@ pub(crate) fn replace_type( original_enum_type_id: ty.original_enum_type_id, field_names: ty.field_names, type_id: new_type_id, - rest: ty.rest, + nil_terminated: ty.nil_terminated, generic_types: ty.generic_types, discriminant: ty.discriminant, })) diff --git a/crates/rue-typing/src/semantic_types.rs b/crates/rue-typing/src/semantic_types.rs index 60a2f56..784df15 100644 --- a/crates/rue-typing/src/semantic_types.rs +++ b/crates/rue-typing/src/semantic_types.rs @@ -3,19 +3,6 @@ use num_bigint::BigInt; use crate::{Comparison, Type, TypeId, TypeSystem}; -/// The kind of ending that a list has. -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Rest { - /// This means that the list is nil-terminated. - #[default] - Nil, - /// This means that there is no special terminator for the list. - /// The last element is the rest value. - Spread, - /// This means that the list is nil-terminated, but may contain an additional optional value. - Optional, -} - /// Allows you to map generic types lazily without having to resolve them immediately. /// This prevents stack overflows when resolving generic type definitions that reference themselves. /// When reducing a type to its structural form, lazy types should be removed. @@ -41,7 +28,7 @@ pub struct Struct { pub original_type_id: TypeId, pub field_names: IndexSet, pub type_id: TypeId, - pub rest: Rest, + pub nil_terminated: bool, pub generic_types: Vec, } @@ -53,7 +40,7 @@ pub struct Callable { pub parameter_names: IndexSet, pub parameters: TypeId, pub return_type: TypeId, - pub rest: Rest, + pub nil_terminated: bool, pub generic_types: Vec, } @@ -81,8 +68,8 @@ pub struct Variant { pub field_names: Option>, /// The structural type of the enum variant. pub type_id: TypeId, - /// The rest kind of the variant. - pub rest: Rest, + /// Whether the chain of cons pairs is nil terminated. + pub nil_terminated: bool, /// The generic types of the variant. pub generic_types: Vec, /// The discriminant value. @@ -93,23 +80,13 @@ pub struct Variant { pub fn construct_items( db: &mut TypeSystem, items: impl DoubleEndedIterator, - rest: Rest, + nil_terminated: bool, ) -> 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 => {} - } + if i == 0 && !nil_terminated { + result = item; + continue; } result = db.alloc(Type::Pair(item, result)); } @@ -121,43 +98,26 @@ pub fn deconstruct_items( db: &mut TypeSystem, type_id: TypeId, length: usize, - rest: Rest, + nil_terminated: bool, ) -> 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; - } + if !nil_terminated { + items.push(current); + break; + } - 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); + if db.compare(rest, db.std().nil) > Comparison::Equal { + return None; } + break; } + let (first, rest) = db.get_pair(current)?; items.push(first); current = rest; @@ -192,8 +152,8 @@ mod tests { 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); + let type_id = construct_items(&mut db, [std.int].into_iter(), true); + let items = deconstruct_items(&mut db, type_id, 1, true); assert_eq!(items, Some(vec![std.int])); } @@ -201,41 +161,24 @@ mod tests { 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); + let type_id = construct_items(&mut db, [std.int].into_iter(), false); + let items = deconstruct_items(&mut db, type_id, 1, false); 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); + let type_id = construct_items(&mut db, [].into_iter(), true); + let items = deconstruct_items(&mut db, type_id, 0, true); 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); + let type_id = construct_items(&mut db, [].into_iter(), false); + let items = deconstruct_items(&mut db, type_id, 0, false); assert_eq!(items, Some(vec![])); } @@ -243,8 +186,8 @@ mod tests { 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); + let type_id = construct_items(&mut db, [std.int, std.int].into_iter(), true); + let items = deconstruct_items(&mut db, type_id, 2, true); assert_eq!(items, Some(vec![std.int, std.int])); } @@ -252,17 +195,8 @@ mod tests { 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); + let type_id = construct_items(&mut db, [std.int, std.int].into_iter(), false); + let items = deconstruct_items(&mut db, type_id, 2, false); assert_eq!(items, Some(vec![std.int, std.int])); } @@ -271,8 +205,8 @@ mod tests { 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); + let type_id = construct_items(&mut db, [std.bytes32, pair].into_iter(), true); + let items = deconstruct_items(&mut db, type_id, 2, true); assert_eq!(items, Some(vec![std.bytes32, pair])); } @@ -281,18 +215,8 @@ mod tests { 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); + let type_id = construct_items(&mut db, [std.bytes32, pair].into_iter(), false); + let items = deconstruct_items(&mut db, type_id, 2, false); assert_eq!(items, Some(vec![std.bytes32, pair])); } diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs index e95c07c..4eb1bf1 100644 --- a/crates/rue-typing/src/stringify.rs +++ b/crates/rue-typing/src/stringify.rs @@ -88,7 +88,7 @@ pub(crate) fn stringify_type( mod tests { use indexmap::indexmap; - use crate::{alloc_callable, Rest}; + use crate::alloc_callable; use super::*; @@ -131,7 +131,7 @@ mod tests { "b".to_string() => types.bytes, }, types.bool, - Rest::Nil, + true, ); assert_eq!( diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index aa31dca..5aff258 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -92,7 +92,7 @@ pub(crate) fn substitute_type( original_type_id: ty.original_type_id, type_id: new_type_id, field_names: ty.field_names, - rest: ty.rest, + nil_terminated: ty.nil_terminated, generic_types: ty.generic_types, })) } @@ -110,7 +110,7 @@ pub(crate) fn substitute_type( original_enum_type_id: ty.original_enum_type_id, type_id: new_type_id, field_names: ty.field_names, - rest: ty.rest, + nil_terminated: ty.nil_terminated, generic_types: ty.generic_types, discriminant: ty.discriminant, })) @@ -147,7 +147,7 @@ pub(crate) fn substitute_type( parameter_names: callable.parameter_names, parameters: new_parameters, return_type: new_return_type, - rest: callable.rest, + nil_terminated: callable.nil_terminated, generic_types: callable.generic_types, })) } diff --git a/crates/rue-typing/src/test_tools.rs b/crates/rue-typing/src/test_tools.rs index 005de1b..411249b 100644 --- a/crates/rue-typing/src/test_tools.rs +++ b/crates/rue-typing/src/test_tools.rs @@ -1,6 +1,6 @@ use indexmap::{indexmap, IndexMap}; -use crate::{Callable, Lazy, Rest, Struct, Type, TypeId, TypeSystem}; +use crate::{Callable, Lazy, Struct, Type, TypeId, TypeSystem}; pub fn alloc_list(db: &mut TypeSystem, item_type_id: TypeId) -> TypeId { db.alloc(Type::Lazy(Lazy { @@ -15,26 +15,12 @@ pub fn alloc_callable( db: &mut TypeSystem, parameters: &IndexMap, return_type: TypeId, - rest: Rest, + nil_terminated: bool, ) -> TypeId { - let structure = match rest { - Rest::Nil => alloc_list_of(db, parameters.values().copied()), - Rest::Spread => alloc_tuple_of(db, parameters.values().copied()), - Rest::Optional => { - let parameters: Vec = parameters - .values() - .copied() - .enumerate() - .map(|(i, field)| { - if i == parameters.len() - 1 { - db.alloc(Type::Union(vec![field, db.std().nil])) - } else { - field - } - }) - .collect(); - alloc_tuple_of(db, parameters.into_iter()) - } + let structure = if nil_terminated { + alloc_list_of(db, parameters.values().copied()) + } else { + alloc_tuple_of(db, parameters.values().copied()) }; let type_id = db.alloc(Type::Unknown); @@ -44,32 +30,22 @@ pub fn alloc_callable( parameter_names: parameters.keys().cloned().collect(), parameters: structure, return_type, - rest, + nil_terminated, generic_types: Vec::new(), }); type_id } -pub fn alloc_struct(db: &mut TypeSystem, fields: &IndexMap, rest: Rest) -> TypeId { - let structure = match rest { - Rest::Nil => alloc_list_of(db, fields.values().copied()), - Rest::Spread => alloc_tuple_of(db, fields.values().copied()), - Rest::Optional => { - let fields: Vec = fields - .values() - .copied() - .enumerate() - .map(|(i, field)| { - if i == fields.len() - 1 { - db.alloc(Type::Union(vec![field, db.std().nil])) - } else { - field - } - }) - .collect(); - alloc_tuple_of(db, fields.into_iter()) - } +pub fn alloc_struct( + db: &mut TypeSystem, + fields: &IndexMap, + nil_terminated: bool, +) -> TypeId { + let structure = if nil_terminated { + alloc_list_of(db, fields.values().copied()) + } else { + alloc_tuple_of(db, fields.values().copied()) }; let type_id = db.alloc(Type::Unknown); @@ -78,7 +54,7 @@ pub fn alloc_struct(db: &mut TypeSystem, fields: &IndexMap, rest original_type_id: type_id, type_id: structure, field_names: fields.keys().cloned().collect(), - rest, + nil_terminated, generic_types: Vec::new(), }); diff --git a/tests/block/block_let_function.rue b/tests/block/block_let_function.rue index e62877a..0ba8703 100644 --- a/tests/block/block_let_function.rue +++ b/tests/block/block_let_function.rue @@ -1,5 +1,5 @@ fun main() -> Bool { - let outer: Int? = 42; + let outer: Int | Nil = 42; let another_outer = 19; let not_used_in_block = false; let result = !not_used_in_block && outer is Int && { diff --git a/tests/function/function_call.rue b/tests/function/function_call.rue index 4996b81..94973ef 100644 --- a/tests/function/function_call.rue +++ b/tests/function/function_call.rue @@ -2,23 +2,6 @@ fun main() -> Nil { assert empty(); assert empty(1); - assert one_optional(); - assert one_optional(1); - assert one_optional(1, 2); - assert one_optional(...1); - assert one_optional(1, ...2); - - assert two_optional(); - assert two_optional(1); - assert two_optional(1, 2); - assert two_optional(1, 2, 3); - assert two_optional(1, 2, 3, 4); - assert two_optional(...[1, 2, 3, 4]); - assert two_optional(1, ...[2, 3, 4]); - assert two_optional(1, 2, ...[3, 4]); - assert two_optional(1, 2, 3, ...[4]); - assert two_optional(1, ...2); - assert one_spread_list(); assert one_spread_list(1); assert one_spread_list(1, 2); @@ -60,14 +43,6 @@ fun empty() -> Bool { true } -fun one_optional(_a?: Int) -> Bool { - true -} - -fun two_optional(_a: Int, _b?: Int) -> Bool { - true -} - fun one_spread_list(..._a: List) -> Bool { true } diff --git a/tests/function/function_optional_param.rue b/tests/function/function_optional_param.rue deleted file mode 100644 index b684a94..0000000 --- a/tests/function/function_optional_param.rue +++ /dev/null @@ -1,11 +0,0 @@ -fun main() -> Int { - multiply(10) * multiply(34, 42) -} - -fun multiply(num: Int, factor?: Int) -> Int { - if factor? { - num * factor - } else { - num * num - } -} diff --git a/tests/function/lambda_optional_param.rue b/tests/function/lambda_optional_param.rue deleted file mode 100644 index 8e642cf..0000000 --- a/tests/function/lambda_optional_param.rue +++ /dev/null @@ -1,10 +0,0 @@ -fun main() -> Int { - let max = fun(a: Int, b?: Int) => { - if b? && b > a { - b - } else { - a - } - }; - max(42) + max(12124, 81) -} diff --git a/tests/struct/struct_inner_optional.rue b/tests/struct/struct_inner_optional.rue deleted file mode 100644 index b81e625..0000000 --- a/tests/struct/struct_inner_optional.rue +++ /dev/null @@ -1,23 +0,0 @@ -struct Value { - inner?: Value, -} - -fun main() -> Nil { - let value = Value { - inner: Value { - inner: Value { - inner: Value { - inner: Value {} - } - } - } - }; - - assert value.inner?; - assert value.inner.inner?; - assert value.inner.inner.inner?; - assert value.inner.inner.inner.inner?; - assert !value.inner.inner.inner.inner.inner?; - - nil -} diff --git a/tests/struct/struct_optional.rue b/tests/struct/struct_optional.rue deleted file mode 100644 index 427cb29..0000000 --- a/tests/struct/struct_optional.rue +++ /dev/null @@ -1,19 +0,0 @@ -struct Point { - x: Int, - y: Int, - z?: Int, -} - -fun main() -> Int { - let point_xy = Point { x: 42, y: 34 }; - let point_xyz = Point { x: 42, y: 34, z: 69 }; - sum(point_xy) + sum(point_xyz) -} - -fun sum(point: Point) -> Int { - if point.z? { - point.x + point.y + point.z - } else { - point.x + point.y - } -} diff --git a/tests/struct/struct_single_optional.rue b/tests/struct/struct_single_optional.rue deleted file mode 100644 index 6096dc1..0000000 --- a/tests/struct/struct_single_optional.rue +++ /dev/null @@ -1,11 +0,0 @@ -struct Value { - num?: Int, -} - -fun main() -> Int { - let empty = Value {}; - let value = Value { num: 42 }; - assert !empty.num?; - assert value.num?; - value.num -} From 24ca4fd6c8f34b7bec0da3e37683fab9a574b63e Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 31 Jul 2024 15:02:15 -0400 Subject: [PATCH 085/100] Fix generic inference --- crates/rue-compiler/src/compiler/expr.rs | 2 +- .../src/compiler/expr/function_call_expr.rs | 11 +++ .../src/compiler/expr/list_expr.rs | 53 +++------- crates/rue-typing/src/comparison.rs | 98 +++++++++++-------- crates/rue-typing/src/type_system.rs | 14 +-- tests/struct/struct_optional_initializer.rue | 18 ---- 6 files changed, 85 insertions(+), 111 deletions(-) delete mode 100644 tests/struct/struct_optional_initializer.rue diff --git a/crates/rue-compiler/src/compiler/expr.rs b/crates/rue-compiler/src/compiler/expr.rs index 2ade03d..cfa0ef0 100644 --- a/crates/rue-compiler/src/compiler/expr.rs +++ b/crates/rue-compiler/src/compiler/expr.rs @@ -34,7 +34,7 @@ impl Compiler<'_> { } Expr::InitializerExpr(initializer) => self.compile_initializer_expr(initializer), Expr::LiteralExpr(literal) => self.compile_literal_expr(literal), - Expr::ListExpr(list) => self.compile_list_expr(list, expected_type), + Expr::ListExpr(list) => self.compile_list_expr(list), Expr::PairExpr(pair) => self.compile_pair_expr(pair, expected_type), Expr::Block(block) => self.compile_block_expr(block, expected_type), Expr::LambdaExpr(lambda) => self.compile_lambda_expr(lambda, expected_type), diff --git a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs index f0eff1e..7621fcb 100644 --- a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs @@ -79,6 +79,13 @@ impl Compiler<'_> { } }); + println!("\n\n{}", call.syntax().text()); + if let Some(ty) = expected_type { + println!("{}", self.ty.debug(ty)); + } else { + println!("NO EXPECTED"); + } + // Compile the argument expression, if present. // Otherwise, it's a parser error let expr = arg @@ -86,6 +93,8 @@ impl Compiler<'_> { .map(|expr| self.compile_expr(&expr, expected_type)) .unwrap_or_else(|| self.unknown()); + println!("{}", self.ty.debug(expr.type_id)); + // Add the argument to the list. let type_id = expr.type_id; args.push(expr); @@ -130,6 +139,8 @@ impl Compiler<'_> { let param_type = parameter_types[i]; self.type_check(type_id, param_type, call_args[i].syntax().text_range()); } + + println!("{:?}", self.generic_type_stack.last()); } // The generic type context is no longer needed. diff --git a/crates/rue-compiler/src/compiler/expr/list_expr.rs b/crates/rue-compiler/src/compiler/expr/list_expr.rs index 9e904fb..c45b2c0 100644 --- a/crates/rue-compiler/src/compiler/expr/list_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/list_expr.rs @@ -1,54 +1,24 @@ -use rue_parser::{AstNode, ListExpr}; -use rue_typing::{unwrap_list, TypeId}; +use rue_parser::ListExpr; +use rue_typing::construct_items; use crate::{compiler::Compiler, hir::Hir, value::Value, ErrorKind}; impl Compiler<'_> { - pub fn compile_list_expr( - &mut self, - list_expr: &ListExpr, - expected_expr_type: Option, - ) -> Value { + pub fn compile_list_expr(&mut self, list_expr: &ListExpr) -> Value { let mut items = Vec::new(); + let mut types = Vec::new(); let mut nil_terminated = true; - let mut list_type = expected_expr_type; - let mut item_type = expected_expr_type.and_then(|ty| unwrap_list(self.ty, ty)); - - let len = list_expr.items().len(); + let length = list_expr.items().len(); for (i, item) in list_expr.items().into_iter().enumerate() { - let expected_item_type = if item.spread().is_some() { - list_type - } else { - item_type - }; - let output = item .expr() - .map(|expr| self.compile_expr(&expr, expected_item_type)) + .map(|expr| self.compile_expr(&expr, None)) .unwrap_or(self.unknown()); - if let Some(expected_item_type) = expected_item_type { - self.type_check( - output.type_id, - expected_item_type, - item.syntax().text_range(), - ); - } - - if i == 0 && item_type.is_none() { - if item.spread().is_some() { - list_type = Some(output.type_id); - item_type = unwrap_list(self.ty, output.type_id); - } else { - list_type = Some(self.ty.alloc_list(output.type_id)); - item_type = Some(output.type_id); - } - } - if let Some(spread) = item.spread() { - if i + 1 == len { + if i + 1 == length { nil_terminated = false; } else { self.db @@ -57,6 +27,7 @@ impl Compiler<'_> { } items.push(output.hir_id); + types.push(output.type_id); } let mut hir_id = self.builtins.nil; @@ -69,10 +40,8 @@ impl Compiler<'_> { } } - Value::new( - hir_id, - self.ty - .alloc_list(item_type.unwrap_or(self.ty.std().unknown)), - ) + let type_id = construct_items(self.ty, types.into_iter(), nil_terminated); + + Value::new(hir_id, self.ty.alloc_list(type_id)) } } diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index 7a8758e..9f7fbc2 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -19,9 +19,10 @@ pub enum Comparison { pub(crate) struct ComparisonContext<'a> { pub visited: HashSet<(TypeId, TypeId)>, - pub substitution_stack: &'a mut Vec>, - pub initial_substitution_length: usize, - pub generic_stack_frame: Option, + pub inferred: &'a mut Vec>, + pub infer_generics: bool, + pub lhs_substitutions: Vec>, + pub rhs_substitutions: Vec>, } pub(crate) fn compare_type( @@ -30,17 +31,30 @@ pub(crate) fn compare_type( rhs: TypeId, ctx: &mut ComparisonContext<'_>, ) -> Comparison { - if lhs == rhs { - return Comparison::Equal; - } - if !ctx.visited.insert((lhs, rhs)) { return Comparison::Assignable; } + let found_lhs = ctx + .lhs_substitutions + .iter() + .rev() + .find_map(|substitutions| substitutions.get(&lhs).copied()); + + let found_rhs = ctx + .rhs_substitutions + .iter() + .rev() + .find_map(|substitutions| substitutions.get(&rhs).copied()); + let comparison = match (db.get(lhs), db.get(rhs)) { (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), + // Handle generics and substitutions. + (Type::Generic, _) if found_lhs.is_some() => compare_type(db, found_lhs.unwrap(), rhs, ctx), + (_, Type::Generic) if found_rhs.is_some() => compare_type(db, lhs, found_rhs.unwrap(), ctx), + (Type::Generic, Type::Generic) if lhs == rhs => Comparison::Equal, + // These types are identical. (Type::Unknown, Type::Unknown) | (Type::Never, Type::Never) @@ -283,17 +297,17 @@ pub(crate) fn compare_type( // We need to push substititons onto the stack in order to accurately compare them. (Type::Lazy(lazy), _) => { - ctx.substitution_stack + ctx.lhs_substitutions .push(lazy.substitutions.clone().into_iter().collect()); let result = compare_type(db, lazy.type_id, rhs, ctx); - ctx.substitution_stack.pop().unwrap(); + ctx.lhs_substitutions.pop().unwrap(); result } (_, Type::Lazy(lazy)) => { - ctx.substitution_stack + ctx.rhs_substitutions .push(lazy.substitutions.clone().into_iter().collect()); let result = compare_type(db, lhs, lazy.type_id, ctx); - ctx.substitution_stack.pop().unwrap(); + ctx.rhs_substitutions.pop().unwrap(); result } @@ -365,21 +379,17 @@ pub(crate) fn compare_type( ), (Type::Callable(..), _) => compare_type(db, lhs, db.std().any, ctx), - // Generics are resolved by looking up the substitution in the stack. - // If we're infering, we'll push the substitution onto the proper generic stack frame. + // Infer generics. (_, Type::Generic) => { - let mut found = None; - - for substititons in ctx.substitution_stack.iter().rev() { - if let Some(&substititon) = substititons.get(&rhs) { - found = Some(substititon); - } - } - - if let Some(found) = found { - compare_type(db, lhs, found, ctx) - } else if let Some(generic_stack_frame) = ctx.generic_stack_frame { - ctx.substitution_stack[generic_stack_frame].insert(rhs, lhs); + if let Some(inferred) = ctx + .inferred + .iter() + .rev() + .find_map(|map| map.get(&rhs).copied()) + { + compare_type(db, lhs, inferred, ctx) + } else if ctx.infer_generics { + ctx.inferred.last_mut().unwrap().insert(rhs, lhs); Comparison::Assignable } else { Comparison::Incompatible @@ -388,20 +398,13 @@ pub(crate) fn compare_type( // Generics are resolved by looking up the substitution in the stack. (Type::Generic, _) => { - let mut found = None; - - for (i, substititons) in ctx.substitution_stack.iter().enumerate().rev() { - if i < ctx.initial_substitution_length { - break; - } - - if let Some(&substititon) = substititons.get(&lhs) { - found = Some(substititon); - } - } - - if let Some(found) = found { - compare_type(db, found, rhs, ctx) + if let Some(inferred) = ctx + .inferred + .iter() + .rev() + .find_map(|map| map.get(&lhs).copied()) + { + compare_type(db, inferred, rhs, ctx) } else { Comparison::Incompatible } @@ -827,7 +830,7 @@ mod tests { let list = alloc_list(&mut db, generic); let pair = db.alloc(Type::Pair(generic, list)); assert_eq!(db.compare(types.nil, list), Comparison::Assignable); - assert_eq!(db.compare(list, list), Comparison::Equal); + assert_eq!(db.compare(list, list), Comparison::Assignable); assert_eq!(db.compare(pair, list), Comparison::Assignable); } @@ -849,4 +852,19 @@ mod tests { assert_eq!(db.compare(pair_enum, union), Comparison::Assignable); } + + #[test] + fn test_compare_list_generics() { + let mut db = TypeSystem::new(); + let types = db.std(); + + let mut stack = vec![HashMap::new()]; + + let list = alloc_list(&mut db, types.int); + assert_eq!( + db.compare_with_generics(list, types.unmapped_list, &mut stack, true), + Comparison::Assignable + ); + assert_eq!(stack, vec![[(types.generic_list_item, types.int)].into()]); + } } diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index a29b1c7..a21d9f5 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -181,22 +181,16 @@ impl TypeSystem { substitution_stack: &mut Vec>, infer_generics: bool, ) -> Comparison { - let generic_stack_frame = if infer_generics { - Some(substitution_stack.len() - 1) - } else { - None - }; - let initial_substitution_length = substitution_stack.len(); - compare_type( self, lhs, rhs, &mut ComparisonContext { visited: HashSet::new(), - substitution_stack, - initial_substitution_length, - generic_stack_frame, + lhs_substitutions: Vec::new(), + rhs_substitutions: Vec::new(), + inferred: substitution_stack, + infer_generics, }, ) } diff --git a/tests/struct/struct_optional_initializer.rue b/tests/struct/struct_optional_initializer.rue deleted file mode 100644 index 41d8b5d..0000000 --- a/tests/struct/struct_optional_initializer.rue +++ /dev/null @@ -1,18 +0,0 @@ -struct Point { - x: Int, - y: Int, - z?: Int, -} - -fun main() -> Nil { - let point_xy = Point { x: 42, y: 34 }; - let new_point_xy = Point { x: point_xy.x, y: point_xy.y, z: point_xy.z }; - - let point_xyz = Point { x: 42, y: 34, z: 69 }; - let new_point_xyz = Point { x: point_xyz.x, y: point_xyz.y, z: point_xyz.z }; - - assert tree_hash(point_xy) == tree_hash(new_point_xy); - assert tree_hash(point_xyz) == tree_hash(new_point_xyz); - - nil -} From e2708f73953857c7c5ff9ce20be33039cf071d5f Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 1 Aug 2024 10:03:08 -0400 Subject: [PATCH 086/100] Temp --- crates/rue-compiler/src/compiler/builtins.rs | 32 ++++++ .../src/compiler/expr/function_call_expr.rs | 28 ++--- .../src/compiler/expr/list_expr.rs | 2 +- crates/rue-lsp/src/main.rs | 4 +- crates/rue-parser/src/grammar.rs | 8 +- crates/rue-typing/src/comparison.rs | 30 ++++- crates/rue-typing/src/difference.rs | 20 ++++ crates/rue-typing/src/stringify.rs | 2 +- crates/rue-typing/src/substitute_type.rs | 24 ++++ crates/rue-typing/src/type_system.rs | 9 ++ examples/cat.rue | 2 +- tests.toml | 108 +++++++++--------- 12 files changed, 186 insertions(+), 83 deletions(-) diff --git a/crates/rue-compiler/src/compiler/builtins.rs b/crates/rue-compiler/src/compiler/builtins.rs index 0283c07..7592878 100644 --- a/crates/rue-compiler/src/compiler/builtins.rs +++ b/crates/rue-compiler/src/compiler/builtins.rs @@ -38,11 +38,15 @@ pub fn builtins(db: &mut Database, ty: &mut TypeSystem) -> Builtins { unknown, }; + let cast = cast(db, ty); let sha256 = sha256(db, ty); let pubkey_for_exp = pubkey_for_exp(db, ty); let divmod = divmod(db, ty); let substr = substr(db, ty); + db.scope_mut(builtins.scope_id) + .define_symbol("cast".to_string(), cast); + db.scope_mut(builtins.scope_id) .define_symbol("sha256".to_string(), sha256); @@ -58,6 +62,34 @@ pub fn builtins(db: &mut Database, ty: &mut TypeSystem) -> Builtins { builtins } +fn cast(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { + let mut scope = Scope::default(); + + let param = db.alloc_symbol(Symbol::Parameter(ty.std().any)); + scope.define_symbol("value".to_string(), param); + let param_ref = db.alloc_hir(Hir::Reference(param, TextRange::default())); + let scope_id = db.alloc_scope(scope); + + let generic = ty.alloc(Type::Generic); + let type_id = ty.alloc(Type::Unknown); + + *ty.get_mut(type_id) = Type::Callable(Callable { + original_type_id: type_id, + parameter_names: indexset!["value".to_string()], + parameters: ty.alloc(Type::Pair(ty.std().any, ty.std().nil)), + nil_terminated: true, + return_type: generic, + generic_types: vec![generic], + }); + + db.alloc_symbol(Symbol::InlineFunction(Function { + scope_id, + hir_id: param_ref, + type_id, + nil_terminated: true, + })) +} + fn sha256(db: &mut Database, ty: &mut TypeSystem) -> SymbolId { let mut scope = Scope::default(); diff --git a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs index 7621fcb..b62d0e1 100644 --- a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use rowan::TextRange; use rue_parser::{AstNode, FunctionCallExpr}; -use rue_typing::{deconstruct_items, unwrap_list, Callable, Type, TypeId}; +use rue_typing::{deconstruct_items, unwrap_list, Callable, Lazy, Type, TypeId}; use crate::{compiler::Compiler, hir::Hir, value::Value, ErrorKind}; @@ -14,13 +14,9 @@ impl Compiler<'_> { let callee = call.callee().map(|callee| self.compile_expr(&callee, None)); // Get the function type of the callee. - let function_type = - callee - .as_ref() - .and_then(|callee| match self.ty.get(callee.type_id).clone() { - Type::Callable(function_type) => Some(function_type), - _ => None, - }); + let function_type = callee + .as_ref() + .and_then(|callee| self.ty.get_callable_recursive(callee.type_id).cloned()); let parameter_types = function_type.as_ref().map(|ty| { deconstruct_items( @@ -79,13 +75,6 @@ impl Compiler<'_> { } }); - println!("\n\n{}", call.syntax().text()); - if let Some(ty) = expected_type { - println!("{}", self.ty.debug(ty)); - } else { - println!("NO EXPECTED"); - } - // Compile the argument expression, if present. // Otherwise, it's a parser error let expr = arg @@ -93,8 +82,6 @@ impl Compiler<'_> { .map(|expr| self.compile_expr(&expr, expected_type)) .unwrap_or_else(|| self.unknown()); - println!("{}", self.ty.debug(expr.type_id)); - // Add the argument to the list. let type_id = expr.type_id; args.push(expr); @@ -139,8 +126,6 @@ impl Compiler<'_> { let param_type = parameter_types[i]; self.type_check(type_id, param_type, call_args[i].syntax().text_range()); } - - println!("{:?}", self.generic_type_stack.last()); } // The generic type context is no longer needed. @@ -152,7 +137,10 @@ impl Compiler<'_> { function_type.map_or(self.ty.std().unknown, |expected| expected.return_type); if !generic_types.is_empty() { - type_id = self.ty.substitute(type_id, generic_types); + type_id = self.ty.alloc(Type::Lazy(Lazy { + type_id, + substitutions: generic_types.into_iter().collect(), + })); } // Build the HIR for the function call. diff --git a/crates/rue-compiler/src/compiler/expr/list_expr.rs b/crates/rue-compiler/src/compiler/expr/list_expr.rs index c45b2c0..7a17374 100644 --- a/crates/rue-compiler/src/compiler/expr/list_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/list_expr.rs @@ -42,6 +42,6 @@ impl Compiler<'_> { let type_id = construct_items(self.ty, types.into_iter(), nil_terminated); - Value::new(hir_id, self.ty.alloc_list(type_id)) + Value::new(hir_id, type_id) } } diff --git a/crates/rue-lsp/src/main.rs b/crates/rue-lsp/src/main.rs index 9390cb8..2036fab 100644 --- a/crates/rue-lsp/src/main.rs +++ b/crates/rue-lsp/src/main.rs @@ -1,5 +1,5 @@ use clvmr::Allocator; -use rue_compiler::{compile, DiagnosticKind}; +use rue_compiler::{compile_raw, DiagnosticKind}; use rue_parser::{line_col, parse, LineCol}; use tower_lsp::jsonrpc::Result; use tower_lsp::lsp_types::{ @@ -60,7 +60,7 @@ impl LanguageServer for Backend { /// This is a hack to get around a Rust compiler error. #[allow(clippy::needless_pass_by_value)] fn analyze_owned(root: rue_parser::Root) -> Vec { - compile(&mut Allocator::new(), &root, false).diagnostics + compile_raw(&mut Allocator::new(), &root, false, true).diagnostics } impl Backend { diff --git a/crates/rue-parser/src/grammar.rs b/crates/rue-parser/src/grammar.rs index 59a625e..ed1ce7e 100644 --- a/crates/rue-parser/src/grammar.rs +++ b/crates/rue-parser/src/grammar.rs @@ -493,10 +493,16 @@ fn path_expr(p: &mut Parser<'_>) { p.start(SyntaxKind::PathExpr); p.start(SyntaxKind::PathItem); p.expect(SyntaxKind::Ident); + let mut next = p.try_eat(SyntaxKind::PathSeparator); + if next && p.at(SyntaxKind::LessThan) { + generic_args(p); + next = p.try_eat(SyntaxKind::PathSeparator); + } p.finish(); - while p.try_eat(SyntaxKind::PathSeparator) { + while next { p.start(SyntaxKind::PathItem); p.expect(SyntaxKind::Ident); + next = p.try_eat(SyntaxKind::PathSeparator); p.finish(); } p.finish(); diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index 9f7fbc2..d7efeb1 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -53,7 +53,6 @@ pub(crate) fn compare_type( // Handle generics and substitutions. (Type::Generic, _) if found_lhs.is_some() => compare_type(db, found_lhs.unwrap(), rhs, ctx), (_, Type::Generic) if found_rhs.is_some() => compare_type(db, lhs, found_rhs.unwrap(), ctx), - (Type::Generic, Type::Generic) if lhs == rhs => Comparison::Equal, // These types are identical. (Type::Unknown, Type::Unknown) @@ -388,6 +387,8 @@ pub(crate) fn compare_type( .find_map(|map| map.get(&rhs).copied()) { compare_type(db, lhs, inferred, ctx) + } else if lhs == rhs { + Comparison::Equal } else if ctx.infer_generics { ctx.inferred.last_mut().unwrap().insert(rhs, lhs); Comparison::Assignable @@ -396,7 +397,6 @@ pub(crate) fn compare_type( } } - // Generics are resolved by looking up the substitution in the stack. (Type::Generic, _) => { if let Some(inferred) = ctx .inferred @@ -405,6 +405,8 @@ pub(crate) fn compare_type( .find_map(|map| map.get(&lhs).copied()) { compare_type(db, inferred, rhs, ctx) + } else if lhs == rhs { + Comparison::Equal } else { Comparison::Incompatible } @@ -854,7 +856,7 @@ mod tests { } #[test] - fn test_compare_list_generics() { + fn test_compare_list_unmapped_list_generics() { let mut db = TypeSystem::new(); let types = db.std(); @@ -867,4 +869,26 @@ mod tests { ); assert_eq!(stack, vec![[(types.generic_list_item, types.int)].into()]); } + + #[test] + fn test_compare_tuple_list_generics() { + let mut db = TypeSystem::new(); + let types = db.std(); + + let mut stack = vec![HashMap::new()]; + + let tuple = alloc_tuple_of( + &mut db, + [types.int, types.int, types.int, types.nil].into_iter(), + ); + + let generic = db.alloc(Type::Generic); + let list = alloc_list(&mut db, generic); + + assert_eq!( + db.compare_with_generics(tuple, list, &mut stack, true), + Comparison::Assignable + ); + assert_eq!(stack, vec![[(generic, types.int)].into()]); + } } diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index cd82e9a..0683a9d 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -226,3 +226,23 @@ pub(crate) fn difference_type( result } + +#[cfg(test)] +mod tests { + use crate::{alloc_list, Comparison}; + + use super::*; + + #[test] + fn test_difference_list_nil() { + let mut db = TypeSystem::new(); + let types = db.std(); + + let generic = db.alloc(Type::Generic); + let list = alloc_list(&mut db, generic); + let non_nil = db.difference(list, types.nil); + + assert_eq!(db.compare(non_nil, list), Comparison::Assignable); + assert_eq!(db.compare(types.nil, non_nil), Comparison::Incompatible); + } +} diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs index 4eb1bf1..1aa5e07 100644 --- a/crates/rue-typing/src/stringify.rs +++ b/crates/rue-typing/src/stringify.rs @@ -19,7 +19,7 @@ pub(crate) fn stringify_type( let result = match types.get(type_id) { Type::Ref(..) => unreachable!(), Type::Unknown => "{unknown}".to_string(), - Type::Generic => "{generic}".to_string(), + Type::Generic => format!("{{{}}}", type_id.index()), Type::Never => "Never".to_string(), Type::Any => "Any".to_string(), Type::Bytes => "Bytes".to_string(), diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index 5aff258..b42a089 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -158,3 +158,27 @@ pub(crate) fn substitute_type( result } + +#[cfg(test)] +mod tests { + use crate::{alloc_list, Comparison}; + + use super::*; + + #[test] + fn test_substitute_generic_list() { + let mut db = TypeSystem::new(); + let types = db.std(); + + let generic = db.alloc(Type::Generic); + let list = alloc_list(&mut db, generic); + + let mut substitutions = HashMap::new(); + substitutions.insert(generic, types.bool); + + let result = db.substitute(list, substitutions); + let expected = alloc_list(&mut db, types.bool); + + assert_eq!(db.compare(expected, result), Comparison::Assignable); + } +} diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index a21d9f5..cbf0d3a 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -57,6 +57,7 @@ impl Default for TypeSystem { names.insert(false_bool, "False".to_string()); names.insert(nil, "Nil".to_string()); names.insert(unmapped_list, "List".to_string()); + names.insert(generic_list_item, "{item}".to_string()); Self { arena, @@ -146,6 +147,14 @@ impl TypeSystem { } } + pub fn get_callable_recursive(&mut self, type_id: TypeId) -> Option<&Callable> { + let type_id = self.substitute(type_id, HashMap::new()); + match self.get_recursive(type_id) { + Type::Callable(callable) => Some(callable), + _ => None, + } + } + pub fn alloc_list(&mut self, type_id: TypeId) -> TypeId { let mut substitutions = IndexMap::new(); substitutions.insert(self.types.generic_list_item, type_id); diff --git a/examples/cat.rue b/examples/cat.rue index 2598bea..57cb220 100644 --- a/examples/cat.rue +++ b/examples/cat.rue @@ -220,7 +220,7 @@ fun morph_conditions( if condition is Condition::CreateCoin { // If the amount is -113, it's a TAIL reveal. if condition.amount == -113 { - let run_tail = condition as Any as RunTailCondition; + let run_tail = cast::(condition); let rest = morph_conditions(conditions.rest, cat_info, TailInfo { tail_puzzle: run_tail.tail_puzzle, diff --git a/tests.toml b/tests.toml index 7a5887f..8ffc4b9 100644 --- a/tests.toml +++ b/tests.toml @@ -1,6 +1,6 @@ [const_function_cycle] parser_errors = [] -compiler_errors = ["Error: Cannot recursively reference constant. (8:5)"] +compiler_errors = ["Error: Cannot recursively reference constant (8:5)"] [recursive_function] bytes = 95 @@ -18,7 +18,7 @@ hash = "3e851b5ad6b5c395bd46219991d2c7bb113e91918a065c705cc9068445bcc2a7" [inline_function_cycle] parser_errors = [] -compiler_errors = ["Error: Cannot recursively call inline function. (10:5)"] +compiler_errors = ["Error: Cannot recursively call inline function (10:5)"] [const_reference] bytes = 31 @@ -29,7 +29,7 @@ hash = "e3429ee993cedb79fa5b67ebd52635a294daf9c69b1c1d1a967f884fb4c3f1d1" [inline_const_inline_function_cycle] parser_errors = [] -compiler_errors = ["Error: Cannot recursively call inline function. (5:27)"] +compiler_errors = ["Error: Cannot recursively call inline function (5:27)"] [mixed_consts] bytes = 39 @@ -40,27 +40,27 @@ hash = "14870051d753aa4295466e166782fb5da7f18ad450a0ed0af9d7ccb83a82bed9" [inline_const_function_cycle] parser_errors = [] -compiler_errors = ["Error: Cannot recursively reference inline constant. (8:5)"] +compiler_errors = ["Error: Cannot recursively reference inline constant (8:5)"] [inline_const_self] parser_errors = [] -compiler_errors = ["Error: Cannot recursively reference inline constant. (1:27)"] +compiler_errors = ["Error: Cannot recursively reference inline constant (1:27)"] [recursive_inline_function] parser_errors = [] -compiler_errors = ["Error: Cannot recursively call inline function. (6:5)"] +compiler_errors = ["Error: Cannot recursively call inline function (6:5)"] [const_inline_function_cycle] parser_errors = [] -compiler_errors = ["Error: Cannot recursively call inline function. (5:20)"] +compiler_errors = ["Error: Cannot recursively call inline function (5:20)"] [const_self] parser_errors = [] -compiler_errors = ["Error: Cannot recursively reference constant. (1:20)"] +compiler_errors = ["Error: Cannot recursively reference constant (1:20)"] [const_cycle] parser_errors = [] -compiler_errors = ["Error: Cannot recursively reference constant. (2:16)"] +compiler_errors = ["Error: Cannot recursively reference constant (2:16)"] [inline_const_reference] bytes = 3 @@ -185,7 +185,7 @@ hash = "8172dac13078e7c4ce8a6062bb5dd117bb39d45b7e9ca3b9970dfef4be188ac8" [block_return] parser_errors = [] -compiler_errors = ["Error: Explicit return is not allowed within expressions. (2:5)"] +compiler_errors = ["Error: Explicit return is not allowed within expressions (2:5)"] [block_function] bytes = 57 @@ -203,7 +203,7 @@ hash = "6636275e009c26ccaba0bde1f76d4e1451f57462b1d2528a3a5df0a3202c1f78" [block_nested_return] parser_errors = [] -compiler_errors = ["Error: Explicit return is not allowed within expressions. (2:10)"] +compiler_errors = ["Error: Explicit return is not allowed within expressions (2:10)"] [enum_discriminant] bytes = 199 @@ -243,39 +243,39 @@ hash = "600c2c11a7ceb22fd3f5d2559d22120f9ad7e8d47e0966ddcfbcce20e02f373f" [function_call] parser_errors = [] compiler_errors = [ - "Error: Expected 0 arguments, but found 1. (3:11)", - "Error: Expected either 0 or 1 arguments, but found 2. (7:11)", - "Error: This function does not support the spread operator on its last argument. (8:25)", - "Error: Expected either 0 or 1 arguments, but found 2. (9:11)", - "Error: This function does not support the spread operator on its last argument. (9:28)", - "Error: Expected either 1 or 2 arguments, but found 0. (11:11)", - "Error: Expected either 1 or 2 arguments, but found 3. (14:11)", - "Error: Expected either 1 or 2 arguments, but found 4. (15:11)", - "Error: This function does not support the spread operator on its last argument. (16:25)", - "Error: This function does not support the spread operator on its last argument. (17:28)", - "Error: Expected either 1 or 2 arguments, but found 3. (18:11)", - "Error: This function does not support the spread operator on its last argument. (18:31)", - "Error: Expected either 1 or 2 arguments, but found 4. (19:11)", - "Error: This function does not support the spread operator on its last argument. (19:34)", - "Error: This function does not support the spread operator on its last argument. (20:28)", - "Error: Expected type `Int[]`, but found `Int`. (26:28)", - "Error: Expected type `Int[]`, but found `Int`. (27:31)", - "Error: Expected at least 2 arguments, but found 0. (31:11)", - "Error: Expected type `Int[]`, but found `Int`. (36:31)", - "Error: Expected 1 argument, but found 0. (40:11)", - "Error: This function requires the spread operator on its last argument. (41:27)", - "Error: Expected 1 argument, but found 2. (42:11)", - "Error: This function requires the spread operator on its last argument. (42:27)", - "Error: Expected 1 argument, but found 2. (44:11)", - "Error: Expected type `Int`, but found `Int[]`. (45:27)", - "Error: Expected 1 argument, but found 2. (46:11)", - "Error: Expected type `Int`, but found `Int[]`. (46:30)", - "Error: Expected 2 arguments, but found 0. (48:11)", - "Error: Expected 2 arguments, but found 1. (49:11)", - "Error: This function requires the spread operator on its last argument. (50:30)", - "Error: Expected 2 arguments, but found 1. (51:11)", - "Error: Expected 2 arguments, but found 1. (53:11)", - "Error: Expected type `Int`, but found `Int[]`. (54:30)", + "Error: Expected 0 arguments, but found 1 (3:11)", + "Error: Expected either 0 or 1 arguments, but found 2 (7:11)", + "Error: This function does not support the spread operator on its last argument (8:25)", + "Error: Expected either 0 or 1 arguments, but found 2 (9:11)", + "Error: This function does not support the spread operator on its last argument (9:28)", + "Error: Expected either 1 or 2 arguments, but found 0 (11:11)", + "Error: Expected either 1 or 2 arguments, but found 3 (14:11)", + "Error: Expected either 1 or 2 arguments, but found 4 (15:11)", + "Error: This function does not support the spread operator on its last argument (16:25)", + "Error: This function does not support the spread operator on its last argument (17:28)", + "Error: Expected either 1 or 2 arguments, but found 3 (18:11)", + "Error: This function does not support the spread operator on its last argument (18:31)", + "Error: Expected either 1 or 2 arguments, but found 4 (19:11)", + "Error: This function does not support the spread operator on its last argument (19:34)", + "Error: This function does not support the spread operator on its last argument (20:28)", + "Error: Expected type `Int[]`, but found `Int` (26:28)", + "Error: Expected type `Int[]`, but found `Int` (27:31)", + "Error: Expected at least 2 arguments, but found 0 (31:11)", + "Error: Expected type `Int[]`, but found `Int` (36:31)", + "Error: Expected 1 argument, but found 0 (40:11)", + "Error: This function requires the spread operator on its last argument (41:27)", + "Error: Expected 1 argument, but found 2 (42:11)", + "Error: This function requires the spread operator on its last argument (42:27)", + "Error: Expected 1 argument, but found 2 (44:11)", + "Error: Expected type `Int`, but found `Int[]` (45:27)", + "Error: Expected 1 argument, but found 2 (46:11)", + "Error: Expected type `Int`, but found `Int[]` (46:30)", + "Error: Expected 2 arguments, but found 0 (48:11)", + "Error: Expected 2 arguments, but found 1 (49:11)", + "Error: This function requires the spread operator on its last argument (50:30)", + "Error: Expected 2 arguments, but found 1 (51:11)", + "Error: Expected 2 arguments, but found 1 (53:11)", + "Error: Expected type `Int`, but found `Int[]` (54:30)", ] [struct_empty] @@ -309,10 +309,10 @@ hash = "e1bb38bc03b979e06bcfc2341001b12647928e29d682637eee425adf5b1bf212" [infer_lambda_params] parser_errors = [] compiler_errors = [ - "Error: Lambda parameter type could not be inferred. (2:24)", - "Error: Unused let binding `no_infer`. (2:9)", - "Error: Unused let binding `infer`. (3:9)", - "Error: Unused let binding `explicit`. (4:9)", + "Error: Lambda parameter type could not be inferred (2:24)", + "Error: Unused let binding `no_infer` (2:9)", + "Error: Unused let binding `infer` (3:9)", + "Error: Unused let binding `explicit` (4:9)", ] [infer_list] @@ -486,10 +486,10 @@ hash = "e977dca31ec8aba231dd0e858a3235d2dea5ed36aefed5f12c48ec02bf450b77" [enum_duplicate_variants] parser_errors = [] compiler_errors = [ - "Error: Duplicate enum variant `SameName` specified. (3:5)", - "Error: Duplicate enum discriminant `1` specified. (4:29)", - "Error: Unused enum variant `SameName`. (2:5)", - "Error: Unused enum variant `DuplicateDiscriminant`. (4:5)", + "Error: Duplicate enum variant `SameName` specified (3:5)", + "Error: Duplicate enum discriminant `1` specified (4:29)", + "Error: Unused enum variant `SameName` (2:5)", + "Error: Unused enum variant `DuplicateDiscriminant` (4:5)", ] [p2_fusion] @@ -544,7 +544,7 @@ hash = "9bdda9e1aa36d402104334fad3d7d9ce5b7b507b92f985e24c21513a012f60c4" [p2_delegated_or_hidden] bytes = 249 cost = 20906 -input = "(0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 () (q . ((51 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))) 1)" +input = "(0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 () (q ((51 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))) 1)" output = "((g1_multiply 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0x3d7e1145b5969c12f4889f4f1f66bde0e4d3ba54b91784cf604294d162b44b69) (g1_negate 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))" hash = "21f96d7bb1b15b83ce81dff3525d4c98793f906f6cc7ebba52a76524a7db6943" @@ -572,7 +572,7 @@ hash = "00f43ce9fcc63d5019e209c103e6b0aaf56bbe7fc7fafae5af7f5ee6887a8719" [external_function] bytes = 7 cost = 203 -input = "((q . ((100) (200) (300))))" +input = "((q ((100) (200) (300))))" output = "((100) (200) (300))" hash = "942555c6a5c562ab3dcb4d530a29dd55541ce274c3528b9c5aa7794a862547f3" From ab851f4d4c9e7c01d41d85e1d8d56ba78b06047a Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 1 Aug 2024 12:51:55 -0400 Subject: [PATCH 087/100] Temp --- crates/rue-compiler/src/compiler/block.rs | 2 +- .../src/compiler/expr/function_call_expr.rs | 49 ++++++- crates/rue-compiler/src/compiler/path.rs | 33 ++++- crates/rue-parser/src/ast.rs | 4 + crates/rue-parser/src/grammar.rs | 32 +++-- crates/rue-typing/src/comparison.rs | 126 +++++++++++------- crates/rue-typing/src/substitute_type.rs | 2 +- crates/rue-typing/src/test_tools.rs | 14 +- crates/rue-typing/src/type_system.rs | 17 +-- examples/p2_fusion.rue | 2 +- examples/royalty_split.rue | 2 +- tests.toml | 2 +- tests/block/block_let_function.rue | 2 +- 13 files changed, 184 insertions(+), 103 deletions(-) diff --git a/crates/rue-compiler/src/compiler/block.rs b/crates/rue-compiler/src/compiler/block.rs index 4c6f6af..984f1ea 100644 --- a/crates/rue-compiler/src/compiler/block.rs +++ b/crates/rue-compiler/src/compiler/block.rs @@ -84,7 +84,7 @@ impl Compiler<'_> { terminator = BlockTerminator::Raise; is_terminated = true; - statements.push(Statement::Return(Value::new(hir_id, self.ty.std().unknown))); + statements.push(Statement::Return(Value::new(hir_id, self.ty.std().never))); } Stmt::AssertStmt(assert_stmt) => { // Compile the condition expression. diff --git a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs index b62d0e1..30e8763 100644 --- a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use rowan::TextRange; use rue_parser::{AstNode, FunctionCallExpr}; -use rue_typing::{deconstruct_items, unwrap_list, Callable, Lazy, Type, TypeId}; +use rue_typing::{deconstruct_items, unwrap_list, Callable, TypeId}; use crate::{compiler::Compiler, hir::Hir, value::Value, ErrorKind}; @@ -38,8 +38,48 @@ impl Compiler<'_> { } } + let generic_args = if let Some(generic_params) = function_type.as_ref().and_then(|fun| { + if fun.generic_types.is_empty() { + None + } else { + Some(fun.generic_types.clone()) + } + }) { + if let Some(generic_args) = call.generic_args() { + let types = generic_args.types(); + + if types.len() == generic_params.len() { + let mut generic_types = HashMap::new(); + + for (i, ty) in types.into_iter().enumerate() { + let type_id = self.compile_type(ty); + generic_types.insert(generic_params[i], type_id); + } + + generic_types + } else { + self.db.error( + ErrorKind::GenericArgsMismatch(types.len(), generic_params.len()), + generic_args.syntax().text_range(), + ); + + HashMap::new() + } + } else { + HashMap::new() + } + } else { + if let Some(generic_args) = call.generic_args() { + self.db.error( + ErrorKind::UnexpectedGenericArgs, + generic_args.syntax().text_range(), + ); + } + HashMap::new() + }; + // Push a generic type context for the function, and allow inference. - self.generic_type_stack.push(HashMap::new()); + self.generic_type_stack.push(generic_args); self.allow_generic_inference_stack.push(true); // Compile the arguments naively, and defer type checking until later. @@ -137,10 +177,7 @@ impl Compiler<'_> { function_type.map_or(self.ty.std().unknown, |expected| expected.return_type); if !generic_types.is_empty() { - type_id = self.ty.alloc(Type::Lazy(Lazy { - type_id, - substitutions: generic_types.into_iter().collect(), - })); + type_id = self.ty.substitute(type_id, generic_types); } // Build the HIR for the function call. diff --git a/crates/rue-compiler/src/compiler/path.rs b/crates/rue-compiler/src/compiler/path.rs index bc0c4d8..dcba4ec 100644 --- a/crates/rue-compiler/src/compiler/path.rs +++ b/crates/rue-compiler/src/compiler/path.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use indexmap::IndexMap; use rowan::TextRange; use rue_parser::{AstNode, GenericArgs, PathItem}; @@ -197,6 +199,16 @@ impl Compiler<'_> { type_id: TypeId, generic_args: Option, text_range: TextRange, + ) -> Option { + self.handle_generics_impl(type_id, generic_args, text_range) + .map(|type_id| self.ty.substitute(type_id, HashMap::new())) + } + + fn handle_generics_impl( + &mut self, + mut type_id: TypeId, + generic_args: Option, + text_range: TextRange, ) -> Option { let Type::Alias(alias) = self.ty.get(type_id) else { if generic_args.is_some() { @@ -223,17 +235,24 @@ impl Compiler<'_> { return None; } - let mut lazy = Lazy { - type_id, - substitutions: IndexMap::new(), - }; - + let mut substitutions = IndexMap::new(); for (generic_type, arg) in alias.generic_types.clone().into_iter().zip(generic_args) { let arg = self.compile_type(arg); - lazy.substitutions.insert(generic_type, arg); + substitutions.insert(generic_type, arg); + } + + if self.type_definition_stack.is_empty() { + type_id = self + .ty + .substitute(type_id, substitutions.into_iter().collect()); + } else { + type_id = self.ty.alloc(Type::Lazy(Lazy { + type_id, + substitutions, + })); } - Some(self.ty.alloc(Type::Lazy(lazy))) + Some(type_id) } else { Some(type_id) } diff --git a/crates/rue-parser/src/ast.rs b/crates/rue-parser/src/ast.rs index d32e9da..7d2d935 100644 --- a/crates/rue-parser/src/ast.rs +++ b/crates/rue-parser/src/ast.rs @@ -755,6 +755,10 @@ impl IfExpr { } impl FunctionCallExpr { + pub fn generic_args(&self) -> Option { + self.syntax().children().find_map(GenericArgs::cast) + } + pub fn callee(&self) -> Option { self.syntax().children().find_map(Expr::cast) } diff --git a/crates/rue-parser/src/grammar.rs b/crates/rue-parser/src/grammar.rs index ed1ce7e..323c5b9 100644 --- a/crates/rue-parser/src/grammar.rs +++ b/crates/rue-parser/src/grammar.rs @@ -340,6 +340,8 @@ fn expr(p: &mut Parser<'_>) { fn expr_binding_power(p: &mut Parser<'_>, minimum_binding_power: u8, allow_initializer: bool) { let checkpoint = p.checkpoint(); + let mut at_generic_args = false; + if p.at(SyntaxKind::Not) || p.at(SyntaxKind::Minus) || p.at(SyntaxKind::Plus) @@ -360,9 +362,9 @@ fn expr_binding_power(p: &mut Parser<'_>, minimum_binding_power: u8, allow_initi p.bump(); p.finish(); } else if p.at(SyntaxKind::Ident) { - path_expr(p); - - if p.at(SyntaxKind::OpenBrace) && allow_initializer { + if path_expr(p) { + at_generic_args = true; + } else if p.at(SyntaxKind::OpenBrace) && allow_initializer { p.start_at(checkpoint, SyntaxKind::InitializerExpr); p.bump(); while !p.at(SyntaxKind::CloseBrace) { @@ -403,9 +405,12 @@ fn expr_binding_power(p: &mut Parser<'_>, minimum_binding_power: u8, allow_initi } loop { - if p.at(SyntaxKind::OpenParen) { + if at_generic_args || p.at(SyntaxKind::OpenParen) { p.start_at(checkpoint, SyntaxKind::FunctionCallExpr); - p.bump(); + if at_generic_args { + generic_args(p); + } + p.expect(SyntaxKind::OpenParen); while !p.at(SyntaxKind::CloseParen) { function_call_arg(p); if !p.try_eat(SyntaxKind::Comma) { @@ -414,6 +419,7 @@ fn expr_binding_power(p: &mut Parser<'_>, minimum_binding_power: u8, allow_initi } p.expect(SyntaxKind::CloseParen); p.finish(); + at_generic_args = false; } else if p.at(SyntaxKind::Dot) { p.start_at(checkpoint, SyntaxKind::FieldAccessExpr); p.bump(); @@ -489,23 +495,23 @@ fn expr_binding_power(p: &mut Parser<'_>, minimum_binding_power: u8, allow_initi } } -fn path_expr(p: &mut Parser<'_>) { +#[must_use] +fn path_expr(p: &mut Parser<'_>) -> bool { p.start(SyntaxKind::PathExpr); p.start(SyntaxKind::PathItem); p.expect(SyntaxKind::Ident); - let mut next = p.try_eat(SyntaxKind::PathSeparator); - if next && p.at(SyntaxKind::LessThan) { - generic_args(p); - next = p.try_eat(SyntaxKind::PathSeparator); - } p.finish(); - while next { + while p.try_eat(SyntaxKind::PathSeparator) { + if p.at(SyntaxKind::LessThan) { + p.finish(); + return true; + } p.start(SyntaxKind::PathItem); p.expect(SyntaxKind::Ident); - next = p.try_eat(SyntaxKind::PathSeparator); p.finish(); } p.finish(); + false } fn function_call_arg(p: &mut Parser<'_>) { diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index d7efeb1..ce6648e 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -54,6 +54,40 @@ pub(crate) fn compare_type( (Type::Generic, _) if found_lhs.is_some() => compare_type(db, found_lhs.unwrap(), rhs, ctx), (_, Type::Generic) if found_rhs.is_some() => compare_type(db, lhs, found_rhs.unwrap(), ctx), + // Infer generics. + (_, Type::Generic) => { + if let Some(inferred) = ctx + .inferred + .iter() + .rev() + .find_map(|map| map.get(&rhs).copied()) + { + compare_type(db, lhs, inferred, ctx) + } else if lhs == rhs { + Comparison::Equal + } else if ctx.infer_generics { + ctx.inferred.last_mut().unwrap().insert(rhs, lhs); + Comparison::Assignable + } else { + Comparison::Incompatible + } + } + + (Type::Generic, _) => { + if let Some(inferred) = ctx + .inferred + .iter() + .rev() + .find_map(|map| map.get(&lhs).copied()) + { + compare_type(db, inferred, rhs, ctx) + } else if lhs == rhs { + Comparison::Equal + } else { + Comparison::Incompatible + } + } + // These types are identical. (Type::Unknown, Type::Unknown) | (Type::Never, Type::Never) @@ -68,20 +102,7 @@ pub(crate) fn compare_type( // These are assignable since the structure and semantics match. (_, Type::Any | Type::Unknown) - | ( - Type::Unknown | Type::Never, - Type::Bytes - | Type::Bytes32 - | Type::PublicKey - | Type::Int - | Type::Nil - | Type::True - | Type::False - | Type::Value(..) - | Type::Pair(..) - | Type::Callable(..), - ) - | (Type::Unknown, Type::Never) + | (Type::Unknown | Type::Never, _) | (Type::Value(..), Type::Int) | (Type::Bytes32 | Type::Nil, Type::Bytes) => Comparison::Assignable, @@ -377,40 +398,6 @@ pub(crate) fn compare_type( compare_type(db, lhs.return_type, rhs.return_type, ctx), ), (Type::Callable(..), _) => compare_type(db, lhs, db.std().any, ctx), - - // Infer generics. - (_, Type::Generic) => { - if let Some(inferred) = ctx - .inferred - .iter() - .rev() - .find_map(|map| map.get(&rhs).copied()) - { - compare_type(db, lhs, inferred, ctx) - } else if lhs == rhs { - Comparison::Equal - } else if ctx.infer_generics { - ctx.inferred.last_mut().unwrap().insert(rhs, lhs); - Comparison::Assignable - } else { - Comparison::Incompatible - } - } - - (Type::Generic, _) => { - if let Some(inferred) = ctx - .inferred - .iter() - .rev() - .find_map(|map| map.get(&lhs).copied()) - { - compare_type(db, inferred, rhs, ctx) - } else if lhs == rhs { - Comparison::Equal - } else { - Comparison::Incompatible - } - } }; ctx.visited.remove(&(lhs, rhs)); @@ -422,7 +409,7 @@ pub(crate) fn compare_type( mod tests { use indexmap::indexmap; - use crate::{alloc_list, alloc_struct, alloc_tuple_of, Struct}; + use crate::{alloc_list, alloc_struct, alloc_tuple_of, Enum, Struct, Variant}; use super::*; @@ -671,6 +658,47 @@ mod tests { } } + #[test] + fn test_compare_enum_generic_inference() { + let mut db = TypeSystem::new(); + + let generic = db.alloc(Type::Generic); + + let mut stack = vec![HashMap::new()]; + + let enum_type = db.alloc(Type::Unknown); + let variant = db.alloc(Type::Unknown); + + let variant_inner = db.alloc(Type::Value(BigInt::ZERO)); + + *db.get_mut(variant) = Type::Variant(Variant { + original_enum_type_id: enum_type, + original_type_id: variant, + type_id: variant_inner, + field_names: None, + nil_terminated: false, + generic_types: vec![], + discriminant: BigInt::ZERO, + }); + + *db.get_mut(enum_type) = Type::Enum(Enum { + original_type_id: enum_type, + type_id: variant, + has_fields: false, + variants: indexmap! { + "A".to_string() => variant + }, + }); + + assert_eq!( + db.compare_with_generics(enum_type, generic, &mut stack, true), + Comparison::Assignable + ); + + assert_eq!(stack.len(), 1); + assert_eq!(stack[0].get(&generic), Some(&enum_type)); + } + #[test] fn test_compare_union_to_rhs_incompatible() { let mut db = TypeSystem::new(); diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index b42a089..a7f393b 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -63,7 +63,7 @@ pub(crate) fn substitute_type( substitutions.push(lazy.substitutions.clone().into_iter().collect()); let result = substitute_type(types, lazy.type_id, substitutions); substitutions.pop().unwrap(); - substitute_type(types, result, substitutions) + result } Type::Alias(alias) => { let alias = alias.clone(); diff --git a/crates/rue-typing/src/test_tools.rs b/crates/rue-typing/src/test_tools.rs index 411249b..5b68986 100644 --- a/crates/rue-typing/src/test_tools.rs +++ b/crates/rue-typing/src/test_tools.rs @@ -1,14 +1,16 @@ use indexmap::{indexmap, IndexMap}; -use crate::{Callable, Lazy, Struct, Type, TypeId, TypeSystem}; +use crate::{Callable, Struct, Type, TypeId, TypeSystem}; pub fn alloc_list(db: &mut TypeSystem, item_type_id: TypeId) -> TypeId { - db.alloc(Type::Lazy(Lazy { - type_id: db.std().unmapped_list, - substitutions: indexmap! { + db.substitute( + db.std().unmapped_list, + indexmap! { db.std().generic_list_item => item_type_id, - }, - })) + } + .into_iter() + .collect(), + ) } pub fn alloc_callable( diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index cbf0d3a..b9ec40d 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -1,12 +1,11 @@ use std::collections::{HashMap, HashSet}; use id_arena::{Arena, Id}; -use indexmap::IndexMap; use crate::{ check_type, compare_type, debug_type, difference_type, replace_type, simplify_check, stringify_type, substitute_type, Alias, Callable, Check, CheckError, Comparison, - ComparisonContext, Lazy, StandardTypes, Type, TypePath, + ComparisonContext, StandardTypes, Type, TypePath, }; pub type TypeId = Id; @@ -148,22 +147,12 @@ impl TypeSystem { } pub fn get_callable_recursive(&mut self, type_id: TypeId) -> Option<&Callable> { - let type_id = self.substitute(type_id, HashMap::new()); match self.get_recursive(type_id) { Type::Callable(callable) => Some(callable), _ => None, } } - pub fn alloc_list(&mut self, type_id: TypeId) -> TypeId { - let mut substitutions = IndexMap::new(); - substitutions.insert(self.types.generic_list_item, type_id); - self.alloc(Type::Lazy(Lazy { - type_id: self.types.unmapped_list, - substitutions, - })) - } - pub fn stringify_named(&self, type_id: TypeId, mut names: HashMap) -> String { for (id, name) in &self.names { names.entry(*id).or_insert_with(|| name.clone()); @@ -213,14 +202,10 @@ impl TypeSystem { } pub fn check(&mut self, lhs: TypeId, rhs: TypeId) -> Result { - let lhs = self.substitute(lhs, HashMap::new()); - let rhs = self.substitute(rhs, HashMap::new()); check_type(self, lhs, rhs, &mut HashSet::new()).map(simplify_check) } pub fn difference(&mut self, lhs: TypeId, rhs: TypeId) -> TypeId { - let lhs = self.substitute(lhs, HashMap::new()); - let rhs = self.substitute(rhs, HashMap::new()); difference_type(self, lhs, rhs, &mut HashSet::new()) } diff --git a/examples/p2_fusion.rue b/examples/p2_fusion.rue index b01324e..3a896bf 100644 --- a/examples/p2_fusion.rue +++ b/examples/p2_fusion.rue @@ -52,7 +52,7 @@ fun main( Condition::CreateCoin { puzzle_hash: p2_puzzle_hash, amount: my_amount, - memos: [p2_puzzle_hash], + memos: ([p2_puzzle_hash], nil), }, // Announce that a specific singleton is being spent to diff --git a/examples/royalty_split.rue b/examples/royalty_split.rue index a6922f8..df01431 100644 --- a/examples/royalty_split.rue +++ b/examples/royalty_split.rue @@ -40,7 +40,7 @@ fun split_amount_and_create_coins( let create_coin = Condition::CreateCoin { puzzle_hash: payout.puzzle_hash, amount: if payout.share > 0 { this_amount } else { remaining_amount }, - memos: [payout.puzzle_hash], + memos: ([payout.puzzle_hash], nil), }; let rest = calculate_amount_and_split(payouts.rest, total_amount, total_shares, shares_sum + payout.share, remaining_amount - this_amount); [create_coin, ...rest] diff --git a/tests.toml b/tests.toml index 8ffc4b9..1ac2495 100644 --- a/tests.toml +++ b/tests.toml @@ -572,7 +572,7 @@ hash = "00f43ce9fcc63d5019e209c103e6b0aaf56bbe7fc7fafae5af7f5ee6887a8719" [external_function] bytes = 7 cost = 203 -input = "((q ((100) (200) (300))))" +input = "((q . ((100) (200) (300))))" output = "((100) (200) (300))" hash = "942555c6a5c562ab3dcb4d530a29dd55541ce274c3528b9c5aa7794a862547f3" diff --git a/tests/block/block_let_function.rue b/tests/block/block_let_function.rue index 0ba8703..d0feb5a 100644 --- a/tests/block/block_let_function.rue +++ b/tests/block/block_let_function.rue @@ -2,7 +2,7 @@ fun main() -> Bool { let outer: Int | Nil = 42; let another_outer = 19; let not_used_in_block = false; - let result = !not_used_in_block && outer is Int && { + let result = !not_used_in_block && !(outer is Nil) && { let double = double(outer); outer + double + double - double + another_outer == 126 + 19 }; From e05f13e2fd2baea3d12cfad6819cce0352a86fa5 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 1 Aug 2024 12:54:11 -0400 Subject: [PATCH 088/100] Update tests --- tests.toml | 76 ++++++++++++++++++++++-------------------------------- 1 file changed, 31 insertions(+), 45 deletions(-) diff --git a/tests.toml b/tests.toml index 1ac2495..6820620 100644 --- a/tests.toml +++ b/tests.toml @@ -213,11 +213,11 @@ output = "()" hash = "1a9674474efa85b2616b28fc2ae4f1b6a199273973e482ea74498fcc507141cf" [enum_fields] -bytes = 35 -cost = 636 +bytes = 65 +cost = 1004 input = "()" output = "1000" -hash = "9cccd0bde90e1e21f335c88acd5af8788b190c7bcfe652995564064ff2f34e28" +hash = "6f486106a324ee36d5b3e63bd65d7e69c8a95278d4f405307492e0c4960b08f2" [enum_numeric] bytes = 103 @@ -244,38 +244,24 @@ hash = "600c2c11a7ceb22fd3f5d2559d22120f9ad7e8d47e0966ddcfbcce20e02f373f" parser_errors = [] compiler_errors = [ "Error: Expected 0 arguments, but found 1 (3:11)", - "Error: Expected either 0 or 1 arguments, but found 2 (7:11)", - "Error: This function does not support the spread operator on its last argument (8:25)", - "Error: Expected either 0 or 1 arguments, but found 2 (9:11)", - "Error: This function does not support the spread operator on its last argument (9:28)", - "Error: Expected either 1 or 2 arguments, but found 0 (11:11)", - "Error: Expected either 1 or 2 arguments, but found 3 (14:11)", - "Error: Expected either 1 or 2 arguments, but found 4 (15:11)", - "Error: This function does not support the spread operator on its last argument (16:25)", - "Error: This function does not support the spread operator on its last argument (17:28)", - "Error: Expected either 1 or 2 arguments, but found 3 (18:11)", - "Error: This function does not support the spread operator on its last argument (18:31)", - "Error: Expected either 1 or 2 arguments, but found 4 (19:11)", - "Error: This function does not support the spread operator on its last argument (19:34)", - "Error: This function does not support the spread operator on its last argument (20:28)", - "Error: Expected type `Int[]`, but found `Int` (26:28)", - "Error: Expected type `Int[]`, but found `Int` (27:31)", - "Error: Expected at least 2 arguments, but found 0 (31:11)", - "Error: Expected type `Int[]`, but found `Int` (36:31)", - "Error: Expected 1 argument, but found 0 (40:11)", - "Error: This function requires the spread operator on its last argument (41:27)", - "Error: Expected 1 argument, but found 2 (42:11)", - "Error: This function requires the spread operator on its last argument (42:27)", - "Error: Expected 1 argument, but found 2 (44:11)", - "Error: Expected type `Int`, but found `Int[]` (45:27)", - "Error: Expected 1 argument, but found 2 (46:11)", - "Error: Expected type `Int`, but found `Int[]` (46:30)", - "Error: Expected 2 arguments, but found 0 (48:11)", - "Error: Expected 2 arguments, but found 1 (49:11)", - "Error: This function requires the spread operator on its last argument (50:30)", - "Error: Expected 2 arguments, but found 1 (51:11)", - "Error: Expected 2 arguments, but found 1 (53:11)", - "Error: Expected type `Int`, but found `Int[]` (54:30)", + "Error: Expected type `(Int, {recursive} | Nil) | Nil`, but found `Int` (9:28)", + "Error: Expected type `(Int, {recursive} | Nil) | Nil`, but found `Int` (10:31)", + "Error: Expected at least 2 arguments, but found 0 (14:11)", + "Error: Expected type `(Int, {recursive} | Nil) | Nil`, but found `Int` (19:31)", + "Error: Expected 1 argument, but found 0 (23:11)", + "Error: This function requires the spread operator on its last argument (24:27)", + "Error: Expected 1 argument, but found 2 (25:11)", + "Error: This function requires the spread operator on its last argument (25:27)", + "Error: Expected 1 argument, but found 2 (27:11)", + "Error: Expected type `Int`, but found `(Int, (Int, Nil))` (28:27)", + "Error: Expected 1 argument, but found 2 (29:11)", + "Error: Expected type `Int`, but found `(Int, Nil)` (29:30)", + "Error: Expected 2 arguments, but found 0 (31:11)", + "Error: Expected 2 arguments, but found 1 (32:11)", + "Error: This function requires the spread operator on its last argument (33:30)", + "Error: Expected 2 arguments, but found 1 (34:11)", + "Error: Expected 2 arguments, but found 1 (36:11)", + "Error: Expected type `Int`, but found `(Int, Nil)` (37:30)", ] [struct_empty] @@ -543,24 +529,24 @@ hash = "9bdda9e1aa36d402104334fad3d7d9ce5b7b507b92f985e24c21513a012f60c4" [p2_delegated_or_hidden] bytes = 249 -cost = 20906 +cost = 24358 input = "(0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 () (q ((51 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))) 1)" output = "((g1_multiply 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0x3d7e1145b5969c12f4889f4f1f66bde0e4d3ba54b91784cf604294d162b44b69) (g1_negate 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))" hash = "21f96d7bb1b15b83ce81dff3525d4c98793f906f6cc7ebba52a76524a7db6943" [singleton] -bytes = 1395 -cost = 52110 +bytes = 1531 +cost = 54718 input = "((0x42840c6aebec47ce2e01629ce381b461c19695264281a7b1aab5d4ff54506775 0x4696e7a2b7682e2df01ab47e6e002d0dca895f99c6172e4a55a3e033499532b7 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8) 1 (0x9b1c580707ca8282534c02c1a055427e0954818b6195a29f4442ac3e7ea8e8ee () 1) 1 ((51 0x173385b87af5d8940767c328026fe5f8e76bc238d2a3aaddf4f55e844f400fca 1)))" output = "((73 1) (71 0xf92f0ebbd0e5ecb1334331d98c1f3b3e41cfce2c15f1053ffd1e2151b361e909) (g1_negate 0x07d534114dd68436cb7a4026abade359cd9c9f28b253c60e305535c781bbc7ed 1))" -hash = "28a3c50a049c12c4ce49c6098618957b4c6791c563c70c895fa59bf47d93366c" +hash = "3d8892dcdac1a32aab1034fa9489b7b7dc3b6aabf512f38e73d54574b1f6b62c" [enum_type_guard] -bytes = 131 -cost = 1710 +bytes = 101 +cost = 1090 input = "()" output = "()" -hash = "e3153c4596c3b27d1f1cea81cf4c475e2d8f617330a82684bd97f6fae2bacf50" +hash = "146182f765c52c144e4fa6d44fd3073bb3cbed6fac2bf1ca15393fc244c7d2b8" [cat] bytes = 2107 @@ -584,8 +570,8 @@ output = "1500" hash = "c9370ff4457a61a860e99fbbcf92aec2630e45a9447428174a1c5036a0768aad" [block_let_function] -bytes = 281 -cost = 9394 +bytes = 283 +cost = 9356 input = "()" output = "1" -hash = "a1d71c94c9e64a24f50f4aee3496e833aa8bb263457b4cb4459f851f0f3d575b" +hash = "488ceea9106d9d45a4380644bdb88d7f440a901d08c1281a5b94ba92a2ae418d" From 05d18a0db7154c9a705f17611a5069b34559c187 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 1 Aug 2024 13:00:28 -0400 Subject: [PATCH 089/100] Fix unknown --- crates/rue-typing/src/comparison.rs | 34 +++++++++++++++-------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index ce6648e..5f01c33 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -50,6 +50,21 @@ pub(crate) fn compare_type( let comparison = match (db.get(lhs), db.get(rhs)) { (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), + // These types are identical. + (Type::Unknown, Type::Unknown) + | (Type::Never, Type::Never) + | (Type::Any, Type::Any) + | (Type::Bytes, Type::Bytes) + | (Type::Bytes32, Type::Bytes32) + | (Type::PublicKey, Type::PublicKey) + | (Type::Int, Type::Int) + | (Type::Nil, Type::Nil) + | (Type::True, Type::True) + | (Type::False, Type::False) => Comparison::Equal, + + // These should always be the case, regardless of the other type. + (_, Type::Any | Type::Unknown) | (Type::Unknown | Type::Never, _) => Comparison::Assignable, + // Handle generics and substitutions. (Type::Generic, _) if found_lhs.is_some() => compare_type(db, found_lhs.unwrap(), rhs, ctx), (_, Type::Generic) if found_rhs.is_some() => compare_type(db, lhs, found_rhs.unwrap(), ctx), @@ -88,23 +103,10 @@ pub(crate) fn compare_type( } } - // These types are identical. - (Type::Unknown, Type::Unknown) - | (Type::Never, Type::Never) - | (Type::Any, Type::Any) - | (Type::Bytes, Type::Bytes) - | (Type::Bytes32, Type::Bytes32) - | (Type::PublicKey, Type::PublicKey) - | (Type::Int, Type::Int) - | (Type::Nil, Type::Nil) - | (Type::True, Type::True) - | (Type::False, Type::False) => Comparison::Equal, - // These are assignable since the structure and semantics match. - (_, Type::Any | Type::Unknown) - | (Type::Unknown | Type::Never, _) - | (Type::Value(..), Type::Int) - | (Type::Bytes32 | Type::Nil, Type::Bytes) => Comparison::Assignable, + (Type::Value(..), Type::Int) | (Type::Bytes32 | Type::Nil, Type::Bytes) => { + Comparison::Assignable + } // These are castable since the structure matches but the semantics differ. ( From f0447a1db771f89c142f6fcfba24e652accf5987 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 2 Aug 2024 13:44:54 -0400 Subject: [PATCH 090/100] Temp --- Cargo.lock | 49 ++++++++++++++++++- Cargo.toml | 2 + crates/rue-compiler/src/compiler.rs | 2 +- .../src/compiler/expr/function_call_expr.rs | 2 +- .../src/compiler/expr/initializer_expr.rs | 2 +- .../src/compiler/expr/lambda_expr.rs | 2 +- crates/rue-compiler/src/compiler/path.rs | 2 +- .../rue-compiler/src/compiler/stmt/if_stmt.rs | 2 +- crates/rue-compiler/src/lowerer.rs | 2 +- crates/rue-compiler/src/value.rs | 2 +- crates/rue-typing/Cargo.toml | 2 + crates/rue-typing/src/check/attributes.rs | 4 +- crates/rue-typing/src/check/check_type.rs | 2 +- crates/rue-typing/src/comparison.rs | 25 ++-------- crates/rue-typing/src/debug_type.rs | 2 +- crates/rue-typing/src/difference.rs | 2 +- crates/rue-typing/src/lib.rs | 2 + crates/rue-typing/src/map.rs | 6 +++ crates/rue-typing/src/stringify.rs | 2 +- crates/rue-typing/src/substitute_type.rs | 2 +- crates/rue-typing/src/type_system.rs | 21 ++++++-- tests.toml | 4 +- 22 files changed, 97 insertions(+), 44 deletions(-) create mode 100644 crates/rue-typing/src/map.rs diff --git a/Cargo.lock b/Cargo.lock index df43a2a..35bb919 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,19 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -26,6 +39,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "anstream" version = "0.6.13" @@ -787,9 +806,13 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "heck" @@ -1460,8 +1483,10 @@ dependencies = [ name = "rue-typing" version = "0.1.1" dependencies = [ + "ahash", "anyhow", "clvmr 0.6.1", + "hashbrown", "id-arena", "indexmap", "num-bigint", @@ -2340,6 +2365,26 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.61", +] + [[package]] name = "zeroize" version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index 08247ac..36328d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,3 +62,5 @@ toml = "0.8.12" serde = "1.0.197" walkdir = "2.5.0" anyhow = "1.0.86" +hashbrown = "0.14.5" +ahash = "0.8.11" diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index 42a1773..d2a703b 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -1,6 +1,6 @@ #![allow(clippy::map_unwrap_or)] -use std::collections::HashMap; +use rue_typing::HashMap; pub(crate) use builtins::Builtins; diff --git a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs index 30e8763..90a6bf9 100644 --- a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use rue_typing::HashMap; use rowan::TextRange; use rue_parser::{AstNode, FunctionCallExpr}; diff --git a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs index d927334..b9ddd97 100644 --- a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use rue_typing::HashMap; use indexmap::IndexMap; use rowan::TextRange; diff --git a/crates/rue-compiler/src/compiler/expr/lambda_expr.rs b/crates/rue-compiler/src/compiler/expr/lambda_expr.rs index c0f9105..9ef1e4a 100644 --- a/crates/rue-compiler/src/compiler/expr/lambda_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/lambda_expr.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use rue_typing::HashMap; use indexmap::IndexSet; use rue_parser::{AstNode, LambdaExpr}; diff --git a/crates/rue-compiler/src/compiler/path.rs b/crates/rue-compiler/src/compiler/path.rs index dcba4ec..cba8f87 100644 --- a/crates/rue-compiler/src/compiler/path.rs +++ b/crates/rue-compiler/src/compiler/path.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use rue_typing::HashMap; use indexmap::IndexMap; use rowan::TextRange; diff --git a/crates/rue-compiler/src/compiler/stmt/if_stmt.rs b/crates/rue-compiler/src/compiler/stmt/if_stmt.rs index d1af5c8..f131ee7 100644 --- a/crates/rue-compiler/src/compiler/stmt/if_stmt.rs +++ b/crates/rue-compiler/src/compiler/stmt/if_stmt.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use rue_typing::HashMap; use rue_parser::{AstNode, IfStmt}; use rue_typing::TypeId; diff --git a/crates/rue-compiler/src/lowerer.rs b/crates/rue-compiler/src/lowerer.rs index 9ceaa4f..3b956b8 100644 --- a/crates/rue-compiler/src/lowerer.rs +++ b/crates/rue-compiler/src/lowerer.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use rue_typing::HashMap; use indexmap::IndexSet; diff --git a/crates/rue-compiler/src/value.rs b/crates/rue-compiler/src/value.rs index 5560356..4e6bb48 100644 --- a/crates/rue-compiler/src/value.rs +++ b/crates/rue-compiler/src/value.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use rue_typing::HashMap; mod guard; mod guard_path; diff --git a/crates/rue-typing/Cargo.toml b/crates/rue-typing/Cargo.toml index 10602ee..c602775 100644 --- a/crates/rue-typing/Cargo.toml +++ b/crates/rue-typing/Cargo.toml @@ -21,6 +21,8 @@ indexmap = { workspace = true } num-bigint = { workspace = true } num-traits = { workspace = true } clvmr = { workspace = true } +hashbrown = { workspace = true } +ahash = { workspace = true } [dev-dependencies] anyhow = { workspace = true } diff --git a/crates/rue-typing/src/check/attributes.rs b/crates/rue-typing/src/check/attributes.rs index efb79a3..5b6d31d 100644 --- a/crates/rue-typing/src/check/attributes.rs +++ b/crates/rue-typing/src/check/attributes.rs @@ -1,4 +1,6 @@ -use std::collections::{HashMap, HashSet, VecDeque}; +use std::collections::VecDeque; + +use crate::{HashMap, HashSet}; use num_bigint::BigInt; use num_traits::One; diff --git a/crates/rue-typing/src/check/check_type.rs b/crates/rue-typing/src/check/check_type.rs index ed877ee..2435f11 100644 --- a/crates/rue-typing/src/check/check_type.rs +++ b/crates/rue-typing/src/check/check_type.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use crate::HashSet; use num_bigint::BigInt; use num_traits::One; diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index 5f01c33..b24af3e 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -1,12 +1,9 @@ -use std::{ - cmp::{max, min}, - collections::{HashMap, HashSet}, -}; +use std::cmp::{max, min}; use num_bigint::BigInt; use num_traits::One; -use crate::{bigint_to_bytes, Type, TypeId, TypeSystem}; +use crate::{bigint_to_bytes, HashMap, HashSet, Type, TypeId, TypeSystem}; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Comparison { @@ -48,7 +45,7 @@ pub(crate) fn compare_type( .find_map(|substitutions| substitutions.get(&rhs).copied()); let comparison = match (db.get(lhs), db.get(rhs)) { - (Type::Ref(..), _) | (_, Type::Ref(..)) => unreachable!(), + (Type::Ref(..) | Type::Lazy(..), _) | (_, Type::Ref(..) | Type::Lazy(..)) => unreachable!(), // These types are identical. (Type::Unknown, Type::Unknown) @@ -317,22 +314,6 @@ pub(crate) fn compare_type( } } - // We need to push substititons onto the stack in order to accurately compare them. - (Type::Lazy(lazy), _) => { - ctx.lhs_substitutions - .push(lazy.substitutions.clone().into_iter().collect()); - let result = compare_type(db, lazy.type_id, rhs, ctx); - ctx.lhs_substitutions.pop().unwrap(); - result - } - (_, Type::Lazy(lazy)) => { - ctx.rhs_substitutions - .push(lazy.substitutions.clone().into_iter().collect()); - let result = compare_type(db, lhs, lazy.type_id, ctx); - ctx.rhs_substitutions.pop().unwrap(); - result - } - // Resolve the alias to the type that it's pointing to. (Type::Alias(alias), _) => compare_type(db, alias.type_id, rhs, ctx), (_, Type::Alias(alias)) => compare_type(db, lhs, alias.type_id, ctx), diff --git a/crates/rue-typing/src/debug_type.rs b/crates/rue-typing/src/debug_type.rs index 20ef142..d5c3870 100644 --- a/crates/rue-typing/src/debug_type.rs +++ b/crates/rue-typing/src/debug_type.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use crate::HashSet; use crate::{Type, TypeId, TypeSystem}; diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index 0683a9d..2e43d8c 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use crate::HashSet; use num_bigint::BigInt; use num_traits::One; diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index fad806c..abc0237 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -3,6 +3,7 @@ mod check; mod comparison; mod debug_type; mod difference; +mod map; mod replace_type; mod semantic_types; mod standard_types; @@ -15,6 +16,7 @@ mod type_system; pub use bigint::*; pub use check::*; pub use comparison::*; +pub use map::*; pub use semantic_types::*; pub use standard_types::*; pub use ty::*; diff --git a/crates/rue-typing/src/map.rs b/crates/rue-typing/src/map.rs new file mode 100644 index 0000000..c6e996e --- /dev/null +++ b/crates/rue-typing/src/map.rs @@ -0,0 +1,6 @@ +use std::hash::BuildHasherDefault; + +use ahash::AHasher; + +pub type HashMap = hashbrown::HashMap>; +pub type HashSet = hashbrown::HashSet>; diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify.rs index 1aa5e07..4cf18a7 100644 --- a/crates/rue-typing/src/stringify.rs +++ b/crates/rue-typing/src/stringify.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use crate::{HashMap, HashSet}; use crate::{Callable, Enum, Struct, Type, TypeId, TypeSystem, Variant}; diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index a7f393b..212fa21 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use crate::HashMap; use crate::{Alias, Callable, Enum, Struct, Type, TypeId, TypeSystem, Variant}; diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index b9ec40d..a2ec4f1 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -1,11 +1,11 @@ -use std::collections::{HashMap, HashSet}; +use std::time::{Duration, Instant}; use id_arena::{Arena, Id}; use crate::{ check_type, compare_type, debug_type, difference_type, replace_type, simplify_check, stringify_type, substitute_type, Alias, Callable, Check, CheckError, Comparison, - ComparisonContext, StandardTypes, Type, TypePath, + ComparisonContext, HashMap, HashSet, StandardTypes, Type, TypePath, }; pub type TypeId = Id; @@ -179,7 +179,9 @@ impl TypeSystem { substitution_stack: &mut Vec>, infer_generics: bool, ) -> Comparison { - compare_type( + let start = Instant::now(); + + let result = compare_type( self, lhs, rhs, @@ -190,7 +192,18 @@ impl TypeSystem { inferred: substitution_stack, infer_generics, }, - ) + ); + let duration = start.elapsed(); + if duration > Duration::from_millis(1) { + println!( + "\n\n\n{duration:?} between {} => {}", + self.stringify(lhs), + self.stringify(rhs) + ); + println!("LHS {}", self.debug(lhs)); + println!("RHS {}", self.debug(rhs)); + } + result } pub fn substitute( diff --git a/tests.toml b/tests.toml index 6820620..ffa503f 100644 --- a/tests.toml +++ b/tests.toml @@ -529,8 +529,8 @@ hash = "9bdda9e1aa36d402104334fad3d7d9ce5b7b507b92f985e24c21513a012f60c4" [p2_delegated_or_hidden] bytes = 249 -cost = 24358 -input = "(0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 () (q ((51 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))) 1)" +cost = 20906 +input = "(0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 () (q . ((51 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))) 1)" output = "((g1_multiply 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0x3d7e1145b5969c12f4889f4f1f66bde0e4d3ba54b91784cf604294d162b44b69) (g1_negate 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))" hash = "21f96d7bb1b15b83ce81dff3525d4c98793f906f6cc7ebba52a76524a7db6943" From ae9fc623524fbfed0210bbae482a4f193fa3cabd Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 2 Aug 2024 13:51:17 -0400 Subject: [PATCH 091/100] Remove superset for performance --- crates/rue-typing/src/comparison.rs | 99 +++++++++------------------- crates/rue-typing/src/difference.rs | 2 +- crates/rue-typing/src/type_system.rs | 19 +----- 3 files changed, 33 insertions(+), 87 deletions(-) diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/comparison.rs index b24af3e..c470f66 100644 --- a/crates/rue-typing/src/comparison.rs +++ b/crates/rue-typing/src/comparison.rs @@ -10,8 +10,7 @@ pub enum Comparison { Equal, Assignable, Castable, - Superset, - Incompatible, + NotEqual, } pub(crate) struct ComparisonContext<'a> { @@ -81,7 +80,7 @@ pub(crate) fn compare_type( ctx.inferred.last_mut().unwrap().insert(rhs, lhs); Comparison::Assignable } else { - Comparison::Incompatible + Comparison::NotEqual } } @@ -96,7 +95,7 @@ pub(crate) fn compare_type( } else if lhs == rhs { Comparison::Equal } else { - Comparison::Incompatible + Comparison::NotEqual } } @@ -114,7 +113,7 @@ pub(crate) fn compare_type( | (Type::False, Type::Nil) | (Type::Nil, Type::False) => Comparison::Castable, - // These are a superset since the right hand side is castable to the left hand side. + // These are incompatible since the structure differs. ( Type::Any, Type::Bytes @@ -149,10 +148,9 @@ pub(crate) fn compare_type( | Type::Pair(..) | Type::Callable(..), Type::Never, - ) => Comparison::Superset, - - // These are incompatible since the structure differs. - ( + ) + | (Type::Bytes32 | Type::PublicKey, Type::Value(..)) + | ( Type::Pair(..), Type::Bytes | Type::Bytes32 @@ -192,14 +190,14 @@ pub(crate) fn compare_type( | Type::Value(..) | Type::Pair(..), Type::Callable(..), - ) => Comparison::Incompatible, + ) => Comparison::NotEqual, // Value is a subtype of Int, so it's castable to Bytes32 if it's 32 bytes long. (Type::Value(value), Type::Bytes32) => { if bigint_to_bytes(value.clone()).len() == 32 { Comparison::Castable } else { - Comparison::Incompatible + Comparison::NotEqual } } @@ -208,44 +206,26 @@ pub(crate) fn compare_type( if bigint_to_bytes(value.clone()).len() == 48 { Comparison::Castable } else { - Comparison::Incompatible + Comparison::NotEqual } } - // Bytes32 is a superset of Value only if the value is 32 bytes long. - (Type::Bytes32, Type::Value(value)) => { - if bigint_to_bytes(value.clone()).len() == 32 { - Comparison::Superset - } else { - Comparison::Incompatible - } - } - - // PublicKey is a superset of Value only if the value is 48 bytes long. - (Type::PublicKey, Type::Value(value)) => { - if bigint_to_bytes(value.clone()).len() == 48 { - Comparison::Superset - } else { - Comparison::Incompatible - } - } - - // Nil and False are supersets of Value only if the value is zero. + // Nil and False are castable to Value only if the value is zero. (Type::Nil | Type::False, Type::Value(value)) | (Type::Value(value), Type::Nil | Type::False) => { if value == &BigInt::ZERO { Comparison::Castable } else { - Comparison::Incompatible + Comparison::NotEqual } } - // True is a superset of Value only if the value is one. + // True is castable to Value only if the value is one. (Type::True, Type::Value(value)) | (Type::Value(value), Type::True) => { if value == &BigInt::one() { Comparison::Castable } else { - Comparison::Incompatible + Comparison::NotEqual } } @@ -254,7 +234,7 @@ pub(crate) fn compare_type( if lhs == rhs { Comparison::Equal } else { - Comparison::Incompatible + Comparison::NotEqual } } @@ -270,29 +250,18 @@ pub(crate) fn compare_type( let items = items.clone(); let mut result = Comparison::Assignable; - let mut any_castable = false; - for item in items { let cmp = compare_type(db, item, rhs, ctx); result = max(result, cmp); - - if compare_type(db, rhs, item, ctx) <= Comparison::Castable { - any_castable = true; - } } - if result == Comparison::Incompatible && any_castable { - Comparison::Superset - } else { - result - } + result } // Anything can be assigned to a union so long as it's assignable to at least one of the items. (_, Type::Union(items)) => { let items = items.clone(); - let mut result = Comparison::Incompatible; - let mut any_incompatible = false; + let mut result = Comparison::NotEqual; for item in &items { if matches!(db.get_recursive(*item), Type::Never) { @@ -301,17 +270,9 @@ pub(crate) fn compare_type( let cmp = compare_type(db, lhs, *item, ctx); result = min(result, cmp); - - if cmp == Comparison::Incompatible { - any_incompatible = true; - } } - if any_incompatible && result == Comparison::Superset { - Comparison::Incompatible - } else { - max(result, Comparison::Assignable) - } + max(result, Comparison::Assignable) } // Resolve the alias to the type that it's pointing to. @@ -421,7 +382,7 @@ mod tests { fn test_compare_bytes_bytes32() { let db = TypeSystem::new(); let types = db.std(); - assert_eq!(db.compare(types.bytes, types.bytes32), Comparison::Superset); + assert_eq!(db.compare(types.bytes, types.bytes32), Comparison::NotEqual); } #[test] @@ -440,7 +401,7 @@ mod tests { let types = db.std(); assert_eq!( db.compare(types.bytes, types.public_key), - Comparison::Superset + Comparison::NotEqual ); } @@ -460,7 +421,7 @@ mod tests { let types = db.std(); assert_eq!( db.compare(types.bytes32, types.public_key), - Comparison::Incompatible + Comparison::NotEqual ); } @@ -470,7 +431,7 @@ mod tests { let types = db.std(); assert_eq!( db.compare(types.public_key, types.bytes32), - Comparison::Incompatible + Comparison::NotEqual ); } @@ -485,7 +446,7 @@ mod tests { fn test_compare_any_bytes() { let db = TypeSystem::new(); let types = db.std(); - assert_eq!(db.compare(types.any, types.bytes), Comparison::Superset); + assert_eq!(db.compare(types.any, types.bytes), Comparison::NotEqual); } #[test] @@ -499,7 +460,7 @@ mod tests { fn test_compare_any_bytes32() { let db = TypeSystem::new(); let types = db.std(); - assert_eq!(db.compare(types.any, types.bytes32), Comparison::Superset); + assert_eq!(db.compare(types.any, types.bytes32), Comparison::NotEqual); } #[test] @@ -574,7 +535,7 @@ mod tests { let types = db.std(); let lhs = db.alloc(Type::Pair(types.int, types.public_key)); let rhs = db.alloc(Type::Pair(types.bytes, types.nil)); - assert_eq!(db.compare(lhs, rhs), Comparison::Incompatible); + assert_eq!(db.compare(lhs, rhs), Comparison::NotEqual); } #[test] @@ -636,7 +597,7 @@ mod tests { ); assert_eq!( db.compare_with_generics(types.any, generic, &mut stack, infer), - Comparison::Superset + Comparison::NotEqual ); } } @@ -689,7 +650,7 @@ mod tests { let pair = db.alloc(Type::Pair(types.int, types.public_key)); let union = db.alloc(Type::Union(vec![types.bytes32, pair, types.nil])); - assert_eq!(db.compare(union, types.bytes), Comparison::Incompatible); + assert_eq!(db.compare(union, types.bytes), Comparison::NotEqual); } #[test] @@ -699,7 +660,7 @@ mod tests { let pair = db.alloc(Type::Pair(types.int, types.public_key)); let union = db.alloc(Type::Union(vec![types.bytes, pair])); - assert_eq!(db.compare(union, types.bytes), Comparison::Superset); + assert_eq!(db.compare(union, types.bytes), Comparison::NotEqual); } #[test] @@ -718,7 +679,7 @@ mod tests { let pair = db.alloc(Type::Pair(types.int, types.public_key)); let union = db.alloc(Type::Union(vec![types.bytes32, pair, types.nil])); - assert_eq!(db.compare(types.bytes, union), Comparison::Incompatible); + assert_eq!(db.compare(types.bytes, union), Comparison::NotEqual); } #[test] @@ -831,7 +792,7 @@ mod tests { let mut db = TypeSystem::new(); let types = db.std(); let generic = db.alloc(Type::Generic); - assert_eq!(db.compare(types.int, generic), Comparison::Incompatible); + assert_eq!(db.compare(types.int, generic), Comparison::NotEqual); assert_eq!(db.compare(generic, generic), Comparison::Equal); } diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference.rs index 2e43d8c..e98c727 100644 --- a/crates/rue-typing/src/difference.rs +++ b/crates/rue-typing/src/difference.rs @@ -243,6 +243,6 @@ mod tests { let non_nil = db.difference(list, types.nil); assert_eq!(db.compare(non_nil, list), Comparison::Assignable); - assert_eq!(db.compare(types.nil, non_nil), Comparison::Incompatible); + assert_eq!(db.compare(types.nil, non_nil), Comparison::NotEqual); } } diff --git a/crates/rue-typing/src/type_system.rs b/crates/rue-typing/src/type_system.rs index a2ec4f1..90c9e18 100644 --- a/crates/rue-typing/src/type_system.rs +++ b/crates/rue-typing/src/type_system.rs @@ -1,5 +1,3 @@ -use std::time::{Duration, Instant}; - use id_arena::{Arena, Id}; use crate::{ @@ -179,9 +177,7 @@ impl TypeSystem { substitution_stack: &mut Vec>, infer_generics: bool, ) -> Comparison { - let start = Instant::now(); - - let result = compare_type( + compare_type( self, lhs, rhs, @@ -192,18 +188,7 @@ impl TypeSystem { inferred: substitution_stack, infer_generics, }, - ); - let duration = start.elapsed(); - if duration > Duration::from_millis(1) { - println!( - "\n\n\n{duration:?} between {} => {}", - self.stringify(lhs), - self.stringify(rhs) - ); - println!("LHS {}", self.debug(lhs)); - println!("RHS {}", self.debug(rhs)); - } - result + ) } pub fn substitute( From 91c95b4d0a35bb6396f599286b261a943d467955 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 2 Aug 2024 14:22:44 -0400 Subject: [PATCH 092/100] Fix bug --- crates/rue-typing/src/substitute_type.rs | 28 ++++++++++++++++-------- tests.toml | 9 ++++---- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/crates/rue-typing/src/substitute_type.rs b/crates/rue-typing/src/substitute_type.rs index 212fa21..2f979cf 100644 --- a/crates/rue-typing/src/substitute_type.rs +++ b/crates/rue-typing/src/substitute_type.rs @@ -13,6 +13,22 @@ pub(crate) fn substitute_type( } } + match types.get(type_id) { + Type::Unknown + | Type::Generic + | Type::Never + | Type::Any + | Type::Bytes + | Type::Bytes32 + | Type::PublicKey + | Type::Int + | Type::Nil + | Type::True + | Type::False + | Type::Value(..) => return type_id, + _ => {} + } + let placeholder = types.alloc(Type::Unknown); substitutions .last_mut() @@ -20,8 +36,8 @@ pub(crate) fn substitute_type( .insert(type_id, placeholder); let result = match types.get(type_id) { - Type::Ref(..) => unreachable!(), - Type::Unknown + Type::Ref(..) + | Type::Unknown | Type::Generic | Type::Never | Type::Any @@ -32,7 +48,7 @@ pub(crate) fn substitute_type( | Type::Nil | Type::True | Type::False - | Type::Value(..) => type_id, + | Type::Value(..) => unreachable!(), Type::Pair(first, rest) => { let (first, rest) = (*first, *rest); @@ -67,7 +83,6 @@ pub(crate) fn substitute_type( } Type::Alias(alias) => { let alias = alias.clone(); - let new_type_id = substitute_type(types, alias.type_id, substitutions); if new_type_id == alias.type_id { @@ -82,7 +97,6 @@ pub(crate) fn substitute_type( } Type::Struct(ty) => { let ty = ty.clone(); - let new_type_id = substitute_type(types, ty.type_id, substitutions); if new_type_id == ty.type_id { @@ -99,7 +113,6 @@ pub(crate) fn substitute_type( } Type::Variant(ty) => { let ty = ty.clone(); - let new_type_id = substitute_type(types, ty.type_id, substitutions); if new_type_id == ty.type_id { @@ -118,7 +131,6 @@ pub(crate) fn substitute_type( } Type::Enum(ty) => { let ty = ty.clone(); - let new_type_id = substitute_type(types, ty.type_id, substitutions); if new_type_id == ty.type_id { @@ -134,9 +146,7 @@ pub(crate) fn substitute_type( } Type::Callable(callable) => { let callable = callable.clone(); - let new_return_type = substitute_type(types, callable.return_type, substitutions); - let new_parameters = substitute_type(types, callable.parameters, substitutions); if new_return_type == callable.return_type && new_parameters == callable.parameters { diff --git a/tests.toml b/tests.toml index ffa503f..536f961 100644 --- a/tests.toml +++ b/tests.toml @@ -549,11 +549,12 @@ output = "()" hash = "146182f765c52c144e4fa6d44fd3073bb3cbed6fac2bf1ca15393fc244c7d2b8" [cat] -bytes = 2107 -cost = 359978 +bytes = 2323 +cost = 0 input = "(0x00f43ce9fcc63d5019e209c103e6b0aaf56bbe7fc7fafae5af7f5ee6887a8719 0xd622c62a7292ffee5cf2537a90360ca0b7337b76d7014ec042930c0a87592213 (q (g1_negate () -113 (a (q 2 (i 47 (q 8) (q 2 (i (= 45 2) () (q 8)) 1)) 1) (c (q . 0x895eb35a355941ba7f6a8679a73bb9b8b62cae2b04ef5351eda42583c0f2d861) 1)) ()) (g1_negate 0xb8705f94744e7fc30300ac9b12d306b283f5a702937ee99beabf665be6023001 1 (0xb8705f94744e7fc30300ac9b12d306b283f5a702937ee99beabf665be6023001))) () () 0x615236766bed52d7abaa41d270407f3ec852981852334b213bd8515924459a5d (0x895eb35a355941ba7f6a8679a73bb9b8b62cae2b04ef5351eda42583c0f2d861 0x1ecb863db5d2ae6c71e9a8b0741acb3e034e8164b8ca0e564d5fad8b9dc875d5 1) (0x895eb35a355941ba7f6a8679a73bb9b8b62cae2b04ef5351eda42583c0f2d861 0x130deb20b44082a68293974f8cab9c51e21f9a9f3005000168eb77e49e0fc378 1) () ())" -output = "((70 0x615236766bed52d7abaa41d270407f3ec852981852334b213bd8515924459a5d) (60 0xcb7f53b5de05b4afe58ab663b952aa785fd9ad911564709bd8eacbf4c58ba1e589) (61 0x389f1ea7b9fab7a9294104eb3a181d662f2dbe9c43fbe52802ba9b7eef357e77) (g1_negate 0xc9644528436f44cd9b33282684b4964f55d5551cb3a970ab9cf8f536e0d72ad1 1 (0xb8705f94744e7fc30300ac9b12d306b283f5a702937ee99beabf665be6023001)))" -hash = "00f43ce9fcc63d5019e209c103e6b0aaf56bbe7fc7fafae5af7f5ee6887a8719" +output = "()" +hash = "5db0d2b8b2ea766c75afdfee8127d704f41e849a781fda6655e305199d4c5e23" +error = "()" [external_function] bytes = 7 From 787325854f15d77ca035f5e2da3e4a8aa0a04754 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 2 Aug 2024 16:49:19 -0400 Subject: [PATCH 093/100] Next phase --- crates/rue-compiler/src/compiler/builtins.rs | 1 - crates/rue-compiler/src/compiler/ty.rs | 2 ++ .../src/compiler/ty/literal_type.rs | 32 +++++++++++++++++++ crates/rue-compiler/stdlib.rue | 14 ++++---- crates/rue-parser/src/ast.rs | 18 ++++++++++- crates/rue-parser/src/grammar.rs | 8 +++++ crates/rue-parser/src/syntax_kind.rs | 2 ++ examples/cat.rue | 12 +++---- examples/multisig.rue | 2 +- examples/p2_delegated_or_hidden.rue | 2 +- examples/singleton.rue | 4 +-- tests/block/block_let_function.rue | 4 +-- tests/enum/enum_discriminant.rue | 2 +- tests/enum/enum_empty.rue | 2 +- tests/enum/enum_mixed.rue | 2 +- tests/enum/enum_numeric.rue | 2 +- tests/function/function_call.rue | 2 +- tests/std.rue | 2 +- tests/struct/struct_empty.rue | 2 +- 19 files changed, 87 insertions(+), 28 deletions(-) create mode 100644 crates/rue-compiler/src/compiler/ty/literal_type.rs diff --git a/crates/rue-compiler/src/compiler/builtins.rs b/crates/rue-compiler/src/compiler/builtins.rs index 7592878..1045357 100644 --- a/crates/rue-compiler/src/compiler/builtins.rs +++ b/crates/rue-compiler/src/compiler/builtins.rs @@ -23,7 +23,6 @@ pub fn builtins(db: &mut Database, ty: &mut TypeSystem) -> Builtins { let nil = db.alloc_hir(Hir::Atom(Vec::new())); let unknown = db.alloc_hir(Hir::Unknown); - scope.define_type("Nil".to_string(), ty.std().nil); scope.define_type("Int".to_string(), ty.std().int); scope.define_type("Bool".to_string(), ty.std().bool); scope.define_type("Bytes".to_string(), ty.std().bytes); diff --git a/crates/rue-compiler/src/compiler/ty.rs b/crates/rue-compiler/src/compiler/ty.rs index 4516f0d..e1fdae5 100644 --- a/crates/rue-compiler/src/compiler/ty.rs +++ b/crates/rue-compiler/src/compiler/ty.rs @@ -4,6 +4,7 @@ use rue_typing::TypeId; use super::Compiler; mod function_type; +mod literal_type; mod pair_type; mod path_type; mod union_type; @@ -11,6 +12,7 @@ mod union_type; impl Compiler<'_> { pub fn compile_type(&mut self, ty: Type) -> TypeId { match ty { + Type::LiteralType(lit) => self.compile_literal_type(&lit), Type::PathType(path) => { self.compile_path_type(&path.items(), path.syntax().text_range()) } diff --git a/crates/rue-compiler/src/compiler/ty/literal_type.rs b/crates/rue-compiler/src/compiler/ty/literal_type.rs new file mode 100644 index 0000000..c56e5c9 --- /dev/null +++ b/crates/rue-compiler/src/compiler/ty/literal_type.rs @@ -0,0 +1,32 @@ +use rue_parser::{LiteralType, SyntaxKind, SyntaxToken}; +use rue_typing::{Type, TypeId}; + +use crate::compiler::Compiler; + +impl Compiler<'_> { + pub fn compile_literal_type(&mut self, literal: &LiteralType) -> TypeId { + let Some(value) = literal.value() else { + return self.ty.std().unknown; + }; + + match value.kind() { + SyntaxKind::Int => self.compile_int_type(&value), + SyntaxKind::True => self.ty.std().true_bool, + SyntaxKind::False => self.ty.std().false_bool, + SyntaxKind::Nil => self.ty.std().nil, + _ => unreachable!(), + } + } + + fn compile_int_type(&mut self, int: &SyntaxToken) -> TypeId { + // Parse the literal into `BigInt`. + // It should not be possible to have a syntax error at this point. + let bigint = int + .text() + .replace('_', "") + .parse() + .expect("failed to parse integer literal"); + + self.ty.alloc(Type::Value(bigint)) + } +} diff --git a/crates/rue-compiler/stdlib.rue b/crates/rue-compiler/stdlib.rue index d5a327d..f265304 100644 --- a/crates/rue-compiler/stdlib.rue +++ b/crates/rue-compiler/stdlib.rue @@ -1,6 +1,6 @@ export enum Condition { Remark = 1 { - ...value: (Any, Nil) | Nil, + ...value: (Any, nil) | nil, }, AggSigParent = 43 { public_key: PublicKey, @@ -37,7 +37,7 @@ export enum Condition { CreateCoin = 51 { puzzle_hash: Bytes32, amount: Int, - ...memos: (List, Nil) | Nil, + ...memos: (List, nil) | nil, }, ReserveFee = 52 { amount: Int, @@ -105,7 +105,7 @@ export enum Condition { }, Softfork = 90 { cost: Int, - ...value: (Any, Nil) | Nil, + ...value: (Any, nil) | nil, }, } @@ -168,7 +168,7 @@ inline fun update_hash_with_parameter( } fun curried_params_hash(parameters: List) -> Bytes32 { - if parameters is Nil { + if parameters is nil { return ONE_TREE_HASH; } update_hash_with_parameter(parameters.first, curried_params_hash(parameters.rest)) @@ -192,14 +192,14 @@ export fun calculate_coin_id( } export fun map(list: List, fn: fun(item: T) -> U) -> List { - if list is Nil { + if list is nil { return nil; } [fn(list.first), ...map(list.rest, fn)] } export fun filter(list: List, fn: fun(item: T) -> Bool) -> List { - if list is Nil { + if list is nil { return nil; } if fn(list.first) { @@ -209,7 +209,7 @@ export fun filter(list: List, fn: fun(item: T) -> Bool) -> List { } export fun fold(list: List, initial: U, fn: fun(acc: U, item: T) -> U) -> U { - if list is Nil { + if list is nil { return initial; } fold(list.rest, fn(initial, list.first), fn) diff --git a/crates/rue-parser/src/ast.rs b/crates/rue-parser/src/ast.rs index 7d2d935..d644c6e 100644 --- a/crates/rue-parser/src/ast.rs +++ b/crates/rue-parser/src/ast.rs @@ -118,7 +118,15 @@ ast_node!(FieldAccessExpr); ast_node!(LambdaExpr); ast_node!(LambdaParam); -ast_enum!(Type, PathType, PairType, FunctionType, UnionType); +ast_enum!( + Type, + LiteralType, + PathType, + PairType, + FunctionType, + UnionType +); +ast_node!(LiteralType); ast_node!(PathType); ast_node!(PairType); ast_node!(FunctionType); @@ -797,6 +805,14 @@ impl FieldAccessExpr { } } +impl LiteralType { + pub fn value(&self) -> Option { + self.syntax() + .children_with_tokens() + .find_map(SyntaxElement::into_token) + } +} + impl PathType { pub fn items(&self) -> Vec { self.syntax() diff --git a/crates/rue-parser/src/grammar.rs b/crates/rue-parser/src/grammar.rs index 323c5b9..233a44a 100644 --- a/crates/rue-parser/src/grammar.rs +++ b/crates/rue-parser/src/grammar.rs @@ -576,6 +576,14 @@ fn ty(p: &mut Parser<'_>) { if p.at(SyntaxKind::Ident) { path_type(p); + } else if p.at(SyntaxKind::Int) + || p.at(SyntaxKind::True) + || p.at(SyntaxKind::False) + || p.at(SyntaxKind::Nil) + { + p.start(SyntaxKind::LiteralType); + p.bump(); + p.finish(); } else if p.at(SyntaxKind::Fun) { p.start(SyntaxKind::FunctionType); p.bump(); diff --git a/crates/rue-parser/src/syntax_kind.rs b/crates/rue-parser/src/syntax_kind.rs index 8b74432..df99093 100644 --- a/crates/rue-parser/src/syntax_kind.rs +++ b/crates/rue-parser/src/syntax_kind.rs @@ -112,6 +112,7 @@ pub enum SyntaxKind { FunctionCallExpr, FunctionCallArg, FieldAccessExpr, + LiteralType, PathType, PairType, FunctionType, @@ -233,6 +234,7 @@ impl fmt::Display for SyntaxKind { Self::FunctionCallExpr => "function call expression", Self::FunctionCallArg => "function call argument", Self::FieldAccessExpr => "field access expression", + Self::LiteralType => "literal type", Self::PathType => "path type", Self::PairType => "pair type", Self::FunctionType => "function type", diff --git a/examples/cat.rue b/examples/cat.rue index 57cb220..9b8cb59 100644 --- a/examples/cat.rue +++ b/examples/cat.rue @@ -29,7 +29,7 @@ struct Truths { type Tail = fun( truths: Truths, parent_is_cat: Bool, - lineage_proof: LineageProof | Nil, + lineage_proof: LineageProof | nil, extra_delta: Int, conditions: List, tail_solution: List, @@ -46,7 +46,7 @@ struct TailInfo { // We can check `Condition::CreateCoin` then cast to this type instead. struct RunTailCondition { opcode: Int, - puzzle_hash: Nil, + puzzle_hash: nil, amount: Int, tail_puzzle: Tail, tail_solution: List, @@ -92,7 +92,7 @@ fun main( asset_id: Bytes32, inner_puzzle: fun(...solution: Any) -> List, inner_solution: Any, - lineage_proof: LineageProof | Nil, + lineage_proof: LineageProof | nil, prev_coin_id: Bytes32, my_coin: Coin, next_coin_proof: CoinProof, @@ -197,17 +197,17 @@ struct Morph { sum: Int, // Information about the TAIL, revealed in the conditions. - tail_info: TailInfo | Nil, + tail_info: TailInfo | nil, } // Morph all of the conditions and extract the TAIL info. fun morph_conditions( conditions: List, cat_info: CatInfo, - tail_info: TailInfo | Nil, + tail_info: TailInfo | nil, ) -> Morph { // If there are no conditions, return an empty morph. - if conditions is Nil { + if conditions is nil { return Morph { conditions: nil, sum: 0, diff --git a/examples/multisig.rue b/examples/multisig.rue index 4205d82..a92e417 100644 --- a/examples/multisig.rue +++ b/examples/multisig.rue @@ -22,7 +22,7 @@ fun check_signatures( return nil; } - assume !(public_keys is Nil) && !(indices is Nil); + assume !(public_keys is nil) && !(indices is nil); if indices.first != pos { return check_signatures(public_keys.rest, required, indices, pos + 1, message); diff --git a/examples/p2_delegated_or_hidden.rue b/examples/p2_delegated_or_hidden.rue index 188add2..2d25e74 100644 --- a/examples/p2_delegated_or_hidden.rue +++ b/examples/p2_delegated_or_hidden.rue @@ -2,7 +2,7 @@ fun main( synthetic_pk: PublicKey, - original_pk: PublicKey | Nil, + original_pk: PublicKey | nil, delegated_puzzle: fun(...solution: Any) -> List, delegated_solution: Any ) -> List { diff --git a/examples/singleton.rue b/examples/singleton.rue index 7cff2b7..819d164 100644 --- a/examples/singleton.rue +++ b/examples/singleton.rue @@ -8,7 +8,7 @@ struct Singleton { struct LineageProof { parent_parent_coin_info: Bytes32, - parent_inner_puzzle_hash: Bytes32 | Nil, + parent_inner_puzzle_hash: Bytes32 | nil, parent_amount: Int, } @@ -58,7 +58,7 @@ fun morph_conditions( conditions: List, found_singleton_output: Bool, ) -> List { - if conditions is Nil { + if conditions is nil { // We must have a singleton output. assert found_singleton_output; return nil; diff --git a/tests/block/block_let_function.rue b/tests/block/block_let_function.rue index d0feb5a..444ca69 100644 --- a/tests/block/block_let_function.rue +++ b/tests/block/block_let_function.rue @@ -1,8 +1,8 @@ fun main() -> Bool { - let outer: Int | Nil = 42; + let outer: Int | nil = 42; let another_outer = 19; let not_used_in_block = false; - let result = !not_used_in_block && !(outer is Nil) && { + let result = !not_used_in_block && !(outer is nil) && { let double = double(outer); outer + double + double - double + another_outer == 126 + 19 }; diff --git a/tests/enum/enum_discriminant.rue b/tests/enum/enum_discriminant.rue index 2f9be1a..d401f31 100644 --- a/tests/enum/enum_discriminant.rue +++ b/tests/enum/enum_discriminant.rue @@ -7,7 +7,7 @@ enum Num { Six = 6, } -fun main() -> Nil { +fun main() -> nil { assert Num::Zero as Int == 0; assert Num::One as Int == 1; assert Num::Two as Int == 2; diff --git a/tests/enum/enum_empty.rue b/tests/enum/enum_empty.rue index ca521ee..880200f 100644 --- a/tests/enum/enum_empty.rue +++ b/tests/enum/enum_empty.rue @@ -2,7 +2,7 @@ enum Enum { Empty {}, } -fun main() -> Nil { +fun main() -> nil { assert tree_hash(Enum::Empty {}) == tree_hash([0]); nil } diff --git a/tests/enum/enum_mixed.rue b/tests/enum/enum_mixed.rue index 69724ac..ed60141 100644 --- a/tests/enum/enum_mixed.rue +++ b/tests/enum/enum_mixed.rue @@ -3,7 +3,7 @@ enum Test { Value = 1 { num: Int }, } -fun main() -> Nil { +fun main() -> nil { assert tree_hash(Test::Unit) == tree_hash([0]); assert tree_hash(Test::Value { num: 1000 }) == tree_hash([1, 1000]); nil diff --git a/tests/enum/enum_numeric.rue b/tests/enum/enum_numeric.rue index e2b0f11..d6176c4 100644 --- a/tests/enum/enum_numeric.rue +++ b/tests/enum/enum_numeric.rue @@ -4,7 +4,7 @@ enum Mode { Open, } -fun main() -> Nil { +fun main() -> nil { let open: Mode = Mode::Open; assert open is Mode::Open; diff --git a/tests/function/function_call.rue b/tests/function/function_call.rue index 94973ef..dbbab41 100644 --- a/tests/function/function_call.rue +++ b/tests/function/function_call.rue @@ -1,4 +1,4 @@ -fun main() -> Nil { +fun main() -> nil { assert empty(); assert empty(1); diff --git a/tests/std.rue b/tests/std.rue index 6950624..715c9e9 100644 --- a/tests/std.rue +++ b/tests/std.rue @@ -1,4 +1,4 @@ -fun main() -> Nil { +fun main() -> nil { assert tree_hash(map([1, 2, 3], fun(num) => num * 2)) == tree_hash([2, 4, 6]); assert tree_hash(filter([1, 2, 3, 4, 5], fun(num) => num < 4)) == tree_hash([1, 2, 3]); assert tree_hash(fold([1, 2, 3], 0, fun(acc, num) => acc + num)) == tree_hash(6); diff --git a/tests/struct/struct_empty.rue b/tests/struct/struct_empty.rue index 2f3f62c..162d234 100644 --- a/tests/struct/struct_empty.rue +++ b/tests/struct/struct_empty.rue @@ -1,6 +1,6 @@ struct Empty {} -fun main() -> Nil { +fun main() -> nil { assert tree_hash(Empty {}) == tree_hash(nil); nil } From a293329b775679c2638fb2de3a92bd6789b5c69d Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sat, 3 Aug 2024 13:59:25 -0400 Subject: [PATCH 094/100] HashSet --- crates/rue-compiler/src/compiler/context.rs | 2 +- crates/rue-compiler/src/compiler/item.rs | 2 +- crates/rue-compiler/src/compiler/item/enum_item.rs | 2 +- crates/rue-compiler/src/compiler/symbol_table.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/rue-compiler/src/compiler/context.rs b/crates/rue-compiler/src/compiler/context.rs index e530c7f..bb11625 100644 --- a/crates/rue-compiler/src/compiler/context.rs +++ b/crates/rue-compiler/src/compiler/context.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use rue_typing::HashSet; use clvmr::{Allocator, NodePtr}; use indexmap::IndexMap; diff --git a/crates/rue-compiler/src/compiler/item.rs b/crates/rue-compiler/src/compiler/item.rs index 33ef470..d5fc38e 100644 --- a/crates/rue-compiler/src/compiler/item.rs +++ b/crates/rue-compiler/src/compiler/item.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use rue_typing::HashSet; use rue_parser::Item; use rue_typing::{Type, TypeId}; diff --git a/crates/rue-compiler/src/compiler/item/enum_item.rs b/crates/rue-compiler/src/compiler/item/enum_item.rs index 80391c9..50f679f 100644 --- a/crates/rue-compiler/src/compiler/item/enum_item.rs +++ b/crates/rue-compiler/src/compiler/item/enum_item.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use rue_typing::HashSet; use indexmap::IndexMap; use num_bigint::BigInt; diff --git a/crates/rue-compiler/src/compiler/symbol_table.rs b/crates/rue-compiler/src/compiler/symbol_table.rs index d1cc991..77dea9c 100644 --- a/crates/rue-compiler/src/compiler/symbol_table.rs +++ b/crates/rue-compiler/src/compiler/symbol_table.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use rue_typing::HashSet; use indexmap::{IndexMap, IndexSet}; use rue_typing::{Type, TypeId, TypeSystem}; From 0acb1d7cd7afea976dd1b88966996a4b0a96d1a2 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sat, 3 Aug 2024 14:12:12 -0400 Subject: [PATCH 095/100] Rename files --- .../rue-typing/src/{difference.rs => difference_type.rs} | 0 crates/rue-typing/src/lib.rs | 8 ++++---- crates/rue-typing/src/{stringify.rs => stringify_type.rs} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename crates/rue-typing/src/{difference.rs => difference_type.rs} (100%) rename crates/rue-typing/src/{stringify.rs => stringify_type.rs} (100%) diff --git a/crates/rue-typing/src/difference.rs b/crates/rue-typing/src/difference_type.rs similarity index 100% rename from crates/rue-typing/src/difference.rs rename to crates/rue-typing/src/difference_type.rs diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index abc0237..6cb1660 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -2,12 +2,12 @@ mod bigint; mod check; mod comparison; mod debug_type; -mod difference; +mod difference_type; mod map; mod replace_type; mod semantic_types; mod standard_types; -mod stringify; +mod stringify_type; mod substitute_type; mod ty; mod type_path; @@ -24,9 +24,9 @@ pub use type_path::*; pub use type_system::*; pub(crate) use debug_type::debug_type; -pub(crate) use difference::difference_type; +pub(crate) use difference_type::difference_type; pub(crate) use replace_type::replace_type; -pub(crate) use stringify::stringify_type; +pub(crate) use stringify_type::stringify_type; pub(crate) use substitute_type::substitute_type; #[cfg(test)] diff --git a/crates/rue-typing/src/stringify.rs b/crates/rue-typing/src/stringify_type.rs similarity index 100% rename from crates/rue-typing/src/stringify.rs rename to crates/rue-typing/src/stringify_type.rs From 11c220d48a1c9349183e8a2f3e367ed282635ec4 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sat, 3 Aug 2024 14:13:49 -0400 Subject: [PATCH 096/100] Rename compare --- crates/rue-typing/src/{comparison.rs => compare_type.rs} | 0 crates/rue-typing/src/lib.rs | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename crates/rue-typing/src/{comparison.rs => compare_type.rs} (100%) diff --git a/crates/rue-typing/src/comparison.rs b/crates/rue-typing/src/compare_type.rs similarity index 100% rename from crates/rue-typing/src/comparison.rs rename to crates/rue-typing/src/compare_type.rs diff --git a/crates/rue-typing/src/lib.rs b/crates/rue-typing/src/lib.rs index 6cb1660..55f8e8e 100644 --- a/crates/rue-typing/src/lib.rs +++ b/crates/rue-typing/src/lib.rs @@ -1,6 +1,6 @@ mod bigint; mod check; -mod comparison; +mod compare_type; mod debug_type; mod difference_type; mod map; @@ -15,7 +15,7 @@ mod type_system; pub use bigint::*; pub use check::*; -pub use comparison::*; +pub use compare_type::*; pub use map::*; pub use semantic_types::*; pub use standard_types::*; From 9557f08a98f89c36c6e5231afea0e5254df81422 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sat, 3 Aug 2024 15:01:00 -0400 Subject: [PATCH 097/100] Initial simplification of type guards --- crates/rue-compiler/src/compiler.rs | 30 +-- .../src/compiler/expr/binary_expr.rs | 16 +- .../src/compiler/expr/field_access_expr.rs | 237 ++++++++++-------- .../src/compiler/expr/guard_expr.rs | 8 +- .../src/compiler/expr/path_expr.rs | 13 +- .../rue-compiler/src/compiler/stmt/if_stmt.rs | 4 +- crates/rue-compiler/src/value.rs | 64 +++-- crates/rue-compiler/src/value/guard.rs | 51 ---- crates/rue-compiler/src/value/guard_path.rs | 23 -- crates/rue-typing/src/type_path.rs | 11 + 10 files changed, 219 insertions(+), 238 deletions(-) delete mode 100644 crates/rue-compiler/src/value/guard.rs delete mode 100644 crates/rue-compiler/src/value/guard_path.rs diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index d2a703b..756c003 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -1,6 +1,6 @@ #![allow(clippy::map_unwrap_or)] -use rue_typing::HashMap; +use rue_typing::{HashMap, TypePath}; pub(crate) use builtins::Builtins; @@ -12,7 +12,7 @@ use crate::{ database::{Database, HirId, ScopeId, SymbolId}, hir::{Hir, Op}, scope::Scope, - value::{GuardPath, Mutation, TypeOverride, Value}, + value::{GuardPath, Value}, ErrorKind, }; @@ -47,7 +47,7 @@ pub struct Compiler<'a> { type_definition_stack: Vec, // The type guard stack is used for overriding types in certain contexts. - type_guard_stack: Vec>, + type_guard_stack: Vec>, // The generic type stack is used for overriding generic types that are being checked against. generic_type_stack: Vec>, @@ -88,15 +88,14 @@ impl<'a> Compiler<'a> { self.sym } - fn compile_index(&mut self, value: HirId, index: usize, rest: bool) -> HirId { - let mut result = value; - for _ in 0..index { - result = self.db.alloc_hir(Hir::Op(Op::Rest, result)); - } - if !rest { - result = self.db.alloc_hir(Hir::Op(Op::First, result)); + fn hir_path(&mut self, mut value: HirId, path_items: &[TypePath]) -> HirId { + for path in path_items { + match path { + TypePath::First => value = self.db.alloc_hir(Hir::Op(Op::First, value)), + TypePath::Rest => value = self.db.alloc_hir(Hir::Op(Op::Rest, value)), + } } - result + value } fn type_reference(&mut self, referenced_type_id: TypeId) { @@ -170,7 +169,7 @@ impl<'a> Compiler<'a> { Value::new(self.builtins.unknown, self.ty.std().unknown) } - fn symbol_type(&self, guard_path: &GuardPath) -> Option { + fn symbol_type(&self, guard_path: &GuardPath) -> Option { for guards in self.type_guard_stack.iter().rev() { if let Some(guard) = guards.get(guard_path) { return Some(*guard); @@ -179,13 +178,6 @@ impl<'a> Compiler<'a> { None } - fn apply_mutation(&mut self, hir_id: HirId, mutation: Mutation) -> HirId { - match mutation { - Mutation::None => hir_id, - Mutation::UnwrapOptional => self.db.alloc_hir(Hir::Op(Op::First, hir_id)), - } - } - fn scope(&self) -> &Scope { self.db .scope(self.scope_stack.last().copied().expect("no scope found")) diff --git a/crates/rue-compiler/src/compiler/expr/binary_expr.rs b/crates/rue-compiler/src/compiler/expr/binary_expr.rs index ddca43c..1272f0e 100644 --- a/crates/rue-compiler/src/compiler/expr/binary_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/binary_expr.rs @@ -5,7 +5,7 @@ use rue_typing::{Comparison, Type, TypeId}; use crate::{ compiler::Compiler, hir::{BinOp, Hir, Op}, - value::{Guard, TypeOverride, Value}, + value::{Guard, Value}, ErrorKind, HirId, }; @@ -170,10 +170,9 @@ impl Compiler<'_> { if let Some(guard_path) = rhs.guard_path { let then_type = self.ty.std().nil; let else_type = self.ty.difference(rhs.type_id, self.ty.std().nil); - value.guards.insert( - guard_path, - Guard::new(TypeOverride::new(then_type), TypeOverride::new(else_type)), - ); + value + .guards + .insert(guard_path, Guard::new(then_type, else_type)); } } @@ -181,10 +180,9 @@ impl Compiler<'_> { if let Some(guard_path) = lhs.guard_path.clone() { let then_type = self.ty.std().nil; let else_type = self.ty.difference(lhs.type_id, self.ty.std().nil); - value.guards.insert( - guard_path, - Guard::new(TypeOverride::new(then_type), TypeOverride::new(else_type)), - ); + value + .guards + .insert(guard_path, Guard::new(then_type, else_type)); } } diff --git a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs index 9d51ef8..dffcc67 100644 --- a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs @@ -1,10 +1,10 @@ -use rue_parser::FieldAccessExpr; -use rue_typing::{deconstruct_items, Type}; +use rue_parser::{FieldAccessExpr, SyntaxToken}; +use rue_typing::{deconstruct_items, index_to_path, Struct, Type, TypeId, TypePath, Variant}; use crate::{ compiler::Compiler, hir::{Hir, Op}, - value::{GuardPathItem, Value}, + value::Value, ErrorKind, }; @@ -18,120 +18,42 @@ impl Compiler<'_> { return self.unknown(); }; - let Some(field_name) = field_access.field() else { + let Some(name) = field_access.field() else { return self.unknown(); }; let mut new_value = match self.ty.get(old_value.type_id).clone() { Type::Unknown => return self.unknown(), Type::Struct(ty) => { - let fields = - deconstruct_items(self.ty, ty.type_id, ty.field_names.len(), ty.nil_terminated) - .expect("invalid struct type"); - - if let Some(index) = ty.field_names.get_index_of(field_name.text()) { - let type_id = fields[index]; - - Value::new( - self.compile_index( - old_value.hir_id, - index, - index == ty.field_names.len() - 1 && !ty.nil_terminated, - ), - type_id, - ) - .extend_guard_path(old_value, GuardPathItem::Field(field_name.to_string())) - } else { - self.db.error( - ErrorKind::UnknownField(field_name.to_string()), - field_name.text_range(), - ); + let Some(value) = self.compile_struct_field_access(old_value, &ty, &name) else { return self.unknown(); - } - } - Type::Variant(variant) => { - let field_names = variant.field_names.clone().unwrap_or_default(); - - let Type::Enum(ty) = self.ty.get(variant.original_enum_type_id) else { - unreachable!(); - }; - - let fields = if ty.has_fields { - let type_id = self - .ty - .get_pair(variant.type_id) - .expect("expected a pair") - .1; - - variant - .field_names - .as_ref() - .map(|field_names| { - deconstruct_items( - self.ty, - type_id, - field_names.len(), - variant.nil_terminated, - ) - .expect("invalid struct type") - }) - .unwrap_or_default() - } else { - Vec::new() }; - - if let Some(index) = field_names.get_index_of(field_name.text()) { - let type_id = fields[index]; - - let fields_hir_id = self.db.alloc_hir(Hir::Op(Op::Rest, old_value.hir_id)); - - Value::new( - self.compile_index( - fields_hir_id, - index, - index == fields.len() - 1 && !variant.nil_terminated, - ), - type_id, - ) - .extend_guard_path(old_value, GuardPathItem::Field(field_name.to_string())) - } else { - self.db.error( - ErrorKind::UnknownField(field_name.to_string()), - field_name.text_range(), - ); + value + } + Type::Variant(ty) => { + let Some(value) = self.compile_variant_field_access(old_value, &ty, &name) else { return self.unknown(); - } + }; + value } - Type::Pair(first, rest) => match field_name.text() { - "first" => Value::new( - self.db.alloc_hir(Hir::Op(Op::First, old_value.hir_id)), - first, - ) - .extend_guard_path(old_value, GuardPathItem::First), - "rest" => Value::new(self.db.alloc_hir(Hir::Op(Op::Rest, old_value.hir_id)), rest) - .extend_guard_path(old_value, GuardPathItem::Rest), - _ => { - self.db.error( - ErrorKind::InvalidFieldAccess( - field_name.to_string(), - self.type_name(old_value.type_id), - ), - field_name.text_range(), - ); + Type::Pair(first, rest) => { + let Some(value) = self.compile_pair_field_access(old_value, first, rest, &name) + else { return self.unknown(); - } - }, - Type::Bytes | Type::Bytes32 if field_name.text() == "length" => Value::new( + }; + value + } + Type::Bytes | Type::Bytes32 if name.text() == "length" => Value::new( self.db.alloc_hir(Hir::Op(Op::Strlen, old_value.hir_id)), self.ty.std().int, ), _ => { self.db.error( ErrorKind::InvalidFieldAccess( - field_name.to_string(), + name.to_string(), self.type_name(old_value.type_id), ), - field_name.text_range(), + name.text_range(), ); return self.unknown(); } @@ -139,11 +61,126 @@ impl Compiler<'_> { if let Some(guard_path) = new_value.guard_path.as_ref() { if let Some(type_override) = self.symbol_type(guard_path) { - new_value.type_id = type_override.type_id; - new_value.hir_id = self.apply_mutation(new_value.hir_id, type_override.mutation); + new_value.type_id = type_override; } } new_value } + + fn compile_pair_field_access( + &mut self, + old_value: Value, + first: TypeId, + rest: TypeId, + name: &SyntaxToken, + ) -> Option { + let path = match name.text() { + "first" => TypePath::First, + "rest" => TypePath::Rest, + _ => { + self.db.error( + ErrorKind::InvalidFieldAccess( + name.to_string(), + self.type_name(old_value.type_id), + ), + name.text_range(), + ); + return None; + } + }; + + let type_id = match path { + TypePath::First => first, + TypePath::Rest => rest, + }; + + let mut value = Value::new(self.hir_path(old_value.hir_id, &[path]), type_id); + + value.guard_path = old_value.guard_path.map(|mut guard_path| { + guard_path.items.push(path); + guard_path + }); + + Some(value) + } + + fn compile_struct_field_access( + &mut self, + old_value: Value, + ty: &Struct, + name: &SyntaxToken, + ) -> Option { + let fields = + deconstruct_items(self.ty, ty.type_id, ty.field_names.len(), ty.nil_terminated) + .expect("invalid struct type"); + + let Some(index) = ty.field_names.get_index_of(name.text()) else { + self.db + .error(ErrorKind::UnknownField(name.to_string()), name.text_range()); + return None; + }; + + let type_id = fields[index]; + + let path_items = index_to_path( + index, + index != ty.field_names.len() - 1 || ty.nil_terminated, + ); + + let mut value = Value::new(self.hir_path(old_value.hir_id, &path_items), type_id); + + value.guard_path = old_value.guard_path.map(|mut guard_path| { + guard_path.items.extend(path_items); + guard_path + }); + + Some(value) + } + + fn compile_variant_field_access( + &mut self, + old_value: Value, + ty: &Variant, + name: &SyntaxToken, + ) -> Option { + let field_names = ty.field_names.clone().unwrap_or_default(); + + let Type::Enum(enum_type) = self.ty.get(ty.original_enum_type_id) else { + unreachable!(); + }; + + let fields = if enum_type.has_fields { + let type_id = self.ty.get_pair(ty.type_id).expect("expected a pair").1; + + ty.field_names + .as_ref() + .map(|field_names| { + deconstruct_items(self.ty, type_id, field_names.len(), ty.nil_terminated) + .expect("invalid struct type") + }) + .unwrap_or_default() + } else { + Vec::new() + }; + + let Some(index) = field_names.get_index_of(name.text()) else { + self.db + .error(ErrorKind::UnknownField(name.to_string()), name.text_range()); + return None; + }; + + let type_id = fields[index]; + + let path_items = index_to_path(index + 1, index != fields.len() - 1 || ty.nil_terminated); + + let mut value = Value::new(self.hir_path(old_value.hir_id, &path_items), type_id); + + value.guard_path = old_value.guard_path.map(|mut guard_path| { + guard_path.items.extend(path_items); + guard_path + }); + + Some(value) + } } diff --git a/crates/rue-compiler/src/compiler/expr/guard_expr.rs b/crates/rue-compiler/src/compiler/expr/guard_expr.rs index 6e3a66a..8f2d51e 100644 --- a/crates/rue-compiler/src/compiler/expr/guard_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/guard_expr.rs @@ -4,7 +4,7 @@ use rue_typing::{bigint_to_bytes, Check, TypeId}; use crate::{ compiler::Compiler, hir::{BinOp, Hir, Op}, - value::{Guard, TypeOverride, Value}, + value::{Guard, Value}, ErrorKind, HirId, WarningKind, }; @@ -62,11 +62,7 @@ impl Compiler<'_> { if let Some(guard_path) = expr.guard_path { let difference = self.ty.difference(expr.type_id, rhs); - - value.guards.insert( - guard_path, - Guard::new(TypeOverride::new(rhs), TypeOverride::new(difference)), - ); + value.guards.insert(guard_path, Guard::new(rhs, difference)); } value diff --git a/crates/rue-compiler/src/compiler/expr/path_expr.rs b/crates/rue-compiler/src/compiler/expr/path_expr.rs index 0c19033..f857f4d 100644 --- a/crates/rue-compiler/src/compiler/expr/path_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/path_expr.rs @@ -76,22 +76,15 @@ impl Compiler<'_> { } let type_override = self.symbol_type(&GuardPath::new(symbol_id)); - let override_type_id = type_override.map(|ty| ty.type_id); - let mut reference = self.db.alloc_hir(Hir::Reference(symbol_id, text_range)); - - if let Some(mutation) = type_override.map(|ty| ty.mutation) { - reference = self.apply_mutation(reference, mutation); - } + let reference = self.db.alloc_hir(Hir::Reference(symbol_id, text_range)); let mut value = match self.db.symbol(symbol_id).clone() { Symbol::Unknown | Symbol::Module(..) => unreachable!(), Symbol::Function(Function { type_id, .. }) | Symbol::InlineFunction(Function { type_id, .. }) - | Symbol::Parameter(type_id) => { - Value::new(reference, override_type_id.unwrap_or(type_id)) - } + | Symbol::Parameter(type_id) => Value::new(reference, type_override.unwrap_or(type_id)), Symbol::Let(mut value) | Symbol::Const(mut value) | Symbol::InlineConst(mut value) => { - if let Some(type_id) = override_type_id { + if let Some(type_id) = type_override { value.type_id = type_id; } value.hir_id = reference; diff --git a/crates/rue-compiler/src/compiler/stmt/if_stmt.rs b/crates/rue-compiler/src/compiler/stmt/if_stmt.rs index f131ee7..9927551 100644 --- a/crates/rue-compiler/src/compiler/stmt/if_stmt.rs +++ b/crates/rue-compiler/src/compiler/stmt/if_stmt.rs @@ -6,7 +6,7 @@ use rue_typing::TypeId; use crate::{ compiler::{block::BlockTerminator, Compiler}, scope::Scope, - value::{GuardPath, TypeOverride}, + value::GuardPath, ErrorKind, HirId, }; @@ -16,7 +16,7 @@ impl Compiler<'_> { &mut self, if_stmt: &IfStmt, expected_type: Option, - ) -> (HirId, HirId, HashMap) { + ) -> (HirId, HirId, HashMap) { // Compile the condition expression. let condition = if_stmt .condition() diff --git a/crates/rue-compiler/src/value.rs b/crates/rue-compiler/src/value.rs index 4e6bb48..89b61ac 100644 --- a/crates/rue-compiler/src/value.rs +++ b/crates/rue-compiler/src/value.rs @@ -1,14 +1,12 @@ -use rue_typing::HashMap; - -mod guard; -mod guard_path; +use std::ops::Not; -pub use guard::*; -pub use guard_path::*; +use rue_typing::HashMap; use rue_typing::TypeId; +use rue_typing::TypePath; use crate::HirId; +use crate::SymbolId; #[derive(Debug, Clone)] pub struct Value { @@ -28,28 +26,58 @@ impl Value { } } - pub fn then_guards(&self) -> HashMap { + pub fn then_guards(&self) -> HashMap { self.guards .iter() - .map(|(k, v)| (k.clone(), v.then_type)) + .map(|(guard_path, guard)| (guard_path.clone(), guard.then_type)) .collect() } - pub fn else_guards(&self) -> HashMap { + pub fn else_guards(&self) -> HashMap { self.guards .iter() - .map(|(k, v)| (k.clone(), v.else_type)) + .map(|(guard_path, guard)| (guard_path.clone(), guard.else_type)) .collect() } +} + +#[derive(Debug, Clone, Copy)] +pub struct Guard { + pub then_type: TypeId, + pub else_type: TypeId, +} + +impl Guard { + pub fn new(then_type: TypeId, else_type: TypeId) -> Self { + Self { + then_type, + else_type, + } + } +} + +impl Not for Guard { + type Output = Self; + + fn not(self) -> Self::Output { + Self { + then_type: self.else_type, + else_type: self.then_type, + } + } +} - pub fn extend_guard_path(mut self, old_value: Value, item: GuardPathItem) -> Self { - match old_value.guard_path { - Some(mut path) => { - path.items.push(item); - self.guard_path = Some(path); - self - } - None => self, +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GuardPath { + pub symbol_id: SymbolId, + pub items: Vec, +} + +impl GuardPath { + pub fn new(symbol_id: SymbolId) -> Self { + Self { + symbol_id, + items: Vec::new(), } } } diff --git a/crates/rue-compiler/src/value/guard.rs b/crates/rue-compiler/src/value/guard.rs deleted file mode 100644 index 302b82f..0000000 --- a/crates/rue-compiler/src/value/guard.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::ops::Not; - -use rue_typing::TypeId; - -#[derive(Debug, Clone, Copy)] -pub struct Guard { - pub then_type: TypeOverride, - pub else_type: TypeOverride, -} - -impl Guard { - pub fn new(then_type: TypeOverride, else_type: TypeOverride) -> Self { - Self { - then_type, - else_type, - } - } -} - -impl Not for Guard { - type Output = Self; - - fn not(self) -> Self::Output { - Self { - then_type: self.else_type, - else_type: self.then_type, - } - } -} - -#[derive(Debug, Clone, Copy)] -pub struct TypeOverride { - pub type_id: TypeId, - pub mutation: Mutation, -} - -impl TypeOverride { - pub fn new(type_id: TypeId) -> Self { - Self { - type_id, - mutation: Mutation::None, - } - } -} - -#[derive(Debug, Default, Clone, Copy)] -pub enum Mutation { - #[default] - None, - UnwrapOptional, -} diff --git a/crates/rue-compiler/src/value/guard_path.rs b/crates/rue-compiler/src/value/guard_path.rs deleted file mode 100644 index 9b1e9a1..0000000 --- a/crates/rue-compiler/src/value/guard_path.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::SymbolId; - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct GuardPath { - pub symbol_id: SymbolId, - pub items: Vec, -} - -impl GuardPath { - pub fn new(symbol_id: SymbolId) -> Self { - Self { - symbol_id, - items: Vec::new(), - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum GuardPathItem { - Field(String), - First, - Rest, -} diff --git a/crates/rue-typing/src/type_path.rs b/crates/rue-typing/src/type_path.rs index a557a1a..2cf8f2e 100644 --- a/crates/rue-typing/src/type_path.rs +++ b/crates/rue-typing/src/type_path.rs @@ -3,3 +3,14 @@ pub enum TypePath { First, Rest, } + +pub fn index_to_path(index: usize, nil_terminated: bool) -> Vec { + let mut path = Vec::with_capacity(index); + for _ in 0..index { + path.push(TypePath::Rest); + } + if nil_terminated { + path.push(TypePath::First); + } + path +} From 56204e246ce0dec462f2844b43ca17ba4871287c Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sat, 3 Aug 2024 15:02:02 -0400 Subject: [PATCH 098/100] Fix --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 36328d6..412c942 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ missing_debug_implementations = "warn" missing_copy_implementations = "warn" [workspace.lints.clippy] -all = "deny" +all = { level = "deny", priority = -1 } pedantic = { level = "warn", priority = -1 } missing_errors_doc = "allow" missing_panics_doc = "allow" From 6eff77e13db7070dbc6ed3466e0bf0d9b80d283a Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sat, 3 Aug 2024 15:04:12 -0400 Subject: [PATCH 099/100] Fix priority --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 412c942..61f6797 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,10 +8,10 @@ keywords = ["chia", "blockchain", "crypto"] categories = ["cryptography::cryptocurrencies", "development-tools"] [workspace.lints.rust] +rust_2018_idioms = { level = "deny", priority = -1 } +rust_2021_compatibility = { level = "deny", priority = -1 } +future_incompatible = { level = "deny", priority = -1 } unsafe_code = "deny" -rust_2018_idioms = "deny" -rust_2021_compatibility = "deny" -future_incompatible = "deny" non_ascii_idents = "deny" nonstandard_style = "deny" unused_extern_crates = "deny" From e80eee2bffedda289ef2acf992277921d185c7d3 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sat, 3 Aug 2024 15:06:54 -0400 Subject: [PATCH 100/100] One more --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 61f6797..f455dee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,9 +11,9 @@ categories = ["cryptography::cryptocurrencies", "development-tools"] rust_2018_idioms = { level = "deny", priority = -1 } rust_2021_compatibility = { level = "deny", priority = -1 } future_incompatible = { level = "deny", priority = -1 } +nonstandard_style = { level = "deny", priority = -1 } unsafe_code = "deny" non_ascii_idents = "deny" -nonstandard_style = "deny" unused_extern_crates = "deny" trivial_casts = "deny" trivial_numeric_casts = "deny"