diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index c0c02f3466..4b1ab497f3 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -206,7 +206,7 @@ impl<'a> Default for CapturedSymbols<'a> { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct PendingSpecialization<'a> { solved_type: SolvedType, host_exposed_aliases: BumpMap, @@ -550,8 +550,6 @@ impl<'a> Procs<'a> { // if we've already specialized this one, no further work is needed. if !already_specialized { - let pending = PendingSpecialization::from_var(env.arena, env.subs, annotation); - if self.is_module_thunk(symbol) { debug_assert!(layout.arguments.is_empty()); } @@ -559,6 +557,8 @@ impl<'a> Procs<'a> { match &mut self.pending_specializations { Some(pending_specializations) => { // 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); match self.partial_procs.symbol_to_id(symbol) { @@ -626,12 +626,13 @@ impl<'a> Procs<'a> { self.partial_procs.insert(symbol, partial_proc) }; - match specialize( + match specialize_variable( env, self, symbol, layout_cache, - pending, + annotation, + BumpMap::new_in(env.arena), partial_proc_id, ) { Ok((proc, layout)) => { @@ -1819,27 +1820,11 @@ fn specialize_externals_others_need<'a>( layout_cache: &mut LayoutCache<'a>, ) { 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 { - let hash = hash_the_thing(solved_type); - - if seen_hashes.iter().any(|h| *h == hash) { - // we've seen this one already - continue; - } - - seen_hashes.push(hash); + // historical note: we used to deduplicate with a hash here, + // but the cost of that hash is very high. So for now we make + // duplicate specializations, and the insertion into a hash map + // below will deduplicate them. let name = *symbol; @@ -6633,8 +6618,6 @@ fn call_by_name_help<'a>( assign_to_symbols(env, procs, layout_cache, iter, result) } } else { - let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var); - // When requested (that is, when procs.pending_specializations is `Some`), // 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)); // register the pending specialization, so this gets code genned later + let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var); add_pending( pending_specializations, proc_name, @@ -6689,17 +6673,6 @@ fn call_by_name_help<'a>( None => { 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(); match opt_partial_proc { @@ -6711,8 +6684,15 @@ fn call_by_name_help<'a>( .specialized .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)) => { // now we just call our freshly-specialized function call_specialized_proc( @@ -6790,8 +6770,6 @@ fn call_by_name_module_thunk<'a>( if already_specialized { force_thunk(env, proc_name, inner_layout, assigned, hole) } else { - let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var); - // When requested (that is, when procs.pending_specializations is `Some`), // 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)); // register the pending specialization, so this gets code genned later + let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var); add_pending( pending_specializations, proc_name, @@ -6833,8 +6812,15 @@ fn call_by_name_module_thunk<'a>( .specialized .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)) => { debug_assert!( raw_layout.is_zero_argument_thunk(), diff --git a/compiler/types/src/solved_types.rs b/compiler/types/src/solved_types.rs index a1768f6e7f..5173dc43af 100644 --- a/compiler/types/src/solved_types.rs +++ b/compiler/types/src/solved_types.rs @@ -4,7 +4,6 @@ use roc_collections::all::{ImMap, MutSet, SendMap}; use roc_module::ident::{Lowercase, TagName}; use roc_module::symbol::Symbol; use roc_region::all::{Located, Region}; -use std::hash::{Hash, Hasher}; /// A marker that a given Subs has been solved. /// The only way to obtain a Solved is by running the solver on it. @@ -25,151 +24,11 @@ impl Solved { } } -/// A custom hash instance, that treats flex vars specially, so that -/// -/// `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(&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( - initial: &SolvedType, - flex_vars: &mut Vec, - 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(var_id: VarId, flex_vars: &mut Vec, 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)] +#[derive(Debug, Clone)] pub struct SolvedLambdaSet(pub SolvedType); /// This is a fully solved type, with no Variables remaining in it. -#[derive(Debug, Clone, Eq)] +#[derive(Debug, Clone)] pub enum SolvedType { /// A function. The types of its arguments, then the type of its return value. Func(Vec, Box, Box), @@ -531,7 +390,7 @@ impl SolvedType { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct BuiltinAlias { pub region: Region, pub vars: Vec>,