diff --git a/crates/aiken-lang/src/gen_uplc.rs b/crates/aiken-lang/src/gen_uplc.rs index e1e442e0..286bd18c 100644 --- a/crates/aiken-lang/src/gen_uplc.rs +++ b/crates/aiken-lang/src/gen_uplc.rs @@ -1091,6 +1091,7 @@ impl<'a> CodeGenerator<'a> { tipo: constr_tipo, .. } => { + // TODO: Implement Pair pattern if tipo.is_bool() { assert!(props.kind.is_expect()); @@ -1364,12 +1365,13 @@ impl<'a> CodeGenerator<'a> { msg_func.clone(), ); - let anon_func_body = AirTree::tuple_access( - vec![fst_name, snd_name], + let anon_func_body = AirTree::pair_access( + Some(fst_name), + Some(snd_name), inner_list_type.clone(), AirTree::local_var(&pair_name, inner_list_type.clone()), msg_func.clone(), - msg_func.is_some(), + true, AirTree::let_assignment("_", expect_fst, expect_snd), ); @@ -1512,16 +1514,17 @@ impl<'a> CodeGenerator<'a> { msg_func.clone(), ); - let tuple_access = AirTree::tuple_access( - vec![fst_name, snd_name], + let pair_access = AirTree::pair_access( + Some(fst_name.clone()), + Some(snd_name.clone()), tipo.clone(), AirTree::local_var(&pair_name, tipo.clone()), msg_func.clone(), - msg_func.is_some(), + true, AirTree::let_assignment("_", expect_fst, expect_snd), ); - AirTree::let_assignment(&pair_name, value, tuple_access) + AirTree::let_assignment(&pair_name, value, pair_access) } else if tipo.is_tuple() { let tuple_inner_types = tipo.get_inner_types(); @@ -1799,6 +1802,7 @@ impl<'a> CodeGenerator<'a> { } match &mut props.specific_clause { + // TODO: Implement PairClause and PairClauseGuard SpecificClause::ConstrClause => { let data_type = lookup_data_type_by_tipo(&self.data_types, subject_tipo); @@ -3845,7 +3849,7 @@ impl<'a> CodeGenerator<'a> { DefaultFunction::MkCons | DefaultFunction::MkPairData => { unimplemented!( - "MkCons and MkPairData should be handled by an anon function or using [] or ( a, b, .., z).\n" + "MkCons and MkPairData should be handled by an anon function or using [] or ( a, b, .., z) or Pair {{fst:a, snd: b}}.\n" ) } _ => { @@ -4792,31 +4796,49 @@ impl<'a> CodeGenerator<'a> { .apply(next_clause.delay()); } - if tipo.is_pair() { - for (index, name) in indices.iter() { - if name == "_" { - continue; - } - let builtin = if *index == 0 { - Term::fst_pair() - } else { - Term::snd_pair() - }; - - term = term.lambda(name).apply(builder::known_data_to_type( - builtin.apply(Term::var(subject_name.clone())), - &tuple_types[*index].clone(), - )); - } - } else { - for (index, name) in indices.iter() { - term = term.lambda(name.clone()).apply(builder::known_data_to_type( - Term::head_list() - .apply(Term::var(subject_name.clone()).repeat_tail_list(*index)), - &tuple_types[*index].clone(), - )); - } + for (index, name) in indices.iter() { + term = term.lambda(name.clone()).apply(builder::known_data_to_type( + Term::head_list() + .apply(Term::var(subject_name.clone()).repeat_tail_list(*index)), + &tuple_types[*index].clone(), + )); } + + Some(term) + } + Air::PairClause { + subject_tipo: tipo, + complex_clause, + subject_name, + fst_name, + snd_name, + } => { + let mut term = arg_stack.pop().unwrap(); + + let next_clause = arg_stack.pop().unwrap(); + + let pair_types = tipo.get_inner_types(); + + if complex_clause { + term = term + .lambda("__other_clauses_delayed") + .apply(next_clause.delay()); + } + + if let Some(fst) = fst_name { + term = term.lambda(fst).apply(builder::known_data_to_type( + Term::fst_pair().apply(Term::var(subject_name.clone())), + &pair_types[0].clone(), + )); + } + + if let Some(snd) = snd_name { + term = term.lambda(snd).apply(builder::known_data_to_type( + Term::snd_pair().apply(Term::var(subject_name.clone())), + &pair_types[1].clone(), + )); + } + Some(term) } Air::ClauseGuard { @@ -4913,31 +4935,40 @@ impl<'a> CodeGenerator<'a> { let tuple_types = tipo.get_inner_types(); - if tuple_types.len() == 2 { - for (index, name) in indices.iter() { - if name == "_" { - continue; - } - let builtin = if *index == 0 { - Term::fst_pair() - } else { - Term::snd_pair() - }; - - term = term.lambda(name).apply(builder::known_data_to_type( - builtin.apply(Term::var(subject_name.clone())), - &tuple_types[*index].clone(), - )); - } - } else { - for (index, name) in indices.iter() { - term = term.lambda(name.clone()).apply(builder::known_data_to_type( - Term::head_list() - .apply(Term::var(subject_name.clone()).repeat_tail_list(*index)), - &tuple_types[*index].clone(), - )); - } + for (index, name) in indices.iter() { + term = term.lambda(name.clone()).apply(builder::known_data_to_type( + Term::head_list() + .apply(Term::var(subject_name.clone()).repeat_tail_list(*index)), + &tuple_types[*index].clone(), + )); } + + Some(term) + } + Air::PairGuard { + subject_tipo: tipo, + subject_name, + fst_name, + snd_name, + } => { + let mut term = arg_stack.pop().unwrap(); + + let tuple_types = tipo.get_inner_types(); + + if let Some(fst) = fst_name { + term = term.lambda(fst).apply(builder::known_data_to_type( + Term::fst_pair().apply(Term::var(subject_name.clone())), + &tuple_types[0].clone(), + )); + } + + if let Some(snd) = snd_name { + term = term.lambda(snd).apply(builder::known_data_to_type( + Term::snd_pair().apply(Term::var(subject_name.clone())), + &tuple_types[1].clone(), + )); + } + Some(term) } Air::Finally => { @@ -5094,34 +5125,9 @@ impl<'a> CodeGenerator<'a> { if constants.len() == args.len() { let data_constants = builder::convert_constants_to_data(constants); - if count == 2 { - let term = Term::Constant( - UplcConstant::ProtoPair( - UplcType::Data, - UplcType::Data, - data_constants[0].clone().into(), - data_constants[1].clone().into(), - ) - .into(), - ); - Some(term) - } else { - let term = Term::Constant( - UplcConstant::ProtoList(UplcType::Data, data_constants).into(), - ); - Some(term) - } - } else if count == 2 { - let term = Term::mk_pair_data() - .apply(builder::convert_type_to_data( - args[0].clone(), - &tuple_sub_types[0], - )) - .apply(builder::convert_type_to_data( - args[1].clone(), - &tuple_sub_types[1], - )); - + let term = Term::Constant( + UplcConstant::ProtoList(UplcType::Data, data_constants).into(), + ); Some(term) } else { let mut term = Term::empty_list(); @@ -5133,6 +5139,38 @@ impl<'a> CodeGenerator<'a> { Some(term) } } + Air::Pair { tipo } => { + let fst = arg_stack.pop().unwrap(); + let snd = arg_stack.pop().unwrap(); + + match (extract_constant(&fst), extract_constant(&snd)) { + (Some(fst), Some(snd)) => { + let term = Term::Constant( + UplcConstant::ProtoPair( + UplcType::Data, + UplcType::Data, + fst.clone(), + snd.clone(), + ) + .into(), + ); + Some(term) + } + _ => { + let term = Term::mk_pair_data() + .apply(builder::convert_type_to_data( + fst, + &tipo.get_inner_types()[0], + )) + .apply(builder::convert_type_to_data( + snd, + &tipo.get_inner_types()[1], + )); + + Some(term) + } + } + } Air::RecordUpdate { highest_index, indices, @@ -5261,67 +5299,75 @@ impl<'a> CodeGenerator<'a> { let mut term = arg_stack.pop().unwrap(); let list_id = self.id_gen.next(); - if tipo.is_pair() { - assert!(names.len() == 2); + let mut id_list = vec![]; + id_list.push(list_id); - if names[1] != "_" { - term = term.lambda(names[1].clone()).apply(if is_expect { - convert_data_to_type( - Term::snd_pair().apply(Term::var(format!("__tuple_{list_id}"))), - &inner_types[1], - ) - } else { - known_data_to_type( - Term::snd_pair().apply(Term::var(format!("__tuple_{list_id}"))), - &inner_types[1], - ) - }); - } + names.iter().for_each(|_| { + id_list.push(self.id_gen.next()); + }); - if names[0] != "_" { - term = term.lambda(names[0].clone()).apply(if is_expect { - convert_data_to_type( - Term::fst_pair().apply(Term::var(format!("__tuple_{list_id}"))), - &inner_types[0], - ) - } else { - known_data_to_type( - Term::fst_pair().apply(Term::var(format!("__tuple_{list_id}"))), - &inner_types[0], - ) - }) - } + let names_types = names + .into_iter() + .zip(inner_types) + .zip(id_list) + .map(|((name, tipo), id)| (name, tipo, id)) + .collect_vec(); - term = term.lambda(format!("__tuple_{list_id}")).apply(value); + term = builder::list_access_to_uplc( + &names_types, + false, + term, + false, + is_expect.into(), + error_term, + ) + .apply(value); - Some(term) - } else { - let mut id_list = vec![]; - id_list.push(list_id); + Some(term) + } + Air::PairAccessor { + fst, + snd, + tipo, + is_expect, + } => { + let inner_types = tipo.get_inner_types(); + let value = arg_stack.pop().unwrap(); - names.iter().for_each(|_| { - id_list.push(self.id_gen.next()); + let mut term = arg_stack.pop().unwrap(); + let list_id = self.id_gen.next(); + + if let Some(name) = snd { + term = term.lambda(name).apply(if is_expect { + convert_data_to_type( + Term::snd_pair().apply(Term::var(format!("__pair_{list_id}"))), + &inner_types[1], + ) + } else { + known_data_to_type( + Term::snd_pair().apply(Term::var(format!("__pair_{list_id}"))), + &inner_types[1], + ) }); - - let names_types = names - .into_iter() - .zip(inner_types) - .zip(id_list) - .map(|((name, tipo), id)| (name, tipo, id)) - .collect_vec(); - - term = builder::list_access_to_uplc( - &names_types, - false, - term, - false, - is_expect.into(), - error_term, - ) - .apply(value); - - Some(term) } + + if let Some(name) = fst { + term = term.lambda(name).apply(if is_expect { + convert_data_to_type( + Term::fst_pair().apply(Term::var(format!("__pair_{list_id}"))), + &inner_types[0], + ) + } else { + known_data_to_type( + Term::fst_pair().apply(Term::var(format!("__pair_{list_id}"))), + &inner_types[0], + ) + }) + } + + term = term.lambda(format!("__pair_{list_id}")).apply(value); + + Some(term) } Air::Trace { .. } => { let text = arg_stack.pop().unwrap(); diff --git a/crates/aiken-lang/src/gen_uplc/air.rs b/crates/aiken-lang/src/gen_uplc/air.rs index c86c29bd..74acc6bd 100644 --- a/crates/aiken-lang/src/gen_uplc/air.rs +++ b/crates/aiken-lang/src/gen_uplc/air.rs @@ -51,6 +51,9 @@ pub enum Air { tipo: Rc, count: usize, }, + Pair { + tipo: Rc, + }, Void, Var { constructor: ValueConstructor, @@ -136,6 +139,13 @@ pub enum Air { subject_name: String, complex_clause: bool, }, + PairClause { + subject_tipo: Rc, + subject_name: String, + fst_name: Option, + snd_name: Option, + complex_clause: bool, + }, ClauseGuard { subject_name: String, subject_tipo: Rc, @@ -151,6 +161,12 @@ pub enum Air { indices: IndexSet<(usize, String)>, subject_name: String, }, + PairGuard { + subject_tipo: Rc, + subject_name: String, + fst_name: Option, + snd_name: Option, + }, Finally, // If If { @@ -190,6 +206,13 @@ pub enum Air { tipo: Rc, is_expect: bool, }, + // Tuple Access + PairAccessor { + fst: Option, + snd: Option, + tipo: Rc, + is_expect: bool, + }, // Misc. ErrorTerm { tipo: Rc, diff --git a/crates/aiken-lang/src/gen_uplc/builder.rs b/crates/aiken-lang/src/gen_uplc/builder.rs index 0272a877..5711373b 100644 --- a/crates/aiken-lang/src/gen_uplc/builder.rs +++ b/crates/aiken-lang/src/gen_uplc/builder.rs @@ -1852,6 +1852,7 @@ pub fn air_holds_msg(air: &Air) -> bool { Air::FieldsExpose { is_expect, .. } | Air::TupleAccessor { is_expect, .. } + | Air::PairAccessor { is_expect, .. } | Air::CastFromData { is_expect, .. } => *is_expect, Air::ListAccessor { expect_level, .. } => { diff --git a/crates/aiken-lang/src/gen_uplc/tree.rs b/crates/aiken-lang/src/gen_uplc/tree.rs index 9666a63a..b3af15cb 100644 --- a/crates/aiken-lang/src/gen_uplc/tree.rs +++ b/crates/aiken-lang/src/gen_uplc/tree.rs @@ -162,6 +162,13 @@ pub enum AirTree { subject_name: String, then: Box, }, + PairGuard { + subject_tipo: Rc, + subject_name: String, + fst_name: Option, + snd_name: Option, + then: Box, + }, // Field Access FieldsExpose { indices: Vec<(usize, String, Rc)>, @@ -195,6 +202,16 @@ pub enum AirTree { msg: Option, then: Box, }, + // Pair Access + PairAccessor { + fst: Option, + snd: Option, + tipo: Rc, + is_expect: bool, + msg: Option, + pair: Box, + then: Box, + }, // Misc. FieldsEmpty { constr: Box, @@ -237,6 +254,11 @@ pub enum AirTree { tipo: Rc, items: Vec, }, + Pair { + tipo: Rc, + fst: Box, + snd: Box, + }, Void, Var { constructor: ValueConstructor, @@ -320,6 +342,16 @@ pub enum AirTree { otherwise: Box, }, + PairClause { + subject_tipo: Rc, + subject_name: String, + fst_name: Option, + snd_name: Option, + complex_clause: bool, + then: Box, + otherwise: Box, + }, + Finally { pattern: Box, then: Box, @@ -363,20 +395,25 @@ impl AirTree { value: value.to_string(), } } + pub fn string(value: impl ToString) -> AirTree { AirTree::String { value: value.to_string(), } } + pub fn byte_array(bytes: Vec) -> AirTree { AirTree::ByteArray { bytes } } + pub fn curve(point: Curve) -> AirTree { AirTree::CurvePoint { point } } + pub fn bool(value: bool) -> AirTree { AirTree::Bool { value } } + pub fn list(mut items: Vec, tipo: Rc, tail: Option) -> AirTree { if let Some(tail) = tail { items.push(tail); @@ -394,12 +431,23 @@ impl AirTree { } } } + pub fn tuple(items: Vec, tipo: Rc) -> AirTree { AirTree::Tuple { tipo, items } } + + pub fn pair(fst: AirTree, snd: AirTree, tipo: Rc) -> AirTree { + AirTree::Pair { + tipo, + fst: fst.into(), + snd: snd.into(), + } + } + pub fn void() -> AirTree { AirTree::Void } + pub fn var( constructor: ValueConstructor, name: impl ToString, @@ -411,6 +459,7 @@ impl AirTree { variant_name: variant_name.to_string(), } } + pub fn local_var(name: impl ToString, tipo: Rc) -> AirTree { AirTree::Var { constructor: ValueConstructor::public( @@ -423,6 +472,7 @@ impl AirTree { variant_name: "".to_string(), } } + pub fn call(func: AirTree, tipo: Rc, args: Vec) -> AirTree { AirTree::Call { tipo, @@ -453,6 +503,7 @@ impl AirTree { then: then.into(), } } + pub fn define_cyclic_func( func_name: impl ToString, module_name: impl ToString, @@ -468,15 +519,18 @@ impl AirTree { then: then.into(), } } + pub fn anon_func(params: Vec, func_body: AirTree) -> AirTree { AirTree::Fn { params, func_body: func_body.into(), } } + pub fn builtin(func: DefaultFunction, tipo: Rc, args: Vec) -> AirTree { AirTree::Builtin { func, tipo, args } } + pub fn binop( op: BinOp, tipo: Rc, @@ -492,12 +546,14 @@ impl AirTree { argument_tipo, } } + pub fn unop(op: UnOp, arg: AirTree) -> AirTree { AirTree::UnOp { op, arg: arg.into(), } } + pub fn let_assignment(name: impl ToString, value: AirTree, then: AirTree) -> AirTree { AirTree::Let { name: name.to_string(), @@ -505,6 +561,7 @@ impl AirTree { then: then.into(), } } + pub fn cast_from_data(value: AirTree, tipo: Rc, msg: Option) -> AirTree { AirTree::CastFromData { tipo, @@ -512,12 +569,14 @@ impl AirTree { msg, } } + pub fn cast_to_data(value: AirTree, tipo: Rc) -> AirTree { AirTree::CastToData { tipo, value: value.into(), } } + pub fn assert_constr_index( constr_index: usize, constr: AirTree, @@ -531,6 +590,7 @@ impl AirTree { then: then.into(), } } + pub fn assert_bool( is_true: bool, value: AirTree, @@ -544,6 +604,7 @@ impl AirTree { then: then.into(), } } + pub fn when( subject_name: impl ToString, tipo: Rc, @@ -559,6 +620,7 @@ impl AirTree { clauses: clauses.into(), } } + pub fn clause( subject_name: impl ToString, pattern: AirTree, @@ -576,6 +638,7 @@ impl AirTree { otherwise: otherwise.into(), } } + pub fn list_clause( tail_name: impl ToString, subject_tipo: Rc, @@ -593,6 +656,7 @@ impl AirTree { otherwise: otherwise.into(), } } + pub fn tuple_clause( subject_name: impl ToString, subject_tipo: Rc, @@ -612,12 +676,34 @@ impl AirTree { otherwise: otherwise.into(), } } + + pub fn pair_clause( + subject_name: impl ToString, + subject_tipo: Rc, + fst_name: Option, + snd_name: Option, + then: AirTree, + otherwise: AirTree, + complex_clause: bool, + ) -> AirTree { + AirTree::PairClause { + subject_tipo, + subject_name: subject_name.to_string(), + fst_name, + snd_name, + complex_clause, + then: then.into(), + otherwise: otherwise.into(), + } + } + pub fn wrap_clause(then: AirTree, otherwise: AirTree) -> AirTree { AirTree::WrapClause { then: then.into(), otherwise: otherwise.into(), } } + pub fn clause_guard( subject_name: impl ToString, pattern: AirTree, @@ -631,6 +717,7 @@ impl AirTree { then: then.into(), } } + pub fn list_clause_guard( tail_name: impl ToString, subject_tipo: Rc, @@ -646,6 +733,7 @@ impl AirTree { then: then.into(), } } + pub fn tuple_clause_guard( subject_name: impl ToString, subject_tipo: Rc, @@ -659,12 +747,30 @@ impl AirTree { then: then.into(), } } + + pub fn pair_clause_guard( + subject_name: impl ToString, + subject_tipo: Rc, + fst_name: Option, + snd_name: Option, + then: AirTree, + ) -> AirTree { + AirTree::PairGuard { + subject_name: subject_name.to_string(), + subject_tipo, + fst_name, + snd_name, + then: then.into(), + } + } + pub fn finally(pattern: AirTree, then: AirTree) -> AirTree { AirTree::Finally { pattern: pattern.into(), then: then.into(), } } + pub fn if_branches( mut branches: Vec<(AirTree, AirTree)>, tipo: Rc, @@ -691,6 +797,7 @@ impl AirTree { final_if } + pub fn create_constr(tag: usize, tipo: Rc, args: Vec) -> AirTree { AirTree::Constr { tag, tipo, args } } @@ -710,6 +817,7 @@ impl AirTree { args, } } + pub fn index_access(function_name: String, tipo: Rc, list_of_fields: AirTree) -> AirTree { AirTree::cast_from_data( AirTree::call( @@ -740,6 +848,7 @@ impl AirTree { None, ) } + pub fn fields_expose( indices: Vec<(usize, String, Rc)>, record: AirTree, @@ -775,6 +884,7 @@ impl AirTree { then: then.into(), } } + pub fn list_expose( tail_head_names: Vec<(String, String)>, tail: Option<(String, String)>, @@ -788,6 +898,7 @@ impl AirTree { then: then.into(), } } + pub fn tuple_access( names: Vec, tipo: Rc, @@ -805,6 +916,27 @@ impl AirTree { then: then.into(), } } + + pub fn pair_access( + fst: Option, + snd: Option, + tipo: Rc, + pair: AirTree, + msg: Option, + is_expect: bool, + then: AirTree, + ) -> AirTree { + AirTree::PairAccessor { + fst, + snd, + tipo, + is_expect, + msg, + pair: pair.into(), + then: then.into(), + } + } + pub fn pair_index(index: usize, tipo: Rc, tuple: AirTree) -> AirTree { AirTree::cast_from_data( AirTree::builtin( @@ -820,9 +952,11 @@ impl AirTree { None, ) } + pub fn error(tipo: Rc, validator: bool) -> AirTree { AirTree::ErrorTerm { tipo, validator } } + pub fn trace(msg: AirTree, tipo: Rc, then: AirTree) -> AirTree { AirTree::Trace { tipo, @@ -840,6 +974,7 @@ impl AirTree { pub fn no_op(then: AirTree) -> AirTree { AirTree::NoOp { then: then.into() } } + pub fn fields_empty(constr: AirTree, msg: Option, then: AirTree) -> AirTree { AirTree::FieldsEmpty { constr: constr.into(), @@ -847,6 +982,7 @@ impl AirTree { then: then.into(), } } + pub fn list_empty(list: AirTree, msg: Option, then: AirTree) -> AirTree { AirTree::ListEmpty { list: list.into(), @@ -1058,6 +1194,21 @@ impl AirTree { }); then.create_air_vec(air_vec); } + AirTree::PairGuard { + subject_tipo, + subject_name, + fst_name, + snd_name, + then, + } => { + air_vec.push(Air::PairGuard { + subject_tipo: subject_tipo.clone(), + subject_name: subject_name.clone(), + fst_name: fst_name.clone(), + snd_name: snd_name.clone(), + }); + then.create_air_vec(air_vec); + } AirTree::FieldsExpose { indices, record, @@ -1134,6 +1285,29 @@ impl AirTree { tuple.create_air_vec(air_vec); then.create_air_vec(air_vec); } + AirTree::PairAccessor { + fst, + snd, + tipo, + is_expect, + msg, + pair, + then, + } => { + air_vec.push(Air::PairAccessor { + fst: fst.clone(), + snd: snd.clone(), + tipo: tipo.clone(), + 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); + } AirTree::FieldsEmpty { constr, msg, then } => { air_vec.push(Air::FieldsEmpty); @@ -1189,6 +1363,11 @@ impl AirTree { item.create_air_vec(air_vec); } } + AirTree::Pair { tipo, fst, snd } => { + air_vec.push(Air::Pair { tipo: tipo.clone() }); + fst.create_air_vec(air_vec); + snd.create_air_vec(air_vec); + } AirTree::Void => air_vec.push(Air::Void), AirTree::Var { constructor, @@ -1334,6 +1513,25 @@ impl AirTree { then.create_air_vec(air_vec); otherwise.create_air_vec(air_vec); } + AirTree::PairClause { + subject_tipo, + subject_name, + fst_name, + snd_name, + complex_clause, + then, + otherwise, + } => { + air_vec.push(Air::PairClause { + subject_tipo: subject_tipo.clone(), + subject_name: subject_name.clone(), + fst_name: fst_name.clone(), + snd_name: snd_name.clone(), + complex_clause: *complex_clause, + }); + then.create_air_vec(air_vec); + otherwise.create_air_vec(air_vec); + } AirTree::Finally { pattern, then } => { air_vec.push(Air::Finally); pattern.create_air_vec(air_vec); @@ -1398,6 +1596,7 @@ impl AirTree { AirTree::CurvePoint { point } => point.tipo(), AirTree::List { tipo, .. } | AirTree::Tuple { tipo, .. } + | AirTree::Pair { tipo, .. } | AirTree::Call { tipo, .. } | AirTree::Builtin { tipo, .. } | AirTree::BinOp { tipo, .. } @@ -1420,6 +1619,7 @@ impl AirTree { | AirTree::ListClause { then, .. } | AirTree::WrapClause { then, .. } | AirTree::TupleClause { then, .. } + | AirTree::PairClause { then, .. } | AirTree::Finally { then, .. } | AirTree::Let { then, .. } | AirTree::DefineFunc { then, .. } @@ -1429,10 +1629,12 @@ impl AirTree { | AirTree::ClauseGuard { then, .. } | AirTree::ListClauseGuard { then, .. } | AirTree::TupleGuard { then, .. } + | AirTree::PairGuard { then, .. } | AirTree::FieldsExpose { then, .. } | AirTree::ListAccessor { then, .. } | AirTree::ListExpose { then, .. } | AirTree::TupleAccessor { then, .. } + | AirTree::PairAccessor { then, .. } | AirTree::FieldsEmpty { then, .. } | AirTree::ListEmpty { then, .. } | AirTree::NoOp { then } => then.return_type(), @@ -1525,7 +1727,8 @@ impl AirTree { tree_path.push(current_depth, depth_index); let mut tuple_then_index = None; - // Assignments/Statements get traversed here + // Assignments'/Statements' values get traversed here + // Then the body under these assignments/statements get traversed later on match self { AirTree::Let { value, .. } => { value.do_traverse_tree_with( @@ -1592,6 +1795,15 @@ impl AirTree { apply_with_func_last, ); } + AirTree::PairAccessor { pair, .. } => { + pair.do_traverse_tree_with( + tree_path, + current_depth + 1, + index_count.next_number(), + with, + apply_with_func_last, + ); + } AirTree::FieldsEmpty { constr, .. } => { constr.do_traverse_tree_with( tree_path, @@ -1629,7 +1841,49 @@ impl AirTree { apply_with_func_last, ); } - _ => {} + AirTree::PairClause { otherwise, .. } => { + tuple_then_index = Some(index_count.next_number()); + otherwise.do_traverse_tree_with( + tree_path, + current_depth + 1, + index_count.next_number(), + with, + apply_with_func_last, + ); + } + AirTree::DefineFunc { .. } + | AirTree::DefineCyclicFuncs { .. } + | AirTree::ListClauseGuard { .. } + | AirTree::TupleGuard { .. } + | AirTree::PairGuard { .. } + | AirTree::ListExpose { .. } + | AirTree::NoOp { .. } + | AirTree::Int { .. } + | AirTree::String { .. } + | AirTree::ByteArray { .. } + | AirTree::CurvePoint { .. } + | AirTree::Bool { .. } + | AirTree::List { .. } + | AirTree::Tuple { .. } + | AirTree::Pair { .. } + | AirTree::Void + | AirTree::Var { .. } + | AirTree::Call { .. } + | AirTree::Fn { .. } + | AirTree::Builtin { .. } + | AirTree::BinOp { .. } + | AirTree::UnOp { .. } + | AirTree::CastFromData { .. } + | AirTree::CastToData { .. } + | AirTree::Clause { .. } + | AirTree::ListClause { .. } + | AirTree::WrapClause { .. } + | AirTree::Finally { .. } + | AirTree::If { .. } + | AirTree::Constr { .. } + | AirTree::RecordUpdate { .. } + | AirTree::ErrorTerm { .. } + | AirTree::Trace { .. } => {} } if !apply_with_func_last { @@ -1645,11 +1899,13 @@ impl AirTree { | AirTree::FieldsExpose { then, .. } | AirTree::ListAccessor { then, .. } | AirTree::TupleAccessor { then, .. } + | AirTree::PairAccessor { then, .. } | AirTree::FieldsEmpty { then, .. } | AirTree::ListEmpty { then, .. } | AirTree::ListExpose { then, .. } | AirTree::ListClauseGuard { then, .. } | AirTree::TupleGuard { then, .. } + | AirTree::PairGuard { then, .. } | AirTree::NoOp { then } => { then.do_traverse_tree_with( tree_path, @@ -1681,6 +1937,19 @@ impl AirTree { apply_with_func_last, ); } + AirTree::PairClause { then, .. } => { + let Some(index) = tuple_then_index else { + unreachable!() + }; + + then.do_traverse_tree_with( + tree_path, + current_depth + 1, + index, + with, + apply_with_func_last, + ); + } AirTree::List { items, .. } => { for item in items { item.do_traverse_tree_with( @@ -1703,6 +1972,23 @@ impl AirTree { ); } } + AirTree::Pair { fst, snd, .. } => { + fst.do_traverse_tree_with( + tree_path, + current_depth + 1, + index_count.next_number(), + with, + apply_with_func_last, + ); + + snd.do_traverse_tree_with( + tree_path, + current_depth + 1, + index_count.next_number(), + with, + apply_with_func_last, + ); + } AirTree::Call { func, args, .. } => { func.do_traverse_tree_with( tree_path, @@ -2071,6 +2357,13 @@ impl AirTree { panic!("Tree Path index outside tree children nodes") } } + AirTree::PairGuard { then, .. } => { + if *index == 0 { + then.as_mut().do_find_air_tree_node(tree_path_iter) + } else { + panic!("Tree Path index outside tree children nodes") + } + } AirTree::FieldsExpose { record, then, .. } => { if *index == 0 { record.as_mut().do_find_air_tree_node(tree_path_iter) @@ -2105,6 +2398,15 @@ impl AirTree { panic!("Tree Path index outside tree children nodes") } } + AirTree::PairAccessor { pair, then, .. } => { + if *index == 0 { + pair.as_mut().do_find_air_tree_node(tree_path_iter) + } else if *index == 1 { + then.as_mut().do_find_air_tree_node(tree_path_iter) + } else { + panic!("Tree Path index outside tree children nodes") + } + } AirTree::NoOp { then } => { if *index == 0 { then.as_mut().do_find_air_tree_node(tree_path_iter) @@ -2112,8 +2414,7 @@ impl AirTree { panic!("Tree Path index outside tree children nodes") } } - AirTree::DefineFunc { .. } => unreachable!(), - AirTree::DefineCyclicFuncs { .. } => unreachable!(), + AirTree::DefineFunc { .. } | AirTree::DefineCyclicFuncs { .. } => unreachable!(), AirTree::FieldsEmpty { constr, then, .. } => { if *index == 0 { constr.as_mut().do_find_air_tree_node(tree_path_iter) @@ -2140,6 +2441,15 @@ impl AirTree { .expect("Tree Path index outside tree children nodes"); item.do_find_air_tree_node(tree_path_iter) } + AirTree::Pair { fst, snd, .. } => { + if *index == 0 { + fst.as_mut().do_find_air_tree_node(tree_path_iter) + } else if *index == 1 { + snd.as_mut().do_find_air_tree_node(tree_path_iter) + } else { + panic!("Tree Path index outside tree children nodes") + } + } AirTree::Call { func, args, .. } => { children_nodes.push(func.as_mut()); children_nodes.extend(args.iter_mut()); @@ -2243,6 +2553,17 @@ impl AirTree { panic!("Tree Path index outside tree children nodes") } } + AirTree::PairClause { + then, otherwise, .. + } => { + if *index == 0 { + then.as_mut().do_find_air_tree_node(tree_path_iter) + } else if *index == 1 { + otherwise.as_mut().do_find_air_tree_node(tree_path_iter) + } else { + panic!("Tree Path index outside tree children nodes") + } + } AirTree::Finally { pattern, then } => { if *index == 0 { pattern.as_mut().do_find_air_tree_node(tree_path_iter) @@ -2291,7 +2612,15 @@ impl AirTree { panic!("Tree Path index outside tree children nodes") } } - _ => { + + AirTree::Int { .. } + | AirTree::String { .. } + | AirTree::ByteArray { .. } + | AirTree::CurvePoint { .. } + | AirTree::Bool { .. } + | AirTree::Void + | AirTree::Var { .. } + | AirTree::ErrorTerm { .. } => { panic!("A tree node with no children was encountered with a longer tree path.") } } diff --git a/crates/aiken-lang/src/tipo.rs b/crates/aiken-lang/src/tipo.rs index 21cadca2..fe13b374 100644 --- a/crates/aiken-lang/src/tipo.rs +++ b/crates/aiken-lang/src/tipo.rs @@ -453,6 +453,12 @@ impl Type { Self::Var { tipo, .. } => tipo.borrow().get_inner_types(), _ => vec![], } + } else if self.is_pair() { + match self { + Self::Pair { fst, snd, .. } => vec![fst.clone(), snd.clone()], + Self::Var { tipo, .. } => tipo.borrow().get_inner_types(), + _ => vec![], + } } else if matches!(self.get_uplc_type(), UplcType::Data) { match self { Type::App { args, .. } => args.clone(),