diff --git a/crates/aiken-lang/src/builder.rs b/crates/aiken-lang/src/builder.rs index c1cac151..cc58c19f 100644 --- a/crates/aiken-lang/src/builder.rs +++ b/crates/aiken-lang/src/builder.rs @@ -510,11 +510,14 @@ pub fn list_access_to_uplc( term: Term, tipos: Vec>, check_last_item: bool, + is_list_accessor: bool, ) -> Term { if let Some((first, names)) = names.split_first() { let (current_tipo, tipos) = tipos.split_first().unwrap(); - let head_list = if current_tipo.is_map() { + let head_list = if matches!(current_tipo.get_uplc_type(), UplcType::Pair(_, _)) + && is_list_accessor + { apply_wrap( Term::Builtin(DefaultFunction::HeadList).force_wrap(), Term::Var( @@ -622,7 +625,7 @@ pub fn list_access_to_uplc( Term::Builtin(DefaultFunction::Trace).force_wrap(), Term::Constant( UplcConstant::String( - "List/Tuple contains more items than it should" + "List/Tuple/Constr contains more items than it should" .to_string(), ) .into(), @@ -664,6 +667,7 @@ pub fn list_access_to_uplc( term, tipos.to_owned(), check_last_item, + is_list_accessor, ), apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), @@ -757,7 +761,9 @@ pub fn check_when_pattern_needs( check_when_pattern_needs(&argument.value, clause_properties); } } - Pattern::Discard { .. } => {} + Pattern::Discard { .. } => { + *clause_properties.needs_constr_var() = true; + } _ => todo!("{pattern:#?}"), } } diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index 8c3e0539..cf975c62 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -354,7 +354,7 @@ impl<'a> CodeGenerator<'a> { self.build_ir(&clauses[0].then, &mut value_vec, scope.clone()); - self.build_ir(&subject, &mut value_vec, scope.clone()); + self.build_ir(&subject, &mut subject_vec, scope.clone()); self.assignment_ir( &clauses[0].pattern[0], @@ -1192,7 +1192,10 @@ impl<'a> CodeGenerator<'a> { scope.clone(), ); - var_name.map(|var_name| (label, var_name, *field_index)) + var_name.map_or( + Some((label.clone(), "_".to_string(), *field_index)), + |var_name| Some((label, var_name, *field_index)), + ) }) .sorted_by(|item1, item2| item1.2.cmp(&item2.2)) .collect::>(); @@ -1230,7 +1233,9 @@ impl<'a> CodeGenerator<'a> { scope.clone(), ); - var_name.map(|var_name| (var_name, index)) + var_name.map_or(Some(("_".to_string(), index)), |var_name| { + Some((var_name, index)) + }) }) .collect::>(); @@ -1788,7 +1793,7 @@ impl<'a> CodeGenerator<'a> { .filter_map(|(index, item)| { let label = item.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() + *field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&index) } else { index }; @@ -1800,6 +1805,7 @@ impl<'a> CodeGenerator<'a> { &assignment_properties, &scope, ) + .map_or(Some(("_".to_string(), index)), Some) }) .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) .collect::>(); @@ -1852,14 +1858,25 @@ impl<'a> CodeGenerator<'a> { .collect::>(); if !arguments_index.is_empty() { + let mut current_index = 0; + let mut final_args = vec![]; + + for index in 0..elems.len() { + if arguments_index.get(current_index).is_some() + && arguments_index[current_index].1 == index + { + final_args.push(arguments_index.get(current_index).unwrap().clone()); + current_index += 1; + } else { + final_args.push(("_".to_string(), index)); + } + } + pattern_vec.push(Air::TupleAccessor { scope, - names: arguments_index - .into_iter() - .map(|(item, _)| item) - .collect_vec(), + names: final_args.into_iter().map(|(item, _)| item).collect_vec(), tipo: tipo.clone().into(), - check_last_item: true, + check_last_item: false, }); } else { pattern_vec.push(Air::Let { @@ -2109,7 +2126,7 @@ impl<'a> CodeGenerator<'a> { if arguments_index.get(current_index).is_some() && arguments_index[current_index].1 == index { - final_args.push(arguments_index.get(index).unwrap().clone()); + final_args.push(arguments_index.get(current_index).unwrap().clone()); current_index += 1; } else { let id_next = self.id_gen.next(); @@ -3826,6 +3843,7 @@ impl<'a> CodeGenerator<'a> { term, inner_types, check_last_item, + true, ), apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), @@ -5083,76 +5101,34 @@ impl<'a> CodeGenerator<'a> { let mut term = arg_stack.pop().unwrap(); let list_id = self.id_gen.next(); + id_list.push(list_id); + for _ in 0..indices.len() { id_list.push(self.id_gen.next()); } let current_index = 0; - let (first_name, indices) = indices.split_first().unwrap(); - - let head_list = convert_data_to_type( - apply_wrap( - Term::Builtin(DefaultFunction::HeadList).force_wrap(), - Term::Var( - Name { - text: format!("__constr_fields_{list_id}"), - unique: 0.into(), - } - .into(), - ), - ), - &first_name.2, - ); let names = indices.iter().cloned().map(|item| item.1).collect_vec(); let inner_types = indices.iter().cloned().map(|item| item.2).collect_vec(); - let tail_list = if !indices.is_empty() { - apply_wrap( - list_access_to_uplc( - &names, - &id_list, - false, - current_index, - term, - inner_types, - check_last_item, - ), - apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), - Term::Var( - Name { - text: format!("__constr_fields_{list_id}"), - unique: 0.into(), - } - .into(), - ), - ), + term = if !indices.is_empty() { + list_access_to_uplc( + &names, + &id_list, + false, + current_index, + term, + inner_types, + check_last_item, + false, ) } else { term }; term = apply_wrap( - Term::Lambda { - parameter_name: Name { - text: format!("__constr_fields_{list_id}"), - unique: 0.into(), - } - .into(), - body: apply_wrap( - Term::Lambda { - parameter_name: Name { - text: first_name.1.clone(), - unique: 0.into(), - } - .into(), - body: tail_list.into(), - }, - head_list, - ) - .into(), - }, + term, apply_wrap( Term::Var( Name { @@ -5557,69 +5533,23 @@ impl<'a> CodeGenerator<'a> { ); } else { let mut id_list = vec![]; + id_list.push(list_id); for _ in 0..names.len() { id_list.push(self.id_gen.next()); } - let current_index = 0; - let (first_name, names) = names.split_first().unwrap(); - - let head_list = convert_data_to_type( - apply_wrap( - Term::Builtin(DefaultFunction::HeadList).force_wrap(), - Term::Var( - Name { - text: format!("__tuple_{list_id}"), - unique: 0.into(), - } - .into(), - ), - ), - &tipo.get_inner_types()[0], - ); - term = apply_wrap( - Term::Lambda { - parameter_name: Name { - text: format!("__tuple_{list_id}"), - unique: 0.into(), - } - .into(), - body: apply_wrap( - Term::Lambda { - parameter_name: Name { - text: first_name.clone(), - unique: 0.into(), - } - .into(), - body: apply_wrap( - list_access_to_uplc( - names, - &id_list, - false, - current_index, - term, - tipo.get_inner_types(), - check_last_item, - ), - apply_wrap( - Term::Builtin(DefaultFunction::TailList).force_wrap(), - Term::Var( - Name { - text: format!("__tuple_{list_id}"), - unique: 0.into(), - } - .into(), - ), - ), - ) - .into(), - }, - head_list, - ) - .into(), - }, + list_access_to_uplc( + &names, + &id_list, + false, + 0, + term, + tipo.get_inner_types(), + check_last_item, + false, + ), value, ); } diff --git a/crates/uplc/src/optimize/shrinker.rs b/crates/uplc/src/optimize/shrinker.rs index ef8eb28f..fbbf4686 100644 --- a/crates/uplc/src/optimize/shrinker.rs +++ b/crates/uplc/src/optimize/shrinker.rs @@ -142,17 +142,31 @@ fn inline_basic_reduce(term: &mut Term) { body, } = func { - if let replace_term @ (Term::Var(_) - | Term::Constant(_) - | Term::Error - | Term::Delay(_) - | Term::Lambda { .. }) = argument.as_ref() - { - let mut occurrences = 0; - var_occurrences(body, parameter_name.clone(), &mut occurrences); - if occurrences <= 1 { - *term = - substitute_term(body.as_ref(), parameter_name.clone(), replace_term); + let mut occurrences = 0; + var_occurrences(body, parameter_name.clone(), &mut occurrences); + if occurrences == 1 { + if let replace_term @ (Term::Var(_) + | Term::Constant(_) + | Term::Error + | Term::Delay(_) + | Term::Lambda { .. }) = argument.as_ref() + { + if occurrences == 1 { + *term = substitute_term( + body.as_ref(), + parameter_name.clone(), + replace_term, + ); + } + } + } else if occurrences == 0 { + error_occurrences(argument.as_ref(), &mut occurrences); + if occurrences == 0 { + *term = substitute_term( + body.as_ref(), + parameter_name.clone(), + argument.as_ref(), + ); } } } @@ -194,6 +208,28 @@ fn var_occurrences(term: &Term, search_for: Rc, occurrences: &mut us } } +fn error_occurrences(term: &Term, occurrences: &mut usize) { + match term { + Term::Delay(body) => { + error_occurrences(body.as_ref(), occurrences); + } + Term::Lambda { body, .. } => { + error_occurrences(body.as_ref(), occurrences); + } + Term::Apply { function, argument } => { + error_occurrences(function.as_ref(), occurrences); + error_occurrences(argument.as_ref(), occurrences); + } + Term::Force(x) => { + error_occurrences(x.as_ref(), occurrences); + } + Term::Error => { + *occurrences += 1; + } + _ => {} + } +} + fn lambda_reduce(term: &mut Term) { match term { Term::Apply { function, argument } => { diff --git a/examples/acceptance_tests/054/aiken.lock b/examples/acceptance_tests/054/aiken.lock new file mode 100644 index 00000000..d9cf5aff --- /dev/null +++ b/examples/acceptance_tests/054/aiken.lock @@ -0,0 +1,13 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +[[requirements]] +name = "aiken-lang/stdlib" +version = "1cedbe85b7c7e9c4036d63d45cad4ced27b0d50b" +source = "github" + +[[packages]] +name = "aiken-lang/stdlib" +version = "1cedbe85b7c7e9c4036d63d45cad4ced27b0d50b" +requirements = [] +source = "github" diff --git a/examples/acceptance_tests/054/aiken.toml b/examples/acceptance_tests/054/aiken.toml new file mode 100644 index 00000000..bfa5d50c --- /dev/null +++ b/examples/acceptance_tests/054/aiken.toml @@ -0,0 +1,6 @@ +name = "aiken-lang/acceptance_test_036" +version = "0.0.0" + +dependencies = [ + { name = "aiken-lang/stdlib", version = "1cedbe85b7c7e9c4036d63d45cad4ced27b0d50b", source = "github" }, +] diff --git a/examples/acceptance_tests/054/lib/tests.ak b/examples/acceptance_tests/054/lib/tests.ak new file mode 100644 index 00000000..0a271902 --- /dev/null +++ b/examples/acceptance_tests/054/lib/tests.ak @@ -0,0 +1,8 @@ +use aiken/dict +use aiken/list +use aiken/transaction/value.{Value} + +test test_quantity_of_1() { + let x = value.from_asset(#"000000", #"000020e05363726970744f776e6572", -1) + value.quantity_of(x, #"000000", #"000020e05363726970744f776e6572") < 0 +}