diff --git a/compiler/build/src/program.rs b/compiler/build/src/program.rs index ab35d95158..8e8f7288ed 100644 --- a/compiler/build/src/program.rs +++ b/compiler/build/src/program.rs @@ -21,6 +21,7 @@ use inkwell::targets::{ use std::path::{Path, PathBuf}; use target_lexicon::{Architecture, OperatingSystem, Triple, Vendor}; +// TODO how should imported modules factor into this? What if those use builtins too? // TODO this should probably use more helper functions // TODO make this polymorphic in the llvm functions so it can be reused for another backend. #[allow(clippy::cognitive_complexity)] @@ -110,7 +111,9 @@ pub fn build( } } } - InvalidCycle(_, _) => {} + InvalidCycle(_, _) | Builtin(_) => { + // These can never contain main. + } } } @@ -181,7 +184,7 @@ pub fn build( use roc_can::pattern::Pattern::*; match decl { - Declare(def) => match def.loc_pattern.value { + Declare(def) | Builtin(def) => match def.loc_pattern.value { Identifier(symbol) => { match def.loc_expr.value { Closure(annotation, _, _, loc_args, boxed_body) => { diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index d4835a78dc..2cd85482f7 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -91,6 +91,7 @@ pub enum Declaration { Vec>, Vec<(Region /* pattern */, Region /* expr */)>, ), + Builtin(Def), } impl Declaration { @@ -100,6 +101,7 @@ impl Declaration { Declare(_) => 1, DeclareRec(defs) => defs.len(), InvalidCycle(_, _) => 0, + Builtin(_) => 0, } } } @@ -1249,6 +1251,10 @@ fn decl_to_let( Declaration::InvalidCycle(symbols, regions) => { Expr::RuntimeError(RuntimeError::CircularDef(symbols, regions)) } + Declaration::Builtin(_) => { + // Builtins should only be added to top-level decls, not to let-exprs! + unreachable!() + } } } diff --git a/compiler/can/src/module.rs b/compiler/can/src/module.rs index 81adc519f3..4e7966cc96 100644 --- a/compiler/can/src/module.rs +++ b/compiler/can/src/module.rs @@ -151,6 +151,7 @@ pub fn canonicalize_module_defs<'a>( (Ok(declarations), output) => { use crate::def::Declaration::*; + // Record the variables for all exposed symbols. let mut exposed_vars_by_symbol = Vec::with_capacity(exposed_symbols.len()); for decl in declarations.iter() { @@ -193,6 +194,14 @@ pub fn canonicalize_module_defs<'a>( InvalidCycle(identifiers, _) => { panic!("TODO gracefully handle potentially attempting to expose invalid cyclic defs {:?}" , identifiers); } + Builtin(def) => { + // Builtins cannot be exposed in module declarations. + // This should never happen! + debug_assert!(def + .pattern_vars + .iter() + .all(|(symbol, _)| !exposed_symbols.contains(symbol))); + } } } diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 687aac0baa..9f4ae3eaf9 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -880,7 +880,7 @@ pub fn constrain_decls( for decl in decls.iter().rev() { // NOTE: rigids are empty because they are not shared between top-level definitions match decl { - Declaration::Declare(def) => { + Declaration::Declare(def) | Declaration::Builtin(def) => { constraint = exists_with_aliases( aliases.clone(), Vec::new(), diff --git a/compiler/constrain/src/uniq.rs b/compiler/constrain/src/uniq.rs index 4ae49fb048..9496cd2700 100644 --- a/compiler/constrain/src/uniq.rs +++ b/compiler/constrain/src/uniq.rs @@ -70,7 +70,7 @@ pub fn constrain_decls( for decl in decls.iter().rev() { // NOTE: rigids are empty because they are not shared between top-level definitions match decl { - Declaration::Declare(def) => { + Declaration::Declare(def) | Declaration::Builtin(def) => { sharing::annotate_usage(&def.loc_expr.value, &mut var_usage); } Declaration::DeclareRec(defs) => { @@ -87,7 +87,7 @@ pub fn constrain_decls( for decl in decls.iter().rev() { // NOTE: rigids are empty because they are not shared between top-level definitions match decl { - Declaration::Declare(def) => { + Declaration::Declare(def) | Declaration::Builtin(def) => { constraint = exists_with_aliases( aliases.clone(), Vec::new(), diff --git a/compiler/gen/tests/gen_module.rs b/compiler/gen/tests/gen_module.rs index 69269da461..5e5f68ea19 100644 --- a/compiler/gen/tests/gen_module.rs +++ b/compiler/gen/tests/gen_module.rs @@ -19,7 +19,15 @@ mod gen_module { use inkwell::passes::PassManager; use inkwell::types::BasicType; use inkwell::OptimizationLevel; - use roc_can::{canonicalize_module_defs, operator}; + use roc_can::{ + builtins::builtin_defs, + def::Declaration, + expected::Expected, + expr::{canonicalize_expr, Env}, + module::canonicalize_module_defs, + operator, + scope::Scope, + }; use roc_collections::all::{ImMap, MutMap, MutSet}; use roc_gen::llvm::build::{build_proc, build_proc_header}; use roc_gen::llvm::convert::basic_type_from_layout; @@ -30,7 +38,8 @@ mod gen_module { use roc_parse::blankspace::space0_before; use roc_parse::parser::{self, loc, Parser}; use roc_region::all::{Located, Region}; - use roc_types::subs::{Content, Subs, VarStore, Variable}; + use roc_types::subs::{Subs, VarStore}; + use roc_types::types::Type; static TEST_MODULE_NAME: &str = "Test"; @@ -72,7 +81,10 @@ mod gen_module { region: Region::zero(), value: main_pattern, }; - let loc_def = ast::Def::Body(&loc_main_pattern, &loc_expr); + let loc_def = Located { + region: Region::zero(), + value: ast::Def::Body(&loc_main_pattern, &loc_expr), + }; let loc_defs = bumpalo::vec![in arena; loc_def]; let module_ids = ModuleIds::default(); let home: ModuleId = module_ids.get_or_insert(&TEST_MODULE_NAME.into()); @@ -85,11 +97,11 @@ mod gen_module { exposed_ident_ids.add(main_fn_name.into()); // Canonicalize the module. - let module_output = canonicalize_module_defs( + let mut module_output = canonicalize_module_defs( arena, loc_defs, home, - module_ids, + &module_ids, exposed_ident_ids, dep_idents, exposed_imports, @@ -98,9 +110,26 @@ mod gen_module { ) .expect("Error canonicalizing test module"); - // TODO add the builtins as separate (unexposed) Declarations to the module + // Add the builtins as Declarations to the module. + let module_output = { + let builtins = builtin_defs(&var_store); + let decls = &mut module_output.declarations; + let references = module_output.references; - // TODO type-check the module + decls.reserve(builtins.len()); + + for (symbol, builtin_def) in builtins.into_iter() { + // Only add decls for builtins that were actually referenced. + if references.contains(&symbol) { + decls.push(Declaration::Builtin(builtin_def)); + } + } + + // Release the borrows on declarations and references + module_output + }; + + // TODO type-check the module, making sure to use builtin types from std.rs/unique.rs // TODO monomorphize the module // TODO code gen the module @@ -119,24 +148,6 @@ mod gen_module { &loc_expr.value, ); - let mut with_builtins = loc_expr.value; - - // Add builtin defs (e.g. List.get) directly to the canonical Expr, - // since we aren't using modules here. - let builtin_defs = roc_can::builtins::builtin_defs(&var_store); - - for def in builtin_defs { - with_builtins = Expr::LetNonRec( - Box::new(def), - Box::new(Located { - region: Region::zero(), - value: with_builtins, - }), - var_store.fresh(), - SendMap::default(), - ); - } - let loc_expr = Located { region: loc_expr.region, value: with_builtins, diff --git a/compiler/mono/src/expr.rs b/compiler/mono/src/expr.rs index fad0f4f21d..4c375cfbf9 100644 --- a/compiler/mono/src/expr.rs +++ b/compiler/mono/src/expr.rs @@ -67,6 +67,8 @@ impl<'a> Procs<'a> { self.add_pending_specialization(name, layout.clone(), pending); + debug_assert!(!self.partial_procs.contains_key(&name), "Procs was told to insert a value for key {:?}, but there was already an entry for that key! Procs should never attempt to insert duplicates.", name); + // a named closure self.partial_procs.insert( name,