mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 23:31:12 +00:00
Merge pull request #1981 from rtfeldman/solved-type-remove-hash
Solved type remove hash
This commit is contained in:
commit
b28430451c
2 changed files with 33 additions and 188 deletions
|
@ -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>,
|
||||||
|
@ -550,8 +550,6 @@ impl<'a> Procs<'a> {
|
||||||
|
|
||||||
// if we've already specialized this one, no further work is needed.
|
// if we've already specialized this one, no further work is needed.
|
||||||
if !already_specialized {
|
if !already_specialized {
|
||||||
let pending = PendingSpecialization::from_var(env.arena, env.subs, annotation);
|
|
||||||
|
|
||||||
if self.is_module_thunk(symbol) {
|
if self.is_module_thunk(symbol) {
|
||||||
debug_assert!(layout.arguments.is_empty());
|
debug_assert!(layout.arguments.is_empty());
|
||||||
}
|
}
|
||||||
|
@ -559,6 +557,8 @@ impl<'a> Procs<'a> {
|
||||||
match &mut self.pending_specializations {
|
match &mut self.pending_specializations {
|
||||||
Some(pending_specializations) => {
|
Some(pending_specializations) => {
|
||||||
// register the pending specialization, so this gets code genned later
|
// register the pending specialization, so this gets code genned later
|
||||||
|
let pending =
|
||||||
|
PendingSpecialization::from_var(env.arena, env.subs, annotation);
|
||||||
add_pending(pending_specializations, symbol, layout, pending);
|
add_pending(pending_specializations, symbol, layout, pending);
|
||||||
|
|
||||||
match self.partial_procs.symbol_to_id(symbol) {
|
match self.partial_procs.symbol_to_id(symbol) {
|
||||||
|
@ -626,12 +626,13 @@ impl<'a> Procs<'a> {
|
||||||
self.partial_procs.insert(symbol, partial_proc)
|
self.partial_procs.insert(symbol, partial_proc)
|
||||||
};
|
};
|
||||||
|
|
||||||
match specialize(
|
match specialize_variable(
|
||||||
env,
|
env,
|
||||||
self,
|
self,
|
||||||
symbol,
|
symbol,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
pending,
|
annotation,
|
||||||
|
BumpMap::new_in(env.arena),
|
||||||
partial_proc_id,
|
partial_proc_id,
|
||||||
) {
|
) {
|
||||||
Ok((proc, layout)) => {
|
Ok((proc, layout)) => {
|
||||||
|
@ -1819,27 +1820,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;
|
||||||
|
|
||||||
|
@ -6633,8 +6618,6 @@ fn call_by_name_help<'a>(
|
||||||
assign_to_symbols(env, procs, layout_cache, iter, result)
|
assign_to_symbols(env, procs, layout_cache, iter, result)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
|
|
||||||
|
|
||||||
// When requested (that is, when procs.pending_specializations is `Some`),
|
// When requested (that is, when procs.pending_specializations is `Some`),
|
||||||
// store a pending specialization rather than specializing immediately.
|
// store a pending specialization rather than specializing immediately.
|
||||||
//
|
//
|
||||||
|
@ -6655,6 +6638,7 @@ fn call_by_name_help<'a>(
|
||||||
debug_assert!(!env.is_imported_symbol(proc_name));
|
debug_assert!(!env.is_imported_symbol(proc_name));
|
||||||
|
|
||||||
// register the pending specialization, so this gets code genned later
|
// register the pending specialization, so this gets code genned later
|
||||||
|
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
|
||||||
add_pending(
|
add_pending(
|
||||||
pending_specializations,
|
pending_specializations,
|
||||||
proc_name,
|
proc_name,
|
||||||
|
@ -6689,17 +6673,6 @@ fn call_by_name_help<'a>(
|
||||||
None => {
|
None => {
|
||||||
let opt_partial_proc = procs.partial_procs.symbol_to_id(proc_name);
|
let opt_partial_proc = procs.partial_procs.symbol_to_id(proc_name);
|
||||||
|
|
||||||
/*
|
|
||||||
debug_assert_eq!(
|
|
||||||
argument_layouts.len(),
|
|
||||||
field_symbols.len(),
|
|
||||||
"Function {:?} is called with {} arguments, but the layout expects {}",
|
|
||||||
proc_name,
|
|
||||||
field_symbols.len(),
|
|
||||||
argument_layouts.len(),
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
|
|
||||||
let field_symbols = field_symbols.into_bump_slice();
|
let field_symbols = field_symbols.into_bump_slice();
|
||||||
|
|
||||||
match opt_partial_proc {
|
match opt_partial_proc {
|
||||||
|
@ -6711,8 +6684,15 @@ fn call_by_name_help<'a>(
|
||||||
.specialized
|
.specialized
|
||||||
.insert((proc_name, top_level_layout), InProgress);
|
.insert((proc_name, top_level_layout), InProgress);
|
||||||
|
|
||||||
match specialize(env, procs, proc_name, layout_cache, pending, partial_proc)
|
match specialize_variable(
|
||||||
{
|
env,
|
||||||
|
procs,
|
||||||
|
proc_name,
|
||||||
|
layout_cache,
|
||||||
|
fn_var,
|
||||||
|
BumpMap::new_in(env.arena),
|
||||||
|
partial_proc,
|
||||||
|
) {
|
||||||
Ok((proc, layout)) => {
|
Ok((proc, layout)) => {
|
||||||
// now we just call our freshly-specialized function
|
// now we just call our freshly-specialized function
|
||||||
call_specialized_proc(
|
call_specialized_proc(
|
||||||
|
@ -6790,8 +6770,6 @@ fn call_by_name_module_thunk<'a>(
|
||||||
if already_specialized {
|
if already_specialized {
|
||||||
force_thunk(env, proc_name, inner_layout, assigned, hole)
|
force_thunk(env, proc_name, inner_layout, assigned, hole)
|
||||||
} else {
|
} else {
|
||||||
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
|
|
||||||
|
|
||||||
// When requested (that is, when procs.pending_specializations is `Some`),
|
// When requested (that is, when procs.pending_specializations is `Some`),
|
||||||
// store a pending specialization rather than specializing immediately.
|
// store a pending specialization rather than specializing immediately.
|
||||||
//
|
//
|
||||||
|
@ -6812,6 +6790,7 @@ fn call_by_name_module_thunk<'a>(
|
||||||
debug_assert!(!env.is_imported_symbol(proc_name));
|
debug_assert!(!env.is_imported_symbol(proc_name));
|
||||||
|
|
||||||
// register the pending specialization, so this gets code genned later
|
// register the pending specialization, so this gets code genned later
|
||||||
|
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
|
||||||
add_pending(
|
add_pending(
|
||||||
pending_specializations,
|
pending_specializations,
|
||||||
proc_name,
|
proc_name,
|
||||||
|
@ -6833,8 +6812,15 @@ fn call_by_name_module_thunk<'a>(
|
||||||
.specialized
|
.specialized
|
||||||
.insert((proc_name, top_level_layout), InProgress);
|
.insert((proc_name, top_level_layout), InProgress);
|
||||||
|
|
||||||
match specialize(env, procs, proc_name, layout_cache, pending, partial_proc)
|
match specialize_variable(
|
||||||
{
|
env,
|
||||||
|
procs,
|
||||||
|
proc_name,
|
||||||
|
layout_cache,
|
||||||
|
fn_var,
|
||||||
|
BumpMap::new_in(env.arena),
|
||||||
|
partial_proc,
|
||||||
|
) {
|
||||||
Ok((proc, raw_layout)) => {
|
Ok((proc, raw_layout)) => {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
raw_layout.is_zero_argument_thunk(),
|
raw_layout.is_zero_argument_thunk(),
|
||||||
|
|
|
@ -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>>,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue