diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 51814eb973..da89a2fd9f 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -15,7 +15,7 @@ pub struct Symbol(u64); // Set it to false if you want to see the raw ModuleId and IdentId ints, // but please set it back to true before checking in the result! #[cfg(debug_assertions)] -const PRETTY_PRINT_DEBUG_SYMBOLS: bool = false; +const PRETTY_PRINT_DEBUG_SYMBOLS: bool = true; /// In Debug builds only, Symbol has a name() method that lets /// you look up its name in a global intern table. This table is diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index af4b85cd2b..d8ac286f40 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -444,7 +444,7 @@ impl<'a> Procs<'a> { // register the pending specialization, so this gets code genned later add_pending(pending_specializations, symbol, layout.clone(), pending); - debug_assert!(!self.partial_procs.contains_key(&symbol), "Procs was told to insert a value for symbol {:?}, but there was already an entry for that key! Procs should never attempt to insert duplicates.", symbol); + debug_assert!(!self.partial_procs.contains_key(&symbol), "Procs was told to insert a value for symbol {:?}, but there was already an entry for that key! The same PartialProc should never be added twice", symbol); self.partial_procs.insert( symbol, @@ -1336,10 +1336,26 @@ pub fn specialize_all<'a>( mut procs: Procs<'a>, layout_cache: &mut LayoutCache<'a>, ) -> Procs<'a> { + dbg!(&procs.externals_others_need); let it = procs.externals_others_need.specs.clone(); let it = it .into_iter() - .map(|(symbol, solved_types)| solved_types.into_iter().map(move |s| (symbol, s))) + .map(|(symbol, solved_types)| { + let mut as_vec: std::vec::Vec<_> = solved_types.into_iter().collect(); + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let hash_the_thing = |x: &SolvedType| { + let mut hasher = DefaultHasher::new(); + x.hash(&mut hasher); + (hasher.finish()); + }; + + as_vec.sort_by_key(|x| hash_the_thing(x)); + as_vec.dedup_by_key(|x| hash_the_thing(x)); + + as_vec.into_iter().map(move |s| (symbol, s)) + }) .flatten(); for (name, solved_type) in it.into_iter() { let partial_proc = match procs.partial_procs.get(&name) { diff --git a/compiler/types/src/solved_types.rs b/compiler/types/src/solved_types.rs index 5ecb986997..a031c95ecd 100644 --- a/compiler/types/src/solved_types.rs +++ b/compiler/types/src/solved_types.rs @@ -5,6 +5,7 @@ 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,8 +26,114 @@ impl Solved { } } +impl Hash for SolvedType { + fn hash(&self, state: &mut H) { + hash_solved_type_help(self, &mut Vec::new(), state); + } +} + +fn hash_solved_type_help( + solved_type: &SolvedType, + flex_vars: &mut Vec, + state: &mut H, +) { + use SolvedType::*; + + 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) => { + for x in arguments { + hash_solved_type_help(x, flex_vars, state); + } + + hash_solved_type_help(closure, flex_vars, state); + hash_solved_type_help(result, flex_vars, state); + } + Apply(name, arguments) => { + name.hash(state); + for x in arguments { + hash_solved_type_help(x, flex_vars, state); + } + } + Rigid(name) => name.hash(state), + Erroneous(problem) => problem.hash(state), + Boolean(solved_bool) => solved_bool.hash(state), + + Record { fields, ext } => { + for (name, x) in fields { + name.hash(state); + "record_field".hash(state); + hash_solved_type_help(x.as_inner(), flex_vars, state); + } + hash_solved_type_help(ext, flex_vars, state); + } + + TagUnion(tags, ext) => { + for (name, arguments) in tags { + name.hash(state); + for x in arguments { + hash_solved_type_help(x, flex_vars, state); + } + } + hash_solved_type_help(ext, flex_vars, state); + } + + RecursiveTagUnion(rec, tags, ext) => { + var_id_hash_help(*rec, flex_vars, state); + for (name, arguments) in tags { + name.hash(state); + for x in arguments { + hash_solved_type_help(x, flex_vars, state); + } + } + hash_solved_type_help(ext, flex_vars, state); + } + + Alias(name, arguments, actual) => { + name.hash(state); + for (name, x) in arguments { + name.hash(state); + hash_solved_type_help(x, flex_vars, state); + } + hash_solved_type_help(actual, flex_vars, state); + } + + HostExposedAlias { + name, + arguments, + actual, + actual_var, + } => { + name.hash(state); + for (name, x) in arguments { + name.hash(state); + hash_solved_type_help(x, flex_vars, state); + } + hash_solved_type_help(actual, flex_vars, state); + 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); + } + } +} + /// This is a fully solved type, with no Variables remaining in it. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum SolvedType { /// A function. The types of its arguments, then the type of its return value. Func(Vec, Box, Box), diff --git a/examples/effect/Main.roc b/examples/effect/Main.roc index 0a3d1273ab..820b8b02af 100644 --- a/examples/effect/Main.roc +++ b/examples/effect/Main.roc @@ -1,10 +1,8 @@ app Main provides [ main ] imports [ Effect ] - - main : Effect.Effect {} as Fx main = - d = Effect.putChar 68 e = Effect.putChar 69 + d = Effect.putChar 68 - e |> Effect.after \{} -> e + e |> Effect.after \{} -> d