From 24303605ea61c302f1753b4bf55e104d82722c23 Mon Sep 17 00:00:00 2001 From: microproofs Date: Fri, 29 Mar 2024 16:05:16 -0400 Subject: [PATCH] Few more places in codegen where we need to be able to deal with Pair records --- crates/aiken-lang/src/gen_uplc.rs | 181 ++++++++++------ crates/aiken-lang/src/gen_uplc/tree.rs | 2 +- crates/aiken-project/src/tests/gen_uplc.rs | 233 ++++++++++++++++++++- 3 files changed, 347 insertions(+), 69 deletions(-) diff --git a/crates/aiken-lang/src/gen_uplc.rs b/crates/aiken-lang/src/gen_uplc.rs index 151da200b..4c7df4d67 100644 --- a/crates/aiken-lang/src/gen_uplc.rs +++ b/crates/aiken-lang/src/gen_uplc.rs @@ -659,6 +659,12 @@ impl<'a> CodeGenerator<'a> { } => { if check_replaceable_opaque_type(&record.tipo(), &self.data_types) { self.build(record, module_build_name, &[]) + } else if record.tipo().is_pair() { + AirTree::pair_index( + *index, + tipo.clone(), + self.build(record, module_build_name, &[]), + ) } else { let function_name = format!("__access_index_{}", *index); @@ -708,23 +714,37 @@ impl<'a> CodeGenerator<'a> { field_map, .. } => { - let data_type = lookup_data_type_by_tipo(&self.data_types, tipo); + let val_constructor = if tipo.is_pair() { + ValueConstructor::public( + tipo.clone(), + ValueConstructorVariant::Record { + module: "".into(), + name: name.clone(), + field_map: field_map.clone(), + arity: 2, + location: Span::empty(), + constructors_count: 1, + }, + ) + } else { + let data_type = lookup_data_type_by_tipo(&self.data_types, tipo); - let val_constructor = ValueConstructor::public( - tipo.clone(), - ValueConstructorVariant::Record { - name: name.clone(), - arity: *arity, - field_map: field_map.clone(), - location: Span::empty(), - module: module_name.clone(), - constructors_count: data_type - .expect("Created a module type without a definition?") - .constructors - .len() - as u16, - }, - ); + ValueConstructor::public( + tipo.clone(), + ValueConstructorVariant::Record { + name: name.clone(), + arity: *arity, + field_map: field_map.clone(), + location: Span::empty(), + module: module_name.clone(), + constructors_count: data_type + .expect("Created a module type without a definition?") + .constructors + .len() + as u16, + }, + ) + }; AirTree::var(val_constructor, name, "") } @@ -772,43 +792,35 @@ impl<'a> CodeGenerator<'a> { TypedExpr::TupleIndex { index, tuple, tipo, .. } => { - if tuple.tipo().is_pair() { - AirTree::pair_index( - *index, - tipo.clone(), - self.build(tuple, module_build_name, &[]), - ) - } else { - let function_name = format!("__access_index_{}", *index); + let function_name = format!("__access_index_{}", *index); - if self.code_gen_functions.get(&function_name).is_none() { - let mut body = AirTree::local_var("__fields", list(data())); + if self.code_gen_functions.get(&function_name).is_none() { + let mut body = AirTree::local_var("__fields", list(data())); - for _ in 0..*index { - body = AirTree::builtin( - DefaultFunction::TailList, - list(data()), - vec![body], - ) - } - - body = AirTree::builtin(DefaultFunction::HeadList, data(), vec![body]); - - self.code_gen_functions.insert( - function_name.clone(), - CodeGenFunction::Function { - body, - params: vec!["__fields".to_string()], - }, - ); + for _ in 0..*index { + body = AirTree::builtin( + DefaultFunction::TailList, + list(data()), + vec![body], + ) } - AirTree::index_access( - function_name, - tipo.clone(), - self.build(tuple, module_build_name, &[]), - ) + body = AirTree::builtin(DefaultFunction::HeadList, data(), vec![body]); + + self.code_gen_functions.insert( + function_name.clone(), + CodeGenFunction::Function { + body, + params: vec!["__fields".to_string()], + }, + ); } + + AirTree::index_access( + function_name, + tipo.clone(), + self.build(tuple, module_build_name, &[]), + ) } TypedExpr::ErrorTerm { tipo, .. } => AirTree::error(tipo.clone(), false), @@ -816,32 +828,66 @@ impl<'a> CodeGenerator<'a> { TypedExpr::RecordUpdate { tipo, spread, args, .. } => { - let mut index_types = vec![]; - let mut update_args = vec![]; + if tipo.is_pair() { + assert!(args.len() == 1); - let mut highest_index = 0; + let Some(arg) = args.first() else { + unreachable!("Pair update with no arguments") + }; - for arg in args - .iter() - .sorted_by(|arg1, arg2| arg1.index.cmp(&arg2.index)) - { let arg_val = self.build(&arg.value, module_build_name, &[]); - if arg.index > highest_index { - highest_index = arg.index; + let other_pair = self.build(spread, module_build_name, &[]); + + if arg.index == 0 { + AirTree::pair( + arg_val, + AirTree::pair_index( + 1, + tipo.get_inner_types()[1].clone(), + other_pair, + ), + tipo.clone(), + ) + } else { + AirTree::pair( + AirTree::pair_index( + 0, + tipo.get_inner_types()[0].clone(), + other_pair, + ), + arg_val, + tipo.clone(), + ) } + } else { + let mut index_types = vec![]; + let mut update_args = vec![]; - index_types.push((arg.index, arg.value.tipo())); - update_args.push(arg_val); - } + let mut highest_index = 0; - AirTree::record_update( - index_types, - highest_index, - tipo.clone(), - self.build(spread, module_build_name, &[]), - update_args, - ) + for arg in args + .iter() + .sorted_by(|arg1, arg2| arg1.index.cmp(&arg2.index)) + { + let arg_val = self.build(&arg.value, module_build_name, &[]); + + if arg.index > highest_index { + highest_index = arg.index; + } + + index_types.push((arg.index, arg.value.tipo())); + update_args.push(arg_val); + } + + AirTree::record_update( + index_types, + highest_index, + tipo.clone(), + self.build(spread, module_build_name, &[]), + update_args, + ) + } } TypedExpr::UnOp { value, op, .. } => { AirTree::unop(*op, self.build(value, module_build_name, &[])) @@ -4195,6 +4241,7 @@ impl<'a> CodeGenerator<'a> { ValueConstructorVariant::Record { name: constr_name, .. } => { + // TODO handle pair if constructor.tipo.is_bool() { Some(Term::bool(constr_name == "True")) } else if constructor.tipo.is_void() { diff --git a/crates/aiken-lang/src/gen_uplc/tree.rs b/crates/aiken-lang/src/gen_uplc/tree.rs index 8de60875a..4c923f012 100644 --- a/crates/aiken-lang/src/gen_uplc/tree.rs +++ b/crates/aiken-lang/src/gen_uplc/tree.rs @@ -937,7 +937,7 @@ impl AirTree { } } - pub fn pair_index(index: usize, tipo: Rc, tuple: AirTree) -> AirTree { + pub fn pair_index(index: u64, tipo: Rc, tuple: AirTree) -> AirTree { AirTree::cast_from_data( AirTree::builtin( if index == 0 { diff --git a/crates/aiken-project/src/tests/gen_uplc.rs b/crates/aiken-project/src/tests/gen_uplc.rs index 59b75e5d7..cc96d8549 100644 --- a/crates/aiken-project/src/tests/gen_uplc.rs +++ b/crates/aiken-project/src/tests/gen_uplc.rs @@ -2994,7 +2994,238 @@ fn acceptance_test_28_unique_list() { } #[test] -fn acceptance_test_29_union() { +fn acceptance_test_29_union_pair() { + let src = r#" + type Map = List> + + pub opaque type AssocList { + inner: Map, + } + + pub fn new() -> AssocList { + AssocList { inner: [] } + } + + pub fn from_list(xs: Map) -> AssocList { + AssocList { inner: do_from_list(xs) } + } + + fn do_from_list(xs: Map) -> Map { + when xs is { + [] -> + [] + [Pair(k, v), ..rest] -> + do_insert(do_from_list(rest), k, v) + } + } + + pub fn insert( + in m: AssocList, + key k: key, + value v: value, + ) -> AssocList { + AssocList { inner: do_insert(m.inner, k, v) } + } + + fn do_insert(elems: Map, k: key, v: value) -> Map { + when elems is { + [] -> + [Pair(k, v)] + [Pair(k2, v2), ..rest] -> + if k == k2 { + [Pair(k, v), ..rest] + } else { + [Pair(k2, v2), ..do_insert(rest, k, v)] + } + } + } + + pub fn union( + left: AssocList, + right: AssocList, + ) -> AssocList { + AssocList { inner: do_union(left.inner, right.inner) } + } + + fn do_union( + left: Map, + right: Map, + ) -> Map { + when left is { + [] -> + right + [Pair{fst: k, snd: v}, ..rest] -> + do_union(rest, do_insert(right, k, v)) + } + } + + fn fixture_1() { + new() + |> insert("foo", 42) + |> insert("bar", 14) + } + + test union_1() { + union(fixture_1(), new()) == fixture_1() + } + + "#; + + assert_uplc( + src, + Term::equals_data() + .apply( + Term::map_data().apply( + Term::var("union") + .lambda("union") + .apply( + Term::var("do_union") + .apply(Term::var("left")) + .apply(Term::var("right")) + .lambda("right") + .lambda("left"), + ) + .lambda("do_union") + .apply(Term::var("do_union").apply(Term::var("do_union"))) + .lambda("do_union") + .apply( + Term::var("left") + .delayed_choose_list( + Term::var("right"), + Term::var("do_union") + .apply(Term::var("do_union")) + .apply(Term::var("rest")) + .apply( + Term::var("do_insert") + .apply(Term::var("right")) + .apply(Term::var("k")) + .apply(Term::var("v")), + ) + .lambda("v") + .apply( + Term::un_i_data() + .apply(Term::snd_pair().apply(Term::var("pair"))), + ) + .lambda("k") + .apply( + Term::un_b_data() + .apply(Term::fst_pair().apply(Term::var("pair"))), + ) + .lambda("rest") + .apply(Term::tail_list().apply(Term::var("left"))) + .lambda("pair") + .apply(Term::head_list().apply(Term::var("left"))), + ) + .lambda("right") + .lambda("left") + .lambda("do_union"), + ) + .lambda("do_insert") + .apply( + Term::var("do_insert") + .apply(Term::var("do_insert")) + .apply(Term::var("elems")) + .lambda("do_insert") + .apply( + Term::var("elems") + .delayed_choose_list( + Term::mk_cons() + .apply( + Term::mk_pair_data() + .apply(Term::b_data().apply(Term::var("k"))) + .apply( + Term::i_data().apply(Term::var("v")), + ), + ) + .apply(Term::empty_map()), + Term::equals_bytestring() + .apply(Term::var("k")) + .apply(Term::var("k2")) + .delayed_if_then_else( + Term::mk_cons() + .apply( + Term::mk_pair_data() + .apply( + Term::b_data() + .apply(Term::var("k")), + ) + .apply( + Term::i_data() + .apply(Term::var("v")), + ), + ) + .apply(Term::var("rest")), + Term::mk_cons() + .apply( + Term::mk_pair_data() + .apply( + Term::b_data() + .apply(Term::var("k2")), + ) + .apply( + Term::i_data() + .apply(Term::var("v2")), + ), + ) + .apply( + Term::var("do_insert") + .apply(Term::var("do_insert")) + .apply(Term::var("rest")), + ), + ) + .lambda("v2") + .apply(Term::un_i_data().apply( + Term::snd_pair().apply(Term::var("pair")), + )) + .lambda("k2") + .apply(Term::un_b_data().apply( + Term::fst_pair().apply(Term::var("pair")), + )) + .lambda("rest") + .apply(Term::tail_list().apply(Term::var("elems"))) + .lambda("pair") + .apply(Term::head_list().apply(Term::var("elems"))), + ) + .lambda("elems") + .lambda("do_insert"), + ) + .lambda("v") + .lambda("k") + .lambda("elems"), + ) + .apply(Term::map_values(vec![ + Constant::ProtoPair( + Type::Data, + Type::Data, + Constant::Data(Data::bytestring("foo".as_bytes().to_vec())).into(), + Constant::Data(Data::integer(42.into())).into(), + ), + Constant::ProtoPair( + Type::Data, + Type::Data, + Constant::Data(Data::bytestring("bar".as_bytes().to_vec())).into(), + Constant::Data(Data::integer(14.into())).into(), + ), + ])) + .apply(Term::empty_map()), + ), + ) + .apply(Term::data(Data::map(vec![ + ( + Data::bytestring("foo".as_bytes().to_vec()), + Data::integer(42.into()), + ), + ( + Data::bytestring("bar".as_bytes().to_vec()), + Data::integer(14.into()), + ), + ]))), + false, + ); +} + +#[test] +fn acceptance_test_29_union_tuple() { let src = r#" pub opaque type AssocList { inner: List<(key, value)>,