Merge pull request #3835 from roc-lang/ext-vars-in-derivers

Support bound and unbound extension variables in deriving
This commit is contained in:
Folkert de Vries 2022-08-30 00:34:50 +02:00 committed by GitHub
commit acb71d6007
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 202 additions and 61 deletions

View file

@ -6,14 +6,14 @@
use crate::{
test_key_eq, test_key_neq,
util::{check_immediate, check_underivable, derive_test},
util::{check_derivable, check_immediate, check_underivable, derive_test},
v,
};
use insta::assert_snapshot;
use roc_module::symbol::Symbol;
use roc_types::subs::Variable;
use roc_derive_key::{DeriveBuiltin::Decoder, DeriveError};
use roc_derive_key::{decoding::FlatDecodableKey, DeriveBuiltin::Decoder, DeriveError, DeriveKey};
test_key_eq! {
Decoder,
@ -43,11 +43,6 @@ test_key_neq! {
v!(EMPTY_RECORD), v!({ a: v!(U8), })
}
#[test]
fn optional_record_field_derive_error() {
check_underivable(Decoder, v!({ ?a: v!(U8), }), DeriveError::Underivable);
}
#[test]
fn immediates() {
check_immediate(Decoder, v!(U8), Symbol::DECODE_U8);
@ -66,6 +61,38 @@ fn immediates() {
check_immediate(Decoder, v!(STR), Symbol::DECODE_STRING);
}
#[test]
fn optional_record_field_derive_error() {
check_underivable(Decoder, v!({ ?a: v!(U8), }), DeriveError::Underivable);
}
#[test]
fn derivable_record_ext_flex_var() {
check_derivable(
Decoder,
v!({ a: v!(STR), }* ),
DeriveKey::Decoder(FlatDecodableKey::Record(vec!["a".into()])),
);
}
#[test]
fn derivable_record_ext_flex_able_var() {
check_derivable(
Decoder,
v!({ a: v!(STR), }a has Symbol::DECODE_DECODER ),
DeriveKey::Decoder(FlatDecodableKey::Record(vec!["a".into()])),
);
}
#[test]
fn derivable_record_with_record_ext() {
check_derivable(
Decoder,
v!({ b: v!(STR), }{ a: v!(STR), } ),
DeriveKey::Decoder(FlatDecodableKey::Record(vec!["a".into(), "b".into()])),
);
}
#[test]
fn list() {
derive_test(Decoder, v!(Symbol::LIST_LIST v!(STR)), |golden| {

View file

@ -8,10 +8,10 @@ use insta::assert_snapshot;
use crate::{
test_key_eq, test_key_neq,
util::{check_immediate, derive_test},
util::{check_derivable, check_immediate, derive_test},
v,
};
use roc_derive_key::DeriveBuiltin::ToEncoder;
use roc_derive_key::{encoding::FlatEncodableKey, DeriveBuiltin::ToEncoder, DeriveKey};
use roc_module::symbol::Symbol;
use roc_types::subs::Variable;
@ -118,6 +118,63 @@ fn immediates() {
check_immediate(ToEncoder, v!(STR), Symbol::ENCODE_STRING);
}
#[test]
fn derivable_record_ext_flex_var() {
check_derivable(
ToEncoder,
v!({ a: v!(STR), }* ),
DeriveKey::ToEncoder(FlatEncodableKey::Record(vec!["a".into()])),
);
}
#[test]
fn derivable_record_ext_flex_able_var() {
check_derivable(
ToEncoder,
v!({ a: v!(STR), }a has Symbol::ENCODE_TO_ENCODER),
DeriveKey::ToEncoder(FlatEncodableKey::Record(vec!["a".into()])),
);
}
#[test]
fn derivable_record_with_record_ext() {
check_derivable(
ToEncoder,
v!({ b: v!(STR), }{ a: v!(STR), } ),
DeriveKey::ToEncoder(FlatEncodableKey::Record(vec!["a".into(), "b".into()])),
);
}
#[test]
fn derivable_tag_ext_flex_var() {
check_derivable(
ToEncoder,
v!([ A v!(STR) ]* ),
DeriveKey::ToEncoder(FlatEncodableKey::TagUnion(vec![("A".into(), 1)])),
);
}
#[test]
fn derivable_tag_ext_flex_able_var() {
check_derivable(
ToEncoder,
v!([ A v!(STR) ]a has Symbol::ENCODE_TO_ENCODER),
DeriveKey::ToEncoder(FlatEncodableKey::TagUnion(vec![("A".into(), 1)])),
);
}
#[test]
fn derivable_tag_with_tag_ext() {
check_derivable(
ToEncoder,
v!([ B v!(STR) v!(U8) ][ A v!(STR) ]),
DeriveKey::ToEncoder(FlatEncodableKey::TagUnion(vec![
("A".into(), 1),
("B".into(), 2),
])),
);
}
#[test]
fn empty_record() {
derive_test(ToEncoder, v!(EMPTY_RECORD), |golden| {

View file

@ -56,7 +56,7 @@ fn module_source_and_path(builtin: DeriveBuiltin) -> (ModuleId, &'static str, Pa
/// DSL for creating [`Content`][roc_types::subs::Content].
#[macro_export]
macro_rules! v {
({ $($field:ident: $make_v:expr,)* $(?$opt_field:ident : $make_opt_v:expr,)* }) => {{
({ $($field:ident: $make_v:expr,)* $(?$opt_field:ident : $make_opt_v:expr,)* }$( $($ext:tt)+ )?) => {{
#[allow(unused)]
use roc_types::types::RecordField;
use roc_types::subs::{Subs, RecordFields, Content, FlatType, Variable};
@ -68,25 +68,12 @@ macro_rules! v {
$( (stringify!($opt_field).into(), RecordField::Optional($opt_field)) ,)*
];
let fields = RecordFields::insert_into_subs(subs, fields);
roc_derive::synth_var(subs, Content::Structure(FlatType::Record(fields, Variable::EMPTY_RECORD)))
}
}};
([ $($tag:ident $($payload:expr)*),* ]$( $ext:tt )?) => {{
#[allow(unused)]
use roc_types::subs::{Subs, UnionTags, Content, FlatType, Variable};
#[allow(unused)]
use roc_module::ident::TagName;
|subs: &mut Subs| {
$(
let $tag = vec![ $( $payload(subs), )* ];
)*
let tags = UnionTags::insert_into_subs::<_, Vec<Variable>>(subs, vec![ $( (TagName(stringify!($tag).into()), $tag) ,)* ]);
#[allow(unused_mut)]
let mut ext = Variable::EMPTY_TAG_UNION;
$( ext = $crate::v!($ext)(subs); )?
#[allow(unused_mut, unused)]
let mut ext = Variable::EMPTY_RECORD;
$( ext = $crate::v!($($ext)+)(subs); )?
roc_derive::synth_var(subs, Content::Structure(FlatType::TagUnion(tags, ext)))
roc_derive::synth_var(subs, Content::Structure(FlatType::Record(fields, ext)))
}
}};
([ $($tag:ident $($payload:expr)*),* ] as $rec_var:ident) => {{
@ -113,6 +100,24 @@ macro_rules! v {
tag_union_var
}
}};
([ $($tag:ident $($payload:expr)*),* ]$( $($ext:tt)+ )?) => {{
#[allow(unused)]
use roc_types::subs::{Subs, UnionTags, Content, FlatType, Variable};
#[allow(unused)]
use roc_module::ident::TagName;
|subs: &mut Subs| {
$(
let $tag = vec![ $( $payload(subs), )* ];
)*
let tags = UnionTags::insert_into_subs::<_, Vec<Variable>>(subs, vec![ $( (TagName(stringify!($tag).into()), $tag) ,)* ]);
#[allow(unused_mut, unused)]
let mut ext = Variable::EMPTY_TAG_UNION;
$( ext = $crate::v!($($ext)+)(subs); )?
roc_derive::synth_var(subs, Content::Structure(FlatType::TagUnion(tags, ext)))
}
}};
(Symbol::$sym:ident $($arg:expr)*) => {{
use roc_types::subs::{Subs, SubsSlice, Content, FlatType};
use roc_module::symbol::Symbol;
@ -148,6 +153,15 @@ macro_rules! v {
use roc_types::subs::{Subs, Content};
|subs: &mut Subs| { roc_derive::synth_var(subs, Content::FlexVar(None)) }
}};
($name:ident has $ability:path) => {{
use roc_types::subs::{Subs, SubsIndex, Content};
|subs: &mut Subs| {
let name_index =
SubsIndex::push_new(&mut subs.field_names, stringify!($name).into());
roc_derive::synth_var(subs, Content::FlexAbleVar(Some(name_index), $ability))
}
}};
(^$rec_var:ident) => {{
use roc_types::subs::{Subs};
|_: &mut Subs| { $rec_var }
@ -197,6 +211,18 @@ macro_rules! test_key_neq {
)*};
}
pub(crate) fn check_derivable<Sy>(builtin: DeriveBuiltin, synth: Sy, key: DeriveKey)
where
Sy: FnOnce(&mut Subs) -> Variable,
{
let mut subs = Subs::new();
let var = synth(&mut subs);
let derived = Derived::builtin(builtin, &subs, var);
assert_eq!(derived, Ok(Derived::Key(key)));
}
pub(crate) fn check_underivable<Sy>(builtin: DeriveBuiltin, synth: Sy, err: DeriveError)
where
Sy: FnOnce(&mut Subs) -> Variable,