diff --git a/crates/aiken-lang/src/gen_uplc.rs b/crates/aiken-lang/src/gen_uplc.rs index d1523434c..e387b5c22 100644 --- a/crates/aiken-lang/src/gen_uplc.rs +++ b/crates/aiken-lang/src/gen_uplc.rs @@ -131,8 +131,9 @@ impl<'a> CodeGenerator<'a> { .. }: &TypedValidator, module_name: &String, + context: &[TypedExpr], ) -> Program { - let mut air_tree_fun = self.build(&fun.body, module_name); + let mut air_tree_fun = self.build(&fun.body, module_name, context); air_tree_fun = wrap_validator_condition(air_tree_fun, self.tracing); @@ -143,7 +144,7 @@ impl<'a> CodeGenerator<'a> { validator_args_tree = AirTree::no_op().hoist_over(validator_args_tree); - let full_tree = self.hoist_functions_to_validator(validator_args_tree); + let full_tree = self.hoist_functions_to_validator(validator_args_tree, context); // optimizations on air tree @@ -154,7 +155,7 @@ impl<'a> CodeGenerator<'a> { if let Some(other) = other_fun { self.reset(false); - let mut air_tree_fun_other = self.build(&other.body, module_name); + let mut air_tree_fun_other = self.build(&other.body, module_name, context); air_tree_fun_other = wrap_validator_condition(air_tree_fun_other, self.tracing); @@ -168,7 +169,8 @@ impl<'a> CodeGenerator<'a> { validator_args_tree_other = AirTree::no_op().hoist_over(validator_args_tree_other); - let full_tree_other = self.hoist_functions_to_validator(validator_args_tree_other); + let full_tree_other = + self.hoist_functions_to_validator(validator_args_tree_other, context); // optimizations on air tree @@ -199,11 +201,11 @@ impl<'a> CodeGenerator<'a> { } pub fn generate_test(&mut self, test_body: &TypedExpr, module_name: &String) -> Program { - let mut air_tree = self.build(test_body, module_name); + let mut air_tree = self.build(test_body, module_name, &[]); air_tree = AirTree::no_op().hoist_over(air_tree); - let full_tree = self.hoist_functions_to_validator(air_tree); + let full_tree = self.hoist_functions_to_validator(air_tree, &[]); // optimizations on air tree let full_vec = full_tree.to_vec(); @@ -236,30 +238,21 @@ impl<'a> CodeGenerator<'a> { program } - fn build(&mut self, body: &TypedExpr, module_build_name: &String) -> AirTree { + fn build( + &mut self, + body: &TypedExpr, + module_build_name: &String, + context: &[TypedExpr], + ) -> AirTree { match body { TypedExpr::UInt { value, .. } => AirTree::int(value), TypedExpr::String { value, .. } => AirTree::string(value), TypedExpr::ByteArray { bytes, .. } => AirTree::byte_array(bytes.clone()), TypedExpr::Sequence { expressions, .. } | TypedExpr::Pipeline { expressions, .. } => { - let mut expressions = expressions.clone(); - - assert!( - !expressions.is_empty(), - "Sequence or Pipeline should have at least one expression" - ); - - let mut last_exp = self.build( - &expressions.pop().unwrap_or_else(|| unreachable!()), - module_build_name, - ); - - while let Some(expression) = expressions.pop() { - let exp_tree = self.build(&expression, module_build_name); - - last_exp = exp_tree.hoist_over(last_exp); - } - last_exp + let (expr, dangling_expressions) = expressions + .split_first() + .expect("Sequence or Pipeline should have at least one expression"); + self.build(expr, module_build_name, dangling_expressions) } TypedExpr::Var { @@ -273,7 +266,7 @@ impl<'a> CodeGenerator<'a> { args.iter() .map(|arg| arg.arg_name.get_variable_name().unwrap_or("_").to_string()) .collect_vec(), - self.build(body, module_build_name), + self.build(body, module_build_name, context), ), TypedExpr::List { @@ -284,11 +277,11 @@ impl<'a> CodeGenerator<'a> { } => AirTree::list( elements .iter() - .map(|elem| self.build(elem, module_build_name)) + .map(|elem| self.build(elem, module_build_name, context)) .collect_vec(), tipo.clone(), tail.as_ref() - .map(|tail| self.build(tail, module_build_name)), + .map(|tail| self.build(tail, module_build_name, context)), ), TypedExpr::Call { @@ -331,11 +324,11 @@ impl<'a> CodeGenerator<'a> { .map(|(arg, tipo)| { if tipo.is_data() { AirTree::cast_to_data( - self.build(&arg.value, module_build_name), + self.build(&arg.value, module_build_name, context), arg.value.tipo(), ) } else { - self.build(&arg.value, module_build_name) + self.build(&arg.value, module_build_name, context) } }) .collect_vec(); @@ -362,8 +355,7 @@ impl<'a> CodeGenerator<'a> { .iter() .zip(fun_arg_types) .map(|(arg, arg_tipo)| { - let mut arg_val = self.build(&arg.value, module_build_name); - + let mut arg_val = self.build(&arg.value, module_build_name, context); if arg_tipo.is_data() && !arg.value.tipo().is_data() { arg_val = AirTree::cast_to_data(arg_val, arg.value.tipo()) } @@ -375,7 +367,7 @@ impl<'a> CodeGenerator<'a> { AirTree::builtin(*func, tipo.clone(), func_args) } else { AirTree::call( - self.build(fun.as_ref(), module_build_name), + self.build(fun.as_ref(), module_build_name, context), tipo.clone(), func_args, ) @@ -405,7 +397,7 @@ impl<'a> CodeGenerator<'a> { .iter() .zip(fun_arg_types) .map(|(arg, arg_tipo)| { - let mut arg_val = self.build(&arg.value, module_build_name); + let mut arg_val = self.build(&arg.value, module_build_name, context); if arg_tipo.is_data() && !arg.value.tipo().is_data() { arg_val = AirTree::cast_to_data(arg_val, arg.value.tipo()) @@ -418,7 +410,7 @@ impl<'a> CodeGenerator<'a> { AirTree::builtin(*func, tipo.clone(), func_args) } else { AirTree::call( - self.build(fun.as_ref(), module_build_name), + self.build(fun.as_ref(), module_build_name, context), tipo.clone(), func_args, ) @@ -436,8 +428,7 @@ impl<'a> CodeGenerator<'a> { .iter() .zip(fun_arg_types) .map(|(arg, arg_tipo)| { - let mut arg_val = self.build(&arg.value, module_build_name); - + let mut arg_val = self.build(&arg.value, module_build_name, context); if arg_tipo.is_data() && !arg.value.tipo().is_data() { arg_val = AirTree::cast_to_data(arg_val, arg.value.tipo()) } @@ -446,7 +437,7 @@ impl<'a> CodeGenerator<'a> { .collect_vec(); AirTree::call( - self.build(fun.as_ref(), module_build_name), + self.build(fun.as_ref(), module_build_name, context), tipo.clone(), func_args, ) @@ -461,8 +452,8 @@ impl<'a> CodeGenerator<'a> { } => AirTree::binop( *name, tipo.clone(), - self.build(left, module_build_name), - self.build(right, module_build_name), + self.build(left, module_build_name, context), + self.build(right, module_build_name, context), left.tipo(), ), @@ -476,7 +467,7 @@ impl<'a> CodeGenerator<'a> { } => { let replaced_type = convert_opaque_type(tipo, &self.data_types); - let air_value = self.build(value, module_build_name); + let air_value = self.build(value, module_build_name, context); let msg_func = match self.tracing { TraceLevel::Silent => None, @@ -512,9 +503,16 @@ impl<'a> CodeGenerator<'a> { } }; + let (then, context) = context + .split_first() + .expect("found an assignment without a next expression?"); + + let then = self.build(then, module_build_name, context); + self.assignment( pattern, air_value, + then, &replaced_type, AssignmentProperties { value_type: value.tipo(), @@ -529,9 +527,9 @@ impl<'a> CodeGenerator<'a> { TypedExpr::Trace { tipo, then, text, .. } => AirTree::trace( - self.build(text, module_build_name), + self.build(text, module_build_name, context), tipo.clone(), - self.build(then, module_build_name), + self.build(then, module_build_name, context), ), TypedExpr::When { @@ -544,18 +542,22 @@ impl<'a> CodeGenerator<'a> { if clauses.is_empty() { unreachable!("We should have one clause at least") + // TODO: This whole branch can _probably_ be removed, if handle_each_clause + // works fine with an empty clauses list. This is orthogonal to the + // current refactoring so not changing it now. } else if clauses.len() == 1 { let last_clause = clauses.pop().unwrap(); - let clause_then = self.build(&last_clause.then, module_build_name); + let clause_then = self.build(&last_clause.then, module_build_name, context); let subject_type = subject.tipo(); - let subject_val = self.build(subject, module_build_name); + let subject_val = self.build(subject, module_build_name, context); - let assignment = self.assignment( + self.assignment( &last_clause.pattern, subject_val, + clause_then, &subject_type, AssignmentProperties { value_type: subject.tipo(), @@ -564,9 +566,7 @@ impl<'a> CodeGenerator<'a> { full_check: false, msg_func: None, }, - ); - - assignment.hoist_over(clause_then) + ) } else { clauses = if subject.tipo().is_list() { rearrange_list_clauses(clauses, &self.data_types) @@ -598,11 +598,7 @@ impl<'a> CodeGenerator<'a> { subject_name.clone(), ), module_build_name, - ); - - let constr_assign = AirTree::let_assignment( - &constr_var, - self.build(subject, module_build_name), + context, ); let when_assign = AirTree::when( @@ -613,7 +609,11 @@ impl<'a> CodeGenerator<'a> { clauses, ); - constr_assign.hoist_over(when_assign) + AirTree::let_assignment( + &constr_var, + self.build(subject, module_build_name, context), + when_assign, + ) } } @@ -627,13 +627,13 @@ impl<'a> CodeGenerator<'a> { .iter() .map(|branch| { ( - self.build(&branch.condition, module_build_name), - self.build(&branch.body, module_build_name), + self.build(&branch.condition, module_build_name, context), + self.build(&branch.body, module_build_name, context), ) }) .collect_vec(), tipo.clone(), - self.build(final_else, module_build_name), + self.build(final_else, module_build_name, context), ), TypedExpr::RecordAccess { @@ -643,7 +643,7 @@ impl<'a> CodeGenerator<'a> { .. } => { if check_replaceable_opaque_type(&record.tipo(), &self.data_types) { - self.build(record, module_build_name) + self.build(record, module_build_name, context) } else { let function_name = format!("__access_index_{}", *index); @@ -673,7 +673,7 @@ impl<'a> CodeGenerator<'a> { self.special_functions .use_function_tree(CONSTR_FIELDS_EXPOSER.to_string()), list(data()), - vec![self.build(record, module_build_name)], + vec![self.build(record, module_build_name, context)], ); AirTree::index_access(function_name, tipo.clone(), list_of_fields) @@ -746,7 +746,7 @@ impl<'a> CodeGenerator<'a> { TypedExpr::Tuple { tipo, elems, .. } => AirTree::tuple( elems .iter() - .map(|elem| self.build(elem, module_build_name)) + .map(|elem| self.build(elem, module_build_name, context)) .collect_vec(), tipo.clone(), ), @@ -755,7 +755,11 @@ impl<'a> CodeGenerator<'a> { index, tuple, tipo, .. } => { if tuple.tipo().is_2_tuple() { - AirTree::pair_index(*index, tipo.clone(), self.build(tuple, module_build_name)) + AirTree::pair_index( + *index, + tipo.clone(), + self.build(tuple, module_build_name, context), + ) } else { let function_name = format!("__access_index_{}", *index); @@ -784,7 +788,7 @@ impl<'a> CodeGenerator<'a> { AirTree::index_access( function_name, tipo.clone(), - self.build(tuple, module_build_name), + self.build(tuple, module_build_name, context), ) } } @@ -803,7 +807,7 @@ impl<'a> CodeGenerator<'a> { .iter() .sorted_by(|arg1, arg2| arg1.index.cmp(&arg2.index)) { - let arg_val = self.build(&arg.value, module_build_name); + let arg_val = self.build(&arg.value, module_build_name, context); if arg.index > highest_index { highest_index = arg.index; @@ -817,13 +821,12 @@ impl<'a> CodeGenerator<'a> { index_types, highest_index, tipo.clone(), - self.build(spread, module_build_name), + self.build(spread, module_build_name, context), update_args, ) } - TypedExpr::UnOp { value, op, .. } => { - AirTree::unop(*op, self.build(value, module_build_name)) + AirTree::unop(*op, self.build(value, module_build_name, context)) } TypedExpr::CurvePoint { point, .. } => AirTree::curve(*point.as_ref()), } @@ -833,6 +836,7 @@ impl<'a> CodeGenerator<'a> { &mut self, pattern: &TypedPattern, mut value: AirTree, + then: AirTree, tipo: &Rc, props: AssignmentProperties, ) -> AirTree { @@ -868,8 +872,6 @@ impl<'a> CodeGenerator<'a> { expected_int, location.start, location.end ); - let assignment = AirTree::let_assignment(&name, value); - let expect = AirTree::binop( BinOp::Eq, bool(), @@ -878,20 +880,21 @@ impl<'a> CodeGenerator<'a> { int(), ); - AirTree::assert_bool(true, assignment.hoist_over(expect), props.msg_func.clone()) + let expr = AirTree::let_assignment(&name, value, expect); + + AirTree::assert_bool(true, expr, props.msg_func.clone()) } + Pattern::Var { name, .. } => { if props.full_check { let mut index_map = IndexMap::new(); let non_opaque_tipo = convert_opaque_type(tipo, &self.data_types); - let assignment = AirTree::let_assignment(name, value); - let val = AirTree::local_var(name, tipo.clone()); if non_opaque_tipo.is_primitive() { - assignment + AirTree::let_assignment(name, value, then) } else { let expect = self.expect_type_assign( &non_opaque_tipo, @@ -901,23 +904,23 @@ impl<'a> CodeGenerator<'a> { props.msg_func, ); - let assign_expect = AirTree::let_assignment("_", expect); - - let sequence = vec![assignment, assign_expect]; + let assign_expect = AirTree::let_assignment("_", expect, then); - AirTree::UnhoistedSequence(sequence) + AirTree::let_assignment(name, value, assign_expect) } } else { - AirTree::let_assignment(name, value) + AirTree::let_assignment(name, value, then) } } Pattern::Assign { name, pattern, .. } => { - let inner_pattern = - self.assignment(pattern, AirTree::local_var(name, tipo.clone()), tipo, props); - - let assign = AirTree::let_assignment(name, value); - - AirTree::UnhoistedSequence(vec![assign, inner_pattern]) + let inner_pattern = self.assignment( + pattern, + AirTree::local_var(name, tipo.clone()), + then, + tipo, + props, + ); + AirTree::let_assignment(name, value, inner_pattern) } Pattern::Discard { name, .. } => { if props.full_check { @@ -926,12 +929,10 @@ impl<'a> CodeGenerator<'a> { let non_opaque_tipo = convert_opaque_type(tipo, &self.data_types); - let assignment = AirTree::let_assignment(name, value); - let val = AirTree::local_var(name, tipo.clone()); if non_opaque_tipo.is_primitive() { - assignment + AirTree::let_assignment(name, value, then) } else { let expect = self.expect_type_assign( &non_opaque_tipo, @@ -941,14 +942,12 @@ impl<'a> CodeGenerator<'a> { props.msg_func, ); - let assign_expect = AirTree::let_assignment("_", expect); + let assignment = AirTree::let_assignment("_", expect, then); - let sequence = vec![assignment, assign_expect]; - - AirTree::UnhoistedSequence(sequence) + AirTree::let_assignment(name, value, assignment) } } else if !props.remove_unused { - AirTree::let_assignment(name, value) + AirTree::let_assignment(name, value, then) } else { AirTree::no_op() } @@ -963,10 +962,58 @@ impl<'a> CodeGenerator<'a> { .first() .unwrap_or_else(|| unreachable!("No list element type?")); - let mut elems = elements + let mut elems = vec![]; + + // If Some then push tail onto elems + let then = match tail { + None => then, + Some(tail) => { + let tail_name = match tail.as_ref() { + Pattern::Var { name, .. } => name.to_string(), + Pattern::Assign { name, .. } => name.to_string(), + Pattern::Discard { name, .. } => { + if props.full_check { + format!("__discard_{}_tail", name) + } else { + "_".to_string() + } + } + _ => format!( + "tail_span_{}_{}", + tail.location().start, + tail.location().end + ), + }; + + let val = AirTree::local_var(&tail_name, tipo.clone()); + + elems.push(tail_name); + + if tail_name != "_" { + self.assignment( + tail, + val, + then, + tipo, + AssignmentProperties { + value_type: tipo.clone(), + kind: props.kind, + remove_unused: true, + full_check: props.full_check, + msg_func: props.msg_func.clone(), + }, + ) + } else { + then + } + } + }; + + let then = elements .iter() .enumerate() - .map(|(index, elem)| { + .rev() + .fold(then, |then, (index, elem)| { let elem_name = match elem { Pattern::Var { name, .. } => name.to_string(), Pattern::Assign { name, .. } => name.to_string(), @@ -987,10 +1034,13 @@ impl<'a> CodeGenerator<'a> { let val = AirTree::local_var(&elem_name, list_elem_type.clone()); - let assign = if elem_name != "_" { + elems.push(elem_name); + + if elem_name != "_" { self.assignment( elem, val, + then, list_elem_type, AssignmentProperties { value_type: list_elem_type.clone(), @@ -1001,55 +1051,11 @@ impl<'a> CodeGenerator<'a> { }, ) } else { - AirTree::no_op() - }; - - (elem_name, assign) - }) - .collect_vec(); - - // If Some then push tail onto elems - tail.iter().for_each(|tail| { - let tail_name = match tail.as_ref() { - Pattern::Var { name, .. } => name.to_string(), - Pattern::Assign { name, .. } => name.to_string(), - Pattern::Discard { name, .. } => { - if props.full_check { - format!("__discard_{}_tail", name) - } else { - "_".to_string() - } + then } - _ => format!( - "tail_span_{}_{}", - tail.location().start, - tail.location().end - ), - }; - - let val = AirTree::local_var(&tail_name, tipo.clone()); - - let assign = if tail_name != "_" { - self.assignment( - tail, - val, - tipo, - AssignmentProperties { - value_type: tipo.clone(), - kind: props.kind, - remove_unused: true, - full_check: props.full_check, - msg_func: props.msg_func.clone(), - }, - ) - } else { - AirTree::no_op() - }; - - elems.push((tail_name, assign)); - }); + }); - let names = elems.iter().map(|(name, _)| name.to_string()).collect_vec(); + let names = elems.iter().rev().cloned().collect(); let list_access = if elements.is_empty() { AirTree::list_empty(value, props.msg_func) @@ -1068,11 +1074,7 @@ impl<'a> CodeGenerator<'a> { ) }; - let mut sequence = vec![list_access]; - - sequence.append(&mut elems.into_iter().map(|(_, elem)| elem).collect_vec()); - - AirTree::UnhoistedSequence(sequence) + list_access.hoist_over(then) } Pattern::Constructor { arguments, @@ -1080,56 +1082,13 @@ impl<'a> CodeGenerator<'a> { tipo: constr_tipo, .. } => { - let mut sequence = vec![]; - if tipo.is_bool() { assert!(props.kind.is_expect()); AirTree::assert_bool(name == "True", value, props.msg_func) } else if tipo.is_void() { - AirTree::let_assignment("_", value) + AirTree::let_assignment("_", value, then) } else { - if props.kind.is_expect() { - let data_type = lookup_data_type_by_tipo(&self.data_types, tipo) - .unwrap_or_else(|| { - unreachable!("Failed to find definition for {}", name) - }); - - if data_type.constructors.len() > 1 || props.full_check { - let (index, _) = data_type - .constructors - .iter() - .enumerate() - .find(|(_, constr)| constr.name == *name) - .unwrap_or_else(|| { - panic!("Found constructor type {} with 0 constructors", name) - }); - - let constructor_name = format!( - "__constructor_{}_span_{}_{}", - name, - pattern.location().start, - pattern.location().end - ); - - // I'm consuming `value` here - let constructor_val = AirTree::let_assignment(&constructor_name, value); - - sequence.push(constructor_val); - - let assert_constr = AirTree::assert_constr_index( - index, - AirTree::local_var(&constructor_name, tipo.clone()), - props.msg_func.clone(), - ); - - sequence.push(assert_constr); - - //I'm reusing the `value` pointer - value = AirTree::local_var(constructor_name, tipo.clone()); - } - } - let field_map = field_map.clone(); let mut type_map: IndexMap> = IndexMap::new(); @@ -1147,92 +1106,134 @@ impl<'a> CodeGenerator<'a> { assert!(type_map.len() >= arguments.len()); - let fields = arguments - .iter() - .enumerate() - .map(|(index, arg)| { - let label = arg.label.clone().unwrap_or_default(); + let mut fields = vec![]; - 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 then = + arguments + .iter() + .enumerate() + .rev() + .fold(then, |then, (index, arg)| { + let label = arg.label.clone().unwrap_or_default(); - let field_name = match &arg.value { - Pattern::Var { name, .. } => name.to_string(), - Pattern::Assign { name, .. } => name.to_string(), - Pattern::Discard { name, .. } => { - if props.full_check { - format!("__discard_{}_{}", name, index) - } else { - "_".to_string() + 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::Var { name, .. } => name.to_string(), + Pattern::Assign { name, .. } => name.to_string(), + Pattern::Discard { name, .. } => { + if props.full_check { + format!("__discard_{}_{}", name, index) + } else { + "_".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 val = AirTree::local_var(field_name.to_string(), arg_type.clone()); + _ => format!( + "field_{}_span_{}_{}", + field_index, + arg.value.location().start, + arg.value.location().end + ), + }; - let assign = if field_name != "_" { - self.assignment( - &arg.value, - val, - arg_type, - AssignmentProperties { - value_type: arg_type.clone(), - kind: props.kind, - remove_unused: true, - full_check: props.full_check, - msg_func: props.msg_func.clone(), - }, - ) - } else { - AirTree::no_op() - }; + let arg_type = type_map.get(&field_index).unwrap_or_else(|| { + unreachable!( + "Missing type for field {} of constr {}", + field_index, name + ) + }); - (field_index, field_name, arg_type.clone(), assign) - }) - .collect_vec(); + let val = + AirTree::local_var(field_name.to_string(), arg_type.clone()); + + fields.push((field_index, field_name, arg_type.clone())); + + if field_name != "_" { + self.assignment( + &arg.value, + val, + then, + arg_type, + AssignmentProperties { + value_type: arg_type.clone(), + kind: props.kind, + remove_unused: true, + full_check: props.full_check, + msg_func: props.msg_func.clone(), + }, + ) + } else { + then + } + }); let indices = fields .iter() - .map(|(index, name, tipo, _)| (*index, name.to_string(), tipo.clone())) + .rev() + .map(|(index, name, tipo)| (*index, name.to_string(), tipo.clone())) .collect_vec(); // This `value` is either value param that was passed in or // local var - if check_replaceable_opaque_type(tipo, &self.data_types) { - sequence.push(AirTree::let_assignment(&indices[0].1, value)); + let constructor_name = format!( + "__constructor_{}_span_{}_{}", + name, + pattern.location().start, + pattern.location().end + ); + + let local_value = AirTree::local_var(constructor_name, tipo.clone()); + + let then = if check_replaceable_opaque_type(tipo, &self.data_types) { + AirTree::let_assignment(&indices[0].1, local_value, then) } else { let (is_expect, msg) = if props.full_check { (true, props.msg_func) } else { (false, None) }; - sequence.push(AirTree::fields_expose(indices, value, msg, is_expect)); - } + AirTree::fields_expose(indices, local_value, msg, is_expect) + .hoist_over(then) + }; - sequence.append( - &mut fields - .into_iter() - .map(|(_, _, _, field)| field) - .collect_vec(), - ); + // TODO: See if we can combine these two if-conditions; + // + // i.e. can we lift data_type assignment out of the first if? + let then = if props.kind.is_expect() { + let data_type = lookup_data_type_by_tipo(&self.data_types, tipo) + .unwrap_or_else(|| { + unreachable!("Failed to find definition for {}", name) + }); + + if data_type.constructors.len() > 1 || props.full_check { + let (index, _) = data_type + .constructors + .iter() + .enumerate() + .find(|(_, constr)| constr.name == *name) + .unwrap_or_else(|| { + panic!("Found constructor type {} with 0 constructors", name) + }); + + let assert_constr = AirTree::assert_constr_index( + index, + AirTree::local_var(&constructor_name, tipo.clone()), + props.msg_func.clone(), + ); + + assert_constr.hoist_over(then) + } else { + then + } + } else { + then + }; - AirTree::UnhoistedSequence(sequence) + AirTree::let_assignment(constructor_name, value, then) } } Pattern::Tuple { @@ -1240,8 +1241,6 @@ impl<'a> CodeGenerator<'a> { } => { let mut type_map: IndexMap> = IndexMap::new(); - let mut sequence = vec![]; - for (index, arg) in tipo.get_inner_types().iter().enumerate() { let field_type = arg.clone(); type_map.insert(index, field_type); @@ -1249,10 +1248,13 @@ impl<'a> CodeGenerator<'a> { assert!(type_map.len() == elems.len()); - let elems = elems + let mut fields = vec![]; + + let then = elems .iter() .enumerate() - .map(|(index, arg)| { + .rev() + .fold(then, |then, (index, arg)| { let tuple_name = match &arg { Pattern::Var { name, .. } => name.to_string(), Pattern::Assign { name, .. } => name.to_string(), @@ -1280,10 +1282,13 @@ impl<'a> CodeGenerator<'a> { let val = AirTree::local_var(tuple_name.to_string(), arg_type.clone()); - let assign = if "_" != tuple_name { + fields.push(tuple_name); + + if "_" != tuple_name { self.assignment( arg, val, + then, arg_type, AssignmentProperties { value_type: arg_type.clone(), @@ -1294,14 +1299,9 @@ impl<'a> CodeGenerator<'a> { }, ) } else { - AirTree::no_op() - }; - - (tuple_name, assign) - }) - .collect_vec(); - - let indices = elems.iter().map(|(name, _)| name.to_string()).collect_vec(); + then + } + }); let (is_expect, msg) = if props.full_check { (true, props.msg_func) @@ -1309,19 +1309,11 @@ impl<'a> CodeGenerator<'a> { (false, None) }; - // This `value` is either value param that was passed in or - // local var - sequence.push(AirTree::tuple_access( - indices, - tipo.clone(), - value, - msg, - is_expect, - )); - - sequence.append(&mut elems.into_iter().map(|(_, field)| field).collect_vec()); + // This `value` is either value param that was passed in or local var + let tuple_access = + AirTree::tuple_access(fields, tipo.clone(), value, msg, is_expect); - AirTree::UnhoistedSequence(sequence) + tuple_access.hoist_over(then) } } } @@ -1353,8 +1345,6 @@ impl<'a> CodeGenerator<'a> { let fst_name = format!("__pair_fst_span_{}_{}", location.start, location.end); let snd_name = format!("__pair_snd_span_{}_{}", location.start, location.end); - let assign = AirTree::let_assignment(&map_name, value); - let tuple_access = AirTree::tuple_access( vec![fst_name.clone(), snd_name.clone()], inner_list_type.clone(), @@ -1379,11 +1369,8 @@ impl<'a> CodeGenerator<'a> { msg_func.clone(), ); - let anon_func_body = AirTree::UnhoistedSequence(vec![ - tuple_access, - AirTree::let_assignment("_", expect_fst), - ]) - .hoist_over(expect_snd); + let anon_func_body = + tuple_access.hoist_over(AirTree::let_assignment("_", expect_fst, expect_snd)); let unwrap_function = AirTree::anon_func(vec![pair_name], anon_func_body); @@ -1426,7 +1413,7 @@ impl<'a> CodeGenerator<'a> { vec![AirTree::local_var(map_name, tipo.clone()), unwrap_function], ); - assign.hoist_over(func_call) + AirTree::let_assignment(&map_name, value, func_call) } else if tipo.is_list() { assert!(!tipo.get_inner_types().is_empty()); @@ -1438,8 +1425,6 @@ impl<'a> CodeGenerator<'a> { let list_name = format!("__list_span_{}_{}", location.start, location.end); let item_name = format!("__item_span_{}_{}", location.start, location.end); - let assign = AirTree::let_assignment(&list_name, value); - let expect_item = self.expect_type_assign( inner_list_type, AirTree::cast_from_data( @@ -1495,7 +1480,7 @@ impl<'a> CodeGenerator<'a> { vec![AirTree::local_var(list_name, tipo.clone()), unwrap_function], ); - assign.hoist_over(func_call) + AirTree::let_assignment(&list_name, value, func_call) } } else if tipo.is_2_tuple() { let tuple_inner_types = tipo.get_inner_types(); @@ -1507,8 +1492,6 @@ impl<'a> CodeGenerator<'a> { let fst_name = format!("__pair_fst_span_{}_{}", location.start, location.end); let snd_name = format!("__pair_snd_span_{}_{}", location.start, location.end); - let tuple_assign = AirTree::let_assignment(&pair_name, value); - let tuple_access = AirTree::tuple_access( vec![fst_name.clone(), snd_name.clone()], tipo.clone(), @@ -1533,24 +1516,23 @@ impl<'a> CodeGenerator<'a> { msg_func, ); - AirTree::UnhoistedSequence(vec![ - tuple_assign, - tuple_access, - AirTree::let_assignment("_", expect_fst), - ]) - .hoist_over(expect_snd) + AirTree::let_assignment( + &pair_name, + value, + tuple_access.hoist_over(AirTree::let_assignment("_", expect_fst, expect_snd)), + ) } else if tipo.is_tuple() { let tuple_inner_types = tipo.get_inner_types(); assert!(!tuple_inner_types.is_empty()); let tuple_name = format!("__tuple_span_{}_{}", location.start, location.end); - let tuple_assign = AirTree::let_assignment(&tuple_name, value); - let tuple_expect_items = tuple_inner_types - .iter() - .enumerate() - .map(|(index, arg)| { + let mut tuple_expect_items = vec![]; + + let then = tuple_inner_types.iter().enumerate().rev().fold( + AirTree::void(), + |then, (index, arg)| { let tuple_index_name = format!( "__tuple_index_{}_span_{}_{}", index, location.start, location.end @@ -1564,17 +1546,13 @@ impl<'a> CodeGenerator<'a> { msg_func.clone(), ); - ( - tuple_index_name, - AirTree::let_assignment("_", expect_tuple_item), - ) - }) - .collect_vec(); + tuple_expect_items.push(tuple_index_name); - let tuple_index_names = tuple_expect_items - .iter() - .map(|(name, _)| name.clone()) - .collect_vec(); + AirTree::let_assignment("_", expect_tuple_item, then) + }, + ); + + let tuple_index_names = tuple_expect_items.iter().rev().cloned().collect_vec(); let tuple_access = AirTree::tuple_access( tuple_index_names, @@ -1584,16 +1562,11 @@ impl<'a> CodeGenerator<'a> { true, ); - let mut tuple_expects = tuple_expect_items - .into_iter() - .map(|(_, item)| item) - .collect_vec(); - - let mut sequence = vec![tuple_assign, tuple_access]; + let tupl_access = tuple_access.hoist_over(then); - sequence.append(&mut tuple_expects); + AirTree::let_assignment(&tuple_name, value, tupl_access) - AirTree::UnhoistedSequence(sequence).hoist_over(AirTree::void()) + // Constructor } else { let data_type = lookup_data_type_by_tipo(&self.data_types, tipo).unwrap_or_else(|| { unreachable!("We need a data type definition for type {:#?}", tipo) @@ -1646,17 +1619,19 @@ impl<'a> CodeGenerator<'a> { let constr_clauses = data_type.constructors.iter().enumerate().rfold( error_term, |acc, (index, constr)| { - let constr_args = constr - .arguments - .iter() - .enumerate() - .map(|(index, arg)| { + let mut constr_args = vec![]; + + let constr_then = constr.arguments.iter().enumerate().rev().fold( + AirTree::void(), + |then, (index, arg)| { let arg_name = arg.label.clone().unwrap_or(format!("__field_{index}")); let arg_tipo = find_and_replace_generics(&arg.tipo, &mono_types); - let assign = AirTree::let_assignment( + constr_args.push((index, arg_name, arg_tipo)); + + AirTree::let_assignment( "_", self.expect_type_assign( &arg_tipo, @@ -1665,26 +1640,21 @@ impl<'a> CodeGenerator<'a> { location, msg_term.clone(), ), - ); - - (index, arg_name, arg_tipo, assign) - }) - .collect_vec(); + then, + ) + }, + ); let indices = constr_args .iter() - .map(|(index, arg_name, arg_tipo, _)| { + .rev() + .map(|(index, arg_name, arg_tipo)| { (*index, arg_name.clone(), (*arg_tipo).clone()) }) .collect_vec(); - let mut assigns = constr_args - .into_iter() - .map(|(_, _, _, assign)| assign) - .collect_vec(); - - if assigns.is_empty() { - let empty = AirTree::fields_empty( + let assign = if constr_args.is_empty() { + AirTree::fields_empty( AirTree::local_var( format!( "__constr_var_span_{}_{}", @@ -1693,11 +1663,9 @@ impl<'a> CodeGenerator<'a> { tipo.clone(), ), msg_term.clone(), - ); - - assigns.insert(0, empty); + ) } else { - let expose = AirTree::fields_expose( + AirTree::fields_expose( indices, AirTree::local_var( format!( @@ -1708,12 +1676,10 @@ impl<'a> CodeGenerator<'a> { ), msg_term.clone(), true, - ); - - assigns.insert(0, expose); + ) }; - let then = AirTree::UnhoistedSequence(assigns).hoist_over(AirTree::void()); + let then = assign.hoist_over(constr_then); AirTree::clause( format!("__subject_span_{}_{}", location.start, location.end), @@ -1726,11 +1692,6 @@ impl<'a> CodeGenerator<'a> { }, ); - let overhead_assign = AirTree::let_assignment( - format!("__constr_var_span_{}_{}", location.start, location.end), - AirTree::local_var("__param_0", tipo.clone()), - ); - let when_expr = AirTree::when( format!("__subject_span_{}_{}", location.start, location.end), void(), @@ -1742,7 +1703,11 @@ impl<'a> CodeGenerator<'a> { constr_clauses, ); - let func_body = overhead_assign.hoist_over(when_expr); + let func_body = AirTree::let_assignment( + format!("__constr_var_span_{}_{}", location.start, location.end), + AirTree::local_var("__param_0", tipo.clone()), + when_expr, + ); for (inner_data_type, inner_count) in defined_data_types.iter() { if let Some(prev_count) = current_defined.get(inner_data_type) { @@ -1809,6 +1774,7 @@ impl<'a> CodeGenerator<'a> { subject_tipo: &Rc, props: &mut ClauseProperties, module_name: &String, + context: &[TypedExpr], ) -> AirTree { assert!( !subject_tipo.is_void(), @@ -1817,7 +1783,7 @@ impl<'a> CodeGenerator<'a> { props.complex_clause = false; if let Some((clause, rest_clauses)) = clauses.split_first() { - let mut clause_then = self.build(&clause.then, module_name); + let mut clause_then = self.build(&clause.then, module_name, context); // handles clause guard if it exists if let Some(guard) = &clause.guard { @@ -1828,12 +1794,9 @@ impl<'a> CodeGenerator<'a> { clause.location.start, clause.location.end ); - let clause_guard_assign = AirTree::let_assignment( + clause_then = AirTree::let_assignment( &clause_guard_name, builder::handle_clause_guard(guard), - ); - - clause_then = clause_guard_assign.hoist_over( AirTree::clause_guard(clause_guard_name, AirTree::bool(true), bool()) .hoist_over(clause_then), ); @@ -1844,9 +1807,7 @@ impl<'a> CodeGenerator<'a> { 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); - - let clause_assign_hoisted = clause_assign.hoist_over(clause_then); + self.clause_pattern(&clause.pattern, subject_tipo, props, clause_then); let complex_clause = props.complex_clause; @@ -1861,13 +1822,14 @@ impl<'a> CodeGenerator<'a> { Pattern::Var { .. } | Pattern::Discard { .. } ) { AirTree::wrap_clause( - clause_assign_hoisted, + clause_assign, self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, module_name, + context, ), ) } else if let Some(data_type) = data_type { @@ -1876,25 +1838,27 @@ impl<'a> CodeGenerator<'a> { &props.original_subject_name, clause_cond, subject_tipo.clone(), - clause_assign_hoisted, + clause_assign, self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, module_name, + context, ), complex_clause, ) } else { AirTree::wrap_clause( - clause_assign_hoisted, + clause_assign, self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, module_name, + context, ), ) } @@ -1906,13 +1870,14 @@ impl<'a> CodeGenerator<'a> { &props.original_subject_name, clause_cond, subject_tipo.clone(), - clause_assign_hoisted, + clause_assign, self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, module_name, + context, ), complex_clause, ) @@ -1949,18 +1914,17 @@ impl<'a> CodeGenerator<'a> { }; let (_, clause_assign) = - self.clause_pattern(&clause.pattern, subject_tipo, props); - - let clause_assign_hoisted = clause_assign.hoist_over(clause_then); + self.clause_pattern(&clause.pattern, subject_tipo, props, clause_then); return AirTree::wrap_clause( - clause_assign_hoisted, + clause_assign, self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, module_name, + context, ), ); }; @@ -2045,9 +2009,7 @@ impl<'a> CodeGenerator<'a> { }; let (_, clause_assign) = - self.clause_pattern(&clause.pattern, subject_tipo, props); - - let clause_assign_hoisted = clause_assign.hoist_over(clause_then); + self.clause_pattern(&clause.pattern, subject_tipo, props, clause_then); let complex_clause = props.complex_clause; @@ -2057,26 +2019,28 @@ impl<'a> CodeGenerator<'a> { AirTree::list_clause( tail_name, subject_tipo.clone(), - clause_assign_hoisted, + clause_assign, self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, module_name, + context, ), next_tail_name, complex_clause, ) } else { AirTree::wrap_clause( - clause_assign_hoisted, + clause_assign, self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, module_name, + context, ), ) } @@ -2087,7 +2051,7 @@ impl<'a> CodeGenerator<'a> { let current_defined_indices = defined_tuple_indices.clone(); let (_, pattern_assigns) = - self.clause_pattern(&clause.pattern, subject_tipo, props); + self.clause_pattern(&clause.pattern, subject_tipo, props, clause_then); let ClauseProperties { specific_clause: @@ -2118,13 +2082,14 @@ impl<'a> CodeGenerator<'a> { if new_defined_indices.is_empty() { AirTree::wrap_clause( - pattern_assigns.hoist_over(clause_then), + pattern_assigns, self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, module_name, + context, ), ) } else { @@ -2133,13 +2098,14 @@ impl<'a> CodeGenerator<'a> { subject_tipo.clone(), new_defined_indices, current_defined_indices, - pattern_assigns.hoist_over(clause_then), + pattern_assigns, self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, module_name, + context, ), props.complex_clause, ) @@ -2152,11 +2118,12 @@ impl<'a> CodeGenerator<'a> { assert!(final_clause.guard.is_none()); - let clause_then = self.build(&final_clause.then, module_name); + let clause_then = self.build(&final_clause.then, module_name, context); + let (condition, assignments) = - self.clause_pattern(&final_clause.pattern, subject_tipo, props); + self.clause_pattern(&final_clause.pattern, subject_tipo, props, clause_then); - AirTree::finally(condition, assignments.hoist_over(clause_then)) + AirTree::finally(condition, assignments) } } @@ -2165,35 +2132,36 @@ impl<'a> CodeGenerator<'a> { 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), AirTree::no_op()) + (AirTree::int(value), then) } Pattern::Var { name, .. } => ( AirTree::void(), AirTree::let_assignment( name, AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), + then, ), ), Pattern::Assign { name, pattern, .. } => { let (inner_condition, inner_assignment) = - self.clause_pattern(pattern, subject_tipo, props); + self.clause_pattern(pattern, subject_tipo, props, then); - let sequence = vec![ + ( + inner_condition, AirTree::let_assignment( name, AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), + inner_assignment, ), - inner_assignment, - ]; - - (inner_condition, AirTree::UnhoistedSequence(sequence)) + ) } - Pattern::Discard { .. } => (AirTree::void(), AirTree::no_op()), + Pattern::Discard { .. } => (AirTree::void(), then), Pattern::List { elements, tail, .. } => { let ClauseProperties { specific_clause: SpecificClause::ListClause { defined_tails, .. }, @@ -2212,52 +2180,54 @@ impl<'a> CodeGenerator<'a> { let defined_tails = defined_tails.clone(); - let elems = elements - .iter() - .enumerate() - .map(|(index, elem)| { - let elem_name = match elem { - Pattern::Var { name, .. } => name.to_string(), - Pattern::Assign { name, .. } => name.to_string(), - Pattern::Discard { .. } => "_".to_string(), - _ => format!( - "elem_{}_span_{}_{}", - index, - elem.location().start, - elem.location().end - ), - }; + let mut elems = vec![]; - let mut elem_props = ClauseProperties::init_inner( - list_elem_type, - elem_name.clone(), - elem_name.clone(), - props.final_clause, - ); - - let statement = if elem_name != "_" { - self.nested_clause_condition(elem, list_elem_type, &mut elem_props) - } else { - AirTree::no_op() - }; + let elems_then = + elements + .iter() + .enumerate() + .rev() + .fold(then, |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::Var { name, .. } => name.to_string(), + Pattern::Assign { name, .. } => name.to_string(), + Pattern::Discard { .. } => "_".to_string(), + _ => format!( + "elem_{}_span_{}_{}", + index, + elem.location().start, + elem.location().end + ), + }; - *complex_clause = *complex_clause || elem_props.complex_clause; + let mut elem_props = ClauseProperties::init_inner( + list_elem_type, + elem_name.clone(), + elem_name.clone(), + props.final_clause, + ); - (elem_name, statement) - }) - .collect_vec(); + *complex_clause = *complex_clause || elem_props.complex_clause; - let mut defined_heads = - elems.iter().map(|(head, _)| head.to_string()).collect_vec(); + elems.push(elem_name); - let mut air_elems = elems - .into_iter() - .map(|(_, statement)| statement) - .collect_vec(); + if elem_name != "_" { + self.nested_clause_condition( + elem, + list_elem_type, + &mut elem_props, + then, + ) + } else { + then + } + }); let mut list_tail = None; - tail.iter().for_each(|elem| { + let tail_then = tail.iter().rev().fold(elems_then, |then, elem| { assert!(!elements.is_empty()); let tail = defined_tails.get(elements.len() - 1); let elem_name = match elem.as_ref() { @@ -2278,24 +2248,25 @@ impl<'a> CodeGenerator<'a> { props.final_clause, ); - let statement = if elem_name != "_" { - self.nested_clause_condition(elem, subject_tipo, &mut elem_props) - } else { - AirTree::no_op() - }; - *complex_clause = *complex_clause || elem_props.complex_clause; - air_elems.push(statement); if &elem_name != "_" && !defined_tails.is_empty() { list_tail = Some((tail.unwrap().to_string(), elem_name.to_string())); } if props.final_clause && defined_tails.is_empty() { - defined_heads.push(elem_name); + elems.push(elem_name); + } + + if elem_name != "_" { + self.nested_clause_condition(elem, subject_tipo, &mut elem_props, then) + } else { + then } }); + let mut defined_heads = elems.iter().rev().cloned().collect_vec(); + let list_assign = if props.final_clause && defined_tails.is_empty() { AirTree::list_access( defined_heads, @@ -2323,11 +2294,7 @@ impl<'a> CodeGenerator<'a> { ) }; - let mut sequence = vec![list_assign]; - - sequence.append(&mut air_elems); - - (AirTree::void(), AirTree::UnhoistedSequence(sequence)) + (AirTree::void(), list_assign.hoist_over(tail_then)) } Pattern::Constructor { name, @@ -2371,10 +2338,11 @@ impl<'a> CodeGenerator<'a> { type_map.insert(index, field_type); } - let fields = arguments - .iter() - .enumerate() - .map(|(index, arg)| { + let mut fields = vec![]; + + let next_then = arguments.iter().enumerate().rev().fold( + then, + |inner_then, (index, arg)| { let label = arg.label.clone().unwrap_or_default(); let field_index = if let Some(field_map) = &field_map { @@ -2410,41 +2378,42 @@ impl<'a> CodeGenerator<'a> { ); let statement = if field_name != "_" { - self.nested_clause_condition(&arg.value, arg_type, &mut field_props) + self.nested_clause_condition( + &arg.value, + arg_type, + &mut field_props, + inner_then, + ) } else { - AirTree::no_op() + inner_then }; props.complex_clause = props.complex_clause || field_props.complex_clause; - (field_index, field_name, arg_type, statement) - }) - .collect_vec(); + fields.push((field_index, field_name, *arg_type)); - let indices = fields - .iter() - .map(|(constr_index, name, tipo, _)| { - (*constr_index, name.to_string(), (*tipo).clone()) - }) - .collect_vec(); + statement + }, + ); - let mut air_fields = fields.into_iter().map(|(_, _, _, val)| val).collect_vec(); + fields.reverse(); let field_assign = if check_replaceable_opaque_type(subject_tipo, &self.data_types) { AirTree::let_assignment( - &indices[0].1, + &fields[0].1, AirTree::local_var( props.clause_var_name.clone(), subject_tipo.clone(), ), + next_then, ) - } else if indices.iter().all(|s| s.1 == "_") { - AirTree::no_op() + } else if fields.iter().all(|s| s.1 == "_") { + next_then } else { AirTree::fields_expose( - indices, + fields, AirTree::local_var( props.clause_var_name.clone(), subject_tipo.clone(), @@ -2452,59 +2421,62 @@ impl<'a> CodeGenerator<'a> { None, false, ) + .hoist_over(next_then) }; - let mut sequence = vec![field_assign]; - - sequence.append(&mut air_fields); - - ( - AirTree::int(constr_index), - AirTree::UnhoistedSequence(sequence), - ) + (AirTree::int(constr_index), field_assign) } } Pattern::Tuple { elems, .. } => { let items_type = subject_tipo.get_inner_types(); - let name_index_assigns = elems - .iter() - .enumerate() - .map(|(index, element)| { - let elem_name = match element { - Pattern::Var { name, .. } => name.to_string(), - Pattern::Assign { name, .. } => name.to_string(), - Pattern::Discard { .. } => "_".to_string(), - _ => format!( - "tuple_index_{}_span_{}_{}", - index, - element.location().start, - element.location().end - ), - }; + let mut name_index_assigns = vec![]; - let mut tuple_props = ClauseProperties::init_inner( - &items_type[index], - elem_name.clone(), - elem_name.clone(), - props.final_clause, - ); + let next_then = + elems + .iter() + .enumerate() + .rev() + .fold(then, |inner_then, (index, element)| { + let elem_name = match element { + Pattern::Var { name, .. } => name.to_string(), + Pattern::Assign { name, .. } => name.to_string(), + Pattern::Discard { .. } => "_".to_string(), + _ => format!( + "tuple_index_{}_span_{}_{}", + index, + element.location().start, + element.location().end + ), + }; - let elem = if elem_name != "_" { - self.nested_clause_condition( - element, + let mut tuple_props = ClauseProperties::init_inner( &items_type[index], - &mut tuple_props, - ) - } else { - AirTree::no_op() - }; + elem_name.clone(), + elem_name.clone(), + props.final_clause, + ); - props.complex_clause = props.complex_clause || tuple_props.complex_clause; + let elem = if elem_name != "_" { + self.nested_clause_condition( + element, + &items_type[index], + &mut tuple_props, + inner_then, + ) + } else { + inner_then + }; - (elem_name, index, elem) - }) - .collect_vec(); + 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 { @@ -2599,10 +2571,11 @@ impl<'a> CodeGenerator<'a> { 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); + let (_, assign) = self.clause_pattern(pattern, subject_tipo, props, then); assign } else { assert!( @@ -2613,19 +2586,19 @@ impl<'a> CodeGenerator<'a> { Pattern::Int { value, .. } => { props.complex_clause = true; AirTree::clause_guard(&props.original_subject_name, AirTree::int(value), int()) + .hoist_over(then) } Pattern::Var { name, .. } => AirTree::let_assignment( name, AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), + then, ), - Pattern::Assign { name, pattern, .. } => AirTree::UnhoistedSequence(vec![ - AirTree::let_assignment( - name, - AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), - ), - self.nested_clause_condition(pattern, subject_tipo, props), - ]), - Pattern::Discard { .. } => AirTree::no_op(), + Pattern::Assign { name, pattern, .. } => 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(); @@ -2642,6 +2615,7 @@ impl<'a> CodeGenerator<'a> { false, None, ) + .hoist_over(then) } else { let mut clause_assigns = vec![]; @@ -2707,9 +2681,8 @@ impl<'a> CodeGenerator<'a> { }; } - let (_, assigns) = self.clause_pattern(pattern, subject_tipo, props); - clause_assigns.push(assigns); - AirTree::UnhoistedSequence(clause_assigns) + let (_, assigns) = self.clause_pattern(pattern, subject_tipo, props, then); + AirTree::UnhoistedSequence(clause_assigns).hoist_over(assigns) } } Pattern::Constructor { @@ -2723,7 +2696,8 @@ impl<'a> CodeGenerator<'a> { bool(), ) } else { - let (cond, assign) = self.clause_pattern(pattern, subject_tipo, props); + 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"); @@ -2732,19 +2706,17 @@ impl<'a> CodeGenerator<'a> { assign } else { props.complex_clause = true; - AirTree::UnhoistedSequence(vec![ - AirTree::clause_guard( - &props.original_subject_name, - cond, - subject_tipo.clone(), - ), - assign, - ]) + AirTree::clause_guard( + &props.original_subject_name, + cond, + subject_tipo.clone(), + ) + .hoist_over(assign) } } } Pattern::Tuple { .. } => { - let (_, assign) = self.clause_pattern(pattern, subject_tipo, props); + let (_, assign) = self.clause_pattern(pattern, subject_tipo, props, then); let defined_indices = match &props.specific_clause { SpecificClause::TupleClause { @@ -2759,7 +2731,7 @@ impl<'a> CodeGenerator<'a> { defined_indices, ); - AirTree::UnhoistedSequence(vec![tuple_access, assign]) + tuple_access.hoist_over(assign) } } } @@ -2846,7 +2818,11 @@ impl<'a> CodeGenerator<'a> { AirTree::anon_func(arg_names, arg_assigns.hoist_over(body)) } - fn hoist_functions_to_validator(&mut self, mut air_tree: AirTree) -> AirTree { + fn hoist_functions_to_validator( + &mut self, + mut air_tree: AirTree, + context: &[TypedExpr], + ) -> AirTree { let mut functions_to_hoist = IndexMap::new(); let mut used_functions = vec![]; let mut defined_functions = vec![]; @@ -2868,6 +2844,7 @@ impl<'a> CodeGenerator<'a> { &mut TreePath::new(), 0, 0, + context, ); validator_hoistable = used_functions.clone(); @@ -2897,6 +2874,7 @@ impl<'a> CodeGenerator<'a> { &defined_functions, &mut hoist_deps, tree_path, + context, ); let function_variants = functions_to_hoist @@ -3506,6 +3484,7 @@ impl<'a> CodeGenerator<'a> { defined_functions: &[(FunctionAccessKey, String)], current_function_deps: &mut Vec<(FunctionAccessKey, String)>, mut function_tree_path: TreePath, + context: &[TypedExpr], ) { let Some((depth, index)) = function_tree_path.pop() else { return; @@ -3520,6 +3499,7 @@ impl<'a> CodeGenerator<'a> { &mut function_tree_path, depth + 1, 0, + context, ); for (generic_function_key, variant_name) in current_function_deps.iter() { @@ -3546,6 +3526,7 @@ impl<'a> CodeGenerator<'a> { path: &mut TreePath, current_depth: usize, depth_index: usize, + context: &[TypedExpr], ) { air_tree.traverse_tree_with_path( path, @@ -3688,8 +3669,11 @@ impl<'a> CodeGenerator<'a> { }) .collect_vec(); - let mut function_air_tree_body = - self.build(&function_def.body, &generic_function_key.module_name); + let mut function_air_tree_body = self.build( + &function_def.body, + &generic_function_key.module_name, + context, + ); function_air_tree_body.traverse_tree_with( &mut |air_tree, _| { @@ -3718,8 +3702,11 @@ impl<'a> CodeGenerator<'a> { .map(|arg| arg.arg_name.get_variable_name().unwrap_or("_").to_string()) .collect_vec(); - let mut function_air_tree_body = - self.build(&function_def.body, &generic_function_key.module_name); + let mut function_air_tree_body = self.build( + &function_def.body, + &generic_function_key.module_name, + context, + ); function_air_tree_body.traverse_tree_with( &mut |air_tree, _| { diff --git a/crates/aiken-lang/src/gen_uplc/builder.rs b/crates/aiken-lang/src/gen_uplc/builder.rs index ee1f66564..fd9fbfc4b 100644 --- a/crates/aiken-lang/src/gen_uplc/builder.rs +++ b/crates/aiken-lang/src/gen_uplc/builder.rs @@ -659,10 +659,7 @@ pub fn erase_opaque_type_operations( /// Determine whether this air_tree node introduces any shadowing over `potential_matches` pub fn find_introduced_variables(air_tree: &AirTree) -> Vec { match air_tree { - AirTree::Statement { - statement: AirStatement::Let { name, .. }, - .. - } => vec![name.clone()], + AirTree::Expression(AirExpression::Let { name, .. }) => vec![name.clone()], AirTree::Statement { statement: AirStatement::TupleGuard { indices, .. }, .. diff --git a/crates/aiken-lang/src/gen_uplc/tree.rs b/crates/aiken-lang/src/gen_uplc/tree.rs index bc6054848..224999b64 100644 --- a/crates/aiken-lang/src/gen_uplc/tree.rs +++ b/crates/aiken-lang/src/gen_uplc/tree.rs @@ -117,11 +117,6 @@ pub enum AirTree { #[derive(Debug, Clone, PartialEq)] pub enum AirStatement { - // Assignment - Let { - name: String, - value: Box, - }, DefineFunc { func_name: String, module_name: String, @@ -209,6 +204,13 @@ pub enum AirStatement { #[derive(Debug, Clone, PartialEq)] pub enum AirExpression { + // Assignment + Let { + name: String, + value: Box, + then: Box, + }, + // Primitives Int { value: String, @@ -494,14 +496,12 @@ impl AirTree { arg: arg.into(), }) } - pub fn let_assignment(name: impl ToString, value: AirTree) -> AirTree { - AirTree::Statement { - statement: AirStatement::Let { - name: name.to_string(), - value: value.into(), - }, - hoisted_over: None, - } + pub fn let_assignment(name: impl ToString, value: AirTree, then: AirTree) -> AirTree { + AirTree::Expression(AirExpression::Let { + name: name.to_string(), + value: value.into(), + then: then.into(), + }) } pub fn cast_from_data(value: AirTree, tipo: Rc, msg: Option) -> AirTree { AirTree::Expression(AirExpression::CastFromData { @@ -755,6 +755,7 @@ impl AirTree { hoisted_over: None, } } + pub fn list_access( names: Vec, tipo: Rc, @@ -889,8 +890,6 @@ impl AirTree { vec![head_list], ); - let assign = AirTree::let_assignment("_", expect_on_head); - let next_call = AirTree::call( AirTree::var( ValueConstructor::public( @@ -918,11 +917,13 @@ impl AirTree { ], ); + let assign = AirTree::let_assignment("_", expect_on_head, next_call); + AirTree::list_clause( "__list_to_check", void(), AirTree::void(), - assign.hoist_over(next_call), + assign, None, false, ) @@ -941,10 +942,6 @@ impl AirTree { hoisted_over: Some(exp), } => { match statement { - AirStatement::Let { value, name } => { - air_vec.push(Air::Let { name: name.clone() }); - value.create_air_vec(air_vec); - } AirStatement::DefineFunc { func_name, module_name, @@ -1143,6 +1140,11 @@ impl AirTree { exp.create_air_vec(air_vec); } AirTree::Expression(exp) => match exp { + AirExpression::Let { value, then, name } => { + air_vec.push(Air::Let { name: name.clone() }); + value.create_air_vec(air_vec); + then.create_air_vec(air_vec); + } AirExpression::Int { value } => air_vec.push(Air::Int { value: value.clone(), }), @@ -1517,9 +1519,10 @@ impl AirTree { let mut index_count = IndexCounter::new(); tree_path.push(current_depth, depth_index); - if let AirTree::Statement { statement, .. } = self { - match statement { - AirStatement::Let { value, .. } => { + match self { + AirTree::UnhoistedSequence(..) => unreachable!("No unhoisted sequence at this point"), + AirTree::Expression(expr) => match expr { + AirExpression::Let { value, .. } => { value.do_traverse_tree_with( tree_path, current_depth + 1, @@ -1528,6 +1531,9 @@ impl AirTree { apply_with_func_last, ); } + _ => (), + }, + AirTree::Statement { statement, .. } => match statement { AirStatement::DefineFunc { func_body, .. } => { func_body.do_traverse_tree_with( tree_path, @@ -1628,7 +1634,7 @@ impl AirTree { apply_with_func_last, ); } - }; + }, } if !apply_with_func_last { @@ -1649,6 +1655,15 @@ impl AirTree { ); } AirTree::Expression(e) => match e { + AirExpression::Let { then, .. } => { + then.do_traverse_tree_with( + tree_path, + current_depth + 1, + index_count.next_number(), + with, + apply_with_func_last, + ); + } AirExpression::List { items, .. } => { for item in items { item.do_traverse_tree_with( @@ -1987,15 +2002,6 @@ impl AirTree { statement, hoisted_over: Some(hoisted_over), } => match statement { - AirStatement::Let { value, .. } => { - if *index == 0 { - value.as_mut().do_find_air_tree_node(tree_path_iter) - } else if *index == 1 { - hoisted_over.as_mut().do_find_air_tree_node(tree_path_iter) - } else { - panic!("Tree Path index outside tree children nodes") - } - } AirStatement::AssertConstr { constr, .. } => { if *index == 0 { constr.as_mut().do_find_air_tree_node(tree_path_iter) @@ -2100,6 +2106,15 @@ impl AirTree { } }, AirTree::Expression(e) => match e { + AirExpression::Let { value, then, .. } => { + if *index == 0 { + value.as_mut().do_find_air_tree_node(tree_path_iter) + } else if *index == 1 { + then.as_mut().do_find_air_tree_node(tree_path_iter) + } else { + panic!("Tree Path index outside tree children nodes") + } + } AirExpression::List { items, .. } | AirExpression::Tuple { items, .. } | AirExpression::Builtin { args: items, .. } => {