use std::collections::HashMap; use roc_module::{ident, symbol}; use roc_types::{ num, subs::{self, GetSubsSlice, Subs, SubsIndex, SubsSlice, UnionLabels}, types, }; use roc_checkmate_schema::{ AliasKind, AliasTypeVariables, ClosureType, Content, NumericRange, NumericRangeKind, Rank, RecordField, RecordFieldKind, Symbol, TagUnionExtension, UnificationMode, UnspecializedClosureType, Variable, }; pub trait AsSchema { fn as_schema(&self, subs: &Subs) -> T; } impl AsSchema> for Option where T: AsSchema, T: Copy, { fn as_schema(&self, subs: &Subs) -> Option { self.map(|i| i.as_schema(subs)) } } impl AsSchema> for &[T] where T: AsSchema, { fn as_schema(&self, subs: &Subs) -> Vec { self.iter().map(|i| i.as_schema(subs)).collect() } } impl AsSchema for SubsIndex where Subs: std::ops::Index, Output = T>, T: AsSchema, { fn as_schema(&self, subs: &Subs) -> U { subs[*self].as_schema(subs) } } impl AsSchema> for SubsSlice where Subs: GetSubsSlice, T: AsSchema, { fn as_schema(&self, subs: &Subs) -> Vec { subs.get_subs_slice(*self) .iter() .map(|i| i.as_schema(subs)) .collect() } } impl AsSchema for subs::Content { fn as_schema(&self, subs: &Subs) -> Content { use {subs::Content as A, Content as B}; match self { A::FlexVar(name) => B::Flex(name.as_schema(subs)), A::RigidVar(name) => B::Rigid(name.as_schema(subs)), A::FlexAbleVar(name, abilities) => { B::FlexAble(name.as_schema(subs), abilities.as_schema(subs)) } A::RigidAbleVar(name, abilities) => { B::RigidAble(name.as_schema(subs), abilities.as_schema(subs)) } A::RecursionVar { structure, opt_name, } => B::Recursive(opt_name.as_schema(subs), structure.as_schema(subs)), A::LambdaSet(lambda_set) => lambda_set.as_schema(subs), A::ErasedLambda => B::ErasedLambda(), A::Structure(flat_type) => flat_type.as_schema(subs), A::Alias(name, type_vars, real_var, kind) => B::Alias( name.as_schema(subs), type_vars.as_schema(subs), real_var.as_schema(subs), kind.as_schema(subs), ), A::RangedNumber(range) => B::RangedNumber(range.as_schema(subs)), A::Error => B::Error(), } } } impl AsSchema for subs::FlatType { fn as_schema(&self, subs: &Subs) -> Content { match self { subs::FlatType::Apply(symbol, variables) => { Content::Apply(symbol.as_schema(subs), variables.as_schema(subs)) } subs::FlatType::Func(arguments, closure, ret) => Content::Function( arguments.as_schema(subs), closure.as_schema(subs), ret.as_schema(subs), ), subs::FlatType::Record(fields, ext) => { Content::Record(fields.as_schema(subs), ext.as_schema(subs)) } subs::FlatType::Tuple(elems, ext) => { Content::Tuple(elems.as_schema(subs), ext.as_schema(subs)) } subs::FlatType::TagUnion(tags, ext) => { Content::TagUnion(tags.as_schema(subs), ext.as_schema(subs)) } subs::FlatType::FunctionOrTagUnion(tags, functions, ext) => { Content::FunctionOrTagUnion( functions.as_schema(subs), tags.as_schema(subs), ext.as_schema(subs), ) } subs::FlatType::RecursiveTagUnion(rec_var, tags, ext) => Content::RecursiveTagUnion( rec_var.as_schema(subs), tags.as_schema(subs), ext.as_schema(subs), ), subs::FlatType::EmptyRecord => Content::EmptyRecord(), subs::FlatType::EmptyTuple => Content::EmptyTuple(), subs::FlatType::EmptyTagUnion => Content::EmptyTagUnion(), } } } impl AsSchema for subs::LambdaSet { fn as_schema(&self, subs: &Subs) -> Content { let subs::LambdaSet { solved, unspecialized, recursion_var, ambient_function, } = self; Content::LambdaSet( solved.as_schema(subs), unspecialized.as_schema(subs), recursion_var.as_schema(subs), ambient_function.as_schema(subs), ) } } impl AsSchema for ident::Lowercase { fn as_schema(&self, _subs: &Subs) -> String { self.to_string() } } impl AsSchema for symbol::Symbol { fn as_schema(&self, _subs: &Subs) -> Symbol { Symbol(format!("{:#?}", self)) } } impl AsSchema for subs::Variable { fn as_schema(&self, _subs: &Subs) -> Variable { Variable(self.index()) } } impl AsSchema> for subs::OptVariable { fn as_schema(&self, _subs: &Subs) -> Option { self.into_variable().map(|i| i.as_schema(_subs)) } } impl AsSchema> for UnionLabels { fn as_schema(&self, subs: &Subs) -> Vec { self.iter_from_subs(subs) .map(|(function, environment)| ClosureType { function: function.as_schema(subs), environment: environment.as_schema(subs), }) .collect() } } impl AsSchema for types::Uls { fn as_schema(&self, subs: &Subs) -> UnspecializedClosureType { let types::Uls(specialization, ability_member, lambda_set_region) = self; UnspecializedClosureType { specialization: specialization.as_schema(subs), ability_member: ability_member.as_schema(subs), lambda_set_region: *lambda_set_region, } } } impl AsSchema for subs::AliasVariables { fn as_schema(&self, subs: &Subs) -> AliasTypeVariables { let type_variables = self.type_variables().as_schema(subs); let lambda_set_variables = self.lambda_set_variables().as_schema(subs); let infer_ext_in_output_position_variables = self.infer_ext_in_output_variables().as_schema(subs); AliasTypeVariables { type_variables, lambda_set_variables, infer_ext_in_output_position_variables, } } } impl AsSchema for types::AliasKind { fn as_schema(&self, _subs: &Subs) -> AliasKind { match self { types::AliasKind::Structural => AliasKind::Structural, types::AliasKind::Opaque => AliasKind::Opaque, } } } impl AsSchema> for subs::RecordFields { fn as_schema(&self, subs: &Subs) -> HashMap { let mut map = HashMap::new(); for (name, var, field) in self.iter_all() { let name = name.as_schema(subs); let field_type = var.as_schema(subs); let kind = field.as_schema(subs); map.insert(name, RecordField { field_type, kind }); } map } } impl AsSchema for types::RecordField<()> { fn as_schema(&self, _subs: &Subs) -> RecordFieldKind { match self { types::RecordField::Demanded(_) => RecordFieldKind::Demanded, types::RecordField::Required(_) => RecordFieldKind::Required { rigid: false }, types::RecordField::Optional(_) => RecordFieldKind::Optional { rigid: false }, types::RecordField::RigidRequired(_) => RecordFieldKind::Required { rigid: true }, types::RecordField::RigidOptional(_) => RecordFieldKind::Optional { rigid: true }, } } } impl AsSchema> for subs::TupleElems { fn as_schema(&self, subs: &Subs) -> HashMap { let mut map = HashMap::new(); for (index, var) in self.iter_all() { let name = subs[index] as _; let var = var.as_schema(subs); map.insert(name, var); } map } } impl AsSchema>> for subs::UnionTags { fn as_schema(&self, subs: &Subs) -> HashMap> { let mut map = HashMap::new(); for (tag, payloads) in self.iter_from_subs(subs) { map.insert(tag.as_schema(subs), payloads.as_schema(subs)); } map } } impl AsSchema for subs::TagExt { fn as_schema(&self, subs: &Subs) -> TagUnionExtension { match self { subs::TagExt::Openness(var) => TagUnionExtension::Openness(var.as_schema(subs)), subs::TagExt::Any(var) => TagUnionExtension::Any(var.as_schema(subs)), } } } impl AsSchema for num::NumericRange { fn as_schema(&self, _subs: &Subs) -> NumericRange { let kind = match self { num::NumericRange::IntAtLeastSigned(_) | num::NumericRange::IntAtLeastEitherSign(_) => NumericRangeKind::Int, num::NumericRange::NumAtLeastSigned(_) | num::NumericRange::NumAtLeastEitherSign(_) => NumericRangeKind::AnyNum, }; let min_width = self.min_width(); let (signedness, width) = min_width.signedness_and_width(); let signed = signedness.is_signed(); NumericRange { kind, signed, min_width: width, } } } impl AsSchema for ident::TagName { fn as_schema(&self, _subs: &Subs) -> String { self.0.to_string() } } impl AsSchema for subs::Rank { fn as_schema(&self, _subs: &Subs) -> Rank { Rank(self.into_usize() as _) } } impl AsSchema for roc_solve_schema::UnificationMode { fn as_schema(&self, _subs: &Subs) -> UnificationMode { if self.is_eq() { UnificationMode::Eq } else if self.is_present() { UnificationMode::Present } else if self.is_lambda_set_specialization() { UnificationMode::LambdaSetSpecialization } else { unreachable!() } } }