mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
Implement hash derivation for tuple
This commit is contained in:
parent
66fb2f476e
commit
8f7b6aaeaa
2 changed files with 90 additions and 3 deletions
|
@ -19,8 +19,8 @@ use roc_types::{
|
|||
num::int_lit_width_to_variable,
|
||||
subs::{
|
||||
Content, ExhaustiveMark, FlatType, GetSubsSlice, LambdaSet, OptVariable, RecordFields,
|
||||
RedundantMark, Subs, SubsIndex, SubsSlice, TagExt, UnionLambdas, UnionTags, Variable,
|
||||
VariableSubsSlice,
|
||||
RedundantMark, Subs, SubsIndex, SubsSlice, TagExt, TupleElems, UnionLambdas, UnionTags,
|
||||
Variable, VariableSubsSlice,
|
||||
},
|
||||
types::RecordField,
|
||||
};
|
||||
|
@ -30,7 +30,7 @@ use crate::{synth_var, util::Env, DerivedBody};
|
|||
pub(crate) fn derive_hash(env: &mut Env<'_>, key: FlatHashKey, def_symbol: Symbol) -> DerivedBody {
|
||||
let (body_type, body) = match key {
|
||||
FlatHashKey::Record(fields) => hash_record(env, def_symbol, fields),
|
||||
FlatHashKey::Tuple(_fields) => todo!(),
|
||||
FlatHashKey::Tuple(arity) => hash_tuple(env, def_symbol, arity),
|
||||
FlatHashKey::TagUnion(tags) => {
|
||||
if tags.len() == 1 {
|
||||
hash_newtype_tag_union(env, def_symbol, tags.into_iter().next().unwrap())
|
||||
|
@ -123,6 +123,76 @@ fn hash_record(env: &mut Env<'_>, fn_name: Symbol, fields: Vec<Lowercase>) -> (V
|
|||
)
|
||||
}
|
||||
|
||||
fn hash_tuple(env: &mut Env<'_>, fn_name: Symbol, arity: u32) -> (Variable, Expr) {
|
||||
// Suppose tup = (v1, ..., vn).
|
||||
// Build a generalized type t_tup = (t1, ..., tn), with fresh t1, ..., tn,
|
||||
// so that we can re-use the derived impl for many tuples of the same arity.
|
||||
let (tuple_var, tuple_elems) = {
|
||||
// TODO: avoid an allocation here by pre-allocating the indices and variables `TupleElems`
|
||||
// will be instantiated with.
|
||||
let flex_elems: Vec<_> = (0..arity)
|
||||
.into_iter()
|
||||
.map(|i| (i as usize, env.subs.fresh_unnamed_flex_var()))
|
||||
.collect();
|
||||
let elems = TupleElems::insert_into_subs(env.subs, flex_elems);
|
||||
let tuple_var = synth_var(
|
||||
env.subs,
|
||||
Content::Structure(FlatType::Tuple(elems, Variable::EMPTY_TUPLE)),
|
||||
);
|
||||
|
||||
(tuple_var, elems)
|
||||
};
|
||||
|
||||
// Now, a hasher for this tuple is
|
||||
//
|
||||
// hash_tup : hasher, (t1, ..., tn) -> hasher | hasher has Hasher
|
||||
// hash_tup = \hasher, tup ->
|
||||
// Hash.hash (
|
||||
// Hash.hash
|
||||
// ...
|
||||
// (Hash.hash hasher tup.0)
|
||||
// ...
|
||||
// tup.n1)
|
||||
// tup.n
|
||||
//
|
||||
// So, just a build a fold travelling up the elements.
|
||||
let tup_sym = env.new_symbol("tup");
|
||||
|
||||
let hasher_sym = env.new_symbol("hasher");
|
||||
let hasher_var = synth_var(env.subs, Content::FlexAbleVar(None, Subs::AB_HASHER));
|
||||
|
||||
let (body_var, body) = tuple_elems.iter_all().fold(
|
||||
(hasher_var, Expr::Var(hasher_sym, hasher_var)),
|
||||
|total_hasher, (elem_idx, elem_var)| {
|
||||
let index = env.subs[elem_idx];
|
||||
let elem_var = env.subs[elem_var];
|
||||
|
||||
let elem_access = Expr::TupleAccess {
|
||||
tuple_var,
|
||||
elem_var,
|
||||
ext_var: env.subs.fresh_unnamed_flex_var(),
|
||||
loc_expr: Box::new(Loc::at_zero(Expr::Var(
|
||||
tup_sym,
|
||||
env.subs.fresh_unnamed_flex_var(),
|
||||
))),
|
||||
index,
|
||||
};
|
||||
|
||||
call_hash_hash(env, total_hasher, (elem_var, elem_access))
|
||||
},
|
||||
);
|
||||
|
||||
// Finally, build the closure
|
||||
// \hasher, rcd -> body
|
||||
build_outer_derived_closure(
|
||||
env,
|
||||
fn_name,
|
||||
(hasher_var, hasher_sym),
|
||||
(tuple_var, Pattern::Identifier(tup_sym)),
|
||||
(body_var, body),
|
||||
)
|
||||
}
|
||||
|
||||
/// Build a `hash` implementation for a non-singleton tag union.
|
||||
fn hash_tag_union(
|
||||
env: &mut Env<'_>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue