From 9dbe66bc0c3b9fc07e3f600b754702ac4208d4de Mon Sep 17 00:00:00 2001 From: microproofs Date: Wed, 23 Oct 2024 18:14:02 -0400 Subject: [PATCH] Producing uplc now. Just need to finish ListSwitch --- crates/aiken-lang/src/gen_uplc.rs | 1153 +---------------- crates/aiken-lang/src/gen_uplc/builder.rs | 465 +------ .../src/gen_uplc/stick_break_set.rs | 29 +- 3 files changed, 56 insertions(+), 1591 deletions(-) diff --git a/crates/aiken-lang/src/gen_uplc.rs b/crates/aiken-lang/src/gen_uplc.rs index 0839833d..52af1a0c 100644 --- a/crates/aiken-lang/src/gen_uplc.rs +++ b/crates/aiken-lang/src/gen_uplc.rs @@ -9,26 +9,25 @@ use self::{ air::Air, builder::{ cast_validator_args, convert_type_to_data, extract_constant, modify_cyclic_calls, - modify_self_calls, AssignmentProperties, ClauseProperties, CodeGenSpecialFuncs, - CycleFunctionNames, HoistableFunction, Variant, + modify_self_calls, AssignmentProperties, CodeGenSpecialFuncs, CycleFunctionNames, + HoistableFunction, Variant, }, tree::{AirTree, TreePath}, }; use crate::{ ast::{ AssignmentKind, BinOp, Bls12_381Point, Curve, DataTypeKey, FunctionAccessKey, Pattern, - Span, TraceLevel, Tracing, TypedArg, TypedClause, TypedDataType, TypedFunction, - TypedPattern, TypedValidator, UnOp, + Span, TraceLevel, Tracing, TypedArg, TypedDataType, TypedFunction, TypedPattern, + TypedValidator, UnOp, }, builtins::PRELUDE, expr::TypedExpr, gen_uplc::{ air::ExpectLevel, builder::{ - erase_opaque_type_operations, find_list_clause_or_default_first, - get_generic_variant_name, get_line_columns_by_span, get_src_code_by_span, - known_data_to_type, monomorphize, pattern_has_conditions, wrap_validator_condition, - CodeGenFunction, SpecificClause, + erase_opaque_type_operations, get_generic_variant_name, get_line_columns_by_span, + get_src_code_by_span, known_data_to_type, monomorphize, wrap_validator_condition, + CodeGenFunction, }, }, line_numbers::LineNumbers, @@ -47,7 +46,7 @@ use builder::{ }; use decision_tree::{get_tipo_by_path, Assigned, DecisionTree, TreeGen}; -use indexmap::{IndexMap, IndexSet}; +use indexmap::IndexMap; use interner::AirInterner; use itertools::Itertools; use petgraph::{algo, Graph}; @@ -622,7 +621,7 @@ impl<'a> CodeGenerator<'a> { let stick_set = TreeSet::new(); let clauses = self.handle_decision_tree( - &subject_name, + &subject_name_interned, subject.tipo(), tipo.clone(), module_build_name, @@ -2459,17 +2458,13 @@ impl<'a> CodeGenerator<'a> { cases.pop().unwrap().1 }; - let last_clause = AirTree::anon_func( - vec![], - self.handle_decision_tree( - subject_name, - subject_tipo.clone(), - return_tipo.clone(), - module_build_name, - last_clause, - stick_set.clone(), - ), - true, + let last_clause = self.handle_decision_tree( + subject_name, + subject_tipo.clone(), + return_tipo.clone(), + module_build_name, + last_clause, + stick_set.clone(), ); let test_subject_name = if data_type.is_some() { @@ -2493,7 +2488,7 @@ impl<'a> CodeGenerator<'a> { case.get_air_pattern(), current_tipo.clone(), case_air, - acc, + AirTree::anon_func(vec![], acc, true), false, ) }); @@ -2524,31 +2519,17 @@ impl<'a> CodeGenerator<'a> { }) .collect_vec(); - let then = if args.is_empty() { + let then = AirTree::call( AirTree::local_var( name, Type::function( air_args.iter().map(|i| i.0.clone()).collect_vec(), return_tipo.clone(), ), - ) - } else { - AirTree::anon_func( - vec![], - AirTree::call( - AirTree::local_var( - name, - Type::function( - air_args.iter().map(|i| i.0.clone()).collect_vec(), - return_tipo.clone(), - ), - ), - Type::void(), - air_args.into_iter().map(|i| i.1).collect_vec(), - ), - true, - ) - }; + ), + Type::void(), + air_args.into_iter().map(|i| i.1).collect_vec(), + ); args.into_iter().rfold(then, |acc, assign| { let Assigned { path, assigned } = assign; @@ -2629,1091 +2610,6 @@ impl<'a> CodeGenerator<'a> { } } - pub fn handle_each_clause( - &mut self, - clauses: &[TypedClause], - final_clause: TypedClause, - subject_tipo: &Rc, - props: &mut ClauseProperties, - module_name: &str, - ) -> AirTree { - assert!( - !subject_tipo.is_void(), - "WHY ARE YOU PATTERN MATCHING VOID???" - ); - props.complex_clause = false; - - if let Some((clause, rest_clauses)) = clauses.split_first() { - introduce_pattern(&mut self.interner, &clause.pattern); - - let clause_then = self.build(&clause.then, module_name, &[]); - - let tree = match &mut props.specific_clause { - SpecificClause::ConstrClause => { - let data_type = lookup_data_type_by_tipo(&self.data_types, subject_tipo); - - let (clause_cond, clause_assign) = - self.clause_pattern(&clause.pattern, subject_tipo, props, clause_then); - - let complex_clause = props.complex_clause; - - let mut next_clause_props = ClauseProperties::init( - subject_tipo, - props.clause_var_name.clone(), - props.original_subject_name.clone(), - ); - - if matches!( - &clause.pattern, - Pattern::Var { .. } | Pattern::Discard { .. } - ) { - AirTree::wrap_clause( - clause_assign, - self.handle_each_clause( - rest_clauses, - final_clause, - subject_tipo, - &mut next_clause_props, - module_name, - ), - ) - } else if let Some(data_type) = data_type { - if data_type.constructors.len() > 1 && !data_type.is_never() { - AirTree::clause( - &props.original_subject_name, - clause_cond, - subject_tipo.clone(), - clause_assign, - self.handle_each_clause( - rest_clauses, - final_clause, - subject_tipo, - &mut next_clause_props, - module_name, - ), - complex_clause, - ) - } else { - AirTree::wrap_clause( - clause_assign, - self.handle_each_clause( - rest_clauses, - final_clause, - subject_tipo, - &mut next_clause_props, - module_name, - ), - ) - } - } else { - // Case of ByteArray or Int or Bool matches - assert!(subject_tipo.is_primitive()); - - AirTree::clause( - &props.original_subject_name, - clause_cond, - subject_tipo.clone(), - clause_assign, - self.handle_each_clause( - rest_clauses, - final_clause, - subject_tipo, - &mut next_clause_props, - module_name, - ), - complex_clause, - ) - } - } - SpecificClause::ListClause { - defined_tails_index, - defined_tails, - checked_index, - } => { - let mut clause_pattern = &clause.pattern; - - if let Pattern::Assign { pattern, .. } = clause_pattern { - clause_pattern = pattern; - } - - assert!(matches!( - clause_pattern, - Pattern::List { .. } | Pattern::Var { .. } | Pattern::Discard { .. } - )); - - let Pattern::List { elements, tail, .. } = clause_pattern else { - let mut next_clause_props = ClauseProperties { - clause_var_name: props.clause_var_name.clone(), - complex_clause: false, - needs_constr_var: false, - original_subject_name: props.original_subject_name.clone(), - final_clause: false, - specific_clause: SpecificClause::ListClause { - defined_tails_index: *defined_tails_index, - defined_tails: defined_tails.clone(), - checked_index: *checked_index, - }, - }; - - let (_, clause_assign) = - self.clause_pattern(&clause.pattern, subject_tipo, props, clause_then); - - return AirTree::wrap_clause( - clause_assign, - self.handle_each_clause( - rest_clauses, - final_clause, - subject_tipo, - &mut next_clause_props, - module_name, - ), - ); - }; - - assert!(!elements.is_empty() || tail.is_none()); - let elements_len = elements.len() + usize::from(tail.is_none()) - 1; - let current_checked_index = *checked_index; - - let tail_name = defined_tails - .get(elements_len) - .cloned() - .unwrap_or(props.original_subject_name.clone()); - - let next_tail_name = { - if rest_clauses.is_empty() { - None - } else { - let next_clause = find_list_clause_or_default_first(rest_clauses); - let mut next_clause_pattern = &next_clause.pattern; - - if let Pattern::Assign { pattern, .. } = next_clause_pattern { - next_clause_pattern = pattern; - } - - let next_elements_len = match next_clause_pattern { - Pattern::List { elements, tail, .. } => { - assert!(!elements.is_empty() || tail.is_none()); - elements.len() + usize::from(tail.is_none()) - 1 - } - _ => 0, - }; - - if (*defined_tails_index as usize) < next_elements_len { - *defined_tails_index += 1; - let current_defined_tail = defined_tails.last().unwrap().clone(); - - defined_tails.push(format!( - "tail_index_{}_span_{}_{}", - *defined_tails_index, - next_clause.pattern.location().start, - next_clause.pattern.location().end - )); - - Some(( - current_defined_tail, - format!( - "tail_index_{}_span_{}_{}", - *defined_tails_index, - next_clause.pattern.location().start, - next_clause.pattern.location().end - ), - )) - } else { - None - } - } - }; - - let mut is_wild_card_elems_clause = true; - for element in elements.iter() { - is_wild_card_elems_clause = is_wild_card_elems_clause - && !pattern_has_conditions(element, &self.data_types); - } - - if *checked_index < elements_len.try_into().unwrap() - && is_wild_card_elems_clause - { - *checked_index += 1; - } - - let mut next_clause_props = ClauseProperties { - clause_var_name: props.clause_var_name.clone(), - complex_clause: false, - needs_constr_var: false, - original_subject_name: props.original_subject_name.clone(), - final_clause: false, - specific_clause: SpecificClause::ListClause { - defined_tails_index: *defined_tails_index, - defined_tails: defined_tails.clone(), - checked_index: *checked_index, - }, - }; - - let (_, clause_assign) = - self.clause_pattern(&clause.pattern, subject_tipo, props, clause_then); - - let complex_clause = props.complex_clause; - - if current_checked_index < elements_len.try_into().unwrap() - || next_tail_name.is_some() - { - AirTree::list_clause( - tail_name, - subject_tipo.clone(), - clause_assign, - self.handle_each_clause( - rest_clauses, - final_clause, - subject_tipo, - &mut next_clause_props, - module_name, - ), - next_tail_name, - complex_clause, - ) - } else { - AirTree::wrap_clause( - clause_assign, - self.handle_each_clause( - rest_clauses, - final_clause, - subject_tipo, - &mut next_clause_props, - module_name, - ), - ) - } - } - SpecificClause::TupleClause { - defined_tuple_indices, - } => { - let current_defined_indices = defined_tuple_indices.clone(); - - let (_, pattern_assigns) = - self.clause_pattern(&clause.pattern, subject_tipo, props, clause_then); - - let ClauseProperties { - specific_clause: - SpecificClause::TupleClause { - defined_tuple_indices, - }, - .. - } = props - else { - unreachable!() - }; - - let new_defined_indices: IndexSet<(usize, String)> = defined_tuple_indices - .difference(¤t_defined_indices) - .cloned() - .collect(); - - let mut next_clause_props = ClauseProperties { - clause_var_name: props.clause_var_name.clone(), - complex_clause: false, - needs_constr_var: false, - original_subject_name: props.original_subject_name.clone(), - final_clause: false, - specific_clause: SpecificClause::TupleClause { - defined_tuple_indices: defined_tuple_indices.clone(), - }, - }; - - if new_defined_indices.is_empty() { - AirTree::wrap_clause( - pattern_assigns, - self.handle_each_clause( - rest_clauses, - final_clause, - subject_tipo, - &mut next_clause_props, - module_name, - ), - ) - } else { - AirTree::tuple_clause( - &props.original_subject_name, - subject_tipo.clone(), - new_defined_indices, - current_defined_indices, - pattern_assigns, - self.handle_each_clause( - rest_clauses, - final_clause, - subject_tipo, - &mut next_clause_props, - module_name, - ), - props.complex_clause, - ) - } - } - SpecificClause::PairClause => { - let (_, pattern_assigns) = - self.clause_pattern(&clause.pattern, subject_tipo, props, clause_then); - - let mut next_clause_props = ClauseProperties::init( - subject_tipo, - props.clause_var_name.clone(), - props.original_subject_name.clone(), - ); - - AirTree::wrap_clause( - pattern_assigns, - self.handle_each_clause( - rest_clauses, - final_clause, - subject_tipo, - &mut next_clause_props, - module_name, - ), - ) - } - }; - - pop_pattern(&mut self.interner, &clause.pattern); - - tree - } else { - // handle final_clause - props.final_clause = true; - - introduce_pattern(&mut self.interner, &final_clause.pattern); - - let clause_then = self.build(&final_clause.then, module_name, &[]); - - let (condition, assignments) = - self.clause_pattern(&final_clause.pattern, subject_tipo, props, clause_then); - - pop_pattern(&mut self.interner, &final_clause.pattern); - - AirTree::finally(condition, assignments) - } - } - - pub fn clause_pattern( - &self, - pattern: &Pattern>, - subject_tipo: &Rc, - props: &mut ClauseProperties, - then: AirTree, - // We return condition and then assignments sequence - ) -> (AirTree, AirTree) { - match pattern { - Pattern::Int { value, .. } => { - assert!(!props.final_clause); - (AirTree::int(value), then) - } - Pattern::ByteArray { value, .. } => { - assert!(!props.final_clause); - (AirTree::byte_array(value.clone()), then) - } - Pattern::Var { name, .. } => { - let name = self.interner.lookup_interned(name); - - ( - AirTree::void(), - AirTree::let_assignment( - name, - AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), - then, - ), - ) - } - Pattern::Assign { name, pattern, .. } => { - let name = self.interner.lookup_interned(name); - let (inner_condition, inner_assignment) = - self.clause_pattern(pattern, subject_tipo, props, then); - - ( - inner_condition, - AirTree::let_assignment( - name, - AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), - inner_assignment, - ), - ) - } - Pattern::Discard { .. } => (AirTree::void(), then), - Pattern::List { elements, tail, .. } => { - let ClauseProperties { - specific_clause: SpecificClause::ListClause { defined_tails, .. }, - complex_clause, - .. - } = props - else { - unreachable!() - }; - - let list_elem_types = subject_tipo.get_inner_types(); - - let list_elem_type = list_elem_types - .first() - .unwrap_or_else(|| unreachable!("No list element type?")); - - let defined_tails = defined_tails.clone(); - - let mut elems = vec![]; - - let mut list_tail = None; - - let elems_then = tail.iter().rfold(then, |inner_then, elem| { - assert!(!elements.is_empty()); - let tail = defined_tails.get(elements.len() - 1); - let elem_name = match elem.as_ref() { - Pattern::Discard { .. } => DISCARDED.to_string(), - _ => format!( - "tail_span_{}_{}", - elem.location().start, - elem.location().end - ), - }; - - let mut elem_props = ClauseProperties::init_inner( - subject_tipo, - elem_name.clone(), - elem_name.clone(), - props.final_clause, - ); - - if elem_name != DISCARDED && !defined_tails.is_empty() { - list_tail = Some((tail.unwrap().to_string(), elem_name.to_string())); - } - - let inner_then = if elem_name != DISCARDED { - self.nested_clause_condition( - elem, - subject_tipo, - &mut elem_props, - inner_then, - ) - } else { - inner_then - }; - - if props.final_clause && defined_tails.is_empty() { - elems.push(elem_name); - } - - *complex_clause = *complex_clause || elem_props.complex_clause; - - inner_then - }); - - let elems_then = - elements - .iter() - .enumerate() - .rfold(elems_then, |elems_then, (index, elem)| { - // TODO: Turn 'Pattern' into another type instead of using strings and - // expecting a special magic string '_'. - let elem_name = match elem { - Pattern::Discard { .. } => DISCARDED.to_string(), - _ => format!( - "elem_{}_span_{}_{}", - index, - elem.location().start, - elem.location().end - ), - }; - - let mut elem_props = ClauseProperties::init_inner( - list_elem_type, - elem_name.clone(), - elem_name.clone(), - props.final_clause, - ); - - let elems_then = if elem_name != DISCARDED { - self.nested_clause_condition( - elem, - list_elem_type, - &mut elem_props, - elems_then, - ) - } else { - elems_then - }; - - elems.push(elem_name); - - *complex_clause = *complex_clause || elem_props.complex_clause; - - elems_then - }); - - elems.reverse(); - - // This case is really only possible with something like - // when_tuple_empty_lists - - let list_assign = if props.final_clause && defined_tails.is_empty() { - AirTree::list_access( - elems, - subject_tipo.clone(), - tail.is_some(), - AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), - // One special usage of list access here - // So for the msg we pass in empty string if tracing is on - // Since check_last_item is false this will never get added to the final uplc anyway - ExpectLevel::None, - elems_then, - AirTree::error(Type::void(), false), - ) - } else { - assert!(defined_tails.len() >= elems.len()); - - AirTree::list_expose( - elems - .into_iter() - .zip(defined_tails) - .filter(|(head, _)| head != DISCARDED) - .map(|(head, tail)| (tail, head)) - .collect_vec(), - list_tail, - subject_tipo.clone(), - elems_then, - ) - }; - - (AirTree::void(), list_assign) - } - - Pattern::Pair { fst, snd, .. } => { - let items_type = subject_tipo.get_inner_types(); - - let mut name_index_assigns = vec![]; - - let next_then = - [fst, snd] - .iter() - .enumerate() - .rfold(then, |inner_then, (index, element)| { - let elem_name = match element.as_ref() { - Pattern::Discard { .. } => None, - _ => Some(format!( - "pair_index_{}_span_{}_{}", - index, - element.location().start, - element.location().end - )), - }; - - let mut pair_props = ClauseProperties::init_inner( - &items_type[index], - elem_name.clone().unwrap_or_else(|| DISCARDED.to_string()), - elem_name.clone().unwrap_or_else(|| DISCARDED.to_string()), - props.final_clause, - ); - - let elem = if elem_name.is_some() { - self.nested_clause_condition( - element, - &items_type[index], - &mut pair_props, - inner_then, - ) - } else { - inner_then - }; - - props.complex_clause = - props.complex_clause || pair_props.complex_clause; - - name_index_assigns.push((elem_name, index)); - - elem - }); - - name_index_assigns.reverse(); - - let field_assign = if name_index_assigns.iter().all(|s| s.0.is_none()) { - next_then - } else { - AirTree::pair_access( - name_index_assigns[0].0.clone(), - name_index_assigns[1].0.clone(), - subject_tipo.clone(), - AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()), - false, - next_then, - AirTree::error(Type::void(), false), - ) - }; - - (AirTree::void(), field_assign) - } - - Pattern::Constructor { name, .. } if subject_tipo.is_bool() => { - (AirTree::bool(name == "True"), then) - } - - Pattern::Constructor { - name, - arguments, - constructor, - tipo: function_tipo, - .. - } => { - assert!( - matches!(function_tipo.as_ref().clone(), Type::Fn { .. }) - || matches!(function_tipo.as_ref().clone(), Type::App { .. }) - ); - let data_type = lookup_data_type_by_tipo(&self.data_types, subject_tipo) - .unwrap_or_else(|| { - unreachable!( - "Code Gen should have the definition for this constructor {}", - name - ) - }); - - assert!( - !data_type.constructors.is_empty(), - "{}\n{constructor:#?}\n{data_type:#?}", - subject_tipo.to_pretty(0) - ); - - let (constr_index, _) = data_type - .constructors - .iter() - .enumerate() - .find(|(_, dt)| &dt.name == name) - .unwrap(); - - let field_map = match constructor { - PatternConstructor::Record { field_map, .. } => field_map.clone(), - }; - - let mut type_map: IndexMap> = IndexMap::new(); - - for (index, arg) in function_tipo.arg_types().unwrap().iter().enumerate() { - let field_type = arg.clone(); - type_map.insert(index, field_type); - } - - let mut fields = vec![]; - - let next_then = - arguments - .iter() - .enumerate() - .rfold(then, |inner_then, (index, arg)| { - let label = arg.label.clone().unwrap_or_default(); - - let field_index = if let Some(field_map) = &field_map { - *field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&index) - } else { - index - }; - - let field_name = match &arg.value { - Pattern::Discard { .. } => DISCARDED.to_string(), - _ => format!( - "field_{}_span_{}_{}", - field_index, - arg.value.location().start, - arg.value.location().end - ), - }; - - let arg_type = type_map.get(&field_index).unwrap_or_else(|| { - unreachable!( - "Missing type for field {} of constr {}", - field_index, name - ) - }); - - let mut field_props = ClauseProperties::init_inner( - arg_type, - field_name.clone(), - field_name.clone(), - props.final_clause, - ); - - let statement = if field_name != DISCARDED { - self.nested_clause_condition( - &arg.value, - arg_type, - &mut field_props, - inner_then, - ) - } else { - inner_then - }; - - props.complex_clause = - props.complex_clause || field_props.complex_clause; - - fields.push((field_index, field_name, arg_type.clone())); - - statement - }); - - fields.reverse(); - - let field_assign = if check_replaceable_opaque_type(subject_tipo, &self.data_types) - { - AirTree::let_assignment( - &fields[0].1, - AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()), - next_then, - ) - } else if fields.iter().all(|s| s.1 == DISCARDED) { - next_then - } else { - AirTree::fields_expose( - fields, - AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()), - false, - next_then, - AirTree::error(Type::void(), false), - ) - }; - - (AirTree::int(constr_index), field_assign) - } - Pattern::Tuple { elems, .. } => { - let items_type = subject_tipo.get_inner_types(); - - let mut name_index_assigns = vec![]; - - let next_then = - elems - .iter() - .enumerate() - .rfold(then, |inner_then, (index, element)| { - let elem_name = match element { - Pattern::Discard { .. } => DISCARDED.to_string(), - _ => format!( - "tuple_index_{}_span_{}_{}", - index, - element.location().start, - element.location().end - ), - }; - - let mut tuple_props = ClauseProperties::init_inner( - &items_type[index], - elem_name.clone(), - elem_name.clone(), - props.final_clause, - ); - - let elem = if elem_name != DISCARDED { - self.nested_clause_condition( - element, - &items_type[index], - &mut tuple_props, - inner_then, - ) - } else { - inner_then - }; - - props.complex_clause = - props.complex_clause || tuple_props.complex_clause; - - name_index_assigns.push((elem_name, index)); - - elem - }); - - name_index_assigns.reverse(); - - let mut defined_indices = match props.clone() { - ClauseProperties { - specific_clause: - SpecificClause::TupleClause { - defined_tuple_indices, - }, - .. - } => defined_tuple_indices, - _ => unreachable!(), - }; - - let mut previous_defined_names = vec![]; - let mut names_to_define = vec![]; - name_index_assigns.iter().for_each(|(name, index)| { - if let Some((index, prev_name)) = defined_indices - .iter() - .find(|(defined_index, _nm)| defined_index == index) - { - previous_defined_names.push((*index, prev_name.clone(), name.clone())); - } else if name != DISCARDED { - assert!(defined_indices.insert((*index, name.clone()))); - names_to_define.push((*index, name.clone())); - } else { - names_to_define.push((*index, name.clone())); - } - }); - - let tuple_name_assigns = previous_defined_names.into_iter().rev().fold( - next_then, - |inner_then, (index, prev_name, name)| { - AirTree::let_assignment( - name, - AirTree::local_var(prev_name, items_type[index].clone()), - inner_then, - ) - }, - ); - - match props { - ClauseProperties { - specific_clause: - SpecificClause::TupleClause { - defined_tuple_indices, - }, - .. - } => { - *defined_tuple_indices = defined_indices; - } - _ => unreachable!(), - } - - if props.final_clause && !names_to_define.is_empty() { - names_to_define.sort_by(|(id1, _), (id2, _)| id1.cmp(id2)); - - let names = - names_to_define - .into_iter() - .fold(vec![], |mut names, (index, name)| { - while names.len() < index { - names.push(DISCARDED.to_string()); - } - names.push(name); - names - }); - - ( - AirTree::void(), - AirTree::tuple_access( - names, - subject_tipo.clone(), - AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), - false, - tuple_name_assigns, - AirTree::error(Type::void(), false), - ), - ) - } else { - (AirTree::void(), tuple_name_assigns) - } - } - } - } - - fn nested_clause_condition( - &self, - pattern: &Pattern>, - subject_tipo: &Rc, - props: &mut ClauseProperties, - then: AirTree, - ) -> AirTree { - if props.final_clause { - props.complex_clause = false; - let (_, assign) = self.clause_pattern(pattern, subject_tipo, props, then); - assign - } else { - match pattern { - Pattern::Int { value, .. } => { - props.complex_clause = true; - AirTree::clause_guard( - &props.original_subject_name, - AirTree::int(value), - Type::int(), - then, - ) - } - Pattern::ByteArray { value, .. } => { - props.complex_clause = true; - AirTree::clause_guard( - &props.original_subject_name, - AirTree::byte_array(value.clone()), - Type::byte_array(), - then, - ) - } - Pattern::Var { name, .. } => { - let name = self.interner.lookup_interned(name); - - AirTree::let_assignment( - name, - AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), - then, - ) - } - Pattern::Assign { name, pattern, .. } => { - let name = self.interner.lookup_interned(name); - - AirTree::let_assignment( - name, - AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), - self.nested_clause_condition(pattern, subject_tipo, props, then), - ) - } - Pattern::Discard { .. } => then, - Pattern::List { elements, tail, .. } => { - props.complex_clause = true; - let tail_name_base = "__tail".to_string(); - - if elements.is_empty() { - assert!( - tail.is_none(), - "Why do you have a [..] in a clause? Use a var." - ); - - AirTree::list_clause_guard( - &props.original_subject_name, - subject_tipo.clone(), - false, - None, - then, - ) - } else { - let ClauseProperties { - specific_clause: - SpecificClause::ListClause { - defined_tails_index: current_index, - defined_tails, - checked_index: _, - }, - .. - } = props - else { - unreachable!() - }; - - defined_tails.push(props.original_subject_name.clone()); - - for (index, _) in elements.iter().enumerate() { - let tail_name = format!("{tail_name_base}_{index}"); - - if elements.len() - 1 == index { - if tail.is_none() { - *current_index += 1; - defined_tails.push(tail_name); - } - } else { - *current_index += 1; - defined_tails.push(tail_name); - }; - } - - let (_, assigns) = self.clause_pattern(pattern, subject_tipo, props, then); - - elements - .iter() - .enumerate() - .rfold(assigns, |then, (index, _)| { - let prev_tail_name = if index == 0 { - props.original_subject_name.clone() - } else { - format!("{}_{}", tail_name_base, index - 1) - }; - - let tail_name = format!("{tail_name_base}_{index}"); - if elements.len() - 1 == index { - if tail.is_some() { - AirTree::list_clause_guard( - prev_tail_name, - subject_tipo.clone(), - true, - None, - then, - ) - } else { - AirTree::list_clause_guard( - prev_tail_name, - subject_tipo.clone(), - true, - Some(tail_name.to_string()), - AirTree::list_clause_guard( - tail_name.to_string(), - subject_tipo.clone(), - false, - None, - then, - ), - ) - } - } else { - AirTree::list_clause_guard( - prev_tail_name, - subject_tipo.clone(), - true, - Some(tail_name.to_string()), - then, - ) - } - }) - } - } - - Pattern::Pair { .. } => { - let (_, assign) = self.clause_pattern(pattern, subject_tipo, props, then); - assign - } - - Pattern::Constructor { - name: constr_name, .. - } => { - if subject_tipo.is_bool() { - props.complex_clause = true; - AirTree::clause_guard( - &props.original_subject_name, - AirTree::bool(constr_name == "True"), - Type::bool(), - then, - ) - } else if subject_tipo.is_void() { - AirTree::clause_guard( - &props.original_subject_name, - AirTree::void(), - Type::void(), - then, - ) - } else { - let (cond, assign) = - self.clause_pattern(pattern, subject_tipo, props, then); - - let data_type = lookup_data_type_by_tipo(&self.data_types, subject_tipo) - .expect("Missing data type"); - - if data_type.constructors.len() == 1 { - assign - } else { - props.complex_clause = true; - AirTree::clause_guard( - &props.original_subject_name, - cond, - subject_tipo.clone(), - assign, - ) - } - } - } - Pattern::Tuple { .. } => { - let (_, assign) = self.clause_pattern(pattern, subject_tipo, props, then); - - let defined_indices = match &props.specific_clause { - SpecificClause::TupleClause { - defined_tuple_indices, - } => defined_tuple_indices.clone(), - _ => unreachable!(), - }; - - AirTree::tuple_clause_guard( - &props.original_subject_name, - subject_tipo.clone(), - defined_indices, - assign, - ) - } - } - } - } - fn hoist_functions_to_validator(&mut self, mut air_tree: AirTree) -> AirTree { let mut functions_to_hoist = IndexMap::new(); let mut used_functions = vec![]; @@ -5477,12 +4373,15 @@ impl<'a> CodeGenerator<'a> { let body = arg_stack.pop().unwrap(); // the next branch in the when expression + // Expected to be delayed let term = arg_stack.pop().unwrap(); + assert!(matches!(term, Term::Delay(_))); + let other_clauses = if complex_clause { Term::var("__other_clauses_delayed") } else { - term.clone().delay() + term.clone() }; let body = if tipo.is_bool() { diff --git a/crates/aiken-lang/src/gen_uplc/builder.rs b/crates/aiken-lang/src/gen_uplc/builder.rs index c6a9276b..97b4f2a3 100644 --- a/crates/aiken-lang/src/gen_uplc/builder.rs +++ b/crates/aiken-lang/src/gen_uplc/builder.rs @@ -6,17 +6,15 @@ use super::{ use crate::{ ast::{ DataTypeKey, FunctionAccessKey, Pattern, Span, TraceLevel, TypedArg, TypedAssignmentKind, - TypedClause, TypedDataType, TypedPattern, + TypedDataType, TypedPattern, }, - expr::TypedExpr, line_numbers::{LineColumn, LineNumbers}, tipo::{ - check_replaceable_opaque_type, convert_opaque_type, find_and_replace_generics, - lookup_data_type_by_tipo, PatternConstructor, Type, ValueConstructor, - ValueConstructorVariant, + check_replaceable_opaque_type, convert_opaque_type, find_and_replace_generics, Type, + ValueConstructor, ValueConstructorVariant, }, }; -use indexmap::{IndexMap, IndexSet}; +use indexmap::IndexMap; use itertools::{Itertools, Position}; use std::{ops::Deref, rc::Rc}; use uplc::{ @@ -74,129 +72,6 @@ pub struct AssignmentProperties { pub otherwise: Option, } -#[derive(Clone, Debug)] - -pub struct ClauseProperties { - pub clause_var_name: String, - pub complex_clause: bool, - pub needs_constr_var: bool, - pub original_subject_name: String, - pub final_clause: bool, - pub specific_clause: SpecificClause, -} -#[derive(Clone, Debug)] -pub enum SpecificClause { - ConstrClause, - ListClause { - defined_tails_index: i64, - defined_tails: Vec, - checked_index: i64, - }, - TupleClause { - defined_tuple_indices: IndexSet<(usize, String)>, - }, - PairClause, -} - -impl ClauseProperties { - pub fn init(t: &Rc, constr_var: String, subject_name: String) -> Self { - if t.is_list() { - ClauseProperties { - clause_var_name: constr_var, - complex_clause: false, - original_subject_name: subject_name.clone(), - final_clause: false, - needs_constr_var: false, - specific_clause: SpecificClause::ListClause { - defined_tails_index: 0, - defined_tails: vec![subject_name], - checked_index: -1, - }, - } - } else if t.is_tuple() { - ClauseProperties { - clause_var_name: constr_var, - complex_clause: false, - original_subject_name: subject_name, - needs_constr_var: false, - final_clause: false, - specific_clause: SpecificClause::TupleClause { - defined_tuple_indices: IndexSet::new(), - }, - } - } else if t.is_pair() { - ClauseProperties { - clause_var_name: constr_var, - complex_clause: false, - original_subject_name: subject_name, - needs_constr_var: false, - final_clause: false, - specific_clause: SpecificClause::PairClause, - } - } else { - ClauseProperties { - clause_var_name: constr_var, - complex_clause: false, - original_subject_name: subject_name, - needs_constr_var: false, - final_clause: false, - specific_clause: SpecificClause::ConstrClause, - } - } - } - - pub fn init_inner( - t: &Rc, - constr_var: String, - subject_name: String, - final_clause: bool, - ) -> Self { - if t.is_list() { - ClauseProperties { - clause_var_name: constr_var, - complex_clause: false, - original_subject_name: subject_name, - final_clause, - needs_constr_var: false, - specific_clause: SpecificClause::ListClause { - defined_tails_index: 0, - defined_tails: vec![], - checked_index: -1, - }, - } - } else if t.is_tuple() { - ClauseProperties { - clause_var_name: constr_var, - complex_clause: false, - original_subject_name: subject_name, - needs_constr_var: false, - final_clause, - specific_clause: SpecificClause::TupleClause { - defined_tuple_indices: IndexSet::new(), - }, - } - } else if t.is_pair() { - ClauseProperties { - clause_var_name: constr_var, - complex_clause: false, - original_subject_name: subject_name, - needs_constr_var: false, - final_clause, - specific_clause: SpecificClause::PairClause, - } - } else { - ClauseProperties { - clause_var_name: constr_var, - complex_clause: false, - original_subject_name: subject_name, - needs_constr_var: false, - final_clause, - specific_clause: SpecificClause::ConstrClause, - } - } - } -} - #[derive(Clone, Debug)] pub struct CodeGenSpecialFuncs { pub used_funcs: Vec, @@ -603,248 +478,6 @@ pub fn modify_cyclic_calls( }); } -pub fn pattern_has_conditions( - pattern: &TypedPattern, - data_types: &IndexMap<&DataTypeKey, &TypedDataType>, -) -> bool { - match pattern { - Pattern::List { .. } | Pattern::Int { .. } | Pattern::ByteArray { .. } => true, - Pattern::Tuple { elems, .. } => elems - .iter() - .any(|elem| pattern_has_conditions(elem, data_types)), - Pattern::Pair { fst, snd, .. } => { - pattern_has_conditions(fst, data_types) || pattern_has_conditions(snd, data_types) - } - Pattern::Constructor { - arguments, tipo, .. - } => { - let data_type = lookup_data_type_by_tipo(data_types, tipo) - .unwrap_or_else(|| panic!("Data type not found: {:#?}", tipo)); - - data_type.constructors.len() > 1 - || arguments - .iter() - .any(|arg| pattern_has_conditions(&arg.value, data_types)) - } - Pattern::Assign { pattern, .. } => pattern_has_conditions(pattern, data_types), - Pattern::Var { .. } | Pattern::Discard { .. } => false, - } -} - -// TODO: write some tests -pub fn rearrange_list_clauses( - clauses: Vec, - data_types: &IndexMap<&DataTypeKey, &TypedDataType>, -) -> Vec { - let mut sorted_clauses = clauses; - - // if we have a list sort clauses so we can plug holes for cases not covered by clauses - // Now we sort by elements + tail if possible and otherwise leave an index in place if var or discard - // This is a stable sort. i.e. matching elements amounts will remain in user given order. - sorted_clauses = sorted_clauses - .into_iter() - .enumerate() - .sorted_by(|(index1, clause1), (index2, clause2)| { - let mut clause_pattern1 = &clause1.pattern; - let mut clause_pattern2 = &clause2.pattern; - - if let Pattern::Assign { pattern, .. } = clause_pattern1 { - clause_pattern1 = pattern; - } - - if let Pattern::Assign { pattern, .. } = clause_pattern2 { - clause_pattern2 = pattern; - } - - let clause1_len = match clause_pattern1 { - Pattern::List { elements, tail, .. } => { - Some(elements.len() + usize::from(tail.is_some())) - } - _ => Some(100000), - }; - - let clause2_len = match clause_pattern2 { - Pattern::List { elements, tail, .. } => { - Some(elements.len() + usize::from(tail.is_some())) - } - _ => Some(100001), - }; - - if let Some(clause1_len) = clause1_len { - if let Some(clause2_len) = clause2_len { - return clause1_len.cmp(&clause2_len); - } - } - - index1.cmp(index2) - }) - .map(|(_, item)| item) - .collect_vec(); - - let mut final_clauses = sorted_clauses.clone(); - let mut holes_to_fill = vec![]; - let mut last_clause_index = 0; - let mut last_clause_set = false; - let mut wild_card_clause_elems = 0; - - // 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 = &|index: usize, last_clause: &TypedClause| match &last_clause.pattern { - Pattern::Var { .. } | Pattern::Discard { .. } => last_clause.clone().then, - _ => { - let tipo = last_clause.then.tipo(); - - TypedExpr::Trace { - location: Span::empty(), - tipo: tipo.clone(), - text: Box::new(TypedExpr::String { - location: Span::empty(), - tipo: Type::string(), - value: format!("Clause hole found for {index} elements."), - }), - then: Box::new(TypedExpr::ErrorTerm { - location: Span::empty(), - tipo, - }), - } - } - }; - - let last_clause = &sorted_clauses[sorted_clauses.len() - 1]; - let assign_plug_in_name = if let Pattern::Var { name, .. } = &last_clause.pattern { - Some(name) - } else { - None - }; - - for (index, clause) in sorted_clauses.iter().enumerate() { - if last_clause_set { - continue; - } - - let mut clause_pattern = &clause.pattern; - - if let Pattern::Assign { pattern, .. } = clause_pattern { - clause_pattern = pattern; - } - - assert!(matches!( - clause_pattern, - Pattern::List { .. } | Pattern::Var { .. } | Pattern::Discard { .. } - )); - - if let Pattern::List { elements, tail, .. } = clause_pattern { - // found a hole and now we plug it - while wild_card_clause_elems < elements.len() { - let mut discard_elems = vec![]; - - for _ in 0..wild_card_clause_elems { - discard_elems.push(Pattern::Discard { - name: "__fill".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 { - TypedClause { - location: Span::empty(), - pattern: Pattern::Assign { - name: name.clone(), - location: Span::empty(), - pattern: Pattern::List { - location: Span::empty(), - elements: discard_elems, - tail: None, - } - .into(), - }, - then: plug_in_then(wild_card_clause_elems, last_clause), - } - } else { - TypedClause { - location: Span::empty(), - pattern: Pattern::List { - location: Span::empty(), - elements: discard_elems, - tail: None, - }, - then: plug_in_then(wild_card_clause_elems, last_clause), - } - }; - - holes_to_fill.push((index, clause_to_fill)); - wild_card_clause_elems += 1; - } - - let mut is_wild_card_elems_clause = true; - - for element in elements.iter() { - is_wild_card_elems_clause = - is_wild_card_elems_clause && !pattern_has_conditions(element, data_types); - } - - if is_wild_card_elems_clause { - if wild_card_clause_elems < elements.len() + usize::from(tail.is_none()) { - wild_card_clause_elems += 1; - } - - if tail.is_some() && !elements.is_empty() { - last_clause_index = index; - last_clause_set = true; - } - } - } else if let Pattern::Var { .. } | Pattern::Discard { .. } = &clause.pattern { - last_clause_set = true; - last_clause_index = index; - } else { - unreachable!("Found a clause that is not a list or var or discard"); - } - - // 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 { - final_clauses.push(TypedClause { - location: Span::empty(), - pattern: Pattern::Discard { - name: "_".to_string(), - location: Span::empty(), - }, - then: plug_in_then(index + 1, last_clause), - }); - } - } - } - - // Encountered a tail so stop there with that as last clause - if last_clause_set { - for _ in 0..(sorted_clauses.len() - 1 - last_clause_index) { - final_clauses.pop(); - } - } - - // insert hole fillers into clauses - for (index, clause) in holes_to_fill.into_iter().rev() { - final_clauses.insert(index, clause); - } - assert!(final_clauses.len() > 1); - - final_clauses -} - -pub fn find_list_clause_or_default_first(clauses: &[TypedClause]) -> &TypedClause { - assert!(!clauses.is_empty()); - - clauses - .iter() - .find(|clause| match &clause.pattern { - Pattern::List { .. } => true, - Pattern::Assign { pattern, .. } if matches!(&**pattern, Pattern::List { .. }) => true, - _ => false, - }) - .unwrap_or(&clauses[0]) -} - pub fn known_data_to_type(term: Term, field_type: &Type) -> Term { let uplc_type = field_type.get_uplc_type(); @@ -936,7 +569,7 @@ pub fn softcast_data_to_type_otherwise( Some(UplcType::Data) => callback(Term::Var(val)), Some(UplcType::Bls12_381MlResult) => { - unreachable!("attempted to cast Data into Bls12_381MlResult ?!") + unreachable!("attempted to cast Data into Bls12_381MlResult?!") } Some(UplcType::Integer) => Term::choose_data_integer(val, callback, &otherwise_delayed), @@ -945,6 +578,7 @@ pub fn softcast_data_to_type_otherwise( Term::choose_data_bytearray(val, callback, &otherwise_delayed) } + // TODO: Discuss if we should change this to error since it's not error safe Some(UplcType::String) => Term::choose_data_bytearray( val, |bytes| callback(Term::decode_utf8().apply(bytes)), @@ -957,12 +591,14 @@ pub fn softcast_data_to_type_otherwise( Some(UplcType::List(_)) => Term::choose_data_list(val, callback, &otherwise_delayed), + // TODO: Discuss if we should change this to error since it's not error safe Some(UplcType::Bls12_381G1Element) => Term::choose_data_bytearray( val, |bytes| callback(Term::bls12_381_g1_uncompress().apply(bytes)), &otherwise_delayed, ), + // TODO: Discuss if we should change this to error since it's not error safe Some(UplcType::Bls12_381G2Element) => Term::choose_data_bytearray( val, |bytes| callback(Term::bls12_381_g2_uncompress().apply(bytes)), @@ -1501,91 +1137,6 @@ pub fn special_case_builtin( } } -pub fn wrap_as_multi_validator( - spend: Term, - mint: Term, - trace: TraceLevel, - spend_name: String, - mint_name: String, -) -> Term { - match trace { - TraceLevel::Silent | TraceLevel::Compact => Term::equals_integer() - .apply(Term::integer(0.into())) - .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("__second_arg"))) - .delayed_if_then_else( - mint.apply(Term::var("__first_arg")) - .apply(Term::var("__second_arg")), - spend.apply(Term::var("__first_arg")).apply( - Term::head_list() - .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("__second_arg"))), - ), - ) - .lambda("__second_arg") - .lambda("__first_arg"), - TraceLevel::Verbose => { - let trace_string = format!( - "Incorrect redeemer type for validator {}. - Double check you have wrapped the redeemer type as specified in your plutus.json", - spend_name - ); - - let error_term = Term::Error.delayed_trace(Term::var("__incorrect_second_arg_type")); - - let then_term = mint - .apply(Term::var("__first_arg")) - .apply(Term::var("__second_arg")); - - let else_term = spend.apply(Term::var("__first_arg")).apply( - Term::head_list() - .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("__second_arg"))), - ); - - Term::var("__second_arg") - .delayed_choose_data( - Term::equals_integer() - .apply(Term::integer(0.into())) - .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("__second_arg"))) - .delayed_if_then_else( - then_term.delayed_trace(Term::string(format!( - "Running 2 arg validator {}", - mint_name - ))), - else_term.delayed_trace(Term::string(format!( - "Running 3 arg validator {}", - spend_name - ))), - ), - error_term.clone(), - error_term.clone(), - error_term.clone(), - error_term, - ) - .lambda("__incorrect_second_arg_type") - .apply(Term::string(trace_string)) - .lambda("__second_arg") - .lambda("__first_arg") - } - } -} - -/// If the pattern is a list the return the number of elements and if it has a tail -/// Otherwise return None -pub fn get_list_elements_len_and_tail( - pattern: &Pattern>, -) -> Option<(usize, bool)> { - if let Pattern::List { elements, tail, .. } = &pattern { - Some((elements.len(), tail.is_some())) - } else if let Pattern::Assign { pattern, .. } = &pattern { - if let Pattern::List { elements, tail, .. } = pattern.as_ref() { - Some((elements.len(), tail.is_some())) - } else { - None - } - } else { - None - } -} - pub fn cast_validator_args( term: Term, arguments: &[TypedArg], diff --git a/crates/aiken-lang/src/gen_uplc/stick_break_set.rs b/crates/aiken-lang/src/gen_uplc/stick_break_set.rs index 4cc537d0..7366a25a 100644 --- a/crates/aiken-lang/src/gen_uplc/stick_break_set.rs +++ b/crates/aiken-lang/src/gen_uplc/stick_break_set.rs @@ -10,7 +10,7 @@ use super::{ tree::AirTree, }; -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum Builtin { HeadList(Rc), TailList, @@ -43,7 +43,7 @@ impl Builtin { vec![arg], ), Builtin::UnConstr => AirTree::builtin( - DefaultFunction::TailList, + DefaultFunction::UnConstrData, Type::pair(Type::int(), Type::list(Type::data())), vec![arg], ), @@ -77,7 +77,7 @@ impl ToString for Builtin { } } -#[derive(PartialEq, Eq, Clone)] +#[derive(PartialEq, Eq, Clone, Debug)] pub struct Builtins { pub vec: Vec, } @@ -204,7 +204,22 @@ pub struct TreeNode { children: Vec, } -impl TreeNode {} +impl TreeNode { + fn diff_union_builtins(&mut self, builtins: Builtins) -> Builtins { + if let Some((first, rest)) = builtins.vec.split_first() { + if let Some(item) = self.children.iter_mut().find(|item| first == &item.node) { + item.diff_union_builtins(Builtins { vec: rest.to_vec() }) + } else { + self.children + .extend(TreeSet::new_from_builtins(builtins.clone()).children); + + builtins + } + } else { + builtins + } + } +} impl TreeSet { pub fn new() -> Self { @@ -231,9 +246,9 @@ impl TreeSet { } pub fn diff_union_builtins(&mut self, builtins: Builtins) -> Builtins { - if let Some((first, _rest)) = builtins.vec.split_first() { - if self.children.iter().any(|item| first == &item.node) { - todo!() + if let Some((first, rest)) = builtins.vec.split_first() { + if let Some(item) = self.children.iter_mut().find(|item| first == &item.node) { + item.diff_union_builtins(Builtins { vec: rest.to_vec() }) } else { self.children .extend(TreeSet::new_from_builtins(builtins.clone()).children);