feat: expects now print the line of code that failed

This commit is contained in:
microproofs 2024-01-03 01:34:10 -05:00 committed by Kasey
parent 412945af3a
commit 355e38d6e2
4 changed files with 310 additions and 236 deletions

View File

@ -29,7 +29,7 @@ use crate::{
find_and_replace_generics, find_list_clause_or_default_first, get_arg_type_name, find_and_replace_generics, find_list_clause_or_default_first, get_arg_type_name,
get_generic_id_and_type, get_src_code_by_span, get_variant_name, monomorphize, get_generic_id_and_type, get_src_code_by_span, get_variant_name, monomorphize,
pattern_has_conditions, wrap_as_multi_validator, wrap_validator_condition, CodeGenFunction, pattern_has_conditions, wrap_as_multi_validator, wrap_validator_condition, CodeGenFunction,
SpecificClause, CONSTR_INDEX_MISMATCH, SpecificClause,
}, },
tipo::{ tipo::{
ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor,
@ -44,8 +44,7 @@ use self::{
cast_validator_args, constants_ir, convert_type_to_data, extract_constant, cast_validator_args, constants_ir, convert_type_to_data, extract_constant,
lookup_data_type_by_tipo, modify_cyclic_calls, modify_self_calls, rearrange_list_clauses, lookup_data_type_by_tipo, modify_cyclic_calls, modify_self_calls, rearrange_list_clauses,
AssignmentProperties, ClauseProperties, CodeGenSpecialFuncs, CycleFunctionNames, AssignmentProperties, ClauseProperties, CodeGenSpecialFuncs, CycleFunctionNames,
DataTypeKey, FunctionAccessKey, HoistableFunction, Variant, CONSTR_NOT_EMPTY, DataTypeKey, FunctionAccessKey, HoistableFunction, Variant,
LIST_NOT_EMPTY, TOO_MANY_ITEMS,
}, },
tree::{AirExpression, AirTree, TreePath}, tree::{AirExpression, AirTree, TreePath},
}; };
@ -133,7 +132,10 @@ impl<'a> CodeGenerator<'a> {
air_tree_fun = wrap_validator_condition(air_tree_fun, self.tracing); air_tree_fun = wrap_validator_condition(air_tree_fun, self.tracing);
let mut validator_args_tree = self.check_validator_args(&fun.arguments, true, air_tree_fun); let src_code = self.module_src.get(module_name).unwrap().clone();
let mut validator_args_tree =
self.check_validator_args(&fun.arguments, true, air_tree_fun, &src_code);
validator_args_tree = AirTree::no_op().hoist_over(validator_args_tree); validator_args_tree = AirTree::no_op().hoist_over(validator_args_tree);
@ -153,7 +155,7 @@ impl<'a> CodeGenerator<'a> {
air_tree_fun_other = wrap_validator_condition(air_tree_fun_other, self.tracing); air_tree_fun_other = wrap_validator_condition(air_tree_fun_other, self.tracing);
let mut validator_args_tree_other = let mut validator_args_tree_other =
self.check_validator_args(&other.arguments, true, air_tree_fun_other); self.check_validator_args(&other.arguments, true, air_tree_fun_other, &src_code);
validator_args_tree_other = AirTree::no_op().hoist_over(validator_args_tree_other); validator_args_tree_other = AirTree::no_op().hoist_over(validator_args_tree_other);
@ -173,8 +175,11 @@ impl<'a> CodeGenerator<'a> {
}; };
// Special Case with multi_validators // Special Case with multi_validators
self.special_functions.use_function(CONSTR_FIELDS_EXPOSER); self.special_functions
self.special_functions.use_function(CONSTR_INDEX_EXPOSER); .use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string());
self.special_functions
.use_function_uplc(CONSTR_INDEX_EXPOSER.to_string());
term = wrap_as_multi_validator(spend, mint, self.tracing, spend_name, mint_name); term = wrap_as_multi_validator(spend, mint, self.tracing, spend_name, mint_name);
} }
@ -463,6 +468,22 @@ impl<'a> CodeGenerator<'a> {
let air_value = self.build(value, module_name); let air_value = self.build(value, module_name);
let msg = get_src_code_by_span(module_name, location, &self.module_src);
let msg_func_name = msg.split_whitespace().join("");
self.special_functions.insert_new_function(
msg_func_name.clone(),
Term::string(msg),
string(),
);
let msg_func = if self.tracing {
self.special_functions.use_function_tree(msg_func_name)
} else {
AirTree::no_op()
};
self.assignment( self.assignment(
pattern, pattern,
air_value, air_value,
@ -472,8 +493,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(),
location: *location, msg_func,
module_name: module_name.clone(),
}, },
) )
} }
@ -514,8 +534,7 @@ impl<'a> CodeGenerator<'a> {
kind: AssignmentKind::Let, kind: AssignmentKind::Let,
remove_unused: false, remove_unused: false,
full_check: false, full_check: false,
location: Span::empty(), msg_func: AirTree::no_op(),
module_name: module_name.clone(),
}, },
); );
@ -621,10 +640,8 @@ impl<'a> CodeGenerator<'a> {
} }
let list_of_fields = AirTree::call( let list_of_fields = AirTree::call(
AirTree::local_var( self.special_functions
self.special_functions.use_function(CONSTR_FIELDS_EXPOSER), .use_function_tree(CONSTR_FIELDS_EXPOSER.to_string()),
void(),
),
list(data()), list(data()),
vec![self.build(record, module_name)], vec![self.build(record, module_name)],
); );
@ -828,10 +845,8 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(name, int()), AirTree::local_var(name, int()),
int(), int(),
); );
let msg =
get_src_code_by_span(&props.module_name, &props.location, &self.module_src);
AirTree::assert_bool(true, assignment.hoist_over(expect), msg) AirTree::assert_bool(true, assignment.hoist_over(expect), props.msg_func.clone())
} }
Pattern::Var { name, .. } => { Pattern::Var { name, .. } => {
if props.full_check { if props.full_check {
@ -851,6 +866,7 @@ impl<'a> CodeGenerator<'a> {
val, val,
&mut index_map, &mut index_map,
pattern.location(), pattern.location(),
props.msg_func,
); );
let assign_expect = AirTree::let_assignment("_", expect); let assign_expect = AirTree::let_assignment("_", expect);
@ -890,6 +906,7 @@ impl<'a> CodeGenerator<'a> {
val, val,
&mut index_map, &mut index_map,
pattern.location(), pattern.location(),
props.msg_func,
); );
let assign_expect = AirTree::let_assignment("_", expect); let assign_expect = AirTree::let_assignment("_", expect);
@ -948,8 +965,7 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind, kind: props.kind,
remove_unused: true, remove_unused: true,
full_check: props.full_check, full_check: props.full_check,
location: props.location, msg_func: props.msg_func.clone(),
module_name: props.module_name.clone(),
}, },
) )
} else { } else {
@ -991,8 +1007,7 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind, kind: props.kind,
remove_unused: true, remove_unused: true,
full_check: props.full_check, full_check: props.full_check,
location: props.location, msg_func: props.msg_func.clone(),
module_name: props.module_name.clone(),
}, },
) )
} else { } else {
@ -1005,9 +1020,15 @@ impl<'a> CodeGenerator<'a> {
let names = elems.iter().map(|(name, _)| name.to_string()).collect_vec(); let names = elems.iter().map(|(name, _)| name.to_string()).collect_vec();
let list_access = if elements.is_empty() { let list_access = if elements.is_empty() {
AirTree::list_empty(value) AirTree::list_empty(value, props.msg_func)
} else { } else {
AirTree::list_access(names, tipo.clone(), tail.is_some(), tail.is_none(), value) AirTree::list_access(
names,
tipo.clone(),
tail.is_some(),
value,
Some(props.msg_func),
)
}; };
let mut sequence = vec![list_access]; let mut sequence = vec![list_access];
@ -1027,10 +1048,7 @@ impl<'a> CodeGenerator<'a> {
if tipo.is_bool() { if tipo.is_bool() {
assert!(props.kind.is_expect()); assert!(props.kind.is_expect());
let msg = AirTree::assert_bool(name == "True", value, props.msg_func)
get_src_code_by_span(&props.module_name, &props.location, &self.module_src);
AirTree::assert_bool(name == "True", value, msg)
} else if tipo.is_void() { } else if tipo.is_void() {
AirTree::let_assignment("_", value) AirTree::let_assignment("_", value)
} else { } else {
@ -1062,16 +1080,10 @@ impl<'a> CodeGenerator<'a> {
sequence.push(constructor_val); sequence.push(constructor_val);
let msg = get_src_code_by_span(
&props.module_name,
&props.location,
&self.module_src,
);
let assert_constr = AirTree::assert_constr_index( let assert_constr = AirTree::assert_constr_index(
index, index,
AirTree::local_var(&constructor_name, tipo.clone()), AirTree::local_var(&constructor_name, tipo.clone()),
msg, props.msg_func.clone(),
); );
sequence.push(assert_constr); sequence.push(assert_constr);
@ -1147,8 +1159,7 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind, kind: props.kind,
remove_unused: true, remove_unused: true,
full_check: props.full_check, full_check: props.full_check,
location: props.location, msg_func: props.msg_func.clone(),
module_name: props.module_name.clone(),
}, },
) )
} else { } else {
@ -1169,7 +1180,12 @@ impl<'a> CodeGenerator<'a> {
if check_replaceable_opaque_type(tipo, &self.data_types) { if check_replaceable_opaque_type(tipo, &self.data_types) {
sequence.push(AirTree::let_assignment(&indices[0].1, value)); sequence.push(AirTree::let_assignment(&indices[0].1, value));
} else { } else {
sequence.push(AirTree::fields_expose(indices, props.full_check, value)); let msg = if props.full_check {
Some(props.msg_func)
} else {
None
};
sequence.push(AirTree::fields_expose(indices, value, msg));
} }
sequence.append( sequence.append(
@ -1237,8 +1253,7 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind, kind: props.kind,
remove_unused: true, remove_unused: true,
full_check: props.full_check, full_check: props.full_check,
location: props.location, msg_func: props.msg_func.clone(),
module_name: props.module_name.clone(),
}, },
) )
} else { } else {
@ -1251,14 +1266,15 @@ impl<'a> CodeGenerator<'a> {
let indices = elems.iter().map(|(name, _)| name.to_string()).collect_vec(); let indices = elems.iter().map(|(name, _)| name.to_string()).collect_vec();
let msg = if props.full_check {
Some(props.msg_func)
} else {
None
};
// This `value` is either value param that was passed in or // This `value` is either value param that was passed in or
// local var // local var
sequence.push(AirTree::tuple_access( sequence.push(AirTree::tuple_access(indices, tipo.clone(), value, msg));
indices,
tipo.clone(),
props.full_check,
value,
));
sequence.append(&mut elems.into_iter().map(|(_, field)| field).collect_vec()); sequence.append(&mut elems.into_iter().map(|(_, field)| field).collect_vec());
@ -1273,6 +1289,7 @@ impl<'a> CodeGenerator<'a> {
value: AirTree, value: AirTree,
defined_data_types: &mut IndexMap<String, u64>, defined_data_types: &mut IndexMap<String, u64>,
location: Span, location: Span,
msg_func: AirTree,
) -> AirTree { ) -> AirTree {
assert!(tipo.get_generic().is_none()); assert!(tipo.get_generic().is_none());
let tipo = &convert_opaque_type(tipo, &self.data_types); let tipo = &convert_opaque_type(tipo, &self.data_types);
@ -1298,8 +1315,8 @@ impl<'a> CodeGenerator<'a> {
let tuple_access = AirTree::tuple_access( let tuple_access = AirTree::tuple_access(
vec![fst_name.clone(), snd_name.clone()], vec![fst_name.clone(), snd_name.clone()],
inner_list_type.clone(), inner_list_type.clone(),
false,
AirTree::local_var(&pair_name, inner_list_type.clone()), AirTree::local_var(&pair_name, inner_list_type.clone()),
None,
); );
let expect_fst = self.expect_type_assign( let expect_fst = self.expect_type_assign(
@ -1307,6 +1324,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(fst_name, inner_pair_types[0].clone()), AirTree::local_var(fst_name, inner_pair_types[0].clone()),
defined_data_types, defined_data_types,
location, location,
msg_func.clone(),
); );
let expect_snd = self.expect_type_assign( let expect_snd = self.expect_type_assign(
@ -1314,6 +1332,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(snd_name, inner_pair_types[1].clone()), AirTree::local_var(snd_name, inner_pair_types[1].clone()),
defined_data_types, defined_data_types,
location, location,
msg_func.clone(),
); );
let anon_func_body = AirTree::UnhoistedSequence(vec![ let anon_func_body = AirTree::UnhoistedSequence(vec![
@ -1385,6 +1404,7 @@ impl<'a> CodeGenerator<'a> {
), ),
defined_data_types, defined_data_types,
location, location,
msg_func,
); );
let anon_func_body = expect_item; let anon_func_body = expect_item;
@ -1447,8 +1467,8 @@ impl<'a> CodeGenerator<'a> {
let tuple_access = AirTree::tuple_access( let tuple_access = AirTree::tuple_access(
vec![fst_name.clone(), snd_name.clone()], vec![fst_name.clone(), snd_name.clone()],
tipo.clone(), tipo.clone(),
false,
AirTree::local_var(pair_name, tipo.clone()), AirTree::local_var(pair_name, tipo.clone()),
None,
); );
let expect_fst = self.expect_type_assign( let expect_fst = self.expect_type_assign(
@ -1456,6 +1476,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(fst_name, tuple_inner_types[0].clone()), AirTree::local_var(fst_name, tuple_inner_types[0].clone()),
defined_data_types, defined_data_types,
location, location,
msg_func.clone(),
); );
let expect_snd = self.expect_type_assign( let expect_snd = self.expect_type_assign(
@ -1463,6 +1484,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(snd_name, tuple_inner_types[1].clone()), AirTree::local_var(snd_name, tuple_inner_types[1].clone()),
defined_data_types, defined_data_types,
location, location,
msg_func,
); );
AirTree::UnhoistedSequence(vec![ AirTree::UnhoistedSequence(vec![
@ -1493,6 +1515,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(&tuple_index_name, arg.clone()), AirTree::local_var(&tuple_index_name, arg.clone()),
defined_data_types, defined_data_types,
location, location,
msg_func.clone(),
); );
( (
@ -1510,8 +1533,8 @@ impl<'a> CodeGenerator<'a> {
let tuple_access = AirTree::tuple_access( let tuple_access = AirTree::tuple_access(
tuple_index_names, tuple_index_names,
tipo.clone(), tipo.clone(),
true,
AirTree::local_var(tuple_name, tipo.clone()), AirTree::local_var(tuple_name, tipo.clone()),
Some(msg_func),
); );
let mut tuple_expects = tuple_expect_items let mut tuple_expects = tuple_expect_items
@ -1553,10 +1576,7 @@ impl<'a> CodeGenerator<'a> {
let error_term = if self.tracing { let error_term = if self.tracing {
AirTree::trace( AirTree::trace(
AirTree::local_var( msg_func.clone(),
self.special_functions.use_function(CONSTR_INDEX_MISMATCH),
string(),
),
tipo.clone(), tipo.clone(),
AirTree::error(tipo.clone(), false), AirTree::error(tipo.clone(), false),
) )
@ -1590,6 +1610,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(&arg_name, arg_tipo.clone()), AirTree::local_var(&arg_name, arg_tipo.clone()),
defined_data_types, defined_data_types,
location, location,
msg_func.clone(),
), ),
); );
@ -1610,16 +1631,7 @@ impl<'a> CodeGenerator<'a> {
.collect_vec(); .collect_vec();
if assigns.is_empty() { if assigns.is_empty() {
let empty = AirTree::fields_empty(AirTree::local_var( let empty = AirTree::fields_empty(
format!("__constr_var_span_{}_{}", location.start, location.end),
tipo.clone(),
));
assigns.insert(0, empty);
} else {
let expose = AirTree::fields_expose(
indices,
true,
AirTree::local_var( AirTree::local_var(
format!( format!(
"__constr_var_span_{}_{}", "__constr_var_span_{}_{}",
@ -1627,6 +1639,21 @@ impl<'a> CodeGenerator<'a> {
), ),
tipo.clone(), tipo.clone(),
), ),
msg_func.clone(),
);
assigns.insert(0, empty);
} else {
let expose = AirTree::fields_expose(
indices,
AirTree::local_var(
format!(
"__constr_var_span_{}_{}",
location.start, location.end
),
tipo.clone(),
),
Some(msg_func.clone()),
); );
assigns.insert(0, expose); assigns.insert(0, expose);
@ -2205,8 +2232,11 @@ impl<'a> CodeGenerator<'a> {
defined_heads, defined_heads,
subject_tipo.clone(), subject_tipo.clone(),
tail.is_some(), tail.is_some(),
false,
AirTree::local_var(&props.original_subject_name, subject_tipo.clone()), AirTree::local_var(&props.original_subject_name, subject_tipo.clone()),
// One special usage of list access here
// So for the msg we pass in empty string if tracing is on
// Since check_last_item is false this will never get added to the final uplc anyway
None,
) )
} else { } else {
assert!(defined_tails.len() >= defined_heads.len()); assert!(defined_tails.len() >= defined_heads.len());
@ -2345,11 +2375,11 @@ impl<'a> CodeGenerator<'a> {
} else { } else {
AirTree::fields_expose( AirTree::fields_expose(
indices, indices,
false,
AirTree::local_var( AirTree::local_var(
props.clause_var_name.clone(), props.clause_var_name.clone(),
subject_tipo.clone(), subject_tipo.clone(),
), ),
None,
) )
}; };
@ -2482,8 +2512,8 @@ impl<'a> CodeGenerator<'a> {
AirTree::tuple_access( AirTree::tuple_access(
names, names,
subject_tipo.clone(), subject_tipo.clone(),
false,
AirTree::local_var(&props.original_subject_name, subject_tipo.clone()), AirTree::local_var(&props.original_subject_name, subject_tipo.clone()),
None,
), ),
); );
} }
@ -2668,17 +2698,39 @@ impl<'a> CodeGenerator<'a> {
arguments: &[TypedArg], arguments: &[TypedArg],
has_context: bool, has_context: bool,
body: AirTree, body: AirTree,
src_code: &str,
) -> AirTree { ) -> AirTree {
let checked_args = arguments let checked_args = arguments
.iter() .iter()
.enumerate() .enumerate()
.map(|(index, arg)| { .map(|(index, arg)| {
let arg_name = arg.arg_name.get_variable_name().unwrap_or("_").to_string(); let arg_name = arg.arg_name.get_variable_name().unwrap_or("_").to_string();
let arg_span = arg.location;
if !(has_context && index == arguments.len() - 1) && &arg_name != "_" { if !(has_context && index == arguments.len() - 1) && &arg_name != "_" {
let param = AirTree::local_var(&arg_name, data()); let param = AirTree::local_var(&arg_name, data());
let actual_type = convert_opaque_type(&arg.tipo, &self.data_types); let actual_type = convert_opaque_type(&arg.tipo, &self.data_types);
let msg = src_code
.get(arg_span.start..arg_span.end)
.expect("Out of bounds span")
.to_string();
let msg_func_name = msg.split_whitespace().join("");
self.special_functions.insert_new_function(
msg_func_name.to_string(),
Term::string(msg),
string(),
);
let msg_func = if self.tracing {
self.special_functions.use_function_tree(msg_func_name)
} else {
AirTree::no_op()
};
let assign = self.assignment( let assign = self.assignment(
&Pattern::Var { &Pattern::Var {
location: Span::empty(), location: Span::empty(),
@ -2691,8 +2743,7 @@ impl<'a> CodeGenerator<'a> {
kind: AssignmentKind::Expect, kind: AssignmentKind::Expect,
remove_unused: false, remove_unused: false,
full_check: true, full_check: true,
location: Span::empty(), msg_func,
module_name: String::new(),
}, },
); );
@ -3911,9 +3962,17 @@ impl<'a> CodeGenerator<'a> {
// tipo here refers to the list type while the actual return // tipo here refers to the list type while the actual return
// type is nothing since this is an assignment over some expression // type is nothing since this is an assignment over some expression
tipo, tipo,
check_last_item, is_expect,
} => { } => {
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
let error_term = if self.tracing && is_expect {
let msg = arg_stack.pop().unwrap();
Term::Error.delayed_trace(msg)
} else {
Term::Error
};
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
let list_id = self.id_gen.next(); let list_id = self.id_gen.next();
@ -3937,37 +3996,21 @@ impl<'a> CodeGenerator<'a> {
.collect_vec(); .collect_vec();
if !names_empty { if !names_empty {
let error_term = if self.tracing {
Term::Error.delayed_trace(Term::var(
self.special_functions.use_function(TOO_MANY_ITEMS),
))
} else {
Term::Error
};
term = builder::list_access_to_uplc( term = builder::list_access_to_uplc(
&names_types, &names_types,
&id_list, &id_list,
tail, tail,
0, 0,
term, term,
check_last_item, is_expect && !tail,
true, true,
error_term, error_term,
) )
.apply(value); .apply(value);
arg_stack.push(term); arg_stack.push(term);
} else if check_last_item { } else if is_expect {
let trace_term = if self.tracing { term = value.delayed_choose_list(term, error_term);
Term::Error.delayed_trace(Term::var(
self.special_functions.use_function(LIST_NOT_EMPTY),
))
} else {
Term::Error
};
term = value.delayed_choose_list(term, trace_term);
arg_stack.push(term); arg_stack.push(term);
} else { } else {
@ -4449,37 +4492,44 @@ impl<'a> CodeGenerator<'a> {
arg_stack.push(term); arg_stack.push(term);
} }
Air::AssertConstr { constr_index, msg } => { Air::AssertConstr { constr_index } => {
let constr = arg_stack.pop().unwrap(); let constr = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap();
let trace_term = if self.tracing { let trace_term = if self.tracing {
Term::Error.delayed_trace(Term::string(msg)) let msg = arg_stack.pop().unwrap();
Term::Error.delayed_trace(msg)
} else { } else {
Term::Error Term::Error
}; };
let mut term = arg_stack.pop().unwrap();
term = Term::equals_integer() term = Term::equals_integer()
.apply(Term::integer(constr_index.into())) .apply(Term::integer(constr_index.into()))
.apply( .apply(
Term::var(self.special_functions.use_function(CONSTR_INDEX_EXPOSER)) Term::var(
.apply(constr), self.special_functions
.use_function_uplc(CONSTR_INDEX_EXPOSER.to_string()),
)
.apply(constr),
) )
.delayed_if_then_else(term, trace_term); .delayed_if_then_else(term, trace_term);
arg_stack.push(term); arg_stack.push(term);
} }
Air::AssertBool { is_true, msg } => { Air::AssertBool { is_true } => {
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap();
let trace_term = if self.tracing { let trace_term = if self.tracing {
Term::Error.delayed_trace(Term::string(msg)) let msg = arg_stack.pop().unwrap();
Term::Error.delayed_trace(msg)
} else { } else {
Term::Error Term::Error
}; };
let mut term = arg_stack.pop().unwrap();
if is_true { if is_true {
term = value.delayed_if_then_else(term, trace_term) term = value.delayed_if_then_else(term, trace_term)
} else { } else {
@ -4504,8 +4554,11 @@ impl<'a> CodeGenerator<'a> {
{ {
subject subject
} else { } else {
Term::var(self.special_functions.use_function(CONSTR_INDEX_EXPOSER)) Term::var(
.apply(subject) self.special_functions
.use_function_uplc(CONSTR_INDEX_EXPOSER.to_string()),
)
.apply(subject)
}; };
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
@ -4710,8 +4763,11 @@ impl<'a> CodeGenerator<'a> {
unreachable!() unreachable!()
} else { } else {
Term::equals_integer().apply(checker).apply( Term::equals_integer().apply(checker).apply(
Term::var(self.special_functions.use_function(CONSTR_INDEX_EXPOSER)) Term::var(
.apply(Term::var(subject_name)), self.special_functions
.use_function_uplc(CONSTR_INDEX_EXPOSER.to_string()),
)
.apply(Term::var(subject_name)),
) )
}; };
@ -4850,13 +4906,18 @@ impl<'a> CodeGenerator<'a> {
arg_stack.push(term); arg_stack.push(term);
} }
Air::FieldsExpose { Air::FieldsExpose { indices, is_expect } => {
indices,
check_last_item,
} => {
let mut id_list = vec![]; let mut id_list = vec![];
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
let error_term = if self.tracing && is_expect {
let msg = arg_stack.pop().unwrap();
Term::Error.delayed_trace(msg)
} else {
Term::Error
};
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
let list_id = self.id_gen.next(); let list_id = self.id_gen.next();
@ -4875,43 +4936,33 @@ impl<'a> CodeGenerator<'a> {
.collect_vec(); .collect_vec();
if !indices.is_empty() { if !indices.is_empty() {
let error_term = if self.tracing {
Term::Error.delayed_trace(Term::var(
self.special_functions.use_function(TOO_MANY_ITEMS),
))
} else {
Term::Error
};
term = builder::list_access_to_uplc( term = builder::list_access_to_uplc(
&names_types, &names_types,
&id_list, &id_list,
false, false,
current_index, current_index,
term, term,
check_last_item, is_expect,
false, false,
error_term, error_term,
); );
term = term.apply( term = term.apply(
Term::var(self.special_functions.use_function(CONSTR_FIELDS_EXPOSER)) Term::var(
.apply(value), self.special_functions
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string()),
)
.apply(value),
); );
arg_stack.push(term); arg_stack.push(term);
} else if check_last_item { } else if is_expect {
let trace_term = if self.tracing { term = Term::var(
Term::Error.delayed_trace(Term::var( self.special_functions
self.special_functions.use_function(CONSTR_NOT_EMPTY), .use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string()),
)) )
} else { .apply(value)
Term::Error .delayed_choose_list(term, error_term);
};
term = Term::var(self.special_functions.use_function(CONSTR_FIELDS_EXPOSER))
.apply(value)
.delayed_choose_list(term, trace_term);
arg_stack.push(term); arg_stack.push(term);
} else { } else {
@ -4920,35 +4971,38 @@ impl<'a> CodeGenerator<'a> {
} }
Air::FieldsEmpty => { Air::FieldsEmpty => {
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap();
let trace_term = if self.tracing { let error_term = if self.tracing {
Term::Error.delayed_trace(Term::var( let msg = arg_stack.pop().unwrap();
self.special_functions.use_function(CONSTR_NOT_EMPTY), Term::Error.delayed_trace(msg)
))
} else { } else {
Term::Error Term::Error
}; };
term = Term::var(self.special_functions.use_function(CONSTR_FIELDS_EXPOSER)) let mut term = arg_stack.pop().unwrap();
.apply(value)
.delayed_choose_list(term, trace_term); term = Term::var(
self.special_functions
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string()),
)
.apply(value)
.delayed_choose_list(term, error_term);
arg_stack.push(term); arg_stack.push(term);
} }
Air::ListEmpty => { Air::ListEmpty => {
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap();
let trace_term = if self.tracing { let error_term = if self.tracing {
Term::Error.delayed_trace(Term::var( let msg = arg_stack.pop().unwrap();
self.special_functions.use_function(LIST_NOT_EMPTY), Term::Error.delayed_trace(msg)
))
} else { } else {
Term::Error Term::Error
}; };
term = value.delayed_choose_list(term, trace_term); let mut term = arg_stack.pop().unwrap();
term = value.delayed_choose_list(term, error_term);
arg_stack.push(term); arg_stack.push(term);
} }
@ -5095,8 +5149,11 @@ impl<'a> CodeGenerator<'a> {
} }
term = term.lambda(format!("{tail_name_prefix}_0")).apply( term = term.lambda(format!("{tail_name_prefix}_0")).apply(
Term::var(self.special_functions.use_function(CONSTR_FIELDS_EXPOSER)) Term::var(
.apply(record), self.special_functions
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string()),
)
.apply(record),
); );
arg_stack.push(term); arg_stack.push(term);
@ -5128,10 +5185,18 @@ impl<'a> CodeGenerator<'a> {
Air::TupleAccessor { Air::TupleAccessor {
tipo, tipo,
names, names,
check_last_item, is_expect,
} => { } => {
let inner_types = tipo.get_inner_types(); let inner_types = tipo.get_inner_types();
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
let error_term = if self.tracing && is_expect && !tipo.is_2_tuple() {
let msg = arg_stack.pop().unwrap();
Term::Error.delayed_trace(msg)
} else {
Term::Error
};
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
let list_id = self.id_gen.next(); let list_id = self.id_gen.next();
@ -5169,31 +5234,21 @@ impl<'a> CodeGenerator<'a> {
let names_types = names.into_iter().zip(inner_types).collect_vec(); let names_types = names.into_iter().zip(inner_types).collect_vec();
let error_term = if self.tracing {
Term::Error.delayed_trace(Term::var(
self.special_functions.use_function(TOO_MANY_ITEMS),
))
} else {
Term::Error
};
term = builder::list_access_to_uplc( term = builder::list_access_to_uplc(
&names_types, &names_types,
&id_list, &id_list,
false, false,
0, 0,
term, term,
check_last_item, is_expect,
false, false,
error_term, error_term,
) )
.apply(value); .apply(value);
arg_stack.push(term); arg_stack.push(term);
} else if check_last_item {
unreachable!("HOW DID YOU DO THIS");
} else { } else {
arg_stack.push(term); unreachable!("HOW DID YOU DO THIS");
} }
} }
Air::Trace { .. } => { Air::Trace { .. } => {

View File

@ -89,11 +89,9 @@ pub enum Air {
}, },
AssertConstr { AssertConstr {
constr_index: usize, constr_index: usize,
msg: String,
}, },
AssertBool { AssertBool {
is_true: bool, is_true: bool,
msg: String,
}, },
// When // When
When { When {
@ -154,14 +152,14 @@ pub enum Air {
// Field Access // Field Access
FieldsExpose { FieldsExpose {
indices: Vec<(usize, String, Rc<Type>)>, indices: Vec<(usize, String, Rc<Type>)>,
check_last_item: bool, is_expect: bool,
}, },
// ListAccess // ListAccess
ListAccessor { ListAccessor {
tipo: Rc<Type>, tipo: Rc<Type>,
names: Vec<String>, names: Vec<String>,
tail: bool, tail: bool,
check_last_item: bool, is_expect: bool,
}, },
ListExpose { ListExpose {
tipo: Rc<Type>, tipo: Rc<Type>,
@ -172,7 +170,7 @@ pub enum Air {
TupleAccessor { TupleAccessor {
names: Vec<String>, names: Vec<String>,
tipo: Rc<Type>, tipo: Rc<Type>,
check_last_item: bool, is_expect: bool,
}, },
// Misc. // Misc.
ErrorTerm { ErrorTerm {

View File

@ -18,7 +18,7 @@ use crate::{
AssignmentKind, DataType, Pattern, Span, TypedArg, TypedClause, TypedClauseGuard, AssignmentKind, DataType, Pattern, Span, TypedArg, TypedClause, TypedClauseGuard,
TypedDataType, TypedPattern, TypedDataType, TypedPattern,
}, },
builtins::{bool, void}, builtins::{bool, data, function, int, list, string, void},
expr::TypedExpr, expr::TypedExpr,
tipo::{PatternConstructor, TypeVar, ValueConstructor, ValueConstructorVariant}, tipo::{PatternConstructor, TypeVar, ValueConstructor, ValueConstructorVariant},
}; };
@ -82,8 +82,7 @@ pub struct AssignmentProperties {
pub kind: AssignmentKind, pub kind: AssignmentKind,
pub remove_unused: bool, pub remove_unused: bool,
pub full_check: bool, pub full_check: bool,
pub location: Span, pub msg_func: AirTree,
pub module_name: String,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -193,7 +192,7 @@ impl ClauseProperties {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CodeGenSpecialFuncs { pub struct CodeGenSpecialFuncs {
pub used_funcs: Vec<String>, pub used_funcs: Vec<String>,
pub key_to_func: IndexMap<String, Term<Name>>, pub key_to_func: IndexMap<String, (Term<Name>, Rc<Type>)>,
} }
impl CodeGenSpecialFuncs { impl CodeGenSpecialFuncs {
@ -202,46 +201,30 @@ impl CodeGenSpecialFuncs {
key_to_func.insert( key_to_func.insert(
CONSTR_FIELDS_EXPOSER.to_string(), CONSTR_FIELDS_EXPOSER.to_string(),
Term::snd_pair() (
.apply(Term::unconstr_data().apply(Term::var("__constr_var"))) Term::snd_pair()
.lambda("__constr_var"), .apply(Term::unconstr_data().apply(Term::var("__constr_var")))
.lambda("__constr_var"),
function(vec![data()], list(data())),
),
); );
key_to_func.insert( key_to_func.insert(
CONSTR_INDEX_EXPOSER.to_string(), CONSTR_INDEX_EXPOSER.to_string(),
Term::fst_pair() (
.apply(Term::unconstr_data().apply(Term::var("__constr_var"))) Term::fst_pair()
.lambda("__constr_var"), .apply(Term::unconstr_data().apply(Term::var("__constr_var")))
.lambda("__constr_var"),
function(vec![data()], int()),
),
); );
key_to_func.insert( key_to_func.insert(
TOO_MANY_ITEMS.to_string(), TOO_MANY_ITEMS.to_string(),
Term::string("List/Tuple/Constr contains more items than expected"), (
); Term::string("List/Tuple/Constr contains more items than expected"),
string(),
key_to_func.insert( ),
LIST_NOT_EMPTY.to_string(),
Term::string("Expected no items for List"),
);
key_to_func.insert(
CONSTR_NOT_EMPTY.to_string(),
Term::string("Expected no fields for Constr"),
);
key_to_func.insert(
INCORRECT_BOOLEAN.to_string(),
Term::string("Expected on incorrect Boolean variant"),
);
key_to_func.insert(
INCORRECT_CONSTR.to_string(),
Term::string("Expected on incorrect Constr variant"),
);
key_to_func.insert(
CONSTR_INDEX_MISMATCH.to_string(),
Term::string("Constr index didn't match a type variant"),
); );
CodeGenSpecialFuncs { CodeGenSpecialFuncs {
@ -250,15 +233,26 @@ impl CodeGenSpecialFuncs {
} }
} }
pub fn use_function(&mut self, func_name: &'static str) -> &'static str { pub fn use_function_tree(&mut self, func_name: String) -> AirTree {
if !self.used_funcs.contains(&func_name.to_string()) { if !self.used_funcs.contains(&func_name) {
self.used_funcs.push(func_name.to_string()); self.used_funcs.push(func_name.to_string());
} }
let tipo = self.key_to_func.get(&func_name).unwrap().1.clone();
AirTree::local_var(func_name, tipo)
}
pub fn use_function_uplc(&mut self, func_name: String) -> String {
if !self.used_funcs.contains(&func_name) {
self.used_funcs.push(func_name.to_string());
}
func_name func_name
} }
pub fn get_function(&self, func_name: &String) -> Term<Name> { pub fn get_function(&self, func_name: &String) -> Term<Name> {
self.key_to_func[func_name].clone() self.key_to_func[func_name].0.clone()
} }
pub fn apply_used_functions(&self, mut term: Term<Name>) -> Term<Name> { pub fn apply_used_functions(&self, mut term: Term<Name>) -> Term<Name> {
@ -267,6 +261,18 @@ impl CodeGenSpecialFuncs {
} }
term term
} }
pub fn insert_new_function(
&mut self,
func_name: String,
function: Term<Name>,
function_type: Rc<Type>,
) {
if !self.key_to_func.contains_key(&func_name) {
self.key_to_func
.insert(func_name, (function, function_type));
}
}
} }
impl Default for CodeGenSpecialFuncs { impl Default for CodeGenSpecialFuncs {

View File

@ -127,12 +127,12 @@ pub enum AirStatement {
AssertConstr { AssertConstr {
constr_index: usize, constr_index: usize,
constr: Box<AirTree>, constr: Box<AirTree>,
msg: String, msg: Box<AirTree>,
}, },
AssertBool { AssertBool {
is_true: bool, is_true: bool,
value: Box<AirTree>, value: Box<AirTree>,
msg: String, msg: Box<AirTree>,
}, },
// Clause Guards // Clause Guards
ClauseGuard { ClauseGuard {
@ -154,16 +154,16 @@ pub enum AirStatement {
// Field Access // Field Access
FieldsExpose { FieldsExpose {
indices: Vec<(usize, String, Rc<Type>)>, indices: Vec<(usize, String, Rc<Type>)>,
check_last_item: bool,
record: Box<AirTree>, record: Box<AirTree>,
msg: Box<Option<AirTree>>,
}, },
// List Access // List Access
ListAccessor { ListAccessor {
tipo: Rc<Type>, tipo: Rc<Type>,
names: Vec<String>, names: Vec<String>,
tail: bool, tail: bool,
check_last_item: bool,
list: Box<AirTree>, list: Box<AirTree>,
msg: Box<Option<AirTree>>,
}, },
ListExpose { ListExpose {
tipo: Rc<Type>, tipo: Rc<Type>,
@ -174,15 +174,17 @@ pub enum AirStatement {
TupleAccessor { TupleAccessor {
names: Vec<String>, names: Vec<String>,
tipo: Rc<Type>, tipo: Rc<Type>,
check_last_item: bool,
tuple: Box<AirTree>, tuple: Box<AirTree>,
msg: Box<Option<AirTree>>,
}, },
// Misc. // Misc.
FieldsEmpty { FieldsEmpty {
constr: Box<AirTree>, constr: Box<AirTree>,
msg: Box<AirTree>,
}, },
ListEmpty { ListEmpty {
list: Box<AirTree>, list: Box<AirTree>,
msg: Box<AirTree>,
}, },
NoOp, NoOp,
} }
@ -494,22 +496,22 @@ impl AirTree {
value: value.into(), value: value.into(),
}) })
} }
pub fn assert_constr_index(constr_index: usize, constr: AirTree, msg: String) -> AirTree { pub fn assert_constr_index(constr_index: usize, constr: AirTree, msg: AirTree) -> AirTree {
AirTree::Statement { AirTree::Statement {
statement: AirStatement::AssertConstr { statement: AirStatement::AssertConstr {
constr_index, constr_index,
constr: constr.into(), constr: constr.into(),
msg, msg: msg.into(),
}, },
hoisted_over: None, hoisted_over: None,
} }
} }
pub fn assert_bool(is_true: bool, value: AirTree, msg: String) -> AirTree { pub fn assert_bool(is_true: bool, value: AirTree, msg: AirTree) -> AirTree {
AirTree::Statement { AirTree::Statement {
statement: AirStatement::AssertBool { statement: AirStatement::AssertBool {
is_true, is_true,
value: value.into(), value: value.into(),
msg, msg: msg.into(),
}, },
hoisted_over: None, hoisted_over: None,
} }
@ -714,14 +716,14 @@ impl AirTree {
pub fn fields_expose( pub fn fields_expose(
indices: Vec<(usize, String, Rc<Type>)>, indices: Vec<(usize, String, Rc<Type>)>,
check_last_item: bool,
record: AirTree, record: AirTree,
msg: Option<AirTree>,
) -> AirTree { ) -> AirTree {
AirTree::Statement { AirTree::Statement {
statement: AirStatement::FieldsExpose { statement: AirStatement::FieldsExpose {
indices, indices,
check_last_item,
record: record.into(), record: record.into(),
msg: msg.into(),
}, },
hoisted_over: None, hoisted_over: None,
} }
@ -730,16 +732,16 @@ impl AirTree {
names: Vec<String>, names: Vec<String>,
tipo: Rc<Type>, tipo: Rc<Type>,
tail: bool, tail: bool,
check_last_item: bool,
list: AirTree, list: AirTree,
msg: Option<AirTree>,
) -> AirTree { ) -> AirTree {
AirTree::Statement { AirTree::Statement {
statement: AirStatement::ListAccessor { statement: AirStatement::ListAccessor {
tipo, tipo,
names, names,
tail, tail,
check_last_item,
list: list.into(), list: list.into(),
msg: msg.into(),
}, },
hoisted_over: None, hoisted_over: None,
} }
@ -761,15 +763,15 @@ impl AirTree {
pub fn tuple_access( pub fn tuple_access(
names: Vec<String>, names: Vec<String>,
tipo: Rc<Type>, tipo: Rc<Type>,
check_last_item: bool,
tuple: AirTree, tuple: AirTree,
msg: Option<AirTree>,
) -> AirTree { ) -> AirTree {
AirTree::Statement { AirTree::Statement {
statement: AirStatement::TupleAccessor { statement: AirStatement::TupleAccessor {
names, names,
tipo, tipo,
check_last_item,
tuple: tuple.into(), tuple: tuple.into(),
msg: msg.into(),
}, },
hoisted_over: None, hoisted_over: None,
} }
@ -804,17 +806,21 @@ impl AirTree {
hoisted_over: None, hoisted_over: None,
} }
} }
pub fn fields_empty(constr: AirTree) -> AirTree { pub fn fields_empty(constr: AirTree, msg: AirTree) -> AirTree {
AirTree::Statement { AirTree::Statement {
statement: AirStatement::FieldsEmpty { statement: AirStatement::FieldsEmpty {
constr: constr.into(), constr: constr.into(),
msg: msg.into(),
}, },
hoisted_over: None, hoisted_over: None,
} }
} }
pub fn list_empty(list: AirTree) -> AirTree { pub fn list_empty(list: AirTree, msg: AirTree) -> AirTree {
AirTree::Statement { AirTree::Statement {
statement: AirStatement::ListEmpty { list: list.into() }, statement: AirStatement::ListEmpty {
list: list.into(),
msg: msg.into(),
},
hoisted_over: None, hoisted_over: None,
} }
} }
@ -952,20 +958,18 @@ impl AirTree {
} => { } => {
air_vec.push(Air::AssertConstr { air_vec.push(Air::AssertConstr {
constr_index: *constr_index, constr_index: *constr_index,
msg: msg.clone(),
}); });
constr.create_air_vec(air_vec); constr.create_air_vec(air_vec);
msg.create_air_vec(air_vec);
} }
AirStatement::AssertBool { AirStatement::AssertBool {
is_true, is_true,
value, value,
msg, msg,
} => { } => {
air_vec.push(Air::AssertBool { air_vec.push(Air::AssertBool { is_true: *is_true });
is_true: *is_true,
msg: msg.clone(),
});
value.create_air_vec(air_vec); value.create_air_vec(air_vec);
msg.create_air_vec(air_vec);
} }
AirStatement::ClauseGuard { AirStatement::ClauseGuard {
subject_name, subject_name,
@ -1005,29 +1009,35 @@ impl AirTree {
} }
AirStatement::FieldsExpose { AirStatement::FieldsExpose {
indices, indices,
check_last_item,
record, record,
msg,
} => { } => {
air_vec.push(Air::FieldsExpose { air_vec.push(Air::FieldsExpose {
indices: indices.clone(), indices: indices.clone(),
check_last_item: *check_last_item, is_expect: msg.is_some(),
}); });
record.create_air_vec(air_vec); record.create_air_vec(air_vec);
msg.iter().for_each(|msg| {
msg.create_air_vec(air_vec);
});
} }
AirStatement::ListAccessor { AirStatement::ListAccessor {
tipo, tipo,
names, names,
tail, tail,
check_last_item,
list, list,
msg,
} => { } => {
air_vec.push(Air::ListAccessor { air_vec.push(Air::ListAccessor {
tipo: tipo.clone(), tipo: tipo.clone(),
names: names.clone(), names: names.clone(),
tail: *tail, tail: *tail,
check_last_item: *check_last_item, is_expect: msg.is_some(),
}); });
list.create_air_vec(air_vec); list.create_air_vec(air_vec);
msg.iter().for_each(|msg| {
msg.create_air_vec(air_vec);
});
} }
AirStatement::ListExpose { AirStatement::ListExpose {
tipo, tipo,
@ -1043,26 +1053,31 @@ impl AirTree {
AirStatement::TupleAccessor { AirStatement::TupleAccessor {
names, names,
tipo, tipo,
check_last_item,
tuple, tuple,
msg,
} => { } => {
air_vec.push(Air::TupleAccessor { air_vec.push(Air::TupleAccessor {
names: names.clone(), names: names.clone(),
tipo: tipo.clone(), tipo: tipo.clone(),
check_last_item: *check_last_item, is_expect: msg.is_some(),
}); });
tuple.create_air_vec(air_vec); tuple.create_air_vec(air_vec);
msg.iter().for_each(|msg| {
msg.create_air_vec(air_vec);
});
} }
AirStatement::NoOp => { AirStatement::NoOp => {
air_vec.push(Air::NoOp); air_vec.push(Air::NoOp);
} }
AirStatement::FieldsEmpty { constr } => { AirStatement::FieldsEmpty { constr, msg } => {
air_vec.push(Air::FieldsEmpty); air_vec.push(Air::FieldsEmpty);
constr.create_air_vec(air_vec); constr.create_air_vec(air_vec);
msg.create_air_vec(air_vec);
} }
AirStatement::ListEmpty { list } => { AirStatement::ListEmpty { list, msg } => {
air_vec.push(Air::ListEmpty); air_vec.push(Air::ListEmpty);
list.create_air_vec(air_vec); list.create_air_vec(air_vec);
msg.create_air_vec(air_vec);
} }
}; };
exp.create_air_vec(air_vec); exp.create_air_vec(air_vec);
@ -1527,7 +1542,7 @@ impl AirTree {
); );
} }
AirStatement::NoOp => {} AirStatement::NoOp => {}
AirStatement::FieldsEmpty { constr } => { AirStatement::FieldsEmpty { constr, .. } => {
constr.do_traverse_tree_with( constr.do_traverse_tree_with(
tree_path, tree_path,
current_depth + 1, current_depth + 1,
@ -1536,7 +1551,7 @@ impl AirTree {
apply_with_func_last, apply_with_func_last,
); );
} }
AirStatement::ListEmpty { list } => { AirStatement::ListEmpty { list, .. } => {
list.do_traverse_tree_with( list.do_traverse_tree_with(
tree_path, tree_path,
current_depth + 1, current_depth + 1,
@ -1997,7 +2012,7 @@ impl AirTree {
} }
AirStatement::DefineFunc { .. } => unreachable!(), AirStatement::DefineFunc { .. } => unreachable!(),
AirStatement::DefineCyclicFuncs { .. } => unreachable!(), AirStatement::DefineCyclicFuncs { .. } => unreachable!(),
AirStatement::FieldsEmpty { constr } => { AirStatement::FieldsEmpty { constr, .. } => {
if *index == 0 { if *index == 0 {
constr.as_mut().do_find_air_tree_node(tree_path_iter) constr.as_mut().do_find_air_tree_node(tree_path_iter)
} else if *index == 1 { } else if *index == 1 {
@ -2006,7 +2021,7 @@ impl AirTree {
panic!("Tree Path index outside tree children nodes") panic!("Tree Path index outside tree children nodes")
} }
} }
AirStatement::ListEmpty { list } => { AirStatement::ListEmpty { list, .. } => {
if *index == 0 { if *index == 0 {
list.as_mut().do_find_air_tree_node(tree_path_iter) list.as_mut().do_find_air_tree_node(tree_path_iter)
} else if *index == 1 { } else if *index == 1 {