From 5024bd3f9c28679666f945216644cb951c80bfd7 Mon Sep 17 00:00:00 2001 From: rvcas Date: Tue, 11 Jun 2024 19:29:55 -0400 Subject: [PATCH] feat: code gen support for if/is Co-authored-by: Kasey White --- crates/aiken-lang/src/gen_uplc.rs | 296 ++++++++++++---------- crates/aiken-lang/src/gen_uplc/air.rs | 1 - crates/aiken-lang/src/gen_uplc/builder.rs | 62 ++--- crates/aiken-lang/src/gen_uplc/tree.rs | 170 ++++++------- 4 files changed, 267 insertions(+), 262 deletions(-) diff --git a/crates/aiken-lang/src/gen_uplc.rs b/crates/aiken-lang/src/gen_uplc.rs index 27f66104..e021b02c 100644 --- a/crates/aiken-lang/src/gen_uplc.rs +++ b/crates/aiken-lang/src/gen_uplc.rs @@ -5,7 +5,7 @@ pub mod tree; use self::{ air::Air, builder::{ - air_holds_msg, cast_validator_args, constants_ir, convert_type_to_data, extract_constant, + cast_validator_args, constants_ir, convert_type_to_data, extract_constant, modify_cyclic_calls, modify_self_calls, rearrange_list_clauses, AssignmentProperties, ClauseProperties, CodeGenSpecialFuncs, CycleFunctionNames, HoistableFunction, Variant, }, @@ -42,6 +42,7 @@ use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use petgraph::{algo, Graph}; use std::{collections::HashMap, rc::Rc}; + use uplc::{ ast::{Constant as UplcConstant, Name, NamedDeBruijn, Program, Term, Type as UplcType}, builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER, EXPECT_ON_LIST}, @@ -258,35 +259,40 @@ impl<'a> CodeGenerator<'a> { let air_value = self.build(value, module_build_name, &[]); - let msg_func = match self.tracing { - TraceLevel::Silent => None, - TraceLevel::Verbose | TraceLevel::Compact => { - if kind.is_expect() { - let msg = match self.tracing { - TraceLevel::Silent => unreachable!("excluded from pattern guards"), - TraceLevel::Compact => get_line_columns_by_span( - module_build_name, - location, - &self.module_src, - ) - .to_string(), - TraceLevel::Verbose => { - get_src_code_by_span(module_build_name, location, &self.module_src) - } - }; + let otherwise = match (self.tracing, kind) { + ( + TraceLevel::Silent, + AssignmentKind::Let { .. } | AssignmentKind::Expect { .. }, + ) => AirTree::error(void(), false), - let msg_func_name = msg.split_whitespace().join(""); + (TraceLevel::Compact | TraceLevel::Verbose, AssignmentKind::Let { .. }) => { + AirTree::error(void(), false) + } - self.special_functions.insert_new_function( - msg_func_name.clone(), - Term::string(msg), - string(), - ); + (TraceLevel::Verbose | TraceLevel::Compact, AssignmentKind::Expect { .. }) => { + let msg = match self.tracing { + TraceLevel::Silent => unreachable!("excluded from pattern guards"), + TraceLevel::Compact => { + get_line_columns_by_span(module_build_name, location, &self.module_src) + .to_string() + } + TraceLevel::Verbose => { + get_src_code_by_span(module_build_name, location, &self.module_src) + } + }; - Some(self.special_functions.use_function_msg(msg_func_name)) - } else { - None - } + let msg_func_name = msg.split_whitespace().join(""); + + self.special_functions.insert_new_function( + msg_func_name.clone(), + Term::string(msg), + string(), + ); + + let msg_string = + AirTree::string(self.special_functions.use_function_string(msg_func_name)); + + AirTree::trace(msg_string, void(), AirTree::error(void(), false)) } }; @@ -304,7 +310,7 @@ impl<'a> CodeGenerator<'a> { kind: *kind, remove_unused: kind.is_let(), full_check: !tipo.is_data() && value.tipo().is_data() && kind.is_expect(), - msg_func, + otherwise, }, ) } else { @@ -574,7 +580,7 @@ impl<'a> CodeGenerator<'a> { kind: AssignmentKind::let_(), remove_unused: false, full_check: false, - msg_func: None, + otherwise: AirTree::error(void(), false), }, ) } else { @@ -625,24 +631,39 @@ impl<'a> CodeGenerator<'a> { ) } } - + // let pattern = branch.condition + // branch.body + // + // if is : { } + // [(builtin ifThenElse) (condition is pattern) (body) (else) ] TypedExpr::If { branches, final_else, tipo, .. - } => AirTree::if_branches( - branches - .iter() - .map(|branch| { - ( - self.build(&branch.condition, module_build_name, &[]), - self.build(&branch.body, module_build_name, &[]), - ) - }) - .collect_vec(), - tipo.clone(), + } => branches.iter().rfold( self.build(final_else, module_build_name, &[]), + |acc, branch| { + let condition = self.build(&branch.condition, module_build_name, &[]); + let body = self.build(&branch.body, module_build_name, &[]); + + match &branch.is { + Some(pattern) => self.assignment( + pattern, + condition, + body, + &pattern.tipo(&branch.condition).unwrap(), + AssignmentProperties { + value_type: branch.condition.tipo(), + kind: AssignmentKind::Expect { backpassing: () }, + remove_unused: false, + full_check: true, + otherwise: acc, + }, + ), + None => AirTree::if_branch(tipo.clone(), condition, body, acc), + } + }, ), TypedExpr::RecordAccess { @@ -878,7 +899,7 @@ impl<'a> CodeGenerator<'a> { // Cast value to or from data so we don't have to worry from this point onward if props.value_type.is_data() && props.kind.is_expect() && !tipo.is_data() { - value = AirTree::cast_from_data(value, tipo.clone(), props.msg_func.clone()); + value = AirTree::cast_from_data(value, tipo.clone(), props.otherwise.clone()); } else if !props.value_type.is_data() && tipo.is_data() { value = AirTree::cast_to_data(value, props.value_type.clone()); } @@ -906,7 +927,7 @@ impl<'a> CodeGenerator<'a> { let expr = AirTree::let_assignment(name, value, expect); - AirTree::assert_bool(true, expr, props.msg_func.clone(), then) + AirTree::assert_bool(true, expr, then, props.otherwise.clone()) } Pattern::Var { name, .. } => { @@ -925,7 +946,7 @@ impl<'a> CodeGenerator<'a> { val, &mut index_map, pattern.location(), - props.msg_func, + props.otherwise, ); let assign_expect = AirTree::let_assignment("_", expect, then); @@ -965,7 +986,7 @@ impl<'a> CodeGenerator<'a> { val, &mut index_map, pattern.location(), - props.msg_func, + props.otherwise, ); let assignment = AirTree::let_assignment("_", expect, then); @@ -975,7 +996,7 @@ impl<'a> CodeGenerator<'a> { } else if !props.remove_unused { AirTree::let_assignment(name, value, then) } else { - AirTree::no_op(then) + then } } @@ -1025,7 +1046,7 @@ impl<'a> CodeGenerator<'a> { kind: props.kind, remove_unused: true, full_check: props.full_check, - msg_func: props.msg_func.clone(), + otherwise: props.otherwise.clone(), }, ) } else { @@ -1073,7 +1094,7 @@ impl<'a> CodeGenerator<'a> { kind: props.kind, remove_unused: true, full_check: props.full_check, - msg_func: props.msg_func.clone(), + otherwise: props.otherwise.clone(), }, ) } else { @@ -1088,20 +1109,20 @@ impl<'a> CodeGenerator<'a> { elems.reverse(); if elements.is_empty() { - AirTree::list_empty(value, props.msg_func, then) + AirTree::list_empty(value, then, props.otherwise.clone()) } else { AirTree::list_access( elems, tipo.clone(), tail.is_some(), value, - props.msg_func, if props.full_check { ExpectLevel::Full } else { ExpectLevel::Items }, then, + props.otherwise.clone(), ) } } @@ -1164,7 +1185,7 @@ impl<'a> CodeGenerator<'a> { kind: props.kind, remove_unused: true, full_check: props.full_check, - msg_func: props.msg_func.clone(), + otherwise: props.otherwise.clone(), }, ) } else { @@ -1190,11 +1211,6 @@ impl<'a> CodeGenerator<'a> { let local_value = AirTree::local_var(&constructor_name, tipo.clone()); let then = { - let (is_expect, msg) = if props.full_check { - (true, props.msg_func.clone()) - } else { - (false, None) - }; assert!(fields.len() == 2); AirTree::pair_access( @@ -1208,9 +1224,9 @@ impl<'a> CodeGenerator<'a> { .unwrap(), tipo.clone(), local_value, - msg, - is_expect, + props.full_check, then, + props.otherwise.clone(), ) }; @@ -1223,7 +1239,7 @@ impl<'a> CodeGenerator<'a> { } if tipo.is_bool() => { assert!(props.kind.is_expect()); - AirTree::assert_bool(name == "True", value, props.msg_func, then) + AirTree::assert_bool(name == "True", value, then, props.otherwise.clone()) } Pattern::Constructor { .. } if tipo.is_void() => { @@ -1308,7 +1324,7 @@ impl<'a> CodeGenerator<'a> { kind: props.kind, remove_unused: true, full_check: props.full_check, - msg_func: props.msg_func.clone(), + otherwise: props.otherwise.clone(), }, ) } else { @@ -1336,12 +1352,13 @@ impl<'a> CodeGenerator<'a> { let then = if check_replaceable_opaque_type(tipo, &self.data_types) { AirTree::let_assignment(&fields[0].1, local_value, then) } else { - let (is_expect, msg) = if props.full_check { - (true, props.msg_func.clone()) - } else { - (false, None) - }; - AirTree::fields_expose(fields, local_value, msg, is_expect, then) + AirTree::fields_expose( + fields, + local_value, + props.full_check, + then, + props.otherwise.clone(), + ) }; let data_type = lookup_data_type_by_tipo(&self.data_types, tipo) @@ -1362,10 +1379,11 @@ impl<'a> CodeGenerator<'a> { AirTree::assert_constr_index( index, AirTree::local_var(&constructor_name, tipo.clone()), - props.msg_func, then, + props.otherwise.clone(), ) } else { + assert!(data_type.constructors.len() == 1); then }; @@ -1425,7 +1443,7 @@ impl<'a> CodeGenerator<'a> { kind: props.kind, remove_unused: true, full_check: props.full_check, - msg_func: props.msg_func.clone(), + otherwise: props.otherwise.clone(), }, ) } else { @@ -1439,15 +1457,16 @@ impl<'a> CodeGenerator<'a> { fields.reverse(); - let (is_expect, msg) = if props.full_check { - (true, props.msg_func) - } else { - (false, None) - }; - // This `value` is either value param that was passed in or local var - AirTree::tuple_access(fields, tipo.clone(), value, msg, is_expect, then) + AirTree::tuple_access( + fields, + tipo.clone(), + value, + props.full_check, + then, + props.otherwise.clone(), + ) } } } @@ -1458,7 +1477,7 @@ impl<'a> CodeGenerator<'a> { value: AirTree, defined_data_types: &mut IndexMap, location: Span, - msg_func: Option, + otherwise: AirTree, ) -> AirTree { assert!(tipo.get_generic().is_none()); // Shouldn't be needed but still here just in case @@ -1468,6 +1487,7 @@ impl<'a> CodeGenerator<'a> { match uplc_type { // primitives + // Untyped Data Some( UplcType::Integer | UplcType::String @@ -1476,10 +1496,9 @@ impl<'a> CodeGenerator<'a> { | UplcType::Unit | UplcType::Bls12_381G1Element | UplcType::Bls12_381G2Element - | UplcType::Bls12_381MlResult, + | UplcType::Bls12_381MlResult + | UplcType::Data, ) => value, - // Untyped Data - Some(UplcType::Data) => value, // Map type Some(UplcType::List(_)) if tipo.is_map() => { @@ -1500,7 +1519,7 @@ impl<'a> CodeGenerator<'a> { AirTree::local_var(fst_name.clone(), inner_pair_types[0].clone()), defined_data_types, location, - msg_func.clone(), + otherwise.clone(), ); let expect_snd = self.expect_type_assign( @@ -1508,7 +1527,7 @@ impl<'a> CodeGenerator<'a> { AirTree::local_var(snd_name.clone(), inner_pair_types[1].clone()), defined_data_types, location, - msg_func.clone(), + otherwise.clone(), ); let anon_func_body = AirTree::pair_access( @@ -1516,9 +1535,9 @@ impl<'a> CodeGenerator<'a> { Some(snd_name), inner_list_type.clone(), AirTree::local_var(&pair_name, inner_list_type.clone()), - msg_func.clone(), true, AirTree::let_assignment("_", expect_fst, expect_snd), + otherwise.clone(), ); let unwrap_function = AirTree::anon_func(vec![pair_name], anon_func_body); @@ -1587,7 +1606,7 @@ impl<'a> CodeGenerator<'a> { AirTree::local_var(&tuple_index_name, arg.clone()), defined_data_types, location, - msg_func.clone(), + otherwise.clone(), ); tuple_expect_items.push(tuple_index_name); @@ -1602,9 +1621,9 @@ impl<'a> CodeGenerator<'a> { tuple_expect_items, tipo.clone(), AirTree::local_var(&tuple_name, tipo.clone()), - msg_func, true, then, + otherwise, ); AirTree::let_assignment(&tuple_name, value, tuple_access) @@ -1626,11 +1645,11 @@ impl<'a> CodeGenerator<'a> { AirTree::cast_from_data( AirTree::local_var(&item_name, data()), inner_list_type.clone(), - msg_func.clone(), + otherwise.clone(), ), defined_data_types, location, - msg_func, + otherwise, ); let anon_func_body = expect_item; @@ -1701,7 +1720,7 @@ impl<'a> CodeGenerator<'a> { AirTree::local_var(fst_name.clone(), tuple_inner_types[0].clone()), defined_data_types, location, - msg_func.clone(), + otherwise.clone(), ); let expect_snd = self.expect_type_assign( @@ -1709,7 +1728,7 @@ impl<'a> CodeGenerator<'a> { AirTree::local_var(snd_name.clone(), tuple_inner_types[1].clone()), defined_data_types, location, - msg_func.clone(), + otherwise.clone(), ); let pair_access = AirTree::pair_access( @@ -1717,9 +1736,9 @@ impl<'a> CodeGenerator<'a> { Some(snd_name.clone()), tipo.clone(), AirTree::local_var(&pair_name, tipo.clone()), - msg_func.clone(), true, AirTree::let_assignment("_", expect_fst, expect_snd), + otherwise, ); AirTree::let_assignment(&pair_name, value, pair_access) @@ -1800,7 +1819,7 @@ impl<'a> CodeGenerator<'a> { AirTree::local_var(arg_name, arg_tipo), defined_data_types, location, - msg_term.clone(), + otherwise.clone(), ), then, ) @@ -1830,9 +1849,9 @@ impl<'a> CodeGenerator<'a> { ), tipo.clone(), ), - msg_term.clone(), true, constr_then, + otherwise.clone(), ) }; @@ -1895,12 +1914,7 @@ impl<'a> CodeGenerator<'a> { let args = match self.tracing { TraceLevel::Silent => vec![value], - TraceLevel::Compact | TraceLevel::Verbose => vec![ - value, - msg_func - .expect("should be unreachable: no msg func with tracing enabled.") - .to_air_tree(), - ], + TraceLevel::Compact | TraceLevel::Verbose => vec![value, otherwise], }; let module_fn = ValueConstructorVariant::ModuleFn { @@ -2459,9 +2473,9 @@ impl<'a> CodeGenerator<'a> { // One special usage of list access here // So for the msg we pass in empty string if tracing is on // Since check_last_item is false this will never get added to the final uplc anyway - None, ExpectLevel::None, elems_then, + AirTree::error(void(), false), ) } else { assert!(defined_tails.len() >= elems.len()); @@ -2540,9 +2554,9 @@ impl<'a> CodeGenerator<'a> { name_index_assigns[1].0.clone(), subject_tipo.clone(), AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()), - None, false, next_then, + AirTree::error(void(), false), ) }; @@ -2667,9 +2681,9 @@ impl<'a> CodeGenerator<'a> { AirTree::fields_expose( fields, AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()), - None, false, next_then, + AirTree::error(void(), false), ) }; @@ -2796,9 +2810,9 @@ impl<'a> CodeGenerator<'a> { names, subject_tipo.clone(), AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), - None, false, tuple_name_assigns, + AirTree::error(void(), false), ), ) } else { @@ -3034,8 +3048,8 @@ impl<'a> CodeGenerator<'a> { let actual_type = convert_opaque_type(&arg.tipo, &self.data_types, true); - let msg_func = match self.tracing { - TraceLevel::Silent => None, + let otherwise = match self.tracing { + TraceLevel::Silent => AirTree::error(void(), false), TraceLevel::Compact | TraceLevel::Verbose => { let msg = match self.tracing { TraceLevel::Silent => { @@ -3059,7 +3073,11 @@ impl<'a> CodeGenerator<'a> { string(), ); - Some(self.special_functions.use_function_msg(msg_func_name)) + let msg_string = AirTree::string( + self.special_functions.use_function_string(msg_func_name), + ); + + AirTree::trace(msg_string, void(), AirTree::error(void(), false)) } }; @@ -3076,7 +3094,7 @@ impl<'a> CodeGenerator<'a> { kind: AssignmentKind::expect(), remove_unused: false, full_check: true, - msg_func, + otherwise, }, ); @@ -4017,24 +4035,11 @@ impl<'a> CodeGenerator<'a> { } fn gen_uplc(&mut self, ir: Air, arg_stack: &mut Vec>) -> Option> { - // Going to mark the changes made to code gen after air tree implementation - let error_term = match self.tracing { - TraceLevel::Silent => Term::Error, - TraceLevel::Compact | TraceLevel::Verbose => { - if air_holds_msg(&ir) { - let msg = arg_stack.pop().unwrap(); - Term::Error.delayed_trace(msg) - } else { - Term::Error - } - } - }; - - let convert_data_to_type = |term, tipo| { - if error_term == Term::Error { + let convert_data_to_type = |term, tipo, otherwise| { + if otherwise == Term::Error { builder::unknown_data_to_type(term, tipo) } else { - builder::unknown_data_to_type_debug(term, tipo, error_term.clone()) + builder::unknown_data_to_type_otherwise(term, tipo, otherwise) } }; @@ -4337,6 +4342,12 @@ impl<'a> CodeGenerator<'a> { let mut term = arg_stack.pop().unwrap(); + let otherwise = if matches!(expect_level, ExpectLevel::Full | ExpectLevel::Items) { + arg_stack.pop().unwrap() + } else { + Term::Error + }; + let list_id = self.id_gen.next(); let mut id_list = vec![]; @@ -4362,7 +4373,7 @@ impl<'a> CodeGenerator<'a> { term, true, expect_level, - error_term, + otherwise, ) .apply(value); @@ -4774,8 +4785,9 @@ impl<'a> CodeGenerator<'a> { } Air::CastFromData { tipo, .. } => { let mut term = arg_stack.pop().unwrap(); + let otherwise = arg_stack.pop().unwrap(); - term = convert_data_to_type(term, &tipo); + term = convert_data_to_type(term, &tipo, otherwise); if extract_constant(&term).is_some() { let mut program: Program = Program { @@ -4834,6 +4846,7 @@ impl<'a> CodeGenerator<'a> { let constr = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); + let otherwise = arg_stack.pop().unwrap(); term = Term::equals_integer() .apply(Term::integer(constr_index.into())) @@ -4844,7 +4857,7 @@ impl<'a> CodeGenerator<'a> { ) .apply(constr), ) - .delayed_if_then_else(term, error_term); + .delayed_if_then_else(term, otherwise); Some(term) } @@ -4852,11 +4865,12 @@ impl<'a> CodeGenerator<'a> { let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); + let otherwise = arg_stack.pop().unwrap(); if is_true { - term = value.delayed_if_then_else(term, error_term) + term = value.delayed_if_then_else(term, otherwise) } else { - term = value.delayed_if_then_else(error_term, term) + term = value.delayed_if_then_else(otherwise, term) } Some(term) } @@ -5286,6 +5300,13 @@ impl<'a> CodeGenerator<'a> { let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); + + let otherwise = if is_expect { + arg_stack.pop().unwrap() + } else { + Term::Error + }; + let list_id = self.id_gen.next(); id_list.push(list_id); @@ -5313,7 +5334,7 @@ impl<'a> CodeGenerator<'a> { term, false, is_expect.into(), - error_term, + otherwise, ); term = term.apply( @@ -5333,13 +5354,14 @@ impl<'a> CodeGenerator<'a> { let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); + let otherwise = arg_stack.pop().unwrap(); term = Term::var( self.special_functions .use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string()), ) .apply(value) - .delayed_choose_list(term, error_term); + .delayed_choose_list(term, otherwise); Some(term) } @@ -5347,8 +5369,9 @@ impl<'a> CodeGenerator<'a> { let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); + let otherwise = arg_stack.pop().unwrap(); - term = value.delayed_choose_list(term, error_term); + term = value.delayed_choose_list(term, otherwise); Some(term) } @@ -5545,6 +5568,11 @@ impl<'a> CodeGenerator<'a> { let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); + let otherwise = if is_expect { + arg_stack.pop().unwrap() + } else { + Term::Error + }; let list_id = self.id_gen.next(); let mut id_list = vec![]; @@ -5567,7 +5595,7 @@ impl<'a> CodeGenerator<'a> { term, false, is_expect.into(), - error_term, + otherwise, ) .apply(value); @@ -5583,6 +5611,12 @@ impl<'a> CodeGenerator<'a> { let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); + let otherwise = if is_expect { + arg_stack.pop().unwrap() + } else { + Term::Error + }; + let list_id = self.id_gen.next(); if let Some(name) = snd { @@ -5590,6 +5624,7 @@ impl<'a> CodeGenerator<'a> { convert_data_to_type( Term::snd_pair().apply(Term::var(format!("__pair_{list_id}"))), &inner_types[1], + otherwise.clone(), ) } else { known_data_to_type( @@ -5604,6 +5639,7 @@ impl<'a> CodeGenerator<'a> { convert_data_to_type( Term::fst_pair().apply(Term::var(format!("__pair_{list_id}"))), &inner_types[0], + otherwise, ) } else { known_data_to_type( diff --git a/crates/aiken-lang/src/gen_uplc/air.rs b/crates/aiken-lang/src/gen_uplc/air.rs index a3df8503..327da0c7 100644 --- a/crates/aiken-lang/src/gen_uplc/air.rs +++ b/crates/aiken-lang/src/gen_uplc/air.rs @@ -102,7 +102,6 @@ pub enum Air { }, CastFromData { tipo: Rc, - is_expect: bool, }, CastToData { tipo: Rc, diff --git a/crates/aiken-lang/src/gen_uplc/builder.rs b/crates/aiken-lang/src/gen_uplc/builder.rs index bc68b175..763b0750 100644 --- a/crates/aiken-lang/src/gen_uplc/builder.rs +++ b/crates/aiken-lang/src/gen_uplc/builder.rs @@ -1,5 +1,5 @@ use super::{ - air::{Air, ExpectLevel}, + air::ExpectLevel, tree::{AirMsg, AirTree, TreePath}, }; use crate::{ @@ -71,7 +71,7 @@ pub struct AssignmentProperties { pub kind: TypedAssignmentKind, pub remove_unused: bool, pub full_check: bool, - pub msg_func: Option, + pub otherwise: AirTree, } #[derive(Clone, Debug)] @@ -233,6 +233,14 @@ impl CodeGenSpecialFuncs { } } + pub fn use_function_string(&mut self, func_name: String) -> String { + if !self.used_funcs.contains(&func_name) { + self.used_funcs.push(func_name.to_string()); + } + + func_name + } + pub fn use_function_tree(&mut self, func_name: String) -> AirTree { if !self.used_funcs.contains(&func_name) { self.used_funcs.push(func_name.to_string()); @@ -1035,7 +1043,7 @@ pub fn unknown_data_to_type(term: Term, field_type: &Type) -> Term { /// Due to the nature of the types BLS12_381_G1Element and BLS12_381_G2Element and String coming from bytearray /// We don't have error handling if the bytearray is not properly aligned to the type. Oh well lol /// For BLS12_381_G1Element and BLS12_381_G2Element, hash to group exists so just adopt that. -pub fn unknown_data_to_type_debug( +pub fn unknown_data_to_type_otherwise( term: Term, field_type: &Type, error_term: Term, @@ -1362,7 +1370,7 @@ pub fn list_access_to_uplc( term: Term, is_list_accessor: bool, expect_level: ExpectLevel, - error_term: Term, + otherwise: Term, ) -> Term { let names_len = names_types_ids.len(); @@ -1393,7 +1401,7 @@ pub fn list_access_to_uplc( } return Term::var("empty_list") - .delayed_choose_list(term, error_term) + .delayed_choose_list(term, otherwise) .lambda("empty_list"); } @@ -1412,16 +1420,16 @@ pub fn list_access_to_uplc( Term::head_list().apply(Term::var(tail_name.to_string())) } else if matches!(expect_level, ExpectLevel::Full) { // Expect level is full so we have an unknown piece of data to cast - if error_term == Term::Error { + if otherwise == Term::Error { unknown_data_to_type( Term::head_list().apply(Term::var(tail_name.to_string())), &tipo.to_owned(), ) } else { - unknown_data_to_type_debug( + unknown_data_to_type_otherwise( Term::head_list().apply(Term::var(tail_name.to_string())), &tipo.to_owned(), - error_term.clone(), + otherwise.clone(), ) } } else { @@ -1456,22 +1464,22 @@ pub fn list_access_to_uplc( ExpectLevel::None => acc.lambda(name).apply(head_item).lambda(tail_name), ExpectLevel::Full | ExpectLevel::Items => { - if error_term == Term::Error && tail_present { + if otherwise == Term::Error && tail_present { // No need to check last item if tail was present acc.lambda(name).apply(head_item).lambda(tail_name) } else if tail_present { // Custom error instead of trying to do head_item on a possibly empty list. Term::var(tail_name.to_string()) .delayed_choose_list( - error_term.clone(), + otherwise.clone(), acc.lambda(name).apply(head_item), ) .lambda(tail_name) - } else if error_term == Term::Error { + } else if otherwise == Term::Error { // Check head is last item in this list Term::tail_list() .apply(Term::var(tail_name.to_string())) - .delayed_choose_list(acc, error_term.clone()) + .delayed_choose_list(acc, otherwise.clone()) .lambda(name) .apply(head_item) .lambda(tail_name) @@ -1479,10 +1487,10 @@ pub fn list_access_to_uplc( // Custom error if list is not empty after this head Term::var(tail_name.to_string()) .delayed_choose_list( - error_term.clone(), + otherwise.clone(), Term::tail_list() .apply(Term::var(tail_name.to_string())) - .delayed_choose_list(acc, error_term.clone()) + .delayed_choose_list(acc, otherwise.clone()) .lambda(name) .apply(head_item), ) @@ -1498,7 +1506,7 @@ pub fn list_access_to_uplc( let head_item = head_item(name, tipo, &tail_name); - if matches!(expect_level, ExpectLevel::None) || error_term == Term::Error { + if matches!(expect_level, ExpectLevel::None) || otherwise == Term::Error { acc.apply(Term::tail_list().apply(Term::var(tail_name.to_string()))) .lambda(name) .apply(head_item) @@ -1507,7 +1515,7 @@ pub fn list_access_to_uplc( // case for a custom error if the list is empty at this point Term::var(tail_name.to_string()) .delayed_choose_list( - error_term.clone(), + otherwise.clone(), acc.apply( Term::tail_list().apply(Term::var(tail_name.to_string())), ) @@ -1778,7 +1786,6 @@ pub fn cast_validator_args(term: Term, arguments: &[TypedArg]) -> Term AirTree { - let success_branch = vec![(air_tree, AirTree::void())]; let otherwise = match trace { TraceLevel::Silent | TraceLevel::Compact => AirTree::error(void(), true), TraceLevel::Verbose => AirTree::trace( @@ -1788,7 +1795,7 @@ pub fn wrap_validator_condition(air_tree: AirTree, trace: TraceLevel) -> AirTree ), }; - AirTree::if_branches(success_branch, void(), otherwise) + AirTree::if_branch(void(), air_tree, AirTree::void(), otherwise) } pub fn extract_constant(term: &Term) -> Option> { @@ -1841,22 +1848,3 @@ pub fn get_line_columns_by_span( .line_and_column_number(span.start) .expect("Out of bounds span") } - -pub fn air_holds_msg(air: &Air) -> bool { - match air { - Air::AssertConstr { .. } | Air::AssertBool { .. } | Air::FieldsEmpty | Air::ListEmpty => { - true - } - - Air::FieldsExpose { is_expect, .. } - | Air::TupleAccessor { is_expect, .. } - | Air::PairAccessor { is_expect, .. } - | Air::CastFromData { is_expect, .. } => *is_expect, - - Air::ListAccessor { expect_level, .. } => { - matches!(expect_level, ExpectLevel::Full | ExpectLevel::Items) - } - - _ => false, - } -} diff --git a/crates/aiken-lang/src/gen_uplc/tree.rs b/crates/aiken-lang/src/gen_uplc/tree.rs index de07950f..c8aa3007 100644 --- a/crates/aiken-lang/src/gen_uplc/tree.rs +++ b/crates/aiken-lang/src/gen_uplc/tree.rs @@ -133,14 +133,14 @@ pub enum AirTree { AssertConstr { constr_index: usize, constr: Box, - msg: Option, then: Box, + otherwise: Box, }, AssertBool { is_true: bool, value: Box, - msg: Option, then: Box, + otherwise: Box, }, // Clause Guards ClauseGuard { @@ -174,8 +174,8 @@ pub enum AirTree { indices: Vec<(usize, String, Rc)>, record: Box, is_expect: bool, - msg: Option, then: Box, + otherwise: Box, }, // List Access ListAccessor { @@ -184,8 +184,8 @@ pub enum AirTree { tail: bool, list: Box, expect_level: ExpectLevel, - msg: Option, then: Box, + otherwise: Box, }, ListExpose { tipo: Rc, @@ -199,8 +199,8 @@ pub enum AirTree { tipo: Rc, tuple: Box, is_expect: bool, - msg: Option, then: Box, + otherwise: Box, }, // Pair Access PairAccessor { @@ -208,9 +208,9 @@ pub enum AirTree { snd: Option, tipo: Rc, is_expect: bool, - msg: Option, pair: Box, then: Box, + otherwise: Box, }, // Misc. FieldsEmpty { @@ -220,8 +220,8 @@ pub enum AirTree { }, ListEmpty { list: Box, - msg: Option, then: Box, + otherwise: Box, }, NoOp { then: Box, @@ -297,7 +297,7 @@ pub enum AirTree { CastFromData { tipo: Rc, value: Box, - msg: Option, + otherwise: Box, }, CastToData { tipo: Rc, @@ -359,7 +359,7 @@ pub enum AirTree { // If If { tipo: Rc, - pattern: Box, + condition: Box, then: Box, otherwise: Box, }, @@ -390,6 +390,10 @@ pub enum AirTree { } impl AirTree { + pub fn is_error(&self) -> bool { + matches!(self, AirTree::ErrorTerm { .. }) + } + pub fn int(value: impl ToString) -> AirTree { AirTree::Int { value: value.to_string(), @@ -562,11 +566,11 @@ impl AirTree { } } - pub fn cast_from_data(value: AirTree, tipo: Rc, msg: Option) -> AirTree { + pub fn cast_from_data(value: AirTree, tipo: Rc, otherwise: AirTree) -> AirTree { AirTree::CastFromData { tipo, value: value.into(), - msg, + otherwise: otherwise.into(), } } @@ -580,28 +584,28 @@ impl AirTree { pub fn assert_constr_index( constr_index: usize, constr: AirTree, - msg: Option, then: AirTree, + otherwise: AirTree, ) -> AirTree { AirTree::AssertConstr { constr_index, constr: constr.into(), - msg, then: then.into(), + otherwise: otherwise.into(), } } pub fn assert_bool( is_true: bool, value: AirTree, - msg: Option, then: AirTree, + otherwise: AirTree, ) -> AirTree { AirTree::AssertBool { is_true, value: value.into(), - msg, then: then.into(), + otherwise: otherwise.into(), } } @@ -771,31 +775,18 @@ impl AirTree { } } - pub fn if_branches( - mut branches: Vec<(AirTree, AirTree)>, + pub fn if_branch( tipo: Rc, + condition: AirTree, + branch: AirTree, otherwise: AirTree, ) -> AirTree { - assert!(!branches.is_empty()); - let last_if = branches.pop().unwrap(); - - let mut final_if = AirTree::If { - tipo: tipo.clone(), - pattern: Box::new(last_if.0), - then: Box::new(last_if.1), + AirTree::If { + tipo, + condition: condition.into(), + then: branch.into(), otherwise: otherwise.into(), - }; - - while let Some(branch) = branches.pop() { - final_if = AirTree::If { - tipo: tipo.clone(), - pattern: Box::new(branch.0), - then: Box::new(branch.1), - otherwise: final_if.into(), - }; } - - final_if } pub fn create_constr(tag: usize, tipo: Rc, args: Vec) -> AirTree { @@ -845,23 +836,23 @@ impl AirTree { vec![list_of_fields], ), tipo.clone(), - None, + AirTree::error(void(), false), ) } pub fn fields_expose( indices: Vec<(usize, String, Rc)>, record: AirTree, - msg: Option, is_expect: bool, then: AirTree, + otherwise: AirTree, ) -> AirTree { AirTree::FieldsExpose { indices, record: record.into(), - msg, is_expect, then: then.into(), + otherwise: otherwise.into(), } } @@ -870,9 +861,10 @@ impl AirTree { tipo: Rc, tail: bool, list: AirTree, - msg: Option, + expect_level: ExpectLevel, then: AirTree, + otherwise: AirTree, ) -> AirTree { AirTree::ListAccessor { tipo, @@ -880,8 +872,8 @@ impl AirTree { tail, list: list.into(), expect_level, - msg, then: then.into(), + otherwise: otherwise.into(), } } @@ -903,17 +895,17 @@ impl AirTree { names: Vec, tipo: Rc, tuple: AirTree, - msg: Option, is_expect: bool, then: AirTree, + otherwise: AirTree, ) -> AirTree { AirTree::TupleAccessor { names, tipo, tuple: tuple.into(), - msg, is_expect, then: then.into(), + otherwise: otherwise.into(), } } @@ -922,18 +914,18 @@ impl AirTree { snd: Option, tipo: Rc, pair: AirTree, - msg: Option, is_expect: bool, then: AirTree, + otherwise: AirTree, ) -> AirTree { AirTree::PairAccessor { fst, snd, tipo, is_expect, - msg, pair: pair.into(), then: then.into(), + otherwise: otherwise.into(), } } @@ -949,7 +941,7 @@ impl AirTree { vec![tuple], ), tipo.clone(), - None, + AirTree::error(void(), false), ) } @@ -976,11 +968,12 @@ impl AirTree { } } - pub fn list_empty(list: AirTree, msg: Option, then: AirTree) -> AirTree { + pub fn list_empty(list: AirTree, then: AirTree, otherwise: AirTree) -> AirTree { AirTree::ListEmpty { list: list.into(), - msg, + then: then.into(), + otherwise: otherwise.into(), } } @@ -1114,8 +1107,8 @@ impl AirTree { AirTree::AssertConstr { constr, constr_index, - msg, then, + otherwise, } => { air_vec.push(Air::AssertConstr { constr_index: *constr_index, @@ -1123,27 +1116,21 @@ impl AirTree { // msg is first so we can pop it off first in uplc_gen // if traces are on - if let Some(msg) = msg { - msg.to_air_tree().create_air_vec(air_vec); - } - constr.create_air_vec(air_vec); then.create_air_vec(air_vec); + otherwise.create_air_vec(air_vec); } AirTree::AssertBool { is_true, value, - msg, then, + otherwise, } => { air_vec.push(Air::AssertBool { is_true: *is_true }); - if let Some(msg) = msg { - msg.to_air_tree().create_air_vec(air_vec); - } - value.create_air_vec(air_vec); then.create_air_vec(air_vec); + otherwise.create_air_vec(air_vec); } AirTree::ClauseGuard { subject_name, @@ -1205,30 +1192,29 @@ impl AirTree { AirTree::FieldsExpose { indices, record, - msg, is_expect, then, + otherwise, } => { air_vec.push(Air::FieldsExpose { indices: indices.clone(), is_expect: *is_expect, }); - if let Some(msg) = msg { - msg.to_air_tree().create_air_vec(air_vec); - } - record.create_air_vec(air_vec); then.create_air_vec(air_vec); + if *is_expect { + otherwise.create_air_vec(air_vec); + } } AirTree::ListAccessor { tipo, names, tail, list, - msg, expect_level, then, + otherwise, } => { air_vec.push(Air::ListAccessor { tipo: tipo.clone(), @@ -1237,12 +1223,11 @@ impl AirTree { expect_level: *expect_level, }); - if let Some(msg) = msg { - msg.to_air_tree().create_air_vec(air_vec); - } - list.create_air_vec(air_vec); then.create_air_vec(air_vec); + if matches!(expect_level, ExpectLevel::Full | ExpectLevel::Items) { + otherwise.create_air_vec(air_vec); + } } AirTree::ListExpose { tipo, @@ -1261,9 +1246,9 @@ impl AirTree { names, tipo, tuple, - msg, is_expect, then, + otherwise, } => { air_vec.push(Air::TupleAccessor { names: names.clone(), @@ -1271,21 +1256,20 @@ impl AirTree { is_expect: *is_expect, }); - if let Some(msg) = msg { - msg.to_air_tree().create_air_vec(air_vec); - } - tuple.create_air_vec(air_vec); then.create_air_vec(air_vec); + if *is_expect { + otherwise.create_air_vec(air_vec); + } } AirTree::PairAccessor { fst, snd, tipo, is_expect, - msg, pair, then, + otherwise, } => { air_vec.push(Air::PairAccessor { fst: fst.clone(), @@ -1294,12 +1278,11 @@ impl AirTree { is_expect: *is_expect, }); - if let Some(msg) = msg { - msg.to_air_tree().create_air_vec(air_vec); - } - pair.create_air_vec(air_vec); then.create_air_vec(air_vec); + if *is_expect { + otherwise.create_air_vec(air_vec); + } } AirTree::FieldsEmpty { constr, msg, then } => { air_vec.push(Air::FieldsEmpty); @@ -1311,15 +1294,16 @@ impl AirTree { constr.create_air_vec(air_vec); then.create_air_vec(air_vec); } - AirTree::ListEmpty { list, msg, then } => { + AirTree::ListEmpty { + list, + then, + otherwise, + } => { air_vec.push(Air::ListEmpty); - if let Some(msg) = msg { - msg.to_air_tree().create_air_vec(air_vec); - } - list.create_air_vec(air_vec); then.create_air_vec(air_vec); + otherwise.create_air_vec(air_vec); } AirTree::NoOp { then } => { air_vec.push(Air::NoOp); @@ -1417,17 +1401,15 @@ impl AirTree { air_vec.push(Air::UnOp { op: *op }); arg.create_air_vec(air_vec); } - AirTree::CastFromData { tipo, value, msg } => { - air_vec.push(Air::CastFromData { - tipo: tipo.clone(), - is_expect: msg.is_some(), - }); - - if let Some(msg) = msg { - msg.to_air_tree().create_air_vec(air_vec); - } + AirTree::CastFromData { + tipo, + value, + otherwise, + } => { + air_vec.push(Air::CastFromData { tipo: tipo.clone() }); value.create_air_vec(air_vec); + otherwise.create_air_vec(air_vec); } AirTree::CastToData { tipo, value } => { air_vec.push(Air::CastToData { tipo: tipo.clone() }); @@ -1532,7 +1514,7 @@ impl AirTree { } AirTree::If { tipo, - pattern, + condition: pattern, then, otherwise, } => { @@ -2174,7 +2156,7 @@ impl AirTree { ); } AirTree::If { - pattern, + condition: pattern, then, otherwise, .. @@ -2590,7 +2572,7 @@ impl AirTree { } } AirTree::If { - pattern, + condition: pattern, then, otherwise, ..