use std::{cell::RefCell, rc::Rc, sync::Arc}; use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use uplc::{ ast::{ builder::{apply_wrap, delayed_choose_list, if_else}, Constant as UplcConstant, Name, Term, Type as UplcType, }, builtins::DefaultFunction, machine::runtime::{convert_constr_to_tag, ANY_TAG}, BigInt, Constr, KeyValuePairs, PlutusData, }; use crate::{ air::Air, ast::{ AssignmentKind, BinOp, Clause, ClauseGuard, Constant, DataType, Pattern, Span, TypedArg, TypedDataType, UnOp, }, expr::TypedExpr, tipo::{PatternConstructor, Type, TypeVar, ValueConstructor, ValueConstructorVariant}, }; #[derive(Clone, Debug)] pub struct FuncComponents { pub ir: Vec, pub dependencies: Vec, pub args: Vec, pub recursive: bool, } #[derive(Clone, Eq, Debug, PartialEq, Hash)] pub struct ConstrFieldKey { pub local_var: String, pub field_name: String, } #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct DataTypeKey { pub module_name: String, pub defined_type: String, } pub type ConstrUsageKey = String; #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct FunctionAccessKey { pub module_name: String, pub function_name: String, pub variant_name: String, } #[derive(Clone, Debug)] pub struct AssignmentProperties { pub value_type: Arc, pub kind: AssignmentKind, } #[derive(Clone, Debug)] pub enum ClauseProperties { ConstrClause { clause_var_name: String, needs_constr_var: bool, is_complex_clause: bool, original_subject_name: String, }, ListClause { clause_var_name: String, needs_constr_var: bool, is_complex_clause: bool, original_subject_name: String, current_index: i64, }, TupleClause { clause_var_name: String, needs_constr_var: bool, is_complex_clause: bool, original_subject_name: String, defined_tuple_indices: IndexSet<(usize, String)>, }, } impl ClauseProperties { pub fn init(t: &Arc, constr_var: String, subject_name: String) -> Self { if t.is_list() { ClauseProperties::ListClause { clause_var_name: constr_var, needs_constr_var: false, is_complex_clause: false, original_subject_name: subject_name, current_index: -1, } } else if t.is_tuple() { ClauseProperties::TupleClause { clause_var_name: constr_var, needs_constr_var: false, is_complex_clause: false, original_subject_name: subject_name, defined_tuple_indices: IndexSet::new(), } } else { ClauseProperties::ConstrClause { clause_var_name: constr_var, needs_constr_var: false, is_complex_clause: false, original_subject_name: subject_name, } } } pub fn is_complex_clause(&mut self) -> &mut bool { match self { ClauseProperties::ConstrClause { is_complex_clause, .. } | ClauseProperties::ListClause { is_complex_clause, .. } | ClauseProperties::TupleClause { is_complex_clause, .. } => is_complex_clause, } } pub fn needs_constr_var(&mut self) -> &mut bool { match self { ClauseProperties::ConstrClause { needs_constr_var, .. } | ClauseProperties::ListClause { needs_constr_var, .. } | ClauseProperties::TupleClause { needs_constr_var, .. } => needs_constr_var, } } pub fn clause_var_name(&mut self) -> &mut String { match self { ClauseProperties::ConstrClause { clause_var_name, .. } | ClauseProperties::ListClause { clause_var_name, .. } | ClauseProperties::TupleClause { clause_var_name, .. } => clause_var_name, } } pub fn original_subject_name(&mut self) -> &mut String { match self { ClauseProperties::ConstrClause { original_subject_name, .. } | ClauseProperties::ListClause { original_subject_name, .. } | ClauseProperties::TupleClause { original_subject_name, .. } => original_subject_name, } } } pub fn convert_type_to_data(term: Term, field_type: &Arc) -> Term { if field_type.is_bytearray() { apply_wrap(DefaultFunction::BData.into(), term) } else if field_type.is_int() { apply_wrap(DefaultFunction::IData.into(), term) } else if field_type.is_map() { apply_wrap(DefaultFunction::MapData.into(), term) } else if field_type.is_string() { apply_wrap( DefaultFunction::BData.into(), apply_wrap(DefaultFunction::EncodeUtf8.into(), term), ) } else if field_type.is_tuple() && matches!(field_type.get_uplc_type(), UplcType::Pair(_, _)) { apply_wrap( Term::Lambda { parameter_name: Name { text: "__pair".to_string(), unique: 0.into(), } .into(), body: apply_wrap( DefaultFunction::ListData.into(), apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::MkCons).force_wrap(), apply_wrap( Term::Builtin(DefaultFunction::FstPair) .force_wrap() .force_wrap(), Term::Var( Name { text: "__pair".to_string(), unique: 0.into(), } .into(), ), ), ), apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::MkCons).force_wrap(), apply_wrap( Term::Builtin(DefaultFunction::SndPair) .force_wrap() .force_wrap(), Term::Var( Name { text: "__pair".to_string(), unique: 0.into(), } .into(), ), ), ), Term::Constant(UplcConstant::ProtoList(UplcType::Data, vec![]).into()), ), ), ) .into(), }, term, ) } else if field_type.is_list() || field_type.is_tuple() { apply_wrap(DefaultFunction::ListData.into(), term) } else if field_type.is_bool() { if_else( term, Term::Constant( UplcConstant::Data(PlutusData::Constr(Constr { tag: convert_constr_to_tag(1).unwrap(), any_constructor: None, fields: vec![], })) .into(), ), Term::Constant( UplcConstant::Data(PlutusData::Constr(Constr { tag: convert_constr_to_tag(0).unwrap(), any_constructor: None, fields: vec![], })) .into(), ), ) } else { term } } pub fn convert_data_to_type(term: Term, field_type: &Arc) -> Term { if field_type.is_int() { apply_wrap(DefaultFunction::UnIData.into(), term) } else if field_type.is_bytearray() { apply_wrap(DefaultFunction::UnBData.into(), term) } else if field_type.is_map() { apply_wrap(DefaultFunction::UnMapData.into(), term) } else if field_type.is_string() { apply_wrap( DefaultFunction::DecodeUtf8.into(), apply_wrap(DefaultFunction::UnBData.into(), term), ) } else if field_type.is_tuple() && matches!(field_type.get_uplc_type(), UplcType::Pair(_, _)) { apply_wrap( Term::Lambda { parameter_name: Name { text: "__list_data".to_string(), unique: 0.into(), } .into(), body: apply_wrap( Term::Lambda { parameter_name: Name { text: "__tail".to_string(), unique: 0.into(), } .into(), body: apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::MkPairData), apply_wrap( Term::Builtin(DefaultFunction::HeadList).force_wrap(), Term::Var( Name { text: "__list_data".to_string(), unique: 0.into(), } .into(), ), ), ), apply_wrap( Term::Builtin(DefaultFunction::HeadList).force_wrap(), Term::Var( Name { text: "__tail".to_string(), unique: 0.into(), } .into(), ), ), ) .into(), }, apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), Term::Var( Name { text: "__list_data".to_string(), unique: 0.into(), } .into(), ), ), ) .into(), }, apply_wrap(Term::Builtin(DefaultFunction::UnListData), term), ) } else if field_type.is_list() || field_type.is_tuple() { apply_wrap(DefaultFunction::UnListData.into(), term) } else if field_type.is_bool() { apply_wrap( apply_wrap( DefaultFunction::EqualsInteger.into(), Term::Constant(UplcConstant::Integer(1).into()), ), apply_wrap( Term::Builtin(DefaultFunction::FstPair) .force_wrap() .force_wrap(), apply_wrap(DefaultFunction::UnConstrData.into(), term), ), ) } else { term } } pub fn rearrange_clauses( clauses: Vec, String>>, ) -> Vec, String>> { let mut sorted_clauses = clauses; // if we have a list sort clauses so we can plug holes for cases not covered by clauses // TODO: while having 10000000 element list is impossible to destructure in plutus budget, // let's sort clauses by a safer manner // TODO: how shall tails be weighted? Since any clause after will not run sorted_clauses.sort_by(|clause1, clause2| { let clause1_len = match &clause1.pattern[0] { Pattern::List { elements, tail, .. } => elements.len() + usize::from(tail.is_some()), _ => 10000000, }; let clause2_len = match &clause2.pattern[0] { Pattern::List { elements, tail, .. } => elements.len() + usize::from(tail.is_some()), _ => 10000001, }; clause1_len.cmp(&clause2_len) }); let mut elems_len = 0; let mut final_clauses = sorted_clauses.clone(); let mut holes_to_fill = vec![]; let mut assign_plug_in_name = None; let mut last_clause_index = 0; let mut last_clause_set = false; // If we have a catch all, use that. Otherwise use todo which will result in error // TODO: fill in todo label with description let plug_in_then = match &sorted_clauses[sorted_clauses.len() - 1].pattern[0] { Pattern::Var { name, .. } => { assign_plug_in_name = Some(name); sorted_clauses[sorted_clauses.len() - 1].clone().then } Pattern::Discard { .. } => sorted_clauses[sorted_clauses.len() - 1].clone().then, _ => TypedExpr::ErrorTerm { location: Span::empty(), tipo: sorted_clauses[sorted_clauses.len() - 1].then.tipo(), label: Some("Clause not filled".to_string()), }, }; for (index, clause) in sorted_clauses.iter().enumerate() { if let Pattern::List { elements, .. } = &clause.pattern[0] { // found a hole and now we plug it while elems_len < elements.len() { let mut discard_elems = vec![]; for _ in 0..elems_len { discard_elems.push(Pattern::Discard { name: "_".to_string(), location: Span::empty(), }); } // If we have a named catch all then in scope the name and create list of discards, otherwise list of discards let clause_to_fill = if let Some(name) = assign_plug_in_name { Clause { location: Span::empty(), pattern: vec![Pattern::Assign { name: name.clone(), location: Span::empty(), pattern: Pattern::List { location: Span::empty(), elements: discard_elems, tail: None, } .into(), }], alternative_patterns: vec![], guard: None, then: plug_in_then.clone(), } } else { Clause { location: Span::empty(), pattern: vec![Pattern::List { location: Span::empty(), elements: discard_elems, tail: None, }], alternative_patterns: vec![], guard: None, then: plug_in_then.clone(), } }; holes_to_fill.push((index, clause_to_fill)); elems_len += 1; } } // if we have a pattern with no clause guards and a tail then no lists will get past here to other clauses match &clause.pattern[0] { Pattern::Var { .. } => { last_clause_index = index + 1; last_clause_set = true; } Pattern::Discard { .. } => { last_clause_index = index + 1; last_clause_set = true; } Pattern::List { elements, tail: Some(tail), .. } => { let mut elements = elements.clone(); elements.push(*tail.clone()); if elements .iter() .all(|element| matches!(element, Pattern::Var { .. } | Pattern::Discard { .. })) && !last_clause_set && !elements.is_empty() { last_clause_index = index + 1; last_clause_set = true; } } _ => {} } // If the last condition doesn't have a catch all or tail then add a catch all with a todo if index == sorted_clauses.len() - 1 { if let Pattern::List { tail: None, .. } = &clause.pattern[0] { final_clauses.push(Clause { location: Span::empty(), pattern: vec![Pattern::Discard { name: "_".to_string(), location: Span::empty(), }], alternative_patterns: vec![], guard: None, then: plug_in_then.clone(), }); } } elems_len += 1; } // Encountered a tail so stop there with that as last clause if last_clause_set { final_clauses = final_clauses[0..last_clause_index].to_vec(); } // insert hole fillers into clauses for (index, clause) in holes_to_fill.into_iter().rev() { final_clauses.insert(index, clause); } final_clauses } #[allow(clippy::too_many_arguments)] pub fn list_access_to_uplc( names: &[String], id_list: &[u64], tail: bool, current_index: usize, term: Term, tipos: Vec>, check_last_item: bool, is_list_accessor: bool, ) -> Term { if let Some((first, names)) = names.split_first() { let (current_tipo, tipos) = tipos.split_first().unwrap(); let head_list = if matches!(current_tipo.get_uplc_type(), UplcType::Pair(_, _)) && is_list_accessor { apply_wrap( Term::Builtin(DefaultFunction::HeadList).force_wrap(), Term::Var( Name { text: format!("tail_index_{}_{}", current_index, id_list[current_index]), unique: 0.into(), } .into(), ), ) } else { convert_data_to_type( apply_wrap( Term::Builtin(DefaultFunction::HeadList).force_wrap(), Term::Var( Name { text: format!( "tail_index_{}_{}", current_index, id_list[current_index] ), unique: 0.into(), } .into(), ), ), ¤t_tipo.to_owned(), ) }; if names.len() == 1 && tail { Term::Lambda { parameter_name: Name { text: format!("tail_index_{}_{}", current_index, id_list[current_index]), unique: 0.into(), } .into(), body: apply_wrap( Term::Lambda { parameter_name: Name { text: first.clone(), unique: 0.into(), } .into(), body: apply_wrap( Term::Lambda { parameter_name: Name { text: names[0].clone(), unique: 0.into(), } .into(), body: term.into(), }, apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), Term::Var( Name { text: format!( "tail_index_{}_{}", current_index, id_list[current_index] ), unique: 0.into(), } .into(), ), ), ) .into(), }, head_list, ) .into(), } } else if names.is_empty() { Term::Lambda { parameter_name: Name { text: format!("tail_index_{}_{}", current_index, id_list[current_index]), unique: 0.into(), } .into(), body: apply_wrap( Term::Lambda { parameter_name: Name { text: first.clone(), unique: 0.into(), } .into(), body: if check_last_item { delayed_choose_list( apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), Term::Var( Name { text: format!( "tail_index_{}_{}", current_index, id_list[current_index] ), unique: 0.into(), } .into(), ), ), term, apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::Trace).force_wrap(), Term::Constant( UplcConstant::String( "List/Tuple/Constr contains more items than it should" .to_string(), ) .into(), ), ), Term::Delay(Term::Error.into()), ) .force_wrap(), ) .into() } else { term.into() }, }, head_list, ) .into(), } } else { Term::Lambda { parameter_name: Name { text: format!("tail_index_{}_{}", current_index, id_list[current_index]), unique: 0.into(), } .into(), body: apply_wrap( Term::Lambda { parameter_name: Name { text: first.clone(), unique: 0.into(), } .into(), body: apply_wrap( list_access_to_uplc( names, id_list, tail, current_index + 1, term, tipos.to_owned(), check_last_item, is_list_accessor, ), apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), Term::Var( Name { text: format!( "tail_index_{}_{}", current_index, id_list[current_index] ), unique: 0.into(), } .into(), ), ), ) .into(), }, head_list, ) .into(), } } } else { term } } pub fn get_common_ancestor(scope: &[u64], scope_prev: &[u64]) -> Vec { let longest_length = if scope.len() >= scope_prev.len() { scope.len() } else { scope_prev.len() }; if *scope == *scope_prev { return scope.to_vec(); } for index in 0..longest_length { if scope.get(index).is_none() { return scope.to_vec(); } else if scope_prev.get(index).is_none() { return scope_prev.to_vec(); } else if scope[index] != scope_prev[index] { return scope[0..index].to_vec(); } } vec![] } pub fn check_when_pattern_needs( pattern: &Pattern>, clause_properties: &mut ClauseProperties, ) { match pattern { Pattern::Var { .. } => { *clause_properties.needs_constr_var() = true; } Pattern::List { elements, tail, .. } => { *clause_properties.needs_constr_var() = true; *clause_properties.is_complex_clause() = true; for element in elements { check_when_pattern_needs(element, clause_properties); } if let Some(tail) = tail { check_when_pattern_needs(tail, clause_properties); } } Pattern::Tuple { elems, .. } => { *clause_properties.needs_constr_var() = true; *clause_properties.is_complex_clause() = true; for element in elems { check_when_pattern_needs(element, clause_properties); } } Pattern::Int { .. } => { *clause_properties.needs_constr_var() = true; *clause_properties.is_complex_clause() = true; } Pattern::Constructor { arguments, .. } => { *clause_properties.needs_constr_var() = true; *clause_properties.is_complex_clause() = true; for argument in arguments { check_when_pattern_needs(&argument.value, clause_properties); } } Pattern::Discard { .. } => { *clause_properties.needs_constr_var() = true; } _ => todo!("{pattern:#?}"), } } pub fn constants_ir( literal: &Constant, String>, ir_stack: &mut Vec, scope: Vec, ) { match literal { Constant::Int { value, .. } => { ir_stack.push(Air::Int { scope, value: value.clone(), }); } Constant::String { value, .. } => { ir_stack.push(Air::String { scope, value: value.clone(), }); } Constant::Tuple { .. } => { todo!() } Constant::List { elements, tipo, .. } => { ir_stack.push(Air::List { scope: scope.clone(), count: elements.len(), tipo: tipo.clone(), tail: false, }); for element in elements { constants_ir(element, ir_stack, scope.clone()); } } Constant::Record { .. } => { // ir_stack.push(Air::Record { scope, }); todo!() } Constant::ByteArray { bytes, .. } => { ir_stack.push(Air::ByteArray { scope, bytes: bytes.clone(), }); } Constant::Var { .. } => todo!(), }; } pub fn match_ir_for_recursion( ir: Air, insert_var_vec: &mut Vec<(usize, Air)>, function_access_key: &FunctionAccessKey, index: usize, ) { if let Air::Var { scope, constructor, variant_name, .. } = ir { if let ValueConstructorVariant::ModuleFn { name: func_name, module, .. } = constructor.clone().variant { let var_func_access = FunctionAccessKey { module_name: module, function_name: func_name.clone(), variant_name: variant_name.clone(), }; if function_access_key.clone() == var_func_access { insert_var_vec.push(( index, Air::Var { scope, constructor, name: func_name, variant_name, }, )); } } } } pub fn find_generics_to_replace(tipo: &mut Arc, generic_types: &IndexMap>) { if let Some(id) = tipo.get_generic() { //If generic does not have a type we know of like a None in option then just use same type *tipo = generic_types.get(&id).unwrap_or(tipo).clone(); } else if tipo.is_generic() { match &**tipo { Type::App { args, public, module, name, } => { let mut new_args = vec![]; for arg in args { let mut arg = arg.clone(); find_generics_to_replace(&mut arg, generic_types); new_args.push(arg); } let t = Type::App { args: new_args, public: *public, module: module.clone(), name: name.clone(), }; *tipo = t.into(); } Type::Fn { args, ret } => { let mut new_args = vec![]; for arg in args { let mut arg = arg.clone(); find_generics_to_replace(&mut arg, generic_types); new_args.push(arg); } let mut ret = ret.clone(); find_generics_to_replace(&mut ret, generic_types); let t = Type::Fn { args: new_args, ret, }; *tipo = t.into(); } Type::Tuple { elems } => { let mut new_elems = vec![]; for elem in elems { let mut elem = elem.clone(); find_generics_to_replace(&mut elem, generic_types); new_elems.push(elem); } let t = Type::Tuple { elems: new_elems }; *tipo = t.into(); } Type::Var { tipo: var_tipo } => { let var_type = var_tipo.as_ref().borrow().clone(); let var_tipo = match var_type { TypeVar::Unbound { .. } => todo!(), TypeVar::Link { tipo } => { let mut tipo = tipo; find_generics_to_replace(&mut tipo, generic_types); tipo } TypeVar::Generic { .. } => unreachable!(), }; let t = Type::Var { tipo: RefCell::from(TypeVar::Link { tipo: var_tipo }).into(), }; *tipo = t.into() } }; } } pub fn get_generics_and_type(tipo: &Type, param: &Type) -> Vec<(u64, Arc)> { let mut generics_ids = vec![]; if let Some(id) = tipo.get_generic() { generics_ids.push((id, param.clone().into())); return generics_ids; } for (tipo, param_type) in tipo .get_inner_types() .iter() .zip(param.get_inner_types().iter()) { generics_ids.append(&mut get_generics_and_type(tipo, param_type)); } generics_ids } pub fn get_variant_name(new_name: &mut String, t: &Arc) { new_name.push_str(&if t.is_string() { "_string".to_string() } else if t.is_int() { "_int".to_string() } else if t.is_bool() { "_bool".to_string() } else if t.is_bytearray() { "_bytearray".to_string() } else if t.is_map() { let mut full_type = "_map".to_string(); let pair_type = &t.get_inner_types()[0]; let fst_type = &pair_type.get_inner_types()[0]; let snd_type = &pair_type.get_inner_types()[1]; get_variant_name(&mut full_type, fst_type); get_variant_name(&mut full_type, snd_type); full_type } else if t.is_list() { let mut full_type = "_list".to_string(); let list_type = &t.get_inner_types()[0]; get_variant_name(&mut full_type, list_type); full_type } else if t.is_tuple() { let mut full_type = "_tuple".to_string(); let inner_types = t.get_inner_types(); for arg_type in inner_types { get_variant_name(&mut full_type, &arg_type); } full_type } else if t.is_unbound() { "_unbound".to_string() } else { let mut full_type = "_data".to_string(); let inner_types = t.get_inner_types(); for arg_type in inner_types { get_variant_name(&mut full_type, &arg_type); } full_type }); } pub fn convert_constants_to_data(constants: Vec>) -> Vec { let mut new_constants = vec![]; for constant in constants { let constant = match constant.as_ref() { UplcConstant::Integer(i) => { UplcConstant::Data(PlutusData::BigInt(BigInt::Int((*i).try_into().unwrap()))) } UplcConstant::ByteString(b) => { UplcConstant::Data(PlutusData::BoundedBytes(b.clone().try_into().unwrap())) } UplcConstant::String(s) => UplcConstant::Data(PlutusData::BoundedBytes( s.as_bytes().to_vec().try_into().unwrap(), )), UplcConstant::Bool(b) => UplcConstant::Data(PlutusData::Constr(Constr { tag: convert_constr_to_tag((*b).into()).unwrap_or(ANY_TAG), any_constructor: convert_constr_to_tag((*b).into()) .map_or(Some((*b).into()), |_| None), fields: vec![], })), UplcConstant::ProtoList(_, constants) => { let inner_constants = convert_constants_to_data(constants.iter().cloned().map(Rc::new).collect()) .into_iter() .map(|constant| match constant { UplcConstant::Data(d) => d, _ => todo!(), }) .collect_vec(); UplcConstant::Data(PlutusData::Array(inner_constants)) } UplcConstant::ProtoPair(_, _, left, right) => { let inner_constants = vec![left.clone(), right.clone()]; let inner_constants = convert_constants_to_data(inner_constants) .into_iter() .map(|constant| match constant { UplcConstant::Data(d) => d, _ => todo!(), }) .collect_vec(); UplcConstant::Data(PlutusData::Map(KeyValuePairs::Def(vec![( inner_constants[0].clone(), inner_constants[1].clone(), )]))) } d @ UplcConstant::Data(_) => d.clone(), _ => unreachable!(), }; new_constants.push(constant); } new_constants } pub fn wrap_validator_args(term: Term, arguments: &[TypedArg]) -> Term { let mut term = term; for arg in arguments.iter().rev() { if !matches!(arg.tipo.get_uplc_type(), UplcType::Data) { term = apply_wrap( Term::Lambda { parameter_name: Name { text: arg.arg_name.get_variable_name().unwrap_or("_").to_string(), unique: 0.into(), } .into(), body: term.into(), }, convert_data_to_type( Term::Var( Name { text: arg.arg_name.get_variable_name().unwrap_or("_").to_string(), unique: 0.into(), } .into(), ), &arg.tipo, ), ); } term = Term::Lambda { parameter_name: Name { text: arg.arg_name.get_variable_name().unwrap_or("_").to_string(), unique: 0.into(), } .into(), body: term.into(), } } term } pub fn monomorphize( ir: Vec, generic_types: IndexMap>, full_type: &Arc, ) -> (String, Vec) { let mut new_air = ir.clone(); let mut new_name = String::new(); let mut needs_variant = false; for (index, ir) in ir.into_iter().enumerate() { match ir { Air::Var { constructor, scope, name, .. } => { if constructor.tipo.is_generic() { let mut tipo = constructor.tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); let mut variant = String::new(); let mut constructor = constructor.clone(); constructor.tipo = tipo; if let Type::Fn { args, .. } = &*constructor.tipo { if matches!( constructor.variant, ValueConstructorVariant::ModuleFn { .. } ) { for arg in args { get_variant_name(&mut variant, arg); } } } new_air[index] = Air::Var { scope, constructor, name, variant_name: variant, }; needs_variant = true; } } Air::List { tipo, scope, count, tail, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::List { scope, count, tipo, tail, }; needs_variant = true; } } Air::ListAccessor { scope, tipo, names, tail, check_last_item, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::ListAccessor { scope, names, tipo, tail, check_last_item, }; needs_variant = true; } } Air::ListExpose { scope, tipo, tail_head_names, tail, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::ListExpose { scope, tail_head_names, tipo, tail, }; needs_variant = true; } } Air::BinOp { scope, name, count, tipo, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::BinOp { scope, name, tipo, count, }; needs_variant = true; } } Air::Builtin { scope, func, tipo } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::Builtin { scope, func, tipo }; needs_variant = true; } } Air::UnWrapData { scope, tipo } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::UnWrapData { scope, tipo }; needs_variant = true; } } Air::WrapData { scope, tipo } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::WrapData { scope, tipo }; needs_variant = true; } } Air::When { scope, tipo, subject_name, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::When { scope, subject_name, tipo, }; needs_variant = true; } } Air::Clause { scope, tipo, subject_name, complex_clause, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::Clause { scope, tipo, subject_name, complex_clause, }; needs_variant = true; } } Air::ListClause { scope, tipo, tail_name, complex_clause, next_tail_name, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::ListClause { scope, tipo, tail_name, complex_clause, next_tail_name, }; needs_variant = true; } } Air::TupleClause { scope, tipo, indices, predefined_indices, subject_name, count, complex_clause, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::TupleClause { scope, tipo, indices, predefined_indices, subject_name, count, complex_clause, }; needs_variant = true; } } Air::ClauseGuard { tipo, scope, subject_name, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::ClauseGuard { scope, subject_name, tipo, }; needs_variant = true; } } Air::ListClauseGuard { scope, tipo, tail_name, next_tail_name, inverse, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::ListClauseGuard { scope, tipo, tail_name, next_tail_name, inverse, }; needs_variant = true; } } Air::Tuple { scope, tipo, count } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::Tuple { scope, tipo, count }; needs_variant = true; } } Air::TupleIndex { scope, tipo, tuple_index, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::TupleIndex { scope, tipo, tuple_index, }; needs_variant = true; } } Air::Todo { scope, label, tipo } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::Todo { scope, tipo, label }; needs_variant = true; } } Air::ErrorTerm { scope, label, tipo } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::ErrorTerm { scope, tipo, label }; needs_variant = true; } } Air::Trace { scope, text, tipo } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::Trace { scope, tipo, text }; needs_variant = true; } } Air::Record { scope, constr_index, tipo, count, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::Record { scope, tipo, constr_index, count, }; needs_variant = true; } } Air::RecordAccess { scope, record_index, tipo, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::RecordAccess { scope, record_index, tipo, }; needs_variant = true; } } Air::FieldsExpose { scope, indices, check_last_item, } => { let mut new_indices = vec![]; for (ind, name, tipo) in indices { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); needs_variant = true; new_indices.push((ind, name, tipo)); } else { new_indices.push((ind, name, tipo)); } } new_air[index] = Air::FieldsExpose { scope, indices: new_indices, check_last_item, }; } Air::RecordUpdate { scope, highest_index, indices, tipo, } => { let mut new_indices = vec![]; let mut tipo = tipo.clone(); for (ind, tipo) in indices { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); needs_variant = true; new_indices.push((ind, tipo)); } else { new_indices.push((ind, tipo)); } } if tipo.is_generic() { find_generics_to_replace(&mut tipo, &generic_types); } new_air[index] = Air::RecordUpdate { scope, highest_index, indices: new_indices, tipo, }; } Air::TupleAccessor { scope, names, tipo, check_last_item, } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::TupleAccessor { scope, names, tipo, check_last_item, }; needs_variant = true; } } Air::Call { scope, count, tipo } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::Call { scope, count, tipo }; needs_variant = true; } } Air::If { scope, tipo } => { if tipo.is_generic() { let mut tipo = tipo.clone(); find_generics_to_replace(&mut tipo, &generic_types); new_air[index] = Air::If { scope, tipo }; needs_variant = true; } } _ => {} } } if let Type::Fn { args, .. } = &**full_type { if needs_variant { for arg in args { get_variant_name(&mut new_name, arg); } } } (new_name, new_air) } pub fn handle_func_dependencies_ir( dependencies_ir: &mut Vec, funt_comp: &FuncComponents, func_components: &IndexMap, defined_functions: &mut IndexMap, func_index_map: &IndexMap>, func_scope: &[u64], to_be_defined: &mut IndexMap, ) { let mut funt_comp = funt_comp.clone(); let mut dependency_map = IndexMap::new(); let mut dependency_vec = vec![]; // deal with function dependencies by sorting order in which we pop them. while let Some(dependency) = funt_comp.dependencies.pop() { let depend_comp = func_components.get(&dependency).unwrap(); if dependency_map.contains_key(&dependency) { dependency_map.shift_remove(&dependency); } dependency_map.insert(dependency, ()); funt_comp .dependencies .extend(depend_comp.dependencies.clone().into_iter()); } dependency_vec.extend(dependency_map.keys().cloned()); dependency_vec.reverse(); while let Some(dependency) = dependency_vec.pop() { if (defined_functions.contains_key(&dependency) && !funt_comp.args.is_empty()) || func_components.get(&dependency).is_none() { continue; } let depend_comp = func_components.get(&dependency).unwrap(); let dep_scope = func_index_map.get(&dependency).unwrap(); if get_common_ancestor(dep_scope, func_scope) == func_scope.to_vec() || funt_comp.args.is_empty() { // we handle zero arg functions and their dependencies in a unique way if !depend_comp.args.is_empty() { let mut recursion_ir = vec![]; handle_recursion_ir(&dependency, depend_comp, &mut recursion_ir); let mut temp_ir = vec![Air::DefineFunc { scope: func_scope.to_vec(), func_name: dependency.function_name.clone(), module_name: dependency.module_name.clone(), params: depend_comp.args.clone(), recursive: depend_comp.recursive, variant_name: dependency.variant_name.clone(), }]; temp_ir.append(&mut recursion_ir); temp_ir.append(dependencies_ir); *dependencies_ir = temp_ir; if get_common_ancestor(dep_scope, func_scope) == func_scope.to_vec() { defined_functions.insert(dependency, ()); } } } else { // Dependency will need to be defined somewhere in the main body to_be_defined.insert(dependency, ()); } } } pub fn handle_recursion_ir( func_key: &FunctionAccessKey, func_comp: &FuncComponents, recursion_ir: &mut Vec, ) { let mut insert_var_vec = vec![]; for (index, ir) in func_comp.ir.iter().enumerate().rev() { match_ir_for_recursion( ir.clone(), &mut insert_var_vec, &FunctionAccessKey { function_name: func_key.function_name.clone(), module_name: func_key.module_name.clone(), variant_name: func_key.variant_name.clone(), }, index, ); } *recursion_ir = func_comp.ir.clone(); // Deals with self recursive function for (index, ir) in insert_var_vec.clone() { recursion_ir.insert(index, ir); let current_call = recursion_ir[index - 1].clone(); match current_call { Air::Call { scope, count, tipo } => { recursion_ir[index - 1] = Air::Call { scope, count: count + 1, tipo, } } _ => unreachable!(), } } } pub fn lookup_data_type_by_tipo( data_types: IndexMap, tipo: &Type, ) -> Option>> { match tipo { Type::Fn { ret, .. } => match ret.as_ref() { Type::App { module, name, .. } => { let data_type_key = DataTypeKey { module_name: module.clone(), defined_type: name.clone(), }; data_types.get(&data_type_key).map(|item| (*item).clone()) } _ => None, }, Type::App { module, name, .. } => { let data_type_key = DataTypeKey { module_name: module.clone(), defined_type: name.clone(), }; data_types.get(&data_type_key).map(|item| (*item).clone()) } Type::Var { tipo } => { if let TypeVar::Link { tipo } = &*tipo.borrow() { lookup_data_type_by_tipo(data_types, tipo) } else { None } } _ => None, } } pub fn check_replaceable_opaque_type( t: &Arc, data_types: &IndexMap, ) -> bool { let data_type = lookup_data_type_by_tipo(data_types.clone(), t); if let Some(data_type) = data_type { let data_type_args = data_type.constructors[0].arguments.clone(); data_type_args.len() == 1 && data_type.opaque && data_type.constructors.len() == 1 } else { false } } pub fn replace_opaque_type(t: &mut Arc, data_types: IndexMap) { if check_replaceable_opaque_type(t, &data_types) && matches!(&**t, Type::App { .. }) { let data_type = lookup_data_type_by_tipo(data_types.clone(), t).unwrap(); let new_type_fields = data_type.typed_parameters.clone(); let mut generics_type_map: IndexMap> = IndexMap::new(); for (tipo, param) in new_type_fields.iter().zip(t.arg_types().unwrap()) { let mut map = generics_type_map.into_iter().collect_vec(); map.append(&mut get_generics_and_type(tipo, ¶m)); generics_type_map = map.into_iter().collect(); } let mut generic_type = data_type.constructors[0].arguments[0].tipo.clone(); find_generics_to_replace(&mut generic_type, &generics_type_map); replace_opaque_type(&mut generic_type, data_types.clone()); *t = generic_type; } else { match (**t).clone() { Type::App { public, module, name, args, } => { let mut new_args = vec![]; for arg in args { let mut new_arg_type = arg.clone(); replace_opaque_type(&mut new_arg_type, data_types.clone()); new_args.push(new_arg_type); } *t = Type::App { public, module, name, args: new_args, } .into(); } Type::Fn { args, ret } => { let mut new_args = vec![]; for arg in args { let mut new_arg_type = arg.clone(); replace_opaque_type(&mut new_arg_type, data_types.clone()); new_args.push(new_arg_type); } let mut new_ret = ret; replace_opaque_type(&mut new_ret, data_types.clone()); *t = Type::Fn { args: new_args, ret: new_ret, } .into(); } Type::Var { tipo } => { if let TypeVar::Link { tipo } = &*tipo.borrow() { let mut new_type = tipo.clone(); replace_opaque_type(&mut new_type, data_types.clone()); *t = new_type; } } Type::Tuple { elems } => { let mut new_elems = vec![]; for arg in elems { let mut new_arg_type = arg.clone(); replace_opaque_type(&mut new_arg_type, data_types.clone()); new_elems.push(new_arg_type); } *t = Type::Tuple { elems: new_elems }.into(); } } } } pub fn handle_clause_guard( clause_guard: &ClauseGuard, String>, clause_guard_vec: &mut Vec, scope: Vec, ) { match clause_guard { ClauseGuard::Not { value, .. } => { clause_guard_vec.push(Air::UnOp { scope: scope.clone(), op: UnOp::Not, }); handle_clause_guard(value, clause_guard_vec, scope); } ClauseGuard::Equals { left, right, .. } => { clause_guard_vec.push(Air::BinOp { scope: scope.clone(), name: BinOp::Eq, count: 2, tipo: left.tipo(), }); handle_clause_guard(left, clause_guard_vec, scope.clone()); handle_clause_guard(right, clause_guard_vec, scope); } ClauseGuard::NotEquals { left, right, .. } => { clause_guard_vec.push(Air::BinOp { scope: scope.clone(), name: BinOp::NotEq, count: 2, tipo: left.tipo(), }); handle_clause_guard(left, clause_guard_vec, scope.clone()); handle_clause_guard(right, clause_guard_vec, scope); } ClauseGuard::GtInt { left, right, .. } => { clause_guard_vec.push(Air::BinOp { scope: scope.clone(), name: BinOp::GtInt, count: 2, tipo: left.tipo(), }); handle_clause_guard(left, clause_guard_vec, scope.clone()); handle_clause_guard(right, clause_guard_vec, scope); } ClauseGuard::GtEqInt { left, right, .. } => { clause_guard_vec.push(Air::BinOp { scope: scope.clone(), name: BinOp::GtEqInt, count: 2, tipo: left.tipo(), }); handle_clause_guard(left, clause_guard_vec, scope.clone()); handle_clause_guard(right, clause_guard_vec, scope); } ClauseGuard::LtInt { left, right, .. } => { clause_guard_vec.push(Air::BinOp { scope: scope.clone(), name: BinOp::LtInt, count: 2, tipo: left.tipo(), }); handle_clause_guard(left, clause_guard_vec, scope.clone()); handle_clause_guard(right, clause_guard_vec, scope); } ClauseGuard::LtEqInt { left, right, .. } => { clause_guard_vec.push(Air::BinOp { scope: scope.clone(), name: BinOp::LtEqInt, count: 2, tipo: left.tipo(), }); handle_clause_guard(left, clause_guard_vec, scope.clone()); handle_clause_guard(right, clause_guard_vec, scope); } ClauseGuard::Or { left, right, .. } => { clause_guard_vec.push(Air::BinOp { scope: scope.clone(), name: BinOp::Or, count: 2, tipo: left.tipo(), }); handle_clause_guard(left, clause_guard_vec, scope.clone()); handle_clause_guard(right, clause_guard_vec, scope); } ClauseGuard::And { left, right, .. } => { clause_guard_vec.push(Air::BinOp { scope: scope.clone(), name: BinOp::And, count: 2, tipo: left.tipo(), }); handle_clause_guard(left, clause_guard_vec, scope.clone()); handle_clause_guard(right, clause_guard_vec, scope); } ClauseGuard::Var { tipo, name, .. } => { clause_guard_vec.push(Air::Var { scope, constructor: ValueConstructor::public( tipo.clone(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: name.clone(), variant_name: String::new(), }); } ClauseGuard::Constant(constant) => { constants_ir(constant, clause_guard_vec, scope); } } }