Merge pull request #5179 from roc-lang/i5143-tuple-abilities

Implement ability obligation checking and derivation for tuples
This commit is contained in:
Ayaz 2023-03-25 15:51:39 -05:00 committed by GitHub
commit 61dd5cc8c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 4225 additions and 2296 deletions

View file

@ -28,6 +28,11 @@ test_key_eq! {
explicit_empty_record_and_implicit_empty_record:
v!(EMPTY_RECORD), v!({})
same_tuple:
v!((v!(U8), v!(U16),)), v!((v!(U8), v!(U16),))
same_tuple_fields_diff_types:
v!((v!(U8), v!(U16),)), v!((v!(U32), v!(U64),))
list_list_diff_types:
v!(Symbol::LIST_LIST v!(STR)), v!(Symbol::LIST_LIST v!(U8))
str_str:
@ -41,6 +46,9 @@ test_key_neq! {
v!({ a: v!(U8), }), v!({ b: v!(U8), })
record_empty_vs_nonempty:
v!(EMPTY_RECORD), v!({ a: v!(U8), })
different_tuple_arities:
v!((v!(U8), v!(U16),)), v!((v!(U8), v!(U16), v!(U32),))
}
#[test]
@ -167,3 +175,59 @@ fn record_2_fields() {
)
})
}
#[test]
fn tuple_2_fields() {
derive_test(Decoder, v!((v!(STR), v!(U8),)), |golden| {
assert_snapshot!(golden, @r###"
# derived for ( Str, U8 )*
# Decoder ( val, val1 )* fmt | fmt has DecoderFormatting, val has Decoding, val1 has Decoding
# List U8, fmt -[[custom(22)]]-> { rest : List U8, result : [Err [TooShort], Ok ( val, val1 )a] } | fmt has DecoderFormatting, val has Decoding, val1 has Decoding
# Specialization lambda sets:
# @<1>: [[custom(22)]]
#Derived.decoder_(arity:2) =
custom
\#Derived.bytes3, #Derived.fmt3 ->
decodeWith
#Derived.bytes3
(tuple
{ e1: Err NoElem, e0: Err NoElem }
\#Derived.stateRecord2, #Derived.index ->
when #Derived.index is
0 ->
Next (custom
\#Derived.bytes, #Derived.fmt ->
when decodeWith #Derived.bytes decoder #Derived.fmt is
#Derived.rec ->
{
result: when #Derived.rec.result is
Ok #Derived.val ->
Ok { stateRecord2 & e0: Ok #Derived.val }
Err #Derived.err -> Err #Derived.err,
rest: #Derived.rec.rest
})
1 ->
Next (custom
\#Derived.bytes2, #Derived.fmt2 ->
when decodeWith #Derived.bytes2 decoder #Derived.fmt2 is
#Derived.rec2 ->
{
result: when #Derived.rec2.result is
Ok #Derived.val2 ->
Ok { stateRecord2 & e1: Ok #Derived.val2 }
Err #Derived.err2 -> Err #Derived.err2,
rest: #Derived.rec2.rest
})
_ -> TooLong
\#Derived.stateRecord ->
when #Derived.stateRecord.e0 is
Ok #Derived.0 ->
when #Derived.stateRecord.e1 is
Ok #Derived.1 -> Ok ( #Derived.0, #Derived.1 )
_ -> Err TooShort
_ -> Err TooShort)
#Derived.fmt3
"###
)
})
}

View file

@ -33,6 +33,11 @@ test_key_eq! {
v!({ a: v!(U8), b: v!(U8), }),
v!({ ?a: v!(U8), ?b: v!(U8), })
same_tuple:
v!((v!(U8), v!(U16),)), v!((v!(U8), v!(U16),))
same_tuple_fields_diff_types:
v!((v!(U8), v!(U16),)), v!((v!(U32), v!(U64),))
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:
@ -78,6 +83,9 @@ test_key_neq! {
record_empty_vs_nonempty:
v!(EMPTY_RECORD), v!({ a: v!(U8), })
different_tuple_arities:
v!((v!(U8), v!(U16),)), v!((v!(U8), v!(U16), v!(U32),))
different_tag_union_tags:
v!([ A v!(U8) ]), v!([ B v!(U8) ])
tag_union_empty_vs_nonempty:
@ -265,6 +273,29 @@ fn two_field_record() {
})
}
#[test]
fn two_field_tuple() {
derive_test(ToEncoder, v!((v!(U8), v!(STR),)), |golden| {
assert_snapshot!(golden, @r###"
# derived for ( U8, Str )*
# ( val, val1 )* -[[toEncoder_(arity:2)(0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
# ( val, val1 )a -[[toEncoder_(arity:2)(0)]]-> (List U8, fmt -[[custom(2) ( val, val1 )a]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
# Specialization lambda sets:
# @<1>: [[toEncoder_(arity:2)(0)]]
# @<2>: [[custom(2) ( val, val1 )*]] | val has Encoding, val1 has Encoding
#Derived.toEncoder_(arity:2) =
\#Derived.tup ->
custom
\#Derived.bytes, #Derived.fmt ->
appendWith
#Derived.bytes
(tuple [toEncoder #Derived.tup.0, toEncoder #Derived.tup.1])
#Derived.fmt
"###
)
})
}
#[test]
#[ignore = "NOTE: this would never actually happen, because [] is uninhabited, and hence toEncoder can never be called with a value of []!
Rightfully it induces broken assertions in other parts of the compiler, so we ignore it."]

View file

@ -28,6 +28,11 @@ test_key_eq! {
explicit_empty_record_and_implicit_empty_record:
v!(EMPTY_RECORD), v!({})
same_tuple:
v!((v!(U8), v!(U16),)), v!((v!(U8), v!(U16),))
same_tuple_fields_diff_types:
v!((v!(U8), v!(U16),)), v!((v!(U32), v!(U64),))
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:
@ -51,6 +56,9 @@ test_key_neq! {
record_empty_vs_nonempty:
v!(EMPTY_RECORD), v!({ a: v!(U8), })
different_tuple_arities:
v!((v!(U8), v!(U16),)), v!((v!(U8), v!(U16), v!(U32),))
different_tag_union_tags:
v!([ A v!(U8) ]), v!([ B v!(U8) ])
tag_union_empty_vs_nonempty:
@ -201,6 +209,23 @@ fn two_field_record() {
})
}
#[test]
fn two_element_tuple() {
derive_test(Hash, v!((v!(U8), v!(STR),)), |golden| {
assert_snapshot!(golden, @r###"
# derived for ( U8, Str )*
# hasher, ( a, a1 )* -[[hash_(arity:2)(0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher
# hasher, ( a, a1 )* -[[hash_(arity:2)(0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher
# Specialization lambda sets:
# @<1>: [[hash_(arity:2)(0)]]
#Derived.hash_(arity:2) =
\#Derived.hasher, #Derived.tup ->
hash (hash #Derived.hasher #Derived.tup.0) #Derived.tup.1
"###
)
})
}
#[test]
fn tag_one_label_no_payloads() {
derive_test(Hash, v!([A]), |golden| {

View file

@ -89,6 +89,22 @@ macro_rules! v {
roc_derive::synth_var(subs, Content::Structure(FlatType::Record(fields, ext)))
}
}};
(( $($make_v:expr,)* )$( $($ext:tt)+ )?) => {{
#[allow(unused)]
use roc_types::subs::{Subs, RecordFields, Content, FlatType, Variable, TupleElems};
|subs: &mut Subs| {
let elems = [
$($make_v(subs),)*
].into_iter().enumerate();
let elems = TupleElems::insert_into_subs(subs, elems);
#[allow(unused_mut, unused)]
let mut ext = Variable::EMPTY_TUPLE;
$( ext = $crate::v!($($ext)+)(subs); )?
roc_derive::synth_var(subs, Content::Structure(FlatType::Tuple(elems, ext)))
}
}};
([ $($tag:ident $($payload:expr)*),* ] as $rec_var:ident) => {{
use roc_types::subs::{Subs, SubsIndex, Variable, Content, FlatType, TagExt, UnionTags};
use roc_module::ident::TagName;