Merge remote-tracking branch 'origin/main' into rust-1-62-1

This commit is contained in:
Folkert 2022-11-09 13:51:51 +01:00
commit 14cd48fce7
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
685 changed files with 44531 additions and 20189 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

@ -0,0 +1,58 @@
#![cfg(test)]
// Even with #[allow(non_snake_case)] on individual idents, rust-analyzer issues diagnostics.
// See https://github.com/rust-lang/rust-analyzer/issues/6541.
// For the `v!` macro we use uppercase variables when constructing tag unions.
#![allow(non_snake_case)]
use crate::{util::check_single_lset_immediate, v};
use roc_module::symbol::Symbol;
use roc_types::subs::Variable;
use roc_derive_key::DeriveBuiltin::IsEq;
#[test]
fn immediates() {
// Everything is an immediate for `Eq`.
check_single_lset_immediate(IsEq, v!(U8), Symbol::BOOL_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(U16), Symbol::BOOL_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(U32), Symbol::BOOL_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(U64), Symbol::BOOL_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(U128), Symbol::BOOL_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(I8), Symbol::BOOL_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(I16), Symbol::BOOL_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(I32), Symbol::BOOL_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(I64), Symbol::BOOL_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(I128), Symbol::BOOL_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(STR), Symbol::BOOL_STRUCTURAL_EQ);
check_single_lset_immediate(
IsEq,
v!(Symbol::LIST_LIST v!(U8)),
Symbol::BOOL_STRUCTURAL_EQ,
);
check_single_lset_immediate(
IsEq,
v!(Symbol::LIST_LIST v!(STR)),
Symbol::BOOL_STRUCTURAL_EQ,
);
check_single_lset_immediate(IsEq, v!({ a: v!(U8), }), Symbol::BOOL_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(EMPTY_RECORD), Symbol::BOOL_STRUCTURAL_EQ);
check_single_lset_immediate(
IsEq,
v!([ A v!(U8) v!(STR), B v!(STR) ]),
Symbol::BOOL_STRUCTURAL_EQ,
);
check_single_lset_immediate(
IsEq,
v!([ A v!(U8) v!(STR), B v!(STR) ]),
Symbol::BOOL_STRUCTURAL_EQ,
);
check_single_lset_immediate(
IsEq,
v!([ Nil, Cons v!(^lst)] as lst),
Symbol::BOOL_STRUCTURAL_EQ,
);
// NOTE: despite this reaching an immediate, `F64`s will never actually be allowed to be
// compared, because obligation checking will rule them out from `isEq`!
check_single_lset_immediate(IsEq, v!(F64), Symbol::BOOL_STRUCTURAL_EQ);
}

View file

@ -0,0 +1,299 @@
#![cfg(test)]
// Even with #[allow(non_snake_case)] on individual idents, rust-analyzer issues diagnostics.
// See https://github.com/rust-lang/rust-analyzer/issues/6541.
// For the `v!` macro we use uppercase variables when constructing tag unions.
#![allow(non_snake_case)]
use crate::{
test_key_eq, test_key_neq,
util::{check_derivable, check_single_lset_immediate, check_underivable, derive_test},
v,
};
use insta::assert_snapshot;
use roc_module::symbol::Symbol;
use roc_types::subs::Variable;
use roc_derive_key::{hash::FlatHashKey, DeriveBuiltin::Hash, DeriveError, DeriveKey};
test_key_eq! {
Hash,
same_record:
v!({ a: v!(U8), }), v!({ a: v!(U8), })
same_record_fields_diff_types:
v!({ a: v!(U8), }), v!({ a: v!(STR), })
same_record_fields_any_order:
v!({ a: v!(U8), b: v!(U8), c: v!(U8), }),
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! {
Hash,
different_record_fields:
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]
fn immediates() {
check_single_lset_immediate(Hash, v!(U8), Symbol::HASH_ADD_U8);
check_single_lset_immediate(Hash, v!(U16), Symbol::HASH_ADD_U16);
check_single_lset_immediate(Hash, v!(U32), Symbol::HASH_ADD_U32);
check_single_lset_immediate(Hash, v!(U64), Symbol::HASH_ADD_U64);
check_single_lset_immediate(Hash, v!(U128), Symbol::HASH_ADD_U128);
check_single_lset_immediate(Hash, v!(I8), Symbol::HASH_HASH_I8);
check_single_lset_immediate(Hash, v!(I16), Symbol::HASH_HASH_I16);
check_single_lset_immediate(Hash, v!(I32), Symbol::HASH_HASH_I32);
check_single_lset_immediate(Hash, v!(I64), Symbol::HASH_HASH_I64);
check_single_lset_immediate(Hash, v!(I128), Symbol::HASH_HASH_I128);
check_single_lset_immediate(Hash, v!(STR), Symbol::HASH_HASH_STR_BYTES);
check_single_lset_immediate(Hash, v!(Symbol::LIST_LIST v!(U8)), Symbol::HASH_HASH_LIST);
check_single_lset_immediate(Hash, v!(Symbol::LIST_LIST v!(STR)), Symbol::HASH_HASH_LIST);
}
#[test]
fn optional_record_field_derive_error() {
check_underivable(Hash, v!({ ?a: v!(U8), }), DeriveError::Underivable);
}
#[test]
fn derivable_record_ext_flex_var() {
check_derivable(
Hash,
v!({ a: v!(STR), }* ),
DeriveKey::Hash(FlatHashKey::Record(vec!["a".into()])),
);
}
#[test]
fn derivable_record_ext_flex_able_var() {
check_derivable(
Hash,
v!({ a: v!(STR), }a has Symbol::DECODE_DECODER ),
DeriveKey::Hash(FlatHashKey::Record(vec!["a".into()])),
);
}
#[test]
fn derivable_record_with_record_ext() {
check_derivable(
Hash,
v!({ b: v!(STR), }{ a: v!(STR), } ),
DeriveKey::Hash(FlatHashKey::Record(vec!["a".into(), "b".into()])),
);
}
#[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| {
assert_snapshot!(golden, @r###"
# derived for {}
# hasher, {} -[[hash_{}(0)]]-> hasher | hasher has Hasher
# hasher, {} -[[hash_{}(0)]]-> hasher | hasher has Hasher
# Specialization lambda sets:
# @<1>: [[hash_{}(0)]]
#Derived.hash_{} = \#Derived.hasher, #Derived.rcd -> #Derived.hasher
"###
)
})
}
#[test]
fn zero_field_record() {
derive_test(Hash, v!({}), |golden| {
assert_snapshot!(golden, @r###"
# derived for {}
# hasher, {} -[[hash_{}(0)]]-> hasher | hasher has Hasher
# hasher, {} -[[hash_{}(0)]]-> hasher | hasher has Hasher
# Specialization lambda sets:
# @<1>: [[hash_{}(0)]]
#Derived.hash_{} = \#Derived.hasher, #Derived.rcd -> #Derived.hasher
"###
)
})
}
#[test]
fn one_field_record() {
derive_test(Hash, v!({ a: v!(U8), }), |golden| {
assert_snapshot!(golden, @r###"
# derived for { a : U8 }
# hasher, { a : a } -[[hash_{a}(0)]]-> hasher | a has Hash, hasher has Hasher
# hasher, { a : a } -[[hash_{a}(0)]]-> hasher | a has Hash, hasher has Hasher
# Specialization lambda sets:
# @<1>: [[hash_{a}(0)]]
#Derived.hash_{a} =
\#Derived.hasher, #Derived.rcd -> Hash.hash #Derived.hasher #Derived.rcd.a
"###
)
})
}
#[test]
fn two_field_record() {
derive_test(Hash, v!({ a: v!(U8), b: v!(STR), }), |golden| {
assert_snapshot!(golden, @r###"
# derived for { a : U8, b : Str }
# hasher, { a : a, b : a1 } -[[hash_{a,b}(0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher
# hasher, { a : a, b : a1 } -[[hash_{a,b}(0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher
# Specialization lambda sets:
# @<1>: [[hash_{a,b}(0)]]
#Derived.hash_{a,b} =
\#Derived.hasher, #Derived.rcd ->
Hash.hash (Hash.hash #Derived.hasher #Derived.rcd.a) #Derived.rcd.b
"###
)
})
}
#[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
"###
)
})
}

View file

@ -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> {
@ -58,7 +59,7 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
match e {
Num(_, n, _, _) | Int(_, _, n, _, _) | Float(_, _, n, _, _) => f.text(&**n),
Str(s) => f.text(format!(r#""{}""#, s)),
SingleQuote(c) => f.text(format!("'{}'", c)),
SingleQuote(_, _, c, _) => f.text(format!("'{}'", c)),
List {
elem_var: _,
loc_elems,
@ -86,7 +87,7 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
.append("]")
.group(),
),
Var(sym) | AbilityMember(sym, _, _) => f.text(format!(
Var(sym, _) | AbilityMember(sym, _, _) => f.text(format!(
"{}.{}",
sym.module_string(c.interns),
sym.as_str(c.interns),
@ -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,
@ -362,11 +387,12 @@ fn pattern<'a>(
)
.append(f.text("}"))
.group(),
List { .. } => todo!(),
NumLiteral(_, n, _, _) | IntLiteral(_, _, n, _, _) | FloatLiteral(_, _, n, _, _) => {
f.text(&**n)
}
StrLiteral(s) => f.text(format!(r#""{}""#, s)),
SingleQuote(c) => f.text(format!("'{}'", c)),
SingleQuote(_, _, c, _) => f.text(format!("'{}'", c)),
Underscore => f.text("_"),
Shadowed(_, _, _) => todo!(),

View file

@ -2,6 +2,8 @@
mod decoding;
mod encoding;
mod eq;
mod hash;
mod pretty_print;
mod util;

View file

@ -51,6 +51,16 @@ fn module_source_and_path(builtin: DeriveBuiltin) -> (ModuleId, &'static str, Pa
module_source(ModuleId::DECODE),
builtins_path.join("Decode.roc"),
),
DeriveBuiltin::Hash => (
ModuleId::HASH,
module_source(ModuleId::HASH),
builtins_path.join("Hash.roc"),
),
DeriveBuiltin::IsEq => (
ModuleId::BOOL,
module_source(ModuleId::BOOL),
builtins_path.join("Bool.roc"),
),
}
}
@ -134,7 +144,7 @@ macro_rules! v {
use roc_module::symbol::Symbol;
|subs: &mut Subs| {
let args = vec![$( $arg(subs) )*];
let alias_variables = AliasVariables::insert_into_subs::<Vec<_>, Vec<_>>(subs, args, vec![]);
let alias_variables = AliasVariables::insert_into_subs::<Vec<_>, Vec<_>, _>(subs, args, vec![], vec![]);
let real_var = $real_var(subs);
roc_derive::synth_var(subs, Content::Alias(Symbol::$alias, alias_variables, real_var, AliasKind::Structural))
}
@ -145,7 +155,7 @@ macro_rules! v {
use roc_module::symbol::Symbol;
|subs: &mut Subs| {
let args = vec![$( $arg(subs) )*];
let alias_variables = AliasVariables::insert_into_subs::<Vec<_>, Vec<_>>(subs, args, vec![]);
let alias_variables = AliasVariables::insert_into_subs::<Vec<_>, Vec<_>, _>(subs, args, vec![], vec![]);
let real_var = $real_var(subs);
roc_derive::synth_var(subs, Content::Alias(Symbol::$alias, alias_variables, real_var, AliasKind::Opaque))
}
@ -155,12 +165,14 @@ macro_rules! v {
|subs: &mut Subs| { roc_derive::synth_var(subs, Content::FlexVar(None)) }
}};
($name:ident has $ability:path) => {{
use roc_types::subs::{Subs, SubsIndex, Content};
use roc_types::subs::{Subs, SubsIndex, SubsSlice, 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))
let abilities_slice = SubsSlice::extend_new(&mut subs.symbol_names, [$ability]);
roc_derive::synth_var(subs, Content::FlexAbleVar(Some(name_index), abilities_slice))
}
}};
(^$rec_var:ident) => {{
@ -248,6 +260,18 @@ where
assert_eq!(key, Ok(Derived::Immediate(immediate)));
}
pub(crate) fn check_single_lset_immediate<S>(builtin: DeriveBuiltin, synth: S, immediate: Symbol)
where
S: FnOnce(&mut Subs) -> Variable,
{
let mut subs = Subs::new();
let var = synth(&mut subs);
let key = Derived::builtin(builtin, &subs, var);
assert_eq!(key, Ok(Derived::SingleLambdaSetImmediate(immediate)));
}
#[allow(clippy::too_many_arguments)]
fn assemble_derived_golden(
subs: &mut Subs,
@ -268,6 +292,7 @@ fn assemble_derived_golden(
DebugPrint {
print_lambda_sets: true,
print_only_under_alias,
..DebugPrint::NOTHING
},
);
subs.rollback_to(snapshot);
@ -352,6 +377,7 @@ fn check_derived_typechecks_and_golden(
let mut rigid_vars = Default::default();
let (import_variables, abilities_store) = add_imports(
test_module,
&mut constraints,
&mut test_subs,
pending_abilities,
&exposed_for_module,