Support mk_cons builtin

While this builtin is readily available through the Aiken syntax
  `[head, ..tail]`, there's no reason to not support its builtin form
  even though we may not encourage its usage. For completeness and to
  avoid bad surprises, it is now supported.

  Fixes #964.
This commit is contained in:
KtorZ 2024-07-27 11:30:00 +02:00 committed by Kasey
parent bf5a406ffb
commit 1c58da4d86
3 changed files with 122 additions and 56 deletions

View File

@ -4060,61 +4060,17 @@ impl<'a> CodeGenerator<'a> {
ValueConstructorVariant::ModuleConstant { .. } => { ValueConstructorVariant::ModuleConstant { .. } => {
unreachable!("{:#?}, {}", constructor, name) 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<Name> = (*builtin).into();
term = builder::apply_builtin_forces(term, builtin.force_count());
term
}
};
Some(term)
}
ValueConstructorVariant::ModuleFn { ValueConstructorVariant::ModuleFn {
name: func_name, name: func_name,
module, 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(&( if let Some((names, index, cyclic_name)) = self.cyclic_functions.get(&(
FunctionAccessKey { FunctionAccessKey {
module_name: module.clone(), module_name: module.clone(),
@ -4514,21 +4470,19 @@ impl<'a> CodeGenerator<'a> {
| DefaultFunction::Trace | DefaultFunction::Trace
| DefaultFunction::ChooseList | DefaultFunction::ChooseList
| DefaultFunction::ChooseData | DefaultFunction::ChooseData
| DefaultFunction::MkCons
| DefaultFunction::UnConstrData => { | DefaultFunction::UnConstrData => {
builder::special_case_builtin(&func, count, arg_vec) builder::special_case_builtin(&func, tipo, count, arg_vec)
} }
DefaultFunction::FstPair | DefaultFunction::SndPair => { DefaultFunction::FstPair | DefaultFunction::SndPair => {
builder::undata_builtin(&func, count, ret_tipo, arg_vec) builder::undata_builtin(&func, count, ret_tipo, arg_vec)
} }
DefaultFunction::HeadList if !tipo.is_pair() => { DefaultFunction::HeadList if !tipo.is_pair() => {
builder::undata_builtin(&func, count, ret_tipo, arg_vec) builder::undata_builtin(&func, count, ret_tipo, arg_vec)
} }
DefaultFunction::MkPairData => {
DefaultFunction::MkCons | DefaultFunction::MkPairData => {
unimplemented!( 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"
) )
} }
_ => { _ => {

View File

@ -1554,6 +1554,7 @@ pub fn to_data_builtin(
pub fn special_case_builtin( pub fn special_case_builtin(
func: &DefaultFunction, func: &DefaultFunction,
tipo: Rc<Type>,
count: usize, count: usize,
mut args: Vec<Term<Name>>, mut args: Vec<Term<Name>>,
) -> Term<Name> { ) -> Term<Name> {
@ -1564,6 +1565,26 @@ pub fn special_case_builtin(
term.lambda("_").apply(unit) 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::ChooseUnit
| DefaultFunction::IfThenElse | DefaultFunction::IfThenElse
| DefaultFunction::ChooseList | DefaultFunction::ChooseList

View File

@ -6471,3 +6471,94 @@ fn qualified_prelude_functions() {
false, 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,
)
}