use std::cell::Cell; use std::path::PathBuf; use std::sync::Arc; use crate::abilities::SpecializationId; use crate::exhaustive::{ExhaustiveContext, SketchedRows}; use crate::expected::{Expected, PExpected}; use roc_collections::soa::{EitherIndex, Index, Slice}; use roc_module::ident::TagName; use roc_module::symbol::{ModuleId, Symbol}; use roc_region::all::{Loc, Region}; use roc_types::subs::{ExhaustiveMark, IllegalCycleMark, Variable}; use roc_types::types::{Category, PatternCategory, TypeTag, Types}; pub struct Constraints { pub constraints: Vec, pub type_slices: Vec, pub variables: Vec, pub loc_symbols: Vec<(Symbol, Region)>, pub let_constraints: Vec, pub categories: Vec, pub pattern_categories: Vec, pub expectations: Vec>, pub pattern_expectations: Vec>, pub includes_tags: Vec, pub strings: Vec<&'static str>, pub sketched_rows: Vec, pub eq: Vec, pub pattern_eq: Vec, pub cycles: Vec, } impl std::fmt::Debug for Constraints { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Constraints") .field("constraints", &self.constraints) .field("types", &"") .field("type_slices", &self.type_slices) .field("variables", &self.variables) .field("loc_symbols", &self.loc_symbols) .field("let_constraints", &self.let_constraints) .field("categories", &self.categories) .field("pattern_categories", &self.pattern_categories) .field("expectations", &"") .field("pattern_expectations", &"") .field("includes_tags", &self.includes_tags) .field("strings", &self.strings) .field("sketched_rows", &self.sketched_rows) .field("eq", &self.eq) .field("pattern_eq", &self.pattern_eq) .field("cycles", &self.cycles) .finish() } } impl Default for Constraints { fn default() -> Self { Self::new() } } pub type ExpectedTypeIndex = Index>; pub type PExpectedTypeIndex = Index>; pub type TypeOrVar = EitherIndex; impl Constraints { pub fn new() -> Self { let constraints = Vec::new(); let type_slices = Vec::with_capacity(16); let variables = Vec::new(); let loc_symbols = Vec::new(); let let_constraints = Vec::new(); let mut categories = Vec::with_capacity(16); let mut pattern_categories = Vec::with_capacity(16); let expectations = Vec::new(); let pattern_expectations = Vec::new(); let includes_tags = Vec::new(); let strings = Vec::new(); let sketched_rows = Vec::new(); let eq = Vec::new(); let pattern_eq = Vec::new(); let cycles = Vec::new(); categories.extend([ Category::Record, Category::ForeignCall, Category::OpaqueArg, Category::Lambda, Category::ClosureSize, Category::StrInterpolation, Category::If, Category::When, Category::Frac, Category::Int, Category::Num, Category::List, Category::Str, Category::Character, ]); pattern_categories.extend([ PatternCategory::Record, PatternCategory::EmptyRecord, PatternCategory::PatternGuard, PatternCategory::PatternDefault, PatternCategory::Set, PatternCategory::Map, PatternCategory::Str, PatternCategory::Num, PatternCategory::Int, PatternCategory::Float, PatternCategory::Character, ]); Self { constraints, type_slices, variables, loc_symbols, let_constraints, categories, pattern_categories, expectations, pattern_expectations, includes_tags, strings, sketched_rows, eq, pattern_eq, cycles, } } pub const EMPTY_RECORD: Index>> = Index::new(0); pub const EMPTY_TAG_UNION: Index>> = Index::new(1); pub const STR: Index>> = Index::new(2); pub const CATEGORY_RECORD: Index = Index::new(0); pub const CATEGORY_FOREIGNCALL: Index = Index::new(1); pub const CATEGORY_OPAQUEARG: Index = Index::new(2); pub const CATEGORY_LAMBDA: Index = Index::new(3); pub const CATEGORY_CLOSURESIZE: Index = Index::new(4); pub const CATEGORY_STRINTERPOLATION: Index = Index::new(5); pub const CATEGORY_IF: Index = Index::new(6); pub const CATEGORY_WHEN: Index = Index::new(7); pub const CATEGORY_FLOAT: Index = Index::new(8); pub const CATEGORY_INT: Index = Index::new(9); pub const CATEGORY_NUM: Index = Index::new(10); pub const CATEGORY_LIST: Index = Index::new(11); pub const CATEGORY_STR: Index = Index::new(12); pub const CATEGORY_CHARACTER: Index = Index::new(13); pub const PCATEGORY_RECORD: Index = Index::new(0); pub const PCATEGORY_EMPTYRECORD: Index = Index::new(1); pub const PCATEGORY_PATTERNGUARD: Index = Index::new(2); pub const PCATEGORY_PATTERNDEFAULT: Index = Index::new(3); pub const PCATEGORY_SET: Index = Index::new(4); pub const PCATEGORY_MAP: Index = Index::new(5); pub const PCATEGORY_STR: Index = Index::new(6); pub const PCATEGORY_NUM: Index = Index::new(7); pub const PCATEGORY_INT: Index = Index::new(8); pub const PCATEGORY_FLOAT: Index = Index::new(9); pub const PCATEGORY_CHARACTER: Index = Index::new(10); #[inline(always)] pub fn push_type(&mut self, types: &Types, typ: Index) -> TypeOrVar { if let TypeTag::Variable(var) = types[typ] { Self::push_type_variable(var) } else { EitherIndex::from_left(typ) } } pub fn statistics(&self, module_id: ModuleId) -> Result { use std::fmt::Write; let mut buf = String::new(); writeln!(buf, "Constraints statistics for module {:?}:", module_id)?; writeln!(buf, " constraints length: {}:", self.constraints.len())?; writeln!( buf, " let_constraints length: {}:", self.let_constraints.len() )?; writeln!(buf, " expectations length: {}:", self.expectations.len())?; writeln!(buf, " categories length: {}:", self.categories.len())?; Ok(buf) } #[inline(always)] pub const fn push_variable(&self, var: Variable) -> TypeOrVar { Self::push_type_variable(var) } #[inline(always)] const fn push_type_variable(var: Variable) -> TypeOrVar { // that's right, we use the variable's integer value as the index // that way, we don't need to push anything onto a vector let index: Index = Index::new(var.index()); EitherIndex::from_right(index) } pub fn push_expected_type(&mut self, expected: Expected) -> ExpectedTypeIndex { Index::push_new(&mut self.expectations, expected) } pub fn push_pat_expected_type(&mut self, expected: PExpected) -> PExpectedTypeIndex { Index::push_new(&mut self.pattern_expectations, expected) } #[inline(always)] pub fn push_category(&mut self, category: Category) -> Index { match category { Category::Record => Self::CATEGORY_RECORD, Category::ForeignCall => Self::CATEGORY_FOREIGNCALL, Category::OpaqueArg => Self::CATEGORY_OPAQUEARG, Category::Lambda => Self::CATEGORY_LAMBDA, Category::ClosureSize => Self::CATEGORY_CLOSURESIZE, Category::StrInterpolation => Self::CATEGORY_STRINTERPOLATION, Category::If => Self::CATEGORY_IF, Category::When => Self::CATEGORY_WHEN, Category::Frac => Self::CATEGORY_FLOAT, Category::Int => Self::CATEGORY_INT, Category::Num => Self::CATEGORY_NUM, Category::List => Self::CATEGORY_LIST, Category::Str => Self::CATEGORY_STR, Category::Character => Self::CATEGORY_CHARACTER, other => Index::push_new(&mut self.categories, other), } } #[inline(always)] pub fn push_pattern_category(&mut self, category: PatternCategory) -> Index { match category { PatternCategory::Record => Self::PCATEGORY_RECORD, PatternCategory::EmptyRecord => Self::PCATEGORY_EMPTYRECORD, PatternCategory::PatternGuard => Self::PCATEGORY_PATTERNGUARD, PatternCategory::PatternDefault => Self::PCATEGORY_PATTERNDEFAULT, PatternCategory::Set => Self::PCATEGORY_SET, PatternCategory::Map => Self::PCATEGORY_MAP, PatternCategory::Str => Self::PCATEGORY_STR, PatternCategory::Num => Self::PCATEGORY_NUM, PatternCategory::Int => Self::PCATEGORY_INT, PatternCategory::Float => Self::PCATEGORY_FLOAT, PatternCategory::Character => Self::PCATEGORY_CHARACTER, other => Index::push_new(&mut self.pattern_categories, other), } } pub fn equal_types( &mut self, type_index: TypeOrVar, expected_index: ExpectedTypeIndex, category: Category, region: Region, ) -> Constraint { let category_index = Self::push_category(self, category); Constraint::Eq(Eq(type_index, expected_index, category_index, region)) } pub fn equal_types_var( &mut self, var: Variable, expected_index: ExpectedTypeIndex, category: Category, region: Region, ) -> Constraint { let type_index = Self::push_type_variable(var); let category_index = Self::push_category(self, category); Constraint::Eq(Eq(type_index, expected_index, category_index, region)) } pub fn equal_types_with_storage( &mut self, type_index: TypeOrVar, expected_index: ExpectedTypeIndex, category: Category, region: Region, storage_var: Variable, ) -> Constraint { let category_index = Self::push_category(self, category); let equal = Constraint::Eq(Eq(type_index, expected_index, category_index, region)); let storage_type_index = Self::push_type_variable(storage_var); let storage_category = Category::Storage(std::file!(), std::line!()); let storage_category_index = Self::push_category(self, storage_category); let storage = Constraint::Eq(Eq( storage_type_index, expected_index, storage_category_index, region, )); self.and_constraint([equal, storage]) } pub fn equal_pattern_types( &mut self, type_index: TypeOrVar, expected_index: PExpectedTypeIndex, category: PatternCategory, region: Region, ) -> Constraint { let category_index = Self::push_pattern_category(self, category); Constraint::Pattern(type_index, expected_index, category_index, region) } pub fn pattern_presence( &mut self, type_index: TypeOrVar, expected_index: PExpectedTypeIndex, category: PatternCategory, region: Region, ) -> Constraint { let category_index = Index::push_new(&mut self.pattern_categories, category); Constraint::PatternPresence(type_index, expected_index, category_index, region) } pub fn is_open_type(&mut self, type_index: TypeOrVar) -> Constraint { Constraint::IsOpenType(type_index) } pub fn includes_tag( &mut self, type_index: TypeOrVar, tag_name: TagName, payloads: Slice, category: PatternCategory, region: Region, ) -> Constraint { let category_index = Index::push_new(&mut self.pattern_categories, category); let includes_tag = IncludesTag { type_index, tag_name, types: payloads, pattern_category: category_index, region, }; let includes_tag_index = Index::push_new(&mut self.includes_tags, includes_tag); Constraint::IncludesTag(includes_tag_index) } pub fn variable_slice(&mut self, it: I) -> Slice where I: IntoIterator, { let start = self.variables.len(); self.variables.extend(it); let length = self.variables.len() - start; Slice::new(start as _, length as _) } fn def_types_slice(&mut self, it: I) -> DefTypes where I: IntoIterator)>, I::IntoIter: ExactSizeIterator, { let it = it.into_iter(); let types_start = self.type_slices.len(); let loc_symbols_start = self.loc_symbols.len(); // because we have an ExactSizeIterator, we can reserve space here let length = it.len(); self.type_slices.reserve(length); self.loc_symbols.reserve(length); for (symbol, loc_type) in it { let Loc { region, value } = loc_type; self.type_slices.push(value); self.loc_symbols.push((symbol, region)); } DefTypes { types: Slice::new(types_start as _, length as _), loc_symbols: Slice::new(loc_symbols_start as _, length as _), } } /// A constraint that some type variables exist and should be introduced into the environment /// at a given rank. #[inline(always)] pub fn exists(&mut self, flex_vars: I, defs_constraint: Constraint) -> Constraint where I: IntoIterator, { let defs_and_ret_constraint = Index::new(self.constraints.len() as _); self.constraints.push(defs_constraint); self.constraints.push(Constraint::True); let let_contraint = LetConstraint { rigid_vars: Slice::default(), flex_vars: self.variable_slice(flex_vars), def_types: DefTypes::default(), defs_and_ret_constraint, // We're just marking that the variables exist, not that they should be generalized // (in fact there is no return constraint, so nothing to generalize at this level) generalizable: Generalizable(false), }; let let_index = Index::new(self.let_constraints.len() as _); self.let_constraints.push(let_contraint); Constraint::Let(let_index, Slice::default()) } #[inline(always)] pub fn exists_many(&mut self, flex_vars: I, defs_constraint: C) -> Constraint where I: IntoIterator, C: IntoIterator, C::IntoIter: ExactSizeIterator, { let defs_constraint = self.and_constraint(defs_constraint); let defs_and_ret_constraint = Index::new(self.constraints.len() as _); self.constraints.push(defs_constraint); self.constraints.push(Constraint::True); let let_contraint = LetConstraint { rigid_vars: Slice::default(), flex_vars: self.variable_slice(flex_vars), def_types: DefTypes::default(), defs_and_ret_constraint, generalizable: Generalizable(false), }; let let_index = Index::new(self.let_constraints.len() as _); self.let_constraints.push(let_contraint); Constraint::Let(let_index, Slice::default()) } #[inline(always)] pub fn let_constraint( &mut self, rigid_vars: I1, flex_vars: I2, def_types: I3, defs_constraint: Constraint, ret_constraint: Constraint, generalizable: Generalizable, ) -> Constraint where I1: IntoIterator, I2: IntoIterator, I3: IntoIterator)>, I3::IntoIter: ExactSizeIterator, { // defs and ret constraint are stored consequtively, so we only need to store one index let defs_and_ret_constraint = Index::new(self.constraints.len() as _); self.constraints.push(defs_constraint); self.constraints.push(ret_constraint); let let_constraint = LetConstraint { rigid_vars: self.variable_slice(rigid_vars), flex_vars: self.variable_slice(flex_vars), def_types: self.def_types_slice(def_types), defs_and_ret_constraint, generalizable, }; let let_index = Index::new(self.let_constraints.len() as _); self.let_constraints.push(let_constraint); Constraint::Let(let_index, Slice::default()) } /// A variant of `Let` used specifically for imports. When importing types from another module, /// we use a StorageSubs to store the data, and copy over the relevant /// variables/content/flattype/tagname etc. /// /// The general idea is to let-generalize the imorted types in the target module. /// More concretely, we need to simulate what `type_to_var` (solve.rs) does to a `Type`. /// While the copying puts all the data the right place, it misses that `type_to_var` puts /// the variables that it creates (to store the nodes of a Type in Subs) in the pool of the /// current rank (so they can be generalized). /// /// So, during copying of an import (`copy_import_to`, subs.rs) we track the variables that /// we need to put into the pool (simulating what `type_to_var` would do). Those variables /// then need to find their way to the pool, and a convenient approach turned out to be to /// tag them onto the `Let` that we used to add the imported values. #[inline(always)] pub fn let_import_constraint( &mut self, rigid_vars: I1, flex_vars: I2, def_types: I3, module_constraint: Constraint, pool_variables: &[Variable], ) -> Constraint where I1: IntoIterator, I2: IntoIterator, I3: IntoIterator)>, I3::IntoIter: ExactSizeIterator, { // defs and ret constraint are stored consequtively, so we only need to store one index let defs_and_ret_constraint = Index::new(self.constraints.len() as _); self.constraints.push(Constraint::True); self.constraints.push(module_constraint); let let_contraint = LetConstraint { rigid_vars: self.variable_slice(rigid_vars), flex_vars: self.variable_slice(flex_vars), def_types: self.def_types_slice(def_types), defs_and_ret_constraint, // If the module these variables were solved in solved them as generalized, // they should be generalized here too. generalizable: Generalizable(true), }; let let_index = Index::new(self.let_constraints.len() as _); self.let_constraints.push(let_contraint); let pool_slice = self.variable_slice(pool_variables.iter().copied()); Constraint::Let(let_index, pool_slice) } #[inline(always)] pub fn and_constraint(&mut self, constraints: I) -> Constraint where I: IntoIterator, I::IntoIter: ExactSizeIterator, { let mut it = constraints.into_iter(); match it.len() { 0 => Constraint::True, 1 => it.next().unwrap(), _ => { let start = self.constraints.len() as u32; self.constraints.extend(it); let end = self.constraints.len() as u32; let slice = Slice::new(start, (end - start) as u16); Constraint::And(slice) } } } pub fn lookup( &mut self, symbol: Symbol, expected_index: ExpectedTypeIndex, region: Region, ) -> Constraint { Constraint::Lookup(symbol, expected_index, region) } pub fn contains_save_the_environment(&self, constraint: &Constraint) -> bool { match constraint { Constraint::SaveTheEnvironment => true, Constraint::Let(index, _) => { let let_constraint = &self.let_constraints[index.index()]; let offset = let_constraint.defs_and_ret_constraint.index(); let defs_constraint = &self.constraints[offset]; let ret_constraint = &self.constraints[offset + 1]; self.contains_save_the_environment(defs_constraint) || self.contains_save_the_environment(ret_constraint) } Constraint::And(slice) => { let constraints = &self.constraints[slice.indices()]; constraints .iter() .any(|c| self.contains_save_the_environment(c)) } Constraint::Eq(..) | Constraint::Store(..) | Constraint::Lookup(..) | Constraint::Pattern(..) | Constraint::True | Constraint::IsOpenType(_) | Constraint::IncludesTag(_) | Constraint::PatternPresence(_, _, _, _) | Constraint::Exhaustive { .. } | Constraint::Resolve(..) | Constraint::IngestedFile(..) | Constraint::CheckCycle(..) => false, } } pub fn store( &mut self, type_index: TypeOrVar, variable: Variable, filename: &'static str, line_number: u32, ) -> Constraint { let string_index = Index::push_new(&mut self.strings, filename); Constraint::Store(type_index, variable, string_index, line_number) } pub fn exhaustive( &mut self, real_var: Variable, real_region: Region, category_and_expectation: Result< (Category, ExpectedTypeIndex), (PatternCategory, PExpectedTypeIndex), >, sketched_rows: SketchedRows, context: ExhaustiveContext, exhaustive: ExhaustiveMark, ) -> Constraint { let real_var = Self::push_type_variable(real_var); let sketched_rows = Index::push_new(&mut self.sketched_rows, sketched_rows); let equality = match category_and_expectation { Ok((category, expected)) => { let category = Index::push_new(&mut self.categories, category); let equality = Eq(real_var, expected, category, real_region); let equality = Index::push_new(&mut self.eq, equality); Ok(equality) } Err((category, expected)) => { let category = Index::push_new(&mut self.pattern_categories, category); let equality = PatternEq(real_var, expected, category, real_region); let equality = Index::push_new(&mut self.pattern_eq, equality); Err(equality) } }; Constraint::Exhaustive(equality, sketched_rows, context, exhaustive) } pub fn check_cycle( &mut self, loc_symbols: I, expr_regions: I1, cycle_mark: IllegalCycleMark, ) -> Constraint where I: IntoIterator, I1: IntoIterator, { let def_names = Slice::extend_new(&mut self.loc_symbols, loc_symbols); // we add a dummy symbol to these regions, so we can store the data in the loc_symbols vec let it = expr_regions.into_iter().map(|r| (Symbol::ATTR_ATTR, r)); let expr_regions = Slice::extend_new(&mut self.loc_symbols, it); let expr_regions = Slice::new(expr_regions.start() as _, expr_regions.len() as _); let cycle = Cycle { def_names, expr_regions, }; let cycle_index = Index::push_new(&mut self.cycles, cycle); Constraint::CheckCycle(cycle_index, cycle_mark) } pub fn ingested_file( &mut self, type_index: TypeOrVar, file_path: Box, bytes: Arc>, ) -> Constraint { Constraint::IngestedFile(type_index, file_path, bytes) } } roc_error_macros::assert_sizeof_default!(Constraint, 3 * 8); roc_error_macros::assert_sizeof_aarch64!(Constraint, 3 * 8); impl std::ops::Index for Constraints { type Output = Expected; fn index(&self, index: ExpectedTypeIndex) -> &Self::Output { &self.expectations[index.index()] } } impl std::ops::Index for Constraints { type Output = PExpected; fn index(&self, index: PExpectedTypeIndex) -> &Self::Output { &self.pattern_expectations[index.index()] } } #[derive(Clone, Copy, Debug)] pub struct Eq( pub TypeOrVar, pub ExpectedTypeIndex, pub Index, pub Region, ); #[derive(Clone, Copy, Debug)] pub struct PatternEq( pub TypeOrVar, pub PExpectedTypeIndex, pub Index, pub Region, ); /// When we come across a lookup of an ability member, we'd like to try to specialize that /// lookup during solving (knowing the specialization statically avoids re-solving during mono, /// and always gives us a way to show what specialization was intended in the editor). /// /// However, we attempting to resolve the specialization right at the lookup site is futile /// (we may not have solved enough of the surrounding context to know the specialization). /// So, we only collect what resolutions we'd like to make, and attempt to resolve them once /// we pass through a let-binding (a def, or a normal `=` binding). At those positions, the /// expression is generalized, so if there is a static specialization, we'd know it at that /// point. /// /// Note that this entirely opportunistic; if a lookup of an ability member uses it /// polymorphically, we won't find its specialization(s) until monomorphization. #[derive(Clone, Copy, Debug)] pub struct OpportunisticResolve { /// The specialized type of this lookup, to try to resolve. pub specialization_variable: Variable, /// The ability member to try to resolve. pub member: Symbol, /// If we resolve a specialization, what specialization ID to store it on. pub specialization_id: SpecializationId, } #[derive(Clone)] pub enum Constraint { Eq(Eq), Store(TypeOrVar, Variable, Index<&'static str>, u32), Lookup(Symbol, ExpectedTypeIndex, Region), Pattern( TypeOrVar, PExpectedTypeIndex, Index, Region, ), /// Used for things that always unify, e.g. blanks and runtime errors True, SaveTheEnvironment, /// A Let constraint introduces symbols and their annotation at a certain level of nesting /// /// The `Slice` is used for imports where we manually put the Content into Subs /// by copying from another module, but have to make sure that any variables we use to store /// these contents are added to `Pool` at the correct rank Let(Index, Slice), And(Slice), /// Presence constraints IsOpenType(TypeOrVar), // Theory; always applied to a variable? if yes the use that IncludesTag(Index), PatternPresence( TypeOrVar, PExpectedTypeIndex, Index, Region, ), Exhaustive( Result, Index>, Index, ExhaustiveContext, ExhaustiveMark, ), /// Attempt to resolve a specialization. Resolve(OpportunisticResolve), CheckCycle(Index, IllegalCycleMark), IngestedFile(TypeOrVar, Box, Arc>), } #[derive(Debug, Clone, Copy, Default)] pub struct DefTypes { pub types: Slice, pub loc_symbols: Slice<(Symbol, Region)>, } #[derive(Debug, Clone, Copy)] pub struct Generalizable(pub bool); #[derive(Debug, Clone)] pub struct LetConstraint { pub rigid_vars: Slice, pub flex_vars: Slice, pub def_types: DefTypes, pub defs_and_ret_constraint: Index<(Constraint, Constraint)>, /// Whether the defs introduces in the let-binding can be generalized. /// Only /// - syntactic functions /// - syntactic numbers /// may be eligible for generalization, though there are other restrictions too. pub generalizable: Generalizable, } #[derive(Debug, Clone)] pub struct IncludesTag { pub type_index: TypeOrVar, pub tag_name: TagName, pub types: Slice, pub pattern_category: Index, pub region: Region, } #[derive(Debug, Clone, Copy)] pub struct Cycle { pub def_names: Slice<(Symbol, Region)>, pub expr_regions: Slice, } /// Custom impl to limit vertical space used by the debug output impl std::fmt::Debug for Constraint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Eq(Eq(arg0, arg1, arg2, arg3)) => { write!(f, "Eq({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3) } Self::Store(arg0, arg1, arg2, arg3) => { write!(f, "Store({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3) } Self::Lookup(arg0, arg1, arg2) => { write!(f, "Lookup({:?}, {:?}, {:?})", arg0, arg1, arg2) } Self::Pattern(arg0, arg1, arg2, arg3) => { write!(f, "Pattern({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3) } Self::True => write!(f, "True"), Self::SaveTheEnvironment => write!(f, "SaveTheEnvironment"), Self::Let(arg0, arg1) => f.debug_tuple("Let").field(arg0).field(arg1).finish(), Self::And(arg0) => f.debug_tuple("And").field(arg0).finish(), Self::IsOpenType(arg0) => f.debug_tuple("IsOpenType").field(arg0).finish(), Self::IncludesTag(arg0) => f.debug_tuple("IncludesTag").field(arg0).finish(), Self::PatternPresence(arg0, arg1, arg2, arg3) => { write!( f, "PatternPresence({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3 ) } Self::Exhaustive(arg0, arg1, arg2, arg3) => { write!( f, "Exhaustive({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3 ) } Self::Resolve(arg0) => { write!(f, "Resolve({:?})", arg0) } Self::CheckCycle(arg0, arg1) => { write!(f, "CheckCycle({:?}, {:?})", arg0, arg1) } Self::IngestedFile(arg0, arg1, arg2) => { write!(f, "IngestedFile({:?}, {:?}, {:?})", arg0, arg1, arg2) } } } }