use crate::expected::{Expected, PExpected}; use roc_collections::soa::{Index, Slice}; use roc_module::ident::TagName; use roc_module::symbol::Symbol; use roc_region::all::{Loc, Region}; use roc_types::types::{Category, PatternCategory, Type}; use roc_types::{subs::Variable, types::VariableDetail}; pub struct Constraints { constraints: Vec, types: Vec, variables: Vec, def_types: Vec<(Symbol, Loc>)>, let_constraints: Vec, categories: Vec, pattern_categories: Vec, expectations: Vec>, pattern_expectations: Vec>, includes_tags: Vec, } impl Constraints { pub const EMPTY_RECORD: Index = Index::new(0); pub const EMPTY_TAG_UNION: Index = Index::new(1); pub const CATEGORY_RECORD: Index = Index::new(0); #[inline(always)] pub fn push_type(&mut self, typ: Type) -> Index { match typ { Type::EmptyRec => Self::EMPTY_RECORD, Type::EmptyTagUnion => Self::EMPTY_TAG_UNION, other => Index::push_new(&mut self.types, other), } } #[inline(always)] pub fn push_expected_type(&mut self, expected: Expected) -> Index> { Index::push_new(&mut self.expectations, expected) } #[inline(always)] pub fn push_category(&mut self, category: Category) -> Index { match category { Category::Record => Self::CATEGORY_RECORD, other => Index::push_new(&mut self.categories, other), } } pub fn equal_types( &mut self, typ: Type, expected: Expected, category: Category, region: Region, ) -> Constraint { let type_index = Index::push_new(&mut self.types, typ); let expected_index = Index::push_new(&mut self.expectations, expected); let category_index = Index::push_new(&mut self.categories, category); Constraint::Eq(type_index, expected_index, category_index, region) } pub fn equal_pattern_types( &mut self, typ: Type, expected: PExpected, category: PatternCategory, region: Region, ) -> Constraint { let type_index = Index::push_new(&mut self.types, typ); let expected_index = Index::push_new(&mut self.pattern_expectations, expected); let category_index = Index::push_new(&mut self.pattern_categories, category); Constraint::Pattern(type_index, expected_index, category_index, region) } pub fn pattern_presence( &mut self, typ: Type, expected: PExpected, category: PatternCategory, region: Region, ) -> Constraint { let type_index = Index::push_new(&mut self.types, typ); let expected_index = Index::push_new(&mut self.pattern_expectations, expected); 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, typ: Type) -> Constraint { let type_index = Index::push_new(&mut self.types, typ); Constraint::IsOpenType(type_index) } pub fn includes_tag( &mut self, typ: Type, tag_name: TagName, types: I, category: PatternCategory, region: Region, ) -> Constraint where I: IntoIterator, { let type_index = Index::push_new(&mut self.types, typ); let category_index = Index::push_new(&mut self.pattern_categories, category); let types_slice = Slice::extend_new(&mut self.types, types); let includes_tag = IncludesTag { type_index, tag_name, types: types_slice, pattern_category: category_index, region, }; let includes_tag_index = Index::push_new(&mut self.includes_tags, includes_tag); Constraint::IncludesTag(includes_tag_index) } 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) -> Slice<(Symbol, Loc)> where I: IntoIterator)>, { let start = self.def_types.len(); for (symbol, loc_type) in it { let type_index = Index::new(self.types.len() as _); let Loc { region, value } = loc_type; self.types.push(value); self.def_types.push((symbol, Loc::at(region, type_index))); } let length = self.def_types.len() - start; Slice::new(start as _, length as _) } 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: Slice::default(), defs_and_ret_constraint, }; let let_index = Index::new(self.let_constraints.len() as _); self.let_constraints.push(let_contraint); Constraint::Let(let_index) } pub fn exists_many(&mut self, flex_vars: I, defs_constraint: C) -> Constraint where I: IntoIterator, C: IntoIterator, { let defs_and_ret_constraint = Index::new(self.constraints.len() as _); self.and_constraint(defs_constraint); self.constraints.push(Constraint::True); let let_contraint = LetConstraint { rigid_vars: Slice::default(), flex_vars: self.variable_slice(flex_vars), def_types: Slice::default(), defs_and_ret_constraint, }; let let_index = Index::new(self.let_constraints.len() as _); self.let_constraints.push(let_contraint); Constraint::Let(let_index) } pub fn let_constraint( &mut self, rigid_vars: I1, flex_vars: I2, def_types: I3, defs_constraint: Constraint, ret_constraint: Constraint, ) -> Constraint where I1: IntoIterator, I2: IntoIterator, I3: IntoIterator)>, { let defs_and_ret_constraint = Index::new(self.constraints.len() as _); self.constraints.push(defs_constraint); self.constraints.push(ret_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, }; let let_index = Index::new(self.let_constraints.len() as _); self.let_constraints.push(let_contraint); Constraint::Let(let_index) } pub fn and_constraint(&mut self, constraints: I) -> Constraint where I: IntoIterator, { let start = self.constraints.len() as u32; self.constraints.extend(constraints); 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: Expected, region: Region, ) -> Constraint { Constraint::Lookup( symbol, Index::push_new(&mut self.expectations, expected), region, ) } pub fn contains_save_the_environment(&self, constraint: &Constraint) -> bool { todo!() } pub fn store( &mut self, typ: Type, variable: Variable, filename: &'static str, line_number: u32, ) -> Constraint { let type_index = Index::new(self.types.len() as _); self.types.push(typ); Constraint::Store(type_index, variable, filename, line_number) } } static_assertions::assert_eq_size!([u8; 4 * 8], Constraint); static_assertions::assert_eq_size!([u8; 3 * 8 + 4], LetConstraint); #[derive(Debug, Clone, PartialEq)] pub enum Constraint { Eq(Index, Index>, Index, Region), Store(Index, Variable, &'static str, u32), Lookup(Symbol, Index>, Region), Pattern( Index, Index>, Index, Region, ), True, // Used for things that always unify, e.g. blanks and runtime errors SaveTheEnvironment, Let(Index), And(Slice), /// Presence constraints IsOpenType(Index), // Theory; always applied to a variable? if yes the use that IncludesTag(Index), PatternPresence( Index, Index>, Index, Region, ), } #[derive(Debug, Clone, PartialEq)] pub struct LetConstraint { pub rigid_vars: Slice, pub flex_vars: Slice, pub def_types: Slice<(Symbol, Loc)>, pub defs_and_ret_constraint: Index<(Constraint, Constraint)>, } #[derive(Debug, Clone, PartialEq)] pub struct IncludesTag { pub type_index: Index, pub tag_name: TagName, pub types: Slice, pub pattern_category: Index, pub region: Region, }