Derive hash for tag unions with a discriminant

This commit is contained in:
Ayaz Hafiz 2022-10-04 17:19:30 -05:00
parent 5eb00c4f94
commit ff70832068
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
4 changed files with 366 additions and 64 deletions

View file

@ -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
"###
)

View file

@ -200,3 +200,70 @@ fn two_field_record() {
)
})
}
#[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 ->
#Derived.discrHasher =
Hash.hash #Derived.hasher (@tag_discriminant #Derived.union)
when #Derived.union is
A #Derived.4 #Derived.5 #Derived.6 ->
Hash.hash
(Hash.hash (Hash.hash #Derived.discrHasher #Derived.4) #Derived.5)
#Derived.6
B #Derived.7 -> Hash.hash #Derived.discrHasher #Derived.7
"###
)
})
}
#[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 ->
#Derived.discrHasher =
Hash.hash #Derived.hasher (@tag_discriminant #Derived.union)
when #Derived.union is
A -> #Derived.discrHasher
B -> #Derived.discrHasher
"###
)
})
}
#[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 ->
#Derived.discrHasher =
Hash.hash #Derived.hasher (@tag_discriminant #Derived.union)
when #Derived.union is
Cons #Derived.4 #Derived.5 ->
Hash.hash (Hash.hash #Derived.discrHasher #Derived.4) #Derived.5
Nil -> #Derived.discrHasher
"###
)
})
}

View file

@ -5,7 +5,9 @@ use roc_can::expr::Expr::{self, *};
use roc_can::expr::{ClosureData, OpaqueWrapFunctionData, WhenBranch};
use roc_can::pattern::{Pattern, RecordDestruct};
use roc_module::low_level::LowLevel;
use roc_module::symbol::Interns;
use ven_pretty::{Arena, DocAllocator, DocBuilder};
pub struct Ctx<'a> {
@ -100,8 +102,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 +140,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 +163,27 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
.nest(2)
)
}
RunLowLevel { .. } => todo!(),
RunLowLevel { op, args, .. } => {
let op = match op {
LowLevel::TagDiscriminant => "@tag_discriminant",
_ => unreachable!(),
};
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,