Skip to content

Commit

Permalink
Support mk_cons builtin
Browse files Browse the repository at this point in the history
  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.
  • Loading branch information
KtorZ committed Jul 27, 2024
1 parent b8bb480 commit 8673c56
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 56 deletions.
66 changes: 10 additions & 56 deletions crates/aiken-lang/src/gen_uplc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Name> = (*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(),
Expand Down Expand Up @@ -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"
)
}
_ => {
Expand Down
21 changes: 21 additions & 0 deletions crates/aiken-lang/src/gen_uplc/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1642,6 +1642,7 @@ pub fn to_data_builtin(

pub fn special_case_builtin(
func: &DefaultFunction,
tipo: Rc<Type>,
count: usize,
mut args: Vec<Term<Name>>,
) -> Term<Name> {
Expand All @@ -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
Expand Down
91 changes: 91 additions & 0 deletions crates/aiken-project/src/tests/gen_uplc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
}

0 comments on commit 8673c56

Please sign in to comment.