use crate::ident::{Ident, ModuleName}; use crate::module_err::{IdentIdNotFound, ModuleIdNotFound, ModuleResult}; use roc_collections::all::{default_hasher, MutMap, SendMap}; use roc_ident::IdentStr; use roc_region::all::Region; use snafu::OptionExt; use std::collections::HashMap; use std::{fmt, u32}; // TODO: benchmark this as { ident_id: u32, module_id: u32 } and see if perf stays the same #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Symbol(u64); // When this is `true` (which it normally should be), Symbol's Debug::fmt implementation // attempts to pretty print debug symbols using interns recorded using // register_debug_idents calls (which should be made in debug mode). // 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 = 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 /// behind a mutex, so it is neither populated nor available in release builds. impl Symbol { // NOTE: the define_builtins! macro adds a bunch of constants to this impl, // // e.g. pub const NUM_NUM: Symbol = … pub const fn new(module_id: ModuleId, ident_id: IdentId) -> Symbol { // The bit layout of the u64 inside a Symbol is: // // |------ 32 bits -----|------ 32 bits -----| // | ident_id | module_id | // |--------------------|--------------------| // // module_id comes second because we need to query it more often, // and this way we can get it by truncating the u64 to u32, // whereas accessing the first slot requires a bit shift first. let bits = ((ident_id.0 as u64) << 32) | (module_id.0 as u64); Symbol(bits) } pub fn module_id(self) -> ModuleId { ModuleId(self.0 as u32) } pub fn ident_id(self) -> IdentId { IdentId((self.0 >> 32) as u32) } pub fn is_builtin(self) -> bool { self.module_id().is_builtin() } pub fn module_string<'a>(&self, interns: &'a Interns) -> &'a ModuleName { interns .module_ids .get_name(self.module_id()) .unwrap_or_else(|| { panic!( "module_string could not find IdentIds for module {:?} in {:?}", self.module_id(), interns ) }) } pub fn as_str(self, interns: &Interns) -> &str { self.ident_str(interns).as_str() } pub fn ident_str(self, interns: &Interns) -> &IdentStr { let ident_ids = interns .all_ident_ids .get(&self.module_id()) .unwrap_or_else(|| { panic!( "ident_string could not find IdentIds for module {:?} in {:?}", self.module_id(), interns ) }); ident_ids .get_name(self.ident_id()) .unwrap_or_else(|| { panic!( "ident_string's IdentIds did not contain an entry for {} in module {:?}", self.ident_id().0, self.module_id() ) }) .into() } pub fn as_u64(self) -> u64 { self.0 } pub fn fully_qualified(self, interns: &Interns, home: ModuleId) -> ModuleName { let module_id = self.module_id(); if module_id == home { self.ident_str(interns).clone().into() } else { // TODO do this without format! to avoid allocation for short strings format!( "{}.{}", self.module_string(interns).as_str(), self.ident_str(interns) ) .into() } } pub const fn to_ne_bytes(self) -> [u8; 8] { self.0.to_ne_bytes() } } /// Rather than displaying as this: /// /// Symbol("Foo.bar") /// /// ...instead display as this: /// /// `Foo.bar` impl fmt::Debug for Symbol { #[cfg(debug_assertions)] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if PRETTY_PRINT_DEBUG_SYMBOLS { let module_id = self.module_id(); let ident_id = self.ident_id(); match DEBUG_IDENT_IDS_BY_MODULE_ID.lock() { Ok(names) => match &names.get(&module_id.0) { Some(ident_ids) => match ident_ids.get_name(ident_id) { Some(ident_str) => write!(f, "`{:?}.{}`", module_id, ident_str), None => fallback_debug_fmt(*self, f), }, None => fallback_debug_fmt(*self, f), }, Err(err) => { // Print and return Err rather than panicking, because this // might be used in a panic error message, and if we panick // while we're already panicking it'll kill the process // without printing any of the errors! println!("DEBUG INFO: Failed to acquire lock for Debug reading from DEBUG_IDENT_IDS_BY_MODULE_ID, presumably because a thread panicked: {:?}", err); fallback_debug_fmt(*self, f) } } } else { fallback_debug_fmt(*self, f) } } #[cfg(not(debug_assertions))] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fallback_debug_fmt(*self, f) } } impl fmt::Display for Symbol { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let module_id = self.module_id(); let ident_id = self.ident_id(); match ident_id { IdentId(value) => write!(f, "{:?}.{:?}", module_id, value), } } } impl From for u64 { fn from(symbol: Symbol) -> Self { symbol.0 } } fn fallback_debug_fmt(symbol: Symbol, f: &mut fmt::Formatter) -> fmt::Result { let module_id = symbol.module_id(); let ident_id = symbol.ident_id(); write!(f, "`{:?}.{:?}`", module_id, ident_id) } // TODO this is only here to prevent clippy from complaining about an unused // #[macro_use] on lazy_statc in --release builds, because as of January 2020, // we only use lazy_static in the debug configuration. If we ever start using // lazy_static in release builds, this do-nothing macro invocation will be safe to delete! // // There's probably also a way to get clippy to stop complaining about the unused // #[macro_use] but it didn't seem worth the effort since probably someday we'll // end up using it in release builds anyway. Right? ...Right? lazy_static! {} #[cfg(debug_assertions)] lazy_static! { /// This is used in Debug builds only, to let us have a Debug instance /// which displays not only the Module ID, but also the Module Name which /// corresponds to that ID. /// static ref DEBUG_MODULE_ID_NAMES: std::sync::Mutex>> = // This stores a u32 key instead of a ModuleId key so that if there's // a problem with ModuleId's Debug implementation, logging this for diagnostic // purposes won't recursively trigger ModuleId's Debug instance in the course of printing // this out. std::sync::Mutex::new(roc_collections::all::MutMap::default()); } #[derive(Debug, Default)] pub struct Interns { pub module_ids: ModuleIds, pub all_ident_ids: MutMap, } impl Interns { pub fn module_id(&mut self, name: &ModuleName) -> ModuleId { self.module_ids.get_or_insert(name) } pub fn module_name(&self, module_id: ModuleId) -> &ModuleName { self.module_ids.get_name(module_id).unwrap_or_else(|| { panic!( "Unable to find interns entry for module_id {:?} in Interns {:?}", module_id, self ) }) } pub fn symbol(&self, module_id: ModuleId, ident: IdentStr) -> Symbol { let ident: Ident = ident.into(); match self.all_ident_ids.get(&module_id) { Some(ident_ids) => match ident_ids.get_id(&ident) { Some(ident_id) => Symbol::new(module_id, *ident_id), None => { panic!("Interns::symbol could not find ident entry for {:?} for module {:?} in Interns {:?}", ident, module_id, self); } }, None => { panic!( "Interns::symbol could not find entry for module {:?} in Interns {:?}", module_id, self ); } } } pub fn from_index(module_id: ModuleId, ident_id: u32) -> Symbol { Symbol::new(module_id, IdentId(ident_id)) } } pub fn get_module_ident_ids<'a>( all_ident_ids: &'a MutMap, module_id: &ModuleId, ) -> ModuleResult<&'a IdentIds> { all_ident_ids .get(module_id) .with_context(|| ModuleIdNotFound { module_id: format!("{:?}", module_id), all_ident_ids: format!("{:?}", all_ident_ids), }) } pub fn get_module_ident_ids_mut<'a>( all_ident_ids: &'a mut MutMap, module_id: &ModuleId, ) -> ModuleResult<&'a mut IdentIds> { all_ident_ids .get_mut(module_id) .with_context(|| ModuleIdNotFound { module_id: format!("{:?}", module_id), all_ident_ids: "I could not return all_ident_ids here because of borrowing issues.", }) } #[cfg(debug_assertions)] lazy_static! { /// This is used in Debug builds only, to let us have a Debug instance /// which displays not only the Module ID, but also the Module Name which /// corresponds to that ID. static ref DEBUG_IDENT_IDS_BY_MODULE_ID: std::sync::Mutex> = // This stores a u32 key instead of a ModuleId key so that if there's // a problem with ModuleId's Debug implementation, logging this for diagnostic // purposes won't recursively trigger ModuleId's Debug instance in the course of printing // this out. std::sync::Mutex::new(roc_collections::all::MutMap::default()); } /// A globally unique ID that gets assigned to each module as it is loaded. #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct ModuleId(u32); impl ModuleId { // NOTE: the define_builtins! macro adds a bunch of constants to this impl, // // e.g. pub const NUM: ModuleId = … #[cfg(debug_assertions)] pub fn register_debug_idents(self, ident_ids: &IdentIds) { let mut all = DEBUG_IDENT_IDS_BY_MODULE_ID.lock().expect("Failed to acquire lock for Debug interning into DEBUG_MODULE_ID_NAMES, presumably because a thread panicked."); all.insert(self.0, ident_ids.clone()); } #[cfg(not(debug_assertions))] pub fn register_debug_idents(self, _ident_ids: &IdentIds) { // This is a no-op that should get DCE'd } pub fn to_ident_str(self, interns: &Interns) -> &ModuleName { interns .module_ids .get_name(self) .unwrap_or_else(|| panic!("Could not find ModuleIds for {:?}", self)) } } impl fmt::Debug for ModuleId { /// In debug builds, whenever we create a new ModuleId, we record is name in /// a global interning table so that Debug can look it up later. That table /// needs a global mutex, so we don't do this in release builds. This means /// the Debug impl in release builds only shows the number, not the name (which /// it does not have available, due to having never stored it in the mutexed intern table.) #[cfg(debug_assertions)] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Originally, this printed both name and numeric ID, but the numeric ID // didn't seem to add anything useful. Feel free to temporarily re-add it // if it's helpful in debugging! let names = DEBUG_MODULE_ID_NAMES .lock() .expect("Failed to acquire lock for Debug reading from DEBUG_MODULE_ID_NAMES, presumably because a thread panicked."); if PRETTY_PRINT_DEBUG_SYMBOLS { match names.get(&self.0) { Some(str_ref) => write!(f, "{}", str_ref.clone()), None => { panic!( "Could not find a Debug name for module ID {} in {:?}", self.0, names, ); } } } else { write!(f, "{}", self.0) } } /// In release builds, all we have access to is the number, so only display that. #[cfg(not(debug_assertions))] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) } } /// pf.Task /// 1. build mapping from short name to package /// 2. when adding new modules from package we need to register them in some other map (this module id goes with short name) (shortname, module-name) -> moduleId /// 3. pass this around to other modules getting headers parsed. when parsing interfaces we need to use this map to reference shortnames /// 4. throw away short names. stash the module id in the can env under the resolved module name /// 5. test: #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum PackageQualified<'a, T> { Unqualified(T), Qualified(&'a str, T), } /// Package-qualified module name pub type PQModuleName<'a> = PackageQualified<'a, ModuleName>; impl<'a, T> PackageQualified<'a, T> { pub fn as_inner(&self) -> &T { match self { PackageQualified::Unqualified(name) => name, PackageQualified::Qualified(_, name) => name, } } } #[derive(Debug, Clone)] pub struct PackageModuleIds<'a> { by_name: MutMap, ModuleId>, by_id: Vec>, } impl<'a> PackageModuleIds<'a> { pub fn get_or_insert(&mut self, module_name: &PQModuleName<'a>) -> ModuleId { match self.by_name.get(module_name) { Some(id) => *id, None => { let by_id = &mut self.by_id; let module_id = ModuleId(by_id.len() as u32); by_id.push(module_name.clone()); self.by_name.insert(module_name.clone(), module_id); if cfg!(debug_assertions) { Self::insert_debug_name(module_id, module_name); } module_id } } } pub fn into_module_ids(self) -> ModuleIds { let by_name: MutMap = self .by_name .into_iter() .map(|(pqname, module_id)| (pqname.as_inner().clone(), module_id)) .collect(); let by_id: Vec = self .by_id .into_iter() .map(|pqname| pqname.as_inner().clone()) .collect(); ModuleIds { by_name, by_id } } #[cfg(debug_assertions)] fn insert_debug_name(module_id: ModuleId, module_name: &PQModuleName) { let mut names = DEBUG_MODULE_ID_NAMES.lock().expect("Failed to acquire lock for Debug interning into DEBUG_MODULE_ID_NAMES, presumably because a thread panicked."); names .entry(module_id.0) .or_insert_with(|| match module_name { PQModuleName::Unqualified(module) => module.as_str().into(), PQModuleName::Qualified(package, module) => { let name = format!("{}.{}", package, module.as_str()).into(); name } }); } #[cfg(not(debug_assertions))] fn insert_debug_name(_module_id: ModuleId, _module_name: &PQModuleName) { // By design, this is a no-op in release builds! } pub fn get_id(&self, module_name: &PQModuleName<'a>) -> Option<&ModuleId> { self.by_name.get(module_name) } pub fn get_name(&self, id: ModuleId) -> Option<&PQModuleName> { self.by_id.get(id.0 as usize) } pub fn available_modules(&self) -> impl Iterator { self.by_id.iter() } } /// Stores a mapping between ModuleId and InlinableString. /// /// Each module name is stored twice, for faster lookups. /// Since these are interned strings, this shouldn't result in many total allocations in practice. #[derive(Debug, Clone)] pub struct ModuleIds { by_name: MutMap, /// Each ModuleId is an index into this Vec by_id: Vec, } impl ModuleIds { pub fn get_or_insert(&mut self, module_name: &ModuleName) -> ModuleId { match self.by_name.get(module_name) { Some(id) => *id, None => { let by_id = &mut self.by_id; let module_id = ModuleId(by_id.len() as u32); by_id.push(module_name.clone()); self.by_name.insert(module_name.clone(), module_id); if cfg!(debug_assertions) { Self::insert_debug_name(module_id, module_name); } module_id } } } #[cfg(debug_assertions)] fn insert_debug_name(module_id: ModuleId, module_name: &ModuleName) { let mut names = DEBUG_MODULE_ID_NAMES.lock().expect("Failed to acquire lock for Debug interning into DEBUG_MODULE_ID_NAMES, presumably because a thread panicked."); // TODO make sure modules are never added more than once! names .entry(module_id.0) .or_insert_with(|| module_name.as_str().to_string().into()); } #[cfg(not(debug_assertions))] fn insert_debug_name(_module_id: ModuleId, _module_name: &ModuleName) { // By design, this is a no-op in release builds! } pub fn get_id(&self, module_name: &ModuleName) -> Option<&ModuleId> { self.by_name.get(module_name) } pub fn get_name(&self, id: ModuleId) -> Option<&ModuleName> { self.by_id.get(id.0 as usize) } pub fn available_modules(&self) -> impl Iterator { self.by_id.iter() } } /// An ID that is assigned to interned string identifiers within a module. /// By turning these strings into numbers, post-canonicalization processes /// like unification and optimization can run a lot faster. /// /// This ID is unique within a given module, not globally - so to turn this back into /// a string, you would need a ModuleId, an IdentId, and a Map>. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct IdentId(u32); /// Stores a mapping between IdentId and InlinableString. /// /// Each module name is stored twice, for faster lookups. /// Since these are interned strings, this shouldn't result in many total allocations in practice. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct IdentIds { by_ident: MutMap, /// Each IdentId is an index into this Vec by_id: Vec, next_generated_name: u32, } impl IdentIds { pub fn idents(&self) -> impl Iterator { self.by_id .iter() .enumerate() .map(|(index, ident)| (IdentId(index as u32), ident)) } pub fn add(&mut self, ident_name: Ident) -> IdentId { let by_id = &mut self.by_id; let ident_id = IdentId(by_id.len() as u32); self.by_ident.insert(ident_name.clone(), ident_id); by_id.push(ident_name); ident_id } pub fn get_or_insert(&mut self, name: &Ident) -> IdentId { use std::collections::hash_map::Entry; match self.by_ident.entry(name.clone()) { Entry::Occupied(occupied) => *occupied.get(), Entry::Vacant(vacant) => { let by_id = &mut self.by_id; let ident_id = IdentId(by_id.len() as u32); by_id.push(name.clone()); vacant.insert(ident_id); ident_id } } } // necessary when the name of a value is changed in the editor pub fn update_key( &mut self, old_ident_name: &str, new_ident_name: &str, ) -> Result { let old_ident: Ident = old_ident_name.into(); let ident_id_ref_opt = self.by_ident.get(&old_ident); match ident_id_ref_opt { Some(ident_id_ref) => { let ident_id = *ident_id_ref; self.by_ident.remove(&old_ident); self.by_ident.insert(new_ident_name.into(), ident_id); let by_id = &mut self.by_id; let key_index_opt = by_id.iter().position(|x| *x == old_ident); if let Some(key_index) = key_index_opt { if let Some(vec_elt) = by_id.get_mut(key_index) { *vec_elt = new_ident_name.into(); } else { // we get the index from by_id unreachable!() } Ok(ident_id) } else { Err( format!( "Tried to find position of key {:?} in IdentIds.by_id but I could not find the key. IdentIds.by_id: {:?}", old_ident_name, self.by_id ) ) } } None => Err(format!( "Tried to update key in IdentIds ({:?}) but I could not find the key ({}).", self.by_ident, old_ident_name )), } } /// Generates a unique, new name that's just a strigified integer /// (e.g. "1" or "5"), using an internal counter. Since valid Roc variable /// names cannot begin with a number, this has no chance of colliding /// with actual user-defined variables. /// /// This is used, for example, during canonicalization of an Expr::Closure /// to generate a unique symbol to refer to that closure. pub fn gen_unique(&mut self) -> IdentId { // TODO convert this directly from u32 into IdentStr, // without allocating an extra string along the way like this. let ident = self.next_generated_name.to_string().into(); self.next_generated_name += 1; self.add(ident) } pub fn get_id(&self, ident_name: &Ident) -> Option<&IdentId> { self.by_ident.get(ident_name) } pub fn get_name(&self, id: IdentId) -> Option<&Ident> { self.by_id.get(id.0 as usize) } pub fn get_name_str_res(&self, ident_id: IdentId) -> ModuleResult<&str> { Ok(self .get_name(ident_id) .with_context(|| IdentIdNotFound { ident_id, ident_ids_str: format!("{:?}", self), })? .as_inline_str() .as_str()) } } // BUILTINS macro_rules! define_builtins { { $( $module_id:literal $module_const:ident: $module_name:literal => { $( $ident_id:literal $ident_const:ident: $ident_name:literal $($imported:ident)? )+ } )+ num_modules: $total:literal } => { impl IdentIds { pub fn exposed_builtins(extra_capacity: usize) -> MutMap { let mut exposed_idents_by_module = HashMap::with_capacity_and_hasher(extra_capacity + $total, default_hasher()); $( debug_assert!(!exposed_idents_by_module.contains_key(&ModuleId($module_id)), "Error setting up Builtins: when setting up module {} {:?} - the module ID {} is already present in the map. Check the map for duplicate module IDs!", $module_id, $module_name, $module_id); let ident_ids = { let by_id = vec! [ $( $ident_name.into(), )+ ]; let mut by_ident = MutMap::with_capacity_and_hasher(by_id.len(), default_hasher()); $( debug_assert!(by_ident.len() == $ident_id, "Error setting up Builtins: when inserting {} …: {:?} into module {} …: {:?} - this entry was assigned an ID of {}, but based on insertion order, it should have had an ID of {} instead! To fix this, change it from {} …: {:?} to {} …: {:?} instead.", $ident_id, $ident_name, $module_id, $module_name, $ident_id, by_ident.len(), $ident_id, $ident_name, by_ident.len(), $ident_name); let exists = by_ident.insert($ident_name.into(), IdentId($ident_id)); if let Some(_) = exists { debug_assert!(false, "Error setting up Builtins: when inserting {} …: {:?} into module {} …: {:?} - the Ident name {:?} is already present in the map. Check the map for duplicate ident names within the {:?} module!", $ident_id, $ident_name, $module_id, $module_name, $ident_name, $module_name); } )+ IdentIds { by_ident, by_id, next_generated_name: 0, } }; if cfg!(debug_assertions) { let module_id = ModuleId($module_id); let name = PQModuleName::Unqualified($module_name.into()); PackageModuleIds::insert_debug_name(module_id, &name); module_id.register_debug_idents(&ident_ids); } exposed_idents_by_module.insert( ModuleId($module_id), ident_ids ); )+ debug_assert!(exposed_idents_by_module.len() == $total, "Error setting up Builtins: `total:` is set to the wrong amount. It was set to {} but {} modules were set up.", $total, exposed_idents_by_module.len()); exposed_idents_by_module } } impl ModuleId { pub fn is_builtin(&self) -> bool { // This is a builtin ModuleId iff it's below the // total number of builtin modules, since they // take up the first $total ModuleId numbers. self.0 < $total } $( pub const $module_const: ModuleId = ModuleId($module_id); )+ } impl Default for ModuleIds { fn default() -> Self { // +1 because the user will be compiling at least 1 non-builtin module! let capacity = $total + 1; let mut by_name = HashMap::with_capacity_and_hasher(capacity, default_hasher()); let mut by_id = Vec::with_capacity(capacity); let mut insert_both = |id: ModuleId, name_str: &'static str| { let name: ModuleName = name_str.into(); if cfg!(debug_assertions) { Self::insert_debug_name(id, &name); } by_name.insert(name.clone(), id); by_id.push(name); }; $( insert_both(ModuleId($module_id), $module_name); )+ ModuleIds { by_name, by_id } } } impl<'a> Default for PackageModuleIds<'a> { fn default() -> Self { // +1 because the user will be compiling at least 1 non-builtin module! let capacity = $total + 1; let mut by_name = HashMap::with_capacity_and_hasher(capacity, default_hasher()); let mut by_id = Vec::with_capacity(capacity); let mut insert_both = |id: ModuleId, name_str: &'static str| { let raw_name: IdentStr = name_str.into(); let name = PQModuleName::Unqualified(raw_name.into()); if cfg!(debug_assertions) { Self::insert_debug_name(id, &name); } by_name.insert(name.clone(), id); by_id.push(name); }; $( insert_both(ModuleId($module_id), $module_name); )+ PackageModuleIds { by_name, by_id } } } impl Symbol { $( $( pub const $ident_const: Symbol = Symbol::new(ModuleId($module_id), IdentId($ident_id)); )+ )+ /// The default idents that should be in scope, /// and what symbols they should resolve to. /// /// This is for type aliases like `Int` and `Str` and such. pub fn default_in_scope() -> SendMap { let mut scope = SendMap::default(); $( $( $( // TODO is there a cleaner way to do this? // The goal is to make sure that we only // actually import things into scope if // they are tagged as "imported" in define_builtins! let $imported = true; if $imported { scope.insert($ident_name.into(), (Symbol::new(ModuleId($module_id), IdentId($ident_id)), Region::zero())); } )? )+ )+ scope } } }; } // NOTE: Some of these builtins have a # in their names. // This is because they are for compiler use only, and should not cause // namespace conflicts with userspace! define_builtins! { 0 ATTR: "#Attr" => { 0 UNDERSCORE: "_" // the _ used in pattern matches. This is Symbol 0. 1 ATTR_ATTR: "Attr" // the #Attr.Attr type alias, used in uniqueness types. 2 ARG_1: "#arg1" 3 ARG_2: "#arg2" 4 ARG_3: "#arg3" 5 ARG_4: "#arg4" 6 ARG_5: "#arg5" 7 ARG_6: "#arg6" 8 ARG_7: "#arg7" 9 ARG_8: "#arg8" 10 INC: "#inc" // internal function that increments the refcount 11 DEC: "#dec" // internal function that increments the refcount 12 ARG_CLOSURE: "#arg_closure" // symbol used to store the closure record 13 LIST_EQ: "#list_eq" // internal function that checks list equality 14 GENERIC_HASH: "#generic_hash" // hash of arbitrary layouts 15 GENERIC_HASH_REF: "#generic_hash_by_ref" // hash of arbitrary layouts, passed as an opaque pointer 16 GENERIC_EQ_REF: "#generic_eq_by_ref" // equality of arbitrary layouts, passed as an opaque pointer 17 GENERIC_RC_REF: "#generic_rc_by_ref" // refcount of arbitrary layouts, passed as an opaque pointer 18 GENERIC_EQ: "#generic_eq" // internal function that checks generic equality // a user-defined function that we need to capture in a closure // see e.g. Set.walk 19 USER_FUNCTION: "#user_function" // A caller (wrapper) that we pass to zig for it to be able to call Roc functions 20 ZIG_FUNCTION_CALLER: "#zig_function_caller" // a caller (wrapper) for comparison 21 GENERIC_COMPARE_REF: "#generic_compare_ref" // used to initialize parameters in borrow.rs 22 EMPTY_PARAM: "#empty_param" // used by the dev backend to store the pointer to where to store large return types 23 RET_POINTER: "#ret_pointer" // used in wasm dev backend to mark temporary values in the VM stack 24 WASM_TMP: "#wasm_tmp" // the _ used in mono when a specialized symbol is deleted 25 REMOVED_SPECIALIZATION: "#removed_specialization" // used in dev backend 26 DEV_TMP: "#dev_tmp" } 1 NUM: "Num" => { 0 NUM_NUM: "Num" imported // the Num.Num type alias 1 NUM_AT_NUM: "@Num" // the Num.@Num private tag 2 NUM_I128: "I128" imported // the Num.I128 type alias 3 NUM_U128: "U128" imported // the Num.U128 type alias 4 NUM_I64: "I64" imported // the Num.I64 type alias 5 NUM_U64: "U64" imported // the Num.U64 type alias 6 NUM_I32: "I32" imported // the Num.I32 type alias 7 NUM_U32: "U32" imported // the Num.U32 type alias 8 NUM_I16: "I16" imported // the Num.I16 type alias 9 NUM_U16: "U16" imported // the Num.U16 type alias 10 NUM_I8: "I8" imported // the Num.I8 type alias 11 NUM_U8: "U8" imported // the Num.U8 type alias 12 NUM_INTEGER: "Integer" imported // Int : Num Integer 13 NUM_AT_INTEGER: "@Integer" // the Int.@Integer private tag 14 NUM_F64: "F64" imported // the Num.F64 type alias 15 NUM_F32: "F32" imported // the Num.F32 type alias 16 NUM_FLOATINGPOINT: "FloatingPoint" imported // Float : Num FloatingPoint 17 NUM_AT_FLOATINGPOINT: "@FloatingPoint" // the Float.@FloatingPoint private tag 18 NUM_MAX_FLOAT: "maxFloat" 19 NUM_MIN_FLOAT: "minFloat" 20 NUM_ABS: "abs" 21 NUM_NEG: "neg" 22 NUM_ADD: "add" 23 NUM_SUB: "sub" 24 NUM_MUL: "mul" 25 NUM_LT: "isLt" 26 NUM_LTE: "isLte" 27 NUM_GT: "isGt" 28 NUM_GTE: "isGte" 29 NUM_TO_FLOAT: "toFloat" 30 NUM_SIN: "sin" 31 NUM_COS: "cos" 32 NUM_TAN: "tan" 33 NUM_IS_ZERO: "isZero" 34 NUM_IS_EVEN: "isEven" 35 NUM_IS_ODD: "isOdd" 36 NUM_IS_POSITIVE: "isPositive" 37 NUM_IS_NEGATIVE: "isNegative" 38 NUM_REM: "rem" 39 NUM_DIV_FLOAT: "div" 40 NUM_DIV_INT: "divFloor" 41 NUM_MOD_INT: "modInt" 42 NUM_MOD_FLOAT: "modFloat" 43 NUM_SQRT: "sqrt" 44 NUM_LOG: "log" 45 NUM_ROUND: "round" 46 NUM_COMPARE: "compare" 47 NUM_POW: "pow" 48 NUM_CEILING: "ceiling" 49 NUM_POW_INT: "powInt" 50 NUM_FLOOR: "floor" 51 NUM_ADD_WRAP: "addWrap" 52 NUM_ADD_CHECKED: "addChecked" 53 NUM_ADD_SATURATED: "addSaturated" 54 NUM_ATAN: "atan" 55 NUM_ACOS: "acos" 56 NUM_ASIN: "asin" 57 NUM_AT_SIGNED128: "@Signed128" 58 NUM_SIGNED128: "Signed128" imported 59 NUM_AT_SIGNED64: "@Signed64" 60 NUM_SIGNED64: "Signed64" imported 61 NUM_AT_SIGNED32: "@Signed32" 62 NUM_SIGNED32: "Signed32" imported 63 NUM_AT_SIGNED16: "@Signed16" 64 NUM_SIGNED16: "Signed16" imported 65 NUM_AT_SIGNED8: "@Signed8" 66 NUM_SIGNED8: "Signed8" imported 67 NUM_AT_UNSIGNED128: "@Unsigned128" 68 NUM_UNSIGNED128: "Unsigned128" imported 69 NUM_AT_UNSIGNED64: "@Unsigned64" 70 NUM_UNSIGNED64: "Unsigned64" imported 71 NUM_AT_UNSIGNED32: "@Unsigned32" 72 NUM_UNSIGNED32: "Unsigned32" imported 73 NUM_AT_UNSIGNED16: "@Unsigned16" 74 NUM_UNSIGNED16: "Unsigned16" imported 75 NUM_AT_UNSIGNED8: "@Unsigned8" 76 NUM_UNSIGNED8: "Unsigned8" imported 77 NUM_AT_BINARY64: "@Binary64" 78 NUM_BINARY64: "Binary64" imported 79 NUM_AT_BINARY32: "@Binary32" 80 NUM_BINARY32: "Binary32" imported 81 NUM_BITWISE_AND: "bitwiseAnd" 82 NUM_BITWISE_XOR: "bitwiseXor" 83 NUM_BITWISE_OR: "bitwiseOr" 84 NUM_SHIFT_LEFT: "shiftLeftBy" 85 NUM_SHIFT_RIGHT: "shiftRightBy" 86 NUM_SHIFT_RIGHT_ZERO_FILL: "shiftRightZfBy" 87 NUM_SUB_WRAP: "subWrap" 88 NUM_SUB_CHECKED: "subChecked" 89 NUM_SUB_SATURATED: "subSaturated" 90 NUM_MUL_WRAP: "mulWrap" 91 NUM_MUL_CHECKED: "mulChecked" 92 NUM_INT: "Int" imported 93 NUM_FLOAT: "Float" imported 94 NUM_AT_NATURAL: "@Natural" 95 NUM_NATURAL: "Natural" imported 96 NUM_NAT: "Nat" imported 97 NUM_INT_CAST: "intCast" 98 NUM_IS_MULTIPLE_OF: "isMultipleOf" 99 NUM_AT_DECIMAL: "@Decimal" 100 NUM_DECIMAL: "Decimal" imported 101 NUM_DEC: "Dec" imported // the Num.Dectype alias 102 NUM_BYTES_TO_U16: "bytesToU16" 103 NUM_BYTES_TO_U32: "bytesToU32" 104 NUM_CAST_TO_NAT: "#castToNat" 105 NUM_DIV_CEIL: "divCeil" 106 NUM_TO_STR: "toStr" 107 NUM_MIN_I8: "minI8" 108 NUM_MAX_I8: "maxI8" 109 NUM_MIN_U8: "minU8" 110 NUM_MAX_U8: "maxU8" 111 NUM_MIN_I16: "minI16" 112 NUM_MAX_I16: "maxI16" 113 NUM_MIN_U16: "minU16" 114 NUM_MAX_U16: "maxU16" 115 NUM_MIN_I32: "minI32" 116 NUM_MAX_I32: "maxI32" 117 NUM_MIN_U32: "minU32" 118 NUM_MAX_U32: "maxU32" 119 NUM_MIN_I64: "minI64" 120 NUM_MAX_I64: "maxI64" 121 NUM_MIN_U64: "minU64" 122 NUM_MAX_U64: "maxU64" 123 NUM_MIN_I128: "minI128" 124 NUM_MAX_I128: "maxI128" 125 NUM_TO_I8: "toI8" 126 NUM_TO_I8_CHECKED: "toI8Checked" 127 NUM_TO_I16: "toI16" 128 NUM_TO_I16_CHECKED: "toI16Checked" 129 NUM_TO_I32: "toI32" 130 NUM_TO_I32_CHECKED: "toI32Checked" 131 NUM_TO_I64: "toI64" 132 NUM_TO_I64_CHECKED: "toI64Checked" 133 NUM_TO_I128: "toI128" 134 NUM_TO_I128_CHECKED: "toI128Checked" 135 NUM_TO_U8: "toU8" 136 NUM_TO_U8_CHECKED: "toU8Checked" 137 NUM_TO_U16: "toU16" 138 NUM_TO_U16_CHECKED: "toU16Checked" 139 NUM_TO_U32: "toU32" 140 NUM_TO_U32_CHECKED: "toU32Checked" 141 NUM_TO_U64: "toU64" 142 NUM_TO_U64_CHECKED: "toU64Checked" 143 NUM_TO_U128: "toU128" 144 NUM_TO_U128_CHECKED: "toU128Checked" } 2 BOOL: "Bool" => { 0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias 1 BOOL_FALSE: "False" imported // Bool.Bool = [ False, True ] // NB: not strictly needed; used for finding global tag names in error suggestions 2 BOOL_TRUE: "True" imported // Bool.Bool = [ False, True ] // NB: not strictly needed; used for finding global tag names in error suggestions 3 BOOL_AND: "and" 4 BOOL_OR: "or" 5 BOOL_NOT: "not" 6 BOOL_XOR: "xor" 7 BOOL_EQ: "isEq" 8 BOOL_NEQ: "isNotEq" } 3 STR: "Str" => { 0 STR_STR: "Str" imported // the Str.Str type alias 1 STR_AT_STR: "@Str" // the Str.@Str private tag 2 STR_IS_EMPTY: "isEmpty" 3 STR_APPEND: "append" 4 STR_CONCAT: "concat" 5 STR_JOIN_WITH: "joinWith" 6 STR_SPLIT: "split" 7 STR_COUNT_GRAPHEMES: "countGraphemes" 8 STR_STARTS_WITH: "startsWith" 9 STR_ENDS_WITH: "endsWith" 10 STR_FROM_UTF8: "fromUtf8" 11 STR_UT8_PROBLEM: "Utf8Problem" // the Utf8Problem type alias 12 STR_UT8_BYTE_PROBLEM: "Utf8ByteProblem" // the Utf8ByteProblem type alias 13 STR_TO_UTF8: "toUtf8" 14 STR_STARTS_WITH_CODE_PT: "startsWithCodePt" 15 STR_ALIAS_ANALYSIS_STATIC: "#aliasAnalysisStatic" // string with the static lifetime 16 STR_FROM_UTF8_RANGE: "fromUtf8Range" 17 STR_REPEAT: "repeat" 18 STR_TRIM: "trim" 19 STR_TRIM_LEFT: "trimLeft" 20 STR_TRIM_RIGHT: "trimRight" 21 STR_TO_DEC: "toDec" 22 STR_TO_F64: "toF64" 23 STR_TO_F32: "toF32" 24 STR_TO_NAT: "toNat" 25 STR_TO_U128: "toU128" 26 STR_TO_I128: "toI128" 27 STR_TO_U64: "toU64" 28 STR_TO_I64: "toI64" 29 STR_TO_U32: "toU32" 30 STR_TO_I32: "toI32" 31 STR_TO_U16: "toU16" 32 STR_TO_I16: "toI16" 33 STR_TO_U8: "toU8" 34 STR_TO_I8: "toI8" } 4 LIST: "List" => { 0 LIST_LIST: "List" imported // the List.List type alias 1 LIST_AT_LIST: "@List" // the List.@List private tag 2 LIST_IS_EMPTY: "isEmpty" 3 LIST_GET: "get" 4 LIST_SET: "set" 5 LIST_APPEND: "append" 6 LIST_MAP: "map" 7 LIST_LEN: "len" 8 LIST_WALK_BACKWARDS: "walkBackwards" 9 LIST_CONCAT: "concat" 10 LIST_FIRST: "first" 11 LIST_SINGLE: "single" 12 LIST_REPEAT: "repeat" 13 LIST_REVERSE: "reverse" 14 LIST_PREPEND: "prepend" 15 LIST_JOIN: "join" 16 LIST_KEEP_IF: "keepIf" 17 LIST_CONTAINS: "contains" 18 LIST_SUM: "sum" 19 LIST_WALK: "walk" 20 LIST_LAST: "last" 21 LIST_KEEP_OKS: "keepOks" 22 LIST_KEEP_ERRS: "keepErrs" 23 LIST_MAP_WITH_INDEX: "mapWithIndex" 24 LIST_MAP2: "map2" 25 LIST_MAP3: "map3" 26 LIST_PRODUCT: "product" 27 LIST_WALK_UNTIL: "walkUntil" 28 LIST_RANGE: "range" 29 LIST_SORT_WITH: "sortWith" 30 LIST_DROP: "drop" 31 LIST_SWAP: "swap" 32 LIST_DROP_AT: "dropAt" 33 LIST_DROP_LAST: "dropLast" 34 LIST_MIN: "min" 35 LIST_MIN_LT: "#minlt" 36 LIST_MAX: "max" 37 LIST_MAX_GT: "#maxGt" 38 LIST_MAP4: "map4" 39 LIST_DROP_FIRST: "dropFirst" 40 LIST_JOIN_MAP: "joinMap" 41 LIST_JOIN_MAP_CONCAT: "#joinMapConcat" 42 LIST_ANY: "any" 43 LIST_TAKE_FIRST: "takeFirst" 44 LIST_TAKE_LAST: "takeLast" 45 LIST_FIND: "find" 46 LIST_FIND_RESULT: "#find_result" // symbol used in the definition of List.find 47 LIST_SUBLIST: "sublist" 48 LIST_INTERSPERSE: "intersperse" 49 LIST_INTERSPERSE_CLOS: "#intersperseClos" 50 LIST_SPLIT: "split" 51 LIST_SPLIT_CLOS: "#splitClos" 52 LIST_ALL: "all" 53 LIST_DROP_IF: "dropIf" 54 LIST_DROP_IF_PREDICATE: "#dropIfPred" 55 LIST_SORT_ASC: "sortAsc" 56 LIST_SORT_DESC: "sortDesc" 57 LIST_SORT_DESC_COMPARE: "#sortDescCompare" } 5 RESULT: "Result" => { 0 RESULT_RESULT: "Result" imported // the Result.Result type alias 1 RESULT_OK: "Ok" imported // Result.Result a e = [ Ok a, Err e ] // NB: not strictly needed; used for finding global tag names in error suggestions 2 RESULT_ERR: "Err" imported // Result.Result a e = [ Ok a, Err e ] // NB: not strictly needed; used for finding global tag names in error suggestions 3 RESULT_MAP: "map" 4 RESULT_MAP_ERR: "mapErr" 5 RESULT_WITH_DEFAULT: "withDefault" 6 RESULT_AFTER: "after" 7 RESULT_IS_OK: "isOk" 8 RESULT_IS_ERR: "isErr" } 6 DICT: "Dict" => { 0 DICT_DICT: "Dict" imported // the Dict.Dict type alias 1 DICT_AT_DICT: "@Dict" // the Dict.@Dict private tag 2 DICT_EMPTY: "empty" 3 DICT_SINGLE: "single" 4 DICT_GET: "get" 5 DICT_GET_RESULT: "#get_result" // symbol used in the definition of Dict.get 6 DICT_WALK: "walk" 7 DICT_INSERT: "insert" 8 DICT_LEN: "len" 9 DICT_REMOVE: "remove" 10 DICT_CONTAINS: "contains" 11 DICT_KEYS: "keys" 12 DICT_VALUES: "values" 13 DICT_UNION: "union" 14 DICT_INTERSECTION: "intersection" 15 DICT_DIFFERENCE: "difference" } 7 SET: "Set" => { 0 SET_SET: "Set" imported // the Set.Set type alias 1 SET_AT_SET: "@Set" // the Set.@Set private tag 2 SET_EMPTY: "empty" 3 SET_SINGLE: "single" 4 SET_LEN: "len" 5 SET_INSERT: "insert" 6 SET_REMOVE: "remove" 7 SET_UNION: "union" 8 SET_DIFFERENCE: "difference" 9 SET_INTERSECTION: "intersection" 10 SET_TO_LIST: "toList" 11 SET_FROM_LIST: "fromList" 12 SET_WALK: "walk" 13 SET_WALK_USER_FUNCTION: "#walk_user_function" 14 SET_CONTAINS: "contains" } num_modules: 8 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro) }