diff --git a/crates/aiken-lang/src/gen_uplc.rs b/crates/aiken-lang/src/gen_uplc.rs index c9f387d00..d036622e8 100644 --- a/crates/aiken-lang/src/gen_uplc.rs +++ b/crates/aiken-lang/src/gen_uplc.rs @@ -4083,61 +4083,17 @@ impl<'a> CodeGenerator<'a> { ValueConstructorVariant::ModuleConstant { .. } => { unreachable!("{:#?}, {}", constructor, name) } - - ValueConstructorVariant::ModuleFn { - builtin: Some(builtin), - .. - } => { - let term = match builtin { - DefaultFunction::IfThenElse - | DefaultFunction::ChooseUnit - | DefaultFunction::Trace - | DefaultFunction::ChooseList - | DefaultFunction::ChooseData - | DefaultFunction::UnConstrData => { - builder::special_case_builtin(builtin, 0, vec![]) - } - - DefaultFunction::FstPair | DefaultFunction::SndPair => { - builder::undata_builtin( - builtin, - 0, - &constructor.tipo.return_type().unwrap(), - vec![], - ) - } - - DefaultFunction::HeadList - if !constructor.tipo.return_type().unwrap().is_pair() => - { - builder::undata_builtin( - builtin, - 0, - &constructor.tipo.return_type().unwrap(), - vec![], - ) - } - - DefaultFunction::MkCons | DefaultFunction::MkPairData => { - unimplemented!( - "MkCons and MkPairData should be handled by an anon function or using [] or ( a, b, .., z) or Pair {{fst:a, snd: b}}.\n" - ) - } - _ => { - let mut term: Term = (*builtin).into(); - - term = builder::apply_builtin_forces(term, builtin.force_count()); - - term - } - }; - Some(term) - } ValueConstructorVariant::ModuleFn { name: func_name, module, + builtin, .. } => { + assert!( + builtin.is_none(), + "found remaining builtin function {func_name:?} ({builtin:?} declared as a module function in {module:?}" + ); + if let Some((names, index, cyclic_name)) = self.cyclic_functions.get(&( FunctionAccessKey { module_name: module.clone(), @@ -4537,21 +4493,19 @@ impl<'a> CodeGenerator<'a> { | DefaultFunction::Trace | DefaultFunction::ChooseList | DefaultFunction::ChooseData + | DefaultFunction::MkCons | DefaultFunction::UnConstrData => { - builder::special_case_builtin(&func, count, arg_vec) + builder::special_case_builtin(&func, tipo, count, arg_vec) } - DefaultFunction::FstPair | DefaultFunction::SndPair => { builder::undata_builtin(&func, count, ret_tipo, arg_vec) } - DefaultFunction::HeadList if !tipo.is_pair() => { builder::undata_builtin(&func, count, ret_tipo, arg_vec) } - - DefaultFunction::MkCons | DefaultFunction::MkPairData => { + DefaultFunction::MkPairData => { unimplemented!( - "MkCons and MkPairData should be handled by an anon function or using [] or ( a, b, .., z).\n" + "MkPairData should be handled by an anon function ( a, b, .., z).\n" ) } _ => { diff --git a/crates/aiken-lang/src/gen_uplc/builder.rs b/crates/aiken-lang/src/gen_uplc/builder.rs index 4ad6b63dd..8b3043155 100644 --- a/crates/aiken-lang/src/gen_uplc/builder.rs +++ b/crates/aiken-lang/src/gen_uplc/builder.rs @@ -1642,6 +1642,7 @@ pub fn to_data_builtin( pub fn special_case_builtin( func: &DefaultFunction, + tipo: Rc, count: usize, mut args: Vec>, ) -> Term { @@ -1652,6 +1653,26 @@ pub fn special_case_builtin( term.lambda("_").apply(unit) } + + DefaultFunction::MkCons => { + let arg_type = tipo + .arg_types() + .and_then(|generics| generics.first().cloned()) + .expect("mk_cons should have (exactly) one type parameter"); + + if let [head, tail] = &args[..] { + Term::mk_cons() + .apply(if arg_type.is_pair() { + head.clone() + } else { + convert_type_to_data(head.clone(), &arg_type) + }) + .apply(tail.clone()) + } else { + unreachable!("mk_cons has two arguments."); + } + } + DefaultFunction::ChooseUnit | DefaultFunction::IfThenElse | DefaultFunction::ChooseList diff --git a/crates/aiken-project/src/tests/gen_uplc.rs b/crates/aiken-project/src/tests/gen_uplc.rs index 0019836e4..ff20150a0 100644 --- a/crates/aiken-project/src/tests/gen_uplc.rs +++ b/crates/aiken-project/src/tests/gen_uplc.rs @@ -7025,3 +7025,94 @@ fn qualified_prelude_functions() { false, ) } + +#[test] +fn mk_cons_direct_invoke_1() { + let src = r#" + use aiken/builtin + + test mk_cons_1() { + builtin.cons_list(1, []) == [1] + } + "#; + + assert_uplc( + src, + Term::equals_data() + .apply( + Term::list_data().apply( + Term::mk_cons() + .apply(Term::data(Data::integer(1.into()))) + .apply(Term::empty_list()), + ), + ) + .apply(Term::data(Data::list(vec![Data::integer(1.into())]))), + false, + ) +} + +#[test] +fn mk_cons_direct_invoke_2() { + let src = r#" + use aiken/builtin.{cons_list} + + test mk_cons_2() { + cons_list(Some(42), [None]) == [Some(42), None] + } + "#; + + let none = Data::constr(1, Vec::new()); + let some = Data::constr(0, vec![Data::integer(42.into())]); + + assert_uplc( + src, + Term::equals_data() + .apply( + Term::list_data().apply(Term::mk_cons().apply(Term::data(some.clone())).apply( + Term::Constant( + Constant::ProtoList(Type::Data, vec![Constant::Data(none.clone())]).into(), + ), + )), + ) + .apply(Term::data(Data::list(vec![some, none]))), + false, + ) +} + +#[test] +fn mk_cons_direct_invoke_3() { + let src = r#" + use aiken/builtin.{cons_list, i_data, mk_nil_pair_data} + + test mk_cons_3() { + cons_list(Pair(i_data(1), i_data(1)), mk_nil_pair_data()) == [ + Pair(i_data(1), i_data(1)), + ] + } + "#; + + assert_uplc( + src, + Term::equals_data() + .apply( + Term::map_data().apply( + Term::mk_cons() + .apply(Term::Constant( + Constant::ProtoPair( + Type::Data, + Type::Data, + Constant::Data(Data::integer(1.into())).into(), + Constant::Data(Data::integer(1.into())).into(), + ) + .into(), + )) + .apply(Term::mk_nil_pair_data().apply(Term::unit())), + ), + ) + .apply(Term::data(Data::map(vec![( + Data::integer(1.into()), + Data::integer(1.into()), + )]))), + false, + ) +}