diff --git a/crates/aiken-lang/src/gen_uplc.rs b/crates/aiken-lang/src/gen_uplc.rs index fbb78502..f90a8324 100644 --- a/crates/aiken-lang/src/gen_uplc.rs +++ b/crates/aiken-lang/src/gen_uplc.rs @@ -259,7 +259,7 @@ impl<'a> CodeGenerator<'a> { let air_value = self.build(value, module_build_name, &[]); - let otherwise = match (self.tracing, kind) { + let otherwise_delayed = match (self.tracing, kind) { ( TraceLevel::Silent, AssignmentKind::Let { .. } | AssignmentKind::Expect { .. }, @@ -285,7 +285,7 @@ impl<'a> CodeGenerator<'a> { self.special_functions.insert_new_function( msg_func_name.clone(), - Term::Error.delayed_trace(Term::string(msg)), + Term::Error.delayed_trace(Term::string(msg)).delay(), void(), ); @@ -307,7 +307,7 @@ impl<'a> CodeGenerator<'a> { kind: *kind, remove_unused: kind.is_let(), full_check: !tipo.is_data() && value.tipo().is_data() && kind.is_expect(), - otherwise, + otherwise: otherwise_delayed, }, ) } else { @@ -645,18 +645,23 @@ impl<'a> CodeGenerator<'a> { let body = self.build(&branch.body, module_build_name, &[]); match &branch.is { - Some(pattern) => self.assignment( - pattern, - condition, - body, - &pattern.tipo(&branch.condition).unwrap(), - AssignmentProperties { - value_type: branch.condition.tipo(), - kind: AssignmentKind::Expect { backpassing: () }, - remove_unused: false, - full_check: true, - otherwise: acc, - }, + Some(pattern) => AirTree::let_assignment( + "acc_var", + // use anon function as a delay to avoid evaluating the acc + AirTree::anon_func(vec![], acc), + self.assignment( + pattern, + condition, + body, + &pattern.tipo(&branch.condition).unwrap(), + AssignmentProperties { + value_type: branch.condition.tipo(), + kind: AssignmentKind::Expect { backpassing: () }, + remove_unused: false, + full_check: true, + otherwise: AirTree::local_var("acc_var", void()), + }, + ), ), None => AirTree::if_branch(tipo.clone(), condition, body, acc), } @@ -3030,7 +3035,7 @@ impl<'a> CodeGenerator<'a> { let actual_type = convert_opaque_type(&arg.tipo, &self.data_types, true); - let otherwise = match self.tracing { + let otherwise_delayed = match self.tracing { TraceLevel::Silent => AirTree::error(void(), false), TraceLevel::Compact | TraceLevel::Verbose => { let msg = match self.tracing { @@ -3051,7 +3056,7 @@ impl<'a> CodeGenerator<'a> { self.special_functions.insert_new_function( msg_func_name.to_string(), - Term::Error.delayed_trace(Term::string(msg)), + Term::Error.delayed_trace(Term::string(msg)).delay(), void(), ); @@ -3072,7 +3077,7 @@ impl<'a> CodeGenerator<'a> { kind: AssignmentKind::expect(), remove_unused: false, full_check: true, - otherwise, + otherwise: otherwise_delayed, }, ); @@ -4835,7 +4840,8 @@ impl<'a> CodeGenerator<'a> { ) .apply(constr), ) - .delayed_if_then_else(term, otherwise); + .if_then_else(term.delay(), otherwise) + .force(); Some(term) } @@ -4846,9 +4852,9 @@ impl<'a> CodeGenerator<'a> { let otherwise = arg_stack.pop().unwrap(); if is_true { - term = value.delayed_if_then_else(term, otherwise) + term = value.if_then_else(term.delay(), otherwise).force() } else { - term = value.delayed_if_then_else(otherwise, term) + term = value.if_then_else(otherwise, term.delay()).force() } Some(term) } @@ -5339,7 +5345,8 @@ impl<'a> CodeGenerator<'a> { .use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string()), ) .apply(value) - .delayed_choose_list(term, otherwise); + .choose_list(term.delay(), otherwise) + .force(); Some(term) } @@ -5349,7 +5356,7 @@ impl<'a> CodeGenerator<'a> { let mut term = arg_stack.pop().unwrap(); let otherwise = arg_stack.pop().unwrap(); - term = value.delayed_choose_list(term, otherwise); + term = value.choose_list(term.delay(), otherwise).force(); Some(term) } diff --git a/crates/aiken-lang/src/gen_uplc/builder.rs b/crates/aiken-lang/src/gen_uplc/builder.rs index 1ffb9365..11df41f8 100644 --- a/crates/aiken-lang/src/gen_uplc/builder.rs +++ b/crates/aiken-lang/src/gen_uplc/builder.rs @@ -1038,117 +1038,138 @@ pub fn unknown_data_to_type(term: Term, field_type: &Type) -> Term { pub fn unknown_data_to_type_otherwise( term: Term, field_type: &Type, - error_term: Term, + otherwise_delayed: Term, ) -> Term { let uplc_type = field_type.get_uplc_type(); match uplc_type { Some(UplcType::Integer) => Term::var("__val") - .delayed_choose_data( - error_term.clone(), - error_term.clone(), - error_term.clone(), - Term::un_i_data().apply(Term::var("__val")), - error_term.clone(), + .choose_data( + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + Term::un_i_data().apply(Term::var("__val")).delay(), + otherwise_delayed.clone(), ) + .force() .lambda("__val") .apply(term), Some(UplcType::ByteString) => Term::var("__val") - .delayed_choose_data( - error_term.clone(), - error_term.clone(), - error_term.clone(), - error_term.clone(), - Term::un_b_data().apply(Term::var("__val")), + .choose_data( + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + Term::un_b_data().apply(Term::var("__val")).delay(), ) + .force() .lambda("__val") .apply(term), Some(UplcType::String) => Term::var("__val") - .delayed_choose_data( - error_term.clone(), - error_term.clone(), - error_term.clone(), - error_term.clone(), - Term::decode_utf8().apply(Term::un_b_data().apply(Term::var("__val"))), + .choose_data( + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + Term::decode_utf8() + .apply(Term::un_b_data().apply(Term::var("__val"))) + .delay(), ) + .force() .lambda("__val") .apply(term), Some(UplcType::List(_)) if field_type.is_map() => Term::var("__val") - .delayed_choose_data( - error_term.clone(), - Term::unmap_data().apply(Term::var("__val")), - error_term.clone(), - error_term.clone(), - error_term.clone(), + .choose_data( + otherwise_delayed.clone(), + Term::unmap_data().apply(Term::var("__val")).delay(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), ) + .force() .lambda("__val") .apply(term), Some(UplcType::List(_)) => Term::var("__val") - .delayed_choose_data( - error_term.clone(), - error_term.clone(), - Term::unlist_data().apply(Term::var("__val")), - error_term.clone(), - error_term.clone(), + .choose_data( + otherwise_delayed.clone(), + otherwise_delayed.clone(), + Term::unlist_data().apply(Term::var("__val")).delay(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), ) + .force() .lambda("__val") .apply(term), Some(UplcType::Bls12_381G1Element) => Term::var("__val") - .delayed_choose_data( - error_term.clone(), - error_term.clone(), - error_term.clone(), - error_term.clone(), - Term::bls12_381_g1_uncompress().apply(Term::un_b_data().apply(Term::var("__val"))), + .choose_data( + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + Term::bls12_381_g1_uncompress() + .apply(Term::un_b_data().apply(Term::var("__val"))) + .delay(), ) + .force() .lambda("__val") .apply(term), Some(UplcType::Bls12_381G2Element) => Term::var("__val") - .delayed_choose_data( - error_term.clone(), - error_term.clone(), - error_term.clone(), - error_term.clone(), - Term::bls12_381_g2_uncompress().apply(Term::un_b_data().apply(Term::var("__val"))), + .choose_data( + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + Term::bls12_381_g2_uncompress() + .apply(Term::un_b_data().apply(Term::var("__val"))) + .delay(), ) + .force() .lambda("__val") .apply(term), Some(UplcType::Bls12_381MlResult) => panic!("ML Result not supported"), Some(UplcType::Pair(_, _)) => Term::var("__val") - .delayed_choose_data( - error_term.clone(), - error_term.clone(), + .choose_data( + otherwise_delayed.clone(), + otherwise_delayed.clone(), Term::var("__list_data") - .delayed_choose_list( - error_term.clone(), + .choose_list( + otherwise_delayed.clone(), Term::var("__tail") - .delayed_choose_list( - error_term.clone(), + .choose_list( + otherwise_delayed.clone(), Term::tail_list() .apply(Term::var("__tail")) - .delayed_choose_list( + .choose_list( Term::mk_pair_data() .apply( Term::head_list().apply(Term::var("__list_data")), ) - .apply(Term::head_list().apply(Term::var("__tail"))), - error_term.clone(), - ), + .apply(Term::head_list().apply(Term::var("__tail"))) + .delay(), + otherwise_delayed.clone(), + ) + .force() + .delay(), ) + .force() .lambda("__tail") - .apply(Term::tail_list().apply(Term::var("__list_data"))), + .apply(Term::tail_list().apply(Term::var("__list_data"))) + .delay(), ) + .force() .lambda("__list_data") - .apply(Term::unlist_data().apply(Term::var("__val"))), - error_term.clone(), - error_term.clone(), + .apply(Term::unlist_data().apply(Term::var("__val"))) + .delay(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), ) + .force() .lambda("__val") .apply(term), Some(UplcType::Bool) => Term::var("__val") - .delayed_choose_data( + .choose_data( Term::snd_pair() .apply(Term::var("__pair__")) .delayed_choose_list( @@ -1160,48 +1181,56 @@ pub fn unknown_data_to_type_otherwise( Term::equals_integer() .apply(Term::integer(0.into())) .apply(Term::fst_pair().apply(Term::var("__pair__"))) - .delayed_if_then_else(Term::bool(false), error_term.clone()), + .delayed_if_then_else( + Term::bool(false), + otherwise_delayed.clone(), + ), ), - error_term.clone(), + otherwise_delayed.clone(), ) .lambda("__pair__") - .apply(Term::unconstr_data().apply(Term::var("__val"))), - error_term.clone(), - error_term.clone(), - error_term.clone(), - error_term.clone(), + .apply(Term::unconstr_data().apply(Term::var("__val"))) + .delay(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), ) + .force() .lambda("__val") .apply(term), Some(UplcType::Unit) => Term::var("__val") - .delayed_choose_data( + .choose_data( Term::equals_integer() .apply(Term::integer(0.into())) .apply(Term::fst_pair().apply(Term::unconstr_data().apply(Term::var("__val")))) .delayed_if_then_else( Term::snd_pair() .apply(Term::unconstr_data().apply(Term::var("__val"))) - .delayed_choose_list(Term::unit(), error_term.clone()), - error_term.clone(), - ), - error_term.clone(), - error_term.clone(), - error_term.clone(), - error_term.clone(), + .delayed_choose_list(Term::unit(), otherwise_delayed.clone()), + otherwise_delayed.clone(), + ) + .delay(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), ) + .force() .lambda("__val") .apply(term), Some(UplcType::Data) => term, // constr type None => Term::var("__val") - .delayed_choose_data( - Term::var("__val"), - error_term.clone(), - error_term.clone(), - error_term.clone(), - error_term.clone(), + .choose_data( + Term::var("__val").delay(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), + otherwise_delayed.clone(), ) + .force() .lambda("__val") .apply(term), } @@ -1362,7 +1391,7 @@ pub fn list_access_to_uplc( term: Term, is_list_accessor: bool, expect_level: ExpectLevel, - otherwise: Term, + otherwise_delayed: Term, ) -> Term { let names_len = names_types_ids.len(); @@ -1393,7 +1422,8 @@ pub fn list_access_to_uplc( } return Term::var("empty_list") - .delayed_choose_list(term, otherwise) + .choose_list(term.delay(), otherwise_delayed) + .force() .lambda("empty_list"); } @@ -1412,7 +1442,7 @@ pub fn list_access_to_uplc( Term::head_list().apply(Term::var(tail_name.to_string())) } else if matches!(expect_level, ExpectLevel::Full) { // Expect level is full so we have an unknown piece of data to cast - if otherwise == Term::Error { + if otherwise_delayed == Term::Error { unknown_data_to_type( Term::head_list().apply(Term::var(tail_name.to_string())), &tipo.to_owned(), @@ -1421,7 +1451,7 @@ pub fn list_access_to_uplc( unknown_data_to_type_otherwise( Term::head_list().apply(Term::var(tail_name.to_string())), &tipo.to_owned(), - otherwise.clone(), + otherwise_delayed.clone(), ) } } else { @@ -1456,36 +1486,40 @@ pub fn list_access_to_uplc( ExpectLevel::None => acc.lambda(name).apply(head_item).lambda(tail_name), ExpectLevel::Full | ExpectLevel::Items => { - if otherwise == Term::Error && tail_present { + if otherwise_delayed == Term::Error && tail_present { // No need to check last item if tail was present acc.lambda(name).apply(head_item).lambda(tail_name) } else if tail_present { // Custom error instead of trying to do head_item on a possibly empty list. Term::var(tail_name.to_string()) - .delayed_choose_list( - otherwise.clone(), - acc.lambda(name).apply(head_item), + .choose_list( + otherwise_delayed.clone(), + acc.lambda(name).apply(head_item).delay(), ) + .force() .lambda(tail_name) - } else if otherwise == Term::Error { + } else if otherwise_delayed == Term::Error { // Check head is last item in this list Term::tail_list() .apply(Term::var(tail_name.to_string())) - .delayed_choose_list(acc, otherwise.clone()) + .choose_list(acc.delay(), otherwise_delayed.clone()) + .force() .lambda(name) .apply(head_item) .lambda(tail_name) } else { // Custom error if list is not empty after this head Term::var(tail_name.to_string()) - .delayed_choose_list( - otherwise.clone(), + .choose_list( + otherwise_delayed.clone(), Term::tail_list() .apply(Term::var(tail_name.to_string())) - .delayed_choose_list(acc, otherwise.clone()) + .choose_list(acc.delay(), otherwise_delayed.clone()) + .force() .lambda(name) .apply(head_item), ) + .force() .lambda(tail_name) } } @@ -1498,7 +1532,8 @@ pub fn list_access_to_uplc( let head_item = head_item(name, tipo, &tail_name); - if matches!(expect_level, ExpectLevel::None) || otherwise == Term::Error { + if matches!(expect_level, ExpectLevel::None) || otherwise_delayed == Term::Error + { acc.apply(Term::tail_list().apply(Term::var(tail_name.to_string()))) .lambda(name) .apply(head_item) @@ -1506,14 +1541,16 @@ pub fn list_access_to_uplc( } else { // case for a custom error if the list is empty at this point Term::var(tail_name.to_string()) - .delayed_choose_list( - otherwise.clone(), + .choose_list( + otherwise_delayed.clone(), acc.apply( Term::tail_list().apply(Term::var(tail_name.to_string())), ) .lambda(name) - .apply(head_item), + .apply(head_item) + .delay(), ) + .force() .lambda(tail_name) } }