delay otherwise branch to prevent premature errors

This commit is contained in:
microproofs 2024-06-12 13:31:21 -04:00 committed by Lucas
parent df939e20ce
commit e09f6bbc87
2 changed files with 164 additions and 120 deletions

View File

@ -259,7 +259,7 @@ impl<'a> CodeGenerator<'a> {
let air_value = self.build(value, module_build_name, &[]); let air_value = self.build(value, module_build_name, &[]);
let otherwise = match (self.tracing, kind) { let otherwise_delayed = match (self.tracing, kind) {
( (
TraceLevel::Silent, TraceLevel::Silent,
AssignmentKind::Let { .. } | AssignmentKind::Expect { .. }, AssignmentKind::Let { .. } | AssignmentKind::Expect { .. },
@ -285,7 +285,7 @@ impl<'a> CodeGenerator<'a> {
self.special_functions.insert_new_function( self.special_functions.insert_new_function(
msg_func_name.clone(), msg_func_name.clone(),
Term::Error.delayed_trace(Term::string(msg)), Term::Error.delayed_trace(Term::string(msg)).delay(),
void(), void(),
); );
@ -307,7 +307,7 @@ impl<'a> CodeGenerator<'a> {
kind: *kind, kind: *kind,
remove_unused: kind.is_let(), remove_unused: kind.is_let(),
full_check: !tipo.is_data() && value.tipo().is_data() && kind.is_expect(), full_check: !tipo.is_data() && value.tipo().is_data() && kind.is_expect(),
otherwise, otherwise: otherwise_delayed,
}, },
) )
} else { } else {
@ -645,7 +645,11 @@ impl<'a> CodeGenerator<'a> {
let body = self.build(&branch.body, module_build_name, &[]); let body = self.build(&branch.body, module_build_name, &[]);
match &branch.is { match &branch.is {
Some(pattern) => self.assignment( 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, pattern,
condition, condition,
body, body,
@ -655,9 +659,10 @@ impl<'a> CodeGenerator<'a> {
kind: AssignmentKind::Expect { backpassing: () }, kind: AssignmentKind::Expect { backpassing: () },
remove_unused: false, remove_unused: false,
full_check: true, full_check: true,
otherwise: acc, otherwise: AirTree::local_var("acc_var", void()),
}, },
), ),
),
None => AirTree::if_branch(tipo.clone(), condition, body, acc), 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 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::Silent => AirTree::error(void(), false),
TraceLevel::Compact | TraceLevel::Verbose => { TraceLevel::Compact | TraceLevel::Verbose => {
let msg = match self.tracing { let msg = match self.tracing {
@ -3051,7 +3056,7 @@ impl<'a> CodeGenerator<'a> {
self.special_functions.insert_new_function( self.special_functions.insert_new_function(
msg_func_name.to_string(), msg_func_name.to_string(),
Term::Error.delayed_trace(Term::string(msg)), Term::Error.delayed_trace(Term::string(msg)).delay(),
void(), void(),
); );
@ -3072,7 +3077,7 @@ impl<'a> CodeGenerator<'a> {
kind: AssignmentKind::expect(), kind: AssignmentKind::expect(),
remove_unused: false, remove_unused: false,
full_check: true, full_check: true,
otherwise, otherwise: otherwise_delayed,
}, },
); );
@ -4835,7 +4840,8 @@ impl<'a> CodeGenerator<'a> {
) )
.apply(constr), .apply(constr),
) )
.delayed_if_then_else(term, otherwise); .if_then_else(term.delay(), otherwise)
.force();
Some(term) Some(term)
} }
@ -4846,9 +4852,9 @@ impl<'a> CodeGenerator<'a> {
let otherwise = arg_stack.pop().unwrap(); let otherwise = arg_stack.pop().unwrap();
if is_true { if is_true {
term = value.delayed_if_then_else(term, otherwise) term = value.if_then_else(term.delay(), otherwise).force()
} else { } else {
term = value.delayed_if_then_else(otherwise, term) term = value.if_then_else(otherwise, term.delay()).force()
} }
Some(term) Some(term)
} }
@ -5339,7 +5345,8 @@ impl<'a> CodeGenerator<'a> {
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string()), .use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string()),
) )
.apply(value) .apply(value)
.delayed_choose_list(term, otherwise); .choose_list(term.delay(), otherwise)
.force();
Some(term) Some(term)
} }
@ -5349,7 +5356,7 @@ impl<'a> CodeGenerator<'a> {
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
let otherwise = 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) Some(term)
} }

View File

@ -1038,117 +1038,138 @@ pub fn unknown_data_to_type(term: Term<Name>, field_type: &Type) -> Term<Name> {
pub fn unknown_data_to_type_otherwise( pub fn unknown_data_to_type_otherwise(
term: Term<Name>, term: Term<Name>,
field_type: &Type, field_type: &Type,
error_term: Term<Name>, otherwise_delayed: Term<Name>,
) -> Term<Name> { ) -> Term<Name> {
let uplc_type = field_type.get_uplc_type(); let uplc_type = field_type.get_uplc_type();
match uplc_type { match uplc_type {
Some(UplcType::Integer) => Term::var("__val") Some(UplcType::Integer) => Term::var("__val")
.delayed_choose_data( .choose_data(
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
Term::un_i_data().apply(Term::var("__val")), Term::un_i_data().apply(Term::var("__val")).delay(),
error_term.clone(), otherwise_delayed.clone(),
) )
.force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(term),
Some(UplcType::ByteString) => Term::var("__val") Some(UplcType::ByteString) => Term::var("__val")
.delayed_choose_data( .choose_data(
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
Term::un_b_data().apply(Term::var("__val")), Term::un_b_data().apply(Term::var("__val")).delay(),
) )
.force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(term),
Some(UplcType::String) => Term::var("__val") Some(UplcType::String) => Term::var("__val")
.delayed_choose_data( .choose_data(
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
Term::decode_utf8().apply(Term::un_b_data().apply(Term::var("__val"))), Term::decode_utf8()
.apply(Term::un_b_data().apply(Term::var("__val")))
.delay(),
) )
.force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(term),
Some(UplcType::List(_)) if field_type.is_map() => Term::var("__val") Some(UplcType::List(_)) if field_type.is_map() => Term::var("__val")
.delayed_choose_data( .choose_data(
error_term.clone(), otherwise_delayed.clone(),
Term::unmap_data().apply(Term::var("__val")), Term::unmap_data().apply(Term::var("__val")).delay(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
) )
.force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(term),
Some(UplcType::List(_)) => Term::var("__val") Some(UplcType::List(_)) => Term::var("__val")
.delayed_choose_data( .choose_data(
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
Term::unlist_data().apply(Term::var("__val")), Term::unlist_data().apply(Term::var("__val")).delay(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
) )
.force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(term),
Some(UplcType::Bls12_381G1Element) => Term::var("__val") Some(UplcType::Bls12_381G1Element) => Term::var("__val")
.delayed_choose_data( .choose_data(
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
Term::bls12_381_g1_uncompress().apply(Term::un_b_data().apply(Term::var("__val"))), Term::bls12_381_g1_uncompress()
.apply(Term::un_b_data().apply(Term::var("__val")))
.delay(),
) )
.force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(term),
Some(UplcType::Bls12_381G2Element) => Term::var("__val") Some(UplcType::Bls12_381G2Element) => Term::var("__val")
.delayed_choose_data( .choose_data(
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
Term::bls12_381_g2_uncompress().apply(Term::un_b_data().apply(Term::var("__val"))), Term::bls12_381_g2_uncompress()
.apply(Term::un_b_data().apply(Term::var("__val")))
.delay(),
) )
.force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(term),
Some(UplcType::Bls12_381MlResult) => panic!("ML Result not supported"), Some(UplcType::Bls12_381MlResult) => panic!("ML Result not supported"),
Some(UplcType::Pair(_, _)) => Term::var("__val") Some(UplcType::Pair(_, _)) => Term::var("__val")
.delayed_choose_data( .choose_data(
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
Term::var("__list_data") Term::var("__list_data")
.delayed_choose_list( .choose_list(
error_term.clone(), otherwise_delayed.clone(),
Term::var("__tail") Term::var("__tail")
.delayed_choose_list( .choose_list(
error_term.clone(), otherwise_delayed.clone(),
Term::tail_list() Term::tail_list()
.apply(Term::var("__tail")) .apply(Term::var("__tail"))
.delayed_choose_list( .choose_list(
Term::mk_pair_data() Term::mk_pair_data()
.apply( .apply(
Term::head_list().apply(Term::var("__list_data")), Term::head_list().apply(Term::var("__list_data")),
) )
.apply(Term::head_list().apply(Term::var("__tail"))), .apply(Term::head_list().apply(Term::var("__tail")))
error_term.clone(), .delay(),
), otherwise_delayed.clone(),
) )
.force()
.delay(),
)
.force()
.lambda("__tail") .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") .lambda("__list_data")
.apply(Term::unlist_data().apply(Term::var("__val"))), .apply(Term::unlist_data().apply(Term::var("__val")))
error_term.clone(), .delay(),
error_term.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(),
) )
.force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(term),
Some(UplcType::Bool) => Term::var("__val") Some(UplcType::Bool) => Term::var("__val")
.delayed_choose_data( .choose_data(
Term::snd_pair() Term::snd_pair()
.apply(Term::var("__pair__")) .apply(Term::var("__pair__"))
.delayed_choose_list( .delayed_choose_list(
@ -1160,48 +1181,56 @@ pub fn unknown_data_to_type_otherwise(
Term::equals_integer() Term::equals_integer()
.apply(Term::integer(0.into())) .apply(Term::integer(0.into()))
.apply(Term::fst_pair().apply(Term::var("__pair__"))) .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__") .lambda("__pair__")
.apply(Term::unconstr_data().apply(Term::var("__val"))), .apply(Term::unconstr_data().apply(Term::var("__val")))
error_term.clone(), .delay(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
otherwise_delayed.clone(),
) )
.force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(term),
Some(UplcType::Unit) => Term::var("__val") Some(UplcType::Unit) => Term::var("__val")
.delayed_choose_data( .choose_data(
Term::equals_integer() Term::equals_integer()
.apply(Term::integer(0.into())) .apply(Term::integer(0.into()))
.apply(Term::fst_pair().apply(Term::unconstr_data().apply(Term::var("__val")))) .apply(Term::fst_pair().apply(Term::unconstr_data().apply(Term::var("__val"))))
.delayed_if_then_else( .delayed_if_then_else(
Term::snd_pair() Term::snd_pair()
.apply(Term::unconstr_data().apply(Term::var("__val"))) .apply(Term::unconstr_data().apply(Term::var("__val")))
.delayed_choose_list(Term::unit(), error_term.clone()), .delayed_choose_list(Term::unit(), otherwise_delayed.clone()),
error_term.clone(), otherwise_delayed.clone(),
),
error_term.clone(),
error_term.clone(),
error_term.clone(),
error_term.clone(),
) )
.delay(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
)
.force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(term),
Some(UplcType::Data) => term, Some(UplcType::Data) => term,
// constr type // constr type
None => Term::var("__val") None => Term::var("__val")
.delayed_choose_data( .choose_data(
Term::var("__val"), Term::var("__val").delay(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
error_term.clone(), otherwise_delayed.clone(),
) )
.force()
.lambda("__val") .lambda("__val")
.apply(term), .apply(term),
} }
@ -1362,7 +1391,7 @@ pub fn list_access_to_uplc(
term: Term<Name>, term: Term<Name>,
is_list_accessor: bool, is_list_accessor: bool,
expect_level: ExpectLevel, expect_level: ExpectLevel,
otherwise: Term<Name>, otherwise_delayed: Term<Name>,
) -> Term<Name> { ) -> Term<Name> {
let names_len = names_types_ids.len(); let names_len = names_types_ids.len();
@ -1393,7 +1422,8 @@ pub fn list_access_to_uplc(
} }
return Term::var("empty_list") return Term::var("empty_list")
.delayed_choose_list(term, otherwise) .choose_list(term.delay(), otherwise_delayed)
.force()
.lambda("empty_list"); .lambda("empty_list");
} }
@ -1412,7 +1442,7 @@ pub fn list_access_to_uplc(
Term::head_list().apply(Term::var(tail_name.to_string())) Term::head_list().apply(Term::var(tail_name.to_string()))
} else if matches!(expect_level, ExpectLevel::Full) { } else if matches!(expect_level, ExpectLevel::Full) {
// Expect level is full so we have an unknown piece of data to cast // 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( unknown_data_to_type(
Term::head_list().apply(Term::var(tail_name.to_string())), Term::head_list().apply(Term::var(tail_name.to_string())),
&tipo.to_owned(), &tipo.to_owned(),
@ -1421,7 +1451,7 @@ pub fn list_access_to_uplc(
unknown_data_to_type_otherwise( unknown_data_to_type_otherwise(
Term::head_list().apply(Term::var(tail_name.to_string())), Term::head_list().apply(Term::var(tail_name.to_string())),
&tipo.to_owned(), &tipo.to_owned(),
otherwise.clone(), otherwise_delayed.clone(),
) )
} }
} else { } else {
@ -1456,36 +1486,40 @@ pub fn list_access_to_uplc(
ExpectLevel::None => acc.lambda(name).apply(head_item).lambda(tail_name), ExpectLevel::None => acc.lambda(name).apply(head_item).lambda(tail_name),
ExpectLevel::Full | ExpectLevel::Items => { 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 // No need to check last item if tail was present
acc.lambda(name).apply(head_item).lambda(tail_name) acc.lambda(name).apply(head_item).lambda(tail_name)
} else if tail_present { } else if tail_present {
// Custom error instead of trying to do head_item on a possibly empty list. // Custom error instead of trying to do head_item on a possibly empty list.
Term::var(tail_name.to_string()) Term::var(tail_name.to_string())
.delayed_choose_list( .choose_list(
otherwise.clone(), otherwise_delayed.clone(),
acc.lambda(name).apply(head_item), acc.lambda(name).apply(head_item).delay(),
) )
.force()
.lambda(tail_name) .lambda(tail_name)
} else if otherwise == Term::Error { } else if otherwise_delayed == Term::Error {
// Check head is last item in this list // Check head is last item in this list
Term::tail_list() Term::tail_list()
.apply(Term::var(tail_name.to_string())) .apply(Term::var(tail_name.to_string()))
.delayed_choose_list(acc, otherwise.clone()) .choose_list(acc.delay(), otherwise_delayed.clone())
.force()
.lambda(name) .lambda(name)
.apply(head_item) .apply(head_item)
.lambda(tail_name) .lambda(tail_name)
} else { } else {
// Custom error if list is not empty after this head // Custom error if list is not empty after this head
Term::var(tail_name.to_string()) Term::var(tail_name.to_string())
.delayed_choose_list( .choose_list(
otherwise.clone(), otherwise_delayed.clone(),
Term::tail_list() Term::tail_list()
.apply(Term::var(tail_name.to_string())) .apply(Term::var(tail_name.to_string()))
.delayed_choose_list(acc, otherwise.clone()) .choose_list(acc.delay(), otherwise_delayed.clone())
.force()
.lambda(name) .lambda(name)
.apply(head_item), .apply(head_item),
) )
.force()
.lambda(tail_name) .lambda(tail_name)
} }
} }
@ -1498,7 +1532,8 @@ pub fn list_access_to_uplc(
let head_item = head_item(name, tipo, &tail_name); 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()))) acc.apply(Term::tail_list().apply(Term::var(tail_name.to_string())))
.lambda(name) .lambda(name)
.apply(head_item) .apply(head_item)
@ -1506,14 +1541,16 @@ pub fn list_access_to_uplc(
} else { } else {
// case for a custom error if the list is empty at this point // case for a custom error if the list is empty at this point
Term::var(tail_name.to_string()) Term::var(tail_name.to_string())
.delayed_choose_list( .choose_list(
otherwise.clone(), otherwise_delayed.clone(),
acc.apply( acc.apply(
Term::tail_list().apply(Term::var(tail_name.to_string())), Term::tail_list().apply(Term::var(tail_name.to_string())),
) )
.lambda(name) .lambda(name)
.apply(head_item), .apply(head_item)
.delay(),
) )
.force()
.lambda(tail_name) .lambda(tail_name)
} }
} }