change: traverse_with_tree now has a boolean to determine when with is called

fix: Opaque types are now properly handled in code gen (i.e. code gen functions, in datums/redeemers, in from data casts)
chore: add specific nested opaque type tests to code gen
This commit is contained in:
microproofs 2023-08-19 20:07:37 -04:00
parent c6f764d2db
commit 084b900b2a
4 changed files with 445 additions and 94 deletions

View File

@ -23,10 +23,10 @@ use crate::{
builtins::{bool, data, int, void}, builtins::{bool, data, int, void},
expr::TypedExpr, expr::TypedExpr,
gen_uplc::builder::{ gen_uplc::builder::{
convert_opaque_type, erase_opaque_type_operations, find_and_replace_generics, check_replaceable_opaque_type, convert_opaque_type, erase_opaque_type_operations,
find_list_clause_or_default_first, get_arg_type_name, get_generic_id_and_type, find_and_replace_generics, find_list_clause_or_default_first, get_arg_type_name,
get_variant_name, monomorphize, pattern_has_conditions, wrap_as_multi_validator, get_generic_id_and_type, get_variant_name, monomorphize, pattern_has_conditions,
wrap_validator_condition, CodeGenFunction, SpecificClause, wrap_as_multi_validator, wrap_validator_condition, CodeGenFunction, SpecificClause,
}, },
tipo::{ tipo::{
ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor,
@ -539,7 +539,13 @@ impl<'a> CodeGenerator<'a> {
index, index,
record, record,
.. ..
} => AirTree::record_access(*index, tipo.clone(), self.build(record)), } => {
if check_replaceable_opaque_type(&record.tipo(), &self.data_types) {
self.build(record)
} else {
AirTree::record_access(*index, tipo.clone(), self.build(record))
}
}
TypedExpr::ModuleSelect { TypedExpr::ModuleSelect {
tipo, tipo,
@ -1013,7 +1019,11 @@ impl<'a> CodeGenerator<'a> {
// 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::fields_expose(indices, props.full_check, value)); if check_replaceable_opaque_type(tipo, &self.data_types) {
sequence.push(AirTree::let_assignment(&indices[0].1, value));
} else {
sequence.push(AirTree::fields_expose(indices, props.full_check, value));
}
sequence.append( sequence.append(
&mut fields &mut fields
@ -1116,6 +1126,7 @@ impl<'a> CodeGenerator<'a> {
location: Span, location: Span,
) -> AirTree { ) -> AirTree {
assert!(tipo.get_generic().is_none()); assert!(tipo.get_generic().is_none());
let tipo = &convert_opaque_type(tipo, &self.data_types);
if tipo.is_primitive() { if tipo.is_primitive() {
// Since we would return void anyway and ignore then we can just return value here and ignore // Since we would return void anyway and ignore then we can just return value here and ignore
@ -2158,11 +2169,25 @@ impl<'a> CodeGenerator<'a> {
let mut air_fields = fields.into_iter().map(|(_, _, _, val)| val).collect_vec(); let mut air_fields = fields.into_iter().map(|(_, _, _, val)| val).collect_vec();
let field_assign = AirTree::fields_expose( let field_assign =
indices, if check_replaceable_opaque_type(subject_tipo, &self.data_types) {
false, AirTree::let_assignment(
AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()), &indices[0].1,
); AirTree::local_var(
props.clause_var_name.clone(),
subject_tipo.clone(),
),
)
} else {
AirTree::fields_expose(
indices,
false,
AirTree::local_var(
props.clause_var_name.clone(),
subject_tipo.clone(),
),
)
};
let mut sequence = vec![field_assign]; let mut sequence = vec![field_assign];
@ -2530,9 +2555,12 @@ impl<'a> CodeGenerator<'a> {
let mut validator_hoistable; let mut validator_hoistable;
// TODO change subsequent tree traversals to be more like a stream. // TODO change subsequent tree traversals to be more like a stream.
air_tree.traverse_tree_with(&mut |air_tree: &mut AirTree, _| { air_tree.traverse_tree_with(
erase_opaque_type_operations(air_tree, &self.data_types); &mut |air_tree: &mut AirTree, _| {
}); erase_opaque_type_operations(air_tree, &self.data_types);
},
true,
);
self.find_function_vars_and_depth( self.find_function_vars_and_depth(
&mut air_tree, &mut air_tree,
@ -2964,7 +2992,8 @@ impl<'a> CodeGenerator<'a> {
let function_def = self.functions.get(&generic_function_key); let function_def = self.functions.get(&generic_function_key);
let Some(function_def) = function_def else { let Some(function_def) = function_def
else {
let code_gen_func = self let code_gen_func = self
.code_gen_functions .code_gen_functions
.get(&generic_function_key.function_name) .get(&generic_function_key.function_name)
@ -2988,12 +3017,21 @@ impl<'a> CodeGenerator<'a> {
let mut function_variant_path = IndexMap::new(); let mut function_variant_path = IndexMap::new();
let mut body = body.clone();
body.traverse_tree_with(
&mut |air_tree, _| {
erase_opaque_type_operations(air_tree, &self.data_types);
},
true,
);
function_variant_path.insert( function_variant_path.insert(
"".to_string(), "".to_string(),
( (
tree_path.clone(), tree_path.clone(),
UserFunction::Function { UserFunction::Function {
body: body.clone(), body,
deps: vec![], deps: vec![],
params: params.clone(), params: params.clone(),
}, },
@ -3067,10 +3105,13 @@ impl<'a> CodeGenerator<'a> {
let mut function_air_tree_body = self.build(&function_def.body); let mut function_air_tree_body = self.build(&function_def.body);
function_air_tree_body.traverse_tree_with(&mut |air_tree, _| { function_air_tree_body.traverse_tree_with(
erase_opaque_type_operations(air_tree, &self.data_types); &mut |air_tree, _| {
monomorphize(air_tree, &mono_types); erase_opaque_type_operations(air_tree, &self.data_types);
}); monomorphize(air_tree, &mono_types);
},
true,
);
func_variants.insert( func_variants.insert(
variant, variant,
@ -3093,10 +3134,13 @@ impl<'a> CodeGenerator<'a> {
let mut function_air_tree_body = self.build(&function_def.body); let mut function_air_tree_body = self.build(&function_def.body);
function_air_tree_body.traverse_tree_with(&mut |air_tree, _| { function_air_tree_body.traverse_tree_with(
erase_opaque_type_operations(air_tree, &self.data_types); &mut |air_tree, _| {
monomorphize(air_tree, &mono_types); erase_opaque_type_operations(air_tree, &self.data_types);
}); monomorphize(air_tree, &mono_types);
},
true,
);
let mut function_variant_path = IndexMap::new(); let mut function_variant_path = IndexMap::new();
@ -3116,6 +3160,7 @@ impl<'a> CodeGenerator<'a> {
} }
} }
}, },
true,
); );
} }

View File

@ -542,37 +542,14 @@ pub fn erase_opaque_type_operations(
air_tree: &mut AirTree, air_tree: &mut AirTree,
data_types: &IndexMap<DataTypeKey, &TypedDataType>, data_types: &IndexMap<DataTypeKey, &TypedDataType>,
) { ) {
if let AirTree::Expression(e) = air_tree { if let AirTree::Expression(AirExpression::Constr { tipo, args, .. }) = air_tree {
match e { if check_replaceable_opaque_type(tipo, data_types) {
AirExpression::Constr { tipo, args, .. } => { let arg = args.pop().unwrap();
if check_replaceable_opaque_type(tipo, data_types) { if let AirTree::Expression(AirExpression::CastToData { value, .. }) = arg {
let arg = args.pop().unwrap(); *air_tree = *value;
if let AirTree::Expression(AirExpression::CastToData { value, .. }) = arg { } else {
*air_tree = *value; *air_tree = arg;
} else {
*air_tree = arg;
}
}
} }
AirExpression::RecordAccess { record, .. } => {
if check_replaceable_opaque_type(&record.return_type(), data_types) {
*air_tree = (**record).clone();
}
}
_ => {}
}
} else if let AirTree::Statement {
statement: AirStatement::FieldsExpose {
record, indices, ..
},
hoisted_over: Some(hoisted_over),
} = air_tree
{
if check_replaceable_opaque_type(&record.return_type(), data_types) {
let name = indices[0].1.clone();
*air_tree = AirTree::let_assignment(name, (**record).clone())
.hoist_over((**hoisted_over).clone())
} }
} }
@ -720,17 +697,20 @@ pub fn modify_self_calls(
// TODO: this would be a lot simpler if each `Var`, `Let`, function argument, etc. had a unique identifier // 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 // 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<String, TreePath> = HashMap::new(); let mut shadowed_parameters: HashMap<String, TreePath> = HashMap::new();
body.traverse_tree_with(&mut |air_tree: &mut AirTree, tree_path| { body.traverse_tree_with(
identify_recursive_static_params( &mut |air_tree: &mut AirTree, tree_path| {
air_tree, identify_recursive_static_params(
tree_path, air_tree,
func_params, tree_path,
func_key, func_params,
variant, func_key,
&mut shadowed_parameters, variant,
&mut potential_recursive_statics, &mut shadowed_parameters,
); &mut potential_recursive_statics,
}); );
},
false,
);
// Find the index of any recursively static parameters, // Find the index of any recursively static parameters,
// so we can remove them from the call-site of each recursive call // so we can remove them from the call-site of each recursive call
@ -742,35 +722,38 @@ pub fn modify_self_calls(
.collect(); .collect();
// Modify any self calls to remove recursive static parameters and append `self` as a parameter for the recursion // Modify any self calls to remove recursive static parameters and append `self` as a parameter for the recursion
body.traverse_tree_with(&mut |air_tree: &mut AirTree, _| { body.traverse_tree_with(
if let AirTree::Expression(AirExpression::Call { func, args, .. }) = air_tree { &mut |air_tree: &mut AirTree, _| {
if let AirTree::Expression(AirExpression::Var { if let AirTree::Expression(AirExpression::Call { func, args, .. }) = air_tree {
constructor: if let AirTree::Expression(AirExpression::Var {
ValueConstructor { constructor:
variant: ValueConstructorVariant::ModuleFn { name, module, .. }, ValueConstructor {
.. variant: ValueConstructorVariant::ModuleFn { name, module, .. },
}, ..
variant_name, },
.. variant_name,
}) = func.as_ref() ..
{ }) = func.as_ref()
if name == &func_key.function_name
&& module == &func_key.module_name
&& variant == variant_name
{ {
// Remove any static-recursive-parameters, because they'll be bound statically if name == &func_key.function_name
// above the recursive part of the function && module == &func_key.module_name
// note: assumes that static_recursive_params is sorted && variant == variant_name
for arg in recursive_static_indexes.iter().rev() { {
args.remove(*arg); // Remove any static-recursive-parameters, because they'll be bound statically
// above the recursive part of the function
// note: assumes that static_recursive_params is sorted
for arg in recursive_static_indexes.iter().rev() {
args.remove(*arg);
}
let mut new_args = vec![func.as_ref().clone()];
new_args.append(args);
*args = new_args;
} }
let mut new_args = vec![func.as_ref().clone()];
new_args.append(args);
*args = new_args;
} }
} }
} },
}); true,
);
let recursive_nonstatics = func_params let recursive_nonstatics = func_params
.iter() .iter()
.filter(|p| !potential_recursive_statics.contains(p)) .filter(|p| !potential_recursive_statics.contains(p))

View File

@ -1362,9 +1362,13 @@ impl AirTree {
} }
} }
pub fn traverse_tree_with(&mut self, with: &mut impl FnMut(&mut AirTree, &TreePath)) { pub fn traverse_tree_with(
&mut self,
with: &mut impl FnMut(&mut AirTree, &TreePath),
apply_with_last: bool,
) {
let mut tree_path = TreePath::new(); let mut tree_path = TreePath::new();
self.do_traverse_tree_with(&mut tree_path, 0, 0, with); self.do_traverse_tree_with(&mut tree_path, 0, 0, with, apply_with_last);
} }
pub fn traverse_tree_with_path( pub fn traverse_tree_with_path(
@ -1373,8 +1377,9 @@ impl AirTree {
current_depth: usize, current_depth: usize,
depth_index: usize, depth_index: usize,
with: &mut impl FnMut(&mut AirTree, &TreePath), with: &mut impl FnMut(&mut AirTree, &TreePath),
apply_with_last: bool,
) { ) {
self.do_traverse_tree_with(path, current_depth, depth_index, with); self.do_traverse_tree_with(path, current_depth, depth_index, with, apply_with_last);
} }
fn do_traverse_tree_with( fn do_traverse_tree_with(
@ -1383,6 +1388,7 @@ impl AirTree {
current_depth: usize, current_depth: usize,
depth_index: usize, depth_index: usize,
with: &mut impl FnMut(&mut AirTree, &TreePath), with: &mut impl FnMut(&mut AirTree, &TreePath),
apply_with_last: bool,
) { ) {
let mut index_count = IndexCounter::new(); let mut index_count = IndexCounter::new();
tree_path.push(current_depth, depth_index); tree_path.push(current_depth, depth_index);
@ -1395,6 +1401,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirStatement::DefineFunc { func_body, .. } => { AirStatement::DefineFunc { func_body, .. } => {
@ -1403,6 +1410,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirStatement::AssertConstr { constr, .. } => { AirStatement::AssertConstr { constr, .. } => {
@ -1411,6 +1419,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirStatement::AssertBool { value, .. } => { AirStatement::AssertBool { value, .. } => {
@ -1419,6 +1428,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirStatement::ClauseGuard { pattern, .. } => { AirStatement::ClauseGuard { pattern, .. } => {
@ -1427,6 +1437,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirStatement::ListClauseGuard { .. } => {} AirStatement::ListClauseGuard { .. } => {}
@ -1437,6 +1448,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirStatement::ListAccessor { list, .. } => { AirStatement::ListAccessor { list, .. } => {
@ -1445,6 +1457,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirStatement::ListExpose { .. } => {} AirStatement::ListExpose { .. } => {}
@ -1454,6 +1467,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirStatement::NoOp => {} AirStatement::NoOp => {}
@ -1463,6 +1477,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirStatement::ListEmpty { list } => { AirStatement::ListEmpty { list } => {
@ -1471,12 +1486,15 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
}; };
} }
with(self, tree_path); if !apply_with_last {
with(self, tree_path);
}
match self { match self {
AirTree::Statement { AirTree::Statement {
@ -1488,6 +1506,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirTree::Expression(e) => match e { AirTree::Expression(e) => match e {
@ -1498,6 +1517,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
} }
@ -1508,6 +1528,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
} }
@ -1517,6 +1538,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
for arg in args { for arg in args {
@ -1525,6 +1547,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
} }
@ -1534,6 +1557,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirExpression::Builtin { args, .. } => { AirExpression::Builtin { args, .. } => {
@ -1543,6 +1567,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
} }
@ -1552,6 +1577,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
right.do_traverse_tree_with( right.do_traverse_tree_with(
@ -1559,6 +1585,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirExpression::UnOp { arg, .. } => { AirExpression::UnOp { arg, .. } => {
@ -1567,6 +1594,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirExpression::CastFromData { value, .. } => { AirExpression::CastFromData { value, .. } => {
@ -1575,6 +1603,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirExpression::CastToData { value, .. } => { AirExpression::CastToData { value, .. } => {
@ -1583,6 +1612,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirExpression::When { AirExpression::When {
@ -1593,6 +1623,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
clauses.do_traverse_tree_with( clauses.do_traverse_tree_with(
@ -1600,6 +1631,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirExpression::Clause { AirExpression::Clause {
@ -1613,6 +1645,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
then.do_traverse_tree_with( then.do_traverse_tree_with(
@ -1620,6 +1653,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
otherwise.do_traverse_tree_with( otherwise.do_traverse_tree_with(
@ -1627,6 +1661,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirExpression::ListClause { AirExpression::ListClause {
@ -1637,6 +1672,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
otherwise.do_traverse_tree_with( otherwise.do_traverse_tree_with(
@ -1644,6 +1680,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirExpression::WrapClause { then, otherwise } => { AirExpression::WrapClause { then, otherwise } => {
@ -1652,6 +1689,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
otherwise.do_traverse_tree_with( otherwise.do_traverse_tree_with(
@ -1659,6 +1697,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirExpression::TupleClause { AirExpression::TupleClause {
@ -1669,6 +1708,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
otherwise.do_traverse_tree_with( otherwise.do_traverse_tree_with(
@ -1676,6 +1716,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirExpression::Finally { pattern, then } => { AirExpression::Finally { pattern, then } => {
@ -1684,6 +1725,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
then.do_traverse_tree_with( then.do_traverse_tree_with(
@ -1691,6 +1733,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirExpression::If { AirExpression::If {
@ -1704,6 +1747,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
then.do_traverse_tree_with( then.do_traverse_tree_with(
@ -1711,6 +1755,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
otherwise.do_traverse_tree_with( otherwise.do_traverse_tree_with(
@ -1718,6 +1763,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirExpression::Constr { args, .. } => { AirExpression::Constr { args, .. } => {
@ -1727,6 +1773,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
} }
@ -1736,6 +1783,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
for arg in args { for arg in args {
arg.do_traverse_tree_with( arg.do_traverse_tree_with(
@ -1743,6 +1791,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
} }
@ -1752,6 +1801,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirExpression::TupleIndex { tuple, .. } => { AirExpression::TupleIndex { tuple, .. } => {
@ -1760,6 +1810,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
AirExpression::Trace { msg, then, .. } => { AirExpression::Trace { msg, then, .. } => {
@ -1768,6 +1819,7 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
then.do_traverse_tree_with( then.do_traverse_tree_with(
@ -1775,12 +1827,18 @@ impl AirTree {
current_depth + 1, current_depth + 1,
index_count.next_number(), index_count.next_number(),
with, with,
apply_with_last,
); );
} }
_ => {} _ => {}
}, },
a => unreachable!("GOT THIS {:#?}", a), a => unreachable!("GOT THIS {:#?}", a),
} }
if apply_with_last {
with(self, tree_path);
}
tree_path.pop(); tree_path.pop();
} }

View File

@ -5077,3 +5077,268 @@ fn list_clause_with_assign2() {
false, false,
); );
} }
#[test]
fn opaque_value_in_datum() {
let src = r#"
opaque type Value {
inner: Dict<Dict<Int>>
}
opaque type Dict<v> {
inner: List<(ByteArray, v)>
}
type Dat {
c: Int,
a: Value
}
validator {
fn spend(dat: Dat, red: Data, ctx: Data) {
let val = dat.a
expect [(_, amount)] = val.inner.inner
let final_amount = [(#"AA", 4)] |> Dict
final_amount == amount
}
}
"#;
assert_uplc(
src,
Term::tail_list()
.apply(Term::var("val"))
.delayed_choose_list(
Term::equals_data()
.apply(Term::map_data().apply(Term::var("final_amount")))
.apply(Term::map_data().apply(Term::var("amount")))
.lambda("final_amount")
.apply(Term::map_values(vec![Constant::ProtoPair(
Type::Data,
Type::Data,
Constant::Data(Data::bytestring(vec![170])).into(),
Constant::Data(Data::integer(4.into())).into(),
)]))
.lambda("amount")
.apply(
Term::unmap_data().apply(Term::snd_pair().apply(Term::var("tuple_item_0"))),
),
Term::Error.trace(Term::string(
"List/Tuple/Constr contains more items than expected",
)),
)
.lambda("tuple_item_0")
.apply(Term::head_list().apply(Term::var("val")))
.lambda("val")
.apply(
Term::unmap_data().apply(
Term::var(CONSTR_GET_FIELD)
.apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("dat")))
.apply(Term::integer(1.into())),
),
)
.delayed_if_else(Term::unit(), Term::Error)
.lambda("_")
.apply(
Term::equals_integer()
.apply(Term::integer(0.into()))
.apply(Term::var("subject"))
.delayed_if_else(
Term::tail_list()
.apply(Term::var("tail_1"))
.delayed_choose_list(
Term::unit().lambda("_").apply(
Term::var("expect_on_list").apply(Term::var("a")).apply(
Term::var("expect_on_list")
.apply(Term::var("pair_snd_outer"))
.apply(
Term::un_i_data()
.apply(
Term::snd_pair().apply(Term::var("pair")),
)
.lambda("pair_fst")
.apply(Term::un_b_data().apply(
Term::fst_pair().apply(Term::var("pair")),
))
.lambda("pair"),
)
.lambda("pair_snd_outer")
.apply(Term::unmap_data().apply(
Term::snd_pair().apply(Term::var("pair_outer")),
))
.lambda("pair_fst_outer")
.apply(Term::un_b_data().apply(
Term::fst_pair().apply(Term::var("pair_outer")),
))
.lambda("pair_outer"),
),
),
Term::Error.trace(Term::string(
"List/Tuple/Constr contains more items than expected",
)),
)
.lambda("a")
.apply(
Term::unmap_data()
.apply(Term::head_list().apply(Term::var("tail_1"))),
)
.lambda("tail_1")
.apply(Term::tail_list().apply(Term::var("dat_fields")))
.lambda("c")
.apply(
Term::un_i_data()
.apply(Term::head_list().apply(Term::var("dat_fields"))),
)
.lambda("dat_fields")
.apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("param_0"))),
Term::Error
.trace(Term::string("Constr index did not match any type variant")),
)
.lambda("subject")
.apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("param_0")))
.lambda("param_0")
.lambda("expect_on_list")
.apply(
Term::var("expect_on_list")
.apply(Term::var("expect_on_list"))
.apply(Term::var("list_to_check"))
.lambda("expect_on_list")
.apply(
Term::var("list_to_check")
.delayed_choose_list(
Term::unit(),
Term::var("expect_on_list")
.apply(Term::var("expect_on_list"))
.apply(
Term::tail_list().apply(Term::var("list_to_check")),
)
.lambda("_")
.apply(Term::var("check_with").apply(
Term::head_list().apply(Term::var("list_to_check")),
)),
)
.lambda("list_to_check")
.lambda("expect_on_list"),
)
.lambda("check_with")
.lambda("list_to_check"),
)
.apply(Term::var("dat")),
)
.lambda("ctx")
.lambda("red")
.lambda("dat")
.constr_get_field()
.constr_fields_exposer()
.constr_index_exposer(),
false,
);
}
#[test]
fn opaque_value_in_test() {
let src = r#"
pub opaque type Value {
inner: Dict<Dict<Int>>
}
pub opaque type Dict<v> {
inner: List<(ByteArray, v)>
}
pub type Dat {
c: Int,
a: Value
}
pub fn dat_new() -> Dat {
let v = Value { inner: Dict { inner: [("", [(#"aa", 4)] |> Dict)] } }
Dat {
c: 0,
a: v
}
}
test spend() {
let dat = dat_new()
let val = dat.a
expect [(_, amount)] = val.inner.inner
let final_amount = [(#"AA", 4)] |> Dict
final_amount == amount
}
"#;
assert_uplc(
src,
Term::tail_list()
.apply(Term::var("val"))
.delayed_choose_list(
Term::equals_data()
.apply(Term::map_data().apply(Term::var("final_amount")))
.apply(Term::map_data().apply(Term::var("amount")))
.lambda("final_amount")
.apply(Term::map_values(vec![Constant::ProtoPair(
Type::Data,
Type::Data,
Constant::Data(Data::bytestring(vec![170])).into(),
Constant::Data(Data::integer(4.into())).into(),
)]))
.lambda("amount")
.apply(
Term::unmap_data().apply(Term::snd_pair().apply(Term::var("tuple_item_0"))),
),
Term::Error.trace(Term::string(
"List/Tuple/Constr contains more items than expected",
)),
)
.lambda("tuple_item_0")
.apply(Term::head_list().apply(Term::var("val")))
.lambda("val")
.apply(
Term::unmap_data().apply(
Term::var(CONSTR_GET_FIELD)
.apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("dat")))
.apply(Term::integer(1.into())),
),
)
.lambda("dat")
.apply(Term::Constant(
Constant::Data(Data::constr(
0,
vec![
Data::integer(0.into()),
Data::map(vec![(
Data::bytestring(vec![]),
Data::map(vec![(Data::bytestring(vec![170]), Data::integer(4.into()))]),
)]),
],
))
.into(),
))
.lambda("v")
.apply(Term::map_values(vec![Constant::ProtoPair(
Type::Data,
Type::Data,
Constant::Data(Data::bytestring(vec![])).into(),
Constant::Data(Data::map(vec![(
Data::bytestring(vec![170]),
Data::integer(4.into()),
)]))
.into(),
)]))
.constr_get_field()
.constr_fields_exposer()
.constr_index_exposer(),
false,
);
}