From 03dd13dc7d894e8770bcc578e03fee589d33c47e Mon Sep 17 00:00:00 2001 From: microproofs Date: Fri, 28 Jul 2023 11:47:27 -0400 Subject: [PATCH] fixing list condition edge cases and clean up rearrange list clauses --- crates/aiken-lang/src/gen_uplc.rs | 22 ++--- crates/aiken-lang/src/gen_uplc/builder.rs | 52 +++++++---- crates/aiken-project/src/tests/gen_uplc.rs | 101 ++++++++++----------- 3 files changed, 91 insertions(+), 84 deletions(-) diff --git a/crates/aiken-lang/src/gen_uplc.rs b/crates/aiken-lang/src/gen_uplc.rs index 9adb8fba..d084c77e 100644 --- a/crates/aiken-lang/src/gen_uplc.rs +++ b/crates/aiken-lang/src/gen_uplc.rs @@ -25,7 +25,8 @@ use crate::{ gen_uplc::builder::{ convert_opaque_type, erase_opaque_type_operations, find_and_replace_generics, get_arg_type_name, get_generic_id_and_type, get_variant_name, monomorphize, - wrap_as_multi_validator, wrap_validator_condition, CodeGenFunction, SpecificClause, + pattern_has_conditions, wrap_as_multi_validator, wrap_validator_condition, CodeGenFunction, + SpecificClause, }, tipo::{ ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, @@ -1680,7 +1681,7 @@ impl<'a> CodeGenerator<'a> { let next_elements_len = match next_clause_pattern { Pattern::List { elements, tail, .. } => { - elements.len() - usize::from(tail.is_some()) + elements.len() + usize::from(tail.is_none()) } _ => 0, }; @@ -1708,16 +1709,11 @@ impl<'a> CodeGenerator<'a> { }; let mut is_wild_card_elems_clause = clause.guard.is_none(); - for elements in elements.iter() { - if let Pattern::Constructor { .. } - | Pattern::Tuple { .. } - | Pattern::List { .. } - | Pattern::Assign { .. } = elements - { - is_wild_card_elems_clause = false; - } + for element in elements.iter() { + is_wild_card_elems_clause = + is_wild_card_elems_clause && !pattern_has_conditions(element); } - let elements_len = elements.len() - usize::from(tail.is_some()); + let elements_len = elements.len() + usize::from(tail.is_none()); let current_checked_index = *checked_index; if *checked_index < elements_len.try_into().unwrap() @@ -2360,8 +2356,8 @@ impl<'a> CodeGenerator<'a> { is_record, .. } => { - props.complex_clause = true; if subject_tipo.is_bool() { + props.complex_clause = true; AirTree::clause_guard( &props.original_subject_name, AirTree::bool(constr_name == "True"), @@ -2373,6 +2369,7 @@ impl<'a> CodeGenerator<'a> { if *is_record { assign } else { + props.complex_clause = true; AirTree::UnhoistedSequence(vec![ AirTree::clause_guard( &props.original_subject_name, @@ -2385,7 +2382,6 @@ impl<'a> CodeGenerator<'a> { } } Pattern::Tuple { .. } => { - props.complex_clause = true; let (_, assign) = self.clause_pattern(pattern, subject_tipo, props); let defined_indices = match &props.specific_clause { diff --git a/crates/aiken-lang/src/gen_uplc/builder.rs b/crates/aiken-lang/src/gen_uplc/builder.rs index 4cab244f..b7579791 100644 --- a/crates/aiken-lang/src/gen_uplc/builder.rs +++ b/crates/aiken-lang/src/gen_uplc/builder.rs @@ -14,7 +14,9 @@ use uplc::{ }; use crate::{ - ast::{AssignmentKind, DataType, Pattern, Span, TypedArg, TypedClause, TypedDataType}, + ast::{ + AssignmentKind, DataType, Pattern, Span, TypedArg, TypedClause, TypedDataType, TypedPattern, + }, builtins::{bool, void}, expr::TypedExpr, tipo::{PatternConstructor, TypeVar, ValueConstructor, ValueConstructorVariant}, @@ -521,7 +523,6 @@ pub fn get_variant_name(t: &Arc) -> String { let full_type = "_data".to_string(); if t.is_generic() { - println!("FULL TYPE: {:#?}", t); panic!("FOUND A POLYMORPHIC TYPE. EXPECTED MONOMORPHIC TYPE"); } @@ -601,6 +602,25 @@ pub fn modify_self_calls(air_tree: &mut AirTree, func_key: &FunctionAccessKey, v } } +pub fn pattern_has_conditions(pattern: &TypedPattern) -> bool { + match pattern { + Pattern::Constructor { + is_record: false, .. + } + | Pattern::List { .. } + | Pattern::Int { .. } => true, + Pattern::Tuple { elems, .. } => elems.iter().any(pattern_has_conditions), + Pattern::Constructor { + is_record: true, + arguments, + .. + } => arguments + .iter() + .any(|arg| pattern_has_conditions(&arg.value)), + Pattern::Var { .. } | Pattern::Discard { .. } | Pattern::Assign { .. } => false, + } +} + // TODO: write some tests pub fn rearrange_list_clauses(clauses: Vec) -> Vec { let mut sorted_clauses = clauses; @@ -695,8 +715,6 @@ pub fn rearrange_list_clauses(clauses: Vec) -> Vec { None }; - println!("sorted clauses: {:#?}", sorted_clauses); - for (index, clause) in sorted_clauses.iter().enumerate() { if last_clause_set { continue; @@ -710,15 +728,12 @@ pub fn rearrange_list_clauses(clauses: Vec) -> Vec { assert!(matches!( clause_pattern, - Pattern::List { .. } - | Pattern::Var { .. } - | Pattern::Discard { .. } - | Pattern::Assign { .. } + Pattern::List { .. } | Pattern::Var { .. } | Pattern::Discard { .. } )); - if let Pattern::List { elements, tail, .. } = &clause.pattern { + if let Pattern::List { elements, tail, .. } = clause_pattern { // found a hole and now we plug it - while wild_card_clause_elems < elements.len() - usize::from(tail.is_some()) { + while wild_card_clause_elems < elements.len() { let mut discard_elems = vec![]; for _ in 0..wild_card_clause_elems { @@ -764,17 +779,14 @@ pub fn rearrange_list_clauses(clauses: Vec) -> Vec { let mut is_wild_card_elems_clause = clause.guard.is_none(); - for elements in elements.iter() { - if let Pattern::Constructor { .. } - | Pattern::Tuple { .. } - | Pattern::List { .. } - | Pattern::Assign { .. } = elements - { - is_wild_card_elems_clause = false; - } + for element in elements.iter() { + is_wild_card_elems_clause = + is_wild_card_elems_clause && !pattern_has_conditions(element); } - if is_wild_card_elems_clause { + if is_wild_card_elems_clause + && wild_card_clause_elems < elements.len() + usize::from(tail.is_none()) + { wild_card_clause_elems += 1; if clause.guard.is_none() && tail.is_some() && !elements.is_empty() { last_clause_index = index; @@ -808,7 +820,7 @@ pub fn rearrange_list_clauses(clauses: Vec) -> Vec { // Encountered a tail so stop there with that as last clause if last_clause_set { - final_clauses = final_clauses[0..last_clause_index + 1].to_vec(); + final_clauses = final_clauses[0..last_clause_index].to_vec(); } // insert hole fillers into clauses diff --git a/crates/aiken-project/src/tests/gen_uplc.rs b/crates/aiken-project/src/tests/gen_uplc.rs index 0f6a91d5..9761d5cb 100644 --- a/crates/aiken-project/src/tests/gen_uplc.rs +++ b/crates/aiken-project/src/tests/gen_uplc.rs @@ -103,6 +103,8 @@ fn assert_uplc(source_code: &str, expected: Term, should_fail: bool) { term: expected, }; + println!("expected: {}", expected.to_pretty()); + let expected = optimize::aiken_optimize_and_intern(expected); let expected: Program = expected.try_into().unwrap(); @@ -2898,6 +2900,52 @@ fn when_tuple_deconstruction() { .apply(Term::var("red")), ) .delayed_if_else(Term::unit(), Term::Error) + .lambda("red") + .apply( + Term::var("red").lambda("_").apply( + Term::var("expect_RedSpend") + .lambda("expect_RedSpend") + .apply( + Term::equals_integer() + .apply(Term::integer(0.into())) + .apply(Term::var("subject")) + .delayed_if_else( + Term::tail_list() + .apply(Term::var("red_constr_fields")) + .delayed_choose_list( + Term::unit(), + Term::Error.trace(Term::string("List/Tuple/Constr contains more items than expected")), + ) + .lambda("field_1") + .apply(Term::un_i_data().apply( + Term::head_list().apply(Term::var("red_constr_fields")), + )) + .lambda("red_constr_fields") + .apply( + Term::var(CONSTR_FIELDS_EXPOSER) + .apply(Term::var("red")), + ), + Term::equals_integer() + .apply(Term::integer(1.into())) + .apply(Term::var("subject")) + .delayed_if_else( + Term::var(CONSTR_FIELDS_EXPOSER) + .apply(Term::var("red")) + .delayed_choose_list( + Term::unit(), + Term::Error + .trace(Term::string("Expected no fields for Constr")) + ), + Term::Error.trace(Term::string("Constr index did not match any type variant")), + ), + ) + .lambda("subject") + .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("red"))) + .lambda("red"), + ) + .apply(Term::var("red")), + ), + ) .lambda("dat") .apply( Term::var("dat").lambda("_").apply( @@ -2980,53 +3028,7 @@ fn when_tuple_deconstruction() { ) .apply(Term::var("dat")), ), - ) - .lambda("red") - .apply( - Term::var("red").lambda("_").apply( - Term::var("expect_RedSpend") - .lambda("expect_RedSpend") - .apply( - Term::equals_integer() - .apply(Term::integer(0.into())) - .apply(Term::var("subject")) - .delayed_if_else( - Term::tail_list() - .apply(Term::var("red_constr_fields")) - .delayed_choose_list( - Term::unit(), - Term::Error.trace(Term::string("List/Tuple/Constr contains more items than expected")), - ) - .lambda("field_1") - .apply(Term::un_i_data().apply( - Term::head_list().apply(Term::var("red_constr_fields")), - )) - .lambda("red_constr_fields") - .apply( - Term::var(CONSTR_FIELDS_EXPOSER) - .apply(Term::var("red")), - ), - Term::equals_integer() - .apply(Term::integer(1.into())) - .apply(Term::var("subject")) - .delayed_if_else( - Term::var(CONSTR_FIELDS_EXPOSER) - .apply(Term::var("red")) - .delayed_choose_list( - Term::unit(), - Term::Error - .trace(Term::string("Expected no fields for Constr")) - ), - Term::Error.trace(Term::string("Constr index did not match any type variant")), - ), - ) - .lambda("subject") - .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("red"))) - .lambda("red"), - ) - .apply(Term::var("red")), - ), - ) + ) .lambda("ctx") .lambda("red") .lambda("dat") @@ -3771,10 +3773,7 @@ fn list_fields_unwrap() { .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("item_1"))) .lambda("item_1") .apply(Term::head_list().apply(Term::var("field_list"))) - .lambda("clauses_delayed") - .apply(Term::bool(false).delay()) - .lambda("tail_1") - .apply(Term::tail_list().apply(Term::var("field_list"))), + ) .lambda("field_list") .apply(Term::list_values(vec![