mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Merge pull request #4209 from roc-lang/impl-tag-discriminant
Derive `Hash` implementations for tag unions
This commit is contained in:
commit
83b64c4fb3
13 changed files with 844 additions and 106 deletions
|
@ -294,7 +294,8 @@ fn tag_one_label_zero_args() {
|
|||
\#Derived.bytes, #Derived.fmt ->
|
||||
Encode.appendWith
|
||||
#Derived.bytes
|
||||
(when #Derived.tag is A -> Encode.tag "A" [])
|
||||
(when #Derived.tag is
|
||||
A -> Encode.tag "A" [])
|
||||
#Derived.fmt
|
||||
"###
|
||||
)
|
||||
|
|
|
@ -27,6 +27,20 @@ test_key_eq! {
|
|||
v!({ c: v!(U8), a: v!(U8), b: v!(U8), })
|
||||
explicit_empty_record_and_implicit_empty_record:
|
||||
v!(EMPTY_RECORD), v!({})
|
||||
|
||||
same_tag_union:
|
||||
v!([ A v!(U8) v!(STR), B v!(STR) ]), v!([ A v!(U8) v!(STR), B v!(STR) ])
|
||||
same_tag_union_tags_diff_types:
|
||||
v!([ A v!(U8) v!(U8), B v!(U8) ]), v!([ A v!(STR) v!(STR), B v!(STR) ])
|
||||
same_tag_union_tags_any_order:
|
||||
v!([ A v!(U8) v!(U8), B v!(U8), C ]), v!([ C, B v!(STR), A v!(STR) v!(STR) ])
|
||||
explicit_empty_tag_union_and_implicit_empty_tag_union:
|
||||
v!(EMPTY_TAG_UNION), v!([])
|
||||
|
||||
same_recursive_tag_union:
|
||||
v!([ Nil, Cons v!(^lst)] as lst), v!([ Nil, Cons v!(^lst)] as lst)
|
||||
same_tag_union_and_recursive_tag_union_fields:
|
||||
v!([ Nil, Cons v!(STR)]), v!([ Nil, Cons v!(^lst)] as lst)
|
||||
}
|
||||
|
||||
test_key_neq! {
|
||||
|
@ -36,6 +50,13 @@ test_key_neq! {
|
|||
v!({ a: v!(U8), }), v!({ b: v!(U8), })
|
||||
record_empty_vs_nonempty:
|
||||
v!(EMPTY_RECORD), v!({ a: v!(U8), })
|
||||
|
||||
different_tag_union_tags:
|
||||
v!([ A v!(U8) ]), v!([ B v!(U8) ])
|
||||
tag_union_empty_vs_nonempty:
|
||||
v!(EMPTY_TAG_UNION), v!([ B v!(U8) ])
|
||||
different_recursive_tag_union_tags:
|
||||
v!([ Nil, Cons v!(^lst) ] as lst), v!([ Nil, Next v!(^lst) ] as lst)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -87,6 +108,36 @@ fn derivable_record_with_record_ext() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derivable_tag_ext_flex_var() {
|
||||
check_derivable(
|
||||
Hash,
|
||||
v!([ A v!(STR) ]* ),
|
||||
DeriveKey::Hash(FlatHashKey::TagUnion(vec![("A".into(), 1)])),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derivable_tag_ext_flex_able_var() {
|
||||
check_derivable(
|
||||
Hash,
|
||||
v!([ A v!(STR) ]a has Symbol::ENCODE_TO_ENCODER),
|
||||
DeriveKey::Hash(FlatHashKey::TagUnion(vec![("A".into(), 1)])),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derivable_tag_with_tag_ext() {
|
||||
check_derivable(
|
||||
Hash,
|
||||
v!([ B v!(STR) v!(U8) ][ A v!(STR) ]),
|
||||
DeriveKey::Hash(FlatHashKey::TagUnion(vec![
|
||||
("A".into(), 1),
|
||||
("B".into(), 2),
|
||||
])),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_record() {
|
||||
derive_test(Hash, v!(EMPTY_RECORD), |golden| {
|
||||
|
@ -149,3 +200,100 @@ fn two_field_record() {
|
|||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_one_label_no_payloads() {
|
||||
derive_test(Hash, v!([A]), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [A]
|
||||
# hasher, [A] -[[hash_[A 0](0)]]-> hasher | hasher has Hasher
|
||||
# hasher, [A] -[[hash_[A 0](0)]]-> hasher | hasher has Hasher
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_[A 0](0)]]
|
||||
#Derived.hash_[A 0] = \#Derived.hasher, A -> #Derived.hasher
|
||||
"###
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_one_label_newtype() {
|
||||
derive_test(Hash, v!([A v!(U8) v!(STR)]), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [A U8 Str]
|
||||
# hasher, [A a a1] -[[hash_[A 2](0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher
|
||||
# hasher, [A a a1] -[[hash_[A 2](0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_[A 2](0)]]
|
||||
#Derived.hash_[A 2] =
|
||||
\#Derived.hasher, A #Derived.2 #Derived.3 ->
|
||||
Hash.hash (Hash.hash #Derived.hasher #Derived.2) #Derived.3
|
||||
"###
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_two_labels() {
|
||||
derive_test(Hash, v!([A v!(U8) v!(STR) v!(U16), B v!(STR)]), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [A U8 Str U16, B Str]
|
||||
# a, [A a1 a2 a3, B a3] -[[hash_[A 3,B 1](0)]]-> a | a has Hasher, a1 has Hash, a2 has Hash, a3 has Hash
|
||||
# a, [A a1 a2 a3, B a3] -[[hash_[A 3,B 1](0)]]-> a | a has Hasher, a1 has Hash, a2 has Hash, a3 has Hash
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_[A 3,B 1](0)]]
|
||||
#Derived.hash_[A 3,B 1] =
|
||||
\#Derived.hasher, #Derived.union ->
|
||||
when #Derived.union is
|
||||
A #Derived.3 #Derived.4 #Derived.5 ->
|
||||
Hash.hash
|
||||
(Hash.hash
|
||||
(Hash.hash (Hash.addU8 #Derived.hasher 0) #Derived.3)
|
||||
#Derived.4)
|
||||
#Derived.5
|
||||
B #Derived.6 -> Hash.hash (Hash.addU8 #Derived.hasher 1) #Derived.6
|
||||
"###
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_two_labels_no_payloads() {
|
||||
derive_test(Hash, v!([A, B]), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [A, B]
|
||||
# a, [A, B] -[[hash_[A 0,B 0](0)]]-> a | a has Hasher
|
||||
# a, [A, B] -[[hash_[A 0,B 0](0)]]-> a | a has Hasher
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_[A 0,B 0](0)]]
|
||||
#Derived.hash_[A 0,B 0] =
|
||||
\#Derived.hasher, #Derived.union ->
|
||||
when #Derived.union is
|
||||
A -> Hash.addU8 #Derived.hasher 0
|
||||
B -> Hash.addU8 #Derived.hasher 1
|
||||
"###
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recursive_tag_union() {
|
||||
derive_test(Hash, v!([Nil, Cons v!(U8) v!(^lst) ] as lst), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [Cons U8 $rec, Nil] as $rec
|
||||
# a, [Cons a1 a2, Nil] -[[hash_[Cons 2,Nil 0](0)]]-> a | a has Hasher, a1 has Hash, a2 has Hash
|
||||
# a, [Cons a1 a2, Nil] -[[hash_[Cons 2,Nil 0](0)]]-> a | a has Hasher, a1 has Hash, a2 has Hash
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_[Cons 2,Nil 0](0)]]
|
||||
#Derived.hash_[Cons 2,Nil 0] =
|
||||
\#Derived.hasher, #Derived.union ->
|
||||
when #Derived.union is
|
||||
Cons #Derived.3 #Derived.4 ->
|
||||
Hash.hash
|
||||
(Hash.hash (Hash.addU8 #Derived.hasher 0) #Derived.3)
|
||||
#Derived.4
|
||||
Nil -> Hash.addU8 #Derived.hasher 1
|
||||
"###
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use roc_can::expr::{ClosureData, OpaqueWrapFunctionData, WhenBranch};
|
|||
use roc_can::pattern::{Pattern, RecordDestruct};
|
||||
|
||||
use roc_module::symbol::Interns;
|
||||
|
||||
use ven_pretty::{Arena, DocAllocator, DocBuilder};
|
||||
|
||||
pub struct Ctx<'a> {
|
||||
|
@ -100,8 +101,12 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
|||
.append(expr(c, Free, f, &loc_cond.value))
|
||||
.append(f.text(" is"))
|
||||
.append(
|
||||
f.concat(branches.iter().map(|b| f.line().append(branch(c, f, b))))
|
||||
.group(),
|
||||
f.concat(
|
||||
branches
|
||||
.iter()
|
||||
.map(|b| f.hardline().append(branch(c, f, b)))
|
||||
)
|
||||
.group(),
|
||||
)
|
||||
.nest(2)
|
||||
.group()
|
||||
|
@ -134,7 +139,10 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
|||
)
|
||||
.group(),
|
||||
LetRec(_, _, _) => todo!(),
|
||||
LetNonRec(_, _) => todo!(),
|
||||
LetNonRec(loc_def, body) => def(c, f, loc_def)
|
||||
.append(f.hardline())
|
||||
.append(expr(c, Free, f, &body.value))
|
||||
.group(),
|
||||
Call(fun, args, _) => {
|
||||
let (_, fun, _, _) = &**fun;
|
||||
maybe_paren!(
|
||||
|
@ -154,7 +162,24 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
|||
.nest(2)
|
||||
)
|
||||
}
|
||||
RunLowLevel { .. } => todo!(),
|
||||
RunLowLevel { args, .. } => {
|
||||
let op = "LowLevel";
|
||||
|
||||
maybe_paren!(
|
||||
Free,
|
||||
p,
|
||||
f.reflow(op)
|
||||
.append(
|
||||
f.concat(
|
||||
args.iter()
|
||||
.map(|le| f.line().append(expr(c, AppArg, f, &le.1)))
|
||||
)
|
||||
.group()
|
||||
)
|
||||
.group()
|
||||
.nest(2)
|
||||
)
|
||||
}
|
||||
ForeignCall { .. } => todo!(),
|
||||
Closure(ClosureData {
|
||||
arguments,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue