fix: Issue where using var pattern in a when was passing the constr index instead of the constr
fix: Issue where expecting on a list had unexpected behaviors based on list length
This commit is contained in:
microproofs 2023-06-13 10:55:21 -04:00 committed by Kasey
parent 37b2f0c239
commit af36b5ac77
2 changed files with 514 additions and 12 deletions

View File

@ -998,7 +998,24 @@ impl<'a> CodeGenerator<'a> {
var_stack.local_var(
tipo.clone().into(),
clause_properties.original_subject_name(),
match clause_properties {
ClauseProperties::ConstrClause {
clause_var_name,
needs_constr_var,
..
} => {
*needs_constr_var = true;
clause_var_name
}
ClauseProperties::ListClause {
original_subject_name,
..
} => original_subject_name,
ClauseProperties::TupleClause {
original_subject_name,
..
} => original_subject_name,
},
);
pattern_stack.let_assignment(name, var_stack);
@ -1010,7 +1027,24 @@ impl<'a> CodeGenerator<'a> {
new_stack.local_var(
tipo.clone().into(),
clause_properties.original_subject_name(),
match clause_properties {
ClauseProperties::ConstrClause {
clause_var_name,
needs_constr_var,
..
} => {
*needs_constr_var = true;
clause_var_name
}
ClauseProperties::ListClause {
original_subject_name,
..
} => original_subject_name,
ClauseProperties::TupleClause {
original_subject_name,
..
} => original_subject_name,
},
);
let mut let_stack = pattern_stack.empty_with_scope();
@ -1687,7 +1721,28 @@ impl<'a> CodeGenerator<'a> {
pattern_stack.merge(expect_stack);
}
}
Pattern::Assign { .. } => todo!("Assign not yet implemented"),
Pattern::Assign { name, pattern, .. } => {
let mut inner_value_stack = pattern_stack.empty_with_scope();
inner_value_stack.var(
ValueConstructor::public(
tipo.clone().into(),
ValueConstructorVariant::LocalVariable {
location: Span::empty(),
},
),
name,
"",
);
pattern_stack.let_assignment(name, value_stack);
self.assignment(
pattern,
pattern_stack,
inner_value_stack,
tipo,
assignment_properties,
);
}
Pattern::Discard { .. } => {
pattern_stack.let_assignment("_", value_stack);
}
@ -1827,7 +1882,7 @@ impl<'a> CodeGenerator<'a> {
tipo.clone().into(),
names,
tail.is_some(),
true,
!tail.is_some(),
value_stack,
);
} else {
@ -2074,18 +2129,21 @@ impl<'a> CodeGenerator<'a> {
format!("__tail_{}", self.id_gen.next())
};
self.expect_type(
inner_list_type,
&mut tail_stack,
&name,
&mut IndexMap::new(),
);
self.expect_type(tipo, &mut tail_stack, &name, &mut IndexMap::new());
expect_list_stacks.push(tail_stack);
if tail.is_some() {
names.push(name);
}
expect_stack.list_accessor(tipo.clone().into(), names, true, false, value_stack);
expect_stack.list_accessor(
tipo.clone().into(),
names,
tail.is_some(),
!tail.is_some(),
value_stack,
);
expect_stack.merge_children(expect_list_stacks);
}

View File

@ -2919,3 +2919,447 @@ fn list_fields_unwrap() {
false,
);
}
#[test]
fn acceptance_test_23_to_list() {
let src = r#"
pub opaque type AssocList<key, value> {
inner: List<(key, value)>,
}
pub fn new() -> AssocList<key, value> {
AssocList { inner: [] }
}
pub fn to_list(m: AssocList<key, value>) -> List<(key, value)> {
m.inner
}
pub fn insert(
in m: AssocList<key, value>,
key k: key,
value v: value,
) -> AssocList<key, value> {
AssocList { inner: do_insert(m.inner, k, v) }
}
fn do_insert(elems: List<(key, value)>, k: key, v: value) -> List<(key, value)> {
when elems is {
[] ->
[(k, v)]
[(k2, v2), ..rest] ->
if k == k2 {
[(k, v), ..rest]
} else {
[(k2, v2), ..do_insert(rest, k, v)]
}
}
}
fn fixture_1() {
new()
|> insert("foo", 42)
|> insert("bar", 14)
}
test to_list_2() {
to_list(fixture_1()) == [("foo", 42), ("bar", 14)]
}
"#;
assert_uplc(
src,
Term::equals_data()
.apply(Term::map_data().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::map_data().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(),
),
]))),
false,
);
}
#[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_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_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::mk_cons()
.apply(
Term::fst_pair().apply(
Term::var("pair"),
),
)
.apply(
Term::mk_cons()
.apply(
Term::snd_pair()
.apply(
Term::var(
"pair",
),
),
)
.apply(
Term::empty_list(),
),
),
)
.lambda("pair")
.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_pair_data()
.apply(Term::i_data().apply(Term::var("a")))
.apply(Term::i_data().apply(Term::var("b")))
.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_get_field()
.constr_fields_exposer()
.constr_index_exposer(),
false,
);
}
#[test]
fn foldl_type_mismatch() {
let src = r#"
type Address {
payment_credential: ByteArray,
stake_credential: Option<ByteArray>,
}
type Output {
address: Address,
value: List<Int>,
datum: Option<Int>,
reference_script: Option<Int>,
}
pub fn foldl(self: List<a>, with: fn(a, b) -> b, zero: b) -> b {
when self is {
[] -> zero
[x, ..xs] -> foldl(xs, with, with(x, zero))
}
}
test hi() {
let addr1 = Address { payment_credential: #"adff", stake_credential: None }
let out =
Output { address: addr1, value: [], datum: None, reference_script: None }
let outputs: List<Output> =
[out, out, out]
let cry =
foldl(
outputs,
fn(o: Output, mb_b: Option<Output>) -> Option<Output> {
when mb_b is {
None ->
if o.address == addr1 {
Some(o)
} else {
None
}
otherwise -> otherwise
}
},
None,
)
cry == cry
}
"#;
assert_uplc(src, Term::equals_data(), false);
}
#[test]
fn expect_head_discard_tail() {
let src = r#"
test hi() {
let a = [1, 2, 3]
expect [h, ..] = a
h == h
}
"#;
assert_uplc(
src,
Term::equals_integer()
.apply(Term::var("h"))
.apply(Term::var("h"))
.lambda("h")
.apply(Term::un_i_data().apply(Term::head_list().apply(Term::var("a"))))
.lambda("a")
.apply(Term::list_values(vec![
Constant::Data(Data::integer(1.into())),
Constant::Data(Data::integer(2.into())),
Constant::Data(Data::integer(3.into())),
])),
false,
);
}
#[test]
fn expect_head_no_tail() {
let src = r#"
test hi() {
let a = [1, 2, 3]
expect [h] = a
h == h
}
"#;
assert_uplc(
src,
Term::tail_list()
.apply(Term::var("a"))
.delayed_choose_list(
Term::equals_integer()
.apply(Term::var("h"))
.apply(Term::var("h")),
Term::Error.trace(Term::string(
"List/Tuple/Constr contains more items than expected",
)),
)
.lambda("h")
.apply(Term::un_i_data().apply(Term::head_list().apply(Term::var("a"))))
.lambda("a")
.apply(Term::list_values(vec![
Constant::Data(Data::integer(1.into())),
Constant::Data(Data::integer(2.into())),
Constant::Data(Data::integer(3.into())),
])),
true,
);
}
#[test]
fn expect_head_cast_data_no_tail() {
let src = r#"
test hi() {
let a: Data = [1, 2, 3]
expect [h]: List<Int> = a
h == h
}
"#;
assert_uplc(
src,
Term::tail_list()
.apply(Term::var("unwrap_a"))
.delayed_choose_list(
Term::equals_integer()
.apply(Term::var("h"))
.apply(Term::var("h")),
Term::Error.trace(Term::string(
"List/Tuple/Constr contains more items than expected",
)),
)
.lambda("h")
.apply(Term::un_i_data().apply(Term::head_list().apply(Term::var("unwrap_a"))))
.lambda("unwrap_a")
.apply(Term::unlist_data().apply(Term::var("a")))
.lambda("a")
.apply(Term::list_data().apply(Term::list_values(vec![
Constant::Data(Data::integer(1.into())),
Constant::Data(Data::integer(2.into())),
Constant::Data(Data::integer(3.into())),
]))),
true,
);
}
#[test]
fn expect_head_cast_data_with_tail() {
let src = r#"
test hi() {
let a: Data = [1, 2, 3]
expect [h, j, ..]: List<Int> = a
h == h
}
"#;
assert_uplc(
src,
Term::equals_integer()
.apply(Term::var("h"))
.apply(Term::var("h"))
.lambda("_")
.apply(
Term::var("expect_on_list")
.lambda("expect_on_list")
.apply(Term::var("expect_on_list").apply(Term::var("expect_on_list")))
.lambda("expect_on_list")
.apply(
Term::var("list_to_check")
.delayed_choose_list(
Term::unit(),
Term::var("expect_on_list")
.apply(Term::var("expect_on_list"))
.apply(Term::tail_list().apply(Term::var("list_to_check")))
.apply(Term::var("check_with"))
.lambda("_")
.apply(Term::var("check_with").apply(
Term::head_list().apply(Term::var("list_to_check")),
)),
)
.lambda("check_with")
.lambda("list_to_check")
.lambda("expect_on_list"),
)
.apply(Term::var("tail_2"))
.apply(
Term::unit()
.lambda("_")
.apply(Term::un_i_data().apply(Term::var("list_item")))
.lambda("list_item"),
),
)
.lambda("tail_2")
.apply(Term::tail_list().apply(Term::var("tail_1")))
.lambda("j")
.apply(Term::un_i_data().apply(Term::head_list().apply(Term::var("tail_1"))))
.lambda("tail_1")
.apply(Term::tail_list().apply(Term::var("unwrap_a")))
.lambda("h")
.apply(Term::un_i_data().apply(Term::head_list().apply(Term::var("unwrap_a"))))
.lambda("unwrap_a")
.apply(Term::unlist_data().apply(Term::var("a")))
.lambda("a")
.apply(Term::list_data().apply(Term::list_values(vec![
Constant::Data(Data::integer(1.into())),
Constant::Data(Data::integer(2.into())),
Constant::Data(Data::integer(3.into())),
]))),
false,
);
}