From 4baf36efee9fb5a0f1327be8efbda2dca1f71db4 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Fri, 26 Jul 2024 13:03:53 -0400 Subject: [PATCH] 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 {