fix: found various unify and type issues while running tests

This commit is contained in:
microproofs 2024-03-29 13:00:39 -04:00 committed by Kasey
parent fd226be51f
commit 26f68c2fb4
5 changed files with 537 additions and 149 deletions

View File

@ -372,6 +372,15 @@ impl<'a> CodeGenerator<'a> {
}, },
.. ..
} => { } => {
if tipo.is_pair() {
assert!(args.len() == 2);
let arg1 = self.build(&args[0].value, module_build_name, &[]);
let arg2 = self.build(&args[1].value, module_build_name, &[]);
AirTree::pair(arg1, arg2, tipo.clone())
} else {
let data_type = lookup_data_type_by_tipo(&self.data_types, tipo) let data_type = lookup_data_type_by_tipo(&self.data_types, tipo)
.expect("Creating a record with no record definition."); .expect("Creating a record with no record definition.");
@ -399,6 +408,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::create_constr(constr_index, constr_tipo.clone(), constr_args) AirTree::create_constr(constr_index, constr_tipo.clone(), constr_args)
} }
}
TypedExpr::Var { TypedExpr::Var {
constructor: constructor:
@ -5420,12 +5430,13 @@ impl<'a> CodeGenerator<'a> {
match (extract_constant(&fst), extract_constant(&snd)) { match (extract_constant(&fst), extract_constant(&snd)) {
(Some(fst), Some(snd)) => { (Some(fst), Some(snd)) => {
let mut pair_fields = builder::convert_constants_to_data(vec![fst, snd]);
let term = Term::Constant( let term = Term::Constant(
UplcConstant::ProtoPair( UplcConstant::ProtoPair(
UplcType::Data, UplcType::Data,
UplcType::Data, UplcType::Data,
fst.clone(), pair_fields.remove(0).into(),
snd.clone(), pair_fields.remove(0).into(),
) )
.into(), .into(),
); );

View File

@ -695,14 +695,22 @@ pub fn pattern_has_conditions(
Pattern::Constructor { Pattern::Constructor {
arguments, tipo, .. arguments, tipo, ..
} => { } => {
let data_type = if tipo.is_pair()
lookup_data_type_by_tipo(data_types, tipo).expect("Data type not found"); || (tipo.is_function() && tipo.return_type().map(|t| t.is_pair()).unwrap_or(false))
{
arguments
.iter()
.any(|arg| pattern_has_conditions(&arg.value, data_types))
} else {
let data_type = lookup_data_type_by_tipo(data_types, tipo)
.unwrap_or_else(|| panic!("Data type not found: {:#?}", tipo));
data_type.constructors.len() > 1 data_type.constructors.len() > 1
|| arguments || arguments
.iter() .iter()
.any(|arg| pattern_has_conditions(&arg.value, data_types)) .any(|arg| pattern_has_conditions(&arg.value, data_types))
} }
}
Pattern::Assign { pattern, .. } => pattern_has_conditions(pattern, data_types), Pattern::Assign { pattern, .. } => pattern_has_conditions(pattern, data_types),
Pattern::Var { .. } | Pattern::Discard { .. } => false, Pattern::Var { .. } | Pattern::Discard { .. } => false,
} }

View File

@ -1495,8 +1495,7 @@ impl<'a> Environment<'a> {
.map_err(|e| e.flip_unify()); .map_err(|e| e.flip_unify());
} }
match (lhs.deref(), rhs.deref()) { match lhs.deref() {
(
Type::App { Type::App {
module: m1, module: m1,
name: n1, name: n1,
@ -1504,16 +1503,17 @@ impl<'a> Environment<'a> {
public: _, public: _,
contains_opaque: _, contains_opaque: _,
alias: _, alias: _,
}, } => {
Type::App { if let Type::App {
module: m2, module: m2,
name: n2, name: n2,
args: args2, args: args2,
public: _, public: _,
contains_opaque: _, contains_opaque: _,
alias: _, alias: _,
}, } = rhs.deref()
) if m1 == m2 && n1 == n2 && args1.len() == args2.len() => { {
if m1 == m2 && n1 == n2 && args1.len() == args2.len() {
for (a, b) in args1.iter().zip(args2) { for (a, b) in args1.iter().zip(args2) {
unify_enclosed_type( unify_enclosed_type(
lhs.clone(), lhs.clone(),
@ -1522,18 +1522,36 @@ impl<'a> Environment<'a> {
)?; )?;
} }
Ok(()) Ok(())
} else {
Err(Error::CouldNotUnify {
location,
expected: lhs.clone(),
given: rhs.clone(),
situation: None,
rigid_type_names: HashMap::new(),
})
}
} else {
Err(Error::CouldNotUnify {
location,
expected: lhs.clone(),
given: rhs.clone(),
situation: None,
rigid_type_names: HashMap::new(),
})
}
} }
(
Type::Tuple { Type::Tuple {
elems: elems1, elems: elems1,
alias: _, alias: _,
}, } => {
Type::Tuple { if let Type::Tuple {
elems: elems2, elems: elems2,
alias: _, alias: _,
}, } = rhs.deref()
) if elems1.len() == elems2.len() => { {
if elems1.len() == elems2.len() {
for (a, b) in elems1.iter().zip(elems2) { for (a, b) in elems1.iter().zip(elems2) {
unify_enclosed_type( unify_enclosed_type(
lhs.clone(), lhs.clone(),
@ -1542,20 +1560,38 @@ impl<'a> Environment<'a> {
)?; )?;
} }
Ok(()) Ok(())
} else {
Err(Error::CouldNotUnify {
location,
expected: lhs.clone(),
given: rhs.clone(),
situation: None,
rigid_type_names: HashMap::new(),
})
}
} else {
Err(Error::CouldNotUnify {
location,
expected: lhs.clone(),
given: rhs.clone(),
situation: None,
rigid_type_names: HashMap::new(),
})
}
} }
(
Type::Fn { Type::Fn {
args: args1, args: args1,
ret: retrn1, ret: retrn1,
alias: _, alias: _,
}, } => {
Type::Fn { if let Type::Fn {
args: args2, args: args2,
ret: retrn2, ret: retrn2,
alias: _, alias: _,
}, } = rhs.deref()
) if args1.len() == args2.len() => { {
if args1.len() == args2.len() {
for (a, b) in args1.iter().zip(args2) { for (a, b) in args1.iter().zip(args2) {
self.unify(a.clone(), b.clone(), location, allow_cast) self.unify(a.clone(), b.clone(), location, allow_cast)
.map_err(|_| Error::CouldNotUnify { .map_err(|_| Error::CouldNotUnify {
@ -1574,15 +1610,57 @@ impl<'a> Environment<'a> {
situation: None, situation: None,
rigid_type_names: HashMap::new(), rigid_type_names: HashMap::new(),
}) })
} } else {
Err(Error::CouldNotUnify {
_ => Err(Error::CouldNotUnify {
location, location,
expected: lhs.clone(), expected: lhs.clone(),
given: rhs.clone(), given: rhs.clone(),
situation: None, situation: None,
rigid_type_names: HashMap::new(), rigid_type_names: HashMap::new(),
}), })
}
} else {
Err(Error::CouldNotUnify {
location,
expected: lhs.clone(),
given: rhs.clone(),
situation: None,
rigid_type_names: HashMap::new(),
})
}
}
Type::Pair { fst, snd, alias: _ } => {
if let Type::Pair {
fst: fst2,
snd: snd2,
alias: _,
} = rhs.deref()
{
unify_enclosed_type(
lhs.clone(),
rhs.clone(),
self.unify(fst.clone(), fst2.clone(), location, false),
)?;
unify_enclosed_type(
lhs.clone(),
rhs.clone(),
self.unify(snd.clone(), snd2.clone(), location, false),
)?;
Ok(())
} else {
Err(Error::CouldNotUnify {
location,
expected: lhs.clone(),
given: rhs.clone(),
situation: None,
rigid_type_names: HashMap::new(),
})
}
}
Type::Var { .. } => unreachable!(),
} }
} }
@ -1801,7 +1879,7 @@ fn unify_unbound_type(tipo: Rc<Type>, own_id: u64, location: Span) -> Result<(),
Ok(()) Ok(())
} }
Type::Pair { fst, snd, .. } => { Type::Pair { fst, snd, alias: _ } => {
unify_unbound_type(fst.clone(), own_id, location)?; unify_unbound_type(fst.clone(), own_id, location)?;
unify_unbound_type(snd.clone(), own_id, location)?; unify_unbound_type(snd.clone(), own_id, location)?;

View File

@ -3,7 +3,8 @@ use std::{collections::BTreeMap, iter, ops::Deref};
use itertools::Itertools; use itertools::Itertools;
use crate::{ use crate::{
ast, builtins, ast,
builtins::{self, PAIR},
tipo::{self, environment::Environment, error::Error}, tipo::{self, environment::Environment, error::Error},
}; };
@ -563,6 +564,8 @@ pub(super) fn simplify(
constructor: super::PatternConstructor::Record { name, .. }, constructor: super::PatternConstructor::Record { name, .. },
.. ..
} => { } => {
let (empty, pair) = (&"".to_string(), &PAIR.to_string());
let (module, type_name, arity) = match tipo.deref() { let (module, type_name, arity) = match tipo.deref() {
tipo::Type::App { tipo::Type::App {
name: type_name, name: type_name,
@ -575,11 +578,13 @@ pub(super) fn simplify(
module, module,
.. ..
} => (module, type_name, args.len()), } => (module, type_name, args.len()),
tipo::Type::Pair { .. } => (empty, pair, 2),
_ => { _ => {
unreachable!("ret should be a Type::App") unreachable!("ret should be a Type::App or Type::Pair")
} }
}, },
_ => unreachable!("tipo should be a Type::App"), tipo::Type::Pair { .. } => (empty, pair, 2),
_ => unreachable!("tipo should be a Type::App or Type::Pair"),
}; };
let alts = environment.get_constructors_for_type(module, type_name, *location)?; let alts = environment.get_constructors_for_type(module, type_name, *location)?;

View File

@ -680,10 +680,10 @@ fn acceptance_test_6_if_else() {
} }
#[test] #[test]
fn acceptance_test_6_equals() { fn acceptance_test_6_equals_pair() {
let src = r#" let src = r#"
test foo() { test foo() {
(1, []) == (1, []) Pair(1, []) == Pair(1, [])
} }
"#; "#;
@ -725,7 +725,46 @@ fn acceptance_test_6_equals() {
} }
#[test] #[test]
fn acceptance_test_7_unzip() { fn acceptance_test_6_equals_tuple() {
let src = r#"
test foo() {
(1, []) == (1, [])
}
"#;
assert_uplc(
src,
Term::equals_data()
.apply(
Term::list_data().apply(Term::Constant(
Constant::ProtoList(
Type::Data,
vec![
Constant::Data(Data::integer(1.into())),
Constant::Data(Data::list(vec![])),
],
)
.into(),
)),
)
.apply(
Term::list_data().apply(Term::Constant(
Constant::ProtoList(
Type::Data,
vec![
Constant::Data(Data::integer(1.into())),
Constant::Data(Data::list(vec![])),
],
)
.into(),
)),
),
false,
);
}
#[test]
fn acceptance_test_7_unzip_tuple() {
let src = r#" let src = r#"
pub fn unzip(xs: List<(a, b)>) -> (List<a>, List<b>) { pub fn unzip(xs: List<(a, b)>) -> (List<a>, List<b>) {
when xs is { when xs is {
@ -744,6 +783,127 @@ fn acceptance_test_7_unzip() {
} }
"#; "#;
assert_uplc(
src,
Term::equals_data()
.apply(
Term::list_data().apply(
Term::var("unzip")
.lambda("unzip")
.apply(Term::var("unzip").apply(Term::var("unzip")))
.lambda("unzip")
.apply(
Term::var("xs")
.delayed_choose_list(
Term::list_values(vec![
Constant::Data(Data::list(vec![])),
Constant::Data(Data::list(vec![])),
]),
Term::mk_cons()
.apply(
Term::list_data().apply(
Term::mk_cons()
.apply(Term::i_data().apply(Term::var("a")))
.apply(Term::var("a_tail")),
),
)
.apply(
Term::mk_cons()
.apply(
Term::list_data().apply(
Term::mk_cons()
.apply(
Term::b_data()
.apply(Term::var("b")),
)
.apply(Term::var("b_tail")),
),
)
.apply(Term::empty_list()),
)
.lambda("b_tail")
.apply(Term::unlist_data().apply(Term::head_list().apply(
Term::tail_list().apply(Term::var("tail_tuple")),
)))
.lambda("a_tail")
.apply(Term::unlist_data().apply(
Term::head_list().apply(Term::var("tail_tuple")),
))
.lambda("tail_tuple")
.apply(
Term::var("unzip")
.apply(Term::var("unzip"))
.apply(Term::var("rest")),
)
.lambda("b")
.apply(Term::un_b_data().apply(Term::head_list().apply(
Term::tail_list().apply(Term::var("head_tuple")),
)))
.lambda("a")
.apply(Term::un_i_data().apply(
Term::head_list().apply(Term::var("head_tuple")),
))
.lambda("rest")
.apply(Term::tail_list().apply(Term::var("xs")))
.lambda("head_tuple")
.apply(
Term::unlist_data()
.apply(Term::head_list().apply(Term::var("xs"))),
),
)
.lambda("xs")
.lambda("unzip"),
)
.apply(Term::var("x")),
),
)
.apply(Term::list_data().apply(Term::list_values(vec![
Constant::Data(Data::list(vec![
Data::integer(3.into()),
Data::integer(4.into()),
])),
Constant::Data(Data::list(vec![
Data::bytestring(vec![85]),
Data::bytestring(vec![119, 153]),
])),
])))
.lambda("x")
.apply(Term::list_values(vec![
Constant::Data(Data::list(vec![
Data::integer(3.into()),
Data::bytestring(vec![85]),
])),
Constant::Data(Data::list(vec![
Data::integer(4.into()),
Data::bytestring(vec![119, 153]),
])),
])),
false,
);
}
#[test]
fn acceptance_test_7_unzip_pair() {
let src = r#"
type Map<a,b> = List<Pair<a,b>>
pub fn unzip(xs: Map<a, b>) -> Pair<List<a>, List<b>> {
when xs is {
[] -> Pair([], [])
[Pair(a, b), ..rest] -> {
let Pair(a_tail, b_tail) = unzip(rest)
Pair([a, ..a_tail], [b, ..b_tail])
}
}
}
test unzip1() {
let x = [Pair(3, #"55"), Pair(4, #"7799")]
unzip(x) == Pair([3, 4], [#"55", #"7799"])
}
"#;
assert_uplc( assert_uplc(
src, src,
Term::equals_data() Term::equals_data()
@ -1414,7 +1574,7 @@ fn acceptance_test_14_list_creation() {
fn acceptance_test_15_zero_arg() { fn acceptance_test_15_zero_arg() {
let src = r#" let src = r#"
pub opaque type Map<key, value> { pub opaque type Map<key, value> {
inner: List<(key, value)>, inner: List<Pair<key, value>>,
} }
pub fn new() { pub fn new() {
@ -1956,15 +2116,18 @@ fn acceptance_test_22_filter_map() {
#[test] #[test]
fn acceptance_test_23_to_list() { fn acceptance_test_23_to_list() {
let src = r#" let src = r#"
pub type Map<key, value> =
List<Pair<key, value>>
pub opaque type AssocList<key, value> { pub opaque type AssocList<key, value> {
inner: List<(key, value)>, inner: Map<key, value>,
} }
pub fn new() -> AssocList<key, value> { pub fn new() -> AssocList<key, value> {
AssocList { inner: [] } AssocList { inner: [] }
} }
pub fn to_list(m: AssocList<key, value>) -> List<(key, value)> { pub fn to_list(m: AssocList<key, value>) -> Map<key, value> {
m.inner m.inner
} }
@ -1976,15 +2139,15 @@ fn acceptance_test_23_to_list() {
AssocList { inner: do_insert(m.inner, k, v) } AssocList { inner: do_insert(m.inner, k, v) }
} }
fn do_insert(elems: List<(key, value)>, k: key, v: value) -> List<(key, value)> { fn do_insert(elems: Map<key, value>, k: key, v: value) -> Map<key, value> {
when elems is { when elems is {
[] -> [] ->
[(k, v)] [Pair(k, v)]
[(k2, v2), ..rest] -> [Pair { fst: k2, snd: v2 }, ..rest] ->
if k == k2 { if k == k2 {
[(k, v), ..rest] [Pair(k, v), ..rest]
} else { } else {
[(k2, v2), ..do_insert(rest, k, v)] [Pair(k2, v2), ..do_insert(rest, k, v)]
} }
} }
} }
@ -1996,7 +2159,7 @@ fn acceptance_test_23_to_list() {
} }
test to_list_2() { test to_list_2() {
to_list(fixture_1()) == [("foo", 42), ("bar", 14)] to_list(fixture_1()) == [Pair("foo", 42), Pair("bar", 14)]
} }
"#; "#;
@ -2036,7 +2199,7 @@ fn acceptance_test_23_to_list() {
} }
#[test] #[test]
fn acceptance_test_24_map2() { fn acceptance_test_24_map_pair() {
let src = r#" let src = r#"
pub fn map2( pub fn map2(
opt_a: Option<a>, opt_a: Option<a>,
@ -2057,7 +2220,7 @@ fn acceptance_test_24_map2() {
} }
test map2_3() { test map2_3() {
map2(Some(14), Some(42), fn(a, b) { (a, b) }) == Some((14, 42)) map2(Some(14), Some(42), fn(a, b) { Pair(a, b) }) == Some(Pair(14, 42))
} }
"#; "#;
@ -2178,6 +2341,129 @@ fn acceptance_test_24_map2() {
); );
} }
#[test]
fn acceptance_test_24_map2() {
let src = r#"
pub fn map2(
opt_a: Option<a>,
opt_b: Option<b>,
f: fn(a, b) -> result,
) -> Option<result> {
when opt_a is {
None ->
None
Some(a) ->
when opt_b is {
None ->
None
Some(b) ->
Some(f(a, b))
}
}
}
test map2_3() {
map2(Some(14), Some(42), fn(a, b) { (a, b) }) == Some((14, 42))
}
"#;
assert_uplc(
src,
Term::equals_data()
.apply(
Term::var("map2")
.lambda("map2")
.apply(
Term::equals_integer()
.apply(Term::integer(1.into()))
.apply(Term::var("opt_a_index"))
.delayed_if_then_else(
Term::Constant(Constant::Data(Data::constr(1, vec![])).into()),
Term::equals_integer()
.apply(Term::integer(1.into()))
.apply(Term::var("opt_b_index"))
.delayed_if_then_else(
Term::Constant(
Constant::Data(Data::constr(1, vec![])).into(),
),
Term::constr_data()
.apply(Term::integer(0.into()))
.apply(
Term::mk_cons()
.apply(
Term::list_data().apply(
Term::var("f")
.apply(Term::var("a"))
.apply(Term::var("b")),
),
)
.apply(Term::empty_list()),
)
.lambda("b")
.apply(Term::un_i_data().apply(
Term::head_list().apply(Term::var("opt_b_fields")),
))
.lambda("opt_b_fields")
.apply(
Term::var(CONSTR_FIELDS_EXPOSER)
.apply(Term::var("opt_b")),
),
)
.lambda("opt_b_index")
.apply(
Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("opt_b")),
)
.lambda("a")
.apply(
Term::un_i_data().apply(
Term::head_list().apply(Term::var("opt_a_fields")),
),
)
.lambda("opt_a_fields")
.apply(
Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("opt_a")),
),
)
.lambda("opt_a_index")
.apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("opt_a")))
.lambda("f")
.lambda("opt_b")
.lambda("opt_a"),
)
.apply(Term::Constant(
Constant::Data(Data::constr(0, vec![Data::integer(14.into())])).into(),
))
.apply(Term::Constant(
Constant::Data(Data::constr(0, vec![Data::integer(42.into())])).into(),
))
.apply(
Term::mk_cons()
.apply(Term::i_data().apply(Term::var("a")))
.apply(
Term::mk_cons()
.apply(Term::i_data().apply(Term::var("b")))
.apply(Term::empty_list()),
)
.lambda("b")
.lambda("a"),
),
)
.apply(Term::Constant(
Constant::Data(Data::constr(
0,
vec![Data::list(vec![
Data::integer(14.into()),
Data::integer(42.into()),
])],
))
.into(),
))
.constr_fields_exposer()
.constr_index_exposer(),
false,
);
}
#[test] #[test]
fn acceptance_test_25_void_equal() { fn acceptance_test_25_void_equal() {
let src = r#" let src = r#"