Merge branch 'main' into shift-signature

Signed-off-by: Ayaz <20735482+ayazhafiz@users.noreply.github.com>
This commit is contained in:
Ayaz 2022-10-04 12:58:09 -05:00 committed by GitHub
commit a08b45263b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 266 additions and 7 deletions

View File

@ -22,7 +22,7 @@ Hash has
## Hashes a value into a [Hasher].
## Note that [hash] does not produce a hash value itself; the hasher must be
## [complete]d in order to extract the hash value.
hash : a, hasher -> hasher | a has Hash, hasher has Hasher
hash : hasher, a -> hasher | a has Hash, hasher has Hasher
## Describes a hashing algorithm that is fed bytes and produces an integer hash.
##

View File

@ -50,6 +50,7 @@ const PRETTY_PRINT_DEBUG_SYMBOLS: bool = true;
pub const DERIVABLE_ABILITIES: &[(Symbol, &[Symbol])] = &[
(Symbol::ENCODE_ENCODING, &[Symbol::ENCODE_TO_ENCODER]),
(Symbol::DECODE_DECODING, &[Symbol::DECODE_DECODER]),
(Symbol::HASH_HASH_ABILITY, &[Symbol::HASH_HASH]),
];
/// In Debug builds only, Symbol has a name() method that lets

View File

@ -272,6 +272,10 @@ impl ObligationCache {
var,
)),
Symbol::HASH_HASH_ABILITY => {
Some(DeriveHash::is_derivable(self, abilities_store, subs, var))
}
_ => None,
};
@ -893,6 +897,95 @@ impl DerivableVisitor for DeriveDecoding {
}
}
struct DeriveHash;
impl DerivableVisitor for DeriveHash {
const ABILITY: Symbol = Symbol::HASH_HASH_ABILITY;
#[inline(always)]
fn is_derivable_builtin_opaque(symbol: Symbol) -> bool {
is_builtin_number_alias(symbol)
}
#[inline(always)]
fn visit_recursion(_var: Variable) -> Result<Descend, NotDerivable> {
Ok(Descend(true))
}
#[inline(always)]
fn visit_apply(var: Variable, symbol: Symbol) -> Result<Descend, NotDerivable> {
if matches!(
symbol,
Symbol::LIST_LIST | Symbol::SET_SET | Symbol::DICT_DICT | Symbol::STR_STR,
) {
Ok(Descend(true))
} else {
Err(NotDerivable {
var,
context: NotDerivableContext::NoContext,
})
}
}
#[inline(always)]
fn visit_record(
subs: &Subs,
var: Variable,
fields: RecordFields,
) -> Result<Descend, NotDerivable> {
for (field_name, _, field) in fields.iter_all() {
if subs[field].is_optional() {
return Err(NotDerivable {
var,
context: NotDerivableContext::Decode(NotDerivableDecode::OptionalRecordField(
subs[field_name].clone(),
)),
});
}
}
Ok(Descend(true))
}
#[inline(always)]
fn visit_tag_union(_var: Variable) -> Result<Descend, NotDerivable> {
Ok(Descend(true))
}
#[inline(always)]
fn visit_recursive_tag_union(_var: Variable) -> Result<Descend, NotDerivable> {
Ok(Descend(true))
}
#[inline(always)]
fn visit_function_or_tag_union(_var: Variable) -> Result<Descend, NotDerivable> {
Ok(Descend(true))
}
#[inline(always)]
fn visit_empty_record(_var: Variable) -> Result<(), NotDerivable> {
Ok(())
}
#[inline(always)]
fn visit_empty_tag_union(_var: Variable) -> Result<(), NotDerivable> {
Ok(())
}
#[inline(always)]
fn visit_alias(_var: Variable, symbol: Symbol) -> Result<Descend, NotDerivable> {
if is_builtin_number_alias(symbol) {
Ok(Descend(false))
} else {
Ok(Descend(true))
}
}
#[inline(always)]
fn visit_ranged_number(_var: Variable, _range: NumericRange) -> Result<(), NotDerivable> {
Ok(())
}
}
/// Determines what type implements an ability member of a specialized signature, given the
/// [MustImplementAbility] constraints of the signature.
pub fn type_implementing_specialization(

View File

@ -7833,9 +7833,9 @@ mod solve_expr {
Noop := {} has [Hash {hash}]
hash = \@Noop {}, hasher -> hasher
hash = \hasher, @Noop {} -> hasher
main = \hasher -> hash (@Noop {}) hasher
main = \hasher -> hash hasher (@Noop {})
"#
),
"hasher -> hasher | hasher has Hasher",

View File

@ -3868,11 +3868,11 @@ fn flat_type_to_err_type(
ErrorType::TagUnion(sub_tags.union(err_tags), sub_ext)
}
ErrorType::FlexVar(var) => {
ErrorType::FlexVar(var) | ErrorType::FlexAbleVar(var, _) => {
ErrorType::TagUnion(err_tags, TypeExt::FlexOpen(var))
}
ErrorType::RigidVar(var) => {
ErrorType::RigidVar(var) | ErrorType::RigidAbleVar(var, _)=> {
ErrorType::TagUnion(err_tags, TypeExt::RigidOpen(var))
}
@ -3896,11 +3896,11 @@ fn flat_type_to_err_type(
ErrorType::TagUnion(sub_tags.union(err_tags), sub_ext)
}
ErrorType::FlexVar(var) => {
ErrorType::FlexVar(var) | ErrorType::FlexAbleVar(var, _) => {
ErrorType::TagUnion(err_tags, TypeExt::FlexOpen(var))
}
ErrorType::RigidVar(var) => {
ErrorType::RigidVar(var) | ErrorType::RigidAbleVar(var, _)=> {
ErrorType::TagUnion(err_tags, TypeExt::RigidOpen(var))
}

View File

@ -10849,6 +10849,171 @@ All branches in an `if` must have the same type!
"###
);
test_report!(
derive_hash_for_function,
indoc!(
r#"
app "test" provides [A] to "./platform"
A a := a -> a has [Hash]
"#
),
@r###"
INCOMPLETE ABILITY IMPLEMENTATION /code/proj/Main.roc
Roc can't derive an implementation of the `Hash.Hash` for `A`:
3 A a := a -> a has [Hash]
^^^^
Note: `Hash` cannot be generated for functions.
Tip: You can define a custom implementation of `Hash.Hash` for `A`.
"###
);
test_report!(
derive_hash_for_non_hash_opaque,
indoc!(
r#"
app "test" provides [A] to "./platform"
A := B has [Hash]
B := {}
"#
),
@r###"
INCOMPLETE ABILITY IMPLEMENTATION /code/proj/Main.roc
Roc can't derive an implementation of the `Hash.Hash` for `A`:
3 A := B has [Hash]
^^^^
Tip: `B` does not implement `Hash`. Consider adding a custom
implementation or `has Hash.Hash` to the definition of `B`.
Tip: You can define a custom implementation of `Hash.Hash` for `A`.
"###
);
test_report!(
derive_hash_for_other_has_hash,
indoc!(
r#"
app "test" provides [A] to "./platform"
A := B has [Hash]
B := {} has [Hash]
"#
),
@"" // no error
);
test_report!(
derive_hash_for_recursive_deriving,
indoc!(
r#"
app "test" provides [MyNat] to "./platform"
MyNat := [S MyNat, Z] has [Hash]
"#
),
@"" // no error
);
test_report!(
derive_hash_for_record,
indoc!(
r#"
app "test" provides [main] to "./platform"
foo : a -> {} | a has Hash
main = foo {a: "", b: 1}
"#
),
@"" // no error
);
test_report!(
derive_hash_for_tag,
indoc!(
r#"
app "test" provides [main] to "./platform"
foo : a -> {} | a has Hash
t : [A {}, B U8 U64, C Str]
main = foo t
"#
),
@"" // no error
);
test_report!(
cannot_derive_hash_for_function,
indoc!(
r#"
app "test" provides [main] to "./platform"
foo : a -> {} | a has Hash
main = foo (\x -> x)
"#
),
@r###"
TYPE MISMATCH /code/proj/Main.roc
This expression has a type that does not implement the abilities it's expected to:
5 main = foo (\x -> x)
^^^^^^^
Roc can't generate an implementation of the `Hash.Hash` ability for
a -> a
Note: `Hash` cannot be generated for functions.
"###
);
test_report!(
cannot_derive_hash_for_structure_containing_function,
indoc!(
r#"
app "test" provides [main] to "./platform"
foo : a -> {} | a has Hash
main = foo (A (\x -> x) B)
"#
),
@r###"
TYPE MISMATCH /code/proj/Main.roc
This expression has a type that does not implement the abilities it's expected to:
5 main = foo (A (\x -> x) B)
^^^^^^^^^^^^^
Roc can't generate an implementation of the `Hash.Hash` ability for
[A (a -> a) [B]a]b
In particular, an implementation for
a -> a
cannot be generated.
Note: `Hash` cannot be generated for functions.
"###
);
test_report!(
shift_by_negative,
indoc!(