diff --git a/crates/aiken-lang/src/gen_uplc.rs b/crates/aiken-lang/src/gen_uplc.rs index b07e713b..a40a0d92 100644 --- a/crates/aiken-lang/src/gen_uplc.rs +++ b/crates/aiken-lang/src/gen_uplc.rs @@ -1,5 +1,6 @@ pub mod air; pub mod builder; +pub mod interner; pub mod tree; use self::{ @@ -38,8 +39,12 @@ use crate::{ }, IdGenerator, }; -use builder::{softcast_data_to_type_otherwise, unknown_data_to_type}; +use builder::{ + introduce_name, introduce_pattern, pop_pattern, softcast_data_to_type_otherwise, + unknown_data_to_type, DISCARDED, +}; use indexmap::{IndexMap, IndexSet}; +use interner::AirInterner; use itertools::Itertools; use petgraph::{algo, Graph}; use std::{collections::HashMap, rc::Rc}; @@ -76,6 +81,7 @@ pub struct CodeGenerator<'a> { cyclic_functions: IndexMap<(FunctionAccessKey, Variant), (CycleFunctionNames, usize, FunctionAccessKey)>, /// mutable and reset as well + interner: AirInterner, id_gen: IdGenerator, } @@ -105,6 +111,7 @@ impl<'a> CodeGenerator<'a> { special_functions: CodeGenSpecialFuncs::new(), code_gen_functions: IndexMap::new(), cyclic_functions: IndexMap::new(), + interner: AirInterner::new(), id_gen: IdGenerator::new(), } } @@ -113,6 +120,7 @@ impl<'a> CodeGenerator<'a> { self.code_gen_functions = IndexMap::new(); self.defined_functions = IndexMap::new(); self.cyclic_functions = IndexMap::new(); + self.interner = AirInterner::new(); self.id_gen = IdGenerator::new(); if reset_special_functions { self.special_functions = CodeGenSpecialFuncs::new(); @@ -120,12 +128,20 @@ impl<'a> CodeGenerator<'a> { } pub fn generate(&mut self, validator: &TypedValidator, module_name: &str) -> Program { + let context_name = "__context__".to_string(); + let context_name_interned = introduce_name(&mut self.interner, &context_name); + validator.params.iter().for_each(|arg| { + arg.get_variable_name() + .iter() + .for_each(|arg_name| self.interner.intern(arg_name.to_string())) + }); + let air_tree_fun = wrap_validator_condition( self.build(&validator.into_script_context_handler(), module_name, &[]), self.tracing, ); - let air_tree_fun = AirTree::anon_func(vec!["__context__".to_string()], air_tree_fun, true); + let air_tree_fun = AirTree::anon_func(vec![context_name_interned], air_tree_fun, true); let validator_args_tree = AirTree::no_op(air_tree_fun); @@ -137,7 +153,14 @@ impl<'a> CodeGenerator<'a> { let term = self.uplc_code_gen(full_vec); - let term = cast_validator_args(term, &validator.params); + let term = cast_validator_args(term, &validator.params, &self.interner); + + self.interner.pop_text(context_name); + validator.params.iter().for_each(|arg| { + arg.get_variable_name() + .iter() + .for_each(|arg_name| self.interner.pop_text(arg_name.to_string())) + }); self.finalize(term) } @@ -148,6 +171,12 @@ impl<'a> CodeGenerator<'a> { args: &[TypedArg], module_name: &str, ) -> Program { + args.iter().for_each(|arg| { + arg.get_variable_name() + .iter() + .for_each(|arg_name| self.interner.intern(arg_name.to_string())) + }); + let mut air_tree = self.build(body, module_name, &[]); air_tree = AirTree::no_op(air_tree); @@ -162,9 +191,15 @@ impl<'a> CodeGenerator<'a> { term = if args.is_empty() { term } else { - cast_validator_args(term, args) + cast_validator_args(term, args, &self.interner) }; + args.iter().for_each(|arg| { + arg.get_variable_name() + .iter() + .for_each(|arg_name| self.interner.pop_text(arg_name.to_string())) + }); + self.finalize(term) } @@ -236,15 +271,19 @@ impl<'a> CodeGenerator<'a> { Term::Error.delayed_trace(Term::string(msg)).delay(), Type::void(), ); + Some(self.special_functions.use_function_tree(msg_func_name)) } }; + // Intern vars from pattern here + introduce_pattern(&mut self.interner, pattern); + let (then, context) = context.split_first().unwrap(); let then = self.build(then, module_build_name, context); - self.assignment( + let tree = self.assignment( pattern, air_value, then, @@ -256,7 +295,12 @@ impl<'a> CodeGenerator<'a> { full_check: !tipo.is_data() && value.tipo().is_data() && kind.is_expect(), otherwise: otherwise_delayed, }, - ) + ); + + // Now pop off interned pattern + pop_pattern(&mut self.interner, pattern); + + tree } else { match body { TypedExpr::Assignment { .. } => { @@ -275,15 +319,34 @@ impl<'a> CodeGenerator<'a> { TypedExpr::Var { constructor, name, .. - } => AirTree::var(constructor.clone(), name, ""), + } => match constructor.variant { + ValueConstructorVariant::LocalVariable { .. } => { + AirTree::var(constructor.clone(), self.interner.lookup_interned(name), "") + } + _ => AirTree::var(constructor.clone(), name, ""), + }, + + TypedExpr::Fn { args, body, .. } => { + let params = args + .iter() + .map(|arg| { + arg.get_variable_name() + .map(|arg| introduce_name(&mut self.interner, &arg.to_string())) + .unwrap_or_else(|| DISCARDED.to_string()) + }) + .collect_vec(); + + let anon = + AirTree::anon_func(params, self.build(body, module_build_name, &[]), false); - TypedExpr::Fn { args, body, .. } => AirTree::anon_func( args.iter() - .map(|arg| arg.arg_name.get_variable_name().unwrap_or("_").to_string()) - .collect_vec(), - self.build(body, module_build_name, &[]), - false, - ), + .filter_map(|arg| arg.get_variable_name()) + .for_each(|arg| { + self.interner.pop_text(arg.to_string()); + }); + + anon + } TypedExpr::List { tipo, @@ -502,15 +565,18 @@ impl<'a> CodeGenerator<'a> { // works fine with an empty clauses list. This is orthogonal to the // current refactoring so not changing it now. } else if clauses.len() == 1 { + let subject_val = self.build(subject, module_build_name, &[]); + let last_clause = clauses.pop().unwrap(); + // Intern vars from pattern here + introduce_pattern(&mut self.interner, &last_clause.pattern); + let clause_then = self.build(&last_clause.then, module_build_name, &[]); let subject_type = subject.tipo(); - let subject_val = self.build(subject, module_build_name, &[]); - - self.assignment( + let tree = self.assignment( &last_clause.pattern, subject_val, clause_then, @@ -522,7 +588,12 @@ impl<'a> CodeGenerator<'a> { full_check: false, otherwise: None, }, - ) + ); + + // Now pop off interned pattern + pop_pattern(&mut self.interner, &last_clause.pattern); + + tree } else { clauses = if subject.tipo().is_list() { rearrange_list_clauses(clauses, &self.data_types) @@ -544,28 +615,37 @@ impl<'a> CodeGenerator<'a> { subject.location().end ); + self.interner.intern(constr_var.clone()); + self.interner.intern(subject_name.clone()); + + let constr_var_interned = self.interner.lookup_interned(&constr_var); + let subject_name_interned = self.interner.lookup_interned(&subject_name); + let clauses = self.handle_each_clause( &clauses, last_clause, &subject.tipo(), &mut ClauseProperties::init( &subject.tipo(), - constr_var.clone(), - subject_name.clone(), + constr_var_interned.clone(), + subject_name_interned.clone(), ), module_build_name, ); + self.interner.pop_text(constr_var); + self.interner.pop_text(subject_name); + let when_assign = AirTree::when( - subject_name, + subject_name_interned, tipo.clone(), subject.tipo(), - AirTree::local_var(&constr_var, subject.tipo()), + AirTree::local_var(&constr_var_interned, subject.tipo()), clauses, ); AirTree::let_assignment( - constr_var, + constr_var_interned, self.build(subject, module_build_name, &[]), when_assign, ) @@ -590,8 +670,14 @@ impl<'a> CodeGenerator<'a> { match &branch.is { Some((pattern, tipo)) => { - AirTree::let_assignment( - "acc_var", + introduce_pattern(&mut self.interner, pattern); + self.interner.intern("acc_var".to_string()); + + let acc_var = + self.interner.lookup_interned(&"acc_var".to_string()); + + let tree = AirTree::let_assignment( + &acc_var, // use anon function as a delay to avoid evaluating the acc AirTree::anon_func(vec![], acc, true), self.assignment( @@ -605,12 +691,17 @@ impl<'a> CodeGenerator<'a> { remove_unused: false, full_check: true, otherwise: Some(AirTree::local_var( - "acc_var", + &acc_var, tipo.clone(), )), }, ), - ) + ); + + pop_pattern(&mut self.interner, pattern); + self.interner.pop_text("acc_var".to_string()); + + tree } None => AirTree::if_branch(tipo.clone(), condition, body, acc), } @@ -869,7 +960,7 @@ impl<'a> CodeGenerator<'a> { assert!( match &value { AirTree::Var { name, .. } if props.kind.is_let() => { - name != "_" + name != DISCARDED } _ => true, }, @@ -960,58 +1051,17 @@ impl<'a> CodeGenerator<'a> { } Pattern::Var { name, .. } => { + let name = self.interner.lookup_interned(name); + if props.full_check { let mut index_map = IndexMap::new(); let non_opaque_tipo = convert_opaque_type(tipo, &self.data_types, true); - let val = AirTree::local_var(name, tipo.clone()); - - if non_opaque_tipo.is_primitive() { - assign_casted_value(name.clone(), value, then) - } else { - assign_casted_value( - name.clone(), - value, - self.expect_type_assign( - &non_opaque_tipo, - val, - &mut index_map, - pattern.location(), - then, - props.otherwise.clone(), - 0, - ), - ) - } - } else { - assign_casted_value(name.clone(), value, then) - } - } - - Pattern::Assign { name, pattern, .. } => { - // Don't need any data casting for Assign - let inner_pattern = self.assignment( - pattern, - AirTree::local_var(name, tipo.clone()), - then, - tipo, - props, - ); - AirTree::let_assignment(name, value, inner_pattern) - } - - Pattern::Discard { name, .. } => { - if props.full_check { - let name = format!("__discard_expect_{}", name); - let mut index_map = IndexMap::new(); - - let non_opaque_tipo = convert_opaque_type(tipo, &self.data_types, true); - let val = AirTree::local_var(&name, tipo.clone()); if non_opaque_tipo.is_primitive() { - assign_casted_value(name, value, then) + assign_casted_value(name.clone(), value, then) } else { assign_casted_value( name, @@ -1023,11 +1073,61 @@ impl<'a> CodeGenerator<'a> { pattern.location(), then, props.otherwise.clone(), - 0, ), ) } + } else { + assign_casted_value(name.clone(), value, then) + } + } + + Pattern::Assign { name, pattern, .. } => { + let name = self.interner.lookup_interned(name); + // Don't need any data casting for Assign + let inner_pattern = self.assignment( + pattern, + AirTree::local_var(&name, tipo.clone()), + then, + tipo, + props, + ); + AirTree::let_assignment(name, value, inner_pattern) + } + + Pattern::Discard { name, .. } => { + if props.full_check { + let name = format!("__discard_expect_{}", name); + + let name_interned = introduce_name(&mut self.interner, &name); + + let mut index_map = IndexMap::new(); + + let non_opaque_tipo = convert_opaque_type(tipo, &self.data_types, true); + + let val = AirTree::local_var(&name_interned, tipo.clone()); + + let tree = if non_opaque_tipo.is_primitive() { + assign_casted_value(name_interned, value, then) + } else { + assign_casted_value( + name_interned, + value, + self.expect_type_assign( + &non_opaque_tipo, + val, + &mut index_map, + pattern.location(), + then, + props.otherwise.clone(), + ), + ) + }; + + self.interner.pop_text(name); + + tree } else if !props.remove_unused { + //No need to intern, name not used assign_casted_value(name.clone(), value, then) } else { then @@ -1050,28 +1150,33 @@ impl<'a> CodeGenerator<'a> { let then = match tail { None => then, Some(tail) => { - let tail_name = match tail.as_ref() { - Pattern::Var { name, .. } => name.to_string(), + let (tail_name, tail_name_interned) = match tail.as_ref() { + Pattern::Var { name, .. } => { + (None, self.interner.lookup_interned(name)) + } // This Pattern one doesn't even make sense - Pattern::Assign { name, .. } => name.to_string(), + Pattern::Assign { .. } => { + todo!("Has this ever been reached before?") + } Pattern::Discard { name, .. } => { if props.full_check { - format!("__discard_{}_tail", name) + ( + Some(format!("__discard_{}_tail", name)), + introduce_name( + &mut self.interner, + &format!("__discard_{}_tail", name), + ), + ) } else { - "_".to_string() + (None, DISCARDED.to_string()) } } - // This should be unreachable - _ => format!( - "tail_span_{}_{}", - tail.location().start, - tail.location().end - ), + _ => unreachable!(), }; - let val = AirTree::local_var(&tail_name, tipo.clone()); + let val = AirTree::local_var(&tail_name_interned, tipo.clone()); - let then = if tail_name != "_" { + let then = if tail_name_interned != DISCARDED { self.assignment( tail, val, @@ -1092,7 +1197,11 @@ impl<'a> CodeGenerator<'a> { then }; - elems.push(tail_name); + elems.push(tail_name_interned); + + if let Some(tail_name) = tail_name { + self.interner.pop_text(tail_name); + } then } @@ -1102,27 +1211,42 @@ impl<'a> CodeGenerator<'a> { .iter() .enumerate() .rfold(then, |then, (index, elem)| { - let elem_name = match elem { - Pattern::Var { name, .. } => name.to_string(), - Pattern::Assign { name, .. } => name.to_string(), + let (elem_name, elem_name_interned) = match elem { + Pattern::Var { name, .. } => { + (None, self.interner.lookup_interned(name)) + } + Pattern::Assign { name, .. } => { + (None, self.interner.lookup_interned(name)) + } Pattern::Discard { name, .. } => { if props.full_check { - format!("__discard_{}_{}", name, index) + ( + Some(format!("__discard_{}_{}", name, index)), + introduce_name( + &mut self.interner, + &format!("__discard_{}_{}", name, index), + ), + ) } else { - "_".to_string() + (None, DISCARDED.to_string()) } } - _ => format!( - "elem_{}_span_{}_{}", - index, - elem.location().start, - elem.location().end - ), + _ => { + let name = format!( + "elem_{}_span_{}_{}", + index, + elem.location().start, + elem.location().end + ); + let interned = introduce_name(&mut self.interner, &name); + + (Some(name), interned) + } }; - let val = AirTree::local_var(&elem_name, list_elem_type.clone()); + let val = AirTree::local_var(&elem_name_interned, list_elem_type.clone()); - let then = if elem_name != "_" { + let then = if elem_name_interned != DISCARDED { self.assignment( elem, val, @@ -1140,7 +1264,11 @@ impl<'a> CodeGenerator<'a> { then }; - elems.push(elem_name); + elems.push(elem_name_interned); + + if let Some(elem_name) = elem_name { + self.interner.pop_text(elem_name); + } then }); @@ -1153,17 +1281,19 @@ impl<'a> CodeGenerator<'a> { pattern.location().end ); + let name_interned = introduce_name(&mut self.interner, &name); + let casted_var = AirTree::local_var(&name, tipo.clone()); - if elements.is_empty() { + let tree = if elements.is_empty() { assign_casted_value( - name, + name_interned, value, AirTree::list_empty(casted_var, then, otherwise), ) } else { assign_casted_value( - name, + name_interned, value, AirTree::list_access( elems, @@ -1179,7 +1309,11 @@ impl<'a> CodeGenerator<'a> { otherwise, ), ) - } + }; + + self.interner.pop_text(name); + + tree } Pattern::Pair { @@ -1202,34 +1336,46 @@ impl<'a> CodeGenerator<'a> { .iter() .enumerate() .rfold(then, |then, (field_index, arg)| { - let field_name = match arg.as_ref() { - Pattern::Var { name, .. } => name.to_string(), - Pattern::Assign { name, .. } => name.to_string(), + let (field_name, field_name_interned) = match arg.as_ref() { + Pattern::Var { name, .. } => { + (None, self.interner.lookup_interned(name)) + } + Pattern::Assign { name, .. } => { + (None, self.interner.lookup_interned(name)) + } Pattern::Discard { name, .. } => { if props.full_check { - format!("__discard_{}_{}", name, field_index) + ( + Some(format!("__discard_{}_{}", name, field_index)), + introduce_name( + &mut self.interner, + &format!("__discard_{}_{}", name, field_index), + ), + ) } else { - "_".to_string() + (None, DISCARDED.to_string()) } } - _ => format!( - "field_{}_span_{}_{}", - field_index, - arg.location().start, - arg.location().end - ), + _ => { + let name = format!( + "field_{}_span_{}_{}", + field_index, + arg.location().start, + arg.location().end + ); + let interned = introduce_name(&mut self.interner, &name); + + (Some(name), interned) + } }; let arg_type = type_map.get(&field_index).unwrap_or_else(|| { - unreachable!( - "Missing type for field {} of constr {}", - field_index, field_name - ) + unreachable!("Missing type for field {} of Pair", field_index,) }); - let val = AirTree::local_var(&field_name, arg_type.clone()); + let val = AirTree::local_var(&field_name_interned, arg_type.clone()); - let then = if field_name != "_" { + let then = if field_name_interned != DISCARDED { self.assignment( arg, val, @@ -1247,7 +1393,11 @@ impl<'a> CodeGenerator<'a> { then }; - fields.push((field_index, field_name, arg_type.clone())); + fields.push((field_index, field_name_interned, arg_type.clone())); + + if let Some(field_name) = field_name { + self.interner.pop_text(field_name); + } then }); @@ -1262,29 +1412,44 @@ impl<'a> CodeGenerator<'a> { pattern.location().end ); - let local_value = AirTree::local_var(&constructor_name, tipo.clone()); + let constructor_name_interned = + introduce_name(&mut self.interner, &constructor_name); - let then = { - assert!(fields.len() == 2); + let local_value = AirTree::local_var(&constructor_name_interned, tipo.clone()); - AirTree::pair_access( - fields - .first() - .map(|x| if x.1 == "_" { None } else { Some(x.1.clone()) }) - .unwrap(), - fields - .last() - .map(|x| if x.1 == "_" { None } else { Some(x.1.clone()) }) - .unwrap(), - tipo.clone(), - local_value, - props.full_check, - then, - otherwise, - ) - }; + let then = AirTree::pair_access( + fields + .first() + .map(|x| { + if x.1 == DISCARDED { + None + } else { + Some(x.1.clone()) + } + }) + .unwrap(), + fields + .last() + .map(|x| { + if x.1 == DISCARDED { + None + } else { + Some(x.1.clone()) + } + }) + .unwrap(), + tipo.clone(), + local_value, + props.full_check, + then, + otherwise, + ); - assign_casted_value(constructor_name, value, then) + let tree = assign_casted_value(constructor_name_interned, value, then); + + self.interner.pop_text(constructor_name); + + tree } Pattern::Constructor { @@ -1311,7 +1476,7 @@ impl<'a> CodeGenerator<'a> { Pattern::Constructor { .. } if tipo.is_void() => { // Void type is checked when casting from data // So we just assign the value and move on - assign_casted_value("_".to_string(), value, then) + assign_casted_value(DISCARDED.to_string(), value, then) } Pattern::Constructor { @@ -1358,22 +1523,37 @@ impl<'a> CodeGenerator<'a> { index }; - let field_name = match &arg.value { - Pattern::Var { name, .. } => name.to_string(), - Pattern::Assign { name, .. } => name.to_string(), + let (field_name, field_name_interned) = match &arg.value { + Pattern::Var { name, .. } => { + (None, self.interner.lookup_interned(name)) + } + Pattern::Assign { name, .. } => { + (None, self.interner.lookup_interned(name)) + } Pattern::Discard { name, .. } => { if props.full_check { - format!("__discard_{}_{}", name, index) + ( + Some(format!("__discard_{}_{}", name, index)), + introduce_name( + &mut self.interner, + &format!("__discard_{}_{}", name, index), + ), + ) } else { - "_".to_string() + (None, DISCARDED.to_string()) } } - _ => format!( - "field_{}_span_{}_{}", - field_index, - arg.value.location().start, - arg.value.location().end - ), + _ => { + let name = format!( + "field_{}_span_{}_{}", + field_index, + arg.value.location().start, + arg.value.location().end + ); + let interned = introduce_name(&mut self.interner, &name); + + (Some(name), interned) + } }; let arg_type = type_map.get(&field_index).unwrap_or_else(|| { @@ -1383,9 +1563,9 @@ impl<'a> CodeGenerator<'a> { ) }); - let val = AirTree::local_var(&field_name, arg_type.clone()); + let val = AirTree::local_var(&field_name_interned, arg_type.clone()); - let then = if field_name != "_" { + let then = if field_name_interned != DISCARDED { self.assignment( &arg.value, val, @@ -1403,7 +1583,11 @@ impl<'a> CodeGenerator<'a> { then }; - fields.push((field_index, field_name, arg_type.clone())); + fields.push((field_index, field_name_interned, arg_type.clone())); + + if let Some(field_name) = field_name { + self.interner.pop_text(field_name); + } then }); @@ -1426,7 +1610,12 @@ impl<'a> CodeGenerator<'a> { pattern.location().end ); - let local_value = AirTree::local_var(&constructor_name, tipo.clone()); + let constructor_name_interned = + introduce_name(&mut self.interner, &constructor_name); + + let subject_name_interned = introduce_name(&mut self.interner, &subject_name); + + let local_value = AirTree::local_var(&constructor_name_interned, tipo.clone()); let then = if check_replaceable_opaque_type(tipo, &self.data_types) { AirTree::let_assignment(&fields[0].1, local_value, then) @@ -1458,13 +1647,13 @@ impl<'a> CodeGenerator<'a> { }); AirTree::when( - &subject_name, + &subject_name_interned, Type::void(), tipo.clone(), - AirTree::local_var(&constructor_name, tipo.clone()), + AirTree::local_var(&constructor_name_interned, tipo.clone()), AirTree::assert_constr_index( index, - AirTree::local_var(&subject_name, tipo.clone()), + AirTree::local_var(&subject_name_interned, tipo.clone()), then, otherwise, ), @@ -1477,10 +1666,16 @@ impl<'a> CodeGenerator<'a> { props.full_check, name, ); + then }; - assign_casted_value(constructor_name, value, then) + let tree = assign_casted_value(constructor_name_interned, value, then); + + self.interner.pop_text(constructor_name); + self.interner.pop_text(subject_name); + + tree } Pattern::Tuple { @@ -1498,22 +1693,34 @@ impl<'a> CodeGenerator<'a> { let mut fields = vec![]; let then = elems.iter().enumerate().rfold(then, |then, (index, arg)| { - let tuple_name = match &arg { - Pattern::Var { name, .. } => name.to_string(), - Pattern::Assign { name, .. } => name.to_string(), + let (tuple_name, tuple_name_interned) = match &arg { + Pattern::Var { name, .. } => (None, self.interner.lookup_interned(name)), + Pattern::Assign { name, .. } => (None, self.interner.lookup_interned(name)), Pattern::Discard { name, .. } => { if props.full_check { - format!("__discard_{}_{}", name, index) + ( + Some(format!("__discard_{}_{}", name, index)), + introduce_name( + &mut self.interner, + &format!("__discard_{}_{}", name, index), + ), + ) } else { - "_".to_string() + (None, DISCARDED.to_string()) } } - _ => format!( - "tuple_{}_span_{}_{}", - index, - arg.location().start, - arg.location().end - ), + _ => { + let name = format!( + "tuple_{}_span_{}_{}", + index, + arg.location().start, + arg.location().end + ); + + let interned = introduce_name(&mut self.interner, &name); + + (Some(name), interned) + } }; let arg_type = type_map.get(&index).unwrap_or_else(|| { @@ -1523,9 +1730,9 @@ impl<'a> CodeGenerator<'a> { ) }); - let val = AirTree::local_var(&tuple_name, arg_type.clone()); + let val = AirTree::local_var(&tuple_name_interned, arg_type.clone()); - let then = if "_" != tuple_name { + let then = if DISCARDED != tuple_name_interned { self.assignment( arg, val, @@ -1543,7 +1750,11 @@ impl<'a> CodeGenerator<'a> { then }; - fields.push(tuple_name); + fields.push(tuple_name_interned); + + if let Some(tuple_name) = tuple_name { + self.interner.pop_text(tuple_name); + } then }); @@ -1558,10 +1769,12 @@ impl<'a> CodeGenerator<'a> { pattern.location().end ); - let local_var = AirTree::local_var(&name, tipo.clone()); + let name_interned = introduce_name(&mut self.interner, &name); - assign_casted_value( - name, + let local_var = AirTree::local_var(&name_interned, tipo.clone()); + + let tree = assign_casted_value( + name_interned, value, AirTree::tuple_access( fields, @@ -1571,7 +1784,11 @@ impl<'a> CodeGenerator<'a> { then, otherwise, ), - ) + ); + + self.interner.pop_text(name); + + tree } } } @@ -1585,7 +1802,6 @@ impl<'a> CodeGenerator<'a> { location: Span, then: AirTree, otherwise: Otherwise, - depth: usize, ) -> AirTree { assert!( tipo.get_generic().is_none(), @@ -1621,77 +1837,69 @@ impl<'a> CodeGenerator<'a> { assert!(inner_pair_types.len() == 2); - let map_name = format!("__map_{}_span_{}_{}", depth, location.start, location.end); - let pair_name = - format!("__pair_{}_span_{}_{}", depth, location.start, location.end); - let fst_name = format!( - "__pair_fst_{}_span_{}_{}", - depth, location.start, location.end - ); - let snd_name = format!( - "__pair_snd_{}_span_{}_{}", - depth, location.start, location.end - ); + let map_name = format!("__map_span_{}_{}", location.start, location.end); + let pair_name = format!("__pair_span_{}_{}", location.start, location.end); + let fst_name = format!("__pair_fst_span_{}_{}", location.start, location.end); + let snd_name = format!("__pair_snd_span_{}_{}", location.start, location.end); + let curried_expect_on_list = "__curried_expect_on_list".to_string(); + let list = "__list".to_string(); + + let map_name_interned = introduce_name(&mut self.interner, &map_name); + let pair_name_interned = introduce_name(&mut self.interner, &pair_name); + let fst_name_interned = introduce_name(&mut self.interner, &fst_name); + let snd_name_interned = introduce_name(&mut self.interner, &snd_name); + let curried_expect_on_list_interned = + introduce_name(&mut self.interner, &curried_expect_on_list); + let list_interned = introduce_name(&mut self.interner, &list); let expect_snd = self.expect_type_assign( &inner_pair_types[1], - AirTree::local_var(snd_name.clone(), inner_pair_types[1].clone()), + AirTree::local_var(snd_name_interned.clone(), inner_pair_types[1].clone()), defined_data_types, location, AirTree::call( - AirTree::local_var( - format!("__curried_expect_on_list_{}", depth), - Type::void(), - ), + AirTree::local_var(&curried_expect_on_list_interned, Type::void()), Type::void(), vec![AirTree::builtin( DefaultFunction::TailList, Type::list(Type::data()), - vec![AirTree::local_var( - format!("__list_{}", depth), - tipo.clone(), - )], + vec![AirTree::local_var(&list_interned, tipo.clone())], )], ), otherwise.clone(), - depth + 1, ); let expect_fst = self.expect_type_assign( &inner_pair_types[0], - AirTree::local_var(fst_name.clone(), inner_pair_types[0].clone()), + AirTree::local_var(fst_name_interned.clone(), inner_pair_types[0].clone()), defined_data_types, location, expect_snd, otherwise.clone(), - depth + 1, ); let unwrap_function = AirTree::anon_func( - vec![ - format!("__list_{}", depth), - format!("__curried_expect_on_list_{}", depth), - ], + vec![list_interned.clone(), curried_expect_on_list_interned], AirTree::list_empty( - AirTree::local_var(format!("__list_{}", depth), tipo.clone()), + AirTree::local_var(&list_interned, tipo.clone()), then, AirTree::anon_func( vec![], AirTree::let_assignment( - &pair_name, + &pair_name_interned, AirTree::builtin( DefaultFunction::HeadList, Type::pair(Type::data(), Type::data()), - vec![AirTree::local_var( - format!("__list_{}", depth), - tipo.clone(), - )], + vec![AirTree::local_var(list_interned, tipo.clone())], ), AirTree::pair_access( - Some(fst_name), - Some(snd_name), + Some(fst_name_interned), + Some(snd_name_interned), inner_list_type.clone(), - AirTree::local_var(&pair_name, inner_list_type.clone()), + AirTree::local_var( + &pair_name_interned, + inner_list_type.clone(), + ), true, expect_fst, otherwise.unwrap_or_else(DELAY_ERROR), @@ -1705,6 +1913,7 @@ impl<'a> CodeGenerator<'a> { let function = self.code_gen_functions.get(EXPECT_ON_LIST); + // This function can be defined here on in the branch below if function.is_none() { let expect_list_func = AirTree::expect_on_list2(); self.code_gen_functions.insert( @@ -1739,10 +1948,22 @@ impl<'a> CodeGenerator<'a> { "", ), Type::void(), - vec![AirTree::local_var(&map_name, tipo.clone()), unwrap_function], + vec![ + AirTree::local_var(&map_name_interned, tipo.clone()), + unwrap_function, + ], ); - AirTree::let_assignment(&map_name, value, func_call) + let tree = AirTree::let_assignment(map_name_interned, value, func_call); + + self.interner.pop_text(map_name); + self.interner.pop_text(pair_name); + self.interner.pop_text(fst_name); + self.interner.pop_text(snd_name); + self.interner.pop_text(curried_expect_on_list); + self.interner.pop_text(list); + + tree } // Tuple type Some(UplcType::List(_)) if tipo.is_tuple() => { @@ -1750,8 +1971,9 @@ impl<'a> CodeGenerator<'a> { assert!(!tuple_inner_types.is_empty()); - let tuple_name = - format!("__tuple_{}_span_{}_{}", depth, location.start, location.end); + let tuple_name = format!("__tuple_span_{}_{}", location.start, location.end); + + let tuple_name_interned = introduce_name(&mut self.interner, &tuple_name); let mut tuple_expect_items = vec![]; @@ -1761,21 +1983,25 @@ impl<'a> CodeGenerator<'a> { .enumerate() .rfold(then, |then, (index, arg)| { let tuple_index_name = format!( - "__tuple_{}_index_{}_span_{}_{}", - depth, index, location.start, location.end + "__tuple_index_{}_span_{}_{}", + index, location.start, location.end ); + let tuple_index_name_interned = + introduce_name(&mut self.interner, &tuple_index_name); + let expect_tuple_item = self.expect_type_assign( arg, - AirTree::local_var(&tuple_index_name, arg.clone()), + AirTree::local_var(&tuple_index_name_interned, arg.clone()), defined_data_types, location, then, otherwise.clone(), - depth + 1, ); - tuple_expect_items.push(tuple_index_name); + tuple_expect_items.push(tuple_index_name_interned); + + self.interner.pop_text(tuple_index_name); expect_tuple_item }); @@ -1785,13 +2011,17 @@ impl<'a> CodeGenerator<'a> { let tuple_access = AirTree::tuple_access( tuple_expect_items, tipo.clone(), - AirTree::local_var(&tuple_name, tipo.clone()), + AirTree::local_var(&tuple_name_interned, tipo.clone()), true, then, otherwise.unwrap_or_else(DELAY_ERROR), ); - AirTree::let_assignment(&tuple_name, value, tuple_access) + let tree = AirTree::let_assignment(tuple_name_interned, value, tuple_access); + + self.interner.pop_text(tuple_name); + + tree } // Regular List type Some(UplcType::List(_)) => { @@ -1802,43 +2032,45 @@ impl<'a> CodeGenerator<'a> { if inner_list_type.is_data() { then } else { - let list_name = - format!("__list_{}_span_{}_{}", depth, location.start, location.end); - let item_name = - format!("__item_{}_span_{}_{}", depth, location.start, location.end); + let list_name = format!("__list_span_{}_{}", location.start, location.end); + let item_name = format!("__item_span_{}_{}", location.start, location.end); + let list = format!("__list"); + let curried_func = format!("__curried_expect_on_list"); + + let list_name_interned = introduce_name(&mut self.interner, &list_name); + let item_name_interned = introduce_name(&mut self.interner, &item_name); + let list_interned = introduce_name(&mut self.interner, &list); + let curried_func_interned = introduce_name(&mut self.interner, &curried_func); let unwrap_function = AirTree::anon_func( - vec![ - format!("__list_{}", depth), - format!("__curried_expect_on_list_{}", depth), - ], + vec![list_interned.clone(), curried_func_interned.clone()], AirTree::list_empty( - AirTree::local_var(format!("__list_{}", depth), tipo.clone()), + AirTree::local_var(&list_interned, tipo.clone()), then, AirTree::anon_func( vec![], AirTree::let_assignment( - &item_name, + &item_name_interned, AirTree::builtin( DefaultFunction::HeadList, Type::data(), - vec![AirTree::local_var( - format!("__list_{}", depth), - tipo.clone(), - )], + vec![AirTree::local_var(&list_interned, tipo.clone())], ), AirTree::soft_cast_assignment( - &item_name, + &item_name_interned, inner_list_type.clone(), - AirTree::local_var(&item_name, Type::data()), + AirTree::local_var(&item_name_interned, Type::data()), self.expect_type_assign( inner_list_type, - AirTree::local_var(&item_name, inner_list_type.clone()), + AirTree::local_var( + &item_name_interned, + inner_list_type.clone(), + ), defined_data_types, location, AirTree::call( AirTree::local_var( - format!("__curried_expect_on_list_{}", depth), + curried_func_interned, Type::void(), ), Type::void(), @@ -1846,13 +2078,12 @@ impl<'a> CodeGenerator<'a> { DefaultFunction::TailList, Type::list(Type::data()), vec![AirTree::local_var( - format!("__list_{}", depth), + list_interned, tipo.clone(), )], )], ), otherwise.clone(), - depth + 1, ), otherwise.unwrap_or_else(DELAY_ERROR), ), @@ -1903,12 +2134,19 @@ impl<'a> CodeGenerator<'a> { ), Type::void(), vec![ - AirTree::local_var(&list_name, tipo.clone()), + AirTree::local_var(&list_name_interned, tipo.clone()), unwrap_function, ], ); - AirTree::let_assignment(&list_name, value, func_call) + let tree = AirTree::let_assignment(list_name_interned, value, func_call); + + self.interner.pop_text(list_name); + self.interner.pop_text(item_name); + self.interner.pop_text(list); + self.interner.pop_text(curried_func); + + tree } } // Pair type @@ -1917,49 +2155,49 @@ impl<'a> CodeGenerator<'a> { assert!(tuple_inner_types.len() == 2); - let pair_name = - format!("__pair_{}_span_{}_{}", depth, location.start, location.end); + let pair_name = format!("__pair_span_{}_{}", location.start, location.end); + let fst_name = format!("__pair_fst_span_{}_{}", location.start, location.end); + let snd_name = format!("__pair_snd_span_{}_{}", location.start, location.end); - let fst_name = format!( - "__pair_fst_{}_span_{}_{}", - depth, location.start, location.end - ); - let snd_name = format!( - "__pair_snd_{}_span_{}_{}", - depth, location.start, location.end - ); + let pair_name_interned = introduce_name(&mut self.interner, &pair_name); + let fst_name_interned = introduce_name(&mut self.interner, &fst_name); + let snd_name_interned = introduce_name(&mut self.interner, &snd_name); let expect_snd = self.expect_type_assign( &tuple_inner_types[1], - AirTree::local_var(snd_name.clone(), tuple_inner_types[1].clone()), + AirTree::local_var(snd_name_interned.clone(), tuple_inner_types[1].clone()), defined_data_types, location, then, otherwise.clone(), - depth + 1, ); let expect_fst = self.expect_type_assign( &tuple_inner_types[0], - AirTree::local_var(fst_name.clone(), tuple_inner_types[0].clone()), + AirTree::local_var(fst_name_interned.clone(), tuple_inner_types[0].clone()), defined_data_types, location, expect_snd, otherwise.clone(), - depth + 1, ); let pair_access = AirTree::pair_access( - Some(fst_name.clone()), - Some(snd_name.clone()), + Some(fst_name_interned), + Some(snd_name_interned), tipo.clone(), - AirTree::local_var(&pair_name, tipo.clone()), + AirTree::local_var(&pair_name_interned, tipo.clone()), true, expect_fst, otherwise.unwrap_or_else(DELAY_ERROR), ); - AirTree::let_assignment(&pair_name, value, pair_access) + let tree = AirTree::let_assignment(pair_name_interned, value, pair_access); + + self.interner.pop_text(pair_name); + self.interner.pop_text(fst_name); + self.interner.pop_text(snd_name); + + tree } // Constr type @@ -1973,7 +2211,7 @@ impl<'a> CodeGenerator<'a> { .get_inner_types() .iter() .map(|arg| get_arg_type_name(arg)) - .join("_"); + .join(DISCARDED); assert!(data_type.typed_parameters.len() == tipo.arg_types().unwrap().len()); @@ -2043,7 +2281,6 @@ impl<'a> CodeGenerator<'a> { location, then, otherwise_delayed.clone(), - depth + 1, ) }, ); @@ -2186,10 +2423,11 @@ impl<'a> CodeGenerator<'a> { props.complex_clause = false; if let Some((clause, rest_clauses)) = clauses.split_first() { + introduce_pattern(&mut self.interner, &clause.pattern); + let clause_then = self.build(&clause.then, module_name, &[]); - match &mut props.specific_clause { - // TODO: Implement PairClause and PairClauseGuard + let tree = match &mut props.specific_clause { SpecificClause::ConstrClause => { let data_type = lookup_data_type_by_tipo(&self.data_types, subject_tipo); @@ -2510,16 +2748,24 @@ impl<'a> CodeGenerator<'a> { ), ) } - } + }; + + pop_pattern(&mut self.interner, &clause.pattern); + + tree } else { // handle final_clause props.final_clause = true; + introduce_pattern(&mut self.interner, &final_clause.pattern); + let clause_then = self.build(&final_clause.then, module_name, &[]); let (condition, assignments) = self.clause_pattern(&final_clause.pattern, subject_tipo, props, clause_then); + pop_pattern(&mut self.interner, &final_clause.pattern); + AirTree::finally(condition, assignments) } } @@ -2541,15 +2787,20 @@ impl<'a> CodeGenerator<'a> { assert!(!props.final_clause); (AirTree::byte_array(value.clone()), then) } - Pattern::Var { name, .. } => ( - AirTree::void(), - AirTree::let_assignment( - name, - AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), - then, - ), - ), + Pattern::Var { name, .. } => { + let name = self.interner.lookup_interned(name); + + ( + AirTree::void(), + AirTree::let_assignment( + name, + AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), + then, + ), + ) + } Pattern::Assign { name, pattern, .. } => { + let name = self.interner.lookup_interned(name); let (inner_condition, inner_assignment) = self.clause_pattern(pattern, subject_tipo, props, then); @@ -2589,9 +2840,7 @@ impl<'a> CodeGenerator<'a> { assert!(!elements.is_empty()); let tail = defined_tails.get(elements.len() - 1); let elem_name = match elem.as_ref() { - Pattern::Var { name, .. } => name.to_string(), - Pattern::Assign { name, .. } => name.to_string(), - Pattern::Discard { .. } => "_".to_string(), + Pattern::Discard { .. } => DISCARDED.to_string(), _ => format!( "tail_span_{}_{}", elem.location().start, @@ -2606,11 +2855,11 @@ impl<'a> CodeGenerator<'a> { props.final_clause, ); - if &elem_name != "_" && !defined_tails.is_empty() { + if &elem_name != DISCARDED && !defined_tails.is_empty() { list_tail = Some((tail.unwrap().to_string(), elem_name.to_string())); } - let inner_then = if elem_name != "_" { + let inner_then = if elem_name != DISCARDED { self.nested_clause_condition( elem, subject_tipo, @@ -2638,9 +2887,7 @@ impl<'a> CodeGenerator<'a> { // TODO: Turn 'Pattern' into another type instead of using strings and // expecting a special magic string '_'. let elem_name = match elem { - Pattern::Var { name, .. } => name.to_string(), - Pattern::Assign { name, .. } => name.to_string(), - Pattern::Discard { .. } => "_".to_string(), + Pattern::Discard { .. } => DISCARDED.to_string(), _ => format!( "elem_{}_span_{}_{}", index, @@ -2656,7 +2903,7 @@ impl<'a> CodeGenerator<'a> { props.final_clause, ); - let elems_then = if elem_name != "_" { + let elems_then = if elem_name != DISCARDED { self.nested_clause_condition( elem, list_elem_type, @@ -2699,7 +2946,7 @@ impl<'a> CodeGenerator<'a> { elems .into_iter() .zip(defined_tails) - .filter(|(head, _)| head != "_") + .filter(|(head, _)| head != DISCARDED) .map(|(head, tail)| (tail, head)) .collect_vec(), list_tail, @@ -2722,8 +2969,6 @@ impl<'a> CodeGenerator<'a> { .enumerate() .rfold(then, |inner_then, (index, element)| { let elem_name = match element.as_ref() { - Pattern::Var { name, .. } => Some(name.to_string()), - Pattern::Assign { name, .. } => Some(name.to_string()), Pattern::Discard { .. } => None, _ => Some(format!( "pair_index_{}_span_{}_{}", @@ -2735,8 +2980,8 @@ impl<'a> CodeGenerator<'a> { let mut pair_props = ClauseProperties::init_inner( &items_type[index], - elem_name.clone().unwrap_or_else(|| "_".to_string()), - elem_name.clone().unwrap_or_else(|| "_".to_string()), + elem_name.clone().unwrap_or_else(|| DISCARDED.to_string()), + elem_name.clone().unwrap_or_else(|| DISCARDED.to_string()), props.final_clause, ); @@ -2841,9 +3086,7 @@ impl<'a> CodeGenerator<'a> { }; let field_name = match &arg.value { - Pattern::Var { name, .. } => name.to_string(), - Pattern::Assign { name, .. } => name.to_string(), - Pattern::Discard { .. } => "_".to_string(), + Pattern::Discard { .. } => DISCARDED.to_string(), _ => format!( "field_{}_span_{}_{}", field_index, @@ -2866,7 +3109,7 @@ impl<'a> CodeGenerator<'a> { props.final_clause, ); - let statement = if field_name != "_" { + let statement = if field_name != DISCARDED { self.nested_clause_condition( &arg.value, arg_type, @@ -2894,7 +3137,7 @@ impl<'a> CodeGenerator<'a> { AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()), next_then, ) - } else if fields.iter().all(|s| s.1 == "_") { + } else if fields.iter().all(|s| s.1 == DISCARDED) { next_then } else { AirTree::fields_expose( @@ -2919,9 +3162,7 @@ impl<'a> CodeGenerator<'a> { .enumerate() .rfold(then, |inner_then, (index, element)| { let elem_name = match element { - Pattern::Var { name, .. } => name.to_string(), - Pattern::Assign { name, .. } => name.to_string(), - Pattern::Discard { .. } => "_".to_string(), + Pattern::Discard { .. } => DISCARDED.to_string(), _ => format!( "tuple_index_{}_span_{}_{}", index, @@ -2937,7 +3178,7 @@ impl<'a> CodeGenerator<'a> { props.final_clause, ); - let elem = if elem_name != "_" { + let elem = if elem_name != DISCARDED { self.nested_clause_condition( element, &items_type[index], @@ -2977,7 +3218,7 @@ impl<'a> CodeGenerator<'a> { .find(|(defined_index, _nm)| defined_index == index) { previous_defined_names.push((*index, prev_name.clone(), name.clone())); - } else if name != "_" { + } else if name != DISCARDED { assert!(defined_indices.insert((*index, name.clone()))); names_to_define.push((*index, name.clone())); } else { @@ -3017,7 +3258,7 @@ impl<'a> CodeGenerator<'a> { .into_iter() .fold(vec![], |mut names, (index, name)| { while names.len() < index { - names.push("_".to_string()); + names.push(DISCARDED.to_string()); } names.push(name); names @@ -3072,11 +3313,15 @@ impl<'a> CodeGenerator<'a> { then, ) } - Pattern::Var { name, .. } => AirTree::let_assignment( - name, - AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), - then, - ), + Pattern::Var { name, .. } => { + let name = self.interner.lookup_interned(name); + + AirTree::let_assignment( + name, + AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), + then, + ) + } Pattern::Assign { name, pattern, .. } => AirTree::let_assignment( name, AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), @@ -3244,89 +3489,6 @@ impl<'a> CodeGenerator<'a> { } } - pub fn check_validator_args( - &mut self, - arguments: &[TypedArg], - has_context: bool, - body: AirTree, - src_code: &str, - lines: &LineNumbers, - ) -> AirTree { - let mut arg_names = vec![]; - - arguments - .iter() - .rev() - .with_position() - .fold(body, |inner_then, arg_position| match arg_position { - itertools::Position::First(arg) if has_context => { - let arg_name = arg.arg_name.get_variable_name().unwrap_or("_").to_string(); - - AirTree::anon_func(vec![arg_name], inner_then, true) - } - itertools::Position::First(arg) - | itertools::Position::Middle(arg) - | itertools::Position::Last(arg) => { - let arg_name = arg.arg_name.get_variable_name().unwrap_or("_").to_string(); - let arg_span = arg.location; - - arg_names.push(arg_name.clone()); - - let param = AirTree::local_var(&arg_name, Type::data()); - - let actual_type = convert_opaque_type(&arg.tipo, &self.data_types, true); - - let otherwise_delayed = { - let msg = match self.tracing { - TraceLevel::Silent => "".to_string(), - TraceLevel::Compact => lines - .line_and_column_number(arg_span.start) - .expect("Out of bounds span") - .to_string(), - TraceLevel::Verbose => src_code - .get(arg_span.start..arg_span.end) - .expect("Out of bounds span") - .to_string(), - }; - - let msg_func_name = msg.split_whitespace().join(""); - - if msg.is_empty() { - None - } else { - self.special_functions.insert_new_function( - msg_func_name.clone(), - Term::Error.delayed_trace(Term::string(msg)).delay(), - Type::void(), - ); - - Some(self.special_functions.use_function_tree(msg_func_name)) - } - }; - - let inner_then = self.assignment( - &Pattern::Var { - location: Span::empty(), - name: arg_name.to_string(), - }, - param, - inner_then, - &actual_type, - AssignmentProperties { - value_type: Type::data(), - kind: AssignmentKind::expect(), - remove_unused: false, - full_check: true, - otherwise: otherwise_delayed, - }, - ); - - AirTree::anon_func(vec![arg_name], inner_then, true) - } - itertools::Position::Only(_) => unreachable!(), - }) - } - fn hoist_functions_to_validator(&mut self, mut air_tree: AirTree) -> AirTree { let mut functions_to_hoist = IndexMap::new(); let mut used_functions = vec![]; @@ -4139,11 +4301,17 @@ impl<'a> CodeGenerator<'a> { if let Some((path, _)) = func_variants.get_mut(&variant) { *path = path.common_ancestor(tree_path); } else { - let params = function_def - .arguments + let args = function_def.arguments.clone(); + + let params = args .iter() .map(|arg| { - arg.arg_name.get_variable_name().unwrap_or("_").to_string() + arg.arg_name + .get_variable_name() + .map(|arg| { + introduce_name(&mut self.interner, &arg.to_string()) + }) + .unwrap_or_else(|| DISCARDED.to_string()) }) .collect_vec(); @@ -4161,6 +4329,12 @@ impl<'a> CodeGenerator<'a> { true, ); + args.iter().for_each(|arg| { + arg.arg_name.get_variable_name().iter().for_each(|arg| { + self.interner.pop_text(arg.to_string()); + }) + }); + func_variants.insert( variant, ( @@ -4174,10 +4348,16 @@ impl<'a> CodeGenerator<'a> { ); } } else { - let params = function_def - .arguments + let args = function_def.arguments.clone(); + + let params = args .iter() - .map(|arg| arg.arg_name.get_variable_name().unwrap_or("_").to_string()) + .map(|arg| { + arg.arg_name + .get_variable_name() + .map(|arg| introduce_name(&mut self.interner, &arg.to_string())) + .unwrap_or_else(|| DISCARDED.to_string()) + }) .collect_vec(); let mut function_air_tree_body = AirTree::no_op(self.build( @@ -4196,6 +4376,13 @@ impl<'a> CodeGenerator<'a> { let mut function_variant_path = IndexMap::new(); + args.iter().for_each(|arg| { + arg.arg_name + .get_variable_name() + .iter() + .for_each(|arg| self.interner.pop_text(arg.to_string())) + }); + function_variant_path.insert( variant, ( @@ -5255,7 +5442,7 @@ impl<'a> CodeGenerator<'a> { ) } } else if tipo.is_void() { - Some(then.lambda("_").apply(Term::var(subject_name))) + Some(then.lambda(DISCARDED).apply(Term::var(subject_name))) } else { let uplc_type = tipo.get_uplc_type(); @@ -5463,7 +5650,7 @@ impl<'a> CodeGenerator<'a> { let named_indices = names_types .iter() - .skip_while(|(name, _, _)| name == "_") + .skip_while(|(name, _, _)| name == DISCARDED) .collect_vec(); if !named_indices.is_empty() || is_expect { diff --git a/crates/aiken-lang/src/gen_uplc/builder.rs b/crates/aiken-lang/src/gen_uplc/builder.rs index 0daaf372..754f89fa 100644 --- a/crates/aiken-lang/src/gen_uplc/builder.rs +++ b/crates/aiken-lang/src/gen_uplc/builder.rs @@ -1,6 +1,7 @@ use super::{ air::ExpectLevel, - tree::{AirMsg, AirTree, TreePath}, + interner::AirInterner, + tree::{AirMsg, AirTree}, }; use crate::{ ast::{ @@ -17,7 +18,7 @@ use crate::{ }; use indexmap::{IndexMap, IndexSet}; use itertools::{Itertools, Position}; -use std::{collections::HashMap, ops::Deref, rc::Rc}; +use std::{ops::Deref, rc::Rc}; use uplc::{ ast::{Constant as UplcConstant, Name, Term, Type as UplcType}, builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER}, @@ -41,6 +42,7 @@ 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"; +pub const DISCARDED: &str = "_"; #[derive(Clone, Debug)] pub enum CodeGenFunction { @@ -421,21 +423,17 @@ pub fn is_recursive_function_call<'a>( pub fn identify_recursive_static_params( air_tree: &mut AirTree, - tree_path: &TreePath, + func_params: &[String], func_key: &(FunctionAccessKey, String), function_calls_and_usage: &mut (usize, usize), - shadowed_parameters: &mut HashMap, + potential_recursive_statics: &mut Vec, ) { let variant = &func_key.1; let func_key = &func_key.0; - // Find whether any of the potential recursive statics get shadowed (because even if we pass in the same referenced name, it might not be static) - for introduced_variable in find_introduced_variables(air_tree) { - if potential_recursive_statics.contains(&introduced_variable) { - shadowed_parameters.insert(introduced_variable, tree_path.clone()); - } - } + + // Now all variables can't be shadowed at this stage due to interning // Otherwise, if this is a recursive call site, disqualify anything that is different (or the same, but shadowed) if let (true, Some(args)) = is_recursive_function_call(air_tree, func_key, variant) { for (param, arg) in func_params.iter().zip(args) { @@ -446,18 +444,9 @@ pub fn identify_recursive_static_params( // Check if we pass something different in this recursive call site // by different, we mean // - a variable that is bound to a different name - // - a variable with the same name, but that was shadowed in an ancestor scope // - any other type of expression let param_is_different = match arg { - AirTree::Var { name, .. } => { - // "shadowed in an ancestor scope" means "the definition scope is a prefix of our scope" - name != param - || if let Some(p) = shadowed_parameters.get(param) { - p.common_ancestor(tree_path) == *p - } else { - false - } - } + AirTree::Var { name, .. } => name != param || false, _ => true, }; // If so, then we disqualify this parameter from being a recursive static parameter @@ -499,17 +488,14 @@ pub fn modify_self_calls( // identify which parameters are recursively nonstatic (i.e. get modified before the self-call) // TODO: this would be a lot simpler if each `Var`, `Let`, function argument, etc. had a unique identifier // rather than just a name; this would let us track if the Var passed to itself was the same value as the method argument - let mut shadowed_parameters: HashMap = HashMap::new(); let mut calls_and_var_usage = (0, 0); body.traverse_tree_with( - &mut |air_tree: &mut AirTree, tree_path| { + &mut |air_tree: &mut AirTree, _tree_path| { identify_recursive_static_params( air_tree, - tree_path, func_params, &(func_key.clone(), variant.clone()), &mut calls_and_var_usage, - &mut shadowed_parameters, &mut potential_recursive_statics, ); }, @@ -1661,19 +1647,26 @@ pub fn get_list_elements_len_and_tail( } } -pub fn cast_validator_args(term: Term, arguments: &[TypedArg]) -> Term { +pub fn cast_validator_args( + term: Term, + arguments: &[TypedArg], + interner: &AirInterner, +) -> Term { let mut term = term; for arg in arguments.iter().rev() { + let name = arg + .arg_name + .get_variable_name() + .map(|arg| interner.lookup_interned(&arg.to_string())) + .unwrap_or_else(|| "_".to_string()); + if !matches!(arg.tipo.get_uplc_type(), Some(UplcType::Data) | None) { term = term - .lambda(arg.arg_name.get_variable_name().unwrap_or("_")) - .apply(known_data_to_type( - Term::var(arg.arg_name.get_variable_name().unwrap_or("_")), - &arg.tipo, - )); + .lambda(&name) + .apply(known_data_to_type(Term::var(&name), &arg.tipo)); } - term = term.lambda(arg.arg_name.get_variable_name().unwrap_or("_")) + term = term.lambda(name) } term } @@ -1751,3 +1744,89 @@ pub fn get_line_columns_by_span( .line_and_column_number(span.start) .expect("Out of bounds span") } + +pub fn introduce_pattern(interner: &mut AirInterner, pattern: &TypedPattern) { + match pattern { + Pattern::Int { .. } | Pattern::ByteArray { .. } | Pattern::Discard { .. } => (), + + Pattern::Var { name, .. } => { + interner.intern(name.clone()); + } + Pattern::Assign { name, pattern, .. } => { + interner.intern(name.clone()); + introduce_pattern(interner, pattern); + } + + Pattern::List { elements, tail, .. } => { + elements.iter().for_each(|element| { + introduce_pattern(interner, element); + }); + + tail.iter().for_each(|tail| { + introduce_pattern(interner, tail); + }); + } + Pattern::Constructor { + arguments: elems, .. + } => { + elems.iter().for_each(|element| { + introduce_pattern(interner, &element.value); + }); + } + Pattern::Tuple { elems, .. } => { + elems.iter().for_each(|element| { + introduce_pattern(interner, element); + }); + } + Pattern::Pair { fst, snd, .. } => { + introduce_pattern(interner, fst); + introduce_pattern(interner, snd); + } + } +} + +pub fn pop_pattern(interner: &mut AirInterner, pattern: &TypedPattern) { + match pattern { + Pattern::Int { .. } | Pattern::ByteArray { .. } | Pattern::Discard { .. } => (), + + Pattern::Var { name, .. } => { + interner.pop_text(name.clone()); + } + Pattern::Assign { name, pattern, .. } => { + interner.pop_text(name.clone()); + pop_pattern(interner, pattern); + } + + Pattern::List { elements, tail, .. } => { + elements.iter().for_each(|element| { + pop_pattern(interner, element); + }); + + tail.iter().for_each(|tail| { + pop_pattern(interner, tail); + }); + } + Pattern::Constructor { + arguments: elems, .. + } => { + elems.iter().for_each(|element| { + pop_pattern(interner, &element.value); + }); + } + Pattern::Tuple { elems, .. } => { + elems.iter().for_each(|element| { + pop_pattern(interner, element); + }); + } + Pattern::Pair { fst, snd, .. } => { + pop_pattern(interner, fst); + pop_pattern(interner, snd); + } + } +} + +pub fn introduce_name(interner: &mut AirInterner, name: &String) -> String { + interner.intern(name.clone()); + + interner.lookup_interned(&name) +} diff --git a/crates/aiken-lang/src/gen_uplc/interner.rs b/crates/aiken-lang/src/gen_uplc/interner.rs new file mode 100644 index 00000000..166bafb2 --- /dev/null +++ b/crates/aiken-lang/src/gen_uplc/interner.rs @@ -0,0 +1,57 @@ +use std::collections::HashMap; + +use vec1::{vec1, Vec1}; + +#[derive(Clone)] +pub struct AirInterner { + identifiers: HashMap>, + current: usize, +} + +impl Default for AirInterner { + fn default() -> Self { + Self::new() + } +} + +/// Interner that uses previous uniques to prevent future unique collisions +/// when performing optimizations +impl AirInterner { + pub fn new() -> Self { + Self { + identifiers: HashMap::new(), + current: 0, + } + } + + pub fn intern(&mut self, text: String) { + if let Some(u) = self.identifiers.get_mut(&text) { + u.push(self.current); + + self.current += 1; + } else { + self.identifiers.insert(text, vec1!(self.current)); + + self.current += 1; + } + } + + pub fn pop_text(&mut self, text: String) { + if let Some(mut u) = self.identifiers.remove(&text) { + if u.len() != 1 { + u.pop().unwrap(); + self.identifiers.insert(text, u); + } + } else { + unreachable!("Looking up a missing text: {}", text); + } + } + + pub fn lookup_interned(&self, text: &String) -> String { + if let Some(u) = self.identifiers.get(text) { + format!("{}_id_{}", text, *u.last()) + } else { + unreachable!("Looking up a missing text: {}", text); + } + } +}