remove solved type hash instance

This commit is contained in:
Folkert 2021-11-14 22:20:06 +01:00
parent 1c6fab7043
commit 2c0aac2a6a
2 changed files with 8 additions and 165 deletions

View file

@ -206,7 +206,7 @@ impl<'a> Default for CapturedSymbols<'a> {
} }
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug)]
pub struct PendingSpecialization<'a> { pub struct PendingSpecialization<'a> {
solved_type: SolvedType, solved_type: SolvedType,
host_exposed_aliases: BumpMap<Symbol, SolvedType>, host_exposed_aliases: BumpMap<Symbol, SolvedType>,
@ -1819,27 +1819,11 @@ fn specialize_externals_others_need<'a>(
layout_cache: &mut LayoutCache<'a>, layout_cache: &mut LayoutCache<'a>,
) { ) {
for (symbol, solved_types) in externals_others_need.specs.iter() { for (symbol, solved_types) in externals_others_need.specs.iter() {
// de-duplicate by the Hash instance (set only deduplicates by Eq instance)
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut seen_hashes = Vec::with_capacity_in(solved_types.len(), env.arena);
let hash_the_thing = |x: &SolvedType| {
let mut hasher = DefaultHasher::new();
x.hash(&mut hasher);
hasher.finish()
};
for solved_type in solved_types { for solved_type in solved_types {
let hash = hash_the_thing(solved_type); // historical note: we used to deduplicate with a hash here,
// but the cost of that hash is very high. So for now we make
if seen_hashes.iter().any(|h| *h == hash) { // duplicate specializations, and the insertion into a hash map
// we've seen this one already // below will deduplicate them.
continue;
}
seen_hashes.push(hash);
let name = *symbol; let name = *symbol;

View file

@ -4,7 +4,6 @@ use roc_collections::all::{ImMap, MutSet, SendMap};
use roc_module::ident::{Lowercase, TagName}; use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::{Located, Region}; use roc_region::all::{Located, Region};
use std::hash::{Hash, Hasher};
/// A marker that a given Subs has been solved. /// A marker that a given Subs has been solved.
/// The only way to obtain a Solved<Subs> is by running the solver on it. /// The only way to obtain a Solved<Subs> is by running the solver on it.
@ -25,151 +24,11 @@ impl<T> Solved<T> {
} }
} }
/// A custom hash instance, that treats flex vars specially, so that #[derive(Debug, Clone)]
///
/// `Foo 100 200 100` hashes to the same as `Foo 300 100 300`
///
/// i.e., we can rename the flex variables, so long as it happens consistently.
/// this is important so we don't generate the same PartialProc twice.
impl Hash for SolvedType {
fn hash<H: Hasher>(&self, state: &mut H) {
hash_solved_type_help(self, &mut Vec::new(), state);
}
}
impl PartialEq for SolvedType {
fn eq(&self, other: &Self) -> bool {
use std::collections::hash_map::DefaultHasher;
let mut state = DefaultHasher::new();
hash_solved_type_help(self, &mut Vec::new(), &mut state);
let hash1 = state.finish();
let mut state = DefaultHasher::new();
hash_solved_type_help(other, &mut Vec::new(), &mut state);
let hash2 = state.finish();
hash1 == hash2
}
}
fn hash_solved_type_help<H: Hasher>(
initial: &SolvedType,
flex_vars: &mut Vec<VarId>,
state: &mut H,
) {
use SolvedType::*;
let mut stack = Vec::with_capacity(63);
stack.push(initial);
while let Some(solved_type) = stack.pop() {
match solved_type {
Flex(var_id) => {
var_id_hash_help(*var_id, flex_vars, state);
}
Wildcard => "wildcard".hash(state),
EmptyRecord => "empty_record".hash(state),
EmptyTagUnion => "empty_tag_union".hash(state),
Error => "error".hash(state),
Func(arguments, closure, result) => {
stack.extend(arguments);
stack.push(closure);
stack.push(result);
}
Apply(name, arguments) => {
name.hash(state);
stack.extend(arguments);
}
Rigid(name) => name.hash(state),
Erroneous(problem) => problem.hash(state),
Record { fields, ext } => {
for (name, x) in fields {
name.hash(state);
"record_field".hash(state);
stack.push(x.as_inner());
}
stack.push(ext);
}
TagUnion(tags, ext) => {
for (name, arguments) in tags {
name.hash(state);
stack.extend(arguments);
}
stack.push(ext);
}
FunctionOrTagUnion(_, _, ext) => {
stack.push(ext);
}
RecursiveTagUnion(rec, tags, ext) => {
var_id_hash_help(*rec, flex_vars, state);
for (name, arguments) in tags {
name.hash(state);
stack.extend(arguments);
}
stack.push(ext);
}
Alias(name, arguments, solved_lambda_sets, actual) => {
name.hash(state);
for (name, x) in arguments {
name.hash(state);
stack.push(x);
}
for set in solved_lambda_sets {
stack.push(&set.0);
}
stack.push(actual);
}
HostExposedAlias {
name,
arguments,
lambda_set_variables: solved_lambda_sets,
actual,
actual_var,
} => {
name.hash(state);
for (name, x) in arguments {
name.hash(state);
stack.push(x);
}
for set in solved_lambda_sets {
stack.push(&set.0);
}
stack.push(actual);
var_id_hash_help(*actual_var, flex_vars, state);
}
}
}
}
fn var_id_hash_help<H: Hasher>(var_id: VarId, flex_vars: &mut Vec<VarId>, state: &mut H) {
let opt_index = flex_vars.iter().position(|x| *x == var_id);
match opt_index {
Some(index) => index.hash(state),
None => {
flex_vars.len().hash(state);
flex_vars.push(var_id);
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SolvedLambdaSet(pub SolvedType); pub struct SolvedLambdaSet(pub SolvedType);
/// This is a fully solved type, with no Variables remaining in it. /// This is a fully solved type, with no Variables remaining in it.
#[derive(Debug, Clone, Eq)] #[derive(Debug, Clone)]
pub enum SolvedType { pub enum SolvedType {
/// A function. The types of its arguments, then the type of its return value. /// A function. The types of its arguments, then the type of its return value.
Func(Vec<SolvedType>, Box<SolvedType>, Box<SolvedType>), Func(Vec<SolvedType>, Box<SolvedType>, Box<SolvedType>),
@ -531,7 +390,7 @@ impl SolvedType {
} }
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug)]
pub struct BuiltinAlias { pub struct BuiltinAlias {
pub region: Region, pub region: Region,
pub vars: Vec<Located<Lowercase>>, pub vars: Vec<Located<Lowercase>>,