feat: Rework codegen traces to prevent repeated messages from taking up uplc script space

This commit is contained in:
microproofs 2023-10-07 14:43:12 -04:00 committed by Kasey
parent 44021cde19
commit 7a6ddc45a0
2 changed files with 224 additions and 94 deletions

View File

@ -22,13 +22,14 @@ use crate::{
AssignmentKind, BinOp, Pattern, Span, TypedArg, TypedClause, TypedDataType, TypedFunction, AssignmentKind, BinOp, Pattern, Span, TypedArg, TypedClause, TypedDataType, TypedFunction,
TypedPattern, TypedValidator, UnOp, TypedPattern, TypedValidator, UnOp,
}, },
builtins::{bool, data, int, list, void}, builtins::{bool, data, int, list, string, void},
expr::TypedExpr, expr::TypedExpr,
gen_uplc::builder::{ gen_uplc::builder::{
check_replaceable_opaque_type, convert_opaque_type, erase_opaque_type_operations, check_replaceable_opaque_type, convert_opaque_type, erase_opaque_type_operations,
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_variant_name, monomorphize, pattern_has_conditions, get_generic_id_and_type, get_variant_name, monomorphize, pattern_has_conditions,
wrap_as_multi_validator, wrap_validator_condition, CodeGenFunction, SpecificClause, wrap_as_multi_validator, wrap_validator_condition, CodeGenFunction, SpecificClause,
CONSTR_INDEX_MISMATCH,
}, },
tipo::{ tipo::{
ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor,
@ -42,8 +43,9 @@ use self::{
builder::{ builder::{
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, CycleFunctionNames, DataTypeKey, FunctionAccessKey, AssignmentProperties, ClauseProperties, CodeGenSpecialFuncs, CycleFunctionNames,
HoistableFunction, Variant, DataTypeKey, FunctionAccessKey, HoistableFunction, Variant, CONSTR_NOT_EMPTY,
INCORRECT_BOOLEAN, INCORRECT_CONSTR, LIST_NOT_EMPTY, TOO_MANY_ITEMS,
}, },
tree::{AirExpression, AirTree, TreePath}, tree::{AirExpression, AirTree, TreePath},
}; };
@ -54,7 +56,7 @@ pub struct CodeGenerator<'a> {
functions: IndexMap<FunctionAccessKey, &'a TypedFunction>, functions: IndexMap<FunctionAccessKey, &'a TypedFunction>,
data_types: IndexMap<DataTypeKey, &'a TypedDataType>, data_types: IndexMap<DataTypeKey, &'a TypedDataType>,
module_types: IndexMap<&'a String, &'a TypeInfo>, module_types: IndexMap<&'a String, &'a TypeInfo>,
needs_field_access: bool, special_functions: CodeGenSpecialFuncs,
code_gen_functions: IndexMap<String, CodeGenFunction>, code_gen_functions: IndexMap<String, CodeGenFunction>,
zero_arg_functions: IndexMap<(FunctionAccessKey, Variant), Vec<Air>>, zero_arg_functions: IndexMap<(FunctionAccessKey, Variant), Vec<Air>>,
cyclic_functions: cyclic_functions:
@ -75,7 +77,7 @@ impl<'a> CodeGenerator<'a> {
functions, functions,
data_types, data_types,
module_types, module_types,
needs_field_access: false, special_functions: CodeGenSpecialFuncs::new(),
code_gen_functions: IndexMap::new(), code_gen_functions: IndexMap::new(),
zero_arg_functions: IndexMap::new(), zero_arg_functions: IndexMap::new(),
cyclic_functions: IndexMap::new(), cyclic_functions: IndexMap::new(),
@ -87,7 +89,7 @@ impl<'a> CodeGenerator<'a> {
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.code_gen_functions = IndexMap::new(); self.code_gen_functions = IndexMap::new();
self.zero_arg_functions = IndexMap::new(); self.zero_arg_functions = IndexMap::new();
self.needs_field_access = false; self.special_functions = CodeGenSpecialFuncs::new();
self.defined_functions = IndexMap::new(); self.defined_functions = IndexMap::new();
self.cyclic_functions = IndexMap::new(); self.cyclic_functions = IndexMap::new();
self.id_gen = IdGenerator::new(); self.id_gen = IdGenerator::new();
@ -160,9 +162,11 @@ impl<'a> CodeGenerator<'a> {
(term, other_term) (term, other_term)
}; };
term = wrap_as_multi_validator(spend, mint); // Special Case with multi_validators
self.special_functions.use_function(CONSTR_FIELDS_EXPOSER);
self.special_functions.use_function(CONSTR_INDEX_EXPOSER);
self.needs_field_access = true; term = wrap_as_multi_validator(spend, mint);
} }
term = cast_validator_args(term, params); term = cast_validator_args(term, params);
@ -186,9 +190,7 @@ impl<'a> CodeGenerator<'a> {
} }
fn finalize(&mut self, mut term: Term<Name>) -> Program<Name> { fn finalize(&mut self, mut term: Term<Name>) -> Program<Name> {
if self.needs_field_access { term = self.special_functions.apply_used_functions(term);
term = term.constr_fields_exposer().constr_index_exposer();
}
// TODO: Once SOP is implemented, new version is 1.1.0 // TODO: Once SOP is implemented, new version is 1.1.0
let mut program = Program { let mut program = Program {
@ -547,7 +549,6 @@ impl<'a> CodeGenerator<'a> {
if check_replaceable_opaque_type(&record.tipo(), &self.data_types) { if check_replaceable_opaque_type(&record.tipo(), &self.data_types) {
self.build(record) self.build(record)
} else { } else {
self.needs_field_access = true;
let function_name = format!("__access_index_{}", *index); let function_name = format!("__access_index_{}", *index);
if self.code_gen_functions.get(&function_name).is_none() { if self.code_gen_functions.get(&function_name).is_none() {
@ -573,7 +574,10 @@ impl<'a> CodeGenerator<'a> {
} }
let list_of_fields = AirTree::call( let list_of_fields = AirTree::call(
AirTree::local_var(CONSTR_FIELDS_EXPOSER, void()), AirTree::local_var(
self.special_functions.use_function(CONSTR_FIELDS_EXPOSER),
void(),
),
list(data()), list(data()),
vec![self.build(record)], vec![self.build(record)],
); );
@ -1468,7 +1472,10 @@ impl<'a> CodeGenerator<'a> {
let error_term = if self.tracing { let error_term = if self.tracing {
AirTree::trace( AirTree::trace(
AirTree::string("Constr index didn't match a type variant"), AirTree::local_var(
self.special_functions.use_function(CONSTR_INDEX_MISMATCH),
string(),
),
tipo.clone(), tipo.clone(),
AirTree::error(tipo.clone(), false), AirTree::error(tipo.clone(), false),
) )
@ -3819,31 +3826,44 @@ impl<'a> CodeGenerator<'a> {
id_list.push(self.id_gen.next()); id_list.push(self.id_gen.next());
} }
let inner_types = tipo let names_empty = names.is_empty();
let names_types = tipo
.get_inner_types() .get_inner_types()
.into_iter() .into_iter()
.cycle() .cycle()
.take(names.len()) .take(names.len())
.zip(names)
.map(|(tipo, name)| (name, tipo))
.collect_vec(); .collect_vec();
if !names.is_empty() { if !names_empty {
let error_term = if self.tracing {
Term::Error.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, &names_types,
&id_list, &id_list,
tail, tail,
0, 0,
term, term,
inner_types,
check_last_item, check_last_item,
true, true,
self.tracing, error_term,
) )
.apply(value); .apply(value);
arg_stack.push(term); arg_stack.push(term);
} else if check_last_item { } else if check_last_item {
let trace_term = if self.tracing { let trace_term = if self.tracing {
Term::Error.trace(Term::string("Expected no items for List")) Term::Error.trace(Term::var(
self.special_functions.use_function(LIST_NOT_EMPTY),
))
} else { } else {
Term::Error Term::Error
}; };
@ -4323,20 +4343,24 @@ impl<'a> CodeGenerator<'a> {
arg_stack.push(term); arg_stack.push(term);
} }
Air::AssertConstr { constr_index } => { Air::AssertConstr { constr_index } => {
self.needs_field_access = true;
let constr = arg_stack.pop().unwrap(); let constr = arg_stack.pop().unwrap();
let mut term = 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.trace(Term::string("Expected on incorrect Constr variant")) Term::Error.trace(Term::var(
self.special_functions.use_function(INCORRECT_CONSTR),
))
} else { } else {
Term::Error Term::Error
}; };
term = Term::equals_integer() term = Term::equals_integer()
.apply(Term::integer(constr_index.into())) .apply(Term::integer(constr_index.into()))
.apply(Term::var(CONSTR_INDEX_EXPOSER).apply(constr)) .apply(
Term::var(self.special_functions.use_function(CONSTR_INDEX_EXPOSER))
.apply(constr),
)
.delayed_if_else(term, trace_term); .delayed_if_else(term, trace_term);
arg_stack.push(term); arg_stack.push(term);
@ -4346,7 +4370,9 @@ impl<'a> CodeGenerator<'a> {
let mut term = 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.trace(Term::string("Expected on incorrect bool variant")) Term::Error.trace(Term::var(
self.special_functions.use_function(INCORRECT_BOOLEAN),
))
} else { } else {
Term::Error Term::Error
}; };
@ -4375,8 +4401,8 @@ impl<'a> CodeGenerator<'a> {
{ {
subject subject
} else { } else {
self.needs_field_access = true; Term::var(self.special_functions.use_function(CONSTR_INDEX_EXPOSER))
Term::var(CONSTR_INDEX_EXPOSER).apply(subject) .apply(subject)
}; };
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
@ -4576,10 +4602,10 @@ impl<'a> CodeGenerator<'a> {
} else if tipo.is_list() || tipo.is_tuple() { } else if tipo.is_list() || tipo.is_tuple() {
unreachable!() unreachable!()
} else { } else {
self.needs_field_access = true; Term::equals_integer().apply(checker).apply(
Term::equals_integer() Term::var(self.special_functions.use_function(CONSTR_INDEX_EXPOSER))
.apply(checker) .apply(Term::var(subject_name)),
.apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var(subject_name))) )
}; };
let term = condition let term = condition
@ -4721,7 +4747,6 @@ impl<'a> CodeGenerator<'a> {
indices, indices,
check_last_item, check_last_item,
} => { } => {
self.needs_field_access = true;
let mut id_list = vec![]; let mut id_list = vec![];
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
@ -4736,33 +4761,48 @@ impl<'a> CodeGenerator<'a> {
let current_index = 0; let current_index = 0;
let names = indices.iter().cloned().map(|item| item.1).collect_vec(); let names_types = indices
let inner_types = indices.iter().cloned().map(|item| item.2).collect_vec(); .iter()
.cloned()
.map(|item| (item.1, item.2))
.collect_vec();
if !indices.is_empty() { if !indices.is_empty() {
term = builder::list_access_to_uplc( let error_term = if self.tracing {
&names, Term::Error.trace(Term::var(
&id_list, self.special_functions.use_function(TOO_MANY_ITEMS),
false, ))
current_index,
term,
inner_types,
check_last_item,
false,
self.tracing,
);
term = term.apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(value));
arg_stack.push(term);
} else if check_last_item {
let trace_term = if self.tracing {
Term::Error.trace(Term::string("Expected no fields for Constr"))
} else { } else {
Term::Error Term::Error
}; };
term = Term::var(CONSTR_FIELDS_EXPOSER) term = builder::list_access_to_uplc(
&names_types,
&id_list,
false,
current_index,
term,
check_last_item,
false,
error_term,
);
term = term.apply(
Term::var(self.special_functions.use_function(CONSTR_FIELDS_EXPOSER))
.apply(value),
);
arg_stack.push(term);
} else if check_last_item {
let trace_term = if self.tracing {
Term::Error.trace(Term::var(
self.special_functions.use_function(CONSTR_NOT_EMPTY),
))
} else {
Term::Error
};
term = Term::var(self.special_functions.use_function(CONSTR_FIELDS_EXPOSER))
.apply(value) .apply(value)
.delayed_choose_list(term, trace_term); .delayed_choose_list(term, trace_term);
@ -4772,18 +4812,18 @@ impl<'a> CodeGenerator<'a> {
}; };
} }
Air::FieldsEmpty => { Air::FieldsEmpty => {
self.needs_field_access = true;
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
let mut term = 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.trace(Term::string("Expected no fields for Constr")) Term::Error.trace(Term::var(
self.special_functions.use_function(CONSTR_NOT_EMPTY),
))
} else { } else {
Term::Error Term::Error
}; };
term = Term::var(CONSTR_FIELDS_EXPOSER) term = Term::var(self.special_functions.use_function(CONSTR_FIELDS_EXPOSER))
.apply(value) .apply(value)
.delayed_choose_list(term, trace_term); .delayed_choose_list(term, trace_term);
@ -4794,7 +4834,9 @@ impl<'a> CodeGenerator<'a> {
let mut term = 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.trace(Term::string("Expected no items for List")) Term::Error.trace(Term::var(
self.special_functions.use_function(LIST_NOT_EMPTY),
))
} else { } else {
Term::Error Term::Error
}; };
@ -4867,7 +4909,6 @@ impl<'a> CodeGenerator<'a> {
indices, indices,
tipo, tipo,
} => { } => {
self.needs_field_access = true;
let tail_name_prefix = "__tail_index"; let tail_name_prefix = "__tail_index";
let data_type = lookup_data_type_by_tipo(&self.data_types, &tipo) let data_type = lookup_data_type_by_tipo(&self.data_types, &tipo)
@ -4946,9 +4987,10 @@ impl<'a> CodeGenerator<'a> {
} }
} }
term = term term = term.lambda(format!("{tail_name_prefix}_0")).apply(
.lambda(format!("{tail_name_prefix}_0")) Term::var(self.special_functions.use_function(CONSTR_FIELDS_EXPOSER))
.apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(record)); .apply(record),
);
arg_stack.push(term); arg_stack.push(term);
} }
@ -5018,16 +5060,25 @@ impl<'a> CodeGenerator<'a> {
id_list.push(self.id_gen.next()); id_list.push(self.id_gen.next());
} }
let names_types = names.into_iter().zip(inner_types).collect_vec();
let error_term = if self.tracing {
Term::Error.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, &names_types,
&id_list, &id_list,
false, false,
0, 0,
term, term,
tipo.get_inner_types(),
check_last_item, check_last_item,
false, false,
self.tracing, error_term,
) )
.apply(value); .apply(value);

View File

@ -36,6 +36,13 @@ pub type Params = Vec<String>;
pub type CycleFunctionNames = Vec<String>; pub type CycleFunctionNames = Vec<String>;
pub const TOO_MANY_ITEMS: &str = "TOO_MANY_ITEMS";
pub const LIST_NOT_EMPTY: &str = "LIST_NOT_EMPTY";
pub const CONSTR_NOT_EMPTY: &str = "CONSTR_NOT_EMPTY";
pub const INCORRECT_BOOLEAN: &str = "INCORRECT_BOOLEAN";
pub const INCORRECT_CONSTR: &str = "INCORRECT_CONSTR";
pub const CONSTR_INDEX_MISMATCH: &str = "CONSTR_INDEX_MISMATCH";
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum CodeGenFunction { pub enum CodeGenFunction {
Function { body: AirTree, params: Params }, Function { body: AirTree, params: Params },
@ -181,6 +188,91 @@ impl ClauseProperties {
} }
} }
#[derive(Clone, Debug)]
pub struct CodeGenSpecialFuncs {
pub used_funcs: Vec<String>,
pub key_to_func: IndexMap<String, Term<Name>>,
}
impl CodeGenSpecialFuncs {
pub fn new() -> Self {
let mut key_to_func = IndexMap::new();
key_to_func.insert(
CONSTR_FIELDS_EXPOSER.to_string(),
Term::snd_pair()
.apply(Term::unconstr_data().apply(Term::var("__constr_var")))
.lambda("__constr_var"),
);
key_to_func.insert(
CONSTR_INDEX_EXPOSER.to_string(),
Term::fst_pair()
.apply(Term::unconstr_data().apply(Term::var("__constr_var")))
.lambda("__constr_var"),
);
key_to_func.insert(
TOO_MANY_ITEMS.to_string(),
Term::string("List/Tuple/Constr contains more items than expected"),
);
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 {
used_funcs: vec![],
key_to_func,
}
}
pub fn use_function(&mut self, func_name: &'static str) -> &'static str {
if !self.used_funcs.contains(&func_name.to_string()) {
self.used_funcs.push(func_name.to_string());
}
func_name
}
pub fn get_function(&self, func_name: &String) -> Term<Name> {
self.key_to_func[func_name].clone()
}
pub fn apply_used_functions(&self, mut term: Term<Name>) -> Term<Name> {
for func_name in self.used_funcs.iter() {
term = term.lambda(func_name).apply(self.get_function(func_name));
}
term
}
}
impl Default for CodeGenSpecialFuncs {
fn default() -> Self {
Self::new()
}
}
pub fn get_generic_id_and_type(tipo: &Type, param: &Type) -> Vec<(u64, Rc<Type>)> { pub fn get_generic_id_and_type(tipo: &Type, param: &Type) -> Vec<(u64, Rc<Type>)> {
let mut generics_ids = vec![]; let mut generics_ids = vec![];
@ -1265,27 +1357,16 @@ pub fn convert_type_to_data(term: Term<Name>, field_type: &Rc<Type>) -> Term<Nam
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn list_access_to_uplc( pub fn list_access_to_uplc(
names: &[String], names_types: &[(String, Rc<Type>)],
id_list: &[u64], id_list: &[u64],
tail: bool, tail: bool,
current_index: usize, current_index: usize,
term: Term<Name>, term: Term<Name>,
tipos: Vec<Rc<Type>>,
check_last_item: bool, check_last_item: bool,
is_list_accessor: bool, is_list_accessor: bool,
tracing: bool, error_term: Term<Name>,
) -> Term<Name> { ) -> Term<Name> {
let trace_term = if tracing { if let Some(((first, current_tipo), names_types)) = names_types.split_first() {
Term::Error.trace(Term::string(
"List/Tuple/Constr contains more items than expected",
))
} else {
Term::Error
};
if let Some((first, names)) = names.split_first() {
let (current_tipo, tipos) = tipos.split_first().unwrap();
let head_list = let head_list =
if matches!(current_tipo.get_uplc_type(), UplcType::Pair(_, _)) && is_list_accessor { if matches!(current_tipo.get_uplc_type(), UplcType::Pair(_, _)) && is_list_accessor {
Term::head_list().apply(Term::var(format!( Term::head_list().apply(Term::var(format!(
@ -1302,11 +1383,11 @@ pub fn list_access_to_uplc(
) )
}; };
if names.len() == 1 && tail { if names_types.len() == 1 && tail {
if first == "_" && names[0] == "_" { if first == "_" && names_types[0].0 == "_" {
term.lambda("_") term.lambda("_")
} else if first == "_" { } else if first == "_" {
term.lambda(names[0].clone()) term.lambda(&names_types[0].0)
.apply(Term::tail_list().apply(Term::var(format!( .apply(Term::tail_list().apply(Term::var(format!(
"tail_index_{}_{}", "tail_index_{}_{}",
current_index, id_list[current_index] current_index, id_list[current_index]
@ -1315,25 +1396,25 @@ pub fn list_access_to_uplc(
"tail_index_{}_{}", "tail_index_{}_{}",
current_index, id_list[current_index] current_index, id_list[current_index]
)) ))
} else if names[0] == "_" { } else if names_types[0].0 == "_" {
term.lambda(first.clone()).apply(head_list).lambda(format!( term.lambda(first).apply(head_list).lambda(format!(
"tail_index_{}_{}", "tail_index_{}_{}",
current_index, id_list[current_index] current_index, id_list[current_index]
)) ))
} else { } else {
term.lambda(names[0].clone()) term.lambda(&names_types[0].0)
.apply(Term::tail_list().apply(Term::var(format!( .apply(Term::tail_list().apply(Term::var(format!(
"tail_index_{}_{}", "tail_index_{}_{}",
current_index, id_list[current_index] current_index, id_list[current_index]
)))) ))))
.lambda(first.clone()) .lambda(first)
.apply(head_list) .apply(head_list)
.lambda(format!( .lambda(format!(
"tail_index_{}_{}", "tail_index_{}_{}",
current_index, id_list[current_index] current_index, id_list[current_index]
)) ))
} }
} else if names.is_empty() { } else if names_types.is_empty() {
if first == "_" { if first == "_" {
if check_last_item { if check_last_item {
Term::tail_list() Term::tail_list()
@ -1341,7 +1422,7 @@ pub fn list_access_to_uplc(
"tail_index_{}_{}", "tail_index_{}_{}",
current_index, id_list[current_index] current_index, id_list[current_index]
))) )))
.delayed_choose_list(term, trace_term) .delayed_choose_list(term, error_term)
} else { } else {
term term
} }
@ -1357,7 +1438,7 @@ pub fn list_access_to_uplc(
"tail_index_{}_{}", "tail_index_{}_{}",
current_index, id_list[current_index] current_index, id_list[current_index]
))) )))
.delayed_choose_list(term, trace_term) .delayed_choose_list(term, error_term)
} else { } else {
term term
} }
@ -1370,15 +1451,14 @@ pub fn list_access_to_uplc(
} }
} else if first == "_" { } else if first == "_" {
let mut list_access_inner = list_access_to_uplc( let mut list_access_inner = list_access_to_uplc(
names, names_types,
id_list, id_list,
tail, tail,
current_index + 1, current_index + 1,
term, term,
tipos.to_owned(),
check_last_item, check_last_item,
is_list_accessor, is_list_accessor,
tracing, error_term,
); );
list_access_inner = match &list_access_inner { list_access_inner = match &list_access_inner {
@ -1409,15 +1489,14 @@ pub fn list_access_to_uplc(
} }
} else { } else {
let mut list_access_inner = list_access_to_uplc( let mut list_access_inner = list_access_to_uplc(
names, names_types,
id_list, id_list,
tail, tail,
current_index + 1, current_index + 1,
term, term,
tipos.to_owned(),
check_last_item, check_last_item,
is_list_accessor, is_list_accessor,
tracing, error_term,
); );
list_access_inner = match &list_access_inner { list_access_inner = match &list_access_inner {